For my specialisation project at The Game Assembly I decided to work on tooling, with a large focus on reducing iteration speed of creating Node based tooling.

I decided to use DearImGuI as the base for this tool, I was most comfortable with the interface and API of ImGui and thought it would make it easier to focus on the purpose of project. I also found an online header only node based rendering extension to DearImGui called ImNodes these 2 provided a base for me to implementing the graph based functionality and API.

ImNodes provides an immediate-mode style rendering of pins, links and the node boxes, it includes an internal ID system to determine how links render between pins and on nodes, it also provides an interface for creating a graph-based context.

I have created a number of node based tools using my framework extension you can find out more here.

 
 

Why did the extension need extending; What was it lacking?

 

Despite combining these 2 systems I thought it was still lacking a number of features:

  • A Base Node that provides core functionality and graph integration.

  • Any common Quality of Life features, Copying, Pasting, Deletion of nodes or Comment Boxes.

  • An easily customisable graph Saving and Loading system/interface

  • Any storage solutions for storing the links and nodes

  • It does not provide any apparent built in Link data structure, instead a user would need to create their own, containing a minimum of:
    an ID, a Starting Pin ID, and a Ending Pin ID ; Needed to enable pins to be linked and the connecting lines rendered.

 
 

Challenges

 

As I began using Imnodes I realised that although the extension provides a great API for rendering out [empty] nodes, I felt that its API for creating a graph left code looking cluttered. As such my first goal was to implement a façade that wraps the functionality in a more familiar ImGui Style function. Secondly to create a new graph a storage for links and nodes would need to be created, neither of which had a data structure decided by the Imnodes extension.

  • Link data should be stored and managed internally to the graph.

  • Nodes should provide an inheritable and easily extendable interface that hooks into the graph Instances.

On top of data structure and API design challenges were no quality of life or usability features, the graph had no concept of a node structure other than an ID. As such it had no interface for adding nodes to the graph, no function creating instances of said nodes and no easily scalable solutions for saving and iterating graph data.

 
 

API Wrapping

 

The First stages of starting this project included; simplifying the API, providing a built in storage for links, creating a default node interface that nodes can be inherited from.

 
 
 

The ImNodes interface is now wrapped in a more generic ImGui Style call

The ImNodes interface is now wrapped in a more generic ImGui Style call

default_Graph.gif
 
 

This still did not provide an interface for creating nodes, a node data structure nor did it implement rendering of the nodes or links, it was only capable of creating a grid with nothing on it, with some default panning and selection functionality.

 
 

 
 

Inheritable Node Interface

 

InheritableInterface.png

Next I decided to work on the node creation interface, to simplify the process and keep the system easily updatable I created a simple inheritable interface for nodes.

On the left you can see the initial structure I came up with including limited data required to map to the Imnodes rendering as well as providing a simple interface to provide functionality.

As a result of this inheritable Node structure and the internal Link structure, I was able to create a clean and simple Node registration.

 
SingleLineRegistration.png
 
 

 
 

Graph Usability and Quality of Life Systems

 

Node Creation

RegisterAndCreateNodeFunc.gif

Next I implemented structures for storing registered nodes, with categorised construction callbacks to enable a dynamically generated context menu openable with a single right click. After that a simple left click is all that is needed to create a node, 2-click interaction seemed as optimal as it would need to be, but if I were to extend this I would add the ability to add hotkeys to navigate the menu.

Another feature I plan to look at in the future would be to enable multiple nested menus within the node registration that way more complex and easier to navigate structures can be created.

e.g. Maths > Common > Addition
Maths > Advanced > Create Projection Matrix

 
 
 

Node Contents

 

Now that I’ve gone through the graphs and their inherent functionality that is handled inside their Begin() and End() Loop. It’s time to discuss the other major part of a node based tool; The nodes.

One of the core design goals that I was after with the nodes was to provide a simple interface in which standard ImGui Controls could be used and created with as limited use of the Imnodes interface as possible. This way programmers comfortable with ImGui and how it works, would have limited to no additional overhead in learning how to use the graph system.

NodeContent.gif
 
 
 

Node Pin Systems

 
splittable structs.gif

After writing the first two core features I focused on the final one Node Pins, these in the original header had a Begin and End attribute call, where the pins placement was determined automatically by the previous ImGui elements Rect.

Although that works perfectly well for simple node systems, I didn’t like the lack of customisability it provided, as such I extended it to allow offset Values allowing me to more easily create splittable structs. The system also forced a user to identify and change the pins shape in the call, for example a pin being hollow or filled when connected, I replaced this interface so that the imNodes system was capable of updating the pin style if a pin had been connected / disconnected.

 
 
 

Copy, Pasting and Deleting

 

Now that I could create nodes, create multiple graphs and link nodes together I decided to focus on increasing the use speed of the graph system, and as such implemented core features such as copying and pasting. Copying was a simple task of creating an internal buffer that stored the copied IDs so that they can then be pasted at the cursor location with their relative offsets intact. Next came deleting nodes, this however was not as simple as I expected:

CopyPasteDelete.gif
 
 

The internal ID structures for Imnodes did not expect or want a user to reuse ID values, I however did not want holes to be left inside my Graphs internal ID system so when nodes were deleted I wrote an ID replacement system, where a deleted node would give over its ID to the node at the end of the list, this scaled instantly with selecting and deleting multiple nodes at the same time.

As the Imnodes header had no out of the box API to access its internal IDs I had no easy way to remove the nodes that I had just deleted, this meant that after removing a node, creating a new node would crash the program. So I jumped into Imnodes internal systems and rewrote them so that a node that was flagged as deleted internally also reset and cleared its ID flags allowing them to be reused.

 
 
 

Category Colours

 

I also added the ability to make the graph easier to read by allowing a user to define the colours for nodes.

Node Colours.gif
 
 
 

Comment Boxes

 
comment boxes.gif
 
 

Then I added comment boxes these are created with Shift + C and are dynamically size based on the current selection.

 
 

Alignment

 
Alignment2.gif

I also added node alignment allowing linked structures to be more easily aligned, this system is completely agnostic the node types calculating out the sizes using ImGui’s internal systems.

As seen in the gif to the side the alignment works with both a context menu opened with a ‘L-Alt + A’, or the specific alignments can even be achieved with their own keys ‘W’, ‘A’, ’S’ or ‘D’ + L-Shift + L-Alt.

The hotkeys use ImGui’s IO, so no additional Input handler needed to be created.