💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

135 lines
4.3 KiB

  1. /*
  2. * Copyright (C) 2021 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "collision.hpp"
  20. #include "game/component/transform.hpp"
  21. #include "game/component/picking.hpp"
  22. #include "geom/primitive/intersection.hpp"
  23. #include "geom/primitive/plane.hpp"
  24. #include "math/transform-operators.hpp"
  25. #include <limits>
  26. namespace game {
  27. namespace system {
  28. collision::collision(entity::registry& registry):
  29. updatable(registry)
  30. {
  31. registry.on_construct<component::collision>().connect<&collision::on_collision_construct>(this);
  32. registry.on_update<component::collision>().connect<&collision::on_collision_update>(this);
  33. registry.on_destroy<component::collision>().connect<&collision::on_collision_destroy>(this);
  34. }
  35. void collision::update(double t, double dt)
  36. {
  37. registry.on_construct<component::collision>().disconnect<&collision::on_collision_construct>(this);
  38. registry.on_update<component::collision>().disconnect<&collision::on_collision_update>(this);
  39. registry.on_destroy<component::collision>().disconnect<&collision::on_collision_destroy>(this);
  40. }
  41. entity::id collision::pick_nearest(const geom::primitive::ray<float, 3>& ray, std::uint32_t flags) const
  42. {
  43. entity::id nearest_eid = entt::null;
  44. float nearest_distance = std::numeric_limits<float>::infinity();
  45. // For each entity with picking and transform components
  46. registry.view<component::picking, component::transform>().each
  47. (
  48. [&](entity::id entity_id, const auto& picking, const auto& transform)
  49. {
  50. // Skip entity if picking flags don't match
  51. if (!~(flags | picking.flags))
  52. return;
  53. // Transform picking sphere
  54. const geom::primitive::sphere<float> sphere =
  55. {
  56. transform.world * picking.sphere.center,
  57. picking.sphere.radius * math::max(transform.world.scale)
  58. };
  59. // Test for intersection between ray and sphere
  60. auto result = geom::primitive::intersection(ray, sphere);
  61. if (result)
  62. {
  63. float t0 = std::get<0>(*result);
  64. float t1 = std::get<1>(*result);
  65. if (t0 < nearest_distance)
  66. {
  67. nearest_eid = entity_id;
  68. nearest_distance = t0;
  69. }
  70. }
  71. }
  72. );
  73. return nearest_eid;
  74. }
  75. entity::id collision::pick_nearest(const float3& origin, const float3& normal, std::uint32_t flags) const
  76. {
  77. entity::id nearest_eid = entt::null;
  78. float nearest_sqr_distance = std::numeric_limits<float>::infinity();
  79. // Construct picking plane
  80. const geom::primitive::plane<float> picking_plane = geom::primitive::plane<float>(origin, normal);
  81. // For each entity with picking and transform components
  82. registry.view<component::picking, component::transform>().each
  83. (
  84. [&](entity::id entity_id, const auto& picking, const auto& transform)
  85. {
  86. // Skip entity if picking flags don't match
  87. if (!~(flags | picking.flags))
  88. return;
  89. // Transform picking sphere center
  90. float3 picking_sphere_center = transform.world * picking.sphere.center;
  91. // Skip entity if picking sphere center has negative distance from picking plane
  92. if (picking_plane.distance(picking_sphere_center) < 0.0f)
  93. return;
  94. // Measure distance from picking plane origin to picking sphere center
  95. const float sqr_distance = math::sqr_distance(picking_sphere_center, origin);
  96. // Check if entity is nearer than the current nearest entity
  97. if (sqr_distance < nearest_sqr_distance)
  98. {
  99. nearest_eid = entity_id;
  100. nearest_sqr_distance = sqr_distance;
  101. }
  102. }
  103. );
  104. return nearest_eid;
  105. }
  106. void collision::on_collision_construct(entity::registry& registry, entity::id entity_id)
  107. {}
  108. void collision::on_collision_update(entity::registry& registry, entity::id entity_id)
  109. {}
  110. void collision::on_collision_destroy(entity::registry& registry, entity::id entity_id)
  111. {}
  112. } // namespace system
  113. } // namespace game