diff --git a/headers/enduro2d/high/components/touchable.hpp b/headers/enduro2d/high/components/touchable.hpp index fb4ec6b1..edaa273a 100644 --- a/headers/enduro2d/high/components/touchable.hpp +++ b/headers/enduro2d/high/components/touchable.hpp @@ -155,16 +155,27 @@ namespace e2d::touchable_events (pressed) (released)) public: - mouse_evt(gobject target, types type, mouse_button button) + mouse_evt( + gobject target, + types type, + mouse_button button, + const v2f& local_point, + const v2f& world_point) : base_evt(target, true) , type_(type) - , button_(button) {} + , button_(button) + , local_point_(local_point) + , world_point_(world_point) {} [[nodiscard]] types type() const noexcept { return type_; } [[nodiscard]] mouse_button button() const noexcept { return button_; } + [[nodiscard]] const v2f& local_point() const noexcept { return local_point_; } + [[nodiscard]] const v2f& world_point() const noexcept { return world_point_; } private: types type_ = types::pressed; mouse_button button_ = mouse_button::left; + v2f local_point_ = v2f::zero(); + v2f world_point_ = v2f::zero(); }; ENUM_HPP_REGISTER_TRAITS(mouse_evt::types) diff --git a/samples/bin/library/scripts/emmy/components/touchable.lua b/samples/bin/library/scripts/emmy/components/touchable.lua index 1e0b7d80..c444b8a5 100644 --- a/samples/bin/library/scripts/emmy/components/touchable.lua +++ b/samples/bin/library/scripts/emmy/components/touchable.lua @@ -78,7 +78,13 @@ local touchable_mouse_evt = { type = "pressed", ---@type string - button = "left" + button = "left", + + ---@type v2f + local_point = v2f.zero(), + + --@type v2f + world_point = v2f.zero() } touchable.touchable_mouse_evt = touchable_mouse_evt diff --git a/samples/bin/library/scripts/sample_10/sample_10.lua b/samples/bin/library/scripts/sample_10/sample_10.lua index 045fe6bd..a557519f 100644 --- a/samples/bin/library/scripts/sample_10/sample_10.lua +++ b/samples/bin/library/scripts/sample_10/sample_10.lua @@ -14,11 +14,13 @@ end ---@param event touchable_event function M:on_event(go, type, event) if type == "touchable.mouse_evt" then - the_debug:trace(string.format("event: %q, type: %q, button: %q, target: %q", + the_debug:trace(string.format("event: %q\n-->type: %q\n-->button: %q\n-->target: %q\n-->local_point: %s\n-->world_point: %s", type, event.type, event.button, - event.target.named and event.target.named.name or "[unnamed]")) + event.target.named and event.target.named.name or "[unnamed]", + event.local_point, + event.world_point)) end end diff --git a/sources/enduro2d/high/bindings/high_binds/components/touchable_binds.cpp b/sources/enduro2d/high/bindings/high_binds/components/touchable_binds.cpp index bd8761bb..4c709409 100644 --- a/sources/enduro2d/high/bindings/high_binds/components/touchable_binds.cpp +++ b/sources/enduro2d/high/bindings/high_binds/components/touchable_binds.cpp @@ -210,6 +210,16 @@ namespace e2d::bindings::high "button", sol::property( [](const touchable_events::mouse_evt& c) -> str { return str(enum_hpp::to_string_or_throw(c.button())); + }), + + "local_point", sol::property( + [](const touchable_events::mouse_evt& c) -> v2f { + return c.local_point(); + }), + + "world_point", sol::property( + [](const touchable_events::mouse_evt& c) -> v2f { + return c.world_point(); }) ); 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 index 7454eb86..7b844050 100644 --- a/sources/enduro2d/high/systems/touch_system_impl/touch_system_base.hpp +++ b/sources/enduro2d/high/systems/touch_system_impl/touch_system_base.hpp @@ -24,19 +24,26 @@ namespace e2d::touch_system_impl namespace e2d::touch_system_impl { - class touchable_under_mouse final {}; + struct touchable_under_mouse final { + v2f local_point{v2f::zero()}; + v2f world_point{v2f::zero()}; + }; - struct world_space_rect_collider final { + struct world_space_collider_base { + m4f local_to_world{m4f::identity()}; + }; + + struct world_space_rect_collider final : world_space_collider_base { using local_space_collider_t = rect_collider; std::array points{}; }; - struct world_space_circle_collider final { + struct world_space_circle_collider final : world_space_collider_base { using local_space_collider_t = circle_collider; std::array points{}; }; - struct world_space_polygon_collider final { + struct world_space_polygon_collider final : world_space_collider_base { 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 index 2c141921..da268a99 100644 --- a/sources/enduro2d/high/systems/touch_system_impl/touch_system_colliders.cpp +++ b/sources/enduro2d/high/systems/touch_system_impl/touch_system_colliders.cpp @@ -11,10 +11,9 @@ namespace e2d::touch_system_impl::impl { - void update_world_space_collider( + void update_world_space_collider_points( world_space_rect_collider& dst, - const rect_collider& src, - const m4f& local_to_world) + const rect_collider& src) { const v2f& of = src.offset(); const v2f& hs = src.size() * 0.5f; @@ -24,16 +23,15 @@ namespace e2d::touch_system_impl::impl 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); + dst.points[0] = v3f(v4f(p1, 0.f, 1.f) * dst.local_to_world); + dst.points[1] = v3f(v4f(p2, 0.f, 1.f) * dst.local_to_world); + dst.points[2] = v3f(v4f(p3, 0.f, 1.f) * dst.local_to_world); + dst.points[3] = v3f(v4f(p4, 0.f, 1.f) * dst.local_to_world); } - void update_world_space_collider( + void update_world_space_collider_points( world_space_circle_collider& dst, - const circle_collider& src, - const m4f& local_to_world) + const circle_collider& src) { const v2f& of = src.offset(); for ( std::size_t i = 0, e = dst.points.size(); i < e; ++i ) { @@ -45,14 +43,13 @@ namespace e2d::touch_system_impl::impl of + v2f(math::cos(a), math::sin(a)) * src.radius(); - dst.points[i] = v3f(v4f(p, 0.f, 1.f) * local_to_world); + dst.points[i] = v3f(v4f(p, 0.f, 1.f) * dst.local_to_world); } } - void update_world_space_collider( + void update_world_space_collider_points( world_space_polygon_collider& dst, - const polygon_collider& src, - const m4f& local_to_world) + const polygon_collider& src) { const vector& src_points = src.points(); @@ -64,7 +61,7 @@ namespace e2d::touch_system_impl::impl 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)); + dst.points.push_back(v3f(v4f(p, 0.f, 1.f) * dst.local_to_world)); } } } @@ -80,18 +77,25 @@ namespace e2d::touch_system_impl::impl std::array< v2f, std::tuple_size_v - > points; + > screen_points; - std::transform(c.points.begin(), c.points.end(), points.begin(), [ + bool success = true; + std::transform(c.points.begin(), c.points.end(), screen_points.begin(), [ + &success, &camera_vp, &camera_viewport ](const v3f& point) noexcept { - return v2f(math::project(point, camera_vp, camera_viewport).first); + const auto& [screen_point, project_success] = math::project( + point, + camera_vp, + camera_viewport); + success = success && project_success; + return v2f(screen_point); }); - return !!pnpoly_aos( - math::numeric_cast(points.size()), - points.data()->data(), + return success && !!pnpoly_aos( + math::numeric_cast(screen_points.size()), + screen_points.data()->data(), mouse_p.x, mouse_p.y); } @@ -104,18 +108,25 @@ namespace e2d::touch_system_impl::impl std::array< v2f, std::tuple_size_v - > points; + > screen_points; - std::transform(c.points.begin(), c.points.end(), points.begin(), [ + bool success = true; + std::transform(c.points.begin(), c.points.end(), screen_points.begin(), [ + &success, &camera_vp, &camera_viewport ](const v3f& point) noexcept { - return v2f(math::project(point, camera_vp, camera_viewport).first); + const auto& [screen_point, project_success] = math::project( + point, + camera_vp, + camera_viewport); + success = success && project_success; + return v2f(screen_point); }); - return !!pnpoly_aos( - math::numeric_cast(points.size()), - points.data()->data(), + return success && !!pnpoly_aos( + math::numeric_cast(screen_points.size()), + screen_points.data()->data(), mouse_p.x, mouse_p.y); } @@ -125,23 +136,30 @@ namespace e2d::touch_system_impl::impl const m4f& camera_vp, const b2f& camera_viewport) { - static thread_local std::vector points; - E2D_DEFER([](){ points.clear(); }); + static thread_local std::vector screen_points; + E2D_DEFER([](){ screen_points.clear(); }); - if ( points.capacity() < c.points.size() ) { - points.reserve(math::max(points.capacity() * 2u, c.points.size())); + if ( screen_points.capacity() < c.points.size() ) { + screen_points.reserve(math::max(screen_points.capacity() * 2u, c.points.size())); } - std::transform(c.points.begin(), c.points.end(), std::back_inserter(points), [ + bool success = true; + std::transform(c.points.begin(), c.points.end(), std::back_inserter(screen_points), [ + &success, &camera_vp, &camera_viewport ](const v3f& point) noexcept { - return v2f(math::project(point, camera_vp, camera_viewport).first); + const auto& [screen_point, project_success] = math::project( + point, + camera_vp, + camera_viewport); + success = success && project_success; + return v2f(screen_point); }); - return !!pnpoly_aos( - math::numeric_cast(points.size()), - points.data()->data(), + return success && !!pnpoly_aos( + math::numeric_cast(screen_points.size()), + screen_points.data()->data(), mouse_p.x, mouse_p.y); } } 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 index 7d8db924..5bf4bf40 100644 --- a/sources/enduro2d/high/systems/touch_system_impl/touch_system_colliders.hpp +++ b/sources/enduro2d/high/systems/touch_system_impl/touch_system_colliders.hpp @@ -12,20 +12,17 @@ namespace e2d::touch_system_impl { namespace impl { - void update_world_space_collider( + void update_world_space_collider_points( world_space_rect_collider& dst, - const rect_collider& src, - const m4f& local_to_world); + const rect_collider& src); - void update_world_space_collider( + void update_world_space_collider_points( world_space_circle_collider& dst, - const circle_collider& src, - const m4f& local_to_world); + const circle_collider& src); - void update_world_space_collider( + void update_world_space_collider_points( world_space_polygon_collider& dst, - const polygon_collider& src, - const m4f& local_to_world); + const polygon_collider& src); template < typename WorldSpaceCollider > void update_world_space_colliders(ecs::registry& owner) { @@ -49,10 +46,13 @@ namespace e2d::touch_system_impl const touchable&, const actor& a) { - update_world_space_collider( - e.ensure_component(), - src, - a.node() ? a.node()->world_matrix() : m4f::identity()); + world_space_collider_t& world_space_collider = + e.ensure_component(); + + world_space_collider.local_to_world = + a.node() ? a.node()->world_matrix() : m4f::identity(); + + update_world_space_collider_points(world_space_collider, src); }, !ecs::exists_any< disabled, disabled, @@ -91,13 +91,42 @@ namespace e2d::touch_system_impl using world_space_collider_t = WorldSpaceCollider; using local_space_collider_t = typename WorldSpaceCollider::local_space_collider_t; + m4f inv_camera_vp{m4f::identity()}; + bool inv_camera_vp_success{false}; + + std::tie(inv_camera_vp, inv_camera_vp_success) = math::inversed(camera_vp, 0.f); + if ( !inv_camera_vp_success ) { + return; + } + owner.for_joined_components([ &mouse_p, &camera_vp, + &inv_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(); + const auto& [world_point, world_point_success] = math::unproject( + v3f(mouse_p, 0.f), + inv_camera_vp, + camera_viewport); + + if ( !world_point_success ) { + return; + } + + const auto& [world_to_local, world_to_local_success] = math::inversed( + c.local_to_world); + + if ( !world_to_local_success ) { + return; + } + + touchable_under_mouse& under_mouse_c + = e.ensure_component(); + + under_mouse_c.world_point = v2f(world_point); + under_mouse_c.local_point = v2f(v4f(world_point, 1.f) * world_to_local); } }, !ecs::exists_any< disabled, 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 index 8f717971..30418139 100644 --- a/sources/enduro2d/high/systems/touch_system_impl/touch_system_dispatcher.cpp +++ b/sources/enduro2d/high/systems/touch_system_impl/touch_system_dispatcher.cpp @@ -319,55 +319,60 @@ namespace e2d::touch_system_impl // // - for ( const auto& event : collector ) { - std::visit(utils::overloaded { - [](std::monostate){}, - [](const collector::mouse_move_event& event){ - E2D_UNUSED(event); - }, - [](const collector::mouse_scroll_event& event){ - E2D_UNUSED(event); - }, - [&target](const collector::mouse_button_event& event){ - switch ( event.action ) { - case mouse_button_action::press: { - const bool captured = dispatch_event( - target, - touchable_events::mouse_evt( + if ( target ) { + for ( const auto& event : collector ) { + std::visit(utils::overloaded { + [](std::monostate){}, + [](const collector::mouse_move_event& event){ + E2D_UNUSED(event); + }, + [](const collector::mouse_scroll_event& event){ + E2D_UNUSED(event); + }, + [&target](const collector::mouse_button_event& event){ + const auto make_mouse_evt = [ + &event, + &target + ](touchable_events::mouse_evt::types type){ + return touchable_events::mouse_evt( target, - touchable_events::mouse_evt::types::pressed, - event.button)); - if ( captured && event.button == mouse_button::left ) { - target.component().ensure(); - } - break; - } - case mouse_button_action::release: { - const bool captured = dispatch_event( - target, - touchable_events::mouse_evt( + type, + event.button, + target.component()->local_point, + target.component()->world_point); + }; + + switch ( event.action ) { + case mouse_button_action::press: { + const bool captured = dispatch_event( target, - touchable_events::mouse_evt::types::released, - event.button)); - if ( captured && event.button == mouse_button::left ) { - if ( target.component() ) { - target.component().remove(); - dispatch_event( - target, - touchable_events::mouse_evt( - target, - touchable_events::mouse_evt::types::clicked, - event.button)); + make_mouse_evt(touchable_events::mouse_evt::types::pressed)); + if ( captured && event.button == mouse_button::left ) { + target.component().ensure(); } + break; + } + case mouse_button_action::release: { + const bool captured = dispatch_event( + target, + make_mouse_evt(touchable_events::mouse_evt::types::released)); + if ( captured && event.button == mouse_button::left ) { + if ( target.component() ) { + target.component().remove(); + dispatch_event( + target, + make_mouse_evt(touchable_events::mouse_evt::types::clicked)); + } + } + break; + } + default: + E2D_ASSERT_MSG(false, "unexpected mouse button event action type"); + break; } - break; } - default: - E2D_ASSERT_MSG(false, "unexpected mouse button event action type"); - break; - } - } - }, event); + }, event); + } } } }