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;
}