public override void Sanitize() { var newBlock = ScriptableObject.CreateInstance <VFXSpawnerBurst>(); newBlock.SetSettingValue("repeat", VFXSpawnerBurst.RepeatMode.Single); if (advanced) { newBlock.SetSettingValue("spawnMode", VFXSpawnerBurst.RandomMode.Random); newBlock.SetSettingValue("delayMode", VFXSpawnerBurst.RandomMode.Random); } else { newBlock.SetSettingValue("spawnMode", VFXSpawnerBurst.RandomMode.Constant); newBlock.SetSettingValue("delayMode", VFXSpawnerBurst.RandomMode.Constant); } // Count VFXSlot.CopyLinksAndValue(newBlock.GetInputSlot(0), GetInputSlot(0), true); // Delay if (advanced) { VFXSlot.CopyLinksAndValue(newBlock.GetInputSlot(1), GetInputSlot(1), true); } else { newBlock.GetInputSlot(1).value = 0.0f; } ReplaceModel(newBlock, this); base.Sanitize(); }
public override void Sanitize() { // Create new operator var attrib = ScriptableObject.CreateInstance <VFXAttributeParameter>(); attrib.SetSettingValue("location", VFXAttributeLocation.Current); attrib.SetSettingValue("attribute", attribute); attrib.position = position; VFXSlot.CopyLinksAndValue(attrib.GetOutputSlot(0), GetOutputSlot(0), true); ReplaceModel(attrib, this); }
public static void MigrateVector3OutputToSpaceableKeepingLegacyBehavior(VFXOperator op, string newTypeInfo) { Debug.LogFormat("Sanitizing Graph: Automatically replace Vector3 to {0} for {1}. An inline Vector3 operator has been added.", newTypeInfo, op.name); var inlineVector3 = ScriptableObject.CreateInstance <VFXInlineOperator>(); inlineVector3.SetSettingValue("m_Type", (SerializableType)typeof(Vector3)); inlineVector3.position = op.position + new Vector2(128.0f, 64.0f); VFXSlot.CopyLinksAndValue(inlineVector3.outputSlots[0], op.outputSlots[0], false /* we should avoid ReSyncSlot at this stage*/); op.outputSlots[0].Link(inlineVector3.inputSlots[0], true /* notify here to correctly invalidate */); op.GetParent().AddChild(inlineVector3); }
public static void MigrateTConeFromCone(VFXSlot to, VFXSlot from, bool hasLink = false) { var to_center = to[0][0]; var to_baseRadius = to[1]; var to_topRadius = to[2]; var to_height = to[3]; var refSlot = from.refSlot; VFXSlot.CopySpace(to, refSlot, true); if (from.HasLink(false) || hasLink) { var parentCenter = refSlot[0]; var parentBaseRadius = refSlot[1]; var parentTopRadius = refSlot[2]; var parentHeight = refSlot[3]; to_center.Link(parentCenter, true); to_baseRadius.Link(parentBaseRadius, true); to_topRadius.Link(parentTopRadius, true); to_height.Link(parentHeight, true); } else { var center = from[0]; var baseRadius = from[1]; var topRadius = from[2]; var height = from[3]; var value = new TCone() { transform = new Transform() { position = (Vector3)center.value, scale = Vector3.one }, baseRadius = (float)baseRadius.value, topRadius = (float)topRadius.value, height = (float)height.value }; to.value = value; VFXSlot.CopyLinksAndValue(to_center, center, true); VFXSlot.CopyLinksAndValue(to_baseRadius, baseRadius, true); VFXSlot.CopyLinksAndValue(to_topRadius, topRadius, true); VFXSlot.CopyLinksAndValue(to_height, height, true); } }
public override void Sanitize() { var newBlock = ScriptableObject.CreateInstance <VFXSpawnerBurst>(); newBlock.SetSettingValue("repeat", VFXSpawnerBurst.RepeatMode.Periodic); newBlock.SetSettingValue("delayMode", VFXSpawnerBurst.RandomMode.Random); newBlock.SetSettingValue("spawnMode", VFXSpawnerBurst.RandomMode.Random); VFXSlot.CopyLinksAndValue(newBlock.GetInputSlot(0), GetInputSlot(0), true); VFXSlot.CopyLinksAndValue(newBlock.GetInputSlot(1), GetInputSlot(1), true); ReplaceModel(newBlock, this); base.Sanitize(); }
public override void Sanitize(int version) { Debug.Log("Sanitizing Graph: Automatically replace BuiltInParameter by new BuiltInParameter implementation for " + m_expressionOp.ToString()); var entry = VFXDynamicBuiltInParameter.s_BuiltInInfo.First(o => o.Value.expression.operation == m_expressionOp); VFXDynamicBuiltInParameter.BuiltInFlag newBuiltIn = entry.Key; var newBuiltinParameter = CreateInstance <VFXDynamicBuiltInParameter>(); newBuiltinParameter.SetSettingValue("m_BuiltInParameters", newBuiltIn); VFXSlot.CopyLinksAndValue(newBuiltinParameter.GetOutputSlot(0), GetOutputSlot(0), true); ReplaceModel(newBuiltinParameter, this); base.Sanitize(version); }
public override void Sanitize() { if (attribute == "phase") // Replace old phase attribute with random operator { Debug.Log("Sanitizing Graph: Automatically replace Phase Attribute Parameter with a Fixed Random Operator"); var randOp = ScriptableObject.CreateInstance <Operator.Random>(); randOp.constant = true; randOp.seed = Operator.Random.SeedMode.PerParticle; VFXSlot.CopyLinksAndValue(randOp.GetOutputSlot(0), GetOutputSlot(0), true); ReplaceModel(randOp, this); } else { base.Sanitize(); } }
public static void MigrateTTorusFromTorus(VFXSlot to, VFXSlot from, bool hasLink = false) { var to_center = to[0][0]; var to_majorRadius = to[1]; var to_minorRadius = to[2]; var refSlot = from.refSlot; VFXSlot.CopySpace(to, refSlot, true); if (from.HasLink(false) || hasLink) { var parentCenter = refSlot[0]; var parentMajorRadius = refSlot[1]; var parentMinorRadius = refSlot[2]; to_center.Link(parentCenter, true); to_majorRadius.Link(parentMajorRadius, true); to_minorRadius.Link(parentMinorRadius, true); } else { var center = from[0]; var majorRadius = from[1]; var minorRadius = from[2]; var value = new TTorus() { transform = new Transform() { position = (Vector3)center.value, scale = Vector3.one }, majorRadius = (float)majorRadius.value, minorRadius = (float)minorRadius.value }; to.value = value; VFXSlot.CopyLinksAndValue(to_center, center, true); VFXSlot.CopyLinksAndValue(to_majorRadius, majorRadius, true); VFXSlot.CopyLinksAndValue(to_minorRadius, minorRadius, true); } }
public static void MigrateTSphereFromSphere(VFXSlot to, VFXSlot from, bool forceHasLink = false) { var to_center = to[0][0]; var to_radius = to[1]; var refSlot = from.refSlot; VFXSlot.CopySpace(to, refSlot, true); if (from.HasLink(false) || forceHasLink) { //TODO : This behavior leads to an UX issue with if the parent owner of refslot is VFXParameter //This is the same issue than OnCopyLinksMySlot/OnCopyLinksOtherSlot needed in VFXSlot.CopyLinks var parentCenter = refSlot[0]; var parentRadius = refSlot[1]; to_center.Link(parentCenter, true); to_radius.Link(parentRadius, true); } else { var center = from[0]; var radius = from[1]; var value = new TSphere() { transform = new Transform() { position = (Vector3)center.value, scale = Vector3.one }, radius = (float)radius.value }; to.value = value; VFXSlot.CopyLinksAndValue(to_center, center, true); VFXSlot.CopyLinksAndValue(to_radius, radius, true); } }
private static VFXSlot CorrectPositionFromCylinderToCone(VFXGraph graph, Vector2 basePosition, VFXSlot height, VFXSlot center) { var inlineHeight = ScriptableObject.CreateInstance <VFXInlineOperator>(); inlineHeight.SetSettingValue("m_Type", (SerializableType)typeof(float)); inlineHeight.position = basePosition - new Vector2(700.0f, -151.0f); VFXSlot.CopyLinksAndValue(inlineHeight.inputSlots[0], height, true); graph.AddChild(inlineHeight); var halfHeight = ScriptableObject.CreateInstance <Operator.Multiply>(); halfHeight.SetOperandType(0, typeof(float)); halfHeight.SetOperandType(1, typeof(float)); halfHeight.inputSlots[0].Link(inlineHeight.outputSlots[0]); halfHeight.inputSlots[1].value = 0.5f; halfHeight.position = basePosition - new Vector2(480.0f, -111.0f); graph.AddChild(halfHeight); var inlinePosition = ScriptableObject.CreateInstance <VFXInlineOperator>(); inlinePosition.SetSettingValue("m_Type", (SerializableType)typeof(Position)); inlinePosition.position = basePosition - new Vector2(555.0f, -20.0f); VFXSlot.CopyLinksAndValue(inlinePosition.inputSlots[0], center, true); graph.AddChild(inlinePosition); var correctedPosition = ScriptableObject.CreateInstance <Operator.Subtract>(); correctedPosition.SetOperandType(0, typeof(Position)); correctedPosition.SetOperandType(1, typeof(Position)); VFXSlot.CopySpace(correctedPosition.inputSlots[0], inlinePosition.outputSlots[0], true); VFXSlot.CopySpace(correctedPosition.inputSlots[1], inlinePosition.outputSlots[0], true); correctedPosition.inputSlots[0].Link(inlinePosition.outputSlots[0]); correctedPosition.inputSlots[1][0][1].Link(halfHeight.outputSlots[0]); correctedPosition.position = basePosition - new Vector2(282.0f, -20.0f); graph.AddChild(correctedPosition); return(correctedPosition.outputSlots[0]); }
public static void MigrateTArcConeFromArcCone(VFXSlot to, VFXSlot from) { var to_cone = to[0]; var to_arc = to[1]; var refSlot = from.refSlot; var from_cone = refSlot; //The ArcCone wasn't a composition var from_arc = refSlot[4]; VFXSlot.CopySpace(to, refSlot, true); var hasDirectLink = from.HasLink(false); MigrateTConeFromCone(to_cone, from_cone, hasDirectLink); if (hasDirectLink) { to_arc.Link(from_arc, true); } else { to_arc.value = (float)from_arc.value; //The value transfer is only applied on masterslot VFXSlot.CopyLinksAndValue(to_arc, from_arc, true); } }
public static void MigrateTArcSphereFromArcSphere(VFXSlot to, VFXSlot from) { var to_sphere = to[0]; var to_arc = to[1]; var refSlot = from.refSlot; var from_sphere = refSlot[0]; var from_arc = refSlot[1]; VFXSlot.CopySpace(to, refSlot, true); var hasDirectLink = from.HasLink(false); MigrateTSphereFromSphere(to_sphere, from_sphere, hasDirectLink); if (hasDirectLink) { to_arc.Link(from_arc, true); } else { to_arc.value = (float)from_arc.value; //The value transfer is only applied on masterslot VFXSlot.CopyLinksAndValue(to_arc, from_arc, true); } }
public static void MigrateTCircleFromCircle(VFXSlot to, VFXSlot from, bool forceHasLink = false) { var to_center = to[0][0]; var to_radius = to[1]; var refslot = from.refSlot; VFXSlot.CopySpace(to, refslot, true); if (from.HasLink(false) || forceHasLink) { var parentCenter = refslot[0]; var parentRadius = refslot[1]; to_center.Link(parentCenter, true); to_radius.Link(parentRadius, true); } else { var center = from[0]; var radius = from[1]; var value = new TCircle() { transform = new Transform() { position = (Vector3)center.value, scale = Vector3.one }, radius = (float)radius.value }; to.value = value; VFXSlot.CopyLinksAndValue(to_center, center, true); VFXSlot.CopyLinksAndValue(to_radius, radius, true); } }
public static void MigrateBlockTShapeFromShape(VFXBlock to, VFXBlock from) { var fromSettings = from.GetSettings(true); var toSettings = to.GetSettings(true); foreach (var fromSetting in fromSettings) { var toSetting = toSettings.FirstOrDefault(o => o.name.Equals(fromSetting.name, StringComparison.InvariantCultureIgnoreCase)); if (toSetting.field != null) { to.SetSettingValue(toSetting.name, fromSetting.value); } } if (from.inputSlots.Count != to.inputSlots.Count) { throw new InvalidOperationException(); } for (int i = 0; i < from.inputSlots.Count; ++i) { var fromInputSlot = from.inputSlots[i]; var toInputSlot = to.inputSlots[i]; if (toInputSlot.property.type == fromInputSlot.property.type) { VFXSlot.CopyLinksAndValue(toInputSlot, fromInputSlot, true); } else if (toInputSlot.property.type == typeof(TArcSphere)) { MigrateTArcSphereFromArcSphere(toInputSlot, fromInputSlot); } else if (toInputSlot.property.type == typeof(TArcCircle)) { MigrateTArcCircleFromArcCircle(toInputSlot, fromInputSlot); } else if (toInputSlot.property.type == typeof(TArcTorus)) { MigrateTArcTorusFromArcTorus(toInputSlot, fromInputSlot); } else if (toInputSlot.property.type == typeof(TArcCone)) { //There wasn't a TArcCylinder type MigrateTArcConeFromArcCone(toInputSlot, fromInputSlot); } else if (toInputSlot.property.type == typeof(TSphere)) { MigrateTSphereFromSphere(toInputSlot, fromInputSlot); } else if (toInputSlot.property.type == typeof(TCircle)) { MigrateTCircleFromCircle(toInputSlot, fromInputSlot); } else if (toInputSlot.property.type == typeof(TTorus)) { MigrateTTorusFromTorus(toInputSlot, fromInputSlot); } else if (toInputSlot.property.type == typeof(TCone) && fromInputSlot.property.type == typeof(Cone)) { //Actually, no reference of this case MigrateTConeFromCone(toInputSlot, fromInputSlot); } else if (toInputSlot.property.type == typeof(TCone) && fromInputSlot.property.type == typeof(Cylinder)) { MigrateTConeFromCylinder(toInputSlot, fromInputSlot); } else { throw new NotImplementedException(); } } }
protected bool SyncSlots(VFXSlot.Direction direction, bool notify) { bool isInput = direction == VFXSlot.Direction.kInput; var expectedProperties = (isInput ? inputProperties : outputProperties).ToArray(); int nbSlots = isInput ? GetNbInputSlots() : GetNbOutputSlots(); var currentSlots = isInput ? inputSlots : outputSlots; // check all slots owner (TODO Still useful?) for (int i = 0; i < nbSlots; ++i) { VFXSlot slot = currentSlots[i]; var slotOwner = slot.owner as VFXSlotContainerModel <ParentType, ChildrenType>; if (slotOwner != this) { Debug.LogError("Slot :" + slot.name + " of Container" + name + "Has a wrong owner."); slot.SetOwner(this); // make sure everything works even if the owner was lost for some reason. } } bool recreate = false; if (nbSlots != expectedProperties.Length) { recreate = true; } else { for (int i = 0; i < nbSlots; ++i) { if (!currentSlots[i].property.Equals(expectedProperties[i].property)) { recreate = true; break; } } } if (recreate) { var existingSlots = new List <VFXSlot>(currentSlots); // Remove all slots for (int i = nbSlots - 1; i >= 0; --i) { InnerRemoveSlot(currentSlots[i], false); } var newSlotCount = expectedProperties.Length; var newSlots = new VFXSlot[newSlotCount]; var createdSlots = new List <VFXSlot>(newSlotCount); // Reuse slots that already exists or create a new one if not for (int i = 0; i < newSlotCount; ++i) { var p = expectedProperties[i]; var slot = existingSlots.Find(s => p.property.Equals(s.property)); if (slot != null) { slot.UpdateAttributes(p.property.attributes); existingSlots.Remove(slot); } else { slot = VFXSlot.Create(p, direction); createdSlots.Add(slot); } newSlots[i] = slot; } for (int i = 0; i < createdSlots.Count; ++i) { var dstSlot = createdSlots[i]; // Try to keep links and value for slots of same name and compatible types var srcSlot = existingSlots.FirstOrDefault(s => s.property.name == dstSlot.property.name); // Find the first slot with same type (should perform a more clever selection based on name distance) if (srcSlot == null) { srcSlot = existingSlots.FirstOrDefault(s => s.property.type == dstSlot.property.type); } // Try to find a slot that can be implicitely converted if (srcSlot == null) { srcSlot = existingSlots.FirstOrDefault(s => VFXConverter.CanConvertTo(s.property.type, dstSlot.property.type)); } if (srcSlot != null) { VFXSlot.CopyLinksAndValue(dstSlot, srcSlot, notify); srcSlot.UnlinkAll(true, notify); existingSlots.Remove(srcSlot); } } // Remove all remaining links foreach (var slot in existingSlots) { slot.UnlinkAll(true, notify); } // Add all slots foreach (var s in newSlots) { InnerAddSlot(s, -1, false); } if (notify) { Invalidate(InvalidationCause.kStructureChanged); } } else { // Update properties for (int i = 0; i < nbSlots; ++i) { VFXProperty prop = currentSlots[i].property; currentSlots[i].UpdateAttributes(expectedProperties[i].property.attributes); } } return(recreate); }
public static void ToOperatorWithoutFloatN(VFXOperator input, Type outputType) { if (!input.inputSlots.Where(o => o.property.type == typeof(FloatN)).Any()) { Debug.LogError("SanitizeToOperatorNew is dedicated to operator using FloatN " + input); return; } var realTypeAndValue = input.inputSlots.Select(o => { Type type = null; object value = null; bool wasFloatN = o.property.type == typeof(FloatN); if (!wasFloatN) { type = o.property.type; value = o.HasLink() ? null : o.value; } else { if (o.HasLink()) { type = o.LinkedSlots.First().property.type; } else { var floatN = (FloatN)o.value; type = floatN.GetCurrentType(); value = (FloatN)o.value; } } return(new { type = type, value = value, wasFloatN = wasFloatN }); }).ToArray(); var output = ScriptableObject.CreateInstance(outputType) as VFXOperatorDynamicOperand; //Transfer settings var settingsIn = input.GetSettings(true).Where(o => o.FieldType != typeof(SerializableType)); var settingsOut = output.GetSettings(true).Where(o => o.FieldType != typeof(SerializableType)); if (settingsIn.Count() != settingsOut.Count()) { Debug.LogError("Settings has been changed, unable to automatically transfer them"); return; } var itSettingIn = settingsIn.GetEnumerator(); var itSettingOut = settingsOut.GetEnumerator(); while (itSettingIn.MoveNext() && itSettingOut.MoveNext()) { if (itSettingIn.Current.Name != itSettingOut.Current.Name.Replace("m_", string.Empty)) { Debug.Log(string.Format("Unexpected settings : {0} vs {1}", itSettingIn.Current.Name, itSettingOut.Current.Name)); return; } output.SetSettingValue(itSettingOut.Current.Name, itSettingIn.Current.GetValue(input)); } //Apply dynamic type behavior if (output is IVFXOperatorUniform) { var uniform = output as IVFXOperatorUniform; var maxType = realTypeAndValue.Where(o => o.wasFloatN && o.type != typeof(FloatN)) .Select(o => o.type) .OrderBy(o => VFXExpression.TypeToSize(VFXExpression.GetVFXValueTypeFromType(o))) .LastOrDefault(); if (maxType != null) { //ignore int/uint while sanitizing if (maxType == typeof(int) || maxType == typeof(uint)) { maxType = typeof(float); } maxType = output.GetBestAffinityType(maxType); uniform.SetOperandType(maxType); } } else if (output is VFXOperatorNumericCascadedUnified) { var cascaded = output as VFXOperatorNumericCascadedUnified; //Remove all empty last operand (has influence of output type for append) realTypeAndValue = realTypeAndValue.Reverse().SkipWhile(o => o.type == typeof(FloatN)).Reverse().ToArray(); while (cascaded.inputSlots.Count < realTypeAndValue.Length) { cascaded.AddOperand(); } for (int i = 0; i < realTypeAndValue.Length; ++i) { var currentType = cascaded.GetBestAffinityType(realTypeAndValue[i].type); if (currentType != null) { cascaded.SetOperandType(i, currentType); } } } else if (output is VFXOperatorNumericUnified) { var unified = output as VFXOperatorNumericUnified; var slotIndiceThatShouldHaveSameType = Enumerable.Empty <int>(); var slotIndiceThatCanBeScale = Enumerable.Empty <int>(); if (output is IVFXOperatorNumericUnifiedConstrained) { slotIndiceThatShouldHaveSameType = (output as IVFXOperatorNumericUnifiedConstrained).slotIndicesThatMustHaveSameType; slotIndiceThatCanBeScale = (output as IVFXOperatorNumericUnifiedConstrained).slotIndicesThatCanBeScalar; } Type maxTypeConstrained = null; if (slotIndiceThatShouldHaveSameType.Any()) { var typeConstrained = slotIndiceThatShouldHaveSameType.Select(i => { if (i < realTypeAndValue.Length) { return(realTypeAndValue[i].type); } return((Type)null); }).Where(o => o != null); if (!typeConstrained.Any()) { Debug.LogError("Unexpected behavior while sanitizing to unified constrained"); return; } maxTypeConstrained = typeConstrained.OrderBy(o => VFXExpression.TypeToSize(VFXExpression.GetVFXValueTypeFromType(o))).LastOrDefault(); } for (int i = 0; i < realTypeAndValue.Length; ++i) { var currentType = realTypeAndValue[i].type; if (slotIndiceThatShouldHaveSameType.Contains(i) && !(slotIndiceThatCanBeScale.Contains(i) && VFXExpression.GetMatchingScalar(currentType) == currentType)) { currentType = maxTypeConstrained; } currentType = output.GetBestAffinityType(currentType); if (currentType != null) { unified.SetOperandType(i, currentType); } } } else { Debug.LogError("Unable to determine what kind of " + output.GetType()); return; } for (int i = 0; i < realTypeAndValue.Length; ++i) { var current = realTypeAndValue[i]; if (!current.wasFloatN) { VFXSlot.CopyLinksAndValue(output.inputSlots[i], input.inputSlots[i], true); } else { if (current.value == null) { var slotDst = output.inputSlots[i]; var slotSrc = input.inputSlots[i].LinkedSlots.First(); if (slotSrc.CanLink(slotDst)) { //Main path (most common case) VFXSlot.CopyLinks(output.inputSlots[i], input.inputSlots[i], true); } else { //Trying to connect by subslot (e.g. : Vector4 to Vector3) var itSubSlotSrc = slotSrc.children.GetEnumerator(); var itSubSlotDst = slotDst.children.GetEnumerator(); while (itSubSlotDst.MoveNext() && itSubSlotSrc.MoveNext()) { itSubSlotSrc.Current.Link(itSubSlotDst.Current); } } } else { object orgValue = null; var floatN = (FloatN)current.value; var targetType = output.inputSlots[i].property.type; if (targetType == typeof(float)) { orgValue = (float)floatN; } else if (targetType == typeof(Vector2)) { orgValue = (Vector2)floatN; } else if (targetType == typeof(Vector3)) { orgValue = (Vector3)floatN; } else if (targetType == typeof(Vector4)) { orgValue = (Vector4)floatN; } else { Debug.LogError("Unexpected type of FloatN while sanitizing : " + targetType); return; } output.inputSlots[i].value = orgValue; } } } for (int i = 0; i < input.outputSlots.Count; ++i) { VFXSlot.CopyLinks(output.outputSlots[i], input.outputSlots[i], true); } VFXModel.ReplaceModel(output, input); }
public static void MigrateTConeFromCylinder(VFXSlot to, VFXSlot from) { var lastModel = from.owner as VFXModel; while (!(lastModel.GetParent() is VFXGraph)) { lastModel = lastModel.GetParent(); } var basePosition = lastModel.position; var graph = lastModel.GetParent() as VFXGraph; var to_center = to[0][0]; var to_baseRadius = to[1]; var to_topRadius = to[2]; var to_height = to[3]; var refSlot = from.refSlot; VFXSlot.CopySpace(to, refSlot, true); if (from.HasLink(false)) { var parentCenter = refSlot[0]; var parentRadius = refSlot[1]; var parentHeight = refSlot[2]; var correctedPosition = CorrectPositionFromCylinderToCone(graph, basePosition, parentHeight, parentCenter); correctedPosition.Link(to_center); to_baseRadius.Link(parentRadius, true); to_topRadius.Link(parentRadius, true); to_height.Link(parentHeight, true); } else { var center = from[0]; var radius = from[1]; var height = from[2]; var value = new TCone() { transform = new Transform() { position = (Vector3)center.value - new Vector3(0, (float)height.value * 0.5f, 0), scale = Vector3.one }, height = (float)height.value, baseRadius = (float)radius.value, topRadius = (float)radius.value, }; to.value = value; if (from.HasLink(true)) { var correctedPosition = CorrectPositionFromCylinderToCone(graph, basePosition, height, center); correctedPosition.Link(to_center); } VFXSlot.CopyLinksAndValue(to_baseRadius, radius, true); VFXSlot.CopyLinksAndValue(to_topRadius, radius, true); VFXSlot.CopyLinksAndValue(to_height, height, true); } }