diff --git a/src/engine/ai/navmesh.cpp b/src/engine/ai/navmesh.cpp
new file mode 100644
index 0000000..d08a23b
--- /dev/null
+++ b/src/engine/ai/navmesh.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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
+#include
+#include
+#include
+#include
+
+namespace ai {
+
+navmesh_traversal traverse_navmesh(const geom::brep_mesh& mesh, geom::brep_face* face, geom::ray ray, float distance)
+{
+ // Get vertex positions and face normals
+ const auto& vertex_positions = mesh.vertices().attributes().at("position");
+ const auto& face_normals = mesh.faces().attributes().at("normal");
+
+ // Init traversal result
+ navmesh_traversal traversal;
+ traversal.edge = nullptr;
+
+ // Save initial ray origin
+ auto initial_origin = ray.origin;
+
+ float segment_length = 0.0f;
+ int edge_index;
+ do
+ {
+ // Get triangle vertices
+ auto loop_it = face->loops().begin();
+ const auto& a = vertex_positions[loop_it->vertex()->index()];
+ const auto& b = vertex_positions[(++loop_it)->vertex()->index()];
+ const auto& c = vertex_positions[(++loop_it)->vertex()->index()];
+
+ // Find closest feature on triangle to segment endpoint
+ std::tie(traversal.barycentric, edge_index) = geom::closest_feature(a, b, c, ray.extrapolate(distance));
+
+ // Convert barycentric coordinates of closest point to Cartesian coordinates
+ traversal.cartesian = geom::barycentric_to_cartesian(traversal.barycentric, a, b, c);
+
+ // Subtract length of projected segment from remaining distance
+ segment_length = math::length(traversal.cartesian - ray.origin);
+ distance -= segment_length;
+
+ // Advance ray origin
+ ray.origin = traversal.cartesian;
+
+ // If no edge reached or no remaining distance, traversal is complete
+ if (edge_index < 0 || distance <= 0.0f)
+ {
+ break;
+ }
+
+ // Find loop and edge on which the closest point lies
+ auto closest_loop_it = face->loops().begin();
+ std::advance(closest_loop_it, edge_index);
+ geom::brep_loop* closest_loop = *closest_loop_it;
+ geom::brep_edge* closest_edge = closest_loop->edge();
+
+ // Abort if a boundary edge was reached
+ if (closest_edge->loops().size() == 1)
+ {
+ traversal.edge = closest_edge;
+ break;
+ }
+
+ // Find a loop and face that shares the closest edge
+ auto symmetric_loop_it = closest_edge->loops().begin();
+ if (*symmetric_loop_it == closest_loop)
+ {
+ ++symmetric_loop_it;
+ }
+ geom::brep_loop* symmetric_loop = *symmetric_loop_it;
+ geom::brep_face* symmetric_face = symmetric_loop->face();
+
+ // Find quaternion representing rotation from normal of first face to normal of second face
+ const auto& n0 = face_normals[face->index()];
+ const auto& n1 = face_normals[symmetric_face->index()];
+ const auto rotation = math::rotation(n0, n1);
+
+ // Rotate ray direction
+ ray.direction = rotation * ray.direction;
+
+ // Move to next face
+ face = symmetric_face;
+ }
+ while (segment_length > 0.0f);
+
+ traversal.face = face;
+ traversal.remaining_distance = distance;
+
+ return traversal;
+}
+
+} // namespace ai
diff --git a/src/engine/geom/spherical.hpp b/src/engine/ai/navmesh.hpp
similarity index 50%
rename from src/engine/geom/spherical.hpp
rename to src/engine/ai/navmesh.hpp
index 36d387d..387881a 100644
--- a/src/engine/geom/spherical.hpp
+++ b/src/engine/ai/navmesh.hpp
@@ -17,42 +17,32 @@
* along with Antkeeper source code. If not, see .
*/
-#ifndef ANTKEEPER_GEOM_SPHERICAL_HPP
-#define ANTKEEPER_GEOM_SPHERICAL_HPP
+#ifndef ANTKEEPER_AI_NAVMESH_HPP
+#define ANTKEEPER_AI_NAVMESH_HPP
#include
-#include
+#include
+#include
+#include
+#include
+#include
-namespace geom {
+namespace ai {
-/// Functions which operate on spherical coordinates.
-namespace spherical {
+struct navmesh_traversal
+{
+ geom::brep_face* face;
+ geom::brep_edge* edge;
+ geom::point barycentric;
+ geom::point cartesian;
+ float remaining_distance;
+};
/**
- * Converts spherical coordinates to Cartesian (rectangular) coordinates.
- *
- * @param v Spherical coordinates, in the ISO order of radial distance, polar angle (radians), and azimuthal angle (radians).
- * @return Cartesian coordinates.
- *
- * @see geom::coordinates::cartesian
+ *
*/
-template
-math::vec3 to_cartesian(const math::vec3& v);
-
-template
-math::vec3 to_cartesian(const math::vec3& v)
-{
- const T x = v[0] * std::cos(v[1]);
-
- return math::vec3
- {
- x * std::cos(v[2]),
- x * std::sin(v[2]),
- v[0] * std::sin(v[1])
- };
-}
+[[nodiscard]] navmesh_traversal traverse_navmesh(const geom::brep_mesh& mesh, geom::brep_face* face, geom::ray ray, float distance);
-} // namespace spherical
-} // namespace geom
+} // namespace ai
-#endif // ANTKEEPER_GEOM_SPHERICAL_HPP
+#endif // ANTKEEPER_AI_NAVMESH_HPP
diff --git a/src/engine/geom/cartesian.hpp b/src/engine/geom/cartesian.hpp
deleted file mode 100644
index c4effe0..0000000
--- a/src/engine/geom/cartesian.hpp
+++ /dev/null
@@ -1,58 +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_GEOM_CARTESIAN_HPP
-#define ANTKEEPER_GEOM_CARTESIAN_HPP
-
-#include
-#include
-
-namespace geom {
-
-/// Functions which operate on Cartesian (rectangular) coordinates.
-namespace cartesian {
-
-/**
- * Converts Cartesian (rectangular) coordinates to spherical coordinates.
- *
- * @param v Cartesian coordinates.
- * @return Spherical coordinates, in the ISO order of radial distance, polar angle (radians), and azimuthal angle (radians).
- *
- * @see geom::coordinates::spherical
- */
-template
-math::vec3 to_spherical(const math::vec3& v);
-
-template
-math::vec3 to_spherical(const math::vec3& v)
-{
- const T xx_yy = v.x() * v.x() + v.y() * v.y();
-
- return math::vec3
- {
- std::sqrt(xx_yy + v.z() * v.z()),
- std::atan2(v.z(), std::sqrt(xx_yy)),
- std::atan2(v.y(), v.x())
- };
-}
-
-} // namespace cartesian
-} // namespace geom
-
-#endif // ANTKEEPER_GEOM_CARTESIAN_HPP
diff --git a/src/engine/geom/closest-feature.hpp b/src/engine/geom/closest-feature.hpp
new file mode 100644
index 0000000..cb3cfc0
--- /dev/null
+++ b/src/engine/geom/closest-feature.hpp
@@ -0,0 +1,90 @@
+/*
+ * 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_GEOM_CLOSEST_FEATURE_HPP
+#define ANTKEEPER_GEOM_CLOSEST_FEATURE_HPP
+
+#include
+#include
+#include
+#include
+
+namespace geom {
+
+/**
+ * Calculates the closest features on a triangle to a point.
+ *
+ * @tparam T Real type.
+ *
+ * @param tri Triangle.
+ * @param a First point of triangle.
+ * @param b Second point of triangle.
+ * @param c Third point of triangle.
+ * @param p Point.
+ *
+ * @return Tuple containing the Barycentric coordinates of the closest point on the triangle to point @p p, followed by the index of the edge on which the point lies (`-1` if not on an edge).
+ */
+/// @{
+template
+[[nodiscard]] constexpr std::tuple, int> closest_feature(const point& a, const point& b, const point& c, const point& p) noexcept
+{
+ const auto ab = b - a;
+ const auto ca = a - c;
+ const auto ap = p - a;
+ const auto n = math::cross(ab, ca);
+ const auto d = math::sqr_length(n);
+ const auto q = math::cross(n, ap);
+
+ point uvw;
+ if ((uvw.z() = math::dot(q, ab) / d) < T{0})
+ {
+ uvw.z() = T{0};
+ uvw.y() = std::min(std::max(math::dot(ab, ap) / math::sqr_length(ab), T{0}), T{1});
+ uvw.x() = T{1} - uvw.y();
+ return {uvw, 0};
+ }
+ else if ((uvw.y() = math::dot(q, ca) / d) < T{0})
+ {
+ uvw.y() = T{0};
+ uvw.x() = std::min(std::max(math::dot(ca, p - c) / math::sqr_length(ca), T{0}), T{1});
+ uvw.z() = T{1} - uvw.x();
+ return {uvw, 2};
+ }
+ else if ((uvw.x() = T{1} - uvw.y() - uvw.z()) < T{0})
+ {
+ uvw.x() = T{0};
+ const auto bc = c - b;
+ uvw.z() = std::min(std::max(math::dot(bc, p - b) / math::sqr_length(bc), T{0}), T{1});
+ uvw.y() = T{1} - uvw.z();
+ return {uvw, 1};
+ }
+
+ return {uvw, -1};
+}
+
+template
+[[nodiscard]] inline constexpr std::tuple, int> closest_feature(const triangle& tri, const point& p) noexcept
+{
+ return closest_feature(tri.a, tri.b, tri.c, p);
+}
+/// @}
+
+} // namespace geom
+
+#endif // ANTKEEPER_GEOM_CLOSEST_FEATURE_HPP
diff --git a/src/engine/geom/closest-point.hpp b/src/engine/geom/closest-point.hpp
index 5aaf049..2618450 100644
--- a/src/engine/geom/closest-point.hpp
+++ b/src/engine/geom/closest-point.hpp
@@ -27,6 +27,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -189,6 +190,58 @@ template
return b - a.normal * (math::dot(a.normal, b) + a.constant);
}
+/**
+ * Calculates the closest point on a triangle to a point.
+ *
+ * @tparam T Real type.
+ *
+ * @param tri Triangle.
+ * @param a First point of triangle.
+ * @param b Second point of triangle.
+ * @param c Third point of triangle.
+ * @param p Point.
+ *
+ * @return Closest point on the triangle to point @p p, followed by the index of the edge on which the point lies (`-1` if not on an edge).
+ */
+/// @{
+template
+[[nodiscard]] constexpr point closest_point(const point& a, const point& b, const point& c, const point& p) noexcept
+{
+ const auto ab = b - a;
+ const auto ca = a - c;
+ const auto ap = p - a;
+ const auto n = math::cross(ab, ca);
+ const auto d = math::sqr_length(n);
+ const auto q = math::cross(n, ap);
+
+ T u, v, w;
+ if ((w = math::dot(q, ab) / d) < T{0})
+ {
+ v = std::min(std::max(math::dot(ab, ap) / math::sqr_length(ab), T{0}), T{1});
+ return {a * (T{1} - v) + b * v};
+ }
+ else if ((v = math::dot(q, ca) / d) < T{0})
+ {
+ u = std::min(std::max(math::dot(ca, p - c) / math::sqr_length(ca), T{0}), T{1});
+ return {a * u + c * (T{1} - u)};
+ }
+ else if ((u = T{1} - v - w) < T{0})
+ {
+ const auto bc = c - b;
+ w = std::min(std::max(math::dot(bc, p - b) / math::sqr_length(bc), T{0}), T{1});
+ return {b * (T{1} - w) + c * w};
+ }
+
+ return {a * u + b * v + c * w};
+}
+
+template
+[[nodiscard]] inline constexpr point closest_point(const triangle& tri, const point& p) noexcept
+{
+ return closest_point(tri.a, tri.b, tri.c, p);
+}
+/// @}
+
/**
* Calculates the closest point on or in a hypersphere to a point.
*
diff --git a/src/engine/geom/coordinates.hpp b/src/engine/geom/coordinates.hpp
new file mode 100644
index 0000000..ebf0cb9
--- /dev/null
+++ b/src/engine/geom/coordinates.hpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2023 Christopher J. Howard
+ *
+ * This file is part of Antkeeper source code.
+ *
+ * Antkeeper source code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Antkeeper source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Antkeeper source code. If not, see .
+ */
+
+#ifndef ANTKEEPER_GEOM_COORDINATES_HPP
+#define ANTKEEPER_GEOM_COORDINATES_HPP
+
+#include
+#include
+
+namespace geom {
+
+/**
+ * Converts Cartesian coordinates to barycentric coordinates.
+ *
+ * @tparam T Real type.
+ *
+ * @param p Barycentric coordinates of point to convert.
+ * @param a Cartesian coordinates of first point of triangle.
+ * @param b Cartesian coordinates of second point of triangle.
+ * @param c Cartesian coordinates of third point of triangle.
+ *
+ * @return Cartesian coordinates of point @p p.
+ */
+template
+[[nodiscard]] inline constexpr point barycentric_to_cartesian(const point& p, const point& a, const point& b, const point& c) noexcept
+{
+ return a * p.x() + b * p.y() + c * p.z();
+}
+
+/**
+ * Converts Cartesian coordinates to barycentric coordinates.
+ *
+ * @tparam T Real type.
+ *
+ * @param p Cartesian coordinates of point to convert.
+ * @param a Cartesian coordinates of first point of triangle.
+ * @param b Cartesian coordinates of second point of triangle.
+ * @param c Cartesian coordinates of third point of triangle.
+ *
+ * @return Barycentric coordinates of point @p p.
+ */
+template
+[[nodiscard]] constexpr point cartesian_to_barycentric(const point& p, const point& a, const point& b, const point& c) noexcept
+{
+ const auto ab = b - a;
+ const auto ca = a - c;
+ const auto n = math::cross(ab, ca);
+ const auto d = math::sqr_length(n);
+ const auto q = math::cross(n, p - a);
+
+ point uvw;
+ uvw.z() = math::dot(q, ab) / d;
+ uvw.y() = math::dot(q, ca) / d;
+ uvw.x() = T{1} - uvw.y() - uvw.z();
+
+ return uvw;
+}
+
+/**
+ * Converts Cartesian (rectangular) coordinates to spherical coordinates.
+ *
+ * @tparam T Real type.
+ *
+ * @param p Cartesian coordinates of point to convert.
+ *
+ * @return Spherical coordinates of point @p p, in the ISO order of radial distance, polar angle (radians), and azimuthal angle (radians).
+ */
+template
+[[nodiscard]] point cartesian_to_spherical(const point& p)
+{
+ const T xx_yy = p.x() * p.x() + p.y() * p.y();
+
+ return
+ {
+ std::sqrt(xx_yy + p.z() * p.z()),
+ std::atan2(p.z(), std::sqrt(xx_yy)),
+ std::atan2(p.y(), p.x())
+ };
+}
+
+/**
+ * Converts spherical coordinates to Cartesian (rectangular) coordinates.
+ *
+ * @tparam T Real type.
+ *
+ * @param p Spherical coordinates to convert, in the ISO order of radial distance, polar angle (radians), and azimuthal angle (radians).
+ *
+ * @return Cartesian coordinates of point @p p.
+ */
+template
+[[nodiscard]] point spherical_to_cartesian(const point& p)
+{
+ const T x = p.x() * std::cos(p.y());
+
+ return
+ {
+ x * std::cos(p.z()),
+ x * std::sin(p.z()),
+ p.x() * std::sin(p.y())
+ };
+}
+
+} // namespace geom
+
+#endif // ANTKEEPER_GEOM_COORDINATES_HPP
diff --git a/src/engine/math/linear-algebra.hpp b/src/engine/geom/primitives/triangle.hpp
similarity index 58%
rename from src/engine/math/linear-algebra.hpp
rename to src/engine/geom/primitives/triangle.hpp
index c3ef4a4..58df0be 100644
--- a/src/engine/math/linear-algebra.hpp
+++ b/src/engine/geom/primitives/triangle.hpp
@@ -17,11 +17,40 @@
* along with Antkeeper source code. If not, see .
*/
-#ifndef ANTKEEPER_MATH_LINEAR_ALGEBRA_HPP
-#define ANTKEEPER_MATH_LINEAR_ALGEBRA_HPP
+#ifndef ANTKEEPER_GEOM_PRIMITIVES_TRIANGLE_HPP
+#define ANTKEEPER_GEOM_PRIMITIVES_TRIANGLE_HPP
-#include
-#include
#include
-#endif // ANTKEEPER_MATH_LINEAR_ALGEBRA_HPP
+namespace geom {
+namespace primitives {
+
+/**
+ * *n*-dimensional triangle.
+ *
+ * @tparam T Real type.
+ * @tparam N Number of dimensions.
+ */
+template
+struct triangle
+{
+ /// Vector type.
+ using vector_type = math::vector;
+
+ /// First point.
+ vector_type a;
+
+ /// Second point.
+ vector_type b;
+
+ /// Third point.
+ vector_type c;
+};
+
+} // namespace primitives
+
+using namespace primitives;
+
+} // namespace geom
+
+#endif // ANTKEEPER_GEOM_PRIMITIVES_TRIANGLE_HPP
diff --git a/src/engine/math/se3.hpp b/src/engine/math/se3.hpp
index 9d41ede..72986cc 100644
--- a/src/engine/math/se3.hpp
+++ b/src/engine/math/se3.hpp
@@ -24,10 +24,12 @@
#include
namespace math {
-namespace transformation {
+
+/// Transformation types
+namespace transformation_types {
/**
- * 3-dimensional Euclidean proper rigid transformation in SE(3).
+ * SE(3) proper rigid transformation (rototranslation).
*
* @tparam T Scalar type.
*/
@@ -36,101 +38,116 @@ struct se3
{
public:
/// Scalar type.
- typedef T scalar_type;
+ using scalar_type = T;
/// Vector type.
- typedef math::vec3 vector_type;
+ using vector_type = vec3;
/// Quaternion type.
- typedef math::quaternion quaternion_type;
+ using quaternion_type = quat;
/// Transformation matrix type.
- typedef math::mat4 matrix_type;
+ using matrix_type = mat4;
- /// Vector representing the translation component of this SE(3) transformation.
+ /// Vector representing the translation component of the transformation.
vector_type t;
- /// Quaternion representing the rotation component of this SE(3) transformation.
+ /// Quaternion representing the rotation component of the transformation.
quaternion_type r;
- /// Returns the inverse of this SE(3) transformation.
- [[nodiscard]] se3 inverse() const;
+ /// Returns the inverse of this transformation.
+ [[nodiscard]] constexpr se3 inverse() const noexcept
+ {
+ const quaternion_type inverse_r = conjugate(r);
+ const vector_type inverse_t = -(inverse_r * t);
+ return {inverse_t, inverse_r};
+ }
- /// Returns a matrix representation of the SE(3) transformation.
- [[nodiscard]] matrix_type matrix() const;
+ /// Returns a matrix representation of this transformation.
+ /// @{
+ [[nodiscard]] constexpr matrix_type matrix() const noexcept
+ {
+ matrix_type m = mat4(mat3(r));
+
+ m[3].x() = t.x();
+ m[3].y() = t.y();
+ m[3].z() = t.z();
+
+ return m;
+ }
+
+ [[nodiscard]] inline constexpr explicit operator matrix_type() const noexcept
+ {
+ return matrix();
+ }
+ /// @}
/**
- * Transforms a vector by this SE(3) transformation.
+ * Transforms a vector by this transformation.
+ *
+ * @param v Untransformed vector.
*
- * @param x Untransformed vector.
* @return Transformed vector.
*/
- [[nodiscard]] vector_type transform(const vector_type& x) const;
+ /// @{
+ [[nodiscard]] inline constexpr vector_type transform(const vector_type& v) const noexcept
+ {
+ return r * v + t;
+ }
+
+ [[nodiscard]] inline constexpr vector_type operator*(const vector_type& v) const noexcept
+ {
+ return transform(v);
+ }
+ /// @}
/**
- * Transforms an SE(3) transformation by this SE(3) transformation.
+ * Transforms an SE(3) transformation by this transformation.
+ *
+ * @param xf SE(3) transformation.
*
- * @param x Other SE(3) transformation.
* @return Frame in this se3's space.
*/
- [[nodiscard]] se3 transform(const se3& x) const;
-
- /// @copydoc se3::transform(const vector_type&) const
- [[nodiscard]] vector_type operator*(const vector_type& x) const;
+ /// @{
+ [[nodiscard]] constexpr se3 transform(const se3& xf) const noexcept
+ {
+ return {xf.transform(t), normalize(xf.r * r)};
+ }
- /// @copydoc se3::transform(const se3&) const
- [[nodiscard]] se3 operator*(const se3& x) const;
-};
-
-template
-se3 se3::inverse() const
-{
- const quaternion_type inverse_r = math::conjugate(r);
- const vector_type inverse_t = -(inverse_r * t);
- return se3{inverse_t, inverse_r};
-}
-
-template
-typename se3::matrix_type se3::matrix() const
-{
- matrix_type m = math::mat4(math::mat3(r));
+ [[nodiscard]] inline constexpr se3 operator*(const se3& xf) const noexcept
+ {
+ return transform(xf);
+ }
+ /// @}
- m[3].x() = t.x();
- m[3].y() = t.y();
- m[3].z() = t.z();
+ /*
+ * Type-casts the transform scalars using `static_cast`.
+ *
+ * @tparam U Target scalar type.
+ *
+ * @return Type-casted transform.
+ */
+ template
+ [[nodiscard]] inline constexpr explicit operator se3() const noexcept
+ {
+ return {vec3(t), quat(r)};
+ }
- return m;
-}
-
-template
-typename se3::vector_type se3::transform(const vector_type& x) const
-{
- return r * x + t;
-}
-
-template
-se3 se3::transform(const se3& x) const
-{
- return se3
+ /// Returns an identity transformation.
+ [[nodiscard]] static inline constexpr se3 identity() noexcept
{
- x.transform(t),
- math::normalize(x.r * r)
- };
-}
+ return {vector_type::zero(), quaternion_type::identity()};
+ }
+};
-template
-typename se3::vector_type se3::operator*(const vector_type& x) const
-{
- return transform(x);
-}
+} // namespace transformation_types
-template
-se3 se3::operator*(const se3& x) const
-{
- return transform(x);
-}
+// Bring transformation types into math namespace
+using namespace transformation_types;
+
+// Bring transformation types into math::types namespace
+namespace types { using namespace math::transformation_types; }
-} // namespace transformation
} // namespace math
#endif // ANTKEEPER_MATH_TRANSFORMATION_SE3_HPP
diff --git a/src/engine/math/transform.hpp b/src/engine/math/transform.hpp
index 0fb38ac..d50c30d 100644
--- a/src/engine/math/transform.hpp
+++ b/src/engine/math/transform.hpp
@@ -27,9 +27,7 @@
namespace math {
/**
- * 3D transformation.
- *
- * Transformations are applied in the following order: scale, rotation, translation.
+ * SRT transformation.
*
* @tparam T Scalar type.
*/
@@ -40,13 +38,13 @@ struct transform
using scalar_type = T;
/// Vector type.
- using vector_type = vector;
+ using vector_type = vec3;
/// Quaternion type.
- using quaternion_type = quaternion;
+ using quaternion_type = quat;
/// Transformation matrix type.
- using matrix_type = matrix;
+ using matrix_type = mat4;
/// Translation vector.
vector_type translation;
@@ -102,7 +100,7 @@ struct transform
template
[[nodiscard]] inline constexpr explicit operator transform() const noexcept
{
- return {math::vector(translation), math::quaternion(rotation), math::vector(scale)};
+ return {vec3(translation), quat(rotation), vec3(scale)};
}
/// Returns an identity transform.
diff --git a/src/engine/physics/orbit/frame.hpp b/src/engine/physics/orbit/frame.hpp
index 9ecc288..867c804 100644
--- a/src/engine/physics/orbit/frame.hpp
+++ b/src/engine/physics/orbit/frame.hpp
@@ -143,7 +143,7 @@ namespace pqw {
* @return PQW to BCI transformation.
*/
template
- math::transformation::se3 to_bci(T om, T in, T w)
+ math::se3 to_bci(T om, T in, T w)
{
const math::quaternion r = math::normalize
(
@@ -152,7 +152,7 @@ namespace pqw {
math::quaternion::rotate_z(w)
);
- return math::transformation::se3{{T(0), T(0), T(0)}, r};
+ return math::se3{{T(0), T(0), T(0)}, r};
}
} // namespace pqw
@@ -210,7 +210,7 @@ namespace bci {
* @see Archinal, B.A., A’Hearn, M.F., Bowell, E. et al. Report of the IAU Working Group on Cartographic Coordinates and Rotational Elements: 2009. Celest Mech Dyn Astr 109, 101–135 (2011). https://doi.org/10.1007/s10569-010-9320-4
*/
template
- math::transformation::se3 to_bcbf(T ra, T dec, T w)
+ math::se3 to_bcbf(T ra, T dec, T w)
{
const math::quaternion r = math::normalize
(
@@ -219,7 +219,7 @@ namespace bci {
math::quaternion::rotate_z(-w)
);
- return math::transformation::se3{{T(0), T(0), T(0)}, r};
+ return math::se3{{T(0), T(0), T(0)}, r};
}
/**
@@ -231,7 +231,7 @@ namespace bci {
* @return BCI to PQW transformation.
*/
template
- math::transformation::se3 to_pqw(T om, T in, T w)
+ math::se3 to_pqw(T om, T in, T w)
{
const math::quaternion r = math::normalize
(
@@ -240,7 +240,7 @@ namespace bci {
math::quaternion::rotate_z(-om)
);
- return math::transformation::se3{{T(0), T(0), T(0)}, r};
+ return math::se3{{T(0), T(0), T(0)}, r};
}
} // namespace bci
@@ -298,7 +298,7 @@ namespace bcbf {
* @see Archinal, B.A., A’Hearn, M.F., Bowell, E. et al. Report of the IAU Working Group on Cartographic Coordinates and Rotational Elements: 2009. Celest Mech Dyn Astr 109, 101–135 (2011). https://doi.org/10.1007/s10569-010-9320-4
*/
template
- math::transformation::se3 to_bci(T ra, T dec, T w)
+ math::se3 to_bci(T ra, T dec, T w)
{
const math::quaternion r = math::normalize
(
@@ -308,7 +308,7 @@ namespace bcbf {
);
- return math::transformation::se3{{T(0), T(0), T(0)}, r};
+ return math::se3{{T(0), T(0), T(0)}, r};
}
/**
@@ -320,7 +320,7 @@ namespace bcbf {
* @return BCBF to ENU transformation.
*/
template
- math::transformation::se3 to_enu(T distance, T latitude, T longitude)
+ math::se3 to_enu(T distance, T latitude, T longitude)
{
const math::vec3 t = {T(0), T(0), -distance};
const math::quaternion r = math::normalize
@@ -329,7 +329,7 @@ namespace bcbf {
math::quaternion::rotate_z(-longitude - math::half_pi)
);
- return math::transformation::se3{t, r};
+ return math::se3{t, r};
}
} // namespace bcbf
@@ -385,7 +385,7 @@ namespace enu {
* @return ENU to BCBF transformation.
*/
template
- math::transformation::se3 to_bcbf(T distance, T latitude, T longitude)
+ math::se3 to_bcbf(T distance, T latitude, T longitude)
{
const math::vec3 t = {T(0), T(0), distance};
const math::quaternion r = math::normalize
@@ -394,7 +394,7 @@ namespace enu {
math::quaternion::rotate_x(math::half_pi - latitude)
);
- return math::transformation::se3{r * t, r};
+ return math::se3{r * t, r};
}
} // namespace enu
diff --git a/src/engine/render/passes/sky-pass.cpp b/src/engine/render/passes/sky-pass.cpp
index d8bfbf4..5b7ec3a 100644
--- a/src/engine/render/passes/sky-pass.cpp
+++ b/src/engine/render/passes/sky-pass.cpp
@@ -38,8 +38,6 @@
#include
#include
#include
-#include
-#include
#include
#include
#include
@@ -233,7 +231,7 @@ void sky_pass::render(render::context& ctx)
observer_position = observer_position_tween.interpolate(ctx.alpha);
// Construct tweened ICRF to EUS transformation
- math::transformation::se3 icrf_to_eus =
+ math::se3 icrf_to_eus =
{
icrf_to_eus_translation.interpolate(ctx.alpha),
icrf_to_eus_rotation.interpolate(ctx.alpha)
@@ -672,7 +670,7 @@ void sky_pass::set_magnification(float magnification)
this->magnification = magnification;
}
-void sky_pass::set_icrf_to_eus(const math::transformation::se3& transformation)
+void sky_pass::set_icrf_to_eus(const math::se3& transformation)
{
icrf_to_eus_translation[1] = transformation.t;
icrf_to_eus_rotation[1] = transformation.r;
diff --git a/src/engine/render/passes/sky-pass.hpp b/src/engine/render/passes/sky-pass.hpp
index 77ab5b2..3d6a51c 100644
--- a/src/engine/render/passes/sky-pass.hpp
+++ b/src/engine/render/passes/sky-pass.hpp
@@ -184,7 +184,7 @@ public:
void set_moon_model(std::shared_ptr model);
void set_stars_model(std::shared_ptr model);
- void set_icrf_to_eus(const math::transformation::se3& transformation);
+ void set_icrf_to_eus(const math::se3& transformation);
void set_sun_position(const math::fvec3& position);
void set_sun_luminance(const math::fvec3& luminance);
diff --git a/src/game/states/experiments/treadmill-experiment-state.cpp b/src/game/states/experiments/treadmill-experiment-state.cpp
new file mode 100644
index 0000000..90fdc07
--- /dev/null
+++ b/src/game/states/experiments/treadmill-experiment-state.cpp
@@ -0,0 +1,904 @@
+/*
+ * 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/states/experiments/treadmill-experiment-state.hpp"
+
+#include "game/ant/ant-cladogenesis.hpp"
+#include "game/ant/ant-genome.hpp"
+#include "game/ant/ant-morphogenesis.hpp"
+#include "game/ant/ant-phenome.hpp"
+#include "game/commands/commands.hpp"
+#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"
+#include "game/components/terrain-component.hpp"
+#include "game/components/legged-locomotion-component.hpp"
+#include "game/components/winged-locomotion-component.hpp"
+#include "game/components/ik-component.hpp"
+#include "game/components/transform-component.hpp"
+#include "game/constraints/child-of-constraint.hpp"
+#include "game/constraints/copy-rotation-constraint.hpp"
+#include "game/constraints/copy-scale-constraint.hpp"
+#include "game/constraints/copy-transform-constraint.hpp"
+#include "game/constraints/copy-translation-constraint.hpp"
+#include "game/constraints/ease-to-constraint.hpp"
+#include "game/constraints/pivot-constraint.hpp"
+#include "game/constraints/spring-rotation-constraint.hpp"
+#include "game/constraints/spring-to-constraint.hpp"
+#include "game/constraints/spring-translation-constraint.hpp"
+#include "game/constraints/three-dof-constraint.hpp"
+#include "game/constraints/track-to-constraint.hpp"
+#include "game/controls.hpp"
+#include "game/spawn.hpp"
+#include "game/states/pause-menu-state.hpp"
+#include "game/systems/astronomy-system.hpp"
+#include "game/systems/atmosphere-system.hpp"
+#include "game/systems/camera-system.hpp"
+#include "game/systems/collision-system.hpp"
+#include "game/world.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+treadmill_experiment_state::treadmill_experiment_state(::game& ctx):
+ game_state(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_ecoregion = ctx.resource_manager->load<::ecoregion>("seedy-scrub.eco");
+ ::world::enter_ecoregion(ctx, *ctx.active_ecoregion);
+
+ debug::log::trace("Generating genome...");
+ std::unique_ptr genome = ant_cladogenesis(ctx.active_ecoregion->gene_pools[0], ctx.rng);
+ debug::log::trace("Generated genome");
+
+ debug::log::trace("Building worker phenome...");
+ worker_phenome = ant_phenome(*genome, ant_caste_type::worker);
+ debug::log::trace("Built worker phenome...");
+
+ debug::log::trace("Generating worker model...");
+ std::shared_ptr worker_model = ant_morphogenesis(worker_phenome);
+ debug::log::trace("Generated worker model");
+
+ // Create directional light
+ // ctx.underground_directional_light = std::make_unique();
+ // ctx.underground_directional_light->set_color({1.0f, 1.0f, 1.0f});
+ // ctx.underground_directional_light->set_illuminance(2.0f);
+ // ctx.underground_directional_light->set_direction(math::normalize(math::fvec3{0, -1, 0}));
+ // ctx.underground_directional_light->set_shadow_caster(true);
+ // ctx.underground_directional_light->set_shadow_framebuffer(ctx.shadow_map_framebuffer);
+ // ctx.underground_directional_light->set_shadow_bias(0.005f);
+ // ctx.underground_directional_light->set_shadow_cascade_count(4);
+ // ctx.underground_directional_light->set_shadow_cascade_coverage(0.15f);
+ // ctx.underground_directional_light->set_shadow_cascade_distribution(0.8f);
+ // ctx.underground_scene->add_object(*ctx.underground_directional_light);
+
+ // ctx.underground_clear_pass->set_clear_color({0.214f, 0.214f, 0.214f, 1.0f});
+ // ctx.underground_clear_pass->set_clear_color({});
+ // light_probe = std::make_shared();
+ // light_probe->set_luminance_texture(ctx.resource_manager->load("grey-furnace.tex"));
+ // ctx.underground_scene->add_object(*light_probe);
+
+ //const float color_temperature = 5000.0f;
+ //const math::fvec3 light_color = color::aces::ap1.from_xyz * color::cat::matrix(color::illuminant::deg2::d50, color::aces::white_point) * color::cct::to_xyz(color_temperature);
+ const math::fvec3 light_color{1.0f, 1.0f, 1.0f};
+
+ // Create rectangle light
+ // ctx.underground_rectangle_light = std::make_unique();
+ // ctx.underground_rectangle_light->set_color(light_color);
+ // ctx.underground_rectangle_light->set_luminous_flux(1500.0f);
+ // ctx.underground_rectangle_light->set_translation({0.0f, 10.0f, 0.0f});
+ // ctx.underground_rectangle_light->set_rotation(math::fquat::rotate_x(math::radians(90.0f)));
+ // ctx.underground_rectangle_light->set_scale(7.0f);
+ // ctx.underground_scene->add_object(*ctx.underground_rectangle_light);
+
+ // Create light rectangle
+ // auto light_rectangle_model = ctx.resource_manager->load("light-rectangle.mdl");
+ // auto light_rectangle_material = std::make_shared(*light_rectangle_model->get_groups().front().material);
+ // light_rectangle_emissive = std::static_pointer_cast(light_rectangle_material->get_variable("emissive"));
+ // light_rectangle_emissive->set(ctx.underground_rectangle_light->get_colored_luminance());
+ // auto light_rectangle_static_mesh = std::make_shared(light_rectangle_model);
+ // light_rectangle_static_mesh->set_material(0, light_rectangle_material);
+
+ // auto light_rectangle_eid = ctx.entity_registry->create();
+ // ctx.entity_registry->emplace