void UEdGraphSchema_Niagara::GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const
{
	const UNiagaraGraph* NiagaraGraph = CastChecked<UNiagaraGraph>(ContextMenuBuilder.CurrentGraph);
	UNiagaraScriptSource* Source = NiagaraGraph->GetSource();

	TArray<FName> InputAttributeNames;
	Source->GetParticleAttributes(InputAttributeNames);
	Source->GetEmitterAttributes(InputAttributeNames);
	for(int32 i=0; i<InputAttributeNames.Num(); i++)
	{
		const FName AttrName = InputAttributeNames[i];

		FFormatNamedArguments Args;
		Args.Add(TEXT("Attribute"), FText::FromName(AttrName));
		const FText MenuDesc = FText::Format(NSLOCTEXT("Niagara", "GetAttribute", "Get {Attribute}"), Args);

		TSharedPtr<FNiagaraSchemaAction_NewNode> GetAttrAction = AddNewNodeAction(ContextMenuBuilder, TEXT("Get Attribute"), MenuDesc, TEXT(""));

		UNiagaraNodeGetAttr* GetAttrNode = NewObject<UNiagaraNodeGetAttr>(ContextMenuBuilder.OwnerOfTemporaries);
		GetAttrNode->AttrName = AttrName;
		GetAttrAction->NodeTemplate = GetAttrNode;
	}

	// Then get ops.
	uint8 NumOps = VectorVM::GetNumOpCodes();
	for(uint8 OpIdx=0; OpIdx<NumOps; OpIdx++)
	{
		VectorVM::FVectorVMOpInfo const& Info = VectorVM::GetOpCodeInfo(OpIdx);
		if(Info.IsImplemented())
		{
			TSharedPtr<FNiagaraSchemaAction_NewNode> AddOpAction = AddNewNodeAction(ContextMenuBuilder, TEXT("Add Operation"), FText::FromString(Info.FriendlyName), TEXT(""));

			UNiagaraNodeOp* OpNode = NewObject<UNiagaraNodeOp>(ContextMenuBuilder.OwnerOfTemporaries);
			OpNode->OpIndex = OpIdx;
			AddOpAction->NodeTemplate = OpNode;
		}
	}
}
void UEdGraphSchema_Niagara::GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const
{
	const UNiagaraGraph* NiagaraGraph = CastChecked<UNiagaraGraph>(ContextMenuBuilder.CurrentGraph);
	UNiagaraScriptSource* Source = NiagaraGraph->GetSource();

	TArray<FName> InputAttributeNames;
	Source->GetParticleAttributes(InputAttributeNames);
	for(int32 i=0; i<InputAttributeNames.Num(); i++)
	{
		const FName AttrName = InputAttributeNames[i];

		FFormatNamedArguments Args;
		Args.Add(TEXT("Attribute"), FText::FromName(AttrName));
		const FText MenuDesc = FText::Format(NSLOCTEXT("Niagara", "GetAttribute", "Get {Attribute}"), Args);

		TSharedPtr<FNiagaraSchemaAction_NewNode> GetAttrAction = AddNewNodeAction(ContextMenuBuilder, TEXT("Get Attribute"), MenuDesc, TEXT(""));

		UNiagaraNodeGetAttr* GetAttrNode = NewObject<UNiagaraNodeGetAttr>(ContextMenuBuilder.OwnerOfTemporaries);
		GetAttrNode->AttrName = AttrName;
		GetAttrAction->NodeTemplate = GetAttrNode;
	}

	TArray<FName> EmitterConstantNames_Vectors;
	TArray<FName> EmitterConstantNames_Matrices;
	Source->GetEmitterAttributes(EmitterConstantNames_Vectors, EmitterConstantNames_Matrices);
	for (int32 i = 0; i < EmitterConstantNames_Vectors.Num(); i++)
	{
		const FName ConstName = EmitterConstantNames_Vectors[i];

		FFormatNamedArguments Args;
		Args.Add(TEXT("Constant"), FText::FromName(ConstName));
		const FText MenuDesc = FText::Format(NSLOCTEXT("Niagara", "GetConstant", "Get {Constant}"), Args);

		TSharedPtr<FNiagaraSchemaAction_NewNode> GetAttrAction = AddNewNodeAction(ContextMenuBuilder, TEXT("Get Constant"), MenuDesc, TEXT(""));

		UNiagaraNodeConstant* GetConstNode = NewObject<UNiagaraNodeConstant>(ContextMenuBuilder.OwnerOfTemporaries);
		GetConstNode->ConstName = ConstName;
		GetConstNode->DataType = ENiagaraDataType::Vector;
		GetConstNode->bNeedsDefault = false;
		GetAttrAction->NodeTemplate = GetConstNode;
	}

	for (int32 i = 0; i < EmitterConstantNames_Matrices.Num(); i++)
	{
		const FName ConstName = EmitterConstantNames_Matrices[i];

		FFormatNamedArguments Args;
		Args.Add(TEXT("Constant"), FText::FromName(ConstName));
		const FText MenuDesc = FText::Format(NSLOCTEXT("Niagara", "GetConstant", "Get {Constant}"), Args);

		TSharedPtr<FNiagaraSchemaAction_NewNode> GetAttrAction = AddNewNodeAction(ContextMenuBuilder, TEXT("Get Constant"), MenuDesc, TEXT(""));

		UNiagaraNodeConstant* GetConstNode = NewObject<UNiagaraNodeConstant>(ContextMenuBuilder.OwnerOfTemporaries);
		GetConstNode->ConstName = ConstName;
		GetConstNode->DataType = ENiagaraDataType::Matrix;
		GetConstNode->bNeedsDefault = false;
		GetAttrAction->NodeTemplate = GetConstNode;
	}

#define NiagaraOp(OPNAME) \
		if(const FNiagaraOpInfo* OpInfo = FNiagaraOpInfo::GetOpInfo(FNiagaraOpInfo::OPNAME))\
		{\
			TSharedPtr<FNiagaraSchemaAction_NewNode> AddOpAction = AddNewNodeAction(ContextMenuBuilder, TEXT("Add Operation"), OpInfo->FriendlyName, TEXT(""));\
			UNiagaraNodeOp* OpNode = NewObject<UNiagaraNodeOp>(ContextMenuBuilder.OwnerOfTemporaries); \
			OpNode->OpName = OpInfo->Name; \
			AddOpAction->NodeTemplate = OpNode; \
		}

		
	NiagaraOpList

#undef NiagaraOp

	UNiagaraNodeConstant* ConstantNode = NewObject<UNiagaraNodeConstant>(ContextMenuBuilder.OwnerOfTemporaries);
	ConstantNode->DataType = ENiagaraDataType::Vector;
	ConstantNode->bNeedsDefault = true;
	TSharedPtr<FNiagaraSchemaAction_NewNode> AddOpAction = AddNewNodeAction(ContextMenuBuilder, TEXT("Constants"), ConstantNode->GetNodeTitle(ENodeTitleType::ListView), TEXT(""));
	AddOpAction->NodeTemplate = ConstantNode;
}
Exemplo n.º 3
0
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;

	UNiagaraScriptSource* Source = CastChecked<UNiagaraScriptSource>(ScriptToCompile->Source);
	check(Source->UpdateGraph);

	// Results log.
	FCompilerResultsLog MessageLog;

	// Clone the source graph so we can modify it as needed; merging in the child graphs
	UEdGraph* UpdateGraph = FEdGraphUtilities::CloneGraph(Source->UpdateGraph, Source, &MessageLog, true); 
	FEdGraphUtilities::MergeChildrenGraphsIn(UpdateGraph, UpdateGraph, /*bRequireSchemaMatch=*/ true);

	// Find the output node.
	UNiagaraNodeOutputUpdate* OutputNode = NULL;
	{
		TArray<UNiagaraNodeOutputUpdate*> OutputNodes;
		UpdateGraph->GetNodesOfClass(OutputNodes);
		if (OutputNodes.Num() != 1)
		{
			UE_LOG(LogNiagaraCompiler, Error, TEXT("Script contains %s output nodes: %s"),
				OutputNodes.Num() == 0 ? TEXT("no") : TEXT("too many"),
				*ScriptToCompile->GetPathName()
				);
			return;
		}
		OutputNode = OutputNodes[0];
	}
	check(OutputNode);

	// Traverse the node graph for each output and generate expressions as we go.
	FNiagaraCompilerContext Context(MessageLog);
	Source->GetParticleAttributes(Context.Attributes);
	TArray<int32> OutputExpressions;
	OutputExpressions.AddUninitialized(Context.Attributes.Num());
	FMemory::Memset(OutputExpressions.GetData(), 0xff, Context.Attributes.Num() * sizeof(int32));

	for (int32 PinIndex = 0; PinIndex < OutputNode->Pins.Num(); ++PinIndex)
	{
		UEdGraphPin* Pin = OutputNode->Pins[PinIndex];
		int32 AttrIndex = Context.Attributes.Find(FName(*Pin->PinName));
		if (AttrIndex != INDEX_NONE)
		{
			int32 ExpressionIndex = EvaluateGraph(Context, Pin);
			OutputExpressions[AttrIndex] = ExpressionIndex;
		}
	}

	// Generate pass-thru ops for any outputs that are not connected.
	for (int32 AttrIndex = 0; AttrIndex < OutputExpressions.Num(); ++AttrIndex)
	{
		if (OutputExpressions[AttrIndex] == INDEX_NONE)
		{
			// Generate a pass-thru op.
			FNiagaraExpr PassThru;
			PassThru.OpIndex = VectorVM::EOp::add;
			PassThru.Src[0] = AttrIndex | 0x80000000;
			PassThru.Src[1] = 0 | 0x40000000;
			PassThru.Src[2] = INDEX_NONE;
			PassThru.SrcOperandTypeVector = 0x2;
			OutputExpressions[AttrIndex] = Context.Expressions.Add(PassThru);
		}
	}
	
	// Figure out the lifetime of each expression.
	TArray<int32> ExpressionLifetimes;
	ExpressionLifetimes.AddUninitialized(Context.Expressions.Num());
	for (int32 i = 0; i < Context.Expressions.Num(); ++i)
	{
		ExpressionLifetimes[i] = i;
		FNiagaraExpr& Expr = Context.Expressions[i];
		VectorVM::FVectorVMOpInfo const& OpInfo = VectorVM::GetOpCodeInfo(Expr.OpIndex);
		for (int32 k = 0; k < 3; ++k)
		{
			//if (OpInfo.SrcTypes[k] == VectorVM::EOpSrc::Register
			if ((Expr.SrcOperandTypeVector & (1 << k)) == 0 && OpInfo.SrcTypes[k] != VectorVM::EOpSrc::Invalid
				&& !IsAttrIndex(Expr.Src[k]))
			{
				check(Context.Expressions.IsValidIndex(Expr.Src[k]));
				check(Expr.Src[k] < i);
				ExpressionLifetimes[Expr.Src[k]] = i;
			}
		}
	}

	// Allocate temporary registers for the output of each expression.
	int32 Registers[VectorVM::NumTempRegisters];
	FMemory::Memset(Registers, 0xff, sizeof(Registers));
	TArray<int32> RegisterAssignments;
	RegisterAssignments.AddUninitialized(Context.Expressions.Num());
	for (int32 i = 0; i < Context.Expressions.Num(); ++i)
	{
		FNiagaraExpr& Expr = Context.Expressions[i];
		VectorVM::FVectorVMOpInfo const& OpInfo = VectorVM::GetOpCodeInfo(Expr.OpIndex);

		for (int32 j = 0; j < 3; ++j)
		{
			//if (OpInfo.SrcTypes[j] == VectorVM::EOpSrc::Register)
			if ((Expr.SrcOperandTypeVector & (1 << j)) == 0 && OpInfo.SrcTypes[j] != VectorVM::EOpSrc::Invalid)
			{
				if (IsAttrIndex(Expr.Src[j]))
				{
					Expr.Src[j] = VectorVM::FirstInputRegister + AttrIndexFromExpressionIndex(Expr.Src[j]);
				}
				else
				{
					Expr.Src[j] = RegisterAssignments[Expr.Src[j]];
				}
			}
			//else if (OpInfo.SrcTypes[j] == VectorVM::EOpSrc::Const)
			else if ((Expr.SrcOperandTypeVector & (1 << j)) == 1)
			{
				// VectorVM::EOpSrc::Const.
				Expr.Src[j] = ConstIndexFromExpressionIndex(Expr.Src[j]);
				//Expr.SrcOperandTypeVector |= 1<<j;
			}
		}

		// now that we've figured out register/constant assignments, we revert back to the base opcode; the kernel Exec function will handle passing 
		// the data in correctly based on the SrcOperandTypeVector. It would be good to change the compiler so we don't need the permutation
		// opcodes at all, i.e. figure out source operand types without needing them in the OpInfo.
		Expr.OpIndex = OpInfo.BaseOpcode;


		int32 AttrIndex = INDEX_NONE;
		if (OutputExpressions.Find(i, AttrIndex))
		{
			// Assign to an output register.
			RegisterAssignments[i] = VectorVM::FirstOutputRegister + AttrIndex;
		}
		else
		{
			int32 AssignedRegister = INDEX_NONE;
			for (int32 j = 0; j < VectorVM::NumTempRegisters; ++j)
			{
				if (Registers[j] == INDEX_NONE
					|| ExpressionLifetimes[Registers[j]] < i)
				{
					AssignedRegister = j;
					break;
				}
			}
			check(AssignedRegister != INDEX_NONE && AssignedRegister < VectorVM::NumTempRegisters);

			Registers[AssignedRegister] = i;
			RegisterAssignments[i] = AssignedRegister;
		}
	}

	// Generate bytecode!
	TArray<uint8>& Code = ScriptToCompile->ByteCode;
	Code.Empty();
	for (int32 i = 0; i < Context.Expressions.Num(); ++i)
	{
		FNiagaraExpr& Expr = Context.Expressions[i];
		VectorVM::FVectorVMOpInfo const& OpInfo = VectorVM::GetOpCodeInfo(Expr.OpIndex);

		int32 DestRegister = RegisterAssignments[i];
		check(DestRegister < VectorVM::MaxRegisters);

		check(Expr.OpIndex < VectorVM::EOp::NumOpcodes);
		check(Expr.OpIndex == OpInfo.BaseOpcode);

		Code.Add(Expr.OpIndex);
		Code.Add(DestRegister);
		Code.Add(Expr.SrcOperandTypeVector);
		for (int32 j = 0; j < 3; ++j)
		{
			if (OpInfo.SrcTypes[j] == VectorVM::EOpSrc::Invalid)
				break;
			Code.Add(Expr.Src[j]);
		}

		// If the expression is output to multiple attributes, copy them here.
		if (DestRegister >= VectorVM::FirstOutputRegister)
		{
			int32 AttrIndex = DestRegister - VectorVM::FirstOutputRegister;
			for (int32 j = AttrIndex+1; j < OutputExpressions.Num(); ++j)
			{
				if (OutputExpressions[j] == i)
				{
					Code.Add(VectorVM::EOp::add);
					Code.Add(VectorVM::FirstOutputRegister + j);
					Code.Add(0x0);	// all inputs are registers
					Code.Add(DestRegister);
					Code.Add(0);
				}
			}
		}
	}

	// Terminate with the 'done' opcode.
	Code.Add(VectorVM::EOp::done);

	// And copy the constant table and attributes.
	ScriptToCompile->ConstantTable = Context.Constants;
	ScriptToCompile->Attributes = Context.Attributes;
}