Files
UnrealEngine/Engine/Source/Editor/KismetCompiler/Private/KismetCastingUtils.cpp
2025-05-18 13:04:45 +08:00

299 lines
9.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "KismetCastingUtils.h"
#include "BPTerminal.h"
#include "BlueprintCompiledStatement.h"
#include "Containers/Array.h"
#include "Containers/EnumAsByte.h"
#include "Containers/Map.h"
#include "EdGraph/EdGraphNode.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraphSchema_K2.h"
#include "HAL/Platform.h"
#include "HAL/PlatformCrt.h"
#include "Kismet/BlueprintTypeConversions.h"
#include "KismetCompiledFunctionContext.h"
#include "KismetCompilerMisc.h"
#include "KismetCompiler.h"
#include "Misc/AssertionMacros.h"
#include "Misc/Optional.h"
#include "Templates/Casts.h"
#include "Templates/Tuple.h"
#include "UObject/Class.h"
#include "UObject/NameTypes.h"
#include "UObject/WeakObjectPtr.h"
#include "UObject/WeakObjectPtrTemplates.h"
namespace UE::KismetCompiler::CastingUtils
{
FBPTerminal* MakeImplicitCastTerminal(FKismetFunctionContext& Context, UEdGraphPin* Net, UEdGraphNode* SourceNode)
{
check(Net);
FBPTerminal* Result = Context.CreateLocalTerminal();
check(Result);
Result->CopyFromPin(Net, Context.NetNameMap->MakeValidName(Net, TEXT("ImplicitCast")));
Result->Source = (SourceNode ? SourceNode : Net->GetOwningNode());
return Result;
}
void RegisterImplicitCasts(FKismetFunctionContext& Context)
{
auto AddCastMapping = [&Context](UEdGraphPin* DestinationPin, const FConversion& Conversion)
{
check(DestinationPin);
FBPTerminal* NewTerm = MakeImplicitCastTerminal(Context, DestinationPin);
UEdGraphNode* OwningNode = DestinationPin->GetOwningNode();
Context.ImplicitCastMap.Add(DestinationPin, FImplicitCastParams{Conversion, NewTerm, OwningNode});
};
// The current context's NetMap can be a mix of input and output pin types.
// We need to check both pin types in order to get adequate coverage for potential cast points.
for (const auto& It : Context.NetMap)
{
UEdGraphPin* CurrentPin = It.Key;
check(CurrentPin);
bool bIsConnectedOutput =
(CurrentPin->Direction == EGPD_Output) && (CurrentPin->LinkedTo.Num() > 0);
bool bIsConnectedInput =
(CurrentPin->Direction == EGPD_Input) && (CurrentPin->LinkedTo.Num() > 0);
if (bIsConnectedOutput)
{
for (UEdGraphPin* DestinationPin : CurrentPin->LinkedTo)
{
check(DestinationPin);
if (Context.ImplicitCastMap.Contains(DestinationPin))
{
continue;
}
FConversion Conversion = GetFloatingPointConversion(*CurrentPin, *DestinationPin);
if (Conversion.Type != FloatingPointCastType::None)
{
AddCastMapping(DestinationPin, Conversion);
}
}
}
else if (bIsConnectedInput)
{
if (Context.ImplicitCastMap.Contains(CurrentPin))
{
continue;
}
if (CurrentPin->LinkedTo.Num() > 0)
{
const UEdGraphPin* SourcePin = CurrentPin->LinkedTo[0];
check(SourcePin);
FConversion Conversion = GetFloatingPointConversion(*SourcePin, *CurrentPin);
if (Conversion.Type != FloatingPointCastType::None)
{
AddCastMapping(CurrentPin, Conversion);
}
}
}
}
}
void InsertImplicitCastStatement(FKismetFunctionContext& Context, const FImplicitCastParams& CastParams, FBPTerminal* RHSTerm)
{
check(RHSTerm);
check(CastParams.TargetTerminal);
check(CastParams.TargetNode);
EKismetCompiledStatementType CompiledStatementType = {};
UFunction* FunctionToCall = nullptr;
switch (CastParams.Conversion.Type)
{
case FloatingPointCastType::FloatToDouble:
CompiledStatementType = KCST_FloatToDoubleCast;
break;
case FloatingPointCastType::DoubleToFloat:
CompiledStatementType = KCST_DoubleToFloatCast;
break;
case FloatingPointCastType::Container:
case FloatingPointCastType::Struct:
CompiledStatementType = KCST_CallFunction;
FunctionToCall = CastParams.Conversion.Function;
check(FunctionToCall);
break;
default:
check(false);
break;
}
FBlueprintCompiledStatement& CastStatement = Context.AppendStatementForNode(CastParams.TargetNode);
CastStatement.Type = CompiledStatementType;
CastStatement.FunctionToCall = FunctionToCall;
CastStatement.LHS = CastParams.TargetTerminal;
CastStatement.RHS.Add(RHSTerm);
}
FBPTerminal* InsertImplicitCastStatement(FKismetFunctionContext& Context, UEdGraphPin* DestinationPin, FBPTerminal* RHSTerm)
{
check(DestinationPin);
check(RHSTerm);
FBPTerminal* Result = nullptr;
const FImplicitCastParams* CastParams =
Context.ImplicitCastMap.Find(DestinationPin);
if (CastParams != nullptr)
{
InsertImplicitCastStatement(Context, *CastParams, RHSTerm);
Result = CastParams->TargetTerminal;
// Removal of the pin entry indicates to the compiler that the implicit cast has been processed.
Context.ImplicitCastMap.Remove(DestinationPin);
}
return Result;
}
bool RemoveRegisteredImplicitCast(FKismetFunctionContext& Context, const UEdGraphPin* DestinationPin)
{
check(DestinationPin);
int32 RemovedCount = Context.ImplicitCastMap.Remove(DestinationPin);
return (RemovedCount > 0);
}
FConversion GetFloatingPointConversion(const UEdGraphPin& SourcePin, const UEdGraphPin& DestinationPin)
{
using namespace UE::Kismet::BlueprintTypeConversions;
UClass* BlueprintTypeConversionsClass = UBlueprintTypeConversions::StaticClass();
UFunction* ArrayConversionFunction = BlueprintTypeConversionsClass->FindFunctionByName(TEXT("ConvertArrayType"));
check(ArrayConversionFunction);
UFunction* SetConversionFunction = BlueprintTypeConversionsClass->FindFunctionByName(TEXT("ConvertSetType"));
check(SetConversionFunction);
UFunction* MapConversionFunction = BlueprintTypeConversionsClass->FindFunctionByName(TEXT("ConvertMapType"));
check(MapConversionFunction);
if (SourcePin.PinType.IsMap() && DestinationPin.PinType.IsMap())
{
if ((SourcePin.PinType.PinValueType.TerminalCategory == UEdGraphSchema_K2::PC_Real) && (DestinationPin.PinType.PinValueType.TerminalCategory == UEdGraphSchema_K2::PC_Real))
{
if ((SourcePin.PinType.PinValueType.TerminalSubCategory == UEdGraphSchema_K2::PC_Float) && ((DestinationPin.PinType.PinValueType.TerminalSubCategory == UEdGraphSchema_K2::PC_Double)))
{
return {FloatingPointCastType::Container, MapConversionFunction};
}
else if ((SourcePin.PinType.PinValueType.TerminalSubCategory == UEdGraphSchema_K2::PC_Double) && ((DestinationPin.PinType.PinValueType.TerminalSubCategory == UEdGraphSchema_K2::PC_Float)))
{
return {FloatingPointCastType::Container, MapConversionFunction};
}
}
else if ((SourcePin.PinType.PinValueType.TerminalCategory == UEdGraphSchema_K2::PC_Struct) && (DestinationPin.PinType.PinValueType.TerminalCategory == UEdGraphSchema_K2::PC_Struct))
{
UScriptStruct* SourceStruct = Cast<UScriptStruct>(SourcePin.PinType.PinValueType.TerminalSubCategoryObject.Get());
UScriptStruct* DestinationStruct = Cast<UScriptStruct>(DestinationPin.PinType.PinValueType.TerminalSubCategoryObject.Get());
if (FStructConversionTable::Get().GetConversionFunction(SourceStruct, DestinationStruct).IsSet())
{
return {FloatingPointCastType::Container, MapConversionFunction};
}
}
}
if ((SourcePin.PinType.PinCategory == UEdGraphSchema_K2::PC_Real) && (DestinationPin.PinType.PinCategory == UEdGraphSchema_K2::PC_Real))
{
if ((SourcePin.PinType.PinSubCategory == UEdGraphSchema_K2::PC_Float) && ((DestinationPin.PinType.PinSubCategory == UEdGraphSchema_K2::PC_Double)))
{
if (SourcePin.PinType.IsArray())
{
return {FloatingPointCastType::Container, ArrayConversionFunction};
}
else if (SourcePin.PinType.IsSet())
{
return {FloatingPointCastType::Container, SetConversionFunction};
}
else if (SourcePin.PinType.IsMap())
{
return {FloatingPointCastType::Container, MapConversionFunction};
}
else
{
return {FloatingPointCastType::FloatToDouble, nullptr};
}
}
else if ((SourcePin.PinType.PinSubCategory == UEdGraphSchema_K2::PC_Double) && ((DestinationPin.PinType.PinSubCategory == UEdGraphSchema_K2::PC_Float)))
{
if (SourcePin.PinType.IsArray())
{
return {FloatingPointCastType::Container, ArrayConversionFunction};
}
else if (SourcePin.PinType.IsSet())
{
return {FloatingPointCastType::Container, SetConversionFunction};
}
else if (SourcePin.PinType.IsMap())
{
return {FloatingPointCastType::Container, MapConversionFunction};
}
else
{
return {FloatingPointCastType::DoubleToFloat, nullptr};
}
}
}
else if ((SourcePin.PinType.PinCategory == UEdGraphSchema_K2::PC_Struct) && (DestinationPin.PinType.PinCategory == UEdGraphSchema_K2::PC_Struct))
{
UScriptStruct* SourceStruct = Cast<UScriptStruct>(SourcePin.PinType.PinSubCategoryObject.Get());
UScriptStruct* DestinationStruct = Cast<UScriptStruct>(DestinationPin.PinType.PinSubCategoryObject.Get());
// Invalid BPs can have missing PinSubCategoryObject values if there was trouble loading the source struct
if (SourceStruct && DestinationStruct)
{
if (TOptional<ConversionFunctionPairT> ConversionPair = FStructConversionTable::Get().GetConversionFunction(SourceStruct, DestinationStruct))
{
if (SourcePin.PinType.IsArray())
{
return { FloatingPointCastType::Container, ArrayConversionFunction };
}
else if (SourcePin.PinType.IsSet())
{
return { FloatingPointCastType::Container, SetConversionFunction };
}
else if (SourcePin.PinType.IsMap())
{
return { FloatingPointCastType::Container, MapConversionFunction };
}
else
{
return { FloatingPointCastType::Struct, ConversionPair->Get<1>() };
}
}
}
else
{
UE_CLOG(!SourceStruct, LogK2Compiler, Warning, TEXT("Source pin '%s' had null struct object (%s)"), *SourcePin.GetName(), *SourcePin.GetOwningNode()->GetFullName());
UE_CLOG(!DestinationStruct, LogK2Compiler, Warning, TEXT("Destination pin '%s' had null struct object (%s)"), *DestinationPin.GetName(), *DestinationPin.GetOwningNode()->GetFullName());
}
}
return {FloatingPointCastType::None, nullptr};
}
} // namespace UE::KismetCompiler::CastingUtils