Implemented rudimentary workspaces for nodes

This commit is contained in:
Peter Slattery 2019-12-30 22:05:57 -08:00
parent 9973cf4ed9
commit 2b6959f37a
5 changed files with 146 additions and 88 deletions

View File

@ -70,6 +70,8 @@ struct app_state
panel_system PanelSystem; panel_system PanelSystem;
panel* HotPanel; panel* HotPanel;
pattern_node_workspace NodeWorkspace;
}; };
internal void OpenColorPicker(app_state* State, v4* Address); internal void OpenColorPicker(app_state* State, v4* Address);

View File

@ -1,3 +1,19 @@
internal void
PushNodeOnWorkspace(s32 NodeSpecificationIndex, pattern_node_workspace* Workspace)
{
pattern_node* NewNode = Workspace->Nodes.TakeElement();
NewNode->SpecificationIndex = NodeSpecificationIndex;
}
// vv Old vv
internal node_list_iterator internal node_list_iterator
GetNodeListIterator(node_list List) GetNodeListIterator(node_list List)
{ {

View File

@ -90,15 +90,6 @@ struct node_header
u8* PersistentData; u8* PersistentData;
}; };
// TODO(Peter): @Remove Confirm we don't need it first
struct node_free_list_member
{
s32 Handle; // NOTE(Peter): this will always be zero, and must come first to match node_header
s32 Size;
node_free_list_member* Next;
s32 OldHandle;
};
struct node_list_buffer struct node_list_buffer
{ {
node_header* Headers; node_header* Headers;
@ -182,6 +173,36 @@ struct node_render_settings
b32 Display; b32 Display;
}; };
// ^^ OLD ^^
struct pattern_node
{
// TODO(Peter): Something to think about further down the line is the fact that
// SpecificationIndex doesn't have to stay static throughout a single instance of
// an application, let alone across separate runs. If you recompile (hot load or not)
// with a new specification, the indecies all get thrown off. Should we hash the spec
// names or something?
u32 SpecificationIndex;
};
struct pattern_node_connection
{
gs_list_handle UpstreamNodeHandle;
gs_list_handle DownstreamNodeHandle;
u32 UpstreamPortIndex;
u32 DownstreamPortIndex;
};
struct pattern_node_workspace
{
gs_list<pattern_node> Nodes;
gs_bucket<pattern_node_connection> Connections;
};
// vv OLD vv
v4 DragButtonColors[] = { v4 DragButtonColors[] = {
v4{.7f, .7f, .7f, 1}, v4{.7f, .7f, .7f, 1},
BlackV4, BlackV4,

View File

@ -4,6 +4,7 @@
#include <gs_language.h> #include <gs_language.h>
#include "gs_platform.h" #include "gs_platform.h"
#include <gs_list.h> #include <gs_list.h>
#include <gs_bucket.h>
#define GS_MEMORY_TRACK_ALLOCATIONS #define GS_MEMORY_TRACK_ALLOCATIONS
#define GS_MEMORY_NO_STD_LIBS #define GS_MEMORY_NO_STD_LIBS

View File

@ -3,54 +3,12 @@ struct node_graph_state
v2 ViewOffset; v2 ViewOffset;
}; };
struct temp_node_connection
{
u32 DownstreamNodeIndex;
u32 DownstreamNodePort;
u32 UpstreamNodeIndex;
u32 UpstreamNodePort;
};
struct visual_node struct visual_node
{ {
node_specification Spec; node_specification Spec;
v2 Position; v2 Position;
}; };
#define TEMP_NODE_LIST_MAX 10
global_variable u32 TEMP_NodeListUsed = 0;
global_variable node_specification TEMP_NodeList[TEMP_NODE_LIST_MAX];
#define TEMP_CONNECTIONS_LIST_MAX 10
global_variable u32 TEMP_NodeConnectionsUsed = 0;
global_variable temp_node_connection TEMP_NodeConnections[TEMP_CONNECTIONS_LIST_MAX];
internal void
PushNodeOnNodeList(node_specification Spec)
{
if (TEMP_NodeListUsed < TEMP_NODE_LIST_MAX)
{
u32 Index = TEMP_NodeListUsed++;
TEMP_NodeList[Index] = Spec;
}
}
internal void
PushConnectionOnConnectionsList(u32 UpstreamNodeIndex, u32 UpstreamNodePort, u32 DownstreamNodeIndex, u32 DownstreamNodePort)
{
if (TEMP_NodeConnectionsUsed < TEMP_CONNECTIONS_LIST_MAX)
{
u32 Index = TEMP_NodeConnectionsUsed++;
TEMP_NodeConnections[Index].DownstreamNodeIndex = DownstreamNodeIndex;
TEMP_NodeConnections[Index].DownstreamNodePort = DownstreamNodePort;
TEMP_NodeConnections[Index].UpstreamNodeIndex = UpstreamNodeIndex;
TEMP_NodeConnections[Index].UpstreamNodePort = UpstreamNodePort;;
}
}
// //
// Pan Node Graph // Pan Node Graph
// //
@ -273,46 +231,105 @@ DrawNode (v2 Position, node_specification NodeSpecification, r32 NodeWidth, r32
return PortClicked; return PortClicked;
} }
internal visual_node* struct node_layout
ArrangeNodes(node_specification* NodeList, u32 NodesCount, temp_node_connection* ConnectionList, u32 ConnectionsCount, r32 NodeWidth, r32 LayerDistance, memory_arena* Storage)
{ {
// Figure out how to arrange nodes // NOTE(Peter): This Map is a sparse array.
u32 LayerCount = 1; // index i corresponds to index i in some list of nodes
// the value at index i is the index of that node in a compressed list
// if the value at i is -1, that means the entry is free
s32* SparseToContiguousNodeMap;
u32 SparseToContiguousNodeMapCount;
u32* NodeLayers = PushArray(Storage, u32, NodesCount); visual_node* VisualNodes;
GSZeroMemory((u8*)NodeLayers, sizeof(u32) * NodesCount); u32* VisualNodeLayers;
u32 VisualNodesCount;
for (u32 c = 0; c < ConnectionsCount; c++) u32 LayerCount;
v2* LayerPositions;
};
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++)
{ {
temp_node_connection Connection = TEMP_NodeConnections[c]; gs_list_handle Handle = *NodeLUT.GetElementAtIndex(i);
if (GSListHandlesAreEqual(Handle, NodeHandle))
{
Index = i;
break;
}
}
return Index;
}
u32 UpstreamNodeInitialLayer = NodeLayers[Connection.UpstreamNodeIndex]; internal node_layout
u32 DownstreamNodeLayer = NodeLayers[Connection.DownstreamNodeIndex]; ArrangeNodes(pattern_node_workspace Workspace, r32 NodeWidth, r32 LayerDistance, memory_arena* Storage)
{
node_layout Result = {};
NodeLayers[Connection.UpstreamNodeIndex] = GSMax(UpstreamNodeInitialLayer, DownstreamNodeLayer + 1); Result.SparseToContiguousNodeMapCount = Workspace.Nodes.OnePastLastUsed;
LayerCount = GSMax(NodeLayers[Connection.UpstreamNodeIndex] + 1, LayerCount); Result.SparseToContiguousNodeMap = PushArray(Storage, s32, Result.SparseToContiguousNodeMapCount);
u32 DestinationIndex = 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++;
}
else
{
Result.SparseToContiguousNodeMap[i] = -1;
}
}
// Figure out how to arrange nodes
Result.LayerCount = 1;
Result.VisualNodeLayers = PushArray(Storage, u32, Workspace.Nodes.Used);
GSZeroMemory((u8*)Result.VisualNodeLayers, sizeof(u32) * Workspace.Nodes.Used);
for (u32 c = 0; c < Workspace.Connections.Used; c++)
{
pattern_node_connection Connection = *Workspace.Connections.GetElementAtIndex(c);
u32 UpstreamNodeLayerIndex = Result.SparseToContiguousNodeMap[Connection.UpstreamNodeHandle.Index];
u32 DownstreamNodeLayerIndex = Result.SparseToContiguousNodeMap[Connection.DownstreamNodeHandle.Index];
u32 UpstreamNodeInitialLayer = Result.VisualNodeLayers[UpstreamNodeLayerIndex];
u32 DownstreamNodeLayer = Result.VisualNodeLayers[DownstreamNodeLayerIndex];
Result.VisualNodeLayers[UpstreamNodeLayerIndex] = GSMax(UpstreamNodeInitialLayer, DownstreamNodeLayer + 1);
Result.LayerCount = GSMax(Result.VisualNodeLayers[UpstreamNodeLayerIndex] + 1, Result.LayerCount);
} }
// Place Layer Columns // Place Layer Columns
v2* LayerPositions = PushArray(Storage, v2, LayerCount); Result.LayerPositions = PushArray(Storage, v2, Result.LayerCount);
for (u32 l = 0; l < LayerCount; l++) for (u32 l = 0; l < Result.LayerCount; l++)
{ {
u32 FromRight = LayerCount - l; u32 FromRight = Result.LayerCount - l;
LayerPositions[l] = v2{ (NodeWidth + LayerDistance) * FromRight, 0 }; Result.LayerPositions[l] = v2{ (NodeWidth + LayerDistance) * FromRight, 0 };
} }
// Place nodes // Place nodes
visual_node* VisualNodes = PushArray(Storage, visual_node, NodesCount); Result.VisualNodesCount = Workspace.Nodes.Used;
for (u32 n = 0; n < NodesCount; n++) Result.VisualNodes = PushArray(Storage, visual_node, Result.VisualNodesCount);
for (u32 n = 0; n < Workspace.Nodes.Used; n++)
{ {
VisualNodes[n].Spec = TEMP_NodeList[n]; u32 NodeIndex = Result.SparseToContiguousNodeMap[n];
pattern_node* Node = Workspace.Nodes.GetElementAtIndex(NodeIndex);
u32 SpecIndex = Node->SpecificationIndex;
Result.VisualNodes[n].Spec = NodeSpecifications[SpecIndex];
u32 NodeLayer = NodeLayers[n]; u32 NodeLayer = Result.VisualNodeLayers[n];
VisualNodes[n].Position = LayerPositions[NodeLayer]; Result.VisualNodes[n].Position = Result.LayerPositions[NodeLayer];
LayerPositions[NodeLayer].y -= 200; Result.LayerPositions[NodeLayer].y -= 200;
} }
return VisualNodes; return Result;
} }
internal internal
@ -333,29 +350,30 @@ PANEL_RENDER_PROC(NodeGraph_Render)
r32 LayerDistance = 100; r32 LayerDistance = 100;
r32 LineHeight = (State->Interface.Font->PixelHeight + (2 * State->Interface.Margin.y)); r32 LineHeight = (State->Interface.Font->PixelHeight + (2 * State->Interface.Margin.y));
visual_node* VisualNodes = ArrangeNodes(&TEMP_NodeList[0], TEMP_NodeListUsed, node_layout NodeLayout = ArrangeNodes(State->NodeWorkspace, NodeWidth, LayerDistance, &State->Transient);
&TEMP_NodeConnections[0], TEMP_NodeConnectionsUsed,
NodeWidth, LayerDistance,
&State->Transient);
DrawGrid(GraphState->ViewOffset, v2{100, 100}, GraphBounds, RenderBuffer); DrawGrid(GraphState->ViewOffset, v2{100, 100}, GraphBounds, RenderBuffer);
render_quad_batch_constructor ConnectionsLayer = PushRenderQuad2DBatch(RenderBuffer, TEMP_NodeConnectionsUsed); render_quad_batch_constructor ConnectionsLayer = PushRenderQuad2DBatch(RenderBuffer, State->NodeWorkspace.Connections.Used);
for (u32 i = 0; i < TEMP_NodeConnectionsUsed; i++) for (u32 i = 0; i < State->NodeWorkspace.Connections.Used; i++)
{ {
temp_node_connection Connection = TEMP_NodeConnections[i]; pattern_node_connection Connection = *State->NodeWorkspace.Connections.GetElementAtIndex(i);
visual_node UpstreamNode = VisualNodes[Connection.UpstreamNodeIndex];
visual_node DownstreamNode = VisualNodes[Connection.DownstreamNodeIndex];
v2 LineStart = GraphState->ViewOffset + UpstreamNode.Position + v2{NodeWidth, 0} - (v2{0, LineHeight} * (Connection.UpstreamNodePort + 2)) + v2{0, LineHeight / 3}; u32 UpstreamNodeVisualIndex = NodeLayout.SparseToContiguousNodeMap[Connection.UpstreamNodeHandle.Index];
v2 LineEnd = GraphState->ViewOffset + DownstreamNode.Position - (v2{0, LineHeight} * (Connection.DownstreamNodePort + 2)) + v2{0, LineHeight / 3}; u32 DownstreamNodeVisualIndex = NodeLayout.SparseToContiguousNodeMap[Connection.DownstreamNodeHandle.Index];
visual_node UpstreamNode = NodeLayout.VisualNodes[UpstreamNodeVisualIndex];
visual_node DownstreamNode = NodeLayout.VisualNodes[DownstreamNodeVisualIndex];
v2 LineStart = GraphState->ViewOffset + UpstreamNode.Position + v2{NodeWidth, 0} - (v2{0, LineHeight} * (Connection.UpstreamPortIndex + 2)) + v2{0, LineHeight / 3};
v2 LineEnd = GraphState->ViewOffset + DownstreamNode.Position - (v2{0, LineHeight} * (Connection.DownstreamPortIndex + 2)) + v2{0, LineHeight / 3};
PushLine2DOnBatch(&ConnectionsLayer, LineStart, LineEnd, 1.5f, WhiteV4); PushLine2DOnBatch(&ConnectionsLayer, LineStart, LineEnd, 1.5f, WhiteV4);
} }
for (u32 i = 0; i < TEMP_NodeListUsed; i++) for (u32 i = 0; i < NodeLayout.VisualNodesCount; i++)
{ {
visual_node VisualNode = VisualNodes[i]; visual_node VisualNode = NodeLayout.VisualNodes[i];
s32 PortClicked = DrawNode(VisualNode.Position + GraphState->ViewOffset, VisualNode.Spec, NodeWidth, LineHeight, State->Interface, RenderBuffer, Mouse); s32 PortClicked = DrawNode(VisualNode.Position + GraphState->ViewOffset, VisualNode.Spec, NodeWidth, LineHeight, State->Interface, RenderBuffer, Mouse);
if (PortClicked >= 0) if (PortClicked >= 0)
{ {
@ -393,7 +411,7 @@ PANEL_RENDER_PROC(NodeGraph_Render)
if (MouseButtonTransitionedDown(Mouse.LeftButtonState) if (MouseButtonTransitionedDown(Mouse.LeftButtonState)
&& PointIsInRect(Mouse.DownPos, ElementBounds)) && PointIsInRect(Mouse.DownPos, ElementBounds))
{ {
PushNodeOnNodeList(Spec); PushNodeOnWorkspace(i, &State->NodeWorkspace);
} }
} }
} }