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