Exemplo n.º 1
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;
}
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;
}
UObject* UNiagaraScriptFactoryNew::FactoryCreateNew(UClass* Class,UObject* InParent,FName Name,EObjectFlags Flags,UObject* Context,FFeedbackContext* Warn)
{
	check(Class->IsChildOf(UNiagaraScript::StaticClass()));

	// First allocate runtime script 
	UNiagaraScript* NewScript = ConstructObject<UNiagaraScript>(Class, InParent, Name, Flags);
	if(NewScript != NULL)
	{
		// Then allocate editor-only 'source' object
		UNiagaraScriptSource* Source = ConstructObject<UNiagaraScriptSource>(UNiagaraScriptSource::StaticClass(), NewScript, NAME_None, RF_Transactional);
		if(Source)
		{
			// Create 'update graph'
			UNiagaraGraph* CreatedGraph  = ConstructObject<UNiagaraGraph>(UNiagaraGraph::StaticClass(), Source, NAME_None, RF_Transactional);
			Source->UpdateGraph = CreatedGraph;

			FGraphNodeCreator<UNiagaraNodeOutputUpdate> OutputNodeCreator(*CreatedGraph);
			UNiagaraNodeOutputUpdate* OutputNode = OutputNodeCreator.CreateNode();
			OutputNodeCreator.Finalize();

			FGraphNodeCreator<UNiagaraNodeGetAttr> TimeNodeCreator(*CreatedGraph);
			UNiagaraNodeGetAttr* DeltaTimeNode = TimeNodeCreator.CreateNode();
			DeltaTimeNode->AttrName = FName(TEXT("DeltaTime"));
			TimeNodeCreator.Finalize();

			UEdGraphPin* DeltaTimePin = DeltaTimeNode->FindPinChecked(DeltaTimeNode->AttrName.ToString());

			TArray<FName> OutputNames;
			Source->GetUpdateOutputs(OutputNames);

			// Simple Euler integration.
			for(int32 i=0; i<3; i++)
			{
				FGraphNodeCreator<UNiagaraNodeGetAttr> PosNodeCreator(*CreatedGraph);
				UNiagaraNodeGetAttr* PosAttrNode = PosNodeCreator.CreateNode();
				PosAttrNode->AttrName = OutputNames[i];
				PosNodeCreator.Finalize();

				FGraphNodeCreator<UNiagaraNodeGetAttr> VelNodeCreator(*CreatedGraph);
				UNiagaraNodeGetAttr* VelAttrNode = VelNodeCreator.CreateNode();
				VelAttrNode->AttrName = OutputNames[i+3];
				VelNodeCreator.Finalize();


				UEdGraphPin* PosAttrOutputPin = PosAttrNode->FindPinChecked(OutputNames[i].ToString());
				UEdGraphPin* VelAttrOutputPin = VelAttrNode->FindPinChecked(OutputNames[i+3].ToString());
				UEdGraphPin* OutputPin = OutputNode->FindPinChecked(OutputNames[i].ToString());

				UNiagaraNodeOp* MulNode = NULL;
				{
					FGraphNodeCreator<UNiagaraNodeOp> NodeCreator(*CreatedGraph);
					MulNode = NodeCreator.CreateNode();
					MulNode->OpIndex = VectorVM::EOp::mul;
					NodeCreator.Finalize();

					UEdGraphPin* APin = MulNode->FindPinChecked(TEXT("A"));
					UEdGraphPin* BPin = MulNode->FindPinChecked(TEXT("B"));

					APin->MakeLinkTo(VelAttrOutputPin);
					BPin->MakeLinkTo(DeltaTimePin);
				}

				{
					FGraphNodeCreator<UNiagaraNodeOp> NodeCreator(*CreatedGraph);
					UNiagaraNodeOp* AddNode = NodeCreator.CreateNode();
					AddNode->OpIndex = VectorVM::EOp::add;
					NodeCreator.Finalize();

					UEdGraphPin* APin = AddNode->FindPinChecked(TEXT("A"));
					UEdGraphPin* BPin = AddNode->FindPinChecked(TEXT("B"));
					UEdGraphPin* AddResultPin = AddNode->FindPinChecked(TEXT("Result"));
					UEdGraphPin* MulResultPin = MulNode->FindPinChecked(TEXT("Result"));

					APin->MakeLinkTo(PosAttrOutputPin);
					BPin->MakeLinkTo(MulResultPin);
					OutputPin->MakeLinkTo(AddResultPin);
				}
			}

			// Update relative time.
			{
				FGraphNodeCreator<UNiagaraNodeGetAttr> RelTimeNodeCreator(*Source->UpdateGraph);
				UNiagaraNodeGetAttr* RelTimeAttrNode = RelTimeNodeCreator.CreateNode();
				RelTimeAttrNode->AttrName = FName(TEXT("RelativeTime"));
				RelTimeNodeCreator.Finalize();

				FGraphNodeCreator<UNiagaraNodeOp> MadNodeCreator(*Source->UpdateGraph);
				UNiagaraNodeOp* MadNode = MadNodeCreator.CreateNode();
				MadNode->OpIndex = VectorVM::EOp::mad;
				MadNodeCreator.Finalize();

				MadNode->FindPinChecked(TEXT("A"))->MakeLinkTo(DeltaTimePin);
				MadNode->FindPinChecked(TEXT("B"))->DefaultValue = TEXT("0.2");
				MadNode->FindPinChecked(TEXT("C"))->MakeLinkTo(RelTimeAttrNode->FindPinChecked(TEXT("RelativeTime")));

				OutputNode->FindPinChecked(TEXT("RelativeTime"))->MakeLinkTo(MadNode->FindPinChecked(TEXT("Result")));
			}

			// Set pointer in script to source
			NewScript->Source = Source;

			FNiagaraEditorModule& NiagaraEditorModule = FModuleManager::Get().LoadModuleChecked<FNiagaraEditorModule>(TEXT("NiagaraEditor"));
			NiagaraEditorModule.CompileScript(NewScript);
		}
	}

	return NewScript;
}