From 7f731f99bc2c9136b2c69fe3b9d079f7be40a1f5 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 5 Feb 2020 11:12:30 +0700 Subject: [PATCH] basic touch system impl --- headers/enduro2d/high/_all.hpp | 2 +- headers/enduro2d/high/_high.hpp | 2 +- .../{input_system.hpp => touch_system.hpp} | 6 +- samples/bin/library/scenes/sample_08.json | 32 +- .../library/scripts/sample_08/sample_08.lua | 9 +- .../bin/library/scripts/sample_08/ship.lua | 14 + sources/enduro2d/high/starter.cpp | 6 +- .../enduro2d/high/systems/input_system.cpp | 313 ------------------ .../enduro2d/high/systems/touch_system.cpp | 63 ++++ .../touch_system_impl/touch_system_base.hpp | 38 +++ .../touch_system_colliders.cpp | 206 ++++++++++++ .../touch_system_colliders.hpp | 111 +++++++ .../touch_system_dispatcher.cpp | 168 ++++++++++ .../touch_system_dispatcher.hpp | 25 ++ 14 files changed, 668 insertions(+), 327 deletions(-) rename headers/enduro2d/high/systems/{input_system.hpp => touch_system.hpp} (88%) delete mode 100644 sources/enduro2d/high/systems/input_system.cpp create mode 100644 sources/enduro2d/high/systems/touch_system.cpp create mode 100644 sources/enduro2d/high/systems/touch_system_impl/touch_system_base.hpp create mode 100644 sources/enduro2d/high/systems/touch_system_impl/touch_system_colliders.cpp create mode 100644 sources/enduro2d/high/systems/touch_system_impl/touch_system_colliders.hpp create mode 100644 sources/enduro2d/high/systems/touch_system_impl/touch_system_dispatcher.cpp create mode 100644 sources/enduro2d/high/systems/touch_system_impl/touch_system_dispatcher.hpp diff --git a/headers/enduro2d/high/_all.hpp b/headers/enduro2d/high/_all.hpp index 1c745151..d858a7a1 100644 --- a/headers/enduro2d/high/_all.hpp +++ b/headers/enduro2d/high/_all.hpp @@ -50,12 +50,12 @@ #include "systems/flipbook_system.hpp" #include "systems/frame_system.hpp" #include "systems/gizmos_system.hpp" -#include "systems/input_system.hpp" #include "systems/label_system.hpp" #include "systems/physics_system.hpp" #include "systems/render_system.hpp" #include "systems/script_system.hpp" #include "systems/spine_system.hpp" +#include "systems/touch_system.hpp" #include "systems/world_system.hpp" #include "address.hpp" diff --git a/headers/enduro2d/high/_high.hpp b/headers/enduro2d/high/_high.hpp index 3317f3de..a611d566 100644 --- a/headers/enduro2d/high/_high.hpp +++ b/headers/enduro2d/high/_high.hpp @@ -70,12 +70,12 @@ namespace e2d class flipbook_system; class frame_system; class gizmos_system; - class input_system; class label_system; class physics_system; class render_system; class script_system; class spine_system; + class touch_system; class world_system; template < typename Asset, typename Content > diff --git a/headers/enduro2d/high/systems/input_system.hpp b/headers/enduro2d/high/systems/touch_system.hpp similarity index 88% rename from headers/enduro2d/high/systems/input_system.hpp rename to headers/enduro2d/high/systems/touch_system.hpp index cd16aed5..177c1251 100644 --- a/headers/enduro2d/high/systems/input_system.hpp +++ b/headers/enduro2d/high/systems/touch_system.hpp @@ -10,11 +10,11 @@ namespace e2d { - class input_system final + class touch_system final : public ecs::system> { public: - input_system(); - ~input_system() noexcept final; + touch_system(); + ~touch_system() noexcept final; void process( ecs::registry& owner, diff --git a/samples/bin/library/scenes/sample_08.json b/samples/bin/library/scenes/sample_08.json index 78aba80c..51399424 100644 --- a/samples/bin/library/scenes/sample_08.json +++ b/samples/bin/library/scenes/sample_08.json @@ -51,14 +51,36 @@ "rigid_body" : { "type" : "dynamic" }, - "rect_collider" : { - "size" : [66,113] - }, "circle_collider" : { - "offset" : [50,25], + "offset" : [10,15], "radius" : 33 } - } + }, + "children" : [{ + "prototype" : "../prefabs/ship_prefab.json", + "components" : { + "named" : { + "name" : "ship(11)" + }, + "actor" : { + "translation" : [0,0], + "rotation" : 1, + "scale" : [0.5,0.5] + }, + "behaviour" : { + "script" : "../scripts/sample_08/ship.lua" + }, + "touchable" : {}, + "rigid_body" : { + "type" : "dynamic" + }, + "circle_collider" : { + "offset" : [10,15], + "radius" : 33 + } + }, + "children" : [] + }] },{ "prototype" : "../prefabs/ship_prefab.json", "components" : { diff --git a/samples/bin/library/scripts/sample_08/sample_08.lua b/samples/bin/library/scripts/sample_08/sample_08.lua index 2e3cfdf9..77e7b474 100644 --- a/samples/bin/library/scripts/sample_08/sample_08.lua +++ b/samples/bin/library/scripts/sample_08/sample_08.lua @@ -11,8 +11,15 @@ end ---@param go gobject ---@param type string ----@param event any +---@param event touchable_input_evt | touchable_mouse_evt function M:on_event(go, type, event) + if type == "touchable.mouse_evt" then + the_debug:trace(string.format( + "scene %q touched(%s %s)", + go.named and go.named.name or "---", + event.type, + event.button)) + end end ---@param go gobject diff --git a/samples/bin/library/scripts/sample_08/ship.lua b/samples/bin/library/scripts/sample_08/ship.lua index 9ed09365..b7089965 100644 --- a/samples/bin/library/scripts/sample_08/ship.lua +++ b/samples/bin/library/scripts/sample_08/ship.lua @@ -24,4 +24,18 @@ end function M.on_update(meta, go) end +---@param go gobject +---@param type string +---@param event touchable_mouse_evt +function M:on_event(go, type, event) + if type == "touchable.mouse_evt" then + the_debug:trace(string.format( + "ship %q touched(%s %s) - %s", + go.named and go.named.name or "---", + event.type, + event.button, + go == event.target and "self" or "other")) + end +end + return M diff --git a/sources/enduro2d/high/starter.cpp b/sources/enduro2d/high/starter.cpp index 53b699d1..b879ed28 100644 --- a/sources/enduro2d/high/starter.cpp +++ b/sources/enduro2d/high/starter.cpp @@ -36,12 +36,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include namespace @@ -70,8 +70,6 @@ namespace .add_system()) .feature(ecs::feature() .add_system()) - .feature(ecs::feature() - .add_system()) .feature(ecs::feature() .add_system()) .feature(ecs::feature() @@ -82,6 +80,8 @@ namespace .add_system()) .feature(ecs::feature() .add_system()) + .feature(ecs::feature() + .add_system()) .feature(ecs::feature() .add_system()); return !application_ || application_->initialize(); diff --git a/sources/enduro2d/high/systems/input_system.cpp b/sources/enduro2d/high/systems/input_system.cpp deleted file mode 100644 index 47581021..00000000 --- a/sources/enduro2d/high/systems/input_system.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/******************************************************************************* - * 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 -#include -#include - -#include -#include - -#define PNPOLY_IMPLEMENTATION -#include <3rdparty/pnpoly.h/pnpoly.h> - -namespace -{ - using namespace e2d; - - struct world_space_rect_collider { - using local_space_collider_t = rect_collider; - std::array points{}; - }; - - struct world_space_circle_collider { - using local_space_collider_t = circle_collider; - std::array points{}; - }; - - struct world_space_polygon_collider { - using local_space_collider_t = polygon_collider; - vector points{}; - }; - - void update_world_space_collider( - world_space_rect_collider& dst, - const rect_collider& src, - const m4f& local_to_world) - { - const v2f& of = src.offset(); - const v2f& hs = src.size() * 0.5f; - - const v2f p1{of.x - hs.x, of.y - hs.y}; - const v2f p2{of.x + hs.x, of.y - hs.y}; - const v2f p3{of.x + hs.x, of.y + hs.y}; - const v2f p4{of.x - hs.x, of.y + hs.y}; - - dst.points[0] = v3f(v4f(p1, 0.f, 1.f) * local_to_world); - dst.points[1] = v3f(v4f(p2, 0.f, 1.f) * local_to_world); - dst.points[2] = v3f(v4f(p3, 0.f, 1.f) * local_to_world); - dst.points[3] = v3f(v4f(p4, 0.f, 1.f) * local_to_world); - } - - void update_world_space_collider( - world_space_circle_collider& dst, - const circle_collider& src, - const m4f& local_to_world) - { - const v2f& of = src.offset(); - for ( std::size_t i = 0, e = dst.points.size(); i < e; ++i ) { - const radf a = - math::two_pi() / - math::numeric_cast(e) * - math::numeric_cast(i); - const v2f p = - of + - v2f(math::cos(a), math::sin(a)) * - src.radius(); - dst.points[i] = v3f(v4f(p, 0.f, 1.f) * local_to_world); - } - } - - void update_world_space_collider( - world_space_polygon_collider& dst, - const polygon_collider& src, - const m4f& local_to_world) - { - const vector& src_points = src.points(); - - dst.points.clear(); - if ( dst.points.capacity() < src_points.size() ) { - dst.points.reserve(math::max(dst.points.capacity() * 2u, src_points.size())); - } - - const v2f& of = src.offset(); - for ( std::size_t i = 0, e = src_points.size(); i < e; ++i ) { - const v2f p = of + src_points[i]; - dst.points.push_back(v3f(v4f(p, 0.f, 1.f) * local_to_world)); - } - } - - template < typename WorldSpaceCollider > - void update_world_space_colliders(ecs::registry& owner) { - using world_space_collider_t = WorldSpaceCollider; - using local_space_collider_t = typename WorldSpaceCollider::local_space_collider_t; - - ecsex::remove_all_components( - owner, - !ecs::exists_all()); - - owner.for_joined_components([]( - ecs::entity e, - const touchable&, - const local_space_collider_t& src, - const actor& a) - { - update_world_space_collider( - e.ensure_component(), - src, - a.node() ? a.node()->world_matrix() : m4f::identity()); - }, !ecs::exists_any< - disabled, - disabled, - disabled>()); - } -} - -namespace -{ - using namespace e2d; - - bool is_collider_under_mouse( - const world_space_rect_collider& c, - const v2f& mouse_p, - const m4f& camera_vp, - const b2f& camera_viewport) noexcept - { - std::array< - v2f, - std::tuple_size_v - > points; - - std::transform(c.points.begin(), c.points.end(), points.begin(), [ - &camera_vp, - &camera_viewport - ](const v3f& point) noexcept { - return v2f(math::project(point, camera_vp, camera_viewport).first); - }); - - return !!pnpoly_aos( - math::numeric_cast(points.size()), - points.data()->data(), - mouse_p.x, mouse_p.y); - } - - bool is_collider_under_mouse( - const world_space_circle_collider& c, - const v2f& mouse_p, - const m4f& camera_vp, - const b2f& camera_viewport) noexcept - { - std::array< - v2f, - std::tuple_size_v - > points; - - std::transform(c.points.begin(), c.points.end(), points.begin(), [ - &camera_vp, - &camera_viewport - ](const v3f& point) noexcept { - return v2f(math::project(point, camera_vp, camera_viewport).first); - }); - - return !!pnpoly_aos( - math::numeric_cast(points.size()), - points.data()->data(), - mouse_p.x, mouse_p.y); - } - - bool is_collider_under_mouse( - const world_space_polygon_collider& c, - const v2f& mouse_p, - const m4f& camera_vp, - const b2f& camera_viewport) noexcept - { - static thread_local std::vector points; - points.clear(); - - if ( points.capacity() < c.points.size() ) { - points.reserve(math::max(points.capacity() * 2u, c.points.size())); - } - - std::transform(c.points.begin(), c.points.end(), std::back_inserter(points), [ - &camera_vp, - &camera_viewport - ](const v3f& point) noexcept { - return v2f(math::project(point, camera_vp, camera_viewport).first); - }); - - return !!pnpoly_aos( - math::numeric_cast(points.size()), - points.data()->data(), - mouse_p.x, mouse_p.y); - } - - template < typename WorldSpaceCollider > - void update_colliders_under_mouse( - ecs::registry& owner, - const v2f& mouse_p, - const m4f& camera_vp, - const b2f& camera_viewport) - { - using world_space_collider_t = WorldSpaceCollider; - using local_space_collider_t = typename WorldSpaceCollider::local_space_collider_t; - owner.for_joined_components([ - &mouse_p, - &camera_vp, - &camera_viewport - ](ecs::entity e, const touchable&, const world_space_collider_t& c){ - if ( is_collider_under_mouse(c, mouse_p, camera_vp, camera_viewport) ) { - e.ensure_component(); - } - }, !ecs::exists_any< - disabled, - disabled, - disabled>()); - } -} - -namespace e2d -{ - // - // input_system::internal_state - // - - class input_system::internal_state final : private noncopyable { - public: - internal_state(input& i, window& w) - : input_(i) - , window_(w) {} - ~internal_state() noexcept = default; - - void process_update(ecs::registry& owner) { - owner.remove_all_components(); - owner.remove_all_components(); - - update_world_space_colliders(owner); - update_world_space_colliders(owner); - update_world_space_colliders(owner); - - owner.for_joined_components([this, &owner]( - const ecs::const_entity&, - const camera::input&, - const camera& camera) - { - const v2u& target_size = camera.target() - ? camera.target()->size() - : window_.framebuffer_size(); - - const v2f& mouse_p = math::normalized_to_point( - b2f(target_size.cast_to()), - math::point_to_normalized( - b2f(window_.real_size().cast_to()), - input_.mouse().cursor_pos())); - - const m4f& camera_vp = - camera.view() * camera.projection(); - - const b2f& camera_viewport = b2f( - camera.viewport().position * target_size.cast_to(), - camera.viewport().size * target_size.cast_to()); - - if ( !math::inside(camera_viewport, mouse_p) ) { - return; - } - - update_colliders_under_mouse( - owner, - mouse_p, - camera_vp, - camera_viewport); - - update_colliders_under_mouse( - owner, - mouse_p, - camera_vp, - camera_viewport); - - update_colliders_under_mouse( - owner, - mouse_p, - camera_vp, - camera_viewport); - }, !ecs::exists>()); - } - private: - input& input_; - window& window_; - }; - - // - // input_system - // - - input_system::input_system() - : state_(new internal_state(the(), the())) {} - input_system::~input_system() noexcept = default; - - void input_system::process( - ecs::registry& owner, - const ecs::before& trigger) - { - E2D_UNUSED(trigger); - E2D_PROFILER_SCOPE("input_system.process_update"); - state_->process_update(owner); - } -} diff --git a/sources/enduro2d/high/systems/touch_system.cpp b/sources/enduro2d/high/systems/touch_system.cpp new file mode 100644 index 00000000..6930306b --- /dev/null +++ b/sources/enduro2d/high/systems/touch_system.cpp @@ -0,0 +1,63 @@ +/******************************************************************************* + * 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 "touch_system_impl/touch_system_base.hpp" +#include "touch_system_impl/touch_system_colliders.hpp" +#include "touch_system_impl/touch_system_dispatcher.hpp" + +namespace +{ + using namespace e2d; + using namespace e2d::touch_system_impl; +} + +namespace e2d +{ + // + // touch_system::internal_state + // + + class touch_system::internal_state final : private noncopyable { + public: + internal_state(input& i, window& w) + : input_(i) + , window_(w) + , dispatcher_(window_.register_event_listener()) {} + + ~internal_state() noexcept { + window_.unregister_event_listener(dispatcher_); + } + + void process_update(ecs::registry& owner) { + update_world_space_colliders(owner); + update_world_space_colliders_under_mouse(input_, window_, owner); + dispatcher_.dispatch_all_events(owner); + } + private: + input& input_; + window& window_; + dispatcher& dispatcher_; + }; + + // + // touch_system + // + + touch_system::touch_system() + : state_(new internal_state(the(), the())) {} + touch_system::~touch_system() noexcept = default; + + void touch_system::process( + ecs::registry& owner, + const ecs::before& trigger) + { + E2D_UNUSED(trigger); + E2D_PROFILER_SCOPE("touch_system.process_update"); + state_->process_update(owner); + } +} diff --git a/sources/enduro2d/high/systems/touch_system_impl/touch_system_base.hpp b/sources/enduro2d/high/systems/touch_system_impl/touch_system_base.hpp new file mode 100644 index 00000000..8aa39ee4 --- /dev/null +++ b/sources/enduro2d/high/systems/touch_system_impl/touch_system_base.hpp @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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 + +#include +#include +#include +#include +#include +#include +#include + +namespace e2d::touch_system_impl +{ + class touchable_under_mouse final { + }; + + struct world_space_rect_collider final { + using local_space_collider_t = rect_collider; + std::array points{}; + }; + + struct world_space_circle_collider final { + using local_space_collider_t = circle_collider; + std::array points{}; + }; + + struct world_space_polygon_collider final { + using local_space_collider_t = polygon_collider; + vector points{}; + }; +} diff --git a/sources/enduro2d/high/systems/touch_system_impl/touch_system_colliders.cpp b/sources/enduro2d/high/systems/touch_system_impl/touch_system_colliders.cpp new file mode 100644 index 00000000..bd7d7d94 --- /dev/null +++ b/sources/enduro2d/high/systems/touch_system_impl/touch_system_colliders.cpp @@ -0,0 +1,206 @@ +/******************************************************************************* + * 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 "touch_system_colliders.hpp" + +#define PNPOLY_IMPLEMENTATION +#include <3rdparty/pnpoly.h/pnpoly.h> + +namespace e2d::touch_system_impl::impl +{ + void update_world_space_collider( + world_space_rect_collider& dst, + const rect_collider& src, + const m4f& local_to_world) + { + const v2f& of = src.offset(); + const v2f& hs = src.size() * 0.5f; + + const v2f p1{of.x - hs.x, of.y - hs.y}; + const v2f p2{of.x + hs.x, of.y - hs.y}; + const v2f p3{of.x + hs.x, of.y + hs.y}; + const v2f p4{of.x - hs.x, of.y + hs.y}; + + dst.points[0] = v3f(v4f(p1, 0.f, 1.f) * local_to_world); + dst.points[1] = v3f(v4f(p2, 0.f, 1.f) * local_to_world); + dst.points[2] = v3f(v4f(p3, 0.f, 1.f) * local_to_world); + dst.points[3] = v3f(v4f(p4, 0.f, 1.f) * local_to_world); + } + + void update_world_space_collider( + world_space_circle_collider& dst, + const circle_collider& src, + const m4f& local_to_world) + { + const v2f& of = src.offset(); + for ( std::size_t i = 0, e = dst.points.size(); i < e; ++i ) { + const radf a = + math::two_pi() / + math::numeric_cast(e) * + math::numeric_cast(i); + const v2f p = + of + + v2f(math::cos(a), math::sin(a)) * + src.radius(); + dst.points[i] = v3f(v4f(p, 0.f, 1.f) * local_to_world); + } + } + + void update_world_space_collider( + world_space_polygon_collider& dst, + const polygon_collider& src, + const m4f& local_to_world) + { + const vector& src_points = src.points(); + + dst.points.clear(); + if ( dst.points.capacity() < src_points.size() ) { + dst.points.reserve(math::max(dst.points.capacity() * 2u, src_points.size())); + } + + const v2f& of = src.offset(); + for ( std::size_t i = 0, e = src_points.size(); i < e; ++i ) { + const v2f p = of + src_points[i]; + dst.points.push_back(v3f(v4f(p, 0.f, 1.f) * local_to_world)); + } + } +} + +namespace e2d::touch_system_impl::impl +{ + bool is_world_space_collider_under_mouse( + const world_space_rect_collider& c, + const v2f& mouse_p, + const m4f& camera_vp, + const b2f& camera_viewport) + { + std::array< + v2f, + std::tuple_size_v + > points; + + std::transform(c.points.begin(), c.points.end(), points.begin(), [ + &camera_vp, + &camera_viewport + ](const v3f& point) noexcept { + return v2f(math::project(point, camera_vp, camera_viewport).first); + }); + + return !!pnpoly_aos( + math::numeric_cast(points.size()), + points.data()->data(), + mouse_p.x, mouse_p.y); + } + + bool is_world_space_collider_under_mouse( + const world_space_circle_collider& c, + const v2f& mouse_p, + const m4f& camera_vp, + const b2f& camera_viewport) + { + std::array< + v2f, + std::tuple_size_v + > points; + + std::transform(c.points.begin(), c.points.end(), points.begin(), [ + &camera_vp, + &camera_viewport + ](const v3f& point) noexcept { + return v2f(math::project(point, camera_vp, camera_viewport).first); + }); + + return !!pnpoly_aos( + math::numeric_cast(points.size()), + points.data()->data(), + mouse_p.x, mouse_p.y); + } + + bool is_world_space_collider_under_mouse( + const world_space_polygon_collider& c, + const v2f& mouse_p, + const m4f& camera_vp, + const b2f& camera_viewport) + { + static thread_local std::vector points; + E2D_DEFER([](){ points.clear(); }); + + if ( points.capacity() < c.points.size() ) { + points.reserve(math::max(points.capacity() * 2u, c.points.size())); + } + + std::transform(c.points.begin(), c.points.end(), std::back_inserter(points), [ + &camera_vp, + &camera_viewport + ](const v3f& point) noexcept { + return v2f(math::project(point, camera_vp, camera_viewport).first); + }); + + return !!pnpoly_aos( + math::numeric_cast(points.size()), + points.data()->data(), + mouse_p.x, mouse_p.y); + } +} + +namespace e2d::touch_system_impl +{ + void update_world_space_colliders(ecs::registry& owner) { + impl::update_world_space_colliders(owner); + impl::update_world_space_colliders(owner); + impl::update_world_space_colliders(owner); + } + + void update_world_space_colliders_under_mouse(input& input, window& window, ecs::registry& owner) { + owner.remove_all_components(); + owner.for_joined_components([&input, &window, &owner]( + const ecs::const_entity&, + const camera::input&, + const camera& camera) + { + const v2u& target_size = camera.target() + ? camera.target()->size() + : window.framebuffer_size(); + + const v2f& mouse_p = math::normalized_to_point( + b2f(target_size.cast_to()), + math::point_to_normalized( + b2f(window.real_size().cast_to()), + input.mouse().cursor_pos())); + + const m4f& camera_vp = + camera.view() * camera.projection(); + + const b2f& camera_viewport = b2f( + camera.viewport().position * target_size.cast_to(), + camera.viewport().size * target_size.cast_to()); + + if ( !math::inside(camera_viewport, mouse_p) ) { + return; + } + + impl::update_world_space_colliders_under_mouse( + owner, + mouse_p, + camera_vp, + camera_viewport); + + impl::update_world_space_colliders_under_mouse( + owner, + mouse_p, + camera_vp, + camera_viewport); + + impl::update_world_space_colliders_under_mouse( + owner, + mouse_p, + camera_vp, + camera_viewport); + }, !ecs::exists_any< + disabled, + disabled>()); + } +} diff --git a/sources/enduro2d/high/systems/touch_system_impl/touch_system_colliders.hpp b/sources/enduro2d/high/systems/touch_system_impl/touch_system_colliders.hpp new file mode 100644 index 00000000..c10ff52e --- /dev/null +++ b/sources/enduro2d/high/systems/touch_system_impl/touch_system_colliders.hpp @@ -0,0 +1,111 @@ +/******************************************************************************* + * 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 "touch_system_base.hpp" + +namespace e2d::touch_system_impl +{ + namespace impl + { + void update_world_space_collider( + world_space_rect_collider& dst, + const rect_collider& src, + const m4f& local_to_world); + + void update_world_space_collider( + world_space_circle_collider& dst, + const circle_collider& src, + const m4f& local_to_world); + + void update_world_space_collider( + world_space_polygon_collider& dst, + const polygon_collider& src, + const m4f& local_to_world); + + template < typename WorldSpaceCollider > + void update_world_space_colliders(ecs::registry& owner) { + using world_space_collider_t = WorldSpaceCollider; + using local_space_collider_t = typename WorldSpaceCollider::local_space_collider_t; + + ecsex::remove_all_components( + owner, + !ecs::exists_all< + touchable, + local_space_collider_t>() || + ecs::exists_any< + disabled, + disabled, + disabled, + disabled>()); + + owner.for_joined_components([]( + ecs::entity e, + const local_space_collider_t& src, + const touchable&, + const actor& a) + { + update_world_space_collider( + e.ensure_component(), + src, + a.node() ? a.node()->world_matrix() : m4f::identity()); + }, !ecs::exists_any< + disabled, + disabled, + disabled, + disabled>()); + } + } + + namespace impl + { + bool is_world_space_collider_under_mouse( + const world_space_rect_collider& c, + const v2f& mouse_p, + const m4f& camera_vp, + const b2f& camera_viewport); + + bool is_world_space_collider_under_mouse( + const world_space_circle_collider& c, + const v2f& mouse_p, + const m4f& camera_vp, + const b2f& camera_viewport); + + bool is_world_space_collider_under_mouse( + const world_space_polygon_collider& c, + const v2f& mouse_p, + const m4f& camera_vp, + const b2f& camera_viewport); + + template < typename WorldSpaceCollider > + void update_world_space_colliders_under_mouse( + ecs::registry& owner, + const v2f& mouse_p, + const m4f& camera_vp, + const b2f& camera_viewport) + { + using world_space_collider_t = WorldSpaceCollider; + using local_space_collider_t = typename WorldSpaceCollider::local_space_collider_t; + + owner.for_joined_components([ + &mouse_p, + &camera_vp, + &camera_viewport + ](ecs::entity e, const touchable&, const world_space_collider_t& c){ + if ( is_world_space_collider_under_mouse(c, mouse_p, camera_vp, camera_viewport) ) { + e.ensure_component(); + } + }, !ecs::exists_any< + disabled, + disabled, + disabled>()); + } + } + + void update_world_space_colliders(ecs::registry& owner); + void update_world_space_colliders_under_mouse(input& input, window& window, ecs::registry& owner); +} diff --git a/sources/enduro2d/high/systems/touch_system_impl/touch_system_dispatcher.cpp b/sources/enduro2d/high/systems/touch_system_impl/touch_system_dispatcher.cpp new file mode 100644 index 00000000..156e2b9c --- /dev/null +++ b/sources/enduro2d/high/systems/touch_system_impl/touch_system_dispatcher.cpp @@ -0,0 +1,168 @@ +/******************************************************************************* + * 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 "touch_system_dispatcher.hpp" + +namespace +{ + using namespace e2d; + using namespace e2d::touch_system_impl; + + gobject find_event_target(const ecs::registry& owner) { + static thread_local vector> scenes; + + E2D_DEFER([](){ scenes.clear(); }); + ecsex::extract_components( + owner, + std::back_inserter(scenes), + !ecs::exists_any< + disabled, + disabled>()); + + std::sort(scenes.begin(), scenes.end(), [](const auto& l, const auto& r){ + return std::get(l).depth() >= std::get(r).depth(); + }); + + for ( const auto& t : scenes ) { + gobject target = nodes::find_component_from_children( + std::get(t).node(), + nodes::options() + .reversed(true) + .recursive(true) + .include_root(true)).owner(); + if ( target ) { + return target; + } + } + + return gobject(); + } + + template < typename E > + void dispatch_event(E&& event) { + gobject target = event.target(); + if ( !target ) { + return; + } + + const_gcomponent target_actor = target.component(); + const_gcomponent target_touchable = target.component(); + if ( !target_actor || !target_touchable ) { + return; + } + + // + // parents + // + + static thread_local std::vector> parents; + E2D_DEFER([](){ parents.clear(); }); + + nodes::extract_components_from_parents( + target_actor->node(), + std::back_inserter(parents), + nodes::options().recursive(true)); + + // + // capturing + // + + if ( event.capturing() ) { + for ( auto iter = parents.rbegin(); iter != parents.rend(); ++iter ) { + const bool parent_disabled = ecs::exists_any< + disabled, + disabled + >()(iter->owner().raw_entity()); + if ( !parent_disabled && !(*iter)->capturing() ) { + return; + } + } + } + + // + // targeting + // + + target.component>().ensure().add(event); + + // + // bubbling + // + + if ( event.bubbling() && target_touchable->bubbling() ) { + for ( auto iter = parents.begin(); iter != parents.end(); ++iter ) { + const bool parent_disabled = ecs::exists_any< + disabled, + disabled + >()(iter->owner().raw_entity()); + if ( !parent_disabled ) { + iter->owner().component>().ensure().add(event); + if ( !(*iter)->bubbling() ) { + return; + } + } + } + } + } +} + +namespace e2d::touch_system_impl +{ + void dispatcher::dispatch_all_events(ecs::registry& owner) { + E2D_DEFER([this](){ events_.clear(); }); + + owner.for_each_component>([ + ](const ecs::const_entity&, events& es) { + es.clear(); + }); + + if ( events_.empty() ) { + return; + } + + gobject target = find_event_target(owner); + if ( !target ) { + return; + } + + for ( const auto& event : events_ ) { + std::visit(utils::overloaded { + [](std::monostate){}, + [&target](auto event_copy){ + dispatch_event(event_copy.target(target)); + } + }, event); + } + } + + void dispatcher::on_mouse_button( + mouse_button button, + mouse_button_action action) noexcept + { + switch ( action ) { + case mouse_button_action::press: + events_.push_back(touchable_events::mouse_evt( + gobject(), + touchable_events::mouse_evt::types::pressed, + button)); + break; + case mouse_button_action::release: + events_.push_back(touchable_events::mouse_evt( + gobject(), + touchable_events::mouse_evt::types::released, + button)); + break; + case mouse_button_action::unknown: + break; + default: + E2D_ASSERT_MSG(false, "unexpected mouse action"); + break; + } + } +} diff --git a/sources/enduro2d/high/systems/touch_system_impl/touch_system_dispatcher.hpp b/sources/enduro2d/high/systems/touch_system_impl/touch_system_dispatcher.hpp new file mode 100644 index 00000000..ccac6311 --- /dev/null +++ b/sources/enduro2d/high/systems/touch_system_impl/touch_system_dispatcher.hpp @@ -0,0 +1,25 @@ +/******************************************************************************* + * 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 "touch_system_base.hpp" + +namespace e2d::touch_system_impl +{ + class dispatcher final : public window::event_listener { + public: + dispatcher() = default; + + void dispatch_all_events(ecs::registry& owner); + private: + void on_mouse_button( + mouse_button button, + mouse_button_action action) noexcept final; + private: + vector events_; + }; +}