273 lines
5.3 KiB
C++

//
// Created by Maddie on 7/1/2024.
//
#ifndef SHADERGRAPH_H
#define SHADERGRAPH_H
#include <Editor/EditorWindow.h>
#include <Utility/Startup.h>
#include <vector>
#include <unordered_map>
#include <filesystem>
#include <unordered_set>
#include <Utility/DirectedGraph.h>
#include <Utility/Optional.h>
#define RegisterNode(Name, Type) \
Node* Create##Type(ShaderGraph& graph, ImVec2 pos) { return new Type(graph, pos); } \
STARTUP(_Register##Type) { ShaderGraph::Register(Name, Create##Type); }
namespace OpenShaderDesigner
{
class ShaderGraph;
using PinId = uint16_t;
using NodeId = uint32_t;
struct Pin
{
enum PinType
{
INT = 0
, UINT
, FLOAT
, VECTOR
, TEXTURE
, ANY
, COUNT
};
enum PinDirection
{
INPUT
, OUTPUT
};
inline const static ImColor Colors[COUNT] = {
ImColor(0xB9, 0xF5, 0x94)
, ImColor(0x8C, 0xC0, 0x8C)
, ImColor(0x37, 0x95, 0x85)
, ImColor(0xE3, 0x7D, 0xDC)
, ImColor(0xD2, 0x6E, 0x46)
, ImColor(0xD2, 0xD5, 0xD3)
};
std::string Name;
PinType Type;
PinDirection Direction;
};
struct Node
{
ImVec2 Position = { 0, 0 };
struct
{
std::string Title = "Node";
ImColor Color = Pin::Colors[Pin::VECTOR];
bool Enabled = true;
} Header;
struct
{
std::vector<Pin> Inputs, Outputs;
bool DynamicInputs = false;
} IO;
struct
{
ImVec2 Size;
bool Const;
} Info;
Node(
ShaderGraph& graph, ImVec2 pos
, const std::string& title, ImColor color
, const std::vector<Pin>& inputs, bool dyn_inputs
, const std::vector<Pin>& outputs
, bool constant = false);
virtual Node* Copy(ShaderGraph& graph) const = 0;
};
class ShaderGraph
: public EditorWindow
{
private:
friend Node;
struct PinPtr
{
struct Hash
{
size_t operator()(const PinPtr& p) const
{
return p.hash();
}
};
NodeId Node;
PinId Pin;
bool Input;
size_t hash() const { return (Input ? 0 : 0x8000000) | static_cast<size_t>(Node) << 32 | static_cast<size_t>(Pin & 0x7FFFFFFF); }
bool operator<(const PinPtr& o) const { return hash() < o.hash(); }
bool operator==(const PinPtr& o) const { return hash() == o.hash(); }
};
using Connection = std::pair<const PinPtr, PinPtr>;
using ConnectionMap = std::unordered_multimap<PinPtr, PinPtr, PinPtr::Hash>;
struct Line
{
ImColor Color;
float Thickness;
};
using ConstructorPtr = Node*(*)(ShaderGraph&, ImVec2);
struct ContextMenuItem
{
std::string Name;
ConstructorPtr Constructor;
};
using ContextMenuHierarchy = DirectedGraph<ContextMenuItem>;
using ContextID = ContextMenuHierarchy::Node;
inline static ContextMenuHierarchy ContextMenu;
// Helper functions
float CalculateWidth(Node& node);
float CalculateHeight(Node& node);
// Base Draw and Input functions
void HandleInput();
void DrawGrid();
void DrawNode(Node& node, NodeId id);
void DrawPin(NodeId node_id, Pin& pin, PinId pin_id, ImVec2 location, bool input);
void DrawContextMenu();
// Connection functions
void DrawConnections();
void DrawConnection(const PinPtr& a, const PinPtr& b);
auto StartConnection(const PinPtr& ptr) -> void;
void StopConnection();
void CreateConnection(const PinPtr& a, const PinPtr& b);
void EraseConnection(const PinPtr& a, const PinPtr& b);
void EraseConnections(const PinPtr& a);
NodeId AddNode(Node* node);
void RemoveNode(NodeId id);
// Clipboard functionality
void ClearClipboard();
void Copy();
void Paste(const ImVec2& location);
void EraseSelection();
// Helper functions
float BezierOffset(const ImVec2& out, const ImVec2& in);
bool AABB(const ImVec2& a0, const ImVec2& a1, const ImVec2& b0, const ImVec2& b1);
ImVec2 GridToScreen(const ImVec2& position);
ImVec2 ScreenToGrid(const ImVec2& position);
public:
ShaderGraph();
~ShaderGraph();
void OnOpen() override;
void DrawWindow() override;
static void Register(const std::filesystem::path& path, ConstructorPtr constructor);
private:
std::unordered_set<NodeId> Selected;
std::vector<Node*> Nodes;
std::unordered_set<PinId> Erased;
ConnectionMap Connections;
struct
{
struct
{
ImColor BackgroundColor;
struct
{
Line Thin, Thick;
float Padding;
} Lines;
} Grid;
struct
{
float Rounding;
Line Border, SelectedBorder;
ImColor Content;
ImColor Title;
struct
{
float Padding;
float BorderThickness;
ImColor Background;
ImColor Text;
Line Connections;
} Pins;
} Nodes;
struct
{
ImColor Background;
Line Border;
} Selection;
float FontSize;
} Style;
struct
{
struct
{
struct
{
float Rate, Smoothing;
} Scroll;
} Input;
} Settings;
struct
{
ImVec2 Location, ScreenLocation, Delta;
float Scroll;
bool ClickedSomething;
Optional<NodeId> FocusedNode;
std::unordered_map<NodeId, ImVec2> Locks;
std::unordered_set<NodeId> DragSelect;
bool LocksDragged, NodeHovered;
Optional<PinPtr> NewConnection;
} Mouse;
struct
{
ImVec2 Location;
float Zoom, Scroll;
} Camera;
struct
{
std::vector<Node*> Nodes;
ConnectionMap Connections;
} Clipboard;
bool Focused;
ImVec2 ContextMenuPosition;
};
}
#endif //SHADERGRAPH_H