basic gizmos system

This commit is contained in:
2020-01-24 04:00:28 +07:00
parent 5ba9b666bd
commit 228554bc9b
12 changed files with 430 additions and 26 deletions

View File

@@ -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"

View File

@@ -63,6 +63,7 @@ namespace e2d
class flipbook_system;
class frame_system;
class gizmos_system;
class label_system;
class render_system;
class script_system;

View File

@@ -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<component_inspector<Component>>()(
std::declval<gcomponent<Component>&>()));
template < typename Component >
using has_component_inspector_gizmos =
decltype(std::declval<component_inspector<Component>>()(
std::declval<gcomponent<Component>&>(),
std::declval<component_inspector<>::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<Component> 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<str_hash, impl::inspector_drawer_iptr> drawers_;

View File

@@ -17,17 +17,39 @@ namespace e2d
namespace impl
{
template < typename Component >
void typed_inspector_drawer<Component>::operator()(gobject& go) const {
gcomponent<Component> co = go.component<Component>();
if ( !co ) {
return;
void typed_inspector_drawer<Component>::operator()(
gobject& go) const
{
if constexpr ( utils::is_detected<has_component_inspector, Component>() ) {
gcomponent<Component> co = go.component<Component>();
if ( !co ) {
return;
}
ImGui::PushID(co.find());
E2D_DEFER([](){ ImGui::PopID(); });
if ( ImGui::CollapsingHeader(component_inspector<Component>::title) ) {
inspector_(co);
}
}
}
ImGui::PushID(co.find());
E2D_DEFER([](){ ImGui::PopID(); });
template < typename Component >
void typed_inspector_drawer<Component>::operator()(
gobject& go,
component_inspector<>::gizmos_context& ctx) const
{
if constexpr ( utils::is_detected<has_component_inspector_gizmos, Component>() ) {
gcomponent<Component> co = go.component<Component>();
if ( !co ) {
return;
}
if ( ImGui::CollapsingHeader(component_inspector<Component>::title) ) {
inspector_(co);
ImGui::PushID(co.find());
E2D_DEFER([](){ ImGui::PopID(); });
inspector_(co, ctx);
}
}
}

View File

@@ -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<ecs::after<systems::render_event>> {
public:
gizmos_system();
~gizmos_system() noexcept;
void process(
ecs::registry& owner,
const ecs::after<systems::render_event>& trigger) override;
private:
class internal_state;
std::unique_ptr<internal_state> state_;
};
}

View File

@@ -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<Ts...>;
//
// 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<Default, std::void_t<Op<Args...>>, Op, Args...> {
using value_t = std::true_type;
using type = Op<Args...>;
};
}
template < template < typename... > class Op, typename... Args >
using is_detected = typename impl::detector<nonesuch, void, Op, Args...>::value_t;
template < template < typename... > class Op, typename... Args >
using detected_t = typename impl::detector<nonesuch, void, Op, Args...>::type;
template < typename Default, template < typename... > class Op, typename... Args >
using detected_or = impl::detector<Default, void, Op, Args...>;
}

View File

@@ -84,4 +84,32 @@ namespace e2d::imgui_utils
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
template < typename F, typename... Args >
void with_fullscreen_window(str_view name, F&& f, Args&&... args) {
char* name_cstr = static_cast<char*>(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>(f), std::forward<Args>(args)...);
}
}

View File

@@ -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" : {

View File

@@ -8,10 +8,17 @@
namespace e2d
{
void inspector::show_inspector_for(gobject& go) {
void inspector::show_for(gobject& go) {
std::lock_guard<std::mutex> guard(mutex_);
for ( auto& p : drawers_ ) {
(*p.second)(go);
}
}
void inspector::show_for(gobject& go, component_inspector<>::gizmos_context& ctx) {
std::lock_guard<std::mutex> guard(mutex_);
for ( auto& p : drawers_ ) {
(*p.second)(go, ctx);
}
}
}

View File

@@ -30,6 +30,7 @@
#include <enduro2d/high/systems/flipbook_system.hpp>
#include <enduro2d/high/systems/frame_system.hpp>
#include <enduro2d/high/systems/gizmos_system.hpp>
#include <enduro2d/high/systems/label_system.hpp>
#include <enduro2d/high/systems/render_system.hpp>
#include <enduro2d/high/systems/script_system.hpp>
@@ -58,6 +59,8 @@ namespace
.add_system<flipbook_system>())
.feature<struct frame_feature>(ecs::feature()
.add_system<frame_system>())
.feature<struct gizmos_feature>(ecs::feature()
.add_system<gizmos_system>())
.feature<struct label_feature>(ecs::feature()
.add_system<label_system>())
.feature<struct render_feature>(ecs::feature()

View File

@@ -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 <enduro2d/high/systems/gizmos_system.hpp>
#include <enduro2d/high/components/actor.hpp>
#include <enduro2d/high/components/camera.hpp>
#include <enduro2d/high/editor.hpp>
#include <enduro2d/high/inspector.hpp>
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<actor, camera, camera::gizmos>()(cam_e) ) {
return false;
}
const camera& cam = cam_e.get_component<camera>();
const actor& cam_a = cam_e.get_component<actor>();
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<actor>()(e) ) {
return false;
}
const actor& e_a = e.get_component<actor>();
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<f32>() /
math::numeric_cast<f32>(e) *
math::numeric_cast<f32>(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<f32>() /
math::numeric_cast<f32>(e) *
math::numeric_cast<f32>(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<actor>([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<dbgui>(), the<editor>(), the<inspector>())) {}
gizmos_system::~gizmos_system() noexcept = default;
void gizmos_system::process(
ecs::registry& owner,
const ecs::after<systems::render_event>& trigger)
{
E2D_PROFILER_SCOPE("gizmos_system.process_render");
state_->process_render(trigger.event.cam_e, owner);
}
}

View File

@@ -23,7 +23,7 @@ namespace e2d::dbgui_widgets
}
gobject go = the<editor>().selection();
the<inspector>().show_inspector_for(go);
the<inspector>().show_for(go);
return true;
}