diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96eb53bb..a1203160 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,4 +19,31 @@ Before pull request please run clang-tidy on your code (on windows you can use c **T.2 - Naming** Try to be nice to the template user. Refrein from using `class T` as universal name for everything. Do you expect enum class? Name it! `template`. -**T.3 - SFINAE** Be nice to the user. Rather than using `std::enable_if<>::type` use C++17 less boiler plate `std::enable_if_t<>`. And every time you can use `static_asser` instead as you can explain reason to not allow instantiation of the template with given arguments. +**T.3 - SFINAE** Be nice to the user. Rather than using `std::enable_if<>::type` use C++17 less boiler plate `std::enable_if_t<>`. And every time you can use `static_assert` instead as you can explain reason to not allow instantiation of the template with given arguments. + +###### Classes + +**C.1 Declaration order** - First declare `public` part of the class and place constructors and destructors as first memeber functions. Than `protected` part and `private` as last part. +Reason: Bear in mind you want to comunicate usage of the class first, than implementation details. Programmer who will come to your header will most likely will be looking for usage of the class, not the implementation details such as member variables. + +**C2. Definition** - The source file should first state reflection of the class if present. Followd by static variables definition. The first member functions defined should be constructors and after them destructor. The order of member functions is not defined. + +**C2.1 Destructor definition** - Put destructor definition in source file even for defaulted destructors. This helps header separation. + +**C2.2 Separators** - Each member function definition should be separated by separator. This helps readablitiy. + +``` +//================================================================================= +``` + +**C.3 Initialize member variables** - Uninitialized variables leads to undefined behaviour. Initialize all member variables in the constructor. + +**C.3.1 Unify initialization** - Either init all member variables in initialization list or as inline initialization in header. Do not combine those as it makes **C.2** rule harder to follow. + +###### Testing + +This project is not fully tested. But unit test proven to be useful to keep at least basic invariants intact even during biggest changes. Rule of thumb I am trying to follow is when I encounter bug I write a unit test to fixate the invariant by it. I am trying to implement unit tests at least for the basic building block of this engine. + +**TEST.1 Test structure** - Place tests to separate projects following naming convention of engine projects. Tests should be placed to source files named same way as engine sources. This could be broken by moving of files in the engine, but helps to keep track of test placemennt. Tests are executed during CI and fail of the test will block Pull requests. + +**TEST.2 Private functions testing** - When testing private functions of the class use fixture that is set as `friend class` of the tested class. \ No newline at end of file diff --git a/Editor/Editor/Editors/Image/Tools/BrickGenerator.cpp b/Editor/Editor/Editors/Image/Tools/BrickGenerator.cpp new file mode 100644 index 00000000..9b0f6601 --- /dev/null +++ b/Editor/Editor/Editors/Image/Tools/BrickGenerator.cpp @@ -0,0 +1,142 @@ +#include + +#include + +#include + +#include +#include + +// clang-format off +RTTR_REGISTRATION +{ + using namespace GLEngine::Editor; + using namespace Utils::Reflection; + + rttr::registration::class_("C_BrickGenerator") + .property("Height", &C_BrickGenerator::m_RowHeight)( + rttr::policy::prop::bind_as_ptr, + RegisterMetaclass(), + RegisterMetamember("Row height:"), + RegisterMetamember(1), + RegisterMetamember(100)) + .property("BrickWidth", &C_BrickGenerator::m_BrickWidth)( + rttr::policy::prop::bind_as_ptr, + RegisterMetaclass(), + RegisterMetamember("Row width:"), + RegisterMetamember(1), + RegisterMetamember(100)) + .property("BrickOffset", &C_BrickGenerator::m_BrickOffset)( + rttr::policy::prop::bind_as_ptr, + RegisterMetaclass(), + RegisterMetamember("Brick offset:"), + RegisterMetamember(1), + RegisterMetamember(100)) +#pragma region Randomness + .property("RowHeightDeviation", &C_BrickGenerator::m_RowHeightDeviation)( + rttr::policy::prop::bind_as_ptr, + RegisterMetaclass(), + RegisterMetamember("Randomness"), + RegisterMetamember("Row height deviation:"), + RegisterMetamember(1), + RegisterMetamember(100)) + .property("BrickWidthDeviation", &C_BrickGenerator::m_BrickWidthDeviation)( + rttr::policy::prop::bind_as_ptr, + RegisterMetaclass(), + RegisterMetamember("Randomness"), + RegisterMetamember("Brick width deviation:"), + RegisterMetamember(1), + RegisterMetamember(100)) + .property("MortarWidthDeviation", &C_BrickGenerator::m_MortarWidthDeviation)( + rttr::policy::prop::bind_as_ptr, + RegisterMetaclass(), + RegisterMetamember("Randomness"), + RegisterMetamember("Mortar width deviation:"), + RegisterMetamember(1), + RegisterMetamember(100)) + .property("MortarHeightDeviation", &C_BrickGenerator::m_MortarHeightDeviation)( + rttr::policy::prop::bind_as_ptr, + RegisterMetaclass(), + RegisterMetamember("Randomness"), + RegisterMetamember("Mortar height deviation:"), + RegisterMetamember(1), + RegisterMetamember(100)) +#pragma endregion + .property("MortarThickness", &C_BrickGenerator::m_MortarThickness)( + rttr::policy::prop::bind_as_ptr, + RegisterMetaclass(), + RegisterMetamember("Mortar thickness:"), + RegisterMetamember(1), + RegisterMetamember(100)) + .property("BrickColour", &C_BrickGenerator::m_BrickColour)( + rttr::policy::prop::bind_as_ptr, + RegisterMetaclass(), + RegisterMetamember("Brick colour:")) + .property("MortarColour", &C_BrickGenerator::m_MortarColour)( + rttr::policy::prop::bind_as_ptr, + RegisterMetaclass(), + RegisterMetamember("Mortar colour:") + ); +} +// clang-format on + +namespace GLEngine::Editor { +//================================================================================= +C_BrickGenerator::C_BrickGenerator(Renderer::C_TextureView view) + : C_ImageEditorTool(view) + , m_RowHeight(10) + , m_BrickWidth(30) + , m_MortarThickness(2) + , m_BrickOffset(20) + , m_RowHeightDeviation(0) + , m_BrickWidthDeviation(0) + , m_MortarWidthDeviation(0) + , m_MortarHeightDeviation(0) + , m_BrickColour(Colours::T_Colour{227.f / 255.f, 3.f / 255.f, 12.f / 255.f}) + , m_MortarColour(Colours::T_Colour{93.f / 255.f, 93.f / 255.f, 93.f / 255.f}) +{ +} + +//================================================================================= +void C_BrickGenerator::Apply() +{ + std::random_device rd; // a seed source for the random number engine + std::mt19937 gen(rd()); // mersenne_twister_engine seeded with rd() + std::uniform_int_distribution<> rowHeightGen(-m_RowHeightDeviation / 2, m_RowHeightDeviation / 2); + std::uniform_int_distribution<> brickWidthGen(-m_BrickWidthDeviation / 2, m_BrickWidthDeviation / 2); + std::uniform_int_distribution<> mortarWidthGen(-m_MortarWidthDeviation / 2, m_MortarWidthDeviation / 2); + std::uniform_int_distribution<> mortarHeightGen(-m_MortarHeightDeviation / 2, m_MortarHeightDeviation / 2); + + m_View.ClearColor({m_MortarColour, 1.f}); + unsigned int mortarTop = 0; + int totalOffset = 0; + int row = 0; + while (mortarTop < m_View.GetDimensions().y) + { + int mortarLeft = totalOffset; + int rowHeight = m_RowHeight + rowHeightGen(gen); + while (mortarLeft < static_cast(m_View.GetDimensions().x)) + { + int brickWidth = m_BrickWidth + brickWidthGen(gen); + int leftMortarThickness = m_MortarThickness + mortarWidthGen(gen); + int brickEnd = mortarLeft + leftMortarThickness + brickWidth - 1; + if (brickEnd >= 0) + { + for (int i = 0; i < rowHeight; ++i) + { + m_View.FillLineSpan(m_BrickColour, mortarTop + m_MortarThickness + i, std::max(0, mortarLeft + leftMortarThickness), + mortarLeft + leftMortarThickness + brickWidth - 1); + } + } + mortarLeft += leftMortarThickness + brickWidth; + } + mortarTop += m_MortarThickness + rowHeight + mortarHeightGen(gen); + totalOffset -= m_BrickOffset; + if (totalOffset <= -2 * (m_BrickWidth + m_MortarThickness)) + totalOffset += (m_BrickWidth + m_MortarThickness); + + row++; + } +} + +} // namespace GLEngine::Editor \ No newline at end of file diff --git a/Editor/Editor/Editors/Image/Tools/BrickGenerator.h b/Editor/Editor/Editors/Image/Tools/BrickGenerator.h new file mode 100644 index 00000000..1ad86c85 --- /dev/null +++ b/Editor/Editor/Editors/Image/Tools/BrickGenerator.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include +#include + +#include +#include + +namespace GLEngine::Editor { +class C_BrickGenerator : public C_ImageEditorTool { +public: + C_BrickGenerator(Renderer::C_TextureView view); + RTTR_ENABLE(C_ImageEditorTool); + RTTR_REGISTRATION_FRIEND; + + void Apply() override; + +private: + int m_RowHeight; + int m_BrickWidth; + int m_MortarThickness; + int m_BrickOffset; + + // randomness + int m_RowHeightDeviation; + int m_BrickWidthDeviation; + int m_MortarWidthDeviation; + int m_MortarHeightDeviation; + + Colours::T_Colour m_BrickColour; + Colours::T_Colour m_MortarColour; +}; +} // namespace GLEngine::Editor \ No newline at end of file diff --git a/Editor/Editor/Editors/ImageEditor.cpp b/Editor/Editor/Editors/ImageEditor.cpp new file mode 100644 index 00000000..c408ada6 --- /dev/null +++ b/Editor/Editor/Editors/ImageEditor.cpp @@ -0,0 +1,182 @@ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +namespace GLEngine::Editor { + +const static glm::vec2 s_ImageDrawArea{800, 800}; +const static glm::uvec2 s_BackgroundDim{2, 2}; + +//================================================================================= +C_ImageEditor::C_ImageEditor(GUID guid, GUI::C_GUIManager& guiMGR) + : GUI::C_Window(guid, "Image editor") + , m_Storage(1024, 1024, 4) + , m_GUIImage(nullptr) + , m_FileMenu("File") + , m_Tools("Tools") + , m_DeviceImage(nullptr) + , m_Background(nullptr) +{ + auto& device = Core::C_Application::Get().GetActiveRenderer().GetDevice(); + const Renderer::TextureDescriptor desc{.name = "Editor image", + .width = 1024, + .height = 1024, + .type = Renderer::E_TextureType::TEXTURE_2D, + .format = Renderer::E_TextureFormat::RGBA32f, + .m_bStreamable = false}; + const Renderer::TextureDescriptor descTransparent{.name = "Transparent background", + .width = s_BackgroundDim.x, + .height = s_BackgroundDim.y, + .type = Renderer::E_TextureType::TEXTURE_2D, + .format = Renderer::E_TextureFormat::RGB32f, + .m_bStreamable = false}; + + m_DeviceImage = device.CreateTextureHandle(desc); + m_Background = device.CreateTextureHandle(descTransparent); + device.AllocateTexture(*m_DeviceImage.get()); + device.AllocateTexture(*m_Background.get()); + m_Storage.SetAll(glm::vec4(Colours::white, 0.f)); + { + Renderer::C_TextureViewStorageCPU storage(s_BackgroundDim.x, s_BackgroundDim.y, 3); + storage.SetAll(glm::vec4(Colours::white, 0.f)); + Renderer::C_TextureView view(&storage); + + view.Set(glm::ivec2{0, 0}, glm::vec3{Colours::gray}); + view.Set(glm::ivec2{1, 1}, glm::vec3{Colours::gray}); + + m_Background->SetTexData2D(0, &storage); + m_Background->SetWrap(Renderer::E_WrapFunction::Repeat, Renderer::E_WrapFunction::Repeat); + m_Background->SetFilter(Renderer::E_TextureFilter::Nearest, Renderer::E_TextureFilter::Nearest); + } + + AddMenu(m_FileMenu); + AddMenu(m_Tools); + + m_Tools.AddMenuItem(guiMGR.CreateMenuItem("Histogram", std::bind(&C_ImageEditor::ToggleHistogram, this))); + m_Tools.AddMenuItem(guiMGR.CreateMenuItem("Brick", [&]() { m_ActiveTool = std::make_unique(Renderer::C_TextureView(&m_Storage)); })); + m_Tools.AddMenuItem(guiMGR.CreateMenuItem("Blur", [&]() { m_ActiveTool = std::make_unique(Renderer::C_TextureView(&m_Storage)); })); + std::reference_wrapper createMenuItem = guiMGR.CreateMenuItem("Save as...", [&]() { + const auto textureSelectorGUID = NextGUID(); + auto* textureSelectWindow = new GUI::C_FileDialogWindow( + ".bmp,.hdr,.ppm", "Save image as...", + [&, textureSelectorGUID](const std::filesystem::path& texture) { + // TODO save here + + guiMGR.DestroyWindow(textureSelectorGUID); + }, + textureSelectorGUID, "./Images"); + guiMGR.AddCustomWindow(textureSelectWindow); + textureSelectWindow->SetVisible(); + }); + m_FileMenu.AddMenuItem(createMenuItem); + + m_GUIImage = new GUI::C_ImageViewer(*m_DeviceImage.get()); + m_GUIImage->SetSize({800, 800}); + m_DeviceImage->SetTexData2D(0, &m_Storage); + + std::thread([&]() { + Utils::HighResolutionTimer timer; + timer.reset(); + Renderer::C_TextureView view(&m_Storage); + view.EnableBlending(); + int x1 = 100; + int x2 = 900; + int y1 = 100; + int y2 = 900; + + glm::uvec2 center{512, 512}; + Renderer::C_CPURasterizer rasterizer(view); + rasterizer.DrawCircle(Colours::red, center, 200); + rasterizer.DrawCircle(Colours::red, center, 400); + // rasterizer.FloodFill(Colours::blue, {212, 512}); + // rasterizer.FloodFill(Colours::green, {512, 512}); + // for (int i = 0; i < 36; ++i) + //{ + // glm::ivec2 dir{std::cos(glm::radians(i * 10.f)) * 400, std::sin(glm::radians(i * 10.f)) * 400}; + // rasterizer.DrawLine(Colours::red, center, glm::ivec2{center} + dir, true); + //} + m_bDone = true; + m_bModified = true; + CORE_LOG(E_Level::Info, E_Context::Render, "Line render time {}", float(timer.getElapsedTimeFromLastQueryMilliseconds()) / 1000.f); + }).detach(); +} + +//================================================================================= +C_ImageEditor::~C_ImageEditor() +{ + delete m_GUIImage; + auto& device = Core::C_Application::Get().GetActiveRenderer().GetDevice(); + device.DestroyTexture(*m_DeviceImage.get()); +} + +//================================================================================= +void C_ImageEditor::Update() +{ + static int numUpdates = 0; + if (!m_bFinish) + { + ++numUpdates; + if (m_bDone) + { + CORE_LOG(E_Level::Info, E_Context::Render, "Updates: {}", numUpdates); + } + m_DeviceImage->SetTexData2D(0, &m_Storage); // possibly miss last update + m_bFinish = m_bDone; + } +} + +//================================================================================= +void C_ImageEditor::DrawComponents() const +{ + const ImVec2 canvas_p0 = ImGui::GetCursorPos(); + ImGui::Image((void*)(intptr_t)(m_Background->GetGPUHandle()), {s_ImageDrawArea.x, s_ImageDrawArea.y}, {0, 0}, + {s_ImageDrawArea.x / (s_BackgroundDim.x * 5), s_ImageDrawArea.y / (s_BackgroundDim.y * 5)}); + ImGui::SetCursorPos(canvas_p0); + m_GUIImage->Draw(); + ::ImGui::SameLine(); + if (m_ActiveTool) + { + ::ImGui::BeginChild("ImageTools"); + rttr::instance obj(m_ActiveTool.get()); + if (GUI::DrawAllPropertyGUI(obj).empty() == false) + { + + } + if (ImGui::Button("Apply")) { + m_ActiveTool->Apply(); + m_bFinish = false; // hack + } + ::ImGui::EndChild(); + } +} + +void C_ImageEditor::ToggleHistogram() +{ + CORE_LOG(E_Level::Error, E_Context::Core, "Histogram."); +} + +void C_ImageEditor::SetupToolPreview() +{ +} + +} // namespace GLEngine::Editor \ No newline at end of file diff --git a/Editor/Editor/Editors/ImageEditor.h b/Editor/Editor/Editors/ImageEditor.h new file mode 100644 index 00000000..cd16a56b --- /dev/null +++ b/Editor/Editor/Editors/ImageEditor.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include + +#include +#include + +namespace GLEngine::Renderer { +class I_DeviceTexture; +} + +namespace GLEngine::Editor { +class C_ImageEditorTool; + +class EDITOR_API_EXPORT C_ImageEditor : public GUI::C_Window { +public: + C_ImageEditor(GUID guid, GUI::C_GUIManager& guiMGR); + ~C_ImageEditor(); + virtual void Update() override; + +protected: + virtual void DrawComponents() const override; + + void ToggleHistogram(); + void SetupToolPreview(); + +private: + Renderer::C_TextureViewStorageCPU m_Storage; + GUI::C_ImageViewer* m_GUIImage; // view + GUI::Menu::C_Menu m_FileMenu; + GUI::Menu::C_Menu m_Tools; + std::shared_ptr m_DeviceImage; // should not be owning ptr + std::shared_ptr m_Background; // should not be owning ptr + + + std::unique_ptr m_ActiveTool; + + bool m_bDone = false; + mutable bool m_bFinish = false; + bool m_bModified = false; +}; +} // namespace GLEngine::Editor diff --git a/Editor/Editor/Editors/ImageEditorTool.cpp b/Editor/Editor/Editors/ImageEditorTool.cpp new file mode 100644 index 00000000..e95f45ce --- /dev/null +++ b/Editor/Editor/Editors/ImageEditorTool.cpp @@ -0,0 +1,46 @@ +#include + +#include + +#include + +#include + +RTTR_REGISTRATION +{ + using namespace GLEngine::Editor; + using namespace Utils::Reflection; + + rttr::registration::class_("C_ImageEditorTool"); + rttr::registration::class_("C_GaussianBlur") + .property("Sigma", &C_GaussianBlur::m_Sigma)( + rttr::policy::prop::bind_as_ptr, + RegisterMetaclass(), + RegisterMetamember("Sigma:"), + RegisterMetamember(0.001f), + RegisterMetamember(1.0f)); +} + +namespace GLEngine::Editor { + + +//================================================================================= +C_ImageEditorTool::C_ImageEditorTool(Renderer::C_TextureView view) + : m_View(view) +{ +} + +//================================================================================= +C_GaussianBlur::C_GaussianBlur(Renderer::C_TextureView view) + : C_ImageEditorTool(view) + , m_KernelSize(5) + , m_Sigma(0.1f) +{ +} + +//================================================================================= +void C_GaussianBlur::Apply() +{ +} + +} // namespace GLEngine::Editor \ No newline at end of file diff --git a/Editor/Editor/Editors/ImageEditorTool.h b/Editor/Editor/Editors/ImageEditorTool.h new file mode 100644 index 00000000..fbda573e --- /dev/null +++ b/Editor/Editor/Editors/ImageEditorTool.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#include +#include + +namespace GLEngine::Renderer { +class I_Device; +} + +namespace GLEngine::Editor { +// event system should handle resource update? Or should I propagate it otherwise? +// There is no resource so just propagate to the editor and editor should handle +// this. +class C_ImageEditorTool { +public: + // reflection GUI create the view + // needs access to the storage + C_ImageEditorTool(Renderer::C_TextureView view); // can be performed on whole image or just part of it + + virtual void Apply() {} + virtual void GeneratePreview() {} + virtual void SetupPreview(Renderer::I_Device& device) {} + // for reflection GUI + RTTR_ENABLE(); + +protected: + Renderer::C_TextureView m_View; +}; + +class C_GaussianBlur : public C_ImageEditorTool { +public: + C_GaussianBlur(Renderer::C_TextureView view); + RTTR_ENABLE(C_ImageEditorTool); + RTTR_REGISTRATION_FRIEND; + + void Apply() override; + +private: + /*std::uint8_t*/ int m_KernelSize; + float m_Sigma; +}; +} // namespace GLEngine::Editor \ No newline at end of file diff --git a/GLRenderer/GLRenderer/Textures/Texture.cpp b/GLRenderer/GLRenderer/Textures/Texture.cpp index 2661f1a4..501a582d 100644 --- a/GLRenderer/GLRenderer/Textures/Texture.cpp +++ b/GLRenderer/GLRenderer/Textures/Texture.cpp @@ -144,6 +144,7 @@ void C_Texture::GenerateMipMaps() void C_Texture::SetTexData2D(int level, const Renderer::I_TextureViewStorage* tex) { GLE_ASSERT(tex, "This should be smth like reference"); + GLE_ASSERT(GetDimensions() == tex->GetDimensions(), "Size of storage and texture should match"); // https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.compatibility.pdf Table 8.35 glTextureSubImage2D(m_texture, 0, //!< Level diff --git a/GLRenderer/GLRenderer/Windows/ExperimentWindow.cpp b/GLRenderer/GLRenderer/Windows/ExperimentWindow.cpp index 804fccce..911d1c12 100644 --- a/GLRenderer/GLRenderer/Windows/ExperimentWindow.cpp +++ b/GLRenderer/GLRenderer/Windows/ExperimentWindow.cpp @@ -40,6 +40,8 @@ #include #include +#include + #include #include #include @@ -393,6 +395,19 @@ void C_ExplerimentWindow::OnAppInit() rayTraceWindow->SetVisible(true); })); + m_Windows.AddMenuItem(guiMGR.CreateMenuItem("Image editor", [&]() { + if (guiMGR.GetWindow(m_ImageEditorGUID) != nullptr) + { + return; + } + m_ImageEditorGUID = NextGUID(); + + auto* imageEditorWindow = new Editor::C_ImageEditor(m_ImageEditorGUID, guiMGR); + + guiMGR.AddCustomWindow(imageEditorWindow); + imageEditorWindow->SetVisible(true); + })); + const auto rendererWindow = static_cast(m_renderer.get())->SetupControls(guiMGR); m_Windows.AddMenuItem(guiMGR.CreateMenuItem("Renderer", rendererWindow, guiMGR)); diff --git a/GLRenderer/GLRenderer/Windows/ExperimentWindow.h b/GLRenderer/GLRenderer/Windows/ExperimentWindow.h index dbda8d0d..ece3595a 100644 --- a/GLRenderer/GLRenderer/Windows/ExperimentWindow.h +++ b/GLRenderer/GLRenderer/Windows/ExperimentWindow.h @@ -73,7 +73,7 @@ class C_ExplerimentWindow : public GLFW::C_GLFWoGLWindow { Core::C_LayerStack m_LayerStack; Temporar::C_CameraManager m_CamManager; C_GLImGUILayer* m_ImGUI; - Utils::HighResolutionTimer m_FrameTimer; + ::Utils::HighResolutionTimer m_FrameTimer; //=========================== // GUI @@ -93,6 +93,7 @@ class C_ExplerimentWindow : public GLFW::C_GLFWoGLWindow { GUID m_FrameStatsGUID; GUID m_ConsoleWindowGUID; GUID m_RayTraceGUID; + GUID m_ImageEditorGUID; GUID m_EntitiesWindowGUID; GUID m_HDRSettingsGUID; GUI::Menu::C_Menu m_Windows; diff --git a/GUI/GUI/Menu/Menu.h b/GUI/GUI/Menu/Menu.h index 0e1a5554..8a30a098 100644 --- a/GUI/GUI/Menu/Menu.h +++ b/GUI/GUI/Menu/Menu.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include diff --git a/GUI/GUI/ReflectionGUI.cpp b/GUI/GUI/ReflectionGUI.cpp index db87136f..c5db1927 100644 --- a/GUI/GUI/ReflectionGUI.cpp +++ b/GUI/GUI/ReflectionGUI.cpp @@ -39,6 +39,15 @@ bool DrawSlider(rttr::instance& obj, const rttr::property& prop) GetMetadataMember(prop)); } +//================================================================================= +bool DrawSliderInt(rttr::instance& obj, const rttr::property& prop) +{ + using namespace ::Utils::Reflection; + + return ::ImGui::SliderInt(GetMetadataMember(prop).c_str(), (prop.get_value(obj).convert()), GetMetadataMember(prop), + GetMetadataMember(prop)); +} + //================================================================================= bool DrawAngle(rttr::instance& obj, const rttr::property& prop) { @@ -69,11 +78,36 @@ std::vector DrawAllPropertyGUI(rttr::instance& obj) return {}; } - for (const auto& prop : obj.get_type().get_properties()) + rttr::type type = obj.get_type().get_raw_type(); + if (type.get_derived_classes().empty() == false) + type = obj.get_derived_type(); + + std::map> collapsableHeaders; + for (auto& prop : type.get_properties()) { - if (DrawPropertyGUI(obj, prop)) + if (HasMetadataMember(prop)) + { + collapsableHeaders[GetMetadataMember(prop)].emplace_back(prop); + } + else { - changedVals.emplace_back(prop); + if (DrawPropertyGUI(obj, prop)) + { + changedVals.emplace_back(prop); + } + } + } + for (auto& collapsableHeader : collapsableHeaders) + { + if (::ImGui::CollapsingHeader(collapsableHeader.first.c_str())) + { + for (auto& prop : collapsableHeader.second) + { + if (DrawPropertyGUI(obj, prop)) + { + changedVals.emplace_back(prop); + } + } } } return changedVals; @@ -99,6 +133,10 @@ bool DrawPropertyGUI(rttr::instance& obj, const rttr::property& prop) { return DrawSlider(obj, prop); } + else if (UI::IsUIMetaclass(prop)) + { + return DrawSliderInt(obj, prop); + } else if (UI::IsUIMetaclass(prop)) { return DrawAngle(obj, prop); @@ -108,7 +146,11 @@ bool DrawPropertyGUI(rttr::instance& obj, const rttr::property& prop) DrawText(obj, prop); return false; } - return false; + else + { + rttr::instance ins{prop.get_value(obj)}; + return GUI::DrawAllPropertyGUI(ins).empty() == false; // this will not return changed properties + } } } // namespace GLEngine::GUI diff --git a/Renderer/Renderer/Render/CPURasterizer.cpp b/Renderer/Renderer/Render/CPURasterizer.cpp new file mode 100644 index 00000000..10cde75e --- /dev/null +++ b/Renderer/Renderer/Render/CPURasterizer.cpp @@ -0,0 +1,292 @@ +#include + +#include + +#include + +namespace GLEngine::Renderer { + +//================================================================================= +C_CPURasterizer::C_CPURasterizer(Renderer::C_TextureView& view) + : m_view(view) +{ +} + +//================================================================================= +void C_CPURasterizer::DrawLine(const Colours::T_Colour& colour, const glm::ivec2& p1, const glm::ivec2& p2, bool antiAliased) +{ + if (antiAliased) + { + XiaolinWu(colour, p1, p2); + } + else + { + if (std::abs(p2.y - p1.y) < std::abs(p2.x - p1.x)) + { + BresenhamHorizontal(colour, p1, p2); + } + else + { + BresenhamVertical(colour, p1, p2); + } + } +} + +//================================================================================= +void C_CPURasterizer::DrawCircle(const Colours::T_Colour& colour, const glm::ivec2& p, float radius, bool antiAliased) +{ + if (antiAliased) + { + float t1 = radius / 16; + float t2 = 0.f; + int y = 0; + int x = radius; + float xIntersect = 0.f; + while (x >= y) + { + m_view.Set(p + glm::ivec2{x, y}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{x, -y}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{-x, -y}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{-x, y}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{y, x}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{y, -x}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{-y, -x}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{-y, x}, glm::vec3{colour}); + ++y; + t1 = t1 + y; + t2 = t1 - x; + if (t2 >= 0) + { + t1 = t2; + --x; + } + } + } + else + { + float t1 = radius / 16; + float t2 = 0.f; + int y = 0; + int x = radius; + while (x >= y) + { + m_view.Set(p + glm::ivec2{x, y}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{x, -y}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{-x, -y}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{-x, y}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{y, x}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{y, -x}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{-y, -x}, glm::vec3{colour}); + m_view.Set(p + glm::ivec2{-y, x}, glm::vec3{colour}); + ++y; + t1 = t1 + y; + t2 = t1 - x; + if (t2 >= 0) + { + t1 = t2; + --x; + } + } + } +} + +//================================================================================= +void C_CPURasterizer::FloodFill(const Colours::T_Colour& colour, const glm::ivec2& p) +{ + ScanLineFloodFill(colour, p); +} + +//================================================================================= +void C_CPURasterizer::QueueFloodFill(const Colours::T_Colour& colour, const glm::ivec2& p) +{ + const glm::vec3 clearColor = m_view.Get(p); + std::queue open; + open.push(p); + std::array dirs{ + glm::ivec2{1, 0}, + glm::ivec2{-1, 0}, + glm::ivec2{0, 1}, + glm::ivec2{0, -1}, + }; + m_view.Set(p, glm::vec3{colour}); + while (open.empty() == false) + { + const auto current = open.front(); + open.pop(); + for (const auto& dir : dirs) + { + const glm::vec3 testedColour = m_view.Get(current + dir); + if (testedColour == clearColor) + { + m_view.Set(current + dir, glm::vec3{colour}); + open.push(current + dir); + } + } + } +} + +//================================================================================= +void C_CPURasterizer::ScanLineFloodFill(const Colours::T_Colour& colour, const glm::ivec2& p) +{ + const glm::vec3 clearColor = m_view.Get(p); + std::queue open; + const auto scanLine = [&](int min, int max, int line) { + bool spanAdded = false; + for (int i = min; i < max; ++i) + { + if (m_view.Get(glm::ivec2{i, line}) != clearColor) + { + spanAdded = false; + } + else if (!spanAdded) + { + open.push(glm::ivec2{i, line}); + spanAdded = true; + } + } + }; + open.push(p); + m_view.Set(p, glm::vec3{colour}); + while (open.empty() == false) + { + auto current = open.front(); + const static glm::ivec2 leftStep{1, 0}; + open.pop(); + auto leftCurrent = current; + while (m_view.Get(leftCurrent - leftStep) == clearColor) + { + leftCurrent -= leftStep; + } + while (m_view.Get(current + leftStep) == clearColor) + { + current += leftStep; + } + m_view.FillLineSpan(colour, current.y, leftCurrent.x, current.x); + scanLine(leftCurrent.x, current.x, current.y - 1); + scanLine(leftCurrent.x, current.x, current.y + 1); + } +} + +//================================================================================= +void C_CPURasterizer::BresenhamHorizontal(const Colours::T_Colour& colour, glm::ivec2 p1, glm::ivec2 p2) +{ + // Bresenham's algorithm + if (p1.x > p2.x) + { + std::swap(p1, p2); + } + + const int dx = p2.x - p1.x; + int dy = p2.y - p1.y; + unsigned int y = p1.y; + int pred = 2 * dy - dx; + int ydelta = 1; + if (dy < 0) + { + dy = -dy; + ydelta = -ydelta; + } + + for (int x = p1.x; x <= p2.x; ++x) + { + m_view.Set(glm::ivec2(x, y), glm::vec3(colour)); + if (pred > 0) + { + y += ydelta; + pred = pred + 2 * dy - 2 * dx; + } + else + { + pred = pred + 2 * dy; + } + } +} + +//================================================================================= +void C_CPURasterizer::BresenhamVertical(const Colours::T_Colour& colour, glm::ivec2 p1, glm::ivec2 p2) +{ + // Bresenham's algorithm + if (p1.y > p2.y) + { + std::swap(p1, p2); + } + + int dx = p2.x - p1.x; + const int dy = p2.y - p1.y; + unsigned int x = p1.x; + int pred = 2 * dx - dy; + int xdelta = 1; + if (dx < 0) + { + dx = -dx; + xdelta = -xdelta; + } + + for (int y = p1.y; y <= p2.y; ++y) + { + m_view.Set(glm::ivec2(x, y), glm::vec3(colour)); + if (pred > 0) + { + x += xdelta; + pred = pred + 2 * dx - 2 * dy; + } + else + { + pred = pred + 2 * dx; + } + } +} + +//================================================================================= +void C_CPURasterizer::XiaolinWu(const Colours::T_Colour& colour, glm::ivec2 p1, glm::ivec2 p2) +{ + bool vertical = std::abs(p2.y - p1.y) > std::abs(p2.x - p1.x); + if (vertical) + { + std::swap(p1.x, p1.y); + std::swap(p2.x, p2.y); + } + if (p1.x > p2.x) + { + std::swap(p1, p2); + } + + int dx = p2.x - p1.x; + const int dy = p2.y - p1.y; + float gradient = 1.0f; + if (dx != 0) + { + gradient = static_cast(dy) / dx; + } + // float yend = y0 + gradient * (xend - p1.x); + float yIntersect = p1.y + gradient; + + const auto fract = [](float x) { return x - std::floor(x); }; + // endpoints + + + if (vertical) + { + for (int x = p1.x + 1; x < p2.x; ++x) + { + // needs alpha blending + const auto fraction = fract(yIntersect); + m_view.DrawPixel(glm::ivec2(std::floor(yIntersect), x), glm::vec4(colour, 1.f - fraction)); + m_view.DrawPixel(glm::ivec2(std::floor(yIntersect) + 1, x), glm::vec4(colour, fraction)); + yIntersect = yIntersect + gradient; + } + } + else + { + for (int x = p1.x + 1; x < p2.x; ++x) + { + // needs alpha blending + const auto fraction = fract(yIntersect); + m_view.DrawPixel(glm::ivec2(x, std::floor(yIntersect)), glm::vec4(colour, 1.f - fraction)); + m_view.DrawPixel(glm::ivec2(x, std::floor(yIntersect) + 1), glm::vec4(colour, fraction)); + yIntersect = yIntersect + gradient; + } + } +} + +} // namespace GLEngine::Renderer \ No newline at end of file diff --git a/Renderer/Renderer/Render/CPURasterizer.h b/Renderer/Renderer/Render/CPURasterizer.h new file mode 100644 index 00000000..18405c6a --- /dev/null +++ b/Renderer/Renderer/Render/CPURasterizer.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +namespace GLEngine::Renderer { + +class RENDERER_API_EXPORT C_CPURasterizer { +public: + C_CPURasterizer(Renderer::C_TextureView& view); + void DrawLine(const Colours::T_Colour& colour, const glm::ivec2& p1, const glm::ivec2& p2, bool antiAliased = false); + void DrawCircle(const Colours::T_Colour& colour, const glm::ivec2& p, float radius, bool antiAliased = false); + void FloodFill(const Colours::T_Colour& colour, const glm::ivec2& p); + +private: + void BresenhamHorizontal(const Colours::T_Colour& colour, glm::ivec2 p1, glm::ivec2 p2); + void BresenhamVertical(const Colours::T_Colour& colour, glm::ivec2 p1, glm::ivec2 p2); + void XiaolinWu(const Colours::T_Colour& colour, glm::ivec2 p1, glm::ivec2 p2); + + void QueueFloodFill(const Colours::T_Colour& colour, const glm::ivec2& p); + void ScanLineFloodFill(const Colours::T_Colour& colour, const glm::ivec2& p); + Renderer::C_TextureView m_view; +}; +} // namespace GLEngine::Renderer \ No newline at end of file diff --git a/Renderer/Renderer/Textures/Storage/TextureLinearStorage.h b/Renderer/Renderer/Textures/Storage/TextureLinearStorage.h index c58ca3d2..75547698 100644 --- a/Renderer/Renderer/Textures/Storage/TextureLinearStorage.h +++ b/Renderer/Renderer/Textures/Storage/TextureLinearStorage.h @@ -31,6 +31,11 @@ template class C_TextureViewStorageCPU : public I_Texture [[nodiscard]] virtual std::uint8_t GetChannelOffset(E_TextureChannel element) const override; [[nodiscard]] virtual E_TextureTypes GetStorageType() const override; + /** + * Fills line span including start and end pixel + */ + void FillLineSpan(const glm::vec3& colour, unsigned int line, unsigned int start, unsigned int end) override; + protected: virtual void SetInternal(double value, std::size_t position) override; virtual void SetInternal(int value, std::size_t position) override; diff --git a/Renderer/Renderer/Textures/Storage/TextureLinearStorage.inl b/Renderer/Renderer/Textures/Storage/TextureLinearStorage.inl index 7f675013..f6bd2a9a 100644 --- a/Renderer/Renderer/Textures/Storage/TextureLinearStorage.inl +++ b/Renderer/Renderer/Textures/Storage/TextureLinearStorage.inl @@ -118,6 +118,19 @@ template void C_TextureViewStorageCPU::Se std::copy_n(static_cast(&realValue.x), m_Elements, it); } +//================================================================================= +template inline void C_TextureViewStorageCPU::FillLineSpan(const glm::vec3& colour, unsigned int line, unsigned int start, unsigned int end) +{ + glm::vec<4, internalFormat, glm::defaultp> realValue(Swizzle(glm::vec4{colour, 1.f})); + auto it = m_Data.begin(); + auto position = line * GetDimensions().x + start; + std::advance(it, position * m_Elements); + for (unsigned int i = start; i <= end; ++i, it += m_Elements) + { + std::copy_n(static_cast(&realValue.x), m_Elements, it); + } +} + //================================================================================= template glm::vec4 C_TextureViewStorageCPU::GetPixel(std::size_t pixelIndex) const { diff --git a/Renderer/Renderer/Textures/Storage/TextureStorage.h b/Renderer/Renderer/Textures/Storage/TextureStorage.h index c2609374..aae53592 100644 --- a/Renderer/Renderer/Textures/Storage/TextureStorage.h +++ b/Renderer/Renderer/Textures/Storage/TextureStorage.h @@ -78,6 +78,11 @@ class RENDERER_API_EXPORT I_TextureViewStorage { [[nodiscard]] virtual E_TextureTypes GetStorageType() const = 0; + /** + * Fills line span including start and end pixel. Allows for performance improvement in linear storages. + */ + virtual void FillLineSpan(const glm::vec3& colour, unsigned int line, unsigned int start, unsigned int end) = 0; + protected: virtual void SetInternal(double value, std::size_t position) = 0; virtual void SetInternal(int value, std::size_t position) = 0; diff --git a/Renderer/Renderer/Textures/TextureDefinitions.h b/Renderer/Renderer/Textures/TextureDefinitions.h index 245c681e..90d389c2 100644 --- a/Renderer/Renderer/Textures/TextureDefinitions.h +++ b/Renderer/Renderer/Textures/TextureDefinitions.h @@ -53,6 +53,7 @@ enum class E_BlendFactor }; //================================================================================= +// Source is colour written by fragment shader enum class E_BlendFunction { Add, //< Result = Src + Dst diff --git a/Renderer/Renderer/Textures/TextureView.cpp b/Renderer/Renderer/Textures/TextureView.cpp index f6aefb01..2b3f672b 100644 --- a/Renderer/Renderer/Textures/TextureView.cpp +++ b/Renderer/Renderer/Textures/TextureView.cpp @@ -11,9 +11,18 @@ C_TextureView::C_TextureView(I_TextureViewStorage* storage) : m_Storage(storage) , m_BorderColor(1, 0, 1, 0) , m_WrapFunction(E_WrapFunction::Repeat) + , m_EnableBlending(false) + , m_BlendOperation(E_BlendFunction::Add) { } +//================================================================================= +void C_TextureView::FillLineSpan(const Colours::T_Colour& colour, unsigned int line, unsigned int start, unsigned int end) +{ + if (line > 0 && line < m_Storage->GetDimensions().y - 1) + m_Storage->FillLineSpan(colour, line, std::max(start, 0u), std::min(end, m_Storage->GetDimensions().x - 1)); +} + //================================================================================= std::size_t C_TextureView::GetAddress(const glm::ivec2& uv) const { @@ -60,6 +69,12 @@ void C_TextureView::SetWrapFunction(E_WrapFunction wrap) m_WrapFunction = wrap; } +//================================================================================= +void C_TextureView::EnableBlending(bool enable) +{ + m_EnableBlending = enable; +} + //================================================================================= bool C_TextureView::UseBorderColor() const { @@ -124,6 +139,45 @@ void C_TextureView::ClearColor(const glm::vec4& colour) m_Storage->SetAll(colour); } +class BlendFunctionFactory { +public: + static const std::function GetBlendFunction(E_BlendFunction function) + { + switch (function) + { + case GLEngine::Renderer::E_BlendFunction::Add: + return [](const glm::vec3& dst, const glm::vec3& src) { return src + dst; }; + case GLEngine::Renderer::E_BlendFunction::Subtract: + return [](const glm::vec3& dst, const glm::vec3& src) { return src - dst; }; + case GLEngine::Renderer::E_BlendFunction::ReverseSubtract: + return [](const glm::vec3& dst, const glm::vec3& src) { return dst - src; }; + case GLEngine::Renderer::E_BlendFunction::Min: + return [](const glm::vec3& dst, const glm::vec3& src) { return glm::min(dst, src); }; + case GLEngine::Renderer::E_BlendFunction::Max: + return [](const glm::vec3& dst, const glm::vec3& src) { return glm::max(dst, src); }; + default: + GLE_ASSERT(false, "Unknown blend function."); + break; + } + return [](const glm::vec3& dst, const glm::vec3& src) { return src + dst; }; + } +}; + +//================================================================================= +void C_TextureView::DrawPixel(const glm::ivec2& coord, glm::vec4&& colour) +{ + if (!m_EnableBlending || colour.a >= 1.f) + { + Set(coord, glm::vec3{colour}); + } + else + { + const auto currentCol = Get(coord); + const auto alpha = colour.a; + Set(coord, BlendFunctionFactory::GetBlendFunction(m_BlendOperation)(glm::vec3(currentCol) * (1.f - alpha), glm::vec3{colour} * alpha)); + } +} + //================================================================================= const glm::uvec2 C_TextureView::GetDimensions() const { diff --git a/Renderer/Renderer/Textures/TextureView.h b/Renderer/Renderer/Textures/TextureView.h index d10dfde0..475289ef 100644 --- a/Renderer/Renderer/Textures/TextureView.h +++ b/Renderer/Renderer/Textures/TextureView.h @@ -1,8 +1,10 @@ #pragma once +#include #include #include #include + /** * This class serves as CPU side view to the texture independently whether lies on * CPU or GPU memory. @@ -50,6 +52,8 @@ class RENDERER_API_EXPORT C_TextureView { [[nodiscard]] E_WrapFunction GetWrapFunction() const; void SetWrapFunction(E_WrapFunction wrap); + void EnableBlending(bool enable = true); + template void Set(const glm::ivec2& uv, const T val, E_TextureChannel element); template ::is_vec>> void Set(const glm::ivec2& uv, T&& val); void SetBorderColor(const glm::vec4& color); @@ -62,6 +66,13 @@ class RENDERER_API_EXPORT C_TextureView { void ClearColor(const glm::vec4& colour); + void DrawPixel(const glm::ivec2& coord, glm::vec4&& colour); + + /** + * Fills line span including start and end pixel + */ + void FillLineSpan(const Colours::T_Colour& colour, unsigned int line, unsigned int start, unsigned int end); + protected: [[nodiscard]] std::size_t GetAddress(const glm::ivec2& uv) const; [[nodiscard]] std::size_t GetPixelAddress(const glm::uvec2& uv) const; @@ -73,6 +84,8 @@ class RENDERER_API_EXPORT C_TextureView { I_TextureViewStorage* m_Storage; // not owning ptr glm::vec4 m_BorderColor; E_WrapFunction m_WrapFunction; + bool m_EnableBlending; + E_BlendFunction m_BlendOperation; }; template <> glm::vec4 C_TextureView::GetBorderColor() const; diff --git a/Renderer/Renderer/Textures/TextureView.inl b/Renderer/Renderer/Textures/TextureView.inl index 0530aa08..52b04b91 100644 --- a/Renderer/Renderer/Textures/TextureView.inl +++ b/Renderer/Renderer/Textures/TextureView.inl @@ -107,7 +107,7 @@ template ::is_vec>*/> void C } else if constexpr (std::is_same_v, glm::vec3>) { - m_Storage->SetPixel(glm::vec4(val, 0.f), GetPixelAddress(uv)); + m_Storage->SetPixel(glm::vec4(val, 1.f), GetPixelAddress(uv)); } else { diff --git a/Utils/Utils/Reflection/Metadata.h b/Utils/Utils/Reflection/Metadata.h index 2991754e..c812440c 100644 --- a/Utils/Utils/Reflection/Metadata.h +++ b/Utils/Utils/Reflection/Metadata.h @@ -12,6 +12,7 @@ namespace Utils::Reflection { +// clang-format off template struct IsMetadataName : std::false_type {}; template constexpr bool IsMetadataName_v = IsMetadataName::value; @@ -28,13 +29,18 @@ template constexpr bool MemberIsOptional_v = MemberIsOptional: template <> struct ParentMetatype { using type = Parent; }; \ template <> struct IsMetadataName : std::true_type {} +template concept IsMetaclassConcept = IsMetadataName_v; + #define REGISTER_META_MEMBER_TYPE(member, Type) \ template <> struct MemberType {using type = Type;}; +template concept IsCorrectMetaClassMemberType = + std::is_same_v, T> || std::is_convertible_v>; +// clang-format on + namespace detail { //================================================================================= -template -[[nodiscard]] rttr::variant GetMetadata(Type&& arg) +template [[nodiscard]] rttr::variant GetMetadata(Type&& arg) { if constexpr (std::is_same_v, rttr::type>) { @@ -53,27 +59,27 @@ template return {}; } } -} +} // namespace detail //================================================================================= // Getters //================================================================================= // Type could be rttr::type, rttr::property, rttr::instance -template -MemberType_t GetMetadataMember(const Type& prop) +template requires IsMetaclassConcept MemberType_t GetMetadataMember(const Type& prop) { static_assert(IsMetadataName_v, "Given member name must be registered meta member."); const auto metadata = detail::GetMetadata(prop); - if constexpr (!MemberIsOptional_v) { + if constexpr (!MemberIsOptional_v) + { GLE_ASSERT(metadata.is_valid(), "Mandatory property metamember missing."); } return metadata.template get_value>(); } //================================================================================= -template -rttr::variant GetMetadataMember(const rttr::property& prop, const Enum member) +template requires IsMetaclassConcept rttr::variant GetMetadataMember(const rttr::property& prop, const Enum member) { + static_assert(std::is_enum_v, "Only enum accepted"); static_assert(IsMetadataName_v, "Given member name must be registered meta member."); const auto metadata = prop.get_metadata(member); GLE_ASSERT(MemberIsOptional_v || metadata.is_valid(), "Mandatory property metamember missing."); @@ -82,8 +88,7 @@ rttr::variant GetMetadataMember(const rttr::property& prop, const Enum member) //================================================================================= // Type could be rttr::type, rttr::property, rttr::instance -template -[[nodiscard]] bool HasMetadataMember(const Type& prop) +template requires IsMetaclassConcept [[nodiscard]] bool HasMetadataMember(const Type& prop) { static_assert(IsMetadataName_v, "Given member name must be registered meta member."); const auto metadata = detail::GetMetadata(prop); @@ -91,8 +96,7 @@ template } //================================================================================= -template -[[nodiscard]] bool IsMetaclass(const rttr::property& prop) +template requires IsMetaclassConcept [[nodiscard]] bool IsMetaclass(const rttr::property& prop) { static_assert(std::is_enum_v, "Class name could be only enum class."); static_assert(IsMetadataName_v, "Given class name must be registered meta member."); @@ -103,7 +107,7 @@ template // Registration //================================================================================= template -rttr::detail::metadata RegisterMetamember(const Type& value) +requires IsMetaclassConcept && IsCorrectMetaClassMemberType rttr::detail::metadata RegisterMetamember(const Type& value) { static_assert(std::is_enum_v, "Member name could be only enum class."); static_assert(IsMetadataName_v, "Given member name must be registered meta member."); @@ -112,8 +116,7 @@ rttr::detail::metadata RegisterMetamember(const Type& value) } //================================================================================= -template -rttr::detail::metadata RegisterMetaclass() +template requires IsMetaclassConcept rttr::detail::metadata RegisterMetaclass() { static_assert(std::is_enum_v, "Class name could be only enum class."); static_assert(IsMetadataName_v, "Given class name must be registered meta member."); @@ -125,47 +128,71 @@ rttr::detail::metadata RegisterMetaclass() //================================================================================= enum class Metatype { - Serialization, - GUI, // -> MetaGUI + Serialization, + GUI, // -> MetaGUI + GUIInfo, // -> MetaGUIInfo +}; +template <> struct IsMetadataName : std::true_type { }; -template <> struct IsMetadataName : std::true_type {}; enum class MetaGUI { - Slider, - Angle, - Colour, - Vec3, - Checkbox, - Text, - CustomGUIWidget, //-> function + Slider, + SliderInt, + Angle, + Colour, + Vec3, + Checkbox, + Text, + CustomGUIWidget, //-> function }; REGISTER_META_CLASS(MetaGUI, Metatype); enum class SerializationCls { - NoSerialize, - DerefSerialize, // dereference before serialization - MandatoryProperty, + NoSerialize, + DerefSerialize, // dereference before serialization + MandatoryProperty, }; REGISTER_META_CLASS(SerializationCls, Metatype); REGISTER_META_MEMBER_TYPE(SerializationCls::NoSerialize, bool); REGISTER_META_MEMBER_TYPE(SerializationCls::DerefSerialize, bool); REGISTER_META_MEMBER_TYPE(SerializationCls::MandatoryProperty, bool); -namespace UI +enum class MetaGUIInfo { -template struct UIMetaclassToType {}; + CollapsableGroup, // name of group +}; +REGISTER_META_CLASS(MetaGUIInfo, Metatype); +REGISTER_META_MEMBER_TYPE(MetaGUIInfo::CollapsableGroup, std::string); + +namespace UI { +template struct UIMetaclassToType { +}; template using UIMetaclassToType_t = typename UIMetaclassToType::type; -template <> struct UIMetaclassToType { using type = float; }; -template <> struct UIMetaclassToType { using type = float; }; -template <> struct UIMetaclassToType { using type = glm::vec3; }; -template <> struct UIMetaclassToType { using type = glm::vec3; }; -template <> struct UIMetaclassToType { using type = bool; }; -template <> struct UIMetaclassToType { using type = std::string; }; +template <> struct UIMetaclassToType { + using type = float; +}; +template <> struct UIMetaclassToType { + using type = int; +}; +template <> struct UIMetaclassToType { + using type = float; +}; +template <> struct UIMetaclassToType { + using type = glm::vec3; +}; +template <> struct UIMetaclassToType { + using type = glm::vec3; +}; +template <> struct UIMetaclassToType { + using type = bool; +}; +template <> struct UIMetaclassToType { + using type = std::string; +}; //================================================================================= -template -[[nodiscard]] bool IsUIMetaclass(const rttr::property& prop) +template [[nodiscard]] bool IsUIMetaclass(const rttr::property& prop) { const auto isRightClass = IsMetaclass(prop); if (isRightClass) @@ -180,17 +207,23 @@ template } //================================================================================= -template -[[nodiscard]] bool IsTypeUIMetaClass(Type&& arg) +template [[nodiscard]] bool IsTypeUIMetaClass(Type&& arg) { return detail::GetMetadata(arg).is_valid(); } enum class Slider { - Name, - Min, - Max, + Name, + Min, + Max, +}; + +enum class SliderInt +{ + Name, + Min, + Max, }; enum class Angle @@ -207,7 +240,7 @@ enum class Colour enum class Vec3 { - Name, + Name, }; enum class Checkbox @@ -228,6 +261,10 @@ REGISTER_META_CLASS(UI::Slider, MetaGUI); REGISTER_META_MEMBER_TYPE(UI::Slider::Name, std::string); REGISTER_META_MEMBER_TYPE(UI::Slider::Min, float); REGISTER_META_MEMBER_TYPE(UI::Slider::Max, float); +REGISTER_META_CLASS(UI::SliderInt, MetaGUI); +REGISTER_META_MEMBER_TYPE(UI::SliderInt::Name, std::string); +REGISTER_META_MEMBER_TYPE(UI::SliderInt::Min, int); +REGISTER_META_MEMBER_TYPE(UI::SliderInt::Max, int); REGISTER_META_CLASS(UI::Angle, MetaGUI); REGISTER_META_MEMBER_TYPE(UI::Angle::Name, std::string); @@ -248,4 +285,4 @@ REGISTER_META_CLASS(UI::Text, MetaGUI); REGISTER_META_CLASS(UI::CustomGUIWidget, MetaGUI); // for whole types REGISTER_META_MEMBER_TYPE(UI::CustomGUIWidget::DrawFunction, std::function); -} \ No newline at end of file +} // namespace Utils::Reflection \ No newline at end of file