2021-09-09 20:42:29 -04:00

418 lines
18 KiB
C#

using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor.VFX;
using UnityEngine.UIElements;
using System.Reflection;
using NodeID = System.UInt32;
namespace UnityEditor.VFX.UI
{
class VFXCopy : VFXCopyPasteCommon
{
VFXContextController[] contexts;
int[] contextsIndices;
VFXOperatorController[] operators;
int[] operatorIndices;
VFXParameterNodeController[] parameters;
int[] parameterIndices;
VFXData[] datas;
Dictionary<VFXNodeController, uint> modelIndices = new Dictionary<VFXNodeController, NodeID>();
static VFXCopy s_Instance;
public static object Copy(IEnumerable<Controller> elements, Rect bounds)
{
if (s_Instance == null)
s_Instance = new VFXCopy();
return s_Instance.CreateCopy(elements, bounds);
}
public static string SerializeElements(IEnumerable<Controller> elements, Rect bounds)
{
if (s_Instance == null)
s_Instance = new VFXCopy();
var serializableGraph = s_Instance.CreateCopy(elements, bounds) as SerializableGraph;
return JsonUtility.ToJson(serializableGraph);
}
public static object CopyBlocks(IEnumerable<VFXBlockController> blocks)
{
if (s_Instance == null)
s_Instance = new VFXCopy();
return s_Instance.DoCopyBlocks(blocks);
}
object CreateCopy(IEnumerable<Controller> elements, Rect bounds)
{
IEnumerable<VFXContextController> contexts = elements.OfType<VFXContextController>();
IEnumerable<VFXNodeController> nodes = elements.Where(t => t is VFXOperatorController || t is VFXParameterNodeController).Cast<VFXNodeController>();
IEnumerable<VFXBlockController> blocks = elements.OfType<VFXBlockController>();
SerializableGraph serializableGraph = new SerializableGraph();
serializableGraph.controllerCount = elements.Count();
if (contexts.Count() == 0 && nodes.Count() == 0 && blocks.Count() > 0)
{
var copiedBlocks = new List<VFXBlockController>(blocks);
modelIndices.Clear();
serializableGraph.operators = CopyBlocks(copiedBlocks, 0);
serializableGraph.blocksOnly = true;
}
else
{
//Don't copy VFXBlockSubgraphContext because they can't be pasted anywhere.
CopyNodes(serializableGraph, elements, contexts.Where(t => !(t.model is VFXBlockSubgraphContext)), nodes, bounds);
}
return serializableGraph;
}
void CopyNodes(SerializableGraph serializableGraph, IEnumerable<Controller> elements, IEnumerable<VFXContextController> copiedContexts, IEnumerable<VFXNodeController> nodes, Rect bounds)
{
Controller[] copiedElements = elements.ToArray();
serializableGraph.bounds = bounds;
IEnumerable<VFXNodeController> dataEdgeTargets = nodes.Concat(copiedContexts.Cast<VFXNodeController>()).Concat(copiedContexts.SelectMany(t => t.blockControllers).Cast<VFXNodeController>()).ToArray();
// consider only edges contained in the selection
IEnumerable<VFXDataEdgeController> dataEdges = elements.OfType<VFXDataEdgeController>().Where(t =>
dataEdgeTargets.Contains((t.input as VFXDataAnchorController).sourceNode as VFXNodeController) &&
dataEdgeTargets.Contains((t.output as VFXDataAnchorController).sourceNode as VFXNodeController)).ToArray();
IEnumerable<VFXFlowEdgeController> flowEdges = elements.OfType<VFXFlowEdgeController>().Where(t =>
copiedContexts.Contains((t.input as VFXFlowAnchorController).context) &&
copiedContexts.Contains((t.output as VFXFlowAnchorController).context)
).ToArray();
modelIndices.Clear();
contexts = copiedContexts.ToArray();
operators = nodes.OfType<VFXOperatorController>().ToArray();
parameters = nodes.OfType<VFXParameterNodeController>().ToArray();
contextsIndices = contexts.Select(t => Array.IndexOf(copiedElements, t)).ToArray();
operatorIndices = operators.Select(t => Array.IndexOf(copiedElements, t)).ToArray();
parameterIndices = parameters.Select(t => Array.IndexOf(copiedElements, t)).ToArray();
datas = contexts.Select(t => t.model.GetData()).Where(t => t != null).ToArray();
CopyOperatorsAndContexts(ref serializableGraph);
CopyParameters(ref serializableGraph);
CopyGroupNodesAndStickyNotes(ref serializableGraph, elements);
CopyDatas(ref serializableGraph);
CopyDataEdge(ref serializableGraph, dataEdges);
CopyFlowEdges(ref serializableGraph, flowEdges);
}
void CopyGroupNodesAndStickyNotes(ref SerializableGraph serializableGraph, IEnumerable<Controller> elements)
{
VFXGroupNodeController[] groupNodes = elements.OfType<VFXGroupNodeController>().ToArray();
VFXStickyNoteController[] stickyNotes = elements.OfType<VFXStickyNoteController>().ToArray();
if (groupNodes.Length > 0 || stickyNotes.Length > 0)
{
var stickyNodeIndexToCopiedIndex = new Dictionary<int, int>();
if (stickyNotes.Length > 0)
{
serializableGraph.stickyNotes = new VFXUI.StickyNoteInfo[stickyNotes.Length];
for (int i = 0; i < stickyNotes.Length; ++i)
{
VFXStickyNoteController stickyNote = stickyNotes[i];
stickyNodeIndexToCopiedIndex[stickyNote.index] = i;
VFXUI.StickyNoteInfo info = stickyNote.model.stickyNoteInfos[stickyNote.index];
serializableGraph.stickyNotes[i] = new VFXUI.StickyNoteInfo(info);
}
}
if (groupNodes.Length > 0)
{
serializableGraph.groupNodes = new GroupNode[groupNodes.Length];
for (int i = 0; i < groupNodes.Length; ++i)
{
VFXGroupNodeController groupNode = groupNodes[i];
VFXUI.GroupInfo info = groupNode.model.groupInfos[groupNode.index];
serializableGraph.groupNodes[i] = new GroupNode { infos = new VFXUI.UIInfo(info)};
// only keep nodes and sticky notes that are copied because a element can not be in two groups at the same time.
if (info.contents != null)
{
var nodeIndices = groupNode.nodes.OfType<VFXNodeController>().Where(t => contexts.Contains(t) || operators.Contains(t) || parameters.Contains(t)).Select(t => modelIndices[t]);
var stickNoteIndices = info.contents.Where(t => t.isStickyNote && stickyNodeIndexToCopiedIndex.ContainsKey(t.id)).Select(t => (uint)stickyNodeIndexToCopiedIndex[t.id]);
serializableGraph.groupNodes[i].contents = nodeIndices.Concat(stickNoteIndices).ToArray();
serializableGraph.groupNodes[i].stickNodeCount = stickNoteIndices.Count();
}
}
}
}
}
void CopyDataEdge(ref SerializableGraph serializableGraph, IEnumerable<VFXDataEdgeController> dataEdges)
{
serializableGraph.dataEdges = new DataEdge[dataEdges.Count()];
int cpt = 0;
foreach (var edge in dataEdges)
{
DataEdge copyPasteEdge = new DataEdge();
var inputController = edge.input as VFXDataAnchorController;
var outputController = edge.output as VFXDataAnchorController;
copyPasteEdge.input.slotPath = MakeSlotPath(inputController.model, true);
copyPasteEdge.input.targetIndex = modelIndices[inputController.sourceNode];
copyPasteEdge.output.slotPath = MakeSlotPath(outputController.model, false);
copyPasteEdge.output.targetIndex = modelIndices[outputController.sourceNode];
serializableGraph.dataEdges[cpt++] = copyPasteEdge;
}
}
void CopyFlowEdges(ref SerializableGraph serializableGraph, IEnumerable<VFXFlowEdgeController> flowEdges)
{
serializableGraph.flowEdges = new FlowEdge[flowEdges.Count()];
int cpt = 0;
foreach (var edge in flowEdges)
{
FlowEdge copyPasteEdge = new FlowEdge();
var inputController = edge.input as VFXFlowAnchorController;
var outputController = edge.output as VFXFlowAnchorController;
copyPasteEdge.input.contextIndex = modelIndices[inputController.context];
copyPasteEdge.input.flowIndex = inputController.slotIndex;
copyPasteEdge.output.contextIndex = modelIndices[outputController.context];
copyPasteEdge.output.flowIndex = outputController.slotIndex;
serializableGraph.flowEdges[cpt++] = copyPasteEdge;
}
}
void CopyDatas(ref SerializableGraph serializableGraph)
{
serializableGraph.datas = new Data[datas.Length];
for (int i = 0; i < datas.Length; ++i)
{
CopyModelSettings(ref serializableGraph.datas[i].settings, datas[i]);
}
}
void CopyModelSettings(ref Property[] properties, VFXModel model)
{
// Copy all fields that are either VFXSettings or serialized by unity
Type type = model.GetType();
var fields = GetFields(type);
properties = new Property[fields.Count()];
for (int i = 0; i < properties.Length; ++i)
{
properties[i].name = fields[i].Name;
properties[i].value = new VFXSerializableObject(fields[i].FieldType, fields[i].GetValue(model));
}
}
ParameterNode CopyParameterNode(int parameterIndex, int nodeIndex, VFXParameterNodeController controller, int indexInClipboard)
{
ParameterNode n = new ParameterNode();
n.position = controller.position;
n.collapsed = controller.superCollapsed;
n.expandedOutput = controller.infos.expandedSlots.Select(t => t.path).ToArray();
n.indexInClipboard = indexInClipboard;
if (parameterIndex < (1 << 18) && nodeIndex < (1 << 11))
modelIndices[controller] = GetParameterNodeID((uint)parameterIndex, (uint)nodeIndex);
else
modelIndices[controller] = InvalidID;
return n;
}
void CopyParameters(ref SerializableGraph serializableGraph)
{
int cpt = 0;
serializableGraph.parameters = parameters.GroupBy(t => t.parentController, t => t, (p, c) =>
{
++cpt;
return new Parameter()
{
originalInstanceID = p.model.GetInstanceID(),
name = p.model.exposedName,
value = new VFXSerializableObject(p.model.type, p.model.value),
exposed = p.model.exposed,
isOutput = p.model.isOutput,
valueFilter = p.valueFilter,
min = p.valueFilter == VFXValueFilter.Range ? new VFXSerializableObject(p.model.type, p.model.min) : null,
max = p.valueFilter == VFXValueFilter.Range ? new VFXSerializableObject(p.model.type, p.model.max) : null,
enumValue = p.valueFilter == VFXValueFilter.Enum ? p.model.enumValues.ToArray() : null,
tooltip = p.model.tooltip,
nodes = c.Select((u, i) => CopyParameterNode(cpt - 1, i, u, parameterIndices[Array.IndexOf(parameters, u)])).ToArray()
};
}
).ToArray();
}
void CopyOperatorsAndContexts(ref SerializableGraph serializableGraph)
{
serializableGraph.contexts = new Context[contexts.Length];
for (int i = 0; i < contexts.Length; ++i)
{
NodeID id = CopyContext(ref serializableGraph.contexts[i], contexts[i], i);
modelIndices[contexts[i]] = id;
}
serializableGraph.operators = new Node[operators.Length];
for (int i = 0; i < operators.Length; ++i)
{
uint id = CopyNode(ref serializableGraph.operators[i], operators[i].model, (NodeID)i);
serializableGraph.operators[i].indexInClipboard = operatorIndices[i];
modelIndices[operators[i]] = id;
}
}
NodeID CopyNode(ref Node node, VFXModel model, uint index)
{
// Copy node infos
node.position = model.position;
node.type = model.GetType();
node.flags = 0;
if (model.collapsed)
node.flags = Node.Flags.Collapsed;
if (model.superCollapsed)
node.flags = Node.Flags.SuperCollapsed;
uint id = 0;
if (model is VFXOperator)
{
id = OperatorFlag;
}
else if (model is VFXContext)
{
id = ContextFlag;
}
id |= (uint)index;
//Copy settings value
CopyModelSettings(ref node.settings, model);
var inputSlots = (model as IVFXSlotContainer).inputSlots;
node.inputSlots = new Property[inputSlots.Count];
for (int i = 0; i < inputSlots.Count; i++)
{
node.inputSlots[i].name = inputSlots[i].name;
if (inputSlots[i].spaceable)
node.inputSlots[i].space = inputSlots[i].space;
node.inputSlots[i].value = new VFXSerializableObject(inputSlots[i].property.type, inputSlots[i].value);
}
node.expandedInputs = AllSlots(inputSlots).Where(t => !t.collapsed).Select(t => t.path).ToArray();
node.expandedOutputs = AllSlots((model as IVFXSlotContainer).outputSlots).Where(t => !t.collapsed).Select(t => t.path).ToArray();
return id;
}
SerializableGraph DoCopyBlocks(IEnumerable<VFXBlockController> blocks)
{
var newBlocks = new Node[blocks.Count()];
uint cpt = 0;
foreach (var block in blocks)
{
CopyNode(ref newBlocks[(int)cpt], block.model, cpt);
++cpt;
}
var graph = new SerializableGraph();
graph.blocksOnly = true;
graph.operators = newBlocks;
return graph;
}
Node[] CopyBlocks(IList<VFXBlockController> blocks, int contextIndex)
{
var newBlocks = new Node[blocks.Count];
for (uint i = 0; i < newBlocks.Length; ++i)
{
CopyNode(ref newBlocks[(int)i], blocks[(int)i].model, i);
if (blocks[(int)i].model.enabled)
{
newBlocks[i].flags |= Node.Flags.Enabled;
}
if (contextIndex < (1 << 18) && i < (1 << 11))
modelIndices[blocks[(int)i]] = BlockFlag | (i << 18) | (uint)contextIndex;
else
modelIndices[blocks[(int)i]] = InvalidID;
}
return newBlocks;
}
NodeID CopyContext(ref Context context, VFXContextController controller, int index)
{
NodeID id = CopyNode(ref context.node, controller.model, (NodeID)index);
var blocks = controller.blockControllers;
context.label = controller.model.label;
context.systemName = VFXSystemNames.GetSystemName(controller.model);
if (controller.model.GetData() != null)
context.dataIndex = Array.IndexOf(datas, controller.model.GetData());
else
context.dataIndex = -1;
context.blocks = CopyBlocks(controller.blockControllers, index);
context.node.indexInClipboard = contextsIndices[index];
if (controller.model is VFXAbstractRenderedOutput)
context.subOutputs = CopySubOutputs(((VFXAbstractRenderedOutput)controller.model).GetSubOutputs());
else
context.subOutputs = null;
return id;
}
SubOutput[] CopySubOutputs(List<VFXSRPSubOutput> subOutputs)
{
var newSubOutputs = new SubOutput[subOutputs.Count];
for (int i = 0; i < newSubOutputs.Length; ++i)
{
if (subOutputs[i] != null) // Can be null if associated SRP is unknown
{
newSubOutputs[i].type = subOutputs[i].GetType();
CopyModelSettings(ref newSubOutputs[i].settings, subOutputs[i]);
}
}
return newSubOutputs;
}
static int[] MakeSlotPath(VFXSlot slot, bool input)
{
List<int> slotPath = new List<int>(slot.depth + 1);
while (slot.GetParent() != null)
{
slotPath.Add(slot.GetParent().GetIndex(slot));
slot = slot.GetParent();
}
slotPath.Add((input ? (slot.owner as IVFXSlotContainer).inputSlots : (slot.owner as IVFXSlotContainer).outputSlots).IndexOf(slot));
return slotPath.ToArray();
}
}
}