Got most of the way to introducing new connections into the node workspace.

This commit is contained in:
Peter Slattery 2019-12-30 23:13:04 -08:00
parent 2bfed22bc2
commit 7545e3bbd0
2 changed files with 150 additions and 87 deletions

View File

@ -144,6 +144,15 @@ struct node_interaction
s32 OutputValue;
};
struct node_render_settings
{
v4 PortColors[MemberTypeCount];
bitmap_font* Font;
b32 Display;
};
// ^^ OLD ^^
struct node_struct_member
{
struct_member_type Type;
@ -161,20 +170,11 @@ struct node_specification
node_struct_member* MemberList;
u32 DataStructSize;
u32 MemberListLength;
b32 IsPattern;
};
struct node_render_settings
{
v4 PortColors[MemberTypeCount];
bitmap_font* Font;
b32 Display;
};
// ^^ OLD ^^
struct pattern_node
{
// TODO(Peter): Something to think about further down the line is the fact that

View File

@ -4,6 +4,13 @@ struct visual_node
v2 Position;
};
struct visual_port
{
u32 SparseNodeIndex;
u32 PortIndex;
rect PortBounds;
};
struct node_layout
{
// NOTE(Peter): This Map is a sparse array.
@ -17,8 +24,15 @@ struct node_layout
u32* VisualNodeLayers;
u32 VisualNodesCount;
visual_port* VisualPorts;
u32 VisualPortsCount;
u32 LayerCount;
v2* LayerPositions;
b32 ConnectionIsInProgress;
v2 InProgressConnectionStart;
v2 InProgressConnectionEnd;
};
struct node_graph_state
@ -72,33 +86,65 @@ FOLDHAUS_INPUT_COMMAND_PROC(BeginPanNodeGraph)
OPERATION_STATE_DEF(connect_nodes_operation_state)
{
u32 NodeIndex;
u32 PortIndex;
visual_port VisualPort;
u32 VisualPortIndex;
b32 IsInput;
};
OPERATION_RENDER_PROC(UpdateConnectNodeOperation)
{
panel_and_bounds NodeGraphPanel = GetPanelContainingPoint(Mouse.DownPos, &State->PanelSystem, State->WindowBounds);
node_graph_state* GraphState = (node_graph_state*)NodeGraphPanel.Panel->PanelStateMemory;
GraphState->Layout.InProgressConnectionEnd = Mouse.Pos;
}
FOLDHAUS_INPUT_COMMAND_PROC(EndConnectNodesOperation)
{
connect_nodes_operation_state* OpState = GetCurrentOperationState(State->Modes, connect_nodes_operation_state);
panel_and_bounds NodeGraphPanel = GetPanelContainingPoint(Mouse.DownPos, &State->PanelSystem, State->WindowBounds);
node_graph_state* GraphState = (node_graph_state*)NodeGraphPanel.Panel->PanelStateMemory;
GraphState->Layout.ConnectionIsInProgress = false;
for (u32 p = 0; p < GraphState->Layout.VisualPortsCount; p++)
{
visual_port VisualPort = GraphState->Layout.VisualPorts[p];
rect ViewAdjustedBounds = RectOffsetByVector(VisualPort.PortBounds, GraphState->ViewOffset);
if (PointIsInRect(Mouse.Pos, ViewAdjustedBounds))
{
pattern_node_connection Connection = {};
visual_port UpstreamPort = (OpState->IsInput & IsInputMember) ? VisualPort : OpState->VisualPort;
visual_port DownstreamPort = (OpState->IsInput & IsInputMember) ? OpState->VisualPort : VisualPort;
// Make Connection
// TODO(Peter): START HERE
//start here;
// You were working on connection UpstreamPort and DownstreamPort
// You need to get each node's handle and add a new connection to the connection bucket
}
}
EndCurrentOperationMode(State, Event, Mouse);
}
input_command ConnectNodesOperationCommands[] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, EndConnectNodesOperation },
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndConnectNodesOperation },
};
internal void
BeginConnectNodesOperation(u32 NodeIndex, u32 PortIndex, app_state* State)
BeginConnectNodesOperation(visual_port VisualPort, u32 VisualPortIndex, mouse_state Mouse, app_state* State)
{
operation_mode* ConnectNodesOperation = ActivateOperationModeWithCommands(&State->Modes, ConnectNodesOperationCommands, UpdateConnectNodeOperation);
connect_nodes_operation_state* OpState = CreateOperationState(ConnectNodesOperation, &State->Modes, connect_nodes_operation_state);
OpState->NodeIndex = NodeIndex;
OpState->PortIndex = PortIndex;
OpState->VisualPort = VisualPort;
OpState->VisualPortIndex = VisualPortIndex;
panel_and_bounds NodeGraphPanel = GetPanelContainingPoint(Mouse.DownPos, &State->PanelSystem, State->WindowBounds);
node_graph_state* GraphState = (node_graph_state*)NodeGraphPanel.Panel->PanelStateMemory;
GraphState->Layout.ConnectionIsInProgress = true;
GraphState->Layout.InProgressConnectionStart = CalculateRectCenter(VisualPort.PortBounds);
}
//
@ -106,7 +152,7 @@ BeginConnectNodesOperation(u32 NodeIndex, u32 PortIndex, app_state* State)
//
input_command NodeGraph_Commands[] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, BeginPanNodeGraph }
{ 0 }
};
PANEL_INIT_PROC(NodeGraph_Init)
@ -161,11 +207,9 @@ DrawGrid (v2 Offset, v2 GridSquareDim, rect PanelBounds, render_command_buffer*
}
internal s32
internal void
DrawNodePorts(node_specification Spec, b32 InputMask, v2 Position, r32 LineHeight, string_alignment TextAlign, v2 TextOffset, interface_config Interface, render_command_buffer* RenderBuffer, mouse_state Mouse)
{
s32 PortClicked = -1;
rect PortBounds = rect{v2{0, 0}, v2{6, 6}};
v2 LinePosition = Position;
@ -174,39 +218,18 @@ DrawNodePorts(node_specification Spec, b32 InputMask, v2 Position, r32 LineHeigh
node_struct_member Member = Spec.MemberList[i];
if ((Member.IsInput & InputMask) > 0)
{
// TODO(Peter): Can we make this rely on the same data that we use to
// render the actual connection points?
string MemberName = MakeString(Member.Name, CharArrayLength(Member.Name));
DrawString(RenderBuffer, MemberName, Interface.Font, LinePosition + TextOffset, WhiteV4, TextAlign);
rect PositionedPortBounds = PortBounds;
PositionedPortBounds.Min += LinePosition + v2{0, LineHeight / 4};
PositionedPortBounds.Max += LinePosition + v2{0, LineHeight / 4};
if (TextAlign == Align_Left)
{
PositionedPortBounds.Min -= v2{PortBounds.Max.x, 0};
PositionedPortBounds.Max -= v2{PortBounds.Max.x, 0};
}
PushRenderQuad2D(RenderBuffer, PositionedPortBounds.Min, PositionedPortBounds.Max, WhiteV4);
if (MouseButtonTransitionedDown(Mouse.LeftButtonState)
&& PointIsInRect(Mouse.DownPos, PositionedPortBounds))
{
PortClicked = i;
}
LinePosition.y -= LineHeight;
}
}
return PortClicked;
}
internal s32
internal void
DrawNode (v2 Position, node_specification NodeSpecification, r32 NodeWidth, r32 LineHeight, interface_config Interface, render_command_buffer* RenderBuffer, mouse_state Mouse)
{
s32 PortClicked = -1;
u32 InputMembers = 0;
u32 OutputMembers = 0;
for (u32 i = 0; i < NodeSpecification.MemberListLength; i++)
@ -236,57 +259,32 @@ DrawNode (v2 Position, node_specification NodeSpecification, r32 NodeWidth, r32
DrawString(RenderBuffer, NodeName, Interface.Font, LinePosition + TextOffset, WhiteV4);
LinePosition.y -= LineHeight;
// Draw Ports
s32 InputPortClicked = DrawNodePorts(NodeSpecification, IsInputMember, LinePosition, LineHeight, Align_Left, TextOffset, Interface, RenderBuffer, Mouse);
DrawNodePorts(NodeSpecification, IsInputMember, LinePosition, LineHeight, Align_Left, TextOffset, Interface, RenderBuffer, Mouse);
v2 OutputLinePosition = v2{LinePosition.x + NodeDim.x, LinePosition.y };
v2 OutputTextOffset = v2{-TextOffset.x, TextOffset.y};
s32 OutputPortClicked = DrawNodePorts(NodeSpecification, IsOutputMember, OutputLinePosition, LineHeight, Align_Right, OutputTextOffset, Interface, RenderBuffer, Mouse);
if (InputPortClicked >= 0)
{
PortClicked = InputPortClicked;
}
else if (OutputPortClicked >= 0)
{
PortClicked = OutputPortClicked;
}
return PortClicked;
}
internal u32
FindLayerForNodeInList(gs_list_handle NodeHandle, gs_bucket<gs_list_handle> NodeLUT)
{
u32 Index = 0;
// TODO(Peter): This is turning this layout code into an n^2 lookup
for (u32 i = 0; i < NodeLUT.Used; i++)
{
gs_list_handle Handle = *NodeLUT.GetElementAtIndex(i);
if (GSListHandlesAreEqual(Handle, NodeHandle))
{
Index = i;
break;
}
}
return Index;
DrawNodePorts(NodeSpecification, IsOutputMember, OutputLinePosition, LineHeight, Align_Right, OutputTextOffset, Interface, RenderBuffer, Mouse);
}
internal node_layout
ArrangeNodes(pattern_node_workspace Workspace, r32 NodeWidth, r32 LayerDistance, memory_arena* Storage)
ArrangeNodes(pattern_node_workspace Workspace, r32 NodeWidth, r32 LayerDistance, r32 LineHeight, memory_arena* Storage)
{
node_layout Result = {};
Result.SparseToContiguousNodeMapCount = Workspace.Nodes.OnePastLastUsed;
Result.SparseToContiguousNodeMap = PushArray(Storage, s32, Result.SparseToContiguousNodeMapCount);
u32 DestinationIndex = 0;
Result.VisualPortsCount = 0;
for (u32 i = 0; i < Result.SparseToContiguousNodeMapCount; i++)
{
gs_list_entry<pattern_node>* Entry = Workspace.Nodes.GetEntryAtIndex(i);
if (!EntryIsFree(Entry))
{
Result.SparseToContiguousNodeMap[i] = DestinationIndex++;
pattern_node Node = Entry->Value;
node_specification Spec = NodeSpecifications[Node.SpecificationIndex];
Result.VisualPortsCount += Spec.MemberListLength;
}
else
{
@ -322,19 +320,53 @@ ArrangeNodes(pattern_node_workspace Workspace, r32 NodeWidth, r32 LayerDistance,
Result.LayerPositions[l] = v2{ (NodeWidth + LayerDistance) * FromRight, 0 };
}
// Place nodes
// Place nodes and connections
Result.VisualNodesCount = Workspace.Nodes.Used;
Result.VisualNodes = PushArray(Storage, visual_node, Result.VisualNodesCount);
for (u32 n = 0; n < Workspace.Nodes.Used; n++)
u32 VisualPortsUsed = 0;
Result.VisualPorts = PushArray(Storage, visual_port, Result.VisualPortsCount);
for (u32 n = 0; n < Result.SparseToContiguousNodeMapCount; n++)
{
u32 NodeIndex = Result.SparseToContiguousNodeMap[n];
pattern_node* Node = Workspace.Nodes.GetElementAtIndex(NodeIndex);
u32 SpecIndex = Node->SpecificationIndex;
Result.VisualNodes[n].Spec = NodeSpecifications[SpecIndex];
node_specification Spec = NodeSpecifications[SpecIndex];
u32 NodeLayer = Result.VisualNodeLayers[n];
Result.VisualNodes[n].Position = Result.LayerPositions[NodeLayer];
visual_node* VisualNode = Result.VisualNodes + n;
VisualNode->Spec = Spec;
VisualNode->Position = Result.LayerPositions[NodeLayer];
Result.LayerPositions[NodeLayer].y -= 200;
// NOTE(Peter): These start at 2 to account for the offset past the node title
s32 InputsCount = 2;
s32 OutputsCount = 2;
for (u32 p = 0; p < Spec.MemberListLength; p++)
{
node_struct_member Member = Spec.MemberList[p];
rect PortBounds = {0};
v2 PortDim = v2{8, 8};
PortBounds.Min = VisualNode->Position + v2{0, PortDim.y / 2};
if ((Member.IsInput & IsInputMember) > 0)
{
PortBounds.Min.y -= LineHeight * InputsCount++;
PortBounds.Min.x -= PortDim.x;
}
else if ((Member.IsInput & IsOutputMember) > 0)
{
PortBounds.Min.y -= LineHeight * OutputsCount++;
PortBounds.Min.x += NodeWidth;
}
PortBounds.Max = PortBounds.Min + v2{8, 8};
visual_port* VisualPort = Result.VisualPorts + VisualPortsUsed++;
VisualPort->SparseNodeIndex = n;
VisualPort->PortIndex = p;
VisualPort->PortBounds = PortBounds;
}
}
return Result;
@ -344,6 +376,7 @@ internal
PANEL_RENDER_PROC(NodeGraph_Render)
{
node_graph_state* GraphState = (node_graph_state*)Panel.PanelStateMemory;
b32 MouseHandled = false;
rect NodeSelectionWindowBounds = rect{
PanelBounds.Min,
@ -366,13 +399,13 @@ PANEL_RENDER_PROC(NodeGraph_Render)
ClearArena(&GraphState->LayoutMemory);
GraphState->Layout = {};
GraphState->Layout = ArrangeNodes(State->NodeWorkspace, NodeWidth, LayerDistance, &GraphState->LayoutMemory);
GraphState->Layout = ArrangeNodes(State->NodeWorkspace, NodeWidth, LayerDistance, LineHeight, &GraphState->LayoutMemory);
GraphState->LayoutIsDirty = false;
}
DrawGrid(GraphState->ViewOffset, v2{100, 100}, GraphBounds, RenderBuffer);
render_quad_batch_constructor ConnectionsLayer = PushRenderQuad2DBatch(RenderBuffer, State->NodeWorkspace.Connections.Used);
render_quad_batch_constructor ConnectionsLayer = PushRenderQuad2DBatch(RenderBuffer, State->NodeWorkspace.Connections.Used + 1);
for (u32 i = 0; i < State->NodeWorkspace.Connections.Used; i++)
{
pattern_node_connection Connection = *State->NodeWorkspace.Connections.GetElementAtIndex(i);
@ -389,14 +422,38 @@ PANEL_RENDER_PROC(NodeGraph_Render)
PushLine2DOnBatch(&ConnectionsLayer, LineStart, LineEnd, 1.5f, WhiteV4);
}
if (GraphState->Layout.ConnectionIsInProgress)
{
PushLine2DOnBatch(&ConnectionsLayer,
GraphState->Layout.InProgressConnectionStart,
GraphState->Layout.InProgressConnectionEnd,
1.5f, WhiteV4);
}
for (u32 i = 0; i < GraphState->Layout.VisualNodesCount; i++)
{
visual_node VisualNode = GraphState->Layout.VisualNodes[i];
s32 PortClicked = DrawNode(VisualNode.Position + GraphState->ViewOffset, VisualNode.Spec, NodeWidth, LineHeight, State->Interface, RenderBuffer, Mouse);
if (PortClicked >= 0)
{
BeginConnectNodesOperation(i, PortClicked, State);
DrawNode(VisualNode.Position + GraphState->ViewOffset, VisualNode.Spec, NodeWidth, LineHeight, State->Interface, RenderBuffer, Mouse);
}
for (u32 p = 0; p < GraphState->Layout.VisualPortsCount; p++)
{
visual_port VisualPort = GraphState->Layout.VisualPorts[p];
VisualPort.PortBounds.Min += GraphState->ViewOffset;
VisualPort.PortBounds.Max += GraphState->ViewOffset;
v4 PortColor = WhiteV4;
if (PointIsInRange(Mouse.Pos, VisualPort.PortBounds.Min, VisualPort.PortBounds.Max))
{
PortColor = PinkV4;
if (MouseButtonTransitionedDown(Mouse.LeftButtonState))
{
BeginConnectNodesOperation(VisualPort, p, Mouse, State);
MouseHandled = true;
}
}
PushRenderQuad2D(RenderBuffer, VisualPort.PortBounds.Min, VisualPort.PortBounds.Max, PortColor);
}
// Node Selection Panel
@ -431,6 +488,12 @@ PANEL_RENDER_PROC(NodeGraph_Render)
{
PushNodeOnWorkspace(i, &State->NodeWorkspace);
GraphState->LayoutIsDirty = true;
MouseHandled = true;
}
}
if (!MouseHandled && MouseButtonTransitionedDown(Mouse.LeftButtonState))
{
BeginPanNodeGraph(State, {}, Mouse);
}
}