commit 55ab6e28224e1f56f21f8b86b41c1d2ac42f4b1e Author: indigo Date: Tue Sep 26 14:56:30 2023 +0800 Init Repo diff --git a/FlipScope.sln b/FlipScope.sln new file mode 100644 index 0000000..973d21d --- /dev/null +++ b/FlipScope.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FlipScope", "FlipScope.vcxproj", "{B88A50A3-94F6-4CDC-B92D-940418AD1F7C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B88A50A3-94F6-4CDC-B92D-940418AD1F7C}.Debug|x64.ActiveCfg = Debug|x64 + {B88A50A3-94F6-4CDC-B92D-940418AD1F7C}.Debug|x64.Build.0 = Debug|x64 + {B88A50A3-94F6-4CDC-B92D-940418AD1F7C}.Debug|x86.ActiveCfg = Debug|Win32 + {B88A50A3-94F6-4CDC-B92D-940418AD1F7C}.Debug|x86.Build.0 = Debug|Win32 + {B88A50A3-94F6-4CDC-B92D-940418AD1F7C}.Release|x64.ActiveCfg = Release|x64 + {B88A50A3-94F6-4CDC-B92D-940418AD1F7C}.Release|x64.Build.0 = Release|x64 + {B88A50A3-94F6-4CDC-B92D-940418AD1F7C}.Release|x86.ActiveCfg = Release|Win32 + {B88A50A3-94F6-4CDC-B92D-940418AD1F7C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/FlipScope.vcxproj b/FlipScope.vcxproj new file mode 100644 index 0000000..37e88a2 --- /dev/null +++ b/FlipScope.vcxproj @@ -0,0 +1,216 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {B88A50A3-94F6-4CDC-B92D-940418AD1F7C} + FlipScope + 10.0.19041.0 + + + + Application + true + v143 + MultiByte + + + Application + false + v143 + true + MultiByte + + + Application + true + v143 + MultiByte + + + Application + false + v140 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + + + true + + + + + Level3 + Disabled + true + + + true + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level1 + Disabled + true + true + true + $(SolutionDir)src;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;NOMINMAX;%(PreprocessorDefinitions) + Use + pch.h + 4819 + true + + + true + true + true + + + copy $(TargetPath) $(SolutionDir)build\$(ProjectName) /Y/B + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FlipScope.vcxproj.filters b/FlipScope.vcxproj.filters new file mode 100644 index 0000000..b4a99df --- /dev/null +++ b/FlipScope.vcxproj.filters @@ -0,0 +1,243 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + \ No newline at end of file diff --git a/FlipScope.vcxproj.user b/FlipScope.vcxproj.user new file mode 100644 index 0000000..ddaa0dd --- /dev/null +++ b/FlipScope.vcxproj.user @@ -0,0 +1,13 @@ + + + + true + + + $(SolutionDir)Build\$(ProjectName)\$(TargetName)$(TargetExt) + WindowsLocalDebugger + $(SolutionDir)Build\$(ProjectName) + PATH=%PATH%;$(SolutionDir)libs\usd\build\bin;$(SolutionDir)libs\usd\build\lib +$(LocalDebuggerEnvironment) + + \ No newline at end of file diff --git a/FlipScopeProperty.props b/FlipScopeProperty.props new file mode 100644 index 0000000..1c4dc47 --- /dev/null +++ b/FlipScopeProperty.props @@ -0,0 +1,17 @@ + + + + + + + + $(SolutionDir)libs\glfw\build\include;$(SolutionDir)libs\glad\build\include;$(SolutionDir)libs\spdlog\build\include;$(SolutionDir)libs\imgui\build\include;$(SolutionDir)libs\stb_image\build\include;$(SolutionDir)libs\jsoncpp\build\include;$(SolutionDir)libs\glm;$(SolutionDir)libs\stb;$(SolutionDir)libs\usd\build\include;$(SolutionDir)libs\ext;%(AdditionalIncludeDirectories) + GLEW_STATIC;%(PreprocessorDefinitions) + + + $(SolutionDir)libs\glfw\build\lib;$(SolutionDir)libs\glad\build\lib;$(SolutionDir)libs\spdlog\build\lib;$(SolutionDir)libs\imgui\build\lib;$(SolutionDir)libs\jsoncpp\build\lib;$(SolutionDir)libs\stb_image\build\lib;$(SolutionDir)libs\usd\build\lib;%(AdditionalLibraryDirectories) + glfw3.lib;glad.lib;spdlog.lib;imgui.lib;jsoncpp.lib;stb_image.lib;opengl32.lib;%(AdditionalDependencies) + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..819fdf5 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# FlipScope +Usd Layer Editor for lighing. Basic concept is create layer override like maya render layer. Prototype for now + +## Requirements +* Imgui +* glad +* glew +* jsoncpp +* spdlog +* stb_image +* USD 20.02 +* Visual Studio 2022 + +![FlipScope](images/FlipScopeUI.png) + +## To Do +* Outliner Editor +* Property Editor +* Layer Editor \ No newline at end of file diff --git a/UsdPropertySheet.props b/UsdPropertySheet.props new file mode 100644 index 0000000..f8ea6b5 --- /dev/null +++ b/UsdPropertySheet.props @@ -0,0 +1,16 @@ + + + + + + + + $(SolutionDir)libs\usd\build\include;$(SolutionDir)libs\usd\build\include\boost-1_61;C:\Python27\include;%(AdditionalIncludeDirectories) + + + $(SolutionDir)libs\usd\build\lib;C:\Python27\libs;%(AdditionalLibraryDirectories) + hgi.lib;hgiGL.lib;hio.lib;usd.lib;usdAppUtils.lib;usdGeom.lib;usdHydra.lib;usdImaging.lib;usdImagingGL.lib;usdLux.lib;usdMedia.lib;usdRender.lib;usdRi.lib;usdRiImaging.lib;usdShade.lib;usdSkel.lib;usdSkelImaging.lib;usdUI.lib;usdUtils.lib;usdviewq.lib;usdVol.lib;usdVolImaging.lib;vt.lib;work.lib;ar.lib;arch.lib;boost_atomic-vc140-mt-1_61.lib;boost_chrono-vc140-mt-1_61.lib;boost_date_time-vc140-mt-1_61.lib;boost_filesystem-vc140-mt-1_61.lib;boost_program_options-vc140-mt-1_61.lib;boost_python-vc140-mt-1_61.lib;boost_regex-vc140-mt-1_61.lib;boost_system-vc140-mt-1_61.lib;boost_thread-vc140-mt-1_61.lib;cameraUtil.lib;garch.lib;gf.lib;glf.lib;Half.lib;hd.lib;hdSt.lib;hdx.lib;hf.lib;sdf.lib;tf.lib;js.lib;python27.lib;%(AdditionalDependencies) + + + + \ No newline at end of file diff --git a/images/FlipScopeUI.png b/images/FlipScopeUI.png new file mode 100644 index 0000000..36901d3 Binary files /dev/null and b/images/FlipScopeUI.png differ diff --git a/imgui.ini b/imgui.ini new file mode 100644 index 0000000..6b5a893 --- /dev/null +++ b/imgui.ini @@ -0,0 +1,46 @@ +[Window][DockSpace Demo] +Pos=0,0 +Size=1280,720 +Collapsed=0 + +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][Dear ImGui Demo] +ViewportPos=1168,64 +ViewportId=0xE927CF2F +Size=378,580 +Collapsed=0 + +[Window][Example: Console] +Pos=0,646 +Size=1140,292 +Collapsed=0 +DockId=0x00000005,0 + +[Window][Example: Property editor] +Pos=1142,601 +Size=378,337 +Collapsed=0 +DockId=0x00000002,0 + +[Window][Example: Simple layout] +Pos=0,19 +Size=1140,625 +Collapsed=0 +DockId=0x00000003,0 + +[Window][Example: Auto-resizing window] +Pos=515,424 +Size=450,267 +Collapsed=0 + +[Docking][Data] +DockSpace ID=0x3BC79352 Window=0x4647B76E Pos=26,64 Size=1280,701 Split=X + DockNode ID=0x00000001 Parent=0x3BC79352 SizeRef=1140,919 Split=Y + DockNode ID=0x00000003 Parent=0x00000001 SizeRef=1270,366 CentralNode=1 Selected=0x32FFF4A6 + DockNode ID=0x00000005 Parent=0x00000001 SizeRef=1270,292 Selected=0x46B5E99C + DockNode ID=0x00000002 Parent=0x3BC79352 SizeRef=378,919 Selected=0xE927CF2F + diff --git a/src/FlipScope/Core/AppController.cpp b/src/FlipScope/Core/AppController.cpp new file mode 100644 index 0000000..49ed731 --- /dev/null +++ b/src/FlipScope/Core/AppController.cpp @@ -0,0 +1,144 @@ +#include "pch.h" + +#include "FlipScope/Core/Log.h" +#include "FlipScope/Core/AppController.h" + +#include "FlipScope/Usd/StageDataModel.h" + +#include "pxr/pxr.h" +#include "pxr/usd/usd/stage.h" + +#include + +namespace FSV +{ + std::shared_ptr AppController::m_StageDataModel; + + bool AppController::m_StageChanged = false; + AppController::AppController() + { + m_StageDataModel = std::make_shared(); + NewStage(); + m_StageChanged = false; + } + + AppController::~AppController() + { + } + + void AppController::NewStage() + { + auto stageDataModel = GetStageDataModel(); + std::string tmpDir = getenv("TEMP"); + std::string stageFilePath = tmpDir + "/untitled.usd"; + m_StageDataModel->SetStage(UsdStage::CreateNew(stageFilePath)); + m_StageDataModel->SetStageFilePath(stageFilePath); + } + + void AppController::OpenStageDialog() + { + static std::vector filters = { + "USD Formats (*.usd, *.usdc, *.usda, *.usdz)", "*.usd; *.usda; *.usdc; *.usdz" + }; + + std::vector selection = pfd::open_file("Open USD Stage", "", filters, false).result(); + + if (!selection.size()) + { + FS_ERROR("OpenStageDialog() :: Nothing to open..."); + return; + } + + auto stageDataModel = GetStageDataModel(); + + if (stageDataModel->GetStageFilePath() == selection[0]) + { + FS_WARN("OpenStageDialog() :: Stage {} is aleady opened...", selection[0]); + return; + } + + stageDataModel->SetStage(UsdStage::Open(selection[0])); + stageDataModel->SetStageFilePath(selection[0]); + m_StageChanged = true; + FS_INFO("OpenStageDialog() :: Stage {} opened...", selection[0]); + } + + bool AppController::SaveStage() + { + auto stageDataModel = GetStageDataModel(); + std::string result = stageDataModel->GetStageFilePath(); + if (!stageDataModel->GetStage()->GetRootLayer()->Save()) + { + FS_ERROR("Stage not saved :: {}", result); + return false; + } + FS_INFO("SaveStage() : Stage saved :: {}", result); + return true; + } + + bool AppController::ExportStage(std::string filePath, bool composed = false) + { + auto stageDataModel = GetStageDataModel(); + if (composed) + { + if (!stageDataModel->GetStage()->Export(filePath)) + { + FS_ERROR("ExportStage() :: Stage (composed) not export"); + return false; + } + FS_INFO("ExportStage() :: Stage (composed) exported :: {}", filePath); + return true; + } + + if (!stageDataModel->GetStage()->GetRootLayer()->Export(filePath)) + { + FS_ERROR("ExportStage() :: Stage not exported :: {}", filePath); + return false; + } + FS_INFO("ExportStage() :: Stage exported :: {}", filePath); + return true; + } + + bool AppController::ExportDialog(bool composed = false) + { + static std::vector filters = { + "USDC (*.usdc)", "*.usdc", + "USDA (*.usda)", "*.usda", + "USDZ (*.usdz)", "*.usdz" + }; + std::string result = pfd::save_file("Export Stage as ...", "", filters, true).result(); + return ExportStage(result, composed); + } + + bool AppController::ExportComposedDialog() + { + return ExportDialog(true); + } + + bool AppController::CreatePrim(std::string path, std::string typeName) + { + auto stageDataModel = GetStageDataModel(); + UsdPrim prim = stageDataModel->GetStage()->DefinePrim(SdfPath(path), TfToken(typeName)); + if ( !prim || !prim.IsDefined()) + { + FS_WARN("Prim({}) : {} is not defined", typeName, path); + return false; + } + FS_INFO("Prim({}) : {} defined", typeName, path); + return true; + } + + bool AppController::CreateOverridePrim(std::string path) + { + auto stageDataModel = GetStageDataModel(); + UsdPrim prim = stageDataModel->GetStage()->OverridePrim(SdfPath(path)); + if ( !prim ) + { + FS_WARN("Override Prim : {} is not defined", path); + return false; + } + FS_INFO("Override Prim : {} defined", path); + return true; + } + +} \ No newline at end of file diff --git a/src/FlipScope/Core/AppController.h b/src/FlipScope/Core/AppController.h new file mode 100644 index 0000000..2d7e50e --- /dev/null +++ b/src/FlipScope/Core/AppController.h @@ -0,0 +1,41 @@ +#pragma once + +#include "pch.h" +#include "pxr/usd/usd/stage.h" +#include "pxr/usd/usd/stagePopulationMask.h" + +#include "FlipScope/Usd/StageDataModel.h" + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace FSV +{ + class AppController + { + public: + AppController(); + virtual ~AppController(); + + static void NewStage(); + static void OpenStageDialog(); + + static bool SaveStage(); + static bool ExportStage(std::string filePath, bool composed); + + bool IsStageChanged() { return m_StageChanged; } + void SetStageChanged(bool value) { m_StageChanged = value; } + + static bool ExportDialog(bool composed); + static bool ExportComposedDialog(); + + static bool CreatePrim(std::string path, std::string typeName); + static bool CreateOverridePrim(std::string path); + + static std::shared_ptr GetStageDataModel() { return m_StageDataModel; }; + + + private: + static std::shared_ptr m_StageDataModel; + static bool m_StageChanged; + }; +} diff --git a/src/FlipScope/Core/Application.cpp b/src/FlipScope/Core/Application.cpp new file mode 100644 index 0000000..88f4a2c --- /dev/null +++ b/src/FlipScope/Core/Application.cpp @@ -0,0 +1,59 @@ +#include "pch.h" + +#include "FlipScope/Core/Core.h" +#include "FlipScope/Core/Application.h" +#include "FlipScope/Core/Log.h" + +#include "FlipScope/Pipeline/ProjectInfo.h" +#include "FlipScope/Renderer/Renderer.h" +#include "FlipScope/Core/AppController.h" + +namespace FSV { + Application* Application::s_Instance = nullptr; + ProjectInfo* Application::s_ProjectInfo = nullptr; + AppController* Application::s_Controller = nullptr; + + Application::Application() + { + s_Instance = this; + s_ProjectInfo = new ProjectInfo(); + s_Controller = new AppController(); + m_Window = Window::Create(); + + Renderer::Init(); + + m_ImGuiLayer = new ImGuiLayer(); + m_ImGuiLayer->OnAttach(); + } + + Application::~Application() + { + Renderer::Shutdown(); + m_ImGuiLayer->OnDetach(); + } + + void Application::Run() { + FS_INFO("FlipScope Running..."); + while (!m_Window->Close()) + { + m_ImGuiLayer->OnUpdate(); + + glClearColor(0.45f, 0.55f, 0.6f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + + m_ImGuiLayer->Begin(); + m_ImGuiLayer->Render(); + m_ImGuiLayer->End(); + + m_Window->OnUpdate(); + + } + } + + Application * CreateApplication() { + Application *app = new Application(); + return app; + } +} \ No newline at end of file diff --git a/src/FlipScope/Core/Application.h b/src/FlipScope/Core/Application.h new file mode 100644 index 0000000..51dcef7 --- /dev/null +++ b/src/FlipScope/Core/Application.h @@ -0,0 +1,42 @@ +#pragma once + +#include "FlipScope/Core/Window.h" + +#include "FlipScope/Pipeline/ProjectInfo.h" +#include "FlipScope/ImGui/ImGuiLayer.h" +#include "FlipScope/Core/AppController.h" + +#include + +int main(int argc, char** argv); + +namespace FSV +{ + class Application + { + public: + Application(); + ~Application(); + + //void OnEvent() + Window& GetWindow(){ return *m_Window; } + static Application& Get() { return *s_Instance; } + static ProjectInfo& GetProjectInfo() { return *s_ProjectInfo; } + static AppController& GetController() { return *s_Controller; } + void Run(); + + private: + std::unique_ptr m_Window; + ImGuiLayer* m_ImGuiLayer; + bool m_Running = true; + bool m_Minimized = false; + + private: + static Application* s_Instance; + static ProjectInfo* s_ProjectInfo; + static AppController* s_Controller; + friend int ::main(int argc, char** argv); + }; + + Application* CreateApplication(); +} \ No newline at end of file diff --git a/src/FlipScope/Core/Core.h b/src/FlipScope/Core/Core.h new file mode 100644 index 0000000..dceafe8 --- /dev/null +++ b/src/FlipScope/Core/Core.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +#include +//#include +#include + +#define BIT(x) (1 << x) + +#define BIND_EVENT_FN(fn) std::bind(&fn, this, std::placeholders::_1) \ No newline at end of file diff --git a/src/FlipScope/Core/Log.cpp b/src/FlipScope/Core/Log.cpp new file mode 100644 index 0000000..8fd350a --- /dev/null +++ b/src/FlipScope/Core/Log.cpp @@ -0,0 +1,23 @@ +#include "pch.h" + +#include "FlipScope/Core/Log.h" + +#include +#include + +namespace FSV +{ + std::shared_ptr Log::s_Logger; + + void Log::Init() + { + std::vector logSinks; + logSinks.emplace_back(std::make_shared()); + logSinks[0]->set_pattern("%^[%T] %n: %v%$"); + s_Logger = std::make_shared("FlipScope", begin(logSinks), end(logSinks)); + + spdlog::register_logger(s_Logger); + s_Logger->set_level(spdlog::level::trace); + s_Logger->flush_on(spdlog::level::trace); + } +} \ No newline at end of file diff --git a/src/FlipScope/Core/Log.h b/src/FlipScope/Core/Log.h new file mode 100644 index 0000000..89ff8cc --- /dev/null +++ b/src/FlipScope/Core/Log.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +namespace FSV +{ + class Log + { + public: + static void Init(); + static std::shared_ptr GetLogger() { return s_Logger; } + + private: + static std::shared_ptr s_Logger; + }; + +} + +// Log macros +#define FS_TRACE(...) ::FSV::Log::GetLogger()->trace(__VA_ARGS__) +#define FS_INFO(...) ::FSV::Log::GetLogger()->info(__VA_ARGS__) +#define FS_WARN(...) ::FSV::Log::GetLogger()->warn(__VA_ARGS__) +#define FS_ERROR(...) ::FSV::Log::GetLogger()->error(__VA_ARGS__) +#define FS_CRITICAL(...) ::FSV::Log::GetLogger()->critical(__VA_ARGS__) + +#define FsTrace(...) ::FSV::Log::GetLogger()->trace(__VA_ARGS__) +#define fsInfo(...) ::FSV::Log::GetLogger()->info(__VA_ARGS__) +#define fsWarn(...) ::FSV::Log::GetLogger()->warn(__VA_ARGS__) +#define fsError(...) ::FSV::Log::GetLogger()->error(__VA_ARGS__) +#define fsCritical(...) ::FSV::Log::GetLogger()->critical(__VA_ARGS__) diff --git a/src/FlipScope/Core/Window.cpp b/src/FlipScope/Core/Window.cpp new file mode 100644 index 0000000..877f459 --- /dev/null +++ b/src/FlipScope/Core/Window.cpp @@ -0,0 +1,13 @@ +#include "pch.h" + +#include "Window.h" + +#include "Platform/Windows/WindowsWindow.h" + +namespace FSV +{ + std::unique_ptr Window::Create(const WindowProps& props) + { + return std::make_unique(props); + } +} \ No newline at end of file diff --git a/src/FlipScope/Core/Window.h b/src/FlipScope/Core/Window.h new file mode 100644 index 0000000..0cc7a25 --- /dev/null +++ b/src/FlipScope/Core/Window.h @@ -0,0 +1,45 @@ +#pragma once + +#include "FlipScope/Event/Event.h" + +#include +#include + +namespace FSV { + + struct WindowProps + { + std::string Title; + unsigned int Width; + unsigned int Height; + + WindowProps(const std::string& title="FlipScope Viewer", + unsigned int width=1280, + unsigned int height=720) + :Title(title), Width(width), Height(height) + { + } + }; + + class Window + { + public: + using EventCallbackFn = std::function; + + virtual ~Window() = default; + + virtual void OnUpdate() = 0; + + virtual unsigned int GetWidth() const = 0; + virtual unsigned int GetHeight() const = 0; + + virtual void SetVSync(bool enabled) = 0; + virtual bool IsVSync() const = 0; + virtual void SetEventCallback(const EventCallbackFn& callback) = 0; + + virtual bool Close() const = 0; + virtual void* GetNativeWindow() const = 0; + + static std::unique_ptr Create(const WindowProps& props = WindowProps()); + }; +} \ No newline at end of file diff --git a/src/FlipScope/Event/ApplicationEvent.h b/src/FlipScope/Event/ApplicationEvent.h new file mode 100644 index 0000000..91c9790 --- /dev/null +++ b/src/FlipScope/Event/ApplicationEvent.h @@ -0,0 +1,57 @@ +#pragma once + +#include "FlipScope/Event/Event.h" +#include +#include + +namespace FSV +{ + class WindowResizeEvent : public Event + { + public: + WindowResizeEvent(unsigned int width, unsigned int height) + : m_Width(width), m_Height(height) {} + + unsigned int GetWidth() const { return m_Width; } + unsigned int GetHeight() const { return m_Height; } + + static EventType GetStaticType() { return EventType::WindowResize; } + virtual EventType GetEventType() const override { return GetStaticType(); } + virtual const char* GetName() const override { return "WindowResize"; } + virtual int GetCategoryFlags() const override { return EventCategory::EventCategoryApplication; } + + private: + unsigned int m_Width, m_Height; + }; + + class WindowCloseEvent : public Event + { + public: + WindowCloseEvent() = default; + static EventType GetStaticType() { return EventType::WindowClose; } + virtual EventType GetEventType() const override { return GetStaticType(); } + virtual const char* GetName() const override { return "WindowClose"; } + virtual int GetCategoryFlags() const override { return EventCategory::EventCategoryApplication; } + }; + + class WindowFileDropEvent : public Event + { + public: + WindowFileDropEvent(unsigned int count, const char* paths[]) + : m_Count(count), m_Paths(paths) {} + + unsigned int GetCount() const { return m_Count; } + const char** GetPaths() const { return m_Paths; } + + static EventType GetStaticType() { return EventType::FileDroped; } + virtual EventType GetEventType() const override { return GetStaticType(); } + virtual const char* GetName() const override { return "FileDroped"; } + virtual int GetCategoryFlags() const override { return EventCategory::EventCategoryApplication; } + + private: + int m_Count; + const char** m_Paths; + }; + +} + diff --git a/src/FlipScope/Event/Event.h b/src/FlipScope/Event/Event.h new file mode 100644 index 0000000..2fb6e58 --- /dev/null +++ b/src/FlipScope/Event/Event.h @@ -0,0 +1,83 @@ +#pragma once + +#include "FlipScope/Core/Core.h" + +#include + +namespace FSV +{ + enum class EventType + { + None = 0, + WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMove, FileDroped, + KeyPressed, KeyReleased, KeyTyped, + MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled + }; + + enum EventCategory + { + None = 0, + EventCategoryApplication = BIT(0), + EventCategoryInput = BIT(1), + EventCategoryKeyboard = BIT(2), + EventCategoryMouse = BIT(3), + EventCategoryMouseButton = BIT(4) + }; + +/*#define EVENT_CLASS_TYPE(type) static EventType GetStaticType(){return EventType::type;}\ + virtual EventType GetEventType() const override { return GetStaticType(); }\ + virtual const char* GetName() const override { return #type; }*/ + + class Event + { + public: + bool Handled = false; + virtual EventType GetEventType() const = 0; + virtual const char* GetName() const = 0; + virtual int GetCategoryFlags() const = 0; + virtual std::string ToString() const { return GetName(); } + + bool IsInCategory(EventCategory category) + { + if (GetCategoryFlags() & category) + return true; + return false; + } + + template + inline std::string stringify(const T& x) { + std::ostringstream o; + o << x; + return o.str(); + } + + }; + + class EventDispatcher + { + public: + EventDispatcher(Event& event) + : m_Event(event) + { + } + + // F will be deduced by the compiler + template + bool Dispatch(const F& func) + { + if (m_Event.GetEventType() == T::GetStaticType()) + { + m_Event.Handled = func(static_cast(m_Event)); + return true; + } + return false; + } + private: + Event& m_Event; + }; + + inline std::ostream& operator<<(std::ostream& os, const Event& e) + { + return os << e.ToString(); + } +} diff --git a/src/FlipScope/ImGui/ImGuiBuild.cpp b/src/FlipScope/ImGui/ImGuiBuild.cpp new file mode 100644 index 0000000..2cca027 --- /dev/null +++ b/src/FlipScope/ImGui/ImGuiBuild.cpp @@ -0,0 +1,6 @@ +#include "pch.h" + +#define IMGUI_IMPL_OPENGL_LOADER_GLAD +//#define IMGUI_IMPL_OPENGL_LOADER_GLEW +#include "FlipScope/ImGui/imgui_impl_opengl3.cpp" +#include "FlipScope/ImGui/imgui_impl_glfw.cpp" \ No newline at end of file diff --git a/src/FlipScope/ImGui/ImGuiLayer.cpp b/src/FlipScope/ImGui/ImGuiLayer.cpp new file mode 100644 index 0000000..5009ab7 --- /dev/null +++ b/src/FlipScope/ImGui/ImGuiLayer.cpp @@ -0,0 +1,358 @@ +#include "pch.h" +#include "FlipScope/Core/Core.h" +#include "FlipScope/Core/AppController.h" +#include "FlipScope/ImGui/ImGuiLayer.h" +#include "FlipScope/Renderer/RenderCommand.h" + +#include +#include + +#include +#include + +#include + +#include "FlipScope/Core/Application.h" + + +namespace FSV +{ + static ImGuiDockNodeFlags opt_flags = ImGuiDockNodeFlags_PassthruCentralNode; + ImFont* ImGuiLayer::m_NormalFont; + ImFont* ImGuiLayer::m_SmallFont; + ImFont* ImGuiLayer::m_IconFont; + + //Project* ImGuiLayer::m_CurrentProject; + std::string ImGuiLayer::m_CurrentProjectName; + std::string ImGuiLayer::m_CurrentEpisodeName; + + static bool show_timeline = false; + static bool show_stageview = false; + static bool show_viewport = false; + static bool show_property = false; + static bool show_shotview = false; + + ImGuiLayer::ImGuiLayer() + { + } + + void ImGuiLayer::OnAttach() + { + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigDockingWithShift = true; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + ImFontConfig config; + config.FontDataOwnedByAtlas = false; + + m_NormalFont = io.Fonts->AddFontFromFileTTF("resources/fonts/Roboto-Regular.ttf", 16.0, &config); + config.MergeMode = true; + + static const ImWchar icon_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; + io.Fonts->AddFontFromFileTTF("resources/fonts/fa-solid-900.ttf", 14.0, &config, icon_ranges); + + m_SmallFont = io.Fonts->AddFontFromFileTTF("resources/fonts/Roboto-Regular.ttf", 14.0, &config); + + io.FontDefault = m_NormalFont; + + ImGui::StyleColorsDark(); + ImGuiStyle& style = ImGui::GetStyle(); + + Application& app = Application::Get(); + GLFWwindow* window = static_cast(app.GetWindow().GetNativeWindow()); + + ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplOpenGL3_Init("#version 410"); + + m_Texture = Texture::Create("C:/Test.png"); + + // Build framebuffer + //FramebufferSpecification fbspec; + //fbspec.Width = 1280; + //fbspec.Height = 720; + //m_Framebuffer = Framebuffer::Create(fbspec); + + // Initialize Renderer + //m_HydraRenderer = std::make_shared(1280, 720); + //m_HydraRenderer->Init(1280, 720); + + m_Viewport = std::make_shared(1280, 720); + m_StageView = std::make_shared(); + m_ShotView = std::make_shared(); + } + + void ImGuiLayer::OnUpdate() + { + Application& app = Application::Get(); + AppController& controller = app.GetController(); + + if (controller.IsStageChanged()) + { + m_Viewport->Update(); + controller.SetStageChanged(false); + } + //m_Framebuffer->Bind(); + // Renderer Draw Code + //RenderCommand::SetClearColor(glm::vec4(1.0f, 0.1f, 0.1f, 1)); + //RenderCommand::Clear(); + + //m_HydraRenderer->OnDraw(); + m_Viewport->GetRenderer()->OnRender(); + //m_HydraRenderer->OnRender(); + + //m_Framebuffer->Unbind(); + } + + void ImGuiLayer::OnDetach() + { + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + } + + void ImGuiLayer::Begin() + { + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + } + + /*void ImGuiLayer::BeginDockSpace() + { + static bool opt_fullscreen_persistant = true; + ImGuiViewport *viewport = ImGui::GetMainViewport(); + + if (ImGui::GetIO().DisplaySize.y > 0) + { + + bool opt_fullscreen = opt_fullscreen_persistant; + + // We are using the ImGuiWindowFlags_NoDocking flag to make the parent + // window not dockable into, because it would be confusing to have two + // docking targets within each others. + ImGuiWindowFlags window_flags = + ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; + + if (opt_fullscreen) { + auto dockSpaceSize = viewport->Size; + dockSpaceSize.y -= m_StatusBarSize; // remove the status bar + ImGui::SetNextWindowPos(viewport->Pos); + ImGui::SetNextWindowSize(dockSpaceSize); + ImGui::SetNextWindowViewport(viewport->ID); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + + window_flags |= ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove; + window_flags |= + ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + } + + // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render + // our background and handle the pass-thru hole, so we ask Begin() to not + // render a background. + if (opt_flags & ImGuiDockNodeFlags_PassthruCentralNode) + window_flags |= ImGuiWindowFlags_NoBackground; + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + static bool alwaysOpened = true; + ImGui::Begin("DockSpace", &alwaysOpened, window_flags); + ImGui::PopStyleVar(); + + if (opt_fullscreen) ImGui::PopStyleVar(2); + + // Dockspace + ImGuiIO &io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { + ImGuiID dockspace_id = ImGui::GetID("FsDockspace"); + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), opt_flags); + } + else { + // TODO: emit a log message + } + + + } + ImGui::End(); + }*/ + void ImGuiLayer::BeginDockSpace() + { + static bool alwaysOpened = true; + static ImGuiDockNodeFlags dockFlags = ImGuiDockNodeFlags_PassthruCentralNode; + static ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; + windowFlags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + windowFlags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + windowFlags |= ImGuiWindowFlags_NoBackground; + ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(viewport->Pos); + ImGui::SetNextWindowSize(viewport->Size); + ImGui::SetNextWindowViewport(viewport->ID); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::Begin("DockSpace", &alwaysOpened, windowFlags); + ImGui::PopStyleVar(3); + + ImGuiID dockspaceid = ImGui::GetID("dockspace"); + ImGui::DockSpace(dockspaceid, ImVec2(0.0f, 0.0f), dockFlags); + + EndDockSpace(); + } + + void ImGuiLayer::EndDockSpace() + { + ImGui::End(); + } + + float ImGuiLayer::DrawMenuBar() + { + auto menu_height = 0.0f; + if (ImGui::BeginMainMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + ImGui::MenuItem("New", "Ctrl+N", nullptr); + if (ImGui::MenuItem("Open", "Ctrl+O", nullptr)) + { + AppController::OpenStageDialog(); + } + ImGui::MenuItem("Close", "Ctrl+W", nullptr); + ImGui::Separator(); + if (ImGui::MenuItem("Save", "Ctrl+S", nullptr)) + { + AppController::SaveStage(); + } + ImGui::Separator(); + ImGui::MenuItem("Export", "Ctrl+E", nullptr); + ImGui::Separator(); + if (ImGui::MenuItem("Exit", "Ctrl+Q", nullptr)) + { + Application& app = Application::Get(); + app.GetWindow().Close(); + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("View")) + { + ImGui::MenuItem("Shots", "Alt+P", &show_shotview); + ImGui::MenuItem("Timeline", "Alt+T", &show_timeline); + ImGui::MenuItem("Stage", "Alt+O", &show_stageview); + ImGui::MenuItem("Viewport", "Alt+V", &show_viewport); + ImGui::MenuItem("Property", "Alt+P", &show_property); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Help")) + { + ImGui::MenuItem("About", "", nullptr); + ImGui::EndMenu(); + } + menu_height = ImGui::GetWindowSize().y; + ImGui::EndMainMenuBar(); + } + + return menu_height; + } + + void ImGuiLayer::DrawStatusBar() + { + ImGuiContext& g = *ImGui::GetCurrentContext(); + ImGuiViewport *viewport = ImGui::GetMainViewport(); + + // Draw status bar(no docking) + ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, m_StatusBarSize)); + ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + viewport->Size.y - m_StatusBarSize)); + //ImGui::SetNextWindowPos(ImVec2(0.0f, g.IO.DisplaySize.y - mFooterHeight)); + //ImGui::SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, mFooterHeight)); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0.0f, 20.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(2.0f, 2.0f)); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking; + + if (!ImGui::Begin("##statusbar", nullptr, window_flags)) + { + ImGui::End(); + return; + } + + ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + // Draw the common stuff + ImGui::SameLine(viewport->Size.x - 100.f); + ImFont *font = ImGui::GetFont(); + font->Scale = 0.875; + ImGui::PushFont(font); + ImGuiIO& io = ImGui::GetIO(); + ImGui::Text("FPS: (%.1f FPS)", io.Framerate); + font->Scale = 1.0; + ImGui::PopFont(); + + ImGui::End(); + } + + void ImGuiLayer::Render() + { + static bool show = true; + auto menu_height = DrawMenuBar(); + BeginDockSpace(); + DrawStatusBar(); + ImGui::ShowDemoWindow(&show); + if (show_timeline) ShowTimeline(&show_timeline); + if (show_property) ShowProperty(&show_property); + if (show_shotview) m_ShotView->Show(&show_shotview); + if (show_viewport) m_Viewport->Show(&show_viewport); + if (show_stageview) m_StageView->Show(&show_stageview); + } + + void ImGuiLayer::ShowTimeline(bool* p_open) + { + ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + if (!ImGui::Begin("Timeline", p_open)) + { + ImGui::End(); + return; + } + ImGui::PopStyleVar(); + ImGui::End(); + } + + void ImGuiLayer::ShowProperty(bool* p_open) + { + ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + if (!ImGui::Begin("Property", p_open)) + { + ImGui::End(); + return; + } + ImGui::PopStyleVar(); + ImGui::End(); + } + + + void ImGuiLayer::End() + { + ImGuiIO& io = ImGui::GetIO(); + Application& app = Application::Get(); + io.DisplaySize = ImVec2((float)app.GetWindow().GetWidth(), (float)app.GetWindow().GetHeight()); + + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + GLFWwindow* backup_current_context = glfwGetCurrentContext(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + glfwMakeContextCurrent(backup_current_context); + } + } +} \ No newline at end of file diff --git a/src/FlipScope/ImGui/ImGuiLayer.h b/src/FlipScope/ImGui/ImGuiLayer.h new file mode 100644 index 0000000..8811be2 --- /dev/null +++ b/src/FlipScope/ImGui/ImGuiLayer.h @@ -0,0 +1,69 @@ +#pragma once + +#include "FlipScope/Pipeline/Project.h" + +#include "FlipScope/Renderer/FrameBuffer.h" +#include "FlipScope/Renderer/Texture.h" + +//#include "FlipScope/Usd/HydraRenderer.h" +#include "FlipScope/Widgets/Viewport.h" +#include "FlipScope/Widgets/StageView.h" +#include "FlipScope/Widgets/ShotView.h" + +#include + +namespace FSV +{ + class ImGuiLayer + { + public: + ImGuiLayer(); + ~ImGuiLayer() = default; + void OnAttach(); + void OnDetach(); + + void OnUpdate(); + + void Render(); + void Begin(); + void End(); + public: + void BeginDockSpace(); + void EndDockSpace(); + float DrawMenuBar(); + void DrawStatusBar(); + void ShowViewport(bool *p_open); + void ShowShotList(bool *p_open); + void ShowTimeline(bool *p_open); + void ShowProperty(bool *p_open); + + ImFont* GetNormalFont() { return m_NormalFont; } + ImFont* GetSmallFont() { return m_SmallFont; } + ImFont* GetIconFont() { return m_IconFont; } + + private: + float m_Time = 0.0f; + float m_StatusBarSize = 20.0f; + + glm::vec2 m_ViewportSize = { 0.0f, 0.0f }; + + std::shared_ptr m_Texture; + std::shared_ptr m_Framebuffer; + + //std::shared_ptr m_HydraRenderer; + + static ImFont* m_NormalFont; + static ImFont* m_SmallFont; + static ImFont* m_IconFont; + + static std::string m_CurrentProjectName; + static std::string m_CurrentEpisodeName; + + static bool m_IsViewportFocused; + static bool m_IsViewportHovered; + + std::shared_ptr m_Viewport; + std::shared_ptr m_StageView; + std::shared_ptr m_ShotView; + }; +} diff --git a/src/FlipScope/ImGui/imgui_impl_glfw.cpp b/src/FlipScope/ImGui/imgui_impl_glfw.cpp new file mode 100644 index 0000000..2889373 --- /dev/null +++ b/src/FlipScope/ImGui/imgui_impl_glfw.cpp @@ -0,0 +1,1179 @@ +// dear imgui: Platform Backend for GLFW +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) +// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// (Requires: GLFW 3.1+. Prefer GLFW 3.3+ for full feature support.) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] +// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). +// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. + +// Issues: +// [ ] Platform: Multi-viewport support: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position. +// 2022-04-30: Inputs: Fixed ImGui_ImplGlfw_TranslateUntranslatedKey() for lower case letters on OSX. +// 2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11. +// 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend. +// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. +// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. +// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). +// 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates. +// 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback(). +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. +// 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). +// 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors. +// 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor). +// 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown. +// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter. +// 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter(). +// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized. +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. +// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. +// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. +// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. +// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. +// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. +// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()). +// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. +// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. +// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. +// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). +// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. +// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. +// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). +// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. + +#include "imgui.h" +#include "imgui_impl_glfw.h" + +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif +#endif + +// GLFW +#include + +#ifdef _WIN32 +#undef APIENTRY +#define GLFW_EXPOSE_NATIVE_WIN32 +#include // for glfwGetWin32Window() +#endif +#ifdef __APPLE__ +#define GLFW_EXPOSE_NATIVE_COCOA +#include // for glfwGetCocoaWindow() +#endif + +#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING +#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED +#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity +#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale +#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface +#define GLFW_HAS_FOCUS_WINDOW (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwFocusWindow +#define GLFW_HAS_FOCUS_ON_SHOW (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_FOCUS_ON_SHOW +#define GLFW_HAS_MONITOR_WORK_AREA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorWorkarea +#define GLFW_HAS_OSX_WINDOW_POS_FIX (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION * 10 >= 3310) // 3.3.1+ Fixed: Resizing window repositions it on MacOS #1553 +#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? +#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR +#else +#define GLFW_HAS_NEW_CURSORS (0) +#endif +#ifdef GLFW_MOUSE_PASSTHROUGH // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2020-07-17 (passthrough) +#define GLFW_HAS_MOUSE_PASSTHROUGH (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_MOUSE_PASSTHROUGH +#else +#define GLFW_HAS_MOUSE_PASSTHROUGH (0) +#endif +#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetGamepadState() new api +#define GLFW_HAS_GET_KEY_NAME (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwGetKeyName() + +// GLFW data +enum GlfwClientApi +{ + GlfwClientApi_Unknown, + GlfwClientApi_OpenGL, + GlfwClientApi_Vulkan +}; + +struct ImGui_ImplGlfw_Data +{ + GLFWwindow* Window; + GlfwClientApi ClientApi; + double Time; + GLFWwindow* MouseWindow; + GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; + ImVec2 LastValidMousePos; + GLFWwindow* KeyOwnerWindows[GLFW_KEY_LAST]; + bool InstalledCallbacks; + bool WantUpdateMonitors; +#ifdef _WIN32 + WNDPROC GlfwWndProc; +#endif + + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + GLFWwindowfocusfun PrevUserCallbackWindowFocus; + GLFWcursorposfun PrevUserCallbackCursorPos; + GLFWcursorenterfun PrevUserCallbackCursorEnter; + GLFWmousebuttonfun PrevUserCallbackMousebutton; + GLFWscrollfun PrevUserCallbackScroll; + GLFWkeyfun PrevUserCallbackKey; + GLFWcharfun PrevUserCallbackChar; + GLFWmonitorfun PrevUserCallbackMonitor; + + ImGui_ImplGlfw_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +// FIXME: multi-context support is not well tested and probably dysfunctional in this backend. +// - Because glfwPollEvents() process all windows and some events may be called outside of it, you will need to register your own callbacks +// (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks. +// - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it. +// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. +static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : NULL; +} + +// Forward Declarations +static void ImGui_ImplGlfw_UpdateMonitors(); +static void ImGui_ImplGlfw_InitPlatformInterface(); +static void ImGui_ImplGlfw_ShutdownPlatformInterface(); + +// Functions +static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) +{ + return glfwGetClipboardString((GLFWwindow*)user_data); +} + +static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) +{ + glfwSetClipboardString((GLFWwindow*)user_data, text); +} + +static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) +{ + switch (key) + { + case GLFW_KEY_TAB: return ImGuiKey_Tab; + case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow; + case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow; + case GLFW_KEY_UP: return ImGuiKey_UpArrow; + case GLFW_KEY_DOWN: return ImGuiKey_DownArrow; + case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp; + case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown; + case GLFW_KEY_HOME: return ImGuiKey_Home; + case GLFW_KEY_END: return ImGuiKey_End; + case GLFW_KEY_INSERT: return ImGuiKey_Insert; + case GLFW_KEY_DELETE: return ImGuiKey_Delete; + case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace; + case GLFW_KEY_SPACE: return ImGuiKey_Space; + case GLFW_KEY_ENTER: return ImGuiKey_Enter; + case GLFW_KEY_ESCAPE: return ImGuiKey_Escape; + case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe; + case GLFW_KEY_COMMA: return ImGuiKey_Comma; + case GLFW_KEY_MINUS: return ImGuiKey_Minus; + case GLFW_KEY_PERIOD: return ImGuiKey_Period; + case GLFW_KEY_SLASH: return ImGuiKey_Slash; + case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon; + case GLFW_KEY_EQUAL: return ImGuiKey_Equal; + case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash; + case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket; + case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent; + case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock; + case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock; + case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen; + case GLFW_KEY_PAUSE: return ImGuiKey_Pause; + case GLFW_KEY_KP_0: return ImGuiKey_Keypad0; + case GLFW_KEY_KP_1: return ImGuiKey_Keypad1; + case GLFW_KEY_KP_2: return ImGuiKey_Keypad2; + case GLFW_KEY_KP_3: return ImGuiKey_Keypad3; + case GLFW_KEY_KP_4: return ImGuiKey_Keypad4; + case GLFW_KEY_KP_5: return ImGuiKey_Keypad5; + case GLFW_KEY_KP_6: return ImGuiKey_Keypad6; + case GLFW_KEY_KP_7: return ImGuiKey_Keypad7; + case GLFW_KEY_KP_8: return ImGuiKey_Keypad8; + case GLFW_KEY_KP_9: return ImGuiKey_Keypad9; + case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal; + case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; + case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd; + case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter; + case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual; + case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift; + case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl; + case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt; + case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper; + case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift; + case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl; + case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt; + case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper; + case GLFW_KEY_MENU: return ImGuiKey_Menu; + case GLFW_KEY_0: return ImGuiKey_0; + case GLFW_KEY_1: return ImGuiKey_1; + case GLFW_KEY_2: return ImGuiKey_2; + case GLFW_KEY_3: return ImGuiKey_3; + case GLFW_KEY_4: return ImGuiKey_4; + case GLFW_KEY_5: return ImGuiKey_5; + case GLFW_KEY_6: return ImGuiKey_6; + case GLFW_KEY_7: return ImGuiKey_7; + case GLFW_KEY_8: return ImGuiKey_8; + case GLFW_KEY_9: return ImGuiKey_9; + case GLFW_KEY_A: return ImGuiKey_A; + case GLFW_KEY_B: return ImGuiKey_B; + case GLFW_KEY_C: return ImGuiKey_C; + case GLFW_KEY_D: return ImGuiKey_D; + case GLFW_KEY_E: return ImGuiKey_E; + case GLFW_KEY_F: return ImGuiKey_F; + case GLFW_KEY_G: return ImGuiKey_G; + case GLFW_KEY_H: return ImGuiKey_H; + case GLFW_KEY_I: return ImGuiKey_I; + case GLFW_KEY_J: return ImGuiKey_J; + case GLFW_KEY_K: return ImGuiKey_K; + case GLFW_KEY_L: return ImGuiKey_L; + case GLFW_KEY_M: return ImGuiKey_M; + case GLFW_KEY_N: return ImGuiKey_N; + case GLFW_KEY_O: return ImGuiKey_O; + case GLFW_KEY_P: return ImGuiKey_P; + case GLFW_KEY_Q: return ImGuiKey_Q; + case GLFW_KEY_R: return ImGuiKey_R; + case GLFW_KEY_S: return ImGuiKey_S; + case GLFW_KEY_T: return ImGuiKey_T; + case GLFW_KEY_U: return ImGuiKey_U; + case GLFW_KEY_V: return ImGuiKey_V; + case GLFW_KEY_W: return ImGuiKey_W; + case GLFW_KEY_X: return ImGuiKey_X; + case GLFW_KEY_Y: return ImGuiKey_Y; + case GLFW_KEY_Z: return ImGuiKey_Z; + case GLFW_KEY_F1: return ImGuiKey_F1; + case GLFW_KEY_F2: return ImGuiKey_F2; + case GLFW_KEY_F3: return ImGuiKey_F3; + case GLFW_KEY_F4: return ImGuiKey_F4; + case GLFW_KEY_F5: return ImGuiKey_F5; + case GLFW_KEY_F6: return ImGuiKey_F6; + case GLFW_KEY_F7: return ImGuiKey_F7; + case GLFW_KEY_F8: return ImGuiKey_F8; + case GLFW_KEY_F9: return ImGuiKey_F9; + case GLFW_KEY_F10: return ImGuiKey_F10; + case GLFW_KEY_F11: return ImGuiKey_F11; + case GLFW_KEY_F12: return ImGuiKey_F12; + default: return ImGuiKey_None; + } +} + +static int ImGui_ImplGlfw_KeyToModifier(int key) +{ + if (key == GLFW_KEY_LEFT_CONTROL || key == GLFW_KEY_RIGHT_CONTROL) + return GLFW_MOD_CONTROL; + if (key == GLFW_KEY_LEFT_SHIFT || key == GLFW_KEY_RIGHT_SHIFT) + return GLFW_MOD_SHIFT; + if (key == GLFW_KEY_LEFT_ALT || key == GLFW_KEY_RIGHT_ALT) + return GLFW_MOD_ALT; + if (key == GLFW_KEY_LEFT_SUPER || key == GLFW_KEY_RIGHT_SUPER) + return GLFW_MOD_SUPER; + return 0; +} + +static void ImGui_ImplGlfw_UpdateKeyModifiers(int mods) +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddKeyEvent(ImGuiKey_ModCtrl, (mods & GLFW_MOD_CONTROL) != 0); + io.AddKeyEvent(ImGuiKey_ModShift, (mods & GLFW_MOD_SHIFT) != 0); + io.AddKeyEvent(ImGuiKey_ModAlt, (mods & GLFW_MOD_ALT) != 0); + io.AddKeyEvent(ImGuiKey_ModSuper, (mods & GLFW_MOD_SUPER) != 0); +} + +void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackMousebutton != NULL && window == bd->Window) + bd->PrevUserCallbackMousebutton(window, button, action, mods); + + ImGui_ImplGlfw_UpdateKeyModifiers(mods); + + ImGuiIO& io = ImGui::GetIO(); + if (button >= 0 && button < ImGuiMouseButton_COUNT) + io.AddMouseButtonEvent(button, action == GLFW_PRESS); +} + +void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackScroll != NULL && window == bd->Window) + bd->PrevUserCallbackScroll(window, xoffset, yoffset); + + ImGuiIO& io = ImGui::GetIO(); + io.AddMouseWheelEvent((float)xoffset, (float)yoffset); +} + +static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) +{ +#if GLFW_HAS_GET_KEY_NAME && !defined(__EMSCRIPTEN__) + // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. + // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) + // See https://github.com/glfw/glfw/issues/1502 for details. + // Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process). + // This won't cover edge cases but this is at least going to cover common cases. + if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL) + return key; + const char* key_name = glfwGetKeyName(key, scancode); + if (key_name && key_name[0] != 0 && key_name[1] == 0) + { + const char char_names[] = "`-=[]\\,;\'./"; + const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 }; + IM_ASSERT(IM_ARRAYSIZE(char_names) == IM_ARRAYSIZE(char_keys)); + if (key_name[0] >= '0' && key_name[0] <= '9') { key = GLFW_KEY_0 + (key_name[0] - '0'); } + else if (key_name[0] >= 'A' && key_name[0] <= 'Z') { key = GLFW_KEY_A + (key_name[0] - 'A'); } + else if (key_name[0] >= 'a' && key_name[0] <= 'z') { key = GLFW_KEY_A + (key_name[0] - 'a'); } + else if (const char* p = strchr(char_names, key_name[0])) { key = char_keys[p - char_names]; } + } + // if (action == GLFW_PRESS) printf("key %d scancode %d name '%s'\n", key, scancode, key_name); +#else + IM_UNUSED(scancode); +#endif + return key; +} + +void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackKey != NULL && window == bd->Window) + bd->PrevUserCallbackKey(window, keycode, scancode, action, mods); + + if (action != GLFW_PRESS && action != GLFW_RELEASE) + return; + + // Workaround: X11 does not include current pressed/released modifier key in 'mods' flags. https://github.com/glfw/glfw/issues/1630 + if (int keycode_to_mod = ImGui_ImplGlfw_KeyToModifier(keycode)) + mods = (action == GLFW_PRESS) ? (mods | keycode_to_mod) : (mods & ~keycode_to_mod); + ImGui_ImplGlfw_UpdateKeyModifiers(mods); + + if (keycode >= 0 && keycode < IM_ARRAYSIZE(bd->KeyOwnerWindows)) + bd->KeyOwnerWindows[keycode] = (action == GLFW_PRESS) ? window : NULL; + + keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); + + ImGuiIO& io = ImGui::GetIO(); + ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode); + io.AddKeyEvent(imgui_key, (action == GLFW_PRESS)); + io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code) +} + +void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackWindowFocus != NULL && window == bd->Window) + bd->PrevUserCallbackWindowFocus(window, focused); + + ImGuiIO& io = ImGui::GetIO(); + io.AddFocusEvent(focused != 0); +} + +void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackCursorPos != NULL && window == bd->Window) + bd->PrevUserCallbackCursorPos(window, x, y); + if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + return; + + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + int window_x, window_y; + glfwGetWindowPos(window, &window_x, &window_y); + x += window_x; + y += window_y; + } + io.AddMousePosEvent((float)x, (float)y); + bd->LastValidMousePos = ImVec2((float)x, (float)y); +} + +// Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position, +// so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984) +void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackCursorEnter != NULL && window == bd->Window) + bd->PrevUserCallbackCursorEnter(window, entered); + if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + return; + + ImGuiIO& io = ImGui::GetIO(); + if (entered) + { + bd->MouseWindow = window; + io.AddMousePosEvent(bd->LastValidMousePos.x, bd->LastValidMousePos.y); + } + else if (!entered && bd->MouseWindow == window) + { + bd->LastValidMousePos = io.MousePos; + bd->MouseWindow = NULL; + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + } +} + +void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackChar != NULL && window == bd->Window) + bd->PrevUserCallbackChar(window, c); + + ImGuiIO& io = ImGui::GetIO(); + io.AddInputCharacter(c); +} + +void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + bd->WantUpdateMonitors = true; +} + +void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!"); + IM_ASSERT(bd->Window == window); + + bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback); + bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback); + bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback); + bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); + bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); + bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); + bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); + bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); + bd->InstalledCallbacks = true; +} + +void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!"); + IM_ASSERT(bd->Window == window); + + glfwSetWindowFocusCallback(window, bd->PrevUserCallbackWindowFocus); + glfwSetCursorEnterCallback(window, bd->PrevUserCallbackCursorEnter); + glfwSetCursorPosCallback(window, bd->PrevUserCallbackCursorPos); + glfwSetMouseButtonCallback(window, bd->PrevUserCallbackMousebutton); + glfwSetScrollCallback(window, bd->PrevUserCallbackScroll); + glfwSetKeyCallback(window, bd->PrevUserCallbackKey); + glfwSetCharCallback(window, bd->PrevUserCallbackChar); + glfwSetMonitorCallback(bd->PrevUserCallbackMonitor); + bd->InstalledCallbacks = false; + bd->PrevUserCallbackWindowFocus = NULL; + bd->PrevUserCallbackCursorEnter = NULL; + bd->PrevUserCallbackCursorPos = NULL; + bd->PrevUserCallbackMousebutton = NULL; + bd->PrevUserCallbackScroll = NULL; + bd->PrevUserCallbackKey = NULL; + bd->PrevUserCallbackChar = NULL; + bd->PrevUserCallbackMonitor = NULL; +} + +static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); + + // Setup backend capabilities flags + ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_glfw"; + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) +#if GLFW_HAS_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) +#endif + + bd->Window = window; + bd->Time = 0.0; + bd->WantUpdateMonitors = true; + + io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; + io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; + io.ClipboardUserData = bd->Window; + + // Create mouse cursors + // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, + // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. + // Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.) + GLFWerrorfun prev_error_callback = glfwSetErrorCallback(NULL); + bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); +#if GLFW_HAS_NEW_CURSORS + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); +#else + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); +#endif + glfwSetErrorCallback(prev_error_callback); + + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + if (install_callbacks) + ImGui_ImplGlfw_InstallCallbacks(window); + + // Update monitors the first time (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784) + ImGui_ImplGlfw_UpdateMonitors(); + glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); + + // Our mouse update function expect PlatformHandle to be filled for the main viewport + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->PlatformHandle = (void*)bd->Window; +#ifdef _WIN32 + main_viewport->PlatformHandleRaw = glfwGetWin32Window(bd->Window); +#elif defined(__APPLE__) + main_viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(bd->Window); +#endif + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + ImGui_ImplGlfw_InitPlatformInterface(); + + bd->ClientApi = client_api; + return true; +} + +bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL); +} + +bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); +} + +bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown); +} + +void ImGui_ImplGlfw_Shutdown() +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + ImGui_ImplGlfw_ShutdownPlatformInterface(); + + if (bd->InstalledCallbacks) + ImGui_ImplGlfw_RestoreCallbacks(bd->Window); + + for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) + glfwDestroyCursor(bd->MouseCursors[cursor_n]); + + io.BackendPlatformName = NULL; + io.BackendPlatformUserData = NULL; + IM_DELETE(bd); +} + +static void ImGui_ImplGlfw_UpdateMouseData() +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + + if (glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + { + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + return; + } + + ImGuiID mouse_viewport_id = 0; + const ImVec2 mouse_pos_prev = io.MousePos; + for (int n = 0; n < platform_io.Viewports.Size; n++) + { + ImGuiViewport* viewport = platform_io.Viewports[n]; + GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle; + +#ifdef __EMSCRIPTEN__ + const bool is_window_focused = true; +#else + const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; +#endif + if (is_window_focused) + { + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + // When multi-viewports are enabled, all Dear ImGui positions are same as OS positions. + if (io.WantSetMousePos) + glfwSetCursorPos(window, (double)(mouse_pos_prev.x - viewport->Pos.x), (double)(mouse_pos_prev.y - viewport->Pos.y)); + + // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured) + if (bd->MouseWindow == NULL) + { + double mouse_x, mouse_y; + glfwGetCursorPos(window, &mouse_x, &mouse_y); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) + // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) + int window_x, window_y; + glfwGetWindowPos(window, &window_x, &window_y); + mouse_x += window_x; + mouse_y += window_y; + } + bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y); + io.AddMousePosEvent((float)mouse_x, (float)mouse_y); + } + } + + // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. + // - [X] GLFW >= 3.3 backend ON WINDOWS ONLY does correctly ignore viewports with the _NoInputs flag. + // - [!] GLFW <= 3.2 backend CANNOT correctly ignore viewports with the _NoInputs flag, and CANNOT reported Hovered Viewport because of mouse capture. + // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window + // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported + // by the backend, and use its flawed heuristic to guess the viewport behind. + // - [X] GLFW backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). + // FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems. + // See https://github.com/glfw/glfw/issues/1236 if you want to help in making this a GLFW feature. +#if GLFW_HAS_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)) + const bool window_no_input = (viewport->Flags & ImGuiViewportFlags_NoInputs) != 0; +#if GLFW_HAS_MOUSE_PASSTHROUGH + glfwSetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH, window_no_input); +#endif + if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !window_no_input) + mouse_viewport_id = viewport->ID; +#else + // We cannot use bd->MouseWindow maintained from CursorEnter/Leave callbacks, because it is locked to the window capturing mouse. +#endif + } + + if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) + io.AddMouseViewportEvent(mouse_viewport_id); +} + +static void ImGui_ImplGlfw_UpdateMouseCursor() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + return; + + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int n = 0; n < platform_io.Viewports.Size; n++) + { + GLFWwindow* window = (GLFWwindow*)platform_io.Viewports[n]->PlatformHandle; + if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + } + else + { + // Show OS mouse cursor + // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. + glfwSetCursor(window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } + } +} + +// Update gamepad inputs +static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; } +static void ImGui_ImplGlfw_UpdateGamepads() +{ + ImGuiIO& io = ImGui::GetIO(); + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. + return; + + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; +#if GLFW_HAS_GAMEPAD_API + GLFWgamepadstate gamepad; + if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) + return; + #define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED) do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0); } while (0) + #define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1) do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) +#else + int axes_count = 0, buttons_count = 0; + const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); + const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); + if (axes_count == 0 || buttons_count == 0) + return; + #define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO) do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS)); } while (0) + #define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) +#endif + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + MAP_BUTTON(ImGuiKey_GamepadStart, GLFW_GAMEPAD_BUTTON_START, 7); + MAP_BUTTON(ImGuiKey_GamepadBack, GLFW_GAMEPAD_BUTTON_BACK, 6); + MAP_BUTTON(ImGuiKey_GamepadFaceLeft, GLFW_GAMEPAD_BUTTON_X, 2); // Xbox X, PS Square + MAP_BUTTON(ImGuiKey_GamepadFaceRight, GLFW_GAMEPAD_BUTTON_B, 1); // Xbox B, PS Circle + MAP_BUTTON(ImGuiKey_GamepadFaceUp, GLFW_GAMEPAD_BUTTON_Y, 3); // Xbox Y, PS Triangle + MAP_BUTTON(ImGuiKey_GamepadFaceDown, GLFW_GAMEPAD_BUTTON_A, 0); // Xbox A, PS Cross + MAP_BUTTON(ImGuiKey_GamepadDpadLeft, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, 13); + MAP_BUTTON(ImGuiKey_GamepadDpadRight, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, 11); + MAP_BUTTON(ImGuiKey_GamepadDpadUp, GLFW_GAMEPAD_BUTTON_DPAD_UP, 10); + MAP_BUTTON(ImGuiKey_GamepadDpadDown, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, 12); + MAP_BUTTON(ImGuiKey_GamepadL1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, 4); + MAP_BUTTON(ImGuiKey_GamepadR1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, 5); + MAP_ANALOG(ImGuiKey_GamepadL2, GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, 4, -0.75f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadR2, GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, 5, -0.75f, +1.0f); + MAP_BUTTON(ImGuiKey_GamepadL3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, 8); + MAP_BUTTON(ImGuiKey_GamepadR3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, 9); + MAP_ANALOG(ImGuiKey_GamepadLStickLeft, GLFW_GAMEPAD_AXIS_LEFT_X, 0, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickRight, GLFW_GAMEPAD_AXIS_LEFT_X, 0, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickUp, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickDown, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickLeft, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickRight, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickUp, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickDown, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, +0.25f, +1.0f); + #undef MAP_BUTTON + #undef MAP_ANALOG +} + +static void ImGui_ImplGlfw_UpdateMonitors() +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + int monitors_count = 0; + GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count); + platform_io.Monitors.resize(0); + for (int n = 0; n < monitors_count; n++) + { + ImGuiPlatformMonitor monitor; + int x, y; + glfwGetMonitorPos(glfw_monitors[n], &x, &y); + const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); + monitor.MainPos = monitor.WorkPos = ImVec2((float)x, (float)y); + monitor.MainSize = monitor.WorkSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); +#if GLFW_HAS_MONITOR_WORK_AREA + int w, h; + glfwGetMonitorWorkarea(glfw_monitors[n], &x, &y, &w, &h); + if (w > 0 && h > 0) // Workaround a small GLFW issue reporting zero on monitor changes: https://github.com/glfw/glfw/pull/1761 + { + monitor.WorkPos = ImVec2((float)x, (float)y); + monitor.WorkSize = ImVec2((float)w, (float)h); + } +#endif +#if GLFW_HAS_PER_MONITOR_DPI + // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. + float x_scale, y_scale; + glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale); + monitor.DpiScale = x_scale; +#endif + platform_io.Monitors.push_back(monitor); + } + bd->WantUpdateMonitors = false; +} + +void ImGui_ImplGlfw_NewFrame() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != NULL && "Did you call ImGui_ImplGlfw_InitForXXX()?"); + + // Setup display size (every frame to accommodate for window resizing) + int w, h; + int display_w, display_h; + glfwGetWindowSize(bd->Window, &w, &h); + glfwGetFramebufferSize(bd->Window, &display_w, &display_h); + io.DisplaySize = ImVec2((float)w, (float)h); + if (w > 0 && h > 0) + io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h); + if (bd->WantUpdateMonitors) + ImGui_ImplGlfw_UpdateMonitors(); + + // Setup time step + double current_time = glfwGetTime(); + io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); + bd->Time = current_time; + + ImGui_ImplGlfw_UpdateMouseData(); + ImGui_ImplGlfw_UpdateMouseCursor(); + + // Update game controllers (if enabled and available) + ImGui_ImplGlfw_UpdateGamepads(); +} + +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. +struct ImGui_ImplGlfw_ViewportData +{ + GLFWwindow* Window; + bool WindowOwned; + int IgnoreWindowPosEventFrame; + int IgnoreWindowSizeEventFrame; + + ImGui_ImplGlfw_ViewportData() { Window = NULL; WindowOwned = false; IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; } + ~ImGui_ImplGlfw_ViewportData() { IM_ASSERT(Window == NULL); } +}; + +static void ImGui_ImplGlfw_WindowCloseCallback(GLFWwindow* window) +{ + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) + viewport->PlatformRequestClose = true; +} + +// GLFW may dispatch window pos/size events after calling glfwSetWindowPos()/glfwSetWindowSize(). +// However: depending on the platform the callback may be invoked at different time: +// - on Windows it appears to be called within the glfwSetWindowPos()/glfwSetWindowSize() call +// - on Linux it is queued and invoked during glfwPollEvents() +// Because the event doesn't always fire on glfwSetWindowXXX() we use a frame counter tag to only +// ignore recent glfwSetWindowXXX() calls. +static void ImGui_ImplGlfw_WindowPosCallback(GLFWwindow* window, int, int) +{ + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) + { + if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) + { + bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowPosEventFrame + 1); + //data->IgnoreWindowPosEventFrame = -1; + if (ignore_event) + return; + } + viewport->PlatformRequestMove = true; + } +} + +static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) +{ + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) + { + if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) + { + bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowSizeEventFrame + 1); + //data->IgnoreWindowSizeEventFrame = -1; + if (ignore_event) + return; + } + viewport->PlatformRequestResize = true; + } +} + +static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)(); + viewport->PlatformUserData = vd; + + // GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED + // With GLFW 3.3, the hint GLFW_FOCUS_ON_SHOW fixes this problem + glfwWindowHint(GLFW_VISIBLE, false); + glfwWindowHint(GLFW_FOCUSED, false); +#if GLFW_HAS_FOCUS_ON_SHOW + glfwWindowHint(GLFW_FOCUS_ON_SHOW, false); + #endif + glfwWindowHint(GLFW_DECORATED, (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? false : true); +#if GLFW_HAS_WINDOW_TOPMOST + glfwWindowHint(GLFW_FLOATING, (viewport->Flags & ImGuiViewportFlags_TopMost) ? true : false); +#endif + GLFWwindow* share_window = (bd->ClientApi == GlfwClientApi_OpenGL) ? bd->Window : NULL; + vd->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, share_window); + vd->WindowOwned = true; + viewport->PlatformHandle = (void*)vd->Window; +#ifdef _WIN32 + viewport->PlatformHandleRaw = glfwGetWin32Window(vd->Window); +#elif defined(__APPLE__) + viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(vd->Window); +#endif + glfwSetWindowPos(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y); + + // Install GLFW callbacks for secondary viewports + glfwSetWindowFocusCallback(vd->Window, ImGui_ImplGlfw_WindowFocusCallback); + glfwSetCursorEnterCallback(vd->Window, ImGui_ImplGlfw_CursorEnterCallback); + glfwSetCursorPosCallback(vd->Window, ImGui_ImplGlfw_CursorPosCallback); + glfwSetMouseButtonCallback(vd->Window, ImGui_ImplGlfw_MouseButtonCallback); + glfwSetScrollCallback(vd->Window, ImGui_ImplGlfw_ScrollCallback); + glfwSetKeyCallback(vd->Window, ImGui_ImplGlfw_KeyCallback); + glfwSetCharCallback(vd->Window, ImGui_ImplGlfw_CharCallback); + glfwSetWindowCloseCallback(vd->Window, ImGui_ImplGlfw_WindowCloseCallback); + glfwSetWindowPosCallback(vd->Window, ImGui_ImplGlfw_WindowPosCallback); + glfwSetWindowSizeCallback(vd->Window, ImGui_ImplGlfw_WindowSizeCallback); + if (bd->ClientApi == GlfwClientApi_OpenGL) + { + glfwMakeContextCurrent(vd->Window); + glfwSwapInterval(0); + } +} + +static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) + { + if (vd->WindowOwned) + { +#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) + HWND hwnd = (HWND)viewport->PlatformHandleRaw; + ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); +#endif + + // Release any keys that were pressed in the window being destroyed and are still held down, + // because we will not receive any release events after window is destroyed. + for (int i = 0; i < IM_ARRAYSIZE(bd->KeyOwnerWindows); i++) + if (bd->KeyOwnerWindows[i] == vd->Window) + ImGui_ImplGlfw_KeyCallback(vd->Window, i, 0, GLFW_RELEASE, 0); // Later params are only used for main viewport, on which this function is never called. + + glfwDestroyWindow(vd->Window); + } + vd->Window = NULL; + IM_DELETE(vd); + } + viewport->PlatformUserData = viewport->PlatformHandle = NULL; +} + +// We have submitted https://github.com/glfw/glfw/pull/1568 to allow GLFW to support "transparent inputs". +// In the meanwhile we implement custom per-platform workarounds here (FIXME-VIEWPORT: Implement same work-around for Linux/OSX!) +#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) +static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (msg == WM_NCHITTEST) + { + // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() properly (which is OPTIONAL). + // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. + // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in + // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. + ImGuiViewport* viewport = (ImGuiViewport*)::GetPropA(hWnd, "IMGUI_VIEWPORT"); + if (viewport->Flags & ImGuiViewportFlags_NoInputs) + return HTTRANSPARENT; + } + return ::CallWindowProc(bd->GlfwWndProc, hWnd, msg, wParam, lParam); +} +#endif + +static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + +#if defined(_WIN32) + // GLFW hack: Hide icon from task bar + HWND hwnd = (HWND)viewport->PlatformHandleRaw; + if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) + { + LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + ex_style &= ~WS_EX_APPWINDOW; + ex_style |= WS_EX_TOOLWINDOW; + ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); + } + + // GLFW hack: install hook for WM_NCHITTEST message handler +#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ::SetPropA(hwnd, "IMGUI_VIEWPORT", viewport); + if (bd->GlfwWndProc == NULL) + bd->GlfwWndProc = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_WNDPROC); + ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WndProcNoInputs); +#endif + +#if !GLFW_HAS_FOCUS_ON_SHOW + // GLFW hack: GLFW 3.2 has a bug where glfwShowWindow() also activates/focus the window. + // The fix was pushed to GLFW repository on 2018/01/09 and should be included in GLFW 3.3 via a GLFW_FOCUS_ON_SHOW window attribute. + // See https://github.com/glfw/glfw/issues/1189 + // FIXME-VIEWPORT: Implement same work-around for Linux/OSX in the meanwhile. + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + { + ::ShowWindow(hwnd, SW_SHOWNA); + return; + } +#endif +#endif + + glfwShowWindow(vd->Window); +} + +static ImVec2 ImGui_ImplGlfw_GetWindowPos(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + int x = 0, y = 0; + glfwGetWindowPos(vd->Window, &x, &y); + return ImVec2((float)x, (float)y); +} + +static void ImGui_ImplGlfw_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + vd->IgnoreWindowPosEventFrame = ImGui::GetFrameCount(); + glfwSetWindowPos(vd->Window, (int)pos.x, (int)pos.y); +} + +static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + int w = 0, h = 0; + glfwGetWindowSize(vd->Window, &w, &h); + return ImVec2((float)w, (float)h); +} + +static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; +#if __APPLE__ && !GLFW_HAS_OSX_WINDOW_POS_FIX + // Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are + // positioned from the upper-left corner. GLFW makes an effort to convert macOS style coordinates, however it + // doesn't handle it when changing size. We are manually moving the window in order for changes of size to be based + // on the upper-left corner. + int x, y, width, height; + glfwGetWindowPos(vd->Window, &x, &y); + glfwGetWindowSize(vd->Window, &width, &height); + glfwSetWindowPos(vd->Window, x, y - height + size.y); +#endif + vd->IgnoreWindowSizeEventFrame = ImGui::GetFrameCount(); + glfwSetWindowSize(vd->Window, (int)size.x, (int)size.y); +} + +static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* title) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + glfwSetWindowTitle(vd->Window, title); +} + +static void ImGui_ImplGlfw_SetWindowFocus(ImGuiViewport* viewport) +{ +#if GLFW_HAS_FOCUS_WINDOW + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + glfwFocusWindow(vd->Window); +#else + // FIXME: What are the effect of not having this function? At the moment imgui doesn't actually call SetWindowFocus - we set that up ahead, will answer that question later. + (void)viewport; +#endif +} + +static bool ImGui_ImplGlfw_GetWindowFocus(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + return glfwGetWindowAttrib(vd->Window, GLFW_FOCUSED) != 0; +} + +static bool ImGui_ImplGlfw_GetWindowMinimized(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + return glfwGetWindowAttrib(vd->Window, GLFW_ICONIFIED) != 0; +} + +#if GLFW_HAS_WINDOW_ALPHA +static void ImGui_ImplGlfw_SetWindowAlpha(ImGuiViewport* viewport, float alpha) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + glfwSetWindowOpacity(vd->Window, alpha); +} +#endif + +static void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + if (bd->ClientApi == GlfwClientApi_OpenGL) + glfwMakeContextCurrent(vd->Window); +} + +static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + if (bd->ClientApi == GlfwClientApi_OpenGL) + { + glfwMakeContextCurrent(vd->Window); + glfwSwapBuffers(vd->Window); + } +} + +//-------------------------------------------------------------------------------------------------------- +// Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface) +//-------------------------------------------------------------------------------------------------------- + +// Avoid including so we can build without it +#if GLFW_HAS_VULKAN +#ifndef VULKAN_H_ +#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; +#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) +#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; +#else +#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; +#endif +VK_DEFINE_HANDLE(VkInstance) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) +struct VkAllocationCallbacks; +enum VkResult { VK_RESULT_MAX_ENUM = 0x7FFFFFFF }; +#endif // VULKAN_H_ +extern "C" { extern GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); } +static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + IM_UNUSED(bd); + IM_ASSERT(bd->ClientApi == GlfwClientApi_Vulkan); + VkResult err = glfwCreateWindowSurface((VkInstance)vk_instance, vd->Window, (const VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface); + return (int)err; +} +#endif // GLFW_HAS_VULKAN + +static void ImGui_ImplGlfw_InitPlatformInterface() +{ + // Register platform interface (will be coupled with a renderer interface) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_CreateWindow = ImGui_ImplGlfw_CreateWindow; + platform_io.Platform_DestroyWindow = ImGui_ImplGlfw_DestroyWindow; + platform_io.Platform_ShowWindow = ImGui_ImplGlfw_ShowWindow; + platform_io.Platform_SetWindowPos = ImGui_ImplGlfw_SetWindowPos; + platform_io.Platform_GetWindowPos = ImGui_ImplGlfw_GetWindowPos; + platform_io.Platform_SetWindowSize = ImGui_ImplGlfw_SetWindowSize; + platform_io.Platform_GetWindowSize = ImGui_ImplGlfw_GetWindowSize; + platform_io.Platform_SetWindowFocus = ImGui_ImplGlfw_SetWindowFocus; + platform_io.Platform_GetWindowFocus = ImGui_ImplGlfw_GetWindowFocus; + platform_io.Platform_GetWindowMinimized = ImGui_ImplGlfw_GetWindowMinimized; + platform_io.Platform_SetWindowTitle = ImGui_ImplGlfw_SetWindowTitle; + platform_io.Platform_RenderWindow = ImGui_ImplGlfw_RenderWindow; + platform_io.Platform_SwapBuffers = ImGui_ImplGlfw_SwapBuffers; +#if GLFW_HAS_WINDOW_ALPHA + platform_io.Platform_SetWindowAlpha = ImGui_ImplGlfw_SetWindowAlpha; +#endif +#if GLFW_HAS_VULKAN + platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface; +#endif + + // Register main window handle (which is owned by the main application, not by us) + // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)(); + vd->Window = bd->Window; + vd->WindowOwned = false; + main_viewport->PlatformUserData = vd; + main_viewport->PlatformHandle = (void*)bd->Window; +} + +static void ImGui_ImplGlfw_ShutdownPlatformInterface() +{ + ImGui::DestroyPlatformWindows(); +} + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif diff --git a/src/FlipScope/ImGui/imgui_impl_opengl3.cpp b/src/FlipScope/ImGui/imgui_impl_opengl3.cpp new file mode 100644 index 0000000..f7d7b95 --- /dev/null +++ b/src/FlipScope/ImGui/imgui_impl_opengl3.cpp @@ -0,0 +1,726 @@ +// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline +// - Desktop GL: 2.x 3.x 4.x +// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2020-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader. +// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader. +// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders. +// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility. +// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call. +// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. +// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. +// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop. +// 2019-03-15: OpenGL: Added a dummy GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early. +// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0). +// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader. +// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display. +// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450). +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. +// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN. +// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used. +// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES". +// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation. +// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link. +// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples. +// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. +// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state. +// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer. +// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150". +// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. +// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. +// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. +// 2017-05-01: OpenGL: Fixed save and restore of current blend func state. +// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. +// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. +// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752) + +//---------------------------------------- +// OpenGL GLSL GLSL +// version version string +//---------------------------------------- +// 2.0 110 "#version 110" +// 2.1 120 "#version 120" +// 3.0 130 "#version 130" +// 3.1 140 "#version 140" +// 3.2 150 "#version 150" +// 3.3 330 "#version 330 core" +// 4.0 400 "#version 400 core" +// 4.1 410 "#version 410 core" +// 4.2 420 "#version 410 core" +// 4.3 430 "#version 430 core" +// ES 2.0 100 "#version 100" = WebGL 1.0 +// ES 3.0 300 "#version 300 es" = WebGL 2.0 +//---------------------------------------- + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "imgui.h" +#include "imgui_impl_opengl3.h" +#include +#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier +#include // intptr_t +#else +#include // intptr_t +#endif +#if defined(__APPLE__) +#include "TargetConditionals.h" +#endif + +// Auto-enable GLES on matching platforms +#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) +#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) +#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" +#elif defined(__EMSCRIPTEN__) +#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" +#endif +#endif + +#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3) +#undef IMGUI_IMPL_OPENGL_LOADER_GL3W +#undef IMGUI_IMPL_OPENGL_LOADER_GLEW +#undef IMGUI_IMPL_OPENGL_LOADER_GLAD +#undef IMGUI_IMPL_OPENGL_LOADER_GLBINDING2 +#undef IMGUI_IMPL_OPENGL_LOADER_GLBINDING3 +#undef IMGUI_IMPL_OPENGL_LOADER_CUSTOM +#endif + +// GL includes +#if defined(IMGUI_IMPL_OPENGL_ES2) +#include +#elif defined(IMGUI_IMPL_OPENGL_ES3) +#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) +#include // Use GL ES 3 +#else +#include // Use GL ES 3 +#endif +#else +// About Desktop OpenGL function loaders: +// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers. +// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad). +// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. +#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) +#include // Needs to be initialized with gl3wInit() in user's code +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) +#include // Needs to be initialized with glewInit() in user's code. +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) +#include // Needs to be initialized with gladLoadGL() in user's code. +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) +#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors. +#include // Needs to be initialized with glbinding::Binding::initialize() in user's code. +#include +using namespace gl; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) +#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors. +#include // Needs to be initialized with glbinding::initialize() in user's code. +#include +using namespace gl; +#else +#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM +#endif +#endif + +// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have. +#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3) || !defined(GL_VERSION_3_2) +#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 0 +#else +#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 1 +#endif + +// OpenGL Data +static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries. +static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings. +static GLuint g_FontTexture = 0; +static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; +static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location +static int g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location +static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; + +// Forward Declarations +static void ImGui_ImplOpenGL3_InitPlatformInterface(); +static void ImGui_ImplOpenGL3_ShutdownPlatformInterface(); + +// Functions +bool ImGui_ImplOpenGL3_Init(const char* glsl_version) +{ + // Query for GL version +#if !defined(IMGUI_IMPL_OPENGL_ES2) + GLint major, minor; + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MINOR_VERSION, &minor); + g_GlVersion = major * 1000 + minor; +#else + g_GlVersion = 2000; // GLES 2 +#endif + + // Setup back-end capabilities flags + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_opengl3"; +#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET + if (g_GlVersion >= 3200) + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. +#endif + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) + + // Store GLSL version string so we can refer to it later in case we recreate shaders. + // Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. +#if defined(IMGUI_IMPL_OPENGL_ES2) + if (glsl_version == NULL) + glsl_version = "#version 100"; +#elif defined(IMGUI_IMPL_OPENGL_ES3) + if (glsl_version == NULL) + glsl_version = "#version 300 es"; +#else + if (glsl_version == NULL) + glsl_version = "#version 130"; +#endif + IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); + strcpy(g_GlslVersionString, glsl_version); + strcat(g_GlslVersionString, "\n"); + + // Dummy construct to make it easily visible in the IDE and debugger which GL loader has been selected. + // The code actually never uses the 'gl_loader' variable! It is only here so you can read it! + // If auto-detection fails or doesn't select the same GL loader file as used by your application, + // you are likely to get a crash below. + // You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line. + const char* gl_loader = "Unknown"; + IM_UNUSED(gl_loader); +#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) + gl_loader = "GL3W"; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) + gl_loader = "GLEW"; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) + gl_loader = "GLAD"; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) + gl_loader = "glbinding2"; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) + gl_loader = "glbinding3"; +#elif defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) + gl_loader = "custom"; +#else + gl_loader = "none"; +#endif + + // Make a dummy GL call (we don't actually need the result) + // IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code. + // Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above. + GLint current_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture); + + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + ImGui_ImplOpenGL3_InitPlatformInterface(); + + return true; +} + +void ImGui_ImplOpenGL3_Shutdown() +{ + ImGui_ImplOpenGL3_ShutdownPlatformInterface(); + ImGui_ImplOpenGL3_DestroyDeviceObjects(); +} + +void ImGui_ImplOpenGL3_NewFrame() +{ + if (!g_ShaderHandle) + ImGui_ImplOpenGL3_CreateDeviceObjects(); +} + +static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object) +{ + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); +#ifdef GL_POLYGON_MODE + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +#endif + + // Setup viewport, orthographic projection matrix + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; + const float ortho_projection[4][4] = + { + { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, + { 0.0f, 0.0f, -1.0f, 0.0f }, + { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, + }; + glUseProgram(g_ShaderHandle); + glUniform1i(g_AttribLocationTex, 0); + glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); +#ifdef GL_SAMPLER_BINDING + glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. +#endif + + (void)vertex_array_object; +#ifndef IMGUI_IMPL_OPENGL_ES2 + glBindVertexArray(vertex_array_object); +#endif + + // Bind vertex/index buffers and setup attributes for ImDrawVert + glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); + glEnableVertexAttribArray(g_AttribLocationVtxPos); + glEnableVertexAttribArray(g_AttribLocationVtxUV); + glEnableVertexAttribArray(g_AttribLocationVtxColor); + glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); + glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); + glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); +} + +// OpenGL3 Render function. +// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) +// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. +void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) +{ + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); + if (fb_width <= 0 || fb_height <= 0) + return; + + // Backup GL state + GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); + glActiveTexture(GL_TEXTURE0); + GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); + GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); +#ifdef GL_SAMPLER_BINDING + GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); +#endif + GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_ES2 + GLint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object); +#endif +#ifdef GL_POLYGON_MODE + GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); +#endif + GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); + GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); + GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); + GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); + GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); + GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); + GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); + GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); + GLboolean last_enable_blend = glIsEnabled(GL_BLEND); + GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); + GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); + GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + bool clip_origin_lower_left = true; +#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__) + GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) + if (last_clip_origin == GL_UPPER_LEFT) + clip_origin_lower_left = false; +#endif + + // Setup desired GL state + // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts) + // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. + GLuint vertex_array_object = 0; +#ifndef IMGUI_IMPL_OPENGL_ES2 + glGenVertexArrays(1, &vertex_array_object); +#endif + ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); + + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) + + // Render command lists + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + + // Upload vertex/index buffers + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback != NULL) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); + else + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec4 clip_rect; + clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; + clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; + clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; + clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; + + if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) + { + // Apply scissor/clipping rectangle + if (clip_origin_lower_left) + glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); + else + glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT) + + // Bind texture, Draw + glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); +#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET + if (g_GlVersion >= 3200) + glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset); + else +#endif + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx))); + } + } + } + } + + // Destroy the temporary VAO +#ifndef IMGUI_IMPL_OPENGL_ES2 + glDeleteVertexArrays(1, &vertex_array_object); +#endif + + // Restore modified GL state + glUseProgram(last_program); + glBindTexture(GL_TEXTURE_2D, last_texture); +#ifdef GL_SAMPLER_BINDING + glBindSampler(0, last_sampler); +#endif + glActiveTexture(last_active_texture); +#ifndef IMGUI_IMPL_OPENGL_ES2 + glBindVertexArray(last_vertex_array_object); +#endif + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); + glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); + glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); + if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); + if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); + if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); + if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); +#ifdef GL_POLYGON_MODE + glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); +#endif + glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); + glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); +} + +bool ImGui_ImplOpenGL3_CreateFontsTexture() +{ + // Build texture atlas + ImGuiIO& io = ImGui::GetIO(); + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system + GLint last_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGenTextures(1, &g_FontTexture); + glBindTexture(GL_TEXTURE_2D, g_FontTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +#ifdef GL_UNPACK_ROW_LENGTH + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +#endif + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Store our identifier + io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture; + + // Restore state + glBindTexture(GL_TEXTURE_2D, last_texture); + + return true; +} + +void ImGui_ImplOpenGL3_DestroyFontsTexture() +{ + if (g_FontTexture) + { + ImGuiIO& io = ImGui::GetIO(); + glDeleteTextures(1, &g_FontTexture); + io.Fonts->TexID = 0; + g_FontTexture = 0; + } +} + +// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file. +static bool CheckShader(GLuint handle, const char* desc) +{ + GLint status = 0, log_length = 0; + glGetShaderiv(handle, GL_COMPILE_STATUS, &status); + glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); + if ((GLboolean)status == GL_FALSE) + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc); + if (log_length > 1) + { + ImVector buf; + buf.resize((int)(log_length + 1)); + glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); + fprintf(stderr, "%s\n", buf.begin()); + } + return (GLboolean)status == GL_TRUE; +} + +// If you get an error please report on GitHub. You may try different GL context version or GLSL version. +static bool CheckProgram(GLuint handle, const char* desc) +{ + GLint status = 0, log_length = 0; + glGetProgramiv(handle, GL_LINK_STATUS, &status); + glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); + if ((GLboolean)status == GL_FALSE) + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString); + if (log_length > 1) + { + ImVector buf; + buf.resize((int)(log_length + 1)); + glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); + fprintf(stderr, "%s\n", buf.begin()); + } + return (GLboolean)status == GL_TRUE; +} + +bool ImGui_ImplOpenGL3_CreateDeviceObjects() +{ + // Backup GL state + GLint last_texture, last_array_buffer; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_ES2 + GLint last_vertex_array; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); +#endif + + // Parse GLSL version string + int glsl_version = 130; + sscanf(g_GlslVersionString, "#version %d", &glsl_version); + + const GLchar* vertex_shader_glsl_120 = + "uniform mat4 ProjMtx;\n" + "attribute vec2 Position;\n" + "attribute vec2 UV;\n" + "attribute vec4 Color;\n" + "varying vec2 Frag_UV;\n" + "varying vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_130 = + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 UV;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_300_es = + "precision mediump float;\n" + "layout (location = 0) in vec2 Position;\n" + "layout (location = 1) in vec2 UV;\n" + "layout (location = 2) in vec4 Color;\n" + "uniform mat4 ProjMtx;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_410_core = + "layout (location = 0) in vec2 Position;\n" + "layout (location = 1) in vec2 UV;\n" + "layout (location = 2) in vec4 Color;\n" + "uniform mat4 ProjMtx;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_120 = + "#ifdef GL_ES\n" + " precision mediump float;\n" + "#endif\n" + "uniform sampler2D Texture;\n" + "varying vec2 Frag_UV;\n" + "varying vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_130 = + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main()\n" + "{\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_300_es = + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "layout (location = 0) out vec4 Out_Color;\n" + "void main()\n" + "{\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_410_core = + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "uniform sampler2D Texture;\n" + "layout (location = 0) out vec4 Out_Color;\n" + "void main()\n" + "{\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + // Select shaders matching our GLSL versions + const GLchar* vertex_shader = NULL; + const GLchar* fragment_shader = NULL; + if (glsl_version < 130) + { + vertex_shader = vertex_shader_glsl_120; + fragment_shader = fragment_shader_glsl_120; + } + else if (glsl_version >= 410) + { + vertex_shader = vertex_shader_glsl_410_core; + fragment_shader = fragment_shader_glsl_410_core; + } + else if (glsl_version == 300) + { + vertex_shader = vertex_shader_glsl_300_es; + fragment_shader = fragment_shader_glsl_300_es; + } + else + { + vertex_shader = vertex_shader_glsl_130; + fragment_shader = fragment_shader_glsl_130; + } + + // Create shaders + const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader }; + g_VertHandle = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL); + glCompileShader(g_VertHandle); + CheckShader(g_VertHandle, "vertex shader"); + + const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader }; + g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL); + glCompileShader(g_FragHandle); + CheckShader(g_FragHandle, "fragment shader"); + + g_ShaderHandle = glCreateProgram(); + glAttachShader(g_ShaderHandle, g_VertHandle); + glAttachShader(g_ShaderHandle, g_FragHandle); + glLinkProgram(g_ShaderHandle); + CheckProgram(g_ShaderHandle, "shader program"); + + g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); + g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); + g_AttribLocationVtxPos = glGetAttribLocation(g_ShaderHandle, "Position"); + g_AttribLocationVtxUV = glGetAttribLocation(g_ShaderHandle, "UV"); + g_AttribLocationVtxColor = glGetAttribLocation(g_ShaderHandle, "Color"); + + // Create buffers + glGenBuffers(1, &g_VboHandle); + glGenBuffers(1, &g_ElementsHandle); + + ImGui_ImplOpenGL3_CreateFontsTexture(); + + // Restore modified GL state + glBindTexture(GL_TEXTURE_2D, last_texture); + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_ES2 + glBindVertexArray(last_vertex_array); +#endif + + return true; +} + +void ImGui_ImplOpenGL3_DestroyDeviceObjects() +{ + if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; } + if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; } + if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); } + if (g_ShaderHandle && g_FragHandle) { glDetachShader(g_ShaderHandle, g_FragHandle); } + if (g_VertHandle) { glDeleteShader(g_VertHandle); g_VertHandle = 0; } + if (g_FragHandle) { glDeleteShader(g_FragHandle); g_FragHandle = 0; } + if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; } + + ImGui_ImplOpenGL3_DestroyFontsTexture(); +} + +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*) +{ + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + { + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + } + ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData); +} + +static void ImGui_ImplOpenGL3_InitPlatformInterface() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow; +} + +static void ImGui_ImplOpenGL3_ShutdownPlatformInterface() +{ + ImGui::DestroyPlatformWindows(); +} diff --git a/src/FlipScope/Pipeline/Episode.cpp b/src/FlipScope/Pipeline/Episode.cpp new file mode 100644 index 0000000..7f91ed3 --- /dev/null +++ b/src/FlipScope/Pipeline/Episode.cpp @@ -0,0 +1,22 @@ +#include "pch.h" + +#include "FlipScope/Pipeline/Episode.h" + +namespace FSV +{ + Episode::Episode(Project *project, std::string name) + : m_Project(project), + m_Name(name) + { + } + + Episode::~Episode() + { + + } + + void Episode::AddShot(Shot &shot) + { + m_ShotList.push_back(shot); + } +} \ No newline at end of file diff --git a/src/FlipScope/Pipeline/Episode.h b/src/FlipScope/Pipeline/Episode.h new file mode 100644 index 0000000..1f16e28 --- /dev/null +++ b/src/FlipScope/Pipeline/Episode.h @@ -0,0 +1,27 @@ +#pragma once + +#include "FlipScope/Pipeline/Project.h" +#include "FlipScope/Pipeline/Shot.h" + +#include +#include + +namespace FSV +{ + class Episode + { + public: + Episode(Project *project, const std::string name); + ~Episode(); + + std::string GetName() { return m_Name; } + ShotList GetShotList() { return m_ShotList; } + + void AddShot(Shot &shot); + + private: + std::string m_Name; + Project* m_Project; + ShotList m_ShotList; + }; +} diff --git a/src/FlipScope/Pipeline/Project.cpp b/src/FlipScope/Pipeline/Project.cpp new file mode 100644 index 0000000..31f4249 --- /dev/null +++ b/src/FlipScope/Pipeline/Project.cpp @@ -0,0 +1,155 @@ +#include "pch.h" + +#include "FlipScope/Pipeline/Project.h" +#include "FlipScope/Pipeline/Episode.h" +#include "FlipScope/Pipeline/Shot.h" +#include "FlipScope/Core/Log.h" + +namespace FSV +{ + + Project::Project(const std::string name, + const std::string aliasname, + const std::string type, + const std::string location, + const std::string ntPath, + const std::string unixPath, + const StringList users) : + m_Name(name), + m_AliasName(aliasname), + m_Type(type), + m_Location(location), + m_NtPath(ntPath), + m_UnixPath(unixPath), + m_Users(users) + { + FS_INFO("Init ProjectData : {0}", aliasname); + const std::string ddtProjectDataRoot = "//ren/render/WEB/ddt2/" + aliasname; + + m_ShotLastReviewConfigPath = ddtProjectDataRoot + "/" + "shot_lastest_review_file.tbs"; + m_ShotInfomationConfigPath = ddtProjectDataRoot + "/" + "shot_information.tbs"; + m_ShotTaskReviewConfigPath = ddtProjectDataRoot + "/" + "shottask_information.tbs"; + + } + + Project::~Project() + { + m_Users.clear(); + } + + void Project::ShotLastReviewConfigInit() + { + if (!m_ShotLastReviewConfigRoot.isNull()) + { + return; + } + + std::ifstream config_doc(m_ShotLastReviewConfigPath, std::ifstream::binary); + Json::CharReaderBuilder rbuilder; + std::string errs; + + if (!Json::parseFromStream(rbuilder, config_doc, &m_ShotLastReviewConfigRoot, &errs)) + { + FS_ERROR(errs); + return; + } + + // Initialize Episode List + if (m_EpisodeNameList.size() == 0) + { + for (auto &info : m_ShotLastReviewConfigRoot) + { + std::string episodeName = info["name"].asString(); + m_EpisodeNameList.push_back(episodeName); + FS_INFO("GetEpisodeList() : {0} : {1}", m_AliasName, info["name"].asString()); + } + } + } + + void Project::ShotInfomationConfigInit() + { + if (!m_ShotInfomationConfigRoot.isNull()) + { + return; + } + + std::ifstream config_doc(m_ShotInfomationConfigPath, std::ifstream::binary); + Json::CharReaderBuilder rbuilder; + std::string errs; + + if (!Json::parseFromStream(rbuilder, config_doc, &m_ShotInfomationConfigRoot, &errs)) + { + FS_ERROR(errs); + return; + } + + for (auto &info : m_ShotInfomationConfigRoot) + { + for (unsigned int n = 1; n < info["table"].size(); n++) + { + std::string name = info["table"][n][2].asString(); + std::string act = info["table"][n][3].asString(); + std::string sequence = info["table"][n][4].asString(); + int frameStart = info["table"][n][5].asInt(); + int frameEnd = info["table"][n][6].asInt(); + int duration = info["table"][n][7].asInt(); + int handleLeft = info["table"][n][8].asInt(); + int handleRight = info["table"][n][9].asInt(); + std::string shotType = info["table"][n][11].asString(); + std::string episodeName = info["table"][n][12].asString(); + Shot shot(name, act, sequence, + frameStart, frameEnd, duration, handleLeft, handleRight, + shotType, m_AliasName, episodeName); + m_ShotList.push_back(shot); + } + + FS_INFO("Init {0} Shots", info["table"].size()); + } + } + + bool Project::IsProjectUser(std::string userName) + { + for (auto &user : m_Users) + { + if (user == userName) + { + return true; + } + } + return false; + } + + StringList Project::GetEpisodeNameList() + { + ShotLastReviewConfigInit(); + + if (m_EpisodeNameList.size() == 0) + { + for (auto &info : m_ShotLastReviewConfigRoot) + { + std::string episodeName = info["name"].asString(); + m_EpisodeNameList.push_back(episodeName); + FS_INFO("GetEpisodeList() : {0} : {1}", m_AliasName, info["name"].asString()); + } + } + return m_EpisodeNameList; + } + + ShotList Project::GetShotListByEpisode(std::string episodeName) + { + ShotInfomationConfigInit(); + + ShotList shotList; + if (m_ShotList.size() == 0) + return shotList; + + for (auto &shot : m_ShotList) + { + if (shot.GetEpisodeName() == episodeName) + { + shotList.push_back(shot); + } + } + return shotList; + } +} diff --git a/src/FlipScope/Pipeline/Project.h b/src/FlipScope/Pipeline/Project.h new file mode 100644 index 0000000..3d1bf6d --- /dev/null +++ b/src/FlipScope/Pipeline/Project.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include + +#include "FlipScope/Pipeline/Shot.h" + +namespace FSV +{ + typedef std::vector StringList; + //typedef std::vector EpisodeList; + typedef std::vector ShotList; + + class Project + { + public: + Project(const std::string name, + const std::string aliasname, + const std::string type, + const std::string location, + const std::string ntPath, + const std::string unixPath, + const StringList users = StringList()); + ~Project(); + + public: + bool IsEmpty() { return m_Name.length() <= 0; } + std::string GetName() { return m_Name; } + std::string GetAliasName() { return m_AliasName; } + std::string GetType() { return m_AliasName; } + std::string GetLocation() { return m_Location; } + std::string GetNtPath() { return m_NtPath; } + std::string GetUnixPath() { return m_UnixPath; } + StringList GetUsers() { return m_Users; } + + public: + bool IsProjectUser(std::string userName); + + void ShotLastReviewConfigInit(); + void ShotInfomationConfigInit(); + + std::string GetShotLastReviewConfigPath() { return m_ShotLastReviewConfigPath; } + std::string GetShotInfomationConfigPath() { return m_ShotInfomationConfigPath; } + std::string GetShotTaskReviewConfigPath() { return m_ShotTaskReviewConfigPath; } + + StringList GetEpisodeNameList(); + //EpisodeList GetEpisodeList() { return m_EpisodeList; } + ShotList GetShotList() { return m_ShotList; } + ShotList GetShotListByEpisode(std::string episodeName); + + private: + std::string m_Name; + std::string m_AliasName; + std::string m_Type; + std::string m_Location; + std::string m_NtPath; + std::string m_UnixPath; + + StringList m_Users; + + std::string m_ShotLastReviewConfigPath; + std::string m_ShotInfomationConfigPath; + std::string m_ShotTaskReviewConfigPath; + + Json::Value m_ShotLastReviewConfigRoot; + Json::Value m_ShotInfomationConfigRoot; + Json::Value m_ShotTaskReviewConfigRoot; + + StringList m_EpisodeNameList; + //EpisodeList m_EpisodeList; + ShotList m_ShotList; + }; +} diff --git a/src/FlipScope/Pipeline/ProjectInfo.cpp b/src/FlipScope/Pipeline/ProjectInfo.cpp new file mode 100644 index 0000000..192b50d --- /dev/null +++ b/src/FlipScope/Pipeline/ProjectInfo.cpp @@ -0,0 +1,139 @@ +#include "pch.h" + +#include "FlipScope/Pipeline/ProjectInfo.h" +#include "FlipScope/Core/Log.h" + +namespace FSV +{ + const char* projectConfigPath = "//ren/render/spider/project.json"; + + ProjectInfo::ProjectInfo() + { + //if(!m_Initialized) + // Init(); + } + + ProjectInfo::~ProjectInfo() + { + Shutdown(); + } + + std::string ProjectInfo::GetCurrentLocation() + { + const char* domain = getenv("USERDOMAIN"); + if (strcmp(domain, "CG") == 0) + return "cgtp"; + if (strcmp(domain, "CGCG") == 0) + return "cgks"; + if (strcmp(domain, "CGXM") == 0) + return "cgxm"; + if (strcmp(domain, "GLOWINGSTUDIOS") == 0) + return "bgs"; + return "unknown"; + } + + std::string ProjectInfo::GetCurrentUser() + { + return getenv("USERNAME"); + } + + Project ProjectInfo::GetCurrentProjectByName(std::string projectName) + { + for (auto &p : m_UserProjectList) + { + if (p.GetAliasName() == projectName) + { + return p; + } + } + return Project("", "", "", "", "", ""); + } + + ProjectList ProjectInfo::GetUserProjects() + { + Init(); + return m_UserProjectList; + } + + void ProjectInfo::Reload() + { + m_UserProjectList.clear(); + m_Initialized = false; + Init(); + } + + void ProjectInfo::Init() + { + if (m_Initialized) + return; + + std::ifstream config_doc(projectConfigPath, std::ifstream::binary); + Json::CharReaderBuilder rbuilder; + + std::string errs; + Json::Value root; + + if (!Json::parseFromStream(rbuilder, config_doc, &root, &errs)) + { + FS_ERROR("{0}", errs.c_str()); + return; + } + + std::string currentUser = GetCurrentUser(); + auto projectNames = root.getMemberNames(); + + std::sort(projectNames.begin(), projectNames.end()); + + for(auto &name: projectNames) + { + std::string type = root[name]["Type"].asString(); + + if (type == "data") + { + std::string aliasName = root[name]["AliasName"].asString(); + std::string location = root[name]["Location"].asString(); + std::string ntPath = root[name]["NT"].asString(); + std::string unixPath = root[name]["UNIX"].asString(); + StringList users; + bool isProjectMember = false; + if (!root[name]["USER"].isNull()) + { + for (auto& user : root[name]["USER"]) + { + users.push_back(user.asString()); + } + } + std::string currentLocation = GetCurrentLocation(); + if (currentLocation == location) + { + // Current User Name in Project Users + if (std::find(users.begin(), users.end(), currentUser) != users.end()) + { + Project p(name, aliasName, type, location, ntPath, unixPath, users); + m_UserProjectList.push_back(p); + FS_INFO("Project :{0}", name); + } + } + } + } + + // For debug only + /*for (auto &p : m_CurrentProjectList) + { + FS_INFO("Check {0}", p.GetName()); + ProjectData data = p.GetProjectData(); + FS_INFO("Check {0}", data.GetShotLastReviewConfigPath()); + for (auto&e : p.GetProjectData().GetEpisodeList()) + { + FS_INFO("{0}", e.c_str()); + } + }*/ + + //std::sort(m_ProjectNames.begin(), m_ProjectNames.end()); + } + + void ProjectInfo::Shutdown() + { + m_UserProjectList.clear(); + } +} \ No newline at end of file diff --git a/src/FlipScope/Pipeline/ProjectInfo.h b/src/FlipScope/Pipeline/ProjectInfo.h new file mode 100644 index 0000000..405126b --- /dev/null +++ b/src/FlipScope/Pipeline/ProjectInfo.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +#include "FlipScope/Core/Log.h" +#include "FlipScope/Pipeline/Project.h" + +namespace FSV +{ + typedef std::vector ProjectList; + + class ProjectInfo + { + public: + + ProjectInfo(); + ~ProjectInfo(); + + void Init(); + void Reload(); + void Shutdown(); + + public: + static std::string GetCurrentLocation(); + static std::string GetCurrentUser(); + + Project GetCurrentProjectByName(std::string projectName); + ProjectList GetUserProjects(); + + private: + bool m_Initialized = false; + ProjectList m_UserProjectList; + }; +} diff --git a/src/FlipScope/Pipeline/Shot.cpp b/src/FlipScope/Pipeline/Shot.cpp new file mode 100644 index 0000000..b2da380 --- /dev/null +++ b/src/FlipScope/Pipeline/Shot.cpp @@ -0,0 +1,32 @@ +#include "pch.h" + +#include "FlipScope/Pipeline/Shot.h" + +namespace FSV +{ + Shot::Shot( const std::string name, + const std::string act, + const std::string sequence, + const int frameStart, + const int frameEnd, + const int duration, + const int handleLeft, + const int handleRight, + const std::string shotType, + const std::string projectName, + const std::string episodeName) + : m_Name(name), + m_Act(act), + m_Sequence(sequence), + m_FrameStart(frameStart), + m_FrameEnd(frameEnd), + m_Duration(duration), + m_HandleLeft(handleLeft), + m_HandleRight(handleRight), + m_ShotType(shotType), + m_ProjectName(projectName), + m_EpisodeName(episodeName) + { + } + +} \ No newline at end of file diff --git a/src/FlipScope/Pipeline/Shot.h b/src/FlipScope/Pipeline/Shot.h new file mode 100644 index 0000000..457d158 --- /dev/null +++ b/src/FlipScope/Pipeline/Shot.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +namespace FSV +{ + + class Shot + { + public: + + Shot( const std::string name, + const std::string act, + const std::string sequence, + const int frameStart, + const int frameEnd, + const int duration, + const int handleLeft, + const int handleRight, + const std::string shotType, + const std::string projectName, + const std::string episodeName); + ~Shot(){}; + + std::string GetName() { return m_Name; } + std::string GetAct() { return m_Act; } + std::string GetSequence() { return m_Sequence; } + int GetFrameStart() { return m_FrameStart; } + int GetFrameEnd() { return m_FrameEnd; } + std::string GetProjectName() { return m_ProjectName; } + std::string GetEpisodeName() { return m_EpisodeName; } + + private: + std::string m_Name; + std::string m_Act; + std::string m_Sequence; + int m_FrameStart = 0; + int m_FrameEnd = 0; + int m_Duration = 0; + int m_HandleLeft = 0; + int m_HandleRight = 0; + std::string m_ShotType; + + std::string m_ProjectName; + std::string m_EpisodeName; + + }; +} diff --git a/src/FlipScope/Renderer/FrameBuffer.cpp b/src/FlipScope/Renderer/FrameBuffer.cpp new file mode 100644 index 0000000..8dd8dc8 --- /dev/null +++ b/src/FlipScope/Renderer/FrameBuffer.cpp @@ -0,0 +1,24 @@ +#include "pch.h" + +#include "FlipScope/Renderer/FrameBuffer.h" +#include "FlipScope/Renderer/Renderer.h" +#include "FlipScope/Core/Log.h" +#include "Platform/OpenGL/OpenGLFramebuffer.h" + +namespace FSV +{ + std::shared_ptr Framebuffer::Create(const FramebufferSpecification& spec) + { + switch (Renderer::GetAPI()) + { + case RendererAPI::API::None: + return nullptr; + case RendererAPI::API::OpenGL: + return std::make_shared(spec); + } + + FS_ERROR("Unknown RendererAPI!"); + + return nullptr; + } +} \ No newline at end of file diff --git a/src/FlipScope/Renderer/FrameBuffer.h b/src/FlipScope/Renderer/FrameBuffer.h new file mode 100644 index 0000000..f2664e2 --- /dev/null +++ b/src/FlipScope/Renderer/FrameBuffer.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +namespace FSV +{ + struct FramebufferSpecification + { + uint32_t Width, Height; + uint32_t Sample = 1; + bool SwapChainTarget = false; + }; + + class Framebuffer + { + public: + virtual void Bind() = 0; + virtual void Unbind() = 0; + + virtual void Resize(uint32_t width, uint32_t height) = 0; + + virtual uint32_t GetColorAttachmentRendererID() const = 0; + + virtual const FramebufferSpecification& GetSpecification() const = 0; + + static std::shared_ptr Create(const FramebufferSpecification& spec); + }; +} \ No newline at end of file diff --git a/src/FlipScope/Renderer/GraphicsContext.cpp b/src/FlipScope/Renderer/GraphicsContext.cpp new file mode 100644 index 0000000..6b71983 --- /dev/null +++ b/src/FlipScope/Renderer/GraphicsContext.cpp @@ -0,0 +1,21 @@ +#include "pch.h" + +#include "FlipScope/Renderer/GraphicsContext.h" + +#include "FlipScope/Renderer/Renderer.h" +#include "Platform/OpenGL/OpenGLContext.h" + +namespace FSV +{ + std::unique_ptr GraphicsContext::Create(void* window) + { + switch (Renderer::GetAPI()) + { + case RendererAPI::API::None: + return nullptr; + case RendererAPI::API::OpenGL: + return std::make_unique(static_cast(window)); + } + return nullptr; + } +} \ No newline at end of file diff --git a/src/FlipScope/Renderer/GraphicsContext.h b/src/FlipScope/Renderer/GraphicsContext.h new file mode 100644 index 0000000..881dce0 --- /dev/null +++ b/src/FlipScope/Renderer/GraphicsContext.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace FSV +{ + class GraphicsContext + { + public: + virtual void Init() = 0; + virtual void SwapBuffers() = 0; + + static std::unique_ptr Create(void* window); + }; +} diff --git a/src/FlipScope/Renderer/RenderCommand.cpp b/src/FlipScope/Renderer/RenderCommand.cpp new file mode 100644 index 0000000..541e55d --- /dev/null +++ b/src/FlipScope/Renderer/RenderCommand.cpp @@ -0,0 +1,8 @@ +#include "pch.h" + +#include "FlipScope/Renderer/RenderCommand.h" + +namespace FSV +{ + std::unique_ptr RenderCommand::s_RendererAPI = RendererAPI::Create(); +} \ No newline at end of file diff --git a/src/FlipScope/Renderer/RenderCommand.h b/src/FlipScope/Renderer/RenderCommand.h new file mode 100644 index 0000000..27c1c40 --- /dev/null +++ b/src/FlipScope/Renderer/RenderCommand.h @@ -0,0 +1,32 @@ +#pragma once + +#include "FlipScope/Renderer/RendererAPI.h" + +namespace FSV +{ + class RenderCommand + { + public: + static void Init() + { + s_RendererAPI->Init(); + } + + static void SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) + { + s_RendererAPI->SetViewport(x, y, width, height); + } + + static void SetClearColor(glm::vec4& color) + { + s_RendererAPI->SetClearColor(color); + } + static void Clear() + { + s_RendererAPI->Clear(); + } + + private: + static std::unique_ptr s_RendererAPI; + }; +} diff --git a/src/FlipScope/Renderer/Renderer.cpp b/src/FlipScope/Renderer/Renderer.cpp new file mode 100644 index 0000000..43860d1 --- /dev/null +++ b/src/FlipScope/Renderer/Renderer.cpp @@ -0,0 +1,31 @@ +#include "pch.h" + +#include "FlipScope/Renderer/Renderer.h" + +namespace FSV +{ + void Renderer::Init() + { + RenderCommand::Init(); + } + + void Renderer::BeginScene() + { + + } + + void Renderer::EndScene() + { + + } + + void Renderer::OnWindowResize(uint32_t width, uint32_t height) + { + RenderCommand::SetViewport(0, 0, width, height); + } + + void Renderer::Shutdown() + { + + } +} \ No newline at end of file diff --git a/src/FlipScope/Renderer/Renderer.h b/src/FlipScope/Renderer/Renderer.h new file mode 100644 index 0000000..55e7e0c --- /dev/null +++ b/src/FlipScope/Renderer/Renderer.h @@ -0,0 +1,20 @@ +#pragma once +#include "FlipScope/Renderer/RenderCommand.h" +#include "FlipScope/Renderer/RendererAPI.h" + +namespace FSV +{ + class Renderer + { + public: + static void Init(); + static void Shutdown(); + + static void OnWindowResize(uint32_t width, uint32_t height); + + static void BeginScene(); + static void EndScene(); + + inline static RendererAPI::API GetAPI() { return RendererAPI::GetAPI(); } + }; +} diff --git a/src/FlipScope/Renderer/RendererAPI.cpp b/src/FlipScope/Renderer/RendererAPI.cpp new file mode 100644 index 0000000..4c7d1ae --- /dev/null +++ b/src/FlipScope/Renderer/RendererAPI.cpp @@ -0,0 +1,17 @@ +#include "pch.h" + +#include "FlipScope/Renderer/RendererAPI.h" +#include "Platform/OpenGL/OpenGLRendererAPI.h" + +namespace FSV +{ + RendererAPI::API RendererAPI::s_API = RendererAPI::API::OpenGL; + std::unique_ptr RendererAPI::Create() + { + switch (s_API) + { + case RendererAPI::API::OpenGL: return std::make_unique(); + } + return nullptr; + } +} \ No newline at end of file diff --git a/src/FlipScope/Renderer/RendererAPI.h b/src/FlipScope/Renderer/RendererAPI.h new file mode 100644 index 0000000..3026b17 --- /dev/null +++ b/src/FlipScope/Renderer/RendererAPI.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include + +namespace FSV +{ + class RendererAPI + { + public: + enum class API + { + None = 0, OpenGL = 1 + }; + + public: + virtual void Init() = 0; + virtual void SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) = 0; + virtual void SetClearColor(const glm::vec4& color) = 0; + + virtual void Clear() = 0; + + inline static API GetAPI() { return s_API; } + + static std::unique_ptr Create(); + + private: + static API s_API; + }; +} diff --git a/src/FlipScope/Renderer/Texture.cpp b/src/FlipScope/Renderer/Texture.cpp new file mode 100644 index 0000000..b1d0d57 --- /dev/null +++ b/src/FlipScope/Renderer/Texture.cpp @@ -0,0 +1,46 @@ +#include "pch.h" + +#include "FlipScope/Renderer/Texture.h" +#include "FlipScope/Renderer/Renderer.h" +#include "FlipScope/Core/Log.h" + +#include "Platform/OpenGL/OpenGLTexture.h" + +namespace FSV +{ + std::shared_ptr Texture::Create(uint32_t width, uint32_t height) + { + switch (Renderer::GetAPI()) + { + case RendererAPI::API::None: + { + FS_ERROR("RendererAPI::None is currently not supported!"); + return nullptr; + } + case RendererAPI::API::OpenGL: + return std::make_shared(width, height); + } + + FS_ERROR("Unknown RendererAPI"); + + return nullptr; + } + + std::shared_ptr Texture::Create(const std::string& path) + { + switch (Renderer::GetAPI()) + { + case RendererAPI::API::None: + { + FS_ERROR("RendererAPI::None is currently not supported!"); + return nullptr; + } + case RendererAPI::API::OpenGL: + return std::make_shared(path); + } + + FS_ERROR("Unknown RendererAPI"); + + return nullptr; + } +} \ No newline at end of file diff --git a/src/FlipScope/Renderer/Texture.h b/src/FlipScope/Renderer/Texture.h new file mode 100644 index 0000000..4b3ee9e --- /dev/null +++ b/src/FlipScope/Renderer/Texture.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace FSV +{ + class Texture + { + public: + ~Texture() = default; + + virtual uint32_t GetWidth() const = 0; + virtual uint32_t GetHeight() const = 0; + virtual uint32_t GetRendererID() const = 0; + + virtual void SetData(void* data, uint32_t size) const = 0; + + + virtual void Bind(uint32_t slot = 0) const = 0; + virtual bool operator==(const Texture& other) const = 0; + + static std::shared_ptr Create(uint32_t width, uint32_t height); + static std::shared_ptr Create(const std::string& path); + + }; +} diff --git a/src/FlipScope/Renderer/ViewportRenderer.cpp b/src/FlipScope/Renderer/ViewportRenderer.cpp new file mode 100644 index 0000000..2bdf3f7 --- /dev/null +++ b/src/FlipScope/Renderer/ViewportRenderer.cpp @@ -0,0 +1,25 @@ +#include "pch.h" + +#include "FlipScope/Renderer/ViewportRenderer.h" +#include "FlipScope/Renderer/Renderer.h" +#include "FlipScope/Core/Log.h" + +#include "Platform/OpenGL/OpenGLViewportRenderer.h" + +namespace FSV +{ + std::shared_ptr ViewportRenderer::Create() + { + switch (Renderer::GetAPI()) + { + case RendererAPI::API::None: + return nullptr; + case RendererAPI::API::OpenGL: + return std::make_shared(); + } + + FS_ERROR("Unknown RendererAPI"); + + return nullptr; + } +} \ No newline at end of file diff --git a/src/FlipScope/Renderer/ViewportRenderer.h b/src/FlipScope/Renderer/ViewportRenderer.h new file mode 100644 index 0000000..a19dba2 --- /dev/null +++ b/src/FlipScope/Renderer/ViewportRenderer.h @@ -0,0 +1,26 @@ +#pragma once +#include "FlipScope/Renderer/Renderer.h" + +namespace FSV +{ + class ViewportRenderer + { + public: + ViewportRenderer() = default; + ~ViewportRenderer() = default; + + static std::shared_ptr Create(); + + virtual void Init() = 0; + virtual void Shutdown() = 0; + + virtual uint32_t GetWidth() const = 0; + virtual uint32_t GetHeight() const = 0; + + virtual void OnWindowResize(uint32_t width, uint32_t height) = 0; + + virtual void OnRender() = 0; + + inline static RendererAPI::API GetAPI() { return RendererAPI::GetAPI(); } + }; +} diff --git a/src/FlipScope/Usd/FreeCamera.cpp b/src/FlipScope/Usd/FreeCamera.cpp new file mode 100644 index 0000000..773d64f --- /dev/null +++ b/src/FlipScope/Usd/FreeCamera.cpp @@ -0,0 +1,300 @@ +#include "pch.h" + +#include "FlipScope/Usd/FreeCamera.h" + + +namespace FSV +{ + FreeCamera::FreeCamera(CameraOptions option, bool isZUp, float fov=60.0) + : m_Options(option) + { + m_Camera = GfCamera(); + m_Camera.SetPerspectiveFromAspectRatioAndFieldOfView(1.0, fov, GfCamera::FOVVertical); + + ResetClipingPlanes(); + + m_IsZUp = isZUp; + m_CameraTransformDirty = true; + + m_RotTheta = 0; + m_RotPhi = 0; + m_RotPsi = 0; + m_Center = GfVec3d(0, 0, 0); + m_Dist = 100; + m_Camera.SetFocusDistance(m_Dist); + m_SelSize = 10; + + if (isZUp) + { + m_YZUpMatrix = GfMatrix4d().SetRotate(GfRotation(GfVec3d().XAxis(), -90)); + m_YZUpInvMatrix = m_YZUpMatrix.GetInverse(); + } + else + { + m_YZUpMatrix = GfMatrix4d(1.0); + m_YZUpInvMatrix = GfMatrix4d(1.0); + } + } + + FreeCamera::~FreeCamera() + { + } + + float FreeCamera::GetFov() + { + // The vertical field of view, in degrees, for perspective cameras. + // For orthographic cameras fov is the height of the view frustum, in + // world units. + // + if (m_Camera.GetProjection() == GfCamera::Projection::Perspective) + { + return m_Camera.GetFieldOfView(GfCamera::FOVVertical); + } + else + { + return m_Camera.GetVerticalAperture() * GfCamera::APERTURE_UNIT; + } + } + + void FreeCamera::SetFov(float value) + { + if (m_Camera.GetProjection() == GfCamera::Projection::Perspective) + { + m_Camera.SetPerspectiveFromAspectRatioAndFieldOfView( + m_Camera.GetAspectRatio(), value, GfCamera::FOVVertical); + } + else + { + m_Camera.SetOrthographicFromAspectRatioAndSize( + m_Camera.GetAspectRatio(), value, GfCamera::FOVVertical); + } + } + + float FreeCamera::GetNearClip() + { + return m_Camera.GetClippingRange().GetMin(); + } + + float FreeCamera::GetFarClip() + { + return m_Camera.GetClippingRange().GetMax(); + } + + void FreeCamera::ResetClipingPlanes() + { + double _near = m_OverrideNear || m_Options.DefaultNear; + double _far = m_OverrideFar || m_Options.DefaultFar; + m_Camera.SetClippingRange(GfRange1f(_near, _far)); + } + + void FreeCamera::FrameSelection(GfBBox3d selBBox, float frameFit) + { + m_ClosestVisibleDist = -1; + m_Center = selBBox.ComputeCentroid(); + GfRange3d selRange = selBBox.ComputeAlignedRange(); + GfVec3d selSize = selRange.GetSize(); + m_SelSize = std::max(std::max(selSize[0], selSize[1]), selSize[2]); + if (IsOrthographic()) + { + //m_Fov = m_SelSize * frameFit; + SetFov(m_SelSize * frameFit); + m_Dist = m_SelSize + m_Options.DefaultNear; + } + else + { + float fov = GetFov(); + float halfFov = fov * 0.5 || 0.5; + float lengthToFit = m_SelSize * frameFit * 0.5; + m_Dist = lengthToFit / atan(((halfFov * M_PI) / 180.0)); + + if (m_Dist < (m_Options.DefaultNear + m_SelSize * 0.5)) + { + m_Dist = m_Options.DefaultNear + lengthToFit; + } + } + + } + + void FreeCamera::SetClosestVisibleDistFromPoint(GfVec3d point) + { + GfFrustum frustum = m_Camera.GetFrustum(); + GfVec3d camPos = frustum.GetPosition(); + GfRay camRay = GfRay(camPos, frustum.ComputeViewDirection()); + m_ClosestVisibleDist = camRay.FindClosestPoint(point)[1]; + m_LastFramedDist = m_Dist; + m_LastFramedClosestDist = m_ClosestVisibleDist; + } + + float FreeCamera::ComputePixelsToWorldFactor(int viewportHeight) + { + PushToCameraTransform(); + if (IsOrthographic()) + { + return GetFov() / viewportHeight; + } + else + { + float frustumHeight = m_Camera.GetFrustum().GetWindow().GetSize()[1]; + return frustumHeight * m_Dist / viewportHeight; + } + } + + bool FreeCamera::IsOrthographic() + { + return m_Camera.GetProjection() == GfCamera::Projection::Orthographic; + } + + void FreeCamera::SetProjection(GfCamera::Projection projection) + { + m_Camera.SetProjection(projection); + } + + GfCamera::Projection FreeCamera::GetProjection() + { + return m_Camera.GetProjection(); + } + + void FreeCamera::SetClippingPlanes(GfBBox3d stageBBox) + { + //TfDebug::IsDebugSymbolNameEnabled(); + + double computedNear, computedFar = 0; + if (stageBBox.GetRange().IsEmpty() || (m_OverrideNear && m_OverrideFar)) + { + computedNear = m_Options.DefaultNear; + computedFar = m_Options.DefaultFar; + } + else + { + GfFrustum frustum = m_Camera.GetFrustum(); + GfVec3d camPos = frustum.GetPosition(); + GfRay camRay = GfRay(camPos, frustum.ComputeViewDirection()); + + RangeOfBoxAlongRay(computedNear, computedFar, camRay, stageBBox); + double precisionNear = computedFar / m_Options.MaxGoodZResolution; + + if (m_ClosestVisibleDist) + { + double halfClose = m_ClosestVisibleDist / 2.0; + if (m_ClosestVisibleDist < m_LastFramedClosestDist) + { + halfClose = std::max(std::max(precisionNear, halfClose), computedNear); + } + + if (halfClose < computedNear) + { + computedNear = halfClose; + } + else if (precisionNear > computedNear) + { + computedNear = std::min(((precisionNear + halfClose) / 2.0), halfClose); + } + + } + + } + + double _near = m_OverrideNear || computedNear; + double _far = m_OverrideFar || computedFar; + + _far = std::max(_near + 1, _far); + m_Camera.SetClippingRange(GfRange1f(_near, _far)); + + } + + GfCamera FreeCamera::ComputeGfCamera(GfBBox3d stageBBox, bool autoClip=true) + { + PushToCameraTransform(); + + if (autoClip) + SetClippingPlanes(stageBBox); + else + ResetClipingPlanes(); + + return m_Camera; + } + + GfMatrix4d FreeCamera::RotMatrix(GfVec3d &vec, double angle) + { + return GfMatrix4d(1.0).SetRotate(GfRotation(vec, angle)); + } + + void FreeCamera::RangeOfBoxAlongRay(double &minDist, double &maxDist, GfRay camRay, GfBBox3d stageBBox) + { + minDist = -1 * std::numeric_limits::min(); + maxDist = 1 * std::numeric_limits::max(); + + GfRange3d boxRange = stageBBox.GetRange(); + GfMatrix4d boxXform = stageBBox.GetMatrix(); + for (int i = 0; i < 8; i++) + { + GfVec3d point = boxXform.Transform(boxRange.GetCorner(i)); + double pointDist = camRay.FindClosestPoint(point)[1]; + if (pointDist > maxDist) + maxDist = pointDist; + if (pointDist < minDist) + minDist = pointDist; + } + if (minDist < m_Options.DefaultNear) + minDist = m_Options.DefaultNear; + else + minDist *= 0.99; + + maxDist *= 1.01; + } + + void FreeCamera::PushToCameraTransform() + { + // Updates the camera's transform matrix, that is, the matrix that brings + // the camera to the origin, with the camera view pointing down: + // +Y if this is a Zup camera, or + // -Z if this is a Yup camera . + + if (!m_CameraTransformDirty) + return; + + m_Camera.SetTransform(GfMatrix4d().SetTranslate(GfVec3d().ZAxis() * m_Dist) * + RotMatrix(GfVec3d().ZAxis(), -m_RotPsi) * + RotMatrix(GfVec3d().XAxis(), -m_RotPhi) * + RotMatrix(GfVec3d().YAxis(), -m_RotTheta) * + m_YZUpInvMatrix * + GfMatrix4d().SetTranslate(m_Center) + ); + + m_Camera.SetFocusDistance(m_Dist); + m_CameraTransformDirty = false; + } + + void FreeCamera::PullFromCameraTransform() + { + // Updates parameters (center, rotTheta, etc.) from the camera transform. + // + // reads the transform set on the camera and updates all the other + // parameters.This is the inverse of _pushToCameraTransform + + GfMatrix4d camTransfrom = m_Camera.GetTransform(); + float dist = m_Camera.GetFocusDistance(); + GfFrustum frustum = m_Camera.GetFrustum(); + GfVec3d camPos = frustum.GetPosition(); + GfVec3d camAxis = frustum.ComputeViewDirection(); + + // Compute translational parts + m_Dist = dist; + m_SelSize = dist / 10.0; + m_Center = camPos + dist * camAxis; + + // Compute rotational part + GfMatrix4d transform = camTransfrom * m_YZUpMatrix; + transform.Orthonormalize(); + GfRotation rotation = transform.ExtractRotation(); + + // Decompose and set angles + GfVec3d _decompose = -rotation.Decompose(GfVec3d().YAxis(), GfVec3d().XAxis(), GfVec3d().ZAxis()); + m_RotTheta = _decompose[0]; + m_RotPhi = _decompose[1]; + m_RotPsi = _decompose[2]; + + m_CameraTransformDirty = true; + } + +} \ No newline at end of file diff --git a/src/FlipScope/Usd/FreeCamera.h b/src/FlipScope/Usd/FreeCamera.h new file mode 100644 index 0000000..86087eb --- /dev/null +++ b/src/FlipScope/Usd/FreeCamera.h @@ -0,0 +1,82 @@ +#pragma once + +#include "pxr/pxr.h" +#include "pxr/base/gf/camera.h" +#include "pxr/base/gf/bbox3d.h" +#include "pxr/base/gf/frustum.h" +#include "pxr/base/tf/debug.h" + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace FSV +{ + struct CameraOptions + { + float DefaultNear = 1; + float DefaultFar = 2000000; + float MaxSafeZResolution = 1e6; + float MaxGoodZResolution = 5e4; + }; + + class FreeCamera + { + public: + + FreeCamera(CameraOptions option, bool isZUp, float fov); + ~FreeCamera(); + CameraOptions GetOption() { return m_Options; } + + GfCamera ComputeGfCamera(GfBBox3d stageBBox, bool autoClip); + void SetClippingPlanes(GfBBox3d stageBBox); + void ResetClipingPlanes(); + + void FrameSelection(GfBBox3d selBBox, float frameFit); + void SetClosestVisibleDistFromPoint(GfVec3d point); + + float ComputePixelsToWorldFactor(int viewportHeight); + + bool IsOrthographic(); + void SetProjection(GfCamera::Projection projection); + GfCamera::Projection GetProjection(); + + GfMatrix4d RotMatrix(GfVec3d &vec, double angle); + bool IsZUp() { return m_IsZUp; } + + float GetFov(); + void SetFov(float value); + + float GetNearClip(); + float GetFarClip(); + + private: + + void RangeOfBoxAlongRay(double &minDist, double &maxDist, GfRay camRay, GfBBox3d stageBBox); + void PushToCameraTransform(); + void PullFromCameraTransform(); + + CameraOptions m_Options; + GfCamera m_Camera; + + bool m_IsZUp; + float m_RotTheta; + float m_RotPhi; + float m_RotPsi; + + GfVec3d m_Center; + GfMatrix4d m_YZUpMatrix; + GfMatrix4d m_YZUpInvMatrix; + + float m_Dist; + float m_OverrideNear = -1; + float m_OverrideFar = -1; + + float m_ClosestVisibleDist = -1; + float m_LastFramedDist = -1; + float m_LastFramedClosestDist = -1; + + float m_Fov; + float m_SelSize; + + bool m_CameraTransformDirty = false; + }; +} diff --git a/src/FlipScope/Usd/HdRenderer.cpp b/src/FlipScope/Usd/HdRenderer.cpp new file mode 100644 index 0000000..1730571 --- /dev/null +++ b/src/FlipScope/Usd/HdRenderer.cpp @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/src/FlipScope/Usd/HdRenderer.h b/src/FlipScope/Usd/HdRenderer.h new file mode 100644 index 0000000..ff6cb72 --- /dev/null +++ b/src/FlipScope/Usd/HdRenderer.h @@ -0,0 +1,61 @@ +#pragma once + +#include "pxr/pxr.h" +#include "pxr/usd/usd/stage.h" +#include "pxr/imaging/glf/drawTarget.h" +#include "pxr/imaging/hd/engine.h" +#include "pxr/imaging/hd/renderIndex.h" +#include "pxr/imaging/hd/rendererPlugin.h" +#include "pxr/imaging/hd/rprimCollection.h" +#include "pxr/imaging/hdSt/renderDelegate.h" +#include "pxr/imaging/hdx/taskController.h" + +#include "pxr/usdImaging/usdImagingGL/engine.h" + + +PXR_NAMESPACE_USING_DIRECTIVE + +using HgiUniquePtr = std::unique_ptr; + +namespace FSV +{ + class HdRenderer + { + public: + HdRenderer(uint32_t width, uint32_t height); + ~HdRenderer(); + private: + uint32_t _width, _height; + + GlfDrawTargetRefPtr _drawTarget; + + HgiUniquePtr _hgi; + HdEngine _engine; + HdRendererPlugin* _renderPlugin = nullptr; + HdxTaskController* _taskController = nullptr; + HdRenderIndex* _renderIndex = nullptr; + HdxSelectionTrackerSharedPtr _selectionTracker; + HdRprimCollection _renderCollection + { + HdTokens->geometry, + HdReprSelector(HdReprTokens->refined), + SdfPath::AbsoluteRootPath() + }; + HdRprimCollection _selectionCollection{ HdReprTokens->wire, + HdReprSelector(HdReprTokens->wire) }; + HdRprimCollection _pointSnappingCollection{ + HdTokens->geometry, + HdReprSelector(HdReprTokens->refined, TfToken(), HdReprTokens->points), + SdfPath::AbsoluteRootPath() + }; + + GlfSimpleLight _defaultLight; + GfVec4d _viewport; + + const bool _isUsingHdSt = false; + bool _initializationAttempted = false; + bool _initializationSucceeded = false; + bool _hasDefaultLighting = false; + bool _selectionChanged = true; + }; +} \ No newline at end of file diff --git a/src/FlipScope/Usd/HydraRenderer.cpp b/src/FlipScope/Usd/HydraRenderer.cpp new file mode 100644 index 0000000..455cb82 --- /dev/null +++ b/src/FlipScope/Usd/HydraRenderer.cpp @@ -0,0 +1,488 @@ +#include "pch.h" + +#include +//#include +#include + +#include "FlipScope/Core/Application.h" +#include "FlipScope/Usd/HydraRenderer.h" +#include "FlipScope/Core/Log.h" + +#include "pxr/pxr.h" +#include "pxr/usd/usd/prim.h" +#include "pxr/usd/usdGeom/metrics.h" +#include "pxr/usd/usdGeom/bboxCache.h" +#include "pxr/usd/usdGeom/tokens.h" +#include "pxr/imaging/glf/contextCaps.h" +#include "pxr/imaging/glf/simpleLightingContext.h" + + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace FSV +{ + HydraRenderer::HydraRenderer(uint32_t width, uint32_t height) + :m_Width(width), m_Height(height) + { + m_Translate[0] = 0.0; + m_Translate[1] = 1000.0; + m_Translate[2] = 2500.0; + + } + + HydraRenderer::~HydraRenderer() + { + + } + + void HydraRenderer::Init(uint32_t width, uint32_t height) + { + GlfContextCaps::InitInstance(); + + // Build render target + m_DrawTarget = GlfDrawTarget::New(GfVec2i(GetWidth(), GetHeight())); + + m_DrawTarget->Bind(); + + m_DrawTarget->AddAttachment("color", GL_RGBA, GL_FLOAT, GL_RGBA); + m_DrawTarget->AddAttachment("depth", GL_DEPTH_COMPONENT, GL_FLOAT, GL_DEPTH_COMPONENT32F); + + m_Width = width; + m_Height = height; + + SetupRenderer(true); + + m_DrawTarget->Unbind(); + } + + void HydraRenderer::SetupRenderer(bool IsInit=false) + { + FS_INFO("SetupRenderer() : Setp UsdImagingGLEngine."); + + AppController& controller = Application::GetController(); + m_Stage = controller.GetStageDataModel()->GetStage(); + + //m_Stage = UsdStage::Open("C:/TEMP/Kitchen_set/Kitchen_set.usd"); + //m_Stage = UsdStage::CreateInMemory(""); + + SdfPathVector excludedPaths; + + if (UsdImagingGLEngine::IsHydraEnabled()) { + + FS_INFO("Using HD Renderer"); + m_Renderer.reset(new UsdImagingGLEngine(m_Stage->GetPseudoRoot().GetPath(), excludedPaths)); + if (!m_CurrentRenderer.empty()) + { + if (!m_Renderer->SetRendererPlugin(TfToken(m_CurrentRenderer))) + { + FS_ERROR("Couldn't set renderer plugin: {0}", m_CurrentRenderer); + } + else { + FS_INFO("Renderer plugin: {0}", m_CurrentRenderer); + } + } + } + else { + FS_INFO("Using Reference Renderer"); + m_Renderer.reset(new UsdImagingGLEngine(m_Stage->GetPseudoRoot().GetPath(), excludedPaths)); + } + + if (controller.IsStageChanged() || IsInit) + { + m_ShouldFrameAll = true; + + if (m_ShouldFrameAll) + { + TfTokenVector purposes; + purposes.push_back(UsdGeomTokens->default_); + purposes.push_back(UsdGeomTokens->proxy); + + bool useExtentHints = true; + UsdGeomBBoxCache bboxCache(UsdTimeCode::Default(), purposes, useExtentHints); + + GfBBox3d bbox = bboxCache.ComputeWorldBound(m_Stage->GetPseudoRoot()); + GfRange3d world = bbox.ComputeAlignedRange(); + + GfVec3d worldCenter = (world.GetMin() + world.GetMax()) / 2.0; + double worldSize = world.GetSize().GetLength(); + + if (UsdGeomGetStageUpAxis(m_Stage) == UsdGeomTokens->z) + { + m_Translate[0] = -worldCenter[0]; + m_Translate[1] = -worldCenter[2]; + m_Translate[2] = -worldCenter[1] - worldSize; + } + else { + m_Translate[0] = -worldCenter[0]; + m_Translate[1] = -worldCenter[1]; + m_Translate[2] = -worldCenter[2] - worldSize; + } + } + } + + if (IsEnabledLighting()) + { + if (UsdImagingGLEngine::IsHydraEnabled()) { + m_LightingContext = GlfSimpleLightingContext::New(); + GlfSimpleLight light; + + if (IsEnabledCameraLight()) { + + light.SetPosition(GfVec4f(m_Translate[0], m_Translate[2], m_Translate[1], 0)); + } + else + { + light.SetPosition(GfVec4f(0, -.5, .5, 0)); + } + light.SetDiffuse(GfVec4f(1, 1, 1, 1)); + light.SetAmbient(GfVec4f(0, 0, 0, 1)); + light.SetSpecular(GfVec4f(1, 1, 1, 1)); + GlfSimpleLightVector lights; + lights.push_back(light); + m_LightingContext->SetLights(lights); + + GlfSimpleMaterial material; + material.SetAmbient(GfVec4f(0.2, 0.2, 0.2, 1.0)); + material.SetDiffuse(GfVec4f(0.8, 0.8, 0.8, 1.0)); + material.SetSpecular(GfVec4f(0, 0, 0, 1)); + material.SetShininess(0.0001f); + m_LightingContext->SetMaterial(material); + m_LightingContext->SetSceneAmbient(GfVec4f(0.2, 0.2, 0.2, 1.0)); + + } + else { + + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + + if (IsEnabledCameraLight()) + { + float position[4] = { m_Translate[0], m_Translate[2], m_Translate[1], 0 }; + glLightfv(GL_LIGHT0, GL_POSITION, position); + + } + else + { + float position[4] = { 0,-.5,.5,0 }; + glLightfv(GL_LIGHT0, GL_POSITION, position); + } + } + } + + } + + uint32_t HydraRenderer::GetRendererID() + { + return m_DrawTarget->GetAttachment("color")->GetGlTextureName(); + } + + void HydraRenderer::Shutdown() + { + m_DrawTarget = GlfDrawTargetRefPtr(); + FS_INFO("Shutdown HydraEngine"); + m_Renderer->InvalidateBuffers(); + } + + void HydraRenderer::OnWindowResize(uint32_t width, uint32_t height) + { + if (width <= 0 || height <= 0) + return; + + m_Width = width; + m_Height = height; + //FS_INFO("OnWindowResize :: {}, {}", width, height); + OnRender(); + } + + void HydraRenderer::OnRender() + { + int width = GetWidth(); + int height = GetHeight(); + + //FS_INFO("OnRender :: {}, {}", width, height); + if (width <= 0 || height <= 0) + return; + + m_DrawTarget->Bind(); + m_DrawTarget->SetSize(GfVec2i(width, height)); + + // Draw Code Here + OnDraw(); + + m_DrawTarget->Unbind(); + + /* + //Blit color framebuffer to screen + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, m_DrawTarget->GetFramebufferId()); + + glBlitFramebuffer(0, 0, width, height, + 0, 0, width, height, + GL_COLOR_BUFFER_BIT, + GL_NEAREST); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + */ + } + + void HydraRenderer::OnDraw() + { + int width = GetWidth(); + int height = GetHeight(); + //m_DrawTarget->SetSize(GfVec2i(width, height)); + //FS_INFO("HydraRenderer::Draw : {0}, {1}", width, height); + //m_DrawTarget->Bind(); + + + GfVec4d viewport(0, 0, width, height); + + if (GetCameraPath().empty()) + { + //FS_INFO("CameraPath Empty"); + + GfMatrix4d viewMatrix(1.0); + viewMatrix *= GfMatrix4d().SetRotate(GfRotation(GfVec3d(0, 1, 0), m_Rotate[0])); + viewMatrix *= GfMatrix4d().SetRotate(GfRotation(GfVec3d(1, 0, 0), m_Rotate[1])); + viewMatrix *= GfMatrix4d().SetTranslate(GfVec3d(m_Translate[0], m_Translate[1], m_Translate[2])); + + //FS_INFO("Camera Rotate [{0}, {1}]", m_Rotate[0], m_Rotate[1]); + //FS_INFO("Camera Translate [{0}, {1}, {2}]", m_Translate[0], m_Translate[1], m_Translate[2]); + + GfMatrix4d modelViewMatrix = viewMatrix; + if (UsdGeomGetStageUpAxis(m_Stage) == UsdGeomTokens->z) { + // rotate from z-up to y-up + modelViewMatrix = + GfMatrix4d().SetRotate(GfRotation(GfVec3d(1.0, 0.0, 0.0), -90.0)) * + modelViewMatrix; + } + + const double aspectRatio = double(width) / height; + GfFrustum frustum; + frustum.SetPerspective(60.0, aspectRatio, 1, 100000.0); + const GfMatrix4d projMatrix = frustum.ComputeProjectionMatrix(); + + m_Renderer->SetCameraState(modelViewMatrix, projMatrix); + + } else { + m_Renderer->SetCameraPath(SdfPath(GetCameraPath())); + } + + GfVec4f const &clearColor = GfVec4f(0.12f, 0.12f, 0.12f, 1.0f); + + m_Renderer->SetRenderViewport(viewport); + + m_RenderParams = UsdImagingGLRenderParams(); + m_RenderParams.enableLighting = true; + m_RenderParams.showGuides = true; + m_RenderParams.showRender = false; + m_RenderParams.showProxy = true; + m_RenderParams.clearColor = clearColor; + m_RenderParams.forceRefresh = true; + m_RenderParams.drawMode = UsdImagingGLDrawMode::DRAW_SHADED_SMOOTH; + //m_RenderParams.clipPlanes = + glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + + if (GetCurrentRendererName() == "GL") + { + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glViewport(0, 0, width, height); + + m_Renderer->SetRendererAov(TfToken("color")); + + if (IsEnabledLighting()) + { + if (UsdImagingGLEngine::IsHydraEnabled()) + { + m_Renderer->SetLightingState(m_LightingContext); + } else { + m_Renderer->SetLightingStateFromOpenGL(); + } + } + /* + bool const clearOnlyOnce = true; + bool cleared = false; + int convergenceIterations = 0; + GLfloat clearDepth[1] = { 1.0f }; + + do { + convergenceIterations++; + + if (cleared && clearOnlyOnce) + { + // Don't clear the FBO + } else { + glClearBufferfv(GL_COLOR, 0, clearColor.data()); + glClearBufferfv(GL_DEPTH, 0, clearDepth); + cleared = true; + } + + m_Engine->Render(m_Stage->GetPseudoRoot(), m_RenderParams); + //m_Engine->InvalidateBuffers(); + + } while (!m_Engine->IsConverged());*/ + + m_Renderer->Render(m_Stage->GetPseudoRoot(), m_RenderParams); + + //glFinish(); + //m_DrawTarget->Unbind(); + //FS_INFO("Iterations to convergence: {}", convergenceIterations); + //glDisable(GL_DEPTH_TEST); + } + + void DrawAxis() + { + + } + + TfTokenVector HydraRenderer::GetRendererPlugins() + { + TfTokenVector rendererPlugins = m_Renderer->GetRendererPlugins(); + return rendererPlugins; + } + + std::vector HydraRenderer::GetRendererPluginsNames() + { + std::vector rendererPluginNames; + for (auto &plugin : GetRendererPlugins()) + { + rendererPluginNames.push_back(GetRenderer()->GetRendererDisplayName(plugin)); + } + return rendererPluginNames; + } + + TfToken HydraRenderer::GetCurrentRenderer() + { + return GetRenderer()->GetCurrentRendererId(); + } + + std::string HydraRenderer::GetCurrentRendererName() + { + //FS_INFO("GetCurrentRendererName : {}", GetCurrentRenderer().GetText()); + return GetRenderer()->GetRendererDisplayName(GetCurrentRenderer()); + } + + void HydraRenderer::SetCurrentRenderer(std::string rendererName) + { + TfToken rendererId; + for (auto &plugin : GetRendererPlugins()) + { + if (GetRenderer()->GetRendererDisplayName(plugin) == rendererName) + { + rendererId = plugin; + break; + } + } + + if (rendererId.IsEmpty()) + { + FS_WARN("Renderer name : \"{}\" not in register renderer plugin list, skip...", rendererName); + return; + } + m_CurrentRenderer = rendererId; + SetupRenderer(); + //GetEngine()->SetRendererPlugin(rendererId); + //GetEngine()->SetRendererAov(TfToken("color")); + } + + void HydraRenderer::SetCurrentRenderer(TfToken rendererId) + { + TfTokenVector rendererPlugins = GetRendererPlugins(); + auto it = std::find(rendererPlugins.begin(), rendererPlugins.end(), rendererId); + if (it == rendererPlugins.end()) + { + FS_WARN("Renderer plugin : \"{}\" not in register renderer plugin list, skip...", rendererId.GetString()); + return; + } + GetRenderer()->SetRendererPlugin(rendererId); + } + + bool HydraRenderer::WriteToFile(std::string const & attachment, std::string const & filename) + { + if (m_DrawTarget->IsBound()) + m_DrawTarget->Unbind(); + + bool result = m_DrawTarget->WriteToFile(attachment, filename); + + if (m_DrawTarget->IsBound()) + m_DrawTarget->Bind(); + + FS_INFO("Write {0} attachment to {1}", attachment, filename); + return result; + } + + void HydraRenderer::OnMousePress(bool button[], int x, int y, const int& modKeys) + { + FS_INFO("Mouse Press ({}, {}, {}) : {} : {}", button[0], button[1], button[2], x, y); + + m_MouseButton[0] = button[0]; + m_MouseButton[1] = button[1]; + m_MouseButton[2] = button[2]; + + if (modKeys & ImGuiKeyModFlags_Alt) + { + m_MousePos[0] = x; + m_MousePos[1] = y; + } + } + + void HydraRenderer::OnMouseRelease(bool button[], int x, int y, const int& modKeys) + { + FS_INFO("Mouse Release ({}, {}, {}) : {} : {}", button[0], button[1], button[2], x, y); + + m_MouseButton[0] = button[0]; + m_MouseButton[1] = button[1]; + m_MouseButton[2] = button[2]; + } + + void HydraRenderer::OnMouseMove(bool button[], int x, int y, const int& modKeys) + { + FS_INFO("Mouse Move Buttons : {0}, {1}, {2}", m_MouseButton[0], m_MouseButton[1], m_MouseButton[2]); + + int dx = x - m_MousePos[0]; + int dy = y - m_MousePos[1]; + + if (modKeys & ImGuiKeyModFlags_Alt) + { + if (m_MouseButton[0]) { + m_Rotate[0] += dx; + m_Rotate[1] += dy; + } + else if (m_MouseButton[2]) { + m_Translate[0] += dx; + m_Translate[1] -= dy; + } + else if (m_MouseButton[1]) { + m_Translate[2] += dx; + } + + m_MousePos[0] = x; + m_MousePos[1] = y; + } + } + + void HydraRenderer::ResolveCamera() + { + } + + void HydraRenderer::ComputePickFrustum(int x, int y) + { + } + + void HydraRenderer::PickObject(int x, int y, bool button[], const int & modKeys) + { + } + + void HydraRenderer::UpdateSelection() + { + + } +} \ No newline at end of file diff --git a/src/FlipScope/Usd/HydraRenderer.h b/src/FlipScope/Usd/HydraRenderer.h new file mode 100644 index 0000000..426c266 --- /dev/null +++ b/src/FlipScope/Usd/HydraRenderer.h @@ -0,0 +1,96 @@ +#pragma once + +#include "FlipScope/Core/AppController.h" +#include "FlipScope/Renderer/ViewportRenderer.h" + +#include "pxr/pxr.h" +#include "pxr/usd/usd/stage.h" +#include "pxr/imaging/glf/drawTarget.h" +#include "pxr/usdImaging/usdImagingGL/engine.h" + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace FSV +{ + class HydraRenderer + { + public: + HydraRenderer(uint32_t width, uint32_t height); + ~HydraRenderer(); + + virtual void Init(uint32_t width, uint32_t height); + virtual void SetupRenderer(bool IsInit); + virtual void Shutdown(); + + virtual uint32_t GetWidth() { return m_Width; } + virtual uint32_t GetHeight() { return m_Height; } + + UsdStageRefPtr GetStage() { return m_Stage; } + std::shared_ptr GetRenderer() { return m_Renderer; } + + virtual void OnWindowResize(uint32_t width, uint32_t height); + + virtual void OnRender(); + virtual void OnDraw(); + + void DrawAxis(); + + std::string GetCameraPath() { return m_CameraPath; } + + TfTokenVector GetRendererPlugins(); + std::vector GetRendererPluginsNames(); + TfToken GetCurrentRenderer(); + std::string GetCurrentRendererName(); + + void SetCurrentRenderer(std::string rendererName); + void SetCurrentRenderer(TfToken renderer); + + // Lighting Settings + bool IsEnabledLighting() { return m_EnabledLighting; } + void SetEnabledLighting(bool enabled) { m_EnabledLighting = enabled; } + bool IsEnabledCameraLight() { return m_EnabledCameraLight; } + void SetEnabledCameraLight(bool enabled) { m_EnabledCameraLight = enabled; } + + uint32_t GetRendererID(); + + bool WriteToFile(std::string const & attachment, std::string const & filename); + + void OnMousePress(bool button[], int x, int y, const int& modKeys); + void OnMouseRelease(bool button[], int x, int y, const int& modKeys); + void OnMouseMove(bool button[], int x, int y, const int& modKeys); + + void ResolveCamera(); + void ComputePickFrustum(int x, int y); + void PickObject(int x, int y, bool button[], const int& modKeys); + void UpdateSelection(); + + private: + uint32_t m_Width, m_Height = 0; + AppController *m_Controller; + + UsdStageRefPtr m_Stage; + + std::string m_CurrentRenderer; + std::shared_ptr m_Renderer; + + GlfDrawTargetRefPtr m_DrawTarget; + + UsdImagingGLRenderParams m_RenderParams; + + bool m_ShouldFrameAll = false; + + GlfSimpleLightingContextRefPtr m_LightingContext; + bool m_EnabledLighting = true; + bool m_EnabledCameraLight = false; + + // For Camera manipulate transform + std::string m_CameraPath; + + float m_Translate[3]; + float m_Rotate[2]; + + int m_MousePos[2]; + bool m_MouseButton[3]; + + }; +} \ No newline at end of file diff --git a/src/FlipScope/Usd/SelectionModel.cpp b/src/FlipScope/Usd/SelectionModel.cpp new file mode 100644 index 0000000..618217b --- /dev/null +++ b/src/FlipScope/Usd/SelectionModel.cpp @@ -0,0 +1,57 @@ +#include "pch.h" + +#include "FlipScope/Core/Log.h" +#include "FlipScope/Usd/SelectionModel.h" + +#include + +#include +#include + +FSV::SelectionModel::SelectionModel() +{ +} + +FSV::SelectionModel::~SelectionModel() +{ +} + +bool FSV::SelectionModel::IsSelected(UsdPrim& prim) +{ + if(HasPrim(prim)) + return true; + return false; +} + +void FSV::SelectionModel::AddSelection(UsdPrim& prim) +{ + Clear(); + //m_SelectionData.push_back(prim.GetPrimPath().GetString()); + AppendSelection(prim); + //FS_INFO("Add to Selection : {}, HasPrim : {}", prim.GetPrimPath().GetString(), HasPrim(prim)); +} + +void FSV::SelectionModel::AppendSelection(UsdPrim & prim) +{ + if (IsSelected(prim)) + return; + m_SelectionData.push_back(prim.GetPrimPath().GetString()); + m_SelectionPrimData.push_back(prim); + //FS_INFO("Append to Selection : {}", prim.GetPrimPath().GetString()); +} + +void FSV::SelectionModel::Clear() +{ + m_SelectionData.clear(); + m_SelectionPrimData.clear(); +} + +bool FSV::SelectionModel::HasPrim(UsdPrim & prim) +{ + return std::find(m_SelectionData.begin(), m_SelectionData.end(), prim.GetPrimPath().GetString()) != m_SelectionData.end(); +} + +UsdPrim& FSV::SelectionModel::CurrentSelection() +{ + return UsdPrim(); +} \ No newline at end of file diff --git a/src/FlipScope/Usd/SelectionModel.h b/src/FlipScope/Usd/SelectionModel.h new file mode 100644 index 0000000..a284051 --- /dev/null +++ b/src/FlipScope/Usd/SelectionModel.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace FSV +{ + typedef std::vector SelectionData; + typedef std::vector SelectionPrimData; + class SelectionModel + { + public: + SelectionModel(); + ~SelectionModel(); + + bool IsSelected(UsdPrim& prim); + + void AddSelection(UsdPrim& prim); + void AppendSelection(UsdPrim& prim); + SelectionData GetSelection() { return m_SelectionData; }; + UsdPrim& CurrentSelection(); + + void Clear(); + + private: + bool HasPrim(UsdPrim& prim); + + SelectionData m_SelectionData; + SelectionPrimData m_SelectionPrimData; + }; +} \ No newline at end of file diff --git a/src/FlipScope/Usd/StageDataModel.cpp b/src/FlipScope/Usd/StageDataModel.cpp new file mode 100644 index 0000000..1e7a570 --- /dev/null +++ b/src/FlipScope/Usd/StageDataModel.cpp @@ -0,0 +1,105 @@ +#include "pch.h" + +#include "FlipScope/Usd/StageDataModel.h" + +#include "pxr/pxr.h" +#include "pxr/usd/usdGeom/tokens.h" +#include "pxr/usd/usd/notice.h" + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace FSV +{ + StageDataModel::StageDataModel() + { + TfTokenVector includedPurposes; + includedPurposes.push_back(UsdGeomTokens->default_); + includedPurposes.push_back(UsdGeomTokens->proxy); + m_BboxCache = std::make_shared(GetCurrentFrame(), includedPurposes); + + m_XformCache = std::make_shared(); + //m_Stage = UsdStage::CreateNew("C:/Temp/Test.usd"); + //m_Stage = UsdStage::Open("C:/TEMP/Kitchen_set/Kitchen_set.usd"); + //SetStage(UsdStage::Open("C:/TEMP/Kitchen_set/Kitchen_set.usd")); + } + + StageDataModel::~StageDataModel() + { + } + + void StageDataModel::SetStage(UsdStageRefPtr stage) + { + m_StageNoticeListener.SetStage(UsdStageWeakPtr()); + m_StageNoticeListener.SetStageContentsChangedCallback(nullptr); + m_StageNoticeListener.SetStageObjectsChangedCallback(nullptr); + + m_Stage = stage; + + m_StageNoticeListener.SetStage(stage); + m_StageNoticeListener.SetStageContentsChangedCallback( + std::bind(&StageDataModel::OnStageContentChanged, + this, + std::placeholders::_1)); + + m_StageNoticeListener.SetStageObjectsChangedCallback( + std::bind(&StageDataModel::OnStageObjectChanged, + this, + std::placeholders::_1)); + } + + void StageDataModel::GetStageCamera() + { + } + + void StageDataModel::SetCurrentFrame(double value) + { + m_CurrentFrame = UsdTimeCode(value); + m_BboxCache->SetTime(m_CurrentFrame); + m_XformCache->SetTime(m_CurrentFrame); + } + + TfTokenVector StageDataModel::GetIncludedPurposes() + { + return m_BboxCache->GetIncludedPurposes(); + } + + void StageDataModel::SetIncludedPurposes(TfTokenVector includedPurposes) + { + m_BboxCache->SetIncludedPurposes(includedPurposes); + } + + GfBBox3d StageDataModel::ComputeWorldBound(const UsdPrim & prim) + { + return m_BboxCache->ComputeWorldBound(prim); + } + + GfMatrix4d StageDataModel::GetLocalToWorldTransform(const UsdPrim & prim) + { + return m_XformCache->GetLocalToWorldTransform(prim); + } + + UsdShadeMaterial StageDataModel::ComputeBoundMaterial(const UsdPrim & prim) + { + return UsdShadeMaterialBindingAPI(prim).ComputeBoundMaterial(); + } + + void StageDataModel::OnStageContentChanged(const UsdNotice::StageContentsChanged & notice) + { + } + + void StageDataModel::OnStageObjectChanged(const UsdNotice::ObjectsChanged & notice) + { + /*for (auto i : notice.GetResyncedPaths()) { + }*/ + } + + void StageDataModel::ClearCaches() + { + m_BboxCache->Clear(); + m_XformCache->Clear(); + } + std::unique_ptr StageDataModel::Create() + { + return std::make_unique(); + } +} \ No newline at end of file diff --git a/src/FlipScope/Usd/StageDataModel.h b/src/FlipScope/Usd/StageDataModel.h new file mode 100644 index 0000000..e3bb843 --- /dev/null +++ b/src/FlipScope/Usd/StageDataModel.h @@ -0,0 +1,72 @@ +#pragma once + +#include "pch.h" + +#include "pxr/pxr.h" +#include "pxr/usd/usd/timecode.h" +#include "pxr/usd/usd/stage.h" +#include "pxr/usd/usdGeom/bboxCache.h" +#include "pxr/usd/usdGeom/xformCache.h" +#include "pxr/base/tf/notice.h" +#include "pxr/usd/usdShade/materialBindingAPI.h" + +#include "FlipScope/Usd/StageNoticeListener.h" +#include "FlipScope/Usd/SelectionModel.h" + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace FSV +{ + class StageDataModel + { + public: + StageDataModel(); + ~StageDataModel(); + + UsdStageRefPtr GetStage() { return m_Stage; }; + void SetStage(UsdStageRefPtr stage); + + std::string GetStageFilePath() { return m_StageFilePath; } + void SetStageFilePath(std::string filePath) { m_StageFilePath = filePath; } + + void GetStageCamera(); + + UsdTimeCode GetCurrentFrame() { return m_CurrentFrame; }; + void SetCurrentFrame(double value); + + TfTokenVector GetIncludedPurposes(); + void SetIncludedPurposes(TfTokenVector includedPurposes); + //void OnPrimChanged(UsdNotice::ObjectsChanged ¬ice, UsdStageWeakPtr &stage); + + GfBBox3d ComputeWorldBound(const UsdPrim& prim) ; + GfMatrix4d GetLocalToWorldTransform(const UsdPrim& prim); + UsdShadeMaterial ComputeBoundMaterial(const UsdPrim& prim); + + void OnStageContentChanged(const UsdNotice::StageContentsChanged& notice); + void OnStageObjectChanged(const UsdNotice::ObjectsChanged& notice); + + bool IsPlaying() { return m_Playing; }; + void SetPlaying(bool playing) { m_Playing = playing; }; + + void ClearCaches(); + + static std::unique_ptr Create(); + + SelectionModel& GetSelectionModel() { return m_SelectionModel; } + + private: + UsdTimeCode m_CurrentFrame = UsdTimeCode::Default(); + UsdStageRefPtr m_Stage; + + std::string m_StageFilePath; + + std::shared_ptr m_BboxCache; + std::shared_ptr m_XformCache; + + StageNoticeListener m_StageNoticeListener; + + SelectionModel m_SelectionModel; + + bool m_Playing; + }; +} diff --git a/src/FlipScope/Usd/StageNoticeListener.cpp b/src/FlipScope/Usd/StageNoticeListener.cpp new file mode 100644 index 0000000..f6a5973 --- /dev/null +++ b/src/FlipScope/Usd/StageNoticeListener.cpp @@ -0,0 +1,90 @@ +#include "pch.h" + +#include "FlipScope/Usd/StageNoticeListener.h" + +#include "pxr/pxr.h" +#include "pxr/base/tf/notice.h" +#include "pxr/base/tf/weakBase.h" +#include "pxr/usd/usd/notice.h" +#include "pxr/usd/usd/stage.h" + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace FSV +{ + StageNoticeListener::~StageNoticeListener() + { + if (m_StageContentsChangedKey.IsValid()) { + TfNotice::Revoke(m_StageContentsChangedKey); + } + if (m_StageObjectsChangedKey.IsValid()) { + TfNotice::Revoke(m_StageObjectsChangedKey); + } + + } + + void StageNoticeListener::SetStage(const UsdStageWeakPtr & stage) + { + m_Stage = stage; + UpdateStageContentsChangedRegistration(); + } + + void StageNoticeListener::SetStageContentsChangedCallback(const StageContentsChangedCallback & callback) + { + m_StageContentsChangedCallback = callback; + UpdateStageContentsChangedRegistration(); + } + + void StageNoticeListener::SetStageObjectsChangedCallback(const StageObjectsChangedCallback & callback) + { + m_StageObjectsChangedCallback = callback; + } + + void StageNoticeListener::UpdateStageContentsChangedRegistration() + { + if (m_Stage && m_StageContentsChangedCallback) { + // Register for notices if we're not already listening. + if (!m_StageContentsChangedKey.IsValid()) { + m_StageContentsChangedKey = TfNotice::Register( TfCreateWeakPtr(this), &StageNoticeListener::OnStageContentsChanged); + } + + } + else { + // Either the stage or the callback is invalid, so stop listening for + // notices. + if (m_StageContentsChangedKey.IsValid()) { + TfNotice::Revoke(m_StageContentsChangedKey); + } + } + + if (m_Stage && m_StageObjectsChangedCallback) { + + // Register for notices if we're not already listening. + if (!m_StageObjectsChangedKey.IsValid()) { + m_StageObjectsChangedKey = + TfNotice::Register(TfCreateWeakPtr(this), &StageNoticeListener::OnStageObjectsChanged, m_Stage); + } + } + else { + if (m_StageObjectsChangedKey.IsValid()) { + TfNotice::Revoke(m_StageObjectsChangedKey); + } + } + } + + void StageNoticeListener::OnStageContentsChanged(const UsdNotice::StageContentsChanged & notice) const + { + if (notice.GetStage() == m_Stage && m_StageContentsChangedCallback) + { + m_StageContentsChangedCallback(notice); + } + } + + void StageNoticeListener::OnStageObjectsChanged(const UsdNotice::ObjectsChanged & notice, const UsdStageWeakPtr & sender) const + { + if (notice.GetStage() == m_Stage && m_StageObjectsChangedCallback) + { + m_StageObjectsChangedCallback(notice); + } + } +} \ No newline at end of file diff --git a/src/FlipScope/Usd/StageNoticeListener.h b/src/FlipScope/Usd/StageNoticeListener.h new file mode 100644 index 0000000..ee4f782 --- /dev/null +++ b/src/FlipScope/Usd/StageNoticeListener.h @@ -0,0 +1,45 @@ +#pragma once + +#include "pch.h" + +#include "pxr/pxr.h" +#include "pxr/base/tf/notice.h" +#include "pxr/base/tf/weakBase.h" +#include "pxr/usd/usd/notice.h" +#include "pxr/usd/usd/stage.h" + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace FSV +{ + class StageNoticeListener : public TfWeakBase + { + public: + StageNoticeListener() = default; + virtual ~StageNoticeListener(); + + void SetStage(const UsdStageWeakPtr& stage); + + using StageContentsChangedCallback = std::function; + using StageObjectsChangedCallback = std::function; + + void SetStageContentsChangedCallback( const StageContentsChangedCallback& callback ); + void SetStageObjectsChangedCallback( const StageObjectsChangedCallback& callback ); + + + private: + UsdStageWeakPtr m_Stage; + StageNoticeListener(const StageNoticeListener&) = delete; + + TfNotice::Key m_StageContentsChangedKey{}; + StageContentsChangedCallback m_StageContentsChangedCallback{}; + + TfNotice::Key m_StageObjectsChangedKey{}; + StageObjectsChangedCallback m_StageObjectsChangedCallback{}; + + void UpdateStageContentsChangedRegistration(); + void OnStageContentsChanged( const UsdNotice::StageContentsChanged& notice) const; + void OnStageObjectsChanged( const UsdNotice::ObjectsChanged& notice, const UsdStageWeakPtr& sender) const; + + }; +} \ No newline at end of file diff --git a/src/FlipScope/Usd/ViewSettings.cpp b/src/FlipScope/Usd/ViewSettings.cpp new file mode 100644 index 0000000..4f21d30 --- /dev/null +++ b/src/FlipScope/Usd/ViewSettings.cpp @@ -0,0 +1,25 @@ +#include "pch.h" + +#include "ViewSettings.h" + +namespace FSV +{ + ViewSettings::ViewSettings(Settings settings) + { + } + + ViewSettings::~ViewSettings() + { + } + + bool ViewSettings::SaveSettings() + { + return true; + } + + bool ViewSettings::LoadSettings() + { + return true; + } +} + diff --git a/src/FlipScope/Usd/ViewSettings.h b/src/FlipScope/Usd/ViewSettings.h new file mode 100644 index 0000000..bac7657 --- /dev/null +++ b/src/FlipScope/Usd/ViewSettings.h @@ -0,0 +1,69 @@ +#pragma once + +#include "pxr/pxr.h" +#include "pxr/base/gf/vec4f.h" +#include "pxr/usdImaging/usdImagingGL/renderParams.h" + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace FSV +{ + const char* colorCorrectModes[] = { "disabled", "sRGB", "openColorIO" }; + enum ColorCorrectModes + { + DISABLE, + SRGB, + OPENCOLORIO + }; + const char* pickModes[] = {"Prims", "Models", "Instances", "Prototypes"}; + enum PickModes + { + PRIMS, + MODELS, + INSTANCES, + PROTOTYPES + }; + const char* selectionHightlightModes[] = { "Never", "Only when paused", "Always" }; + struct Settings + { + UsdImagingGLDrawMode renderMode = UsdImagingGLDrawMode::DRAW_SHADED_SMOOTH; + float freeCameraFOV = 60.0; + TfToken colorCorrectionMode = TfToken(colorCorrectModes[ColorCorrectModes::SRGB]); + GfVec4f highlightColor = { 1.0, 1.0, 0.0, 0.5 }; + GfVec4f backgroundColor = {}; + bool cameraLightEnabled = true; + bool domeLightEnabled = false; + bool ambientLightOnly = true; + TfToken pickMode = TfToken(pickModes[PickModes::PRIMS]); + bool showBBoxes = true; + bool showAABBoxes = true; + bool displayGuide = false; + bool displayProxy = true; + bool displayRender = false; + bool enableSceneMaterials = true; + bool showInactivePrims = true; + bool showMasterPrims = false; + bool showUndefinedPrims = false; + bool showAbstractPrims = false; + bool showHUD = true; + + + }; + + class ViewSettings + { + public: + ViewSettings(Settings settings); + ~ViewSettings(); + + Settings Get() { return m_Settings; } + void Set(Settings settings) { m_Settings = settings; } + + bool SaveSettings(); + bool LoadSettings(); + + private: + Settings m_Settings; + + }; +} diff --git a/src/FlipScope/Widgets/ShotView.cpp b/src/FlipScope/Widgets/ShotView.cpp new file mode 100644 index 0000000..54f8391 --- /dev/null +++ b/src/FlipScope/Widgets/ShotView.cpp @@ -0,0 +1,177 @@ +#include "pch.h" + +#include "ShotView.h" + +#include "FlipScope/Core/Application.h" + +#include "FlipScope/Pipeline/ProjectInfo.h" +#include "FlipScope/Pipeline/Project.h" + +#include + + +namespace FSV +{ + ShotView::ShotView() + { + } + ShotView::~ShotView() + { + } + void ShotView::Show(bool * p_open) + { + ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + if (!ImGui::Begin("Shots", p_open)) + { + ImGui::End(); + return; + } + + ImGui::PopStyleVar(); + + ProjectInfo projectInfo = Application::GetProjectInfo(); + static ProjectList projectList = projectInfo.GetUserProjects(); + + static Project* currentProject = nullptr; + + static std::string currentEpisodeName; + static StringList episodeNameList; + + static std::string currentShotName; + static ShotList shotList; + + if (currentProject == nullptr && projectList.size()) + { + currentProject = &projectList[0]; + episodeNameList = currentProject->GetEpisodeNameList(); + if (episodeNameList.size() && !currentEpisodeName.length()) + { + currentEpisodeName = episodeNameList[0]; + shotList = currentProject->GetShotListByEpisode(currentEpisodeName); + } + } + + if (ImGui::BeginCombo("Project", currentProject->GetAliasName().c_str(), (ImGuiComboFlags)projectList.size())) + { + for (int n = 0; n < projectList.size(); n++) + { + bool is_selected = (currentProject->GetAliasName() == projectList[n].GetAliasName()); + if (ImGui::Selectable(projectList[n].GetAliasName().c_str(), is_selected)) + { + currentProject = &projectList[n]; + + // Reload Episode List + episodeNameList = currentProject->GetEpisodeNameList(); + currentEpisodeName = ""; + currentShotName = ""; + if (episodeNameList.size() && !currentEpisodeName.length()) + { + currentEpisodeName = episodeNameList[0]; + + // Reload Shot List + shotList = currentProject->GetShotListByEpisode(currentEpisodeName); + if (shotList.size() && !currentShotName.length()) + { + currentShotName = shotList[0].GetName(); + } + } + FS_INFO("{0}", projectList[n].GetAliasName().c_str()); + } + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + if (ImGui::BeginCombo("Episode", currentEpisodeName.c_str(), (ImGuiComboFlags)episodeNameList.size())) + { + for (int n = 0; n < episodeNameList.size(); n++) + { + bool is_selected = (currentEpisodeName == episodeNameList[n]); + if (ImGui::Selectable(episodeNameList[n].c_str(), is_selected)) + { + currentEpisodeName = episodeNameList[n]; + shotList = currentProject->GetShotListByEpisode(currentEpisodeName); + // Reload Shot List + currentShotName = ""; + shotList = currentProject->GetShotListByEpisode(currentEpisodeName); + if (shotList.size() && !currentShotName.length()) + { + currentShotName = shotList[0].GetName(); + } + FS_INFO("Current Episode : {0}", currentEpisodeName); + } + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + /*for (int n = 0; n < shotList.size(); n++) + { + FS_INFO("{0} : Shot : {1}", currentEpisodeName, shotList[n].GetName()); + }*/ + /*if (ImGui::BeginCombo("Shot", currentShotName.c_str(), shotList.size())) + { + for (int n = 0; n < shotList.size(); n++) + { + bool is_selected = (currentShotName == shotList[n].GetName()); + if (ImGui::Selectable(shotList[n].GetName().c_str(), is_selected)) + { + currentShotName = shotList[n].GetName(); + } + + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + }*/ + + ImGui::ListBoxHeader("Shots"); + for (auto shot : shotList) + { + bool is_selected = (currentShotName == shot.GetName()); + if (ImGui::Selectable(shot.GetName().c_str(), is_selected)) + { + currentShotName = shot.GetName(); + } + } + ImGui::ListBoxFooter(); + + const char* stage_items[] = { "Lay", "Ani", "Lgt", "Sim" }; + static const char* stage_item_current = stage_items[0]; + if (ImGui::BeginCombo("Stage", stage_item_current, IM_ARRAYSIZE(stage_items))) + { + for (int n = 0; n < IM_ARRAYSIZE(stage_items); n++) + { + bool is_selected = (stage_item_current == stage_items[n]); + if (ImGui::Selectable(stage_items[n], is_selected)) + stage_item_current = stage_items[n]; + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + /*StringList shotNames = current_project.GetProjectData().GetEpisodeShotNameList(episode_item_current); + FS_INFO("{0} : {1}", episode_item_current, shotNames.size()); + std::string shot_item_current = shotNames.size() ? shotNames[0] : ""; + if (ImGui::BeginCombo("Shots", shot_item_current.c_str(), shotNames.size())) + { + for (int n = 0; n < shotNames.size(); n++) + { + bool is_selected = (shot_item_current == shotNames[n]); + if (ImGui::Selectable(shotNames[n].c_str(), is_selected)) + { + shot_item_current = shotNames[n]; + FS_INFO("Current Shot : {0}", shot_item_current); + } + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + + }*/ + ImGui::End(); + } +} \ No newline at end of file diff --git a/src/FlipScope/Widgets/ShotView.h b/src/FlipScope/Widgets/ShotView.h new file mode 100644 index 0000000..2c75582 --- /dev/null +++ b/src/FlipScope/Widgets/ShotView.h @@ -0,0 +1,13 @@ +#pragma once + +namespace FSV +{ + class ShotView + { + public: + ShotView(); + ~ShotView(); + + void Show(bool *p_open); + }; +} \ No newline at end of file diff --git a/src/FlipScope/Widgets/StageView.cpp b/src/FlipScope/Widgets/StageView.cpp new file mode 100644 index 0000000..4e7d06c --- /dev/null +++ b/src/FlipScope/Widgets/StageView.cpp @@ -0,0 +1,358 @@ +#include "pch.h" + +#include +#include +#include + +#include "FlipScope/Core/Application.h" +#include "FlipScope/Core/AppController.h" +#include "FlipScope/Widgets/StageView.h" + +#include +#include + +namespace FSV +{ + StageView::StageView() + { + } + + StageView::~StageView() + { + } + + void StageView::InitToolBar() + { + AppController& controller = Application::GetController(); + + if (ImGui::Button(ICON_FA_PLUS, ImVec2(26.0f, 24.0f))) + { + ImGui::OpenPopup("Create Prim"); + } + + if (ImGui::BeginPopupModal("Create Prim", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + static char primName[128] = ""; + static char primTypeName[64] = ""; + static bool useOverPrim = false; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PopStyleVar(); + ImGui::InputTextWithHint("Path", "Prim path start with /", primName, IM_ARRAYSIZE(primName)); + ImGui::Checkbox("Override", &useOverPrim); + const char* stage_items[] = { "Xform", "Mesh", "Light", "Camera" }; + static const char* stage_item_current = stage_items[0]; + + if (useOverPrim) + { + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f); + } + + if (ImGui::BeginCombo("Type", stage_item_current, IM_ARRAYSIZE(stage_items))) + { + for (int n = 0; n < IM_ARRAYSIZE(stage_items); n++) + { + bool is_selected = (stage_item_current == stage_items[n]); + if (ImGui::Selectable(stage_items[n], is_selected)) + stage_item_current = stage_items[n]; + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + if (useOverPrim) + { + ImGui::PopItemFlag(); + ImGui::PopStyleVar(); + } + + if (ImGui::Button("OK", ImVec2(120, 0))) { + controller.CreatePrim(primName, stage_item_current); + ImGui::CloseCurrentPopup(); + } + ImGui::SetItemDefaultFocus(); + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(120, 0))) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + /*if (ImGui::MenuItem("Override Prim", "Alt+O", false, true)) + { + controller.CreateOverridePrim("/refPath"); + }*/ + + ImGui::SameLine(0.0, 2.0); + ImGui::Button(ICON_FA_MINUS, ImVec2(26.0f, 24.0f)); + ImGui::Separator(); + } + + void StageView::BuildTreeNode(UsdPrim &prim) + { + /*if (prim.GetChildren().empty()) + { + ImGui::AlignTextToFramePadding(); + ImGui::Selectable(prim.GetName().GetText()); + //ImGui::Selectable(ICON_FA_HOME); + ImGui::NextColumn(); + ImGui::Text(prim.GetTypeName().GetText()); + ImGui::NextColumn(); + }*/ + ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth; + AppController& controller = Application::GetController(); + static SelectionModel selectionModel = controller.GetStageDataModel()->GetSelectionModel(); + + for (auto &p : prim.GetChildren()) + { + + ImGui::AlignTextToFramePadding(); + ImGuiTreeNodeFlags node_flags = base_flags; + + if (!p.GetChildren()) { + node_flags = ImGuiTreeNodeFlags_Leaf; + } + + //const bool is_selected = selectionModel.HasPrim(p); + + if (selectionModel.IsSelected(p)){ + node_flags |= ImGuiTreeNodeFlags_Selected; + } + + //bool p_open = ImGui::TreeNodeEx(ICON_FA_CUBE + p.GetName().GetText(), node_flags); + char treeText[1024]; + //sprintf(treeText, "%s %s", ICON_FA_CUBE, p.GetName().GetText()); + //sprintf(treeText, "%sXX", p.GetName().GetText()); + //bool p_open = ImGui::TreeNodeEx(treeText, node_flags); + + bool p_open = ImGui::TreeNodeEx(p.GetName().GetText(), node_flags); + /*ImGui::SameLine(); + if (p.GetTypeName().GetString() == "Xform") { + ImGui::TextColored(ImVec4(0.0, 1.0, 0.5, 1.0), ICON_FA_ASTERISK); + } + else if (p.GetTypeName().GetString() == "Mesh") { + //ImGui::Text(ICON_FA_SHAPES); + ImGui::TextColored(ImVec4(0.0, 0.5, 0.8, 1.0), ICON_FA_CUBE); + } + else if (p.GetTypeName().GetString() == "Camera") { + ImGui::TextColored(ImVec4(0.0, 0.5, 0.8, 1.0), ICON_FA_CAMERA); + } + ImGui::SameLine(); + ImGui::Text(p.GetName().GetText()); + */ + //ImGui::SameLine(); + //bool p_open = ImGui::TreeNodeEx(&p, node_flags, p.GetName().GetText()); + + ImGui::NextColumn(); + + if (ImGui::IsItemClicked()) + { + if (ImGui::GetIO().KeyCtrl) { + selectionModel.AppendSelection(p); // Append selection + } else { + selectionModel.AddSelection(p); // Add selection + } + } + + // Some usaful icon + // ICON_FA_SHAPES - + // ICON_FA_LINK - Reference + /*ImGui::AlignTextToFramePadding(); + if (p.GetTypeName().GetString() == "Xform") { + ImGui::TextColored(ImVec4(0.0, 1.0, 0.5, 1.0), ICON_FA_ASTERISK); + } + else if (p.GetTypeName().GetString() == "Mesh") { + //ImGui::Text(ICON_FA_SHAPES); + ImGui::TextColored(ImVec4(0.0, 0.5, 0.8, 1.0), ICON_FA_CUBE); + } + else if (p.GetTypeName().GetString() == "Camera") { + ImGui::TextColored(ImVec4(0.0, 0.5, 0.8, 1.0), ICON_FA_CAMERA); + }*/ + /*node_flags = ImGuiTreeNodeFlags_Leaf; + + if (selectionModel.IsSelected(p)) { + node_flags |= ImGuiTreeNodeFlags_Selected; + }*/ + //ImGui::TreeNodeEx(p.GetTypeName().GetText(), node_flags); + + /*ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_None; + ImGuiIO& io = ImGui::GetIO(); + ImGuiStyle& style = ImGui::GetStyle(); + float font_size = ImGui::GetFontSize(); + //ImGui::AlignTextToFramePadding(); + ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.0, 0.5)); + ImGui::Selectable(p.GetTypeName().GetText(), selectionModel.IsSelected(p), selectable_flags, ImVec2(ImGui::GetColumnWidth(), font_size+ (style.FramePadding.y * 2) - 2)); + ImGui::PopStyleVar(); + //ImGui::SameLine();*/ + ImGui::AlignTextToFramePadding(); + char PrimTypeText[1024]; + auto PrimTypeColor = ImVec4(1.0, 1.0, 1.5, 1.0); + if (p.GetTypeName().GetString() == "Xform") + { + PrimTypeColor = ImVec4(0.0, 1.0, 0.5, 1.0); + sprintf(PrimTypeText, "%s %s", ICON_FA_ASTERISK, p.GetTypeName().GetText()); + } + else if (p.GetTypeName().GetString() == "Mesh") + { + PrimTypeColor = ImVec4(0.0, 0.5, 0.8, 1.0); + sprintf(PrimTypeText, "%s %s", ICON_FA_CUBE, p.GetTypeName().GetText()); + } + else if (p.GetTypeName().GetString() == "Camera") + { + PrimTypeColor = ImVec4(0.0, 0.5, 0.8, 1.0); + sprintf(PrimTypeText, "%s %s", ICON_FA_CAMERA, p.GetTypeName().GetText()); + } + else { + sprintf(PrimTypeText, "%s", p.GetTypeName().GetText()); + } + + //ImGui::TextColored(PrimTypeColor, PrimTypeText); + ImGui::SetItemAllowOverlap(); + //ImGui::Selectable(PrimTypeText, selectionModel.IsSelected(p), ImGuiSelectableFlags_SpanAllColumns); + ImGui::Selectable(PrimTypeText, selectionModel.IsSelected(p)); + + //ImGui::Text(p.GetTypeName().GetText()); + ImGui::NextColumn(); + //ImGui::SetItemAllowOverlap(); + + /*auto primSpec = p.GetPrimDefinition(); + static bool isChecked = !primSpec->GetHidden(); + char chk_name[1024]; + sprintf(chk_name, "%s", p.GetPath().GetText()); + if (ImGui::Checkbox(chk_name, &isChecked)) + { + FS_INFO("Changed {}:{}", p.GetPath().GetText(), isChecked); + //isChecked ? p.SetHidden(true) : p.SetHidden(false); + isChecked ? primSpec->SetHidden(true) : primSpec->SetHidden(false); + }*/ + + constexpr const char* inheritedIcon = ICON_FA_EYE; + UsdGeomImageable imageable(prim); + + if (imageable) { + VtValue visibleValue; + auto attr = imageable.GetVisibilityAttr(); + if (attr.HasAuthoredValue()) { + attr.Get(&visibleValue); + const TfToken& visibilityToken = visibleValue.Get(); + const char* icon = visibilityToken == UsdGeomTokens->invisible + ? ICON_FA_EYE_SLASH : inheritedIcon; + if (ImGui::SmallButton(icon)) { + UsdTimeCode tc = UsdTimeCode::Default(); + if (visibilityToken == UsdGeomTokens->inherited) { + attr.Set(VtValue(UsdGeomTokens->invisible), tc); + } + else if (visibilityToken == UsdGeomTokens->invisible) + { + prim.RemoveProperty(attr.GetName()); + } + } + } + else { + if (ImGui::SmallButton(inheritedIcon)) { + UsdTimeCode tc = UsdTimeCode::Default(); + attr.Set(VtValue(UsdGeomTokens->inherited), tc); + } + } + } + + + //ImGui::SmallButton("") + ImGui::NextColumn(); + + if(p_open){ + BuildTreeNode(p); + ImGui::TreePop(); + } + } + } + + void StageView::BuildOutliner() + { + AppController& controller = Application::GetController(); + constexpr ImGuiTableFlags tableFlags = ImGuiTableFlags_SizingFixedFit | /*ImGuiTableFlags_RowBg |*/ ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable; + /*if (ImGui::BeginTable("##DrawStageOutliner", 3, tableFlags)) { + ImGui::TableSetupScrollFreeze(3, 1); // Freeze the root node of the tree (the layer) + ImGui::TableSetupColumn("Hierarchy"); + ImGui::TableSetupColumn("V", ImGuiTableColumnFlags_WidthFixed, 40); + ImGui::TableSetupColumn("Type"); + + UsdPrim rootPrim = controller.GetStageDataModel()->GetStage()->GetPseudoRoot(); + SdfLayerHandle layer = controller.GetStageDataModel()->GetStage()->GetSessionLayer(); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGuiTreeNodeFlags nodeflags = ImGuiTreeNodeFlags_OpenOnArrow; + + ImGuiListClipper clipper; + + + ImGui::EndTable(); + }*/ + if (ImGui::BeginTable("split2", 3, ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) + { + //ImGui::TableSetupScrollFreeze(3, 1); // Freeze the root node of the tree (the layer) + ImGui::TableSetupColumn("Hierarchy", ImGuiTableColumnFlags_NoHide); + ImGui::TableSetupColumn("V", ImGuiTableColumnFlags_WidthFixed, 40); + ImGui::TableSetupColumn("Type"); + ImGui::TableHeadersRow(); + static bool selected[10] = {}; + for (int i = 0; i < 10; i++) + { + char label[32]; + sprintf(label, "Item %d", i); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Selectable(label, &selected[i], ImGuiSelectableFlags_SpanAllColumns); + ImGui::TableNextColumn(); + ImGui::Text("Some other contents"); + ImGui::TableNextColumn(); + ImGui::Text("123456"); + } + ImGui::EndTable(); + } + } + + void StageView::BuildTreeItem(UsdPrim& prim) + { + + } + + void StageView::Show(bool *p_open) + { + AppController& controller = Application::GetController(); + controller.GetStageDataModel()->GetStage(); + + ImGuiIO& io = ImGui::GetIO(); + ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 2)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0, 4)); + + if (!ImGui::Begin("Stage", p_open)) + { + ImGui::End(); + return; + } + //InitToolBar(); + + BuildOutliner(); + /*ImGui::Columns(3, "tree", true); + ImGui::Text("Path"); + ImGui::NextColumn(); + + ImGui::Text("Type"); + ImGui::NextColumn(); + + ImGui::Text(ICON_FA_EYE); + ImGui::NextColumn(); + + BuildTreeNode(controller.GetStageDataModel()->GetStage()->GetPseudoRoot()); + */ + //ImGui::Columns(1); + ImGui::PopStyleVar(3); + ImGui::End(); + } +} + diff --git a/src/FlipScope/Widgets/StageView.h b/src/FlipScope/Widgets/StageView.h new file mode 100644 index 0000000..e4182a8 --- /dev/null +++ b/src/FlipScope/Widgets/StageView.h @@ -0,0 +1,29 @@ +#pragma once + +#include "FlipScope/Usd/StageDataModel.h" + + +#include +#include + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace FSV +{ + class StageView + { + public: + StageView(); + ~StageView(); + void InitToolBar(); + + //StageDataModel* GetModel() { return m_StageDataModel; }; + //void SetModel(StageDataModel *model); + void BuildTreeNode(UsdPrim &prim); + void BuildOutliner(); + void BuildTreeItem(UsdPrim &prim); + void Show(bool *p_open); + private: + + }; +} diff --git a/src/FlipScope/Widgets/Viewport.cpp b/src/FlipScope/Widgets/Viewport.cpp new file mode 100644 index 0000000..885a319 --- /dev/null +++ b/src/FlipScope/Widgets/Viewport.cpp @@ -0,0 +1,232 @@ +#include "pch.h" + +#include +#include +#include + +#include "FlipScope/Core/Log.h" +#include "FlipScope/Widgets/Viewport.h" + +namespace FSV +{ + + Viewport::Viewport(uint32_t width, uint32_t height) + :m_Width(width), m_Height(height) + { + m_HydraRenderer = std::make_shared(width, height); + m_HydraRenderer->Init(width, height); + } + + Viewport::~Viewport() + { + } + + void Viewport::Update() + { + // Re-initialize Renderer + m_HydraRenderer->SetupRenderer(false); + } + + void Viewport::Show(bool *p_open) + { + ImGuiIO& io = ImGui::GetIO(); +/*#ifdef IMGUI_HAS_VIEWPORT + ImGui::SetNextWindowSize(ImGui::GetMainViewport()->Size); + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->Pos); +#else + ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver); +#endif*/ + ImGui::SetNextWindowSize(io.DisplaySize, ImGuiCond_Once); + //ImGui::SetNextWindowSize(ImVec2(430, 450)); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(1.0f, 1.0f)); + //ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3.0f); + std::string viewportName = "Viewport - (" + GetRenderer()->GetCurrentRendererName() + ")"; + + if (!ImGui::Begin(viewportName.c_str(), p_open)) + { + ImGui::End(); + return; + } + + //InitToolBar(); + + ImVec2 viewportPos = ImGui::GetCursorScreenPos(); + ImVec2 viewportPanelSize = ImGui::GetContentRegionAvail(); + ImVec2 mousePosInViewport = ImVec2(io.MousePos.x - viewportPos.x, io.MousePos.y - viewportPos.y); + + // FS_INFO("viewportPanelSize {}, {}", viewportPanelSize.x, viewportPanelSize.y); + + /*bool IsMouseInViewport = false; + if (mousePosInViewport.x > 0 && mousePosInViewport.x < viewportPanelSize.x && + mousePosInViewport.y > 0 && mousePosInViewport.y < viewportPanelSize.y) + { + io.ConfigFlags |= ImGuiConfigFlags_NoMouse; + IsMouseInViewport = true; + // FS_INFO("{} {}", mousePosInViewport.x, mousePosInViewport.y); + } else { + io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse; + IsMouseInViewport = true; + }*/ + + ImGuiWindow* window = ImGui::GetCurrentWindow(); + + const ImRect bb(window->DC.CursorPos, ImVec2(window->DC.CursorPos.x+ viewportPanelSize.x, window->DC.CursorPos.y + viewportPanelSize.y)); + + uint64_t rendererID = m_HydraRenderer->GetRendererID(); + + ImGui::PushID((void*)(intptr_t)rendererID); + const ImGuiID id = window->GetID("#image"); + ImGui::PopID(); + + bool hovered, held; + bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held); + + if (hovered) + { + + //if (ImGui::GetMergedKeyModFlags() & ImGuiKeyModFlags_Alt) + if (io.KeyMods & ImGuiKeyModFlags_Alt) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow); + if (ImGui::IsAnyMouseDown()) + { + if (io.MouseClicked[0] || io.MouseClicked[1] || io.MouseClicked[2]) + m_HydraRenderer->OnMousePress(io.MouseClicked, io.MousePos.x, io.MousePos.y, io.KeyMods); + + m_HydraRenderer->OnMouseMove(io.MouseDown, io.MousePos.x, io.MousePos.y, io.KeyMods); + } + + if (io.MouseReleased[0] || io.MouseReleased[1] || io.MouseReleased[2]) + { + m_HydraRenderer->OnMouseRelease(io.MouseReleased, io.MousePos.x, io.MousePos.y, io.KeyMods); + } + + } + } + + // Update renderer + m_HydraRenderer->OnWindowResize(viewportPanelSize.x, viewportPanelSize.y); + + ImGui::PopStyleVar(); + ImGui::Image((void*)rendererID, ImVec2{ viewportPanelSize.x, viewportPanelSize.y }, ImVec2{ 0, 1 }, ImVec2{ 1, 0 }); + + //if (ImGui::IsItemHovered()){} + + ImVec2 work_area_pos = viewportPos; + ImVec2 work_area_size = viewportPanelSize; + ImVec2 window_pos = ImVec2(work_area_pos.x + 10, work_area_pos.y + 10); + ImVec2 window_pos_pivot = ImVec2(0, 0); + /*ImGui::SetNextWindowFocus(); + ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); + ImGui::SetNextWindowViewport(window->ViewportId); + ImGui::SetNextWindowBgAlpha(0.35f); + int corner = -1; + if (ImGui::Begin("Example: Simple overlay", p_open, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNav)) + { + ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)"); + ImGui::Separator(); + if (ImGui::IsMousePosValid()) + ImGui::Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y); + else + ImGui::Text("Mouse Position: "); + if (ImGui::BeginPopupContextWindow()) + { + if (ImGui::MenuItem("Top-left", NULL, corner == 0)) corner = 0; + if (ImGui::MenuItem("Top-right", NULL, corner == 1)) corner = 1; + if (ImGui::MenuItem("Bottom-left", NULL, corner == 2)) corner = 2; + if (ImGui::MenuItem("Bottom-right", NULL, corner == 3)) corner = 3; + ImGui::EndPopup(); + } + } + ImGui::End(); + */ + ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); + + ImGui::PushClipRect(window_pos, ImVec2(window_pos.x + work_area_size.x, window_pos.y + work_area_size.y), true); + + ImGui::BeginChild("#ViewportWidgets", ImVec2(work_area_size.x, 100), false); + ImGui::Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y); + ImGui::Button(ICON_FA_HOME, ImVec2(22.0f, 22.0f)); + ImGui::SameLine(); + ImGui::Button(ICON_FA_HOME, ImVec2(22.0f, 22.0f)); + ImGui::SameLine(); + static std::string currentRendererName = GetRenderer()->GetCurrentRendererName(); + std::vector rendererPluginNames = GetRenderer()->GetRendererPluginsNames(); + //FS_INFO("### currentRendererName : {}", currentRendererName); + + ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32(0, 0, 0, 100)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, IM_COL32(0, 0, 0, 150)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 5.0f); + + if (ImGui::BeginCombo("", currentRendererName.c_str(), (ImGuiComboFlags)rendererPluginNames.size())) + { + for (int n = 0; n < rendererPluginNames.size(); n++) + { + bool is_selected = (currentRendererName == rendererPluginNames[n]); + + if (ImGui::Selectable(rendererPluginNames[n].c_str(), is_selected)) + { + currentRendererName = rendererPluginNames[n]; + GetRenderer()->SetCurrentRenderer(currentRendererName); + } + + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + ImGui::PopStyleVar(); + ImGui::PopStyleColor(2); + ImGui::EndChild(); + ImGui::PopClipRect(); + ImGui::End(); + } + + void Viewport::InitToolBar() + { + + ImGui::Button(ICON_FA_HOME, ImVec2(22.0f, 22.0f)); + ImGui::SameLine(); + ImGui::Button(ICON_FA_HOME, ImVec2(22.0f, 22.0f)); + ImGui::SameLine(); + static std::string currentRendererName = GetRenderer()->GetCurrentRendererName(); + std::vector rendererPluginNames = GetRenderer()->GetRendererPluginsNames(); + //FS_INFO("### currentRendererName : {}", currentRendererName); + + if (ImGui::BeginCombo("Renderer", currentRendererName.c_str(), (ImGuiComboFlags)rendererPluginNames.size())) + { + for (int n = 0; n < rendererPluginNames.size(); n++) + { + bool is_selected = (currentRendererName == rendererPluginNames[n]); + + if (ImGui::Selectable(rendererPluginNames[n].c_str(), is_selected)) + { + currentRendererName = rendererPluginNames[n]; + GetRenderer()->SetCurrentRenderer(currentRendererName); + } + + + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + ImGui::SameLine(); + if (ImGui::BeginCombo("Camera", "Free Camera")) + { + ImGui::EndCombo(); + } + } + + void Viewport::OnMousePress(bool button[], int x, int y, int modKeys) + { + } + void Viewport::OnMouseRelease(bool button[], int x, int y, int modKeys) + { + } + void Viewport::OnMouseMove(int x, int y, int modKeys) + { + } +} \ No newline at end of file diff --git a/src/FlipScope/Widgets/Viewport.h b/src/FlipScope/Widgets/Viewport.h new file mode 100644 index 0000000..ad73b97 --- /dev/null +++ b/src/FlipScope/Widgets/Viewport.h @@ -0,0 +1,38 @@ +#pragma once +#include "pch.h" + +#include +#include + +#include "FlipScope/Usd/StageDataModel.h" +#include "FlipScope/Usd/HydraRenderer.h" + +namespace FSV +{ + class Viewport + { + public: + + Viewport(uint32_t width, uint32_t height); + virtual ~Viewport(); + + void Update(); + + void Show(bool *p_open); + + std::shared_ptr GetRenderer() { return m_HydraRenderer; }; + void InitToolBar(); + + int GetWidth() { return m_Width; }; + int GetHeight() { return m_Height; }; + + void OnMousePress(bool button[], int x, int y, int modKeys); + void OnMouseRelease(bool button[], int x, int y, int modKeys); + void OnMouseMove(int x, int y, int modKeys); + + private: + uint32_t m_Width, m_Height; + std::shared_ptr m_HydraRenderer; + std::shared_ptr m_StageDataModel; + }; +} \ No newline at end of file diff --git a/src/Platform/OpenGL/OpenGLContext.cpp b/src/Platform/OpenGL/OpenGLContext.cpp new file mode 100644 index 0000000..a41d76c --- /dev/null +++ b/src/Platform/OpenGL/OpenGLContext.cpp @@ -0,0 +1,42 @@ +#include "pch.h" + +#include "FlipScope/Core/Core.h" +#include "FlipScope/Core/Log.h" + +#include "Platform/OpenGL/OpenGLContext.h" + +namespace FSV +{ + OpenGLContext::OpenGLContext(GLFWwindow* windowHandle) + : m_WindowHandle(windowHandle) + { + + } + + void OpenGLContext::Init() + { + glfwMakeContextCurrent(m_WindowHandle); + + int status = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); + //GLenum status = glewInit(); + if (!status) { + //if(GLEW_OK != status) + //{ + //FS_ERROR("OpenGLContext::Init() : Fail initialize OpenGL."); + FS_ERROR("OpenGLContext::Init() : Fail initialize OpenGL. {0}", status); + } + else { + FS_INFO("OpenGLContext.Init(): {0}", status); + } + + FS_INFO("OpenGL Info:"); + FS_INFO(" Vendor: {0}", glGetString(GL_VENDOR)); + FS_INFO(" Renderer: {0}", glGetString(GL_RENDERER)); + FS_INFO(" Version: {0}", glGetString(GL_VERSION)); + } + + void OpenGLContext::SwapBuffers() + { + glfwSwapBuffers(m_WindowHandle); + } +} \ No newline at end of file diff --git a/src/Platform/OpenGL/OpenGLContext.h b/src/Platform/OpenGL/OpenGLContext.h new file mode 100644 index 0000000..761fee9 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLContext.h @@ -0,0 +1,20 @@ +#pragma once + +#include "FlipScope/Renderer/GraphicsContext.h" + +struct GLFWwindow; + +namespace FSV +{ + class OpenGLContext :public GraphicsContext + { + public: + OpenGLContext(GLFWwindow* windowHandle); + + virtual void Init() override; + virtual void SwapBuffers() override; + + private: + GLFWwindow* m_WindowHandle; + }; +} diff --git a/src/Platform/OpenGL/OpenGLFramebuffer.cpp b/src/Platform/OpenGL/OpenGLFramebuffer.cpp new file mode 100644 index 0000000..15e5f48 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLFramebuffer.cpp @@ -0,0 +1,77 @@ +#include "pch.h" + +#include "FlipScope/Core/Log.h" + +#include "Platform/OpenGL/OpenGLFramebuffer.h" + +#include +//#include + + +namespace FSV +{ + OpenGLFramebuffer::OpenGLFramebuffer(const FramebufferSpecification& spec) + :m_Specification(spec) + { + Invalidate(); + } + + OpenGLFramebuffer::~OpenGLFramebuffer() + { + glDeleteFramebuffers(1, &m_RendererID); + glDeleteTextures(1, &m_ColorAttachment); + glDeleteTextures(1, &m_DepthAttachment); + } + + void OpenGLFramebuffer::Invalidate() + { + + if (m_RendererID) + { + glDeleteFramebuffers(1, &m_RendererID); + glDeleteTextures(1, &m_ColorAttachment); + glDeleteTextures(1, &m_DepthAttachment); + } + + glCreateFramebuffers(1, &m_RendererID); + glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID); + + glCreateTextures(GL_TEXTURE_2D, 1, &m_ColorAttachment); + glBindTexture(GL_TEXTURE_2D, m_ColorAttachment); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_ColorAttachment, 0); + + glCreateTextures(GL_TEXTURE_2D, 1, &m_DepthAttachment); + glBindTexture(GL_TEXTURE_2D, m_DepthAttachment); + + glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_DepthAttachment, 0); + + if (!(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)) + FS_ERROR("Framebuffer is incomplete!"); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + void OpenGLFramebuffer::Bind() + { + glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID); + } + + void OpenGLFramebuffer::Unbind() + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + void OpenGLFramebuffer::Resize(uint32_t width, uint32_t height) + { + m_Specification.Width = width; + m_Specification.Height = height; + + Invalidate(); + } +} \ No newline at end of file diff --git a/src/Platform/OpenGL/OpenGLFramebuffer.h b/src/Platform/OpenGL/OpenGLFramebuffer.h new file mode 100644 index 0000000..b8dbca5 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLFramebuffer.h @@ -0,0 +1,30 @@ +#pragma once + +#include "FlipScope/Renderer/FrameBuffer.h" + +namespace FSV +{ + class OpenGLFramebuffer :public Framebuffer + { + public: + OpenGLFramebuffer(const FramebufferSpecification& spec); + virtual ~OpenGLFramebuffer(); + + virtual void Bind() override; + virtual void Unbind() override; + + void Invalidate(); + + virtual void Resize(uint32_t width, uint32_t height) override; + + + virtual uint32_t GetColorAttachmentRendererID() const override { return m_ColorAttachment; } + + virtual const FramebufferSpecification& GetSpecification() const override { return m_Specification; } + private: + uint32_t m_RendererID = 0; + uint32_t m_ColorAttachment = 0, m_DepthAttachment = 0; + FramebufferSpecification m_Specification; + + }; +} diff --git a/src/Platform/OpenGL/OpenGLRendererAPI.cpp b/src/Platform/OpenGL/OpenGLRendererAPI.cpp new file mode 100644 index 0000000..9e14e34 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLRendererAPI.cpp @@ -0,0 +1,33 @@ +#include "pch.h" + +#include "OpenGLRendererAPI.h" + +#include +//#include + +namespace FSV +{ + void OpenGLRendererAPI::Init() + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glEnable(GL_DEPTH_TEST); + } + + void OpenGLRendererAPI::SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) + { + glViewport(x, y, width, height); + } + + void OpenGLRendererAPI::SetClearColor(const glm::vec4& color) + { + glClearColor(color.r, color.g, color.b, color.a); + } + + void OpenGLRendererAPI::Clear() + { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + +} \ No newline at end of file diff --git a/src/Platform/OpenGL/OpenGLRendererAPI.h b/src/Platform/OpenGL/OpenGLRendererAPI.h new file mode 100644 index 0000000..363af1d --- /dev/null +++ b/src/Platform/OpenGL/OpenGLRendererAPI.h @@ -0,0 +1,18 @@ +#pragma once + +#include "FlipScope/Renderer/RendererAPI.h" + +namespace FSV +{ + class OpenGLRendererAPI : public RendererAPI + { + public: + virtual void Init() override; + virtual void SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) override; + + virtual void SetClearColor(const glm::vec4& color) override; + virtual void Clear() override; + + }; +} + diff --git a/src/Platform/OpenGL/OpenGLTexture.cpp b/src/Platform/OpenGL/OpenGLTexture.cpp new file mode 100644 index 0000000..9b4a7af --- /dev/null +++ b/src/Platform/OpenGL/OpenGLTexture.cpp @@ -0,0 +1,90 @@ +#include "pch.h" + +#include "FlipScope/Core/Log.h" +#include "Platform/OpenGL/OpenGLTexture.h" + +#include + +namespace FSV +{ + OpenGLTexture::OpenGLTexture(uint32_t width, uint32_t height) + :m_Width(width), m_Height(height) + { + m_InternalFormat = GL_RGBA8; + m_DataFormat = GL_RGBA; + + glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID); + glTextureStorage2D(m_RendererID, 1, m_InternalFormat, m_Width, m_Height); + + glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + OpenGLTexture::OpenGLTexture(const std::string path) + :m_Path(path) + { + int width, height, channels; + stbi_uc *data; + stbi_set_flip_vertically_on_load(1); + { + FS_INFO("stbi_load - OpenGLTexture2D::OpenGLTexture2D(const std:string&)"); + data = stbi_load(path.c_str(), &width, &height, &channels, 0); + } + + m_Width = width; + m_Height = height; + + GLenum internalFormat = 0, dataFormat = 0; + if (channels == 4) + { + internalFormat = GL_RGBA8; + dataFormat = GL_RGBA; + } + else if (channels == 3) + { + internalFormat = GL_RGB8; + dataFormat = GL_RGB; + } + + m_InternalFormat = internalFormat; + m_DataFormat = dataFormat; + + if (!(internalFormat & dataFormat)) + { + FS_ERROR("Format not supported!"); + return; + } + + glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID); + glTextureStorage2D(m_RendererID, 1, internalFormat, m_Width, m_Height); + + glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, dataFormat, GL_UNSIGNED_BYTE, data); + + stbi_image_free(data); + } + + OpenGLTexture::~OpenGLTexture() + { + glDeleteTextures(1, &m_RendererID); + } + + void OpenGLTexture::SetData(void *data, uint32_t size) const + { + uint32_t bpp = m_DataFormat == GL_RGBA ? 4 : 3; + glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, m_DataFormat, GL_UNSIGNED_BYTE, data); + } + + void OpenGLTexture::Bind(uint32_t slot) const + { + glBindTextureUnit(slot, m_RendererID); + } +} \ No newline at end of file diff --git a/src/Platform/OpenGL/OpenGLTexture.h b/src/Platform/OpenGL/OpenGLTexture.h new file mode 100644 index 0000000..04ef1a5 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLTexture.h @@ -0,0 +1,38 @@ +#pragma once + +#include "FlipScope/Renderer/Texture.h" + +#include +//#include + +namespace FSV +{ + class OpenGLTexture :public Texture + { + public: + + OpenGLTexture(uint32_t width, uint32_t height); + OpenGLTexture(const std::string path); + + virtual ~OpenGLTexture(); + + virtual uint32_t GetWidth() const override { return m_Width; } + virtual uint32_t GetHeight() const override { return m_Height; } + virtual uint32_t GetRendererID() const override { return m_RendererID; } + + virtual void SetData(void* data, uint32_t size) const override; + + virtual void Bind(uint32_t slot = 0) const override; + + virtual bool operator==(const Texture& other) const override + { + return m_RendererID == ((OpenGLTexture&)other).m_RendererID; + } + + private: + std::string m_Path; + uint32_t m_Width, m_Height; + uint32_t m_RendererID; + GLenum m_InternalFormat, m_DataFormat; + }; +} diff --git a/src/Platform/OpenGL/OpenGLViewportRenderer.cpp b/src/Platform/OpenGL/OpenGLViewportRenderer.cpp new file mode 100644 index 0000000..65054f8 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLViewportRenderer.cpp @@ -0,0 +1,35 @@ +#include "pch.h" + +#include "Platform/OpenGL/OpenGLViewportRenderer.h" + +namespace FSV +{ + OpenGLViewportRenderer::OpenGLViewportRenderer() + { + + } + OpenGLViewportRenderer::~OpenGLViewportRenderer() + { + + } + void OpenGLViewportRenderer::Init() + { + + } + + void OpenGLViewportRenderer::Shutdown() + { + + } + + void OpenGLViewportRenderer::OnWindowResize(uint32_t width, uint32_t height) + { + m_Width = width; + m_Height = height; + } + + void OpenGLViewportRenderer::OnRender() + { + + } +} \ No newline at end of file diff --git a/src/Platform/OpenGL/OpenGLViewportRenderer.h b/src/Platform/OpenGL/OpenGLViewportRenderer.h new file mode 100644 index 0000000..5a60013 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLViewportRenderer.h @@ -0,0 +1,28 @@ +#pragma once + +#include "FlipScope/Renderer/ViewportRenderer.h" +#include +//#include + +namespace FSV +{ + class OpenGLViewportRenderer :public ViewportRenderer + { + public: + OpenGLViewportRenderer(); + virtual ~OpenGLViewportRenderer(); + virtual void Init() override; + virtual void Shutdown() override; + + virtual uint32_t GetWidth() const override { return m_Width; } + virtual uint32_t GetHeight() const override { return m_Height; } + + virtual void OnWindowResize(uint32_t width, uint32_t height); + + virtual void OnRender(); + + private: + uint32_t m_Width, m_Height; + GLenum m_DrawTarget; + }; +} \ No newline at end of file diff --git a/src/Platform/Windows/WindowsWindow.cpp b/src/Platform/Windows/WindowsWindow.cpp new file mode 100644 index 0000000..5b3f5ac --- /dev/null +++ b/src/Platform/Windows/WindowsWindow.cpp @@ -0,0 +1,145 @@ +#include "pch.h" + +#include "Platform/Windows/WindowsWindow.h" + +#include "FlipScope/Core/Log.h" +#include "FlipScope/Event/ApplicationEvent.h" + +#include "FlipScope/Renderer/Renderer.h" + +#include "Platform/OpenGL/OpenGLContext.h" + +namespace FSV +{ + static uint8_t s_GLFWWindowCount = 0; + + static void GLFWErrorCallback(int error, const char* description) + { + FS_ERROR("GLFW Error : {0} , {1}", error, description); + } + + WindowsWindow::WindowsWindow(const WindowProps& props) + { + Init(props); + } + + WindowsWindow::~WindowsWindow() + { + Shutdown(); + } + + void WindowsWindow::Init(const WindowProps& props) + { + FS_INFO("WindowsWindow::Init()"); + m_Data.Title = props.Title; + m_Data.Width = props.Width; + m_Data.Height = props.Height; + + if (s_GLFWWindowCount == 0) + { + int success = glfwInit(); + FS_INFO("WindowsWindow::Init() : glfwInit() : {0}", success); + glfwSetErrorCallback(GLFWErrorCallback); + } + + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); + m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr); + ++s_GLFWWindowCount; + + m_Context = GraphicsContext::Create(m_Window); + m_Context->Init(); + + //glfwSetWindowUserPointer(m_Window, &m_Data); + glfwSetWindowUserPointer(m_Window, this); + SetVSync(true); + + glfwSetWindowSizeCallback(m_Window, WindowResizeCallback); + glfwSetWindowCloseCallback(m_Window, WindowCloseCallback); + glfwSetMouseButtonCallback(m_Window, MouseButtonCallback); + glfwSetCursorPosCallback(m_Window, CursorPosCallback); + glfwSetDropCallback(m_Window, FileDropCallback); + glfwSetScrollCallback(m_Window, ScrollCallback); + } + + void WindowsWindow::Shutdown() + { + glfwDestroyWindow(m_Window); + --s_GLFWWindowCount; + + if (s_GLFWWindowCount == 0) + { + glfwTerminate(); + } + FS_INFO("WindowsWindow::Shutdown()"); + } + + void WindowsWindow::OnUpdate() + { + glfwPollEvents(); + //Renderer + + m_Context->SwapBuffers(); + } + + void WindowsWindow::SetVSync(bool enabled) + { + if (enabled) + glfwSwapInterval(1); + else + glfwSwapInterval(0); + m_Data.VSync = enabled; + } + + bool WindowsWindow::IsVSync() const + { + return m_Data.VSync; + } + + bool WindowsWindow::Close() const + { + if (glfwWindowShouldClose(m_Window)) + return true; + return false; + } + + void WindowsWindow::OnWindowResize(unsigned int width, unsigned int height) + { + m_Data.Width = width; + m_Data.Height = height; + FS_INFO("WindowsWindow::OnWindowResize() : Resize {0} {1}", width, height); + Renderer::OnWindowResize(width, height); + } + + void WindowsWindow::OnWindowClose() + { + FS_INFO("WindowsWindow::OnWindowClose() : Close"); + } + + void WindowsWindow::OnFileDrop(unsigned int count, const char *paths[]) + { + for (unsigned int i = 0; i < count; i++) { + const std::string &filepath = paths[i]; + FS_INFO("WindowsWindow::OnFileDrop() : {0} : {1}", i, filepath); + } + } + + void WindowsWindow::OnScroll(double xOffset, double yOffset) + { + FS_INFO("WindowsWindow::OnScroll() : {0} {1}", xOffset, yOffset); + } + + void WindowsWindow::OnCursorPos(double xPos, double yPos) + { + //FS_INFO("WindowsWindow::OnCursorPos() : {0} {1}", xPos, yPos); + } + + void WindowsWindow::OnMousePress(int button, int mods) + { + FS_INFO("WindowsWindow::OnMousePress() : {0} {1}", button, mods); + } + + void WindowsWindow::OnMouseRelease(int button, int mods) + { + FS_INFO("WindowsWindow::OnMouseRelease() : {0} {1}", button, mods); + } +} \ No newline at end of file diff --git a/src/Platform/Windows/WindowsWindow.h b/src/Platform/Windows/WindowsWindow.h new file mode 100644 index 0000000..829168e --- /dev/null +++ b/src/Platform/Windows/WindowsWindow.h @@ -0,0 +1,93 @@ +#pragma once +#include "FlipScope/Core/Core.h" + +#include "FlipScope/Core/Window.h" +#include "FlipScope/Renderer/GraphicsContext.h" + +namespace FSV +{ + class WindowsWindow : public Window + { + public: + WindowsWindow(const WindowProps& props); + virtual ~WindowsWindow(); + + void OnUpdate() override; + + inline unsigned int GetWidth() const override { return m_Data.Width; } + inline unsigned int GetHeight() const override { return m_Data.Height; } + + void SetEventCallback(const EventCallbackFn& callback) override { m_Data.EventCallback = callback; } + void SetVSync(bool enabled) override; + bool IsVSync() const override; + bool Close() const; + + void OnWindowResize(unsigned int width, unsigned int height); + void OnWindowClose(); + void OnFileDrop(unsigned int count, const char* paths[]); + void OnScroll(double xOffset, double yOffset); + void OnCursorPos(double xPos, double yPos); + void OnMousePress(int button, int mods); + void OnMouseRelease(int button, int mods); + + inline virtual void* GetNativeWindow() const { return m_Window; } + + private: + virtual void Init(const WindowProps& props); + virtual void Shutdown(); + + private: + GLFWwindow* m_Window; + std::unique_ptr m_Context; + bool m_Running; + + struct WindowData + { + std::string Title; + unsigned int Width, Height; + bool VSync; + EventCallbackFn EventCallback; + }; + + WindowData m_Data; + + inline static auto WindowResizeCallback(GLFWwindow *win, int width, int height) -> void { + WindowsWindow *window = static_cast(glfwGetWindowUserPointer(win)); + window->OnWindowResize(width, height); + } + + inline static auto WindowCloseCallback(GLFWwindow *win) -> void { + WindowsWindow *window = static_cast(glfwGetWindowUserPointer(win)); + window->OnWindowClose(); + } + + inline static auto FileDropCallback(GLFWwindow *win, int count, const char* paths[]) -> void { + WindowsWindow *window = static_cast(glfwGetWindowUserPointer(win)); + window->OnFileDrop(count, paths); + } + + inline static auto ScrollCallback(GLFWwindow *win, double xOffset, double yOffset) -> void { + WindowsWindow *window = static_cast(glfwGetWindowUserPointer(win)); + window->OnScroll(xOffset, yOffset); + } + + inline static auto CursorPosCallback(GLFWwindow *win, double xPos, double yPos) -> void { + WindowsWindow *window = static_cast(glfwGetWindowUserPointer(win)); + window->OnCursorPos(xPos, yPos); + } + + + inline static auto MouseButtonCallback(GLFWwindow *win, int button, int action, int mods) -> void { + WindowsWindow *window = static_cast(glfwGetWindowUserPointer(win)); + switch (action) + { + case GLFW_PRESS: + window->OnMousePress(button, mods); + break; + case GLFW_RELEASE: + window->OnMouseRelease(button, mods); + break; + } + } + }; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..e2463b3 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,11 @@ +#include "pch.h" + +#include "FlipScope/Core/Application.h" +#include "FlipScope/Core/Log.h" + + +int main(int argc, char *argv[]) { + FSV::Log::Init(); + auto *app = FSV::CreateApplication(); + app->Run(); +} \ No newline at end of file diff --git a/src/pch.cpp b/src/pch.cpp new file mode 100644 index 0000000..1730571 --- /dev/null +++ b/src/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/src/pch.h b/src/pch.h new file mode 100644 index 0000000..ab3b7fc --- /dev/null +++ b/src/pch.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include \ No newline at end of file