diff --git a/CMakeLists.txt b/CMakeLists.txt index de92036..934a140 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_SYSTEM_NAME}) # Find dependenices find_package(PkgConfig REQUIRED) +find_package(assimp REQUIRED) +find_package(stb REQUIRED) find_package(Freetype REQUIRED) find_package(GLEW REQUIRED) find_package(glm REQUIRED) @@ -53,30 +55,46 @@ add_executable(OpenShaderDesigner Source/Entry.cpp # Core - Source/Core/Window.cpp - Source/Core/Console.cpp - Source/Core/EventSystem.cpp - Source/Core/Engine.cpp + Source/Core/Window.cpp Include/Core/Window.h + Source/Core/Console.cpp Include/Core/Console.h + Source/Core/EventSystem.cpp Include/Core/EventSystem.h + Source/Core/Engine.cpp Include/Core/Engine.h + Source/Renderer/Renderer.cpp Include/Renderer/Renderer.h # Editor - Source/Editor/EditorSystem.cpp - Source/Editor/EditorWindow.cpp - Source/Editor/ConsoleWindow.cpp - Source/Editor/Profiler.cpp + Include/Editor/MainMenuBar.h + Include/Editor/EditorWindow.h + Source/Editor/EditorSystem.cpp Include/Editor/EditorSystem.h + Source/Editor/EditorWindow.cpp Include/Editor/EditorWindow.h + Source/Editor/ConsoleWindow.cpp Include/Editor/ConsoleWindow.h + Source/Editor/Profiler.cpp Include/Editor/Profiler.h + + # File System + Source/FileSystem/FileManager.cpp Include/FileSystem/FileManager.h + + # Assets + Source/Project/Project.cpp Include/Project/Project.h # Graph - Source/Graph/ShaderGraph.cpp + Source/Graph/ShaderGraph.cpp Include/Graph/ShaderGraph.h # Nodes - Source/Graph/Nodes/Math.cpp - Include/Graph/Compiler.h - Source/Graph/Compiler.cpp - Source/FileSystem/FileManager.cpp - Include/FileSystem/FileManager.h - Include/FileSystem/Asset.h - Source/Project/Project.cpp - Include/Project/Project.h - Include/Editor/MainMenuBar.h + Source/Graph/Nodes/Maths.cpp Include/Graph/Nodes/Maths.h + Source/Graph/Nodes/Shaders.cpp Include/Graph/Nodes/Shaders.h + + # Utilities + Include/Utility/Timer.h + Include/Renderer/Assets/Texture.h + Source/Renderer/Assets/Texture.cpp +) + +# Preprocessor Definitions +target_compile_definitions(OpenShaderDesigner PRIVATE + PROJECT_VERSION="${PROJECT_VERSION}" + PROJECT_VERSION_MAJOR=${VERSION_MAJOR} + PROJECT_VERSION_MINOR=${VERSION_MINOR} + PROJECT_VERSION_PATCH=${VERSION_PATCH} + PROJECT_DIR="${CMAKE_CURRENT_SOURCE_DIR}" ) target_link_libraries(OpenShaderDesigner PRIVATE @@ -84,6 +102,7 @@ target_link_libraries(OpenShaderDesigner PRIVATE GLEW::GLEW OpenGL::GL ${SDL2_LIBRARIES} + assimp::assimp rapidjson open-cpp-utils imgui-docking diff --git a/Include/Core/Console.h b/Include/Core/Console.h index 4b9e5cb..d08ea30 100644 --- a/Include/Core/Console.h +++ b/Include/Core/Console.h @@ -18,6 +18,7 @@ #define CONSOLE_H #include +#include #include #include #include @@ -170,10 +171,11 @@ void Console::Log( #pragma warning(disable:4996) #endif auto tm = *std::localtime(&t); + const auto rel = std::filesystem::relative(file, PROJECT_DIR).string(); std::lock_guard guard(Lock_); LogEntry entry{ - std::vformat(fmt.get(), std::make_format_args(vargs...)), severity, file, std::format( + std::vformat(fmt.get(), std::make_format_args(vargs...)), severity, rel, std::format( "{:0>2}:{:0>2}:{:0>2}", tm.tm_hour, tm.tm_min, tm.tm_sec), ThreadID(), line }; diff --git a/Include/Core/Engine.h b/Include/Core/Engine.h index 05c65e4..ef4dc45 100644 --- a/Include/Core/Engine.h +++ b/Include/Core/Engine.h @@ -29,6 +29,11 @@ public: static void Start(const Window::Configuration& config); static void Stop(); + static inline const char* VersionString() { return PROJECT_VERSION; } + static inline int VersionMajor() { return PROJECT_VERSION_MAJOR; } + static inline int VersionMinor() { return PROJECT_VERSION_MINOR; } + static inline int VersionPatch() { return PROJECT_VERSION_PATCH; } + static Window& GetMainWindow() { return *MainWindow; } private: static void Initialize(); diff --git a/Include/FileSystem/FileManager.h b/Include/FileSystem/FileManager.h index 754f333..9aba237 100644 --- a/Include/FileSystem/FileManager.h +++ b/Include/FileSystem/FileManager.h @@ -17,11 +17,13 @@ #define FILESYSTEM_H #include -#include #include #include +#include +#define RegisterAsset(Name, Type, ...) \ + STARTUP(_Register##Type) { FileManager::Register(Name, { __VA_ARGS__ }, ##Type::Create, ##Type::Load, ##Type::Import); } namespace ocu = open_cpp_utils; @@ -31,31 +33,84 @@ namespace OpenShaderDesigner class FileManager : public EditorWindow { public: + class Asset; using FileSystem = ocu::filesystem; using File = FileSystem::file; using Path = std::filesystem::path; using FileID = FileSystem::file_id; - using FileType = uint32_t; + using CreateFunc = Asset* (*)(const Path&); + using LoadFunc = Asset* (*)(const Path&); + using ImportFunc = Asset* (*)(const Path&, const Path&); friend FileSystem; - enum FileType_ +private: + struct AssetDetail { - FileType_Folder = 0 - , FileType_Project + std::string Name; + std::vector Extensions; + CreateFunc Create; + LoadFunc Load; + ImportFunc Import; + + AssetDetail() : Create(nullptr), Load(nullptr), Import(nullptr) { } + AssetDetail(const std::string& name) : Name(name), Create(nullptr), Load(nullptr), Import(nullptr) {} + + + AssetDetail(const std::string& name, const std::vector& exts, + const CreateFunc create, const LoadFunc load, const ImportFunc import) + : Name(name), Extensions(exts), Create(create), Load(load), Import(import) {} }; - inline static ocu::map ExtensionMap = { - { "", FileType_Folder } - , { ".sgp", FileType_Project } + using AssetMenuHierarchy = ocu::directed_tree; + using AssetType = AssetMenuHierarchy::node; + using ExtensionMapping = ocu::map; + + static AssetMenuHierarchy& AssetMenu() { static AssetMenuHierarchy Menu; return Menu; } + static ExtensionMapping& ExtensionMap() { static ExtensionMapping Map; return Map; } + +public: + + class Asset + { + public: + Asset(const Path& path) : Dirty_(false) { } + virtual ~Asset() = default; + + bool Dirty() const { return Dirty_; } + + virtual void Open() { }; + virtual void Save(const Path& path) { Dirty_ = false; } + + File& GetFile() { return Manager_->Get(File_); } + FileID GetID() const { return File_; } + + protected: + void MakeDirty() { Dirty_ = true; } + FileManager* Parent() const { return Manager_; } + + private: + FileManager* Manager_; + FileID File_; + bool Dirty_; + friend FileManager; + }; + + struct Folder : Asset + { + Folder(const std::filesystem::path& p) : Asset(p) { }; + virtual ~Folder() = default; + + void Open() override { Manager_->CurrentDirectory_ = GetID(); } }; private: - static Asset* load(const Path& file); - static Asset* import(const Path& src, const Path& dst); - static Asset* create(const Path& file); + static Asset* load(const Path& file, FileID id); + static Asset* import(const Path& src, const Path& dst, FileID id); + static Asset* create(const Path& file, FileID id); public: FileManager(); + virtual ~FileManager() = default; void DrawMenu() override; void DrawWindow() override; @@ -64,13 +119,13 @@ public: void CurrentDirectory(FileID id) { CurrentDirectory_ = id; } FileID Create(const std::string& name) { return Filesystem_.create(name, CurrentDirectory_); } - FileID Import(const Path& path) { return Filesystem_.import(path, CurrentDirectory_); } + FileID Import(const Path& path) { return Filesystem_.import(path, CurrentDirectory_); } FileID LoadDirectory(const Path& path) { return Filesystem_.load_directory(path); } - void CloseDirectory(FileID dir) { Filesystem_.close_directory(dir); } + void CloseDirectory(FileID dir) { Filesystem_.close_directory(dir); } FileID Get(const Path& path) const { return Filesystem_.find(path); } - File& Get(FileID id) { return Filesystem_[id]; } - const File& Get(FileID id) const { return Filesystem_[id]; } + File& Get(FileID id) { return Filesystem_[id]; } + const File& Get(FileID id) const { return Filesystem_[id]; } FileID Parent(FileID id) const { return Filesystem_.parent(id); } @@ -78,6 +133,9 @@ public: void SaveAll(); static Path GetHomeDirectory(); + static void Register(const std::filesystem::path& path, + const std::vector& extension, + CreateFunc create, LoadFunc load, ImportFunc import); private: FileSystem Filesystem_; FileID CurrentDirectory_, Selected_; diff --git a/Include/Graph/Compiler.h b/Include/Graph/Compiler.h deleted file mode 100644 index f2a5130..0000000 --- a/Include/Graph/Compiler.h +++ /dev/null @@ -1,19 +0,0 @@ -// ===================================================================================================================== -// 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ===================================================================================================================== - -#ifndef COMPILER_H -#define COMPILER_H - -#endif //COMPILER_H diff --git a/Include/Graph/Nodes/Math.h b/Include/Graph/Nodes/Maths.h similarity index 83% rename from Include/Graph/Nodes/Math.h rename to Include/Graph/Nodes/Maths.h index 9d02148..e83e87b 100644 --- a/Include/Graph/Nodes/Math.h +++ b/Include/Graph/Nodes/Maths.h @@ -13,24 +13,24 @@ // limitations under the License. // ===================================================================================================================== -#ifndef MATH_H -#define MATH_H +#ifndef OSD_MATH_H +#define OSD_MATH_H #include #include -#include namespace ocu = open_cpp_utils; namespace OpenShaderDesigner::Nodes::Math { + // Header Colors ======================================================================================================= inline static constexpr ImColor HeaderColor = ImColor(0xA7, 0x62, 0x53); inline static constexpr ImColor HeaderHoveredColor = ImColor(0xC5, 0x79, 0x67); inline static constexpr ImColor HeaderActiveColor = ImColor(0x82, 0x4C, 0x40); -inline static const std::string HeaderMarker = "\uF3B9 "; +inline static const std::string HeaderMarker = "\uF3B9 "; @@ -43,16 +43,14 @@ inline static const std::string HeaderMarker = "\uF3B9 "; struct Integer : public Node { - using ValueType = ocu::any; - Integer(ShaderGraph& graph, ImVec2 pos); - virtual ~Integer() = default; + ~Integer() override = default; [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; void Inspect() override; -}; -RegisterNode("Math/Constants/Integer", Integer); + std::string GetCode() const override; +}; // Unsigned Integer ---------------------------------------------------------------------------------------------------- @@ -62,13 +60,13 @@ struct UnsignedInteger : public Node using ValueType = ocu::any; UnsignedInteger(ShaderGraph& graph, ImVec2 pos); - virtual ~UnsignedInteger() = default; + ~UnsignedInteger() override = default; [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; void Inspect() override; -}; -RegisterNode("Math/Constants/Unsigned Integer", UnsignedInteger); + std::string GetCode() const override; +}; // Scalar -------------------------------------------------------------------------------------------------------------- @@ -78,13 +76,13 @@ struct Scalar : public Node using ValueType = ocu::any; Scalar(ShaderGraph& graph, ImVec2 pos); - virtual ~Scalar() = default; + ~Scalar() override = default; [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; void Inspect() override; -}; -RegisterNode("Math/Constants/Scalar", Scalar); + std::string GetCode() const override; +}; // Vector -------------------------------------------------------------------------------------------------------------- @@ -94,13 +92,13 @@ struct Vector : public Node using ValueType = ocu::any; Vector(ShaderGraph& graph, ImVec2 pos); - virtual ~Vector() = default; + ~Vector() override = default; [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; void Inspect() override; -}; -RegisterNode("Math/Constants/Vector", Vector); + std::string GetCode() const override; +}; @@ -114,7 +112,7 @@ RegisterNode("Math/Constants/Vector", Vector); struct MathOp : public Node { MathOp(ShaderGraph& graph, ImVec2 pos); - virtual ~MathOp() = default; + ~MathOp() override = default; virtual bool CheckConnection(Pin *, Pin *) override; virtual void ValidateConnections() override; @@ -128,13 +126,13 @@ struct MathOp : public Node struct Add : public MathOp { Add(ShaderGraph& graph, ImVec2 pos); - virtual ~Add() = default; + ~Add() override = default; [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; void Inspect() override; -}; -RegisterNode("Math/Operators/Add", Add); + std::string GetCode() const override; +}; // Subtract ------------------------------------------------------------------------------------------------------------ @@ -142,27 +140,26 @@ RegisterNode("Math/Operators/Add", Add); struct Subtract : public MathOp { Subtract(ShaderGraph& graph, ImVec2 pos); - virtual ~Subtract() = default; + ~Subtract() override = default; [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; void Inspect() override; + + std::string GetCode() const override; }; -RegisterNode("Math/Operators/Subtract", Subtract); - - // Multiply ------------------------------------------------------------------------------------------------------------ struct Multiply : public MathOp { Multiply(ShaderGraph& graph, ImVec2 pos); - virtual ~Multiply() = default; + ~Multiply() override = default; [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; void Inspect() override; -}; -RegisterNode("Math/Operators/Multiply", Multiply); + std::string GetCode() const override; +}; // Divide -------------------------------------------------------------------------------------------------------------- @@ -170,16 +167,15 @@ RegisterNode("Math/Operators/Multiply", Multiply); struct Divide : public MathOp { Divide(ShaderGraph& graph, ImVec2 pos); - virtual ~Divide() = default; + ~Divide() override = default; [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; void Inspect() override; + + std::string GetCode() const override; }; -RegisterNode("Math/Operators/Divide", Divide); - - - + // ===================================================================================================================== // Utilities // ===================================================================================================================== @@ -190,13 +186,13 @@ RegisterNode("Math/Operators/Divide", Divide); struct MakeVector : public Node { MakeVector(ShaderGraph& graph, ImVec2 pos); - virtual ~MakeVector() = default; + ~MakeVector() override = default; [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; void Inspect() override; -}; - RegisterNode("Math/Utilities/Make Vector", MakeVector); + std::string GetCode() const override; +}; // Break Vector --------------------------------------------------------------------------------------------------------- @@ -204,14 +200,14 @@ struct MakeVector : public Node struct BreakVector : public Node { BreakVector(ShaderGraph& graph, ImVec2 pos); - virtual ~BreakVector() = default; + ~BreakVector() override = default; [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; void Inspect() override; -}; -RegisterNode("Math/Utilities/Break Vector", BreakVector); + std::string GetCode() const override; +}; } -#endif //MATH_H +#endif // OPEN_SHADER_DESIGNER_MATH_H diff --git a/Include/Graph/Nodes/Shaders.h b/Include/Graph/Nodes/Shaders.h new file mode 100644 index 0000000..af40df8 --- /dev/null +++ b/Include/Graph/Nodes/Shaders.h @@ -0,0 +1,65 @@ +// ===================================================================================================================== +// 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ===================================================================================================================== + +#ifndef OSD_SHADERS_H +#define OSD_SHADERS_H + +#include +#include + +namespace OpenShaderDesigner::Nodes::Shaders +{ + +// Header Colors ======================================================================================================= + +inline static constexpr ImColor HeaderColor = ImColor(0xA9, 0x85, 0xC1); +inline static constexpr ImColor HeaderHoveredColor = ImColor(0xBB, 0x96, 0xD4); +inline static constexpr ImColor HeaderActiveColor = ImColor(0x8D, 0x68, 0xA6); + +inline static const std::string HeaderMarker = "\uF42E "; + +// ===================================================================================================================== +// Shaders +// ===================================================================================================================== + + +// Function ------------------------------------------------------------------------------------------------------------ + +class Function : public Node, public ShaderAsset +{ +public: + Function(const FileManager::Path& path, ShaderGraph& graph); + ~Function() override = default; + + [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; + void Inspect() override; + + void Compile() override; + void Open() override; + + static Asset* Create(const FileManager::Path& path); + static Asset* Load(const FileManager::Path& path); + static Asset* Import(const FileManager::Path& src, const FileManager::Path& dst); + + std::string GetCode() const override; + +private: + NodeId ID_; +}; + + +} + +#endif // OSD_SHADERS_H diff --git a/Include/Graph/ShaderGraph.h b/Include/Graph/ShaderGraph.h index 2383bde..e1d781f 100644 --- a/Include/Graph/ShaderGraph.h +++ b/Include/Graph/ShaderGraph.h @@ -13,33 +13,33 @@ // limitations under the License. // ===================================================================================================================== -#ifndef SHADERGRAPH_H -#define SHADERGRAPH_H +#ifndef OSD_SHADERGRAPH_H +#define OSD_SHADERGRAPH_H #include #include -#include #include #include -#include #include #include +#include #include #include -#include +#include +#include #include -#include "open-cpp-utils/any.h" -#include "open-cpp-utils/object_pool.h" +#include "FileSystem/FileManager.h" + namespace ocu = open_cpp_utils; #define RegisterNode(Name, Type) \ - Node* Create##Type(ShaderGraph& graph, ImVec2 pos) { return new Type(graph, pos); } \ + inline Node* Create##Type(ShaderGraph& graph, ImVec2 pos) { return new Type(graph, pos); } \ STARTUP(_Register##Type) { ShaderGraph::Register(Name, Create##Type); } namespace OpenShaderDesigner @@ -66,6 +66,13 @@ namespace OpenShaderDesigner , PinFlags_AlwaysCollapse = 1 << 1 , PinFlags_NoPadding = 1 << 2 , PinFlags_Ambiguous = 1 << 3 + }; + + enum InterpolationType_ : glw::enum_t + { + InterpolationType_Flat = 0 + , InterpolationType_Screen + , InterpolationType_Smooth }; struct Pin @@ -86,6 +93,14 @@ namespace OpenShaderDesigner , "Any" }; + inline const static std::string TypeKeywords[PinType_COUNT] = { + "uint" + , "int" + , "float" + , "vec3" + , "vec3" + }; + inline const static int TypeWidths[PinType_COUNT] = { 1 // Unsigned Int , 1 // Int @@ -130,57 +145,115 @@ namespace OpenShaderDesigner struct { + std::string Alias; bool Const; } Info; Node(ShaderGraph& graph, ImVec2 pos); - ~Node() = default; + virtual ~Node() = default; void DrawPin(int id, Pin& pin, ImPinDirection direction); void Draw(ImGuiID id); - virtual bool CheckConnection(Pin*, Pin*) { return false; } + inline virtual bool CheckConnection(Pin*, Pin*) { return false; } virtual void ValidateConnections() { } + virtual Node* Copy(ShaderGraph& graph) const = 0; virtual void Inspect() = 0; + virtual std::string GetCode() const = 0; }; + + using NodeList = ocu::object_list; + using NodeId = NodeList::uuid_type; + + struct Attribute + { + std::string Name; + glw::enum_t Type; + glw::enum_t Interpolation; + glw::size_t Count; + }; + + struct Parameter + { + std::string Name; + glw::enum_t Type; + glw::size_t Count; // For arrays + }; + + struct GraphState + { + + ShaderGraph& Parent; + NodeList Nodes; + + GraphState(ShaderGraph& parent); + GraphState(const GraphState& other); + ~GraphState(); + + NodeId AddNode(Node* node) { return Nodes.insert(node); } + void RemoveNode(NodeId node) { if(Nodes[node]->Info.Const) return; Nodes.erase(node); } + + GraphState& operator=(const GraphState& other); + }; + + class ShaderAsset : public FileManager::Asset + { + public: + inline static const std::string VersionString = "#version 430 core"; + + ShaderAsset(const FileManager::Path& path, ShaderGraph& graph) + : Asset(path) + , State_(graph) + { } + + void PushState() { History_.push(State_); } + void PopState() { State_ = History_.top(); History_.pop();} + + GraphState& GetState() { return State_; } + const GraphState& GetState() const { return State_; } + + ShaderGraph& GetGraph() { return State_.Parent; } + const ShaderGraph& GetGraph() const { return State_.Parent; } + + virtual void Compile() = 0; + + std::string GetCode() const { return Code; } + + protected: + std::string Code; + + + private: + GraphState State_; + std::stack History_; + }; class ShaderGraph : public EditorWindow { - public: - struct GraphState - { - ShaderGraph& Parent; - ocu::object_list Nodes; - - GraphState(ShaderGraph& parent); - GraphState(const GraphState& other); - ~GraphState(); - - GraphState& operator=(const GraphState& other); - }; - private: friend Node; using ConstructorPtr = Node*(*)(ShaderGraph&, ImVec2); struct ContextMenuItem { - std::string Name; - ConstructorPtr Constructor; + std::string Name; + ConstructorPtr Constructor; }; using ContextMenuHierarchy = ocu::directed_tree; using ContextID = ContextMenuHierarchy::node; - inline static ContextMenuHierarchy ContextMenu; + + static ContextMenuHierarchy& ContextMenu() { static ContextMenuHierarchy Menu {{ "", nullptr }}; return Menu; } public: ShaderGraph(); - ~ShaderGraph(); + virtual ~ShaderGraph(); void OnOpen() override; + void DrawMenu() override; void DrawWindow() override; void DrawContextMenu(); @@ -188,23 +261,21 @@ namespace OpenShaderDesigner void Copy(); void Erase(); void Paste(ImVec2 pos); + void Clear(); Node* FindNode(ImPinPtr ptr); Pin& FindPin(ImPinPtr ptr); + std::string GetValue(ImPinPtr ptr); + + void OpenShader(ShaderAsset* asset) { Shader_ = asset; } + static void Register(const std::filesystem::path& path, ConstructorPtr constructor); - // History Functionality - void PushState(); - void PopState(); - GraphState& GetState() { return State_; } - private: + // TODO: Make bitfield bool GrabFocus_; - - GraphState State_; - std::stack History_; - + ShaderAsset* Shader_; ImVec2 ContextMenuPosition_; @@ -226,4 +297,4 @@ namespace OpenShaderDesigner }; } -#endif //SHADERGRAPH_H +#endif // OSD_SHADERGRAPH_H diff --git a/Include/Project/Project.h b/Include/Project/Project.h index fb116fc..bce8803 100644 --- a/Include/Project/Project.h +++ b/Include/Project/Project.h @@ -1,6 +1,17 @@ +// ===================================================================================================================== +// 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. +// You may obtain a copy of the License at // -// Created by Maddie on 9/14/2024. +// http://www.apache.org/licenses/LICENSE-2.0 // +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ===================================================================================================================== #ifndef PROJECT_H #define PROJECT_H @@ -11,7 +22,7 @@ namespace OpenShaderDesigner { -class Project : public MainMenuBar, public Asset +class Project : public MainMenuBar, public FileManager::Asset { public: Project(); @@ -20,9 +31,11 @@ public: void DrawMenuBar() override; void Open() override; - void Load(const FileManager::Path& path) override; void Save(const FileManager::Path& path) override; - void Create(const FileManager::Path& path); + + static Asset* Create(const FileManager::Path& path); + static Asset* Load(const FileManager::Path& path); + static Asset* Import(const FileManager::Path& src, const FileManager::Path& dst); private: void Reset(); diff --git a/Include/FileSystem/Asset.h b/Include/Renderer/Assets/Texture.h similarity index 55% rename from Include/FileSystem/Asset.h rename to Include/Renderer/Assets/Texture.h index 497b030..1f8e677 100644 --- a/Include/FileSystem/Asset.h +++ b/Include/Renderer/Assets/Texture.h @@ -13,34 +13,37 @@ // limitations under the License. // ===================================================================================================================== -#ifndef ASSET_H -#define ASSET_H +#ifndef TEXTURE_H +#define TEXTURE_H -#include +#include -namespace ocu = open_cpp_utils; +#include "FileSystem/FileManager.h" namespace OpenShaderDesigner { -class Asset +class Texture : public FileManager::Asset { public: - Asset() : Dirty_(false) { } + using HandleType = glw::texture; - bool Dirty() const { return Dirty_; } + Texture(const FileManager::Path& path); + Texture(const FileManager::Path& src, const FileManager::Path& dst); + ~Texture() override; - virtual void Open() = 0; - virtual void Load(const std::filesystem::path& path) = 0; - virtual void Save(const std::filesystem::path& path) { Dirty_ = false; }; + void Open() override; -protected: - void MakeDirty() { Dirty_ = true; } + static Asset* Create(const FileManager::Path& path); + static Asset* Load(const FileManager::Path& path); + static Asset* Import(const FileManager::Path& src, const FileManager::Path& dst); + HandleType* operator->() { return Handle_; } + const HandleType* operator->() const { return Handle_; } private: - bool Dirty_; + HandleType* Handle_; }; } -#endif //ASSET_H +#endif //TEXTURE_H diff --git a/Include/Core/Renderer.h b/Include/Renderer/Renderer.h similarity index 64% rename from Include/Core/Renderer.h rename to Include/Renderer/Renderer.h index b5396d5..3195095 100644 --- a/Include/Core/Renderer.h +++ b/Include/Renderer/Renderer.h @@ -17,14 +17,40 @@ #ifndef RENDERER_H #define RENDERER_H +#include +#include + +#include "glw/shader.h" namespace OpenShaderDesigner { -class Renderer +class Renderer : public EditorWindow { +public: + enum mode : glw::enum_t + { + none = 0 + , view_texture + , function + }; + + Renderer(); + virtual ~Renderer(); + void DrawMenu() override; + void DrawWindow() override; + + void OpenTexture(Texture* texture); + +private: + void DrawTexture(); + void DrawFunction(); + + glw::enum_t Mode_; + Texture* ViewTexture_; + glw::shader* Shader_; }; } diff --git a/Source/Core/Engine.cpp b/Source/Core/Engine.cpp index 8e33745..c0eecd5 100644 --- a/Source/Core/Engine.cpp +++ b/Source/Core/Engine.cpp @@ -20,15 +20,20 @@ #include #include +#include + #include #include #include "Project/Project.h" -void OpenShaderDesigner::Engine::Start(const Window::Configuration& config) +using namespace OpenShaderDesigner; + +void Engine::Start(const Window::Configuration& config) { - Console::Log(Console::Severity::Alert, "Starting {}", config.Application.Title); + std::string version = VersionString(); + Console::Log(Console::Severity::Alert, "Starting {} ({})", config.Application.Title, version); Console::Log(Console::Message, "Creating Main Window"); MainWindow = new Window(config); @@ -44,12 +49,12 @@ void OpenShaderDesigner::Engine::Start(const Window::Configuration& config) Shutdown(); } -void OpenShaderDesigner::Engine::Stop() +void Engine::Stop() { MainWindow->Close(); } -void OpenShaderDesigner::Engine::Initialize() +void Engine::Initialize() { Console::Log(Console::Message, "Initializing Engine"); @@ -69,16 +74,23 @@ void OpenShaderDesigner::Engine::Initialize() EditorSystem::Open(); EditorSystem::Open(); + Console::Log(Console::Message, "Opening Renderer"); + EditorSystem::Open(); + Console::Log(Console::Message, "Setting up Project"); EditorSystem::SetMainMenuBar(); + + + FileManager* filesystem = EditorSystem::Get(); + filesystem->LoadDirectory("./Test/"); } -void OpenShaderDesigner::Engine::Shutdown() +void Engine::Shutdown() { EditorSystem::Shutdown(); } -void OpenShaderDesigner::Engine::Update() +void Engine::Update() { _Delta = Frame.Poll(); Frame.Reset(); diff --git a/Source/Core/Window.cpp b/Source/Core/Window.cpp index 695f877..762a941 100644 --- a/Source/Core/Window.cpp +++ b/Source/Core/Window.cpp @@ -28,7 +28,7 @@ Window::Window(const Configuration& config) flags |= Config_.Video.Fullscreen == FullscreenMode::WINDOWED ? SDL_WINDOW_RESIZABLE : 0; SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_AUDIO); - + #ifdef NDEBUG SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); #else @@ -49,13 +49,12 @@ Window::Window(const Configuration& config) SDL_GL_SetAttribute(SDL_GL_FLOATBUFFERS, SDL_TRUE); } - if(Config_.Video.Multisamples()) + if(Config_.Video.Multisamples() && Config_.Video.Multisamples > 1) { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, SDL_TRUE); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, Config_.Video.Multisamples); } - if((Handle_ = SDL_CreateWindow( Config_.Application.Title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, diff --git a/Source/Entry.cpp b/Source/Entry.cpp index 3d99568..f520821 100644 --- a/Source/Entry.cpp +++ b/Source/Entry.cpp @@ -23,7 +23,7 @@ int main(int, char**) Window::Configuration config; config.Application.Title = "OpenShaderDesigner"; - config.Video.Multisamples = 16; + //config.Video.Multisamples = 4; config.Video.Fullscreen = Window::FullscreenMode::WINDOWED; config.Video.Resolution = { 1280, 720 }; config.Video.HDR = false; diff --git a/Source/FileSystem/FileManager.cpp b/Source/FileSystem/FileManager.cpp index 30f2b3f..7f34040 100644 --- a/Source/FileSystem/FileManager.cpp +++ b/Source/FileSystem/FileManager.cpp @@ -17,54 +17,80 @@ #include #include + #include -#include "Project/Project.h" +using namespace OpenShaderDesigner; -OpenShaderDesigner::Asset * OpenShaderDesigner::FileManager::load(const Path& path) +FileManager::Asset* FileManager::load(const Path& path, FileID id) { Console::Log(Console::Message, "Loading File {}", path.string()); - FileType type = ExtensionMap[path.extension().string()]; - switch (type) + // Create Folder dummy Asset for Folders + if(is_directory(path)) { - case FileType_Project: - { - Project* project = EditorSystem::GetMainMenuBar(); - project->Load(path); - return project; + Asset* asset = new Folder(path); + asset->Manager_ = EditorSystem::Get(); + asset->File_ = id; + return asset; } - default: - return nullptr; + // Get the type and load the asset + AssetType type = ExtensionMap()[path.extension().string()]; + Asset* asset = AssetMenu()[type].Load(path); + + // Set the ID and Manager of the file + if(asset) + { + asset->Manager_ = EditorSystem::Get(); + asset->File_ = id; } + return asset; } -OpenShaderDesigner::Asset * OpenShaderDesigner::FileManager::import(const Path &src, const Path &dst) +FileManager::Asset* FileManager::import(const Path &src, const Path &dst, FileID id) { - return nullptr; + Console::Log(Console::Message, "Importing File {} to {}", src.string(), dst.string()); + + // Get the type and load the asset + AssetType type = ExtensionMap()[src.extension().string()]; + Asset* asset = AssetMenu()[type].Import(src, dst); + + if(asset) + { + asset->Manager_ = EditorSystem::Get(); + asset->File_ = id; + } + + return asset; } -OpenShaderDesigner::Asset * OpenShaderDesigner::FileManager::create(const Path &path) +FileManager::Asset* FileManager::create(const Path &path, FileID id) { Console::Log(Console::Message, "Creating File {}", path.string()); - FileType type = ExtensionMap[path.extension().string()]; - switch (type) + // Create Folder dummy Asset for Folders + if(is_directory(path)) { - case FileType_Project: - { - Project* project = EditorSystem::GetMainMenuBar(); - project->Create(path); - return project; - } - - default: - return nullptr; + Asset* asset = new Folder(path); + asset->Manager_ = EditorSystem::Get(); + return asset; } + + // Get the type and load the asset + AssetType type = ExtensionMap()[path.extension().string()]; + Asset* asset = AssetMenu()[type].Create(path); + + if(asset) + { + asset->Manager_ = EditorSystem::Get(); + asset->File_ = id; + } + + return asset; } -OpenShaderDesigner::FileManager::FileManager() +FileManager::FileManager() : EditorWindow("File Manager", ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoScrollbar) , CurrentDirectory_(FileSystem::root), Selected_(FileSystem::root) , Rename_(false), FocusRename_(false) @@ -72,12 +98,78 @@ OpenShaderDesigner::FileManager::FileManager() //CurrentDirectory_ = Filesystem_.load_directory(std::filesystem::current_path()); } -void OpenShaderDesigner::FileManager::DrawMenu() +void FileManager::DrawMenu() { - + if(ImGui::BeginMenu("File")) + { + if(ImGui::BeginMenu("Create")) + { + struct Visitor + { + bool operator()(AssetDetail& item, AssetType id) + { + AssetMenuHierarchy& AssetMenu = FileManager::AssetMenu(); + const auto depth = AssetMenu.depth(id); + if(depth > Context.size()) return false; + if(item.Name == "##") return false; + + while(depth < Context.size()) + { + Context.pop(); + ImGui::EndMenu(); + } + + if(Context.top() != AssetMenu.parent(id)) return false; + std::string name = std::format("{}##{}", item.Name, id); + + if(item.Create != nullptr) + { + if(ImGui::MenuItem(name.c_str())) + { + Manager.Create(std::format("{}{}", item.Name, item.Extensions[0])); + } + } + else + { + if(ImGui::BeginMenu(name.c_str())) + { + Context.push(id); + } + else + { + return false; + } + } + + return false; + } + + FileManager& Manager; + std::stack Context; + } MenuVisitor + { + .Manager = *this + }; + + MenuVisitor.Context.push(0); + + AssetMenu().traverse(MenuVisitor); + + MenuVisitor.Context.pop(); + while(MenuVisitor.Context.empty() == false) + { + ImGui::EndMenu(); + MenuVisitor.Context.pop(); + } + + ImGui::EndPopup(); + } + + ImGui::EndMenu(); + } } -void OpenShaderDesigner::FileManager::DrawWindow() +void FileManager::DrawWindow() { // Directory Hierarchy ImGui::BeginGroup(); @@ -90,9 +182,9 @@ void OpenShaderDesigner::FileManager::DrawWindow() { const FileSystem& tree = file.system(); uint32_t depth = tree.depth(id); - - // Skip root - if(id == FileSystem::root) return false; + + if(id == FileSystem::root) return false; // Skip root + if(*file == nullptr) return false; // Skip files without assets // Skip if we are in a closed directory if(depth > stack.size()) return false; @@ -190,6 +282,13 @@ void OpenShaderDesigner::FileManager::DrawWindow() { Selected_ = Rename_ ? Selected_ : it; } + + if(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && selected) + { + File& file = Filesystem_[Selected_]; + Asset* asset = *file; + asset->Open(); + } // Renaming if(selected && Rename_) @@ -225,14 +324,15 @@ void OpenShaderDesigner::FileManager::DrawWindow() } } -bool OpenShaderDesigner::FileManager::AnyDirty() +bool FileManager::AnyDirty() { bool res = false; struct Visitor { bool operator()(File& file, FileID id) { - if(id == FileSystem::root) return false; + if(id == FileSystem::root) return false; // Continue if root node + if(*file == nullptr) return false; // Continue if no asset associated with file res |= file->Dirty(); return false; @@ -244,12 +344,12 @@ bool OpenShaderDesigner::FileManager::AnyDirty() return res; } -void OpenShaderDesigner::FileManager::SaveAll() +void FileManager::SaveAll() { } -OpenShaderDesigner::FileManager::Path OpenShaderDesigner::FileManager::GetHomeDirectory() +FileManager::Path FileManager::GetHomeDirectory() { #ifdef WIN32 return Path(getenv("HOMEDRIVE")) / getenv("HOMEPATH"); @@ -257,3 +357,108 @@ OpenShaderDesigner::FileManager::Path OpenShaderDesigner::FileManager::GetHomeDi return getenv("HOME"); #endif } + +void FileManager::Register(const std::filesystem::path& path, + const std::vector& extensions, + CreateFunc create, LoadFunc load, ImportFunc import) +{ + const std::string name = path.filename().string(); + AssetMenuHierarchy& AssetMenu = FileManager::AssetMenu(); + ExtensionMapping& ExtensionMap = FileManager::ExtensionMap(); + + AssetType node = 0; + for(auto it = path.begin(); it != path.end();) + { + AssetType child = AssetMenu.first_child(node); + + while(child) + { + if(AssetMenu[child].Name == it->filename()) + { + node = child; + ++it; + break; + } + + child = AssetMenu.next_sibling(child); + } + + if(node == 0 || node != child) + { + node = AssetMenu.insert({ it->string(), {}, nullptr, nullptr, nullptr }, node); + ++it; + } + } + + AssetMenu[node].Create = create; + AssetMenu[node].Import = import; + AssetMenu[node].Load = load; + AssetMenu[node].Extensions = extensions; + + for(const auto& ext : extensions) + { + ExtensionMap[ext] = node; + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Graph/Compiler.cpp b/Source/Graph/Compiler.cpp deleted file mode 100644 index d794151..0000000 --- a/Source/Graph/Compiler.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// -// Created by Maddie on 9/11/2024. -// diff --git a/Source/Graph/Nodes/Math.cpp b/Source/Graph/Nodes/Maths.cpp similarity index 72% rename from Source/Graph/Nodes/Math.cpp rename to Source/Graph/Nodes/Maths.cpp index 7a6307d..1bdb6b9 100644 --- a/Source/Graph/Nodes/Math.cpp +++ b/Source/Graph/Nodes/Maths.cpp @@ -13,14 +13,24 @@ // limitations under the License. // ===================================================================================================================== -#include -#include +#include #include using namespace OpenShaderDesigner; using namespace OpenShaderDesigner::Nodes::Math; +RegisterNode("Math/Constants/Integer", Integer); +RegisterNode("Math/Constants/Unsigned Integer", UnsignedInteger); +RegisterNode("Math/Constants/Scalar", Scalar); +RegisterNode("Math/Constants/Vector", Vector); +RegisterNode("Math/Operators/Add", Add); +RegisterNode("Math/Operators/Subtract", Subtract); +RegisterNode("Math/Operators/Multiply", Multiply); +RegisterNode("Math/Operators/Divide", Divide); +RegisterNode("Math/Utilities/Make Vector", MakeVector); +RegisterNode("Math/Utilities/Break Vector", BreakVector); + // ===================================================================================================================== // Constants // ===================================================================================================================== @@ -31,6 +41,7 @@ using namespace OpenShaderDesigner::Nodes::Math; Integer::Integer(ShaderGraph& graph, ImVec2 pos) : Node(graph, pos) { + Info.Alias = "Integer"; Header.Title = HeaderMarker + "Integer"; Header.Color = HeaderColor; Header.HoveredColor = HeaderHoveredColor; @@ -49,12 +60,21 @@ void Integer::Inspect() } +std::string Integer::GetCode() const +{ + return std::format("const int {} = {};", + Graph.GetValue(IO.Outputs[0].Ptr) + , IO.Outputs[0].Value.get() + ); +} + // Unsigned Integer ---------------------------------------------------------------------------------------------------- UnsignedInteger::UnsignedInteger(ShaderGraph& graph, ImVec2 pos) : Node(graph, pos) { + Info.Alias = "UnsignedInteger"; Header.Title = HeaderMarker + "Unsigned Integer"; Header.Color = HeaderColor; Header.HoveredColor = HeaderHoveredColor; @@ -73,12 +93,21 @@ void UnsignedInteger::Inspect() } +std::string UnsignedInteger::GetCode() const +{ + return std::format("const unsigned int {} = {};", + Graph.GetValue(IO.Outputs[0].Ptr) + , IO.Outputs[0].Value.get() + ); +} + // Scalar -------------------------------------------------------------------------------------------------------------- Scalar::Scalar(ShaderGraph& graph, ImVec2 pos) : Node(graph, pos) { + Info.Alias = "Scalar"; Header.Title = HeaderMarker + "Scalar"; Header.Color = HeaderColor; Header.HoveredColor = HeaderHoveredColor; @@ -97,12 +126,21 @@ void Scalar::Inspect() } +std::string Scalar::GetCode() const +{ + return std::format("const float {} = {};", + Graph.GetValue(IO.Outputs[0].Ptr) + , IO.Outputs[0].Value.get() + ); +} + // Vector -------------------------------------------------------------------------------------------------------------- Vector::Vector(ShaderGraph &graph, ImVec2 pos) : Node(graph, pos) { + Info.Alias = "Vector"; Header.Title = HeaderMarker + "Vector"; Header.Color = HeaderColor; Header.HoveredColor = HeaderHoveredColor; @@ -123,6 +161,15 @@ void Vector::Inspect() } +std::string Vector::GetCode() const +{ + const glm::vec3& val = IO.Outputs[0].Value.get(); + return std::format("const int {} = vec3({},{},{});", + Graph.GetValue(IO.Outputs[0].Ptr) + , val.x, val.y, val.z + ); +} + // ===================================================================================================================== // Math Operations @@ -193,6 +240,7 @@ void MathOp::ValidateConnections() Add::Add(ShaderGraph& graph, ImVec2 pos) : MathOp(graph, pos) { + Info.Alias = "Add"; Header.Title = HeaderMarker + "Add"; Header.Color = HeaderColor; Header.HoveredColor = HeaderHoveredColor; @@ -215,12 +263,24 @@ void Add::Inspect() } +std::string Add::GetCode() const +{ + // TODO: Support more than 2 inputs + return std::format("const {} {} = {} + {};", + Pin::TypeKeywords[IO.Outputs[0].Type] + , Graph.GetValue(IO.Outputs[0].Ptr) + , Graph.GetValue(IO.Inputs[0].Ptr) + , Graph.GetValue(IO.Inputs[1].Ptr) + ); +} + // Subtract ------------------------------------------------------------------------------------------------------------ Subtract::Subtract(ShaderGraph& graph, ImVec2 pos) : MathOp(graph, pos) { + Info.Alias = "Subtract"; Header.Title = HeaderMarker + "Subtract"; Header.Color = HeaderColor; Header.HoveredColor = HeaderHoveredColor; @@ -243,12 +303,24 @@ void Subtract::Inspect() } +std::string Subtract::GetCode() const +{ + // TODO: Support more than 2 inputs + return std::format("const {} {} = {} - {};", + Pin::TypeKeywords[IO.Outputs[0].Type] + , Graph.GetValue(IO.Outputs[0].Ptr) + , Graph.GetValue(IO.Inputs[0].Ptr) + , Graph.GetValue(IO.Inputs[1].Ptr) + ); +} + // Multiply ------------------------------------------------------------------------------------------------------------ Multiply::Multiply(ShaderGraph& graph, ImVec2 pos) : MathOp(graph, pos) { + Info.Alias = "Multiply"; Header.Title = HeaderMarker + "Multiply"; Header.Color = HeaderColor; Header.HoveredColor = HeaderHoveredColor; @@ -273,12 +345,24 @@ void Multiply::Inspect() } +std::string Multiply::GetCode() const +{ + // TODO: Support more than 2 inputs + return std::format("const {} {} = {} * {};", + Pin::TypeKeywords[IO.Outputs[0].Type] + , Graph.GetValue(IO.Outputs[0].Ptr) + , Graph.GetValue(IO.Inputs[0].Ptr) + , Graph.GetValue(IO.Inputs[1].Ptr) + ); +} + // Divide -------------------------------------------------------------------------------------------------------------- Divide::Divide(ShaderGraph& graph, ImVec2 pos) : MathOp(graph, pos) { + Info.Alias = "Divide"; Header.Title = HeaderMarker + "Divide"; Header.Color = HeaderColor; Header.HoveredColor = HeaderHoveredColor; @@ -303,6 +387,17 @@ void Divide::Inspect() } +std::string Divide::GetCode() const +{ + // TODO: Support more than 2 inputs + return std::format("const {} {} = {} / {};", + Pin::TypeKeywords[IO.Outputs[0].Type] + , Graph.GetValue(IO.Outputs[0].Ptr) + , Graph.GetValue(IO.Inputs[0].Ptr) + , Graph.GetValue(IO.Inputs[1].Ptr) + ); +} + // ===================================================================================================================== @@ -315,6 +410,7 @@ void Divide::Inspect() MakeVector::MakeVector(ShaderGraph& graph, ImVec2 pos) : Node(graph, pos) { + Info.Alias = "MakeVector"; Header.Title = HeaderMarker + "Make Vector"; Header.Color = HeaderColor; Header.HoveredColor = HeaderHoveredColor; @@ -337,13 +433,25 @@ void MakeVector::Inspect() } +std::string MakeVector::GetCode() const +{ + // TODO: Support more than 2 inputs + return std::format("const vec3 {} = vec3({}, {}, {});", + Graph.GetValue(IO.Outputs[0].Ptr) + , Graph.GetValue(IO.Inputs[0].Ptr) + , Graph.GetValue(IO.Inputs[1].Ptr) + , Graph.GetValue(IO.Inputs[2].Ptr) + ); +} + // Break Vector --------------------------------------------------------------------------------------------------------- BreakVector::BreakVector(ShaderGraph& graph, ImVec2 pos) : Node(graph, pos) { - Header.Title = HeaderMarker + "Make Vector"; + Info.Alias = "BreakVector"; + Header.Title = HeaderMarker + "Break Vector"; Header.Color = HeaderColor; Header.HoveredColor = HeaderHoveredColor; Header.ActiveColor = HeaderActiveColor; @@ -364,3 +472,21 @@ void BreakVector::Inspect() { } + +std::string BreakVector::GetCode() const +{ + // TODO: Support more than 2 inputs + return std::format( + "const float {} = {}.x;\n" + "const float {} = {}.y;\n" + "const float {} = {}.z;" + , Graph.GetValue(IO.Outputs[0].Ptr) + , Graph.GetValue(IO.Inputs[0].Ptr) + + , Graph.GetValue(IO.Outputs[1].Ptr) + , Graph.GetValue(IO.Inputs[0].Ptr) + + , Graph.GetValue(IO.Outputs[2].Ptr) + , Graph.GetValue(IO.Inputs[0].Ptr) + ); +} diff --git a/Source/Graph/Nodes/Shaders.cpp b/Source/Graph/Nodes/Shaders.cpp new file mode 100644 index 0000000..6644038 --- /dev/null +++ b/Source/Graph/Nodes/Shaders.cpp @@ -0,0 +1,150 @@ +// ===================================================================================================================== +// 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ===================================================================================================================== + +#include +#include + +#include "Editor/EditorSystem.h" + +using namespace OpenShaderDesigner; +using namespace OpenShaderDesigner::Nodes::Shaders; + +RegisterAsset("Shaders/Function", Function, ".sf"); + +// ===================================================================================================================== +// Shaders +// ===================================================================================================================== + + +// Function ------------------------------------------------------------------------------------------------------------ + + +Function::Function(const FileManager::Path& path, ShaderGraph& graph) + : Node(graph, { 0, 0 }) + , ShaderAsset(path, graph) +{ + Info.Const = true; + + Header.Title = HeaderMarker + "Function"; + Header.Color = HeaderColor; + Header.HoveredColor = HeaderHoveredColor; + Header.ActiveColor = HeaderActiveColor; + + IO.Inputs.emplace_back("Out", PinType_Vector, PinFlags_NoCollapse | PinFlags_NoPadding | PinFlags_Ambiguous); + + GetState().AddNode(this); + + MakeDirty(); +} + +Node* Function::Copy(ShaderGraph &graph) const +{ + return nullptr; // Non Copyable +} + +void Function::Inspect() +{ + +} + +void Function::Compile() +{ + // Get Static Objects + ShaderGraph& Graph = *EditorSystem::Get(); + GraphState& State = GetState(); + + // Generate node priorities + ocu::map Priority; + ocu::set Skip; + + int p = 0; + std::deque VisitQueue { IO.Inputs[0].Ptr }; + + while(not VisitQueue.empty()) + { + ImPinPtr pin = VisitQueue.front(); VisitQueue.pop_front(); + NodeId node = ImNodeGraph::GetUserID(pin.Node).Int; + + if(Priority.contains(node)) + Skip.insert(Priority[node]); + + Priority[node] = p++; + + + for(Pin& pin : State.Nodes[node]->IO.Inputs) + { + const ImVector& connections = ImNodeGraph::GetConnections(pin.Ptr); + if(connections.empty()) continue; + ImPinConnection connection = ImNodeGraph::GetConnection(connections[0]); + + if(connection.A == pin.Ptr) + VisitQueue.push_back(connection.B); + else + VisitQueue.push_back(connection.A); + } + } + + ocu::dynarray Order(p, 0); + for(auto it = Priority.begin(); it != Priority.end(); ++it) + { + Order[it->value] = it->key; + } + + // Write out the code + std::stringstream Out; + + Out << std::format("{} {}()", + Pin::TypeKeywords[IO.Inputs[0].Type] // Return Type + , GetFile().path().stem().string() + ) << std::endl; + + Out << "{" << std::endl; + + for(int i = 0; i < p; ++i) + { + if(Skip.contains(i)) continue; + + Out << State.Nodes[Order[p - i - 1]]->GetCode() << std::endl; + } + + Out << "}" << std::endl; + + Code = Out.str(); +} + +void Function::Open() +{ + EditorSystem::Get()->OpenShader(this); +} + +FileManager::Asset* Function::Create(const FileManager::Path &path) +{ + return new Function(path, *EditorSystem::Get()); +} + +FileManager::Asset* Function::Load(const FileManager::Path &path) +{ + return nullptr; +} + +FileManager::Asset* Function::Import(const FileManager::Path &src, const FileManager::Path &dst) +{ + return nullptr; +} + +std::string Function::GetCode() const +{ + return std::format("return {};", Graph.GetValue(IO.Inputs[0].Ptr)); +} diff --git a/Source/Graph/ShaderGraph.cpp b/Source/Graph/ShaderGraph.cpp index c7ef15b..caf235f 100644 --- a/Source/Graph/ShaderGraph.cpp +++ b/Source/Graph/ShaderGraph.cpp @@ -49,12 +49,12 @@ 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) +GraphState::GraphState(ShaderGraph& parent) : Parent(parent) { } -ShaderGraph::GraphState::GraphState(const GraphState& other) +GraphState::GraphState(const GraphState& other) : Parent(other.Parent) , Nodes(other.Nodes) { @@ -64,7 +64,7 @@ ShaderGraph::GraphState::GraphState(const GraphState& other) } } -ShaderGraph::GraphState::~GraphState() +GraphState::~GraphState() { for(Node* node : Nodes) { @@ -72,7 +72,7 @@ ShaderGraph::GraphState::~GraphState() } } -ShaderGraph::GraphState& ShaderGraph::GraphState::operator=(const GraphState& other) +GraphState& GraphState::operator=(const GraphState& other) { Nodes = other.Nodes; @@ -209,9 +209,9 @@ void Node::Draw(ImGuiID id) } ShaderGraph::ShaderGraph() - : EditorWindow("\uED46 Shader Graph", 0) + : EditorWindow("\uED46 Shader Graph", ImGuiWindowFlags_MenuBar) , GrabFocus_(false) - , State_(*this) + , Shader_(nullptr) { } @@ -226,11 +226,26 @@ void ShaderGraph::OnOpen() GrabFocus_ = true; } +void ShaderGraph::DrawMenu() +{ + if(ImGui::MenuItem("\uf455 Compile")) + { + ImNodeGraph::BeginGraphPostOp("ShaderGraph"); + + Shader_->Compile(); + Console::Log(Console::Message, "{}", Shader_->GetCode()); + + ImNodeGraph::EndGraphPostOp(); + } +} + void ShaderGraph::DrawWindow() { GCurrentGraph = this; + if(Shader_ == nullptr) return; + ImNodeGraph::BeginGraph("ShaderGraph"); ImNodeGraph::SetPinColors(Pin::Colors); @@ -243,11 +258,12 @@ void ShaderGraph::DrawWindow() ImGui::SetNavWindow(ImGui::GetCurrentWindow()); } - for(ImGuiID id = 0; id < State_.Nodes.size(); ++id) + GraphState& State = Shader_->GetState(); + for(ImGuiID id = 0; id < State.Nodes.size(); ++id) { - if(State_.Nodes(id) == false) continue; + if(State.Nodes(id) == false) continue; - State_.Nodes[id]->Draw(id); + State.Nodes[id]->Draw(id); } DrawContextMenu(); @@ -274,6 +290,8 @@ void ShaderGraph::DrawWindow() void ShaderGraph::DrawContextMenu() { + ContextMenuHierarchy& ContextMenu = ShaderGraph::ContextMenu(); + if(ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { ContextMenuPosition_ = ImNodeGraph::ScreenToGrid(ImGui::GetMousePos()); @@ -298,13 +316,12 @@ void ShaderGraph::DrawContextMenu() // Create Nodes ImVec2 position = ContextMenuPosition_; - std::stack context; context.push(0); - struct Visitor { bool operator()(ContextMenuItem& item, ContextID id) { - const auto depth = Graph.ContextMenu.depth(id); + ContextMenuHierarchy& ContextMenu = ShaderGraph::ContextMenu(); + const auto depth = ContextMenu.depth(id); if(depth > Context.size()) return false; while(depth < Context.size()) @@ -313,14 +330,14 @@ void ShaderGraph::DrawContextMenu() ImGui::EndMenu(); } - if(Context.top() != Graph.ContextMenu.parent(id)) return false; + if(Context.top() != ContextMenu.parent(id)) return false; std::string name = std::format("{}##{}", item.Name, id); if(item.Constructor) { if(ImGui::MenuItem(item.Name.c_str())) { - Graph.State_.Nodes.insert(item.Constructor(Graph, Location)); + Graph.Shader_->GetState().AddNode(item.Constructor(Graph, Location)); } } else @@ -340,21 +357,22 @@ void ShaderGraph::DrawContextMenu() ShaderGraph& Graph; const ImVec2 Location; - std::stack& Context; + std::stack Context; } MenuVisitor { .Graph = *this , .Location = position - , .Context = context }; + MenuVisitor.Context.push(0); + ContextMenu.traverse(MenuVisitor); - context.pop(); - while(context.empty() == false) + MenuVisitor.Context.pop(); + while(MenuVisitor.Context.empty() == false) { ImGui::EndMenu(); - context.pop(); + MenuVisitor.Context.pop(); } ImGui::EndPopup(); @@ -368,24 +386,33 @@ void ShaderGraph::Copy() void ShaderGraph::Erase() { - auto& selected = ImNodeGraph::GetSelected(); - for(ImGuiID node : selected) + GraphState& State = Shader_->GetState(); + auto& Selected = ImNodeGraph::GetSelected(); + for(ImGuiID node : Selected) { - State_.Nodes.erase(ImNodeGraph::GetUserID(node).Int); + State.RemoveNode(ImNodeGraph::GetUserID(node).Int); } - selected.Clear(); + Selected.Clear(); } -void ShaderGraph::Paste(ImVec2) {} +void ShaderGraph::Paste(ImVec2) +{ + +} + +void ShaderGraph::Clear() +{ + +} Node* ShaderGraph::FindNode(ImPinPtr ptr) { - return State_.Nodes[ImNodeGraph::GetUserID(ptr.Node).Int]; + return Shader_->GetState().Nodes[ImNodeGraph::GetUserID(ptr.Node).Int]; } Pin& ShaderGraph::FindPin(ImPinPtr ptr) { - Node* node = State_.Nodes[ImNodeGraph::GetUserID(ptr.Node).Int]; + Node* node = Shader_->GetState().Nodes[ImNodeGraph::GetUserID(ptr.Node).Int]; auto& pins = ptr.Direction ? node->IO.Outputs : node->IO.Inputs; int idx = ImNodeGraph::GetUserID(ptr).Int; if(ptr.Direction) idx *= -1; @@ -393,29 +420,64 @@ Pin& ShaderGraph::FindPin(ImPinPtr ptr) return pins[idx]; } +std::string ShaderGraph::GetValue(ImPinPtr ptr) +{ + if(ptr.Direction == ImPinDirection_Output) + { + NodeId node_id = ImNodeGraph::GetUserID(ptr.Node).Int; + Node* node = Shader_->GetState().Nodes[node_id]; + + NodeId pin_id = ImNodeGraph::GetUserID(ptr).Int; + Pin& pin = FindPin(ptr); + + return std::format("{}{}_{}", node->Info.Alias, pin.Name, node_id); + } + else + { + const ImVector& connections = ImNodeGraph::GetConnections(ptr); + + // L-Value + if(connections.empty()) + { + Pin& pin = FindPin(ptr); + + switch(pin.Type) + { + case PinType_UInt: return std::to_string(pin.Value.get()); + case PinType_Int: return std::to_string(pin.Value.get()); + case PinType_Float: return std::to_string(pin.Value.get()); + case PinType_Vector: + { + const glm::vec3& val = pin.Value.get(); + return std::format("vec3({},{},{})", val.x, val.y, val.z); + } + default: return "0"; + } + } + + // Variable + ImPinConnection connection = ImNodeGraph::GetConnection(connections[0]); + if(connection.A == ptr) return GetValue(connection.B); + return GetValue(connection.A); + } +} + void ShaderGraph::Register(const std::filesystem::path& path, ConstructorPtr constructor) { const std::string name = path.filename().string(); - - std::stack decomp; - std::filesystem::path current = path.parent_path(); - while(current.empty() == false) - { - decomp.push(current.filename().string()); - current = current.parent_path(); - } + ContextMenuHierarchy& ContextMenu = ShaderGraph::ContextMenu(); ContextID node = 0; - while(decomp.empty() == false) + for(auto it = path.begin(); it != path.end();) { ContextID child = ContextMenu.first_child(node); while(child) { - if(ContextMenu[child].Name == decomp.top()) + if(ContextMenu[child].Name == it->string()) { node = child; - decomp.pop(); + ++it; break; } @@ -424,12 +486,12 @@ void ShaderGraph::Register(const std::filesystem::path& path, ConstructorPtr con if(node == 0 || node != child) { - node = ContextMenu.insert({ decomp.top(), nullptr }, node, -1); - decomp.pop(); + node = ContextMenu.insert({ it->string(), nullptr }, node); + ++it; } } - ContextMenu.insert({ name, constructor }, node, -1); + ContextMenu[node].Constructor = constructor; } Inspector::Inspector() @@ -440,810 +502,4 @@ Inspector::Inspector() 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(); - */ -} - - -/** DEPRECATED -void ShaderGraph::DrawWindow() -{ - HandleInput(); - DrawContextMenu(); - - DrawGrid(); - - - NodeId uid = 0; - for(Node* node : State.Nodes) - { - if(node == nullptr) { ++uid; continue; } - DrawNode(*node, uid++); - } - - DrawConnections(); - - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); - - const bool Ctrl = ImGui::IsKeyDown(ImGuiKey_ModCtrl); - const bool Shift = ImGui::IsKeyDown(ImGuiKey_ModShift); - const ImVec2 Drag = ImGui::GetMouseDragDelta(); - if((Drag.x != 0 || Drag.y != 0) && !Mouse.FocusedNode() && !Mouse.NewConnection() && Focused) - { - const ImVec2 A = ImGui::GetMousePos(); - const ImVec2 B = A - Drag; - - const ImVec2 Min = ImMin(A, B); - const ImVec2 Max = ImMax(A, B); - - DrawList.AddRectFilled(Min, Max, Style.Selection.Background); - DrawList.AddRect(Min, Max, Style.Selection.Border.Color, Style.Selection.Border.Thickness); - } - - DrawList.PopClipRect(); - - if(ImGui::IsMouseReleased(ImGuiMouseButton_Left) && Focused) - { - if(Drag.x == 0 && Drag.y == 0 && !Mouse.NodeHovered && !(Ctrl || Shift)) - { - Mouse.Selected.clear(); - } - - Mouse.Locks.clear(); - Mouse.FocusedNode.reset(); - Mouse.DragSelect.clear(); - Mouse.LocksDragged = false; - } -} - - -void ShaderGraph::HandleInput() -{ - - Focused = ImGui::IsWindowFocused(); - - // Calculate Mouse Delta - Mouse.ClickedSomething = false; - Mouse.Location = ScreenToGrid(Mouse.ScreenLocation); - Mouse.Delta = ImGui::GetMousePos() - Mouse.ScreenLocation; - Mouse.ScreenLocation = ImGui::GetMousePos(); - Mouse.Scroll = ImGui::GetIO().MouseWheel; - Mouse.NodeHovered = false; - - // Dragging the Grid - if(ImGui::IsMouseDragging(ImGuiMouseButton_Middle) && Focused) - { - Camera.Location -= Mouse.Delta; - } - - // Zooming - if(Focused) - { - Camera.Scroll -= Mouse.Scroll * Settings.Input.Scroll.Rate; - Camera.Scroll = glm::clamp(Camera.Scroll, 1.0f, 4.0f); - } - - // Smooth out zoom - const float ZoomPrev = Camera.Zoom; - Camera.Zoom = glm::mix(Camera.Zoom, Camera.Scroll, Engine::Delta * Settings.Input.Scroll.Smoothing); - Camera.Location *= (1.0f - (Camera.Zoom - ZoomPrev) / Camera.Zoom); - - // Delete nodes - if(ImGui::IsKeyPressed(ImGuiKey_Delete)) - { - EraseSelection(); - } - - if(ImGui::IsKeyPressed(ImGuiKey_C) && ImGui::IsKeyDown(ImGuiKey_ModCtrl)) - { - Copy(); - } - - if(ImGui::IsKeyPressed(ImGuiKey_X) && ImGui::IsKeyDown(ImGuiKey_ModCtrl)) - { - Copy(); - EraseSelection(); - } - - if(ImGui::IsKeyPressed(ImGuiKey_V) && ImGui::IsKeyDown(ImGuiKey_ModCtrl)) - { - Paste(Mouse.Location); - } -} - -void ShaderGraph::DrawGrid() -{ - // Vars ============================================================================================================ - - // ImGui Vars - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); - - // Style Vars - const ImColor BackgroundColor = Style.Grid.BackgroundColor; - - const ImColor ThinLineColor = Style.Grid.Lines.Thin.Color; - const float ThinLineSpacing = (Style.FontSize + Style.Grid.Lines.Padding); - const float ThinLineThickness = Style.Grid.Lines.Thin.Thickness; - - const ImColor ThickLineColor = Style.Grid.Lines.Thick.Color; - const float ThickLineSpacing = 5.0f * (Style.FontSize + Style.Grid.Lines.Padding); - const float ThickLineThickness = Style.Grid.Lines.Thick.Thickness; - - // Window Info - const ImVec2 CanvasMin = ImGui::GetWindowPos() + ImGui::GetWindowContentRegionMin(); - const ImVec2 CanvasMax = ImGui::GetWindowPos() + ImGui::GetWindowContentRegionMax(); - const ImVec2 CanvasSize = CanvasMax - CanvasMin; - - // Grid Vars - ImVec2 TopLeftReal = Camera.Location - CanvasSize * 0.5f; - ImVec2 ThinGridStart = CanvasMin - ImVec2(glm::mod(TopLeftReal.x, ThinLineSpacing / Camera.Zoom), glm::mod(TopLeftReal.y, ThinLineSpacing / Camera.Zoom)); - ImVec2 ThickGridStart = CanvasMin - ImVec2(glm::mod(TopLeftReal.x, ThickLineSpacing / Camera.Zoom), glm::mod(TopLeftReal.y, ThickLineSpacing / Camera.Zoom)); - - - // Drawing ========================================================================================================= - - DrawList.PushClipRect(CanvasMin, CanvasMax, false); - - // Background - DrawList.AddRectFilled(CanvasMin, CanvasMax, BackgroundColor); - - - // Thin Grid - for(float x = ThinGridStart.x; x <= ThinGridStart.x + (CanvasSize.x + ThinLineSpacing) * Camera.Zoom; x += ThinLineSpacing / Camera.Zoom) - { - DrawList.AddLine({ x, CanvasMin.y }, { x, CanvasMax.y}, ThinLineColor, ThinLineThickness); - } - - for(float y = ThinGridStart.y; y <= ThinGridStart.y + (CanvasSize.y + ThinLineSpacing) * Camera.Zoom; y += ThinLineSpacing / Camera.Zoom) - { - DrawList.AddLine({ CanvasMin.x, y }, { CanvasMax.x, y }, ThinLineColor, ThinLineThickness); - } - - - // Thick Grid - for(float x = ThickGridStart.x; x <= ThickGridStart.x + (CanvasSize.x + ThickLineSpacing) * Camera.Zoom; x += ThickLineSpacing / Camera.Zoom) - { - DrawList.AddLine({ x, CanvasMin.y }, { x, CanvasMax.y}, ThickLineColor, ThickLineThickness); - } - - for(float y = ThickGridStart.y; y <= ThickGridStart.y + (CanvasSize.y + ThickLineSpacing) * Camera.Zoom; y += ThickLineSpacing / Camera.Zoom) - { - DrawList.AddLine({ CanvasMin.x, y }, { CanvasMax.x, y }, ThickLineColor, ThickLineThickness); - } -} - -void ShaderGraph::DrawNode(Node& node, NodeId id) -{ - // Vars ============================================================================================================ - - // Window Info - const ImVec2 CanvasMin = ImGui::GetWindowPos() + ImGui::GetWindowContentRegionMin(); - const ImVec2 CanvasMax = ImGui::GetWindowPos() + ImGui::GetWindowContentRegionMax(); - - // ImGui Vars - 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 BorderThickness = Style.Nodes.Border.Thickness / Camera.Zoom; - - const bool Ctrl = ImGui::IsKeyDown(ImGuiKey_ModCtrl); - const bool Shift = ImGui::IsKeyDown(ImGuiKey_ModShift); - - if(!AABB(CanvasMin, CanvasMax, NodeRoot, NodeEdge)) return; - - // Input =========================================================================================================== - - Mouse.NodeHovered = Mouse.NodeHovered ? true : NodeHovered; - - // Clicking nodes - if(ImGui::IsMouseClicked(ImGuiMouseButton_Left)) - { - Mouse.ClickedSomething = NodeHovered ? true : Mouse.ClickedSomething; - - if(HeaderHovered) - { - if(!Mouse.FocusedNode() && !(Ctrl || Shift)) - { - Mouse.FocusedNode = id; - Mouse.LocksDragged = false; - - for(NodeId selected : Mouse.Selected) - { - Mouse.Locks.emplace(selected, Mouse.Location - State.Nodes[selected]->Position); - } - } - } - } - - // Drag selection - if(ImGui::IsMouseDragging(ImGuiMouseButton_Left) && !Mouse.NewConnection() && Focused) - { - // Reset When Dragging a New Node - if(Mouse.FocusedNode() && Mouse.FocusedNode == id && !Mouse.Selected.contains(id)) - { - if(!(Ctrl || Shift)) - { - Mouse.Selected.clear(); - Mouse.Locks.clear(); - } - Mouse.Selected.insert(id); - } - - // 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 - State.Nodes[selected]->Position); - } - } - - // Make sure we aren't dragging nodes - if(Mouse.LocksDragged == false) - { - // Calculate selection bounds - ImVec2 Drag = ImGui::GetMouseDragDelta() * Camera.Zoom; - ImVec2 A0 = Mouse.Location - Drag; - ImVec2 A1 = Mouse.Location; - ImVec2 B0 = NodePos; - ImVec2 B1 = NodePos + node.Info.Size; - bool Intersect = AABB(A0, A1, B0, B1); - - // Clear selection for new selection - if(Mouse.DragSelect.empty() && !Shift && !Ctrl && !Mouse.FocusedNode()) - { - Mouse.Selected.clear(); - } - - // Select nodes - if(Intersect && !Mouse.DragSelect.contains(id)) - { - if(!Ctrl) - { - if(!Mouse.Selected.contains(id)) - { - Mouse.Selected.insert(id); - Mouse.DragSelect.insert(id); - } - } - else - { - if(Mouse.Selected.contains(id)) Mouse.Selected.erase(id); - else Mouse.Selected.insert(id); - - Mouse.DragSelect.insert(id); - } - } - - // Unselect nodes - if(!Intersect && Mouse.DragSelect.contains(id)) - { - Mouse.DragSelect.erase(id); - - if(!Ctrl) - { - Mouse.Selected.erase(id); - } - else - { - if(Mouse.Selected.contains(id)) Mouse.Selected.erase(id); - else Mouse.Selected.insert(id); - } - } - } - } - - if(ImGui::IsMouseReleased(ImGuiMouseButton_Left) && HeaderHovered) - { - if(Mouse.Selected.contains(id)) - { - if(Ctrl) Mouse.Selected.erase(id); - else if(!Shift && !Mouse.LocksDragged) - { - Mouse.Selected.clear(); - Mouse.Selected.insert(id); - } - } - else - { - if(!(Ctrl || Shift)) - Mouse.Selected.clear(); - Mouse.Selected.insert(id); - } - } - - if(Mouse.Locks.contains(id)) - { - node.Position = Mouse.Location - Mouse.Locks[id]; - node.Position = SnapToGrid(node.Position); - } - - // Content ========================================================================================================= - - DrawList.AddRectFilled(NodeRoot, NodeEdge, Style.Nodes.Content, Rounding + 1); - - // Header ========================================================================================================== - - if(node.Header.Enabled) - { - DrawList.PushClipRect(NodeRoot, HeaderEdge, true); - DrawList.AddRectFilled(NodeRoot, NodeEdge, HeaderColor, Rounding + 1); - DrawList.AddText(NULL, Style.FontSize / Camera.Zoom, HeaderText, Style.Nodes.Title, node.Header.Title.c_str()); - DrawList.PopClipRect(); - - DrawList.AddLine(ImVec2(NodeRoot.x, HeaderEdge.y), HeaderEdge, Style.Nodes.Border.Color, BorderThickness); - } - - // Border ========================================================================================================== - - if(Mouse.Selected.contains(id)) DrawList.AddRect(NodeRoot, NodeEdge, Style.Nodes.SelectedBorder.Color, Rounding, 0, Style.Nodes.SelectedBorder.Thickness / Camera.Zoom); - else DrawList.AddRect(NodeRoot, NodeEdge, Style.Nodes.Border.Color, Rounding, 0, BorderThickness); - - // Pins ============================================================================================================ - - int i = 0; - for(Pin& pin : node.IO.Inputs) - { - DrawPin(id, pin, i, InputRoot + ImVec2(0, PinSpacing) * (i + 0.5f), true); - ++i; - } - - int j = 0; - for(Pin& pin : node.IO.Outputs) - { - DrawPin(id, pin, j, OutputRoot + ImVec2(0, PinSpacing) * (j + 0.5f), false); - ++j; - } -} - -void ShaderGraph::DrawPin(NodeId node_id, Pin& pin, PinId pin_id, ImVec2 location, bool input) -{ - // Vars ============================================================================================================ - - // ImGui Vars - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); - - // Draw Vars - const float HeaderHeight = Style.FontSize / Camera.Zoom; - const ImVec2 Offset = ImVec2(HeaderHeight, HeaderHeight) * 0.5f; - const ImVec2 PinRoot = location; - const ImVec2 PinEdge = PinRoot + Offset * 2.0f; - 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 * 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(MouseClicked && Hovered && !Mouse.NewConnection() && !ImGui::IsKeyDown(ImGuiKey_ModAlt)) - { - StartConnection({ node_id, pin_id, input }); - } - - // Circle - PinPtr ptr = { node_id, pin_id, input }; - 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); - DrawList.AddText(NULL, HeaderHeight, TextOffset, Style.Nodes.Pins.Text, pin.Name.c_str()); - - // Input =========================================================================================================== - - if(ImGui::IsMouseReleased(ImGuiMouseButton_Left)) - { - if(Hovered) - { - if(Mouse.NewConnection()) CreateConnection({ node_id, pin_id, input }, Mouse.NewConnection); - else if(ImGui::IsKeyDown(ImGuiKey_ModAlt)) EraseConnections({ node_id, pin_id, input }); - } - } -} - -void ShaderGraph::DrawConnections() -{ - // Vars ============================================================================================================ - - // ImGui Vars - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); - - const float HeaderHeight = Style.FontSize / Camera.Zoom; - const float PinRadius = (Style.FontSize - Style.Nodes.Pins.Padding - 2.0f * Style.Nodes.Pins.BorderThickness) * 0.5f / Camera.Zoom; - const ImVec2 Padding = { Style.Grid.Lines.Padding, Style.Grid.Lines.Padding }; - const ImVec2 Offset = ImVec2(HeaderHeight, HeaderHeight) * 0.5f; - - // Connections ============================================================================================================= - - for(const Connection& connection : State.Connections) - { - DrawConnection(connection.first, connection.second); - } - - if(Mouse.NewConnection()) - { - 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 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 + 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 - , Color, Style.Nodes.Pins.Connections.Thickness / Camera.Zoom - ); - } - - // Input =========================================================================================================== - - if(ImGui::IsMouseReleased(ImGuiMouseButton_Left)) - { - StopConnection(); - } -} - -void ShaderGraph::DrawConnection(const PinPtr& a, const PinPtr& b) -{ - // Vars ============================================================================================================ - - // ImGui Vars - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); - - // Draw Vars - const float HeaderHeight = Style.FontSize / Camera.Zoom; - const float PinRadius = (Style.FontSize - Style.Nodes.Pins.Padding - 2.0f * Style.Nodes.Pins.BorderThickness) * 0.5f / Camera.Zoom; - const ImVec2 Padding = { Style.Grid.Lines.Padding, Style.Grid.Lines.Padding }; - const ImVec2 CanvasMin = ImGui::GetWindowPos() + ImGui::GetWindowContentRegionMin(); - const ImVec2 CanvasMax = ImGui::GetWindowPos() + ImGui::GetWindowContentRegionMax(); - const ImVec2 CanvasCenter = (CanvasMin + CanvasMax) * 0.5f; - const ImVec2 Offset = ImVec2(HeaderHeight, HeaderHeight) * 0.5f; - const ImVec2 Radius = ImVec2(PinRadius, 0); - - const PinPtr& In = a.Input ? a : b; - const PinPtr& Out = a.Input ? b : a; - - 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 = *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); - - const ImVec2 A = OutputLoc + ImVec2(0, HeaderHeight) * (static_cast(Out.Pin) + 0.5f) + Offset + Radius; - const ImVec2 D = InputLoc + ImVec2(0, HeaderHeight) * (static_cast(In.Pin) + 0.5f) + Offset - Radius; - const float Off = BezierOffset(A, D); - - const ImVec2 B = ImVec2(A.x + Off, A.y); - const ImVec2 C = ImVec2(D.x - Off, D.y); - - const Pin& OutPin = OutNode.IO.Outputs[Out.Pin]; - - // Draw ============================================================================================================= - - DrawList.AddBezierCubic( - A, B, C, D - , Pin::Colors[OutPin.Type], Style.Nodes.Pins.Connections.Thickness / Camera.Zoom - ); -} - -void ShaderGraph::StartConnection(const PinPtr& ptr) -{ - Mouse.NewConnection = ptr; -} - -void ShaderGraph::StopConnection() -{ - Mouse.NewConnection.reset(); -} - -void ShaderGraph::CreateConnection(const PinPtr& a, const PinPtr& b) -{ - // Check if connection is valid - if(a.Input == b.Input) return; - if(a.Node == b.Node) return; - - const PinPtr& In = a.Input ? a : b; - 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 = *State.Nodes[Out.Node]; - const Pin& OutPin = OutNode.IO.Outputs[Out.Pin]; - - // Make sure valid typing - if(InPin.Type != OutPin.Type && InPin.Type != Pin::ANY && OutPin.Type != Pin::ANY) return; - - // Break Input Connection - if(a.Input) EraseConnections(a); - if(b.Input) EraseConnections(b); - - // Add New Connections - State.Connections.emplace(a, b); - State.Connections.emplace(b, a); -} - -void ShaderGraph::EraseConnection(const PinPtr& a, const PinPtr& b) -{ - auto range = State.Connections.equal_range(a); - for(auto it = range.first; it != range.second; ++it) - { - if(it->second == b) - { - State.Connections.erase(it); - break; - } - } - - range = State.Connections.equal_range(b); - for(auto it = range.first; it != range.second; ++it) - { - if(it->second == a) - { - State.Connections.erase(it); - break; - } - } -} - -void ShaderGraph::EraseConnections(const PinPtr& a) -{ - auto it = State.Connections.find(a); - while(it != State.Connections.end()) - { - auto range = State.Connections.equal_range(it->second); - for(auto match = range.first; match != range.second; ++match) - { - if(match->second == a) - { - State.Connections.erase(match); - break; - } - } - - State.Connections.erase(it); - it = State.Connections.find(a); - } -} - -NodeId ShaderGraph::AddNode(Node* node) -{ - if(State.Erased.empty()) - { - State.Nodes.push_back(node); - return static_cast(State.Nodes.size() - 1); - } - - NodeId id = *State.Erased.begin(); - State.Nodes[id] = node; - State.Erased.erase(id); - return id; -} - -void ShaderGraph::RemoveNode(NodeId id) -{ - Node* node = State.Nodes[id]; - if(node->Info.Const) return; - - PinId i = 0; - for(const auto& pin : node->IO.Inputs) EraseConnections({ id, i++, true }); - - i = 0; - for(const auto& pin : node->IO.Outputs) EraseConnections({ id, i++, false }); - - State.Erased.insert(id); - delete node; - State.Nodes[id] = nullptr; -} - -void ShaderGraph::ClearClipboard() -{ - for(auto node : Clipboard.Nodes) delete node; - Clipboard.Nodes.clear(); - Clipboard.Connections.clear(); -} - -void ShaderGraph::Copy() -{ - if(Mouse.Selected.empty()) return; - - // Helper for connections - std::unordered_map clipboardTransform; - ImVec2 min = State.Nodes[*Mouse.Selected.begin()]->Position; - - // Reset Clipboard - ClearClipboard(); - Clipboard.Nodes.reserve(Mouse.Selected.size()); - - // Copy nodes - for(auto id : Mouse.Selected) - { - Node* node = State.Nodes[id]; - clipboardTransform[id] = static_cast(Clipboard.Nodes.size()); - Clipboard.Nodes.push_back(node->Copy(*this)); - min = ImMin(node->Position, min); - } - - // Offset nodes - for(auto node : Clipboard.Nodes) - { - node->Position -= min; - } - - // Copy connections - for(const Connection& connection : State.Connections) - { - if(!(Mouse.Selected.contains(connection.first.Node) && Mouse.Selected.contains(connection.second.Node))) continue; - - Connection copy = { - { clipboardTransform[connection.first.Node], connection.first.Pin, connection.first.Input } - , { clipboardTransform[connection.second.Node], connection.second.Pin, connection.second.Input } - }; - - Clipboard.Connections.insert(copy); - } -} - -void ShaderGraph::Paste(const ImVec2& location) -{ - // Helper for connections - const float GridSize = (Style.FontSize + Style.Grid.Lines.Padding); - std::unordered_map clipboardTransform; - ImVec2 root = SnapToGrid(location); - Mouse.Selected.clear(); - - // Paste the nodes - NodeId id = 0; - for(Node* node : Clipboard.Nodes) - { - NodeId index = clipboardTransform[id++] = AddNode(node->Copy(*this)); - State.Nodes[index]->Position += root; - Mouse.Selected.insert(index); - } - - // Paste the connections - for(const Connection& connection : Clipboard.Connections) - { - CreateConnection( - { clipboardTransform[connection.first.Node], connection.first.Pin, connection.first.Input } - , { clipboardTransform[connection.second.Node], connection.second.Pin, connection.second.Input } - ); - } -} - -void ShaderGraph::EraseSelection() -{ - for(auto node : Mouse.Selected) - { - RemoveNode(node); - } - 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; - const float diff_x = out.x - in.x; - const float diff_y = out.y - in.y; - return abs(diff_y) * (1 + glm::max(diff_x, 0.0f) / (HeaderHeight + abs(diff_y))); -} - -bool ShaderGraph::AABB(const ImVec2& a0, const ImVec2& a1, const ImVec2& b0, const ImVec2& b1) -{ - bool X = glm::max(a0.x, a1.x) >= glm::min(b0.x, b1.x) - && glm::max(b0.x, b1.x) >= glm::min(a0.x, a1.x); - - bool Y = glm::max(a0.y, a1.y) >= glm::min(b0.y, b1.y) - && glm::max(b0.y, b1.y) >= glm::min(a0.y, a1.y); - - return X && Y; -} - -ImVec2 ShaderGraph::GridToScreen(const ImVec2& position) -{ - // Window Info - const ImVec2 CanvasCenter = ImGui::GetWindowPos() - + (ImGui::GetWindowContentRegionMin() + ImGui::GetWindowContentRegionMax()) * 0.5f; - return CanvasCenter - Camera.Location + position / Camera.Zoom; -} - -ImVec2 ShaderGraph::ScreenToGrid(const ImVec2& position) -{ - const ImVec2 CanvasCenter = ImGui::GetWindowPos() - + (ImGui::GetWindowContentRegionMin() + ImGui::GetWindowContentRegionMax()) * 0.5f; - 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]; -} -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/Source/Project/Project.cpp b/Source/Project/Project.cpp index ed59a2e..9815bc3 100644 --- a/Source/Project/Project.cpp +++ b/Source/Project/Project.cpp @@ -1,6 +1,17 @@ +// ===================================================================================================================== +// 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. +// You may obtain a copy of the License at // -// Created by Maddie on 9/14/2024. +// http://www.apache.org/licenses/LICENSE-2.0 // +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ===================================================================================================================== #include #include @@ -16,23 +27,28 @@ #include #include -OpenShaderDesigner::Project::Project() - : ProjectFile_(NULL) +using namespace OpenShaderDesigner; + +RegisterAsset("##/Project", Project, ".sgp"); + +Project::Project() + : Asset("") + , ProjectFile_(NULL) +{ +} + +Project::~Project() { } -OpenShaderDesigner::Project::~Project() -{ - -} - -void OpenShaderDesigner::Project::DrawMenuBar() +void Project::DrawMenuBar() { FileManager* filesystem = EditorSystem::Get(); if(ImGui::BeginMenu("File")) { + Console::Log(Console::Message, "BeginMenu File"); if(ImGui::MenuItem("\uecc9 New...")) { Console::Log(Console::Message, "Creating Project"); @@ -112,32 +128,30 @@ void OpenShaderDesigner::Project::DrawMenuBar() Console::Log(Console::Message, "Closing Old Project"); filesystem->CloseDirectory(filesystem->Parent(ProjectFile_)); - Console::Log(Console::Message, "Closing New Project"); + Console::Log(Console::Message, "Opening New Project"); FileManager::Path res = FileManager::Path(project.result().front()); FileManager::FileID root = filesystem->LoadDirectory(res.parent_path()); filesystem->CurrentDirectory(root); - Console::Log(Console::Message, "Retrieving Project File"); + Console::Log(Console::Message, "Retrieving Project File {}", res.string()); ProjectFile_ = filesystem->Get(res); + + Console::Log(Console::Message, "Loaded Project"); } } } ImGui::EndMenu(); + Console::Log(Console::Message, "EndMenu File"); } } -void OpenShaderDesigner::Project::Open() +void Project::Open() { } -void OpenShaderDesigner::Project::Load(const FileManager::Path& path) -{ - Console::Log(Console::Message, "Loading Project {}", path.string()); -} - -void OpenShaderDesigner::Project::Save(const FileManager::Path& path) +void Project::Save(const FileManager::Path& path) { // Setup rapidjson::Document document; @@ -156,14 +170,30 @@ void OpenShaderDesigner::Project::Save(const FileManager::Path& path) Console::Log(Console::Alert, "Saved Project {} to {}", path.filename().string(), path.parent_path().string()); } -void OpenShaderDesigner::Project::Create(const FileManager::Path &path) +FileManager::Asset* Project::Load(const FileManager::Path& path) { - Console::Log(Console::Message, "Setting Up New Project"); - Reset(); - Save(path); + Project* project = EditorSystem::GetMainMenuBar(); + Console::Log(Console::Message, "Loading Project {}", path.string()); + return project; } -void OpenShaderDesigner::Project::Reset() +FileManager::Asset* Project::Import(const FileManager::Path &src, + const FileManager::Path &dst) +{ + return nullptr; +} + +FileManager::Asset* Project::Create(const FileManager::Path &path) +{ + Project* project = EditorSystem::GetMainMenuBar(); + Console::Log(Console::Message, "Loading Project {}", path.string()); + + project->Reset(); + + return project; +} + +void Project::Reset() { } diff --git a/Source/Renderer/Assets/Texture.cpp b/Source/Renderer/Assets/Texture.cpp new file mode 100644 index 0000000..a45ac79 --- /dev/null +++ b/Source/Renderer/Assets/Texture.cpp @@ -0,0 +1,102 @@ +// ===================================================================================================================== +// 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ===================================================================================================================== + +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include + +#include "Core/Console.h" +#include "Editor/EditorSystem.h" +#include "Renderer/Renderer.h" + +using namespace OpenShaderDesigner; + +RegisterAsset("##/Texture", Texture, ".png", ".jpg", ".bmp"); + +Texture::Texture(const FileManager::Path &path) + : Asset(path) + , Handle_(nullptr) +{ + int width, height, channels; + uint8_t* pixels = stbi_load(path.string().c_str(), &width, &height, &channels, 0); + Handle_ = new HandleType({ width, height }); + glw::enum_t layout; + + switch (channels) + { + case 1: layout = glw::r; break; + case 2: layout = glw::rg; break; + case 3: layout = glw::rgb; break; + case 4: layout = glw::rgba; break; + default: layout = glw::r; break; + } + + + Handle_->upload(pixels, { width, height }, { 0, 0 }, 0, layout, glw::uint8); + Handle_->generate_mipmaps(); + stbi_image_free(pixels); +} + +Texture::Texture(const FileManager::Path &src, const FileManager::Path &dst) + : Asset(dst) + , Handle_(nullptr) +{ + int width, height, channels; + uint8_t* pixels = stbi_load(src.string().c_str(), &width, &height, &channels, 0); + Handle_ = new HandleType({ width, height }); + glw::enum_t layout; + + switch (channels) + { + case 1: layout = glw::r8_i; break; + case 2: layout = glw::rg8_i; break; + case 3: layout = glw::rgb8_i; break; + case 4: layout = glw::rgba8_i; break; + default: layout = glw::r8_i; break; + } + + + Handle_->upload(pixels, { width, height }, { 0, 0 }, 0, layout, glw::uint8); + Handle_->generate_mipmaps(); + stbi_image_free(pixels); +} + +Texture::~Texture() +{ + delete Handle_; +} + +void Texture::Open() +{ + EditorSystem::Get()->OpenTexture(this); +} + +FileManager::Asset* Texture::Create(const FileManager::Path &path) +{ + return nullptr; +} + +FileManager::Asset* Texture::Load(const FileManager::Path &path) +{ + Console::Log(Console::Alert, "Loading {}", path.string()); + return new Texture(path); +} + +FileManager::Asset * Texture::Import(const FileManager::Path &src, const FileManager::Path &dst) +{ + return new Texture(src, dst); +} diff --git a/Source/Renderer/Renderer.cpp b/Source/Renderer/Renderer.cpp new file mode 100644 index 0000000..277e40e --- /dev/null +++ b/Source/Renderer/Renderer.cpp @@ -0,0 +1,96 @@ +// ===================================================================================================================== +// 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ===================================================================================================================== + +#include + +#include "Core/Console.h" + +using namespace OpenShaderDesigner; + +Renderer::Renderer() + : EditorWindow("View", 0) + , Mode_(view_texture) + , ViewTexture_(nullptr) + , Shader_(new glw::shader()) +{ + if(not Shader_->attach_source(glw::compute, { + "#version 430 core\n" + "\n" + "layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;\n" + "\n" + "layout (rgba32f, binding = 0) uniform restrict image2D out_Colour0;\n" + "\n" + "void main(void)\n" + "{\n" + " imageStore(out_Colour0, ivec2(gl_GlobalInvocationID.xy), vec4(1, 0, 0, 1));\n" + "}\n" + })) + { + Console::Log(Console::Error, "Failed to compile shader: \n{}", Shader_->get_error_string()); + } + + if(not Shader_->link()) + { + Console::Log(Console::Error, "Failed to link shader: \n{}", Shader_->get_error_string()); + } +} + +Renderer::~Renderer() +{ + delete ViewTexture_; + delete Shader_; +} + +void Renderer::DrawMenu() +{ + +} + +void Renderer::DrawWindow() +{ + switch(Mode_) + { + default: return; + + case view_texture: + DrawTexture(); + return; + } +} + +void Renderer::OpenTexture(Texture* texture) +{ + ViewTexture_ = texture; + Mode_ = view_texture; +} + +void Renderer::DrawTexture() +{ + if(ViewTexture_ == nullptr) return; + + ImVec2 reg = ImGui::GetContentRegionAvail(); + glm::vec2 size = (*ViewTexture_)->size(); + float min_r = glm::min(reg.x / size.x, reg.y / size.y); + + ImGui::Image( + reinterpret_cast(static_cast((*ViewTexture_)->handle())), + { size.x * min_r, size.y * min_r } + ); +} + +void Renderer::DrawFunction() +{ + +} diff --git a/imgui.ini b/imgui.ini index b52a61b..50775b1 100644 --- a/imgui.ini +++ b/imgui.ini @@ -8,28 +8,28 @@ Size=400,400 Collapsed=0 [Window][Dear ImGui Demo] -Pos=3070,104 -Size=370,884 +Pos=3172,977 +Size=268,54 Collapsed=0 DockId=0x00000008,0 [Window][ Console] -Pos=468,990 -Size=2972,427 +Pos=1012,1011 +Size=2428,406 +Collapsed=0 +DockId=0x0000000B,1 + +[Window][ Profiler] +Pos=0,1011 +Size=1010,406 Collapsed=0 DockId=0x00000004,0 -[Window][ Profiler] -Pos=0,990 -Size=466,427 -Collapsed=0 -DockId=0x00000003,0 - [Window][ Shader Graph] -Pos=0,24 -Size=3068,964 +Pos=344,24 +Size=2826,985 Collapsed=0 -DockId=0x00000005,0 +DockId=0x00000009,0 [Window][WindowOverViewport_11111111] Pos=0,24 @@ -65,29 +65,37 @@ Size=487,534 Collapsed=0 [Window][Inspector] -Pos=3070,24 -Size=370,78 +Pos=3172,24 +Size=268,951 Collapsed=0 DockId=0x00000007,0 [Window][File Manager] -Pos=468,990 -Size=2972,427 +Pos=1012,1011 +Size=2428,406 Collapsed=0 -DockId=0x00000004,1 +DockId=0x0000000B,0 [Window][File Manager/##hierarchy_CA8B7713] IsChild=1 -Size=289,363 +Size=289,295 + +[Window][View] +Pos=0,24 +Size=342,985 +Collapsed=0 +DockId=0x00000002,0 [Docking][Data] DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,24 Size=3440,1393 Split=Y - DockNode ID=0x00000001 Parent=0x7C6B3D9B SizeRef=3440,964 Split=X - DockNode ID=0x00000005 Parent=0x00000001 SizeRef=3068,1417 CentralNode=1 Selected=0xD4C89E35 - DockNode ID=0x00000006 Parent=0x00000001 SizeRef=370,1417 Split=Y Selected=0xE7039252 - DockNode ID=0x00000007 Parent=0x00000006 SizeRef=370,78 Selected=0xE7039252 - DockNode ID=0x00000008 Parent=0x00000006 SizeRef=370,884 Selected=0xE87781F4 - DockNode ID=0x00000002 Parent=0x7C6B3D9B SizeRef=3440,427 Split=X Selected=0xE9F1AFD1 - DockNode ID=0x00000003 Parent=0x00000002 SizeRef=466,448 Selected=0xAC4B19AE - DockNode ID=0x00000004 Parent=0x00000002 SizeRef=2972,448 Selected=0x9E7C6CAA + DockNode ID=0x00000001 Parent=0x7C6B3D9B SizeRef=3440,985 Split=X + DockNode ID=0x00000005 Parent=0x00000001 SizeRef=715,1417 Split=X Selected=0xD4C89E35 + DockNode ID=0x00000002 Parent=0x00000005 SizeRef=342,395 Selected=0x530C566C + DockNode ID=0x00000009 Parent=0x00000005 SizeRef=666,395 CentralNode=1 Selected=0xD4C89E35 + DockNode ID=0x00000006 Parent=0x00000001 SizeRef=268,1417 Split=Y Selected=0xE7039252 + DockNode ID=0x00000007 Parent=0x00000006 SizeRef=370,951 Selected=0xE7039252 + DockNode ID=0x00000008 Parent=0x00000006 SizeRef=370,32 Selected=0xE87781F4 + DockNode ID=0x00000003 Parent=0x7C6B3D9B SizeRef=3440,406 Split=X + DockNode ID=0x00000004 Parent=0x00000003 SizeRef=292,427 Selected=0xAC4B19AE + DockNode ID=0x0000000B Parent=0x00000003 SizeRef=702,427 Selected=0xE9F1AFD1 diff --git a/vcpkg.json b/vcpkg.json index a06b5ee..ec66c03 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -35,8 +35,14 @@ }, { "name" : "rapidjson", "version>=" : "2023-07-17#1" + }, { + "name" : "assimp", + "version>=" : "5.4.3" + }, { + "name" : "stb", + "version>=" : "2024-07-29#1" }, { "name" : "sdl2", - "version>=" : "2.30.6" + "version>=" : "2.30.8" } ] } \ No newline at end of file