Documentation and Updated LICENSE
This commit is contained in:
parent
80b428ad14
commit
d6a7d43d24
3
.gitignore
vendored
3
.gitignore
vendored
@ -9,4 +9,5 @@ install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
/Build/
|
||||
Build
|
||||
.idea
|
||||
|
@ -33,6 +33,15 @@ endif()
|
||||
include_directories(Include)
|
||||
include_directories(External)
|
||||
|
||||
# Configure ImGui
|
||||
set(IMGUI_BACKEND_SDL2 ON)
|
||||
set(IMGUI_BACKEND_OPENGL ON)
|
||||
set(IMGUI_STDLIB ON)
|
||||
set(IMGUI_FREETYPE ON)
|
||||
|
||||
# Add ImGui and any extensions
|
||||
add_subdirectory(External/imgui-docking)
|
||||
|
||||
add_executable(OpenShaderDesigner
|
||||
Source/Entry.cpp
|
||||
|
||||
@ -54,24 +63,42 @@ add_executable(OpenShaderDesigner
|
||||
|
||||
# Nodes
|
||||
Source/Graph/Nodes/Math.cpp
|
||||
|
||||
# ImGui
|
||||
External/imgui-docking/imgui_demo.cpp
|
||||
External/imgui-docking/imgui_draw.cpp
|
||||
External/imgui-docking/imgui_tables.cpp
|
||||
External/imgui-docking/imgui.cpp
|
||||
External/imgui-docking/imgui_widgets.cpp
|
||||
External/imgui-docking/backends/imgui_impl_sdl2.cpp
|
||||
External/imgui-docking/backends/imgui_impl_opengl3.cpp
|
||||
External/imgui-docking/misc/cpp/imgui_stdlib.cpp
|
||||
External/imgui-docking/misc/freetype/imgui_freetype.cpp
|
||||
Include/OpenGL/BufferObject.h
|
||||
)
|
||||
|
||||
|
||||
target_link_libraries(OpenShaderDesigner PRIVATE
|
||||
Freetype::Freetype
|
||||
GLEW::GLEW
|
||||
OpenGL::GL
|
||||
${SDL2_LIBRARIES}
|
||||
)
|
||||
)
|
||||
|
||||
# DOXYGEN ==============================================================================================================
|
||||
# https://vicrucann.github.io/tutorials/quick-cmake-doxygen/
|
||||
|
||||
find_package(Doxygen)
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
set(DOXYGEN_CONFIG_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in)
|
||||
set(DOXYGEN_CONFIG_OUT ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile)
|
||||
|
||||
configure_file(${DOXYGEN_CONFIG_IN} ${DOXYGEN_CONFIG_OUT} @ONLY)
|
||||
message("Doxygen Build Started.")
|
||||
|
||||
if(WIN32)
|
||||
add_custom_target(doxygen ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_CONFIG_OUT}
|
||||
COMMAND start firefox "${CMAKE_CURRENT_SOURCE_DIR}/Documentation/html/index.html"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Generating Doxygen Documentation"
|
||||
VERBATIM)
|
||||
else()
|
||||
add_custom_target(doxygen ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_CONFIG_OUT}
|
||||
COMMAND firefox "${CMAKE_CURRENT_SOURCE_DIR}/Documentation/html/index.html"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Generating Doxygen Documentation"
|
||||
VERBATIM)
|
||||
endif()
|
||||
else()
|
||||
message("Doxygen not found.")
|
||||
endif()
|
@ -95,8 +95,8 @@
|
||||
|
||||
// SDL
|
||||
// (the multi-viewports feature requires SDL features supported from SDL 2.0.4+. SDL 2.0.5+ is highly recommended)
|
||||
#include <SDL.h>
|
||||
#include <SDL_syswm.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_syswm.h>
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
@ -16,15 +16,36 @@
|
||||
#ifndef MATH_H
|
||||
#define MATH_H
|
||||
|
||||
#include <glm/vec4.hpp>
|
||||
#include <Graph/ShaderGraph.h>
|
||||
#include <Utility/Any.h>
|
||||
|
||||
namespace OpenShaderDesigner
|
||||
namespace OpenShaderDesigner::Nodes::Math
|
||||
{
|
||||
inline static constexpr ImColor HeaderColor = ImColor(0x92, 0x16, 0x16);
|
||||
|
||||
struct Constant : public Node
|
||||
{
|
||||
using ValueType = Any<int, unsigned int, float, glm::vec4>;
|
||||
|
||||
Constant(ShaderGraph& graph, ImVec2 pos);
|
||||
virtual ~Constant() = default;
|
||||
|
||||
[[nodiscard]] Node* Copy(ShaderGraph& graph) const override;
|
||||
void Inspect() override;
|
||||
|
||||
ValueType Value;
|
||||
};
|
||||
|
||||
RegisterNode("Math/Constant", Constant);
|
||||
|
||||
struct Add : public Node
|
||||
{
|
||||
Add(ShaderGraph& graph, ImVec2 pos);
|
||||
virtual ~Add() = default;
|
||||
|
||||
[[nodiscard]] Node* Copy(ShaderGraph& graph) const override;
|
||||
void Inspect() override;
|
||||
};
|
||||
|
||||
RegisterNode("Math/Add", Add);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <unordered_map>
|
||||
#include <filesystem>
|
||||
#include <unordered_set>
|
||||
#include <stack>
|
||||
|
||||
#include <Utility/DirectedGraph.h>
|
||||
#include <Utility/Optional.h>
|
||||
@ -37,6 +38,26 @@ namespace OpenShaderDesigner
|
||||
using PinId = uint16_t;
|
||||
using NodeId = uint32_t;
|
||||
|
||||
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(); }
|
||||
};
|
||||
|
||||
struct Pin
|
||||
{
|
||||
enum PinType
|
||||
@ -45,9 +66,8 @@ namespace OpenShaderDesigner
|
||||
, UINT
|
||||
, FLOAT
|
||||
, VECTOR
|
||||
, TEXTURE
|
||||
, ANY
|
||||
|
||||
, ANY
|
||||
, COUNT
|
||||
};
|
||||
|
||||
@ -62,10 +82,17 @@ namespace OpenShaderDesigner
|
||||
, ImColor(0x8C, 0xC0, 0x8C)
|
||||
, ImColor(0x37, 0x95, 0x85)
|
||||
, ImColor(0xE3, 0x7D, 0xDC)
|
||||
, ImColor(0xD2, 0x6E, 0x46)
|
||||
// , ImColor(0xD2, 0x6E, 0x46)
|
||||
, ImColor(0xD2, 0xD5, 0xD3)
|
||||
};
|
||||
|
||||
inline const static std::string TypeNames[COUNT] = {
|
||||
"Int"
|
||||
, "Unsigned Int"
|
||||
, "Float"
|
||||
, "Vector"
|
||||
};
|
||||
|
||||
std::string Name;
|
||||
PinType Type;
|
||||
PinDirection Direction;
|
||||
@ -73,6 +100,7 @@ namespace OpenShaderDesigner
|
||||
|
||||
struct Node
|
||||
{
|
||||
public:
|
||||
ImVec2 Position = { 0, 0 };
|
||||
|
||||
struct
|
||||
@ -91,7 +119,7 @@ namespace OpenShaderDesigner
|
||||
struct
|
||||
{
|
||||
ImVec2 Size;
|
||||
bool Const;
|
||||
bool Const;
|
||||
} Info;
|
||||
|
||||
Node(
|
||||
@ -100,8 +128,10 @@ namespace OpenShaderDesigner
|
||||
, const std::vector<Pin>& inputs, bool dyn_inputs
|
||||
, const std::vector<Pin>& outputs
|
||||
, bool constant = false);
|
||||
~Node() = default;
|
||||
|
||||
virtual Node* Copy(ShaderGraph& graph) const = 0;
|
||||
virtual void Inspect() = 0;
|
||||
};
|
||||
|
||||
class ShaderGraph
|
||||
@ -109,25 +139,6 @@ namespace OpenShaderDesigner
|
||||
{
|
||||
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>;
|
||||
@ -145,6 +156,20 @@ namespace OpenShaderDesigner
|
||||
ConstructorPtr Constructor;
|
||||
};
|
||||
|
||||
struct GraphState
|
||||
{
|
||||
ShaderGraph& Parent;
|
||||
std::vector<Node*> Nodes;
|
||||
std::unordered_set<PinId> Erased;
|
||||
ConnectionMap Connections;
|
||||
|
||||
GraphState(ShaderGraph& parent);
|
||||
GraphState(const GraphState& other);
|
||||
~GraphState();
|
||||
|
||||
GraphState& operator=(const GraphState& other);
|
||||
};
|
||||
|
||||
using ContextMenuHierarchy = DirectedGraph<ContextMenuItem>;
|
||||
using ContextID = ContextMenuHierarchy::Node;
|
||||
inline static ContextMenuHierarchy ContextMenu;
|
||||
@ -178,12 +203,19 @@ namespace OpenShaderDesigner
|
||||
void Paste(const ImVec2& location);
|
||||
void EraseSelection();
|
||||
|
||||
// History Functionality
|
||||
void PushState();
|
||||
void PopState();
|
||||
|
||||
// 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);
|
||||
ImVec2 SnapToGrid(const ImVec2& position);
|
||||
|
||||
Pin& GetPin(const PinPtr& ptr);
|
||||
|
||||
public:
|
||||
ShaderGraph();
|
||||
@ -194,12 +226,9 @@ namespace OpenShaderDesigner
|
||||
|
||||
static void Register(const std::filesystem::path& path, ConstructorPtr constructor);
|
||||
|
||||
|
||||
|
||||
private:
|
||||
std::vector<Node*> Nodes;
|
||||
std::unordered_set<PinId> Erased;
|
||||
ConnectionMap Connections;
|
||||
GraphState State;
|
||||
std::stack<GraphState> History;
|
||||
|
||||
struct
|
||||
{
|
||||
@ -279,6 +308,22 @@ namespace OpenShaderDesigner
|
||||
|
||||
bool Focused;
|
||||
ImVec2 ContextMenuPosition;
|
||||
|
||||
friend class Inspector;
|
||||
};
|
||||
|
||||
class Inspector
|
||||
: public EditorWindow
|
||||
{
|
||||
public:
|
||||
Inspector();
|
||||
|
||||
void DrawWindow() override;
|
||||
|
||||
private:
|
||||
ShaderGraph* Graph;
|
||||
|
||||
friend class ShaderGraph;
|
||||
};
|
||||
}
|
||||
|
||||
|
2
LICENSE
2
LICENSE
@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
Copyright 2024 Medusa Slockbower
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -56,8 +56,12 @@ Window::Window(const Configuration& config)
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 16);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 16);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 16);
|
||||
SDL_GL_SetAttribute(SDL_GL_FLOATBUFFERS, 1);
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
|
||||
|
||||
Context = SDL_GL_CreateContext(Handle);
|
||||
|
||||
if(Context == nullptr)
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <Core/Console.h>
|
||||
#include <Editor/EditorSystem.h>
|
||||
#include <Core/Engine.h>
|
||||
#include <imgui-docking/imgui_internal.h>
|
||||
|
||||
#include <imgui-docking/backends/imgui_impl_sdl2.h>
|
||||
#include <imgui-docking/backends/imgui_impl_opengl3.h>
|
||||
@ -117,12 +118,12 @@ void EditorSystem::Initialize()
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
io.Fonts->AddFontFromFileTTF("./Assets/Fonts/FiraMono-Regular.ttf", 20.0f);
|
||||
static ImWchar ranges[] = { 0x1, static_cast<ImWchar>(0x1FFFF), 0 };
|
||||
static ImFontConfig cfg;
|
||||
cfg.OversampleH = cfg.OversampleV = 1;
|
||||
cfg.OversampleH = cfg.OversampleV = 2;
|
||||
cfg.MergeMode = true;
|
||||
cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_LoadColor;
|
||||
io.Fonts->AddFontFromFileTTF("./Assets/Fonts/FiraMono-Regular.ttf", 20.0f);
|
||||
io.Fonts->AddFontFromFileTTF("./Assets/Fonts/remixicon.ttf", 18.0f, &cfg, ranges);
|
||||
|
||||
|
||||
@ -139,8 +140,8 @@ void EditorSystem::Initialize()
|
||||
void EditorSystem::Draw()
|
||||
{
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
|
||||
ImGui::NewFrame();
|
||||
ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
|
||||
|
||||
|
@ -14,13 +14,74 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
#include <Graph/Nodes/Math.h>
|
||||
#include <imgui-extras/imgui_extras.h>
|
||||
|
||||
using namespace OpenShaderDesigner;
|
||||
using namespace OpenShaderDesigner::Nodes::Math;
|
||||
|
||||
Constant::Constant(ShaderGraph& graph, ImVec2 pos)
|
||||
: Node(
|
||||
graph, pos
|
||||
, "Constant", HeaderColor
|
||||
, { }, false
|
||||
, { { "Out", Pin::FLOAT, Pin::OUTPUT } }
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
Node* Constant::Copy(ShaderGraph& graph) const
|
||||
{
|
||||
return new Constant(graph, Position);
|
||||
}
|
||||
|
||||
void Constant::Inspect()
|
||||
{
|
||||
Pin::PinType& Type = IO.Outputs[0].Type;
|
||||
|
||||
if(ImGui::BeginCombo("Type", Pin::TypeNames[Type].c_str()))
|
||||
{
|
||||
for(int i = 0; i < Pin::ANY; ++i)
|
||||
{
|
||||
Pin::PinType t = static_cast<Pin::PinType>(i);
|
||||
|
||||
if(ImGui::Selectable(Pin::TypeNames[t].c_str(), t == Type))
|
||||
{
|
||||
Type = t;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
glm::vec4& v = Value;
|
||||
|
||||
switch(Type)
|
||||
{
|
||||
case Pin::INT:
|
||||
ImGui::InputInt("Value", Value);
|
||||
break;
|
||||
|
||||
case Pin::UINT:
|
||||
ImGui::InputUInt("Value", Value);
|
||||
break;
|
||||
|
||||
case Pin::FLOAT:
|
||||
ImGui::InputFloat("Value", Value);
|
||||
break;
|
||||
|
||||
case Pin::VECTOR:
|
||||
ImGui::ColorEdit4("Value", &v.x);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Add::Add(ShaderGraph& graph, ImVec2 pos)
|
||||
: Node(
|
||||
graph, pos
|
||||
, "Add", ImColor(0x92, 0x16, 0x16)
|
||||
, "Add", HeaderColor
|
||||
, { { "A", Pin::ANY, Pin::INPUT }, { "B", Pin::ANY, Pin::INPUT } }, true
|
||||
, { { "Out", Pin::ANY, Pin::OUTPUT } }
|
||||
)
|
||||
@ -30,3 +91,8 @@ Node* Add::Copy(ShaderGraph& graph) const
|
||||
{
|
||||
return new Add(graph, Position);
|
||||
}
|
||||
|
||||
void Add::Inspect()
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <Core/Engine.h>
|
||||
#include <Core/Console.h>
|
||||
#include <Editor/EditorSystem.h>
|
||||
|
||||
#include <glm/common.hpp>
|
||||
#include <Graph/ShaderGraph.h>
|
||||
@ -31,6 +32,56 @@ ImColor operator*(const ImColor& c, float f)
|
||||
return ImVec4(c.Value.x * f, c.Value.y * f, c.Value.z * f, c.Value.w);
|
||||
}
|
||||
|
||||
ShaderGraph::GraphState::GraphState(ShaderGraph& parent)
|
||||
: Parent(parent)
|
||||
{
|
||||
}
|
||||
|
||||
ShaderGraph::GraphState::GraphState(const GraphState& other)
|
||||
: Parent(other.Parent)
|
||||
, Nodes(other.Nodes.size(), nullptr)
|
||||
, Connections(other.Connections)
|
||||
, Erased(other.Erased)
|
||||
{
|
||||
NodeId id = 0;
|
||||
for(const Node* node : other.Nodes)
|
||||
{
|
||||
if(node) Nodes[id] = node->Copy(Parent);
|
||||
++id;
|
||||
}
|
||||
}
|
||||
|
||||
ShaderGraph::GraphState::~GraphState()
|
||||
{
|
||||
for(Node* node : Nodes)
|
||||
{
|
||||
if(node) delete node;
|
||||
}
|
||||
}
|
||||
|
||||
ShaderGraph::GraphState& ShaderGraph::GraphState::operator=(const GraphState& other)
|
||||
{
|
||||
for(Node* node : Nodes)
|
||||
{
|
||||
if(node) delete node;
|
||||
}
|
||||
|
||||
Nodes.clear();
|
||||
Nodes.resize(other.Nodes.size(), nullptr);
|
||||
|
||||
NodeId id = 0;
|
||||
for(const Node* node : other.Nodes)
|
||||
{
|
||||
if(node) Nodes[id] = node->Copy(Parent);
|
||||
++id;
|
||||
}
|
||||
|
||||
Connections = other.Connections;
|
||||
Erased = other.Erased;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
float ShaderGraph::CalculateWidth(Node& node)
|
||||
{
|
||||
const float GridSize = Style.FontSize + Style.Grid.Lines.Padding;
|
||||
@ -50,7 +101,7 @@ float ShaderGraph::CalculateWidth(Node& node)
|
||||
OutputWidth = glm::max(OutputWidth, HeaderHeight + ImGui::CalcTextSize(pin.Name.c_str()).x);
|
||||
}
|
||||
|
||||
float Width = glm::max(InputWidth, HeaderWidth) + OutputWidth + 1 * HeaderHeight;
|
||||
float Width = glm::max(InputWidth + OutputWidth, HeaderWidth) + HeaderHeight;
|
||||
Width += GridSize - std::fmod(1.0f + Style.Grid.Lines.Padding + Width, GridSize);
|
||||
|
||||
return Width;
|
||||
@ -90,6 +141,7 @@ Node::Node(
|
||||
|
||||
ShaderGraph::ShaderGraph()
|
||||
: EditorWindow("\uED46 Shader Graph", 0)
|
||||
, State(*this)
|
||||
, Style
|
||||
{
|
||||
.Grid
|
||||
@ -166,6 +218,8 @@ void ShaderGraph::OnOpen()
|
||||
{
|
||||
Mouse.Location = ImGui::GetMousePos();
|
||||
Camera.Scroll = Camera.Zoom = 1.0f;
|
||||
|
||||
EditorSystem::Open<Inspector>()->Graph = this;
|
||||
}
|
||||
|
||||
void ShaderGraph::DrawWindow()
|
||||
@ -175,8 +229,9 @@ void ShaderGraph::DrawWindow()
|
||||
|
||||
DrawGrid();
|
||||
|
||||
|
||||
NodeId uid = 0;
|
||||
for(Node* node : Nodes)
|
||||
for(Node* node : State.Nodes)
|
||||
{
|
||||
if(node == nullptr) { ++uid; continue; }
|
||||
DrawNode(*node, uid++);
|
||||
@ -344,26 +399,23 @@ void ShaderGraph::DrawNode(Node& node, NodeId id)
|
||||
ImDrawList& DrawList = *ImGui::GetWindowDrawList();
|
||||
|
||||
// Draw Vars
|
||||
const float HeaderHeight = Style.FontSize;
|
||||
const ImVec2 Padding = { Style.Grid.Lines.Padding, Style.Grid.Lines.Padding };
|
||||
const ImVec2 NodePos = node.Position + Padding;
|
||||
const ImVec2 NodeRoot = GridToScreen(NodePos);
|
||||
const ImVec2 NodeEdge = GridToScreen(NodePos + node.Info.Size);
|
||||
const ImVec2 HeaderEdge = GridToScreen(NodePos + ImVec2(node.Info.Size.x, HeaderHeight));
|
||||
const ImVec2 HeaderText = GridToScreen(NodePos + ImVec2(Style.Nodes.Rounding, 0) + Padding * 0.5f);
|
||||
const ImVec2 InputRoot = GridToScreen(NodePos + ImVec2(Style.Nodes.Pins.Padding, HeaderHeight));
|
||||
const ImVec2 OutputRoot = GridToScreen(NodePos + ImVec2(node.Info.Size.x - HeaderHeight - Style.Nodes.Pins.Padding, HeaderHeight));
|
||||
|
||||
const bool HasLock = Mouse.FocusedNode();
|
||||
const bool NodeHovered = ImGui::IsMouseHoveringRect(NodeRoot, NodeEdge);
|
||||
const bool HeaderHovered = ImGui::IsMouseHoveringRect(NodeRoot, HeaderEdge);
|
||||
const ImColor HeaderColor = node.Header.Color * (HeaderHovered || HasLock ? 1.2f : 1.0f) * (HasLock ? 0.8f : 1.0f);
|
||||
|
||||
const float Rounding = Style.Nodes.Rounding / Camera.Zoom;
|
||||
const float PinSpacing = HeaderHeight / Camera.Zoom;
|
||||
const float HeaderHeight = Style.FontSize;
|
||||
const ImVec2 Padding = { Style.Grid.Lines.Padding, Style.Grid.Lines.Padding };
|
||||
const ImVec2 NodePos = node.Position + Padding;
|
||||
const ImVec2 NodeRoot = GridToScreen(NodePos);
|
||||
const ImVec2 NodeEdge = GridToScreen(NodePos + node.Info.Size);
|
||||
const ImVec2 HeaderEdge = GridToScreen(NodePos + ImVec2(node.Info.Size.x, HeaderHeight));
|
||||
const ImVec2 HeaderText = GridToScreen(NodePos + ImVec2(Style.Nodes.Rounding, 0) + Padding * 0.5f);
|
||||
const ImVec2 InputRoot = GridToScreen(NodePos + ImVec2(Style.Nodes.Pins.Padding, HeaderHeight));
|
||||
const ImVec2 OutputRoot = GridToScreen(NodePos + ImVec2(node.Info.Size.x - HeaderHeight - Style.Nodes.Pins.Padding, HeaderHeight));
|
||||
const bool HasLock = Mouse.FocusedNode();
|
||||
const bool NodeHovered = ImGui::IsMouseHoveringRect(NodeRoot, NodeEdge);
|
||||
const bool HeaderHovered = ImGui::IsMouseHoveringRect(NodeRoot, HeaderEdge);
|
||||
const ImColor HeaderColor = node.Header.Color * (HeaderHovered || HasLock ? 1.2f : 1.0f) * (HasLock ? 0.8f : 1.0f);
|
||||
const float Rounding = Style.Nodes.Rounding / Camera.Zoom;
|
||||
const float PinSpacing = HeaderHeight / Camera.Zoom;
|
||||
const float BorderThickness = Style.Nodes.Border.Thickness / Camera.Zoom;
|
||||
|
||||
const float GridSize = (Style.FontSize + Style.Grid.Lines.Padding);
|
||||
const bool Ctrl = ImGui::IsKeyDown(ImGuiKey_ModCtrl);
|
||||
const bool Shift = ImGui::IsKeyDown(ImGuiKey_ModShift);
|
||||
|
||||
@ -387,7 +439,7 @@ void ShaderGraph::DrawNode(Node& node, NodeId id)
|
||||
|
||||
for(NodeId selected : Mouse.Selected)
|
||||
{
|
||||
Mouse.Locks.emplace(selected, Mouse.Location - Nodes[selected]->Position);
|
||||
Mouse.Locks.emplace(selected, Mouse.Location - State.Nodes[selected]->Position);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -407,14 +459,16 @@ void ShaderGraph::DrawNode(Node& node, NodeId id)
|
||||
Mouse.Selected.insert(id);
|
||||
}
|
||||
|
||||
// Begin selection
|
||||
// Begin Dragging
|
||||
if(Mouse.FocusedNode() && Mouse.Selected.contains(id) && !(Ctrl || Shift))
|
||||
{
|
||||
if(Mouse.LocksDragged == false) PushState();
|
||||
|
||||
Mouse.LocksDragged = true;
|
||||
|
||||
for(NodeId selected : Mouse.Selected)
|
||||
{
|
||||
Mouse.Locks.emplace(selected, Mouse.Location - Nodes[selected]->Position);
|
||||
Mouse.Locks.emplace(selected, Mouse.Location - State.Nodes[selected]->Position);
|
||||
}
|
||||
}
|
||||
|
||||
@ -495,7 +549,7 @@ void ShaderGraph::DrawNode(Node& node, NodeId id)
|
||||
if(Mouse.Locks.contains(id))
|
||||
{
|
||||
node.Position = Mouse.Location - Mouse.Locks[id];
|
||||
node.Position = ImFloor(node.Position / GridSize + ImVec2(0.5f, 0.5f)) * GridSize;
|
||||
node.Position = SnapToGrid(node.Position);
|
||||
}
|
||||
|
||||
// Content =========================================================================================================
|
||||
@ -511,7 +565,7 @@ void ShaderGraph::DrawNode(Node& node, NodeId id)
|
||||
DrawList.AddText(NULL, Style.FontSize / Camera.Zoom, HeaderText, Style.Nodes.Title, node.Header.Title.c_str());
|
||||
DrawList.PopClipRect();
|
||||
|
||||
DrawList.AddLine(InputRoot, HeaderEdge, Style.Nodes.Border.Color, BorderThickness);
|
||||
DrawList.AddLine(ImVec2(NodeRoot.x, HeaderEdge.y), HeaderEdge, Style.Nodes.Border.Color, BorderThickness);
|
||||
}
|
||||
|
||||
// Border ==========================================================================================================
|
||||
@ -551,26 +605,45 @@ void ShaderGraph::DrawPin(NodeId node_id, Pin& pin, PinId pin_id, ImVec2 locatio
|
||||
const ImVec2 PinCenter = PinRoot + Offset;
|
||||
const float PinPadding = Style.Nodes.Pins.Padding / Camera.Zoom;
|
||||
const float BorderThickness = Style.Nodes.Pins.BorderThickness / Camera.Zoom;
|
||||
const float PinRadius = (HeaderHeight - PinPadding - 2.0f * Style.Nodes.Pins.BorderThickness) * 0.5f;
|
||||
const float PinRadius = (HeaderHeight - PinPadding - 2.0f * BorderThickness) * 0.5f;
|
||||
const bool Hovered = ImGui::IsMouseHoveringRect(PinRoot, PinEdge);
|
||||
const bool MouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
||||
const bool MouseClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||
|
||||
// Pin =============================================================================================================
|
||||
|
||||
// Input
|
||||
if(ImGui::IsMouseClicked(ImGuiMouseButton_Left) && Hovered && !Mouse.NewConnection() && !ImGui::IsKeyDown(ImGuiKey_ModAlt))
|
||||
if(MouseClicked && Hovered && !Mouse.NewConnection() && !ImGui::IsKeyDown(ImGuiKey_ModAlt))
|
||||
{
|
||||
StartConnection({ node_id, pin_id, input });
|
||||
}
|
||||
|
||||
// Circle
|
||||
PinPtr ptr = { node_id, pin_id, input };
|
||||
auto it = Connections.find(ptr);
|
||||
if(Mouse.NewConnection() && *Mouse.NewConnection == ptr) DrawList.AddCircleFilled(PinCenter, PinRadius, Pin::Colors[pin.Type]);
|
||||
else if(!input && it != Connections.end()) DrawList.AddCircleFilled(PinCenter, PinRadius, Pin::Colors[pin.Type] * (Hovered ? 0.8f : 1.0f));
|
||||
else if(it != Connections.end()) DrawList.AddCircleFilled(PinCenter, PinRadius, Pin::Colors[Nodes[it->second.Node]->IO.Outputs[it->second.Pin].Type] * (Hovered ? 0.8f : 1.0f));
|
||||
else if(Hovered) DrawList.AddCircleFilled(PinCenter, PinRadius, Pin::Colors[pin.Type]);
|
||||
else DrawList.AddCircleFilled(PinCenter, PinRadius, Style.Nodes.Pins.Background);
|
||||
DrawList.AddCircle(PinCenter, PinRadius, Pin::Colors[pin.Type], 0, BorderThickness);
|
||||
auto it = State.Connections.find(ptr);
|
||||
const bool Connected = (it != State.Connections.end());
|
||||
const bool NewConnectionRoot = Mouse.NewConnection() && *Mouse.NewConnection == ptr;
|
||||
const bool NewConnectionNext = Mouse.NewConnection() && *Mouse.NewConnection != ptr && Hovered;
|
||||
const bool Pressed = Hovered && MouseDown;
|
||||
const bool Filled = Hovered || Connected || NewConnectionRoot || NewConnectionNext;
|
||||
ImColor pinColor = Pin::Colors[pin.Type];
|
||||
ImColor fillColor = Style.Nodes.Pins.Background;
|
||||
|
||||
if(input)
|
||||
{
|
||||
if(Connected) pinColor = Pin::Colors[GetPin(it->second).Type];
|
||||
else if(NewConnectionNext)
|
||||
{
|
||||
Pin& Next = GetPin(*Mouse.NewConnection);
|
||||
if(pin.Type == Next.Type || pin.Type == Pin::ANY) pinColor = Pin::Colors[Next.Type];
|
||||
}
|
||||
}
|
||||
|
||||
if(Pressed && !NewConnectionNext) pinColor = pinColor * 0.8f;
|
||||
if(Filled) fillColor = pinColor;
|
||||
|
||||
DrawList.AddCircleFilled(PinCenter, PinRadius, fillColor);
|
||||
DrawList.AddCircle(PinCenter, PinRadius, pinColor, 0, BorderThickness);
|
||||
|
||||
// Text
|
||||
const ImVec2 TextOffset = location + ImVec2((input ? HeaderHeight : -ImGui::CalcTextSize(pin.Name.c_str()).x / Camera.Zoom), 0);
|
||||
@ -615,7 +688,7 @@ void ShaderGraph::DrawContextMenu()
|
||||
|
||||
// Create Nodes
|
||||
ImVec2 position = ContextMenuPosition;
|
||||
position = ImFloor(position / GridSize) * GridSize;
|
||||
position = SnapToGrid(position);
|
||||
|
||||
std::stack<ContextID> context; context.push(0);
|
||||
|
||||
@ -694,32 +767,39 @@ void ShaderGraph::DrawConnections()
|
||||
|
||||
// Connections =============================================================================================================
|
||||
|
||||
for(const Connection& connection : Connections)
|
||||
for(const Connection& connection : State.Connections)
|
||||
{
|
||||
DrawConnection(connection.first, connection.second);
|
||||
}
|
||||
|
||||
if(Mouse.NewConnection())
|
||||
{
|
||||
const Node& Node = *Nodes[Mouse.NewConnection->Node];
|
||||
const Node& Node = *State.Nodes[Mouse.NewConnection->Node];
|
||||
const Pin& Pin = Mouse.NewConnection->Input ? Node.IO.Inputs[Mouse.NewConnection->Pin] : Node.IO.Outputs[Mouse.NewConnection->Pin];
|
||||
const auto Connection = State.Connections.find(*Mouse.NewConnection);
|
||||
const bool Connected = Connection != State.Connections.end();
|
||||
|
||||
const ImVec2 NodePos = Node.Position + Padding;
|
||||
const ImVec2 InputRoot = GridToScreen(NodePos + ImVec2(Style.Nodes.Pins.Padding, HeaderHeight));
|
||||
const ImVec2 OutputRoot = GridToScreen(NodePos + ImVec2(Node.Info.Size.x - HeaderHeight - Style.Nodes.Pins.Padding, HeaderHeight));
|
||||
|
||||
const ImVec2 Root = (Mouse.NewConnection->Input ? InputRoot : OutputRoot) + ImVec2(0, HeaderHeight) * (Mouse.NewConnection->Pin + 0.5f);
|
||||
|
||||
const ImVec2 A = Root + Offset + ImVec2(Mouse.NewConnection->Input ? -PinRadius : PinRadius, 0);
|
||||
const ImVec2 D = ImGui::GetMousePos();
|
||||
const ImVec2 PinLoc = Root + Offset + ImVec2(Mouse.NewConnection->Input ? -PinRadius : PinRadius, 0);
|
||||
const ImVec2 MouseLoc = ImGui::GetMousePos();
|
||||
|
||||
const ImVec2 A = Mouse.NewConnection->Input ? MouseLoc : PinLoc;
|
||||
const ImVec2 D = Mouse.NewConnection->Input ? PinLoc : MouseLoc;
|
||||
const float Off = BezierOffset(A, D);
|
||||
|
||||
const ImVec2 B = ImVec2(A.x + (Mouse.NewConnection->Input ? -Off : Off), A.y);
|
||||
const ImVec2 C = ImVec2(D.x + (Mouse.NewConnection->Input ? Off : -Off), D.y);
|
||||
const ImVec2 B = ImVec2(A.x + Off, A.y);
|
||||
const ImVec2 C = ImVec2(D.x - Off, D.y);
|
||||
|
||||
const ImColor Color = Mouse.NewConnection->Input && Connected
|
||||
? Pin::Colors[GetPin(Connection->second).Type] : Pin::Colors[Pin.Type];
|
||||
|
||||
DrawList.AddBezierCubic(
|
||||
A, B, C, D
|
||||
, Pin::Colors[Pin.Type], Style.Nodes.Pins.Connections.Thickness / Camera.Zoom
|
||||
, Color, Style.Nodes.Pins.Connections.Thickness / Camera.Zoom
|
||||
);
|
||||
}
|
||||
|
||||
@ -751,12 +831,12 @@ void ShaderGraph::DrawConnection(const PinPtr& a, const PinPtr& b)
|
||||
const PinPtr& In = a.Input ? a : b;
|
||||
const PinPtr& Out = a.Input ? b : a;
|
||||
|
||||
const Node& OutNode = *Nodes[Out.Node];
|
||||
const Node& OutNode = *State.Nodes[Out.Node];
|
||||
const ImVec2 OutSize = OutNode.Info.Size / Camera.Zoom;
|
||||
const ImVec2 OutPosition = CanvasCenter - Camera.Location + OutNode.Position / Camera.Zoom + Padding;
|
||||
const ImVec2 OutputLoc = OutPosition + ImVec2(OutSize.x - HeaderHeight - Style.Nodes.Pins.Padding / Camera.Zoom, HeaderHeight);
|
||||
|
||||
const Node& InNode = *Nodes[In.Node];
|
||||
const Node& InNode = *State.Nodes[In.Node];
|
||||
const ImVec2 InPosition = CanvasCenter - Camera.Location + InNode.Position / Camera.Zoom + Padding;
|
||||
const ImVec2 InputLoc = InPosition + ImVec2(Style.Nodes.Pins.Padding / Camera.Zoom, HeaderHeight);
|
||||
|
||||
@ -794,11 +874,11 @@ void ShaderGraph::CreateConnection(const PinPtr& a, const PinPtr& b)
|
||||
if(a.Node == b.Node) return;
|
||||
|
||||
const PinPtr& In = a.Input ? a : b;
|
||||
const Node& InNode = *Nodes[In.Node];
|
||||
const Node& InNode = *State.Nodes[In.Node];
|
||||
const Pin& InPin = InNode.IO.Inputs[In.Pin];
|
||||
|
||||
const PinPtr& Out = a.Input ? b : a;
|
||||
const Node& OutNode = *Nodes[Out.Node];
|
||||
const Node& OutNode = *State.Nodes[Out.Node];
|
||||
const Pin& OutPin = OutNode.IO.Outputs[Out.Pin];
|
||||
|
||||
// Make sure valid typing
|
||||
@ -809,28 +889,28 @@ void ShaderGraph::CreateConnection(const PinPtr& a, const PinPtr& b)
|
||||
if(b.Input) EraseConnections(b);
|
||||
|
||||
// Add New Connections
|
||||
Connections.emplace(a, b);
|
||||
Connections.emplace(b, a);
|
||||
State.Connections.emplace(a, b);
|
||||
State.Connections.emplace(b, a);
|
||||
}
|
||||
|
||||
void ShaderGraph::EraseConnection(const PinPtr& a, const PinPtr& b)
|
||||
{
|
||||
auto range = Connections.equal_range(a);
|
||||
auto range = State.Connections.equal_range(a);
|
||||
for(auto it = range.first; it != range.second; ++it)
|
||||
{
|
||||
if(it->second == b)
|
||||
{
|
||||
Connections.erase(it);
|
||||
State.Connections.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
range = Connections.equal_range(b);
|
||||
range = State.Connections.equal_range(b);
|
||||
for(auto it = range.first; it != range.second; ++it)
|
||||
{
|
||||
if(it->second == a)
|
||||
{
|
||||
Connections.erase(it);
|
||||
State.Connections.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -838,41 +918,41 @@ void ShaderGraph::EraseConnection(const PinPtr& a, const PinPtr& b)
|
||||
|
||||
void ShaderGraph::EraseConnections(const PinPtr& a)
|
||||
{
|
||||
auto it = Connections.find(a);
|
||||
while(it != Connections.end())
|
||||
auto it = State.Connections.find(a);
|
||||
while(it != State.Connections.end())
|
||||
{
|
||||
auto range = Connections.equal_range(it->second);
|
||||
auto range = State.Connections.equal_range(it->second);
|
||||
for(auto match = range.first; match != range.second; ++match)
|
||||
{
|
||||
if(match->second == a)
|
||||
{
|
||||
Connections.erase(match);
|
||||
State.Connections.erase(match);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Connections.erase(it);
|
||||
it = Connections.find(a);
|
||||
State.Connections.erase(it);
|
||||
it = State.Connections.find(a);
|
||||
}
|
||||
}
|
||||
|
||||
NodeId ShaderGraph::AddNode(Node* node)
|
||||
{
|
||||
if(Erased.empty())
|
||||
if(State.Erased.empty())
|
||||
{
|
||||
Nodes.push_back(node);
|
||||
return static_cast<NodeId>(Nodes.size() - 1);
|
||||
State.Nodes.push_back(node);
|
||||
return static_cast<NodeId>(State.Nodes.size() - 1);
|
||||
}
|
||||
|
||||
NodeId id = *Erased.begin();
|
||||
Nodes[id] = node;
|
||||
Erased.erase(id);
|
||||
NodeId id = *State.Erased.begin();
|
||||
State.Nodes[id] = node;
|
||||
State.Erased.erase(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
void ShaderGraph::RemoveNode(NodeId id)
|
||||
{
|
||||
Node* node = Nodes[id];
|
||||
Node* node = State.Nodes[id];
|
||||
if(node->Info.Const) return;
|
||||
|
||||
PinId i = 0;
|
||||
@ -881,9 +961,9 @@ void ShaderGraph::RemoveNode(NodeId id)
|
||||
i = 0;
|
||||
for(const auto& pin : node->IO.Outputs) EraseConnections({ id, i++, false });
|
||||
|
||||
Erased.insert(id);
|
||||
State.Erased.insert(id);
|
||||
delete node;
|
||||
Nodes[id] = nullptr;
|
||||
State.Nodes[id] = nullptr;
|
||||
}
|
||||
|
||||
void ShaderGraph::ClearClipboard()
|
||||
@ -899,7 +979,7 @@ void ShaderGraph::Copy()
|
||||
|
||||
// Helper for connections
|
||||
std::unordered_map<NodeId, NodeId> clipboardTransform;
|
||||
ImVec2 min = Nodes[*Mouse.Selected.begin()]->Position;
|
||||
ImVec2 min = State.Nodes[*Mouse.Selected.begin()]->Position;
|
||||
|
||||
// Reset Clipboard
|
||||
ClearClipboard();
|
||||
@ -908,7 +988,7 @@ void ShaderGraph::Copy()
|
||||
// Copy nodes
|
||||
for(auto id : Mouse.Selected)
|
||||
{
|
||||
Node* node = Nodes[id];
|
||||
Node* node = State.Nodes[id];
|
||||
clipboardTransform[id] = static_cast<NodeId>(Clipboard.Nodes.size());
|
||||
Clipboard.Nodes.push_back(node->Copy(*this));
|
||||
min = ImMin(node->Position, min);
|
||||
@ -921,7 +1001,7 @@ void ShaderGraph::Copy()
|
||||
}
|
||||
|
||||
// Copy connections
|
||||
for(const Connection& connection : Connections)
|
||||
for(const Connection& connection : State.Connections)
|
||||
{
|
||||
if(!(Mouse.Selected.contains(connection.first.Node) && Mouse.Selected.contains(connection.second.Node))) continue;
|
||||
|
||||
@ -939,7 +1019,7 @@ void ShaderGraph::Paste(const ImVec2& location)
|
||||
// Helper for connections
|
||||
const float GridSize = (Style.FontSize + Style.Grid.Lines.Padding);
|
||||
std::unordered_map<NodeId, NodeId> clipboardTransform;
|
||||
ImVec2 root = ImFloor(location / GridSize + ImVec2(0.5f, 0.5f)) * GridSize;
|
||||
ImVec2 root = SnapToGrid(location);
|
||||
Mouse.Selected.clear();
|
||||
|
||||
// Paste the nodes
|
||||
@ -947,7 +1027,7 @@ void ShaderGraph::Paste(const ImVec2& location)
|
||||
for(Node* node : Clipboard.Nodes)
|
||||
{
|
||||
NodeId index = clipboardTransform[id++] = AddNode(node->Copy(*this));
|
||||
Nodes[index]->Position += root;
|
||||
State.Nodes[index]->Position += root;
|
||||
Mouse.Selected.insert(index);
|
||||
}
|
||||
|
||||
@ -970,6 +1050,17 @@ void ShaderGraph::EraseSelection()
|
||||
Mouse.Selected.clear();
|
||||
}
|
||||
|
||||
void ShaderGraph::PushState()
|
||||
{
|
||||
History.push(State);
|
||||
}
|
||||
|
||||
void ShaderGraph::PopState()
|
||||
{
|
||||
State = History.top();
|
||||
History.pop();
|
||||
}
|
||||
|
||||
float ShaderGraph::BezierOffset(const ImVec2& out, const ImVec2& in)
|
||||
{
|
||||
const float HeaderHeight = Style.FontSize / Camera.Zoom;
|
||||
@ -1004,6 +1095,18 @@ ImVec2 ShaderGraph::ScreenToGrid(const ImVec2& position)
|
||||
return (position - CanvasCenter + Camera.Location) * Camera.Zoom;
|
||||
}
|
||||
|
||||
ImVec2 ShaderGraph::SnapToGrid(const ImVec2& position)
|
||||
{
|
||||
const float GridSize = (Style.FontSize + Style.Grid.Lines.Padding);
|
||||
return ImFloor(position / GridSize) * GridSize;
|
||||
}
|
||||
|
||||
Pin& ShaderGraph::GetPin(const PinPtr &ptr)
|
||||
{
|
||||
Node* node = State.Nodes[ptr.Node];
|
||||
return (ptr.Input ? node->IO.Inputs : node->IO.Outputs)[ptr.Pin];
|
||||
}
|
||||
|
||||
void ShaderGraph::Register(const std::filesystem::path& path, ConstructorPtr constructor)
|
||||
{
|
||||
const std::string name = path.filename().string();
|
||||
@ -1042,3 +1145,20 @@ void ShaderGraph::Register(const std::filesystem::path& path, ConstructorPtr con
|
||||
|
||||
ContextMenu.Insert({ name, constructor }, node);
|
||||
}
|
||||
|
||||
Inspector::Inspector()
|
||||
: EditorWindow("Inspector", 0)
|
||||
, Graph(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void Inspector::DrawWindow()
|
||||
{
|
||||
if(Graph->Mouse.Selected.size() != 1)
|
||||
{
|
||||
ImGui::Text("Selected %d nodes.", Graph->Mouse.Selected.size());
|
||||
return;
|
||||
}
|
||||
|
||||
Graph->State.Nodes[*Graph->Mouse.Selected.begin()]->Inspect();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user