From 4b7ad311805e2738c192eae97644ef10fc2b134c Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Thu, 24 Aug 2023 12:59:06 +0800 Subject: [PATCH] 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. --- CMakeLists.txt | 1 - src/engine/animation/spring.hpp | 136 ---- src/engine/math/quaternion.hpp | 24 +- src/engine/math/vector.hpp | 133 ++-- src/engine/physics/frequency.hpp | 89 +++ .../physics/kinematics/collider-material.hpp | 19 + .../kinematics/colliders/mesh-collider.cpp | 47 +- .../kinematics/colliders/mesh-collider.hpp | 8 +- src/engine/physics/kinematics/rigid-body.hpp | 44 ++ src/engine/physics/spring.hpp | 201 ++++++ src/engine/utility/frame-scheduler.cpp | 2 +- src/engine/utility/frame-scheduler.hpp | 2 +- src/game/ant/ant-cladogenesis.hpp | 2 +- src/game/ant/ant-gene-pool.hpp | 4 +- src/game/ant/ant-genome.hpp | 4 +- src/game/ant/ant-phenome.cpp | 6 +- src/game/ant/ant-phenome.hpp | 4 +- src/game/ant/genes/ant-cocoon-gene.cpp | 55 -- src/game/ant/genes/ant-egg-gene.cpp | 3 + src/game/ant/genes/ant-egg-gene.hpp | 6 + src/game/ant/genes/ant-gene-type.hpp | 2 +- src/game/ant/genes/ant-larva-gene.cpp | 3 + src/game/ant/genes/ant-larva-gene.hpp | 11 +- src/game/ant/genes/ant-legs-gene.cpp | 1 + src/game/ant/genes/ant-legs-gene.hpp | 3 + src/game/ant/genes/ant-pupa-gene.cpp | 58 ++ ...{ant-cocoon-gene.hpp => ant-pupa-gene.hpp} | 28 +- src/game/components/autofocus-component.hpp | 60 ++ .../decay-component.hpp} | 22 +- src/game/components/egg-component.hpp | 14 +- src/game/components/larva-component.hpp | 50 ++ .../legged-locomotion-component.hpp | 5 + src/game/components/metabolism-component.hpp | 32 + src/game/components/pupa-component.hpp | 44 ++ ...component.hpp => spring-arm-component.hpp} | 66 +- src/game/components/spring-component.hpp | 77 --- .../spring-rotation-constraint.hpp | 4 +- src/game/constraints/spring-to-constraint.hpp | 6 +- .../spring-translation-constraint.hpp | 4 +- src/game/controls.cpp | 22 +- src/game/controls/ant-controls.cpp | 92 ++- src/game/controls/camera-controls.cpp | 192 +++++- src/game/ecoregion-loader.cpp | 18 +- src/game/game.cpp | 25 +- src/game/game.hpp | 26 +- .../treadmill-experiment-state.cpp | 223 +++--- .../treadmill-experiment-state.hpp | 2 - src/game/states/main-menu-state.cpp | 4 +- src/game/states/nest-selection-state.cpp | 240 +------ src/game/states/nest-view-state.cpp | 3 +- src/game/states/nuptial-flight-state.cpp | 638 +----------------- src/game/systems/camera-system.cpp | 120 +++- src/game/systems/camera-system.hpp | 3 + src/game/systems/constraint-system.cpp | 24 +- src/game/systems/locomotion-system.cpp | 28 +- src/game/systems/metabolic-system.cpp | 49 ++ src/game/systems/metabolic-system.hpp | 49 ++ src/game/systems/metamorphosis-system.cpp | 149 +++- src/game/systems/physics-system.cpp | 77 ++- src/game/systems/physics-system.hpp | 15 + src/game/systems/reproductive-system.cpp | 32 +- src/game/systems/reproductive-system.hpp | 8 + src/game/systems/spring-system.cpp | 74 -- src/game/textures/cocoon-silk-sdf.cpp | 101 +++ src/game/textures/cocoon-silk-sdf.hpp | 28 + src/game/textures/rgb-voronoi-noise.cpp | 94 +++ src/game/textures/rgb-voronoi-noise.hpp | 27 + src/game/world.cpp | 66 +- src/game/world.hpp | 2 + 69 files changed, 2011 insertions(+), 1700 deletions(-) delete mode 100644 src/engine/animation/spring.hpp create mode 100644 src/engine/physics/frequency.hpp create mode 100644 src/engine/physics/spring.hpp delete mode 100644 src/game/ant/genes/ant-cocoon-gene.cpp create mode 100644 src/game/ant/genes/ant-pupa-gene.cpp rename src/game/ant/genes/{ant-cocoon-gene.hpp => ant-pupa-gene.hpp} (61%) create mode 100644 src/game/components/autofocus-component.hpp rename src/game/{systems/spring-system.hpp => components/decay-component.hpp} (68%) create mode 100644 src/game/components/larva-component.hpp create mode 100644 src/game/components/metabolism-component.hpp create mode 100644 src/game/components/pupa-component.hpp rename src/game/components/{orbit-camera-component.hpp => spring-arm-component.hpp} (56%) delete mode 100644 src/game/components/spring-component.hpp create mode 100644 src/game/systems/metabolic-system.cpp create mode 100644 src/game/systems/metabolic-system.hpp delete mode 100644 src/game/systems/spring-system.cpp create mode 100644 src/game/textures/cocoon-silk-sdf.cpp create mode 100644 src/game/textures/cocoon-silk-sdf.hpp create mode 100644 src/game/textures/rgb-voronoi-noise.cpp create mode 100644 src/game/textures/rgb-voronoi-noise.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 73e1a79..21ca183 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,5 @@ cmake_minimum_required(VERSION 3.25) - option(APPLICATION_NAME "Application name" "Antkeeper") option(APPLICATION_VERSION "Application version string" "0.0.0") option(APPLICATION_AUTHOR "Application author" "C. J. Howard") diff --git a/src/engine/animation/spring.hpp b/src/engine/animation/spring.hpp deleted file mode 100644 index a11e437..0000000 --- a/src/engine/animation/spring.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_SPRING_HPP -#define ANTKEEPER_SPRING_HPP - -#include - -/** - * Contains the variables required for numeric springing. - * - * @tparam T Value type. - * @tparam S Scalar type. - * - * @see spring() - * @see solve_numeric_spring() - */ -template -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 -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 -void solve_numeric_spring(numeric_spring& ns, S dt); - -/** - * Converts a frequency from hertz to radians per second. - * - * @param hz Frequency in hertz. - * @return Frequency in radians per second. - */ -template -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 -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 -T period_to_rads(T t); - -template -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 -void solve_numeric_spring(numeric_spring& ns, S dt) -{ - spring(ns.x0, ns.v, ns.x1, ns.z, ns.w, dt); -} - -template -inline T hz_to_rads(T hz) -{ - return hz * math::two_pi; -} - -template -inline T rads_to_hz(T rads) -{ - return rads / math::two_pi; -} - -template -inline T period_to_rads(T t) -{ - return math::two_pi / t; -} - -#endif // ANTKEEPER_SPRING_HPP diff --git a/src/engine/math/quaternion.hpp b/src/engine/math/quaternion.hpp index 9342c91..af698b9 100644 --- a/src/engine/math/quaternion.hpp +++ b/src/engine/math/quaternion.hpp @@ -445,7 +445,7 @@ template [[nodiscard]] quaternion angle_axis(T angle, const vec3& 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 to Unit vector pointing in the target direction. @@ -458,6 +458,20 @@ template template [[nodiscard]] quaternion rotation(const vec3& from, const vec3& 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 +[[nodiscard]] quaternion rotate_towards(const vec3& from, const vec3& to, T max_angle); + /** * Performs spherical linear interpolation between two quaternions. * @@ -697,6 +711,14 @@ quaternion rotation(const vec3& from, const vec3& to, T tolerance) } } +template +quaternion rotate_towards(const vec3& from, const vec3& 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 quaternion slerp(const quaternion& a, const quaternion& b, T t, T tolerance) { diff --git a/src/engine/math/vector.hpp b/src/engine/math/vector.hpp index d49d187..4c93739 100644 --- a/src/engine/math/vector.hpp +++ b/src/engine/math/vector.hpp @@ -301,14 +301,14 @@ struct vector /** * 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 {}; } /// @private template - [[nodiscard]] static constexpr vector one(std::index_sequence) noexcept + [[nodiscard]] inline static constexpr vector one(std::index_sequence) noexcept { //return {element_type{1}...}; @@ -319,14 +319,14 @@ struct vector /** * 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{}); } /// @private template - [[nodiscard]] static constexpr vector infinity(std::index_sequence) noexcept + [[nodiscard]] inline static constexpr vector infinity(std::index_sequence) noexcept { //return {element_type{1}...}; @@ -337,7 +337,7 @@ struct vector /** * 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{}); } @@ -480,6 +480,17 @@ template template [[nodiscard]] constexpr bool all(const vector& 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 +[[nodiscard]] T angle(const vector& from, const vector& to); + /** * Checks if any elements of a boolean vector are `true`. * @@ -491,13 +502,13 @@ template [[nodiscard]] constexpr bool any(const vector& x) noexcept; /** - * Performs a element-wise ceil operation. + * Performs an element-wise ceil operation. * * @param x Input vector. * * @return Component-wise ceil of input vector. */ -template +template [[nodiscard]] constexpr vector ceil(const vector& x); /** @@ -524,11 +535,11 @@ template * * @return Length-clamped vector. */ -template +template [[nodiscard]] vector clamp_length(const vector& x, T max_length); /** - * Calculate the cross product of two vectors. + * Calculates the cross product of two vectors. * * @param x First vector. * @param y Second vector. @@ -546,7 +557,7 @@ template * * @return Distance between the two points. */ -template +template [[nodiscard]] T distance(const vector& p0, const vector& p1); /** @@ -595,7 +606,7 @@ template * * @return Component-wise floor of input vector. */ -template +template [[nodiscard]] constexpr vector floor(const vector& x); /** @@ -621,7 +632,7 @@ template * * @return Fractional parts of input vector. */ -template +template [[nodiscard]] constexpr vector fract(const vector& x); /** @@ -675,7 +686,7 @@ template * * @return Inverse length of the vector. */ -template +template [[nodiscard]] T inv_length(const vector& x); /** @@ -685,7 +696,7 @@ template * * @return Length of the vector. */ -template +template [[nodiscard]] T length(const vector& x); /** @@ -761,9 +772,9 @@ template * @return Remainders of `x / y`. */ /// @{ -template +template [[nodiscard]] constexpr vector mod(const vector& x, const vector& y); -template +template [[nodiscard]] constexpr vector mod(const vector& x, T y); /// @} @@ -799,7 +810,7 @@ template * * @return Unit vector. */ -template +template [[nodiscard]] vector normalize(const vector& x); /** @@ -832,9 +843,9 @@ template * @return Vector with its elements raised to the given power. */ /// @{ -template +template [[nodiscard]] vector pow(const vector& x, const vector& y); -template +template [[nodiscard]] vector pow(const vector& x, T y); /// @} @@ -845,7 +856,7 @@ template * * @return Component-wise round of input vector. */ -template +template [[nodiscard]] constexpr vector round(const vector& x); /** @@ -854,9 +865,21 @@ template * @param x Input vector * @return Signs of input vector elements. */ -template +template [[nodiscard]] constexpr vector sign(const vector& 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 +[[nodiscard]] T signed_angle(const vector& x, const vector& y, const vector& 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. * @@ -885,7 +908,7 @@ template * * @return Square roots of the input vector elements. */ -template +template [[nodiscard]] vector sqrt(const vector& x); /** @@ -928,13 +951,25 @@ template template [[nodiscard]] constexpr vector swizzle(const vector& 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 +[[nodiscard]] constexpr T triple(const vector& x, const vector& y, const vector& z) noexcept; + /** * Performs a element-wise trunc operation. * * @param x Input vector * @return Component-wise trunc of input vector. */ -template +template [[nodiscard]] constexpr vector trunc(const vector& x); /// @private @@ -989,6 +1024,12 @@ inline constexpr bool all(const vector& x) noexcept return all(x, std::make_index_sequence{}); } +template +T angle(const vector& from, const vector& to) +{ + return std::acos(std::min(std::max(dot(from, to), T{-1}), T{1})); +} + /// @private template inline constexpr bool any(const vector& x, std::index_sequence) noexcept @@ -1044,7 +1085,7 @@ inline constexpr vector clamp(const vector& x, T min, T max) template vector clamp_length(const vector& 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; } @@ -1059,7 +1100,7 @@ inline constexpr vector cross(const vector& x, const vector& y }; } -template +template inline T distance(const vector& p0, const vector& p1) { return length(sub(p0, p1)); @@ -1461,6 +1502,12 @@ inline constexpr vector sign(const vector& x) return sign(x, std::make_index_sequence{}); } +template +T signed_angle(const vector& from, const vector& to, const vector& axis) +{ + return std::atan2(triple(axis, from, to), dot(from, to)); +} + template inline constexpr T sqr_distance(const vector& p0, const vector& p1) noexcept { @@ -1544,6 +1591,12 @@ inline constexpr vector swizzle(const vector& x) no return {x[Indices]...}; } +template +inline constexpr T triple(const vector& x, const vector& y, const vector& z) noexcept +{ + return dot(x, cross(y, z)); +} + /// @private template inline constexpr vector trunc(const vector& x, std::index_sequence) @@ -1561,7 +1614,7 @@ namespace operators { /// @copydoc add(const vector&, const vector&) template -inline constexpr vector operator+(const vector& x, const vector& y) noexcept +[[nodiscard]] inline constexpr vector operator+(const vector& x, const vector& y) noexcept { return add(x, y); } @@ -1569,12 +1622,12 @@ inline constexpr vector operator+(const vector& x, const vector&, T) /// @{ template -inline constexpr vector operator+(const vector& x, T y) noexcept +[[nodiscard]] inline constexpr vector operator+(const vector& x, T y) noexcept { return add(x, y); } template -inline constexpr vector operator+(T x, const vector& y) noexcept +[[nodiscard]] inline constexpr vector operator+(T x, const vector& y) noexcept { return add(y, x); } @@ -1582,28 +1635,28 @@ inline constexpr vector operator+(T x, const vector& y) noexcept /// @copydoc div(const vector&, const vector&) template -inline constexpr vector operator/(const vector& x, const vector& y) noexcept +[[nodiscard]] inline constexpr vector operator/(const vector& x, const vector& y) noexcept { return div(x, y); } /// @copydoc div(const vector&, T) template -inline constexpr vector operator/(const vector& x, T y) noexcept +[[nodiscard]] inline constexpr vector operator/(const vector& x, T y) noexcept { return div(x, y); } /// @copydoc div(T, const vector&) template -inline constexpr vector operator/(T x, const vector& y) noexcept +[[nodiscard]] inline constexpr vector operator/(T x, const vector& y) noexcept { return div(x, y); } /// @copydoc mul(const vector&, const vector&) template -inline constexpr vector operator*(const vector& x, const vector& y) noexcept +[[nodiscard]] inline constexpr vector operator*(const vector& x, const vector& y) noexcept { return mul(x, y); } @@ -1611,12 +1664,12 @@ inline constexpr vector operator*(const vector& x, const vector&, T) /// @{ template -inline constexpr vector operator*(const vector& x, T y) noexcept +[[nodiscard]] inline constexpr vector operator*(const vector& x, T y) noexcept { return mul(x, y); } template -inline constexpr vector operator*(T x, const vector& y) noexcept +[[nodiscard]] inline constexpr vector operator*(T x, const vector& y) noexcept { return mul(y, x); } @@ -1624,28 +1677,28 @@ inline constexpr vector operator*(T x, const vector& y) noexcept /// @copydoc negate(const vector&) template -inline constexpr vector operator-(const vector& x) noexcept +[[nodiscard]] inline constexpr vector operator-(const vector& x) noexcept { return negate(x); } /// @copydoc sub(const vector&, const vector&) template -inline constexpr vector operator-(const vector& x, const vector& y) noexcept +[[nodiscard]] inline constexpr vector operator-(const vector& x, const vector& y) noexcept { return sub(x, y); } /// @copydoc sub(const vector&, T) template -inline constexpr vector operator-(const vector& x, T y) noexcept +[[nodiscard]] inline constexpr vector operator-(const vector& x, T y) noexcept { return sub(x, y); } /// @copydoc sub(T, const vector&) template -inline constexpr vector operator-(T x, const vector& y) noexcept +[[nodiscard]] inline constexpr vector operator-(T x, const vector& y) noexcept { return sub(x, y); } @@ -1772,4 +1825,8 @@ namespace std }; } +// Ensure vectors are POD types +static_assert(std::is_standard_layout_v); +static_assert(std::is_trivial_v); + #endif // ANTKEEPER_MATH_VECTOR_HPP diff --git a/src/engine/physics/frequency.hpp b/src/engine/physics/frequency.hpp new file mode 100644 index 0000000..8cf8eaf --- /dev/null +++ b/src/engine/physics/frequency.hpp @@ -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 . + */ + +#ifndef ANTKEEPER_PHYSICS_FREQUENCY_HPP +#define ANTKEEPER_PHYSICS_FREQUENCY_HPP + +#include + +namespace physics { + +/** + * Converts frequency to angular frequency. + * + * @tparam T Scalar type. + * + * @param f Frequency, in hertz. + * + * @return Angular frequency, in radians per second. + */ +template +[[nodiscard]] inline constexpr T hz_to_rads(T f) noexcept +{ + return f * math::two_pi; +} + +/** + * Converts angular frequency to frequency. + * + * @tparam T Scalar type. + * + * @param w Angular frequency, in radians per second. + * + * @return Frequency, in hertz. + */ +template +[[nodiscard]] inline constexpr T rads_to_hz(T w) noexcept +{ + return w / math::two_pi; +} + +/** + * Converts oscillation period to angular frequency. + * + * @tparam T Scalar type. + * + * @param t Oscillation period, in seconds. + * + * @return Angular frequency, in radians per second. + */ +template +[[nodiscard]] inline constexpr T s_to_rads(T t) noexcept +{ + return math::two_pi / 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 +[[nodiscard]] inline constexpr T rads_to_s(T w) noexcept +{ + return math::two_pi / w; +} + +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_FREQUENCY_HPP diff --git a/src/engine/physics/kinematics/collider-material.hpp b/src/engine/physics/kinematics/collider-material.hpp index d641e02..9c920bd 100644 --- a/src/engine/physics/kinematics/collider-material.hpp +++ b/src/engine/physics/kinematics/collider-material.hpp @@ -97,6 +97,16 @@ public: 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. [[nodiscard]] inline constexpr float get_restitution() const noexcept { @@ -127,6 +137,12 @@ public: return m_friction_combine_mode; } + /// Returns the density of the material. + [[nodiscard]] inline constexpr float get_density() const noexcept + { + return m_density; + } + private: /// Restitution value. float m_restitution{}; @@ -142,6 +158,9 @@ private: /// Friction combine mode. friction_combine_mode m_friction_combine_mode{friction_combine_mode::average}; + + /// Density. + float m_density{1.0f}; }; } // namespace physics diff --git a/src/engine/physics/kinematics/colliders/mesh-collider.cpp b/src/engine/physics/kinematics/colliders/mesh-collider.cpp index f75b313..1aeed2f 100644 --- a/src/engine/physics/kinematics/colliders/mesh-collider.cpp +++ b/src/engine/physics/kinematics/colliders/mesh-collider.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace physics { @@ -34,11 +35,24 @@ void mesh_collider::set_mesh(std::shared_ptr mesh) m_mesh = mesh; if (m_mesh) { + // Store pointer to mesh vertex positions m_vertex_positions = &m_mesh->vertices().attributes().at("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("normal"); } else { m_vertex_positions = nullptr; + m_face_normals = nullptr; } rebuild_bvh(); @@ -56,41 +70,52 @@ void mesh_collider::rebuild_bvh() } } -std::optional> mesh_collider::intersection(const math::transform& mesh_transform, const geom::ray& ray) const +std::optional> mesh_collider::intersection(const geom::ray& ray) const { - // Transform ray into mesh space - const auto inv_mesh_transform = math::inverse(mesh_transform); - const geom::ray 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 triangle_hit_count = 0; float nearest_face_distance = std::numeric_limits::infinity(); std::uint32_t nearest_face_index; + // For each BVH leaf node that intersects ray m_bvh.visit ( - mesh_space_ray, + ray, [&](std::uint32_t index) { ++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]; + + // Get face vertex positions auto loop = face->loops().begin(); const auto& a = (*m_vertex_positions)[loop->vertex()->index()]; const auto& b = (*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; + // If distance to point of intersection is nearer than nearest intersection float t = std::get<0>(*intersection); if (t < nearest_face_distance) { + // Update nearest intersection nearest_face_distance = t; 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) { 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 diff --git a/src/engine/physics/kinematics/colliders/mesh-collider.hpp b/src/engine/physics/kinematics/colliders/mesh-collider.hpp index d193711..9252754 100644 --- a/src/engine/physics/kinematics/colliders/mesh-collider.hpp +++ b/src/engine/physics/kinematics/colliders/mesh-collider.hpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -87,13 +86,18 @@ public: 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> intersection(const math::transform& mesh_transform, const geom::ray& ray) const; + [[nodiscard]] std::optional> intersection(const geom::ray& ray) const; private: std::shared_ptr m_mesh; const geom::brep_attribute* m_vertex_positions{}; + const geom::brep_attribute* m_face_normals{}; bvh_type m_bvh; }; diff --git a/src/engine/physics/kinematics/rigid-body.hpp b/src/engine/physics/kinematics/rigid-body.hpp index a69b82e..9f71c79 100644 --- a/src/engine/physics/kinematics/rigid-body.hpp +++ b/src/engine/physics/kinematics/rigid-body.hpp @@ -64,6 +64,22 @@ public: 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. * @@ -94,6 +110,22 @@ public: 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. * @@ -218,6 +250,12 @@ public: 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. [[nodiscard]] inline constexpr const math::transform& get_previous_transform() const noexcept { @@ -236,6 +274,12 @@ public: 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. [[nodiscard]] inline constexpr const math::fvec3& get_center_of_mass() const noexcept { diff --git a/src/engine/physics/spring.hpp b/src/engine/physics/spring.hpp new file mode 100644 index 0000000..030d501 --- /dev/null +++ b/src/engine/physics/spring.hpp @@ -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 . + */ + +#ifndef ANTKEEPER_PHYSICS_SPRING_HPP +#define ANTKEEPER_PHYSICS_SPRING_HPP + +#include +#include + +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 +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 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}; +}; + +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_SPRING_HPP diff --git a/src/engine/utility/frame-scheduler.cpp b/src/engine/utility/frame-scheduler.cpp index 664756d..10b9607 100644 --- a/src/engine/utility/frame-scheduler.cpp +++ b/src/engine/utility/frame-scheduler.cpp @@ -66,7 +66,7 @@ void frame_scheduler::tick() } // 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 diff --git a/src/engine/utility/frame-scheduler.hpp b/src/engine/utility/frame-scheduler.hpp index 4b69d2d..2c70541 100644 --- a/src/engine/utility/frame-scheduler.hpp +++ b/src/engine/utility/frame-scheduler.hpp @@ -27,7 +27,7 @@ /** * 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 { diff --git a/src/game/ant/ant-cladogenesis.hpp b/src/game/ant/ant-cladogenesis.hpp index bd93d5d..6f45228 100644 --- a/src/game/ant/ant-cladogenesis.hpp +++ b/src/game/ant/ant-cladogenesis.hpp @@ -44,7 +44,7 @@ template // Randomly sample genes genome->antennae = pool.antennae.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->egg = pool.egg.sample(urbg); genome->eyes = pool.eyes.sample(urbg); diff --git a/src/game/ant/ant-gene-pool.hpp b/src/game/ant/ant-gene-pool.hpp index e113123..3c827f5 100644 --- a/src/game/ant/ant-gene-pool.hpp +++ b/src/game/ant/ant-gene-pool.hpp @@ -23,7 +23,7 @@ #include "game/ant/ant-gene-frequency-table.hpp" #include "game/ant/genes/ant-antennae-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-egg-gene.hpp" #include "game/ant/genes/ant-eyes-gene.hpp" @@ -54,7 +54,7 @@ struct ant_gene_pool ant_gene_frequency_table antennae; ant_gene_frequency_table body_size; - ant_gene_frequency_table cocoon; + ant_gene_frequency_table pupa; ant_gene_frequency_table diet; ant_gene_frequency_table egg; ant_gene_frequency_table eyes; diff --git a/src/game/ant/ant-genome.hpp b/src/game/ant/ant-genome.hpp index 91e989d..0b6a95f 100644 --- a/src/game/ant/ant-genome.hpp +++ b/src/game/ant/ant-genome.hpp @@ -22,7 +22,7 @@ #include "game/ant/genes/ant-antennae-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-egg-gene.hpp" #include "game/ant/genes/ant-eyes-gene.hpp" @@ -50,7 +50,7 @@ struct ant_genome { std::shared_ptr antennae; std::shared_ptr body_size; - std::shared_ptr cocoon; + std::shared_ptr pupa; std::shared_ptr diet; std::shared_ptr egg; std::shared_ptr eyes; diff --git a/src/game/ant/ant-phenome.cpp b/src/game/ant/ant-phenome.cpp index 3655ff9..1f0d7cf 100644 --- a/src/game/ant/ant-phenome.cpp +++ b/src/game/ant/ant-phenome.cpp @@ -27,9 +27,9 @@ ant_phenome::ant_phenome(const ant_genome& genome, ant_caste_type caste) if (genome.body_size) if (auto i = genome.body_size->phene_map.find(caste); i != genome.body_size->phene_map.end()) 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 (auto i = genome.diet->phene_map.find(caste); i != genome.diet->phene_map.end()) diet = i->second; diff --git a/src/game/ant/ant-phenome.hpp b/src/game/ant/ant-phenome.hpp index 6e186af..b707d4e 100644 --- a/src/game/ant/ant-phenome.hpp +++ b/src/game/ant/ant-phenome.hpp @@ -22,7 +22,7 @@ #include "game/ant/genes/ant-antennae-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-egg-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_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_egg_phene* egg{nullptr}; const ant_eyes_phene* eyes{nullptr}; diff --git a/src/game/ant/genes/ant-cocoon-gene.cpp b/src/game/ant/genes/ant-cocoon-gene.cpp deleted file mode 100644 index 1e50bcd..0000000 --- a/src/game/ant/genes/ant-cocoon-gene.cpp +++ /dev/null @@ -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 . - */ - -#include "game/ant/genes/ant-cocoon-gene.hpp" -#include "game/ant/genes/ant-gene-loader.hpp" -#include -#include -#include - -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(&present), 1); - phene.present = static_cast(present); - - std::uint8_t model_filename_length{0}; - ctx.read8(reinterpret_cast(&model_filename_length), 1); - std::string model_filename(model_filename_length, '\0'); - ctx.read8(reinterpret_cast(model_filename.data()), model_filename_length); - - if (phene.present) - { - phene.model = resource_manager.load(model_filename); - } -} - -} // namespace - -template <> -std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) -{ - std::unique_ptr gene = std::make_unique(); - - load_ant_gene(*gene, resource_manager, ctx, &load_ant_cocoon_phene); - - return gene; -} diff --git a/src/game/ant/genes/ant-egg-gene.cpp b/src/game/ant/genes/ant-egg-gene.cpp index e7867dd..72819c7 100644 --- a/src/game/ant/genes/ant-egg-gene.cpp +++ b/src/game/ant/genes/ant-egg-gene.cpp @@ -27,6 +27,9 @@ namespace { void load_ant_egg_phene(ant_egg_phene& phene, ::resource_manager& resource_manager, deserialize_context& ctx) { + ctx.read32(reinterpret_cast(&phene.incubation_period), 1); + ctx.read32(reinterpret_cast(&phene.eclosion_period), 1); + std::uint8_t model_filename_length{0}; ctx.read8(reinterpret_cast(&model_filename_length), 1); std::string model_filename(model_filename_length, '\0'); diff --git a/src/game/ant/genes/ant-egg-gene.hpp b/src/game/ant/genes/ant-egg-gene.hpp index 7b15759..be60145 100644 --- a/src/game/ant/genes/ant-egg-gene.hpp +++ b/src/game/ant/genes/ant-egg-gene.hpp @@ -29,6 +29,12 @@ */ 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. std::shared_ptr model; }; diff --git a/src/game/ant/genes/ant-gene-type.hpp b/src/game/ant/genes/ant-gene-type.hpp index aee325b..8a24b67 100644 --- a/src/game/ant/genes/ant-gene-type.hpp +++ b/src/game/ant/genes/ant-gene-type.hpp @@ -29,7 +29,7 @@ enum class ant_gene_type: std::uint8_t { antennae = 1, body_size, - cocoon, + pupa, diet, egg, eyes, diff --git a/src/game/ant/genes/ant-larva-gene.cpp b/src/game/ant/genes/ant-larva-gene.cpp index 74b63fe..32845c7 100644 --- a/src/game/ant/genes/ant-larva-gene.cpp +++ b/src/game/ant/genes/ant-larva-gene.cpp @@ -27,7 +27,10 @@ namespace { void load_ant_larva_phene(ant_larva_phene& phene, ::resource_manager& resource_manager, deserialize_context& ctx) { + ctx.read32(reinterpret_cast(&phene.development_period), 1); + ctx.read32(reinterpret_cast(&phene.spinning_period), 1); ctx.read8(reinterpret_cast(&phene.instar_count), 1); + ctx.read32(reinterpret_cast(&phene.first_instar_scale), 1); std::uint8_t model_filename_length{0}; ctx.read8(reinterpret_cast(&model_filename_length), 1); diff --git a/src/game/ant/genes/ant-larva-gene.hpp b/src/game/ant/genes/ant-larva-gene.hpp index 1c5dd7e..9300af7 100644 --- a/src/game/ant/genes/ant-larva-gene.hpp +++ b/src/game/ant/genes/ant-larva-gene.hpp @@ -30,8 +30,17 @@ */ 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. - 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. std::shared_ptr model; diff --git a/src/game/ant/genes/ant-legs-gene.cpp b/src/game/ant/genes/ant-legs-gene.cpp index aa5e66f..2956095 100644 --- a/src/game/ant/genes/ant-legs-gene.cpp +++ b/src/game/ant/genes/ant-legs-gene.cpp @@ -32,6 +32,7 @@ void load_ant_legs_phene(ant_legs_phene& phene, ::resource_manager& resource_man ctx.read32(reinterpret_cast(&phene.walking_speed), 1); ctx.read32(reinterpret_cast(&phene.running_speed), 1); ctx.read32(reinterpret_cast(&phene.stride_length), 1); + ctx.read32(reinterpret_cast(&phene.max_angular_frequency), 1); ctx.read32(reinterpret_cast(&phene.grip), 1); std::uint8_t model_filename_length{0}; diff --git a/src/game/ant/genes/ant-legs-gene.hpp b/src/game/ant/genes/ant-legs-gene.hpp index 7b356f2..f4afc8e 100644 --- a/src/game/ant/genes/ant-legs-gene.hpp +++ b/src/game/ant/genes/ant-legs-gene.hpp @@ -46,6 +46,9 @@ struct ant_legs_phene /// Distance covered in a single gait cycle, in mesosomal lengths. float stride_length{}; + /// Maximum angular frequency when turning, in radians per second. + float max_angular_frequency{}; + /// Grip factor. float grip{}; diff --git a/src/game/ant/genes/ant-pupa-gene.cpp b/src/game/ant/genes/ant-pupa-gene.cpp new file mode 100644 index 0000000..913b590 --- /dev/null +++ b/src/game/ant/genes/ant-pupa-gene.cpp @@ -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 . + */ + +#include "game/ant/genes/ant-pupa-gene.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" +#include +#include +#include + +namespace { + +void load_ant_pupa_phene(ant_pupa_phene& phene, ::resource_manager& resource_manager, deserialize_context& ctx) +{ + ctx.read32(reinterpret_cast(&phene.development_period), 1); + ctx.read32(reinterpret_cast(&phene.eclosion_period), 1); + + std::uint8_t cocoon_present{0}; + ctx.read8(reinterpret_cast(&cocoon_present), 1); + phene.cocoon_present = static_cast(cocoon_present); + + std::uint8_t cocoon_model_filename_length{0}; + ctx.read8(reinterpret_cast(&cocoon_model_filename_length), 1); + std::string cocoon_model_filename(cocoon_model_filename_length, '\0'); + ctx.read8(reinterpret_cast(cocoon_model_filename.data()), cocoon_model_filename_length); + + if (phene.cocoon_present) + { + phene.cocoon_model = resource_manager.load(cocoon_model_filename); + } +} + +} // namespace + +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + std::unique_ptr gene = std::make_unique(); + + load_ant_gene(*gene, resource_manager, ctx, &load_ant_pupa_phene); + + return gene; +} diff --git a/src/game/ant/genes/ant-cocoon-gene.hpp b/src/game/ant/genes/ant-pupa-gene.hpp similarity index 61% rename from src/game/ant/genes/ant-cocoon-gene.hpp rename to src/game/ant/genes/ant-pupa-gene.hpp index 2836aff..6e30d8a 100644 --- a/src/game/ant/genes/ant-cocoon-gene.hpp +++ b/src/game/ant/genes/ant-pupa-gene.hpp @@ -17,32 +17,38 @@ * along with Antkeeper source code. If not, see . */ -#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 #include /** - * 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. - bool present{false}; + bool cocoon_present{false}; /// 3D model of the cocoon, if present. - std::shared_ptr model; + std::shared_ptr cocoon_model; }; -/// Ant cocoon gene. -using ant_cocoon_gene = ant_gene; +/// Ant pupa gene. +using ant_pupa_gene = ant_gene; 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 diff --git a/src/game/components/autofocus-component.hpp b/src/game/components/autofocus-component.hpp new file mode 100644 index 0000000..ecc2f8b --- /dev/null +++ b/src/game/components/autofocus-component.hpp @@ -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 . + */ + +#ifndef ANTKEEPER_GAME_AUTOFOCUS_COMPONENT_HPP +#define ANTKEEPER_GAME_AUTOFOCUS_COMPONENT_HPP + +#include +#include +#include + +/** + * 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 diff --git a/src/game/systems/spring-system.hpp b/src/game/components/decay-component.hpp similarity index 68% rename from src/game/systems/spring-system.hpp rename to src/game/components/decay-component.hpp index a417258..68fed62 100644 --- a/src/game/systems/spring-system.hpp +++ b/src/game/components/decay-component.hpp @@ -17,23 +17,19 @@ * along with Antkeeper source code. If not, see . */ -#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 diff --git a/src/game/components/egg-component.hpp b/src/game/components/egg-component.hpp index 87e2404..aa2b81d 100644 --- a/src/game/components/egg-component.hpp +++ b/src/game/components/egg-component.hpp @@ -20,22 +20,16 @@ #ifndef ANTKEEPER_GAME_EGG_COMPONENT_HPP #define ANTKEEPER_GAME_EGG_COMPONENT_HPP -#include -#include - /** - * + * Egg incubation parameters. */ struct egg_component { - /// Duration of the incubation period, in seconds. + /// Duration of the incubation period, in days. 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 larva_model; + /// Current incubation phase, on `[0, 1]`. + float incubation_phase{}; }; #endif // ANTKEEPER_GAME_EGG_COMPONENT_HPP diff --git a/src/game/components/larva-component.hpp b/src/game/components/larva-component.hpp new file mode 100644 index 0000000..263e757 --- /dev/null +++ b/src/game/components/larva-component.hpp @@ -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 . + */ + +#ifndef ANTKEEPER_GAME_LARVA_COMPONENT_HPP +#define ANTKEEPER_GAME_LARVA_COMPONENT_HPP + +#include +#include + +/** + * 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 spinning_phase_matvar; +}; + +#endif // ANTKEEPER_GAME_LARVA_COMPONENT_HPP diff --git a/src/game/components/legged-locomotion-component.hpp b/src/game/components/legged-locomotion-component.hpp index 49b58c6..a0867f4 100644 --- a/src/game/components/legged-locomotion-component.hpp +++ b/src/game/components/legged-locomotion-component.hpp @@ -54,10 +54,15 @@ struct legged_locomotion_component /// Distance covered in a single gait cycle. float stride_length{}; + /// Maximum angular frequency when turning, in radians per second. + float max_angular_frequency{}; + float speed{}; float angular_velocity{}; + math::fvec3 target_direction{}; + /// Current phase of the gait cycle, on `[0, 1]`. float gait_phase{}; }; diff --git a/src/game/components/metabolism-component.hpp b/src/game/components/metabolism-component.hpp new file mode 100644 index 0000000..d14d718 --- /dev/null +++ b/src/game/components/metabolism-component.hpp @@ -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 . + */ + +#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 diff --git a/src/game/components/pupa-component.hpp b/src/game/components/pupa-component.hpp new file mode 100644 index 0000000..cfe1bfc --- /dev/null +++ b/src/game/components/pupa-component.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_PUPA_COMPONENT_HPP +#define ANTKEEPER_GAME_PUPA_COMPONENT_HPP + +#include +#include + +/** + * 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 decay_phase_matvar; +}; + +#endif // ANTKEEPER_GAME_PUPA_COMPONENT_HPP diff --git a/src/game/components/orbit-camera-component.hpp b/src/game/components/spring-arm-component.hpp similarity index 56% rename from src/game/components/orbit-camera-component.hpp rename to src/game/components/spring-arm-component.hpp index f54f4d4..03502fa 100644 --- a/src/game/components/orbit-camera-component.hpp +++ b/src/game/components/spring-arm-component.hpp @@ -17,24 +17,54 @@ * along with Antkeeper source code. If not, see . */ -#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 #include #include #include +#include -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. - 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, -std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; + + /// Maximum pitch, yaw, and roll angles, in radians. + math::dvec3 max_angles{math::half_pi, std::numeric_limits::infinity(), std::numeric_limits::infinity()}; + + physics::numeric_spring focal_point_spring; + + /// Pitch, yaw, and roll angles spring. + physics::numeric_spring angles_spring; + + /// Arm length spring. + // physics::numeric_spring arm_length_spring; + + + /// 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. double near_hfov{math::radians(90.0)}; @@ -42,12 +72,6 @@ struct orbit_camera_component /// Horizontal FoV at minimum zoom. 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]`. double zoom{}; @@ -58,7 +82,7 @@ struct orbit_camera_component double vfov{}; /// Position of the focal point, relative to the subject. - math::dvec3 focal_point{}; + math::dvec3 focal_point_offset{}; /// Distance to the focal plane. double focal_distance{}; @@ -68,18 +92,6 @@ struct orbit_camera_component /// Height of the view frustum at the focal distance. 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 diff --git a/src/game/components/spring-component.hpp b/src/game/components/spring-component.hpp deleted file mode 100644 index 9ea26b8..0000000 --- a/src/game/components/spring-component.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_GAME_SPRING_COMPONENT_HPP -#define ANTKEEPER_GAME_SPRING_COMPONENT_HPP - -#include -#include -#include - - -/** - * Numeric spring with one component. - */ -struct spring1_component -{ - /// Numeric spring with one component. - numeric_spring spring; - - /// Spring solved callback. - std::function callback; -}; - -/** - * Numeric spring with two components. - */ -struct spring2_component -{ - /// Numeric spring with two components. - numeric_spring spring; - - /// Spring solved callback. - std::function callback; -}; - -/** - * Numeric spring with three components. - */ -struct spring3_component -{ - /// Numeric spring with three components. - numeric_spring spring; - - /// Spring solved callback. - std::function callback; -}; - -/** - * Numeric spring with four components. - */ -struct spring4_component -{ - /// Numeric spring with four components. - numeric_spring spring; - - /// Spring solved callback. - std::function callback; -}; - - -#endif // ANTKEEPER_GAME_SPRING_COMPONENT_HPP diff --git a/src/game/constraints/spring-rotation-constraint.hpp b/src/game/constraints/spring-rotation-constraint.hpp index 1a27bc9..786eb46 100644 --- a/src/game/constraints/spring-rotation-constraint.hpp +++ b/src/game/constraints/spring-rotation-constraint.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_GAME_SPRING_ROTATION_CONSTRAINT_HPP #define ANTKEEPER_GAME_SPRING_ROTATION_CONSTRAINT_HPP -#include +#include #include @@ -30,7 +30,7 @@ struct spring_rotation_constraint { /// Yaw, pitch, and roll angle spring. - numeric_spring spring; + physics::numeric_spring spring; }; diff --git a/src/game/constraints/spring-to-constraint.hpp b/src/game/constraints/spring-to-constraint.hpp index cb0c611..bc96874 100644 --- a/src/game/constraints/spring-to-constraint.hpp +++ b/src/game/constraints/spring-to-constraint.hpp @@ -21,7 +21,7 @@ #define ANTKEEPER_GAME_SPRING_TO_CONSTRAINT_HPP #include -#include +#include #include @@ -34,10 +34,10 @@ struct spring_to_constraint entity::id target; /// Translation spring. - numeric_spring translation; + physics::numeric_spring translation; /// Rotation spring. - numeric_spring rotation; + physics::numeric_spring rotation; /// Spring translation. bool spring_translation; diff --git a/src/game/constraints/spring-translation-constraint.hpp b/src/game/constraints/spring-translation-constraint.hpp index c0f0293..fc3bbfd 100644 --- a/src/game/constraints/spring-translation-constraint.hpp +++ b/src/game/constraints/spring-translation-constraint.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_GAME_SPRING_TRANSLATION_CONSTRAINT_HPP #define ANTKEEPER_GAME_SPRING_TRANSLATION_CONSTRAINT_HPP -#include +#include #include @@ -30,7 +30,7 @@ struct spring_translation_constraint { /// Translation spring. - numeric_spring spring; + physics::numeric_spring spring; }; diff --git a/src/game/controls.cpp b/src/game/controls.cpp index 694d01c..3121b17 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -121,8 +121,12 @@ void reset_control_profile(::control_profile& profile) mappings.emplace("move_down", std::make_unique(nullptr, input::mouse_scroll_axis::y, true)); mappings.emplace("move_down", std::make_unique(nullptr, input::gamepad_axis::left_trigger, false)); + // Interact + mappings.emplace("interact", std::make_unique(nullptr, input::scancode::e, 0, false)); + mappings.emplace("interact", std::make_unique(nullptr, input::gamepad_button::a)); + // Move fast - mappings.emplace("move_fast", std::make_unique(nullptr, input::scancode::left_shift, 0, false)); + // mappings.emplace("move_fast", std::make_unique(nullptr, input::scancode::left_shift, 0, false)); // Move slow mappings.emplace("move_slow", std::make_unique(nullptr, input::scancode::left_ctrl, 0, false)); @@ -143,13 +147,17 @@ void reset_control_profile(::control_profile& profile) // Camera orbit mappings.emplace("camera_orbit_left", std::make_unique(nullptr, input::scancode::left, 0, false)); - mappings.emplace("camera_orbit_left", std::make_unique(nullptr, input::gamepad_axis::right_stick_x, true)); + mappings.emplace("camera_orbit_left", std::make_unique(nullptr, input::gamepad_axis::right_stick_x, false)); mappings.emplace("camera_orbit_right", std::make_unique(nullptr, input::scancode::right, 0, false)); - mappings.emplace("camera_orbit_right", std::make_unique(nullptr, input::gamepad_axis::right_stick_x, false)); + mappings.emplace("camera_orbit_right", std::make_unique(nullptr, input::gamepad_axis::right_stick_x, true)); mappings.emplace("camera_orbit_up", std::make_unique(nullptr, input::scancode::up, 0, false)); - mappings.emplace("camera_orbit_up", std::make_unique(nullptr, input::gamepad_axis::right_stick_y, false)); + mappings.emplace("camera_orbit_up", std::make_unique(nullptr, input::gamepad_axis::right_stick_y, true)); mappings.emplace("camera_orbit_down", std::make_unique(nullptr, input::scancode::up, 0, false)); - mappings.emplace("camera_orbit_down", std::make_unique(nullptr, input::gamepad_axis::right_stick_y, true)); + mappings.emplace("camera_orbit_down", std::make_unique(nullptr, input::gamepad_axis::right_stick_y, false)); + + // Camera look ahead + mappings.emplace("camera_look_ahead", std::make_unique(nullptr, input::scancode::left_shift, 0, false)); + mappings.emplace("camera_look_ahead", std::make_unique(nullptr, input::gamepad_axis::left_trigger, false)); // Camera presets mappings.emplace("camera_preset_1", std::make_unique(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_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_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_2_action, "camera_preset_2"); 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_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_interact_action, "interact"); add_mappings(ctx.ant_action_map, ctx.ant_oviposit_action, "oviposit"); // 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_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_interact_action, "interact"); add_mappings(ctx.ant_action_map, ctx.ant_oviposit_action, "oviposit"); // 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_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_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_2_action, "camera_preset_2"); add_mappings(ctx.camera_action_map, ctx.camera_preset_3_action, "camera_preset_3"); diff --git a/src/game/controls/ant-controls.cpp b/src/game/controls/ant-controls.cpp index 4d5979a..c04acfd 100644 --- a/src/game/controls/ant-controls.cpp +++ b/src/game/controls/ant-controls.cpp @@ -18,11 +18,14 @@ */ #include "game/controls.hpp" +#include "game/world.hpp" #include "game/components/ant-caste-component.hpp" #include "game/components/rigid-body-component.hpp" #include "game/components/legged-locomotion-component.hpp" #include "game/components/ovary-component.hpp" +#include "game/components/spring-arm-component.hpp" #include +#include #include namespace { @@ -32,16 +35,38 @@ namespace { * * @param ctx Game context. */ - void update_controlled_ant_speed(::game& ctx) + void steer_controlled_ant(::game& ctx) { if (ctx.controlled_ant_eid == entt::null) { 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(ctx.controlled_ant_eid); + // Get phenome of controlled ant caste 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 float locomotive_speed; if (ctx.ant_move_slow_action.is_active()) @@ -60,21 +85,26 @@ namespace { 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 - locomotive_speed *= ctx.entity_registry->get(ctx.controlled_ant_eid).body->get_transform().scale.x(); + auto& rigid_body = *ctx.entity_registry->get(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(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) { - update_controlled_ant_speed(ctx); + steer_controlled_ant(ctx); } ) ); @@ -120,7 +150,7 @@ void setup_ant_controls(::game& ctx) ( [&](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) { - 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) { - turn_controlled_ant(ctx, event.input_value); + steer_controlled_ant(ctx); } ) ); @@ -154,7 +194,7 @@ void setup_ant_controls(::game& ctx) ( [&](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) { - turn_controlled_ant(ctx, -event.input_value); + steer_controlled_ant(ctx); } ) ); @@ -176,7 +216,19 @@ void setup_ant_controls(::game& ctx) ( [&](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); } ) ); diff --git a/src/game/controls/camera-controls.cpp b/src/game/controls/camera-controls.cpp index e81c94a..2cad59c 100644 --- a/src/game/controls/camera-controls.cpp +++ b/src/game/controls/camera-controls.cpp @@ -18,30 +18,12 @@ */ #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 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(ctx.active_camera_eid); - - // Adjust yaw and pitch angles according to mouse motion - orbit_cam.yaw -= ctx.mouse_pan_factor * static_cast(event.difference.x()); - orbit_cam.pitch += ctx.mouse_tilt_factor * static_cast(event.difference.y()); - - // Limit pitch - orbit_cam.pitch = std::min(math::half_pi, std::max(-math::half_pi, orbit_cam.pitch)); - } - */ - void handle_mouse_motion(::game& ctx, const input::mouse_moved_event& event) { if (ctx.active_camera_eid == entt::null) @@ -49,27 +31,31 @@ namespace { return; } - auto& orbit_cam = ctx.entity_registry->get(ctx.active_camera_eid); + auto& spring_arm = ctx.entity_registry->get(ctx.active_camera_eid); // Rotate camera 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(event.difference.x()); - orbit_cam.pitch += ctx.mouse_tilt_factor * static_cast(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(event.difference.y()); + target_angles.y() -= ctx.mouse_pan_factor * static_cast(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, std::max(-math::half_pi, orbit_cam.pitch)); + // Update spring arm target angles + spring_arm.angles_spring.set_target_value(target_angles); } // Zoom camera if (ctx.camera_mouse_zoom_action.is_active()) { // Adjust zoom factor - orbit_cam.zoom -= static_cast(event.difference.y()) / static_cast(ctx.window->get_viewport_size().y()); + spring_arm.zoom -= static_cast(event.difference.y()) / static_cast(ctx.window->get_viewport_size().y()); // Limit zoom factor - orbit_cam.zoom = std::min(std::max(orbit_cam.zoom, 0.0), 1.0); + spring_arm.zoom = std::min(std::max(spring_arm.zoom, 0.0), 1.0); } } @@ -90,14 +76,17 @@ namespace { return; } - auto& orbit_cam = ctx.entity_registry->get(ctx.active_camera_eid); + auto& spring_arm = ctx.entity_registry->get(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(std::max(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) @@ -247,7 +236,7 @@ void setup_camera_controls(::game& ctx) ( [&](const auto& event) { - step_camera_zoom(ctx, 1.0 * static_cast(ctx.camera_zoom_in_action.get_input_value())); + step_camera_zoom(ctx, static_cast(ctx.camera_zoom_in_action.get_input_value())); } ) ); @@ -259,7 +248,7 @@ void setup_camera_controls(::game& ctx) ( [&](const auto& event) { - step_camera_zoom(ctx, -1.0 * static_cast(ctx.camera_zoom_out_action.get_input_value())); + step_camera_zoom(ctx, -static_cast(ctx.camera_zoom_out_action.get_input_value())); } ) ); @@ -271,7 +260,29 @@ void setup_camera_controls(::game& ctx) ( [&](const auto& event) { + if (ctx.active_camera_eid == entt::null) + { + return; + } + auto& spring_arm = ctx.entity_registry->get(ctx.active_camera_eid); + spring_arm.angular_velocities.y() = -ctx.gamepad_pan_factor * static_cast(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(ctx.active_camera_eid); + spring_arm.angular_velocities.y() = 0.0; } ) ); @@ -283,7 +294,29 @@ void setup_camera_controls(::game& ctx) ( [&](const auto& event) { + if (ctx.active_camera_eid == entt::null) + { + return; + } + auto& spring_arm = ctx.entity_registry->get(ctx.active_camera_eid); + spring_arm.angular_velocities.y() = ctx.gamepad_pan_factor * static_cast(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(ctx.active_camera_eid); + spring_arm.angular_velocities.y() = 0.0; } ) ); @@ -295,7 +328,29 @@ void setup_camera_controls(::game& ctx) ( [&](const auto& event) { + if (ctx.active_camera_eid == entt::null) + { + return; + } + auto& spring_arm = ctx.entity_registry->get(ctx.active_camera_eid); + spring_arm.angular_velocities.x() = ctx.gamepad_tilt_factor * static_cast(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(ctx.active_camera_eid); + spring_arm.angular_velocities.x() = 0.0; } ) ); @@ -307,7 +362,72 @@ void setup_camera_controls(::game& ctx) ( [&](const auto& event) { + if (ctx.active_camera_eid == entt::null) + { + return; + } + + auto& spring_arm = ctx.entity_registry->get(ctx.active_camera_eid); + spring_arm.angular_velocities.x() = -ctx.gamepad_tilt_factor * static_cast(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(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(ctx.active_camera_eid); + const auto& subject_rigid_body = *ctx.entity_registry->get(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); } ) ); diff --git a/src/game/ecoregion-loader.cpp b/src/game/ecoregion-loader.cpp index a21423b..c8e0330 100644 --- a/src/game/ecoregion-loader.cpp +++ b/src/game/ecoregion-loader.cpp @@ -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; - std::shared_ptr gene; + std::shared_ptr 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(); - if (auto gene_element = cocoon_element->find("gene"); gene_element != cocoon_element->end()) - gene = resource_manager.load(gene_element->get()); + if (auto gene_element = pupa_element->find("gene"); gene_element != pupa_element->end()) + gene = resource_manager.load(gene_element->get()); 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); } } } diff --git a/src/game/game.cpp b/src/game/game.cpp index ef33aaa..7c48329 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -41,12 +41,12 @@ #include "game/systems/orbit-system.hpp" #include "game/systems/render-system.hpp" #include "game/systems/spatial-system.hpp" -#include "game/systems/spring-system.hpp" #include "game/systems/steering-system.hpp" #include "game/systems/physics-system.hpp" #include "game/systems/subterrain-system.hpp" #include "game/systems/terrain-system.hpp" #include "game/systems/reproductive-system.hpp" +#include "game/systems/metabolic-system.hpp" #include "game/systems/metamorphosis-system.hpp" #include #include @@ -567,12 +567,15 @@ void game::setup_input() } ); + /* // Gamepad deactivation function auto deactivate_gamepad = [&](const auto& event) { if (gamepad_active) { gamepad_active = false; + + // WARNING: huge source of lag input_manager->set_cursor_visible(true); } }; @@ -625,6 +628,7 @@ void game::setup_input() { gamepad_active = false; } + */ } void game::load_strings() @@ -1063,8 +1067,8 @@ void game::setup_systems() // Setup IK system 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 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->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 spatial_system = std::make_unique<::spatial_system>(*entity_registry); @@ -1163,8 +1168,12 @@ void game::setup_controls() ::apply_control_profile(*this, *control_profile); // 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_window_controls(*this); @@ -1280,13 +1289,13 @@ void game::fixed_update(::frame_scheduler::duration_type fixed_update_time, ::fr locomotion_system->update(t, dt); ik_system->update(t, dt); reproductive_system->update(t, dt); + metabolic_system->update(t, dt); metamorphosis_system->update(t, dt); // physics_system->update(t, dt); orbit_system->update(t, dt); blackbody_system->update(t, dt); atmosphere_system->update(t, dt); astronomy_system->update(t, dt); - spring_system->update(t, dt); spatial_system->update(t, dt); constraint_system->update(t, dt); animator->animate(dt); diff --git a/src/game/game.hpp b/src/game/game.hpp index 050be1e..39ebb13 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -111,9 +111,9 @@ class nest_system; class orbit_system; class render_system; class spatial_system; -class spring_system; class steering_system; class reproductive_system; +class metabolic_system; class metamorphosis_system; class physics_system; class subterrain_system; @@ -240,6 +240,7 @@ public: input::action camera_orbit_right_action; input::action camera_orbit_up_action; input::action camera_orbit_down_action; + input::action camera_look_ahead_action; input::action camera_preset_1_action; input::action camera_preset_2_action; input::action camera_preset_3_action; @@ -259,6 +260,7 @@ public: input::action ant_move_right_action; input::action ant_move_fast_action; input::action ant_move_slow_action; + input::action ant_interact_action; input::action ant_oviposit_action; input::action_map debug_action_map; @@ -272,17 +274,27 @@ public: std::vector> menu_mouse_subscriptions; std::vector> movement_action_subscriptions; - // Control settings + // Mouse settings double mouse_radians_per_pixel{math::radians(0.1)}; double mouse_pan_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_grip{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 bool debug_ui_visible{false}; @@ -404,6 +416,7 @@ public: std::unique_ptr<::constraint_system> constraint_system; std::unique_ptr<::steering_system> steering_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<::locomotion_system> locomotion_system; std::unique_ptr<::ik_system> ik_system; @@ -412,7 +425,6 @@ public: std::unique_ptr<::render_system> render_system; std::unique_ptr<::subterrain_system> subterrain_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<::blackbody_system> blackbody_system; std::unique_ptr<::atmosphere_system> atmosphere_system; diff --git a/src/game/states/experiments/treadmill-experiment-state.cpp b/src/game/states/experiments/treadmill-experiment-state.cpp index 2b97460..76faec3 100644 --- a/src/game/states/experiments/treadmill-experiment-state.cpp +++ b/src/game/states/experiments/treadmill-experiment-state.cpp @@ -28,7 +28,6 @@ #include "game/components/constraint-stack-component.hpp" #include "game/components/scene-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-constraint-component.hpp" #include "game/components/steering-component.hpp" @@ -40,7 +39,7 @@ #include "game/components/ik-component.hpp" #include "game/components/transform-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/constraints/child-of-constraint.hpp" #include "game/constraints/copy-rotation-constraint.hpp" @@ -61,6 +60,7 @@ #include "game/systems/atmosphere-system.hpp" #include "game/systems/camera-system.hpp" #include "game/systems/collision-system.hpp" +#include "game/systems/physics-system.hpp" #include "game/world.hpp" #include #include @@ -75,6 +75,7 @@ #include #include #include +#include #include #include #include @@ -97,16 +98,6 @@ treadmill_experiment_state::treadmill_experiment_state(::game& ctx): { 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_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(ctx.resource_manager->load("mobius-strip-nest-400mm.mdl")); - treadmill_scene_component.layer_mask = 1; - ctx.entity_registry->emplace(treadmill_eid, std::move(treadmill_scene_component)); - - // Load navmesh - navmesh = ctx.resource_manager->load("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(ctx.resource_manager->load("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(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(ctx.resource_manager->load("round-petri-dish-nest-100mm-interior.mdl")); + // nest_interior_scene_component.layer_mask = 1; + + // auto nest_interior_mesh = ctx.resource_manager->load("round-petri-dish-nest-100mm-interior.msh"); + // geom::generate_vertex_normals(*nest_interior_mesh); + + // auto nest_interior_rigid_body = std::make_unique(); + // nest_interior_rigid_body->set_mass(0.0f); + // nest_interior_rigid_body->set_collider(std::make_shared(std::move(nest_interior_mesh))); + + // auto nest_interior_eid = ctx.entity_registry->create(); + // ctx.entity_registry->emplace(nest_interior_eid, std::move(nest_interior_scene_component)); + // ctx.entity_registry->emplace(nest_interior_eid, std::move(nest_interior_rigid_body)); + // } - // Build navmesh BVH - debug::log::info("Building BVH..."); - navmesh_bvh = std::make_unique(*navmesh); - debug::log::info("Built BVH"); + { + scene_component nest_interior_scene_component; + nest_interior_scene_component.object = std::make_shared(ctx.resource_manager->load("soil-nest.mdl")); + nest_interior_scene_component.layer_mask = 1; + + auto nest_interior_mesh = ctx.resource_manager->load("soil-nest.msh"); + + auto nest_interior_rigid_body = std::make_unique(); + nest_interior_rigid_body->set_mass(0.0f); + nest_interior_rigid_body->set_collider(std::make_shared(std::move(nest_interior_mesh))); + + auto nest_interior_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(nest_interior_eid, std::move(nest_interior_scene_component)); + ctx.entity_registry->emplace(nest_interior_eid, std::move(nest_interior_rigid_body)); + } // Create worker auto worker_skeletal_mesh = std::make_unique(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.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; @@ -302,7 +320,7 @@ treadmill_experiment_state::treadmill_experiment_state(::game& ctx): ctx.ground_pass->set_enabled(true); sky_probe = std::make_shared(); - sky_probe->set_luminance_texture(std::make_shared(384, 512, gl::pixel_type::float_16, gl::pixel_format::rgb)); + sky_probe->set_luminance_texture(std::make_shared(512, 384, gl::pixel_type::float_16, gl::pixel_format::rgb)); ctx.sky_pass->set_sky_probe(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() { const auto& subject_rigid_body = *ctx.entity_registry->get(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(subject_rigid_body.get_transform().scale.x()); - third_person_camera_rig_eid = ctx.entity_registry->create(); - ctx.entity_registry->emplace(third_person_camera_rig_eid, ctx.surface_camera, std::uint8_t{1}); - ctx.entity_registry->emplace(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(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(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; + spring_arm.max_angles.x() = 0.0; + + third_person_camera_rig_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(third_person_camera_rig_eid, ctx.surface_camera, std::uint8_t{1}); + ctx.entity_registry->emplace(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() @@ -394,49 +410,22 @@ void treadmill_experiment_state::destroy_third_person_camera_rig() void treadmill_experiment_state::set_third_person_camera_zoom(double zoom) { - zoom = std::min(std::max(zoom, 0.0), 1.0); - - auto& orbit_cam = ctx.entity_registry->get(third_person_camera_rig_eid); - orbit_cam.zoom = zoom; } void treadmill_experiment_state::set_third_person_camera_rotation(double yaw, double pitch) { - pitch = std::min(math::half_pi, std::max(-math::half_pi, pitch)); - - auto& orbit_cam = ctx.entity_registry->get(third_person_camera_rig_eid); - orbit_cam.yaw = yaw; - orbit_cam.pitch = pitch; } void treadmill_experiment_state::zoom_third_person_camera(double zoom) { - const auto& orbit_cam = ctx.entity_registry->get(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) { - auto& orbit_cam = ctx.entity_registry->get(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) { - const auto& orbit_cam = ctx.entity_registry->get(third_person_camera_rig_eid); - - const double yaw = orbit_cam.yaw - ctx.mouse_pan_factor * static_cast(event.difference.x()); - const double pitch = orbit_cam.pitch + ctx.mouse_tilt_factor * static_cast(event.difference.y()); - - set_third_person_camera_rotation(yaw, pitch); } 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_ray = get_mouse_ray(mouse_position); - debug::log::info("pick:"); - float nearest_hit = std::numeric_limits::infinity(); - bool hit = false; - std::uint32_t hit_index; - const auto& vertex_positions = navmesh->vertices().attributes().at("position"); - const auto& face_normals = navmesh->faces().attributes().at("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(std::get<0>(*trace)), std::get<1>(*trace), std::get<2>(*trace)); + + const auto& hit_rigid_body = *ctx.entity_registry->get(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(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("normal"); // 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(worker_eid).body; 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_previous_transform(agent_transform); @@ -609,17 +566,11 @@ void treadmill_experiment_state::setup_controls() worker_eid, [&](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"); } } ) diff --git a/src/game/states/experiments/treadmill-experiment-state.hpp b/src/game/states/experiments/treadmill-experiment-state.hpp index 374e387..043526c 100644 --- a/src/game/states/experiments/treadmill-experiment-state.hpp +++ b/src/game/states/experiments/treadmill-experiment-state.hpp @@ -112,8 +112,6 @@ private: std::shared_ptr light_rectangle_emissive; std::shared_ptr light_probe; - std::shared_ptr navmesh; - std::unique_ptr navmesh_bvh; entity::id larva_eid; entity::id worker_eid; diff --git a/src/game/states/main-menu-state.cpp b/src/game/states/main-menu-state.cpp index cec2e2d..fe34f2e 100644 --- a/src/game/states/main-menu-state.cpp +++ b/src/game/states/main-menu-state.cpp @@ -277,8 +277,8 @@ main_menu_state::main_menu_state(::game& ctx, bool fade_in): ctx.sky_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 window_resized_subscription = ctx.window->get_resized_channel().subscribe diff --git a/src/game/states/nest-selection-state.cpp b/src/game/states/nest-selection-state.cpp index 0ab8a9f..5b5855e 100644 --- a/src/game/states/nest-selection-state.cpp +++ b/src/game/states/nest-selection-state.cpp @@ -25,7 +25,6 @@ #include "game/components/constraint-stack-component.hpp" #include "game/components/scene-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-constraint-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(); - ctx.entity_registry->emplace(cocoon_eid, std::make_shared(worker_phenome.cocoon->model), std::uint8_t{1}); + ctx.entity_registry->emplace(cocoon_eid, std::make_shared(worker_phenome.pupa->cocoon_model), std::uint8_t{1}); larva_eid = ctx.entity_registry->create(); @@ -227,9 +226,9 @@ nest_selection_state::nest_selection_state(::game& ctx): const float aspect_ratio = static_cast(viewport_size[0]) / static_cast(viewport_size[1]); // 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_max_elevation = 150.0f; 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) { - // 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 - // ( - // first_person_camera_rig_spring_translation_eid, - // [&](auto& component) - // { - // component.spring.x1[1] = elevation; - // } - // ); - - // ctx.entity_registry->patch - // ( - // 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) { - // 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(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 - // ( - // first_person_camera_rig_spring_translation_eid, - // [&](auto& component) - // { - // component.spring.x1 += velocity * static_cast(1.0 / ctx.fixed_update_rate); - // } - // ); + } void nest_selection_state::satisfy_first_person_camera_rig_constraints() { - // Satisfy first person camera rig spring translation constraint - // ctx.entity_registry->patch - // ( - // 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 - // ( - // 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 - // ( - // 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() @@ -478,11 +411,6 @@ void nest_selection_state::setup_controls() { const transform_component& first_person_camera_rig_transform = ctx.entity_registry->get(first_person_camera_rig_eid); - //const spring_rotation_constraint& first_person_camera_rig_spring_rotation = ctx.entity_registry->get(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(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]}; @@ -822,159 +750,7 @@ void nest_selection_state::setup_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()); - if (ctx.config->contains("mouse_pan_sensitivity")) - mouse_pan_sensitivity = math::radians((*ctx.config)["mouse_pan_sensitivity"].get()); - if (ctx.config->contains("mouse_invert_tilt")) - mouse_invert_tilt = (*ctx.config)["mouse_invert_tilt"].get(); - if (ctx.config->contains("mouse_invert_pan")) - mouse_invert_pan = (*ctx.config)["mouse_invert_pan"].get(); - if (ctx.config->contains("mouse_look_toggle")) - mouse_look_toggle = (*ctx.config)["mouse_look_toggle"].get(); - if (ctx.config->contains("gamepad_tilt_sensitivity")) - gamepad_tilt_sensitivity = math::radians((*ctx.config)["gamepad_tilt_sensitivity"].get()); - if (ctx.config->contains("gamepad_pan_sensitivity")) - gamepad_pan_sensitivity = math::radians((*ctx.config)["gamepad_pan_sensitivity"].get()); - if (ctx.config->contains("gamepad_invert_tilt")) - gamepad_invert_tilt = (*ctx.config)["gamepad_invert_tilt"].get(); - if (ctx.config->contains("gamepad_invert_pan")) - gamepad_invert_pan = (*ctx.config)["gamepad_invert_pan"].get(); - - // 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 - ( - first_person_camera_rig_spring_rotation_eid, - [&, gamepad_pan_factor](auto& component) - { - component.spring.x1[0] -= gamepad_pan_factor * value * static_cast(1.0 / ctx.fixed_update_rate); - } - ); - } - ); - ctx.controls["look_left_gamepad"]->set_active_callback - ( - [&, gamepad_pan_factor](float value) - { - ctx.entity_registry->patch - ( - first_person_camera_rig_spring_rotation_eid, - [&, gamepad_pan_factor](auto& component) - { - component.spring.x1[0] += gamepad_pan_factor * value * static_cast(1.0 / ctx.fixed_update_rate); - } - ); - } - ); - ctx.controls["look_up_gamepad"]->set_active_callback - ( - [&, gamepad_tilt_factor](float value) - { - ctx.entity_registry->patch - ( - first_person_camera_rig_spring_rotation_eid, - [&, gamepad_tilt_factor](auto& component) - { - component.spring.x1[1] -= gamepad_tilt_factor * value * static_cast(1.0 / ctx.fixed_update_rate); - component.spring.x1[1] = std::max(-math::half_pi, component.spring.x1[1]); - } - ); - } - ); - ctx.controls["look_down_gamepad"]->set_active_callback - ( - [&, gamepad_tilt_factor](float value) - { - ctx.entity_registry->patch - ( - first_person_camera_rig_spring_rotation_eid, - [&, gamepad_tilt_factor](auto& component) - { - component.spring.x1[1] += gamepad_tilt_factor * value * static_cast(1.0 / ctx.fixed_update_rate); - component.spring.x1[1] = std::min(math::half_pi, 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(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(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() diff --git a/src/game/states/nest-view-state.cpp b/src/game/states/nest-view-state.cpp index d38bf9d..a43dca7 100644 --- a/src/game/states/nest-view-state.cpp +++ b/src/game/states/nest-view-state.cpp @@ -27,7 +27,6 @@ #include "game/components/constraint-stack-component.hpp" #include "game/components/scene-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-constraint-component.hpp" #include "game/components/steering-component.hpp" @@ -186,7 +185,7 @@ nest_view_state::nest_view_state(::game& ctx): // Create cocoon auto cocoon_eid = ctx.entity_registry->create(); - auto cocoon_static_mesh = std::make_shared(worker_phenome.cocoon->model); + auto cocoon_static_mesh = std::make_shared(worker_phenome.pupa->cocoon_model); cocoon_static_mesh->set_scale(worker_phenome.body_size->mean_mesosoma_length); ctx.entity_registry->emplace(cocoon_eid, std::move(cocoon_static_mesh), std::uint8_t{2}); ctx.entity_registry->patch diff --git a/src/game/states/nuptial-flight-state.cpp b/src/game/states/nuptial-flight-state.cpp index 1a7c797..163fd64 100644 --- a/src/game/states/nuptial-flight-state.cpp +++ b/src/game/states/nuptial-flight-state.cpp @@ -33,7 +33,6 @@ #include "game/components/constraint-stack-component.hpp" #include "game/components/steering-component.hpp" #include "game/components/picking-component.hpp" -#include "game/components/spring-component.hpp" #include "game/components/scene-component.hpp" #include "game/constraints/child-of-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_far_fov = math::vertical_fov(math::radians(60.0f), aspect_ratio); 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; // Create camera rig @@ -274,14 +273,7 @@ void nuptial_flight_state::create_camera_rig() // Construct camera rig spring rotation constraint 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; camera_rig_spring_rotation_node.active = true; 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 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; camera_rig_spring_translation_node.active = true; camera_rig_spring_translation_node.weight = 1.0f; @@ -328,25 +313,6 @@ void nuptial_flight_state::create_camera_rig() ctx.entity_registry->emplace(camera_rig_eid, camera_rig_transform); ctx.entity_registry->emplace(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(camera_rig_fov_spring_eid, camera_rig_fov_spring); - 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) { - 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 - ( - 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 - ( - camera_rig_fov_spring_eid, - [&](auto& component) - { - component.spring.x1 = fov; - } - ); + } void nuptial_flight_state::satisfy_camera_rig_constraints() { - // Satisfy camera rig focus ease to constraint - ctx.entity_registry->patch - ( - camera_rig_focus_ease_to_eid, - [&](auto& component) - { - component.t = component.duration; - } - ); - - // Satisfy camera rig spring translation constraint - ctx.entity_registry->patch - ( - 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 - ( - 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 - ( - camera_rig_fov_spring_eid, - [&](auto& component) - { - component.spring.x0 = component.spring.x1; - component.spring.v *= 0.0f; - } - ); + } 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 - ( - [&](const auto& event) - { - if (!mouse_look) - { - return; - } - - ctx.entity_registry->patch - ( - camera_rig_spring_rotation_eid, - [&](auto& component) - { - component.spring.x1[0] += static_cast(ctx.mouse_pan_factor) * static_cast(event.difference.x()); - component.spring.x1[1] -= static_cast(ctx.mouse_tilt_factor) * static_cast(event.difference.y()); - component.spring.x1[1] = std::min(math::half_pi, std::max(-math::half_pi, 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(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(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(mouse_position.x()) / static_cast(viewport_size.x() - 1) * 2.0f - 1.0f, - (1.0f - static_cast(mouse_position.y()) / static_cast(viewport_size.y() - 1)) * 2.0f - 1.0f - }; - - // Get picking ray from camera - const geom::ray 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() { - /* - // 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()); - if (ctx.config->contains("mouse_pan_sensitivity")) - mouse_pan_sensitivity = math::radians((*ctx.config)["mouse_pan_sensitivity"].get()); - if (ctx.config->contains("mouse_invert_tilt")) - mouse_invert_tilt = (*ctx.config)["mouse_invert_tilt"].get(); - if (ctx.config->contains("mouse_invert_pan")) - mouse_invert_pan = (*ctx.config)["mouse_invert_pan"].get(); - if (ctx.config->contains("mouse_look_toggle")) - mouse_look_toggle = (*ctx.config)["mouse_look_toggle"].get(); - if (ctx.config->contains("gamepad_tilt_sensitivity")) - gamepad_tilt_sensitivity = math::radians((*ctx.config)["gamepad_tilt_sensitivity"].get()); - if (ctx.config->contains("gamepad_pan_sensitivity")) - gamepad_pan_sensitivity = math::radians((*ctx.config)["gamepad_pan_sensitivity"].get()); - if (ctx.config->contains("gamepad_invert_tilt")) - gamepad_invert_tilt = (*ctx.config)["gamepad_invert_tilt"].get(); - if (ctx.config->contains("gamepad_invert_pan")) - gamepad_invert_pan = (*ctx.config)["gamepad_invert_pan"].get(); - - // 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 - ( - 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 - ( - camera_rig_spring_rotation_eid, - [&, gamepad_pan_factor](auto& component) - { - component.spring.x1[0] -= gamepad_pan_factor * value * static_cast(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 - ( - 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 - ( - camera_rig_spring_rotation_eid, - [&, gamepad_pan_factor](auto& component) - { - component.spring.x1[0] += gamepad_pan_factor * value * static_cast(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 - ( - 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, component.spring.x1[1]); - } - ); - } - ); - ctx.controls["look_up_gamepad"]->set_active_callback - ( - [&, gamepad_tilt_factor](float value) - { - ctx.entity_registry->patch - ( - camera_rig_spring_rotation_eid, - [&, gamepad_tilt_factor](auto& component) - { - component.spring.x1[1] -= gamepad_tilt_factor * value * static_cast(1.0 / ctx.fixed_update_rate); - component.spring.x1[1] = std::max(-math::half_pi, 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 - ( - 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, component.spring.x1[1]); - } - ); - } - ); - ctx.controls["look_down_gamepad"]->set_active_callback - ( - [&, gamepad_tilt_factor](float value) - { - ctx.entity_registry->patch - ( - camera_rig_spring_rotation_eid, - [&, gamepad_tilt_factor](auto& component) - { - component.spring.x1[1] += gamepad_tilt_factor * value * static_cast(1.0 / ctx.fixed_update_rate); - component.spring.x1[1] = std::min(math::half_pi, 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(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(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(mouse_x) / static_cast(viewport_size[0] - 1) * 2.0f - 1.0f, - (1.0f - static_cast(mouse_y) / static_cast(viewport_size[1] - 1)) * 2.0f - 1.0f - }; - - // Get picking ray from camera - const geom::ray 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(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(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(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(1.0 / ctx.fixed_update_rate)); - debug::log::info("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); - } - ); - */ + } 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) diff --git a/src/game/systems/camera-system.cpp b/src/game/systems/camera-system.cpp index 1558ba1..bcc5140 100644 --- a/src/game/systems/camera-system.cpp +++ b/src/game/systems/camera-system.cpp @@ -18,11 +18,13 @@ */ #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 #include #include +#include #include 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) { - + m_fixed_update_time = static_cast(t); + m_fixed_timestep = static_cast(dt); } void camera_system::interpolate(float alpha) { - auto orbit_cam_group = registry.group(entt::get); + const double variable_update_time = m_fixed_update_time + m_fixed_timestep * static_cast(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(entt::get); std::for_each ( std::execution::seq, - orbit_cam_group.begin(), - orbit_cam_group.end(), + autofocus_group.begin(), + autofocus_group.end(), [&](auto entity_id) { - auto& orbit_cam = orbit_cam_group.get(entity_id); - auto& scene = orbit_cam_group.get(entity_id); - auto& camera = static_cast(*scene.object); + auto& autofocus = autofocus_group.get(entity_id); + auto& camera = static_cast(*autofocus_group.get(entity_id).object); + + // Clamp zoom factor + autofocus.zoom = std::min(std::max(autofocus.zoom, 0.0), 1.0); + + // Calculate horizontal and vertical FoV + autofocus.hfov = ease::out_sine(autofocus.far_hfov, autofocus.near_hfov, autofocus.zoom); + autofocus.vfov = math::vertical_fov(autofocus.hfov, static_cast(camera.get_aspect_ratio())); + + // Calculate focal plane dimensions + autofocus.focal_plane_size.y() = ease::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(camera.get_aspect_ratio()); + + // Calculate focal distance + autofocus.focal_distance = autofocus.focal_plane_height * 0.5 / std::tan(autofocus.vfov * 0.5); - math::transform subject_transform = math::transform::identity(); - if (orbit_cam.subject_eid != entt::null) + // Update camera projection matrix + camera.set_perspective(static_cast(autofocus.vfov), camera.get_aspect_ratio(), camera.get_clip_near(), camera.get_clip_far()); + } + ); + */ + + auto spring_arm_group = registry.group(entt::get); + std::for_each + ( + std::execution::seq, + spring_arm_group.begin(), + spring_arm_group.end(), + [&](auto entity_id) + { + auto& spring_arm = spring_arm_group.get(entity_id); + auto& camera = static_cast(*spring_arm_group.get(entity_id).object); + + math::transform parent_transform = math::transform::identity(); + if (spring_arm.parent_eid != entt::null) { - const auto subject_scene = registry.try_get(orbit_cam.subject_eid); - if (subject_scene) + const auto parent_scene = registry.try_get(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 - 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::in_sine(1.0, 0.0, spring_arm.angles_spring.get_value().x() / -math::half_pi); + } // Clamp zoom - orbit_cam.zoom = std::min(std::max(orbit_cam.zoom, 0.0), 1.0); + spring_arm.zoom = std::min(std::max(spring_arm.zoom, 0.0), 1.0); // Update FoV - orbit_cam.hfov = ease::out_sine(orbit_cam.far_hfov, orbit_cam.near_hfov, orbit_cam.zoom); - orbit_cam.vfov = math::vertical_fov(orbit_cam.hfov, static_cast(camera.get_aspect_ratio())); + spring_arm.hfov = ease::out_sine(spring_arm.far_hfov, spring_arm.near_hfov, spring_arm.zoom); + spring_arm.vfov = math::vertical_fov(spring_arm.hfov, static_cast(camera.get_aspect_ratio())); // Update focal plane size - orbit_cam.focal_plane_height = ease::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(camera.get_aspect_ratio()); + spring_arm.focal_plane_height = ease::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(camera.get_aspect_ratio()); // 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 - 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 camera_transform; 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}; - // double center_offset = (1.0 - std::abs(orbit_cam.pitch) / math::half_pi) * (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) * (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_perspective(static_cast(orbit_cam.vfov), camera.get_aspect_ratio(), camera.get_clip_near(), camera.get_clip_far()); + camera.set_perspective(static_cast(spring_arm.vfov), camera.get_aspect_ratio(), camera.get_clip_near(), camera.get_clip_far()); } ); } diff --git a/src/game/systems/camera-system.hpp b/src/game/systems/camera-system.hpp index 84c200f..07f12d6 100644 --- a/src/game/systems/camera-system.hpp +++ b/src/game/systems/camera-system.hpp @@ -35,6 +35,9 @@ public: private: math::dvec4 m_viewport{}; double m_aspect_ratio{}; + double m_fixed_update_time{}; + double m_fixed_timestep{}; + double m_variable_update_time{}; }; #endif // ANTKEEPER_GAME_CAMERA_SYSTEM_HPP diff --git a/src/game/systems/constraint-system.cpp b/src/game/systems/constraint-system.cpp index 8bdce0a..8b9a304 100644 --- a/src/game/systems/constraint-system.cpp +++ b/src/game/systems/constraint-system.cpp @@ -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) { // Solve yaw, pitch, and roll angle spring - solve_numeric_spring(constraint.spring, dt); + constraint.spring.solve(dt); // 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 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) { // Update translation spring target - constraint.translation.x1 = target_transform->world.translation; + constraint.translation.set_target_value(target_transform->world.translation); // Solve translation spring - solve_numeric_spring(constraint.translation, dt); + constraint.translation.solve(dt); // Update transform translation - transform.world.translation = constraint.translation.x0; + transform.world.translation = constraint.translation.get_value(); } // Spring rotation if (constraint.spring_rotation) { // 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_numeric_spring(constraint.rotation, dt); + constraint.rotation.solve(dt); // 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) { // Solve translation spring - solve_numeric_spring(constraint.spring, dt); + constraint.spring.solve(dt); // 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) diff --git a/src/game/systems/locomotion-system.cpp b/src/game/systems/locomotion-system.cpp index fa19c59..c32e28b 100644 --- a/src/game/systems/locomotion-system.cpp +++ b/src/game/systems/locomotion-system.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -53,17 +54,34 @@ void locomotion_system::update_legged(float t, float dt) { auto& locomotion = legged_group.get(entity_id); - if (locomotion.angular_velocity != 0.0f) + + float cos_target_direction{}; + if (locomotion.speed != 0.0f) { auto& rigid_body = *legged_group.get(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 auto& navmesh_agent = legged_group.get(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 auto& rigid_body = *legged_group.get(entity_id).body; diff --git a/src/game/systems/metabolic-system.cpp b/src/game/systems/metabolic-system.cpp new file mode 100644 index 0000000..d079107 --- /dev/null +++ b/src/game/systems/metabolic-system.cpp @@ -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 . + */ + +#include "game/systems/metabolic-system.hpp" +#include "game/components/isometric-growth-component.hpp" +#include "game/components/rigid-body-component.hpp" +#include + +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(entt::get); + std::for_each + ( + std::execution::seq, + isometric_growth_group.begin(), + isometric_growth_group.end(), + [&](auto entity_id) + { + auto& growth = isometric_growth_group.get(entity_id); + auto& rigid_body = *isometric_growth_group.get(entity_id).body; + + rigid_body.set_scale(rigid_body.get_scale() + growth.rate * scaled_timestep); + } + ); +} diff --git a/src/game/systems/metabolic-system.hpp b/src/game/systems/metabolic-system.hpp new file mode 100644 index 0000000..262d666 --- /dev/null +++ b/src/game/systems/metabolic-system.hpp @@ -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 . + */ + +#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 diff --git a/src/game/systems/metamorphosis-system.cpp b/src/game/systems/metamorphosis-system.cpp index 235e625..ff0b318 100644 --- a/src/game/systems/metamorphosis-system.cpp +++ b/src/game/systems/metamorphosis-system.cpp @@ -19,8 +19,13 @@ #include "game/systems/metamorphosis-system.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/ant-genome-component.hpp" +#include #include #include #include @@ -31,6 +36,10 @@ metamorphosis_system::metamorphosis_system(entity::registry& registry): 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(entt::get); std::for_each ( @@ -40,19 +49,153 @@ void metamorphosis_system::update(float t, float dt) [&](auto entity_id) { auto& egg = egg_group.get(entity_id); - if (egg.elapsed_incubation_time >= egg.incubation_period) + if (egg.incubation_phase >= 1.0f) { 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(entity_id).genome; const auto layer_mask = registry.get(entity_id).layer_mask; + auto& rigid_body = *registry.get(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(entity_id); + + // Replace egg model with larva model registry.erase(entity_id); registry.emplace(entity_id, std::make_shared(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(entity_id, std::move(larva)); + + // Begin isometric growth + registry.emplace(entity_id, growth_rate); + } + } + ); + + // Develop larvae + auto larva_group = registry.group(entt::get); + std::for_each + ( + std::execution::seq, + larva_group.begin(), + larva_group.end(), + [&](auto entity_id) + { + auto& larva = larva_group.get(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(entity_id).body; + const auto& genome = *larva_group.get(entity_id).genome; + const auto layer_mask = registry.get(entity_id).layer_mask; + + // Halt isometric growth + registry.remove(entity_id); + + // Construct cocoon mesh + auto cocoon_mesh = std::make_shared(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(*cocoon_mesh->get_model()->get_groups().front().material); + + // Store cocoon material spinning phase variable + larva.spinning_phase_matvar = std::static_pointer_cast(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(larva.cocoon_eid, std::move(cocoon_mesh), layer_mask); + } + } + else if (larva.spinning_phase < 1.0f) + { + const auto& genome = *larva_group.get(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(entity_id); + + // Erase scene component + registry.erase(entity_id); + + // Define pupal development period + pupa_component pupa; + pupa.development_period = genome.pupa->phenes.front().development_period; + registry.emplace(entity_id, std::move(pupa)); + } + } + } + ); + + // Develop pupae + auto pupa_group = registry.group(entt::get); + std::for_each + ( + std::execution::seq, + pupa_group.begin(), + pupa_group.end(), + [&](auto entity_id) + { + auto& pupa = pupa_group.get(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(entity_id).genome; + + // Erase pupa component + registry.erase(entity_id); + + // Construct adult model + // registry.emplace(entity_id, std::make_shared(ant_model), layer_mask); } } ); diff --git a/src/game/systems/physics-system.cpp b/src/game/systems/physics-system.cpp index 000ae0f..7937817 100644 --- a/src/game/systems/physics-system.cpp +++ b/src/game/systems/physics-system.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -108,23 +109,71 @@ void physics_system::interpolate(float alpha) ); } -void physics_system::integrate(float dt) +std::optional> physics_system::trace(const geom::ray& 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; - 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::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(); + 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(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 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(*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(); std::for_each ( @@ -136,7 +185,7 @@ void physics_system::integrate(float dt) auto& body = *(view.get(entity_id).body); // 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); } diff --git a/src/game/systems/physics-system.hpp b/src/game/systems/physics-system.hpp index fdd3372..e608baf 100644 --- a/src/game/systems/physics-system.hpp +++ b/src/game/systems/physics-system.hpp @@ -23,10 +23,14 @@ #include "game/systems/updatable-system.hpp" #include #include +#include #include #include +#include #include #include +#include +#include /** * @@ -50,6 +54,17 @@ public: 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> trace(const geom::ray& ray, entity::id ignore_eid = entt::null, std::uint32_t layer_mask = ~std::uint32_t{0}) const; + private: using collision_manifold_type = physics::collision_manifold<4>; diff --git a/src/game/systems/reproductive-system.cpp b/src/game/systems/reproductive-system.cpp index 8883d67..3cd16bd 100644 --- a/src/game/systems/reproductive-system.cpp +++ b/src/game/systems/reproductive-system.cpp @@ -24,6 +24,7 @@ #include "game/components/rigid-body-component.hpp" #include "game/components/egg-component.hpp" #include "game/components/ant-genome-component.hpp" +#include "game/systems/physics-system.hpp" #include #include #include @@ -115,16 +116,27 @@ void reproductive_system::update(float t, float dt) if (ovary.elapsed_oviposition_time >= ovary.oviposition_duration) { - // Construct egg component - egg_component egg; - egg.incubation_period = 5.0f; - registry.emplace(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(ovary.ovipositor_egg_eid).body; + const auto oviposition_ray = geom::ray{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(ovary.ovipositor_egg_eid).genome; + + // Construct egg component + registry.emplace(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; + } } } } diff --git a/src/game/systems/reproductive-system.hpp b/src/game/systems/reproductive-system.hpp index a2b7564..4ab8d48 100644 --- a/src/game/systems/reproductive-system.hpp +++ b/src/game/systems/reproductive-system.hpp @@ -22,6 +22,8 @@ #include "game/systems/updatable-system.hpp" +class physics_system; + /** * */ @@ -42,8 +44,14 @@ public: m_time_scale = scale; } + inline constexpr void set_physics_system(physics_system* physics_system) noexcept + { + m_physics_system = physics_system; + } + private: float m_time_scale{1.0f}; + physics_system* m_physics_system{}; }; #endif // ANTKEEPER_GAME_REPRODUCTIVE_SYSTEM_HPP diff --git a/src/game/systems/spring-system.cpp b/src/game/systems/spring-system.cpp deleted file mode 100644 index ea5d5ff..0000000 --- a/src/game/systems/spring-system.cpp +++ /dev/null @@ -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 . - */ - -#include "game/systems/spring-system.hpp" -#include "game/components/spring-component.hpp" -#include - - -spring_system::spring_system(entity::registry& registry): - updatable_system(registry) -{} - -spring_system::~spring_system() -{} - -void spring_system::update(float t, float dt) -{ - registry.view().each - ( - [&](entity::id spring_eid, auto& component) - { - solve_numeric_spring(component.spring, dt); - if (component.callback) - component.callback(component.spring.x0); - } - ); - - registry.view().each - ( - [&](entity::id spring_eid, auto& component) - { - solve_numeric_spring(component.spring, dt); - if (component.callback) - component.callback(component.spring.x0); - } - ); - - registry.view().each - ( - [&](entity::id spring_eid, auto& component) - { - solve_numeric_spring(component.spring, dt); - if (component.callback) - component.callback(component.spring.x0); - } - ); - - registry.view().each - ( - [&](entity::id spring_eid, auto& component) - { - solve_numeric_spring(component.spring, dt); - if (component.callback) - component.callback(component.spring.x0); - } - ); -} - diff --git a/src/game/textures/cocoon-silk-sdf.cpp b/src/game/textures/cocoon-silk-sdf.cpp new file mode 100644 index 0000000..76ad8ca --- /dev/null +++ b/src/game/textures/cocoon-silk-sdf.cpp @@ -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 . + */ + +#include "game/textures/cocoon-silk-sdf.hpp" +#include +#include +#include +#include +#include +#include +#include + +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(width - 1) * frequency; + float scale_y = 1.0f / static_cast(height - 1) * frequency; + + std::for_each + ( + std::execution::par_unseq, + img.begin>(), + img.end>(), + [pixels, width, height, scale_x, scale_y, frequency](auto& pixel) + { + const std::size_t i = &pixel - (math::vec4*)pixels; + const std::size_t y = i / width; + const std::size_t x = i % width; + + const math::fvec2 position = + { + static_cast(x) * scale_x, + static_cast(y) * scale_y + }; + + const auto + [ + f1_sqr_distance, + f1_displacement, + f1_id, + f1_edge_sqr_distance + ] = math::noise::voronoi::f1_edge(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(std::min(255.0f, f1_edge_distance * scale)), + static_cast(std::min(255.0f, f1_edge_distance * scale)), + static_cast(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(std::min(255.0f, f1_distance * 255.0f)), + // static_cast(std::min(255.0f, uv[0] * 255.0f)), + // static_cast(std::min(255.0f, uv[1] * 255.0f)), + // static_cast(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()); +} diff --git a/src/game/textures/cocoon-silk-sdf.hpp b/src/game/textures/cocoon-silk-sdf.hpp new file mode 100644 index 0000000..d512923 --- /dev/null +++ b/src/game/textures/cocoon-silk-sdf.hpp @@ -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 . + */ + +#ifndef ANTKEEPER_COCOON_SILK_SDF_HPP +#define ANTKEEPER_COCOON_SILK_SDF_HPP + +#include + +/// Generates the cocoon silk signed distance field texture. +void generate_silk_sdf_image(std::filesystem::path path); + +#endif // ANTKEEPER_COCOON_SILK_SDF_HPP diff --git a/src/game/textures/rgb-voronoi-noise.cpp b/src/game/textures/rgb-voronoi-noise.cpp new file mode 100644 index 0000000..8477285 --- /dev/null +++ b/src/game/textures/rgb-voronoi-noise.cpp @@ -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 . + */ + +#include "game/textures/rgb-voronoi-noise.hpp" +#include +#include +#include +#include +#include +#include +#include + +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(width - 1) * frequency; + float scale_y = 1.0f / static_cast(height - 1) * frequency; + + std::for_each + ( + std::execution::par_unseq, + img.begin>(), + img.end>(), + [pixels, width, height, scale_x, scale_y, frequency](auto& pixel) + { + const std::size_t i = &pixel - (math::vec4*)pixels; + const std::size_t y = i / width; + const std::size_t x = i % width; + + const math::fvec2 position = + { + static_cast(x) * scale_x, + static_cast(y) * scale_y + }; + + const auto + [ + f1_sqr_distance, + f1_displacement, + f1_id, + f1_edge_sqr_distance + ] = math::noise::voronoi::f1_edge(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(f1_id & 255), + static_cast((f1_id >> 8) & 255), + static_cast((f1_id >> 16) & 255), + static_cast((f1_id >> 24) & 255) + }; + + // const float f1_distance = std::sqrt(f1_sqr_distance); + // const math::fvec2 uv = (position + f1_displacement) / frequency; + + // pixel = + // { + // static_cast(std::min(255.0f, f1_distance * 255.0f)), + // static_cast(std::min(255.0f, uv[0] * 255.0f)), + // static_cast(std::min(255.0f, uv[1] * 255.0f)), + // static_cast(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()); +} diff --git a/src/game/textures/rgb-voronoi-noise.hpp b/src/game/textures/rgb-voronoi-noise.hpp new file mode 100644 index 0000000..2149160 --- /dev/null +++ b/src/game/textures/rgb-voronoi-noise.hpp @@ -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 . + */ + +#ifndef ANTKEEPER_RGB_VORONOI_NOISE_HPP +#define ANTKEEPER_RGB_VORONOI_NOISE_HPP + +#include + +void generate_rgb_voronoi_noise(std::filesystem::path path); + +#endif // ANTKEEPER_RGB_VORONOI_NOISE_HPP diff --git a/src/game/world.cpp b/src/game/world.cpp index e17f063..1a6ba54 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -68,6 +68,8 @@ #include #include #include +#include +#include namespace world { @@ -443,63 +445,7 @@ void create_moon(::game& ctx) } 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(width - 1) * frequency; - float scale_y = 1.0f / static_cast(height - 1) * frequency; - - std::for_each - ( - std::execution::par_unseq, - img.begin>(), - img.end>(), - [pixels, width, height, scale_x, scale_y, frequency](auto& pixel) - { - const std::size_t i = &pixel - (math::vec4*)pixels; - const std::size_t y = i / width; - const std::size_t x = i % width; - - const math::fvec2 position = - { - static_cast(x) * scale_x, - static_cast(y) * scale_y - }; - - const auto - [ - f1_sqr_distance, - f1_displacement, - f1_id - ] = math::noise::voronoi::f1(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(std::min(255.0f, f1_distance * 255.0f)), - static_cast(std::min(255.0f, uv[0] * 255.0f)), - static_cast(std::min(255.0f, uv[1] * 255.0f)), - static_cast(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); { // Set active ecoregion @@ -545,4 +491,10 @@ void enter_ecoregion(::game& ctx, const ecoregion& ecoregion) 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::out_cubic, false, [](){}); +} + } // namespace world diff --git a/src/game/world.hpp b/src/game/world.hpp index afae12d..cd0e951 100644 --- a/src/game/world.hpp +++ b/src/game/world.hpp @@ -80,6 +80,8 @@ void set_time_scale(::game& ctx, double scale); */ void enter_ecoregion(::game& ctx, const ecoregion& ecoregion); +void switch_scene(::game& ctx); + } // namespace menu #endif // ANTKEEPER_GAME_WORLD_HPP