diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index 2b63bdfb2..e5513b598 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -983,6 +983,103 @@ namespace FlaxEditor.Surface.Archetypes } } + private class RerouteNode : SurfaceNode + { + public static readonly Vector2 DefaultSize = new Vector2(16); + + /// + protected override bool ShowTooltip => false; + + /// + public RerouteNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) + : base(id, context, nodeArch, groupArch) + { + Size = DefaultSize; + Title = string.Empty; + BackgroundColor = Color.Transparent; + } + + /// + public override void OnSurfaceLoaded() + { + base.OnSurfaceLoaded(); + + var inputBox = GetBox(0); + var outputBox = GetBox(1); + inputBox.Location = Vector2.Zero; + outputBox.Location = Vector2.Zero; + + UpdateBoxes(); + } + + /// + public override void ConnectionTick(Box box) + { + base.ConnectionTick(box); + + UpdateBoxes(); + } + + private void UpdateBoxes() + { + var inputBox = GetBox(0); + var outputBox = GetBox(1); + + inputBox.Visible = !inputBox.HasAnyConnection; + outputBox.Visible = !outputBox.HasAnyConnection; + } + + /// + public override bool CanSelect(ref Vector2 location) + { + return new Rectangle(Location, DefaultSize).Contains(ref location); + } + + /// + protected override void UpdateRectangles() + { + _headerRect = Rectangle.Empty; + _closeButtonRect = Rectangle.Empty; + _footerRect = Rectangle.Empty; + } + + public override void Draw() + { + var style = Surface.Style; + var inputBox = GetBox(0); + var outputBox = GetBox(1); + var connectionColor = style.Colors.Default; + float barHorizontalOffset = -2; + float barHeight = 3; + + if (inputBox.HasAnyConnection) + { + var hints = inputBox.Connections[0].ParentNode.Archetype.ConnectionsHints; + Surface.Style.GetConnectionColor(inputBox.Connections[0].CurrentType, hints, out connectionColor); + } + + if (!inputBox.HasAnyConnection) + { + Render2D.FillRectangle(new Rectangle(-barHorizontalOffset - barHeight * 2, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), connectionColor); + } + + if (!outputBox.HasAnyConnection) + { + Render2D.FillRectangle(new Rectangle(DefaultSize.X + barHorizontalOffset, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), connectionColor); + } + + if (inputBox.HasAnyConnection && outputBox.HasAnyConnection) + { + var hints = inputBox.Connections[0].ParentNode.Archetype.ConnectionsHints; + Surface.Style.GetConnectionColor(inputBox.Connections[0].CurrentType, hints, out connectionColor); + SpriteHandle icon = style.Icons.BoxClose; + Render2D.DrawSprite(icon, new Rectangle(Vector2.Zero, DefaultSize), connectionColor); + } + + base.Draw(); + } + } + /// /// The nodes for that group. /// @@ -1438,6 +1535,23 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(object), 1), } }, + new NodeArchetype + { + TypeID = 29, + Title = "Reroute", + Create = (id, context, arch, groupArch) => new RerouteNode(id, context, arch, groupArch), + Description = "Reroute a connection.", + Flags = NodeFlags.NoCloseButton | NodeFlags.NoSpawnViaGUI | NodeFlags.AllGraphs, + Size = RerouteNode.DefaultSize, + ConnectionsHints = ConnectionsHint.All, + IndependentBoxes = new int[] { 0 }, + DependentBoxes = new int[] { 1 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, string.Empty, true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, null, 1, true), + } + }, }; } } diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 015b9213d..9690d5fd3 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -159,6 +159,7 @@ namespace FlaxEditor.Surface.ContextMenu var start = font.GetCharPosition(_archetype.Title, 0); var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height)); + _isFullMatch = true; Visible = true; } else if (NodeArchetype.TryParseText != null && NodeArchetype.TryParseText(filterText, out var data)) diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index 0c3ba8da4..16094d621 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -2,6 +2,7 @@ using FlaxEngine; using FlaxEngine.GUI; +using System; namespace FlaxEditor.Surface.Elements { @@ -12,6 +13,11 @@ namespace FlaxEditor.Surface.Elements [HideInEditor] public class OutputBox : Box { + /// + /// Distance for the mouse to be considered above the connection + /// + public float MouseOverConnectionDistance => 100f / Surface.ViewScale; + /// public OutputBox(SurfaceNode parentNode, NodeElementArchetype archetype) : base(parentNode, archetype, archetype.Position + new Vector2(parentNode.Archetype.Size.X, 0)) @@ -43,23 +49,96 @@ namespace FlaxEditor.Surface.Elements */ } + /// + /// Checks if a point intersects a connection + /// + /// The other box. + /// The mouse position + public bool IntersectsConnection(Box targetBox, ref Vector2 mousePosition) + { + var startPos = Parent.PointToParent(Center); + Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center); + return IntersectsConnection(ref startPos, ref endPos, ref mousePosition, MouseOverConnectionDistance); + } + + /// + /// Checks if a point intersects a bezier curve + /// + /// The start location. + /// The end location. + /// The point + /// Distance at which its an intersection + public static bool IntersectsConnection(ref Vector2 start, ref Vector2 end, ref Vector2 point, float distance) + { + // Pretty much a point in rectangle check + if ((point.X - start.X) * (end.X - point.X) < 0) return false; + + float offset = Mathf.Sign(end.Y - start.Y) * distance; + if ((point.Y - (start.Y - offset)) * ((end.Y + offset) - point.Y) < 0) return false; + + // Taken from the Render2D.DrawBezier code + float squaredDistance = distance; + + var dst = (end - start) * new Vector2(0.5f, 0.05f); + Vector2 control1 = new Vector2(start.X + dst.X, start.Y + dst.Y); + Vector2 control2 = new Vector2(end.X - dst.X, end.Y + dst.Y); + + Vector2 d1 = control1 - start; + Vector2 d2 = control2 - control1; + Vector2 d3 = end - control2; + float len = d1.Length + d2.Length + d3.Length; + int segmentCount = Math.Min(Math.Max(Mathf.CeilToInt(len * 0.05f), 1), 100); + float segmentCountInv = 1.0f / segmentCount; + + Bezier(ref start, ref control1, ref control2, ref end, 0, out Vector2 p); + for (int i = 1; i <= segmentCount; i++) + { + Vector2 oldp = p; + float t = i * segmentCountInv; + Bezier(ref start, ref control1, ref control2, ref end, t, out p); + + // Maybe it would be reasonable to return the point? + CollisionsHelper.ClosestPointPointLine(ref point, ref oldp, ref p, out Vector2 result); + if (Vector2.DistanceSquared(point, result) <= squaredDistance) + { + return true; + } + } + return false; + } + + private static void Bezier(ref Vector2 p0, ref Vector2 p1, ref Vector2 p2, ref Vector2 p3, float alpha, out Vector2 result) + { + Vector2.Lerp(ref p0, ref p1, alpha, out var p01); + Vector2.Lerp(ref p1, ref p2, alpha, out var p12); + Vector2.Lerp(ref p2, ref p3, alpha, out var p23); + Vector2.Lerp(ref p01, ref p12, alpha, out var p012); + Vector2.Lerp(ref p12, ref p23, alpha, out var p123); + Vector2.Lerp(ref p012, ref p123, alpha, out result); + } + /// /// Draw all connections coming from this box. /// - public void DrawConnections() + public void DrawConnections(ref Vector2 mousePosition) { + float mouseOverDistance = MouseOverConnectionDistance; // Draw all the connections - var center = Size * 0.5f; - var tmp = PointToParent(ref center); - var startPos = Parent.PointToParent(ref tmp); + var startPos = Parent.PointToParent(Center); var startHighlight = ConnectionsHighlightIntensity; for (int i = 0; i < Connections.Count; i++) { Box targetBox = Connections[i]; - tmp = targetBox.PointToParent(ref center); - Vector2 endPos = targetBox.Parent.PointToParent(ref tmp); + Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center); var highlight = 1 + Mathf.Max(startHighlight, targetBox.ConnectionsHighlightIntensity); var color = _currentTypeColor * highlight; + + // TODO: Figure out how to only draw the topmost connection + if (IntersectsConnection(ref startPos, ref endPos, ref mousePosition, mouseOverDistance)) + { + highlight += 0.5f; + } + DrawConnection(ref startPos, ref endPos, ref color, highlight); } } @@ -70,12 +149,9 @@ namespace FlaxEditor.Surface.Elements public void DrawSelectedConnection(Box targetBox) { // Draw all the connections - var center = Size * 0.5f; - var tmp = PointToParent(ref center); - var startPos = Parent.PointToParent(ref tmp); - tmp = targetBox.PointToParent(ref center); - Vector2 endPos = targetBox.Parent.PointToParent(ref tmp); - DrawConnection(ref startPos, ref endPos, ref _currentTypeColor, 2); + var startPos = Parent.PointToParent(Center); + Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center); + DrawConnection(ref startPos, ref endPos, ref _currentTypeColor, 2.5f); } /// diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 9ace32695..ff1d376e7 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -125,6 +125,7 @@ namespace FlaxEditor.Surface AutoFocus = false; TooltipText = nodeArch.Description; CullChildren = false; + BackgroundColor = Style.Current.BackgroundNormal; if (Archetype.DefaultValues != null) { @@ -552,19 +553,22 @@ namespace FlaxEditor.Surface internal Box GetNextBox(Box box) { - int i = 0; - for (; i < Elements.Count; i++) + // Get the one after it + for (int i = box.IndexInParent + 1; i < Elements.Count; i++) { - if (Elements[i] == box) + if (Elements[i] is Box b) { - // We found the box - break; + return b; } } - // Get the one after it - i++; - for (; i < Elements.Count; i++) + return null; + } + + internal Box GetPreviousBox(Box box) + { + // Get the one before it + for (int i = box.IndexInParent - 1; i >= 0; i--) { if (Elements[i] is Box b) { @@ -754,29 +758,33 @@ namespace FlaxEditor.Surface { if (Elements[j] is OutputBox ob && ob.HasAnyConnection) { - ob.DrawConnections(); + ob.DrawConnections(ref mousePosition); } } + } + + /// + /// Draws all selected connections between surface objects related to this node. + /// + /// The index of the currently selected connection. + public void DrawSelectedConnections(int selectedConnectionIndex) + { if (_isSelected) { - bool hasBoxesSelection = HasBoxesSelection; - for (int j = 0; j < Elements.Count; j++) + if (HasBoxesSelection) { - if (Elements[j] is Box box && box.HasAnyConnection && (!hasBoxesSelection || box.IsSelected)) + for (int j = 0; j < Elements.Count; j++) { - if (box is OutputBox ob) + if (Elements[j] is Box box && box.IsSelected && selectedConnectionIndex < box.Connections.Count) { - for (int i = 0; i < ob.Connections.Count; i++) + if (box is OutputBox ob) { - ob.DrawSelectedConnection(ob.Connections[i]); + ob.DrawSelectedConnection(ob.Connections[selectedConnectionIndex]); } - } - else - { - for (int i = 0; i < box.Connections.Count; i++) + else { - if (box.Connections[i] is OutputBox outputBox) + if (box.Connections[selectedConnectionIndex] is OutputBox outputBox) { outputBox.DrawSelectedConnection(box); } @@ -784,6 +792,32 @@ namespace FlaxEditor.Surface } } } + else + { + for (int j = 0; j < Elements.Count; j++) + { + if (Elements[j] is Box box) + { + if (box is OutputBox ob) + { + for (int i = 0; i < ob.Connections.Count; i++) + { + ob.DrawSelectedConnection(ob.Connections[i]); + } + } + else + { + for (int i = 0; i < box.Connections.Count; i++) + { + if (box.Connections[i] is OutputBox outputBox) + { + outputBox.DrawSelectedConnection(box); + } + } + } + } + } + } } } @@ -943,7 +977,7 @@ namespace FlaxEditor.Surface // Background var backgroundRect = new Rectangle(Vector2.Zero, Size); - Render2D.FillRectangle(backgroundRect, style.BackgroundNormal); + Render2D.FillRectangle(backgroundRect, BackgroundColor); // Breakpoint hit if (Breakpoint.Hit) @@ -1006,7 +1040,7 @@ namespace FlaxEditor.Surface } // Secondary Context Menu - if (button == MouseButton.Right && false) + if (button == MouseButton.Right) { if (!IsSelected) Surface.Select(this); diff --git a/Source/Editor/Surface/Undo/BoxHandle.cs b/Source/Editor/Surface/Undo/BoxHandle.cs index f50adcfeb..4b77b95f7 100644 --- a/Source/Editor/Surface/Undo/BoxHandle.cs +++ b/Source/Editor/Surface/Undo/BoxHandle.cs @@ -10,7 +10,7 @@ namespace FlaxEditor.Surface.Undo /// The helper structure for Surface node box handle. /// [HideInEditor] - public struct BoxHandle + public struct BoxHandle : IEquatable { private readonly uint _nodeId; private readonly int _boxId; @@ -51,5 +51,27 @@ namespace FlaxEditor.Surface.Undo throw new Exception("Missing box."); return box; } + + /// + public override bool Equals(object obj) + { + return obj is BoxHandle handle && Equals(handle); + } + + /// + public bool Equals(BoxHandle other) + { + return _nodeId == other._nodeId && + _boxId == other._boxId; + } + + /// + public override int GetHashCode() + { + unchecked + { + return (_nodeId.GetHashCode() * 397) ^ _boxId.GetHashCode(); + } + } } } diff --git a/Source/Editor/Surface/Undo/ConnectBoxesAction.cs b/Source/Editor/Surface/Undo/ConnectBoxesAction.cs index c200f147d..cfd0e5c33 100644 --- a/Source/Editor/Surface/Undo/ConnectBoxesAction.cs +++ b/Source/Editor/Surface/Undo/ConnectBoxesAction.cs @@ -106,9 +106,12 @@ namespace FlaxEditor.Surface.Undo } for (int i = 0; i < output.Length; i++) { - var box = output[i].Get(context); - oB.Connections.Add(box); - box.Connections.Add(oB); + if (!output[i].Equals(_input)) + { + var box = output[i].Get(context); + oB.Connections.Add(box); + box.Connections.Add(oB); + } } toUpdate.AddRange(iB.Connections); diff --git a/Source/Editor/Surface/VisjectSurface.CopyPaste.cs b/Source/Editor/Surface/VisjectSurface.CopyPaste.cs index 0d991191a..2fbdb815c 100644 --- a/Source/Editor/Surface/VisjectSurface.CopyPaste.cs +++ b/Source/Editor/Surface/VisjectSurface.CopyPaste.cs @@ -103,7 +103,7 @@ namespace FlaxEditor.Surface { for (int j = 0; j < node.Elements.Count; j++) { - if (node.Elements[j] is Box box) + if (node.Elements[j] is Box box && box.Connections.Count > 0) { var dataModelBox = new DataModelBox { diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index 488324a0a..016e0083f 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -103,6 +103,7 @@ namespace FlaxEditor.Surface for (int i = 0; i < Nodes.Count; i++) { Nodes[i].DrawConnections(ref mousePosition); + Nodes[i].DrawSelectedConnections(_selectedConnectionIndex); } } diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index dec89e143..996f9d2e0 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -274,12 +274,47 @@ namespace FlaxEditor.Surface bool handled = base.OnMouseDoubleClick(location, button); if (!handled) CustomMouseDoubleClick?.Invoke(ref location, button, ref handled); - if (handled) + + if (!handled) { - return true; + var mousePos = _rootControl.PointFromParent(ref _mousePos); + if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox)) + { + if (Undo != null) + { + bool undoEnabled = Undo.Enabled; + Undo.Enabled = false; + var rerouteNode = Context.SpawnNode(7, 29, mousePos); + Undo.Enabled = undoEnabled; + + var spawnNodeAction = new AddRemoveNodeAction(rerouteNode, true); + + var disconnectBoxesAction = new ConnectBoxesAction(inputBox, outputBox, false); + inputBox.BreakConnection(outputBox); + disconnectBoxesAction.End(); + + var addConnectionsAction = new EditNodeConnections(Context, rerouteNode); + outputBox.CreateConnection(rerouteNode.GetBoxes().First(b => !b.IsOutput)); + rerouteNode.GetBoxes().First(b => b.IsOutput).CreateConnection(inputBox); + addConnectionsAction.End(); + + + Undo.AddAction(new MultiUndoAction(spawnNodeAction, disconnectBoxesAction, addConnectionsAction)); + } + else + { + var rerouteNode = Context.SpawnNode(7, 29, mousePos); + inputBox.BreakConnection(outputBox); + outputBox.CreateConnection(rerouteNode.GetBoxes().First(b => !b.IsOutput)); + rerouteNode.GetBoxes().First(b => b.IsOutput).CreateConnection(inputBox); + } + MarkAsEdited(); + + handled = true; + } } - return false; + return handled; } /// @@ -527,6 +562,81 @@ namespace FlaxEditor.Surface } return true; } + if (key == KeyboardKeys.ArrowUp || key == KeyboardKeys.ArrowDown) + { + Box selectedBox = GetSelectedBox(SelectedNodes); + if (selectedBox == null) return true; + + Box toSelect = (key == KeyboardKeys.ArrowUp) ? + selectedBox?.ParentNode.GetPreviousBox(selectedBox) : + selectedBox?.ParentNode.GetNextBox(selectedBox); + + if (toSelect != null && toSelect.IsOutput == selectedBox.IsOutput) + { + Select(toSelect.ParentNode); + toSelect.ParentNode.SelectBox(toSelect); + } + } + + if (key == KeyboardKeys.Tab) + { + Box selectedBox = GetSelectedBox(SelectedNodes); + if (selectedBox == null) return true; + + int connectionCount = selectedBox.Connections.Count; + if (connectionCount == 0) return true; + + if (Root.GetKey(KeyboardKeys.Shift)) + { + _selectedConnectionIndex = ((_selectedConnectionIndex - 1) % connectionCount + connectionCount) % connectionCount; + } + else + { + _selectedConnectionIndex = (_selectedConnectionIndex + 1) % connectionCount; + } + } + + + if (key == KeyboardKeys.ArrowRight || key == KeyboardKeys.ArrowLeft) + { + Box selectedBox = GetSelectedBox(SelectedNodes); + if (selectedBox == null) return true; + + Box toSelect = null; + + if ((key == KeyboardKeys.ArrowRight && selectedBox.IsOutput) || (key == KeyboardKeys.ArrowLeft && !selectedBox.IsOutput)) + { + if (_selectedConnectionIndex < 0 || _selectedConnectionIndex >= selectedBox.Connections.Count) + { + _selectedConnectionIndex = 0; + } + toSelect = selectedBox.Connections[_selectedConnectionIndex]; + } + else + { + // Use the node with the closest Y-level + // Since there are cases like 3 nodes on one side and only 1 node on the other side + + var elements = selectedBox.ParentNode.Elements; + float minDistance = float.PositiveInfinity; + for (int i = 0; i < elements.Count; i++) + { + if (elements[i] is Box box && box.IsOutput != selectedBox.IsOutput && Mathf.Abs(box.Y - selectedBox.Y) < minDistance) + { + toSelect = box; + minDistance = Mathf.Abs(box.Y - selectedBox.Y); + } + } + } + + if (toSelect != null) + { + Select(toSelect.ParentNode); + toSelect.ParentNode.SelectBox(toSelect); + + } + return true; + } } return false; @@ -720,5 +830,31 @@ namespace FlaxEditor.Surface yLocation ); } + + private bool IntersectsConnection(Vector2 mousePosition, out InputBox inputBox, out OutputBox outputBox) + { + for (int i = 0; i < Nodes.Count; i++) + { + for (int j = 0; j < Nodes[i].Elements.Count; j++) + { + if (Nodes[i].Elements[j] is OutputBox ob) + { + for (int k = 0; k < ob.Connections.Count; k++) + { + if (ob.IntersectsConnection(ob.Connections[k], ref mousePosition)) + { + outputBox = ob; + inputBox = ob.Connections[k] as InputBox; + return true; + } + } + } + } + } + + outputBox = null; + inputBox = null; + return false; + } } } diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs index 33786c935..7a136ea38 100644 --- a/Source/Editor/Surface/VisjectSurface.cs +++ b/Source/Editor/Surface/VisjectSurface.cs @@ -39,6 +39,7 @@ namespace FlaxEditor.Surface private GroupArchetype _customNodesGroup; private List _customNodes; private Action _onSave; + private int _selectedConnectionIndex; internal int _isUpdatingBoxTypes; @@ -362,6 +363,8 @@ namespace FlaxEditor.Surface Context.ControlSpawned += OnSurfaceControlSpawned; Context.ControlDeleted += OnSurfaceControlDeleted; + SelectionChanged += () => { _selectedConnectionIndex = 0; }; + // Init drag handlers DragHandlers.Add(_dragAssets = new DragAssets(ValidateDragItem)); DragHandlers.Add(_dragParameters = new DragNames(SurfaceParameter.DragPrefix, ValidateDragParameter)); diff --git a/Source/Engine/Engine/Time.h b/Source/Engine/Engine/Time.h index 0d44afa22..eb176ae7c 100644 --- a/Source/Engine/Engine/Time.h +++ b/Source/Engine/Engine/Time.h @@ -121,8 +121,11 @@ public: API_FIELD() static float PhysicsFPS; /// - /// The target amount of the frames rendered per second (actual game FPS). + /// The target amount of the frames rendered per second (target game FPS). /// + /// + /// To get the actual game FPS use + /// API_FIELD() static float DrawFPS; /// diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index 57c4d7746..abb83bd9b 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -770,6 +770,11 @@ void ShaderGenerator::ProcessGroupTools(Box* box, Node* node, Value& value) #undef PLATFORM_CASE break; } + case 29: + { + value = tryGetValue(node->GetBox(0), Value::Zero); + break; + } default: break; } diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index abe4436dd..8e15ce87b 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -738,6 +738,12 @@ void VisjectExecutor::ProcessGroupTools(Box* box, Node* node, Value& value) value = Scripting::FindObject((Guid)node->Values[0]); break; } + + case 29: + { + value = tryGetValue(node->GetBox(0), Value::Zero); + break; + } default: break; }