Browse Source

Add more vector and quaternion functions. Improve mesh collider. Rename cocoon gene to pupa gene. Add more parameters to various genes. Improve numeric spring class. Improve camera and ant controls. Rename orbit camera to spring arm.

master
C. J. Howard 1 year ago
parent
commit
4b7ad31180
69 changed files with 2011 additions and 1700 deletions
  1. +0
    -1
      CMakeLists.txt
  2. +0
    -136
      src/engine/animation/spring.hpp
  3. +23
    -1
      src/engine/math/quaternion.hpp
  4. +95
    -38
      src/engine/math/vector.hpp
  5. +89
    -0
      src/engine/physics/frequency.hpp
  6. +19
    -0
      src/engine/physics/kinematics/collider-material.hpp
  7. +36
    -11
      src/engine/physics/kinematics/colliders/mesh-collider.cpp
  8. +6
    -2
      src/engine/physics/kinematics/colliders/mesh-collider.hpp
  9. +44
    -0
      src/engine/physics/kinematics/rigid-body.hpp
  10. +201
    -0
      src/engine/physics/spring.hpp
  11. +1
    -1
      src/engine/utility/frame-scheduler.cpp
  12. +1
    -1
      src/engine/utility/frame-scheduler.hpp
  13. +1
    -1
      src/game/ant/ant-cladogenesis.hpp
  14. +2
    -2
      src/game/ant/ant-gene-pool.hpp
  15. +2
    -2
      src/game/ant/ant-genome.hpp
  16. +3
    -3
      src/game/ant/ant-phenome.cpp
  17. +2
    -2
      src/game/ant/ant-phenome.hpp
  18. +0
    -55
      src/game/ant/genes/ant-cocoon-gene.cpp
  19. +3
    -0
      src/game/ant/genes/ant-egg-gene.cpp
  20. +6
    -0
      src/game/ant/genes/ant-egg-gene.hpp
  21. +1
    -1
      src/game/ant/genes/ant-gene-type.hpp
  22. +3
    -0
      src/game/ant/genes/ant-larva-gene.cpp
  23. +10
    -1
      src/game/ant/genes/ant-larva-gene.hpp
  24. +1
    -0
      src/game/ant/genes/ant-legs-gene.cpp
  25. +3
    -0
      src/game/ant/genes/ant-legs-gene.hpp
  26. +58
    -0
      src/game/ant/genes/ant-pupa-gene.cpp
  27. +17
    -11
      src/game/ant/genes/ant-pupa-gene.hpp
  28. +60
    -0
      src/game/components/autofocus-component.hpp
  29. +9
    -13
      src/game/components/decay-component.hpp
  30. +4
    -10
      src/game/components/egg-component.hpp
  31. +50
    -0
      src/game/components/larva-component.hpp
  32. +5
    -0
      src/game/components/legged-locomotion-component.hpp
  33. +32
    -0
      src/game/components/metabolism-component.hpp
  34. +44
    -0
      src/game/components/pupa-component.hpp
  35. +39
    -27
      src/game/components/spring-arm-component.hpp
  36. +0
    -77
      src/game/components/spring-component.hpp
  37. +2
    -2
      src/game/constraints/spring-rotation-constraint.hpp
  38. +3
    -3
      src/game/constraints/spring-to-constraint.hpp
  39. +2
    -2
      src/game/constraints/spring-translation-constraint.hpp
  40. +17
    -5
      src/game/controls.cpp
  41. +72
    -20
      src/game/controls/ant-controls.cpp
  42. +156
    -36
      src/game/controls/camera-controls.cpp
  43. +9
    -9
      src/game/ecoregion-loader.cpp
  44. +17
    -8
      src/game/game.cpp
  45. +19
    -7
      src/game/game.hpp
  46. +87
    -136
      src/game/states/experiments/treadmill-experiment-state.cpp
  47. +0
    -2
      src/game/states/experiments/treadmill-experiment-state.hpp
  48. +2
    -2
      src/game/states/main-menu-state.cpp
  49. +8
    -232
      src/game/states/nest-selection-state.cpp
  50. +1
    -2
      src/game/states/nest-view-state.cpp
  51. +10
    -628
      src/game/states/nuptial-flight-state.cpp
  52. +85
    -35
      src/game/systems/camera-system.cpp
  53. +3
    -0
      src/game/systems/camera-system.hpp
  54. +12
    -12
      src/game/systems/constraint-system.cpp
  55. +23
    -5
      src/game/systems/locomotion-system.cpp
  56. +49
    -0
      src/game/systems/metabolic-system.cpp
  57. +49
    -0
      src/game/systems/metabolic-system.hpp
  58. +146
    -3
      src/game/systems/metamorphosis-system.cpp
  59. +63
    -14
      src/game/systems/physics-system.cpp
  60. +15
    -0
      src/game/systems/physics-system.hpp
  61. +22
    -10
      src/game/systems/reproductive-system.cpp
  62. +8
    -0
      src/game/systems/reproductive-system.hpp
  63. +0
    -74
      src/game/systems/spring-system.cpp
  64. +101
    -0
      src/game/textures/cocoon-silk-sdf.cpp
  65. +28
    -0
      src/game/textures/cocoon-silk-sdf.hpp
  66. +94
    -0
      src/game/textures/rgb-voronoi-noise.cpp
  67. +27
    -0
      src/game/textures/rgb-voronoi-noise.hpp
  68. +9
    -57
      src/game/world.cpp
  69. +2
    -0
      src/game/world.hpp

+ 0
- 1
CMakeLists.txt View File

@ -1,6 +1,5 @@
cmake_minimum_required(VERSION 3.25) cmake_minimum_required(VERSION 3.25)
option(APPLICATION_NAME "Application name" "Antkeeper") option(APPLICATION_NAME "Application name" "Antkeeper")
option(APPLICATION_VERSION "Application version string" "0.0.0") option(APPLICATION_VERSION "Application version string" "0.0.0")
option(APPLICATION_AUTHOR "Application author" "C. J. Howard") option(APPLICATION_AUTHOR "Application author" "C. J. Howard")

+ 0
- 136
src/engine/animation/spring.hpp View File

@ -1,136 +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_SPRING_HPP
#define ANTKEEPER_SPRING_HPP
#include <engine/math/numbers.hpp>
/**
* Contains the variables required for numeric springing.
*
* @tparam T Value type.
* @tparam S Scalar type.
*
* @see spring()
* @see solve_numeric_spring()
*/
template <typename T, typename S>
struct numeric_spring
{
T x0; ///< Start value
T x1; ///< End value
T v; ///< Velocity
S z; ///< Damping ratio, which can be undamped (z = 0), underdamped (z < 1), critically damped (z = 1), or overdamped (z > 1).
S w; ///< Angular frequency of the oscillation, in radians per second (2pi = 1Hz).
};
/**
* Solves a number spring using the implicit Euler method.
*
* @tparam T Value type.
* @tparam S Scalar type.
*
* @param[in,out] x0 Start value, which will be oscillated by this function.
* @param[in,out] v Velocity, which will be modified by this function.
* @param[in] x1 End value.
* @param[in] z Damping ratio, which can be undamped (z = 0), underdamped (z < 1), critically damped (z = 1), or overdamped (z > 1).
* @param[in] w Angular frequency of the oscillation, in radians per second (2pi = 1Hz).
* @param[in] dt Delta time, in seconds.
*/
template <typename T, typename S>
void spring(T& x0, T& v, const T& x1, S z, S w, S dt);
/**
* Solves a number spring using the implicit Euler method.
*
* @param[in,out] ns Numeric spring to be sovled.
* @param dt Delta time, in seconds.
*
* @see spring()
*/
template <typename T, typename S>
void solve_numeric_spring(numeric_spring<T, S>& ns, S dt);
/**
* Converts a frequency from hertz to radians per second.
*
* @param hz Frequency in hertz.
* @return Frequency in radians per second.
*/
template <typename T>
T hz_to_rads(T hz);
/**
* Converts a frequency from radians per second to hertz.
*
* @param rads Frequency in radians per second.
* @return Frequency in hertz.
*/
template <typename T>
T rads_to_hz(T rads);
/**
* Converts a period from seconds to radians per second.
*
* @param t Period, in seconds.
* @return Angular frequency, in radians per second.
*/
template <typename T>
T period_to_rads(T t);
template <typename T, typename S>
void spring(T& x0, T& v, const T& x1, S z, S w, S dt)
{
const S ww_dt = w * w * dt;
const S ww_dtdt = ww_dt * dt;
const S f = z * w * dt * S{2} + S{1};
const T det_x = x0 * f + v * dt + x1 * ww_dtdt;
const T det_v = v + (x1 - x0) * ww_dt;
const S inv_det = S{1} / (f + ww_dtdt);
x0 = det_x * inv_det;
v = det_v * inv_det;
}
template <typename T, typename S>
void solve_numeric_spring(numeric_spring<T, S>& ns, S dt)
{
spring(ns.x0, ns.v, ns.x1, ns.z, ns.w, dt);
}
template <typename T>
inline T hz_to_rads(T hz)
{
return hz * math::two_pi<T>;
}
template <typename T>
inline T rads_to_hz(T rads)
{
return rads / math::two_pi<T>;
}
template <typename T>
inline T period_to_rads(T t)
{
return math::two_pi<T> / t;
}
#endif // ANTKEEPER_SPRING_HPP

+ 23
- 1
src/engine/math/quaternion.hpp View File

@ -445,7 +445,7 @@ template
[[nodiscard]] quaternion<T> angle_axis(T angle, const vec3<T>& axis); [[nodiscard]] quaternion<T> angle_axis(T angle, const vec3<T>& axis);
/** /**
* Calculates a quaternion representing the minimum rotation from one direction to another directions.
* Constructs a quaternion representing the minimum rotation from one direction to another direction.
* *
* @param from Unit vector pointing in the source direction. * @param from Unit vector pointing in the source direction.
* @param to Unit vector pointing in the target direction. * @param to Unit vector pointing in the target direction.
@ -458,6 +458,20 @@ template
template <class T> template <class T>
[[nodiscard]] quaternion<T> rotation(const vec3<T>& from, const vec3<T>& to, T tolerance = T{1e-6}); [[nodiscard]] quaternion<T> rotation(const vec3<T>& from, const vec3<T>& to, T tolerance = T{1e-6});
/**
* Constructs a quaternion representing an angle-limited rotation from one direction towards another direction.
*
* @param from Unit vector pointing in the source direction.
* @param to Unit vector pointing in the target direction.
* @param max_angle Maximum angle of rotation, in radians.
*
* @return Unit quaternion representing a rotation from direction @p from towards direction @p to.
*
* @warning @p from and @p to must be unit vectors.
*/
template <class T>
[[nodiscard]] quaternion<T> rotate_towards(const vec3<T>& from, const vec3<T>& to, T max_angle);
/** /**
* Performs spherical linear interpolation between two quaternions. * Performs spherical linear interpolation between two quaternions.
* *
@ -697,6 +711,14 @@ quaternion rotation(const vec3& from, const vec3& to, T tolerance)
} }
} }
template <class T>
quaternion<T> rotate_towards(const vec3<T>& from, const vec3<T>& to, T max_angle)
{
const auto angle = std::acos(dot(from, to));
const auto axis = cross(from, to);
return angle_axis(std::min(max_angle, angle), axis);
}
template <class T> template <class T>
quaternion<T> slerp(const quaternion<T>& a, const quaternion<T>& b, T t, T tolerance) quaternion<T> slerp(const quaternion<T>& a, const quaternion<T>& b, T t, T tolerance)
{ {

+ 95
- 38
src/engine/math/vector.hpp View File

@ -301,14 +301,14 @@ struct vector
/** /**
* Returns a zero vector, where every element is equal to zero. * Returns a zero vector, where every element is equal to zero.
*/ */
[[nodiscard]] static constexpr vector zero() noexcept
[[nodiscard]] inline static constexpr vector zero() noexcept
{ {
return {}; return {};
} }
/// @private /// @private
template <std::size_t... I> template <std::size_t... I>
[[nodiscard]] static constexpr vector one(std::index_sequence<I...>) noexcept
[[nodiscard]] inline static constexpr vector one(std::index_sequence<I...>) noexcept
{ {
//return {element_type{1}...}; //return {element_type{1}...};
@ -319,14 +319,14 @@ struct vector
/** /**
* Returns a vector of ones, where every element is equal to one. * Returns a vector of ones, where every element is equal to one.
*/ */
[[nodiscard]] static constexpr vector one() noexcept
[[nodiscard]] inline static constexpr vector one() noexcept
{ {
return one(std::make_index_sequence<N>{}); return one(std::make_index_sequence<N>{});
} }
/// @private /// @private
template <std::size_t... I> template <std::size_t... I>
[[nodiscard]] static constexpr vector infinity(std::index_sequence<I...>) noexcept
[[nodiscard]] inline static constexpr vector infinity(std::index_sequence<I...>) noexcept
{ {
//return {element_type{1}...}; //return {element_type{1}...};
@ -337,7 +337,7 @@ struct vector
/** /**
* Returns a vector of infinities, where every element is equal to infinity. * Returns a vector of infinities, where every element is equal to infinity.
*/ */
[[nodiscard]] static constexpr vector infinity() noexcept
[[nodiscard]] inline static constexpr vector infinity() noexcept
{ {
return infinity(std::make_index_sequence<N>{}); return infinity(std::make_index_sequence<N>{});
} }
@ -480,6 +480,17 @@ template
template <std::size_t N> template <std::size_t N>
[[nodiscard]] constexpr bool all(const vector<bool, N>& x) noexcept; [[nodiscard]] constexpr bool all(const vector<bool, N>& x) noexcept;
/**
* Calculates the angle between two direction vectors.
*
* @param from First direction vector.
* @param to Second direction vector.
*
* @return Angle between the two direction vectors, in radians.
*/
template <std::floating_point T, std::size_t N>
[[nodiscard]] T angle(const vector<T, N>& from, const vector<T, N>& to);
/** /**
* Checks if any elements of a boolean vector are `true`. * Checks if any elements of a boolean vector are `true`.
* *
@ -491,13 +502,13 @@ template
[[nodiscard]] constexpr bool any(const vector<bool, N>& x) noexcept; [[nodiscard]] constexpr bool any(const vector<bool, N>& x) noexcept;
/** /**
* Performs a element-wise ceil operation.
* Performs an element-wise ceil operation.
* *
* @param x Input vector. * @param x Input vector.
* *
* @return Component-wise ceil of input vector. * @return Component-wise ceil of input vector.
*/ */
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] constexpr vector<T, N> ceil(const vector<T, N>& x); [[nodiscard]] constexpr vector<T, N> ceil(const vector<T, N>& x);
/** /**
@ -524,11 +535,11 @@ template
* *
* @return Length-clamped vector. * @return Length-clamped vector.
*/ */
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] vector<T, N> clamp_length(const vector<T, N>& x, T max_length); [[nodiscard]] vector<T, N> clamp_length(const vector<T, N>& x, T max_length);
/** /**
* Calculate the cross product of two vectors.
* Calculates the cross product of two vectors.
* *
* @param x First vector. * @param x First vector.
* @param y Second vector. * @param y Second vector.
@ -546,7 +557,7 @@ template
* *
* @return Distance between the two points. * @return Distance between the two points.
*/ */
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] T distance(const vector<T, N>& p0, const vector<T, N>& p1); [[nodiscard]] T distance(const vector<T, N>& p0, const vector<T, N>& p1);
/** /**
@ -595,7 +606,7 @@ template
* *
* @return Component-wise floor of input vector. * @return Component-wise floor of input vector.
*/ */
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] constexpr vector<T, N> floor(const vector<T, N>& x); [[nodiscard]] constexpr vector<T, N> floor(const vector<T, N>& x);
/** /**
@ -621,7 +632,7 @@ template
* *
* @return Fractional parts of input vector. * @return Fractional parts of input vector.
*/ */
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] constexpr vector<T, N> fract(const vector<T, N>& x); [[nodiscard]] constexpr vector<T, N> fract(const vector<T, N>& x);
/** /**
@ -675,7 +686,7 @@ template
* *
* @return Inverse length of the vector. * @return Inverse length of the vector.
*/ */
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] T inv_length(const vector<T, N>& x); [[nodiscard]] T inv_length(const vector<T, N>& x);
/** /**
@ -685,7 +696,7 @@ template
* *
* @return Length of the vector. * @return Length of the vector.
*/ */
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] T length(const vector<T, N>& x); [[nodiscard]] T length(const vector<T, N>& x);
/** /**
@ -761,9 +772,9 @@ template
* @return Remainders of `x / y`. * @return Remainders of `x / y`.
*/ */
/// @{ /// @{
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] constexpr vector<T, N> mod(const vector<T, N>& x, const vector<T, N>& y); [[nodiscard]] constexpr vector<T, N> mod(const vector<T, N>& x, const vector<T, N>& y);
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] constexpr vector<T, N> mod(const vector<T, N>& x, T y); [[nodiscard]] constexpr vector<T, N> mod(const vector<T, N>& x, T y);
/// @} /// @}
@ -799,7 +810,7 @@ template
* *
* @return Unit vector. * @return Unit vector.
*/ */
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] vector<T, N> normalize(const vector<T, N>& x); [[nodiscard]] vector<T, N> normalize(const vector<T, N>& x);
/** /**
@ -832,9 +843,9 @@ template
* @return Vector with its elements raised to the given power. * @return Vector with its elements raised to the given power.
*/ */
/// @{ /// @{
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] vector<T, N> pow(const vector<T, N>& x, const vector<T, N>& y); [[nodiscard]] vector<T, N> pow(const vector<T, N>& x, const vector<T, N>& y);
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] vector<T, N> pow(const vector<T, N>& x, T y); [[nodiscard]] vector<T, N> pow(const vector<T, N>& x, T y);
/// @} /// @}
@ -845,7 +856,7 @@ template
* *
* @return Component-wise round of input vector. * @return Component-wise round of input vector.
*/ */
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] constexpr vector<T, N> round(const vector<T, N>& x); [[nodiscard]] constexpr vector<T, N> round(const vector<T, N>& x);
/** /**
@ -854,9 +865,21 @@ template
* @param x Input vector * @param x Input vector
* @return Signs of input vector elements. * @return Signs of input vector elements.
*/ */
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] constexpr vector<T, N> sign(const vector<T, N>& x); [[nodiscard]] constexpr vector<T, N> sign(const vector<T, N>& x);
/**
* Calculates the signed angle between two direction vectors about axis.
*
* @param from First direction vector.
* @param to Second direction vector.
* @param axis Axis direction vector.
*
* @return Signed angle between @p from and @p to about @p axis, in radians.
*/
template <std::floating_point T>
[[nodiscard]] T signed_angle(const vector<T, 3>& x, const vector<T, 3>& y, const vector<T, 3>& n);
/** /**
* Calculates the square distance between two points. The square distance can be calculated faster than the distance because a call to `std::sqrt` is saved. * Calculates the square distance between two points. The square distance can be calculated faster than the distance because a call to `std::sqrt` is saved.
* *
@ -885,7 +908,7 @@ template
* *
* @return Square roots of the input vector elements. * @return Square roots of the input vector elements.
*/ */
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] vector<T, N> sqrt(const vector<T, N>& x); [[nodiscard]] vector<T, N> sqrt(const vector<T, N>& x);
/** /**
@ -928,13 +951,25 @@ template
template <std::size_t... Indices, class T, std::size_t N> template <std::size_t... Indices, class T, std::size_t N>
[[nodiscard]] constexpr vector<T, sizeof...(Indices)> swizzle(const vector<T, N>& x) noexcept; [[nodiscard]] constexpr vector<T, sizeof...(Indices)> swizzle(const vector<T, N>& x) noexcept;
/**
* Calculates the triple product of three vectors.
*
* @param x First vector.
* @param y Second vector.
* @param z Third vector.
*
* @return Triple product of the three vectors (`dot(x, cross(y, z)`).
*/
template <class T>
[[nodiscard]] constexpr T triple(const vector<T, 3>& x, const vector<T, 3>& y, const vector<T, 3>& z) noexcept;
/** /**
* Performs a element-wise trunc operation. * Performs a element-wise trunc operation.
* *
* @param x Input vector * @param x Input vector
* @return Component-wise trunc of input vector. * @return Component-wise trunc of input vector.
*/ */
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
[[nodiscard]] constexpr vector<T, N> trunc(const vector<T, N>& x); [[nodiscard]] constexpr vector<T, N> trunc(const vector<T, N>& x);
/// @private /// @private
@ -989,6 +1024,12 @@ inline constexpr bool all(const vector& x) noexcept
return all(x, std::make_index_sequence<N>{}); return all(x, std::make_index_sequence<N>{});
} }
template <std::floating_point T, std::size_t N>
T angle(const vector<T, N>& from, const vector<T, N>& to)
{
return std::acos(std::min<T>(std::max<T>(dot(from, to), T{-1}), T{1}));
}
/// @private /// @private
template <std::size_t N, std::size_t... I> template <std::size_t N, std::size_t... I>
inline constexpr bool any(const vector<bool, N>& x, std::index_sequence<I...>) noexcept inline constexpr bool any(const vector<bool, N>& x, std::index_sequence<I...>) noexcept
@ -1044,7 +1085,7 @@ inline constexpr vector clamp(const vector& x, T min, T max)
template <std::floating_point T, std::size_t N> template <std::floating_point T, std::size_t N>
vector<T, N> clamp_length(const vector<T, N>& x, T max_length) vector<T, N> clamp_length(const vector<T, N>& x, T max_length)
{ {
T length2 = sqr_length(x);
const auto length2 = sqr_length(x);
return (length2 > max_length * max_length) ? (x * (max_length / std::sqrt(length2))) : x; return (length2 > max_length * max_length) ? (x * (max_length / std::sqrt(length2))) : x;
} }
@ -1059,7 +1100,7 @@ inline constexpr vector cross(const vector& x, const vector& y
}; };
} }
template <class T, std::size_t N>
template <std::floating_point T, std::size_t N>
inline T distance(const vector<T, N>& p0, const vector<T, N>& p1) inline T distance(const vector<T, N>& p0, const vector<T, N>& p1)
{ {
return length(sub(p0, p1)); return length(sub(p0, p1));
@ -1461,6 +1502,12 @@ inline constexpr vector sign(const vector& x)
return sign(x, std::make_index_sequence<N>{}); return sign(x, std::make_index_sequence<N>{});
} }
template <std::floating_point T>
T signed_angle(const vector<T, 3>& from, const vector<T, 3>& to, const vector<T, 3>& axis)
{
return std::atan2(triple(axis, from, to), dot(from, to));
}
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr T sqr_distance(const vector<T, N>& p0, const vector<T, N>& p1) noexcept inline constexpr T sqr_distance(const vector<T, N>& p0, const vector<T, N>& p1) noexcept
{ {
@ -1544,6 +1591,12 @@ inline constexpr vector swizzle(const vector& x) no
return {x[Indices]...}; return {x[Indices]...};
} }
template <class T>
inline constexpr T triple(const vector<T, 3>& x, const vector<T, 3>& y, const vector<T, 3>& z) noexcept
{
return dot(x, cross(y, z));
}
/// @private /// @private
template <std::floating_point T, std::size_t N, std::size_t... I> template <std::floating_point T, std::size_t N, std::size_t... I>
inline constexpr vector<T, N> trunc(const vector<T, N>& x, std::index_sequence<I...>) inline constexpr vector<T, N> trunc(const vector<T, N>& x, std::index_sequence<I...>)
@ -1561,7 +1614,7 @@ namespace operators {
/// @copydoc add(const vector<T, N>&, const vector<T, N>&) /// @copydoc add(const vector<T, N>&, const vector<T, N>&)
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator+(const vector<T, N>& x, const vector<T, N>& y) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator+(const vector<T, N>& x, const vector<T, N>& y) noexcept
{ {
return add(x, y); return add(x, y);
} }
@ -1569,12 +1622,12 @@ inline constexpr vector operator+(const vector& x, const vector
/// @copydoc add(const vector<T, N>&, T) /// @copydoc add(const vector<T, N>&, T)
/// @{ /// @{
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator+(const vector<T, N>& x, T y) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator+(const vector<T, N>& x, T y) noexcept
{ {
return add(x, y); return add(x, y);
} }
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator+(T x, const vector<T, N>& y) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator+(T x, const vector<T, N>& y) noexcept
{ {
return add(y, x); return add(y, x);
} }
@ -1582,28 +1635,28 @@ inline constexpr vector operator+(T x, const vector& y) noexcept
/// @copydoc div(const vector<T, N>&, const vector<T, N>&) /// @copydoc div(const vector<T, N>&, const vector<T, N>&)
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator/(const vector<T, N>& x, const vector<T, N>& y) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator/(const vector<T, N>& x, const vector<T, N>& y) noexcept
{ {
return div(x, y); return div(x, y);
} }
/// @copydoc div(const vector<T, N>&, T) /// @copydoc div(const vector<T, N>&, T)
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator/(const vector<T, N>& x, T y) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator/(const vector<T, N>& x, T y) noexcept
{ {
return div(x, y); return div(x, y);
} }
/// @copydoc div(T, const vector<T, N>&) /// @copydoc div(T, const vector<T, N>&)
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator/(T x, const vector<T, N>& y) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator/(T x, const vector<T, N>& y) noexcept
{ {
return div(x, y); return div(x, y);
} }
/// @copydoc mul(const vector<T, N>&, const vector<T, N>&) /// @copydoc mul(const vector<T, N>&, const vector<T, N>&)
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator*(const vector<T, N>& x, const vector<T, N>& y) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator*(const vector<T, N>& x, const vector<T, N>& y) noexcept
{ {
return mul(x, y); return mul(x, y);
} }
@ -1611,12 +1664,12 @@ inline constexpr vector operator*(const vector& x, const vector
/// @copydoc mul(const vector<T, N>&, T) /// @copydoc mul(const vector<T, N>&, T)
/// @{ /// @{
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator*(const vector<T, N>& x, T y) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator*(const vector<T, N>& x, T y) noexcept
{ {
return mul(x, y); return mul(x, y);
} }
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator*(T x, const vector<T, N>& y) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator*(T x, const vector<T, N>& y) noexcept
{ {
return mul(y, x); return mul(y, x);
} }
@ -1624,28 +1677,28 @@ inline constexpr vector operator*(T x, const vector& y) noexcept
/// @copydoc negate(const vector<T, N>&) /// @copydoc negate(const vector<T, N>&)
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator-(const vector<T, N>& x) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator-(const vector<T, N>& x) noexcept
{ {
return negate(x); return negate(x);
} }
/// @copydoc sub(const vector<T, N>&, const vector<T, N>&) /// @copydoc sub(const vector<T, N>&, const vector<T, N>&)
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator-(const vector<T, N>& x, const vector<T, N>& y) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator-(const vector<T, N>& x, const vector<T, N>& y) noexcept
{ {
return sub(x, y); return sub(x, y);
} }
/// @copydoc sub(const vector<T, N>&, T) /// @copydoc sub(const vector<T, N>&, T)
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator-(const vector<T, N>& x, T y) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator-(const vector<T, N>& x, T y) noexcept
{ {
return sub(x, y); return sub(x, y);
} }
/// @copydoc sub(T, const vector<T, N>&) /// @copydoc sub(T, const vector<T, N>&)
template <class T, std::size_t N> template <class T, std::size_t N>
inline constexpr vector<T, N> operator-(T x, const vector<T, N>& y) noexcept
[[nodiscard]] inline constexpr vector<T, N> operator-(T x, const vector<T, N>& y) noexcept
{ {
return sub(x, y); return sub(x, y);
} }
@ -1772,4 +1825,8 @@ namespace std
}; };
} }
// Ensure vectors are POD types
static_assert(std::is_standard_layout_v<math::fvec3>);
static_assert(std::is_trivial_v<math::fvec3>);
#endif // ANTKEEPER_MATH_VECTOR_HPP #endif // ANTKEEPER_MATH_VECTOR_HPP

+ 89
- 0
src/engine/physics/frequency.hpp View File

@ -0,0 +1,89 @@
/*
* 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_FREQUENCY_HPP
#define ANTKEEPER_PHYSICS_FREQUENCY_HPP
#include <engine/math/numbers.hpp>
namespace physics {
/**
* Converts frequency to angular frequency.
*
* @tparam T Scalar type.
*
* @param f Frequency, in hertz.
*
* @return Angular frequency, in radians per second.
*/
template <class T>
[[nodiscard]] inline constexpr T hz_to_rads(T f) noexcept
{
return f * math::two_pi<T>;
}
/**
* Converts angular frequency to frequency.
*
* @tparam T Scalar type.
*
* @param w Angular frequency, in radians per second.
*
* @return Frequency, in hertz.
*/
template <class T>
[[nodiscard]] inline constexpr T rads_to_hz(T w) noexcept
{
return w / math::two_pi<T>;
}
/**
* Converts oscillation period to angular frequency.
*
* @tparam T Scalar type.
*
* @param t Oscillation period, in seconds.
*
* @return Angular frequency, in radians per second.
*/
template <class T>
[[nodiscard]] inline constexpr T s_to_rads(T t) noexcept
{
return math::two_pi<T> / t;
}
/**
* Converts angular frequency to oscillation period.
*
* @tparam T Scalar type.
*
* @param w Angular frequency, in radians per second.
*
* @return Oscillation period, in seconds.
*/
template <class T>
[[nodiscard]] inline constexpr T rads_to_s(T w) noexcept
{
return math::two_pi<T> / w;
}
} // namespace physics
#endif // ANTKEEPER_PHYSICS_FREQUENCY_HPP

+ 19
- 0
src/engine/physics/kinematics/collider-material.hpp View File

@ -97,6 +97,16 @@ public:
m_friction_combine_mode = mode; m_friction_combine_mode = mode;
} }
/**
* Sets the density of the material.
*
* @param density Material density.
*/
inline constexpr void set_density(float density) noexcept
{
m_density = density;
}
/// Returns the restitution of the material. /// Returns the restitution of the material.
[[nodiscard]] inline constexpr float get_restitution() const noexcept [[nodiscard]] inline constexpr float get_restitution() const noexcept
{ {
@ -127,6 +137,12 @@ public:
return m_friction_combine_mode; return m_friction_combine_mode;
} }
/// Returns the density of the material.
[[nodiscard]] inline constexpr float get_density() const noexcept
{
return m_density;
}
private: private:
/// Restitution value. /// Restitution value.
float m_restitution{}; float m_restitution{};
@ -142,6 +158,9 @@ private:
/// Friction combine mode. /// Friction combine mode.
friction_combine_mode m_friction_combine_mode{friction_combine_mode::average}; friction_combine_mode m_friction_combine_mode{friction_combine_mode::average};
/// Density.
float m_density{1.0f};
}; };
} // namespace physics } // namespace physics

+ 36
- 11
src/engine/physics/kinematics/colliders/mesh-collider.cpp View File

@ -20,6 +20,7 @@
#include <engine/physics/kinematics/colliders/mesh-collider.hpp> #include <engine/physics/kinematics/colliders/mesh-collider.hpp>
#include <engine/debug/log.hpp> #include <engine/debug/log.hpp>
#include <engine/geom/intersection.hpp> #include <engine/geom/intersection.hpp>
#include <engine/geom/brep/brep-operations.hpp>
#include <limits> #include <limits>
namespace physics { namespace physics {
@ -34,11 +35,24 @@ void mesh_collider::set_mesh(std::shared_ptr mesh)
m_mesh = mesh; m_mesh = mesh;
if (m_mesh) if (m_mesh)
{ {
// Store pointer to mesh vertex positions
m_vertex_positions = &m_mesh->vertices().attributes().at<math::fvec3>("position"); m_vertex_positions = &m_mesh->vertices().attributes().at<math::fvec3>("position");
// If mesh has no face normals
if (!m_mesh->faces().attributes().contains("normal"))
{
// Generate normals
// generate_face_normals(*m_mesh);
generate_vertex_normals(*m_mesh);
}
// Store pointer to mesh face normals
m_face_normals = &m_mesh->faces().attributes().at<math::fvec3>("normal");
} }
else else
{ {
m_vertex_positions = nullptr; m_vertex_positions = nullptr;
m_face_normals = nullptr;
} }
rebuild_bvh(); rebuild_bvh();
@ -56,41 +70,52 @@ void mesh_collider::rebuild_bvh()
} }
} }
std::optional<std::tuple<float, std::uint32_t>> mesh_collider::intersection(const math::transform<float>& mesh_transform, const geom::ray<float, 3>& ray) const
std::optional<std::tuple<float, std::uint32_t, math::fvec3>> mesh_collider::intersection(const geom::ray<float, 3>& ray) const
{ {
// Transform ray into mesh space
const auto inv_mesh_transform = math::inverse(mesh_transform);
const geom::ray<float, 3> mesh_space_ray =
if (!m_mesh)
{ {
inv_mesh_transform * ray.origin,
inv_mesh_transform.rotation * ray.direction
};
return std::nullopt;
}
std::size_t box_hit_count = 0; std::size_t box_hit_count = 0;
std::size_t triangle_hit_count = 0; std::size_t triangle_hit_count = 0;
float nearest_face_distance = std::numeric_limits<float>::infinity(); float nearest_face_distance = std::numeric_limits<float>::infinity();
std::uint32_t nearest_face_index; std::uint32_t nearest_face_index;
// For each BVH leaf node that intersects ray
m_bvh.visit m_bvh.visit
( (
mesh_space_ray,
ray,
[&](std::uint32_t index) [&](std::uint32_t index)
{ {
++box_hit_count; ++box_hit_count;
// If ray is facing backside of face
if (math::dot((*m_face_normals)[index], ray.direction) > 0.0f)
{
// Ignore face
return;
}
// Get pointer to mesh face from BVH primitive index
geom::brep_face* face = m_mesh->faces()[index]; geom::brep_face* face = m_mesh->faces()[index];
// Get face vertex positions
auto loop = face->loops().begin(); auto loop = face->loops().begin();
const auto& a = (*m_vertex_positions)[loop->vertex()->index()]; const auto& a = (*m_vertex_positions)[loop->vertex()->index()];
const auto& b = (*m_vertex_positions)[(++loop)->vertex()->index()]; const auto& b = (*m_vertex_positions)[(++loop)->vertex()->index()];
const auto& c = (*m_vertex_positions)[(++loop)->vertex()->index()]; const auto& c = (*m_vertex_positions)[(++loop)->vertex()->index()];
if (auto intersection = geom::intersection(mesh_space_ray, a, b, c))
// If ray intersects face
if (const auto intersection = geom::intersection(ray, a, b, c))
{ {
++triangle_hit_count; ++triangle_hit_count;
// If distance to point of intersection is nearer than nearest intersection
float t = std::get<0>(*intersection); float t = std::get<0>(*intersection);
if (t < nearest_face_distance) if (t < nearest_face_distance)
{ {
// Update nearest intersection
nearest_face_distance = t; nearest_face_distance = t;
nearest_face_index = index; nearest_face_index = index;
} }
@ -98,14 +123,14 @@ std::optional> mesh_collider::intersection(cons
} }
); );
debug::log::info("mesh collider intersection test:\n\tboxes hit: {}\n\ttriangles hit: {}", box_hit_count, triangle_hit_count);
// debug::log::debug("mesh collider intersection test:\n\tboxes hit: {}\n\ttriangles hit: {}", box_hit_count, triangle_hit_count);
if (!triangle_hit_count) if (!triangle_hit_count)
{ {
return std::nullopt; return std::nullopt;
} }
return std::make_tuple(nearest_face_distance, nearest_face_index);
return std::make_tuple(nearest_face_distance, nearest_face_index, (*m_face_normals)[nearest_face_index]);
} }
} // namespace physics } // namespace physics

+ 6
- 2
src/engine/physics/kinematics/colliders/mesh-collider.hpp View File

@ -25,7 +25,6 @@
#include <engine/geom/bvh/bvh.hpp> #include <engine/geom/bvh/bvh.hpp>
#include <engine/math/vector.hpp> #include <engine/math/vector.hpp>
#include <engine/geom/primitives/ray.hpp> #include <engine/geom/primitives/ray.hpp>
#include <engine/math/transform.hpp>
#include <memory> #include <memory>
#include <optional> #include <optional>
@ -87,13 +86,18 @@ public:
void rebuild_bvh(); void rebuild_bvh();
/** /**
* Finds the nearest point of intersection between a ray and this collision mesh.
* *
* @param ray Mesh-space ray.
*
* @return Tuple containing the distance along the ray to the nearest point of intersection, the index of the nearest mesh face, and the surface normal of the intersected face; or std::nullopt if no intersection occurred.
*/ */
[[nodiscard]] std::optional<std::tuple<float, std::uint32_t>> intersection(const math::transform<float>& mesh_transform, const geom::ray<float, 3>& ray) const;
[[nodiscard]] std::optional<std::tuple<float, std::uint32_t, math::fvec3>> intersection(const geom::ray<float, 3>& ray) const;
private: private:
std::shared_ptr<mesh_type> m_mesh; std::shared_ptr<mesh_type> m_mesh;
const geom::brep_attribute<math::fvec3>* m_vertex_positions{}; const geom::brep_attribute<math::fvec3>* m_vertex_positions{};
const geom::brep_attribute<math::fvec3>* m_face_normals{};
bvh_type m_bvh; bvh_type m_bvh;
}; };

+ 44
- 0
src/engine/physics/kinematics/rigid-body.hpp View File

@ -64,6 +64,22 @@ public:
m_current_transform.rotation = orientation; m_current_transform.rotation = orientation;
} }
/**
* Sets the current scale of the rigid body.
*
* @param scale Scale of the rigid body.
*/
/// @{
inline constexpr void set_scale(const math::fvec3& scale) noexcept
{
m_current_transform.scale = scale;
}
inline constexpr void set_scale(float scale) noexcept
{
m_current_transform.scale = {scale, scale, scale};
}
/// @}
/** /**
* Sets the transformation representing the previous state of the rigid body. * Sets the transformation representing the previous state of the rigid body.
* *
@ -94,6 +110,22 @@ public:
m_previous_transform.rotation = orientation; m_previous_transform.rotation = orientation;
} }
/**
* Sets the previous scale of the rigid body.
*
* @param scale Scale of the rigid body.
*/
/// @{
inline constexpr void set_previous_scale(const math::fvec3& scale) noexcept
{
m_previous_transform.scale = scale;
}
inline constexpr void set_previous_scale(float scale) noexcept
{
m_previous_transform.scale = {scale, scale, scale};
}
/// @}
/** /**
* Sets the center of mass of the rigid body. * Sets the center of mass of the rigid body.
* *
@ -218,6 +250,12 @@ public:
return m_current_transform.rotation; return m_current_transform.rotation;
} }
/// Returns the current scale of the rigid body.
[[nodiscard]] inline constexpr const math::fvec3& get_scale() const noexcept
{
return m_current_transform.scale;
}
/// Returns the transformation representing the previous state of the rigid body. /// Returns the transformation representing the previous state of the rigid body.
[[nodiscard]] inline constexpr const math::transform<float>& get_previous_transform() const noexcept [[nodiscard]] inline constexpr const math::transform<float>& get_previous_transform() const noexcept
{ {
@ -236,6 +274,12 @@ public:
return m_previous_transform.rotation; return m_previous_transform.rotation;
} }
/// Returns the previous scale of the rigid body.
[[nodiscard]] inline constexpr const math::fvec3& get_previous_scale() const noexcept
{
return m_previous_transform.scale;
}
/// Returns the center of mass of the rigid body. /// Returns the center of mass of the rigid body.
[[nodiscard]] inline constexpr const math::fvec3& get_center_of_mass() const noexcept [[nodiscard]] inline constexpr const math::fvec3& get_center_of_mass() const noexcept
{ {

+ 201
- 0
src/engine/physics/spring.hpp View File

@ -0,0 +1,201 @@
/*
* 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_HPP
#define ANTKEEPER_PHYSICS_SPRING_HPP
#include <engine/math/numbers.hpp>
#include <engine/physics/frequency.hpp>
namespace physics {
/**
* Solves a numeric spring using the implicit Euler method.
*
* @tparam T Value type.
* @tparam S Scalar type.
*
* @param[in,out] x Current value of the spring.
* @param[in,out] v Current velocity of the spring.
* @param[in] xt Target value of the spring.
* @param[in] z Damping ratio of the spring, which can be undamped (`0`), underdamped (`< 1`), critically damped (`1`), or overdamped (`> 1`).
* @param[in] w Angular frequency of the spring oscillation, in radians per second.
* @param[in] dt Delta time, in seconds.
*/
template <class T, class S>
constexpr void solve_spring(T& x, T& v, const T& xt, S z, S w, S dt) noexcept
{
const auto ww_dt = w * w * dt;
const auto ww_dtdt = ww_dt * dt;
const auto f = z * w * dt * S{2} + S{1};
const auto inv_det = S{1} / (f + ww_dtdt);
x = (x * f + v * dt + xt * ww_dtdt) * inv_det;
v = (v + (xt - x) * ww_dt) * inv_det;
}
/**
* Numeric spring.
*
* @tparam T Value type.
* @tparam S Scalar type.
*/
template <class T, class S>
class numeric_spring
{
public:
/// Value type.
using value_type = T;
/// Scalar type.
using scalar_type = S;
/**
* Solves the spring using the implicit Euler method.
*
* @param dt Delta time, in seconds.
*/
inline constexpr void solve(scalar_type dt) noexcept
{
solve_spring(m_value, m_velocity, m_target_value, m_damping_ratio, m_angular_frequency, dt);
}
/**
* Sets the current value of the spring.
*
* @param value Current value.
*/
inline constexpr void set_value(const value_type& value) noexcept
{
m_value = value;
}
/**
* Sets the target value of the spring.
*
* @param value Target value.
*/
inline constexpr void set_target_value(const value_type& value) noexcept
{
m_target_value = value;
}
/**
* Sets the velocity of the spring.
*
* @param velocity Spring velocity.
*/
inline constexpr void set_velocity(const value_type& velocity) noexcept
{
m_velocity = velocity;
}
/**
* Sets the damping ratio of the spring.
*
* @param ratio Damping ratio, which can be undamped (`0`), underdamped (`< 1`), critically damped (`1`), or overdamped (`> 1`).
*/
inline constexpr void set_damping_ratio(scalar_type ratio) noexcept
{
m_damping_ratio = ratio;
}
/**
* Sets the angular frequency of the spring oscillation.
*
* @param w Angular frequency, in radians per second.
*/
inline constexpr void set_angular_frequency(scalar_type angular_frequency) noexcept
{
m_angular_frequency = angular_frequency;
}
/**
* Sets the oscillation period of the spring.
*
* @param period Oscillation period, in seconds.
*/
inline constexpr void set_period(scalar_type period) noexcept
{
m_angular_frequency = s_to_rads(period);
}
/**
* Sets the oscillation frequency of the spring.
*
* @param frequency Oscillation frequency, in hertz.
*/
inline constexpr void set_frequency(scalar_type frequency) noexcept
{
m_angular_frequency = hz_to_rads(frequency);
}
/// Returns the current value of the spring.
[[nodiscard]] inline constexpr const value_type& get_value() const noexcept
{
return m_value;
}
/// Returns the target value of the spring.
[[nodiscard]] inline constexpr const value_type& get_target_value() const noexcept
{
return m_target_value;
}
/// Returns the velocity of the spring.
[[nodiscard]] inline constexpr const value_type& get_velocity() const noexcept
{
return m_velocity;
}
/// Returns the damping ratio of the spring.
[[nodiscard]] inline constexpr scalar_type get_damping_ratio() const noexcept
{
return m_damping_ratio;
}
/// Returns the angular frequency of the spring oscillation, in radians per second.
[[nodiscard]] inline constexpr scalar_type get_angular_frequency() const noexcept
{
return m_angular_frequency;
}
/// Returns the oscillation period of the spring, in seconds.
[[nodiscard]] inline constexpr scalar_type get_period() const noexcept
{
return rads_to_s(m_angular_frequency);
}
/// Returns the oscillation frequency of the spring, in hertz.
[[nodiscard]] inline constexpr scalar_type get_frequency() const noexcept
{
return rads_to_hz(m_angular_frequency);
}
private:
value_type m_value{};
value_type m_target_value{};
value_type m_velocity{};
scalar_type m_damping_ratio{1};
scalar_type m_angular_frequency{math::two_pi<scalar_type>};
};
} // namespace physics
#endif // ANTKEEPER_PHYSICS_SPRING_HPP

+ 1
- 1
src/engine/utility/frame-scheduler.cpp View File

@ -66,7 +66,7 @@ void frame_scheduler::tick()
} }
// Perform variable-rate update // Perform variable-rate update
m_variable_update_callback(m_fixed_update_time + m_accumulated_time, m_fixed_update_interval, m_accumulated_time);
m_variable_update_callback(m_fixed_update_time, m_fixed_update_interval, m_accumulated_time);
} }
void frame_scheduler::refresh() noexcept void frame_scheduler::refresh() noexcept

+ 1
- 1
src/engine/utility/frame-scheduler.hpp View File

@ -27,7 +27,7 @@
/** /**
* Schedules fixed- and variable-rate updates. * Schedules fixed- and variable-rate updates.
* *
* @see https://gafferongames.com/post/fix_your_timestep/
* @see Fiedler, G. (2004). Fix your timestep. Gaffer On Games.
*/ */
class frame_scheduler class frame_scheduler
{ {

+ 1
- 1
src/game/ant/ant-cladogenesis.hpp View File

@ -44,7 +44,7 @@ template
// Randomly sample genes // Randomly sample genes
genome->antennae = pool.antennae.sample(urbg); genome->antennae = pool.antennae.sample(urbg);
genome->body_size = pool.body_size.sample(urbg); genome->body_size = pool.body_size.sample(urbg);
genome->cocoon = pool.cocoon.sample(urbg);
genome->pupa = pool.pupa.sample(urbg);
genome->diet = pool.diet.sample(urbg); genome->diet = pool.diet.sample(urbg);
genome->egg = pool.egg.sample(urbg); genome->egg = pool.egg.sample(urbg);
genome->eyes = pool.eyes.sample(urbg); genome->eyes = pool.eyes.sample(urbg);

+ 2
- 2
src/game/ant/ant-gene-pool.hpp View File

@ -23,7 +23,7 @@
#include "game/ant/ant-gene-frequency-table.hpp" #include "game/ant/ant-gene-frequency-table.hpp"
#include "game/ant/genes/ant-antennae-gene.hpp" #include "game/ant/genes/ant-antennae-gene.hpp"
#include "game/ant/genes/ant-body-size-gene.hpp" #include "game/ant/genes/ant-body-size-gene.hpp"
#include "game/ant/genes/ant-cocoon-gene.hpp"
#include "game/ant/genes/ant-pupa-gene.hpp"
#include "game/ant/genes/ant-diet-gene.hpp" #include "game/ant/genes/ant-diet-gene.hpp"
#include "game/ant/genes/ant-egg-gene.hpp" #include "game/ant/genes/ant-egg-gene.hpp"
#include "game/ant/genes/ant-eyes-gene.hpp" #include "game/ant/genes/ant-eyes-gene.hpp"
@ -54,7 +54,7 @@ struct ant_gene_pool
ant_gene_frequency_table<ant_antennae_gene> antennae; ant_gene_frequency_table<ant_antennae_gene> antennae;
ant_gene_frequency_table<ant_body_size_gene> body_size; ant_gene_frequency_table<ant_body_size_gene> body_size;
ant_gene_frequency_table<ant_cocoon_gene> cocoon;
ant_gene_frequency_table<ant_pupa_gene> pupa;
ant_gene_frequency_table<ant_diet_gene> diet; ant_gene_frequency_table<ant_diet_gene> diet;
ant_gene_frequency_table<ant_egg_gene> egg; ant_gene_frequency_table<ant_egg_gene> egg;
ant_gene_frequency_table<ant_eyes_gene> eyes; ant_gene_frequency_table<ant_eyes_gene> eyes;

+ 2
- 2
src/game/ant/ant-genome.hpp View File

@ -22,7 +22,7 @@
#include "game/ant/genes/ant-antennae-gene.hpp" #include "game/ant/genes/ant-antennae-gene.hpp"
#include "game/ant/genes/ant-body-size-gene.hpp" #include "game/ant/genes/ant-body-size-gene.hpp"
#include "game/ant/genes/ant-cocoon-gene.hpp"
#include "game/ant/genes/ant-pupa-gene.hpp"
#include "game/ant/genes/ant-diet-gene.hpp" #include "game/ant/genes/ant-diet-gene.hpp"
#include "game/ant/genes/ant-egg-gene.hpp" #include "game/ant/genes/ant-egg-gene.hpp"
#include "game/ant/genes/ant-eyes-gene.hpp" #include "game/ant/genes/ant-eyes-gene.hpp"
@ -50,7 +50,7 @@ struct ant_genome
{ {
std::shared_ptr<ant_antennae_gene> antennae; std::shared_ptr<ant_antennae_gene> antennae;
std::shared_ptr<ant_body_size_gene> body_size; std::shared_ptr<ant_body_size_gene> body_size;
std::shared_ptr<ant_cocoon_gene> cocoon;
std::shared_ptr<ant_pupa_gene> pupa;
std::shared_ptr<ant_diet_gene> diet; std::shared_ptr<ant_diet_gene> diet;
std::shared_ptr<ant_egg_gene> egg; std::shared_ptr<ant_egg_gene> egg;
std::shared_ptr<ant_eyes_gene> eyes; std::shared_ptr<ant_eyes_gene> eyes;

+ 3
- 3
src/game/ant/ant-phenome.cpp View File

@ -27,9 +27,9 @@ ant_phenome::ant_phenome(const ant_genome& genome, ant_caste_type caste)
if (genome.body_size) if (genome.body_size)
if (auto i = genome.body_size->phene_map.find(caste); i != genome.body_size->phene_map.end()) if (auto i = genome.body_size->phene_map.find(caste); i != genome.body_size->phene_map.end())
body_size = i->second; body_size = i->second;
if (genome.cocoon)
if (auto i = genome.cocoon->phene_map.find(caste); i != genome.cocoon->phene_map.end())
cocoon = i->second;
if (genome.pupa)
if (auto i = genome.pupa->phene_map.find(caste); i != genome.pupa->phene_map.end())
pupa = i->second;
if (genome.diet) if (genome.diet)
if (auto i = genome.diet->phene_map.find(caste); i != genome.diet->phene_map.end()) if (auto i = genome.diet->phene_map.find(caste); i != genome.diet->phene_map.end())
diet = i->second; diet = i->second;

+ 2
- 2
src/game/ant/ant-phenome.hpp View File

@ -22,7 +22,7 @@
#include "game/ant/genes/ant-antennae-gene.hpp" #include "game/ant/genes/ant-antennae-gene.hpp"
#include "game/ant/genes/ant-body-size-gene.hpp" #include "game/ant/genes/ant-body-size-gene.hpp"
#include "game/ant/genes/ant-cocoon-gene.hpp"
#include "game/ant/genes/ant-pupa-gene.hpp"
#include "game/ant/genes/ant-diet-gene.hpp" #include "game/ant/genes/ant-diet-gene.hpp"
#include "game/ant/genes/ant-egg-gene.hpp" #include "game/ant/genes/ant-egg-gene.hpp"
#include "game/ant/genes/ant-eyes-gene.hpp" #include "game/ant/genes/ant-eyes-gene.hpp"
@ -63,7 +63,7 @@ struct ant_phenome
const ant_antennae_phene* antennae{nullptr}; const ant_antennae_phene* antennae{nullptr};
const ant_body_size_phene* body_size{nullptr}; const ant_body_size_phene* body_size{nullptr};
const ant_cocoon_phene* cocoon{nullptr};
const ant_pupa_phene* pupa{nullptr};
const ant_diet_phene* diet{nullptr}; const ant_diet_phene* diet{nullptr};
const ant_egg_phene* egg{nullptr}; const ant_egg_phene* egg{nullptr};
const ant_eyes_phene* eyes{nullptr}; const ant_eyes_phene* eyes{nullptr};

+ 0
- 55
src/game/ant/genes/ant-cocoon-gene.cpp View File

@ -1,55 +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/ant/genes/ant-cocoon-gene.hpp"
#include "game/ant/genes/ant-gene-loader.hpp"
#include <engine/resources/resource-loader.hpp>
#include <engine/resources/resource-manager.hpp>
#include <engine/render/model.hpp>
namespace {
void load_ant_cocoon_phene(ant_cocoon_phene& phene, ::resource_manager& resource_manager, deserialize_context& ctx)
{
std::uint8_t present{0};
ctx.read8(reinterpret_cast<std::byte*>(&present), 1);
phene.present = static_cast<bool>(present);
std::uint8_t model_filename_length{0};
ctx.read8(reinterpret_cast<std::byte*>(&model_filename_length), 1);
std::string model_filename(model_filename_length, '\0');
ctx.read8(reinterpret_cast<std::byte*>(model_filename.data()), model_filename_length);
if (phene.present)
{
phene.model = resource_manager.load<render::model>(model_filename);
}
}
} // namespace
template <>
std::unique_ptr<ant_cocoon_gene> resource_loader<ant_cocoon_gene>::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
std::unique_ptr<ant_cocoon_gene> gene = std::make_unique<ant_cocoon_gene>();
load_ant_gene(*gene, resource_manager, ctx, &load_ant_cocoon_phene);
return gene;
}

+ 3
- 0
src/game/ant/genes/ant-egg-gene.cpp View File

@ -27,6 +27,9 @@ namespace {
void load_ant_egg_phene(ant_egg_phene& phene, ::resource_manager& resource_manager, deserialize_context& ctx) void load_ant_egg_phene(ant_egg_phene& phene, ::resource_manager& resource_manager, deserialize_context& ctx)
{ {
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.incubation_period), 1);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.eclosion_period), 1);
std::uint8_t model_filename_length{0}; std::uint8_t model_filename_length{0};
ctx.read8(reinterpret_cast<std::byte*>(&model_filename_length), 1); ctx.read8(reinterpret_cast<std::byte*>(&model_filename_length), 1);
std::string model_filename(model_filename_length, '\0'); std::string model_filename(model_filename_length, '\0');

+ 6
- 0
src/game/ant/genes/ant-egg-gene.hpp View File

@ -29,6 +29,12 @@
*/ */
struct ant_egg_phene struct ant_egg_phene
{ {
/// Duration required for the embryo to develop into a larva, in days.
float incubation_period{};
/// Duration required for the contained larva to hatch, in days.
float eclosion_period{};
/// 3D model of the egg. /// 3D model of the egg.
std::shared_ptr<render::model> model; std::shared_ptr<render::model> model;
}; };

+ 1
- 1
src/game/ant/genes/ant-gene-type.hpp View File

@ -29,7 +29,7 @@ enum class ant_gene_type: std::uint8_t
{ {
antennae = 1, antennae = 1,
body_size, body_size,
cocoon,
pupa,
diet, diet,
egg, egg,
eyes, eyes,

+ 3
- 0
src/game/ant/genes/ant-larva-gene.cpp View File

@ -27,7 +27,10 @@ namespace {
void load_ant_larva_phene(ant_larva_phene& phene, ::resource_manager& resource_manager, deserialize_context& ctx) void load_ant_larva_phene(ant_larva_phene& phene, ::resource_manager& resource_manager, deserialize_context& ctx)
{ {
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.development_period), 1);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.spinning_period), 1);
ctx.read8(reinterpret_cast<std::byte*>(&phene.instar_count), 1); ctx.read8(reinterpret_cast<std::byte*>(&phene.instar_count), 1);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.first_instar_scale), 1);
std::uint8_t model_filename_length{0}; std::uint8_t model_filename_length{0};
ctx.read8(reinterpret_cast<std::byte*>(&model_filename_length), 1); ctx.read8(reinterpret_cast<std::byte*>(&model_filename_length), 1);

+ 10
- 1
src/game/ant/genes/ant-larva-gene.hpp View File

@ -30,8 +30,17 @@
*/ */
struct ant_larva_phene struct ant_larva_phene
{ {
/// Duration required for the larva to develop into a pupa, in days.
float development_period{};
/// Duration required for the larva to spin a cocoon, in days.
float spinning_period{};
/// Number of larval instars before pupation. /// Number of larval instars before pupation.
std::uint8_t instar_count{0};
std::uint8_t instar_count{};
/// Scale of the first larva instar, relative to the final larval instar.
float first_instar_scale{};
/// 3D model of the larva. /// 3D model of the larva.
std::shared_ptr<render::model> model; std::shared_ptr<render::model> model;

+ 1
- 0
src/game/ant/genes/ant-legs-gene.cpp View File

@ -32,6 +32,7 @@ void load_ant_legs_phene(ant_legs_phene& phene, ::resource_manager& resource_man
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.walking_speed), 1); ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.walking_speed), 1);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.running_speed), 1); ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.running_speed), 1);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.stride_length), 1); ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.stride_length), 1);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.max_angular_frequency), 1);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.grip), 1); ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.grip), 1);
std::uint8_t model_filename_length{0}; std::uint8_t model_filename_length{0};

+ 3
- 0
src/game/ant/genes/ant-legs-gene.hpp View File

@ -46,6 +46,9 @@ struct ant_legs_phene
/// Distance covered in a single gait cycle, in mesosomal lengths. /// Distance covered in a single gait cycle, in mesosomal lengths.
float stride_length{}; float stride_length{};
/// Maximum angular frequency when turning, in radians per second.
float max_angular_frequency{};
/// Grip factor. /// Grip factor.
float grip{}; float grip{};

+ 58
- 0
src/game/ant/genes/ant-pupa-gene.cpp View File

@ -0,0 +1,58 @@
/*
* 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/ant/genes/ant-pupa-gene.hpp"
#include "game/ant/genes/ant-gene-loader.hpp"
#include <engine/resources/resource-loader.hpp>
#include <engine/resources/resource-manager.hpp>
#include <engine/render/model.hpp>
namespace {
void load_ant_pupa_phene(ant_pupa_phene& phene, ::resource_manager& resource_manager, deserialize_context& ctx)
{
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.development_period), 1);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&phene.eclosion_period), 1);
std::uint8_t cocoon_present{0};
ctx.read8(reinterpret_cast<std::byte*>(&cocoon_present), 1);
phene.cocoon_present = static_cast<bool>(cocoon_present);
std::uint8_t cocoon_model_filename_length{0};
ctx.read8(reinterpret_cast<std::byte*>(&cocoon_model_filename_length), 1);
std::string cocoon_model_filename(cocoon_model_filename_length, '\0');
ctx.read8(reinterpret_cast<std::byte*>(cocoon_model_filename.data()), cocoon_model_filename_length);
if (phene.cocoon_present)
{
phene.cocoon_model = resource_manager.load<render::model>(cocoon_model_filename);
}
}
} // namespace
template <>
std::unique_ptr<ant_pupa_gene> resource_loader<ant_pupa_gene>::load(::resource_manager& resource_manager, deserialize_context& ctx)
{
std::unique_ptr<ant_pupa_gene> gene = std::make_unique<ant_pupa_gene>();
load_ant_gene(*gene, resource_manager, ctx, &load_ant_pupa_phene);
return gene;
}

src/game/ant/genes/ant-cocoon-gene.hpp → src/game/ant/genes/ant-pupa-gene.hpp View File

@ -17,32 +17,38 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef ANTKEEPER_GAME_ANT_COCOON_GENE_HPP
#define ANTKEEPER_GAME_ANT_COCOON_GENE_HPP
#ifndef ANTKEEPER_GAME_ANT_PUPA_GENE_HPP
#define ANTKEEPER_GAME_ANT_PUPA_GENE_HPP
#include "game/ant/genes/ant-gene.hpp" #include "game/ant/genes/ant-gene.hpp"
#include <engine/render/model.hpp> #include <engine/render/model.hpp>
#include <memory> #include <memory>
/** /**
* Ant cocoon phene.
* Ant pupa phene.
*/ */
struct ant_cocoon_phene
struct ant_pupa_phene
{ {
/// Duration required for the pupa to develop into an adult, in days.
float development_period{};
/// Duration required for the callow to emerge from the cocoon or become mobile, in days.
float eclosion_period{};
/// Indicates whether a cocoon is formed by the larvae or not. /// Indicates whether a cocoon is formed by the larvae or not.
bool present{false};
bool cocoon_present{false};
/// 3D model of the cocoon, if present. /// 3D model of the cocoon, if present.
std::shared_ptr<render::model> model;
std::shared_ptr<render::model> cocoon_model;
}; };
/// Ant cocoon gene.
using ant_cocoon_gene = ant_gene<ant_cocoon_phene>;
/// Ant pupa gene.
using ant_pupa_gene = ant_gene<ant_pupa_phene>;
template <> template <>
inline constexpr ant_gene_type ant_cocoon_gene::type() const noexcept
inline constexpr ant_gene_type ant_pupa_gene::type() const noexcept
{ {
return ant_gene_type::cocoon;
return ant_gene_type::pupa;
} }
#endif // ANTKEEPER_GAME_ANT_COCOON_GENE_HPP
#endif // ANTKEEPER_GAME_ANT_PUPA_GENE_HPP

+ 60
- 0
src/game/components/autofocus-component.hpp View File

@ -0,0 +1,60 @@
/*
* 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_AUTOFOCUS_COMPONENT_HPP
#define ANTKEEPER_GAME_AUTOFOCUS_COMPONENT_HPP
#include <engine/math/vector.hpp>
#include <engine/math/angles.hpp>
#include <engine/physics/spring.hpp>
/**
* Modulates a camera's field of view and spring arm length.
*/
struct autofocus_component
{
/// Height of the focal plane at maximum zoom.
double near_focal_plane_height{1.0};
/// Height of the focal plane at minimum zoom.
double far_focal_plane_height{50.0};
/// Horizontal FoV at maximum zoom.
double near_hfov{math::radians(90.0)};
/// Horizontal FoV at minimum zoom.
double far_hfov{math::radians(45.0)};
/// Zoom factor, on `[0, 1]`.
double zoom{};
/// Dimensions of the focal plane.
math::dvec2 focal_plane_size;
/// Horizontal FoV of the camera, in radians.
double hfov{};
/// Vertical FoV of the camera, in radians.
double vfov{};
/// Position of the focal point, relative to the subject.
math::dvec3 focal_point_offset{};
};
#endif // ANTKEEPER_GAME_AUTOFOCUS_COMPONENT_HPP

src/game/systems/spring-system.hpp → src/game/components/decay-component.hpp View File

@ -17,23 +17,19 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef ANTKEEPER_GAME_SPRING_SYSTEM_HPP
#define ANTKEEPER_GAME_SPRING_SYSTEM_HPP
#include "game/systems/updatable-system.hpp"
#ifndef ANTKEEPER_GAME_DECAY_COMPONENT_HPP
#define ANTKEEPER_GAME_DECAY_COMPONENT_HPP
/** /**
* Solves numeric springs.
* Causes an entity to decay, deleting the entity when the decay is complete.
*/ */
class spring_system:
public updatable_system
struct decay_component
{ {
public:
explicit spring_system(entity::registry& registry);
~spring_system();
/// Decay rate.
float rate{};
virtual void update(float t, float dt);
/// Decay progress, on `[0, 1]`.
float progress{};
}; };
#endif // ANTKEEPER_GAME_SPRING_SYSTEM_HPP
#endif // ANTKEEPER_GAME_DECAY_COMPONENT_HPP

+ 4
- 10
src/game/components/egg-component.hpp View File

@ -20,22 +20,16 @@
#ifndef ANTKEEPER_GAME_EGG_COMPONENT_HPP #ifndef ANTKEEPER_GAME_EGG_COMPONENT_HPP
#define ANTKEEPER_GAME_EGG_COMPONENT_HPP #define ANTKEEPER_GAME_EGG_COMPONENT_HPP
#include <engine/render/model.hpp>
#include <memory>
/** /**
*
* Egg incubation parameters.
*/ */
struct egg_component struct egg_component
{ {
/// Duration of the incubation period, in seconds.
/// Duration of the incubation period, in days.
float incubation_period{}; float incubation_period{};
/// Elapsed time the egg has been in incubation, in seconds.
float elapsed_incubation_time{};
/// Model of the larval form.
std::shared_ptr<render::model> larva_model;
/// Current incubation phase, on `[0, 1]`.
float incubation_phase{};
}; };
#endif // ANTKEEPER_GAME_EGG_COMPONENT_HPP #endif // ANTKEEPER_GAME_EGG_COMPONENT_HPP

+ 50
- 0
src/game/components/larva-component.hpp View File

@ -0,0 +1,50 @@
/*
* 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_LARVA_COMPONENT_HPP
#define ANTKEEPER_GAME_LARVA_COMPONENT_HPP
#include <engine/entity/id.hpp>
#include <engine/render/material-variable.hpp>
/**
* Larval development parameters.
*/
struct larva_component
{
/// Duration of the development period, in days.
float development_period{};
/// Current development phase, on `[0, 1]`.
float development_phase{};
/// Duration of the cocoon-spinning period, in days.
float spinning_period{};
/// Current phase of the cocoon-spinning, on `[0, 1]`.
float spinning_phase{};
/// ID of the cocoon entity.
entity::id cocoon_eid{entt::null};
/// Material variable associated with the cocoon-spinning phase.
std::shared_ptr<render::matvar_float> spinning_phase_matvar;
};
#endif // ANTKEEPER_GAME_LARVA_COMPONENT_HPP

+ 5
- 0
src/game/components/legged-locomotion-component.hpp View File

@ -54,10 +54,15 @@ struct legged_locomotion_component
/// Distance covered in a single gait cycle. /// Distance covered in a single gait cycle.
float stride_length{}; float stride_length{};
/// Maximum angular frequency when turning, in radians per second.
float max_angular_frequency{};
float speed{}; float speed{};
float angular_velocity{}; float angular_velocity{};
math::fvec3 target_direction{};
/// Current phase of the gait cycle, on `[0, 1]`. /// Current phase of the gait cycle, on `[0, 1]`.
float gait_phase{}; float gait_phase{};
}; };

+ 32
- 0
src/game/components/metabolism-component.hpp View File

@ -0,0 +1,32 @@
/*
* 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_METABOLISM_COMPONENT_HPP
#define ANTKEEPER_GAME_METABOLISM_COMPONENT_HPP
/**
*
*/
struct metabolism_component
{
/// Growth rate multiplier.
float growth_multiplier{1};
};
#endif // ANTKEEPER_GAME_METABOLISM_COMPONENT_HPP

+ 44
- 0
src/game/components/pupa-component.hpp View File

@ -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_GAME_PUPA_COMPONENT_HPP
#define ANTKEEPER_GAME_PUPA_COMPONENT_HPP
#include <engine/entity/id.hpp>
#include <engine/render/material-variable.hpp>
/**
* Pupal development parameters.
*/
struct pupa_component
{
/// Duration of the development period, in days.
float development_period{};
/// Current development phase, on `[0, 1]`.
float development_phase{};
/// ID of the cocoon entity.
entity::id cocoon_eid{entt::null};
/// Material variable associated with the cocoon decay phase.
std::shared_ptr<render::matvar_float> decay_phase_matvar;
};
#endif // ANTKEEPER_GAME_PUPA_COMPONENT_HPP

src/game/components/orbit-camera-component.hpp → src/game/components/spring-arm-component.hpp View File

@ -17,24 +17,54 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef ANTKEEPER_GAME_ORBIT_CAMERA_COMPONENT_HPP
#define ANTKEEPER_GAME_ORBIT_CAMERA_COMPONENT_HPP
#ifndef ANTKEEPER_GAME_SPRING_ARM_COMPONENT_HPP
#define ANTKEEPER_GAME_SPRING_ARM_COMPONENT_HPP
#include <engine/entity/id.hpp> #include <engine/entity/id.hpp>
#include <engine/math/vector.hpp> #include <engine/math/vector.hpp>
#include <engine/math/quaternion.hpp> #include <engine/math/quaternion.hpp>
#include <engine/math/angles.hpp> #include <engine/math/angles.hpp>
#include <engine/physics/spring.hpp>
struct orbit_camera_component
/**
* Attaches a camera to an entity using springs.
*/
struct spring_arm_component
{ {
/// Entity ID of the subject.
entity::id subject_eid{entt::null};
/// ID of the entity to which the spring arm is attached.
entity::id parent_eid{entt::null};
/// Height of the view frustum at the near clipping distance. /// Height of the view frustum at the near clipping distance.
double near_focal_plane_height{1.0f};
double near_focal_plane_height{1.0};
/// Local up rotation quaternion.
math::dquat up_rotation{math::dquat::identity()};
/// Attached camera rotation.
math::dquat camera_rotation{math::dquat::identity()};
/// Pitch, yaw, and roll velocities, in radians per second.
math::dvec3 angular_velocities{};
/// Minimum pitch, yaw, and roll angles, in radians.
math::dvec3 min_angles{-math::half_pi<double>, -std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity()};
/// Maximum pitch, yaw, and roll angles, in radians.
math::dvec3 max_angles{math::half_pi<double>, std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()};
physics::numeric_spring<math::dvec3, double> focal_point_spring;
/// Pitch, yaw, and roll angles spring.
physics::numeric_spring<math::dvec3, double> angles_spring;
/// Arm length spring.
// physics::numeric_spring<double, double> arm_length_spring;
/// Height of the view frustum at the far clipping distance. /// Height of the view frustum at the far clipping distance.
double far_focal_plane_height{50.0f};
double far_focal_plane_height{50.0};
/// Horizontal FoV at maximum zoom. /// Horizontal FoV at maximum zoom.
double near_hfov{math::radians(90.0)}; double near_hfov{math::radians(90.0)};
@ -42,12 +72,6 @@ struct orbit_camera_component
/// Horizontal FoV at minimum zoom. /// Horizontal FoV at minimum zoom.
double far_hfov{math::radians(45.0)}; double far_hfov{math::radians(45.0)};
/// Yaw angle of the camera, in radians.
double yaw{};
/// Pitch angle of the camera, in radians.
double pitch{};
/// Zoom factor, on `[0, 1]`. /// Zoom factor, on `[0, 1]`.
double zoom{}; double zoom{};
@ -58,7 +82,7 @@ struct orbit_camera_component
double vfov{}; double vfov{};
/// Position of the focal point, relative to the subject. /// Position of the focal point, relative to the subject.
math::dvec3 focal_point{};
math::dvec3 focal_point_offset{};
/// Distance to the focal plane. /// Distance to the focal plane.
double focal_distance{}; double focal_distance{};
@ -68,18 +92,6 @@ struct orbit_camera_component
/// Height of the view frustum at the focal distance. /// Height of the view frustum at the focal distance.
double focal_plane_height{}; double focal_plane_height{};
/// Yaw rotation quaternion.
math::dquat yaw_rotation{math::dquat::identity()};
/// Pitch rotation quaternion.
math::dquat pitch_rotation{math::dquat::identity()};
/// Pitch and yaw rotation quaternion.
math::dquat orientation{math::dquat::identity()};
math::dquat up_rotation{math::dquat::identity()};
}; };
#endif // ANTKEEPER_GAME_ORBIT_CAMERA_COMPONENT_HPP
#endif // ANTKEEPER_GAME_SPRING_ARM_COMPONENT_HPP

+ 0
- 77
src/game/components/spring-component.hpp View File

@ -1,77 +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_SPRING_COMPONENT_HPP
#define ANTKEEPER_GAME_SPRING_COMPONENT_HPP
#include <engine/animation/spring.hpp>
#include <engine/math/vector.hpp>
#include <functional>
/**
* Numeric spring with one component.
*/
struct spring1_component
{
/// Numeric spring with one component.
numeric_spring<float, float> spring;
/// Spring solved callback.
std::function<void(float)> callback;
};
/**
* Numeric spring with two components.
*/
struct spring2_component
{
/// Numeric spring with two components.
numeric_spring<math::fvec2, float> spring;
/// Spring solved callback.
std::function<void(const math::fvec2&)> callback;
};
/**
* Numeric spring with three components.
*/
struct spring3_component
{
/// Numeric spring with three components.
numeric_spring<math::fvec3, float> spring;
/// Spring solved callback.
std::function<void(const math::fvec3&)> callback;
};
/**
* Numeric spring with four components.
*/
struct spring4_component
{
/// Numeric spring with four components.
numeric_spring<math::fvec4, float> spring;
/// Spring solved callback.
std::function<void(const math::fvec4&)> callback;
};
#endif // ANTKEEPER_GAME_SPRING_COMPONENT_HPP

+ 2
- 2
src/game/constraints/spring-rotation-constraint.hpp View File

@ -20,7 +20,7 @@
#ifndef ANTKEEPER_GAME_SPRING_ROTATION_CONSTRAINT_HPP #ifndef ANTKEEPER_GAME_SPRING_ROTATION_CONSTRAINT_HPP
#define ANTKEEPER_GAME_SPRING_ROTATION_CONSTRAINT_HPP #define ANTKEEPER_GAME_SPRING_ROTATION_CONSTRAINT_HPP
#include <engine/animation/spring.hpp>
#include <engine/physics/spring.hpp>
#include <engine/math/vector.hpp> #include <engine/math/vector.hpp>
@ -30,7 +30,7 @@
struct spring_rotation_constraint struct spring_rotation_constraint
{ {
/// Yaw, pitch, and roll angle spring. /// Yaw, pitch, and roll angle spring.
numeric_spring<math::fvec3, float> spring;
physics::numeric_spring<math::fvec3, float> spring;
}; };

+ 3
- 3
src/game/constraints/spring-to-constraint.hpp View File

@ -21,7 +21,7 @@
#define ANTKEEPER_GAME_SPRING_TO_CONSTRAINT_HPP #define ANTKEEPER_GAME_SPRING_TO_CONSTRAINT_HPP
#include <engine/entity/id.hpp> #include <engine/entity/id.hpp>
#include <engine/animation/spring.hpp>
#include <engine/physics/spring.hpp>
#include <engine/math/vector.hpp> #include <engine/math/vector.hpp>
@ -34,10 +34,10 @@ struct spring_to_constraint
entity::id target; entity::id target;
/// Translation spring. /// Translation spring.
numeric_spring<math::fvec3, float> translation;
physics::numeric_spring<math::fvec3, float> translation;
/// Rotation spring. /// Rotation spring.
numeric_spring<math::fvec4, float> rotation;
physics::numeric_spring<math::fvec4, float> rotation;
/// Spring translation. /// Spring translation.
bool spring_translation; bool spring_translation;

+ 2
- 2
src/game/constraints/spring-translation-constraint.hpp View File

@ -20,7 +20,7 @@
#ifndef ANTKEEPER_GAME_SPRING_TRANSLATION_CONSTRAINT_HPP #ifndef ANTKEEPER_GAME_SPRING_TRANSLATION_CONSTRAINT_HPP
#define ANTKEEPER_GAME_SPRING_TRANSLATION_CONSTRAINT_HPP #define ANTKEEPER_GAME_SPRING_TRANSLATION_CONSTRAINT_HPP
#include <engine/animation/spring.hpp>
#include <engine/physics/spring.hpp>
#include <engine/math/vector.hpp> #include <engine/math/vector.hpp>
@ -30,7 +30,7 @@
struct spring_translation_constraint struct spring_translation_constraint
{ {
/// Translation spring. /// Translation spring.
numeric_spring<math::fvec3, float> spring;
physics::numeric_spring<math::fvec3, float> spring;
}; };

+ 17
- 5
src/game/controls.cpp View File

@ -121,8 +121,12 @@ void reset_control_profile(::control_profile& profile)
mappings.emplace("move_down", std::make_unique<input::mouse_scroll_mapping>(nullptr, input::mouse_scroll_axis::y, true)); mappings.emplace("move_down", std::make_unique<input::mouse_scroll_mapping>(nullptr, input::mouse_scroll_axis::y, true));
mappings.emplace("move_down", std::make_unique<input::gamepad_axis_mapping>(nullptr, input::gamepad_axis::left_trigger, false)); mappings.emplace("move_down", std::make_unique<input::gamepad_axis_mapping>(nullptr, input::gamepad_axis::left_trigger, false));
// Interact
mappings.emplace("interact", std::make_unique<input::key_mapping>(nullptr, input::scancode::e, 0, false));
mappings.emplace("interact", std::make_unique<input::gamepad_button_mapping>(nullptr, input::gamepad_button::a));
// Move fast // Move fast
mappings.emplace("move_fast", std::make_unique<input::key_mapping>(nullptr, input::scancode::left_shift, 0, false));
// mappings.emplace("move_fast", std::make_unique<input::key_mapping>(nullptr, input::scancode::left_shift, 0, false));
// Move slow // Move slow
mappings.emplace("move_slow", std::make_unique<input::key_mapping>(nullptr, input::scancode::left_ctrl, 0, false)); mappings.emplace("move_slow", std::make_unique<input::key_mapping>(nullptr, input::scancode::left_ctrl, 0, false));
@ -143,13 +147,17 @@ void reset_control_profile(::control_profile& profile)
// Camera orbit // Camera orbit
mappings.emplace("camera_orbit_left", std::make_unique<input::key_mapping>(nullptr, input::scancode::left, 0, false)); mappings.emplace("camera_orbit_left", std::make_unique<input::key_mapping>(nullptr, input::scancode::left, 0, false));
mappings.emplace("camera_orbit_left", std::make_unique<input::gamepad_axis_mapping>(nullptr, input::gamepad_axis::right_stick_x, true));
mappings.emplace("camera_orbit_left", std::make_unique<input::gamepad_axis_mapping>(nullptr, input::gamepad_axis::right_stick_x, false));
mappings.emplace("camera_orbit_right", std::make_unique<input::key_mapping>(nullptr, input::scancode::right, 0, false)); mappings.emplace("camera_orbit_right", std::make_unique<input::key_mapping>(nullptr, input::scancode::right, 0, false));
mappings.emplace("camera_orbit_right", std::make_unique<input::gamepad_axis_mapping>(nullptr, input::gamepad_axis::right_stick_x, false));
mappings.emplace("camera_orbit_right", std::make_unique<input::gamepad_axis_mapping>(nullptr, input::gamepad_axis::right_stick_x, true));
mappings.emplace("camera_orbit_up", std::make_unique<input::key_mapping>(nullptr, input::scancode::up, 0, false)); mappings.emplace("camera_orbit_up", std::make_unique<input::key_mapping>(nullptr, input::scancode::up, 0, false));
mappings.emplace("camera_orbit_up", std::make_unique<input::gamepad_axis_mapping>(nullptr, input::gamepad_axis::right_stick_y, false));
mappings.emplace("camera_orbit_up", std::make_unique<input::gamepad_axis_mapping>(nullptr, input::gamepad_axis::right_stick_y, true));
mappings.emplace("camera_orbit_down", std::make_unique<input::key_mapping>(nullptr, input::scancode::up, 0, false)); mappings.emplace("camera_orbit_down", std::make_unique<input::key_mapping>(nullptr, input::scancode::up, 0, false));
mappings.emplace("camera_orbit_down", std::make_unique<input::gamepad_axis_mapping>(nullptr, input::gamepad_axis::right_stick_y, true));
mappings.emplace("camera_orbit_down", std::make_unique<input::gamepad_axis_mapping>(nullptr, input::gamepad_axis::right_stick_y, false));
// Camera look ahead
mappings.emplace("camera_look_ahead", std::make_unique<input::key_mapping>(nullptr, input::scancode::left_shift, 0, false));
mappings.emplace("camera_look_ahead", std::make_unique<input::gamepad_axis_mapping>(nullptr, input::gamepad_axis::left_trigger, false));
// Camera presets // Camera presets
mappings.emplace("camera_preset_1", std::make_unique<input::key_mapping>(nullptr, input::scancode::digit_1, 0, false)); mappings.emplace("camera_preset_1", std::make_unique<input::key_mapping>(nullptr, input::scancode::digit_1, 0, false));
@ -232,6 +240,7 @@ void apply_control_profile(::game& ctx, const ::control_profile& profile)
add_mappings(ctx.camera_action_map, ctx.camera_orbit_right_action, "camera_orbit_right"); add_mappings(ctx.camera_action_map, ctx.camera_orbit_right_action, "camera_orbit_right");
add_mappings(ctx.camera_action_map, ctx.camera_orbit_up_action, "camera_orbit_up"); add_mappings(ctx.camera_action_map, ctx.camera_orbit_up_action, "camera_orbit_up");
add_mappings(ctx.camera_action_map, ctx.camera_orbit_down_action, "camera_orbit_down"); add_mappings(ctx.camera_action_map, ctx.camera_orbit_down_action, "camera_orbit_down");
add_mappings(ctx.camera_action_map, ctx.camera_look_ahead_action, "camera_look_ahead");
add_mappings(ctx.camera_action_map, ctx.camera_preset_1_action, "camera_preset_1"); add_mappings(ctx.camera_action_map, ctx.camera_preset_1_action, "camera_preset_1");
add_mappings(ctx.camera_action_map, ctx.camera_preset_2_action, "camera_preset_2"); add_mappings(ctx.camera_action_map, ctx.camera_preset_2_action, "camera_preset_2");
add_mappings(ctx.camera_action_map, ctx.camera_preset_3_action, "camera_preset_3"); add_mappings(ctx.camera_action_map, ctx.camera_preset_3_action, "camera_preset_3");
@ -252,6 +261,7 @@ void apply_control_profile(::game& ctx, const ::control_profile& profile)
add_mappings(ctx.ant_action_map, ctx.ant_move_right_action, "move_right"); add_mappings(ctx.ant_action_map, ctx.ant_move_right_action, "move_right");
add_mappings(ctx.ant_action_map, ctx.ant_move_fast_action, "move_fast"); add_mappings(ctx.ant_action_map, ctx.ant_move_fast_action, "move_fast");
add_mappings(ctx.ant_action_map, ctx.ant_move_slow_action, "move_slow"); add_mappings(ctx.ant_action_map, ctx.ant_move_slow_action, "move_slow");
add_mappings(ctx.ant_action_map, ctx.ant_interact_action, "interact");
add_mappings(ctx.ant_action_map, ctx.ant_oviposit_action, "oviposit"); add_mappings(ctx.ant_action_map, ctx.ant_oviposit_action, "oviposit");
// Debug controls // Debug controls
@ -330,6 +340,7 @@ void update_control_profile(::game& ctx, ::control_profile& profile)
add_mappings(ctx.ant_action_map, ctx.ant_move_right_action, "move_right"); add_mappings(ctx.ant_action_map, ctx.ant_move_right_action, "move_right");
add_mappings(ctx.ant_action_map, ctx.ant_move_fast_action, "move_fast"); add_mappings(ctx.ant_action_map, ctx.ant_move_fast_action, "move_fast");
add_mappings(ctx.ant_action_map, ctx.ant_move_slow_action, "move_slow"); add_mappings(ctx.ant_action_map, ctx.ant_move_slow_action, "move_slow");
add_mappings(ctx.ant_action_map, ctx.ant_interact_action, "interact");
add_mappings(ctx.ant_action_map, ctx.ant_oviposit_action, "oviposit"); add_mappings(ctx.ant_action_map, ctx.ant_oviposit_action, "oviposit");
// Camera controls // Camera controls
@ -343,6 +354,7 @@ void update_control_profile(::game& ctx, ::control_profile& profile)
add_mappings(ctx.camera_action_map, ctx.camera_orbit_right_action, "camera_orbit_right"); add_mappings(ctx.camera_action_map, ctx.camera_orbit_right_action, "camera_orbit_right");
add_mappings(ctx.camera_action_map, ctx.camera_orbit_up_action, "camera_orbit_up"); add_mappings(ctx.camera_action_map, ctx.camera_orbit_up_action, "camera_orbit_up");
add_mappings(ctx.camera_action_map, ctx.camera_orbit_down_action, "camera_orbit_down"); add_mappings(ctx.camera_action_map, ctx.camera_orbit_down_action, "camera_orbit_down");
add_mappings(ctx.camera_action_map, ctx.camera_look_ahead_action, "camera_look_ahead");
add_mappings(ctx.camera_action_map, ctx.camera_preset_1_action, "camera_preset_1"); add_mappings(ctx.camera_action_map, ctx.camera_preset_1_action, "camera_preset_1");
add_mappings(ctx.camera_action_map, ctx.camera_preset_2_action, "camera_preset_2"); add_mappings(ctx.camera_action_map, ctx.camera_preset_2_action, "camera_preset_2");
add_mappings(ctx.camera_action_map, ctx.camera_preset_3_action, "camera_preset_3"); add_mappings(ctx.camera_action_map, ctx.camera_preset_3_action, "camera_preset_3");

+ 72
- 20
src/game/controls/ant-controls.cpp View File

@ -18,11 +18,14 @@
*/ */
#include "game/controls.hpp" #include "game/controls.hpp"
#include "game/world.hpp"
#include "game/components/ant-caste-component.hpp" #include "game/components/ant-caste-component.hpp"
#include "game/components/rigid-body-component.hpp" #include "game/components/rigid-body-component.hpp"
#include "game/components/legged-locomotion-component.hpp" #include "game/components/legged-locomotion-component.hpp"
#include "game/components/ovary-component.hpp" #include "game/components/ovary-component.hpp"
#include "game/components/spring-arm-component.hpp"
#include <engine/math/interpolation.hpp> #include <engine/math/interpolation.hpp>
#include <engine/math/euler-angles.hpp>
#include <engine/debug/log.hpp> #include <engine/debug/log.hpp>
namespace { namespace {
@ -32,16 +35,38 @@ namespace {
* *
* @param ctx Game context. * @param ctx Game context.
*/ */
void update_controlled_ant_speed(::game& ctx)
void steer_controlled_ant(::game& ctx)
{ {
if (ctx.controlled_ant_eid == entt::null) if (ctx.controlled_ant_eid == entt::null)
{ {
return; return;
} }
// Build control vector
math::fvec2 control_vector = {};
control_vector.x() -= ctx.ant_move_left_action.get_input_value();
control_vector.x() += ctx.ant_move_right_action.get_input_value();
control_vector.y() -= ctx.ant_move_forward_action.get_input_value();
control_vector.y() += ctx.ant_move_back_action.get_input_value();
// Get locomotion component of controlled ant
auto& locomotion = ctx.entity_registry->get<legged_locomotion_component>(ctx.controlled_ant_eid);
// Get phenome of controlled ant caste // Get phenome of controlled ant caste
const auto& caste_phenome = *ctx.entity_registry->get<::ant_caste_component>(ctx.controlled_ant_eid).phenome; const auto& caste_phenome = *ctx.entity_registry->get<::ant_caste_component>(ctx.controlled_ant_eid).phenome;
// Determine control direction and magnitude
math::fvec2 control_direction{};
float control_magnitude = 0.0f;
if (auto sqr_length = math::sqr_length(control_vector))
{
control_magnitude = std::sqrt(sqr_length);
control_direction = control_vector / control_magnitude;
}
// Clamp control magnitude
const auto clamped_control_magnitude = std::min(1.0f, control_magnitude);
// Determine locomotive speed // Determine locomotive speed
float locomotive_speed; float locomotive_speed;
if (ctx.ant_move_slow_action.is_active()) if (ctx.ant_move_slow_action.is_active())
@ -60,21 +85,26 @@ namespace {
locomotive_speed = caste_phenome.legs->walking_speed; locomotive_speed = caste_phenome.legs->walking_speed;
} }
// Scale locomotive speed by move ant forward action input value
locomotive_speed *= ctx.ant_move_forward_action.get_input_value();
// Scale locomotive speed by clamped control magnitude
locomotive_speed *= clamped_control_magnitude;
// Scale locomotive speed by scale of controlled ant // Scale locomotive speed by scale of controlled ant
locomotive_speed *= ctx.entity_registry->get<rigid_body_component>(ctx.controlled_ant_eid).body->get_transform().scale.x();
auto& rigid_body = *ctx.entity_registry->get<rigid_body_component>(ctx.controlled_ant_eid).body;
locomotive_speed *= rigid_body.get_scale().x();
// Update speed of legged locomotion component
ctx.entity_registry->patch<::legged_locomotion_component>
(
ctx.controlled_ant_eid,
[&](auto& component)
{
component.speed = locomotive_speed;
}
);
locomotion.speed = locomotive_speed;
locomotion.angular_velocity = 0.0f;
if (!locomotive_speed || ctx.active_camera_eid == entt::null)
{
return;
}
const auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
// Calculate steering direction
const auto spring_arm_yaw_rotation = math::angle_axis(spring_arm.angles_spring.get_value().y(), {0.0, 1.0, 0.0});
locomotion.target_direction = math::fquat(math::normalize(spring_arm.up_rotation * spring_arm_yaw_rotation)) * math::fvec3{control_direction.x(), 0.0f, (spring_arm.angles_spring.get_value().x() > 0.0) ? -control_direction.y() : control_direction.y()};
} }
/** /**
@ -110,7 +140,7 @@ void setup_ant_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
update_controlled_ant_speed(ctx);
steer_controlled_ant(ctx);
} }
) )
); );
@ -120,7 +150,7 @@ void setup_ant_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
update_controlled_ant_speed(ctx);
steer_controlled_ant(ctx);
} }
) )
); );
@ -132,7 +162,17 @@ void setup_ant_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
update_controlled_ant_speed(ctx);
steer_controlled_ant(ctx);
}
)
);
ctx.event_subscriptions.emplace_back
(
ctx.ant_move_back_action.get_deactivated_channel().subscribe
(
[&](const auto& event)
{
steer_controlled_ant(ctx);
} }
) )
); );
@ -144,7 +184,7 @@ void setup_ant_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
turn_controlled_ant(ctx, event.input_value);
steer_controlled_ant(ctx);
} }
) )
); );
@ -154,7 +194,7 @@ void setup_ant_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
turn_controlled_ant(ctx, 0.0f);
steer_controlled_ant(ctx);
} }
) )
); );
@ -166,7 +206,7 @@ void setup_ant_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
turn_controlled_ant(ctx, -event.input_value);
steer_controlled_ant(ctx);
} }
) )
); );
@ -176,7 +216,19 @@ void setup_ant_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
turn_controlled_ant(ctx, 0.0f);
steer_controlled_ant(ctx);
}
)
);
// Ant interact
ctx.event_subscriptions.emplace_back
(
ctx.ant_interact_action.get_activated_channel().subscribe
(
[&](const auto& event)
{
world::switch_scene(ctx);
} }
) )
); );

+ 156
- 36
src/game/controls/camera-controls.cpp View File

@ -18,30 +18,12 @@
*/ */
#include "game/controls.hpp" #include "game/controls.hpp"
#include "game/components/orbit-camera-component.hpp"
#include "game/components/spring-arm-component.hpp"
#include "game/components/rigid-body-component.hpp"
#include <engine/debug/log.hpp> #include <engine/debug/log.hpp>
namespace { namespace {
/*
void orbit_camera(::game& ctx, float yaw_factor, float pitch_factor)
{
if (ctx.active_camera_eid == entt::null)
{
return;
}
auto& orbit_cam = ctx.entity_registry->get<orbit_camera_component>(ctx.active_camera_eid);
// Adjust yaw and pitch angles according to mouse motion
orbit_cam.yaw -= ctx.mouse_pan_factor * static_cast<double>(event.difference.x());
orbit_cam.pitch += ctx.mouse_tilt_factor * static_cast<double>(event.difference.y());
// Limit pitch
orbit_cam.pitch = std::min(math::half_pi<double>, std::max(-math::half_pi<double>, orbit_cam.pitch));
}
*/
void handle_mouse_motion(::game& ctx, const input::mouse_moved_event& event) void handle_mouse_motion(::game& ctx, const input::mouse_moved_event& event)
{ {
if (ctx.active_camera_eid == entt::null) if (ctx.active_camera_eid == entt::null)
@ -49,27 +31,31 @@ namespace {
return; return;
} }
auto& orbit_cam = ctx.entity_registry->get<orbit_camera_component>(ctx.active_camera_eid);
auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
// Rotate camera // Rotate camera
if (ctx.camera_mouse_look_action.is_active()) if (ctx.camera_mouse_look_action.is_active())
{ {
// Adjust yaw and pitch angles according to mouse motion
orbit_cam.yaw -= ctx.mouse_pan_factor * static_cast<double>(event.difference.x());
orbit_cam.pitch += ctx.mouse_tilt_factor * static_cast<double>(event.difference.y());
// Adjust target pitch and yaw angles according to mouse motion
auto target_angles = spring_arm.angles_spring.get_target_value();
target_angles.x() -= ctx.mouse_tilt_factor * static_cast<double>(event.difference.y());
target_angles.y() -= ctx.mouse_pan_factor * static_cast<double>(event.difference.x());
// Apply angular constraints
target_angles = math::clamp(target_angles, spring_arm.min_angles, spring_arm.max_angles);
// Limit pitch
orbit_cam.pitch = std::min(math::half_pi<double>, std::max(-math::half_pi<double>, orbit_cam.pitch));
// Update spring arm target angles
spring_arm.angles_spring.set_target_value(target_angles);
} }
// Zoom camera // Zoom camera
if (ctx.camera_mouse_zoom_action.is_active()) if (ctx.camera_mouse_zoom_action.is_active())
{ {
// Adjust zoom factor // Adjust zoom factor
orbit_cam.zoom -= static_cast<double>(event.difference.y()) / static_cast<double>(ctx.window->get_viewport_size().y());
spring_arm.zoom -= static_cast<double>(event.difference.y()) / static_cast<double>(ctx.window->get_viewport_size().y());
// Limit zoom factor // Limit zoom factor
orbit_cam.zoom = std::min<double>(std::max<double>(orbit_cam.zoom, 0.0), 1.0);
spring_arm.zoom = std::min<double>(std::max<double>(spring_arm.zoom, 0.0), 1.0);
} }
} }
@ -90,14 +76,17 @@ namespace {
return; return;
} }
auto& orbit_cam = ctx.entity_registry->get<orbit_camera_component>(ctx.active_camera_eid);
auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
auto target_angles = spring_arm.angles_spring.get_target_value();
// Step zoom factor
constexpr double zoom_step = 1.0 / 6.0;
orbit_cam.zoom += zoom_step * scale;
// Modulate target pitch angle
target_angles.x() += (spring_arm.max_angles.x() - spring_arm.min_angles.x()) / ctx.zoom_steps * scale;
// Limit zoom factor
orbit_cam.zoom = std::min<double>(std::max<double>(orbit_cam.zoom, 0.0), 1.0);
// Apply angular constraints
target_angles = math::clamp(target_angles, spring_arm.min_angles, spring_arm.max_angles);
// Update spring arm target angles
spring_arm.angles_spring.set_target_value(target_angles);
} }
void update_relative_mouse_mode(::game& ctx) void update_relative_mouse_mode(::game& ctx)
@ -247,7 +236,7 @@ void setup_camera_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
step_camera_zoom(ctx, 1.0 * static_cast<double>(ctx.camera_zoom_in_action.get_input_value()));
step_camera_zoom(ctx, static_cast<double>(ctx.camera_zoom_in_action.get_input_value()));
} }
) )
); );
@ -259,7 +248,7 @@ void setup_camera_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
step_camera_zoom(ctx, -1.0 * static_cast<double>(ctx.camera_zoom_out_action.get_input_value()));
step_camera_zoom(ctx, -static_cast<double>(ctx.camera_zoom_out_action.get_input_value()));
} }
) )
); );
@ -271,7 +260,29 @@ void setup_camera_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
if (ctx.active_camera_eid == entt::null)
{
return;
}
auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
spring_arm.angular_velocities.y() = -ctx.gamepad_pan_factor * static_cast<double>(event.input_value);
}
)
);
ctx.event_subscriptions.emplace_back
(
ctx.camera_orbit_left_action.get_deactivated_channel().subscribe
(
[&](const auto& event)
{
if (ctx.active_camera_eid == entt::null)
{
return;
}
auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
spring_arm.angular_velocities.y() = 0.0;
} }
) )
); );
@ -283,7 +294,29 @@ void setup_camera_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
if (ctx.active_camera_eid == entt::null)
{
return;
}
auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
spring_arm.angular_velocities.y() = ctx.gamepad_pan_factor * static_cast<double>(event.input_value);
}
)
);
ctx.event_subscriptions.emplace_back
(
ctx.camera_orbit_right_action.get_deactivated_channel().subscribe
(
[&](const auto& event)
{
if (ctx.active_camera_eid == entt::null)
{
return;
}
auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
spring_arm.angular_velocities.y() = 0.0;
} }
) )
); );
@ -295,7 +328,29 @@ void setup_camera_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
if (ctx.active_camera_eid == entt::null)
{
return;
}
auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
spring_arm.angular_velocities.x() = ctx.gamepad_tilt_factor * static_cast<double>(event.input_value);
}
)
);
ctx.event_subscriptions.emplace_back
(
ctx.camera_orbit_up_action.get_deactivated_channel().subscribe
(
[&](const auto& event)
{
if (ctx.active_camera_eid == entt::null)
{
return;
}
auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
spring_arm.angular_velocities.x() = 0.0;
} }
) )
); );
@ -307,7 +362,72 @@ void setup_camera_controls(::game& ctx)
( (
[&](const auto& event) [&](const auto& event)
{ {
if (ctx.active_camera_eid == entt::null)
{
return;
}
auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
spring_arm.angular_velocities.x() = -ctx.gamepad_tilt_factor * static_cast<double>(event.input_value);
}
)
);
ctx.event_subscriptions.emplace_back
(
ctx.camera_orbit_down_action.get_deactivated_channel().subscribe
(
[&](const auto& event)
{
if (ctx.active_camera_eid == entt::null)
{
return;
}
auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
spring_arm.angular_velocities.x() = 0.0;
}
)
);
// Camera look ahead
ctx.event_subscriptions.emplace_back
(
ctx.camera_look_ahead_action.get_activated_channel().subscribe
(
[&](const auto& event)
{
if (ctx.active_camera_eid == entt::null || ctx.controlled_ant_eid == entt::null)
{
return;
}
auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
const auto& subject_rigid_body = *ctx.entity_registry->get<rigid_body_component>(ctx.controlled_ant_eid).body;
// Determine camera up direction
const auto camera_up = math::fvec3(spring_arm.up_rotation * math::dvec3{0, 1, 0});
// Get spring arm target angles
auto target_angles = spring_arm.angles_spring.get_target_value();
// Determine camera forward direction
const auto camera_yaw_rotation = math::angle_axis(target_angles.y(), {0.0, 1.0, 0.0});
const auto camera_pitchless_orientation = math::normalize(spring_arm.up_rotation * camera_yaw_rotation);
const auto camera_forward = math::fvec3(camera_pitchless_orientation * math::dvec3{0, 0, -1});
// Determine subject forward direction
const auto subject_forward = subject_rigid_body.get_transform().rotation * math::fvec3{0, 0, 1};
// Find signed angle between the two forward directions about camera up axis
const auto angular_difference = math::signed_angle(camera_forward, subject_forward, camera_up);
// Add angular difference to spring arm target yaw angle
target_angles.y() += angular_difference;
// Update spring arm target angles
spring_arm.angles_spring.set_target_value(target_angles);
} }
) )
); );

+ 9
- 9
src/game/ecoregion-loader.cpp View File

@ -125,23 +125,23 @@ std::unique_ptr resource_loader::load(::resource_manager&
} }
} }
// Load cocoon genes
if (auto cocoon_elements = genes_element->find("cocoon"); cocoon_elements != genes_element->end())
// Load pupa genes
if (auto pupa_elements = genes_element->find("pupa"); pupa_elements != genes_element->end())
{ {
for (auto cocoon_element = cocoon_elements->begin(); cocoon_element != cocoon_elements->end(); ++cocoon_element)
for (auto pupa_element = pupa_elements->begin(); pupa_element != pupa_elements->end(); ++pupa_element)
{ {
float weight = 0.0f; float weight = 0.0f;
std::shared_ptr<ant_cocoon_gene> gene;
std::shared_ptr<ant_pupa_gene> gene;
if (auto weight_element = cocoon_element->find("weight"); weight_element != cocoon_element->end())
if (auto weight_element = pupa_element->find("weight"); weight_element != pupa_element->end())
weight = weight_element->get<float>(); weight = weight_element->get<float>();
if (auto gene_element = cocoon_element->find("gene"); gene_element != cocoon_element->end())
gene = resource_manager.load<ant_cocoon_gene>(gene_element->get<std::string>());
if (auto gene_element = pupa_element->find("gene"); gene_element != pupa_element->end())
gene = resource_manager.load<ant_pupa_gene>(gene_element->get<std::string>());
if (gene) if (gene)
{ {
gene_pool.cocoon.weights.push_back(weight);
gene_pool.cocoon.genes.push_back(gene);
gene_pool.pupa.weights.push_back(weight);
gene_pool.pupa.genes.push_back(gene);
} }
} }
} }

+ 17
- 8
src/game/game.cpp View File

@ -41,12 +41,12 @@
#include "game/systems/orbit-system.hpp" #include "game/systems/orbit-system.hpp"
#include "game/systems/render-system.hpp" #include "game/systems/render-system.hpp"
#include "game/systems/spatial-system.hpp" #include "game/systems/spatial-system.hpp"
#include "game/systems/spring-system.hpp"
#include "game/systems/steering-system.hpp" #include "game/systems/steering-system.hpp"
#include "game/systems/physics-system.hpp" #include "game/systems/physics-system.hpp"
#include "game/systems/subterrain-system.hpp" #include "game/systems/subterrain-system.hpp"
#include "game/systems/terrain-system.hpp" #include "game/systems/terrain-system.hpp"
#include "game/systems/reproductive-system.hpp" #include "game/systems/reproductive-system.hpp"
#include "game/systems/metabolic-system.hpp"
#include "game/systems/metamorphosis-system.hpp" #include "game/systems/metamorphosis-system.hpp"
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
@ -567,12 +567,15 @@ void game::setup_input()
} }
); );
/*
// Gamepad deactivation function // Gamepad deactivation function
auto deactivate_gamepad = [&](const auto& event) auto deactivate_gamepad = [&](const auto& event)
{ {
if (gamepad_active) if (gamepad_active)
{ {
gamepad_active = false; gamepad_active = false;
// WARNING: huge source of lag
input_manager->set_cursor_visible(true); input_manager->set_cursor_visible(true);
} }
}; };
@ -625,6 +628,7 @@ void game::setup_input()
{ {
gamepad_active = false; gamepad_active = false;
} }
*/
} }
void game::load_strings() void game::load_strings()
@ -1063,8 +1067,8 @@ void game::setup_systems()
// Setup IK system // Setup IK system
ik_system = std::make_unique<::ik_system>(*entity_registry); ik_system = std::make_unique<::ik_system>(*entity_registry);
// Setup reproductive system
reproductive_system = std::make_unique<::reproductive_system>(*entity_registry);
// Setup metabolic system
metabolic_system = std::make_unique<::metabolic_system>(*entity_registry);
// Setup metamorphosis system // Setup metamorphosis system
metamorphosis_system = std::make_unique<::metamorphosis_system>(*entity_registry); metamorphosis_system = std::make_unique<::metamorphosis_system>(*entity_registry);
@ -1076,8 +1080,9 @@ void game::setup_systems()
physics_system = std::make_unique<::physics_system>(*entity_registry); physics_system = std::make_unique<::physics_system>(*entity_registry);
physics_system->set_gravity({0.0f, -9.80665f * 100.0f, 0.0f}); physics_system->set_gravity({0.0f, -9.80665f * 100.0f, 0.0f});
// Setup spring system
spring_system = std::make_unique<::spring_system>(*entity_registry);
// Setup reproductive system
reproductive_system = std::make_unique<::reproductive_system>(*entity_registry);
reproductive_system->set_physics_system(physics_system.get());
// Setup spatial system // Setup spatial system
spatial_system = std::make_unique<::spatial_system>(*entity_registry); spatial_system = std::make_unique<::spatial_system>(*entity_registry);
@ -1163,8 +1168,12 @@ void game::setup_controls()
::apply_control_profile(*this, *control_profile); ::apply_control_profile(*this, *control_profile);
// Setup mouse sensitivity // Setup mouse sensitivity
mouse_pan_factor = mouse_radians_per_pixel * mouse_pan_sensitivity * (invert_mouse_pan ? -1.0 : 1.0);
mouse_tilt_factor = mouse_radians_per_pixel * mouse_tilt_sensitivity * (invert_mouse_tilt ? -1.0 : 1.0);
mouse_pan_factor = mouse_radians_per_pixel * mouse_pan_sensitivity * (mouse_invert_pan ? -1.0 : 1.0);
mouse_tilt_factor = mouse_radians_per_pixel * mouse_tilt_sensitivity * (mouse_invert_tilt ? -1.0 : 1.0);
// Setup gamepad sensitivity
gamepad_pan_factor = gamepad_radians_per_second * gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0 : 1.0);
gamepad_tilt_factor = gamepad_radians_per_second * gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0 : 1.0);
// Setup action callbacks // Setup action callbacks
setup_window_controls(*this); setup_window_controls(*this);
@ -1280,13 +1289,13 @@ void game::fixed_update(::frame_scheduler::duration_type fixed_update_time, ::fr
locomotion_system->update(t, dt); locomotion_system->update(t, dt);
ik_system->update(t, dt); ik_system->update(t, dt);
reproductive_system->update(t, dt); reproductive_system->update(t, dt);
metabolic_system->update(t, dt);
metamorphosis_system->update(t, dt); metamorphosis_system->update(t, dt);
// physics_system->update(t, dt); // physics_system->update(t, dt);
orbit_system->update(t, dt); orbit_system->update(t, dt);
blackbody_system->update(t, dt); blackbody_system->update(t, dt);
atmosphere_system->update(t, dt); atmosphere_system->update(t, dt);
astronomy_system->update(t, dt); astronomy_system->update(t, dt);
spring_system->update(t, dt);
spatial_system->update(t, dt); spatial_system->update(t, dt);
constraint_system->update(t, dt); constraint_system->update(t, dt);
animator->animate(dt); animator->animate(dt);

+ 19
- 7
src/game/game.hpp View File

@ -111,9 +111,9 @@ class nest_system;
class orbit_system; class orbit_system;
class render_system; class render_system;
class spatial_system; class spatial_system;
class spring_system;
class steering_system; class steering_system;
class reproductive_system; class reproductive_system;
class metabolic_system;
class metamorphosis_system; class metamorphosis_system;
class physics_system; class physics_system;
class subterrain_system; class subterrain_system;
@ -240,6 +240,7 @@ public:
input::action camera_orbit_right_action; input::action camera_orbit_right_action;
input::action camera_orbit_up_action; input::action camera_orbit_up_action;
input::action camera_orbit_down_action; input::action camera_orbit_down_action;
input::action camera_look_ahead_action;
input::action camera_preset_1_action; input::action camera_preset_1_action;
input::action camera_preset_2_action; input::action camera_preset_2_action;
input::action camera_preset_3_action; input::action camera_preset_3_action;
@ -259,6 +260,7 @@ public:
input::action ant_move_right_action; input::action ant_move_right_action;
input::action ant_move_fast_action; input::action ant_move_fast_action;
input::action ant_move_slow_action; input::action ant_move_slow_action;
input::action ant_interact_action;
input::action ant_oviposit_action; input::action ant_oviposit_action;
input::action_map debug_action_map; input::action_map debug_action_map;
@ -272,17 +274,27 @@ public:
std::vector<std::shared_ptr<::event::subscription>> menu_mouse_subscriptions; std::vector<std::shared_ptr<::event::subscription>> menu_mouse_subscriptions;
std::vector<std::shared_ptr<::event::subscription>> movement_action_subscriptions; std::vector<std::shared_ptr<::event::subscription>> movement_action_subscriptions;
// Control settings
// Mouse settings
double mouse_radians_per_pixel{math::radians(0.1)}; double mouse_radians_per_pixel{math::radians(0.1)};
double mouse_pan_sensitivity{1.0}; double mouse_pan_sensitivity{1.0};
double mouse_tilt_sensitivity{1.0}; double mouse_tilt_sensitivity{1.0};
bool invert_mouse_pan{false};
bool invert_mouse_tilt{false};
bool mouse_invert_pan{false};
bool mouse_invert_tilt{false};
double mouse_pan_factor{1.0};
double mouse_tilt_factor{1.0};
bool toggle_mouse_look{false}; bool toggle_mouse_look{false};
bool toggle_mouse_grip{false}; bool toggle_mouse_grip{false};
bool toggle_mouse_zoom{false}; bool toggle_mouse_zoom{false};
double mouse_pan_factor{1.0};
double mouse_tilt_factor{1.0};
float zoom_steps{6.0};
// Gamepad settings
double gamepad_radians_per_second{math::radians(180.0)};
double gamepad_pan_sensitivity{1.0};
double gamepad_tilt_sensitivity{1.0};
bool gamepad_invert_pan{false};
bool gamepad_invert_tilt{false};
double gamepad_pan_factor{1.0};
double gamepad_tilt_factor{1.0};
// Debugging // Debugging
bool debug_ui_visible{false}; bool debug_ui_visible{false};
@ -404,6 +416,7 @@ public:
std::unique_ptr<::constraint_system> constraint_system; std::unique_ptr<::constraint_system> constraint_system;
std::unique_ptr<::steering_system> steering_system; std::unique_ptr<::steering_system> steering_system;
std::unique_ptr<::reproductive_system> reproductive_system; std::unique_ptr<::reproductive_system> reproductive_system;
std::unique_ptr<::metabolic_system> metabolic_system;
std::unique_ptr<::metamorphosis_system> metamorphosis_system; std::unique_ptr<::metamorphosis_system> metamorphosis_system;
std::unique_ptr<::locomotion_system> locomotion_system; std::unique_ptr<::locomotion_system> locomotion_system;
std::unique_ptr<::ik_system> ik_system; std::unique_ptr<::ik_system> ik_system;
@ -412,7 +425,6 @@ public:
std::unique_ptr<::render_system> render_system; std::unique_ptr<::render_system> render_system;
std::unique_ptr<::subterrain_system> subterrain_system; std::unique_ptr<::subterrain_system> subterrain_system;
std::unique_ptr<::terrain_system> terrain_system; std::unique_ptr<::terrain_system> terrain_system;
std::unique_ptr<::spring_system> spring_system;
std::unique_ptr<::spatial_system> spatial_system; std::unique_ptr<::spatial_system> spatial_system;
std::unique_ptr<::blackbody_system> blackbody_system; std::unique_ptr<::blackbody_system> blackbody_system;
std::unique_ptr<::atmosphere_system> atmosphere_system; std::unique_ptr<::atmosphere_system> atmosphere_system;

+ 87
- 136
src/game/states/experiments/treadmill-experiment-state.cpp View File

@ -28,7 +28,6 @@
#include "game/components/constraint-stack-component.hpp" #include "game/components/constraint-stack-component.hpp"
#include "game/components/scene-component.hpp" #include "game/components/scene-component.hpp"
#include "game/components/picking-component.hpp" #include "game/components/picking-component.hpp"
#include "game/components/spring-component.hpp"
#include "game/components/rigid-body-component.hpp" #include "game/components/rigid-body-component.hpp"
#include "game/components/rigid-body-constraint-component.hpp" #include "game/components/rigid-body-constraint-component.hpp"
#include "game/components/steering-component.hpp" #include "game/components/steering-component.hpp"
@ -40,7 +39,7 @@
#include "game/components/ik-component.hpp" #include "game/components/ik-component.hpp"
#include "game/components/transform-component.hpp" #include "game/components/transform-component.hpp"
#include "game/components/ovary-component.hpp" #include "game/components/ovary-component.hpp"
#include "game/components/orbit-camera-component.hpp"
#include "game/components/spring-arm-component.hpp"
#include "game/components/ant-genome-component.hpp" #include "game/components/ant-genome-component.hpp"
#include "game/constraints/child-of-constraint.hpp" #include "game/constraints/child-of-constraint.hpp"
#include "game/constraints/copy-rotation-constraint.hpp" #include "game/constraints/copy-rotation-constraint.hpp"
@ -61,6 +60,7 @@
#include "game/systems/atmosphere-system.hpp" #include "game/systems/atmosphere-system.hpp"
#include "game/systems/camera-system.hpp" #include "game/systems/camera-system.hpp"
#include "game/systems/collision-system.hpp" #include "game/systems/collision-system.hpp"
#include "game/systems/physics-system.hpp"
#include "game/world.hpp" #include "game/world.hpp"
#include <engine/animation/ease.hpp> #include <engine/animation/ease.hpp>
#include <engine/animation/screen-transition.hpp> #include <engine/animation/screen-transition.hpp>
@ -75,6 +75,7 @@
#include <engine/physics/kinematics/colliders/plane-collider.hpp> #include <engine/physics/kinematics/colliders/plane-collider.hpp>
#include <engine/physics/kinematics/colliders/box-collider.hpp> #include <engine/physics/kinematics/colliders/box-collider.hpp>
#include <engine/physics/kinematics/colliders/capsule-collider.hpp> #include <engine/physics/kinematics/colliders/capsule-collider.hpp>
#include <engine/physics/kinematics/colliders/mesh-collider.hpp>
#include <engine/render/passes/clear-pass.hpp> #include <engine/render/passes/clear-pass.hpp>
#include <engine/render/passes/ground-pass.hpp> #include <engine/render/passes/ground-pass.hpp>
#include <engine/render/passes/material-pass.hpp> #include <engine/render/passes/material-pass.hpp>
@ -97,16 +98,6 @@ treadmill_experiment_state::treadmill_experiment_state(::game& ctx):
{ {
debug::log::trace("Entering nest view state..."); debug::log::trace("Entering nest view state...");
// Create world if not yet created
if (ctx.entities.find("earth") == ctx.entities.end())
{
// Create cosmos
::world::cosmogenesis(ctx);
// Create observer
::world::create_observer(ctx);
}
ctx.active_scene = ctx.surface_scene.get(); ctx.active_scene = ctx.surface_scene.get();
ctx.active_ecoregion = ctx.resource_manager->load<::ecoregion>("seedy-scrub.eco"); ctx.active_ecoregion = ctx.resource_manager->load<::ecoregion>("seedy-scrub.eco");
@ -175,23 +166,49 @@ treadmill_experiment_state::treadmill_experiment_state(::game& ctx):
// } // }
// ); // );
// Create treadmill
auto treadmill_eid = ctx.entity_registry->create();
scene_component treadmill_scene_component;
treadmill_scene_component.object = std::make_shared<scene::static_mesh>(ctx.resource_manager->load<render::model>("mobius-strip-nest-400mm.mdl"));
treadmill_scene_component.layer_mask = 1;
ctx.entity_registry->emplace<scene_component>(treadmill_eid, std::move(treadmill_scene_component));
// Load navmesh
navmesh = ctx.resource_manager->load<geom::brep_mesh>("mobius-strip-nest-400mm.msh");
// Generate navmesh attributes
geom::generate_vertex_normals(*navmesh);
// Create nest exterior
// {
// scene_component nest_exterior_scene_component;
// nest_exterior_scene_component.object = std::make_shared<scene::static_mesh>(ctx.resource_manager->load<render::model>("round-petri-dish-nest-100mm-exterior.mdl"));
// nest_exterior_scene_component.layer_mask = 1;
// auto nest_exterior_eid = ctx.entity_registry->create();
// ctx.entity_registry->emplace<scene_component>(nest_exterior_eid, std::move(nest_exterior_scene_component));
// }
// Create nest interior
// {
// scene_component nest_interior_scene_component;
// nest_interior_scene_component.object = std::make_shared<scene::static_mesh>(ctx.resource_manager->load<render::model>("round-petri-dish-nest-100mm-interior.mdl"));
// nest_interior_scene_component.layer_mask = 1;
// auto nest_interior_mesh = ctx.resource_manager->load<geom::brep_mesh>("round-petri-dish-nest-100mm-interior.msh");
// geom::generate_vertex_normals(*nest_interior_mesh);
// auto nest_interior_rigid_body = std::make_unique<physics::rigid_body>();
// nest_interior_rigid_body->set_mass(0.0f);
// nest_interior_rigid_body->set_collider(std::make_shared<physics::mesh_collider>(std::move(nest_interior_mesh)));
// auto nest_interior_eid = ctx.entity_registry->create();
// ctx.entity_registry->emplace<scene_component>(nest_interior_eid, std::move(nest_interior_scene_component));
// ctx.entity_registry->emplace<rigid_body_component>(nest_interior_eid, std::move(nest_interior_rigid_body));
// }
// Build navmesh BVH
debug::log::info("Building BVH...");
navmesh_bvh = std::make_unique<geom::bvh>(*navmesh);
debug::log::info("Built BVH");
{
scene_component nest_interior_scene_component;
nest_interior_scene_component.object = std::make_shared<scene::static_mesh>(ctx.resource_manager->load<render::model>("soil-nest.mdl"));
nest_interior_scene_component.layer_mask = 1;
auto nest_interior_mesh = ctx.resource_manager->load<geom::brep_mesh>("soil-nest.msh");
auto nest_interior_rigid_body = std::make_unique<physics::rigid_body>();
nest_interior_rigid_body->set_mass(0.0f);
nest_interior_rigid_body->set_collider(std::make_shared<physics::mesh_collider>(std::move(nest_interior_mesh)));
auto nest_interior_eid = ctx.entity_registry->create();
ctx.entity_registry->emplace<scene_component>(nest_interior_eid, std::move(nest_interior_scene_component));
ctx.entity_registry->emplace<rigid_body_component>(nest_interior_eid, std::move(nest_interior_rigid_body));
}
// Create worker // Create worker
auto worker_skeletal_mesh = std::make_unique<scene::skeletal_mesh>(worker_model); auto worker_skeletal_mesh = std::make_unique<scene::skeletal_mesh>(worker_model);
@ -255,6 +272,7 @@ treadmill_experiment_state::treadmill_experiment_state(::game& ctx):
} }
worker_locomotion_component.standing_height = worker_phenome->legs->standing_height; worker_locomotion_component.standing_height = worker_phenome->legs->standing_height;
worker_locomotion_component.stride_length = worker_phenome->legs->stride_length * worker_rigid_body_component.body->get_transform().scale.x(); worker_locomotion_component.stride_length = worker_phenome->legs->stride_length * worker_rigid_body_component.body->get_transform().scale.x();
worker_locomotion_component.max_angular_frequency = worker_phenome->legs->max_angular_frequency;
navmesh_agent_component worker_navmesh_agent_component; navmesh_agent_component worker_navmesh_agent_component;
@ -302,7 +320,7 @@ treadmill_experiment_state::treadmill_experiment_state(::game& ctx):
ctx.ground_pass->set_enabled(true); ctx.ground_pass->set_enabled(true);
sky_probe = std::make_shared<scene::light_probe>(); sky_probe = std::make_shared<scene::light_probe>();
sky_probe->set_luminance_texture(std::make_shared<gl::texture_cube>(384, 512, gl::pixel_type::float_16, gl::pixel_format::rgb));
sky_probe->set_luminance_texture(std::make_shared<gl::texture_cube>(512, 384, gl::pixel_type::float_16, gl::pixel_format::rgb));
ctx.sky_pass->set_sky_probe(sky_probe); ctx.sky_pass->set_sky_probe(sky_probe);
ctx.surface_scene->add_object(*sky_probe); ctx.surface_scene->add_object(*sky_probe);
@ -359,32 +377,30 @@ treadmill_experiment_state::~treadmill_experiment_state()
void treadmill_experiment_state::create_third_person_camera_rig() void treadmill_experiment_state::create_third_person_camera_rig()
{ {
const auto& subject_rigid_body = *ctx.entity_registry->get<rigid_body_component>(ctx.controlled_ant_eid).body; const auto& subject_rigid_body = *ctx.entity_registry->get<rigid_body_component>(ctx.controlled_ant_eid).body;
orbit_camera_component orbit_cam;
orbit_cam.subject_eid = ctx.controlled_ant_eid;
orbit_cam.yaw = math::radians(0.0);
orbit_cam.pitch = math::radians(45.0);
orbit_cam.near_focal_plane_height = {1.0};
orbit_cam.far_focal_plane_height = {50.0};
orbit_cam.near_hfov = math::radians(90.0);
orbit_cam.far_hfov = math::radians(45.0);
orbit_cam.zoom = 0.25;
orbit_cam.focal_point = {0, worker_phenome->legs->standing_height * subject_rigid_body.get_transform().scale.x(), 0};
const auto subject_scale = static_cast<double>(subject_rigid_body.get_transform().scale.x());
third_person_camera_rig_eid = ctx.entity_registry->create();
ctx.entity_registry->emplace<scene_component>(third_person_camera_rig_eid, ctx.surface_camera, std::uint8_t{1});
ctx.entity_registry->emplace<orbit_camera_component>(third_person_camera_rig_eid, std::move(orbit_cam));
spring_arm_component spring_arm;
spring_arm.parent_eid = ctx.controlled_ant_eid;
spring_arm.near_focal_plane_height = 8.0 * subject_scale;
spring_arm.far_focal_plane_height = 80.0 * subject_scale;
spring_arm.near_hfov = math::radians(90.0);
spring_arm.far_hfov = math::radians(45.0);
spring_arm.zoom = 0.25;
spring_arm.focal_point_offset = {0, static_cast<double>(worker_phenome->legs->standing_height) * subject_scale, 0};
ctx.active_camera_eid = third_person_camera_rig_eid;
spring_arm.focal_point_spring.set_damping_ratio(1.0);
spring_arm.focal_point_spring.set_period(0.01);
/*
// Construct third person camera rig entity
third_person_camera_rig_eid = ctx.entity_registry->create();
ctx.entity_registry->emplace<scene_component>(third_person_camera_rig_eid, third_person_camera_rig_camera);
spring_arm.angles_spring.set_damping_ratio(1.0);
spring_arm.angles_spring.set_period(0.25);
set_third_person_camera_zoom(third_person_camera_zoom);
set_third_person_camera_rotation(third_person_camera_yaw, third_person_camera_pitch);
update_third_person_camera();
*/
spring_arm.min_angles.x() = -math::half_pi<double>;
spring_arm.max_angles.x() = 0.0;
third_person_camera_rig_eid = ctx.entity_registry->create();
ctx.entity_registry->emplace<scene_component>(third_person_camera_rig_eid, ctx.surface_camera, std::uint8_t{1});
ctx.entity_registry->emplace<spring_arm_component>(third_person_camera_rig_eid, std::move(spring_arm));
ctx.active_camera_eid = third_person_camera_rig_eid;
} }
void treadmill_experiment_state::destroy_third_person_camera_rig() void treadmill_experiment_state::destroy_third_person_camera_rig()
@ -394,49 +410,22 @@ void treadmill_experiment_state::destroy_third_person_camera_rig()
void treadmill_experiment_state::set_third_person_camera_zoom(double zoom) void treadmill_experiment_state::set_third_person_camera_zoom(double zoom)
{ {
zoom = std::min<double>(std::max<double>(zoom, 0.0), 1.0);
auto& orbit_cam = ctx.entity_registry->get<orbit_camera_component>(third_person_camera_rig_eid);
orbit_cam.zoom = zoom;
} }
void treadmill_experiment_state::set_third_person_camera_rotation(double yaw, double pitch) void treadmill_experiment_state::set_third_person_camera_rotation(double yaw, double pitch)
{ {
pitch = std::min(math::half_pi<double>, std::max(-math::half_pi<double>, pitch));
auto& orbit_cam = ctx.entity_registry->get<orbit_camera_component>(third_person_camera_rig_eid);
orbit_cam.yaw = yaw;
orbit_cam.pitch = pitch;
} }
void treadmill_experiment_state::zoom_third_person_camera(double zoom) void treadmill_experiment_state::zoom_third_person_camera(double zoom)
{ {
const auto& orbit_cam = ctx.entity_registry->get<orbit_camera_component>(third_person_camera_rig_eid);
set_third_person_camera_zoom(orbit_cam.zoom + zoom);
} }
void treadmill_experiment_state::translate_third_person_camera(const math::dvec3& direction, double magnitude) void treadmill_experiment_state::translate_third_person_camera(const math::dvec3& direction, double magnitude)
{ {
auto& orbit_cam = ctx.entity_registry->get<orbit_camera_component>(third_person_camera_rig_eid);
// Scale translation magnitude by factor of focal plane height
const auto scaled_magnitude = magnitude * orbit_cam.focal_plane_height;
// Rotate translation direction according to camera yaw
const math::dvec3 rotated_direction = orbit_cam.yaw_rotation * direction;
orbit_cam.focal_point += rotated_direction * scaled_magnitude * third_person_camera_speed;
} }
void treadmill_experiment_state::rotate_third_person_camera(const input::mouse_moved_event& event) void treadmill_experiment_state::rotate_third_person_camera(const input::mouse_moved_event& event)
{ {
const auto& orbit_cam = ctx.entity_registry->get<orbit_camera_component>(third_person_camera_rig_eid);
const double yaw = orbit_cam.yaw - ctx.mouse_pan_factor * static_cast<double>(event.difference.x());
const double pitch = orbit_cam.pitch + ctx.mouse_tilt_factor * static_cast<double>(event.difference.y());
set_third_person_camera_rotation(yaw, pitch);
} }
void treadmill_experiment_state::handle_mouse_motion(const input::mouse_moved_event& event) void treadmill_experiment_state::handle_mouse_motion(const input::mouse_moved_event& event)
@ -546,60 +535,28 @@ void treadmill_experiment_state::setup_controls()
const auto& mouse_position = (*ctx.input_manager->get_mice().begin())->get_position(); const auto& mouse_position = (*ctx.input_manager->get_mice().begin())->get_position();
const auto mouse_ray = get_mouse_ray(mouse_position); const auto mouse_ray = get_mouse_ray(mouse_position);
debug::log::info("pick:");
float nearest_hit = std::numeric_limits<float>::infinity();
bool hit = false;
std::uint32_t hit_index;
const auto& vertex_positions = navmesh->vertices().attributes().at<math::fvec3>("position");
const auto& face_normals = navmesh->faces().attributes().at<math::fvec3>("normal");
std::size_t test_count = 0;
int box_test_passed = 0;
navmesh_bvh->visit
(
mouse_ray,
[&](std::uint32_t index)
if (auto trace = ctx.physics_system->trace(mouse_ray))
{
// debug::log::debug("HIT! EID: {}; distance: {}; face: {}", static_cast<int>(std::get<0>(*trace)), std::get<1>(*trace), std::get<2>(*trace));
const auto& hit_rigid_body = *ctx.entity_registry->get<rigid_body_component>(std::get<0>(*trace)).body;
const auto& hit_collider = *hit_rigid_body.get_collider();
const auto hit_distance = std::get<1>(*trace);
const auto& hit_normal = std::get<3>(*trace);
geom::brep_mesh* hit_mesh = nullptr;
geom::brep_face* hit_face = nullptr;
if (hit_collider.type() == physics::collider_type::mesh)
{ {
++box_test_passed;
geom::brep_face* face = navmesh->faces()[index];
const auto& n = face_normals[index];
if (math::dot(n, mouse_ray.direction) > 0.0f)
{
return;
}
auto loop = face->loops().begin();
const auto& a = vertex_positions[loop->vertex()->index()];
const auto& b = vertex_positions[(++loop)->vertex()->index()];
const auto& c = vertex_positions[(++loop)->vertex()->index()];
if (auto intersection = geom::intersection(mouse_ray, a, b, c))
{
++test_count;
float t = std::get<0>(*intersection);
if (t < nearest_hit)
{
hit = true;
nearest_hit = t;
hit_index = index;
}
}
hit_mesh = static_cast<const physics::mesh_collider&>(hit_collider).get_mesh().get();
hit_face = hit_mesh->faces()[std::get<2>(*trace)];
} }
);
debug::log::info("box tests passed: {}", box_test_passed);
if (hit)
{
const auto& navmesh_face_normals = navmesh->faces().attributes().at<math::fvec3>("normal");
// Update agent transform // Update agent transform
auto& agent_rigid_body = *ctx.entity_registry->get<::rigid_body_component>(worker_eid).body;
auto& agent_rigid_body = *ctx.entity_registry->get<rigid_body_component>(worker_eid).body;
auto agent_transform = agent_rigid_body.get_transform(); auto agent_transform = agent_rigid_body.get_transform();
agent_transform.translation = mouse_ray.extrapolate(nearest_hit);
agent_transform.rotation = math::rotation(math::fvec3{0, 1, 0}, navmesh_face_normals[hit_index]);
agent_transform.translation = mouse_ray.extrapolate(hit_distance);
agent_transform.rotation = math::rotation(math::fvec3{0, 1, 0}, hit_normal);
agent_rigid_body.set_transform(agent_transform); agent_rigid_body.set_transform(agent_transform);
agent_rigid_body.set_previous_transform(agent_transform); agent_rigid_body.set_previous_transform(agent_transform);
@ -609,17 +566,11 @@ void treadmill_experiment_state::setup_controls()
worker_eid, worker_eid,
[&](auto& component) [&](auto& component)
{ {
component.mesh = navmesh.get();
component.face = navmesh->faces()[hit_index];
component.surface_normal = navmesh_face_normals[hit_index];
component.mesh = hit_mesh;
component.face = hit_face;
component.surface_normal = hit_normal;
} }
); );
debug::log::info("hit! test count: {}", test_count);
}
else
{
debug::log::info("no hit");
} }
} }
) )

+ 0
- 2
src/game/states/experiments/treadmill-experiment-state.hpp View File

@ -112,8 +112,6 @@ private:
std::shared_ptr<render::matvar_fvec3> light_rectangle_emissive; std::shared_ptr<render::matvar_fvec3> light_rectangle_emissive;
std::shared_ptr<scene::light_probe> light_probe; std::shared_ptr<scene::light_probe> light_probe;
std::shared_ptr<geom::brep_mesh> navmesh;
std::unique_ptr<geom::bvh> navmesh_bvh;
entity::id larva_eid; entity::id larva_eid;
entity::id worker_eid; entity::id worker_eid;

+ 2
- 2
src/game/states/main-menu-state.cpp View File

@ -277,8 +277,8 @@ main_menu_state::main_menu_state(::game& ctx, bool fade_in):
ctx.sky_pass->set_enabled(true); ctx.sky_pass->set_enabled(true);
ctx.ground_pass->set_enabled(true); ctx.ground_pass->set_enabled(true);
// Disable UI color clear
ctx.ui_clear_pass->set_cleared_buffers(false, true, false);
// Enable UI color clear
ctx.ui_clear_pass->set_cleared_buffers(true, true, false);
// Setup window resized callback // Setup window resized callback
window_resized_subscription = ctx.window->get_resized_channel().subscribe window_resized_subscription = ctx.window->get_resized_channel().subscribe

+ 8
- 232
src/game/states/nest-selection-state.cpp View File

@ -25,7 +25,6 @@
#include "game/components/constraint-stack-component.hpp" #include "game/components/constraint-stack-component.hpp"
#include "game/components/scene-component.hpp" #include "game/components/scene-component.hpp"
#include "game/components/picking-component.hpp" #include "game/components/picking-component.hpp"
#include "game/components/spring-component.hpp"
#include "game/components/rigid-body-component.hpp" #include "game/components/rigid-body-component.hpp"
#include "game/components/rigid-body-constraint-component.hpp" #include "game/components/rigid-body-constraint-component.hpp"
#include "game/components/steering-component.hpp" #include "game/components/steering-component.hpp"
@ -160,7 +159,7 @@ nest_selection_state::nest_selection_state(::game& ctx):
auto cocoon_eid = ctx.entity_registry->create(); auto cocoon_eid = ctx.entity_registry->create();
ctx.entity_registry->emplace<scene_component>(cocoon_eid, std::make_shared<scene::static_mesh>(worker_phenome.cocoon->model), std::uint8_t{1});
ctx.entity_registry->emplace<scene_component>(cocoon_eid, std::make_shared<scene::static_mesh>(worker_phenome.pupa->cocoon_model), std::uint8_t{1});
larva_eid = ctx.entity_registry->create(); larva_eid = ctx.entity_registry->create();
@ -227,9 +226,9 @@ nest_selection_state::nest_selection_state(::game& ctx):
const float aspect_ratio = static_cast<float>(viewport_size[0]) / static_cast<float>(viewport_size[1]); const float aspect_ratio = static_cast<float>(viewport_size[0]) / static_cast<float>(viewport_size[1]);
// Init first person camera rig parameters // Init first person camera rig parameters
first_person_camera_rig_translation_spring_angular_frequency = period_to_rads(0.125f);
first_person_camera_rig_rotation_spring_angular_frequency = period_to_rads(0.125f);
first_person_camera_rig_fov_spring_angular_frequency = period_to_rads(0.125f);
first_person_camera_rig_translation_spring_angular_frequency = physics::s_to_rads(0.125f);
first_person_camera_rig_rotation_spring_angular_frequency = physics::s_to_rads(0.125f);
first_person_camera_rig_fov_spring_angular_frequency = physics::s_to_rads(0.125f);
first_person_camera_rig_min_elevation = 0.25f; first_person_camera_rig_min_elevation = 0.25f;
first_person_camera_rig_max_elevation = 150.0f; first_person_camera_rig_max_elevation = 150.0f;
first_person_camera_near_fov = math::vertical_fov(math::radians(100.0f), aspect_ratio); first_person_camera_near_fov = math::vertical_fov(math::radians(100.0f), aspect_ratio);
@ -390,83 +389,17 @@ void nest_selection_state::destroy_first_person_camera_rig()
void nest_selection_state::set_first_person_camera_rig_pedestal(float pedestal) void nest_selection_state::set_first_person_camera_rig_pedestal(float pedestal)
{ {
// first_person_camera_rig_pedestal = pedestal;
// const float elevation = math::log_lerp(first_person_camera_rig_min_elevation, first_person_camera_rig_max_elevation, first_person_camera_rig_pedestal);
// const float fov = math::log_lerp(first_person_camera_near_fov, first_person_camera_far_fov, first_person_camera_rig_pedestal);
// ctx.entity_registry->patch<spring_translation_constraint>
// (
// first_person_camera_rig_spring_translation_eid,
// [&](auto& component)
// {
// component.spring.x1[1] = elevation;
// }
// );
// ctx.entity_registry->patch<spring1_component>
// (
// first_person_camera_rig_fov_spring_eid,
// [&](auto& component)
// {
// component.spring.x1 = fov;
// }
// );
} }
void nest_selection_state::move_first_person_camera_rig(const math::fvec2& direction, float factor) void nest_selection_state::move_first_person_camera_rig(const math::fvec2& direction, float factor)
{ {
// const float speed = math::log_lerp(first_person_camera_near_speed, first_person_camera_far_speed, first_person_camera_rig_pedestal) * factor;
// const spring_rotation_constraint& first_person_camera_rig_spring_rotation = ctx.entity_registry->get<spring_rotation_constraint>(first_person_camera_rig_spring_rotation_eid);
// const math::fquat yaw_rotation = math::angle_axis(first_person_camera_rig_spring_rotation.spring.x0[0], math::fvec3{0.0f, 1.0f, 0.0f});
// const math::fvec3 rotated_direction = math::normalize(yaw_rotation * math::fvec3{direction[0], 0.0f, direction[1]});
// const math::fvec3 velocity = rotated_direction * speed;
// ctx.entity_registry->patch<spring_translation_constraint>
// (
// first_person_camera_rig_spring_translation_eid,
// [&](auto& component)
// {
// component.spring.x1 += velocity * static_cast<float>(1.0 / ctx.fixed_update_rate);
// }
// );
} }
void nest_selection_state::satisfy_first_person_camera_rig_constraints() void nest_selection_state::satisfy_first_person_camera_rig_constraints()
{ {
// Satisfy first person camera rig spring translation constraint
// ctx.entity_registry->patch<spring_translation_constraint>
// (
// first_person_camera_rig_spring_translation_eid,
// [&](auto& component)
// {
// component.spring.x0 = component.spring.x1;
// component.spring.v *= 0.0f;
// }
// );
// Satisfy first person camera rig spring rotation constraint
// ctx.entity_registry->patch<spring_rotation_constraint>
// (
// first_person_camera_rig_spring_rotation_eid,
// [&](auto& component)
// {
// component.spring.x0 = component.spring.x1;
// component.spring.v *= 0.0f;
// }
// );
// Satisfy first person camera rig fov spring
// ctx.entity_registry->patch<spring1_component>
// (
// first_person_camera_rig_fov_spring_eid,
// [&](auto& component)
// {
// component.spring.x0 = component.spring.x1;
// component.spring.v *= 0.0f;
// }
// );
} }
void nest_selection_state::setup_controls() void nest_selection_state::setup_controls()
@ -478,11 +411,6 @@ void nest_selection_state::setup_controls()
{ {
const transform_component& first_person_camera_rig_transform = ctx.entity_registry->get<transform_component>(first_person_camera_rig_eid); const transform_component& first_person_camera_rig_transform = ctx.entity_registry->get<transform_component>(first_person_camera_rig_eid);
//const spring_rotation_constraint& first_person_camera_rig_spring_rotation = ctx.entity_registry->get<spring_rotation_constraint>(first_person_camera_rig_spring_rotation_eid);
//const math::fquat yaw_rotation = math::angle_axis(first_person_camera_rig_spring_rotation.spring.x0[0], math::fvec3{0.0f, 1.0f, 0.0f});
//const math::fvec3 rotated_direction = yaw_rotation * math::fvec3{direction[0], 0.0f, direction[1]};
const math::fquat yaw_rotation = math::angle_axis(static_cast<float>(first_person_camera_yaw), math::fvec3{0.0f, 1.0f, 0.0f}); const math::fquat yaw_rotation = math::angle_axis(static_cast<float>(first_person_camera_yaw), math::fvec3{0.0f, 1.0f, 0.0f});
const math::fvec3 rotated_direction = yaw_rotation * math::fvec3{direction[0], 0.0f, direction[1]}; const math::fvec3 rotated_direction = yaw_rotation * math::fvec3{direction[0], 0.0f, direction[1]};
@ -822,159 +750,7 @@ void nest_selection_state::setup_controls()
void nest_selection_state::enable_controls() void nest_selection_state::enable_controls()
{ {
/*
// Reset mouse look
mouse_look = false;
double time_scale = 0.0;
double ff_time_scale = 60.0 * 200.0;
// Init control settings
float mouse_tilt_sensitivity = 1.0f;
float mouse_pan_sensitivity = 1.0f;
bool mouse_invert_tilt = false;
bool mouse_invert_pan = false;
bool mouse_look_toggle = false;
float gamepad_tilt_sensitivity = 1.0f;
float gamepad_pan_sensitivity = 1.0f;
bool gamepad_invert_tilt = false;
bool gamepad_invert_pan = false;
// Read control settings
if (ctx.config->contains("mouse_tilt_sensitivity"))
mouse_tilt_sensitivity = math::radians((*ctx.config)["mouse_tilt_sensitivity"].get<float>());
if (ctx.config->contains("mouse_pan_sensitivity"))
mouse_pan_sensitivity = math::radians((*ctx.config)["mouse_pan_sensitivity"].get<float>());
if (ctx.config->contains("mouse_invert_tilt"))
mouse_invert_tilt = (*ctx.config)["mouse_invert_tilt"].get<bool>();
if (ctx.config->contains("mouse_invert_pan"))
mouse_invert_pan = (*ctx.config)["mouse_invert_pan"].get<bool>();
if (ctx.config->contains("mouse_look_toggle"))
mouse_look_toggle = (*ctx.config)["mouse_look_toggle"].get<bool>();
if (ctx.config->contains("gamepad_tilt_sensitivity"))
gamepad_tilt_sensitivity = math::radians((*ctx.config)["gamepad_tilt_sensitivity"].get<float>());
if (ctx.config->contains("gamepad_pan_sensitivity"))
gamepad_pan_sensitivity = math::radians((*ctx.config)["gamepad_pan_sensitivity"].get<float>());
if (ctx.config->contains("gamepad_invert_tilt"))
gamepad_invert_tilt = (*ctx.config)["gamepad_invert_tilt"].get<bool>();
if (ctx.config->contains("gamepad_invert_pan"))
gamepad_invert_pan = (*ctx.config)["gamepad_invert_pan"].get<bool>();
// Determine tilt and pan factors according to sensitivity and inversion
const float mouse_tilt_factor = mouse_tilt_sensitivity * (mouse_invert_tilt ? -1.0f : 1.0f);
const float mouse_pan_factor = mouse_pan_sensitivity * (mouse_invert_pan ? -1.0f : 1.0f);
const float gamepad_tilt_factor = gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0f : 1.0f);
const float gamepad_pan_factor = gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0f : 1.0f);
ctx.controls["look_right_gamepad"]->set_active_callback
(
[&, gamepad_pan_factor](float value)
{
ctx.entity_registry->patch<spring_rotation_constraint>
(
first_person_camera_rig_spring_rotation_eid,
[&, gamepad_pan_factor](auto& component)
{
component.spring.x1[0] -= gamepad_pan_factor * value * static_cast<float>(1.0 / ctx.fixed_update_rate);
}
);
}
);
ctx.controls["look_left_gamepad"]->set_active_callback
(
[&, gamepad_pan_factor](float value)
{
ctx.entity_registry->patch<spring_rotation_constraint>
(
first_person_camera_rig_spring_rotation_eid,
[&, gamepad_pan_factor](auto& component)
{
component.spring.x1[0] += gamepad_pan_factor * value * static_cast<float>(1.0 / ctx.fixed_update_rate);
}
);
}
);
ctx.controls["look_up_gamepad"]->set_active_callback
(
[&, gamepad_tilt_factor](float value)
{
ctx.entity_registry->patch<spring_rotation_constraint>
(
first_person_camera_rig_spring_rotation_eid,
[&, gamepad_tilt_factor](auto& component)
{
component.spring.x1[1] -= gamepad_tilt_factor * value * static_cast<float>(1.0 / ctx.fixed_update_rate);
component.spring.x1[1] = std::max(-math::half_pi<float>, component.spring.x1[1]);
}
);
}
);
ctx.controls["look_down_gamepad"]->set_active_callback
(
[&, gamepad_tilt_factor](float value)
{
ctx.entity_registry->patch<spring_rotation_constraint>
(
first_person_camera_rig_spring_rotation_eid,
[&, gamepad_tilt_factor](auto& component)
{
component.spring.x1[1] += gamepad_tilt_factor * value * static_cast<float>(1.0 / ctx.fixed_update_rate);
component.spring.x1[1] = std::min(math::half_pi<float>, component.spring.x1[1]);
}
);
}
);
// Pedestal up control
ctx.controls["move_up"]->set_active_callback
(
[&](float value)
{
set_first_person_camera_rig_pedestal(std::min(1.0f, first_person_camera_rig_pedestal + first_person_camera_rig_pedestal_speed * static_cast<float>(1.0 / ctx.fixed_update_rate)));
}
);
// Pedestal down control
ctx.controls["move_down"]->set_active_callback
(
[&](float value)
{
set_first_person_camera_rig_pedestal(std::max(0.0f, first_person_camera_rig_pedestal - first_person_camera_rig_pedestal_speed * static_cast<float>(1.0 / ctx.fixed_update_rate)));
}
);
// Fast-forward
ctx.controls["fast_forward"]->set_activated_callback
(
[&ctx = this->ctx, ff_time_scale]()
{
::world::set_time_scale(ctx, ff_time_scale);
}
);
ctx.controls["fast_forward"]->set_deactivated_callback
(
[&ctx = this->ctx, time_scale]()
{
::world::set_time_scale(ctx, time_scale);
}
);
ctx.controls["rewind"]->set_activated_callback
(
[&ctx = this->ctx, ff_time_scale]()
{
::world::set_time_scale(ctx, -ff_time_scale);
}
);
ctx.controls["rewind"]->set_deactivated_callback
(
[&ctx = this->ctx, time_scale]()
{
::world::set_time_scale(ctx, time_scale);
}
);
*/
} }
void nest_selection_state::disable_controls() void nest_selection_state::disable_controls()

+ 1
- 2
src/game/states/nest-view-state.cpp View File

@ -27,7 +27,6 @@
#include "game/components/constraint-stack-component.hpp" #include "game/components/constraint-stack-component.hpp"
#include "game/components/scene-component.hpp" #include "game/components/scene-component.hpp"
#include "game/components/picking-component.hpp" #include "game/components/picking-component.hpp"
#include "game/components/spring-component.hpp"
#include "game/components/rigid-body-component.hpp" #include "game/components/rigid-body-component.hpp"
#include "game/components/rigid-body-constraint-component.hpp" #include "game/components/rigid-body-constraint-component.hpp"
#include "game/components/steering-component.hpp" #include "game/components/steering-component.hpp"
@ -186,7 +185,7 @@ nest_view_state::nest_view_state(::game& ctx):
// Create cocoon // Create cocoon
auto cocoon_eid = ctx.entity_registry->create(); auto cocoon_eid = ctx.entity_registry->create();
auto cocoon_static_mesh = std::make_shared<scene::static_mesh>(worker_phenome.cocoon->model);
auto cocoon_static_mesh = std::make_shared<scene::static_mesh>(worker_phenome.pupa->cocoon_model);
cocoon_static_mesh->set_scale(worker_phenome.body_size->mean_mesosoma_length); cocoon_static_mesh->set_scale(worker_phenome.body_size->mean_mesosoma_length);
ctx.entity_registry->emplace<scene_component>(cocoon_eid, std::move(cocoon_static_mesh), std::uint8_t{2}); ctx.entity_registry->emplace<scene_component>(cocoon_eid, std::move(cocoon_static_mesh), std::uint8_t{2});
ctx.entity_registry->patch<scene_component> ctx.entity_registry->patch<scene_component>

+ 10
- 628
src/game/states/nuptial-flight-state.cpp View File

@ -33,7 +33,6 @@
#include "game/components/constraint-stack-component.hpp" #include "game/components/constraint-stack-component.hpp"
#include "game/components/steering-component.hpp" #include "game/components/steering-component.hpp"
#include "game/components/picking-component.hpp" #include "game/components/picking-component.hpp"
#include "game/components/spring-component.hpp"
#include "game/components/scene-component.hpp" #include "game/components/scene-component.hpp"
#include "game/constraints/child-of-constraint.hpp" #include "game/constraints/child-of-constraint.hpp"
#include "game/constraints/copy-rotation-constraint.hpp" #include "game/constraints/copy-rotation-constraint.hpp"
@ -134,9 +133,9 @@ nuptial_flight_state::nuptial_flight_state(::game& ctx):
camera_rig_near_fov = math::vertical_fov(math::radians(100.0f), aspect_ratio); camera_rig_near_fov = math::vertical_fov(math::radians(100.0f), aspect_ratio);
camera_rig_far_fov = math::vertical_fov(math::radians(60.0f), aspect_ratio); camera_rig_far_fov = math::vertical_fov(math::radians(60.0f), aspect_ratio);
camera_rig_zoom_speed = 4.0f; camera_rig_zoom_speed = 4.0f;
camera_rig_translation_spring_angular_frequency = period_to_rads(0.125f);
camera_rig_rotation_spring_angular_frequency = period_to_rads(0.125f);
camera_rig_fov_spring_angular_frequency = period_to_rads(0.125f);
camera_rig_translation_spring_angular_frequency = physics::s_to_rads(0.125f);
camera_rig_rotation_spring_angular_frequency = physics::s_to_rads(0.125f);
camera_rig_fov_spring_angular_frequency = physics::s_to_rads(0.125f);
camera_rig_focus_ease_to_duration = 1.0f; camera_rig_focus_ease_to_duration = 1.0f;
// Create camera rig // Create camera rig
@ -274,14 +273,7 @@ void nuptial_flight_state::create_camera_rig()
// Construct camera rig spring rotation constraint // Construct camera rig spring rotation constraint
spring_rotation_constraint camera_rig_spring_rotation; spring_rotation_constraint camera_rig_spring_rotation;
camera_rig_spring_rotation.spring =
{
{0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 0.0f},
1.0f,
camera_rig_rotation_spring_angular_frequency
};
camera_rig_spring_rotation.spring.set_angular_frequency(camera_rig_rotation_spring_angular_frequency);
constraint_stack_node_component camera_rig_spring_rotation_node; constraint_stack_node_component camera_rig_spring_rotation_node;
camera_rig_spring_rotation_node.active = true; camera_rig_spring_rotation_node.active = true;
camera_rig_spring_rotation_node.weight = 1.0f; camera_rig_spring_rotation_node.weight = 1.0f;
@ -292,14 +284,7 @@ void nuptial_flight_state::create_camera_rig()
// Construct camera rig spring translation constraint // Construct camera rig spring translation constraint
spring_translation_constraint camera_rig_spring_translation; spring_translation_constraint camera_rig_spring_translation;
camera_rig_spring_translation.spring =
{
{0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 0.0f},
1.0f,
camera_rig_translation_spring_angular_frequency
};
camera_rig_spring_translation.spring.set_angular_frequency(camera_rig_translation_spring_angular_frequency);
constraint_stack_node_component camera_rig_spring_translation_node; constraint_stack_node_component camera_rig_spring_translation_node;
camera_rig_spring_translation_node.active = true; camera_rig_spring_translation_node.active = true;
camera_rig_spring_translation_node.weight = 1.0f; camera_rig_spring_translation_node.weight = 1.0f;
@ -328,25 +313,6 @@ void nuptial_flight_state::create_camera_rig()
ctx.entity_registry->emplace<transform_component>(camera_rig_eid, camera_rig_transform); ctx.entity_registry->emplace<transform_component>(camera_rig_eid, camera_rig_transform);
ctx.entity_registry->emplace<constraint_stack_component>(camera_rig_eid, camera_rig_constraint_stack); ctx.entity_registry->emplace<constraint_stack_component>(camera_rig_eid, camera_rig_constraint_stack);
// Construct camera rig fov spring
spring1_component camera_rig_fov_spring;
camera_rig_fov_spring.spring =
{
0.0f,
0.0f,
0.0f,
1.0f,
camera_rig_fov_spring_angular_frequency
};
camera_rig_fov_spring.callback = [&](float fov)
{
ctx.surface_camera->set_perspective(fov, ctx.surface_camera->get_aspect_ratio(), ctx.surface_camera->get_clip_near(), ctx.surface_camera->get_clip_far());
};
// Construct camera rig fov spring entity
camera_rig_fov_spring_eid = ctx.entity_registry->create();
ctx.entity_registry->emplace<spring1_component>(camera_rig_fov_spring_eid, camera_rig_fov_spring);
set_camera_rig_zoom(0.25f); set_camera_rig_zoom(0.25f);
} }
@ -366,611 +332,27 @@ void nuptial_flight_state::destroy_camera_rig()
void nuptial_flight_state::set_camera_rig_zoom(float zoom) void nuptial_flight_state::set_camera_rig_zoom(float zoom)
{ {
camera_rig_zoom = zoom;
const float distance = math::log_lerp(camera_rig_far_distance, camera_rig_near_distance, camera_rig_zoom);
ctx.entity_registry->patch<spring_translation_constraint>
(
camera_rig_spring_translation_eid,
[&](auto& component)
{
component.spring.x1[2] = distance;
}
);
const float fov = math::log_lerp(camera_rig_far_fov, camera_rig_near_fov, camera_rig_zoom);
ctx.entity_registry->patch<spring1_component>
(
camera_rig_fov_spring_eid,
[&](auto& component)
{
component.spring.x1 = fov;
}
);
} }
void nuptial_flight_state::satisfy_camera_rig_constraints() void nuptial_flight_state::satisfy_camera_rig_constraints()
{ {
// Satisfy camera rig focus ease to constraint
ctx.entity_registry->patch<ease_to_constraint>
(
camera_rig_focus_ease_to_eid,
[&](auto& component)
{
component.t = component.duration;
}
);
// Satisfy camera rig spring translation constraint
ctx.entity_registry->patch<spring_translation_constraint>
(
camera_rig_spring_translation_eid,
[&](auto& component)
{
component.spring.x0 = component.spring.x1;
component.spring.v *= 0.0f;
}
);
// Satisfy camera rig spring rotation constraint
ctx.entity_registry->patch<spring_rotation_constraint>
(
camera_rig_spring_rotation_eid,
[&](auto& component)
{
component.spring.x0 = component.spring.x1;
component.spring.v *= 0.0f;
}
);
// Satisfycamera rig fov spring
ctx.entity_registry->patch<spring1_component>
(
camera_rig_fov_spring_eid,
[&](auto& component)
{
component.spring.x0 = component.spring.x1;
component.spring.v *= 0.0f;
}
);
} }
void nuptial_flight_state::setup_controls() void nuptial_flight_state::setup_controls()
{ {
/*
// Enable/toggle mouse look
action_subscriptions.emplace_back
(
ctx.mouse_look_action.get_activated_channel().subscribe
(
[&](const auto& event)
{
if (ctx.toggle_mouse_look)
{
mouse_look = !mouse_look;
}
else
{
mouse_look = true;
}
//ctx.input_manager->set_cursor_visible(!mouse_look);
ctx.input_manager->set_relative_mouse_mode(mouse_look);
}
)
);
// Disable mouse look
action_subscriptions.emplace_back
(
ctx.mouse_look_action.get_deactivated_channel().subscribe
(
[&](const auto& event)
{
if (!ctx.toggle_mouse_look && mouse_look)
{
mouse_look = false;
//ctx.input_manager->set_cursor_visible(true);
ctx.input_manager->set_relative_mouse_mode(false);
}
}
)
);
// Mouse look
mouse_motion_subscription = ctx.input_manager->get_event_dispatcher().subscribe<input::mouse_moved_event>
(
[&](const auto& event)
{
if (!mouse_look)
{
return;
}
ctx.entity_registry->patch<spring_rotation_constraint>
(
camera_rig_spring_rotation_eid,
[&](auto& component)
{
component.spring.x1[0] += static_cast<float>(ctx.mouse_pan_factor) * static_cast<float>(event.difference.x());
component.spring.x1[1] -= static_cast<float>(ctx.mouse_tilt_factor) * static_cast<float>(event.difference.y());
component.spring.x1[1] = std::min(math::half_pi<float>, std::max(-math::half_pi<float>, component.spring.x1[1]));
}
);
}
);
// Dolly in control
action_subscriptions.emplace_back
(
ctx.move_up_action.get_active_channel().subscribe
(
[&](const auto& event)
{
set_camera_rig_zoom(std::min(1.0f, camera_rig_zoom + camera_rig_zoom_speed * event.input_value * static_cast<float>(1.0 / ctx.fixed_update_rate)));
}
)
);
// Dolly out control
action_subscriptions.emplace_back
(
ctx.move_down_action.get_active_channel().subscribe
(
[&](const auto& event)
{
set_camera_rig_zoom(std::max(0.0f, camera_rig_zoom - camera_rig_zoom_speed * event.input_value * static_cast<float>(1.0 / ctx.fixed_update_rate)));
}
)
);
// Pick alate
action_subscriptions.emplace_back
(
ctx.mouse_pick_action.get_activated_channel().subscribe
(
[&](const auto& event)
{
// Get window-space mouse coordinates
const auto& mouse_position = (*ctx.input_manager->get_mice().begin())->get_position();
// Get window viewport size
const auto& viewport_size = ctx.window->get_viewport_size();
// Transform mouse coordinates from window space to NDC space
const math::fvec2 mouse_ndc =
{
static_cast<float>(mouse_position.x()) / static_cast<float>(viewport_size.x() - 1) * 2.0f - 1.0f,
(1.0f - static_cast<float>(mouse_position.y()) / static_cast<float>(viewport_size.y() - 1)) * 2.0f - 1.0f
};
// Get picking ray from camera
const geom::ray<float, 3> 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);
}
}
)
);
*/
} }
void nuptial_flight_state::enable_controls() void nuptial_flight_state::enable_controls()
{ {
/*
// Reset mouse look
mouse_look = false;
double time_scale = 0.0;
double ff_time_scale = 60.0 * 200.0;
// Init control settings
float mouse_tilt_sensitivity = 1.0f;
float mouse_pan_sensitivity = 1.0f;
bool mouse_invert_tilt = false;
bool mouse_invert_pan = false;
bool mouse_look_toggle = false;
float gamepad_tilt_sensitivity = 1.0f;
float gamepad_pan_sensitivity = 1.0f;
bool gamepad_invert_tilt = false;
bool gamepad_invert_pan = false;
// Read control settings
if (ctx.config->contains("mouse_tilt_sensitivity"))
mouse_tilt_sensitivity = math::radians((*ctx.config)["mouse_tilt_sensitivity"].get<float>());
if (ctx.config->contains("mouse_pan_sensitivity"))
mouse_pan_sensitivity = math::radians((*ctx.config)["mouse_pan_sensitivity"].get<float>());
if (ctx.config->contains("mouse_invert_tilt"))
mouse_invert_tilt = (*ctx.config)["mouse_invert_tilt"].get<bool>();
if (ctx.config->contains("mouse_invert_pan"))
mouse_invert_pan = (*ctx.config)["mouse_invert_pan"].get<bool>();
if (ctx.config->contains("mouse_look_toggle"))
mouse_look_toggle = (*ctx.config)["mouse_look_toggle"].get<bool>();
if (ctx.config->contains("gamepad_tilt_sensitivity"))
gamepad_tilt_sensitivity = math::radians((*ctx.config)["gamepad_tilt_sensitivity"].get<float>());
if (ctx.config->contains("gamepad_pan_sensitivity"))
gamepad_pan_sensitivity = math::radians((*ctx.config)["gamepad_pan_sensitivity"].get<float>());
if (ctx.config->contains("gamepad_invert_tilt"))
gamepad_invert_tilt = (*ctx.config)["gamepad_invert_tilt"].get<bool>();
if (ctx.config->contains("gamepad_invert_pan"))
gamepad_invert_pan = (*ctx.config)["gamepad_invert_pan"].get<bool>();
// Determine tilt and pan factors according to sensitivity and inversion
const float mouse_tilt_factor = mouse_tilt_sensitivity * (mouse_invert_tilt ? -1.0f : 1.0f);
const float mouse_pan_factor = mouse_pan_sensitivity * (mouse_invert_pan ? -1.0f : 1.0f);
const float gamepad_tilt_factor = gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0f : 1.0f);
const float gamepad_pan_factor = gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0f : 1.0f);
// Mouse look
ctx.controls["mouse_look"]->set_activated_callback
(
[&, mouse_look_toggle]()
{
if (mouse_look_toggle)
mouse_look = !mouse_look;
else
mouse_look = true;
ctx.app->set_relative_mouse_mode(mouse_look);
}
);
ctx.controls["mouse_look"]->set_deactivated_callback
(
[&, mouse_look_toggle]()
{
if (!mouse_look_toggle && mouse_look)
{
mouse_look = false;
ctx.app->set_relative_mouse_mode(false);
}
}
);
// Arc left control
ctx.controls["look_right_mouse"]->set_active_callback
(
[&, mouse_pan_factor](float value)
{
if (!mouse_look)
return;
ctx.entity_registry->patch<spring_rotation_constraint>
(
camera_rig_spring_rotation_eid,
[&, mouse_pan_factor](auto& component)
{
component.spring.x1[0] -= mouse_pan_factor * value;
}
);
}
);
ctx.controls["look_right_gamepad"]->set_active_callback
(
[&, gamepad_pan_factor](float value)
{
ctx.entity_registry->patch<spring_rotation_constraint>
(
camera_rig_spring_rotation_eid,
[&, gamepad_pan_factor](auto& component)
{
component.spring.x1[0] -= gamepad_pan_factor * value * static_cast<float>(1.0 / ctx.fixed_update_rate);
}
);
}
);
// Arc right control
ctx.controls["look_left_mouse"]->set_active_callback
(
[&, mouse_pan_factor](float value)
{
if (!mouse_look)
return;
ctx.entity_registry->patch<spring_rotation_constraint>
(
camera_rig_spring_rotation_eid,
[&, mouse_pan_factor](auto& component)
{
component.spring.x1[0] += mouse_pan_factor * value;
}
);
}
);
ctx.controls["look_left_gamepad"]->set_active_callback
(
[&, gamepad_pan_factor](float value)
{
ctx.entity_registry->patch<spring_rotation_constraint>
(
camera_rig_spring_rotation_eid,
[&, gamepad_pan_factor](auto& component)
{
component.spring.x1[0] += gamepad_pan_factor * value * static_cast<float>(1.0 / ctx.fixed_update_rate);
}
);
}
);
// Arc down control
ctx.controls["look_up_mouse"]->set_active_callback
(
[&, mouse_tilt_factor](float value)
{
if (!mouse_look)
return;
ctx.entity_registry->patch<spring_rotation_constraint>
(
camera_rig_spring_rotation_eid,
[&, mouse_tilt_factor](auto& component)
{
component.spring.x1[1] -= mouse_tilt_factor * value;
component.spring.x1[1] = std::max(-math::half_pi<float>, component.spring.x1[1]);
}
);
}
);
ctx.controls["look_up_gamepad"]->set_active_callback
(
[&, gamepad_tilt_factor](float value)
{
ctx.entity_registry->patch<spring_rotation_constraint>
(
camera_rig_spring_rotation_eid,
[&, gamepad_tilt_factor](auto& component)
{
component.spring.x1[1] -= gamepad_tilt_factor * value * static_cast<float>(1.0 / ctx.fixed_update_rate);
component.spring.x1[1] = std::max(-math::half_pi<float>, component.spring.x1[1]);
}
);
}
);
// Arc up control
ctx.controls["look_down_mouse"]->set_active_callback
(
[&, mouse_tilt_factor](float value)
{
if (!mouse_look)
return;
ctx.entity_registry->patch<spring_rotation_constraint>
(
camera_rig_spring_rotation_eid,
[&, mouse_tilt_factor](auto& component)
{
component.spring.x1[1] += mouse_tilt_factor * value;
component.spring.x1[1] = std::min(math::half_pi<float>, component.spring.x1[1]);
}
);
}
);
ctx.controls["look_down_gamepad"]->set_active_callback
(
[&, gamepad_tilt_factor](float value)
{
ctx.entity_registry->patch<spring_rotation_constraint>
(
camera_rig_spring_rotation_eid,
[&, gamepad_tilt_factor](auto& component)
{
component.spring.x1[1] += gamepad_tilt_factor * value * static_cast<float>(1.0 / ctx.fixed_update_rate);
component.spring.x1[1] = std::min(math::half_pi<float>, component.spring.x1[1]);
}
);
}
);
// Dolly in control
ctx.controls["move_up"]->set_active_callback
(
[&](float value)
{
set_camera_rig_zoom(std::min(1.0f, camera_rig_zoom + camera_rig_zoom_speed * static_cast<float>(1.0 / ctx.fixed_update_rate)));
}
);
// Dolly out control
ctx.controls["move_down"]->set_active_callback
(
[&](float value)
{
set_camera_rig_zoom(std::max(0.0f, camera_rig_zoom - camera_rig_zoom_speed * static_cast<float>(1.0 / ctx.fixed_update_rate)));
}
);
// 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 size
const auto viewport_size = ctx.window->get_viewport_size();
// Transform mouse coordinates from window space to NDC space
const math::fvec2 mouse_ndc =
{
static_cast<float>(mouse_x) / static_cast<float>(viewport_size[0] - 1) * 2.0f - 1.0f,
(1.0f - static_cast<float>(mouse_y) / static_cast<float>(viewport_size[1] - 1)) * 2.0f - 1.0f
};
// Get picking ray from camera
const geom::ray<float, 3> 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});
}
);
// Action control
ctx.controls["action"]->set_activated_callback
(
[&]()
{
// Disable controls
disable_controls();
// Change to nest selection state
ctx.state_machine.pop();
ctx.state_machine.emplace(std::make_unique<nest_selection_state>(ctx));
}
);
// Fast-forward
ctx.controls["fast_forward"]->set_activated_callback
(
[&ctx = this->ctx, ff_time_scale]()
{
::world::set_time_scale(ctx, ff_time_scale);
}
);
ctx.controls["fast_forward"]->set_deactivated_callback
(
[&ctx = this->ctx, time_scale]()
{
::world::set_time_scale(ctx, time_scale);
}
);
ctx.controls["rewind"]->set_activated_callback
(
[&ctx = this->ctx, ff_time_scale]()
{
::world::set_time_scale(ctx, -ff_time_scale);
}
);
ctx.controls["rewind"]->set_deactivated_callback
(
[&ctx = this->ctx, time_scale]()
{
::world::set_time_scale(ctx, time_scale);
}
);
// Setup pause control
ctx.controls["pause"]->set_activated_callback
(
[this, &ctx = this->ctx]()
{
// Disable controls
this->disable_controls();
// Set resume callback
ctx.resume_callback = [this, &ctx]()
{
this->enable_controls();
ctx.resume_callback = nullptr;
};
// Push pause menu state
ctx.state_machine.emplace(std::make_unique<pause_menu_state>(ctx));
}
);
ctx.controls["increase_exposure"]->set_active_callback
(
[&ctx = this->ctx](float)
{
//ctx.astronomy_system->set_exposure_offset(ctx.astronomy_system->get_exposure_offset() - 1.0f);
ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() + 3.0f * static_cast<float>(1.0 / ctx.fixed_update_rate));
debug::log::info("EV100: " + std::to_string(ctx.surface_camera->get_exposure()));
}
);
ctx.controls["decrease_exposure"]->set_active_callback
(
[&ctx = this->ctx](float)
{
//ctx.astronomy_system->set_exposure_offset(ctx.astronomy_system->get_exposure_offset() + 1.0f);
ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() - 3.0f * static_cast<float>(1.0 / ctx.fixed_update_rate));
debug::log::info("EV100: " + std::to_string(ctx.surface_camera->get_exposure()));
}
);
*/
} }
void nuptial_flight_state::disable_controls() void nuptial_flight_state::disable_controls()
{ {
/*
if (mouse_look)
{
mouse_look = false;
ctx.app->set_relative_mouse_mode(false);
}
ctx.controls["mouse_look"]->set_activated_callback(nullptr);
ctx.controls["mouse_look"]->set_deactivated_callback(nullptr);
ctx.controls["look_right_mouse"]->set_active_callback(nullptr);
ctx.controls["look_right_gamepad"]->set_active_callback(nullptr);
ctx.controls["look_left_mouse"]->set_active_callback(nullptr);
ctx.controls["look_left_gamepad"]->set_active_callback(nullptr);
ctx.controls["look_up_mouse"]->set_active_callback(nullptr);
ctx.controls["look_up_gamepad"]->set_active_callback(nullptr);
ctx.controls["look_down_mouse"]->set_active_callback(nullptr);
ctx.controls["look_down_gamepad"]->set_active_callback(nullptr);
ctx.controls["move_up"]->set_active_callback(nullptr);
ctx.controls["move_down"]->set_active_callback(nullptr);
ctx.controls["select_mouse"]->set_activated_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["action"]->set_activated_callback(nullptr);
ctx.controls["fast_forward"]->set_activated_callback(nullptr);
ctx.controls["fast_forward"]->set_deactivated_callback(nullptr);
ctx.controls["rewind"]->set_activated_callback(nullptr);
ctx.controls["rewind"]->set_deactivated_callback(nullptr);
ctx.controls["pause"]->set_activated_callback(nullptr);
ctx.controls["increase_exposure"]->set_active_callback(nullptr);
ctx.controls["decrease_exposure"]->set_active_callback(nullptr);
*/
} }
void nuptial_flight_state::select_entity(entity::id entity_id) void nuptial_flight_state::select_entity(entity::id entity_id)

+ 85
- 35
src/game/systems/camera-system.cpp View File

@ -18,11 +18,13 @@
*/ */
#include "game/systems/camera-system.hpp" #include "game/systems/camera-system.hpp"
#include "game/components/orbit-camera-component.hpp"
#include "game/components/autofocus-component.hpp"
#include "game/components/spring-arm-component.hpp"
#include "game/components/scene-component.hpp" #include "game/components/scene-component.hpp"
#include <engine/animation/ease.hpp> #include <engine/animation/ease.hpp>
#include <engine/math/projection.hpp> #include <engine/math/projection.hpp>
#include <engine/scene/camera.hpp> #include <engine/scene/camera.hpp>
#include <engine/math/euler-angles.hpp>
#include <execution> #include <execution>
camera_system::camera_system(entity::registry& registry): camera_system::camera_system(entity::registry& registry):
@ -31,76 +33,124 @@ camera_system::camera_system(entity::registry& registry):
void camera_system::update(float t, float dt) void camera_system::update(float t, float dt)
{ {
m_fixed_update_time = static_cast<double>(t);
m_fixed_timestep = static_cast<double>(dt);
} }
void camera_system::interpolate(float alpha) void camera_system::interpolate(float alpha)
{ {
auto orbit_cam_group = registry.group<orbit_camera_component>(entt::get<scene_component>);
const double variable_update_time = m_fixed_update_time + m_fixed_timestep * static_cast<double>(alpha);
const double variable_timestep = std::max(0.0, variable_update_time - m_variable_update_time);
m_variable_update_time = variable_update_time;
/*
auto autofocus_group = registry.group<autofocus_component>(entt::get<scene_component>);
std::for_each std::for_each
( (
std::execution::seq, std::execution::seq,
orbit_cam_group.begin(),
orbit_cam_group.end(),
autofocus_group.begin(),
autofocus_group.end(),
[&](auto entity_id) [&](auto entity_id)
{ {
auto& orbit_cam = orbit_cam_group.get<orbit_camera_component>(entity_id);
auto& scene = orbit_cam_group.get<scene_component>(entity_id);
auto& camera = static_cast<scene::camera&>(*scene.object);
auto& autofocus = autofocus_group.get<autofocus_component>(entity_id);
auto& camera = static_cast<scene::camera&>(*autofocus_group.get<scene_component>(entity_id).object);
// Clamp zoom factor
autofocus.zoom = std::min<double>(std::max<double>(autofocus.zoom, 0.0), 1.0);
// Calculate horizontal and vertical FoV
autofocus.hfov = ease<double, double>::out_sine(autofocus.far_hfov, autofocus.near_hfov, autofocus.zoom);
autofocus.vfov = math::vertical_fov(autofocus.hfov, static_cast<double>(camera.get_aspect_ratio()));
// Calculate focal plane dimensions
autofocus.focal_plane_size.y() = ease<double, double>::out_sine(autofocus.far_focal_plane_height, autofocus.near_focal_plane_height, autofocus.zoom);
autofocus.focal_plane_size.x() = autofocus.focal_plane_size.y() * static_cast<double>(camera.get_aspect_ratio());
// Calculate focal distance
autofocus.focal_distance = autofocus.focal_plane_height * 0.5 / std::tan(autofocus.vfov * 0.5);
math::transform<double> subject_transform = math::transform<double>::identity();
if (orbit_cam.subject_eid != entt::null)
// Update camera projection matrix
camera.set_perspective(static_cast<float>(autofocus.vfov), camera.get_aspect_ratio(), camera.get_clip_near(), camera.get_clip_far());
}
);
*/
auto spring_arm_group = registry.group<spring_arm_component>(entt::get<scene_component>);
std::for_each
(
std::execution::seq,
spring_arm_group.begin(),
spring_arm_group.end(),
[&](auto entity_id)
{
auto& spring_arm = spring_arm_group.get<spring_arm_component>(entity_id);
auto& camera = static_cast<scene::camera&>(*spring_arm_group.get<scene_component>(entity_id).object);
math::transform<double> parent_transform = math::transform<double>::identity();
if (spring_arm.parent_eid != entt::null)
{ {
const auto subject_scene = registry.try_get<scene_component>(orbit_cam.subject_eid);
if (subject_scene)
const auto parent_scene = registry.try_get<scene_component>(spring_arm.parent_eid);
if (parent_scene)
{ {
subject_transform.translation = math::dvec3(subject_scene->object->get_translation());
subject_transform.rotation = math::dquat(subject_scene->object->get_rotation());
parent_transform.translation = math::dvec3(parent_scene->object->get_translation());
parent_transform.rotation = math::dquat(parent_scene->object->get_rotation());
} }
} }
// Calculate focal point // Calculate focal point
const auto focal_point = subject_transform * orbit_cam.focal_point;
spring_arm.focal_point_spring.set_target_value(parent_transform * spring_arm.focal_point_offset);
// Integrate angular velocities
spring_arm.angles_spring.set_target_value(spring_arm.angles_spring.get_target_value() + spring_arm.angular_velocities * variable_timestep);
// Apply angular constraints
spring_arm.angles_spring.set_target_value(math::clamp(spring_arm.angles_spring.get_target_value(), spring_arm.min_angles, spring_arm.max_angles));
// Solve springs
spring_arm.focal_point_spring.solve(variable_timestep);
spring_arm.angles_spring.solve(variable_timestep);
// Recalculate zoom
// if (spring_arm.pitch_velocity)
{
spring_arm.zoom = ease<double, double>::in_sine(1.0, 0.0, spring_arm.angles_spring.get_value().x() / -math::half_pi<double>);
}
// Clamp zoom // Clamp zoom
orbit_cam.zoom = std::min<double>(std::max<double>(orbit_cam.zoom, 0.0), 1.0);
spring_arm.zoom = std::min<double>(std::max<double>(spring_arm.zoom, 0.0), 1.0);
// Update FoV // Update FoV
orbit_cam.hfov = ease<double, double>::out_sine(orbit_cam.far_hfov, orbit_cam.near_hfov, orbit_cam.zoom);
orbit_cam.vfov = math::vertical_fov(orbit_cam.hfov, static_cast<double>(camera.get_aspect_ratio()));
spring_arm.hfov = ease<double, double>::out_sine(spring_arm.far_hfov, spring_arm.near_hfov, spring_arm.zoom);
spring_arm.vfov = math::vertical_fov(spring_arm.hfov, static_cast<double>(camera.get_aspect_ratio()));
// Update focal plane size // Update focal plane size
orbit_cam.focal_plane_height = ease<double, double>::out_sine(orbit_cam.far_focal_plane_height, orbit_cam.near_focal_plane_height, orbit_cam.zoom);
orbit_cam.focal_plane_width = orbit_cam.focal_plane_height * static_cast<double>(camera.get_aspect_ratio());
spring_arm.focal_plane_height = ease<double, double>::out_sine(spring_arm.far_focal_plane_height, spring_arm.near_focal_plane_height, spring_arm.zoom);
spring_arm.focal_plane_width = spring_arm.focal_plane_height * static_cast<double>(camera.get_aspect_ratio());
// Update focal distance // Update focal distance
orbit_cam.focal_distance = orbit_cam.focal_plane_height * 0.5 / std::tan(orbit_cam.vfov * 0.5);
spring_arm.focal_distance = spring_arm.focal_plane_height * 0.5 / std::tan(spring_arm.vfov * 0.5);
const auto camera_up = orbit_cam.up_rotation * math::dvec3{0, 1, 0};
const auto subject_up = subject_transform.rotation * math::dvec3{0, 1, 0};
orbit_cam.up_rotation = math::normalize(math::rotation(camera_up, subject_up) * orbit_cam.up_rotation);
const auto camera_up = spring_arm.up_rotation * math::dvec3{0, 1, 0};
const auto parent_up = parent_transform.rotation * math::dvec3{0, 1, 0};
spring_arm.up_rotation = math::normalize(math::rotation(camera_up, parent_up) * spring_arm.up_rotation);
// Update orientation
orbit_cam.yaw_rotation = math::angle_axis(orbit_cam.yaw, {0.0, 1.0, 0.0});
orbit_cam.pitch_rotation = math::angle_axis(orbit_cam.pitch, {-1.0, 0.0, 0.0});
orbit_cam.orientation = math::normalize(orbit_cam.up_rotation * math::normalize(orbit_cam.yaw_rotation * orbit_cam.pitch_rotation));
// orbit_cam.orientation = math::normalize(subject_transform.rotation * math::normalize(orbit_cam.yaw_rotation * orbit_cam.pitch_rotation));
// orbit_cam.orientation = math::normalize(math::normalize(orbit_cam.yaw_rotation * orbit_cam.pitch_rotation));
// Update camera rotation
spring_arm.camera_rotation = math::normalize(spring_arm.up_rotation * math::euler_xyz_to_quat(spring_arm.angles_spring.get_value()));
// Update transform // Update transform
const auto camera_translation = focal_point + orbit_cam.orientation * math::dvec3{0.0f, 0.0f, orbit_cam.focal_distance};
const auto camera_translation = spring_arm.focal_point_spring.get_value() + spring_arm.camera_rotation * math::dvec3{0.0f, 0.0f, spring_arm.focal_distance};
math::transform<float> camera_transform; math::transform<float> camera_transform;
camera_transform.translation = math::fvec3(camera_translation); camera_transform.translation = math::fvec3(camera_translation);
camera_transform.rotation = math::fquat(orbit_cam.orientation);
camera_transform.rotation = math::fquat(spring_arm.camera_rotation);
camera_transform.scale = {1, 1, 1}; camera_transform.scale = {1, 1, 1};
// double center_offset = (1.0 - std::abs(orbit_cam.pitch) / math::half_pi<double>) * (orbit_cam.focal_plane_height / 3.0 * 0.5);
// camera_transform.translation += math::fvec3(orbit_cam.orientation * math::dvec3{0, center_offset, 0});
double center_offset = (1.0 - std::abs(spring_arm.angles_spring.get_value().x()) / math::half_pi<double>) * (spring_arm.focal_plane_height / 3.0 * 0.5);
camera_transform.translation += math::fvec3(spring_arm.camera_rotation * math::dvec3{0, center_offset, 0});
camera.set_transform(camera_transform); camera.set_transform(camera_transform);
camera.set_perspective(static_cast<float>(orbit_cam.vfov), camera.get_aspect_ratio(), camera.get_clip_near(), camera.get_clip_far());
camera.set_perspective(static_cast<float>(spring_arm.vfov), camera.get_aspect_ratio(), camera.get_clip_near(), camera.get_clip_far());
} }
); );
} }

+ 3
- 0
src/game/systems/camera-system.hpp View File

@ -35,6 +35,9 @@ public:
private: private:
math::dvec4 m_viewport{}; math::dvec4 m_viewport{};
double m_aspect_ratio{}; double m_aspect_ratio{};
double m_fixed_update_time{};
double m_fixed_timestep{};
double m_variable_update_time{};
}; };
#endif // ANTKEEPER_GAME_CAMERA_SYSTEM_HPP #endif // ANTKEEPER_GAME_CAMERA_SYSTEM_HPP

+ 12
- 12
src/game/systems/constraint-system.cpp View File

@ -271,12 +271,12 @@ void constraint_system::handle_pivot_constraint(transform_component& transform,
void constraint_system::handle_spring_rotation_constraint(transform_component& transform, spring_rotation_constraint& constraint, float dt) void constraint_system::handle_spring_rotation_constraint(transform_component& transform, spring_rotation_constraint& constraint, float dt)
{ {
// Solve yaw, pitch, and roll angle spring // Solve yaw, pitch, and roll angle spring
solve_numeric_spring<math::fvec3, float>(constraint.spring, dt);
constraint.spring.solve(dt);
// Build yaw, pitch, and roll quaternions // Build yaw, pitch, and roll quaternions
const math::fquat yaw = math::angle_axis(constraint.spring.x0[0], {0.0f, 1.0f, 0.0f});
const math::fquat pitch = math::angle_axis(constraint.spring.x0[1], {-1.0f, 0.0f, 0.0f});
const math::fquat roll = math::angle_axis(constraint.spring.x0[2], {0.0f, 0.0f, -1.0f});
const math::fquat yaw = math::angle_axis(constraint.spring.get_value()[0], {0.0f, 1.0f, 0.0f});
const math::fquat pitch = math::angle_axis(constraint.spring.get_value()[1], {-1.0f, 0.0f, 0.0f});
const math::fquat roll = math::angle_axis(constraint.spring.get_value()[2], {0.0f, 0.0f, -1.0f});
// Update transform rotation // Update transform rotation
transform.world.rotation = math::normalize(yaw * pitch * roll); transform.world.rotation = math::normalize(yaw * pitch * roll);
@ -293,26 +293,26 @@ void constraint_system::handle_spring_to_constraint(transform_component& transfo
if (constraint.spring_translation) if (constraint.spring_translation)
{ {
// Update translation spring target // Update translation spring target
constraint.translation.x1 = target_transform->world.translation;
constraint.translation.set_target_value(target_transform->world.translation);
// Solve translation spring // Solve translation spring
solve_numeric_spring<math::fvec3, float>(constraint.translation, dt);
constraint.translation.solve(dt);
// Update transform translation // Update transform translation
transform.world.translation = constraint.translation.x0;
transform.world.translation = constraint.translation.get_value();
} }
// Spring rotation // Spring rotation
if (constraint.spring_rotation) if (constraint.spring_rotation)
{ {
// Update rotation spring target // Update rotation spring target
constraint.rotation.x1 = math::fvec4(target_transform->world.rotation);
constraint.rotation.set_target_value(math::fvec4(target_transform->world.rotation));
// Solve rotation spring // Solve rotation spring
solve_numeric_spring<math::fvec4, float>(constraint.rotation, dt);
constraint.rotation.solve(dt);
// Update transform rotation // Update transform rotation
transform.world.rotation = math::normalize(math::fquat{constraint.rotation.x0[0], constraint.rotation.x0[1], constraint.rotation.x0[2], constraint.rotation.x0[3]});
transform.world.rotation = math::normalize(math::fquat{constraint.rotation.get_value()[0], constraint.rotation.get_value()[1], constraint.rotation.get_value()[2], constraint.rotation.get_value()[3]});
} }
} }
} }
@ -321,10 +321,10 @@ void constraint_system::handle_spring_to_constraint(transform_component& transfo
void constraint_system::handle_spring_translation_constraint(transform_component& transform, spring_translation_constraint& constraint, float dt) void constraint_system::handle_spring_translation_constraint(transform_component& transform, spring_translation_constraint& constraint, float dt)
{ {
// Solve translation spring // Solve translation spring
solve_numeric_spring<math::fvec3, float>(constraint.spring, dt);
constraint.spring.solve(dt);
// Update transform translation // Update transform translation
transform.world.translation = constraint.spring.x0;
transform.world.translation = constraint.spring.get_value();
} }
void constraint_system::handle_three_dof_constraint(transform_component& transform, const three_dof_constraint& constraint) void constraint_system::handle_three_dof_constraint(transform_component& transform, const three_dof_constraint& constraint)

+ 23
- 5
src/game/systems/locomotion-system.cpp View File

@ -27,6 +27,7 @@
#include <engine/animation/skeleton.hpp> #include <engine/animation/skeleton.hpp>
#include <engine/debug/log.hpp> #include <engine/debug/log.hpp>
#include <engine/math/fract.hpp> #include <engine/math/fract.hpp>
#include <engine/math/angles.hpp>
#include <engine/ai/navmesh.hpp> #include <engine/ai/navmesh.hpp>
#include <algorithm> #include <algorithm>
#include <execution> #include <execution>
@ -53,17 +54,34 @@ void locomotion_system::update_legged(float t, float dt)
{ {
auto& locomotion = legged_group.get<legged_locomotion_component>(entity_id); auto& locomotion = legged_group.get<legged_locomotion_component>(entity_id);
if (locomotion.angular_velocity != 0.0f)
float cos_target_direction{};
if (locomotion.speed != 0.0f)
{ {
auto& rigid_body = *legged_group.get<rigid_body_component>(entity_id).body; auto& rigid_body = *legged_group.get<rigid_body_component>(entity_id).body;
const auto up = rigid_body.get_orientation() * math::fvec3{0, 1, 0};
const auto rotation = math::angle_axis(locomotion.angular_velocity * dt, up);
rigid_body.set_orientation(math::normalize(rotation * rigid_body.get_orientation()));
const auto max_steering_angle = locomotion.max_angular_frequency * dt;
const auto current_direction = rigid_body.get_orientation() * math::fvec3{0, 0, 1};
math::fquat steering_rotation;
cos_target_direction = math::dot(current_direction, locomotion.target_direction);
if (cos_target_direction < -0.999f)
{
steering_rotation = math::angle_axis(max_steering_angle, rigid_body.get_orientation() * math::fvec3{0, 1, 0});
}
else
{
steering_rotation = math::rotate_towards(current_direction, locomotion.target_direction, max_steering_angle);
}
rigid_body.set_orientation(math::normalize(steering_rotation * rigid_body.get_orientation()));
} }
// Traverse navmesh // Traverse navmesh
auto& navmesh_agent = legged_group.get<navmesh_agent_component>(entity_id); auto& navmesh_agent = legged_group.get<navmesh_agent_component>(entity_id);
if (locomotion.speed != 0.0f && navmesh_agent.face)
if (locomotion.speed != 0.0f/* && cos_target_direction >= 0.0f*/ && navmesh_agent.face)
{ {
// Get rigid body // Get rigid body
auto& rigid_body = *legged_group.get<rigid_body_component>(entity_id).body; auto& rigid_body = *legged_group.get<rigid_body_component>(entity_id).body;

+ 49
- 0
src/game/systems/metabolic-system.cpp View File

@ -0,0 +1,49 @@
/*
* 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/systems/metabolic-system.hpp"
#include "game/components/isometric-growth-component.hpp"
#include "game/components/rigid-body-component.hpp"
#include <execution>
metabolic_system::metabolic_system(entity::registry& registry):
updatable_system(registry)
{}
void metabolic_system::update(float t, float dt)
{
// Scale timestep
const auto scaled_timestep = dt * m_time_scale;
// Handle isometric growth
auto isometric_growth_group = registry.group<isometric_growth_component>(entt::get<rigid_body_component>);
std::for_each
(
std::execution::seq,
isometric_growth_group.begin(),
isometric_growth_group.end(),
[&](auto entity_id)
{
auto& growth = isometric_growth_group.get<isometric_growth_component>(entity_id);
auto& rigid_body = *isometric_growth_group.get<rigid_body_component>(entity_id).body;
rigid_body.set_scale(rigid_body.get_scale() + growth.rate * scaled_timestep);
}
);
}

+ 49
- 0
src/game/systems/metabolic-system.hpp View File

@ -0,0 +1,49 @@
/*
* 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_METABOLIC_SYSTEM_HPP
#define ANTKEEPER_GAME_METABOLIC_SYSTEM_HPP
#include "game/systems/updatable-system.hpp"
/**
*
*/
class metabolic_system:
public updatable_system
{
public:
explicit metabolic_system(entity::registry& registry);
virtual void update(float t, float dt);
/**
* Sets the factor by which the timestep `dt` will be scaled.
*
* @param scale Factor by which to scale the timestep.
*/
inline constexpr void set_time_scale(float scale) noexcept
{
m_time_scale = scale;
}
private:
float m_time_scale{1.0f};
};
#endif // ANTKEEPER_GAME_METABOLIC_SYSTEM_HPP

+ 146
- 3
src/game/systems/metamorphosis-system.cpp View File

@ -19,8 +19,13 @@
#include "game/systems/metamorphosis-system.hpp" #include "game/systems/metamorphosis-system.hpp"
#include "game/components/egg-component.hpp" #include "game/components/egg-component.hpp"
#include "game/components/larva-component.hpp"
#include "game/components/pupa-component.hpp"
#include "game/components/isometric-growth-component.hpp"
#include "game/components/rigid-body-component.hpp"
#include "game/components/scene-component.hpp" #include "game/components/scene-component.hpp"
#include "game/components/ant-genome-component.hpp" #include "game/components/ant-genome-component.hpp"
#include <engine/scene/static-mesh.hpp>
#include <engine/scene/skeletal-mesh.hpp> #include <engine/scene/skeletal-mesh.hpp>
#include <engine/debug/log.hpp> #include <engine/debug/log.hpp>
#include <execution> #include <execution>
@ -31,6 +36,10 @@ metamorphosis_system::metamorphosis_system(entity::registry& registry):
void metamorphosis_system::update(float t, float dt) void metamorphosis_system::update(float t, float dt)
{ {
// Scale timestep
const auto scaled_timestep = dt * m_time_scale;
// Incubate eggs
auto egg_group = registry.group<egg_component>(entt::get<ant_genome_component>); auto egg_group = registry.group<egg_component>(entt::get<ant_genome_component>);
std::for_each std::for_each
( (
@ -40,19 +49,153 @@ void metamorphosis_system::update(float t, float dt)
[&](auto entity_id) [&](auto entity_id)
{ {
auto& egg = egg_group.get<egg_component>(entity_id); auto& egg = egg_group.get<egg_component>(entity_id);
if (egg.elapsed_incubation_time >= egg.incubation_period)
if (egg.incubation_phase >= 1.0f)
{ {
return; return;
} }
egg.elapsed_incubation_time += dt * m_time_scale;
if (egg.elapsed_incubation_time >= egg.incubation_period)
// Advance incubation phase
egg.incubation_phase += scaled_timestep / egg.incubation_period;
// If incubation complete
if (egg.incubation_phase >= 1.0f)
{ {
const auto& genome = *egg_group.get<ant_genome_component>(entity_id).genome; const auto& genome = *egg_group.get<ant_genome_component>(entity_id).genome;
const auto layer_mask = registry.get<scene_component>(entity_id).layer_mask; const auto layer_mask = registry.get<scene_component>(entity_id).layer_mask;
auto& rigid_body = *registry.get<rigid_body_component>(entity_id).body;
// Calculate scales of first and final instars
const auto egg_scale = rigid_body.get_scale().x();
const auto first_instar_scale = egg_scale * genome.larva->phenes.front().first_instar_scale;
const auto final_instar_scale = egg_scale;
// Calculate larval growth rate
const auto growth_rate = (final_instar_scale - first_instar_scale) / genome.larva->phenes.front().development_period;
// Erase egg component
registry.erase<egg_component>(entity_id);
// Replace egg model with larva model
registry.erase<scene_component>(entity_id); registry.erase<scene_component>(entity_id);
registry.emplace<scene_component>(entity_id, std::make_shared<scene::skeletal_mesh>(genome.larva->phenes.front().model), layer_mask); registry.emplace<scene_component>(entity_id, std::make_shared<scene::skeletal_mesh>(genome.larva->phenes.front().model), layer_mask);
// Init larva scale
rigid_body.set_scale(first_instar_scale);
rigid_body.set_previous_scale(first_instar_scale);
// Define larval development period
larva_component larva;
larva.development_period = genome.larva->phenes.front().development_period;
larva.spinning_period = genome.larva->phenes.front().spinning_period;
registry.emplace<larva_component>(entity_id, std::move(larva));
// Begin isometric growth
registry.emplace<isometric_growth_component>(entity_id, growth_rate);
}
}
);
// Develop larvae
auto larva_group = registry.group<larva_component>(entt::get<ant_genome_component>);
std::for_each
(
std::execution::seq,
larva_group.begin(),
larva_group.end(),
[&](auto entity_id)
{
auto& larva = larva_group.get<larva_component>(entity_id);
if (larva.development_phase < 1.0f)
{
// Advance larval development phase
larva.development_phase += scaled_timestep / larva.development_period;
// If larval development complete
if (larva.development_phase >= 1.0f)
{
const auto& rigid_body = *registry.get<rigid_body_component>(entity_id).body;
const auto& genome = *larva_group.get<ant_genome_component>(entity_id).genome;
const auto layer_mask = registry.get<scene_component>(entity_id).layer_mask;
// Halt isometric growth
registry.remove<isometric_growth_component>(entity_id);
// Construct cocoon mesh
auto cocoon_mesh = std::make_shared<scene::static_mesh>(genome.pupa->phenes.front().cocoon_model);
cocoon_mesh->set_transform(rigid_body.get_transform());
// Construct copy of cocoon material
auto cocoon_material = std::make_shared<render::material>(*cocoon_mesh->get_model()->get_groups().front().material);
// Store cocoon material spinning phase variable
larva.spinning_phase_matvar = std::static_pointer_cast<render::matvar_float>(cocoon_material->get_variable("spinning_phase"));
larva.spinning_phase_matvar->set(0.0f);
// Replace cocoon mesh material
cocoon_mesh->set_material(0, std::move(cocoon_material));
// Construct cocoon entity
larva.cocoon_eid = registry.create();
registry.emplace<scene_component>(larva.cocoon_eid, std::move(cocoon_mesh), layer_mask);
}
}
else if (larva.spinning_phase < 1.0f)
{
const auto& genome = *larva_group.get<ant_genome_component>(entity_id).genome;
// Advance cocoon-spinning phase
larva.spinning_phase += scaled_timestep / larva.spinning_period;
// Update spinning phase material variable
larva.spinning_phase_matvar->set(larva.spinning_phase);
// If cocoon-spinning complete
if (larva.spinning_phase >= 1.0f)
{
// Erase larva component
registry.erase<larva_component>(entity_id);
// Erase scene component
registry.erase<scene_component>(entity_id);
// Define pupal development period
pupa_component pupa;
pupa.development_period = genome.pupa->phenes.front().development_period;
registry.emplace<pupa_component>(entity_id, std::move(pupa));
}
}
}
);
// Develop pupae
auto pupa_group = registry.group<pupa_component>(entt::get<ant_genome_component>);
std::for_each
(
std::execution::seq,
pupa_group.begin(),
pupa_group.end(),
[&](auto entity_id)
{
auto& pupa = pupa_group.get<pupa_component>(entity_id);
if (pupa.development_phase >= 1.0f)
{
return;
}
// Advance pupal development phase
pupa.development_phase += scaled_timestep / pupa.development_period;
// If pupal development complete
if (pupa.development_phase >= 1.0f)
{
const auto& genome = *pupa_group.get<ant_genome_component>(entity_id).genome;
// Erase pupa component
registry.erase<pupa_component>(entity_id);
// Construct adult model
// registry.emplace<scene_component>(entity_id, std::make_shared<scene::skeletal_mesh>(ant_model), layer_mask);
} }
} }
); );

+ 63
- 14
src/game/systems/physics-system.cpp View File

@ -29,6 +29,7 @@
#include <engine/physics/kinematics/colliders/sphere-collider.hpp> #include <engine/physics/kinematics/colliders/sphere-collider.hpp>
#include <engine/physics/kinematics/colliders/box-collider.hpp> #include <engine/physics/kinematics/colliders/box-collider.hpp>
#include <engine/physics/kinematics/colliders/capsule-collider.hpp> #include <engine/physics/kinematics/colliders/capsule-collider.hpp>
#include <engine/physics/kinematics/colliders/mesh-collider.hpp>
#include <engine/geom/closest-point.hpp> #include <engine/geom/closest-point.hpp>
#include <execution> #include <execution>
@ -108,23 +109,71 @@ void physics_system::interpolate(float alpha)
); );
} }
void physics_system::integrate(float dt)
std::optional<std::tuple<entity::id, float, std::uint32_t, math::fvec3>> physics_system::trace(const geom::ray<float, 3>& ray, entity::id ignore_eid, std::uint32_t layer_mask) const
{ {
// Drag
/*
const float air_density = 1.293f;// * 100.0f;
const float radius = 1.0f;
const float sphere_cross_section_area = radius * radius * math::pi<float>;
const float sphere_drag_coef = 0.47f;
const float sqr_speed = math::sqr_length(body.linear_velocity);
if (sqr_speed)
entity::id nearest_entity_id = entt::null;
float nearest_hit_distance = std::numeric_limits<float>::infinity();
std::uint32_t nearest_face_index = 0;
math::fvec3 nearest_hit_normal;
// For each entity with a rigid body
auto rigid_body_view = registry.view<rigid_body_component>();
for (const auto entity_id: rigid_body_view)
{ {
const float drag_magnitude = -0.5f * air_density * sqr_speed * sphere_drag_coef * sphere_cross_section_area;
const math::fvec3 drag_force = math::normalize(body.linear_velocity) * drag_magnitude;
body.apply_central_force(drag_force);
if (entity_id == ignore_eid)
{
// Ignore a specific entity
continue;
}
// Get rigid body and collider of the entity
const auto& rigid_body = *rigid_body_view.get<rigid_body_component>(entity_id).body;
const auto& collider = rigid_body.get_collider();
if (!collider)
{
// Ignore rigid bodies without colliders
continue;
}
if (!(collider->get_layer_mask() & layer_mask))
{
// Ignore rigid bodies without a common collision layer
continue;
}
// Transform ray into rigid body space
const auto inv_transform = math::inverse(rigid_body.get_transform());
geom::ray<float, 3> bs_ray;
bs_ray.origin = inv_transform * ray.origin;
bs_ray.direction = inv_transform.rotation * ray.direction;
if (collider->type() == physics::collider_type::mesh)
{
const auto& mesh = static_cast<const physics::mesh_collider&>(*collider);
if (auto intersection = mesh.intersection(bs_ray))
{
if (std::get<0>(*intersection) < nearest_hit_distance)
{
nearest_entity_id = entity_id;
/// @TODO: doesn't take into account rigid body scale
std::tie(nearest_hit_distance, nearest_face_index, nearest_hit_normal) = *intersection;
}
}
}
} }
*/
if (nearest_entity_id == entt::null)
{
return std::nullopt;
}
return std::make_tuple(nearest_entity_id, nearest_hit_distance, nearest_face_index, nearest_hit_normal);
}
void physics_system::integrate(float dt)
{
auto view = registry.view<rigid_body_component>(); auto view = registry.view<rigid_body_component>();
std::for_each std::for_each
( (
@ -136,7 +185,7 @@ void physics_system::integrate(float dt)
auto& body = *(view.get<rigid_body_component>(entity_id).body); auto& body = *(view.get<rigid_body_component>(entity_id).body);
// Apply gravity // Apply gravity
body.apply_central_force(gravity / 10.0f * body.get_mass());
//body.apply_central_force(gravity / 10.0f * body.get_mass());
body.integrate(dt); body.integrate(dt);
} }

+ 15
- 0
src/game/systems/physics-system.hpp View File

@ -23,10 +23,14 @@
#include "game/systems/updatable-system.hpp" #include "game/systems/updatable-system.hpp"
#include <entt/entt.hpp> #include <entt/entt.hpp>
#include <engine/math/vector.hpp> #include <engine/math/vector.hpp>
#include <engine/entity/id.hpp>
#include <engine/physics/kinematics/rigid-body.hpp> #include <engine/physics/kinematics/rigid-body.hpp>
#include <engine/physics/kinematics/collision-manifold.hpp> #include <engine/physics/kinematics/collision-manifold.hpp>
#include <engine/geom/primitives/ray.hpp>
#include <array> #include <array>
#include <functional> #include <functional>
#include <optional>
#include <tuple>
/** /**
* *
@ -50,6 +54,17 @@ public:
this->gravity = gravity; this->gravity = gravity;
} }
/**
* Traces a ray to the nearest point of intersection.
*
* @param ray World-spce ray.
* @param ignore_eid Entity ID with which to ignore intersection.
* @param layer_mask Mask of collision layers with which the ray can intersect.
*
* @return Tuple containing the ID of the nearest intersecting entity, distance along the ray to the point of intersection, index of the hit face, and surface normal at the point of intersection; or std::nullopt if no intersection occurred.
*/
[[nodiscard]] std::optional<std::tuple<entity::id, float, std::uint32_t, math::fvec3>> trace(const geom::ray<float, 3>& ray, entity::id ignore_eid = entt::null, std::uint32_t layer_mask = ~std::uint32_t{0}) const;
private: private:
using collision_manifold_type = physics::collision_manifold<4>; using collision_manifold_type = physics::collision_manifold<4>;

+ 22
- 10
src/game/systems/reproductive-system.cpp View File

@ -24,6 +24,7 @@
#include "game/components/rigid-body-component.hpp" #include "game/components/rigid-body-component.hpp"
#include "game/components/egg-component.hpp" #include "game/components/egg-component.hpp"
#include "game/components/ant-genome-component.hpp" #include "game/components/ant-genome-component.hpp"
#include "game/systems/physics-system.hpp"
#include <engine/math/fract.hpp> #include <engine/math/fract.hpp>
#include <engine/math/interpolation.hpp> #include <engine/math/interpolation.hpp>
#include <engine/scene/static-mesh.hpp> #include <engine/scene/static-mesh.hpp>
@ -115,16 +116,27 @@ void reproductive_system::update(float t, float dt)
if (ovary.elapsed_oviposition_time >= ovary.oviposition_duration) if (ovary.elapsed_oviposition_time >= ovary.oviposition_duration)
{ {
// Construct egg component
egg_component egg;
egg.incubation_period = 5.0f;
registry.emplace<egg_component>(ovary.ovipositor_egg_eid, std::move(egg));
// Oviposition complete
ovary.ovipositing = false;
ovary.elapsed_oviposition_time = 0.0f;
--ovary.egg_count;
ovary.ovipositor_egg_eid = entt::null;
// Place egg
auto& egg_rigid_body = *registry.get<rigid_body_component>(ovary.ovipositor_egg_eid).body;
const auto oviposition_ray = geom::ray<float, 3>{egg_transform.translation, egg_transform.rotation * math::fvec3{0, 0, -1}};
if (auto trace = m_physics_system->trace(oviposition_ray, ovary.ovipositor_egg_eid, ~std::uint32_t{0}))
{
egg_transform.translation = oviposition_ray.extrapolate(std::get<1>(*trace));
egg_transform.rotation = math::normalize(math::rotation(egg_transform.rotation * math::fvec3{0, 1, 0}, std::get<3>(*trace)) * egg_transform.rotation);
egg_rigid_body.set_transform(egg_transform);
// Get genome of egg
const auto& genome = *registry.get<ant_genome_component>(ovary.ovipositor_egg_eid).genome;
// Construct egg component
registry.emplace<egg_component>(ovary.ovipositor_egg_eid, genome.egg->phenes.front().incubation_period, 0.0f);
// Oviposition complete
ovary.ovipositing = false;
ovary.elapsed_oviposition_time = 0.0f;
--ovary.egg_count;
ovary.ovipositor_egg_eid = entt::null;
}
} }
} }
} }

+ 8
- 0
src/game/systems/reproductive-system.hpp View File

@ -22,6 +22,8 @@
#include "game/systems/updatable-system.hpp" #include "game/systems/updatable-system.hpp"
class physics_system;
/** /**
* *
*/ */
@ -42,8 +44,14 @@ public:
m_time_scale = scale; m_time_scale = scale;
} }
inline constexpr void set_physics_system(physics_system* physics_system) noexcept
{
m_physics_system = physics_system;
}
private: private:
float m_time_scale{1.0f}; float m_time_scale{1.0f};
physics_system* m_physics_system{};
}; };
#endif // ANTKEEPER_GAME_REPRODUCTIVE_SYSTEM_HPP #endif // ANTKEEPER_GAME_REPRODUCTIVE_SYSTEM_HPP

+ 0
- 74
src/game/systems/spring-system.cpp View File

@ -1,74 +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/systems/spring-system.hpp"
#include "game/components/spring-component.hpp"
#include <engine/entity/id.hpp>
spring_system::spring_system(entity::registry& registry):
updatable_system(registry)
{}
spring_system::~spring_system()
{}
void spring_system::update(float t, float dt)
{
registry.view<spring1_component>().each
(
[&](entity::id spring_eid, auto& component)
{
solve_numeric_spring<float, float>(component.spring, dt);
if (component.callback)
component.callback(component.spring.x0);
}
);
registry.view<spring2_component>().each
(
[&](entity::id spring_eid, auto& component)
{
solve_numeric_spring<math::fvec2, float>(component.spring, dt);
if (component.callback)
component.callback(component.spring.x0);
}
);
registry.view<spring3_component>().each
(
[&](entity::id spring_eid, auto& component)
{
solve_numeric_spring<math::fvec3, float>(component.spring, dt);
if (component.callback)
component.callback(component.spring.x0);
}
);
registry.view<spring4_component>().each
(
[&](entity::id spring_eid, auto& component)
{
solve_numeric_spring<math::fvec4, float>(component.spring, dt);
if (component.callback)
component.callback(component.spring.x0);
}
);
}

+ 101
- 0
src/game/textures/cocoon-silk-sdf.cpp View File

@ -0,0 +1,101 @@
/*
* 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/textures/cocoon-silk-sdf.hpp"
#include <engine/math/noise/noise.hpp>
#include <engine/utility/image.hpp>
#include <engine/debug/log.hpp>
#include <algorithm>
#include <execution>
#include <fstream>
#include <stb/stb_image_write.h>
void generate_cocoon_silk_sdf(std::filesystem::path path)
{
debug::log::info("Generating cocoon silk SDF image...");
image img;
img.format(1, 4);
img.resize(2048, 2048);
auto width = img.width();
auto height = img.height();
unsigned char* pixels = (unsigned char*)img.data();
const float frequency = 100.0f;
float scale_x = 1.0f / static_cast<float>(width - 1) * frequency;
float scale_y = 1.0f / static_cast<float>(height - 1) * frequency;
std::for_each
(
std::execution::par_unseq,
img.begin<math::vec4<unsigned char>>(),
img.end<math::vec4<unsigned char>>(),
[pixels, width, height, scale_x, scale_y, frequency](auto& pixel)
{
const std::size_t i = &pixel - (math::vec4<unsigned char>*)pixels;
const std::size_t y = i / width;
const std::size_t x = i % width;
const math::fvec2 position =
{
static_cast<float>(x) * scale_x,
static_cast<float>(y) * scale_y
};
const auto
[
f1_sqr_distance,
f1_displacement,
f1_id,
f1_edge_sqr_distance
] = math::noise::voronoi::f1_edge<float, 2>(position, 1.0f, {frequency, frequency});
const float f1_edge_distance = std::sqrt(f1_edge_sqr_distance);
const float scale = 255.0f * (255.0f / 204.0f);
pixel =
{
static_cast<unsigned char>(std::min(255.0f, f1_edge_distance * scale)),
static_cast<unsigned char>(std::min(255.0f, f1_edge_distance * scale)),
static_cast<unsigned char>(std::min(255.0f, f1_edge_distance * scale)),
255
};
// const float f1_distance = std::sqrt(f1_sqr_distance);
// const math::fvec2 uv = (position + f1_displacement) / frequency;
// pixel =
// {
// static_cast<unsigned char>(std::min(255.0f, f1_distance * 255.0f)),
// static_cast<unsigned char>(std::min(255.0f, uv[0] * 255.0f)),
// static_cast<unsigned char>(std::min(255.0f, uv[1] * 255.0f)),
// static_cast<unsigned char>(f1_id % 256)
// };
}
);
debug::log::info("Generated cocoon silk SDF image");
debug::log::info("Saving cocoon silk SDF image to \"{}\"...", path.string());
stbi_flip_vertically_on_write(1);
stbi_write_png(path.string().c_str(), img.width(), img.height(), img.channel_count(), img.data(), img.width() * img.channel_count());
debug::log::info("Saved cocoon silk SDF image to \"{}\"", path.string());
}

+ 28
- 0
src/game/textures/cocoon-silk-sdf.hpp View File

@ -0,0 +1,28 @@
/*
* 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_COCOON_SILK_SDF_HPP
#define ANTKEEPER_COCOON_SILK_SDF_HPP
#include <filesystem>
/// Generates the cocoon silk signed distance field texture.
void generate_silk_sdf_image(std::filesystem::path path);
#endif // ANTKEEPER_COCOON_SILK_SDF_HPP

+ 94
- 0
src/game/textures/rgb-voronoi-noise.cpp View File

@ -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/>.
*/
#include "game/textures/rgb-voronoi-noise.hpp"
#include <engine/math/noise/noise.hpp>
#include <engine/utility/image.hpp>
#include <engine/debug/log.hpp>
#include <algorithm>
#include <execution>
#include <fstream>
#include <stb/stb_image_write.h>
void generate_rgb_voronoi_noise(std::filesystem::path path)
{
image img;
img.format(1, 4);
img.resize(1024, 1024);
auto width = img.width();
auto height = img.height();
unsigned char* pixels = (unsigned char*)img.data();
const float frequency = 512.0f;
float scale_x = 1.0f / static_cast<float>(width - 1) * frequency;
float scale_y = 1.0f / static_cast<float>(height - 1) * frequency;
std::for_each
(
std::execution::par_unseq,
img.begin<math::vec4<unsigned char>>(),
img.end<math::vec4<unsigned char>>(),
[pixels, width, height, scale_x, scale_y, frequency](auto& pixel)
{
const std::size_t i = &pixel - (math::vec4<unsigned char>*)pixels;
const std::size_t y = i / width;
const std::size_t x = i % width;
const math::fvec2 position =
{
static_cast<float>(x) * scale_x,
static_cast<float>(y) * scale_y
};
const auto
[
f1_sqr_distance,
f1_displacement,
f1_id,
f1_edge_sqr_distance
] = math::noise::voronoi::f1_edge<float, 2>(position, 1.0f, {frequency, frequency});
const float f1_edge_distance = std::sqrt(f1_edge_sqr_distance);
const float scale = 255.0f * (255.0f / 204.0f);
pixel =
{
static_cast<unsigned char>(f1_id & 255),
static_cast<unsigned char>((f1_id >> 8) & 255),
static_cast<unsigned char>((f1_id >> 16) & 255),
static_cast<unsigned char>((f1_id >> 24) & 255)
};
// const float f1_distance = std::sqrt(f1_sqr_distance);
// const math::fvec2 uv = (position + f1_displacement) / frequency;
// pixel =
// {
// static_cast<unsigned char>(std::min(255.0f, f1_distance * 255.0f)),
// static_cast<unsigned char>(std::min(255.0f, uv[0] * 255.0f)),
// static_cast<unsigned char>(std::min(255.0f, uv[1] * 255.0f)),
// static_cast<unsigned char>(f1_id % 256)
// };
}
);
stbi_flip_vertically_on_write(1);
stbi_write_png(path.string().c_str(), img.width(), img.height(), img.channel_count(), img.data(), img.width() * img.channel_count());
}

+ 27
- 0
src/game/textures/rgb-voronoi-noise.hpp View File

@ -0,0 +1,27 @@
/*
* 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_RGB_VORONOI_NOISE_HPP
#define ANTKEEPER_RGB_VORONOI_NOISE_HPP
#include <filesystem>
void generate_rgb_voronoi_noise(std::filesystem::path path);
#endif // ANTKEEPER_RGB_VORONOI_NOISE_HPP

+ 9
- 57
src/game/world.cpp View File

@ -68,6 +68,8 @@
#include <execution> #include <execution>
#include <fstream> #include <fstream>
#include <stb/stb_image_write.h> #include <stb/stb_image_write.h>
#include <engine/animation/screen-transition.hpp>
#include <engine/animation/ease.hpp>
namespace world { namespace world {
@ -443,63 +445,7 @@ void create_moon(::game& ctx)
} }
void enter_ecoregion(::game& ctx, const ecoregion& ecoregion) void enter_ecoregion(::game& ctx, const ecoregion& ecoregion)
{
/*
image img;
img.format(1, 4);
img.resize(2048, 2048);
auto width = img.get_width();
auto height = img.get_height();
unsigned char* pixels = (unsigned char*)img.data();
const float frequency = 400.0f;
float scale_x = 1.0f / static_cast<float>(width - 1) * frequency;
float scale_y = 1.0f / static_cast<float>(height - 1) * frequency;
std::for_each
(
std::execution::par_unseq,
img.begin<math::vec4<unsigned char>>(),
img.end<math::vec4<unsigned char>>(),
[pixels, width, height, scale_x, scale_y, frequency](auto& pixel)
{
const std::size_t i = &pixel - (math::vec4<unsigned char>*)pixels;
const std::size_t y = i / width;
const std::size_t x = i % width;
const math::fvec2 position =
{
static_cast<float>(x) * scale_x,
static_cast<float>(y) * scale_y
};
const auto
[
f1_sqr_distance,
f1_displacement,
f1_id
] = math::noise::voronoi::f1<float, 2>(position, 1.0f, {frequency, frequency});
const float f1_distance = std::sqrt(f1_sqr_distance);
const math::fvec2 uv = (position + f1_displacement) / frequency;
pixel =
{
static_cast<unsigned char>(std::min(255.0f, f1_distance * 255.0f)),
static_cast<unsigned char>(std::min(255.0f, uv[0] * 255.0f)),
static_cast<unsigned char>(std::min(255.0f, uv[1] * 255.0f)),
static_cast<unsigned char>(f1_id % 256)
};
}
);
stbi_flip_vertically_on_write(1);
stbi_write_tga((ctx.screenshots_path / "voronoi-f1-400-nc8-2k.tga").string().c_str(), img.get_width(), img.get_height(), img.get_channel_count(), img.data());
*/
{
debug::log::trace("Entering ecoregion {}...", ecoregion.name); debug::log::trace("Entering ecoregion {}...", ecoregion.name);
{ {
// Set active ecoregion // Set active ecoregion
@ -545,4 +491,10 @@ void enter_ecoregion(::game& ctx, const ecoregion& ecoregion)
debug::log::trace("Entered ecoregion {}", ecoregion.name); debug::log::trace("Entered ecoregion {}", ecoregion.name);
} }
void switch_scene(::game& ctx)
{
ctx.fade_transition_color->set({0, 0, 0});
ctx.fade_transition->transition(1.0f, false, ease<float>::out_cubic, false, [](){});
}
} // namespace world } // namespace world

+ 2
- 0
src/game/world.hpp View File

@ -80,6 +80,8 @@ void set_time_scale(::game& ctx, double scale);
*/ */
void enter_ecoregion(::game& ctx, const ecoregion& ecoregion); void enter_ecoregion(::game& ctx, const ecoregion& ecoregion);
void switch_scene(::game& ctx);
} // namespace menu } // namespace menu
#endif // ANTKEEPER_GAME_WORLD_HPP #endif // ANTKEEPER_GAME_WORLD_HPP

Loading…
Cancel
Save