2014-12-07 19:09:38 -05:00
|
|
|
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
#include "NiagaraEditorPrivatePCH.h"
|
2015-04-13 15:25:14 -04:00
|
|
|
#include "NiagaraScript.h"
|
|
|
|
|
#include "NiagaraComponent.h"
|
2014-03-14 14:13:41 -04:00
|
|
|
#include "CompilerResultsLog.h"
|
|
|
|
|
#include "EdGraphUtilities.h"
|
|
|
|
|
#include "VectorVM.h"
|
2014-06-18 07:25:31 -04:00
|
|
|
#include "ComponentReregisterContext.h"
|
2014-10-16 03:41:17 -04:00
|
|
|
#include "NiagaraCompiler_VectorVM.h"
|
|
|
|
|
#include "NiagaraEditorCommon.h"
|
|
|
|
|
|
|
|
|
|
#define LOCTEXT_NAMESPACE "NiagaraCompiler"
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogNiagaraCompiler,All,All);
|
|
|
|
|
|
|
|
|
|
void FNiagaraEditorModule::CompileScript(UNiagaraScript* ScriptToCompile)
|
|
|
|
|
{
|
|
|
|
|
check(ScriptToCompile != NULL);
|
|
|
|
|
if (ScriptToCompile->Source == NULL)
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogNiagaraCompiler, Error, TEXT("No source for Niagara script: %s"), *ScriptToCompile->GetPathName());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TComponentReregisterContext<UNiagaraComponent> ComponentReregisterContext;
|
|
|
|
|
|
2014-10-16 03:41:17 -04:00
|
|
|
FNiagaraCompiler_VectorVM Compiler;
|
|
|
|
|
Compiler.CompileScript(ScriptToCompile);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2014-10-16 03:41:17 -04:00
|
|
|
//Compile for compute here too?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
/** Expression that gets an input attribute. */
|
|
|
|
|
class FNiagaraExpression_GetAttribute : public FNiagaraExpression
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
|
2015-01-14 15:48:20 -05:00
|
|
|
FNiagaraExpression_GetAttribute(class FNiagaraCompiler* InCompiler, const FNiagaraVariableInfo& InAttribute)
|
|
|
|
|
: FNiagaraExpression(InCompiler, InAttribute)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
|
|
|
|
ResultLocation = ENiagaraExpressionResultLocation::InputData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void Process()override
|
|
|
|
|
{
|
|
|
|
|
check(ResultLocation == ENiagaraExpressionResultLocation::InputData);
|
2015-01-14 15:48:20 -05:00
|
|
|
ResultIndex = Compiler->GetAttributeIndex(Result);
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Expression that gets a constant. */
|
|
|
|
|
class FNiagaraExpression_GetConstant : public FNiagaraExpression
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
|
2015-01-14 15:48:20 -05:00
|
|
|
FNiagaraExpression_GetConstant(class FNiagaraCompiler* InCompiler, const FNiagaraVariableInfo& InConstant, bool bIsInternal)
|
|
|
|
|
: FNiagaraExpression(InCompiler, InConstant)
|
2014-10-16 03:41:17 -04:00
|
|
|
, bInternal(bIsInternal)
|
|
|
|
|
{
|
|
|
|
|
ResultLocation = ENiagaraExpressionResultLocation::Constants;
|
|
|
|
|
//For matrix constants we must add 4 input expressions that will be filled in as the constant is processed.
|
|
|
|
|
//They must at least exist now so that other expressions can reference them.
|
2015-01-14 15:48:20 -05:00
|
|
|
if (Result.Type == ENiagaraDataType::Matrix)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
2015-01-14 15:48:20 -05:00
|
|
|
static const FName ResultName(TEXT("MatrixComponent"));
|
|
|
|
|
SourceExpressions.Add(MakeShareable(new FNiagaraExpression(InCompiler, FNiagaraVariableInfo(ResultName, ENiagaraDataType::Vector))));
|
|
|
|
|
SourceExpressions.Add(MakeShareable(new FNiagaraExpression(InCompiler, FNiagaraVariableInfo(ResultName, ENiagaraDataType::Vector))));
|
|
|
|
|
SourceExpressions.Add(MakeShareable(new FNiagaraExpression(InCompiler, FNiagaraVariableInfo(ResultName, ENiagaraDataType::Vector))));
|
|
|
|
|
SourceExpressions.Add(MakeShareable(new FNiagaraExpression(InCompiler, FNiagaraVariableInfo(ResultName, ENiagaraDataType::Vector))));
|
2015-01-08 11:49:58 -05:00
|
|
|
}
|
2015-01-14 15:48:20 -05:00
|
|
|
// else if (Result.Type == ENiagaraDataType::Curve)
|
|
|
|
|
// {
|
|
|
|
|
// static const FName ResultName(TEXT("Curve"));
|
|
|
|
|
// SourceExpressions.Add(MakeShareable(new FNiagaraExpression(InCompiler, FNiagaraVariableInfo(ResultName, ENiagaraDataType::Curve))));
|
|
|
|
|
// }
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void Process()override
|
|
|
|
|
{
|
|
|
|
|
check(ResultLocation == ENiagaraExpressionResultLocation::Constants);
|
|
|
|
|
//Get the index of the constant. Also gets a component index if this is for a packed scalar constant.
|
2015-01-14 15:48:20 -05:00
|
|
|
Compiler->GetConstantResultIndex(Result, bInternal, ResultIndex, ComponentIndex);
|
2014-10-16 03:41:17 -04:00
|
|
|
|
2015-01-14 15:48:20 -05:00
|
|
|
if (Result.Type == ENiagaraDataType::Matrix)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
|
|
|
|
check(SourceExpressions.Num() == 4);
|
|
|
|
|
TNiagaraExprPtr Src0 = GetSourceExpression(0);
|
|
|
|
|
TNiagaraExprPtr Src1 = GetSourceExpression(1);
|
|
|
|
|
TNiagaraExprPtr Src2 = GetSourceExpression(2);
|
|
|
|
|
TNiagaraExprPtr Src3 = GetSourceExpression(3);
|
|
|
|
|
Src0->ResultLocation = ENiagaraExpressionResultLocation::Constants;
|
|
|
|
|
Src0->ResultIndex = ResultIndex;
|
|
|
|
|
Src1->ResultLocation = ENiagaraExpressionResultLocation::Constants;
|
|
|
|
|
Src1->ResultIndex = ResultIndex + 1;
|
|
|
|
|
Src2->ResultLocation = ENiagaraExpressionResultLocation::Constants;
|
|
|
|
|
Src2->ResultIndex = ResultIndex + 2;
|
|
|
|
|
Src3->ResultLocation = ENiagaraExpressionResultLocation::Constants;
|
|
|
|
|
Src3->ResultIndex = ResultIndex + 3;
|
|
|
|
|
}
|
2015-01-14 15:48:20 -05:00
|
|
|
else if (Result.Type == ENiagaraDataType::Curve)
|
2015-01-08 11:49:58 -05:00
|
|
|
{
|
|
|
|
|
ResultLocation = ENiagaraExpressionResultLocation::BufferConstants;
|
|
|
|
|
}
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bInternal;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Expression that just collects some other expressions together. E.g for a matrix. */
|
|
|
|
|
class FNiagaraExpression_Collection : public FNiagaraExpression
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
|
2015-01-14 15:48:20 -05:00
|
|
|
FNiagaraExpression_Collection(class FNiagaraCompiler* InCompiler, const FNiagaraVariableInfo& Result, TArray<TNiagaraExprPtr>& InSourceExpressions)
|
|
|
|
|
: FNiagaraExpression(InCompiler, Result)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
|
|
|
|
SourceExpressions = InSourceExpressions;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2015-05-15 07:50:13 -04:00
|
|
|
bool FNiagaraCompiler::CheckInputs(FName OpName, TArray<TNiagaraExprPtr>& Inputs)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
|
|
|
|
//check the types of the input expressions.
|
|
|
|
|
const FNiagaraOpInfo* OpInfo = FNiagaraOpInfo::GetOpInfo(OpName);
|
|
|
|
|
check(OpInfo);
|
|
|
|
|
int32 NumInputs = Inputs.Num();
|
|
|
|
|
check(OpInfo->Inputs.Num() == NumInputs);
|
2015-05-15 07:50:13 -04:00
|
|
|
bool bError = false;
|
2014-10-16 03:41:17 -04:00
|
|
|
for (int32 i = 0; i < NumInputs ; ++i)
|
|
|
|
|
{
|
2015-05-15 07:50:13 -04:00
|
|
|
check(Inputs[i].IsValid());
|
2015-01-14 15:48:20 -05:00
|
|
|
if (OpInfo->Inputs[i].DataType != Inputs[i]->Result.Type)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
2015-05-15 07:50:13 -04:00
|
|
|
bError = true;
|
|
|
|
|
FText ErrorText = FText::Format(LOCTEXT("Expression {0} has incorrect inputs!\nExpected: {1} - Actual: {2}", ""),
|
|
|
|
|
FText::FromString(OpName.ToString()),
|
|
|
|
|
FText::AsNumber((int32)OpInfo->Inputs[i].DataType),
|
|
|
|
|
FText::AsNumber((int32)((TNiagaraExprPtr)Inputs[i])->Result.Type.GetValue()));
|
|
|
|
|
MessageLog.Error(*ErrorText.ToString());
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
|
|
|
|
}
|
2015-05-15 07:50:13 -04:00
|
|
|
return bError;
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
|
|
|
|
|
2015-05-15 07:50:13 -04:00
|
|
|
bool FNiagaraCompiler::CheckOutputs(FName OpName, TArray<TNiagaraExprPtr>& Outputs)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
|
|
|
|
//check the types of the input expressions.
|
|
|
|
|
const FNiagaraOpInfo* OpInfo = FNiagaraOpInfo::GetOpInfo(OpName);
|
|
|
|
|
check(OpInfo);
|
|
|
|
|
int32 NumOutputs = Outputs.Num();
|
2015-05-15 07:50:13 -04:00
|
|
|
bool bError = false;
|
2014-10-16 03:41:17 -04:00
|
|
|
check(OpInfo->Outputs.Num() == NumOutputs);
|
|
|
|
|
for (int32 i = 0; i < NumOutputs; ++i)
|
|
|
|
|
{
|
2015-05-15 07:50:13 -04:00
|
|
|
check(Outputs[i].IsValid());
|
2015-01-14 15:48:20 -05:00
|
|
|
if (OpInfo->Outputs[i].DataType != Outputs[i]->Result.Type)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
2015-05-15 07:50:13 -04:00
|
|
|
bError = true;
|
|
|
|
|
FText ErrorText = FText::Format(LOCTEXT("Expression {0} has incorrect inputs!\nExpected: {1} - Actual: {2}", ""),
|
|
|
|
|
FText::FromString(OpName.ToString()),
|
|
|
|
|
FText::AsNumber((int32)OpInfo->Outputs[i].DataType),
|
|
|
|
|
FText::AsNumber((int32)((TNiagaraExprPtr)Outputs[i])->Result.Type.GetValue()));
|
|
|
|
|
MessageLog.Error(*ErrorText.ToString());
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
|
|
|
|
}
|
2015-05-15 07:50:13 -04:00
|
|
|
return bError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FNiagaraCompiler::Error(FText ErrorText, UNiagaraNode* Node, UEdGraphPin* Pin)
|
|
|
|
|
{
|
|
|
|
|
FString ErrorString = FString::Printf(TEXT("Node: @@ - Pin: @@ - %s"), *ErrorText.ToString());
|
|
|
|
|
MessageLog.Error(*ErrorString, Node, Pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FNiagaraCompiler::Warning(FText WarningText, UNiagaraNode* Node, UEdGraphPin* Pin)
|
|
|
|
|
{
|
|
|
|
|
FString WarnString = FString::Printf(TEXT("Node: @@ - Pin: @@ - %s"), *WarningText.ToString());
|
|
|
|
|
MessageLog.Warning(*WarnString, Node, Pin);
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
|
|
|
|
|
2015-01-14 15:48:20 -05:00
|
|
|
int32 FNiagaraCompiler::GetAttributeIndex(const FNiagaraVariableInfo& Attr)const
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
2015-01-14 15:48:20 -05:00
|
|
|
return SourceGraph->GetAttributeIndex(Attr);
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TNiagaraExprPtr FNiagaraCompiler::CompileOutputPin(UEdGraphPin* Pin)
|
|
|
|
|
{
|
|
|
|
|
check(Pin->Direction == EGPD_Output);
|
|
|
|
|
|
|
|
|
|
TNiagaraExprPtr Ret;
|
|
|
|
|
|
|
|
|
|
TNiagaraExprPtr* Expr = PinToExpression.Find(Pin);
|
|
|
|
|
if (Expr)
|
|
|
|
|
{
|
|
|
|
|
Ret = *Expr; //We've compiled this pin before. Return it's expression.
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//Otherwise we need to compile the node to get its output pins.
|
|
|
|
|
UNiagaraNode* Node = Cast<UNiagaraNode>(Pin->GetOwningNode());
|
|
|
|
|
TArray<FNiagaraNodeResult> Outputs;
|
|
|
|
|
Node->Compile(this, Outputs);
|
|
|
|
|
for (int32 i=0; i<Outputs.Num(); ++i)
|
|
|
|
|
{
|
|
|
|
|
PinToExpression.Add(Outputs[i].OutputPin, Outputs[i].Expression);
|
|
|
|
|
//We also grab the expression for the pin we're currently interested in. Otherwise we'd have to search the map for it.
|
|
|
|
|
Ret = Outputs[i].OutputPin == Pin ? Outputs[i].Expression : Ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TNiagaraExprPtr FNiagaraCompiler::CompilePin(UEdGraphPin* Pin)
|
|
|
|
|
{
|
|
|
|
|
check(Pin);
|
|
|
|
|
TNiagaraExprPtr Ret;
|
|
|
|
|
if (Pin->Direction == EGPD_Input)
|
|
|
|
|
{
|
|
|
|
|
if (Pin->LinkedTo.Num() > 0)
|
|
|
|
|
{
|
|
|
|
|
// > 1 ???
|
|
|
|
|
Ret = CompileOutputPin(Pin->LinkedTo[0]);
|
|
|
|
|
}
|
|
|
|
|
else if (!Pin->bDefaultValueIsIgnored)
|
|
|
|
|
{
|
|
|
|
|
//No connections to this input so add the default as a const expression.
|
|
|
|
|
const UEdGraphSchema_Niagara* Schema = Cast<const UEdGraphSchema_Niagara>(Pin->GetSchema());
|
2015-01-14 15:48:20 -05:00
|
|
|
ENiagaraDataType PinType = Schema->GetPinType(Pin);
|
2014-10-16 03:41:17 -04:00
|
|
|
|
|
|
|
|
FString ConstString = Pin->GetDefaultAsString();
|
|
|
|
|
FName ConstName(*ConstString);
|
2015-01-14 15:48:20 -05:00
|
|
|
|
|
|
|
|
FNiagaraVariableInfo Var(ConstName, PinType);
|
|
|
|
|
|
|
|
|
|
switch (PinType)
|
|
|
|
|
{
|
|
|
|
|
case ENiagaraDataType::Scalar:
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
|
|
|
|
float Default;
|
|
|
|
|
Schema->GetPinDefaultValue(Pin, Default);
|
2015-01-14 15:48:20 -05:00
|
|
|
ConstantData.SetOrAddInternal(Var, Default);
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
2015-01-14 15:48:20 -05:00
|
|
|
break;
|
|
|
|
|
case ENiagaraDataType::Vector:
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
|
|
|
|
FVector4 Default;
|
|
|
|
|
Schema->GetPinDefaultValue(Pin, Default);
|
2015-01-14 15:48:20 -05:00
|
|
|
ConstantData.SetOrAddInternal(Var, Default);
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
2015-01-14 15:48:20 -05:00
|
|
|
break;
|
|
|
|
|
case ENiagaraDataType::Matrix:
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
|
|
|
|
FMatrix Default;
|
|
|
|
|
Schema->GetPinDefaultValue(Pin, Default);
|
2015-01-14 15:48:20 -05:00
|
|
|
ConstantData.SetOrAddInternal(Var, Default);
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
2015-01-14 15:48:20 -05:00
|
|
|
break;
|
|
|
|
|
case ENiagaraDataType::Curve:
|
2015-01-08 11:49:58 -05:00
|
|
|
{
|
2015-06-15 15:51:40 -04:00
|
|
|
UNiagaraDataObject *Default = nullptr;
|
2015-01-14 15:48:20 -05:00
|
|
|
ConstantData.SetOrAddInternal(Var, Default);
|
2015-01-08 11:49:58 -05:00
|
|
|
}
|
2015-01-14 15:48:20 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
Ret = Expression_GetInternalConstant(Var);
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Ret = CompileOutputPin(Pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Ret;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-14 15:48:20 -05:00
|
|
|
void FNiagaraCompiler::GetParticleAttributes(TArray<FNiagaraVariableInfo>& OutAttributes)const
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
2015-01-14 15:48:20 -05:00
|
|
|
check(SourceGraph);
|
|
|
|
|
SourceGraph->GetAttributes(OutAttributes);
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
|
|
|
|
|
2015-01-14 15:48:20 -05:00
|
|
|
TNiagaraExprPtr FNiagaraCompiler::Expression_GetAttribute(const FNiagaraVariableInfo& Attribute)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
2015-01-14 15:48:20 -05:00
|
|
|
int32 Index = Expressions.Add(MakeShareable(new FNiagaraExpression_GetAttribute(this, Attribute)));
|
2014-10-16 03:41:17 -04:00
|
|
|
return Expressions[Index];
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-14 15:48:20 -05:00
|
|
|
TNiagaraExprPtr FNiagaraCompiler::Expression_GetExternalConstant(const FNiagaraVariableInfo& Constant)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
2015-01-14 15:48:20 -05:00
|
|
|
int32 Index = Expressions.Add(MakeShareable(new FNiagaraExpression_GetConstant(this, Constant, false)));
|
2014-10-16 03:41:17 -04:00
|
|
|
return Expressions[Index];
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-14 15:48:20 -05:00
|
|
|
TNiagaraExprPtr FNiagaraCompiler::Expression_GetInternalConstant(const FNiagaraVariableInfo& Constant)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
2015-01-14 15:48:20 -05:00
|
|
|
int32 Index = Expressions.Add(MakeShareable(new FNiagaraExpression_GetConstant(this, Constant, true)));
|
2014-10-16 03:41:17 -04:00
|
|
|
return Expressions[Index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TNiagaraExprPtr FNiagaraCompiler::Expression_Collection(TArray<TNiagaraExprPtr>& SourceExpressions)
|
|
|
|
|
{
|
2015-01-14 15:48:20 -05:00
|
|
|
//Todo - Collection expressions are just to collect parts of a matrix currently. Its a crappy way to do things that should be replaced.
|
|
|
|
|
//Definitely don't start using it for collections of other things.
|
|
|
|
|
int32 Index = Expressions.Add(MakeShareable(new FNiagaraExpression_Collection(this, FNiagaraVariableInfo(NAME_None, ENiagaraDataType::Vector), SourceExpressions)));
|
2014-10-16 03:41:17 -04:00
|
|
|
return Expressions[Index];
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-14 15:48:20 -05:00
|
|
|
TNiagaraExprPtr FNiagaraCompiler::GetAttribute(const FNiagaraVariableInfo& Attribute)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
2015-01-14 15:48:20 -05:00
|
|
|
return Expression_GetAttribute(Attribute);
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
|
|
|
|
|
2015-01-14 15:48:20 -05:00
|
|
|
TNiagaraExprPtr FNiagaraCompiler::GetExternalConstant(const FNiagaraVariableInfo& Constant, float Default)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
2015-01-14 15:48:20 -05:00
|
|
|
SetOrAddConstant(false, Constant, Default);
|
|
|
|
|
return Expression_GetExternalConstant(Constant);
|
|
|
|
|
}
|
|
|
|
|
TNiagaraExprPtr FNiagaraCompiler::GetExternalConstant(const FNiagaraVariableInfo& Constant, const FVector4& Default)
|
|
|
|
|
{
|
|
|
|
|
SetOrAddConstant(false, Constant, Default);
|
|
|
|
|
return Expression_GetExternalConstant(Constant);
|
|
|
|
|
}
|
|
|
|
|
TNiagaraExprPtr FNiagaraCompiler::GetExternalConstant(const FNiagaraVariableInfo& Constant, const FMatrix& Default)
|
|
|
|
|
{
|
|
|
|
|
SetOrAddConstant(false, Constant, Default);
|
|
|
|
|
return Expression_GetExternalConstant(Constant);
|
2014-10-16 03:41:17 -04:00
|
|
|
}
|
2015-06-17 10:12:41 -04:00
|
|
|
TNiagaraExprPtr FNiagaraCompiler::GetExternalConstant(const FNiagaraVariableInfo& Constant, const UNiagaraDataObject* Default)
|
|
|
|
|
{
|
|
|
|
|
UNiagaraDataObject* DefaultDupe = Default ? CastChecked<UNiagaraDataObject>(StaticDuplicateObject(Default, Script, NULL, ~RF_Transient)) : nullptr;
|
|
|
|
|
SetOrAddConstant(false, Constant, DefaultDupe);
|
|
|
|
|
return Expression_GetExternalConstant(Constant);
|
|
|
|
|
}
|
2015-04-09 11:27:52 -04:00
|
|
|
TNiagaraExprPtr FNiagaraCompiler::GetInternalConstant(const FNiagaraVariableInfo& Constant, float Default)
|
|
|
|
|
{
|
|
|
|
|
SetOrAddConstant(true, Constant, Default);
|
|
|
|
|
return Expression_GetInternalConstant(Constant);
|
|
|
|
|
}
|
|
|
|
|
TNiagaraExprPtr FNiagaraCompiler::GetInternalConstant(const FNiagaraVariableInfo& Constant, const FVector4& Default)
|
|
|
|
|
{
|
|
|
|
|
SetOrAddConstant(true, Constant, Default);
|
|
|
|
|
return Expression_GetInternalConstant(Constant);
|
|
|
|
|
}
|
|
|
|
|
TNiagaraExprPtr FNiagaraCompiler::GetInternalConstant(const FNiagaraVariableInfo& Constant, const FMatrix& Default)
|
|
|
|
|
{
|
|
|
|
|
SetOrAddConstant(true, Constant, Default);
|
|
|
|
|
return Expression_GetInternalConstant(Constant);
|
|
|
|
|
}
|
2015-06-17 10:12:41 -04:00
|
|
|
TNiagaraExprPtr FNiagaraCompiler::GetInternalConstant(const FNiagaraVariableInfo& Constant, const UNiagaraDataObject* Default)
|
2014-10-16 03:41:17 -04:00
|
|
|
{
|
2015-06-17 10:12:41 -04:00
|
|
|
UNiagaraDataObject* DefaultDupe = Default ? CastChecked<UNiagaraDataObject>(StaticDuplicateObject(Default, Script, NULL, ~RF_Transient)) : nullptr;
|
|
|
|
|
SetOrAddConstant(true, Constant, DefaultDupe);
|
|
|
|
|
return Expression_GetInternalConstant(Constant);
|
2015-01-08 11:49:58 -05:00
|
|
|
}
|
|
|
|
|
|
2015-04-09 11:27:52 -04:00
|
|
|
// TODO: Refactor this (and some other stuff) into a preprocessing step for use by any compiler?
|
|
|
|
|
bool FNiagaraCompiler::MergeInFunctionNodes()
|
|
|
|
|
{
|
|
|
|
|
struct FReconnectionInfo
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
UEdGraphPin* From;
|
|
|
|
|
TArray<UEdGraphPin*> To;
|
|
|
|
|
|
|
|
|
|
//Fallback default value if an input connection is not connected.
|
|
|
|
|
FString FallbackDefault;
|
|
|
|
|
|
|
|
|
|
FReconnectionInfo()
|
|
|
|
|
: From(NULL)
|
|
|
|
|
{}
|
|
|
|
|
};
|
|
|
|
|
TMap<FName, FReconnectionInfo> InputConnections;
|
|
|
|
|
TMap<FName, FReconnectionInfo> OutputConnections;
|
|
|
|
|
|
|
|
|
|
TArray<class UEdGraphPin*> FuncCallInputPins;
|
|
|
|
|
TArray<class UEdGraphPin*> FuncCallOutputPins;
|
|
|
|
|
|
|
|
|
|
//Copies the function graph into the main graph.
|
|
|
|
|
//Removes the Function call in the main graph and the input and output nodes in the function graph, reconnecting their pins appropriately.
|
|
|
|
|
auto MergeFunctionIntoMainGraph = [&](UNiagaraNodeFunctionCall* InFunc, UNiagaraGraph* FuncGraph)
|
|
|
|
|
{
|
|
|
|
|
InputConnections.Empty();
|
|
|
|
|
OutputConnections.Empty();
|
|
|
|
|
FuncCallInputPins.Empty();
|
|
|
|
|
FuncCallOutputPins.Empty();
|
|
|
|
|
|
|
|
|
|
check(InFunc && FuncGraph);
|
|
|
|
|
if (InFunc->FunctionScript)
|
|
|
|
|
{
|
|
|
|
|
//Get all the pins that are connected to the inputs of the function call node in the main graph.
|
|
|
|
|
InFunc->GetInputPins(FuncCallInputPins);
|
|
|
|
|
for (UEdGraphPin* FuncCallInputPin : FuncCallInputPins)
|
|
|
|
|
{
|
|
|
|
|
FName InputName(*FuncCallInputPin->PinName);
|
|
|
|
|
FReconnectionInfo& InputConnection = InputConnections.FindOrAdd(InputName);
|
|
|
|
|
if (FuncCallInputPin->LinkedTo.Num() > 0)
|
|
|
|
|
{
|
|
|
|
|
check(FuncCallInputPin->LinkedTo.Num() == 1);
|
|
|
|
|
UEdGraphPin* LinkFrom = FuncCallInputPin->LinkedTo[0];
|
|
|
|
|
check(LinkFrom->Direction == EGPD_Output);
|
|
|
|
|
InputConnection.From = LinkFrom;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//This input has no link so we need the default value from the pin.
|
|
|
|
|
InputConnection.FallbackDefault = FuncCallInputPin->GetDefaultAsString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//Get all the pins that are connected to the outputs of the function call node in the main graph.
|
|
|
|
|
InFunc->GetOutputPins(FuncCallOutputPins);
|
|
|
|
|
for (UEdGraphPin* FuncCallOutputPin : FuncCallOutputPins)
|
|
|
|
|
{
|
|
|
|
|
FName OutputName(*FuncCallOutputPin->PinName);
|
|
|
|
|
for (UEdGraphPin* LinkTo : FuncCallOutputPin->LinkedTo)
|
|
|
|
|
{
|
2015-06-17 10:12:41 -04:00
|
|
|
if (LinkTo)
|
|
|
|
|
{
|
|
|
|
|
check(LinkTo->Direction == EGPD_Input);
|
|
|
|
|
FReconnectionInfo& OutputConnection = OutputConnections.FindOrAdd(OutputName);
|
|
|
|
|
OutputConnection.To.Add(LinkTo);
|
|
|
|
|
}
|
2015-04-09 11:27:52 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Remove the function call node from the graph now that we have everything we need from it.
|
|
|
|
|
SourceGraph->RemoveNode(InFunc);
|
|
|
|
|
|
|
|
|
|
//Keep a list of the Input and Output nodes we see in the function graph so that we can remove (most of) them later.
|
|
|
|
|
TArray<UEdGraphNode*, TInlineAllocator<64>> ToRemove;
|
|
|
|
|
|
|
|
|
|
//Search the nodes in the function graph, finding any connections to input or output nodes.
|
|
|
|
|
for (UEdGraphNode* FuncGraphNode : FuncGraph->Nodes)
|
|
|
|
|
{
|
|
|
|
|
if (UNiagaraNodeInput* InputNode = Cast<UNiagaraNodeInput>(FuncGraphNode))
|
|
|
|
|
{
|
|
|
|
|
check(InputNode->Pins.Num() == 1);
|
|
|
|
|
//Get an array of "To" pins from one or more input nodes referencing each named input.
|
|
|
|
|
FReconnectionInfo& InputConnection = InputConnections.FindOrAdd(InputNode->Input.Name);
|
|
|
|
|
if (InputConnection.From)
|
|
|
|
|
{
|
|
|
|
|
//We have a connection from the function call so remove the input node and connect to that.
|
|
|
|
|
ToRemove.Add(InputNode);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//This input has no connection from the function call so what do we do here?
|
|
|
|
|
//For now we just leave the input node and connect back to it.
|
|
|
|
|
//This will mean unconnected pins from the function call will look for constants or attributes.
|
|
|
|
|
//In some cases we may want to just take the default value from the function call pin instead?
|
|
|
|
|
//Maybe have some properties on the function call defining that.
|
|
|
|
|
InputConnection.From = InputNode->Pins[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TArray<UEdGraphPin*>& LinkToPins = InputNode->Pins[0]->LinkedTo;
|
|
|
|
|
for (UEdGraphPin* ToPin : LinkToPins)
|
|
|
|
|
{
|
|
|
|
|
check(ToPin->Direction == EGPD_Input);
|
|
|
|
|
InputConnection.To.Add(ToPin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (UNiagaraNodeOutput* OutputNode = Cast<UNiagaraNodeOutput>(FuncGraphNode))
|
|
|
|
|
{
|
|
|
|
|
//Unlike the input nodes, we don't have the option of keeping these if there is no "From" pin. The default values from the node pins should be used.
|
|
|
|
|
ToRemove.Add(OutputNode);
|
|
|
|
|
|
|
|
|
|
//For each output, get the "From" pin to be reconnected later.
|
|
|
|
|
for (int32 OutputIdx = 0; OutputIdx < OutputNode->Outputs.Num(); ++OutputIdx)
|
|
|
|
|
{
|
|
|
|
|
FName OutputName = OutputNode->Outputs[OutputIdx].Name;
|
|
|
|
|
|
|
|
|
|
UEdGraphPin* OutputNodePin = OutputNode->Pins[OutputIdx];
|
|
|
|
|
check(OutputNodePin->LinkedTo.Num() <= 1);
|
|
|
|
|
FReconnectionInfo& OutputConnection = OutputConnections.FindOrAdd(OutputName);
|
|
|
|
|
UEdGraphPin* LinkFromPin = OutputNodePin->LinkedTo.Num() == 1 ? OutputNodePin->LinkedTo[0] : NULL;
|
|
|
|
|
if (LinkFromPin)
|
|
|
|
|
{
|
|
|
|
|
check(LinkFromPin->Direction == EGPD_Output);
|
|
|
|
|
OutputConnection.From = LinkFromPin;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//This output is not connected so links to it in the main graph must use it's default value.
|
|
|
|
|
OutputConnection.FallbackDefault = OutputNodePin->GetDefaultAsString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Remove all the In and Out nodes from the function graph.
|
|
|
|
|
for (UEdGraphNode* Remove : ToRemove)
|
|
|
|
|
{
|
|
|
|
|
FuncGraph->RemoveNode(Remove);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Copy the nodes from the function graph over into the main graph.
|
|
|
|
|
FuncGraph->MoveNodesToAnotherGraph(SourceGraph, false);
|
|
|
|
|
|
|
|
|
|
//Finally, do all the reconnection.
|
|
|
|
|
auto MakeConnection = [&](FReconnectionInfo& Info)
|
|
|
|
|
{
|
|
|
|
|
for (UEdGraphPin* LinkTo : Info.To)
|
|
|
|
|
{
|
|
|
|
|
if (Info.From)
|
|
|
|
|
{
|
|
|
|
|
Info.From->MakeLinkTo(LinkTo);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LinkTo->DefaultValue = Info.FallbackDefault;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
for (TPair<FName, FReconnectionInfo>& ReconnectInfo : InputConnections){ MakeConnection(ReconnectInfo.Value); }
|
|
|
|
|
for (TPair<FName, FReconnectionInfo>& ReconnectInfo : OutputConnections){ MakeConnection(ReconnectInfo.Value); }
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//Helper struct for traversing nested function calls.
|
|
|
|
|
struct FFunctionContext
|
|
|
|
|
{
|
|
|
|
|
//True if this context's function has been merged into the main graph.
|
|
|
|
|
bool bProcessed;
|
|
|
|
|
//The index of this context into the ContextPool.
|
|
|
|
|
int32 PoolIdx;
|
|
|
|
|
//Pointer back to the parent context for traversal.
|
|
|
|
|
FFunctionContext* Parent;
|
|
|
|
|
//The function call node for this function in the source/parent graph.
|
|
|
|
|
UNiagaraNodeFunctionCall* Function;
|
|
|
|
|
//The graph for this function that we are going to merge into the main graph.
|
|
|
|
|
UNiagaraGraph* FunctionGraph;
|
|
|
|
|
//The script from which the graph is copied. Used for re entrance check.
|
|
|
|
|
UNiagaraScript* Script;
|
|
|
|
|
|
|
|
|
|
//Contexts for function calls in this function graph.
|
|
|
|
|
TArray<FFunctionContext*, TInlineAllocator<64>> SubFunctionCalls;
|
|
|
|
|
|
|
|
|
|
FFunctionContext()
|
|
|
|
|
: bProcessed(false)
|
|
|
|
|
, PoolIdx(INDEX_NONE)
|
|
|
|
|
, Parent(NULL)
|
|
|
|
|
, Function(NULL)
|
|
|
|
|
, FunctionGraph(NULL)
|
|
|
|
|
, Script(NULL)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
We don't allow re-entrant functions as this would cause an infinite loop of merging in graphs.
|
|
|
|
|
Maybe in the future if we allow branching in the VM we can allow this.
|
|
|
|
|
*/
|
|
|
|
|
bool CheckForReentrance()const
|
|
|
|
|
{
|
|
|
|
|
UNiagaraNodeFunctionCall* Func = Function;
|
|
|
|
|
FFunctionContext* Curr = Parent;
|
|
|
|
|
while (Curr)
|
|
|
|
|
{
|
|
|
|
|
if (Curr->Script == Script)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
Curr = Curr->Parent;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FString GetCallstack()const
|
|
|
|
|
{
|
|
|
|
|
FString Ret;
|
|
|
|
|
const FFunctionContext* Curr = this;
|
|
|
|
|
while (Curr)
|
|
|
|
|
{
|
|
|
|
|
if (Curr->Script)
|
|
|
|
|
{
|
|
|
|
|
Ret.Append(*(Curr->Script->GetPathName()));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Ret.Append(TEXT("Unknown"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ret.Append(TEXT("\n"));
|
|
|
|
|
|
|
|
|
|
Curr = Curr->Parent;
|
|
|
|
|
}
|
|
|
|
|
return Ret;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//A pool of contexts on the stack to avoid loads of needless, small heap allocations.
|
|
|
|
|
TArray<FFunctionContext, TInlineAllocator<512>> ContextPool;
|
|
|
|
|
ContextPool.Reserve(512);
|
|
|
|
|
|
|
|
|
|
FFunctionContext RootContext;
|
|
|
|
|
FFunctionContext* CurrentContext = &RootContext;
|
|
|
|
|
CurrentContext->FunctionGraph = SourceGraph;
|
|
|
|
|
CurrentContext->Script = Script;
|
|
|
|
|
|
|
|
|
|
//Depth first traversal of all function calls.
|
|
|
|
|
while (CurrentContext)
|
|
|
|
|
{
|
|
|
|
|
//Find any sub functions and process this function call.
|
|
|
|
|
if (!CurrentContext->bProcessed)
|
|
|
|
|
{
|
|
|
|
|
CurrentContext->bProcessed = true;
|
|
|
|
|
|
|
|
|
|
//Find any sub functions and check for re-entrance.
|
|
|
|
|
if (CurrentContext->FunctionGraph)
|
|
|
|
|
{
|
|
|
|
|
for (UEdGraphNode* Node : CurrentContext->FunctionGraph->Nodes)
|
|
|
|
|
{
|
|
|
|
|
UNiagaraNodeFunctionCall* FuncNode = Cast<UNiagaraNodeFunctionCall>(Node);
|
|
|
|
|
if (FuncNode)
|
|
|
|
|
{
|
|
|
|
|
int32 NewIdx = ContextPool.AddZeroed();
|
|
|
|
|
FFunctionContext* SubFuncContext = &ContextPool[NewIdx];
|
|
|
|
|
CurrentContext->SubFunctionCalls.Push(SubFuncContext);
|
|
|
|
|
SubFuncContext->Parent = CurrentContext;
|
|
|
|
|
SubFuncContext->Function = FuncNode;
|
|
|
|
|
SubFuncContext->PoolIdx = NewIdx;
|
|
|
|
|
SubFuncContext->Script = FuncNode->FunctionScript;
|
|
|
|
|
|
|
|
|
|
if (SubFuncContext->CheckForReentrance())
|
|
|
|
|
{
|
|
|
|
|
FString Callstack = SubFuncContext->GetCallstack();
|
|
|
|
|
MessageLog.Error(TEXT("Reentrant function call!\n%s"), *Callstack);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Copy the function graph as we'll be modifying it as we merge in with the main graph.
|
|
|
|
|
UNiagaraScriptSource* FuncSource = CastChecked<UNiagaraScriptSource>(FuncNode->FunctionScript->Source);
|
|
|
|
|
check(FuncSource);
|
|
|
|
|
SubFuncContext->FunctionGraph = CastChecked<UNiagaraGraph>(FEdGraphUtilities::CloneGraph(FuncSource->NodeGraph, NULL, &MessageLog));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Merge this function into the main graph now.
|
|
|
|
|
if (CurrentContext->Function && CurrentContext->FunctionGraph)
|
|
|
|
|
{
|
|
|
|
|
MergeFunctionIntoMainGraph(CurrentContext->Function, CurrentContext->FunctionGraph);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CurrentContext->SubFunctionCalls.Num() > 0)
|
|
|
|
|
{
|
|
|
|
|
//Move to the next sub function.
|
|
|
|
|
CurrentContext = CurrentContext->SubFunctionCalls.Pop();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//Done processing this function so remove it and move back to the parent.
|
|
|
|
|
if (CurrentContext->PoolIdx != INDEX_NONE)
|
|
|
|
|
{
|
|
|
|
|
CurrentContext->FunctionGraph->MarkPendingKill();
|
|
|
|
|
|
|
|
|
|
ContextPool.RemoveAtSwap(CurrentContext->PoolIdx);
|
|
|
|
|
}
|
|
|
|
|
CurrentContext = CurrentContext->Parent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-16 08:22:18 -04:00
|
|
|
#undef LOCTEXT_NAMESPACE
|