Browse Source

Add picking support to nuptial flight state. Add more constraint types. Improve nuptial flight camera.

master
C. J. Howard 2 years ago
parent
commit
6c26a61379
13 changed files with 471 additions and 34 deletions
  1. +13
    -0
      src/game/ant/swarm.cpp
  2. +1
    -0
      src/game/component/constraint/constraint.hpp
  3. +55
    -0
      src/game/component/constraint/ease-to.hpp
  4. +41
    -0
      src/game/component/picking.hpp
  5. +196
    -28
      src/game/state/nuptial-flight.cpp
  6. +10
    -0
      src/game/state/nuptial-flight.hpp
  7. +80
    -0
      src/game/system/collision.cpp
  8. +23
    -1
      src/game/system/collision.hpp
  9. +24
    -0
      src/game/system/constraint.cpp
  10. +1
    -0
      src/game/system/constraint.hpp
  11. +4
    -4
      src/math/vector-functions.hpp
  12. +13
    -1
      src/scene/camera.cpp
  13. +10
    -0
      src/scene/camera.hpp

+ 13
- 0
src/game/ant/swarm.cpp View File

@ -21,6 +21,7 @@
#include "game/component/transform.hpp" #include "game/component/transform.hpp"
#include "game/component/steering.hpp" #include "game/component/steering.hpp"
#include "game/component/model.hpp" #include "game/component/model.hpp"
#include "game/component/picking.hpp"
#include "resources/resource-manager.hpp" #include "resources/resource-manager.hpp"
#include "config.hpp" #include "config.hpp"
#include <cmath> #include <cmath>
@ -68,6 +69,12 @@ entity::id create_swarm(game::context& ctx)
transform.world = transform.local; transform.world = transform.local;
transform.warp = true; transform.warp = true;
// Init picking component
game::component::picking picking;
picking.sphere = {float3{0, 0, 0}, 0.5f};
std::uint32_t male_picking_flags = 0b01;
std::uint32_t queen_picking_flags = 0b10;
// Create swarm entity // Create swarm entity
entity::id swarm_eid = ctx.entity_registry->create(); entity::id swarm_eid = ctx.entity_registry->create();
transform.local.translation = swarm_center; transform.local.translation = swarm_center;
@ -131,6 +138,9 @@ entity::id create_swarm(game::context& ctx)
transform.local.scale = male_scale; transform.local.scale = male_scale;
transform.world = transform.local; transform.world = transform.local;
ctx.entity_registry->emplace<game::component::transform>(alate_eid, transform); ctx.entity_registry->emplace<game::component::transform>(alate_eid, transform);
picking.flags = male_picking_flags;
ctx.entity_registry->emplace<game::component::picking>(alate_eid, picking);
} }
else else
{ {
@ -140,6 +150,9 @@ entity::id create_swarm(game::context& ctx)
transform.local.scale = queen_scale; transform.local.scale = queen_scale;
transform.world = transform.local; transform.world = transform.local;
ctx.entity_registry->emplace<game::component::transform>(alate_eid, transform); ctx.entity_registry->emplace<game::component::transform>(alate_eid, transform);
picking.flags = queen_picking_flags;
ctx.entity_registry->emplace<game::component::picking>(alate_eid, picking);
} }
} }

+ 1
- 0
src/game/component/constraint/constraint.hpp View File

@ -34,6 +34,7 @@ namespace constraint {}
#include "game/component/constraint/copy-scale.hpp" #include "game/component/constraint/copy-scale.hpp"
#include "game/component/constraint/copy-transform.hpp" #include "game/component/constraint/copy-transform.hpp"
#include "game/component/constraint/copy-translation.hpp" #include "game/component/constraint/copy-translation.hpp"
#include "game/component/constraint/ease-to.hpp"
#include "game/component/constraint/pivot.hpp" #include "game/component/constraint/pivot.hpp"
#include "game/component/constraint/spring-rotation.hpp" #include "game/component/constraint/spring-rotation.hpp"
#include "game/component/constraint/spring-to.hpp" #include "game/component/constraint/spring-to.hpp"

+ 55
- 0
src/game/component/constraint/ease-to.hpp View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_EASE_TO_HPP
#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_EASE_TO_HPP
#include "entity/id.hpp"
#include "utility/fundamental-types.hpp"
namespace game {
namespace component {
namespace constraint {
/**
* Eases toward a target entity.
*/
struct ease_to
{
/// Target entity ID.
entity::id target;
/// Start position.
float3 start;
/// Total duration of the ease.
float duration;
/// Elapsed time since ease began.
float t;
/// Pointer to the interpolation function.
float3 (*function)(const float3&, const float3&, float);
};
} // namespace constraint
} // namespace component
} // namespace game
#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_EASE_TO_HPP

+ 41
- 0
src/game/component/picking.hpp View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_COMPONENT_PICKING_HPP
#define ANTKEEPER_GAME_COMPONENT_PICKING_HPP
#include "geom/sphere.hpp"
#include <cstdint>
namespace game {
namespace component {
struct picking
{
/// Picking sphere.
geom::sphere<float> sphere;
/// Picking flags.
std::uint32_t flags;
};
} // namespace component
} // namespace game
#endif // ANTKEEPER_GAME_COMPONENT_PICKING_HPP

+ 196
- 28
src/game/state/nuptial-flight.cpp View File

@ -24,6 +24,7 @@
#include "game/system/camera.hpp" #include "game/system/camera.hpp"
#include "game/system/astronomy.hpp" #include "game/system/astronomy.hpp"
#include "game/system/atmosphere.hpp" #include "game/system/atmosphere.hpp"
#include "game/system/collision.hpp"
#include "game/component/locomotion.hpp" #include "game/component/locomotion.hpp"
#include "game/component/transform.hpp" #include "game/component/transform.hpp"
#include "game/component/terrain.hpp" #include "game/component/terrain.hpp"
@ -32,6 +33,7 @@
#include "game/component/constraint/constraint.hpp" #include "game/component/constraint/constraint.hpp"
#include "game/component/constraint-stack.hpp" #include "game/component/constraint-stack.hpp"
#include "game/component/steering.hpp" #include "game/component/steering.hpp"
#include "game/component/picking.hpp"
#include "game/controls.hpp" #include "game/controls.hpp"
#include "entity/commands.hpp" #include "entity/commands.hpp"
#include "animation/screen-transition.hpp" #include "animation/screen-transition.hpp"
@ -49,6 +51,8 @@
#include "math/interpolation.hpp" #include "math/interpolation.hpp"
#include "physics/light/exposure.hpp" #include "physics/light/exposure.hpp"
#include "color/color.hpp" #include "color/color.hpp"
#include "application.hpp"
#include "input/mouse.hpp"
#include <iostream> #include <iostream>
using namespace game::ant; using namespace game::ant;
@ -61,6 +65,10 @@ nuptial_flight::nuptial_flight(game::context& ctx):
{ {
ctx.logger->push_task("Entering nuptial flight state"); ctx.logger->push_task("Entering nuptial flight state");
// Init selected picking flag
selected_picking_flag = std::uint32_t{1} << (sizeof(std::uint32_t) * 8 - 1);
selected_eid = entt::null;
// Disable UI color clear // Disable UI color clear
ctx.ui_clear_pass->set_cleared_buffers(false, true, false); ctx.ui_clear_pass->set_cleared_buffers(false, true, false);
@ -105,23 +113,7 @@ nuptial_flight::nuptial_flight(game::context& ctx):
( (
[&](entity::id alate_eid, auto& transform, auto& steering) [&](entity::id alate_eid, auto& transform, auto& steering)
{ {
ctx.entity_registry->patch<component::constraint::copy_translation>
(
camera_rig_copy_translation_eid,
[&](auto& component)
{
component.target = alate_eid;
}
);
ctx.entity_registry->patch<component::constraint::pivot>
(
camera_rig_pivot_eid,
[&](auto& component)
{
component.target = alate_eid;
}
);
select_entity(alate_eid);
} }
); );
@ -139,6 +131,9 @@ nuptial_flight::~nuptial_flight()
{ {
ctx.logger->push_task("Exiting nuptial flight state"); ctx.logger->push_task("Exiting nuptial flight state");
// Deselect selected entity
select_entity(entt::null);
destroy_camera_rig(); destroy_camera_rig();
game::ant::destroy_swarm(ctx, swarm_eid); game::ant::destroy_swarm(ctx, swarm_eid);
@ -147,9 +142,40 @@ nuptial_flight::~nuptial_flight()
void nuptial_flight::create_camera_rig() void nuptial_flight::create_camera_rig()
{ {
// Construct camera rig focus ease to constraint
component::constraint::ease_to camera_rig_focus_ease_to;
camera_rig_focus_ease_to.target = selected_eid;
camera_rig_focus_ease_to.start = {0, 0, 0};
camera_rig_focus_ease_to.duration = 1.0f;
camera_rig_focus_ease_to.t = camera_rig_focus_ease_to.duration;
camera_rig_focus_ease_to.function = &ease<float3, float>::out_expo;
component::constraint_stack_node camera_rig_focus_ease_to_node;
camera_rig_focus_ease_to_node.active = true;
camera_rig_focus_ease_to_node.weight = 1.0f;
camera_rig_focus_ease_to_node.next = entt::null;
camera_rig_focus_ease_to_eid = ctx.entity_registry->create();
ctx.entity_registry->emplace<component::constraint::ease_to>(camera_rig_focus_ease_to_eid, camera_rig_focus_ease_to);
ctx.entity_registry->emplace<component::constraint_stack_node>(camera_rig_focus_ease_to_eid, camera_rig_focus_ease_to_node);
// Construct camera rig focus constraint stack
component::constraint_stack camera_rig_focus_constraint_stack;
camera_rig_focus_constraint_stack.priority = 1;
camera_rig_focus_constraint_stack.head = camera_rig_focus_ease_to_eid;
// Construct camera rig focus transform component
component::transform camera_rig_focus_transform;
camera_rig_focus_transform.local = math::transform<float>::identity;
camera_rig_focus_transform.world = camera_rig_focus_transform.local;
camera_rig_focus_transform.warp = true;
// Construct camera rig focus entity
camera_rig_focus_eid = ctx.entity_registry->create();
ctx.entity_registry->emplace<component::transform>(camera_rig_focus_eid, camera_rig_focus_transform);
ctx.entity_registry->emplace<component::constraint_stack>(camera_rig_focus_eid, camera_rig_focus_constraint_stack);
// Construct camera rig pivot constraint // Construct camera rig pivot constraint
component::constraint::pivot camera_rig_pivot; component::constraint::pivot camera_rig_pivot;
camera_rig_pivot.target = swarm_eid;
camera_rig_pivot.target = camera_rig_focus_eid;
camera_rig_pivot.offset = {0, 0, 0}; camera_rig_pivot.offset = {0, 0, 0};
component::constraint_stack_node camera_rig_pivot_node; component::constraint_stack_node camera_rig_pivot_node;
camera_rig_pivot_node.active = true; camera_rig_pivot_node.active = true;
@ -161,7 +187,7 @@ void nuptial_flight::create_camera_rig()
// Construct camera rig copy translation constraint // Construct camera rig copy translation constraint
component::constraint::copy_translation camera_rig_copy_translation; component::constraint::copy_translation camera_rig_copy_translation;
camera_rig_copy_translation.target = swarm_eid;
camera_rig_copy_translation.target = camera_rig_focus_eid;
camera_rig_copy_translation.copy_x = true; camera_rig_copy_translation.copy_x = true;
camera_rig_copy_translation.copy_y = true; camera_rig_copy_translation.copy_y = true;
camera_rig_copy_translation.copy_z = true; camera_rig_copy_translation.copy_z = true;
@ -213,12 +239,12 @@ void nuptial_flight::create_camera_rig()
ctx.entity_registry->emplace<component::constraint::spring_translation>(camera_rig_spring_translation_eid, camera_rig_spring_translation); ctx.entity_registry->emplace<component::constraint::spring_translation>(camera_rig_spring_translation_eid, camera_rig_spring_translation);
ctx.entity_registry->emplace<component::constraint_stack_node>(camera_rig_spring_translation_eid, camera_rig_spring_translation_node); ctx.entity_registry->emplace<component::constraint_stack_node>(camera_rig_spring_translation_eid, camera_rig_spring_translation_node);
// Construct camera constraint stack
// Construct camera rig constraint stack
component::constraint_stack camera_rig_constraint_stack; component::constraint_stack camera_rig_constraint_stack;
camera_rig_constraint_stack.priority = 1;
camera_rig_constraint_stack.priority = 2;
camera_rig_constraint_stack.head = camera_rig_spring_translation_eid; camera_rig_constraint_stack.head = camera_rig_spring_translation_eid;
// Construct camera transform component
// Construct camera rig transform component
component::transform camera_rig_transform; component::transform camera_rig_transform;
camera_rig_transform.local = math::transform<float>::identity; camera_rig_transform.local = math::transform<float>::identity;
camera_rig_transform.world = camera_rig_transform.local; camera_rig_transform.world = camera_rig_transform.local;
@ -242,6 +268,10 @@ void nuptial_flight::destroy_camera_rig()
ctx.entity_registry->destroy(camera_rig_spring_rotation_eid); ctx.entity_registry->destroy(camera_rig_spring_rotation_eid);
ctx.entity_registry->destroy(camera_rig_copy_translation_eid); ctx.entity_registry->destroy(camera_rig_copy_translation_eid);
ctx.entity_registry->destroy(camera_rig_pivot_eid); ctx.entity_registry->destroy(camera_rig_pivot_eid);
ctx.entity_registry->destroy(camera_rig_focus_eid);
ctx.entity_registry->destroy(camera_rig_focus_ease_to_eid);
} }
void nuptial_flight::enable_controls() void nuptial_flight::enable_controls()
@ -456,7 +486,7 @@ void nuptial_flight::enable_controls()
( (
[&](float value) [&](float value)
{ {
ctx.entity_registry->patch<game::component::constraint::spring_translation>
ctx.entity_registry->patch<component::constraint::spring_translation>
( (
camera_rig_spring_translation_eid, camera_rig_spring_translation_eid,
[&](auto& component) [&](auto& component)
@ -472,7 +502,7 @@ void nuptial_flight::enable_controls()
( (
[&](float value) [&](float value)
{ {
ctx.entity_registry->patch<game::component::constraint::spring_translation>
ctx.entity_registry->patch<component::constraint::spring_translation>
( (
camera_rig_spring_translation_eid, camera_rig_spring_translation_eid,
[&](auto& component) [&](auto& component)
@ -483,6 +513,72 @@ void nuptial_flight::enable_controls()
} }
); );
// Mouse select control
ctx.controls["select_mouse"]->set_activated_callback
(
[&]()
{
// Get window-space mouse coordinates
auto [mouse_x, mouse_y] = ctx.app->get_mouse()->get_current_position();
// Get window viewport dimensions
const auto viewport_dimensions = ctx.app->get_viewport_dimensions();
// Transform mouse coordinates from window space to NDC space
const float2 mouse_ndc =
{
static_cast<float>(mouse_x) / static_cast<float>(viewport_dimensions[0] - 1) * 2.0f - 1.0f,
(1.0f - static_cast<float>(mouse_y) / static_cast<float>(viewport_dimensions[1] - 1)) * 2.0f - 1.0f
};
// Get picking ray from camera
const geom::ray<float> ray = ctx.surface_camera->pick(mouse_ndc);
// Pick entity
entity::id picked_eid = ctx.collision_system->pick_nearest(ray, ~selected_picking_flag);
if (picked_eid != entt::null)
{
select_entity(picked_eid);
}
}
);
// Select forward control
ctx.controls["move_forward"]->set_activated_callback
(
[&]()
{
select_nearest_entity({0.0f, 0.0f, -1.0f});
}
);
// Select back control
ctx.controls["move_back"]->set_activated_callback
(
[&]()
{
select_nearest_entity({0.0f, 0.0f, 1.0f});
}
);
// Select right control
ctx.controls["move_right"]->set_activated_callback
(
[&]()
{
select_nearest_entity({1.0f, 0.0f, 0.0f});
}
);
// Select left control
ctx.controls["move_left"]->set_activated_callback
(
[&]()
{
select_nearest_entity({-1.0f, 0.0f, 0.0f});
}
);
// Fast-forward // Fast-forward
ctx.controls["fast_forward"]->set_activated_callback ctx.controls["fast_forward"]->set_activated_callback
( (
@ -632,10 +728,10 @@ void nuptial_flight::disable_controls()
ctx.app->set_relative_mouse_mode(false); ctx.app->set_relative_mouse_mode(false);
} }
ctx.controls["move_forward"]->set_active_callback(nullptr);
ctx.controls["move_back"]->set_active_callback(nullptr);
ctx.controls["move_right"]->set_active_callback(nullptr);
ctx.controls["move_left"]->set_active_callback(nullptr);
ctx.controls["move_forward"]->set_activated_callback(nullptr);
ctx.controls["move_back"]->set_activated_callback(nullptr);
ctx.controls["move_right"]->set_activated_callback(nullptr);
ctx.controls["move_left"]->set_activated_callback(nullptr);
ctx.controls["move_up"]->set_active_callback(nullptr); ctx.controls["move_up"]->set_active_callback(nullptr);
ctx.controls["move_down"]->set_active_callback(nullptr); ctx.controls["move_down"]->set_active_callback(nullptr);
ctx.controls["mouse_look"]->set_activated_callback(nullptr); ctx.controls["mouse_look"]->set_activated_callback(nullptr);
@ -648,6 +744,7 @@ void nuptial_flight::disable_controls()
ctx.controls["look_up_mouse"]->set_active_callback(nullptr); ctx.controls["look_up_mouse"]->set_active_callback(nullptr);
ctx.controls["look_down_gamepad"]->set_active_callback(nullptr); ctx.controls["look_down_gamepad"]->set_active_callback(nullptr);
ctx.controls["look_down_mouse"]->set_active_callback(nullptr); ctx.controls["look_down_mouse"]->set_active_callback(nullptr);
ctx.controls["select_mouse"]->set_activated_callback(nullptr);
ctx.controls["switch_pov"]->set_activated_callback(nullptr); ctx.controls["switch_pov"]->set_activated_callback(nullptr);
ctx.controls["fast_forward"]->set_activated_callback(nullptr); ctx.controls["fast_forward"]->set_activated_callback(nullptr);
ctx.controls["rewind"]->set_activated_callback(nullptr); ctx.controls["rewind"]->set_activated_callback(nullptr);
@ -656,5 +753,76 @@ void nuptial_flight::disable_controls()
ctx.controls["decrease_exposure"]->set_activated_callback(nullptr); ctx.controls["decrease_exposure"]->set_activated_callback(nullptr);
} }
void nuptial_flight::select_entity(entity::id entity_id)
{
if (entity_id != selected_eid)
{
if (ctx.entity_registry->valid(selected_eid) && ctx.entity_registry->all_of<component::picking>(selected_eid))
{
// Unset selected bit on picking flags of previously selected entity
ctx.entity_registry->patch<component::picking>
(
selected_eid,
[&](auto& component)
{
component.flags &= ~selected_picking_flag;
}
);
}
selected_eid = entity_id;
if (ctx.entity_registry->valid(selected_eid) && ctx.entity_registry->all_of<component::picking>(selected_eid))
{
// Set selected bit on picking flags of current selected entity
ctx.entity_registry->patch<component::picking>
(
selected_eid,
[&](auto& component)
{
component.flags |= selected_picking_flag;
}
);
}
// Update camera rig focus ease to target
ctx.entity_registry->patch<component::constraint::ease_to>
(
camera_rig_focus_ease_to_eid,
[&](auto& component)
{
component.target = selected_eid;
component.t = 0.0f;
const component::transform* transform = ctx.entity_registry->try_get<component::transform>(camera_rig_focus_eid);
if (transform)
component.start = transform->world.translation;
}
);
}
}
void nuptial_flight::select_nearest_entity(const float3& direction)
{
if (!ctx.entity_registry->valid(selected_eid))
return;
const component::transform* selected_eid_transform = ctx.entity_registry->try_get<component::transform>(selected_eid);
if (!selected_eid_transform)
return;
// Construct picking plane
const float3 picking_normal = math::normalize(ctx.surface_camera->get_rotation() * direction);
const float3 picking_origin = selected_eid_transform->world.translation;
// Pick entity
entity::id picked_eid = ctx.collision_system->pick_nearest(picking_origin, picking_normal, ~selected_picking_flag);
if (picked_eid != entt::null)
{
select_entity(picked_eid);
}
}
} // namespace state } // namespace state
} // namespace game } // namespace game

+ 10
- 0
src/game/state/nuptial-flight.hpp View File

@ -22,6 +22,7 @@
#include "game/state/base.hpp" #include "game/state/base.hpp"
#include "entity/id.hpp" #include "entity/id.hpp"
#include "utility/fundamental-types.hpp"
namespace game { namespace game {
namespace state { namespace state {
@ -39,6 +40,12 @@ private:
void enable_controls(); void enable_controls();
void disable_controls(); void disable_controls();
void select_entity(entity::id entity_id);
void select_nearest_entity(const float3& direction);
entity::id camera_rig_focus_eid;
entity::id camera_rig_focus_ease_to_eid;
entity::id camera_rig_eid; entity::id camera_rig_eid;
entity::id camera_rig_spring_translation_eid; entity::id camera_rig_spring_translation_eid;
entity::id camera_rig_spring_rotation_eid; entity::id camera_rig_spring_rotation_eid;
@ -47,6 +54,9 @@ private:
entity::id swarm_eid; entity::id swarm_eid;
std::uint32_t selected_picking_flag;
entity::id selected_eid;
bool mouse_look; bool mouse_look;
}; };

+ 80
- 0
src/game/system/collision.cpp View File

@ -19,6 +19,10 @@
#include "collision.hpp" #include "collision.hpp"
#include "game/component/transform.hpp" #include "game/component/transform.hpp"
#include "game/component/picking.hpp"
#include "geom/intersection.hpp"
#include "geom/plane.hpp"
#include <limits>
namespace game { namespace game {
namespace system { namespace system {
@ -38,6 +42,82 @@ void collision::update(double t, double dt)
registry.on_destroy<component::collision>().disconnect<&collision::on_collision_destroy>(this); registry.on_destroy<component::collision>().disconnect<&collision::on_collision_destroy>(this);
} }
entity::id collision::pick_nearest(const geom::ray<float>& ray, std::uint32_t flags) const
{
entity::id nearest_eid = entt::null;
float nearest_distance = std::numeric_limits<float>::infinity();
// For each entity with picking and transform components
registry.view<component::picking, component::transform>().each
(
[&](entity::id entity_id, const auto& picking, const auto& transform)
{
// Skip entity if picking flags don't match
if (!~(flags | picking.flags))
return;
// Transform picking sphere
const geom::sphere<float> sphere =
{
transform.world * picking.sphere.center,
picking.sphere.radius * std::max(std::max(transform.world.scale[0], transform.world.scale[1]), transform.world.scale[2])
};
// Test for intersection between ray and sphere
auto result = geom::ray_sphere_intersection(ray, sphere);
if (std::get<0>(result))
{
if (std::get<1>(result) < nearest_distance)
{
nearest_eid = entity_id;
nearest_distance = std::get<1>(result);
}
}
}
);
return nearest_eid;
}
entity::id collision::pick_nearest(const float3& origin, const float3& normal, std::uint32_t flags) const
{
entity::id nearest_eid = entt::null;
float nearest_distance_squared = std::numeric_limits<float>::infinity();
// Construct picking plane
const geom::plane<float> picking_plane = geom::plane<float>(normal, origin);
// For each entity with picking and transform components
registry.view<component::picking, component::transform>().each
(
[&](entity::id entity_id, const auto& picking, const auto& transform)
{
// Skip entity if picking flags don't match
if (!~(flags | picking.flags))
return;
// Transform picking sphere center
float3 picking_sphere_center = transform.world * picking.sphere.center;
// Skip entity if picking sphere center has negative distance from picking plane
if (picking_plane.signed_distance(picking_sphere_center) < 0.0f)
return;
// Measure distance from picking plane origin to picking sphere center
const float distance_squared = math::distance_squared(picking_sphere_center, origin);
// Check if entity is nearer than the current nearest entity
if (distance_squared < nearest_distance_squared)
{
nearest_eid = entity_id;
nearest_distance_squared = distance_squared;
}
}
);
return nearest_eid;
}
void collision::on_collision_construct(entity::registry& registry, entity::id entity_id) void collision::on_collision_construct(entity::registry& registry, entity::id entity_id)
{} {}

+ 23
- 1
src/game/system/collision.hpp View File

@ -23,18 +23,40 @@
#include "game/system/updatable.hpp" #include "game/system/updatable.hpp"
#include "entity/id.hpp" #include "entity/id.hpp"
#include "game/component/collision.hpp" #include "game/component/collision.hpp"
#include "geom/ray.hpp"
namespace game { namespace game {
namespace system { namespace system {
/** /**
* Maintains a spatially partitioned set of collision meshes. The set of collision meshes isnot owned by the collision system, so it can be accessed by other systems as well.
* Maintains a spatially partitioned set of collision meshes.
*/ */
class collision: public updatable class collision: public updatable
{ {
public: public:
collision(entity::registry& registry); collision(entity::registry& registry);
virtual void update(double t, double dt); virtual void update(double t, double dt);
/**
* Picks the nearest entity with the specified picking flags that intersects a ray.
*
* @param ray Picking ray.
* @param flags Picking flags.
*
* @return ID of the picked entity, or `entt::null` if no entity was picked.
*/
entity::id pick_nearest(const geom::ray<float>& ray, std::uint32_t flags) const;
/**
* Picks the nearest entity with the specified picking flags that has a non-negative distance from a plane.
*
* @param origin Origin of the picking plane.
* @param normal Picking plane normal direction.
* @param flags Picking flags.
*
* @return ID of the picked entity, or `entt::null` if no entity was picked.
*/
entity::id pick_nearest(const float3& origin, const float3& normal, std::uint32_t flags) const;
private: private:
void on_collision_construct(entity::registry& registry, entity::id entity_id); void on_collision_construct(entity::registry& registry, entity::id entity_id);

+ 24
- 0
src/game/system/constraint.cpp View File

@ -107,6 +107,8 @@ void constraint::handle_constraint(component::transform& transform, entity::id c
handle_spring_translation_constraint(transform, *constraint, dt); handle_spring_translation_constraint(transform, *constraint, dt);
else if (auto constraint = registry.try_get<component::constraint::spring_rotation>(constraint_eid); constraint) else if (auto constraint = registry.try_get<component::constraint::spring_rotation>(constraint_eid); constraint)
handle_spring_rotation_constraint(transform, *constraint, dt); handle_spring_rotation_constraint(transform, *constraint, dt);
else if (auto constraint = registry.try_get<component::constraint::ease_to>(constraint_eid); constraint)
handle_ease_to_constraint(transform, *constraint, dt);
} }
void constraint::handle_child_of_constraint(component::transform& transform, const component::constraint::child_of& constraint) void constraint::handle_child_of_constraint(component::transform& transform, const component::constraint::child_of& constraint)
@ -195,6 +197,28 @@ void constraint::handle_copy_translation_constraint(component::transform& transf
} }
} }
void constraint::handle_ease_to_constraint(component::transform& transform, component::constraint::ease_to& constraint, float dt)
{
if (constraint.function && registry.valid(constraint.target))
{
const component::transform* target_transform = registry.try_get<component::transform>(constraint.target);
if (target_transform)
{
if (constraint.t < constraint.duration)
{
const float a = constraint.t / constraint.duration;
transform.world.translation = constraint.function(constraint.start, target_transform->world.translation, a);
}
else
{
transform.world.translation = target_transform->world.translation;
}
constraint.t += dt;
}
}
}
void constraint::handle_pivot_constraint(component::transform& transform, const component::constraint::pivot& constraint) void constraint::handle_pivot_constraint(component::transform& transform, const component::constraint::pivot& constraint)
{ {
if (registry.valid(constraint.target)) if (registry.valid(constraint.target))

+ 1
- 0
src/game/system/constraint.hpp View File

@ -53,6 +53,7 @@ private:
void handle_copy_scale_constraint(component::transform& transform, const component::constraint::copy_scale& constraint); void handle_copy_scale_constraint(component::transform& transform, const component::constraint::copy_scale& constraint);
void handle_copy_transform_constraint(component::transform& transform, const component::constraint::copy_transform& constraint); void handle_copy_transform_constraint(component::transform& transform, const component::constraint::copy_transform& constraint);
void handle_copy_translation_constraint(component::transform& transform, const component::constraint::copy_translation& constraint); void handle_copy_translation_constraint(component::transform& transform, const component::constraint::copy_translation& constraint);
void handle_ease_to_constraint(component::transform& transform, component::constraint::ease_to& constraint, float dt);
void handle_pivot_constraint(component::transform& transform, const component::constraint::pivot& constraint); void handle_pivot_constraint(component::transform& transform, const component::constraint::pivot& constraint);
void handle_spring_rotation_constraint(component::transform& transform, component::constraint::spring_rotation& constraint, float dt); void handle_spring_rotation_constraint(component::transform& transform, component::constraint::spring_rotation& constraint, float dt);
void handle_spring_to_constraint(component::transform& transform, component::constraint::spring_to& constraint, float dt); void handle_spring_to_constraint(component::transform& transform, component::constraint::spring_to& constraint, float dt);

+ 4
- 4
src/math/vector-functions.hpp View File

@ -105,7 +105,7 @@ constexpr vector cross(const vector& x, const vector& y);
* @return Distance between the two points. * @return Distance between the two points.
*/ */
template <class T, std::size_t N> template <class T, std::size_t N>
vector<T, N> distance(const vector<T, N>& p0, const vector<T, N>& p1);
T distance(const vector<T, N>& p0, const vector<T, N>& p1);
/** /**
* Calculates the squared distance between two points. The squared distance can be calculated faster than the distance because a call to `std::sqrt` is saved. * Calculates the squared distance between two points. The squared distance can be calculated faster than the distance because a call to `std::sqrt` is saved.
@ -115,7 +115,7 @@ vector distance(const vector& p0, const vector& p1);
* @return Squared distance between the two points. * @return Squared distance between the two points.
*/ */
template <class T, std::size_t N> template <class T, std::size_t N>
constexpr vector<T, N> distance_squared(const vector<T, N>& p0, const vector<T, N>& p1);
constexpr T distance_squared(const vector<T, N>& p0, const vector<T, N>& p1);
/** /**
* Divides a vector by another vector. * Divides a vector by another vector.
@ -392,14 +392,14 @@ constexpr inline vector cross(const vector& x, const vector& y
} }
template <class T, std::size_t N> template <class T, std::size_t N>
inline vector<T, N> distance(const vector<T, N>& p0, const vector<T, N>& p1)
inline T distance(const vector<T, N>& p0, const vector<T, N>& p1)
{ {
static_assert(std::is_floating_point<T>::value); static_assert(std::is_floating_point<T>::value);
return length(sub(p0, p1)); return length(sub(p0, p1));
} }
template <class T, std::size_t N> template <class T, std::size_t N>
constexpr inline vector<T, N> distance_squared(const vector<T, N>& p0, const vector<T, N>& p1)
constexpr inline T distance_squared(const vector<T, N>& p0, const vector<T, N>& p1)
{ {
static_assert(std::is_floating_point<T>::value); static_assert(std::is_floating_point<T>::value);
return length_squared(sub(p0, p1)); return length_squared(sub(p0, p1));

+ 13
- 1
src/scene/camera.cpp View File

@ -19,7 +19,6 @@
#include "scene/camera.hpp" #include "scene/camera.hpp"
#include "config.hpp" #include "config.hpp"
#include "animation/ease.hpp"
#include "math/constants.hpp" #include "math/constants.hpp"
#include "math/interpolation.hpp" #include "math/interpolation.hpp"
@ -78,6 +77,19 @@ camera::camera():
exposure(0.0f, math::lerp<float, float>) exposure(0.0f, math::lerp<float, float>)
{} {}
geom::ray<float> camera::pick(const float2& ndc) const
{
const float4x4 inverse_view_projection = math::inverse(view_projection[1]);
const float4 near = inverse_view_projection * float4{ndc[0], ndc[1], 1.0f, 1.0f};
const float4 far = inverse_view_projection * float4{ndc[0], ndc[1], 0.0f, 1.0f};
const float3 origin = float3{near[0], near[1], near[2]} / near[3];
const float3 direction = math::normalize(float3{far[0], far[1], far[2]} / far[3] - origin);
return {origin, direction};
}
float3 camera::project(const float3& object, const float4& viewport) const float3 camera::project(const float3& object, const float4& viewport) const
{ {
float4 result = view_projection[1] * float4{object[0], object[1], object[2], 1.0f}; float4 result = view_projection[1] * float4{object[0], object[1], object[2], 1.0f};

+ 10
- 0
src/scene/camera.hpp View File

@ -22,6 +22,7 @@
#include "scene/object.hpp" #include "scene/object.hpp"
#include "geom/view-frustum.hpp" #include "geom/view-frustum.hpp"
#include "geom/ray.hpp"
#include "utility/fundamental-types.hpp" #include "utility/fundamental-types.hpp"
#include "render/compositor.hpp" #include "render/compositor.hpp"
@ -36,6 +37,15 @@ public:
typedef geom::view_frustum<float> view_frustum_type; typedef geom::view_frustum<float> view_frustum_type;
camera(); camera();
/**
* Constructs a picking ray from normalized device coordinates (NDC).
*
* @param ndc NDC coordinates.
*
* @return Picking ray.
*/
geom::ray<float> pick(const float2& ndc) const;
/** /**
* Maps object coordinates to window coordinates. * Maps object coordinates to window coordinates.

Loading…
Cancel
Save