737 lines
35 KiB
C#
737 lines
35 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.VFX;
|
|
using UnityEditor.VFX;
|
|
using UnityEditor.Experimental.GraphView;
|
|
|
|
using NodeID = System.UInt32;
|
|
|
|
namespace UnityEditor.VFX.UI
|
|
{
|
|
static class VFXConvertSubgraph
|
|
{
|
|
public static void ConvertToSubgraphContext(VFXView sourceView, IEnumerable<Controller> controllers, Rect rect, string path = null)
|
|
{
|
|
var ctx = new Context();
|
|
ctx.ConvertToSubgraphContext(sourceView, controllers, rect, path);
|
|
}
|
|
|
|
public static void ConvertToSubgraphOperator(VFXView sourceView, IEnumerable<Controller> controllers, Rect rect)
|
|
{
|
|
var ctx = new Context();
|
|
ctx.ConvertToSubgraphOperator(sourceView, controllers, rect);
|
|
}
|
|
|
|
public static void ConvertToSubgraphBlock(VFXView sourceView, IEnumerable<Controller> controllers, Rect rect)
|
|
{
|
|
var ctx = new Context();
|
|
ctx.ConvertToSubgraphBlock(sourceView, controllers, rect);
|
|
}
|
|
|
|
enum Type
|
|
{
|
|
Context,
|
|
Operator,
|
|
Block
|
|
}
|
|
|
|
static VisualEffectObject CreateUniquePath(VFXView sourceView, Type type)
|
|
{
|
|
string graphPath = AssetDatabase.GetAssetPath(sourceView.controller.model.asset);
|
|
string graphName = Path.GetFileNameWithoutExtension(graphPath);
|
|
string graphDirPath = Path.GetDirectoryName(graphPath);
|
|
|
|
switch (type)
|
|
{
|
|
case Type.Operator:
|
|
{
|
|
string targetSubgraphPath = string.Format("{0}/{1}_SubgraphOperator.vfxoperator", graphDirPath, graphName);
|
|
int cpt = 1;
|
|
while (File.Exists(targetSubgraphPath))
|
|
{
|
|
targetSubgraphPath = string.Format("{0}/{1}_SubgraphOperator_{2}.vfxoperator", graphDirPath, graphName, cpt++);
|
|
}
|
|
return VisualEffectAssetEditorUtility.CreateNew<VisualEffectSubgraphOperator>(targetSubgraphPath);
|
|
}
|
|
case Type.Context:
|
|
{
|
|
string targetSubgraphPath = string.Format("{0}/{1}_Subgraph.vfx", graphDirPath, graphName);
|
|
int cpt = 1;
|
|
while (File.Exists(targetSubgraphPath))
|
|
{
|
|
targetSubgraphPath = string.Format("{0}/{1}_Subgraph_{2}.vfx", graphDirPath, graphName, cpt++);
|
|
}
|
|
return VisualEffectAssetEditorUtility.CreateNewAsset(targetSubgraphPath);
|
|
}
|
|
case Type.Block:
|
|
{
|
|
string targetSubgraphPath = string.Format("{0}/{1}_SubgraphBlock.vfxblock", graphDirPath, graphName);
|
|
int cpt = 1;
|
|
while (File.Exists(targetSubgraphPath))
|
|
{
|
|
targetSubgraphPath = string.Format("{0}/{1}_SubgraphBlock_{2}.vfxblock", graphDirPath, graphName, cpt++);
|
|
}
|
|
return VisualEffectAssetEditorUtility.CreateNew<VisualEffectSubgraphBlock>(targetSubgraphPath);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
class Context
|
|
{
|
|
List<VFXParameterNodeController> parameterNodeControllers;
|
|
|
|
VFXViewController m_SourceController;
|
|
List<Controller> m_SourceControllers;
|
|
VFXView m_SourceView;
|
|
VFXModel m_SourceNode;
|
|
IVFXSlotContainer m_SourceSlotContainer;
|
|
VFXNodeController m_SourceNodeController;
|
|
Dictionary<string, VFXParameterNodeController> m_SourceParameters;
|
|
|
|
VFXViewController m_TargetController;
|
|
List<VFXNodeController> m_TargetControllers;
|
|
List<VFXParameterController> m_TargetParameters = new List<VFXParameterController>();
|
|
VisualEffectObject m_TargetSubgraph;
|
|
|
|
Rect m_Rect;
|
|
|
|
|
|
void Init(VFXView sourceView, IEnumerable<Controller> controllers)
|
|
{
|
|
this.m_SourceView = sourceView;
|
|
|
|
m_SourceControllers = controllers.Concat(sourceView.controller.dataEdges.Where(t => controllers.Contains(t.input.sourceNode) && controllers.Contains(t.output.sourceNode))).Distinct().ToList();
|
|
parameterNodeControllers = m_SourceControllers.OfType<VFXParameterNodeController>().ToList();
|
|
|
|
|
|
m_SourceController = sourceView.controller;
|
|
VFXGraph sourceGraph = m_SourceController.graph;
|
|
m_SourceController.useCount++;
|
|
|
|
m_SourceParameters = new Dictionary<string, VFXParameterNodeController>();
|
|
|
|
foreach (var parameterNode in parameterNodeControllers)
|
|
{
|
|
m_SourceParameters[parameterNode.exposedName] = parameterNode;
|
|
}
|
|
}
|
|
|
|
void Uninit()
|
|
{
|
|
foreach (var element in m_SourceControllers.Where(t => !(t is VFXDataEdgeController) && !(t is VFXParameterNodeController)))
|
|
{
|
|
m_SourceController.RemoveElement(element);
|
|
}
|
|
|
|
foreach (var element in parameterNodeControllers)
|
|
{
|
|
if (element.infos.linkedSlots == null || element.infos.linkedSlots.Count() == 0)
|
|
m_SourceController.RemoveElement(element);
|
|
}
|
|
|
|
m_TargetController.useCount--;
|
|
m_SourceController.useCount--;
|
|
}
|
|
|
|
void UninitSmart()
|
|
{
|
|
var nodeNotToDelete = new HashSet<Controller>();
|
|
|
|
foreach (var node in m_SourceControllers.OfType<VFXNodeController>().Where(t => t.outputPorts.Count() > 0))
|
|
{
|
|
if (nodeNotToDelete.Contains(node))
|
|
continue;
|
|
|
|
var oldBag = new HashSet<VFXNodeController>();
|
|
var newBag = new HashSet<VFXNodeController>();
|
|
|
|
oldBag.Add(node);
|
|
|
|
while (oldBag.Count > 0)
|
|
{
|
|
foreach (var n in oldBag)
|
|
{
|
|
if (n.outputPorts.SelectMany(t => t.connections).Any(t => nodeNotToDelete.Contains(t.input.sourceNode) || !m_SourceControllersWithBlocks.Contains(t.input.sourceNode)))
|
|
{
|
|
nodeNotToDelete.Add(n);
|
|
oldBag.Clear();
|
|
break;
|
|
}
|
|
|
|
foreach (var o in n.inputPorts.SelectMany(t => t.connections).Select(t => t.output))
|
|
{
|
|
newBag.Add(o.sourceNode);
|
|
}
|
|
}
|
|
|
|
oldBag.Clear();
|
|
var tmp = oldBag;
|
|
oldBag = newBag;
|
|
newBag = tmp;
|
|
}
|
|
}
|
|
|
|
foreach (var element in m_SourceControllers.Where(t => !(t is VFXDataEdgeController) && !(t is VFXParameterNodeController) && !nodeNotToDelete.Contains(t)))
|
|
{
|
|
m_SourceController.RemoveElement(element);
|
|
}
|
|
|
|
foreach (var element in parameterNodeControllers)
|
|
{
|
|
if (element.infos.linkedSlots == null || element.infos.linkedSlots.Count() == 0)
|
|
m_SourceController.RemoveElement(element);
|
|
}
|
|
|
|
m_TargetController.useCount--;
|
|
m_SourceController.useCount--;
|
|
}
|
|
|
|
void CopyPasteNodes()
|
|
{
|
|
object result = VFXCopy.Copy(m_SourceControllers, m_Rect);
|
|
|
|
VFXPaste.Paste(m_TargetController, m_Rect.center, result, null, null, m_TargetControllers);
|
|
List<VFXParameterController> targetParameters = new List<VFXParameterController>();
|
|
}
|
|
|
|
List<VFXNodeController> m_SourceOperatorAndParameters;
|
|
List<VFXNodeController> m_TargetOperatorAndParameters;
|
|
|
|
void CopyPasteOperators(Dictionary<VFXNodeController, VFXNodeController> targetNodes)
|
|
{
|
|
m_SourceOperatorAndParameters = m_SourceControllers.OfType<VFXNodeController>().Where(t => !(t is VFXBlockController)).ToList();
|
|
object result = VFXCopy.Copy(m_SourceOperatorAndParameters, m_Rect);
|
|
|
|
m_TargetOperatorAndParameters = new List<VFXNodeController>();
|
|
VFXPaste.Paste(m_TargetController, m_Rect.center, result, null, null, m_TargetOperatorAndParameters);
|
|
|
|
foreach (var st in m_SourceOperatorAndParameters.Zip(m_TargetOperatorAndParameters, (s, t) => new { source = s, target = t }))
|
|
{
|
|
targetNodes[st.source] = st.target;
|
|
}
|
|
}
|
|
|
|
void SetupTargetParameters()
|
|
{
|
|
// Change each parameter created by copy paste ( and therefore a parameter copied ) to exposed
|
|
foreach (var parameter in m_TargetController.parameterControllers)
|
|
{
|
|
m_TargetParameters.Add(parameter);
|
|
parameter.exposed = true;
|
|
}
|
|
}
|
|
|
|
public void ConvertToSubgraphContext(VFXView sourceView, IEnumerable<Controller> controllers, Rect rect, string path)
|
|
{
|
|
this.m_Rect = rect;
|
|
Init(sourceView, controllers);
|
|
if (path == null)
|
|
{
|
|
if (!CreateUniqueSubgraph("Subgraph", VisualEffectResource.Extension, VisualEffectAssetEditorUtility.CreateNewAsset))
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
m_TargetSubgraph = VisualEffectAssetEditorUtility.CreateNewAsset(path);
|
|
|
|
m_TargetController = VFXViewController.GetController(m_TargetSubgraph.GetResource());
|
|
m_TargetController.useCount++;
|
|
m_TargetControllers = new List<VFXNodeController>();
|
|
}
|
|
CopyPasteNodes();
|
|
m_SourceNode = ScriptableObject.CreateInstance<VFXSubgraphContext>();
|
|
PostSetupNode();
|
|
m_SourceControllersWithBlocks = m_SourceControllers.Concat(m_SourceControllers.OfType<VFXContextController>().SelectMany(t => t.blockControllers));
|
|
TransferEdges();
|
|
//TransferContextsFlowEdges();
|
|
UninitSmart();
|
|
}
|
|
|
|
public void ConvertToSubgraphOperator(VFXView sourceView, IEnumerable<Controller> controllers, Rect rect)
|
|
{
|
|
this.m_Rect = rect;
|
|
Init(sourceView, controllers);
|
|
if (!CreateUniqueSubgraph("SubgraphOperator", VisualEffectSubgraphOperator.Extension, VisualEffectAssetEditorUtility.CreateNew<VisualEffectSubgraphOperator>))
|
|
return;
|
|
CopyPasteNodes();
|
|
m_SourceNode = ScriptableObject.CreateInstance<VFXSubgraphOperator>();
|
|
PostSetupNode();
|
|
m_SourceControllersWithBlocks = m_SourceControllers.Concat(m_SourceControllers.OfType<VFXContextController>().SelectMany(t => t.blockControllers));
|
|
TransferEdges();
|
|
TransfertOperatorOutputEdges();
|
|
Uninit();
|
|
}
|
|
|
|
List<VFXBlockController> m_SourceBlockControllers;
|
|
List<VFXBlockController> m_TargetBlocks = null;
|
|
|
|
public void ConvertToSubgraphBlock(VFXView sourceView, IEnumerable<Controller> controllers, Rect rect)
|
|
{
|
|
this.m_Rect = rect;
|
|
Init(sourceView, controllers);
|
|
if (!CreateUniqueSubgraph("SubgraphBlock", VisualEffectSubgraphBlock.Extension, VisualEffectAssetEditorUtility.CreateNew<VisualEffectSubgraphBlock>))
|
|
return;
|
|
|
|
m_SourceControllers.RemoveAll(t => t is VFXContextController); // Don't copy contexts
|
|
|
|
m_SourceBlockControllers = m_SourceControllers.OfType<VFXBlockController>().OrderBy(t => t.index).ToList();
|
|
|
|
VFXContextController sourceContextController = m_SourceBlockControllers.First().contextController;
|
|
|
|
object copyData = VFXCopy.CopyBlocks(m_SourceBlockControllers);
|
|
|
|
var targetContext = m_TargetController.graph.children.OfType<VFXBlockSubgraphContext>().FirstOrDefault();
|
|
if (targetContext == null)
|
|
{
|
|
targetContext = ScriptableObject.CreateInstance<VFXBlockSubgraphContext>();
|
|
m_TargetController.graph.AddChild(targetContext);
|
|
}
|
|
m_TargetController.LightApplyChanges();
|
|
targetContext.position = sourceContextController.position;
|
|
targetContext.SetSettingValue("m_SuitableContexts", (VFXBlockSubgraphContext.ContextType)m_SourceBlockControllers.Select(t => t.model.compatibleContexts).Aggregate((t, s) => t & s));
|
|
m_TargetBlocks = new List<VFXBlockController>();
|
|
|
|
VFXPaste.PasteBlocks(m_TargetController, copyData, targetContext, 0, m_TargetBlocks);
|
|
|
|
|
|
Dictionary<VFXNodeController, VFXNodeController> targetControllers = new Dictionary<VFXNodeController, VFXNodeController>();
|
|
CopyPasteOperators(targetControllers);
|
|
|
|
m_SourceControllersWithBlocks = m_SourceControllers.Concat(m_SourceBlockControllers);
|
|
//Create lost links between nodes and blocks
|
|
foreach (var edge in m_SourceController.dataEdges.Where(t => m_SourceOperatorAndParameters.Contains(t.output.sourceNode) && m_SourceBlockControllers.Contains(t.input.sourceNode)))
|
|
{
|
|
var outputNode = targetControllers[edge.output.sourceNode];
|
|
var output = outputNode.outputPorts.First(t => t.path == edge.output.path);
|
|
|
|
var inputBlock = m_TargetBlocks[m_SourceBlockControllers.IndexOf(edge.input.sourceNode as VFXBlockController)];
|
|
var input = inputBlock.inputPorts.First(t => t.path == edge.input.path);
|
|
|
|
m_TargetController.CreateLink(input, output);
|
|
}
|
|
//Create lost links between nodes
|
|
foreach (var edge in m_SourceController.dataEdges.Where(t => m_SourceOperatorAndParameters.Contains(t.output.sourceNode) && m_SourceOperatorAndParameters.Contains(t.input.sourceNode)))
|
|
{
|
|
var outputNode = targetControllers[edge.output.sourceNode];
|
|
var output = outputNode.outputPorts.First(t => t.path == edge.output.path);
|
|
|
|
var inputNode = targetControllers[edge.input.sourceNode];
|
|
var input = inputNode.inputPorts.First(t => t.path == edge.input.path);
|
|
|
|
m_TargetController.CreateLink(input, output);
|
|
}
|
|
|
|
var sourceBlock = ScriptableObject.CreateInstance<VFXSubgraphBlock>();
|
|
m_SourceNode = sourceBlock;
|
|
sourceContextController.model.AddChild(m_SourceNode, m_SourceBlockControllers.Select(t => t.index).Min());
|
|
(m_SourceView.GetNodeByController(sourceContextController) as VFXContextUI).UpdateSelectionWithNewBlocks();
|
|
sourceContextController.ApplyChanges();
|
|
|
|
m_SourceNodeController = sourceContextController.blockControllers.First(t => t.model == m_SourceNode);
|
|
PostSetup();
|
|
m_SourceNode.SetSettingValue("m_Subgraph", m_TargetSubgraph);
|
|
m_SourceNodeController.ApplyChanges();
|
|
|
|
var targetContextController = m_TargetController.GetRootNodeController(targetContext, 0) as VFXContextController;
|
|
|
|
m_SourceControllersWithBlocks = m_SourceControllers.Concat(m_SourceBlockControllers);
|
|
m_SourceControllers = m_SourceOperatorAndParameters.Cast<Controller>().ToList();
|
|
m_TargetControllers = m_TargetOperatorAndParameters;
|
|
TransferEdges();
|
|
m_SourceControllers = m_SourceControllersWithBlocks.ToList();
|
|
UninitSmart();
|
|
}
|
|
|
|
bool CreateUniqueSubgraph(string typeName, string extension, Func<string, VisualEffectObject> createFunc)
|
|
{
|
|
string graphPath = AssetDatabase.GetAssetPath(m_SourceView.controller.model);
|
|
string graphName;
|
|
string graphDirPath;
|
|
if (string.IsNullOrEmpty(graphPath))
|
|
{
|
|
graphName = m_SourceView.controller.model.name;
|
|
if (string.IsNullOrEmpty(graphName))
|
|
graphName = "New VFX";
|
|
|
|
graphDirPath = "Assets";
|
|
}
|
|
else
|
|
{
|
|
graphName = Path.GetFileNameWithoutExtension(graphPath);
|
|
graphDirPath = Path.GetDirectoryName(graphPath).Replace('\\', '/');
|
|
}
|
|
|
|
|
|
string fileName = $"{graphName}_{typeName}";
|
|
string targetSubgraphPath = string.Format("{0}/{1}{2}", graphDirPath, fileName, extension);
|
|
int cpt = 1;
|
|
|
|
while (File.Exists(targetSubgraphPath))
|
|
{
|
|
fileName = $"{graphName}_{typeName}_{cpt++}";
|
|
targetSubgraphPath = string.Format("{0}/{1}{2}", graphDirPath, fileName, extension);
|
|
}
|
|
targetSubgraphPath = EditorUtility.SaveFilePanelInProject("Create Subgraph", fileName, extension.Substring(1), "Select where you want to save your subgraph.");
|
|
|
|
if (string.IsNullOrEmpty(targetSubgraphPath))
|
|
return false;
|
|
|
|
if (Path.GetExtension(targetSubgraphPath) != extension)
|
|
{
|
|
targetSubgraphPath += extension;
|
|
}
|
|
|
|
if (File.Exists(targetSubgraphPath))
|
|
{
|
|
Debug.LogError("Can't overwrite a subgraph");
|
|
return false;
|
|
}
|
|
|
|
m_TargetSubgraph = createFunc(targetSubgraphPath);
|
|
|
|
m_TargetController = VFXViewController.GetController(m_TargetSubgraph.GetResource());
|
|
m_TargetController.useCount++;
|
|
m_TargetControllers = new List<VFXNodeController>();
|
|
|
|
return true;
|
|
}
|
|
|
|
void PostSetupNode()
|
|
{
|
|
PostSetup();
|
|
m_SourceNode.position = m_Rect.center;
|
|
m_SourceView.UpdateSelectionWithNewNode();
|
|
m_SourceController.graph.AddChild(m_SourceNode);
|
|
m_SourceNode.SetSettingValue("m_Subgraph", m_TargetSubgraph);
|
|
m_SourceController.LightApplyChanges();
|
|
m_SourceNodeController = m_SourceController.GetRootNodeController(m_SourceNode, 0);
|
|
m_SourceNodeController.ApplyChanges();
|
|
}
|
|
|
|
void PostSetup()
|
|
{
|
|
SetupTargetParameters();
|
|
m_SourceSlotContainer = m_SourceNode as IVFXSlotContainer;
|
|
}
|
|
|
|
void TransferEdges()
|
|
{
|
|
for (int i = 0; i < m_TargetParameters.Count; ++i)
|
|
{
|
|
var input = m_SourceNodeController.inputPorts.First(t => t.model == m_SourceSlotContainer.inputSlots[i]);
|
|
var output = m_SourceParameters[m_TargetParameters[i].exposedName].outputPorts.First();
|
|
|
|
m_TargetController.CreateLink(input, output);
|
|
}
|
|
TransfertDataEdges();
|
|
}
|
|
|
|
IEnumerable<Controller> m_SourceControllersWithBlocks;
|
|
|
|
void TransfertDataEdges()
|
|
{
|
|
// Search for links between with inputs in the selected part and the output in other parts of the graph.
|
|
Dictionary<VFXDataAnchorController, List<VFXDataAnchorController>> traversingInEdges = new Dictionary<VFXDataAnchorController, List<VFXDataAnchorController>>();
|
|
|
|
foreach (var edge in m_SourceController.dataEdges.Where(
|
|
t =>
|
|
{
|
|
if (parameterNodeControllers.Contains(t.output.sourceNode))
|
|
return false;
|
|
var inputInControllers = m_SourceControllersWithBlocks.Contains(t.input.sourceNode);
|
|
var outputInControllers = m_SourceControllersWithBlocks.Contains(t.output.sourceNode);
|
|
|
|
return inputInControllers && !outputInControllers;
|
|
}
|
|
))
|
|
{
|
|
List<VFXDataAnchorController> outputs = null;
|
|
if (!traversingInEdges.TryGetValue(edge.input, out outputs))
|
|
{
|
|
outputs = new List<VFXDataAnchorController>();
|
|
traversingInEdges[edge.input] = outputs;
|
|
}
|
|
|
|
outputs.Add(edge.output);
|
|
}
|
|
|
|
var newSourceInputs = traversingInEdges.Keys.ToArray();
|
|
|
|
for (int i = 0; i < newSourceInputs.Length; ++i)
|
|
{
|
|
VFXParameter newTargetParameter = m_TargetController.AddVFXParameter(Vector2.zero, VFXLibrary.GetParameters().First(t => t.model.type == newSourceInputs[i].portType));
|
|
|
|
m_TargetController.LightApplyChanges();
|
|
|
|
VFXParameterController newTargetParamController = m_TargetController.GetParameterController(newTargetParameter);
|
|
newTargetParamController.exposed = true;
|
|
|
|
var outputs = traversingInEdges[newSourceInputs[i]];
|
|
|
|
var linkedParameter = outputs.FirstOrDefault(t => t.sourceNode is VFXParameterNodeController);
|
|
if (linkedParameter != null)
|
|
{
|
|
newTargetParamController.exposedName = (linkedParameter.sourceNode as VFXParameterNodeController).parentController.exposedName;
|
|
{
|
|
VFXParameter originalParameter = (linkedParameter.sourceNode as VFXParameterNodeController).parentController.model;
|
|
|
|
newTargetParameter.valueFilter = originalParameter.valueFilter;
|
|
if (originalParameter.valueFilter == VFXValueFilter.Range)
|
|
{
|
|
newTargetParameter.min = originalParameter.min;
|
|
newTargetParameter.max = originalParameter.max;
|
|
}
|
|
else if (originalParameter.valueFilter == VFXValueFilter.Enum)
|
|
{
|
|
newTargetParameter.enumValues = originalParameter.enumValues.ToList();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
newTargetParamController.exposedName = newSourceInputs[i].name;
|
|
|
|
//first the equivalent of sourceInput in the target
|
|
|
|
VFXNodeController targetNode = null;
|
|
Vector2 position;
|
|
|
|
if (newSourceInputs[i].sourceNode is VFXBlockController)
|
|
{
|
|
var blockController = newSourceInputs[i].sourceNode as VFXBlockController;
|
|
if (m_TargetBlocks != null)
|
|
{
|
|
targetNode = m_TargetBlocks[m_SourceBlockControllers.IndexOf(blockController)];
|
|
position = blockController.contextController.position;
|
|
}
|
|
else
|
|
{
|
|
var targetContext = m_TargetControllers[m_SourceControllers.IndexOf(blockController.contextController)] as VFXContextController;
|
|
|
|
targetNode = targetContext.blockControllers[blockController.index];
|
|
position = blockController.contextController.position;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
targetNode = m_TargetControllers[m_SourceControllers.IndexOf(newSourceInputs[i].sourceNode)];
|
|
position = targetNode.position;
|
|
}
|
|
|
|
VFXDataAnchorController targetAnchor = targetNode.inputPorts.First(t => t.path == newSourceInputs[i].path);
|
|
|
|
|
|
position.y += targetAnchor.model.owner.inputSlots.IndexOf(targetAnchor.model) * 32;
|
|
|
|
VFXNodeController parameterNode = m_TargetController.AddVFXParameter(position - new Vector2(200, 0), newTargetParamController, null);
|
|
|
|
// Link the parameternode and the input in the target
|
|
m_TargetController.CreateLink(targetAnchor, parameterNode.outputPorts[0]);
|
|
|
|
if (m_SourceSlotContainer is VFXOperator)
|
|
(m_SourceSlotContainer as VFXOperator).ResyncSlots(true);
|
|
else if (m_SourceSlotContainer is VFXSubgraphBlock)
|
|
{
|
|
VFXSubgraphBlock blk = (m_SourceSlotContainer as VFXSubgraphBlock);
|
|
blk.RecreateCopy();
|
|
blk.ResyncSlots(true);
|
|
}
|
|
else if (m_SourceSlotContainer is VFXSubgraphContext)
|
|
{
|
|
VFXSubgraphContext ctx = (m_SourceSlotContainer as VFXSubgraphContext);
|
|
ctx.RecreateCopy();
|
|
ctx.ResyncSlots(true);
|
|
}
|
|
m_SourceNodeController.model.Invalidate(VFXModel.InvalidationCause.kSettingChanged); // call to resync slots
|
|
m_SourceNodeController.ApplyChanges();
|
|
//Link all the outputs to the matching input of the subgraph
|
|
foreach (var output in outputs)
|
|
{
|
|
m_SourceController.CreateLink(m_SourceNodeController.inputPorts.First(t => t.model == m_SourceSlotContainer.inputSlots.Last()), output);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TransfertOperatorOutputEdges()
|
|
{
|
|
var traversingOutEdges = new Dictionary<VFXDataAnchorController, List<VFXDataAnchorController>>();
|
|
|
|
foreach (var edge in m_SourceController.dataEdges.Where(
|
|
t =>
|
|
{
|
|
if (t.output.sourceNode is VFXParameterNodeController)
|
|
return false;
|
|
var inputInControllers = m_SourceControllersWithBlocks.Contains(t.input.sourceNode);
|
|
var outputInControllers = m_SourceControllersWithBlocks.Contains(t.output.sourceNode);
|
|
|
|
return !inputInControllers && outputInControllers;
|
|
}
|
|
))
|
|
{
|
|
List<VFXDataAnchorController> inputs = null;
|
|
if (!traversingOutEdges.TryGetValue(edge.output, out inputs))
|
|
{
|
|
inputs = new List<VFXDataAnchorController>();
|
|
traversingOutEdges[edge.output] = inputs;
|
|
}
|
|
|
|
inputs.Add(edge.input);
|
|
}
|
|
|
|
var newSourceOutputs = traversingOutEdges.Keys.ToArray();
|
|
|
|
for (int i = 0; i < newSourceOutputs.Length; ++i)
|
|
{
|
|
VFXParameter newTargetParameter = m_TargetController.AddVFXParameter(Vector2.zero, VFXLibrary.GetParameters().First(t => t.model.type == newSourceOutputs[i].portType));
|
|
|
|
m_TargetController.LightApplyChanges();
|
|
|
|
VFXParameterController newTargetParamController = m_TargetController.GetParameterController(newTargetParameter);
|
|
newTargetParamController.isOutput = true;
|
|
|
|
var inputs = traversingOutEdges[newSourceOutputs[i]];
|
|
|
|
var linkedParameter = inputs.FirstOrDefault(t => t.sourceNode is VFXParameterNodeController);
|
|
if (linkedParameter != null)
|
|
newTargetParamController.exposedName = (linkedParameter.sourceNode as VFXParameterNodeController).parentController.exposedName;
|
|
else
|
|
newTargetParamController.exposedName = newSourceOutputs[i].name;
|
|
|
|
//first the equivalent of sourceInput in the target
|
|
|
|
VFXNodeController targetNode = null;
|
|
|
|
if (newSourceOutputs[i].sourceNode is VFXBlockController)
|
|
{
|
|
var blockController = newSourceOutputs[i].sourceNode as VFXBlockController;
|
|
if (m_TargetBlocks != null)
|
|
{
|
|
targetNode = m_TargetBlocks[m_SourceBlockControllers.IndexOf(blockController)];
|
|
}
|
|
else
|
|
{
|
|
var targetContext = m_TargetControllers[m_SourceControllers.IndexOf(blockController.contextController)] as VFXContextController;
|
|
|
|
targetNode = targetContext.blockControllers[blockController.index];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
targetNode = m_TargetControllers[m_SourceControllers.IndexOf(newSourceOutputs[i].sourceNode)];
|
|
}
|
|
|
|
VFXDataAnchorController targetAnchor = targetNode.outputPorts.FirstOrDefault(t => t.path == newSourceOutputs[i].path);
|
|
|
|
if (targetAnchor != null)
|
|
{
|
|
VFXNodeController parameterNode = m_TargetController.AddVFXParameter(targetNode.position + new Vector2(400, 0), newTargetParamController, null);
|
|
|
|
// Link the parameternode and the input in the target
|
|
m_TargetController.CreateLink(parameterNode.inputPorts[0], targetAnchor);
|
|
|
|
if (m_SourceSlotContainer is VFXOperator)
|
|
(m_SourceSlotContainer as VFXOperator).ResyncSlots(true);
|
|
m_SourceNodeController.ApplyChanges();
|
|
}
|
|
//Link all the outputs to the matching input of the subgraph
|
|
foreach (var input in inputs)
|
|
{
|
|
var port = m_SourceNodeController.outputPorts.FirstOrDefault(t => t.model == m_SourceSlotContainer.outputSlots.Last());
|
|
if (port != null)
|
|
m_SourceController.CreateLink(input, port);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TransferContextsFlowEdges()
|
|
{
|
|
var initializeContexts = m_SourceControllers.OfType<VFXContextController>().Where(t => t.model.contextType == VFXContextType.Init ||
|
|
t.model.contextType == VFXContextType.Spawner ||
|
|
t.model.contextType == VFXContextType.Subgraph).ToArray();
|
|
|
|
var outputSpawners = new Dictionary<VFXContextController, List<VFXFlowAnchorController>>();
|
|
var outputEvents = new Dictionary<string, List<VFXFlowAnchorController>>();
|
|
|
|
foreach (var initializeContext in initializeContexts)
|
|
{
|
|
for (int i = 0; i < initializeContext.flowInputAnchors.Count; ++i)
|
|
if (initializeContext.flowInputAnchors[i].connections.Count() > 0)
|
|
{
|
|
var outputContext = initializeContext.flowInputAnchors[i].connections.First().output.context; //output context must be linked through is it is linked with a spawner
|
|
|
|
if (!m_SourceControllers.Contains(outputContext))
|
|
{
|
|
if (outputContext.model.contextType == VFXContextType.Spawner /*||
|
|
((outputContext.model is VFXBasicEvent) &&
|
|
(new string[] { VisualEffectAsset.PlayEventName, VisualEffectAsset.StopEventName }.Contains((outputContext.model as VFXBasicEvent).eventName) ||
|
|
sourceController.model.isSubgraph && (outputContext.model as VFXBasicEvent).eventName == VFXSubgraphContext.triggerEventName))*/)
|
|
{
|
|
List<VFXFlowAnchorController> inputs = null;
|
|
if (!outputSpawners.TryGetValue(outputContext, out inputs))
|
|
{
|
|
inputs = new List<VFXFlowAnchorController>();
|
|
outputSpawners.Add(outputContext, inputs);
|
|
}
|
|
inputs.Add(initializeContext.flowInputAnchors[i]);
|
|
}
|
|
else if (outputContext.model is VFXBasicEvent)
|
|
{
|
|
List<VFXFlowAnchorController> inputs = null;
|
|
var eventName = (outputContext.model as VFXBasicEvent).eventName;
|
|
if (!outputEvents.TryGetValue(eventName, out inputs))
|
|
{
|
|
inputs = new List<VFXFlowAnchorController>();
|
|
outputEvents.Add(eventName, inputs);
|
|
}
|
|
inputs.Add(initializeContext.flowInputAnchors[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
if (outputSpawners.Count() > 1)
|
|
{
|
|
Debug.LogWarning("More than one spawner is linked to the content if the new subgraph, some links we not be kept");
|
|
}
|
|
}
|
|
{ //link named events as if
|
|
foreach (var kv in outputEvents)
|
|
{
|
|
CreateAndLinkEvent(m_SourceControllers, m_TargetController, m_TargetControllers, kv.Value, kv.Key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void CreateAndLinkEvent(List<Controller> sourceControllers, VFXViewController targetController, List<VFXNodeController> targetControllers, List<VFXFlowAnchorController> inputs, string eventName)
|
|
{
|
|
var triggerEvent = VFXBasicEvent.CreateInstance<VFXBasicEvent>();
|
|
triggerEvent.eventName = eventName;
|
|
|
|
targetController.graph.AddChild(triggerEvent);
|
|
|
|
float xMiddle = 0;
|
|
float yMin = Mathf.Infinity;
|
|
|
|
foreach (var edge in inputs)
|
|
{
|
|
var targetContext = targetControllers[sourceControllers.IndexOf(edge.context)] as VFXContextController;
|
|
|
|
var targetInputLink = edge.slotIndex;
|
|
|
|
triggerEvent.LinkTo(targetContext.model, 0, targetInputLink);
|
|
xMiddle += targetContext.position.x;
|
|
|
|
if (targetContext.position.y < yMin)
|
|
yMin = targetContext.position.y;
|
|
}
|
|
|
|
triggerEvent.position = new Vector2(xMiddle / inputs.Count, yMin) - new Vector2(0, 200); // place the event above the top center of the linked contexts.
|
|
}
|
|
}
|
|
}
|