From 228554bc9beb2b807426a4eb42d2c2606b46ae21 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 24 Jan 2020 04:00:28 +0700 Subject: [PATCH] basic gizmos system --- headers/enduro2d/high/_all.hpp | 1 + headers/enduro2d/high/_high.hpp | 1 + headers/enduro2d/high/inspector.hpp | 70 +++++- headers/enduro2d/high/inspector.inl | 38 +++- .../enduro2d/high/systems/gizmos_system.hpp | 26 +++ headers/enduro2d/utils/_utils.hpp | 39 ++++ headers/enduro2d/utils/imgui_utils.hpp | 28 +++ samples/bin/library/scenes/sample_04.json | 27 +-- sources/enduro2d/high/inspector.cpp | 9 +- sources/enduro2d/high/starter.cpp | 3 + .../enduro2d/high/systems/gizmos_system.cpp | 212 ++++++++++++++++++ .../high/widgets/inspector_widget.cpp | 2 +- 12 files changed, 430 insertions(+), 26 deletions(-) create mode 100644 headers/enduro2d/high/systems/gizmos_system.hpp create mode 100644 sources/enduro2d/high/systems/gizmos_system.cpp diff --git a/headers/enduro2d/high/_all.hpp b/headers/enduro2d/high/_all.hpp index 6d142acc..266215ca 100644 --- a/headers/enduro2d/high/_all.hpp +++ b/headers/enduro2d/high/_all.hpp @@ -45,6 +45,7 @@ #include "systems/flipbook_system.hpp" #include "systems/frame_system.hpp" +#include "systems/gizmos_system.hpp" #include "systems/label_system.hpp" #include "systems/render_system.hpp" #include "systems/script_system.hpp" diff --git a/headers/enduro2d/high/_high.hpp b/headers/enduro2d/high/_high.hpp index d58b674c..2120dc51 100644 --- a/headers/enduro2d/high/_high.hpp +++ b/headers/enduro2d/high/_high.hpp @@ -63,6 +63,7 @@ namespace e2d class flipbook_system; class frame_system; + class gizmos_system; class label_system; class render_system; class script_system; diff --git a/headers/enduro2d/high/inspector.hpp b/headers/enduro2d/high/inspector.hpp index 628e24a4..9595ee04 100644 --- a/headers/enduro2d/high/inspector.hpp +++ b/headers/enduro2d/high/inspector.hpp @@ -33,8 +33,57 @@ namespace e2d template <> class component_inspector<> { public: + class gizmos_context : private noncopyable { + public: + virtual void draw_line( + const v2f& p1, + const v2f& p2, + const color32& color) = 0; + + virtual void draw_rect( + const v2f& p1, + const v2f& p2, + const v2f& p3, + const v2f& p4, + const color32& color) = 0; + + virtual void draw_wire_rect( + const v2f& p1, + const v2f& p2, + const v2f& p3, + const v2f& p4, + const color32& color) = 0; + + virtual void draw_circle( + const v2f& center, + f32 radius, + u32 segments, + const color32& color) = 0; + + virtual void draw_wire_circle( + const v2f& center, + f32 radius, + u32 segments, + const color32& color) = 0; + + virtual bool selected() const noexcept = 0; + }; }; + namespace impl + { + template < typename Component > + using has_component_inspector = + decltype(std::declval>()( + std::declval&>())); + + template < typename Component > + using has_component_inspector_gizmos = + decltype(std::declval>()( + std::declval&>(), + std::declval::gizmos_context&>())); + } + // // inspector_drawer // @@ -51,7 +100,12 @@ namespace e2d inspector_drawer() = default; virtual ~inspector_drawer() noexcept = default; - virtual void operator()(gobject& go) const = 0; + virtual void operator()( + gobject& go) const = 0; + + virtual void operator()( + gobject& go, + component_inspector<>::gizmos_context& ctx) const = 0; }; template < typename Component > @@ -60,7 +114,12 @@ namespace e2d typed_inspector_drawer() = default; ~typed_inspector_drawer() noexcept final = default; - void operator()(gobject& go) const final; + void operator()( + gobject& go) const final; + + void operator()( + gobject& go, + component_inspector<>::gizmos_context& ctx) const final; private: component_inspector inspector_; }; @@ -78,7 +137,12 @@ namespace e2d template < typename Component > inspector& register_component(str_hash type); - void show_inspector_for(gobject& go); + void show_for( + gobject& go); + + void show_for( + gobject& go, + component_inspector<>::gizmos_context& ctx); private: mutable std::mutex mutex_; hash_map drawers_; diff --git a/headers/enduro2d/high/inspector.inl b/headers/enduro2d/high/inspector.inl index 25f150ee..17c84d11 100644 --- a/headers/enduro2d/high/inspector.inl +++ b/headers/enduro2d/high/inspector.inl @@ -17,17 +17,39 @@ namespace e2d namespace impl { template < typename Component > - void typed_inspector_drawer::operator()(gobject& go) const { - gcomponent co = go.component(); - if ( !co ) { - return; + void typed_inspector_drawer::operator()( + gobject& go) const + { + if constexpr ( utils::is_detected() ) { + gcomponent co = go.component(); + if ( !co ) { + return; + } + + ImGui::PushID(co.find()); + E2D_DEFER([](){ ImGui::PopID(); }); + + if ( ImGui::CollapsingHeader(component_inspector::title) ) { + inspector_(co); + } } + } - ImGui::PushID(co.find()); - E2D_DEFER([](){ ImGui::PopID(); }); + template < typename Component > + void typed_inspector_drawer::operator()( + gobject& go, + component_inspector<>::gizmos_context& ctx) const + { + if constexpr ( utils::is_detected() ) { + gcomponent co = go.component(); + if ( !co ) { + return; + } - if ( ImGui::CollapsingHeader(component_inspector::title) ) { - inspector_(co); + ImGui::PushID(co.find()); + E2D_DEFER([](){ ImGui::PopID(); }); + + inspector_(co, ctx); } } } diff --git a/headers/enduro2d/high/systems/gizmos_system.hpp b/headers/enduro2d/high/systems/gizmos_system.hpp new file mode 100644 index 00000000..0a095227 --- /dev/null +++ b/headers/enduro2d/high/systems/gizmos_system.hpp @@ -0,0 +1,26 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "_systems.hpp" + +namespace e2d +{ + class gizmos_system final + : public ecs::system> { + public: + gizmos_system(); + ~gizmos_system() noexcept; + + void process( + ecs::registry& owner, + const ecs::after& trigger) override; + private: + class internal_state; + std::unique_ptr state_; + }; +} diff --git a/headers/enduro2d/utils/_utils.hpp b/headers/enduro2d/utils/_utils.hpp index 8d69b9f4..1e3d217f 100644 --- a/headers/enduro2d/utils/_utils.hpp +++ b/headers/enduro2d/utils/_utils.hpp @@ -73,6 +73,12 @@ namespace e2d namespace e2d { + struct nonesuch { + ~nonesuch() = delete; + nonesuch(const nonesuch&) = delete; + void operator=(const nonesuch&) = delete; + }; + class noncopyable { public: noncopyable(const noncopyable&) = delete; @@ -212,4 +218,37 @@ namespace e2d::utils template < typename... Ts > overloaded(Ts...) -> overloaded; + + // + // detected + // + + namespace impl + { + template < typename Default + , typename AlwaysVoid + , template < typename... > class Op + , typename... Args > + struct detector { + using value_t = std::false_type; + using type = Default; + }; + + template < typename Default + , template < typename... > class Op + , typename... Args > + struct detector>, Op, Args...> { + using value_t = std::true_type; + using type = Op; + }; + } + + template < template < typename... > class Op, typename... Args > + using is_detected = typename impl::detector::value_t; + + template < template < typename... > class Op, typename... Args > + using detected_t = typename impl::detector::type; + + template < typename Default, template < typename... > class Op, typename... Args > + using detected_or = impl::detector; } diff --git a/headers/enduro2d/utils/imgui_utils.hpp b/headers/enduro2d/utils/imgui_utils.hpp index d79fda20..c6a0a9ec 100644 --- a/headers/enduro2d/utils/imgui_utils.hpp +++ b/headers/enduro2d/utils/imgui_utils.hpp @@ -84,4 +84,32 @@ namespace e2d::imgui_utils std::invoke(std::forward(f), std::forward(args)...); } + + template < typename F, typename... Args > + void with_fullscreen_window(str_view name, F&& f, Args&&... args) { + char* name_cstr = static_cast(E2D_CLEAR_ALLOCA(name.size() + 1)); + std::memcpy(name_cstr, name.data(), name.size()); + + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->Pos); + ImGui::SetNextWindowSize(ImGui::GetMainViewport()->Size); + + ImGui::PushStyleColor(ImGuiCol_Border, 0); + ImGui::PushStyleColor(ImGuiCol_WindowBg, 0); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.f); + E2D_DEFER([](){ + ImGui::PopStyleVar(1); + ImGui::PopStyleColor(2); + }); + + ImGui::Begin( + name_cstr, + nullptr, + ImGuiWindowFlags_NoNav | + ImGuiWindowFlags_NoInputs | + ImGuiWindowFlags_NoDecoration | + ImGuiWindowFlags_NoBringToFrontOnFocus); + E2D_DEFER([](){ ImGui::End(); }); + + std::invoke(std::forward(f), std::forward(args)...); + } } diff --git a/samples/bin/library/scenes/sample_04.json b/samples/bin/library/scenes/sample_04.json index 9ca55ed6..012d0001 100644 --- a/samples/bin/library/scenes/sample_04.json +++ b/samples/bin/library/scenes/sample_04.json @@ -27,20 +27,21 @@ "named" : { "name" : "ship(1)" } - } - }, { - "prototype" : "../prefabs/ship_prefab.json", - "components" : { - "sprite_renderer" : { - "tint" : [255,0,0,255] - }, - "actor" : { - "translation" : [50,-50] - }, - "named" : { - "name" : "ship(2)" + }, + "children" : [{ + "prototype" : "../prefabs/ship_prefab.json", + "components" : { + "sprite_renderer" : { + "tint" : [255,0,0,255] + }, + "actor" : { + "translation" : [100,0] + }, + "named" : { + "name" : "ship(2)" + } } - } + }] }, { "prototype" : "../prefabs/label_bm_prefab.json", "components" : { diff --git a/sources/enduro2d/high/inspector.cpp b/sources/enduro2d/high/inspector.cpp index 058370fa..988ac616 100644 --- a/sources/enduro2d/high/inspector.cpp +++ b/sources/enduro2d/high/inspector.cpp @@ -8,10 +8,17 @@ namespace e2d { - void inspector::show_inspector_for(gobject& go) { + void inspector::show_for(gobject& go) { std::lock_guard guard(mutex_); for ( auto& p : drawers_ ) { (*p.second)(go); } } + + void inspector::show_for(gobject& go, component_inspector<>::gizmos_context& ctx) { + std::lock_guard guard(mutex_); + for ( auto& p : drawers_ ) { + (*p.second)(go, ctx); + } + } } diff --git a/sources/enduro2d/high/starter.cpp b/sources/enduro2d/high/starter.cpp index 251ede53..c6fe3f76 100644 --- a/sources/enduro2d/high/starter.cpp +++ b/sources/enduro2d/high/starter.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -58,6 +59,8 @@ namespace .add_system()) .feature(ecs::feature() .add_system()) + .feature(ecs::feature() + .add_system()) .feature(ecs::feature() .add_system()) .feature(ecs::feature() diff --git a/sources/enduro2d/high/systems/gizmos_system.cpp b/sources/enduro2d/high/systems/gizmos_system.cpp new file mode 100644 index 00000000..f2d32472 --- /dev/null +++ b/sources/enduro2d/high/systems/gizmos_system.cpp @@ -0,0 +1,212 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include + +#include +#include + +#include +#include + +namespace +{ + using namespace e2d; + + class imgui_gizmos_context final : public component_inspector<>::gizmos_context { + public: + imgui_gizmos_context(editor& e, inspector& i) + : editor_(e) + , inspector_(i) {} + + bool setup(const ecs::const_entity& cam_e) { + if ( !cam_e.valid() || !ecs::exists_all()(cam_e) ) { + return false; + } + + const camera& cam = cam_e.get_component(); + const actor& cam_a = cam_e.get_component(); + + const const_node_iptr& cam_n = cam_a.node(); + if ( !cam_n ) { + return false; + } + + camera_vp_ = + math::inversed(cam_n->world_matrix()).first * + cam.projection() * + math::make_scale_matrix4(0.5f, -0.5f) * + math::make_translation_matrix4(0.5f, 0.5f) * + math::make_scale_matrix4(v2f(ImGui::GetIO().DisplaySize)); + + return true; + } + + bool show_for(const ecs::const_entity& e) { + if ( !e.valid() || !ecs::exists_all()(e) ) { + return false; + } + + const actor& e_a = e.get_component(); + + const const_node_iptr& e_n = e_a.node(); + if ( !e_n ) { + return false; + } + + gobject e_go = e_n->owner(); + if ( !e_go ) { + return false; + } + + go_matrix_ = e_n->world_matrix() * camera_vp_; + go_selected_ = e_go == editor_.selection(); + inspector_.show_for(e_go, *this); + return true; + } + public: + void draw_line( + const v2f& p1, + const v2f& p2, + const color32& color) final + { + const v2f pp1 = v2f(v4f(p1, 0.f, 1.f) * go_matrix_); + const v2f pp2 = v2f(v4f(p2, 0.f, 1.f) * go_matrix_); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddLine(pp1, pp2, colors::pack_color32(color)); + } + + void draw_rect( + const v2f& p1, + const v2f& p2, + const v2f& p3, + const v2f& p4, + const color32& color) final + { + const v2f pp1 = v2f(v4f(p1, 0.f, 1.f) * go_matrix_); + const v2f pp2 = v2f(v4f(p2, 0.f, 1.f) * go_matrix_); + const v2f pp3 = v2f(v4f(p3, 0.f, 1.f) * go_matrix_); + const v2f pp4 = v2f(v4f(p4, 0.f, 1.f) * go_matrix_); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddQuadFilled(pp1, pp2, pp3, pp4, colors::pack_color32(color)); + } + + void draw_wire_rect( + const v2f& p1, + const v2f& p2, + const v2f& p3, + const v2f& p4, + const color32& color) final + { + const v2f pp1 = v2f(v4f(p1, 0.f, 1.f) * go_matrix_); + const v2f pp2 = v2f(v4f(p2, 0.f, 1.f) * go_matrix_); + const v2f pp3 = v2f(v4f(p3, 0.f, 1.f) * go_matrix_); + const v2f pp4 = v2f(v4f(p4, 0.f, 1.f) * go_matrix_); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddQuad(pp1, pp2, pp3, pp4, colors::pack_color32(color)); + } + + void draw_circle( + const v2f& center, + f32 radius, + u32 segments, + const color32& color) final + { + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + for ( u32 i = 0, e = math::max(6u, segments); i < e; ++i ) { + const radf a = + math::two_pi() / + math::numeric_cast(e) * + math::numeric_cast(i); + const v2f p = + center + + v2f(math::cos(a), math::sin(a)) * + radius; + draw_list->PathLineTo(v2f(v4f(p, 0.f, 1.f) * go_matrix_)); + } + draw_list->PathFillConvex(colors::pack_color32(color)); + } + + void draw_wire_circle( + const v2f& center, + f32 radius, + u32 segments, + const color32& color) final + { + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + for ( u32 i = 0, e = math::max(6u, segments); i < e; ++i ) { + const radf a = + math::two_pi() / + math::numeric_cast(e) * + math::numeric_cast(i); + const v2f p = + center + + v2f(math::cos(a), math::sin(a)) * + radius; + draw_list->PathLineTo(v2f(v4f(p, 0.f, 1.f) * go_matrix_)); + } + draw_list->PathStroke(colors::pack_color32(color), true); + } + + bool selected() const noexcept final { + return go_selected_; + } + private: + editor& editor_; + inspector& inspector_; + m4f camera_vp_ = m4f::identity(); + m4f go_matrix_ = m4f::identity(); + bool go_selected_ = false; + }; +} + +namespace e2d +{ + // + // gizmos_system::internal_state + // + + class gizmos_system::internal_state final : private noncopyable { + public: + internal_state(dbgui& d, editor& e, inspector& i) + : dbgui_(d) + , gcontext_(e, i) {} + ~internal_state() noexcept = default; + + void process_render(const ecs::const_entity& cam_e, ecs::registry& owner) { + if ( !dbgui_.visible() || !gcontext_.setup(cam_e) ) { + return; + } + imgui_utils::with_fullscreen_window("e2d_gizmos_system_window", [this, &owner](){ + owner.for_joined_components([this](const ecs::const_entity& e, const actor&){ + gcontext_.show_for(e); + }); + }); + } + private: + dbgui& dbgui_; + imgui_gizmos_context gcontext_; + }; + + // + // gizmos_system + // + + gizmos_system::gizmos_system() + : state_(new internal_state(the(), the(), the())) {} + gizmos_system::~gizmos_system() noexcept = default; + + void gizmos_system::process( + ecs::registry& owner, + const ecs::after& trigger) + { + E2D_PROFILER_SCOPE("gizmos_system.process_render"); + state_->process_render(trigger.event.cam_e, owner); + } +} diff --git a/sources/enduro2d/high/widgets/inspector_widget.cpp b/sources/enduro2d/high/widgets/inspector_widget.cpp index deced669..d61ee8c8 100644 --- a/sources/enduro2d/high/widgets/inspector_widget.cpp +++ b/sources/enduro2d/high/widgets/inspector_widget.cpp @@ -23,7 +23,7 @@ namespace e2d::dbgui_widgets } gobject go = the().selection(); - the().show_inspector_for(go); + the().show_for(go); return true; }