void PasteBlocks(VFXView view, ref SerializableGraph serializableGraph) { var selectedContexts = view.selection.OfType <VFXContextUI>(); var selectedBlocks = view.selection.OfType <VFXBlockUI>(); VFXBlockUI targetBlock = null; VFXContextUI targetContext = null; if (selectedBlocks.Count() > 0) { targetBlock = selectedBlocks.OrderByDescending(t => t.context.controller.model.GetIndex(t.controller.model)).First(); targetContext = targetBlock.context; } else if (selectedContexts.Count() == 1) { targetContext = selectedContexts.First(); } else { Debug.LogError(m_BlockPasteError.text); return; } VFXContext targetModelContext = targetContext.controller.model; int targetIndex = -1; if (targetBlock != null) { targetIndex = targetModelContext.GetIndex(targetBlock.controller.model) + 1; } targetIndex = PasteBlocks(view.controller, serializableGraph.operators, targetModelContext, targetIndex); targetModelContext.Invalidate(VFXModel.InvalidationCause.kStructureChanged); if (view != null) { view.ClearSelection(); foreach (var uiBlock in targetContext.Query().OfType <VFXBlockUI>().Where(t => m_NodesInTheSameOrder.Any(u => u.model == t.controller.model)).ToList()) { view.AddToSelection(uiBlock); } } }
bool ProviderFilter(VFXNodeProvider.Descriptor d) { if (!(d.modelDescriptor is VFXModelDescriptor <VFXContext>)) { return(false); } var desc = d.modelDescriptor as VFXModelDescriptor <VFXContext>; if (direction == Direction.Input) { return(VFXContext.CanLink(desc.model, controller.context.model)); } else { return(VFXContext.CanLink(controller.context.model, desc.model)); } }
// A key difference between Material Shader and VFX Shader generation is how surface properties are provided. Material Shaders // simply provide properties via UnityPerMaterial cbuffer. VFX expects these same properties to be computed in the vertex // stage (because we must evaluate them with the VFX blocks), and packed with the interpolators for the fragment stage. static StructDescriptor AppendVFXInterpolator(StructDescriptor interpolator, VFXContext context, VFXContextCompiledData contextData) { var fields = interpolator.fields.ToList(); var expressionToName = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null)); expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value); var mainParameters = contextData.gpuMapper.CollectExpression(-1).ToArray(); // Warning/TODO: FragmentParameters are created from the ShaderGraphVfxAsset. // We may ultimately need to move this handling of VFX Interpolators + SurfaceDescriptionFunction function signature directly into the SG Generator (since it knows about the exposed properties). foreach (string fragmentParameter in context.fragmentParameters) { var filteredNamedExpression = mainParameters.FirstOrDefault(o => fragmentParameter == o.name && !(expressionToName.ContainsKey(o.exp) && expressionToName[o.exp] == o.name)); // if parameter already in the global scope, there's nothing to do if (filteredNamedExpression.exp != null) { var type = VFXExpression.TypeToType(filteredNamedExpression.exp.valueType); if (!kVFXShaderValueTypeMap.TryGetValue(type, out var shaderValueType)) { continue; } // TODO: NoInterpolation only for non-strips. fields.Add(new FieldDescriptor(HDStructFields.VaryingsMeshToPS.name, filteredNamedExpression.name, "", shaderValueType, subscriptOptions: StructFieldOptions.Static, interpolation: "nointerpolation")); } } // VFX Object Space Interpolators fields.Add(HDStructFields.VaryingsMeshToPS.worldToElement0); fields.Add(HDStructFields.VaryingsMeshToPS.worldToElement1); fields.Add(HDStructFields.VaryingsMeshToPS.worldToElement2); fields.Add(HDStructFields.VaryingsMeshToPS.elementToWorld0); fields.Add(HDStructFields.VaryingsMeshToPS.elementToWorld1); fields.Add(HDStructFields.VaryingsMeshToPS.elementToWorld2); interpolator.fields = fields.ToArray(); return(interpolator); }
public void Link_Mixing_Spawn_And_GPUEvent_To_Init() { var fromA = ScriptableObject.CreateInstance <VFXBasicGPUEvent>(); var fromB = ScriptableObject.CreateInstance <VFXBasicSpawner>(); var to = ScriptableObject.CreateInstance <VFXBasicInitialize>(); Assert.IsTrue(VFXContext.CanLink(fromA, to)); Assert.IsTrue(VFXContext.CanLink(fromB, to)); to.LinkFrom(fromB); Assert.AreEqual(1u, to.inputFlowSlot[0].link.Count()); Assert.IsTrue(VFXContext.CanLink(fromA, to)); //Expected disconnection of previous link in that case to.LinkFrom(fromA); Assert.IsTrue(VFXContext.CanLink(fromB, to)); Assert.AreEqual(1u, to.inputFlowSlot[0].link.Count()); Assert.AreEqual(fromA, to.inputFlowSlot[0].link.First().context); }
public VFXContextController(VFXContext model, VFXViewController viewController) : base(model, viewController) { UnregisterAnchors(); SyncControllers(); if (model is VFXSubgraphContext) { // Prevent breaking the editor opening. try { SyncFlowAnchors(); model.ResyncSlots(true); } catch (Exception e) { Debug.LogException(e); } } }
void ValidateSlotContainer(IVFXSlotContainer slotContainer, VFXModel expectedParent) { if (!ValidateVFXModel(slotContainer as VFXModel, expectedParent)) { return; } ValidateSlots(slotContainer.inputSlots, slotContainer); ValidateSlots(slotContainer.outputSlots, slotContainer); if (slotContainer is VFXContext) { VFXContext context = slotContainer as VFXContext; foreach (var block in context.children) { ValidateSlotContainer(block, context); } } }
IEnumerable <string> RecurseGetEventNames(VFXContext context) { switch (context) { case VFXBasicEvent basicEvent when !IsDefaultEvent(name): yield return(basicEvent.eventName); break; case VFXSubgraphContext subgraphContext when subgraphContext.subChildren != null: { foreach (var eventName in subgraphContext.subChildren.OfType <VFXContext>().SelectMany(RecurseGetEventNames)) { yield return(eventName); } break; } } }
public static string GetSizeVector(VFXContext context, int nbComponents = 3) { var data = context.GetData(); string sizeX = data.IsCurrentAttributeRead(VFXAttribute.SizeX, context) ? "sizeX" : VFXAttribute.kDefaultSize.ToString(); string sizeY = nbComponents >= 2 && data.IsCurrentAttributeRead(VFXAttribute.SizeY, context) ? "sizeY" : "sizeX"; string sizeZ = nbComponents >= 3 && data.IsCurrentAttributeRead(VFXAttribute.SizeZ, context) ? "sizeZ" : string.Format("min({0},{1})", sizeX, sizeY); switch (nbComponents) { case 1: return(sizeX); case 2: return(string.Format("float2({0},{1})", sizeX, sizeY)); case 3: return(string.Format("float3({0},{1},{2})", sizeX, sizeY, sizeZ)); default: throw new ArgumentException("NbComponents must be between 1 and 3"); } }
IEnumerable <String> RecurseGetEventNames(VFXContext context) { if (context is VFXBasicEvent) { if (!IsDefaultEvent(name)) { yield return((context as VFXBasicEvent).eventName); } } else if (context is VFXSubgraphContext) { foreach (var subContext in (context as VFXSubgraphContext).subChildren.OfType <VFXContext>()) { foreach (var name in RecurseGetEventNames(subContext)) { yield return(name); } } } }
private int PasteBlocks(VFXViewController viewController, Node[] blocks, VFXContext targetModelContext, int targetIndex, List <VFXBlockController> blocksInTheSameOrder = null) { newControllers.Clear(); m_NodesInTheSameOrder = new VFXNodeID[blocks.Length]; int cpt = 0; foreach (var block in blocks) { Node blk = block; VFXBlock newBlock = PasteAndInitializeNode <VFXBlock>(viewController, Vector2.zero, Rect.zero, ref blk); newBlock.enabled = (blk.flags & Node.Flags.Enabled) == Node.Flags.Enabled; if (targetModelContext.AcceptChild(newBlock, targetIndex)) { m_NodesInTheSameOrder[cpt] = new VFXNodeID(newBlock, 0); targetModelContext.AddChild(newBlock, targetIndex, false); // only notify once after all blocks have been added targetIndex++; } ++cpt; } targetModelContext.Invalidate(VFXModel.InvalidationCause.kStructureChanged); var targetContextController = viewController.GetRootNodeController(targetModelContext, 0) as VFXContextController; targetContextController.ApplyChanges(); if (blocksInTheSameOrder != null) { blocksInTheSameOrder.Clear(); for (int i = 0; i < m_NodesInTheSameOrder.Length; ++i) { blocksInTheSameOrder.Add(m_NodesInTheSameOrder[i].model != null ? targetContextController.blockControllers.First(t => t.model == m_NodesInTheSameOrder[i].model as VFXBlock) : null); } } return(targetIndex); }
public static StringBuilder ModifyShader(VFXContext context, StringBuilder source) { if (context.activeChildrenWithImplicit.Any((b) => typeof(FluvioFXBlock).IsAssignableFrom(b.GetType()))) { source // .Replace(rwSolverData, solverData) // .Replace(rwSampler, sampler) // .Replace(solverData, rwSolverData) // .Replace(samplerState, "") // .Replace(sampler, rwSampler) // .Replace(getSampler, rwGetSampler) .Replace(arg1, indexArg1) .Replace(arg2, indexArg2) .Replace(arg3, indexArg3) .Replace(call1, indexCall1) .Replace(call2, indexCall2) .Replace(call3, indexCall3); } return(source); }
public void Subgraph_Event_Link_To_Spawn() { VFXViewWindow window = VFXViewWindow.GetWindow <VFXViewWindow>(); window.LoadAsset(AssetDatabase.LoadAssetAtPath <VisualEffectAsset>(testAssetName), null); //Create Spawner in subgraph { var spawner = ScriptableObject.CreateInstance <VFXBasicSpawner>(); m_ViewController.graph.AddChild(spawner); m_ViewController.LightApplyChanges(); var controller = window.graphView.Query <VFXContextUI>().ToList().Select(t => t.controller).Cast <Controller>(); Assert.AreEqual(1, controller.Count()); VFXConvertSubgraph.ConvertToSubgraphContext(window.graphView, controller, Rect.zero, testSubgraphSubAssetName); } var subGraphController = m_ViewController.allChildren.OfType <VFXContextController>().FirstOrDefault(o => o.model is VFXSubgraphContext); Assert.IsNotNull(subGraphController); //Create Event Context & Link the two input flow var subGraphContext = subGraphController.model; var eventContext = ScriptableObject.CreateInstance <VFXBasicEvent>(); Assert.IsTrue(VFXContext.CanLink(eventContext, subGraphContext, 0, 0)); Assert.IsTrue(VFXContext.CanLink(eventContext, subGraphContext, 0, 1)); eventContext.LinkTo(subGraphContext, 0, 0); eventContext.LinkTo(subGraphContext, 0, 1); var flow = eventContext.outputFlowSlot.First().link; Assert.AreEqual(2, flow.Count()); Assert.IsTrue(flow.All(o => o.context == subGraphContext)); Assert.IsTrue(flow.Any(o => o.slotIndex == 0)); Assert.IsTrue(flow.Any(o => o.slotIndex == 1)); window.graphView.controller = null; }
VFXContext PasteContext(VFXViewController controller, ref Context context) { VFXContext newContext = PasteAndInitializeNode <VFXContext>(controller, ref context.node); if (newContext == null) { newContexts.Add(new KeyValuePair <VFXContext, List <VFXBlock> >(null, null)); return(null); } newContext.label = context.label; VFXSystemNames.SetSystemName(newContext, context.systemName); if (newContext is VFXAbstractRenderedOutput) { PasteSubOutputs((VFXAbstractRenderedOutput)newContext, ref context); } List <VFXBlock> blocks = new List <VFXBlock>(); foreach (var block in context.blocks) { var blk = block; VFXBlock newBlock = PasteAndInitializeNode <VFXBlock>(null, ref blk); newBlock.enabled = (blk.flags & Node.Flags.Enabled) == Node.Flags.Enabled; blocks.Add(newBlock); if (newBlock != null) { newContext.AddChild(newBlock); } } newContexts.Add(new KeyValuePair <VFXContext, List <VFXBlock> >(newContext, blocks)); return(newContext); }
/* fixformat ignore:end */ public static StringBuilder ModifyShader(VFXContext context, StringBuilder source) { var blockFunctionNames = typeof(FluvioFXBlock) .Assembly .GetTypes() .Where(t => (typeof(FluvioFXBlock).IsAssignableFrom(t) || typeof(ICollisionSettings).IsAssignableFrom(t)) && !t.IsAbstract && !t.IsInterface) .Select(t => t.Name); var shouldModify = context.activeChildrenWithImplicit.Any((b) => typeof(FluvioFXBlock).IsAssignableFrom(b.GetType()) || typeof(ICollisionSettings).IsAssignableFrom(b.GetType())); if (shouldModify) { var data = context.GetData() as VFXDataParticle; var defines = FormattableString.Invariant($@" // FluvioFX simulation constants #define FLUVIO_EPSILON {FluvioFXSettings.kEpsilon} #define FLUVIO_MAX_SQR_VELOCITY_CHANGE {FluvioFXSettings.kMaxSqrVelocityChange} #define FLUVIO_MAX_BUCKET_COUNT {FluvioFXSettings.kMaxBucketCount} #define FLUVIO_MAX_NEIGHBOR_COUNT {FluvioFXSettings.kMaxNeighborCount} #define FLUVIO_AUTO_PARTICLE_SIZE_FACTOR {FluvioFXSettings.kAutoParticleSizeFactor} #define FLUVIO_MAX_GRID_SIZE {(uint)Mathf.Pow(data?.capacity ?? 262144, 1.0f / 3.0f)} "); var result = defines + source.ToString().Replace("\r\n", "\n"); // Special case: replacement var replacementBlock = context .activeChildrenWithImplicit .FirstOrDefault((b) => !string.IsNullOrWhiteSpace((b as FluvioFXBlock)?.replacementKernel)) as FluvioFXBlock; if (replacementBlock != null) { result = result.Substring(0, result.LastIndexOf("[numthreads(NB_THREADS_PER_GROUP,1,1)]")); result += $@"[numthreads(NB_THREADS_PER_GROUP,1,1)] void CSMain(uint3 groupId : SV_GroupID, uint3 groupThreadId : SV_GroupThreadID, uint3 dispatchThreadId : SV_DispatchThreadID) {{ {replacementBlock.replacementKernel} }}"; } foreach (var kvp in globalReplacements) { var pattern = kvp.Key; var replacement = kvp.Value; result = Regex.Replace(result, pattern, replacement); } foreach (var functionName in blockFunctionNames) { foreach (var kvp in replacementFormat) { var pattern = string.Format(kvp.Key, functionName); var replacement = string.Format(kvp.Value, functionName); result = Regex.Replace(result, pattern, replacement); } } return(new StringBuilder(result)); } return(source); }
static void PasteNodes(VFXViewController viewController, Vector2 center, Data copyData, ScriptableObject[] allSerializedObjects, VFXView view, VFXGroupNodeController groupNode) { var graph = viewController.graph; Vector2 pasteOffset = (copyData.bounds.width > 0 && copyData.bounds.height > 0) ? center - copyData.bounds.center : Vector2.zero; // look if pasting there will result in the first element beeing exactly on top of other while (true) { bool foundSamePosition = false; if (copyData.contexts != null && copyData.contexts.Length > 0) { VFXContext firstContext = copyData.contexts[0]; foreach (var existingContext in viewController.graph.children.OfType <VFXContext>()) { if ((firstContext.position + pasteOffset - existingContext.position).sqrMagnitude < 1) { foundSamePosition = true; break; } } } else if (copyData.slotContainers != null && copyData.slotContainers.Length > 0) { VFXModel firstContainer = copyData.slotContainers[0]; foreach (var existingSlotContainer in viewController.graph.children.Where(t => t is IVFXSlotContainer)) { if ((firstContainer.position + pasteOffset - existingSlotContainer.position).sqrMagnitude < 1) { foundSamePosition = true; break; } } } else { VFXUI ui = allSerializedObjects.OfType <VFXUI>().First(); if (ui != null) { if (ui.stickyNoteInfos != null && ui.stickyNoteInfos.Length > 0) { foreach (var stickyNote in viewController.stickyNotes) { if ((ui.stickyNoteInfos[0].position.position + pasteOffset - stickyNote.position.position).sqrMagnitude < 1) { foundSamePosition = true; break; } } } else if (ui.groupInfos != null && ui.groupInfos.Length > 0) { foreach (var gn in viewController.groupNodes) { if ((ui.groupInfos[0].position.position + pasteOffset - gn.position.position).sqrMagnitude < 1) { foundSamePosition = true; break; } } } } } if (foundSamePosition) { pasteOffset += Vector2.one * 30; } else { break; } } if (copyData.contexts != null) { foreach (var slotContainer in copyData.contexts) { var newContext = slotContainer; newContext.position += pasteOffset; ClearLinks(newContext); } } if (copyData.slotContainers != null) { foreach (var slotContainer in copyData.slotContainers) { var newSlotContainer = slotContainer; newSlotContainer.position += pasteOffset; ClearLinks(newSlotContainer as IVFXSlotContainer); } } for (int i = 0; i < allSerializedObjects.Length; ++i) { ScriptableObject obj = allSerializedObjects[i]; if (obj is VFXContext || obj is VFXOperator) { graph.AddChild(obj as VFXModel); } else if (obj is VFXParameter) { int paramIndex = System.Array.FindIndex(copyData.parameters, t => t.index == i); VFXParameter existingParameter = graph.children.OfType <VFXParameter>().FirstOrDefault(t => t.GetInstanceID() == copyData.parameters[paramIndex].originalInstanceID); if (existingParameter != null) { // The original parameter is from the current graph, add the nodes to the original copyData.parameters[paramIndex].parameter = existingParameter; copyData.parameters[paramIndex].copiedParameter = obj as VFXParameter; copyData.parameters[paramIndex].infoIndexOffset = existingParameter.nodes.Count; foreach (var info in copyData.parameters[paramIndex].infos) { info.position += pasteOffset; } var oldIDs = copyData.parameters[paramIndex].infos.ToDictionary(t => t, t => t.id); existingParameter.AddNodeRange(copyData.parameters[paramIndex].infos); //keep track of new ids for groupnodes copyData.parameters[paramIndex].idMap = copyData.parameters[paramIndex].infos.ToDictionary(t => oldIDs[t], t => t.id); } else { // The original parameter is from another graph : create the parameter in the other graph, but replace the infos with only the ones copied. copyData.parameters[paramIndex].parameter = obj as VFXParameter; copyData.parameters[paramIndex].parameter.SetNodes(copyData.parameters[paramIndex].infos); graph.AddChild(obj as VFXModel); } } } VFXUI copiedUI = allSerializedObjects.OfType <VFXUI>().FirstOrDefault(); int firstCopiedGroup = -1; int firstCopiedStickyNote = -1; if (copiedUI != null) { VFXUI ui = viewController.graph.UIInfos; firstCopiedStickyNote = ui.stickyNoteInfos != null ? ui.stickyNoteInfos.Length : 0; if (copiedUI.groupInfos != null && copiedUI.groupInfos.Length > 0) { if (ui.groupInfos == null) { ui.groupInfos = new VFXUI.GroupInfo[0]; } firstCopiedGroup = ui.groupInfos.Length; foreach (var groupInfos in copiedUI.groupInfos) { for (int i = 0; i < groupInfos.contents.Length; ++i) { // if we link the parameter node to an existing parameter instead of the copied parameter we have to patch the groupnode content to point the that parameter with the correct id. if (groupInfos.contents[i].model is VFXParameter) { VFXParameter parameter = groupInfos.contents[i].model as VFXParameter; var paramInfo = copyData.parameters.FirstOrDefault(t => t.copiedParameter == parameter); if (paramInfo.parameter != null) // parameter will not be null unless the struct returned is the default. { groupInfos.contents[i].model = paramInfo.parameter; groupInfos.contents[i].id = paramInfo.idMap[groupInfos.contents[i].id]; } } else if (groupInfos.contents[i].isStickyNote) { groupInfos.contents[i].id += firstCopiedStickyNote; } } } ui.groupInfos = ui.groupInfos.Concat(copiedUI.groupInfos.Select(t => new VFXUI.GroupInfo(t) { position = new Rect(t.position.position + pasteOffset, t.position.size) })).ToArray(); } if (copiedUI.stickyNoteInfos != null && copiedUI.stickyNoteInfos.Length > 0) { if (ui.stickyNoteInfos == null) { ui.stickyNoteInfos = new VFXUI.StickyNoteInfo[0]; } ui.stickyNoteInfos = ui.stickyNoteInfos.Concat(copiedUI.stickyNoteInfos.Select(t => new VFXUI.StickyNoteInfo(t) { position = new Rect(t.position.position + pasteOffset, t.position.size) })).ToArray(); } } CopyDataEdges(copyData, allSerializedObjects); if (copyData.flowEdges != null) { foreach (var flowEdge in copyData.flowEdges) { VFXContext inputContext = allSerializedObjects[flowEdge.input.contextIndex] as VFXContext; VFXContext outputContext = allSerializedObjects[flowEdge.output.contextIndex] as VFXContext; inputContext.LinkFrom(outputContext, flowEdge.input.flowIndex, flowEdge.output.flowIndex); } } foreach (var dataAndContexts in copyData.dataAndContexts) { VFXData data = allSerializedObjects[dataAndContexts.dataIndex] as VFXData; foreach (var contextIndex in dataAndContexts.contextsIndexes) { VFXContext context = allSerializedObjects[contextIndex] as VFXContext; data.CopySettings(context.GetData()); } } // Create all ui based on model viewController.LightApplyChanges(); if (view != null) { view.ClearSelection(); var elements = view.graphElements.ToList(); List <VFXNodeUI> newSlotContainerUIs = new List <VFXNodeUI>(); List <VFXContextUI> newContextUIs = new List <VFXContextUI>(); foreach (var slotContainer in allSerializedObjects.OfType <VFXContext>()) { VFXContextUI contextUI = elements.OfType <VFXContextUI>().FirstOrDefault(t => t.controller.model == slotContainer); if (contextUI != null) { newSlotContainerUIs.Add(contextUI); newSlotContainerUIs.AddRange(contextUI.GetAllBlocks().Cast <VFXNodeUI>()); newContextUIs.Add(contextUI); view.AddToSelection(contextUI); } } foreach (var slotContainer in allSerializedObjects.OfType <VFXOperator>()) { VFXOperatorUI slotContainerUI = elements.OfType <VFXOperatorUI>().FirstOrDefault(t => t.controller.model == slotContainer); if (slotContainerUI != null) { newSlotContainerUIs.Add(slotContainerUI); view.AddToSelection(slotContainerUI); } } foreach (var param in copyData.parameters) { foreach (var parameterUI in elements.OfType <VFXParameterUI>().Where(t => t.controller.model == param.parameter && param.parameter.nodes.IndexOf(t.controller.infos) >= param.infoIndexOffset)) { newSlotContainerUIs.Add(parameterUI); view.AddToSelection(parameterUI); } } // Simply selected all data edge with the context or slot container, they can be no other than the copied ones foreach (var dataEdge in elements.OfType <VFXDataEdge>()) { if (newSlotContainerUIs.Contains(dataEdge.input.GetFirstAncestorOfType <VFXNodeUI>())) { view.AddToSelection(dataEdge); } } // Simply selected all data edge with the context or slot container, they can be no other than the copied ones foreach (var flowEdge in elements.OfType <VFXFlowEdge>()) { if (newContextUIs.Contains(flowEdge.input.GetFirstAncestorOfType <VFXContextUI>())) { view.AddToSelection(flowEdge); } } if (groupNode != null) { foreach (var newSlotContainerUI in newSlotContainerUIs) { groupNode.AddNode(newSlotContainerUI.controller); } } //Select all groups that are new if (firstCopiedGroup >= 0) { foreach (var gn in elements.OfType <VFXGroupNode>()) { if (gn.controller.index >= firstCopiedGroup) { view.AddToSelection(gn); } } } //Select all groups that are new if (firstCopiedStickyNote >= 0) { foreach (var gn in elements.OfType <VFXStickyNote>()) { if (gn.controller.index >= firstCopiedStickyNote) { view.AddToSelection(gn); } } } } }
static void PasteBlocks(VFXView view, Data copyData) { var selectedContexts = view.selection.OfType <VFXContextUI>(); var selectedBlocks = view.selection.OfType <VFXBlockUI>(); VFXBlockUI targetBlock = null; VFXContextUI targetContext = null; if (selectedBlocks.Count() > 0) { targetBlock = selectedBlocks.OrderByDescending(t => t.context.controller.model.GetIndex(t.controller.model)).First(); targetContext = targetBlock.context; } else if (selectedContexts.Count() == 1) { targetContext = selectedContexts.First(); } else { Debug.LogError(m_BlockPasteError.text); return; } VFXContext targetModelContext = targetContext.controller.model; int targetIndex = -1; if (targetBlock != null) { targetIndex = targetModelContext.GetIndex(targetBlock.controller.model) + 1; } var newBlocks = new HashSet <VFXBlock>(); foreach (var block in copyData.blocks) { if (targetModelContext.AcceptChild(block, targetIndex)) { newBlocks.Add(block); foreach (var slot in block.inputSlots) { slot.UnlinkAll(true, false); } foreach (var slot in block.outputSlots) { slot.UnlinkAll(true, false); } targetModelContext.AddChild(block, targetIndex, false); // only notify once after all blocks have been added } } targetModelContext.Invalidate(VFXModel.InvalidationCause.kStructureChanged); // Create all ui based on model view.controller.LightApplyChanges(); view.ClearSelection(); foreach (var uiBlock in targetContext.Query().OfType <VFXBlockUI>().Where(t => newBlocks.Contains(t.controller.model)).ToList()) { view.AddToSelection(uiBlock); } }
static void CopyDataEdge(Data copyData, IEnumerable <VFXDataEdgeController> dataEdges, ScriptableObject[] allSerializedObjects) { copyData.dataEdges = new DataEdge[dataEdges.Count()]; int cpt = 0; var orderedEdges = new List <VFXDataEdgeController>(); var edges = new HashSet <VFXDataEdgeController>(dataEdges); // Ensure that operators that can change shape always all their input edges created before their output edges and in the same order bool sortFailed = false; try { while (edges.Count > 0) { var edgeInputs = edges.GroupBy(t => t.input.sourceNode).ToDictionary(t => t.Key, t => t.Select(u => u)); //Select the edges that have an input node which all its input edges have an output node that have no input edge // Order them by index var edgesWithoutParent = edges.Where(t => !edgeInputs[t.input.sourceNode].Any(u => edgeInputs.ContainsKey(u.output.sourceNode))).OrderBy(t => t.input.model.GetMasterSlot().owner.GetSlotIndex(t.input.model.GetMasterSlot())).ToList(); /*foreach(var gen in edgesWithoutParent) * { * int index = gen.input.model.GetMasterSlot().owner.GetSlotIndex(gen.input.model.GetMasterSlot()); * Debug.Log("Edge with input:" + gen.input.sourceNode.title + "index"+ index); * }*/ orderedEdges.AddRange(edgesWithoutParent); int count = edges.Count; foreach (var e in edgesWithoutParent) { edges.Remove(e); } if (edges.Count >= count) { sortFailed = true; Debug.LogError("Sorting of data edges failed. Please provide a screenshot of the graph with the selected node to @tristan"); break; } //Debug.Log("------------------------------"); } } catch (Exception e) { Debug.LogError("Sorting of data edges threw. Please provide a screenshot of the graph with the selected node to @tristan" + e.Message); sortFailed = true; } IEnumerable <VFXDataEdgeController> usedEdges = sortFailed ? dataEdges : orderedEdges; foreach (var edge in usedEdges) { DataEdge copyPasteEdge = new DataEdge(); var inputController = edge.input as VFXDataAnchorController; var outputController = edge.output as VFXDataAnchorController; copyPasteEdge.input.slotPath = MakeSlotPath(inputController.model, true); if (inputController.model.owner is VFXContext) { VFXContext context = inputController.model.owner as VFXContext; copyPasteEdge.inputContext = true; copyPasteEdge.input.targetIndex = System.Array.IndexOf(allSerializedObjects, context); copyPasteEdge.inputBlockIndex = -1; } else if (inputController.model.owner is VFXBlock) { VFXBlock block = inputController.model.owner as VFXBlock; copyPasteEdge.inputContext = true; copyPasteEdge.input.targetIndex = System.Array.IndexOf(allSerializedObjects, block.GetParent()); copyPasteEdge.inputBlockIndex = block.GetParent().GetIndex(block); } else { copyPasteEdge.inputContext = false; copyPasteEdge.input.targetIndex = System.Array.IndexOf(allSerializedObjects, inputController.model.owner as VFXModel); copyPasteEdge.inputBlockIndex = -1; } if (outputController.model.owner is VFXParameter) { copyPasteEdge.outputParameter = true; copyPasteEdge.outputParameterIndex = System.Array.FindIndex(copyData.parameters, t => (IVFXSlotContainer)t.parameter == outputController.model.owner); copyPasteEdge.outputParameterNodeIndex = System.Array.IndexOf(copyData.parameters[copyPasteEdge.outputParameterIndex].infos, (outputController.sourceNode as VFXParameterNodeController).infos); } else { copyPasteEdge.outputParameter = false; } copyPasteEdge.output.slotPath = MakeSlotPath(outputController.model, false); copyPasteEdge.output.targetIndex = System.Array.IndexOf(allSerializedObjects, outputController.model.owner as VFXModel); copyData.dataEdges[cpt++] = copyPasteEdge; } // Sort the edge so the one that links the node that have the least links }
private void CheckContext(VFXContext context, VFXContextType expectedType) { Assert.AreEqual(expectedType, context.contextType); }
// Configures an HDRP Subshader with a VFX context. private static SubShaderDescriptor PostProcessSubShader(SubShaderDescriptor subShaderDescriptor, VFXContext context, VFXContextCompiledData data) { var attributesStruct = GenerateVFXAttributesStruct(context, VFXAttributeType.Current); var sourceAttributesStruct = GenerateVFXAttributesStruct(context, VFXAttributeType.Source); // Defer to VFX to generate various misc. code-gen that ShaderGraph currently can't handle. // We use the AdditionalCommand descriptors for ShaderGraph generation to splice these in. // ( i.e. VFX Graph Block Function declaration + calling, Property Mapping, etc. ) GenerateVFXAdditionalCommands( context, data, out var loadAttributeDescriptor, out var blockFunctionDescriptor, out var blockCallFunctionDescriptor, out var interpolantsGenerationDescriptor, out var buildVFXFragInputs, out var pixelPropertiesAssignDescriptor, out var defineSpaceDescriptor, out var parameterBufferDescriptor, out var additionalDefinesDescriptor, out var loadPositionAttributeDescriptor, out var loadCropFactorAttributesDescriptor, out var loadTexcoordAttributesDescriptor, out var vertexPropertiesGenerationDescriptor, out var vertexPropertiesAssignDescriptor ); // Omit MV or Shadow Pass if disabled on the context. var filteredPasses = subShaderDescriptor.passes.AsEnumerable(); var outputContext = (VFXAbstractParticleOutput)context; if (!outputContext.hasMotionVector) { filteredPasses = filteredPasses.Where(o => o.descriptor.lightMode != "MotionVectors"); } if (!outputContext.hasShadowCasting) { filteredPasses = filteredPasses.Where(o => o.descriptor.lightMode != "ShadowCaster"); } var passes = filteredPasses.ToArray(); PassCollection vfxPasses = new PassCollection(); for (int i = 0; i < passes.Length; i++) { var passDescriptor = passes[i].descriptor; // Warning: We are replacing the struct provided in the regular pass. It is ok as for now the VFX editor don't support // tessellation or raytracing passDescriptor.structs = new StructCollection { AttributesMeshVFX, // TODO: Could probably re-use the original HD Attributes Mesh and just ensure Instancing enabled. Structs.VertexDescriptionInputs, Structs.SurfaceDescriptionInputs, AppendVFXInterpolator(HDStructs.VaryingsMeshToPS, context, data), attributesStruct, sourceAttributesStruct }; // Add additional VFX dependencies passDescriptor.fieldDependencies = passDescriptor.fieldDependencies == null ? new DependencyCollection() : new DependencyCollection { passDescriptor.fieldDependencies }; // Duplicate fieldDependencies to avoid side effects (static list modification) passDescriptor.fieldDependencies.Add(VFXHDRPSubTarget.ElementSpaceDependencies); passDescriptor.additionalCommands = new AdditionalCommandCollection { loadAttributeDescriptor, blockFunctionDescriptor, blockCallFunctionDescriptor, interpolantsGenerationDescriptor, buildVFXFragInputs, pixelPropertiesAssignDescriptor, defineSpaceDescriptor, parameterBufferDescriptor, additionalDefinesDescriptor, loadPositionAttributeDescriptor, loadCropFactorAttributesDescriptor, loadTexcoordAttributesDescriptor, vertexPropertiesGenerationDescriptor, vertexPropertiesAssignDescriptor, GenerateFragInputs(context, data) }; vfxPasses.Add(passDescriptor, passes[i].fieldConditions); } subShaderDescriptor.passes = vfxPasses; return(subShaderDescriptor); }
public override void OnInspectorGUI() { if (dataObject != null) { dataObject.Update(); } if (srpSubOutputObject != null) { srpSubOutputObject.Update(); } if (!serializedObject.isEditingMultipleObjects) { VFXContext model = (VFXContext)target; if (model != null && model.letter != '\0') { GUILayout.Label(model.letter.ToString(), Styles.letter); } } base.OnInspectorGUI(); bool invalidateContext = (dataObject != null && dataObject.ApplyModifiedProperties()) || (srpSubOutputObject != null && srpSubOutputObject.ApplyModifiedProperties()); if (invalidateContext) { foreach (VFXContext ctx in targets.OfType <VFXContext>()) { // notify that something changed. ctx.GetData().Invalidate(VFXModel.InvalidationCause.kSettingChanged); // This will also invalidate contexts } } if (serializedObject.isEditingMultipleObjects) { return; // Summary Only visible for single selection } // Context / SystemData if (dataObject == null) { return; } var context = (VFXContext)target; var data = (VFXData)dataObject.targetObject; // Particle context data if (data.type == VFXDataType.Particle) { VFXDataParticle particleData = data as VFXDataParticle; EditorGUILayout.Space(); { Styles.Row(Styles.header, "Name", "Value"); Styles.Row(Styles.cell, "Capacity", particleData.GetSettingValue("capacity").ToString()); EditorGUILayout.Space(); var attributes = data.GetAttributes(); if (attributes.Count() > 0) { EditorGUILayout.LabelField("System Attribute Summary", Styles.header); foreach (var attr in attributes) { using (new EditorGUILayout.HorizontalScope()) { GUILayout.Label(attr.attrib.name, Styles.cell); Styles.DataTypeLabel(attr.attrib.type.ToString(), attr.attrib.type, Styles.cell, GUILayout.Width(64)); int size = VFXExpressionHelper.GetSizeOfType(attr.attrib.type) * 4; GUILayout.Label(size + " byte" + (size > 1 ? "s" : ""), Styles.cell, GUILayout.Width(64)); var mode = attr.mode; GUILayout.Label(mode.ToString(), Styles.cell, GUILayout.Width(64)); } } } StructureOfArrayProvider.BucketInfo[] current = particleData.GetCurrentAttributeLayout(); StructureOfArrayProvider.BucketInfo[] source = particleData.GetSourceAttributeLayout(); if (current.Length > 0) { GUILayout.Space(24); DoAttributeLayoutGUI("Current Attribute Layout", current); } if (source.Length > 0) { GUILayout.Space(12); DoAttributeLayoutGUI("Source Attribute Layout", source); } } } if (VFXViewPreference.displayExtraDebugInfo) { // Extra debug data EditorGUILayout.Space(); { Styles.Row(Styles.header, "Name", "Value"); Styles.Row(Styles.cell, "Context Type", context.contextType.ToString()); Styles.Row(Styles.cell, "Task Type", context.taskType.ToString()); Styles.Row(Styles.cell, "Input Data Type", context.inputType.ToString()); Styles.Row(Styles.cell, "Context Data Type", data.type.ToString()); Styles.Row(Styles.cell, "Can Be Compiled", context.CanBeCompiled().ToString()); EditorGUILayout.Space(); var attributeInfos = data.GetAttributesForContext(context); VFXAttributeInfo[] infos; // Early check for context consistency try { infos = attributeInfos.ToArray(); } catch { return; } EditorGUILayout.LabelField("Attributes used by Context", Styles.header); foreach (var info in infos) { using (new EditorGUILayout.HorizontalScope()) { GUILayout.Label(info.attrib.name, Styles.cell); Styles.DataTypeLabel(info.attrib.type.ToString(), info.attrib.type, Styles.cell, GUILayout.Width(80)); Styles.AttributeModeLabel(info.mode.ToString(), info.mode, Styles.cell, GUILayout.Width(80)); } } EditorGUILayout.Space(); Styles.Row(Styles.header, "Blocks"); foreach (var block in context.activeChildrenWithImplicit) { Styles.Row(Styles.cell, block.name, !context.children.Contains(block) ? "implicit" : ""); } EditorGUILayout.Space(); } } }
public void ConfigureContextData(VFXContext context, VFXContextCompiledData data) { m_ContextVFX = context; m_ContextDataVFX = data; }
static void GenerateVFXAdditionalCommands(VFXContext context, VFXContextCompiledData contextData, out AdditionalCommandDescriptor loadAttributeDescriptor, out AdditionalCommandDescriptor blockFunctionDescriptor, out AdditionalCommandDescriptor blockCallFunctionDescriptor, out AdditionalCommandDescriptor interpolantsGenerationDescriptor, out AdditionalCommandDescriptor buildVFXFragInputsDescriptor, out AdditionalCommandDescriptor pixelPropertiesAssignDescriptor, out AdditionalCommandDescriptor defineSpaceDescriptor, out AdditionalCommandDescriptor parameterBufferDescriptor, out AdditionalCommandDescriptor additionalDefinesDescriptor, out AdditionalCommandDescriptor loadPositionAttributeDescriptor, out AdditionalCommandDescriptor loadCropFactorAttributesDescriptor, out AdditionalCommandDescriptor loadTexcoordAttributesDescriptor, out AdditionalCommandDescriptor vertexPropertiesGenerationDescriptor, out AdditionalCommandDescriptor vertexPropertiesAssignDescriptor) { // TODO: Clean all of this up. Currently just an adapter between VFX Code Gen + SG Code Gen and *everything* has been stuffed here. // Load Attributes loadAttributeDescriptor = new AdditionalCommandDescriptor("VFXLoadAttribute", VFXCodeGenerator.GenerateLoadAttribute(".", context).ToString()); // Graph Blocks VFXCodeGenerator.BuildContextBlocks(context, contextData, out var blockFunction, out var blockCallFunction); blockFunctionDescriptor = new AdditionalCommandDescriptor("VFXGeneratedBlockFunction", blockFunction); blockCallFunctionDescriptor = new AdditionalCommandDescriptor("VFXProcessBlocks", blockCallFunction); // Vertex Input VFXCodeGenerator.BuildVertexProperties(context, contextData, out var vertexPropertiesGeneration); vertexPropertiesGenerationDescriptor = new AdditionalCommandDescriptor("VFXVertexPropertiesGeneration", vertexPropertiesGeneration); VFXCodeGenerator.BuildVertexPropertiesAssign(context, contextData, out var vertexPropertiesAssign); vertexPropertiesAssignDescriptor = new AdditionalCommandDescriptor("VFXVertexPropertiesAssign", vertexPropertiesAssign); // Interpolator VFXCodeGenerator.BuildInterpolatorBlocks(context, contextData, out var interpolatorsGeneration); interpolantsGenerationDescriptor = new AdditionalCommandDescriptor("VFXInterpolantsGeneration", interpolatorsGeneration); // Frag Inputs - Only VFX will know if frag inputs come from interpolator or the CBuffer. VFXCodeGenerator.BuildFragInputsGeneration(context, contextData, out var buildFragInputsGeneration); buildVFXFragInputsDescriptor = new AdditionalCommandDescriptor("VFXSetFragInputs", buildFragInputsGeneration); VFXCodeGenerator.BuildPixelPropertiesAssign(context, contextData, out var pixelPropertiesAssign); pixelPropertiesAssignDescriptor = new AdditionalCommandDescriptor("VFXPixelPropertiesAssign", pixelPropertiesAssign); // Define coordinate space var defineSpaceDescriptorContent = string.Empty; if (context.GetData() is ISpaceable) { var spaceable = context.GetData() as ISpaceable; defineSpaceDescriptorContent = $"#define {(spaceable.space == VFXCoordinateSpace.World ? "VFX_WORLD_SPACE" : "VFX_LOCAL_SPACE")} 1"; } defineSpaceDescriptor = new AdditionalCommandDescriptor("VFXDefineSpace", defineSpaceDescriptorContent); // Parameter Cbuffer VFXCodeGenerator.BuildParameterBuffer(contextData, out var parameterBuffer); parameterBufferDescriptor = new AdditionalCommandDescriptor("VFXParameterBuffer", parameterBuffer); // Defines & Headers - Not all are necessary, however some important ones are mixed in like indirect draw, strips, flipbook, particle strip info... ShaderStringBuilder additionalDefines = new ShaderStringBuilder(); // TODO: Need to add defines for current/source usage (i.e. scale). var allCurrentAttributes = context.GetData().GetAttributes().Where(a => (context.GetData().IsCurrentAttributeUsed(a.attrib, context)) || (context.contextType == VFXContextType.Init && context.GetData().IsAttributeStored(a.attrib))); // In init, needs to declare all stored attributes for intialization var allSourceAttributes = context.GetData().GetAttributes().Where(a => (context.GetData().IsSourceAttributeUsed(a.attrib, context))); foreach (var attribute in allCurrentAttributes) { additionalDefines.AppendLine("#define VFX_USE_{0}_{1} 1", attribute.attrib.name.ToUpper(CultureInfo.InvariantCulture), "CURRENT"); } foreach (var attribute in allSourceAttributes) { additionalDefines.AppendLine("#define VFX_USE_{0}_{1} 1", attribute.attrib.name.ToUpper(CultureInfo.InvariantCulture), "SOURCE"); } foreach (var header in context.additionalDataHeaders) { additionalDefines.AppendLine(header); } foreach (var define in context.additionalDefines) { additionalDefines.AppendLine($"#define {define} 1"); } additionalDefinesDescriptor = new AdditionalCommandDescriptor("VFXDefines", additionalDefines.ToString()); // Load Position Attribute loadPositionAttributeDescriptor = new AdditionalCommandDescriptor("VFXLoadPositionAttribute", VFXCodeGenerator.GenerateLoadAttribute("position", context).ToString().ToString()); // Load Crop Factor Attribute var mainParameters = contextData.gpuMapper.CollectExpression(-1).ToArray(); var expressionToName = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null)); expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value); loadCropFactorAttributesDescriptor = new AdditionalCommandDescriptor("VFXLoadCropFactorParameter", VFXCodeGenerator.GenerateLoadParameter("cropFactor", mainParameters, expressionToName).ToString().ToString()); loadTexcoordAttributesDescriptor = new AdditionalCommandDescriptor("VFXLoadTexcoordParameter", VFXCodeGenerator.GenerateLoadParameter("texCoord", mainParameters, expressionToName).ToString().ToString()); }
public static void PasteBlocks(VFXViewController viewController, object data, VFXContext targetModelContext, int targetIndex, List <VFXBlockController> blocksInTheSameOrder) { if (s_Instance == null) { s_Instance = new VFXPaste(); } s_Instance.PasteBlocks(viewController, (data as SerializableGraph).operators, targetModelContext, targetIndex, blocksInTheSameOrder); }
// Configures an HDRP Subshader with a VFX context. private static SubShaderDescriptor PostProcessSubShader(SubShaderDescriptor subShaderDescriptor, VFXContext context, VFXContextCompiledData data) { var attributesStruct = GenerateVFXAttributesStruct(context, VFXAttributeType.Current); var sourceAttributesStruct = GenerateVFXAttributesStruct(context, VFXAttributeType.Source); // Defer to VFX to generate various misc. code-gen that ShaderGraph currently can't handle. // We use the AdditionalCommand descriptors for ShaderGraph generation to splice these in. // ( i.e. VFX Graph Block Function declaration + calling, Property Mapping, etc. ) GenerateVFXAdditionalCommands( context, data, out var loadAttributeDescriptor, out var blockFunctionDescriptor, out var blockCallFunctionDescriptor, out var interpolantsGenerationDescriptor, out var buildVFXFragInputs, out var pixelPropertiesAssignDescriptor, out var defineSpaceDescriptor, out var parameterBufferDescriptor, out var additionalDefinesDescriptor, out var loadPositionAttributeDescriptor, out var loadCropFactorAttributesDescriptor, out var loadTexcoordAttributesDescriptor, out var vertexPropertiesGenerationDescriptor, out var vertexPropertiesAssignDescriptor ); // Omit MV or Shadow Pass if disabled on the context. var filteredPasses = subShaderDescriptor.passes.AsEnumerable(); var outputContext = (VFXAbstractParticleOutput)context; if (!outputContext.hasMotionVector) { filteredPasses = filteredPasses.Where(o => o.descriptor.lightMode != "MotionVectors"); } if (!outputContext.hasShadowCasting) { filteredPasses = filteredPasses.Where(o => o.descriptor.lightMode != "ShadowCaster"); } var passes = filteredPasses.ToArray(); PassCollection vfxPasses = new PassCollection(); for (int i = 0; i < passes.Length; i++) { var passDescriptor = passes[i].descriptor; // Warning: Touching the structs field may require to manually append the default structs here. passDescriptor.structs = new StructCollection { AttributesMeshVFX, // TODO: Could probably re-use the original HD Attributes Mesh and just ensure Instancing enabled. AppendVFXInterpolator(HDStructs.VaryingsMeshToPS, context, data), Structs.SurfaceDescriptionInputs, Structs.VertexDescriptionInputs, attributesStruct, sourceAttributesStruct }; passDescriptor.pragmas = new PragmaCollection { ModifyVertexEntry(passDescriptor.pragmas), Pragma.MultiCompileInstancing }; passDescriptor.additionalCommands = new AdditionalCommandCollection { loadAttributeDescriptor, blockFunctionDescriptor, blockCallFunctionDescriptor, interpolantsGenerationDescriptor, buildVFXFragInputs, pixelPropertiesAssignDescriptor, defineSpaceDescriptor, parameterBufferDescriptor, additionalDefinesDescriptor, loadPositionAttributeDescriptor, loadCropFactorAttributesDescriptor, loadTexcoordAttributesDescriptor, vertexPropertiesGenerationDescriptor, vertexPropertiesAssignDescriptor, GenerateFragInputs(context, data) }; vfxPasses.Add(passDescriptor, passes[i].fieldConditions); } subShaderDescriptor.passes = vfxPasses; return(subShaderDescriptor); }
public IEnumerator CopyPast_Context_And_Relink([ValueSource("cutBeforeSource")] CutBefore cutBeforeEncapsultor) { VFXTaskType curBeforeSource = cutBeforeEncapsultor.taskType; EditorApplication.ExecuteMenuItem("Window/General/Game"); var graph = MakeTemporaryGraph(); var spawnerContext = ScriptableObject.CreateInstance <VFXBasicSpawner>(); var basicInitialize = ScriptableObject.CreateInstance <VFXBasicInitialize>(); var basicUpdate = ScriptableObject.CreateInstance <VFXBasicUpdate>(); var quadOutput = ScriptableObject.CreateInstance <VFXQuadOutput>(); var arrayOfContext = new VFXContext[] { spawnerContext, basicInitialize, basicUpdate, quadOutput }; quadOutput.SetSettingValue("blendMode", VFXAbstractParticleOutput.BlendMode.Additive); var setPosition = ScriptableObject.CreateInstance <SetAttribute>(); //only needed to allocate a minimal attributeBuffer setPosition.SetSettingValue("attribute", "position"); basicInitialize.AddChild(setPosition); var capacity = 2u; //(basicInitialize.GetData() as VFXDataParticle).capacity = capacity; //voluntary overflow in case of capacity is not correctly copied basicInitialize.SetSettingValue("capacity", 2u); // pass through regular way to change capacity to have initialize and data up to date var spawnerBurst = ScriptableObject.CreateInstance <VFXSpawnerBurst>(); spawnerBurst.inputSlots[0].value = 4.0f; spawnerContext.AddChild(spawnerBurst); graph.AddChild(spawnerContext); graph.AddChild(basicInitialize); graph.AddChild(basicUpdate); graph.AddChild(quadOutput); basicInitialize.LinkFrom(spawnerContext); basicUpdate.LinkFrom(basicInitialize); quadOutput.LinkFrom(basicUpdate); m_ViewController.NotifyUpdate(); Assert.AreEqual(4, m_View.GetAllContexts().Count()); //Copy partially in two passes var indexOffset = cutBeforeSource.Select((o, i) => new { o = o, i = i }).Where(o => o.o.taskType == curBeforeSource).Select(o => o.i).First(); var contextToCopy_A = arrayOfContext.Skip(indexOffset).Select(o => m_View.GetAllContexts().Where(u => u.controller.model == o).First() as UnityEditor.Experimental.GraphView.GraphElement); foreach (var graphElement in contextToCopy_A) { m_View.AddToSelection(graphElement); } m_View.DuplicateSelectionCallback(); m_View.ClearSelection(); m_ViewController.NotifyUpdate(); if (indexOffset > 0) { var contextToCopy_B = arrayOfContext.Take(indexOffset).Select(o => m_View.GetAllContexts().Where(u => u.controller.model == o).First() as UnityEditor.Experimental.GraphView.GraphElement); foreach (var graphElement in contextToCopy_B) { m_View.AddToSelection(graphElement); } m_View.DuplicateSelectionCallback(); m_View.ClearSelection(); } m_ViewController.NotifyUpdate(); Assert.AreEqual(8, m_View.GetAllContexts().Count()); //Restore missing link var allContext = m_View.GetAllContexts().Select(o => o.controller.model).ToArray(); if (indexOffset > 0) { var from = allContext.Last(); var to = allContext[4]; to.LinkFrom(from); } VFXBasicInitialize[] initializes = graph.children.OfType <VFXBasicInitialize>().ToArray(); Assert.AreEqual(2, initializes.Length); Assert.AreEqual(2, (initializes[0].GetData() as VFXDataParticle).capacity); Assert.AreEqual(2, (initializes[1].GetData() as VFXDataParticle).capacity); Assert.AreEqual(2, initializes[0].GetType().GetField("capacity", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(initializes[0])); Assert.AreEqual(2, initializes[1].GetType().GetField("capacity", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(initializes[1])); graph.RecompileIfNeeded(); var gameObj = new GameObject("CreateAssetAndComponentToCopyPastPerfomedWell"); var vfxComponent = gameObj.AddComponent <VisualEffect>(); vfxComponent.visualEffectAsset = graph.visualEffectResource.asset; var cameraObj = new GameObject("CreateAssetAndComponentToCopyPastPerfomedWell_Camera"); var camera = cameraObj.AddComponent <Camera>(); camera.transform.localPosition = Vector3.one; camera.transform.LookAt(vfxComponent.transform); int maxFrame = 512; while (vfxComponent.culled && --maxFrame > 0) { yield return(null); } Assert.IsTrue(maxFrame > 0); yield return(null); //wait for exactly one more update if visible Assert.AreEqual(capacity * 2, vfxComponent.aliveParticleCount); //Excepted to have two viable equivalent particles system UnityEngine.Object.DestroyImmediate(vfxComponent); UnityEngine.Object.DestroyImmediate(cameraObj); }
void PasteBlocks(VFXView view, ref SerializableGraph serializableGraph) { var selectedContexts = view.selection.OfType <VFXContextUI>(); var selectedBlocks = view.selection.OfType <VFXBlockUI>(); VFXBlockUI targetBlock = null; VFXContextUI targetContext = null; if (selectedBlocks.Count() > 0) { targetBlock = selectedBlocks.OrderByDescending(t => t.context.controller.model.GetIndex(t.controller.model)).First(); targetContext = targetBlock.context; } else if (selectedContexts.Count() == 1) { targetContext = selectedContexts.First(); } else { Debug.LogError(m_BlockPasteError.text); return; } VFXContext targetModelContext = targetContext.controller.model; int targetIndex = -1; if (targetBlock != null) { targetIndex = targetModelContext.GetIndex(targetBlock.controller.model) + 1; } var newBlocks = new HashSet <VFXBlock>(); newControllers.Clear(); foreach (var block in serializableGraph.operatorsOrBlocks) { Node blk = block; VFXBlock newBlock = PasteAndInitializeNode <VFXBlock>(view.controller, ref blk); if (targetModelContext.AcceptChild(newBlock, targetIndex)) { newBlocks.Add(newBlock); targetModelContext.AddChild(newBlock, targetIndex, false); // only notify once after all blocks have been added targetIndex++; } } targetModelContext.Invalidate(VFXModel.InvalidationCause.kStructureChanged); //TODO fill infos.indexToController for when external links will be optionally copied. view.ClearSelection(); foreach (var uiBlock in targetContext.Query().OfType <VFXBlockUI>().Where(t => newBlocks.Contains(t.controller.model)).ToList()) { view.AddToSelection(uiBlock); } }