You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
We were only considering the first linked pin when iterating over the connections for the input pins. An input pin can be connected to multiple internal nodes, so we need to process those. For example, this problem would manifest if the expression were (x+x), and we connected the x input to a single precision float. #jira UE-145269 #preflight 622a6bfe031cac3623f01924 #rb Ben.Hoffman #lockdown #ROBOMERGE-AUTHOR: dave.jones2 #ROBOMERGE-SOURCE: CL 19342336 in //UE5/Release-5.0/... via CL 19349226 #ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v926-19321884) [CL 19350172 by dave jones2 in ue5-main branch]
363 lines
13 KiB
C++
363 lines
13 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MathExpressionHandler.h"
|
|
#include "UObject/UnrealType.h"
|
|
#include "UObject/Interface.h"
|
|
#include "K2Node_MathExpression.h"
|
|
#include "Engine/BlueprintGeneratedClass.h"
|
|
#include "K2Node_CallFunction.h"
|
|
#include "K2Node_VariableGet.h"
|
|
#include "EdGraphUtilities.h"
|
|
#include "KismetCastingUtils.h"
|
|
#include "KismetCompiler.h"
|
|
#include "Kismet/KismetMathLibrary.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "KCHandler_MathExpression"
|
|
|
|
struct KCHandler_MathExpressionHelper
|
|
{
|
|
static bool CreateMap(UEdGraphNode& KeyNode, UEdGraphNode& ValueNode, EEdGraphPinDirection KeyPinDirection, const UEdGraphSchema_K2* Schema, TMap<UEdGraphPin*, UEdGraphPin*>& OutMap)
|
|
{
|
|
check(Schema);
|
|
for (UEdGraphPin* InnerInputPin : KeyNode.Pins)
|
|
{
|
|
if (!InnerInputPin || InnerInputPin->Direction != KeyPinDirection)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
UEdGraphPin* OuterInputPin = ValueNode.FindPin(InnerInputPin->PinName);
|
|
if (!OuterInputPin
|
|
|| !Schema->ArePinsCompatible(InnerInputPin, OuterInputPin, nullptr, false))
|
|
{
|
|
return false;
|
|
}
|
|
OutMap.Add(InnerInputPin, OuterInputPin);
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
bool FKCHandler_MathExpression::CanBeCalledByMathExpression(const UFunction* Function)
|
|
{
|
|
bool bGoodFunction = (nullptr != Function)
|
|
&& Function->HasAllFunctionFlags(FUNC_Static | FUNC_BlueprintPure | FUNC_Final | FUNC_Native)
|
|
&& !Function->HasAnyFunctionFlags(FUNC_BlueprintAuthorityOnly | FUNC_BlueprintCosmetic)
|
|
&& !Function->GetOuterUClass()->IsChildOf(UInterface::StaticClass());
|
|
if (!bGoodFunction)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (TFieldIterator<const FProperty> It(Function); It && (It->PropertyFlags & CPF_Parm); ++It)
|
|
{
|
|
const FProperty* Property = *It;
|
|
const bool bGoodProperty = Property && (!Property->HasAnyPropertyFlags(CPF_OutParm) || Property->HasAllPropertyFlags(CPF_ReturnParm));
|
|
if (!bGoodProperty)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
FBlueprintCompiledStatement* FKCHandler_MathExpression::GenerateFunctionRPN(UEdGraphNode* CurrentNode, FKismetFunctionContext& Context, UK2Node_MathExpression& MENode, FBPTerminal* ResultTerm, TMap<UEdGraphPin*, UEdGraphPin*>& InnerToOuterInput)
|
|
{
|
|
UK2Node_CallFunction* CallFunctionNode = Cast<UK2Node_CallFunction>(CurrentNode);
|
|
UFunction* Function = CallFunctionNode ? CallFunctionNode->GetTargetFunction() : nullptr;
|
|
if (!CanBeCalledByMathExpression(Function))
|
|
{
|
|
CompilerContext.MessageLog.Error(*FText::Format(LOCTEXT("WrongFunction_ErrorFmt", "Function '{0}' cannot be called inside Math Expression @@ - @@"), FText::FromString(GetNameSafe(Function))).ToString(), CallFunctionNode, &MENode);
|
|
return nullptr;
|
|
}
|
|
|
|
FBlueprintCompiledStatement* Statement = new FBlueprintCompiledStatement();
|
|
Statement->FunctionToCall = Function;
|
|
Statement->FunctionContext = nullptr;
|
|
Statement->Type = KCST_CallFunction;
|
|
Statement->LHS = ResultTerm; // required only for the first node
|
|
|
|
check(CallFunctionNode);
|
|
|
|
TArray<FBPTerminal*> RHSTerms;
|
|
for (TFieldIterator<FProperty> It(Function); It && (It->PropertyFlags & CPF_Parm); ++It)
|
|
{
|
|
FProperty* Property = *It;
|
|
if (Property && !Property->HasAnyPropertyFlags(CPF_ReturnParm | CPF_OutParm))
|
|
{
|
|
UEdGraphPin* PinMatch = CallFunctionNode->FindPin(Property->GetFName());
|
|
UEdGraphPin* PinToTry = nullptr;
|
|
{
|
|
const bool bGoodPin = PinMatch && FKismetCompilerUtilities::IsTypeCompatibleWithProperty(PinMatch, Property, CompilerContext.MessageLog, CompilerContext.GetSchema(), Context.NewClass);
|
|
PinToTry = bGoodPin ? FEdGraphUtilities::GetNetFromPin(PinMatch) : nullptr;
|
|
}
|
|
|
|
FBPTerminal* RHSTerm = nullptr;
|
|
{
|
|
UEdGraphPin** OuterInputPtr = PinToTry ? InnerToOuterInput.Find(PinToTry) : nullptr;
|
|
UEdGraphPin* OuterInputNet = (OuterInputPtr && *OuterInputPtr) ? FEdGraphUtilities::GetNetFromPin(*OuterInputPtr) : nullptr;
|
|
FBPTerminal** OuterTerm = OuterInputNet ? Context.NetMap.Find(OuterInputNet) : nullptr;
|
|
// Input is an outer term
|
|
if (OuterTerm && *OuterTerm)
|
|
{
|
|
RHSTerm = *OuterTerm;
|
|
}
|
|
}
|
|
|
|
if (!RHSTerm)
|
|
{
|
|
FBPTerminal** Term = PinToTry ? Context.NetMap.Find(PinToTry) : nullptr;
|
|
const bool bValidTerm = Term && *Term;
|
|
// Input is a literal term
|
|
// Input is a variable
|
|
if (bValidTerm && ((*Term)->bIsLiteral || (*Term)->AssociatedVarProperty))
|
|
{
|
|
RHSTerm = *Term;
|
|
}
|
|
// Input is an InlineGeneratedParameter
|
|
else if (bValidTerm)
|
|
{
|
|
ensure(!(*Term)->InlineGeneratedParameter);
|
|
UEdGraphNode* SourceNode = PinToTry ? PinToTry->GetOwningNodeUnchecked() : nullptr;
|
|
FBlueprintCompiledStatement* InlineGeneratedParameterStatement = GenerateFunctionRPN(SourceNode, Context, MENode, nullptr, InnerToOuterInput);
|
|
if (InlineGeneratedParameterStatement)
|
|
{
|
|
Context.AllGeneratedStatements.Add(InlineGeneratedParameterStatement);
|
|
RHSTerm = *Term;
|
|
RHSTerm->InlineGeneratedParameter = InlineGeneratedParameterStatement;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (RHSTerm)
|
|
{
|
|
const FImplicitCastParams* CastParams =
|
|
Context.ImplicitCastMap.Find(PinMatch);
|
|
if (CastParams)
|
|
{
|
|
using namespace UE::KismetCompiler;
|
|
|
|
check(CastParams->TargetTerminal);
|
|
|
|
UFunction* CastFunction = nullptr;
|
|
|
|
switch (CastParams->CastType)
|
|
{
|
|
case KCST_DoubleToFloatCast:
|
|
CastFunction =
|
|
UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, Conv_DoubleToFloat));
|
|
break;
|
|
|
|
case KCST_FloatToDoubleCast:
|
|
CastFunction =
|
|
UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, Conv_FloatToDouble));
|
|
break;
|
|
|
|
default:
|
|
checkf(false, TEXT("Unsupported cast type used in math expression node: %d"), CastParams->CastType);
|
|
}
|
|
|
|
check(CastFunction);
|
|
|
|
FBlueprintCompiledStatement* CastStatement = new FBlueprintCompiledStatement();
|
|
CastStatement->FunctionToCall = CastFunction;
|
|
CastStatement->Type = KCST_CallFunction;
|
|
CastStatement->RHS.Add(RHSTerm);
|
|
|
|
RHSTerm = CastParams->TargetTerminal;
|
|
CastParams->TargetTerminal->InlineGeneratedParameter = CastStatement;
|
|
Context.AllGeneratedStatements.Add(CastStatement);
|
|
|
|
CastingUtils::RemoveRegisteredImplicitCast(Context, PinMatch);
|
|
}
|
|
|
|
RHSTerms.Add(RHSTerm);
|
|
}
|
|
else
|
|
{
|
|
CompilerContext.MessageLog.Error(*FText::Format(LOCTEXT("FindPinParameter_ErrorFmt", "Could not find a pin for the parameter {0} of {1} on @@"), FText::FromString(GetNameSafe(Property)), FText::FromString(GetNameSafe(Function))).ToString(), CallFunctionNode);
|
|
}
|
|
}
|
|
}
|
|
Statement->RHS = RHSTerms;
|
|
return Statement;
|
|
}
|
|
|
|
void FKCHandler_MathExpression::RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* InNode)
|
|
{
|
|
using namespace UE::KismetCompiler;
|
|
|
|
FNodeHandlingFunctor::RegisterNets(Context, InNode);
|
|
|
|
UK2Node_MathExpression* Node_MathExpression = CastChecked<UK2Node_MathExpression>(InNode);
|
|
UK2Node_Tunnel* InnerEntryNode = Node_MathExpression->GetEntryNode();
|
|
UK2Node_Tunnel* InnerExitNode = Node_MathExpression->GetExitNode();
|
|
check(Node_MathExpression->BoundGraph && InnerEntryNode && InnerExitNode);
|
|
|
|
for (UEdGraphNode* InnerGraphNode : Node_MathExpression->BoundGraph->Nodes)
|
|
{
|
|
if (!InnerGraphNode || (InnerGraphNode == InnerEntryNode) || (InnerGraphNode == InnerExitNode))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (UK2Node_VariableGet* GetVarNode = Cast<UK2Node_VariableGet>(InnerGraphNode))
|
|
{
|
|
FNodeHandlingFunctor* VarHandler = GetVarNode->CreateNodeHandler(CompilerContext);
|
|
if (ensure(VarHandler))
|
|
{
|
|
VarHandler->RegisterNets(Context, GetVarNode);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
for (UEdGraphPin* Pin : InnerGraphNode->Pins)
|
|
{
|
|
// Register fake terms for InlineGeneratedValues
|
|
if (Pin && (Pin->Direction == EEdGraphPinDirection::EGPD_Output) && Pin->LinkedTo.Num())
|
|
{
|
|
UEdGraphPin* Linked = Pin->LinkedTo[0];
|
|
UEdGraphNode* LinkedOwnerNode = Linked ? Linked->GetOwningNodeUnchecked() : nullptr;
|
|
if (LinkedOwnerNode && (InnerExitNode != LinkedOwnerNode))
|
|
{
|
|
FBPTerminal* Term = new FBPTerminal();
|
|
Context.InlineGeneratedValues.Add(Term);
|
|
Term->CopyFromPin(Pin, Context.NetNameMap->MakeValidName(Pin));
|
|
Context.NetMap.Add(Pin, Term);
|
|
}
|
|
}
|
|
|
|
// Register Literals
|
|
if (Pin && (Pin->Direction == EEdGraphPinDirection::EGPD_Input) && !Pin->LinkedTo.Num())
|
|
{
|
|
RegisterLiteral(Context, Pin);
|
|
}
|
|
}
|
|
}
|
|
|
|
// We have two nodes that handle input: the math expression node and the inner entry node.
|
|
// For any pins connected to the entry node, we need to compare their types to the nets
|
|
// of the math expression node.
|
|
//
|
|
// This is a fairly convoluted scenario that RegisterImplicitCasts fails to detect,
|
|
// and it only affects pins connected to the inner entry node.
|
|
|
|
int EntryNodeCursor = 0;
|
|
for (UEdGraphPin* Pin : Node_MathExpression->Pins)
|
|
{
|
|
if (Pin && (Pin->Direction == EEdGraphPinDirection::EGPD_Input))
|
|
{
|
|
UEdGraphPin* PinNet = FEdGraphUtilities::GetNetFromPin(Pin);
|
|
if (PinNet)
|
|
{
|
|
UEdGraphPin* InnerEntryNodePin = InnerEntryNode->Pins[EntryNodeCursor];
|
|
if (InnerEntryNodePin && (InnerEntryNodePin->GetName() == Pin->GetName()))
|
|
{
|
|
for (UEdGraphPin* DestinationPin : InnerEntryNodePin->LinkedTo)
|
|
{
|
|
if (DestinationPin)
|
|
{
|
|
TOptional<CastingUtils::StatementNamePair> ConversionType =
|
|
CastingUtils::GetFloatingPointConversionType(*PinNet, *DestinationPin);
|
|
|
|
if (ConversionType)
|
|
{
|
|
EKismetCompiledStatementType CastType = ConversionType->Get<0>();
|
|
const TCHAR* TermName = ConversionType->Get<1>();
|
|
|
|
FBPTerminal* NewTerm = Context.CreateLocalTerminal();
|
|
UEdGraphNode* OwningNode = DestinationPin->GetOwningNode();
|
|
NewTerm->CopyFromPin(DestinationPin, Context.NetNameMap->MakeValidName(DestinationPin, TermName));
|
|
NewTerm->Source = OwningNode;
|
|
|
|
Context.ImplicitCastMap.Add(DestinationPin, FImplicitCastParams{ CastType, NewTerm, OwningNode });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Context.MessageLog.Error(*LOCTEXT("Compile_PinMismatchError", "ICE - mismatched pins found on @@ and @@!").ToString(), Node_MathExpression, InnerEntryNode);
|
|
}
|
|
}
|
|
|
|
++EntryNodeCursor;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FKCHandler_MathExpression::RegisterNet(FKismetFunctionContext& Context, UEdGraphPin* Net)
|
|
{
|
|
FBPTerminal* Term = Context.CreateLocalTerminalFromPinAutoChooseScope(Net, *Context.NetNameMap->MakeValidName(Net));
|
|
Context.NetMap.Add(Net, Term);
|
|
}
|
|
|
|
void FKCHandler_MathExpression::Compile(FKismetFunctionContext& Context, UEdGraphNode* Node)
|
|
{
|
|
UK2Node_MathExpression* Node_MathExpression = CastChecked<UK2Node_MathExpression>(Node);
|
|
check(Context.Schema);
|
|
|
|
UK2Node_Tunnel* InnerExitNode = Node_MathExpression->GetExitNode();
|
|
UK2Node_Tunnel* InnerEntryNode = Node_MathExpression->GetEntryNode();
|
|
|
|
if (!InnerExitNode || !InnerEntryNode || (InnerExitNode->Pins.Num() != 1) || ((InnerExitNode->Pins.Num() + InnerEntryNode->Pins.Num()) != Node->Pins.Num()))
|
|
{
|
|
Context.MessageLog.Error(*LOCTEXT("Compile_WrongInnerPinError", "ICE - wrong inner pins - @@").ToString(), Node);
|
|
return;
|
|
}
|
|
|
|
TMap<UEdGraphPin*, UEdGraphPin*> InnerToOuterInput;
|
|
bool bProperMap = KCHandler_MathExpressionHelper::CreateMap(*InnerEntryNode, *Node_MathExpression, EEdGraphPinDirection::EGPD_Output, Context.Schema, InnerToOuterInput);
|
|
|
|
TMap<UEdGraphPin*, UEdGraphPin*> InnerToOuterOutput;
|
|
bProperMap &= KCHandler_MathExpressionHelper::CreateMap(*InnerExitNode, *Node_MathExpression, EEdGraphPinDirection::EGPD_Input, Context.Schema, InnerToOuterOutput);
|
|
|
|
if (!bProperMap)
|
|
{
|
|
Context.MessageLog.Error(*LOCTEXT("Compile_WrongMap", "ICE - cannot map pins - @@").ToString(), Node);
|
|
return;
|
|
}
|
|
|
|
UEdGraphPin* InnerOutputPin = InnerExitNode->Pins[0];
|
|
UEdGraphPin** OuterOutputPinPtr = InnerToOuterOutput.Find(InnerOutputPin);
|
|
if (!InnerOutputPin || (InnerOutputPin->LinkedTo.Num() != 1) || !InnerOutputPin->LinkedTo[0] || !OuterOutputPinPtr || !*OuterOutputPinPtr)
|
|
{
|
|
Context.MessageLog.Error(*LOCTEXT("Compile_WrongOutputLink", "ICE - wrong output link - @@").ToString(), Node);
|
|
return;
|
|
}
|
|
|
|
FBPTerminal** OutputTermPtr = Context.NetMap.Find(FEdGraphUtilities::GetNetFromPin(*OuterOutputPinPtr));
|
|
FBPTerminal* OutputTerm = OutputTermPtr ? *OutputTermPtr : nullptr;
|
|
UEdGraphNode* LastInnerNode = InnerOutputPin->LinkedTo[0]->GetOwningNodeUnchecked();
|
|
|
|
FBlueprintCompiledStatement* DetachedStatement = GenerateFunctionRPN(LastInnerNode, Context, *Node_MathExpression, OutputTerm, InnerToOuterInput);
|
|
if (DetachedStatement)
|
|
{
|
|
using namespace UE::KismetCompiler;
|
|
|
|
Context.AllGeneratedStatements.Add(DetachedStatement);
|
|
TArray<FBlueprintCompiledStatement*>& StatementList = Context.StatementsPerNode.FindOrAdd(Node);
|
|
StatementList.Add(DetachedStatement);
|
|
|
|
// The casts that we care about occur *within* the nodes generated by the math expression.
|
|
// The expression node merely serves as a proxy, so we can silently remove the casts
|
|
// on the input pins that were registered by the compiler.
|
|
|
|
for (UEdGraphPin* Pin : Node_MathExpression->Pins)
|
|
{
|
|
check(Pin);
|
|
if (!Context.Schema->IsMetaPin(*Pin) && (Pin->Direction == EGPD_Input))
|
|
{
|
|
CastingUtils::RemoveRegisteredImplicitCast(Context, Pin);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Context.MessageLog.Error(*LOCTEXT("Compile_CannotGenerateFunction", "ICE - cannot generate function - @@").ToString(), Node);
|
|
return;
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|