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; s32 OutputValue;
}; };
struct node_render_settings
{
v4 PortColors[MemberTypeCount];
bitmap_font* Font;
b32 Display;
};
// ^^ OLD ^^
struct node_struct_member struct node_struct_member
{ {
struct_member_type Type; struct_member_type Type;
@ -161,20 +170,11 @@ struct node_specification
node_struct_member* MemberList; node_struct_member* MemberList;
u32 DataStructSize; u32 DataStructSize;
u32 MemberListLength; u32 MemberListLength;
b32 IsPattern; b32 IsPattern;
}; };
struct node_render_settings
{
v4 PortColors[MemberTypeCount];
bitmap_font* Font;
b32 Display;
};
// ^^ OLD ^^
struct pattern_node struct pattern_node
{ {
// TODO(Peter): Something to think about further down the line is the fact that // 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; v2 Position;
}; };
struct visual_port
{
u32 SparseNodeIndex;
u32 PortIndex;
rect PortBounds;
};
struct node_layout struct node_layout
{ {
// NOTE(Peter): This Map is a sparse array. // NOTE(Peter): This Map is a sparse array.
@ -17,8 +24,15 @@ struct node_layout
u32* VisualNodeLayers; u32* VisualNodeLayers;
u32 VisualNodesCount; u32 VisualNodesCount;
visual_port* VisualPorts;
u32 VisualPortsCount;
u32 LayerCount; u32 LayerCount;
v2* LayerPositions; v2* LayerPositions;
b32 ConnectionIsInProgress;
v2 InProgressConnectionStart;
v2 InProgressConnectionEnd;
}; };
struct node_graph_state struct node_graph_state
@ -72,33 +86,65 @@ FOLDHAUS_INPUT_COMMAND_PROC(BeginPanNodeGraph)
OPERATION_STATE_DEF(connect_nodes_operation_state) OPERATION_STATE_DEF(connect_nodes_operation_state)
{ {
u32 NodeIndex; visual_port VisualPort;
u32 PortIndex; u32 VisualPortIndex;
b32 IsInput;
}; };
OPERATION_RENDER_PROC(UpdateConnectNodeOperation) 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) FOLDHAUS_INPUT_COMMAND_PROC(EndConnectNodesOperation)
{ {
connect_nodes_operation_state* OpState = GetCurrentOperationState(State->Modes, connect_nodes_operation_state); 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); EndCurrentOperationMode(State, Event, Mouse);
} }
input_command ConnectNodesOperationCommands[] = { input_command ConnectNodesOperationCommands[] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, EndConnectNodesOperation }, { KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndConnectNodesOperation },
}; };
internal void 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); operation_mode* ConnectNodesOperation = ActivateOperationModeWithCommands(&State->Modes, ConnectNodesOperationCommands, UpdateConnectNodeOperation);
connect_nodes_operation_state* OpState = CreateOperationState(ConnectNodesOperation, &State->Modes, connect_nodes_operation_state); connect_nodes_operation_state* OpState = CreateOperationState(ConnectNodesOperation, &State->Modes, connect_nodes_operation_state);
OpState->NodeIndex = NodeIndex; OpState->VisualPort = VisualPort;
OpState->PortIndex = PortIndex; 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[] = { input_command NodeGraph_Commands[] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, BeginPanNodeGraph } { 0 }
}; };
PANEL_INIT_PROC(NodeGraph_Init) 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) 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}}; rect PortBounds = rect{v2{0, 0}, v2{6, 6}};
v2 LinePosition = Position; v2 LinePosition = Position;
@ -174,39 +218,18 @@ DrawNodePorts(node_specification Spec, b32 InputMask, v2 Position, r32 LineHeigh
node_struct_member Member = Spec.MemberList[i]; node_struct_member Member = Spec.MemberList[i];
if ((Member.IsInput & InputMask) > 0) 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)); string MemberName = MakeString(Member.Name, CharArrayLength(Member.Name));
DrawString(RenderBuffer, MemberName, Interface.Font, LinePosition + TextOffset, WhiteV4, TextAlign); 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; 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) 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 InputMembers = 0;
u32 OutputMembers = 0; u32 OutputMembers = 0;
for (u32 i = 0; i < NodeSpecification.MemberListLength; i++) 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); DrawString(RenderBuffer, NodeName, Interface.Font, LinePosition + TextOffset, WhiteV4);
LinePosition.y -= LineHeight; LinePosition.y -= LineHeight;
// Draw Ports DrawNodePorts(NodeSpecification, IsInputMember, LinePosition, LineHeight, Align_Left, TextOffset, Interface, RenderBuffer, Mouse);
s32 InputPortClicked = DrawNodePorts(NodeSpecification, IsInputMember, LinePosition, LineHeight, Align_Left, TextOffset, Interface, RenderBuffer, Mouse);
v2 OutputLinePosition = v2{LinePosition.x + NodeDim.x, LinePosition.y }; v2 OutputLinePosition = v2{LinePosition.x + NodeDim.x, LinePosition.y };
v2 OutputTextOffset = v2{-TextOffset.x, TextOffset.y}; v2 OutputTextOffset = v2{-TextOffset.x, TextOffset.y};
s32 OutputPortClicked = DrawNodePorts(NodeSpecification, IsOutputMember, OutputLinePosition, LineHeight, Align_Right, OutputTextOffset, Interface, RenderBuffer, Mouse); 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;
} }
internal node_layout 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 = {}; node_layout Result = {};
Result.SparseToContiguousNodeMapCount = Workspace.Nodes.OnePastLastUsed; Result.SparseToContiguousNodeMapCount = Workspace.Nodes.OnePastLastUsed;
Result.SparseToContiguousNodeMap = PushArray(Storage, s32, Result.SparseToContiguousNodeMapCount); Result.SparseToContiguousNodeMap = PushArray(Storage, s32, Result.SparseToContiguousNodeMapCount);
u32 DestinationIndex = 0; u32 DestinationIndex = 0;
Result.VisualPortsCount = 0;
for (u32 i = 0; i < Result.SparseToContiguousNodeMapCount; i++) for (u32 i = 0; i < Result.SparseToContiguousNodeMapCount; i++)
{ {
gs_list_entry<pattern_node>* Entry = Workspace.Nodes.GetEntryAtIndex(i); gs_list_entry<pattern_node>* Entry = Workspace.Nodes.GetEntryAtIndex(i);
if (!EntryIsFree(Entry)) if (!EntryIsFree(Entry))
{ {
Result.SparseToContiguousNodeMap[i] = DestinationIndex++; Result.SparseToContiguousNodeMap[i] = DestinationIndex++;
pattern_node Node = Entry->Value;
node_specification Spec = NodeSpecifications[Node.SpecificationIndex];
Result.VisualPortsCount += Spec.MemberListLength;
} }
else else
{ {
@ -322,19 +320,53 @@ ArrangeNodes(pattern_node_workspace Workspace, r32 NodeWidth, r32 LayerDistance,
Result.LayerPositions[l] = v2{ (NodeWidth + LayerDistance) * FromRight, 0 }; Result.LayerPositions[l] = v2{ (NodeWidth + LayerDistance) * FromRight, 0 };
} }
// Place nodes // Place nodes and connections
Result.VisualNodesCount = Workspace.Nodes.Used; Result.VisualNodesCount = Workspace.Nodes.Used;
Result.VisualNodes = PushArray(Storage, visual_node, Result.VisualNodesCount); 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]; u32 NodeIndex = Result.SparseToContiguousNodeMap[n];
pattern_node* Node = Workspace.Nodes.GetElementAtIndex(NodeIndex); pattern_node* Node = Workspace.Nodes.GetElementAtIndex(NodeIndex);
u32 SpecIndex = Node->SpecificationIndex; u32 SpecIndex = Node->SpecificationIndex;
Result.VisualNodes[n].Spec = NodeSpecifications[SpecIndex]; node_specification Spec = NodeSpecifications[SpecIndex];
u32 NodeLayer = Result.VisualNodeLayers[n]; 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; 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; return Result;
@ -344,6 +376,7 @@ internal
PANEL_RENDER_PROC(NodeGraph_Render) PANEL_RENDER_PROC(NodeGraph_Render)
{ {
node_graph_state* GraphState = (node_graph_state*)Panel.PanelStateMemory; node_graph_state* GraphState = (node_graph_state*)Panel.PanelStateMemory;
b32 MouseHandled = false;
rect NodeSelectionWindowBounds = rect{ rect NodeSelectionWindowBounds = rect{
PanelBounds.Min, PanelBounds.Min,
@ -366,13 +399,13 @@ PANEL_RENDER_PROC(NodeGraph_Render)
ClearArena(&GraphState->LayoutMemory); ClearArena(&GraphState->LayoutMemory);
GraphState->Layout = {}; GraphState->Layout = {};
GraphState->Layout = ArrangeNodes(State->NodeWorkspace, NodeWidth, LayerDistance, &GraphState->LayoutMemory); GraphState->Layout = ArrangeNodes(State->NodeWorkspace, NodeWidth, LayerDistance, LineHeight, &GraphState->LayoutMemory);
GraphState->LayoutIsDirty = false; GraphState->LayoutIsDirty = false;
} }
DrawGrid(GraphState->ViewOffset, v2{100, 100}, GraphBounds, RenderBuffer); 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++) for (u32 i = 0; i < State->NodeWorkspace.Connections.Used; i++)
{ {
pattern_node_connection Connection = *State->NodeWorkspace.Connections.GetElementAtIndex(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); 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++) for (u32 i = 0; i < GraphState->Layout.VisualNodesCount; i++)
{ {
visual_node VisualNode = GraphState->Layout.VisualNodes[i]; visual_node VisualNode = GraphState->Layout.VisualNodes[i];
s32 PortClicked = DrawNode(VisualNode.Position + GraphState->ViewOffset, VisualNode.Spec, NodeWidth, LineHeight, State->Interface, RenderBuffer, Mouse); DrawNode(VisualNode.Position + GraphState->ViewOffset, VisualNode.Spec, NodeWidth, LineHeight, State->Interface, RenderBuffer, Mouse);
if (PortClicked >= 0) }
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))
{ {
BeginConnectNodesOperation(i, PortClicked, State); 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 // Node Selection Panel
@ -431,6 +488,12 @@ PANEL_RENDER_PROC(NodeGraph_Render)
{ {
PushNodeOnWorkspace(i, &State->NodeWorkspace); PushNodeOnWorkspace(i, &State->NodeWorkspace);
GraphState->LayoutIsDirty = true; GraphState->LayoutIsDirty = true;
MouseHandled = true;
} }
} }
if (!MouseHandled && MouseButtonTransitionedDown(Mouse.LeftButtonState))
{
BeginPanNodeGraph(State, {}, Mouse);
}
} }