@ -0,0 +1,94 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_EVENT_DISPATCHER_HPP | |||||
#define ANTKEEPER_EVENT_DISPATCHER_HPP | |||||
#include <engine/event/subscriber.hpp> | |||||
#include <engine/event/subscription.hpp> | |||||
#include <any> | |||||
#include <map> | |||||
#include <memory> | |||||
#include <typeindex> | |||||
#include <utility> | |||||
namespace event { | |||||
/** | |||||
* Forwards messages from publishers to subscribers. | |||||
*/ | |||||
class dispatcher | |||||
{ | |||||
public: | |||||
/** | |||||
* Subscribes a function object to messages dispatched by this dispatcher. | |||||
* | |||||
* @tparam T Message type. | |||||
* | |||||
* @param subscriber Function object to subscribe. | |||||
* | |||||
* @return Shared subscription object which will unsubscribe the subscriber on destruction. | |||||
* | |||||
*/ | |||||
template <class T> | |||||
[[nodiscard]] std::shared_ptr<subscription> subscribe(subscriber<T>&& subscriber) | |||||
{ | |||||
// Allocate shared pointer to std::any object containing subscriber | |||||
std::shared_ptr<std::any> shared_subscriber = std::make_shared<std::any>(std::make_any<event::subscriber<T>>(std::move(subscriber))); | |||||
// Append subscriber to subscriber list and store iterator | |||||
auto iterator = subscribers.emplace(std::type_index(typeid(T)), shared_subscriber); | |||||
// Construct and return a shared subscription object which removes the subscriber from the subscriber list when unsubscribed or destructed | |||||
return std::make_shared<subscription> | |||||
( | |||||
std::static_pointer_cast<void>(shared_subscriber), | |||||
[this, iterator = std::move(iterator)]() | |||||
{ | |||||
this->subscribers.erase(iterator); | |||||
} | |||||
); | |||||
} | |||||
/** | |||||
* Dispatches a message to subscribers of the message type. | |||||
* | |||||
* @tparam T Message type. | |||||
* | |||||
* @param message Message to dispatch. | |||||
*/ | |||||
template <class T> | |||||
void dispatch(const T& message) const | |||||
{ | |||||
// For each subscriber of the given message type | |||||
const auto range = subscribers.equal_range(std::type_index(typeid(T))); | |||||
for (auto i = range.first; i != range.second; ++i) | |||||
{ | |||||
// Send message to subscriber | |||||
std::any_cast<subscriber<T>>(*(i->second))(message); | |||||
} | |||||
} | |||||
private: | |||||
std::multimap<std::type_index, std::shared_ptr<std::any>> subscribers; | |||||
}; | |||||
} // namespace event | |||||
#endif // ANTKEEPER_EVENT_DISPATCHER_HPP |
@ -0,0 +1,149 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_PHYSICS_COLLIDER_MATERIAL_HPP | |||||
#define ANTKEEPER_PHYSICS_COLLIDER_MATERIAL_HPP | |||||
#include <engine/physics/kinematics/friction-combine-mode.hpp> | |||||
#include <engine/physics/kinematics/restitution-combine-mode.hpp> | |||||
namespace physics { | |||||
/** | |||||
* Describes the collision response of a collider. | |||||
*/ | |||||
class collider_material | |||||
{ | |||||
public: | |||||
/** | |||||
* Constructs a collider material. | |||||
* | |||||
* @param restitution Restitution value. | |||||
* @param static_friction Static friction value. | |||||
* @param dynamic_friction Dynamic friction value. | |||||
*/ | |||||
/// @{ | |||||
inline collider_material(float restitution, float static_friction, float dynamic_friction) noexcept: | |||||
m_restitution{restitution}, | |||||
m_static_friction{static_friction}, | |||||
m_dynamic_friction{dynamic_friction} | |||||
{} | |||||
collider_material() noexcept = default; | |||||
/// @} | |||||
/** | |||||
* Sets the of restitution of the material. | |||||
* | |||||
* @param restitution Restitution value. | |||||
*/ | |||||
inline void set_restitution(float restitution) noexcept | |||||
{ | |||||
m_restitution = restitution; | |||||
} | |||||
/** | |||||
* Sets the static friction of the material. | |||||
* | |||||
* @param friction Static friction value. | |||||
*/ | |||||
inline void set_static_friction(float friction) noexcept | |||||
{ | |||||
m_static_friction = friction; | |||||
} | |||||
/** | |||||
* Sets the dynamic friction of the material. | |||||
* | |||||
* @param friction Dynamic friction value. | |||||
*/ | |||||
inline void set_dynamic_friction(float friction) noexcept | |||||
{ | |||||
m_dynamic_friction = friction; | |||||
} | |||||
/** | |||||
* Sets the restitution combine mode of the material. | |||||
* | |||||
* @param mode Restitution combine mode. | |||||
*/ | |||||
inline void set_restitution_combine_mode(restitution_combine_mode mode) noexcept | |||||
{ | |||||
m_restitution_combine_mode = mode; | |||||
} | |||||
/** | |||||
* Sets the friction combine mode of the material. | |||||
* | |||||
* @param mode Friction combine mode. | |||||
*/ | |||||
inline void set_friction_combine_mode(friction_combine_mode mode) noexcept | |||||
{ | |||||
m_friction_combine_mode = mode; | |||||
} | |||||
/// Returns the restitution of the material. | |||||
[[nodiscard]] inline float get_restitution() const noexcept | |||||
{ | |||||
return m_restitution; | |||||
} | |||||
/// Returns the static friction of the material. | |||||
[[nodiscard]] inline float get_static_friction() const noexcept | |||||
{ | |||||
return m_static_friction; | |||||
} | |||||
/// Returns the dynamic friction of the material. | |||||
[[nodiscard]] inline float get_dynamic_friction() const noexcept | |||||
{ | |||||
return m_dynamic_friction; | |||||
} | |||||
/// Returns the restitution combine mode. | |||||
[[nodiscard]] inline restitution_combine_mode get_restitution_combine_mode() const noexcept | |||||
{ | |||||
return m_restitution_combine_mode; | |||||
} | |||||
/// Returns the friction combine mode. | |||||
[[nodiscard]] inline friction_combine_mode get_friction_combine_mode() const noexcept | |||||
{ | |||||
return m_friction_combine_mode; | |||||
} | |||||
private: | |||||
/// Restitution value. | |||||
float m_restitution{0.0f}; | |||||
/// Static friction value. | |||||
float m_static_friction{0.0f}; | |||||
/// Dynamic friction value. | |||||
float m_dynamic_friction{0.0f}; | |||||
/// Restitution combine mode. | |||||
restitution_combine_mode m_restitution_combine_mode{restitution_combine_mode::average}; | |||||
/// Friction combine mode. | |||||
friction_combine_mode m_friction_combine_mode{friction_combine_mode::average}; | |||||
}; | |||||
} // namespace physics | |||||
#endif // ANTKEEPER_PHYSICS_COLLIDER_MATERIAL_HPP |
@ -0,0 +1,44 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_PHYSICS_COLLIDER_TYPE_HPP | |||||
#define ANTKEEPER_PHYSICS_COLLIDER_TYPE_HPP | |||||
#include <cstdint> | |||||
namespace physics { | |||||
/** | |||||
* Collider types. | |||||
*/ | |||||
enum class collider_type: std::uint8_t | |||||
{ | |||||
/// Plane collider. | |||||
plane, | |||||
/// Sphere collider. | |||||
sphere, | |||||
/// Box collider. | |||||
box | |||||
}; | |||||
} // namespace physics | |||||
#endif // ANTKEEPER_PHYSICS_COLLIDER_TYPE_HPP |
@ -0,0 +1,81 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_PHYSICS_COLLIDER_HPP | |||||
#define ANTKEEPER_PHYSICS_COLLIDER_HPP | |||||
#include <engine/physics/kinematics/collider-type.hpp> | |||||
#include <engine/physics/kinematics/collider-material.hpp> | |||||
#include <cstdint> | |||||
#include <memory> | |||||
namespace physics { | |||||
/** | |||||
* Abstract base class for collision objects. | |||||
*/ | |||||
class collider | |||||
{ | |||||
public: | |||||
/** | |||||
* Returns the collider type. | |||||
*/ | |||||
[[nodiscard]] virtual constexpr collider_type type() const noexcept = 0; | |||||
/** | |||||
* Sets the layer mask of the collider. | |||||
* | |||||
* @param mask 32-bit layer mask in which each bit represents a layer with which the collider can interact. | |||||
*/ | |||||
inline void set_layer_mask(std::uint32_t mask) noexcept | |||||
{ | |||||
m_layer_mask = mask; | |||||
} | |||||
/** | |||||
* Sets the collider material. | |||||
*/ | |||||
inline void set_material(std::shared_ptr<collider_material> material) noexcept | |||||
{ | |||||
m_material = material; | |||||
} | |||||
/// Returns the layer mask of the collider. | |||||
[[nodiscard]] inline std::uint32_t get_layer_mask() const noexcept | |||||
{ | |||||
return m_layer_mask; | |||||
} | |||||
/// Returns the collider material. | |||||
[[nodiscard]] inline const std::shared_ptr<collider_material>& get_material() const noexcept | |||||
{ | |||||
return m_material; | |||||
} | |||||
private: | |||||
/// Layer mask, in which each bit represents a layer with which the rigid body can interact. | |||||
std::uint32_t m_layer_mask{1}; | |||||
/// Collider material. | |||||
std::shared_ptr<collider_material> m_material; | |||||
}; | |||||
} // namespace physics | |||||
#endif // ANTKEEPER_PHYSICS_COLLIDER_HPP |
@ -0,0 +1,118 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_PHYSICS_BOX_COLLIDER_HPP | |||||
#define ANTKEEPER_PHYSICS_BOX_COLLIDER_HPP | |||||
#include <engine/physics/kinematics/collider.hpp> | |||||
#include <engine/geom/primitives/box.hpp> | |||||
namespace physics { | |||||
/** | |||||
* Box collision object. | |||||
*/ | |||||
class box_collider: public collider | |||||
{ | |||||
public: | |||||
/// Box type. | |||||
using box_type = geom::primitive::box<float>; | |||||
[[nodiscard]] inline constexpr collider_type type() const noexcept override | |||||
{ | |||||
return collider_type::box; | |||||
} | |||||
/** | |||||
* Constructs a box collider from a box. | |||||
* | |||||
* @param box Box shape. | |||||
*/ | |||||
inline explicit box_collider(const box_type& box) noexcept: | |||||
m_box{box} | |||||
{} | |||||
/** | |||||
* Constructs a box collider. | |||||
* | |||||
* @param min Minimum extent of the box, in object space. | |||||
* @param max Maximum extent of the box, in object space. | |||||
*/ | |||||
/// @{ | |||||
inline box_collider(const math::vector<float, 3>& min, const math::vector<float, 3>& max) noexcept: | |||||
m_box{min, max} | |||||
{} | |||||
box_collider() noexcept = default; | |||||
/// @} | |||||
/** | |||||
* Sets the collider's box. | |||||
* | |||||
* @param box Box shape. | |||||
*/ | |||||
inline void set_box(const box_type& box) noexcept | |||||
{ | |||||
m_box = box; | |||||
} | |||||
/** | |||||
* Sets the minimum extent of the box. | |||||
* | |||||
* @param min Minimum extent of the box, in object space. | |||||
*/ | |||||
inline void set_min(const math::vector<float, 3>& min) noexcept | |||||
{ | |||||
m_box.min = min; | |||||
} | |||||
/** | |||||
* Sets the maximum extent of the box. | |||||
* | |||||
* @param max Maximum extent of the box, in object space. | |||||
*/ | |||||
inline void set_max(const math::vector<float, 3>& max) noexcept | |||||
{ | |||||
m_box.max = max; | |||||
} | |||||
/// Returns the box shape. | |||||
[[nodiscard]] inline const box_type& get_box() const noexcept | |||||
{ | |||||
return m_box; | |||||
} | |||||
/// Returns the minimum extent of the box, in object space. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_min() const noexcept | |||||
{ | |||||
return m_box.min; | |||||
} | |||||
/// Returns the maximum extent of the box, in object space. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_max() const noexcept | |||||
{ | |||||
return m_box.max; | |||||
} | |||||
private: | |||||
box_type m_box{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}; | |||||
}; | |||||
} // namespace physics | |||||
#endif // ANTKEEPER_PHYSICS_BOX_COLLIDER_HPP |
@ -0,0 +1,131 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_PHYSICS_PLANE_COLLIDER_HPP | |||||
#define ANTKEEPER_PHYSICS_PLANE_COLLIDER_HPP | |||||
#include <engine/physics/kinematics/collider.hpp> | |||||
#include <engine/geom/primitives/plane.hpp> | |||||
namespace physics { | |||||
/** | |||||
* Plane collision object. | |||||
*/ | |||||
class plane_collider: public collider | |||||
{ | |||||
public: | |||||
/// Plane type. | |||||
using plane_type = geom::primitive::plane<float>; | |||||
[[nodiscard]] inline constexpr collider_type type() const noexcept override | |||||
{ | |||||
return collider_type::plane; | |||||
} | |||||
/** | |||||
* Constructs a plane collider from a plane. | |||||
* | |||||
* @param plane Plane shape. | |||||
*/ | |||||
inline explicit plane_collider(const plane_type& plane) noexcept: | |||||
m_plane{plane} | |||||
{} | |||||
/** | |||||
* Constructs a plane collider from a normal and constant. | |||||
* | |||||
* @param normal Plane normal, in object space. | |||||
* @param constant Plane constant. | |||||
*/ | |||||
/// @{ | |||||
inline plane_collider(const math::vector<float, 3>& normal, float constant) noexcept: | |||||
m_plane{normal, constant} | |||||
{} | |||||
inline explicit plane_collider(const math::vector<float, 3>& normal) noexcept: | |||||
m_plane{normal, 0.0f} | |||||
{} | |||||
plane_collider() noexcept = default; | |||||
/// @} | |||||
/** | |||||
* Constructs a plane collider from a normal and offset. | |||||
* | |||||
* @param normal Plane normal, in object space. | |||||
* @param offset Offset from the origin, in object space. | |||||
*/ | |||||
inline plane_collider(const math::vector<float, 3>& normal, const math::vector<float, 3>& offset) noexcept: | |||||
m_plane(normal, offset) | |||||
{} | |||||
/** | |||||
* Sets the collider's plane. | |||||
* | |||||
* @param plane Plane shape. | |||||
*/ | |||||
inline void set_plane(const plane_type& plane) noexcept | |||||
{ | |||||
m_plane = plane; | |||||
} | |||||
/** | |||||
* Sets the plane normal. | |||||
* | |||||
* @param normal Plane normal, in object space. | |||||
*/ | |||||
inline void set_normal(const math::vector<float, 3>& normal) noexcept | |||||
{ | |||||
m_plane.normal = normal; | |||||
} | |||||
/** | |||||
* Sets the plane constant. | |||||
* | |||||
* @param constant Plane constant. | |||||
*/ | |||||
inline void set_constant(float constant) noexcept | |||||
{ | |||||
m_plane.constant = constant; | |||||
} | |||||
/// Returns the plane shape. | |||||
[[nodiscard]] inline const plane_type& get_plane() const noexcept | |||||
{ | |||||
return m_plane; | |||||
} | |||||
/// Returns the plane normal, in object space. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_normal() const noexcept | |||||
{ | |||||
return m_plane.normal; | |||||
} | |||||
/// Returns the plane constant. | |||||
[[nodiscard]] inline float get_constant() const noexcept | |||||
{ | |||||
return m_plane.constant; | |||||
} | |||||
private: | |||||
plane_type m_plane{{0.0f, 0.0f, 0.0f}, 0.0f}; | |||||
}; | |||||
} // namespace physics | |||||
#endif // ANTKEEPER_PHYSICS_PLANE_COLLIDER_HPP |
@ -0,0 +1,121 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_PHYSICS_SPHERE_COLLIDER_HPP | |||||
#define ANTKEEPER_PHYSICS_SPHERE_COLLIDER_HPP | |||||
#include <engine/physics/kinematics/collider.hpp> | |||||
#include <engine/geom/primitives/sphere.hpp> | |||||
namespace physics { | |||||
/** | |||||
* Sphere collision object. | |||||
*/ | |||||
class sphere_collider: public collider | |||||
{ | |||||
public: | |||||
/// Sphere type. | |||||
using sphere_type = geom::primitive::sphere<float>; | |||||
[[nodiscard]] inline constexpr collider_type type() const noexcept override | |||||
{ | |||||
return collider_type::sphere; | |||||
} | |||||
/** | |||||
* Constructs a sphere collider from a sphere. | |||||
* | |||||
* @param sphere Sphere shape. | |||||
*/ | |||||
inline explicit sphere_collider(const sphere_type& sphere) noexcept: | |||||
m_sphere{sphere} | |||||
{} | |||||
/** | |||||
* Constructs a sphere collider. | |||||
* | |||||
* @param center Sphere center. | |||||
* @param radius Sphere radius. | |||||
*/ | |||||
/// @{ | |||||
inline sphere_collider(const math::vector<float, 3>& center, float radius) noexcept: | |||||
m_sphere{center, radius} | |||||
{} | |||||
inline explicit sphere_collider(float radius) noexcept: | |||||
m_sphere{{0.0f, 0.0f, 0.0f}, radius} | |||||
{} | |||||
sphere_collider() noexcept = default; | |||||
/// @} | |||||
/** | |||||
* Sets the collider's sphere. | |||||
* | |||||
* @param sphere Sphere shape. | |||||
*/ | |||||
inline void set_sphere(const sphere_type& sphere) noexcept | |||||
{ | |||||
m_sphere = sphere; | |||||
} | |||||
/** | |||||
* Sets the center of the sphere. | |||||
* | |||||
* @param center Sphere center, in object space. | |||||
*/ | |||||
inline void set_center(const math::vector<float, 3>& center) noexcept | |||||
{ | |||||
m_sphere.center = center; | |||||
} | |||||
/** | |||||
* Sets the radius of the sphere. | |||||
* | |||||
* @param radius Sphere radius. | |||||
*/ | |||||
inline void set_radius(float radius) noexcept | |||||
{ | |||||
m_sphere.radius = radius; | |||||
} | |||||
/// Returns the sphere shape. | |||||
[[nodiscard]] inline const sphere_type& get_sphere() const noexcept | |||||
{ | |||||
return m_sphere; | |||||
} | |||||
/// Returns the center of the sphere, in object space. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_center() const noexcept | |||||
{ | |||||
return m_sphere.center; | |||||
} | |||||
/// Returns the radius of the sphere. | |||||
[[nodiscard]] inline float get_radius() const noexcept | |||||
{ | |||||
return m_sphere.radius; | |||||
} | |||||
private: | |||||
sphere_type m_sphere{{0.0f, 0.0f, 0.0f}, 0.0f}; | |||||
}; | |||||
} // namespace physics | |||||
#endif // ANTKEEPER_PHYSICS_SPHERE_COLLIDER_HPP |
@ -0,0 +1,53 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_PHYSICS_COLLISION_MANIFOLD_HPP | |||||
#define ANTKEEPER_PHYSICS_COLLISION_MANIFOLD_HPP | |||||
#include <engine/physics/kinematics/collision-contact.hpp> | |||||
#include <engine/physics/kinematics/rigid-body.hpp> | |||||
#include <array> | |||||
#include <cstdlib> | |||||
namespace physics { | |||||
/** | |||||
* Collection of contact points between two colliding bodies. | |||||
* | |||||
* @param N Maximum number of contact points. | |||||
*/ | |||||
template <std::uint8_t N> | |||||
struct collision_manifold | |||||
{ | |||||
/// First colliding body. | |||||
rigid_body* body_a{nullptr}; | |||||
/// Second colliding body. | |||||
rigid_body* body_b{nullptr}; | |||||
/// Set of contact points between body a and body b. | |||||
std::array<collision_contact, N> contacts; | |||||
/// Number of contact points between body a and body b. | |||||
std::uint8_t contact_count{0}; | |||||
}; | |||||
} // namespace physics | |||||
#endif // ANTKEEPER_PHYSICS_COLLISION_MANIFOLD_HPP |
@ -0,0 +1,41 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_PHYSICS_CONSTRAINT_HPP | |||||
#define ANTKEEPER_PHYSICS_CONSTRAINT_HPP | |||||
namespace physics { | |||||
/** | |||||
* Abstract base class for rigid body constraints. | |||||
*/ | |||||
class constraint | |||||
{ | |||||
public: | |||||
/** | |||||
* Solves the constraint. | |||||
* | |||||
* @param dt Timestep, in seconds. | |||||
*/ | |||||
virtual void solve(float dt) = 0; | |||||
}; | |||||
} // namespace physics | |||||
#endif // ANTKEEPER_PHYSICS_CONSTRAINT_HPP |
@ -0,0 +1,53 @@ | |||||
/* | |||||
* Copyright (C) 2023 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/>. | |||||
*/ | |||||
#include <engine/physics/kinematics/constraints/spring-constraint.hpp> | |||||
namespace physics { | |||||
void spring_constraint::solve(float dt) | |||||
{ | |||||
if (!m_body_a || !m_body_b) | |||||
{ | |||||
return; | |||||
} | |||||
// Get radius vectors from centers of mass to spring attachment points | |||||
const math::vector<float, 3> radius_a = m_body_a->get_orientation() * m_point_a; | |||||
const math::vector<float, 3> radius_b = m_body_b->get_orientation() * m_point_b; | |||||
// Get world-space spring attachment points | |||||
const math::vector<float, 3> point_a = m_body_a->get_center_of_mass() + radius_a; | |||||
const math::vector<float, 3> point_b = m_body_b->get_center_of_mass() + radius_b; | |||||
// Calculate relative velocity between the attachment points | |||||
const math::vector<float, 3> velocity = m_body_b->get_point_velocity(radius_b) - m_body_a->get_point_velocity(radius_a); | |||||
// Calculate spring force | |||||
// F = -k * (|x| - d) * (x / |x|) - bv | |||||
const math::vector<float, 3> difference = point_b - point_a; | |||||
const float distance = std::sqrt(math::dot(difference, difference)); | |||||
const math::vector<float, 3> force = -m_stiffness * (distance - m_resting_length) * (difference / distance) - velocity * m_damping; | |||||
// Apply spring force to bodies at attachment points | |||||
m_body_a->apply_force(-force, radius_a); | |||||
m_body_b->apply_force(force, radius_b); | |||||
} | |||||
} // namespace physics |
@ -0,0 +1,183 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_PHYSICS_SPRING_CONSTRAINT_HPP | |||||
#define ANTKEEPER_PHYSICS_SPRING_CONSTRAINT_HPP | |||||
#include <engine/physics/kinematics/constraint.hpp> | |||||
#include <engine/physics/kinematics/rigid-body.hpp> | |||||
#include <engine/math/vector.hpp> | |||||
namespace physics { | |||||
/** | |||||
* Spring constraint. | |||||
*/ | |||||
class spring_constraint: public constraint | |||||
{ | |||||
public: | |||||
void solve(float dt) override; | |||||
/** | |||||
* Attaches the spring to body a. | |||||
* | |||||
* @param body_a Body to which the spring should be attached. | |||||
* @param point_a Point on body a, in body-space, at which the spring should be attached. | |||||
*/ | |||||
inline void attach_a(rigid_body& body_a, const math::vector<float, 3>& point_a) noexcept | |||||
{ | |||||
m_body_a = &body_a; | |||||
m_point_a = point_a; | |||||
} | |||||
/** | |||||
* Attaches the spring to body b. | |||||
* | |||||
* @param body_b Body to which the spring should be attached. | |||||
* @param point_b Point on body b, in body-space, at which the spring should be attached. | |||||
*/ | |||||
inline void attach_b(rigid_body& body_b, const math::vector<float, 3>& point_b) noexcept | |||||
{ | |||||
m_body_b = &body_b; | |||||
m_point_b = point_b; | |||||
} | |||||
/** | |||||
* Detaches the spring from body a. | |||||
*/ | |||||
inline void detach_a() noexcept | |||||
{ | |||||
m_body_a = nullptr; | |||||
} | |||||
/** | |||||
* Detaches the spring from body b. | |||||
*/ | |||||
inline void detach_b() noexcept | |||||
{ | |||||
m_body_b = nullptr; | |||||
} | |||||
/** | |||||
* Detaches the spring from bodies a and b. | |||||
*/ | |||||
inline void detach() noexcept | |||||
{ | |||||
detach_a(); | |||||
detach_b(); | |||||
} | |||||
/** | |||||
* Sets the resting length of the spring. | |||||
* | |||||
* @param length Resting length, in meters. | |||||
*/ | |||||
inline void set_resting_length(float length) noexcept | |||||
{ | |||||
m_resting_length = length; | |||||
} | |||||
/** | |||||
* Sets the stiffness constant of the spring. | |||||
* | |||||
* @param stiffness Stiffness constant. | |||||
*/ | |||||
inline void set_stiffness(float stiffness) noexcept | |||||
{ | |||||
m_stiffness = stiffness; | |||||
} | |||||
/** | |||||
* Sets the damping constant of the spring. | |||||
* | |||||
* @param damping Damping constant. | |||||
*/ | |||||
inline void set_damping(float damping) noexcept | |||||
{ | |||||
m_damping = damping; | |||||
} | |||||
/// Returns the body to which the spring is attached at point a. | |||||
[[nodiscard]] inline rigid_body* get_body_a() const noexcept | |||||
{ | |||||
return m_body_a; | |||||
} | |||||
/// Returns the body to which the spring is attached at point b. | |||||
[[nodiscard]] inline rigid_body* get_body_b() const noexcept | |||||
{ | |||||
return m_body_b; | |||||
} | |||||
/// Returns the point at which the spring is attached to body a, in body-space. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_point_a() const noexcept | |||||
{ | |||||
return m_point_a; | |||||
} | |||||
/// Returns the point at which the spring is attached to body b, in body-space. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_point_b() const noexcept | |||||
{ | |||||
return m_point_b; | |||||
} | |||||
/// Returns the resting length of the spring, in meters. | |||||
[[nodiscard]] inline float get_resting_length() const noexcept | |||||
{ | |||||
return m_resting_length; | |||||
} | |||||
/// Returns the stiffness constant of the spring. | |||||
[[nodiscard]] inline float get_stiffness() const noexcept | |||||
{ | |||||
return m_stiffness; | |||||
} | |||||
/// Returns the damping constant of the spring. | |||||
[[nodiscard]] inline float get_damping() const noexcept | |||||
{ | |||||
return m_damping; | |||||
} | |||||
private: | |||||
/// Rigid body to which the spring is attached at point a. | |||||
rigid_body* m_body_a{nullptr}; | |||||
/// Rigid body to which the spring is attached at point b. | |||||
rigid_body* m_body_b{nullptr}; | |||||
/// Point at which the spring is attached to body a, in body-space. | |||||
math::vector<float, 3> m_point_a{0.0f, 0.0f, 0.0f}; | |||||
/// Point at which the spring is attached to body b, in body-space. | |||||
math::vector<float, 3> m_point_b{0.0f, 0.0f, 0.0f}; | |||||
/// Resting length of the spring, in meters. | |||||
float m_resting_length{0.0f}; | |||||
/// Stiffness constant of the spring. | |||||
float m_stiffness{1.0f}; | |||||
/// Damping constant of the spring. | |||||
float m_damping{1.0f}; | |||||
}; | |||||
} // namespace physics | |||||
#endif // ANTKEEPER_PHYSICS_SPRING_CONSTRAINT_HPP |
@ -0,0 +1,63 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_PHYSICS_FRICTION_COMBINE_MODE_HPP | |||||
#define ANTKEEPER_PHYSICS_FRICTION_COMBINE_MODE_HPP | |||||
#include <cstdint> | |||||
namespace physics { | |||||
/** | |||||
* Specifies how coefficients of friction should be calculated. | |||||
* | |||||
* A coefficient of friction is calculated from two collider material friction values (`a` and `b`). | |||||
*/ | |||||
enum class friction_combine_mode: std::uint8_t | |||||
{ | |||||
/** | |||||
* Coefficient of friction is calculated as `(a + b) / 2`. | |||||
*/ | |||||
average, | |||||
/** | |||||
* Coefficient of friction is calculated as `min(a, b)`. | |||||
* | |||||
* @note Takes priority over friction_combine_mode::average | |||||
*/ | |||||
minimum, | |||||
/* | |||||
* Coefficient of friction is calculated as `a * b`. | |||||
* | |||||
* @note Takes priority over friction_combine_mode::average and friction_combine_mode::minimum. | |||||
*/ | |||||
multiply, | |||||
/* | |||||
* Coefficient of friction is calculated as `max(a, b)`. | |||||
* | |||||
* @note Takes priority over friction_combine_mode::average, friction_combine_mode::minimum, and friction_combine_mode::multiply. | |||||
*/ | |||||
maximum | |||||
}; | |||||
} // namespace physics | |||||
#endif // ANTKEEPER_PHYSICS_FRICTION_COMBINE_MODE_HPP |
@ -0,0 +1,63 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_PHYSICS_RESTITUTION_COMBINE_MODE_HPP | |||||
#define ANTKEEPER_PHYSICS_RESTITUTION_COMBINE_MODE_HPP | |||||
#include <cstdint> | |||||
namespace physics { | |||||
/** | |||||
* Specifies how coefficients of restitution should be calculated. | |||||
* | |||||
* A coefficient of restitution is calculated from two collider material restitution values (`a` and `b`). | |||||
*/ | |||||
enum class restitution_combine_mode: std::uint8_t | |||||
{ | |||||
/** | |||||
* Coefficient of restitution is calculated as `(a + b) / 2`. | |||||
*/ | |||||
average, | |||||
/** | |||||
* Coefficient of restitution is calculated as `min(a, b)`. | |||||
* | |||||
* @note Takes priority over restitution_combine_mode::average | |||||
*/ | |||||
minimum, | |||||
/* | |||||
* Coefficient of restitution is calculated as `a * b`. | |||||
* | |||||
* @note Takes priority over restitution_combine_mode::average and restitution_combine_mode::minimum. | |||||
*/ | |||||
multiply, | |||||
/* | |||||
* Coefficient of restitution is calculated as `max(a, b)`. | |||||
* | |||||
* @note Takes priority over restitution_combine_mode::average, restitution_combine_mode::minimum, and restitution_combine_mode::multiply. | |||||
*/ | |||||
maximum | |||||
}; | |||||
} // namespace physics | |||||
#endif // ANTKEEPER_PHYSICS_RESTITUTION_COMBINE_MODE_HPP |
@ -0,0 +1,54 @@ | |||||
/* | |||||
* Copyright (C) 2023 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/>. | |||||
*/ | |||||
#include <engine/physics/kinematics/rigid-body.hpp> | |||||
#include <algorithm> | |||||
namespace physics { | |||||
void rigid_body::integrate_forces(float dt) noexcept | |||||
{ | |||||
// Apply forces | |||||
m_linear_momentum += m_applied_force * dt; | |||||
m_angular_momentum += m_applied_torque * dt; | |||||
// Apply damping | |||||
m_linear_momentum *= std::max<float>(0.0f, 1.0f - m_linear_damping * dt); | |||||
m_angular_momentum *= std::max<float>(0.0f, 1.0f - m_angular_damping * dt); | |||||
// Update velocities | |||||
m_linear_velocity = m_inverse_mass * m_linear_momentum; | |||||
m_angular_velocity = m_inverse_inertia * m_angular_momentum; | |||||
// Clear forces | |||||
m_applied_force = math::vector<float, 3>::zero(); | |||||
m_applied_torque = math::vector<float, 3>::zero(); | |||||
} | |||||
void rigid_body::integrate_velocities(float dt) noexcept | |||||
{ | |||||
// Update center of mass | |||||
m_center_of_mass += m_linear_velocity * dt; | |||||
// Update orientation | |||||
const math::quaternion<float> spin = math::quaternion<float>{0.0f, m_angular_velocity * 0.5f} * m_orientation; | |||||
m_orientation = math::normalize(m_orientation + spin * dt); | |||||
} | |||||
} // namespace physics |
@ -0,0 +1,417 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_PHYSICS_RIGID_BODY_HPP | |||||
#define ANTKEEPER_PHYSICS_RIGID_BODY_HPP | |||||
#include <engine/math/vector.hpp> | |||||
#include <engine/math/quaternion.hpp> | |||||
#include <engine/physics/kinematics/collider.hpp> | |||||
#include <memory> | |||||
namespace physics { | |||||
/** | |||||
* Rigid body. | |||||
*/ | |||||
class rigid_body | |||||
{ | |||||
public: | |||||
/** | |||||
* Sets the center of mass of the rigid body. | |||||
* | |||||
* @param point World-space center of mass. | |||||
*/ | |||||
inline void set_center_of_mass(const math::vector<float, 3>& point) noexcept | |||||
{ | |||||
m_center_of_mass = point; | |||||
} | |||||
/** | |||||
* Sets the world-space orientation of the rigid body. | |||||
* | |||||
* @param orientation World-space orientation. | |||||
*/ | |||||
inline void set_orientation(const math::quaternion<float>& orientation) noexcept | |||||
{ | |||||
m_orientation = orientation; | |||||
} | |||||
/** | |||||
* Sets mass of the rigid body. | |||||
* | |||||
* @param mass Mass, in kg. | |||||
*/ | |||||
inline void set_mass(float mass) noexcept | |||||
{ | |||||
m_mass = mass; | |||||
m_inverse_mass = (mass) ? 1.0f / mass : 0.0f; | |||||
} | |||||
/** | |||||
* Sets the moment of inertia of the rigid body. | |||||
* | |||||
* @param inertia Moment of inertia, in kg⋅m^2. | |||||
*/ | |||||
inline void set_inertia(float inertia) noexcept | |||||
{ | |||||
m_inertia = inertia; | |||||
m_inverse_inertia = (inertia) ? 1.0f / inertia : 0.0f; | |||||
} | |||||
/** | |||||
* Sets the collider of the rigid body. | |||||
* | |||||
* @param collider Shared pointer to a collider. | |||||
*/ | |||||
inline void set_collider(std::shared_ptr<collider> collider) noexcept | |||||
{ | |||||
m_collider = collider; | |||||
} | |||||
/** | |||||
* Sets the linear damping factor of the rigid body. | |||||
* | |||||
* @param damping Linear damping factor. | |||||
*/ | |||||
inline void set_linear_damping(float damping) noexcept | |||||
{ | |||||
m_linear_damping = damping; | |||||
} | |||||
/** | |||||
* Sets the angular damping factor of the rigid body. | |||||
* | |||||
* @param damping Angular damping factor. | |||||
*/ | |||||
inline void set_angular_damping(float damping) noexcept | |||||
{ | |||||
m_angular_damping = damping; | |||||
} | |||||
/** | |||||
* Sets the linear momentum of the rigid body. | |||||
* | |||||
* @param momentum Linear momentum, in kg⋅m/s. | |||||
*/ | |||||
inline void set_linear_momentum(const math::vector<float, 3>& momentum) noexcept | |||||
{ | |||||
m_linear_momentum = momentum; | |||||
m_linear_velocity = m_inverse_mass * m_linear_momentum; | |||||
} | |||||
/** | |||||
* Sets the angular momentum of the rigid body. | |||||
* | |||||
* @param momentum Angular momentum, in kg⋅m^2⋅s^-1. | |||||
*/ | |||||
inline void set_angular_momentum(const math::vector<float, 3>& momentum) noexcept | |||||
{ | |||||
m_angular_momentum = momentum; | |||||
m_angular_velocity = m_inverse_inertia * m_angular_momentum; | |||||
} | |||||
/** | |||||
* Sets the linear velocity of the rigid body. | |||||
* | |||||
* @param velocity Linear velocity, in m/s. | |||||
*/ | |||||
inline void set_linear_velocity(const math::vector<float, 3>& velocity) noexcept | |||||
{ | |||||
m_linear_velocity = velocity; | |||||
m_linear_momentum = m_mass * m_linear_velocity; | |||||
} | |||||
/** | |||||
* Sets the angular velocity of the rigid body. | |||||
* | |||||
* @param velocity Angular velocity, rad/s. | |||||
*/ | |||||
inline void set_angular_velocity(const math::vector<float, 3>& velocity) noexcept | |||||
{ | |||||
m_angular_velocity = velocity; | |||||
m_angular_momentum = m_inertia * m_angular_velocity; | |||||
} | |||||
/// Returns the world-space center of mass of the rigid body. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_center_of_mass() const noexcept | |||||
{ | |||||
return m_center_of_mass; | |||||
} | |||||
/// Returns the world-space orientation of the rigid body. | |||||
[[nodiscard]] inline const math::quaternion<float>& get_orientation() const noexcept | |||||
{ | |||||
return m_orientation; | |||||
} | |||||
/// Returns the mass of the rigid body, in kg. | |||||
[[nodiscard]] inline float get_mass() const noexcept | |||||
{ | |||||
return m_mass; | |||||
} | |||||
/// Returns the inverse mass of the rigid body, in kg^-1. | |||||
[[nodiscard]] inline float get_inverse_mass() const noexcept | |||||
{ | |||||
return m_inverse_mass; | |||||
} | |||||
/// Returns the moment of inertia of the rigid body, in kg⋅m^2. | |||||
[[nodiscard]] inline float get_inertia() const noexcept | |||||
{ | |||||
return m_inertia; | |||||
} | |||||
/// Returns the inverse moment of inertia of the rigid body, in kg⋅m^2^-1. | |||||
[[nodiscard]] inline float get_inverse_inertia() const noexcept | |||||
{ | |||||
return m_inverse_inertia; | |||||
} | |||||
/// Returns the linear damping factor of the rigid body. | |||||
[[nodiscard]] inline float get_linear_damping() const noexcept | |||||
{ | |||||
return m_linear_damping; | |||||
} | |||||
/// Returns the angular damping factor of the rigid body. | |||||
[[nodiscard]] inline float get_angular_damping() const noexcept | |||||
{ | |||||
return m_angular_damping; | |||||
} | |||||
/// Returns the collider of the rigid body. | |||||
[[nodiscard]] inline const std::shared_ptr<collider>& get_collider() const noexcept | |||||
{ | |||||
return m_collider; | |||||
} | |||||
/// Returns the linear momentum of the rigid body, in kg⋅m/s. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_linear_momentum() const noexcept | |||||
{ | |||||
return m_linear_momentum; | |||||
} | |||||
/// Returns the angular momentum of the rigid body, in kg⋅m^2⋅s^-1. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_angular_momentum() const noexcept | |||||
{ | |||||
return m_angular_momentum; | |||||
} | |||||
/// Returns the linear velocity of the rigid body, in m/s. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_linear_velocity() const noexcept | |||||
{ | |||||
return m_linear_velocity; | |||||
} | |||||
/// Returns the angular velocity of the rigid body, in rad/s. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_angular_velocity() const noexcept | |||||
{ | |||||
return m_angular_velocity; | |||||
} | |||||
/// Returns the total pre-integrated force, in N. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_applied_force() const noexcept | |||||
{ | |||||
return m_applied_force; | |||||
} | |||||
/// Returns the total pre-integrated torque, in N⋅m. | |||||
[[nodiscard]] inline const math::vector<float, 3>& get_applied_torque() const noexcept | |||||
{ | |||||
return m_applied_torque; | |||||
} | |||||
/** | |||||
* Calculates the total velocity at a point on the rigid body. | |||||
* | |||||
* @param radius Radius vector from the center of mass to the point at which the velocity should be calculated. | |||||
* | |||||
* @return Point velocity. | |||||
*/ | |||||
[[nodiscard]] inline math::vector<float, 3> get_point_velocity(const math::vector<float, 3>& radius) const noexcept | |||||
{ | |||||
return m_linear_velocity + math::cross(m_angular_velocity, radius); | |||||
} | |||||
/** | |||||
* Returns `true` if the rigid body is static, `false` otherwise. | |||||
*/ | |||||
[[nodiscard]] inline bool is_static() const noexcept | |||||
{ | |||||
return (m_mass == 0.0f); | |||||
} | |||||
/** | |||||
* Applies a force at a point on the rigid body. | |||||
* | |||||
* @param force Force to apply, in N. | |||||
* @param radius Radius vector from the center of mass to the point at which the force should be applied. | |||||
*/ | |||||
inline void apply_force(const math::vector<float, 3>& force, const math::vector<float, 3>& radius) noexcept | |||||
{ | |||||
m_applied_force += force; | |||||
m_applied_torque += math::cross(radius, force); | |||||
} | |||||
/** | |||||
* Applies a force at the center of mass of the rigid body. | |||||
* | |||||
* @param force Force to apply, in N. | |||||
*/ | |||||
inline void apply_central_force(const math::vector<float, 3>& force) noexcept | |||||
{ | |||||
m_applied_force += force; | |||||
} | |||||
/** | |||||
* Applies a torque to the rigid body. | |||||
* | |||||
* @param torque Torque to apply. | |||||
*/ | |||||
inline void apply_torque(const math::vector<float, 3>& torque) noexcept | |||||
{ | |||||
m_applied_torque += torque; | |||||
} | |||||
/** | |||||
* Applies an impulse at a point on the rigid body. | |||||
* | |||||
* @param impulse Impulse to apply, in N⋅s. | |||||
* @param radius Radius vector from the center of mass to the point at which the impulse should be applied. | |||||
*/ | |||||
inline void apply_impulse(const math::vector<float, 3>& impulse, const math::vector<float, 3>& radius) noexcept | |||||
{ | |||||
m_linear_momentum += impulse; | |||||
m_angular_momentum += math::cross(radius, impulse); | |||||
// Update velocities | |||||
m_linear_velocity = m_inverse_mass * m_linear_momentum; | |||||
m_angular_velocity = m_inverse_inertia * m_angular_momentum; | |||||
} | |||||
/** | |||||
* Applies an impulse at the center of mass of the rigid body. | |||||
* | |||||
* @param impulse Impulse to apply, in N⋅s. | |||||
*/ | |||||
inline void apply_central_impulse(const math::vector<float, 3>& impulse) noexcept | |||||
{ | |||||
m_linear_momentum += impulse; | |||||
// Update linear velocity | |||||
m_linear_velocity = m_inverse_mass * m_linear_momentum; | |||||
} | |||||
/** | |||||
* Applies a torque impulse to the rigid body. | |||||
* | |||||
* @param torque Torque impulse to apply. | |||||
*/ | |||||
inline void apply_torque_impulse(const math::vector<float, 3>& torque) noexcept | |||||
{ | |||||
m_angular_momentum += torque; | |||||
// Update angular velocity | |||||
m_angular_velocity = m_inverse_inertia * m_angular_momentum; | |||||
} | |||||
/// Clears all pre-integrated forces. | |||||
inline void clear_applied_forces() noexcept | |||||
{ | |||||
m_applied_force = math::vector<float, 3>::zero(); | |||||
m_applied_torque = math::vector<float, 3>::zero(); | |||||
} | |||||
/** | |||||
* Integrates forces, updating the momentums and velocities of the rigid body. | |||||
* | |||||
* @param dt Timestep, in seconds. | |||||
*/ | |||||
void integrate_forces(float dt) noexcept; | |||||
/** | |||||
* Integrates velocities, updating the center of mass and orientation of the rigid body. | |||||
* | |||||
* @param dt Timestep, in seconds. | |||||
*/ | |||||
void integrate_velocities(float dt) noexcept; | |||||
/** | |||||
* Integrates forces and velocities. | |||||
* | |||||
* @param dt Timestep, in seconds. | |||||
*/ | |||||
inline void integrate(float dt) noexcept | |||||
{ | |||||
integrate_forces(dt); | |||||
integrate_velocities(dt); | |||||
} | |||||
private: | |||||
/// World-space center of mass. | |||||
math::vector<float, 3> m_center_of_mass{math::vector<float, 3>::zero()}; | |||||
/// World-space orientation. | |||||
math::quaternion<float> m_orientation{math::quaternion<float>::identity()}; | |||||
/// Mass, in kg. | |||||
float m_mass{1.0f}; | |||||
/// Inverse mass, in kg^-1. | |||||
float m_inverse_mass{1.0f}; | |||||
/// Moment of inertia, in kg⋅m^2. | |||||
float m_inertia{1.0f}; | |||||
/// Inverse moment of inertia, in kg⋅m^2^-1. | |||||
float m_inverse_inertia{1.0f}; | |||||
/// Linear damping factor. | |||||
float m_linear_damping{0.0f}; | |||||
/// Angular damping factor. | |||||
float m_angular_damping{0.0f}; | |||||
/// Collider object. | |||||
std::shared_ptr<collider> m_collider; | |||||
/// Linear momentum, in kg⋅m/s. | |||||
math::vector<float, 3> m_linear_momentum{math::vector<float, 3>::zero()}; | |||||
/// Angular momentum, in kg⋅m^2⋅s^-1. | |||||
math::vector<float, 3> m_angular_momentum{math::vector<float, 3>::zero()}; | |||||
/// Linear velocity, in m/s. | |||||
math::vector<float, 3> m_linear_velocity{math::vector<float, 3>::zero()}; | |||||
/// Angular velocity, in rad/s. | |||||
math::vector<float, 3> m_angular_velocity{math::vector<float, 3>::zero()}; | |||||
/// Applied force, in N. | |||||
math::vector<float, 3> m_applied_force{math::vector<float, 3>::zero()}; | |||||
/// Applied torque, in N⋅m. | |||||
math::vector<float, 3> m_applied_torque{math::vector<float, 3>::zero()}; | |||||
}; | |||||
} // namespace physics | |||||
#endif // ANTKEEPER_PHYSICS_RIGID_BODY_HPP |
@ -0,0 +1,83 @@ | |||||
/* | |||||
* Copyright (C) 2023 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/>. | |||||
*/ | |||||
#include <engine/utility/frame-scheduler.hpp> | |||||
#include <algorithm> | |||||
#include <thread> | |||||
frame_scheduler::frame_scheduler() noexcept | |||||
{ | |||||
m_frame_start_time = clock_type::now(); | |||||
} | |||||
void frame_scheduler::tick() | |||||
{ | |||||
// Measure duration of previous frame | |||||
m_frame_end_time = clock_type::now(); | |||||
m_frame_duration = m_frame_end_time - m_frame_start_time; | |||||
// Idle until the minimum frame duration has passed | |||||
if (m_frame_duration < m_min_frame_duration) | |||||
{ | |||||
// Determine time to idle until | |||||
const time_point_type idle_time = m_frame_end_time + m_min_frame_duration - m_frame_duration; | |||||
// Idle | |||||
do | |||||
{ | |||||
std::this_thread::yield(); | |||||
m_frame_end_time = clock_type::now(); | |||||
} | |||||
while (m_frame_end_time < idle_time); | |||||
// Measure duration of previous frame with idle | |||||
m_frame_duration = m_frame_end_time - m_frame_start_time; | |||||
} | |||||
// Accumulate previous frame duration (with limit to prevent "spiral of death") | |||||
m_accumulated_time += std::min<duration_type>(m_max_frame_duration, m_frame_duration); | |||||
// Start measuring duration of next frame | |||||
m_frame_start_time = m_frame_end_time; | |||||
// Perform fixed-rate updates | |||||
while (m_accumulated_time >= m_fixed_update_interval) | |||||
{ | |||||
m_fixed_update_callback(m_fixed_update_time, m_fixed_update_interval); | |||||
m_fixed_update_time += m_fixed_update_interval; | |||||
m_accumulated_time -= m_fixed_update_interval; | |||||
} | |||||
// Perform variable-rate update | |||||
m_variable_update_callback(m_fixed_update_time + m_accumulated_time, m_fixed_update_interval, m_accumulated_time); | |||||
} | |||||
void frame_scheduler::refresh() noexcept | |||||
{ | |||||
m_accumulated_time = duration_type::zero(); | |||||
m_frame_duration = duration_type::zero(); | |||||
m_frame_start_time = clock_type::now(); | |||||
} | |||||
void frame_scheduler::reset() noexcept | |||||
{ | |||||
m_fixed_update_time = duration_type::zero(); | |||||
refresh(); | |||||
} |
@ -0,0 +1,211 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_UTILITY_FRAME_SCHEDULER_HPP | |||||
#define ANTKEEPER_UTILITY_FRAME_SCHEDULER_HPP | |||||
#include <chrono> | |||||
#include <functional> | |||||
#include <type_traits> | |||||
/** | |||||
* Schedules fixed- and variable-rate updates. | |||||
* | |||||
* @see https://gafferongames.com/post/fix_your_timestep/ | |||||
*/ | |||||
class frame_scheduler | |||||
{ | |||||
public: | |||||
/// Clock type is `std::chrono::high_resolution_clock` if steady, `std::chrono::steady_clock` otherwise. | |||||
using clock_type = std::conditional | |||||
< | |||||
std::chrono::high_resolution_clock::is_steady, | |||||
std::chrono::high_resolution_clock, std::chrono::steady_clock | |||||
>::type; | |||||
/// Duration type matches the clock's duration type. | |||||
using duration_type = clock_type::duration; | |||||
/// Time point type matches the clock's time point type. | |||||
using time_point_type = clock_type::time_point; | |||||
/** | |||||
* Fixed-rate update callback function type. | |||||
* | |||||
* The first parameter is the elapsed time (`t`) and the second parameter is the fixed-rate update interval (`dt`). | |||||
*/ | |||||
using fixed_update_callback_type = std::function<void(duration_type, duration_type)>; | |||||
/** | |||||
* Variable-rate callback function type. | |||||
* | |||||
* The first parameter is the elapsed time (`t`), the second parameter is the fixed-rate update interval (`dt`), and the third parameter is the accumulated time since the previous fixed-rate update (`at`). | |||||
* | |||||
* @note The subframe interpolation factor (`alpha`) can be calculated as `at / dt`. | |||||
*/ | |||||
using variable_update_callback_type = std::function<void(duration_type, duration_type, duration_type)>; | |||||
/// Constructs a frame scheduler and starts its frame timer. | |||||
frame_scheduler() noexcept; | |||||
/** | |||||
* Performs any scheduled fixed-rate updates followed by a single variable-rate update. | |||||
* | |||||
* @warning Both the fixed-rate and variable-rate update callbacks must be valid when calling `tick()`. | |||||
*/ | |||||
void tick(); | |||||
/** | |||||
* Resets the accumulated time (`at`) and frame timer, but not the elapsed fixed-rate update time. | |||||
*/ | |||||
void refresh() noexcept; | |||||
/** | |||||
* Resets the elapsed fixed-rate update time (`t`), accumulated time (`at`), and frame timer. | |||||
*/ | |||||
void reset() noexcept; | |||||
/** | |||||
* Sets the interval (`dt`) at which fixed-rate updates are scheduled. | |||||
* | |||||
* @param interval Fixed-rate update interval. | |||||
*/ | |||||
inline void set_fixed_update_interval(duration_type interval) noexcept | |||||
{ | |||||
m_fixed_update_interval = interval; | |||||
} | |||||
/** | |||||
* Sets the minimum frame duration. If a frame is quicker than the minimum frame duration, the CPU will be idled until the minimum frame duration is met. | |||||
* | |||||
* @param duration Minimum frame duration. | |||||
*/ | |||||
inline void set_min_frame_duration(duration_type duration) noexcept | |||||
{ | |||||
m_min_frame_duration = duration; | |||||
} | |||||
/** | |||||
* Sets the maximum accumulated frame duration. Prevents a "spiral of death", in which updates are scheduled too many times per frame while trying to catch up to the target update rate. | |||||
* | |||||
* @param duration Maximum accumulated frame duration. | |||||
*/ | |||||
inline void set_max_frame_duration(duration_type duration) noexcept | |||||
{ | |||||
m_max_frame_duration = duration; | |||||
} | |||||
/** | |||||
* Sets the fixed-rate update callback. | |||||
* | |||||
* @param callback Fixed-rate update callback. | |||||
*/ | |||||
/// @{ | |||||
inline void set_fixed_update_callback(fixed_update_callback_type&& callback) noexcept | |||||
{ | |||||
m_fixed_update_callback = callback; | |||||
} | |||||
inline void set_fixed_update_callback(const fixed_update_callback_type& callback) noexcept | |||||
{ | |||||
m_fixed_update_callback = callback; | |||||
} | |||||
/// @} | |||||
/** | |||||
* Sets the variable-rate update callback. | |||||
* | |||||
* @param callback Variable-rate update callback. | |||||
*/ | |||||
/// @{ | |||||
inline void set_variable_update_callback(variable_update_callback_type&& callback) noexcept | |||||
{ | |||||
m_variable_update_callback = callback; | |||||
} | |||||
inline void set_variable_update_callback(const variable_update_callback_type& callback) noexcept | |||||
{ | |||||
m_variable_update_callback = callback; | |||||
} | |||||
/// @} | |||||
/// Returns the elapsed fixed-rate update time (`t`). | |||||
[[nodiscard]] inline duration_type get_fixed_update_time() const noexcept | |||||
{ | |||||
return m_fixed_update_time; | |||||
} | |||||
/// Returns the accumulated time (`at`). | |||||
[[nodiscard]] inline duration_type get_accumulated_time() const noexcept | |||||
{ | |||||
return m_accumulated_time; | |||||
} | |||||
/// Returns the duration of the previous frame. | |||||
[[nodiscard]] inline duration_type get_frame_duration() const noexcept | |||||
{ | |||||
return m_frame_duration; | |||||
} | |||||
/// Returns the minimum frame duration. | |||||
[[nodiscard]] inline duration_type get_min_frame_duration() const noexcept | |||||
{ | |||||
return m_min_frame_duration; | |||||
} | |||||
/// Returns the maximum frame duration. | |||||
[[nodiscard]] inline duration_type get_max_frame_duration() const noexcept | |||||
{ | |||||
return m_max_frame_duration; | |||||
} | |||||
/// Returns the fixed-rate update interval (`dt`). | |||||
[[nodiscard]] inline duration_type get_fixed_update_interval() const noexcept | |||||
{ | |||||
return m_fixed_update_interval; | |||||
} | |||||
/// Returns the fixed-rate update callback. | |||||
[[nodiscard]] inline const fixed_update_callback_type& get_fixed_update_callback() const noexcept | |||||
{ | |||||
return m_fixed_update_callback; | |||||
} | |||||
/// Returns the variable-rate update callback. | |||||
[[nodiscard]] inline const variable_update_callback_type& get_variable_update_callback() const noexcept | |||||
{ | |||||
return m_variable_update_callback; | |||||
} | |||||
private: | |||||
duration_type m_fixed_update_time{duration_type::zero()}; | |||||
duration_type m_accumulated_time{duration_type::zero()}; | |||||
time_point_type m_frame_start_time; | |||||
time_point_type m_frame_end_time; | |||||
duration_type m_frame_duration{duration_type::zero()}; | |||||
duration_type m_min_frame_duration{duration_type::zero()}; | |||||
duration_type m_max_frame_duration{duration_type::max()}; | |||||
duration_type m_fixed_update_interval{duration_type::zero()}; | |||||
fixed_update_callback_type m_fixed_update_callback; | |||||
variable_update_callback_type m_variable_update_callback; | |||||
}; | |||||
#endif // ANTKEEPER_UTILITY_FRAME_SCHEDULER_HPP |
@ -0,0 +1,31 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_RIGID_BODY_CONSTRAINT_COMPONENT_HPP | |||||
#define ANTKEEPER_GAME_RIGID_BODY_CONSTRAINT_COMPONENT_HPP | |||||
#include <engine/physics/kinematics/constraint.hpp> | |||||
#include <memory> | |||||
struct rigid_body_constraint_component | |||||
{ | |||||
std::unique_ptr<physics::constraint> constraint; | |||||
}; | |||||
#endif // ANTKEEPER_GAME_RIGID_BODY_CONSTRAINT_COMPONENT_HPP |
@ -0,0 +1,34 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_WINGED_LOCOMOTION_COMPONENT_HPP | |||||
#define ANTKEEPER_GAME_WINGED_LOCOMOTION_COMPONENT_HPP | |||||
#include <engine/math/vector.hpp> | |||||
/** | |||||
* Winged aerial locomotion. | |||||
*/ | |||||
struct winged_locomotion_component | |||||
{ | |||||
/// Force vector. | |||||
math::vector<float, 3> force{0.0f, 0.0f, 0.0f}; | |||||
}; | |||||
#endif // ANTKEEPER_GAME_WINGED_LOCOMOTION_COMPONENT_HPP |
@ -1,86 +0,0 @@ | |||||
/* | |||||
* Copyright (C) 2023 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/>. | |||||
*/ | |||||
#include "game/loop.hpp" | |||||
#include <algorithm> | |||||
loop::loop(): | |||||
update_callback([](double, double){}), | |||||
render_callback([](double){}), | |||||
update_frequency(60.0), | |||||
update_period(1.0 / update_frequency), | |||||
max_frame_duration(update_period) | |||||
{ | |||||
reset(); | |||||
} | |||||
void loop::set_update_callback(std::function<void(double, double)> callback) | |||||
{ | |||||
update_callback = callback; | |||||
} | |||||
void loop::set_render_callback(std::function<void(double)> callback) | |||||
{ | |||||
render_callback = callback; | |||||
} | |||||
void loop::set_update_frequency(double frequency) | |||||
{ | |||||
update_frequency = frequency; | |||||
update_period = 1.0 / update_frequency; | |||||
} | |||||
void loop::set_max_frame_duration(double duration) | |||||
{ | |||||
max_frame_duration = duration; | |||||
} | |||||
double loop::get_frame_duration() const | |||||
{ | |||||
return frame_duration; | |||||
} | |||||
void loop::reset() | |||||
{ | |||||
elapsed_time = 0.0; | |||||
accumulator = 0.0; | |||||
frame_start = std::chrono::high_resolution_clock::now(); | |||||
frame_end = frame_start; | |||||
frame_duration = 0.0; | |||||
} | |||||
void loop::tick() | |||||
{ | |||||
frame_end = std::chrono::high_resolution_clock::now(); | |||||
frame_duration = static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(frame_end - frame_start).count()) / 1000000000.0; | |||||
frame_start = frame_end; | |||||
accumulator += std::min<double>(max_frame_duration, frame_duration); | |||||
while (accumulator >= update_period) | |||||
{ | |||||
update_callback(elapsed_time, update_period); | |||||
elapsed_time += update_period; | |||||
accumulator -= update_period; | |||||
} | |||||
render_callback(accumulator * update_frequency); | |||||
} | |||||
@ -1,110 +0,0 @@ | |||||
/* | |||||
* Copyright (C) 2023 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_LOOP_HPP | |||||
#define ANTKEEPER_GAME_LOOP_HPP | |||||
#include <chrono> | |||||
#include <functional> | |||||
/** | |||||
* Game loop with fixed timestep update calls and variable timestep render calls. | |||||
* | |||||
* @see https://gafferongames.com/post/fix_your_timestep/ | |||||
*/ | |||||
class loop | |||||
{ | |||||
public: | |||||
loop(); | |||||
/** | |||||
* Sets the update callback. | |||||
* | |||||
* @param callback Function which takes two parameters: `t`, the total elapsed time, and `dt`, delta time (timestep) which is calculated as `1.0 / update_rate`. This function will be called at the frequency specified by `loop::set_update_rate()`. | |||||
*/ | |||||
void set_update_callback(std::function<void(double, double)> callback); | |||||
/** | |||||
* Sets the render callback. | |||||
* | |||||
* @param callback Function which takes one parameter: `alpha`, which is a factor that can be used to interpolate between the previous and current update states. | |||||
*/ | |||||
void set_render_callback(std::function<void(double)> callback); | |||||
/** | |||||
* Sets the update frequency. | |||||
* | |||||
* @param frequency Frequency, in hertz, at which the update callback should be called. | |||||
*/ | |||||
void set_update_frequency(double frequency); | |||||
/** | |||||
* Sets the maximum duration of a frame. This limits the number of times the update callback is called per frame, thereby preventing a "spiral of death", in which the update callback is called too many times per frame while trying to catch up to the target update rate. | |||||
* | |||||
* @param duration Maximum frame duration, in seconds.. | |||||
*/ | |||||
void set_max_frame_duration(double duration); | |||||
/** | |||||
* Returns the duration of the last frame, in seconds. | |||||
*/ | |||||
double get_frame_duration() const; | |||||
/// Returns the frequency, in hertz, at which the update callback should be called. | |||||
double get_update_frequency() const; | |||||
/// Returns the period, in seconds, between update callback calls. | |||||
double get_update_period() const; | |||||
/** | |||||
* Resets the total elapsed time, frame duration, and internal timers. | |||||
*/ | |||||
void reset(); | |||||
/** | |||||
* Updates the internal timers and performs the scheduled update and render callbacks. | |||||
*/ | |||||
void tick(); | |||||
private: | |||||
std::function<void(double, double)> update_callback; | |||||
std::function<void(double)> render_callback; | |||||
double update_frequency; | |||||
double update_period; | |||||
double max_frame_duration; | |||||
double elapsed_time; | |||||
double accumulator; | |||||
std::chrono::high_resolution_clock::time_point frame_start; | |||||
std::chrono::high_resolution_clock::time_point frame_end; | |||||
double frame_duration; | |||||
}; | |||||
inline double loop::get_update_frequency() const | |||||
{ | |||||
return update_frequency; | |||||
} | |||||
inline double loop::get_update_period() const | |||||
{ | |||||
return update_period; | |||||
} | |||||
#endif // ANTKEEPER_GAME_LOOP_HPP |
@ -1,8 +1,9 @@ | |||||
<?xml version="1.0"?> | |||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | |||||
<application xmlns="urn:schemas-microsoft-com:asm.v3"> | |||||
<windowsSettings> | |||||
<ms_windowsSettings:dpiAware xmlns:ms_windowsSettings="http://schemas.microsoft.com/SMI/2005/WindowsSettings" xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</ms_windowsSettings:dpiAware> | |||||
</windowsSettings> | |||||
</application> | |||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> | |||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> | |||||
<asmv3:application> | |||||
<asmv3:windowsSettings> | |||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> | |||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> | |||||
</asmv3:windowsSettings> | |||||
</asmv3:application> | |||||
</assembly> | </assembly> |
@ -0,0 +1,38 @@ | |||||
/* | |||||
* Copyright (C) 2023 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/>. | |||||
*/ | |||||
#define WIN32_LEAN_AND_MEAN | |||||
#include <windows.h> | |||||
extern "C" | |||||
{ | |||||
/** | |||||
* Requests NVIDIA Optimus to use high performance graphics. | |||||
* | |||||
* @see https://docs.nvidia.com/gameworks/content/technologies/desktop/optimus.htm | |||||
*/ | |||||
_declspec(dllexport) DWORD NvOptimusEnablement = 1; | |||||
/** | |||||
* Requests AMD PowerXpress to use high performance graphics. | |||||
* | |||||
* @see https://gpuopen.com/learn/amdpowerxpressrequesthighperformance/ | |||||
*/ | |||||
_declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1; | |||||
} |