File System

This commit is contained in:
Maddie Slockbower 2024-09-18 11:46:27 -04:00
parent 2fbdfd14b9
commit 93d383ece0
32 changed files with 1332 additions and 630 deletions

View File

@ -23,6 +23,7 @@ find_package(GLEW REQUIRED)
find_package(glm REQUIRED)
find_package(OpenGL REQUIRED COMPONENTS OpenGL)
find_package(SDL2 REQUIRED)
find_package(RapidJSON REQUIRED)
if(MSVC)
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
@ -33,6 +34,7 @@ include_directories(Include)
include_directories(External)
# Add External Libraries
add_subdirectory(External/portable-file-dialogs)
add_subdirectory(External/open-cpp-utils)
add_subdirectory(External/glw)
@ -55,7 +57,6 @@ add_executable(OpenShaderDesigner
Source/Core/Console.cpp
Source/Core/EventSystem.cpp
Source/Core/Engine.cpp
Source/Core/Renderer.cpp
# Editor
Source/Editor/EditorSystem.cpp
@ -68,6 +69,14 @@ add_executable(OpenShaderDesigner
# 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
)
target_link_libraries(OpenShaderDesigner PRIVATE
@ -75,6 +84,8 @@ target_link_libraries(OpenShaderDesigner PRIVATE
GLEW::GLEW
OpenGL::GL
${SDL2_LIBRARIES}
rapidjson
open-cpp-utils
imgui-docking
imgui-extras
imnode-graph

View File

@ -2282,7 +2282,7 @@ struct ImGuiIO
bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard.
bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)
bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.
float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
float ConfigMemoryCompactTimer; // = 60.0f // Timer_ (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
// Inputs Behaviors
// (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle)

View File

@ -27,158 +27,160 @@
namespace OpenShaderDesigner
{
class Console
class Console
{
public:
/**
* \brief Setting for displaying log entries.
*/
enum Settings
: uint8_t
{
public:
/**
* \brief Setting for displaying log entries.
*/
enum Setting
: uint8_t
{
SHOW_TIMESTAMP = 0b00000001
, SHOW_THREAD = 0b00000010
, SHOW_SEVERITY = 0b00000100
, SHOW_FILE_INFO = 0b00001000
, WRAP_TEXT = 0b00010000
Settings_ShowTimeStamp = 0b00000001
, Settings_ShowThread = 0b00000010
, Settings_Severity = 0b00000100
, Settings_ShowFileInfo = 0b00001000
, Settings_WrapText = 0b00010000
, ALL_SETTINGS = 0xFF
, DEFAULT_SETTINGS = ALL_SETTINGS ^ WRAP_TEXT
};
/**
* \brief String representations of the settings.
*/
inline static const std::string SettingNames[] =
{
"Timestamps", "Thread IDs", "Severity", "File Info", "Wrapping"
};
/**
* \brief Severity levels for log entries.
*/
enum class Severity
: int
{
MESSAGE = 0,
WARNING,
ERROR,
FATAL,
ALERT,
COMMAND,
COUNT,
DEFAULT = WARNING
};
/**
* \brief String representations of the Severity levels.
*/
static inline const std::string Severities[] =
{
"Message", "Warning", "Error", "Fatal", "Alert", "Command"
};
/**
* \brief Integer to floating point color. (ImGui APIVersion)
* \param RGB The Integer color to convert.
* \return The rgba floating point color.
*/
inline static constexpr ImVec4 ImGuiColor(unsigned int RGB)
{
return {
static_cast<float>((RGB >> 24) & 255) / 255.0f, static_cast<float>((RGB >> 16) & 255) / 255.0f,
static_cast<float>((RGB >> 8) & 255) / 255.0f, static_cast<float>((RGB >> 0) & 255) / 255.0f
};
}
/**
* \brief Color for rendering each Severity level text in editor.
*/
inline static const ImVec4 SeverityColors[] = {
ImGuiColor(0xA4B9C4FF), ImGuiColor(0xF2C554FF), ImGuiColor(0xE57327FF), ImGuiColor(0xCC211EFF),
ImGuiColor(0x9CDCFEFF),
};
static std::string ThreadID()
{
std::stringstream ss;
ss << std::this_thread::get_id();
return ss.str();
}
/**
* \brief Thread-Safe Log function for debugging.
* \tparam Args Variadic Arguments template for PixelLayout Parameters
* \param file The name of the file this was called from.
* \param line The line number this was called from.
* \param severity The severity level of the log entry.
* \param message A format string for the entry message.
* \param vargs Arguments for the format string.
*/
template <typename... Args>
static void Log(const std::string& file
, const int line
, Severity severity = Severity::DEFAULT
, const std::format_string<Args...>& message = ""
, Args&&... vargs);
static void DrawMenu();
static void DrawWindow();
static inline bool Open = true;
private:
struct LogEntry
{
const std::string Message;
const Severity Severity;
const std::string File, Timestamp, Thread;
const int Line;
};
/**
* \brief Get a formatted Entry for printing.
* \param entry The Entry to format.
* \return The Entry formatted into a string.
*/
static std::string Format(const LogEntry& entry, Setting settings);
/**
* \brief Command handling.
* \param command Command string to process.
*/
static void ProcessCommand(const std::string& command);
inline static std::list<LogEntry> EntryLog;
inline static std::mutex Lock;
inline static int Filter = static_cast<int>(0xFFFFFFFF);
inline static Setting Settings = DEFAULT_SETTINGS;
inline static std::string Command;
, Settings_ALL = 0xFF
, Settings_Default = Settings_ALL ^ Settings_WrapText
};
template <typename... Args>
void Console::Log(
const std::string& file
, const int line
, Severity severity
, const std::format_string<Args...>& fmt
, Args&&... vargs)
/**
* \brief String representations of the settings.
*/
inline static const std::string SettingNames[] =
{
auto t = std::time(nullptr);
"Timestamps", "Thread IDs", "Severity", "File Info", "Wrapping"
};
/**
* \brief Severity levels for log entries.
*/
enum Severity
: int
{
Message = 0,
Warning,
Error,
Fatal,
Alert,
Command,
COUNT,
DEFAULT = Warning
};
/**
* \brief String representations of the Severity levels.
*/
static inline const std::string Severities[] =
{
"Message", "Warning", "Error", "Fatal", "Alert", "Command"
};
/**
* \brief Integer to floating point color. (ImGui APIVersion)
* \param RGB The Integer color to convert.
* \return The rgba floating point color.
*/
inline static constexpr ImVec4 ImGuiColor(unsigned int RGB)
{
return {
static_cast<float>((RGB >> 24) & 255) / 255.0f, static_cast<float>((RGB >> 16) & 255) / 255.0f,
static_cast<float>((RGB >> 8) & 255) / 255.0f, static_cast<float>((RGB >> 0) & 255) / 255.0f
};
}
/**
* \brief Color for rendering each Severity level text in editor.
*/
inline static const ImVec4 SeverityColors[] = {
ImGuiColor(0xA4B9C4FF), ImGuiColor(0xF2C554FF), ImGuiColor(0xE57327FF), ImGuiColor(0xCC211EFF),
ImGuiColor(0x9CDCFEFF),
};
static std::string ThreadID()
{
std::stringstream ss;
ss << std::this_thread::get_id();
return ss.str();
}
/**
* \brief Thread-Safe Log function for debugging.
* \tparam Args Variadic Arguments template for PixelLayout Parameters
* \param file The name of the file this was called from.
* \param line The line number this was called from.
* \param severity The severity level of the log entry.
* \param message A format string for the entry message.
* \param vargs Arguments for the format string.
*/
template <typename... Args>
static void Log(const std::string& file
, const int line
, Severity severity = Severity::DEFAULT
, const std::format_string<Args...>& message = ""
, Args&&... vargs);
static void DrawMenu();
static void DrawWindow();
static inline bool Open = true;
private:
struct LogEntry
{
const std::string Message;
const Severity Severity;
const std::string File, Timestamp, Thread;
const int Line;
};
/**
* \brief Get a formatted Entry for printing.
* \param entry The Entry to format.
* \return The Entry formatted into a string.
*/
static std::string Format(const LogEntry& entry, uint8_t settings);
/**
* \brief Command handling.
* \param command Command string to process.
*/
static void ProcessCommand(const std::string& command);
inline static std::list<LogEntry> EntryLog_;
inline static std::mutex Lock_;
inline static int Filter_ = static_cast<int>(0xFFFFFFFF);
inline static uint8_t Settings_ = Settings_Default;
inline static std::string CommandBuffer_;
};
template <typename... Args>
void Console::Log(
const std::string& file
, const int line
, Severity severity
, const std::format_string<Args...>& fmt
, Args&&... vargs)
{
auto t = std::time(nullptr);
#ifdef _MSC_VER
#pragma warning(disable:4996)
#endif
auto tm = *std::localtime(&t);
auto tm = *std::localtime(&t);
std::lock_guard guard(Lock);
LogEntry entry{
std::vformat(fmt.get(), std::make_format_args(vargs...)), severity, file, std::format(
"{:0>2}:{:0>2}:{:0>2}", tm.tm_hour, tm.tm_min, tm.tm_sec),
ThreadID(), line
};
EntryLog.push_back(entry);
std::cout << Format(entry, ALL_SETTINGS) << std::endl;
}
std::lock_guard guard(Lock_);
LogEntry entry{
std::vformat(fmt.get(), std::make_format_args(vargs...)), severity, file, std::format(
"{:0>2}:{:0>2}:{:0>2}", tm.tm_hour, tm.tm_min, tm.tm_sec),
ThreadID(), line
};
EntryLog_.push_back(entry);
std::cout << Format(entry, Settings_ALL) << std::endl;
}
}
#define Log(...) Log(__FILE__, __LINE__, __VA_ARGS__)

View File

@ -22,25 +22,27 @@
namespace OpenShaderDesigner
{
class Engine
{
public:
static void Start(const Window::Configuration& config);
static void Stop();
class Engine
{
public:
static void Start(const Window::Configuration& config);
static void Stop();
static Window& GetMainWindow() { return *MainWindow; }
private:
static void Initialize();
static void Shutdown();
static void Update();
static Window& GetMainWindow() { return *MainWindow; }
private:
static void Initialize();
static void Shutdown();
static void Update();
inline static Timer Frame;
inline static double _Delta;
inline static Window* MainWindow;
inline static Timer Frame;
inline static double _Delta;
inline static Window* MainWindow;
public:
inline static const double& Delta = _Delta;
};
public:
inline static const double& Delta = _Delta;
};
}

View File

@ -29,113 +29,115 @@ namespace ocu = open_cpp_utils;
namespace OpenShaderDesigner
{
/**
* \brief Base Event class for sending events to the Engine.
*/
struct Event
{
template<typename T>
static uint8_t TypeOf() { return static_cast<uint8_t>(ocu::unique_id<uint8_t, T>()); }
/**
* \brief Get the Event's type ID.
* \return A pointer to the Event type ID.
*/
virtual inline uint8_t GetID() const = 0;
};
/**
* \brief Base EventHandler for abstraction.
*/
class _ImplEventHandler
{
virtual bool _HandleEvent(const Event* event) = 0;
friend class EventSystem;
};
/**
* \brief EventHandler interface for creating custom EventHandlers
* \tparam EventType The ComponentType of Event handled by the EventHandler
*/
template<typename EventType>
class EventHandler : private _ImplEventHandler
{
public:
using HandledType = EventType; //!< The type handled by the EventHandler
/**
* \brief Virtual function for custom EventHandler implementations.
* \param event The Event being handled.
*/
virtual bool HandleEvent(const HandledType* event) = 0;
private:
/**
* \brief Override for abstraction.
* \param event The Event being handled.
*/
bool _HandleEvent(const Event* event) override;
};
/**
* \brief EventSystem for posting Events to be handled.
*/
class EventSystem
{
public:
/**
* \brief Post an Event to be Handled.
*/
static void PostEvent(const Event*);
/**
* \brief Register an EventHandler with the EventSystem.
* \tparam T ComponentType of Event handled by the EventHandler.
*/
template<typename T>
static void RegisterHandler(EventHandler<T>*);
/**
* \brief Unregister an EventHandler with the EventSystem.
* \tparam T ComponentType of Event handled by the EventHandler.
*/
template<typename T>
static void UnregisterHandler(EventHandler<T>*);
private:
inline static std::list<_ImplEventHandler*> HandlerMap[MAX_EVENT_TYPES];
inline static std::mutex Lock;
EventSystem(const EventSystem&) = delete;
EventSystem(EventSystem&&) = delete;
};
/**
* \brief Base Event class for sending events to the Engine.
*/
struct Event
{
template<typename T>
void EventSystem::UnregisterHandler(EventHandler<T>* handler)
{
// Thread safe
std::lock_guard guard(Lock);
const uint8_t index = T::ID;
std::erase(HandlerMap[index], reinterpret_cast<_ImplEventHandler*>(handler));
}
static uint8_t TypeOf() { return static_cast<uint8_t>(ocu::unique_id<uint8_t, T>()); }
/**
* \brief Get the Event's type ID.
* \return A pointer to the Event type ID.
*/
virtual inline uint8_t GetID() const = 0;
};
/**
* \brief Base EventHandler for abstraction.
*/
class _ImplEventHandler
{
virtual bool _HandleEvent(const Event* event) = 0;
friend class EventSystem;
};
/**
* \brief EventHandler interface for creating custom EventHandlers
* \tparam EventType The ComponentType of Event handled by the EventHandler
*/
template<typename EventType>
class EventHandler : private _ImplEventHandler
{
public:
using HandledType = EventType; //!< The type handled by the EventHandler
/**
* \brief Virtual function for custom EventHandler implementations.
* \param event The Event being handled.
*/
virtual bool HandleEvent(const HandledType* event) = 0;
private:
/**
* \brief Override for abstraction.
* \param event The Event being handled.
*/
bool _HandleEvent(const Event* event) override;
};
/**
* \brief EventSystem for posting Events to be handled.
*/
class EventSystem
{
public:
/**
* \brief Post an Event to be Handled.
*/
static void PostEvent(const Event*);
/**
* \brief Register an EventHandler with the EventSystem.
* \tparam T ComponentType of Event handled by the EventHandler.
*/
template<typename T>
void EventSystem::RegisterHandler(EventHandler<T>* handler)
{
// Thread safe
std::lock_guard guard(Lock);
const uint8_t index = T::ID;
HandlerMap[index].push_back(reinterpret_cast<_ImplEventHandler*>(handler));
}
static void RegisterHandler(EventHandler<T>*);
template<typename EventType>
bool EventHandler<EventType>::_HandleEvent(const Event *event)
{
if(EventType::ID != event->GetID()) return false;
return HandleEvent(reinterpret_cast<const EventType*>(event));
}
/**
* \brief Unregister an EventHandler with the EventSystem.
* \tparam T ComponentType of Event handled by the EventHandler.
*/
template<typename T>
static void UnregisterHandler(EventHandler<T>*);
private:
inline static std::list<_ImplEventHandler*> HandlerMap_[MAX_EVENT_TYPES];
inline static std::mutex Lock_;
EventSystem(const EventSystem&) = delete;
EventSystem(EventSystem&&) = delete;
};
template<typename T>
void EventSystem::UnregisterHandler(EventHandler<T>* handler)
{
// Thread safe
std::lock_guard guard(Lock_);
const uint8_t index = T::ID;
std::erase(HandlerMap_[index], reinterpret_cast<_ImplEventHandler*>(handler));
}
template<typename T>
void EventSystem::RegisterHandler(EventHandler<T>* handler)
{
// Thread safe
std::lock_guard guard(Lock_);
const uint8_t index = T::ID;
HandlerMap_[index].push_back(reinterpret_cast<_ImplEventHandler*>(handler));
}
template<typename EventType>
bool EventHandler<EventType>::_HandleEvent(const Event *event)
{
if(EventType::ID != event->GetID()) return false;
return HandleEvent(reinterpret_cast<const EventType*>(event));
}
}
#define BeginEvent(EVENT) struct EVENT : OpenShaderDesigner::Event \

View File

@ -21,10 +21,12 @@
namespace OpenShaderDesigner
{
class Renderer
{
};
class Renderer
{
};
}

View File

@ -27,90 +27,92 @@ namespace ocu = open_cpp_utils;
namespace OpenShaderDesigner
{
BeginEvent(SDLEvent)
const SDL_Event sdl_event;
SDLEvent() : sdl_event() {}
BeginEvent(SDLEvent)
const SDL_Event sdl_event;
explicit SDLEvent(const SDL_Event &event) : sdl_event(event) {}
EndEvent
SDLEvent() : sdl_event() {}
explicit SDLEvent(const SDL_Event &event) : sdl_event(event) {}
EndEvent
BeginEvent(BeginFrame)
EndEvent;
BeginEvent(BeginFrame)
EndEvent;
BeginEvent(SDLEventsDone)
EndEvent;
BeginEvent(SDLEventsDone)
EndEvent;
BeginEvent(EndFrame)
EndEvent;
BeginEvent(EndFrame)
EndEvent;
class Window
class Window
{
public:
enum class VSyncMode : int
{
public:
enum class VSyncMode : int
{
DISABLED = 0,
ENABLED = 1,
ADAPTIVE = -1,
DEFAULT = DISABLED,
};
enum class FullscreenMode : int
{
WINDOWED = 0,
FULLSCREEN = SDL_WINDOW_FULLSCREEN,
FULLSCREEN_WINDOW = SDL_WINDOW_FULLSCREEN_DESKTOP,
};
struct Configuration
{
struct
{
std::string Title;
} Application;
struct
{
FullscreenMode Fullscreen;
glm::ivec2 Resolution;
VSyncMode VSync;
bool HDR;
ocu::optional<int> Multisamples;
} Video;
Configuration()
: Application { "App" }
, Video { FullscreenMode::WINDOWED, glm::ivec2(1280, 720), VSyncMode::DISABLED, false }
{ }
};
inline static const Configuration DefaultConfiguration;
explicit Window(const Configuration& config);
~Window();
void HandleEvents();
void BeginFrame();
void EndFrame();
void Close() { Open = false; }
[[nodiscard]] bool IsOpen() const { return Open; }
SDL_Window* GetHandle() { return Handle; }
[[nodiscard]] const SDL_Window* GetHandle() const { return Handle; }
SDL_GLContext GetContext() { return Context; }
[[nodiscard]] const SDL_GLContext GetContext() const { return Context; }
[[nodiscard]] glm::ivec2 Size() const { return Config.Video.Resolution; }
private:
Configuration Config;
SDL_Window* Handle;
SDL_GLContext Context;
bool Open;
DISABLED = 0,
ENABLED = 1,
ADAPTIVE = -1,
DEFAULT = DISABLED,
};
enum class FullscreenMode : int
{
WINDOWED = 0,
FULLSCREEN = SDL_WINDOW_FULLSCREEN,
FULLSCREEN_WINDOW = SDL_WINDOW_FULLSCREEN_DESKTOP,
};
struct Configuration
{
struct
{
std::string Title;
} Application;
struct
{
FullscreenMode Fullscreen;
glm::ivec2 Resolution;
VSyncMode VSync;
bool HDR;
ocu::optional<int> Multisamples;
} Video;
Configuration()
: Application { "App" }
, Video { FullscreenMode::WINDOWED, glm::ivec2(1280, 720), VSyncMode::DISABLED, false }
{ }
};
inline static const Configuration DefaultConfiguration;
explicit Window(const Configuration& config);
~Window();
void HandleEvents();
void BeginFrame();
void EndFrame();
void Close() { Open_ = false; }
[[nodiscard]] bool IsOpen() const { return Open_; }
SDL_Window* GetHandle() { return Handle_; }
[[nodiscard]] const SDL_Window* GetHandle() const { return Handle_; }
SDL_GLContext GetContext() { return Context_; }
[[nodiscard]] const SDL_GLContext GetContext() const { return Context_; }
[[nodiscard]] glm::ivec2 Size() const { return Config_.Video.Resolution; }
private:
Configuration Config_;
SDL_Window* Handle_;
SDL_GLContext Context_;
bool Open_;
};
}

View File

@ -21,16 +21,16 @@
namespace OpenShaderDesigner
{
class ConsoleWindow : public EditorWindow
{
public:
ConsoleWindow();
class ConsoleWindow : public EditorWindow
{
public:
ConsoleWindow();
void DrawMenu() override;
void DrawWindow() override;
void DrawMenu() override;
void DrawWindow() override;
private:
};
private:
};
} // OpenShaderDesigner

View File

@ -21,41 +21,51 @@
#include <unordered_map>
#include <Editor/EditorWindow.h>
#include <Editor/MainMenuBar.h>
#define MAX_EDITORS 256
namespace OpenShaderDesigner
{
class EditorSystem
class EditorSystem
{
public:
using WindowID = uint64_t;
template<typename T>
static WindowID ID() { return open_cpp_utils::unique_id<WindowID, T>(); }
template<typename T>
static T* Open() { T* window; (window = Get<T>())->Open(); return window; }
template<typename T>
static T* Close() { T* window; (window = Get<T>())->Close(); return window; }
template<typename T>
static T* Get()
{
public:
using WindowID = uint64_t;
T* window = reinterpret_cast<T*>(Windows_[ID<T>()]);
if(window == nullptr) Windows_[ID<T>()] = window = new T();
return window;
}
template<typename T>
static WindowID ID() { return open_cpp_utils::unique_id<WindowID, T>(); }
template<typename T>
static T* SetMainMenuBar() { delete MainMenuBar_; T* bar = new T(); MainMenuBar_ = bar; return bar; }
template<typename T>
static T* Open() { T* window; (window = Get<T>())->Open(); return window; }
template<typename T>
static T* GetMainMenuBar() { return static_cast<T*>(MainMenuBar_); }
template<typename T>
static T* Close() { T* window; (window = Get<T>())->Close(); return window; }
static void Initialize();
static void Draw();
static void Shutdown();
static void HandleEvents(SDL_Event* event);
template<typename T>
static T* Get()
{
T* window = reinterpret_cast<T*>(Windows[ID<T>()]);
if(window == nullptr) Windows[ID<T>()] = window = new T();
return window;
}
private:
inline static EditorWindow* Windows_[MAX_EDITORS] { nullptr };
inline static MainMenuBar* MainMenuBar_ = nullptr;
};
static void Initialize();
static void Draw();
static void Shutdown();
static void HandleEvents(SDL_Event* event);
private:
inline static EditorWindow* Windows[MAX_EDITORS] { nullptr };
};
}

View File

@ -21,85 +21,87 @@
namespace OpenShaderDesigner
{
/**
* \brief EditorWindow class for wrapping ImGui window functionality.
*/
class EditorWindow
{
public:
/**
* \brief EditorWindow class for wrapping ImGui window functionality.
* \brief Open the EditorWindow.
*/
class EditorWindow
void Open();
/**
* \brief Draw the EditorWindow.
*/
void Draw();
/**
* \brief Close the EditorWindow.
*/
void Close();
/**
* \brief Check if the EditorWindow is open.
* \return
*/
[[nodiscard]] bool IsOpen() const { return bOpen_; }
const std::string Title; //!< Title for the EditorWindow.
void SetFlags(ImGuiWindowFlags flags) { Flags_ |= flags; }
void ClearFlags(ImGuiWindowFlags flags) { Flags_ &= ~flags; }
void ToggleFlags(ImGuiWindowFlags flags) { Flags_ ^= flags; }
[[nodiscard]] bool CheckFlag(ImGuiWindowFlags flag) const { return Flags_ & flag; }
[[nodiscard]] bool HasMenuBar() const { return CheckFlag(ImGuiWindowFlags_MenuBar); }
protected:
~EditorWindow() = default;
EditorWindow(const std::string& title
, ImGuiWindowFlags flags);
/**
* \brief OnOpen callback for when the EditorWindow is opened.
*/
virtual void OnOpen()
{
public:
/**
* \brief Open the EditorWindow.
*/
void Open();
/**
* \brief Draw the EditorWindow.
*/
void Draw();
/**
* \brief Close the EditorWindow.
*/
void Close();
/**
* \brief Check if the EditorWindow is open.
* \return
*/
[[nodiscard]] bool IsOpen() const { return bOpen; }
const std::string Title; //!< Title for the EditorWindow.
void SetFlags(ImGuiWindowFlags flags) { Flags |= flags; }
void ClearFlags(ImGuiWindowFlags flags) { Flags &= ~flags; }
void ToggleFlags(ImGuiWindowFlags flags) { Flags ^= flags; }
[[nodiscard]] bool CheckFlag(ImGuiWindowFlags flag) const { return Flags & flag; }
[[nodiscard]] bool HasMenuBar() const { return CheckFlag(ImGuiWindowFlags_MenuBar); }
protected:
~EditorWindow() = default;
EditorWindow(const std::string& title
, ImGuiWindowFlags flags);
/**
* \brief OnOpen callback for when the EditorWindow is opened.
*/
virtual void OnOpen()
{
};
/**
* \brief DrawWindow function for when the EditorWindow is being drawn.
*/
virtual void DrawWindow()
{
};
/**
* \brief DrawMenu function for when the EditorWindow Menu is being drawn.
*/
virtual void DrawMenu()
{
};
/**
* \brief OnClose callback for when the EditorWindow is closed.
*/
virtual void OnClose()
{
};
private:
EditorWindow(const EditorWindow&) = delete;
EditorWindow(EditorWindow&&) = delete;
int Flags;
bool bOpen;
friend class EditorSystem;
};
/**
* \brief DrawWindow function for when the EditorWindow is being drawn.
*/
virtual void DrawWindow()
{
};
/**
* \brief DrawMenu function for when the EditorWindow Menu is being drawn.
*/
virtual void DrawMenu()
{
};
/**
* \brief OnClose callback for when the EditorWindow is closed.
*/
virtual void OnClose()
{
};
private:
EditorWindow(const EditorWindow&) = delete;
EditorWindow(EditorWindow&&) = delete;
int Flags_;
bool bOpen_;
friend class EditorSystem;
};
}

View File

@ -0,0 +1,26 @@
//
// Created by Maddie on 9/14/2024.
//
#ifndef MAINMENUBAR_H
#define MAINMENUBAR_H
#include <imgui-docking/imgui.h>
namespace OpenShaderDesigner
{
class MainMenuBar
{
public:
virtual void DrawMenuBar() = 0;
friend class EditorSystem;
private:
void Draw() { ImGui::BeginMainMenuBar(); DrawMenuBar(); ImGui::EndMainMenuBar(); }
};
}
#endif //MAINMENUBAR_H

View File

@ -24,37 +24,37 @@
namespace OpenShaderDesigner
{
class Profiler
: public EditorWindow
, public EventHandler<BeginFrame>
, public EventHandler<EndFrame>
class Profiler
: public EditorWindow
, public EventHandler<BeginFrame>
, public EventHandler<EndFrame>
{
public:
Profiler();
~Profiler();
void DrawWindow() override;
bool HandleEvent(const EventHandler<BeginFrame>::HandledType* event) override;
bool HandleEvent(const EventHandler<EndFrame>::HandledType* event) override;
private:
enum
{
public:
Profiler();
~Profiler();
EVENTS = 0
, RENDER
, EDITOR
, END
void DrawWindow() override;
bool HandleEvent(const EventHandler<BeginFrame>::HandledType* event) override;
bool HandleEvent(const EventHandler<EndFrame>::HandledType* event) override;
private:
enum
{
EVENTS = 0
, RENDER
, EDITOR
, END
, COUNT
, LAST = COUNT - 1
};
uint64_t Frame;
double Deltas[COUNT];
Timer Timer;
, COUNT
, LAST = COUNT - 1
};
uint64_t Frame_;
double Deltas_[COUNT];
Timer Timer_;
};
}

View File

@ -0,0 +1,46 @@
// =====================================================================================================================
// 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 ASSET_H
#define ASSET_H
#include <open-cpp-utils/filesystem.h>
namespace ocu = open_cpp_utils;
namespace OpenShaderDesigner
{
class Asset
{
public:
Asset() : Dirty_(false) { }
bool Dirty() const { return Dirty_; }
virtual void Open() = 0;
virtual void Load(const std::filesystem::path& path) = 0;
virtual void Save(const std::filesystem::path& path) { Dirty_ = false; };
protected:
void MakeDirty() { Dirty_ = true; }
private:
bool Dirty_;
};
}
#endif //ASSET_H

View File

@ -0,0 +1,91 @@
// =====================================================================================================================
// 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 FILESYSTEM_H
#define FILESYSTEM_H
#include <Editor/EditorWindow.h>
#include <FileSystem/Asset.h>
#include <open-cpp-utils/filesystem.h>
#include <open-cpp-utils/map.h>
namespace ocu = open_cpp_utils;
namespace OpenShaderDesigner
{
class FileManager : public EditorWindow
{
public:
using FileSystem = ocu::filesystem<Asset, FileManager>;
using File = FileSystem::file;
using Path = std::filesystem::path;
using FileID = FileSystem::file_id;
using FileType = uint32_t;
friend FileSystem;
enum FileType_
{
FileType_Folder = 0
, FileType_Project
};
inline static ocu::map<std::string, FileType> ExtensionMap = {
{ "", FileType_Folder }
, { ".sgp", FileType_Project }
};
private:
static Asset* load(const Path& file);
static Asset* import(const Path& src, const Path& dst);
static Asset* create(const Path& file);
public:
FileManager();
void DrawMenu() override;
void DrawWindow() override;
FileID CurrentDirectory() const { return CurrentDirectory_; }
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 LoadDirectory(const Path& path) { return Filesystem_.load_directory(path); }
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]; }
FileID Parent(FileID id) const { return Filesystem_.parent(id); }
bool AnyDirty();
void SaveAll();
static Path GetHomeDirectory();
private:
FileSystem Filesystem_;
FileID CurrentDirectory_, Selected_;
bool Rename_, FocusRename_;
std::string RenameBuffer_;
};
}
#endif //FILESYSTEM_H

View File

@ -13,5 +13,7 @@
// limitations under the License.
// =====================================================================================================================
#ifndef COMPILER_H
#define COMPILER_H
#include <Core/Renderer.h>
#endif //COMPILER_H

View File

@ -50,8 +50,6 @@ struct Integer : public Node
[[nodiscard]] Node* Copy(ShaderGraph& graph) const override;
void Inspect() override;
ValueType Value;
};
RegisterNode("Math/Constants/Integer", Integer);
@ -68,8 +66,6 @@ struct UnsignedInteger : public Node
[[nodiscard]] Node* Copy(ShaderGraph& graph) const override;
void Inspect() override;
ValueType Value;
};
RegisterNode("Math/Constants/Unsigned Integer", UnsignedInteger);
@ -86,8 +82,6 @@ struct Scalar : public Node
[[nodiscard]] Node* Copy(ShaderGraph& graph) const override;
void Inspect() override;
ValueType Value;
};
RegisterNode("Math/Constants/Scalar", Scalar);
@ -104,8 +98,6 @@ struct Vector : public Node
[[nodiscard]] Node* Copy(ShaderGraph& graph) const override;
void Inspect() override;
ValueType Value;
};
RegisterNode("Math/Constants/Vector", Vector);

View File

@ -197,15 +197,16 @@ namespace OpenShaderDesigner
// History Functionality
void PushState();
void PopState();
GraphState& GetState() { return State; }
GraphState& GetState() { return State_; }
private:
bool GrabFocus;
bool GrabFocus_;
GraphState State;
std::stack<GraphState> History;
GraphState State_;
std::stack<GraphState> History_;
ImVec2 ContextMenuPosition;
ImVec2 ContextMenuPosition_;
friend class Inspector;
};

35
Include/Project/Project.h Normal file
View File

@ -0,0 +1,35 @@
//
// Created by Maddie on 9/14/2024.
//
#ifndef PROJECT_H
#define PROJECT_H
#include <Editor/MainMenuBar.h>
#include <FileSystem/FileManager.h>
namespace OpenShaderDesigner
{
class Project : public MainMenuBar, public Asset
{
public:
Project();
virtual ~Project();
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);
private:
void Reset();
FileManager::FileID ProjectFile_;
};
}
#endif //PROJECT_H

View File

@ -20,18 +20,20 @@
namespace OpenShaderDesigner
{
class Timer
{
public:
Timer() : Start(std::chrono::high_resolution_clock::now()) { }
class Timer
{
public:
Timer() : Start(std::chrono::high_resolution_clock::now()) { }
void Reset() { Start = std::chrono::high_resolution_clock::now(); }
void Reset() { Start = std::chrono::high_resolution_clock::now(); }
[[nodiscard]] double Poll() const
{ return std::chrono::duration<double>(std::chrono::high_resolution_clock::now() - Start).count(); }
private:
std::chrono::high_resolution_clock::time_point Start;
};
[[nodiscard]] double Poll() const
{ return std::chrono::duration<double>(std::chrono::high_resolution_clock::now() - Start).count(); }
private:
std::chrono::high_resolution_clock::time_point Start;
};
}
#endif //TIMER_H

View File

@ -20,16 +20,16 @@ using namespace OpenShaderDesigner;
void Console::DrawMenu()
{
std::lock_guard guard(Lock);
std::lock_guard guard(Lock_);
if(ImGui::BeginMenu("\uf0e5 Settings"))
{
for(uint8_t setting = 0; setting < sizeof(SettingNames) / sizeof(std::string); ++setting)
{
int flag = 0x1 << setting;
if(ImGui::MenuItem(SettingNames[setting].c_str(), nullptr, Settings & flag))
if(ImGui::MenuItem(SettingNames[setting].c_str(), nullptr, Settings_ & flag))
{
Settings = static_cast<Setting>(Settings ^ flag);
Settings_ = Settings_ ^ flag;
}
}
@ -41,9 +41,9 @@ void Console::DrawMenu()
for(int severity = 0; severity < static_cast<int>(Severity::COUNT); ++severity)
{
int flag = 0x1 << severity;
if(ImGui::MenuItem(Severities[severity].c_str(), NULL, Filter & flag))
if(ImGui::MenuItem(Severities[severity].c_str(), NULL, Filter_ & flag))
{
Filter = Filter ^ flag;
Filter_ = Filter_ ^ flag;
}
}
@ -53,23 +53,23 @@ void Console::DrawMenu()
void Console::DrawWindow()
{
std::lock_guard guard(Lock);
std::lock_guard guard(Lock_);
const auto& Log = EntryLog;
const auto& Log = EntryLog_;
const float padding = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
if(ImGui::BeginChild("##scrolling", ImVec2(0, -padding),
ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_AutoResizeY
, ImGuiWindowFlags_AlwaysVerticalScrollbar | (Settings & WRAP_TEXT ? ImGuiWindowFlags_None : ImGuiWindowFlags_AlwaysHorizontalScrollbar)))
, ImGuiWindowFlags_AlwaysVerticalScrollbar | (Settings_ & Settings_WrapText ? ImGuiWindowFlags_None : ImGuiWindowFlags_AlwaysHorizontalScrollbar)))
{
for(const LogEntry& entry : Log)
{
int flag = 0x1 << static_cast<int>(entry.Severity);
if((flag & Filter) == 0) continue;
if((flag & Filter_) == 0) continue;
ImGui::PushStyleColor(ImGuiCol_Text, SeverityColors[static_cast<int>(entry.Severity)]);
if(Settings & WRAP_TEXT) ImGui::TextWrapped("%s", Format(entry, Settings).c_str());
else ImGui::Text("%s", Format(entry, Settings).c_str());
if(Settings_ & Settings_WrapText) ImGui::TextWrapped("%s", Format(entry, Settings_).c_str());
else ImGui::Text("%s", Format(entry, Settings_).c_str());
ImGui::PopStyleColor();
}
@ -80,23 +80,23 @@ void Console::DrawWindow()
ImGui::Text(">"); ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - ImGui::GetStyle().WindowPadding.x);
if(ImGui::InputText("##commandline", &Command, ImGuiInputTextFlags_EnterReturnsTrue))
if(ImGui::InputText("##commandline", &CommandBuffer_, ImGuiInputTextFlags_EnterReturnsTrue))
{
ProcessCommand(Command);
Command.clear();
ProcessCommand(CommandBuffer_);
CommandBuffer_.clear();
ImGui::SetKeyboardFocusHere(-1);
}
}
std::string Console::Format(const LogEntry& entry, Setting settings)
std::string Console::Format(const LogEntry& entry, uint8_t settings)
{
std::string severity = std::format("<{}>", Severities[static_cast<int>(entry.Severity)]);
std::string message;
// Get Timestamp and Thread bits.
const bool showTimestamp = (settings & SHOW_TIMESTAMP);
const bool showThreadID = (settings & SHOW_THREAD) && entry.Severity != Severity::COMMAND;
const bool showTimestamp = (settings & Settings_ShowTimeStamp);
const bool showThreadID = (settings & Settings_ShowThread) && entry.Severity != Severity::Command;
const bool check1 = showTimestamp || showThreadID;
// Print Timestamp and/or Thread if enabled.
@ -104,8 +104,8 @@ std::string Console::Format(const LogEntry& entry, Setting settings)
if(showThreadID) message += std::format("Thread #{:0>3} | ", entry.Thread);
// Get Severity and File bits.
const bool showSeverity = (settings & SHOW_SEVERITY) && entry.Severity != Severity::COMMAND;
const bool showFile = (settings & SHOW_FILE_INFO) && entry.Severity != Severity::COMMAND;
const bool showSeverity = (settings & Settings_Severity) && entry.Severity != Severity::Command;
const bool showFile = (settings & Settings_ShowFileInfo) && entry.Severity != Severity::Command;
const bool check2 = showSeverity || showFile;
// Print Severity and/or File if enabled.
@ -113,7 +113,7 @@ std::string Console::Format(const LogEntry& entry, Setting settings)
if(showFile) message += std::format("{}({})", entry.File, entry.Line);
// Print separator if any are enabled
if(check1 || check2) message += entry.Severity == Severity::COMMAND ? "> " : ": ";
if(check1 || check2) message += entry.Severity == Severity::Command ? "> " : ": ";
// Add message.
message += entry.Message;

View File

@ -20,18 +20,22 @@
#include <Editor/ConsoleWindow.h>
#include <Editor/Profiler.h>
#include <FileSystem/FileManager.h>
#include <Graph/ShaderGraph.h>
#include "Project/Project.h"
void OpenShaderDesigner::Engine::Start(const Window::Configuration& config)
{
Console::Log(Console::Severity::ALERT, "Starting {}", config.Application.Title);
Console::Log(Console::Severity::Alert, "Starting {}", config.Application.Title);
Console::Log(Console::Severity::MESSAGE, "Creating Main Window");
Console::Log(Console::Message, "Creating Main Window");
MainWindow = new Window(config);
Initialize();
Console::Log(Console::Severity::MESSAGE, "Starting Main Loop");
Console::Log(Console::Message, "Starting Main Loop");
while(MainWindow->IsOpen())
{
Update();
@ -47,20 +51,26 @@ void OpenShaderDesigner::Engine::Stop()
void OpenShaderDesigner::Engine::Initialize()
{
Console::Log(Console::Severity::MESSAGE, "Initializing Engine");
Console::Log(Console::Message, "Initializing Engine");
Console::Log(Console::Severity::MESSAGE, "Initializing Editor");
Console::Log(Console::Message, "Initializing Editor");
EditorSystem::Initialize();
Console::Log(Console::Severity::MESSAGE, "Opening Console");
Console::Log(Console::Message, "Opening Console");
EditorSystem::Open<ConsoleWindow>();
Console::Log(Console::Severity::MESSAGE, "Opening Profiler");
Console::Log(Console::Message, "Opening Profiler");
EditorSystem::Open<Profiler>();
Console::Log(Console::Severity::MESSAGE, "Opening Shader Graph");
Console::Log(Console::Message, "Opening File Manager");
EditorSystem::Open<FileManager>();
Console::Log(Console::Message, "Opening Shader Graph");
EditorSystem::Open<Inspector>();
EditorSystem::Open<ShaderGraph>();
Console::Log(Console::Message, "Setting up Project");
EditorSystem::SetMainMenuBar<Project>();
}
void OpenShaderDesigner::Engine::Shutdown()

View File

@ -23,11 +23,11 @@ using namespace OpenShaderDesigner;
void EventSystem::PostEvent(const Event* event)
{
// Thread safe
std::lock_guard guard(Lock);
std::lock_guard guard(Lock_);
// Alert Handlers
const int index = event->GetID();
for(_ImplEventHandler* handler : HandlerMap[index])
for(_ImplEventHandler* handler : HandlerMap_[index])
{
if(handler->_HandleEvent(event)) break;
}

View File

@ -22,10 +22,10 @@
using namespace OpenShaderDesigner;
Window::Window(const Configuration& config)
: Config(config)
: Config_(config)
{
int flags = static_cast<int>(Config.Video.Fullscreen) | SDL_WINDOW_OPENGL;
flags |= Config.Video.Fullscreen == FullscreenMode::WINDOWED ? SDL_WINDOW_RESIZABLE : 0;
int flags = static_cast<int>(Config_.Video.Fullscreen) | SDL_WINDOW_OPENGL;
flags |= Config_.Video.Fullscreen == FullscreenMode::WINDOWED ? SDL_WINDOW_RESIZABLE : 0;
SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_AUDIO);
@ -40,54 +40,54 @@ Window::Window(const Configuration& config)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
if(Config.Video.HDR)
if(Config_.Video.HDR)
{
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 10);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 10);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 10);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 2);
SDL_GL_SetAttribute(SDL_GL_FLOATBUFFERS, SDL_TRUE);
}
if(Config.Video.Multisamples())
if(Config_.Video.Multisamples())
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, SDL_TRUE);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, Config.Video.Multisamples);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, Config_.Video.Multisamples);
}
if((Handle = SDL_CreateWindow(
Config.Application.Title.c_str(),
if((Handle_ = SDL_CreateWindow(
Config_.Application.Title.c_str(),
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
Config.Video.Resolution.x, Config.Video.Resolution.y,
Config_.Video.Resolution.x, Config_.Video.Resolution.y,
flags)) == nullptr)
{
Console::Log(Console::Severity::FATAL, "Failed to create SDL Window: {}", SDL_GetError());
Console::Log(Console::Severity::Fatal, "Failed to create SDL Window: {}", SDL_GetError());
assert(false);
return;
}
Context = SDL_GL_CreateContext(Handle);
Context_ = SDL_GL_CreateContext(Handle_);
if(Context == nullptr)
if(Context_ == nullptr)
{
Console::Log(Console::Severity::FATAL, "Failed to create OpenGL Context: {}", SDL_GetError());
SDL_DestroyWindow(Handle);
Console::Log(Console::Severity::Fatal, "Failed to create OpenGL Context: {}", SDL_GetError());
SDL_DestroyWindow(Handle_);
assert(false);
return;
}
if(SDL_GL_MakeCurrent(Handle, Context))
if(SDL_GL_MakeCurrent(Handle_, Context_))
{
Console::Log(Console::Severity::FATAL, "Failed to set OpenGL Context: {}", SDL_GetError());
SDL_GL_DeleteContext(Context);
SDL_DestroyWindow(Handle);
Console::Log(Console::Severity::Fatal, "Failed to set OpenGL Context: {}", SDL_GetError());
SDL_GL_DeleteContext(Context_);
SDL_DestroyWindow(Handle_);
assert(false);
return;
}
// Set VSync Mode
SDL_GL_SetSwapInterval(static_cast<int>(Config.Video.VSync));
SDL_GL_SetSwapInterval(static_cast<int>(Config_.Video.VSync));
// Enable experimental features
glewExperimental = GL_TRUE;
@ -96,9 +96,9 @@ Window::Window(const Configuration& config)
GLenum error;
if((error = glewInit()) != GLEW_OK)
{
Console::Log(Console::Severity::FATAL, "Failed to Initialize GLEW: {}", reinterpret_cast<const char*>(glewGetErrorString(error)));
SDL_GL_DeleteContext(Context);
SDL_DestroyWindow(Handle);
Console::Log(Console::Severity::Fatal, "Failed to Initialize GLEW: {}", reinterpret_cast<const char*>(glewGetErrorString(error)));
SDL_GL_DeleteContext(Context_);
SDL_DestroyWindow(Handle_);
assert(false);
return;
}
@ -108,22 +108,22 @@ Window::Window(const Configuration& config)
glDisable(GL_DEPTH_TEST);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
SDL_GL_SwapWindow(Handle);
SDL_GL_SwapWindow(Handle_);
// Set running
Open = true;
Open_ = true;
const char* glVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
const char* glewVersion = reinterpret_cast<const char*>(glewGetString(GLEW_VERSION));
SDL_version sdlVersion; SDL_GetVersion(&sdlVersion);
Console::Log(Console::Severity::ALERT, "Initialized SDL ({}.{}.{})", sdlVersion.major, sdlVersion.minor, sdlVersion.patch);
Console::Log(Console::Severity::ALERT, "Running OpenGL ({}), GLEW ({})", glVersion, glewVersion);
Console::Log(Console::Severity::Alert, "Initialized SDL ({}.{}.{})", sdlVersion.major, sdlVersion.minor, sdlVersion.patch);
Console::Log(Console::Severity::Alert, "Running OpenGL ({}), GLEW ({})", glVersion, glewVersion);
}
Window::~Window()
{
SDL_GL_DeleteContext(Context);
SDL_DestroyWindow(Handle);
SDL_GL_DeleteContext(Context_);
SDL_DestroyWindow(Handle_);
SDL_Quit();
}
@ -147,8 +147,8 @@ void Window::HandleEvents()
switch(event.window.event)
{
case SDL_WINDOWEVENT_SIZE_CHANGED:
Config.Video.Resolution.x = event.window.data1;
Config.Video.Resolution.y = event.window.data2;
Config_.Video.Resolution.x = event.window.data1;
Config_.Video.Resolution.y = event.window.data2;
break;
default:
break;
@ -170,14 +170,14 @@ void Window::BeginFrame()
HandleEvents();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, Config.Video.Resolution.x, Config.Video.Resolution.y);
glViewport(0, 0, Config_.Video.Resolution.x, Config_.Video.Resolution.y);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
void Window::EndFrame()
{
SDL_GL_SwapWindow(Handle);
SDL_GL_SwapWindow(Handle_);
OpenShaderDesigner::EndFrame event;
EventSystem::PostEvent(&event);

View File

@ -30,7 +30,7 @@ void EditorSystem::Initialize()
{
Window& Window = Engine::GetMainWindow();
Console::Log(Console::Severity::ALERT, "Initializing Dear ImGUI");
Console::Log(Console::Severity::Alert, "Initializing Dear ImGUI");
IMGUI_CHECKVERSION();
ImGui::CreateContext();
@ -136,7 +136,7 @@ void EditorSystem::Initialize()
ImGui_ImplSDL2_InitForOpenGL(Window.GetHandle(), Window.GetContext());
ImGui_ImplOpenGL3_Init("#version 460 core");
Console::Log(Console::Severity::ALERT, "Initialized ImGui ({})", IMGUI_VERSION);
Console::Log(Console::Severity::Alert, "Initialized ImGui ({})", IMGUI_VERSION);
}
void EditorSystem::Draw()
@ -152,11 +152,16 @@ void EditorSystem::Draw()
ImGui::ResetMouseDragDelta();
}
if(MainMenuBar_)
{
MainMenuBar_->Draw();
}
ImGui::ShowDemoWindow();
for (int i = 0; i < MAX_EDITORS; ++i)
{
EditorWindow* editor = Windows[i];
EditorWindow* editor = Windows_[i];
if(editor == nullptr || !editor->IsOpen()) continue;
editor->Draw();
}
@ -168,6 +173,9 @@ void EditorSystem::Draw()
void EditorSystem::Shutdown()
{
delete MainMenuBar_;
for(auto it : Windows_) delete it;
// Shutdown ImGui
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplSDL2_Shutdown();

View File

@ -19,15 +19,15 @@ using namespace OpenShaderDesigner;
void EditorWindow::Open()
{
if(bOpen) return;
bOpen = true;
if(bOpen_) return;
bOpen_ = true;
OnOpen();
}
void EditorWindow::Draw()
{
const bool prev = bOpen;
if(ImGui::Begin(Title.c_str(), &bOpen, Flags))
const bool prev = bOpen_;
if(ImGui::Begin(Title.c_str(), &bOpen_, Flags_))
{
if(HasMenuBar())
{
@ -43,24 +43,24 @@ void EditorWindow::Draw()
}
ImGui::End();
if(bOpen != prev)
if(bOpen_ != prev)
{
bOpen ? OnOpen() : OnClose();
bOpen_ ? OnOpen() : OnClose();
}
}
void EditorWindow::Close()
{
if(!bOpen) return;
bOpen = false;
if(!bOpen_) return;
bOpen_ = false;
OnClose();
}
EditorWindow::EditorWindow(const std::string &title, ImGuiWindowFlags flags)
: Title(title)
, Flags(flags)
, bOpen(false)
, Flags_(flags)
, bOpen_(false)
{
}

View File

@ -19,8 +19,8 @@ using namespace OpenShaderDesigner;
Profiler::Profiler()
: EditorWindow("\uf214 Profiler", 0)
, Frame(0), Deltas{ 0 }
, Timer()
, Frame_(0), Deltas_{ 0 }
, Timer_()
{
EventSystem::RegisterHandler<BeginFrame>(this);
EventSystem::RegisterHandler<EndFrame>(this);
@ -34,19 +34,19 @@ Profiler::~Profiler()
void Profiler::DrawWindow()
{
ImGui::Text("Frame #%d", Frame);
ImGui::Text("FPS: %07.02f (%06.02f ms)", 1.0 / Deltas[LAST], 1000.0 * Deltas[LAST]);
ImGui::Text("Frame #%d", Frame_);
ImGui::Text("FPS: %07.02f (%06.02f ms)", 1.0 / Deltas_[LAST], 1000.0 * Deltas_[LAST]);
}
bool Profiler::HandleEvent(const EventHandler<BeginFrame>::HandledType* event)
{
++Frame;
Timer.Reset();
++Frame_;
Timer_.Reset();
return false;
}
bool Profiler::HandleEvent(const EventHandler<EndFrame>::HandledType* event)
{
Deltas[END] = Timer.Poll();
Deltas_[END] = Timer_.Poll();
return false;
}

View File

@ -15,6 +15,7 @@
#include <iostream>
#include <Core/Engine.h>
#include <Editor/EditorSystem.h>
int main(int, char**)
{
@ -30,4 +31,4 @@ int main(int, char**)
Engine::Start(config);
return 0;
}
}

View File

@ -0,0 +1,259 @@
// =====================================================================================================================
// 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 <Editor/EditorSystem.h>
#include <FileSystem/FileManager.h>
#include <Core/Console.h>
#include <imgui-docking/misc/cpp/imgui_stdlib.h>
#include "Project/Project.h"
OpenShaderDesigner::Asset * OpenShaderDesigner::FileManager::load(const Path& path)
{
Console::Log(Console::Message, "Loading File {}", path.string());
FileType type = ExtensionMap[path.extension().string()];
switch (type)
{
case FileType_Project:
{
Project* project = EditorSystem::GetMainMenuBar<Project>();
project->Load(path);
return project;
}
default:
return nullptr;
}
}
OpenShaderDesigner::Asset * OpenShaderDesigner::FileManager::import(const Path &src, const Path &dst)
{
return nullptr;
}
OpenShaderDesigner::Asset * OpenShaderDesigner::FileManager::create(const Path &path)
{
Console::Log(Console::Message, "Creating File {}", path.string());
FileType type = ExtensionMap[path.extension().string()];
switch (type)
{
case FileType_Project:
{
Project* project = EditorSystem::GetMainMenuBar<Project>();
project->Create(path);
return project;
}
default:
return nullptr;
}
}
OpenShaderDesigner::FileManager::FileManager()
: EditorWindow("File Manager", ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoScrollbar)
, CurrentDirectory_(FileSystem::root), Selected_(FileSystem::root)
, Rename_(false), FocusRename_(false)
{
//CurrentDirectory_ = Filesystem_.load_directory(std::filesystem::current_path());
}
void OpenShaderDesigner::FileManager::DrawMenu()
{
}
void OpenShaderDesigner::FileManager::DrawWindow()
{
// Directory Hierarchy
ImGui::BeginGroup();
if (ImGui::BeginChild("##hierarchy", ImVec2(200, 0),
ImGuiChildFlags_ResizeX | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_Border))
{
struct Visitor
{
bool operator()(File& file, FileID id)
{
const FileSystem& tree = file.system();
uint32_t depth = tree.depth(id);
// Skip root
if(id == FileSystem::root) return false;
// Skip if we are in a closed directory
if(depth > stack.size()) return false;
// Skip non-directories
const bool is_dir = file.is_directory();
if(not is_dir) return false;
// Pop dead paths
while(depth < stack.size())
{
stack.pop();
ImGui::TreePop();
}
// Skip if the top of the stack is not the parent of the file
if(stack.top() != tree.parent(id)) return false;
// Generate display name
const char* icon = "\ued53";
std::string name = std::format("{} {}##{}", icon, file.path().stem().string(), id);
// Display flags
int flags = 0;
if(file.has_subdirectory())
{
flags |= ImGuiTreeNodeFlags_OpenOnArrow;
flags |= ImGuiTreeNodeFlags_OpenOnDoubleClick;
flags |= ImGuiTreeNodeFlags_SpanAvailWidth;
}
else
{
flags |= ImGuiTreeNodeFlags_Leaf;
}
if(id == directory) flags |= ImGuiTreeNodeFlags_Selected;
if(ImGui::TreeNodeEx(name.c_str(), flags))
{
stack.push(id);
}
if(ImGui::IsItemClicked()) directory = file.get_id();
return false;
}
std::stack<FileID> stack;
FileID directory;
} visitor;
visitor.directory = CurrentDirectory_;
visitor.stack.push(FileSystem::root);
Filesystem_.traverse(visitor);
while(visitor.stack.size() > 1)
{
ImGui::TreePop();
visitor.stack.pop();
}
CurrentDirectory_ = visitor.directory;
}
ImGui::EndChild();
ImGui::EndGroup();
ImGui::SameLine();
if(ImGui::BeginTable("##directory", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter))
{
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn(" Name");
ImGui::TableSetupColumn("Type");
ImGui::TableSetupColumn("Source");
ImGui::TableHeadersRow();
for(FileID it = Filesystem_.begin(CurrentDirectory_); it != Filesystem_.end(CurrentDirectory_); it = Filesystem_.next(it))
{
File& file = Filesystem_[it];
ImGui::TableNextRow();
const bool selected = Selected_ == it;
ImGui::TableNextColumn();
// Display Name
bool dirty = *file ? file->Dirty() : false;
std::string name = std::format("{}{}", dirty ? "\uea81 " : "", file.path().stem().string());
if(ImGui::Selectable(
std::format(" {}##{}", Rename_ && selected ? "" : name.c_str(), file.get_id()).c_str()
, selected, ImGuiSelectableFlags_SpanAllColumns
))
{
Selected_ = Rename_ ? Selected_ : it;
}
// Renaming
if(selected && Rename_)
{
ImGui::SameLine();
if(FocusRename_) ImGui::SetKeyboardFocusHere();
if(ImGui::InputText(" ##rename", &RenameBuffer_, ImGuiInputTextFlags_EnterReturnsTrue))
{
if(not RenameBuffer_.empty()) Filesystem_.rename(Selected_, RenameBuffer_);
Rename_ = false;
}
if(FocusRename_)
{
FocusRename_ = false;
}
else if(ImGui::IsItemFocused() == false)
{
Rename_ = false;
}
}
ImGui::TableNextColumn();
ImGui::Text("%s", file.path().extension().string().c_str());
ImGui::TableNextColumn();
ImGui::Text("%s", file.path().string().c_str());
}
ImGui::EndTable();
}
}
bool OpenShaderDesigner::FileManager::AnyDirty()
{
bool res = false;
struct Visitor
{
bool operator()(File& file, FileID id)
{
if(id == FileSystem::root) return false;
res |= file->Dirty();
return false;
}
bool& res;
} visitor{ res };
Filesystem_.traverse(visitor);
return res;
}
void OpenShaderDesigner::FileManager::SaveAll()
{
}
OpenShaderDesigner::FileManager::Path OpenShaderDesigner::FileManager::GetHomeDirectory()
{
#ifdef WIN32
return Path(getenv("HOMEDRIVE")) / getenv("HOMEPATH");
#else
return getenv("HOME");
#endif
}

View File

@ -0,0 +1,3 @@
//
// Created by Maddie on 9/11/2024.
//

View File

@ -210,8 +210,8 @@ void Node::Draw(ImGuiID id)
ShaderGraph::ShaderGraph()
: EditorWindow("\uED46 Shader Graph", 0)
, GrabFocus(false)
, State(*this)
, GrabFocus_(false)
, State_(*this)
{
}
@ -223,7 +223,7 @@ void ShaderGraph::OnOpen()
{
EditorSystem::Open<Inspector>()->Graph = this;
GrabFocus = true;
GrabFocus_ = true;
}
@ -236,18 +236,18 @@ void ShaderGraph::DrawWindow()
ImNodeGraph::SetGraphValidation(ValidateConnection);
if(GrabFocus)
if(GrabFocus_)
{
GrabFocus = false;
GrabFocus_ = false;
ImGui::SetWindowFocus();
ImGui::SetNavWindow(ImGui::GetCurrentWindow());
}
for(ImGuiID id = 0; id < State.Nodes.size(); ++id)
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();
@ -257,14 +257,15 @@ void ShaderGraph::DrawWindow()
ImNodeGraph::BeginGraphPostOp("ShaderGraph");
if(ImGui::IsKeyPressed(ImGuiKey_Delete))
if(ImGui::IsKeyPressed(ImGuiKey_Delete)) Erase();
ImGuiIO& io = ImGui::GetIO();
if(!(io.KeyMods & ~ImGuiMod_Ctrl))
{
auto& selected = ImNodeGraph::GetSelected();
for(ImGuiID node : selected)
{
State.Nodes.erase(ImNodeGraph::GetUserID(node).Int);
}
selected.Clear();
if(ImGui::IsKeyPressed(ImGuiKey_C)) Copy();
if(ImGui::IsKeyPressed(ImGuiKey_P)) Paste(ImGui::GetMousePos());
if(ImGui::IsKeyPressed(ImGuiKey_X)) { Copy(); Erase(); }
}
ImNodeGraph::EndGraphPostOp();
@ -275,7 +276,7 @@ void ShaderGraph::DrawContextMenu()
{
if(ImGui::IsMouseClicked(ImGuiMouseButton_Right))
{
ContextMenuPosition = ImNodeGraph::ScreenToGrid(ImGui::GetMousePos());
ContextMenuPosition_ = ImNodeGraph::ScreenToGrid(ImGui::GetMousePos());
}
if(ImGui::BeginPopupContextWindow("graph_context"))
@ -286,7 +287,7 @@ void ShaderGraph::DrawContextMenu()
Copy();
Erase();
}
if(ImGui::MenuItem("Paste", "Ctrl+V", false, false)) Paste(ContextMenuPosition);
if(ImGui::MenuItem("Paste", "Ctrl+V", false, false)) Paste(ContextMenuPosition_);
ImGui::Separator();
@ -295,7 +296,7 @@ void ShaderGraph::DrawContextMenu()
ImGui::Separator();
// Create Nodes
ImVec2 position = ContextMenuPosition;
ImVec2 position = ContextMenuPosition_;
std::stack<ContextID> context; context.push(0);
@ -319,7 +320,7 @@ void ShaderGraph::DrawContextMenu()
{
if(ImGui::MenuItem(item.Name.c_str()))
{
Graph.State.Nodes.insert(item.Constructor(Graph, Location));
Graph.State_.Nodes.insert(item.Constructor(Graph, Location));
}
}
else
@ -360,18 +361,31 @@ void ShaderGraph::DrawContextMenu()
}
}
void ShaderGraph::Copy() {}
void ShaderGraph::Erase() {}
void ShaderGraph::Copy()
{
}
void ShaderGraph::Erase()
{
auto& selected = ImNodeGraph::GetSelected();
for(ImGuiID node : selected)
{
State_.Nodes.erase(ImNodeGraph::GetUserID(node).Int);
}
selected.Clear();
}
void ShaderGraph::Paste(ImVec2) {}
Node* ShaderGraph::FindNode(ImPinPtr ptr)
{
return State.Nodes[ImNodeGraph::GetUserID(ptr.Node).Int];
return State_.Nodes[ImNodeGraph::GetUserID(ptr.Node).Int];
}
Pin& ShaderGraph::FindPin(ImPinPtr ptr)
{
Node* node = State.Nodes[ImNodeGraph::GetUserID(ptr.Node).Int];
Node* node = State_.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;
@ -410,12 +424,12 @@ void ShaderGraph::Register(const std::filesystem::path& path, ConstructorPtr con
if(node == 0 || node != child)
{
node = ContextMenu.insert({ decomp.top(), nullptr }, node);
node = ContextMenu.insert({ decomp.top(), nullptr }, node, -1);
decomp.pop();
}
}
ContextMenu.insert({ name, constructor }, node);
ContextMenu.insert({ name, constructor }, node, -1);
}
Inspector::Inspector()

169
Source/Project/Project.cpp Normal file
View File

@ -0,0 +1,169 @@
//
// Created by Maddie on 9/14/2024.
//
#include <Core/Engine.h>
#include <Editor/EditorSystem.h>
#include <Project/Project.h>
#include <fstream>
#include <Core/Console.h>
#include <portable-file-dialogs/portable-file-dialogs.h>
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include <rapidjson/ostreamwrapper.h>
#include <rapidjson/prettywriter.h>
OpenShaderDesigner::Project::Project()
: ProjectFile_(NULL)
{
}
OpenShaderDesigner::Project::~Project()
{
}
void OpenShaderDesigner::Project::DrawMenuBar()
{
FileManager* filesystem = EditorSystem::Get<FileManager>();
if(ImGui::BeginMenu("File"))
{
if(ImGui::MenuItem("\uecc9 New..."))
{
Console::Log(Console::Message, "Creating Project");
bool cancelled = false;
if(filesystem->AnyDirty())
{
auto save = pfd::message("Unsaved Changes", "Would you like to save your changes?", pfd::choice::yes_no_cancel);
switch(save.result())
{
case pfd::button::yes:
Console::Log(Console::Message, "Saving Old Project");
filesystem->SaveAll();
break;
case pfd::button::cancel:
Console::Log(Console::Message, "Cancelled");
cancelled = true;
break;
default: case pfd::button::no:
break;
}
}
if(not cancelled)
{
filesystem->CloseDirectory(filesystem->Parent(ProjectFile_));
Console::Log(Console::Message, "Select Location");
auto res = pfd::save_file("New", FileManager::GetHomeDirectory().string(), { "Project File (.sgp)", "*.sgp" }).result();
if(not res.empty())
{
FileManager::Path path = res;
if(not path.has_extension()) path = res + ".sgp";
Console::Log(Console::Alert, "Creating Project {} in {}", path.filename().string(), path.parent_path().string());
create_directories(path.parent_path());
FileManager::FileID root = filesystem->LoadDirectory(path.parent_path());
filesystem->CurrentDirectory(root);
ProjectFile_ = filesystem->Create(path.filename().string());
}
}
}
if(ImGui::MenuItem("\ued6f Open..."))
{
Console::Log(Console::Message, "Opening Project");
bool cancelled = false;
if(filesystem->AnyDirty())
{
auto save = pfd::message("Unsaved Changes", "Would you like to save your changes?", pfd::choice::yes_no_cancel);
switch(save.result())
{
case pfd::button::yes:
Console::Log(Console::Message, "Saving Old Project");
filesystem->SaveAll();
break;
case pfd::button::cancel:
Console::Log(Console::Message, "Cancelled");
cancelled = true;
break;
default: case pfd::button::no:
break;
}
}
if(not cancelled)
{
Console::Log(Console::Message, "Post Dialogue");
auto project = pfd::open_file("Open", FileManager::GetHomeDirectory().string(), { "Project File (.sgp)", "*.sgp" });
if(not project.result().empty())
{
Console::Log(Console::Message, "Closing Old Project");
filesystem->CloseDirectory(filesystem->Parent(ProjectFile_));
Console::Log(Console::Message, "Closing 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");
ProjectFile_ = filesystem->Get(res);
}
}
}
ImGui::EndMenu();
}
}
void OpenShaderDesigner::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)
{
// Setup
rapidjson::Document document;
document.SetObject();
// Write to file
std::ofstream file(path);
rapidjson::OStreamWrapper out(file);
rapidjson::PrettyWriter writer(out);
document.Accept(writer);
file.close();
// Clear Dirty Flag
Asset::Save(path);
Console::Log(Console::Alert, "Saved Project {} to {}", path.filename().string(), path.parent_path().string());
}
void OpenShaderDesigner::Project::Create(const FileManager::Path &path)
{
Console::Log(Console::Message, "Setting Up New Project");
Reset();
Save(path);
}
void OpenShaderDesigner::Project::Reset()
{
}

View File

@ -8,32 +8,32 @@ Size=400,400
Collapsed=0
[Window][Dear ImGui Demo]
Pos=3070,364
Size=370,711
Pos=3070,104
Size=370,884
Collapsed=0
DockId=0x00000008,0
[Window][ Console]
Pos=303,1077
Size=3137,340
Pos=468,990
Size=2972,427
Collapsed=0
DockId=0x00000004,0
[Window][ Profiler]
Pos=0,1077
Size=301,340
Pos=0,990
Size=466,427
Collapsed=0
DockId=0x00000003,0
[Window][ Shader Graph]
Pos=0,0
Size=3068,1075
Pos=0,24
Size=3068,964
Collapsed=0
DockId=0x00000005,0
[Window][WindowOverViewport_11111111]
Pos=0,0
Size=3440,1417
Pos=0,24
Size=3440,1393
Collapsed=0
[Window][Example: Custom rendering]
@ -65,19 +65,29 @@ Size=487,534
Collapsed=0
[Window][Inspector]
Pos=3070,0
Size=370,362
Pos=3070,24
Size=370,78
Collapsed=0
DockId=0x00000007,0
[Window][File Manager]
Pos=468,990
Size=2972,427
Collapsed=0
DockId=0x00000004,1
[Window][File Manager/##hierarchy_CA8B7713]
IsChild=1
Size=289,363
[Docking][Data]
DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=3440,1417 Split=Y
DockNode ID=0x00000001 Parent=0x7C6B3D9B SizeRef=3440,378 Split=X
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,127 Selected=0xE7039252
DockNode ID=0x00000008 Parent=0x00000006 SizeRef=370,249 Selected=0xE87781F4
DockNode ID=0x00000002 Parent=0x7C6B3D9B SizeRef=3440,340 Split=X Selected=0xE9F1AFD1
DockNode ID=0x00000003 Parent=0x00000002 SizeRef=301,448 Selected=0xAC4B19AE
DockNode ID=0x00000004 Parent=0x00000002 SizeRef=3137,448 Selected=0xE9F1AFD1
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