Browse Source

Improve frustum culling. Remove obsolete geometry primitives

master
C. J. Howard 1 year ago
parent
commit
2afd495633
73 changed files with 934 additions and 2016 deletions
  1. +1
    -0
      CMakeLists.txt
  2. +6
    -6
      src/engine/app/display.hpp
  3. +0
    -208
      src/engine/geom/aabb.hpp
  4. +0
    -95
      src/engine/geom/bounding-volume.hpp
  5. +0
    -143
      src/engine/geom/convex-hull.hpp
  6. +1
    -21
      src/engine/geom/geom.hpp
  7. +0
    -187
      src/engine/geom/intersection.cpp
  8. +224
    -27
      src/engine/geom/intersection.hpp
  9. +15
    -16
      src/engine/geom/mesh-accelerator.cpp
  10. +4
    -4
      src/engine/geom/mesh-accelerator.hpp
  11. +6
    -11
      src/engine/geom/mesh-functions.cpp
  12. +2
    -2
      src/engine/geom/mesh-functions.hpp
  13. +0
    -123
      src/engine/geom/plane.hpp
  14. +2
    -2
      src/engine/geom/primitives/box.hpp
  15. +2
    -2
      src/engine/geom/primitives/circle.hpp
  16. +5
    -2
      src/engine/geom/primitives/hyperplane.hpp
  17. +22
    -2
      src/engine/geom/primitives/hyperrectangle.hpp
  18. +5
    -2
      src/engine/geom/primitives/hypersphere.hpp
  19. +0
    -207
      src/engine/geom/primitives/intersection.hpp
  20. +5
    -2
      src/engine/geom/primitives/line-segment.hpp
  21. +2
    -2
      src/engine/geom/primitives/line.hpp
  22. +2
    -2
      src/engine/geom/primitives/plane.hpp
  23. +5
    -2
      src/engine/geom/primitives/ray.hpp
  24. +2
    -2
      src/engine/geom/primitives/rectangle.hpp
  25. +2
    -2
      src/engine/geom/primitives/sphere.hpp
  26. +317
    -0
      src/engine/geom/primitives/view-frustum.hpp
  27. +0
    -59
      src/engine/geom/ray.hpp
  28. +13
    -7
      src/engine/geom/rect-pack.hpp
  29. +0
    -41
      src/engine/geom/rect.hpp
  30. +0
    -117
      src/engine/geom/sphere.hpp
  31. +0
    -190
      src/engine/geom/view-frustum.hpp
  32. +31
    -59
      src/engine/math/quaternion.hpp
  33. +1
    -1
      src/engine/physics/kinematics/colliders/box-collider.hpp
  34. +1
    -1
      src/engine/physics/kinematics/colliders/plane-collider.hpp
  35. +1
    -1
      src/engine/physics/kinematics/colliders/sphere-collider.hpp
  36. +0
    -5
      src/engine/render/context.hpp
  37. +1
    -2
      src/engine/render/model.cpp
  38. +2
    -2
      src/engine/render/model.hpp
  39. +73
    -41
      src/engine/render/passes/shadow-map-pass.cpp
  40. +4
    -16
      src/engine/render/stages/culling-stage.cpp
  41. +2
    -4
      src/engine/scene/billboard.cpp
  42. +2
    -5
      src/engine/scene/billboard.hpp
  43. +35
    -15
      src/engine/scene/camera.cpp
  44. +15
    -4
      src/engine/scene/camera.hpp
  45. +1
    -2
      src/engine/scene/light.cpp
  46. +2
    -6
      src/engine/scene/light.hpp
  47. +3
    -20
      src/engine/scene/object.hpp
  48. +11
    -2
      src/engine/scene/static-mesh.cpp
  49. +1
    -4
      src/engine/scene/static-mesh.hpp
  50. +13
    -5
      src/engine/scene/text.cpp
  51. +1
    -4
      src/engine/scene/text.hpp
  52. +9
    -9
      src/game/ant/ant-morphogenesis.cpp
  53. +0
    -39
      src/game/components/collision-component.hpp
  54. +1
    -1
      src/game/components/picking-component.hpp
  55. +10
    -10
      src/game/controls.cpp
  56. +0
    -1
      src/game/game.hpp
  57. +0
    -24
      src/game/loaders/entity-archetype-loader.cpp
  58. +8
    -8
      src/game/menu.cpp
  59. +1
    -1
      src/game/states/collection-menu-state.hpp
  60. +6
    -6
      src/game/states/credits-state.cpp
  61. +6
    -6
      src/game/states/main-menu-state.cpp
  62. +2
    -2
      src/game/states/nest-selection-state.cpp
  63. +8
    -8
      src/game/states/nuptial-flight-state.cpp
  64. +7
    -7
      src/game/systems/astronomy-system.cpp
  65. +2
    -2
      src/game/systems/astronomy-system.hpp
  66. +7
    -23
      src/game/systems/collision-system.cpp
  67. +2
    -8
      src/game/systems/collision-system.hpp
  68. +0
    -1
      src/game/systems/physics-system.cpp
  69. +2
    -1
      src/game/systems/render-system.cpp
  70. +6
    -158
      src/game/systems/subterrain-system.cpp
  71. +1
    -2
      src/game/systems/subterrain-system.hpp
  72. +13
    -13
      src/game/systems/terrain-system.cpp
  73. +0
    -1
      src/game/systems/terrain-system.hpp

+ 1
- 0
CMakeLists.txt View File

@ -1,5 +1,6 @@
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")

+ 6
- 6
src/engine/app/display.hpp View File

@ -59,7 +59,7 @@ public:
*
* @param bounds Bounds of the display, in display units.
*/
inline void set_bounds(const geom::primitive::rectangle<int>& bounds) noexcept
inline void set_bounds(const geom::rectangle<int>& bounds) noexcept
{
m_bounds = bounds;
}
@ -67,7 +67,7 @@ public:
/**
* Sets the usable bounds of the display, which excludes areas reserved by the OS for things like menus or docks.
*/
inline void set_usable_bounds(const geom::primitive::rectangle<int>& bounds) noexcept
inline void set_usable_bounds(const geom::rectangle<int>& bounds) noexcept
{
m_usable_bounds = bounds;
}
@ -115,13 +115,13 @@ public:
}
/// Returns the bounds of the display, in display units.
[[nodiscard]] inline const geom::primitive::rectangle<int>& get_bounds() const noexcept
[[nodiscard]] inline const geom::rectangle<int>& get_bounds() const noexcept
{
return m_bounds;
}
/// Returns the usable bounds of the display, which excludes areas reserved by the OS for things like menus or docks, in display units.
[[nodiscard]] inline const geom::primitive::rectangle<int>& get_usable_bounds() const noexcept
[[nodiscard]] inline const geom::rectangle<int>& get_usable_bounds() const noexcept
{
return m_usable_bounds;
}
@ -174,8 +174,8 @@ private:
int m_index{0};
std::string m_name;
geom::primitive::rectangle<int> m_bounds{0};
geom::primitive::rectangle<int> m_usable_bounds{0};
geom::rectangle<int> m_bounds{0};
geom::rectangle<int> m_usable_bounds{0};
int m_refresh_rate{0};
float m_dpi{0.0f};
display_orientation m_orientation{0};

+ 0
- 208
src/engine/geom/aabb.hpp View File

@ -1,208 +0,0 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GEOM_AABB_HPP
#define ANTKEEPER_GEOM_AABB_HPP
#include <engine/geom/bounding-volume.hpp>
#include <engine/geom/sphere.hpp>
#include <engine/math/vector.hpp>
#include <engine/math/matrix.hpp>
#include <engine/math/transform-type.hpp>
#include <limits>
namespace geom {
/**
* Axis-aligned bounding box.
*/
template <class T>
struct aabb: public bounding_volume<T>
{
typedef math::vector<T, 3> vector_type;
typedef math::matrix<T, 4, 4> matrix_type;
typedef math::transform<T> transform_type;
vector_type min_point;
vector_type max_point;
/**
* Transforms an AABB.
*
* @param a AABB to be transformed.
* @param t Transform by which the AABB should be transformed.
* @return Transformed AABB.
*/
static aabb transform(const aabb& a, const transform_type& t);
/**
* Transforms an AABB.
*
* @param a AABB to be transformed.
* @param m Matrix by which the AABB should be transformed.
* @return Transformed AABB.
*/
static aabb transform(const aabb& a, const matrix_type& m);
aabb(const vector_type& min_point, const vector_type& max_point);
aabb();
virtual bounding_volume_type get_bounding_volume_type() const;
virtual bool intersects(const sphere<T>& sphere) const;
virtual bool intersects(const aabb<T>& aabb) const;
virtual bool contains(const sphere<T>& sphere) const;
virtual bool contains(const aabb<T>& aabb) const;
virtual bool contains(const vector_type& point) const;
/**
* Returns the position of the specified corner.
*
* @param index Index of a corner.
* @return Position of the specified corner.
*/
vector_type corner(std::size_t index) const noexcept;
};
template <class T>
aabb<T> aabb<T>::transform(const aabb& a, const transform_type& t)
{
vector_type min_point = {std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()};
vector_type max_point = {-std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity()};
for (std::size_t i = 0; i < 8; ++i)
{
vector_type transformed_corner = math::mul(t, a.corner(i));
for (std::size_t j = 0; j < 3; ++j)
{
min_point[j] = std::min<T>(min_point[j], transformed_corner[j]);
max_point[j] = std::max<T>(max_point[j], transformed_corner[j]);
}
}
return {min_point, max_point};
}
template <class T>
aabb<T> aabb<T>::transform(const aabb& a, const matrix_type& m)
{
vector_type min_point = {std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()};
vector_type max_point = {-std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity()};
for (std::size_t i = 0; i < 8; ++i)
{
vector_type corner = a.corner(i);
math::vector<T, 4> transformed_corner = math::mul(m, math::vector<T, 4>{corner.x(), corner.y(), corner.z(), T{1}});
for (std::size_t j = 0; j < 3; ++j)
{
min_point[j] = std::min<T>(min_point[j], transformed_corner[j]);
max_point[j] = std::max<T>(max_point[j], transformed_corner[j]);
}
}
return {min_point, max_point};
}
template <class T>
aabb<T>::aabb(const vector_type& min_point, const vector_type& max_point):
min_point(min_point),
max_point(max_point)
{}
template <class T>
aabb<T>::aabb()
{}
template <class T>
inline bounding_volume_type aabb<T>::get_bounding_volume_type() const
{
return bounding_volume_type::aabb;
}
template <class T>
bool aabb<T>::intersects(const sphere<T>& sphere) const
{
const vector_type radius_vector = {sphere.radius, sphere.radius, sphere.radius};
return aabb<T>(min_point - radius_vector, max_point + radius_vector).contains(sphere.center);
}
template <class T>
bool aabb<T>::intersects(const aabb<T>& aabb) const
{
if (max_point.x() < aabb.min_point.x() || min_point.x() > aabb.max_point.x())
return false;
if (max_point.y() < aabb.min_point.y() || min_point.y() > aabb.max_point.y())
return false;
if (max_point.z() < aabb.min_point.z() || min_point.z() > aabb.max_point.z())
return false;
return true;
}
template <class T>
bool aabb<T>::contains(const sphere<T>& sphere) const
{
if (sphere.center.x() - sphere.radius < min_point.x() || sphere.center.x() + sphere.radius > max_point.x())
return false;
if (sphere.center.y() - sphere.radius < min_point.y() || sphere.center.y() + sphere.radius > max_point.y())
return false;
if (sphere.center.z() - sphere.radius < min_point.z() || sphere.center.z() + sphere.radius > max_point.z())
return false;
return true;
}
template <class T>
bool aabb<T>::contains(const aabb<T>& aabb) const
{
if (aabb.min_point.x() < min_point.x() || aabb.max_point.x() > max_point.x())
return false;
if (aabb.min_point.y() < min_point.y() || aabb.max_point.y() > max_point.y())
return false;
if (aabb.min_point.z() < min_point.z() || aabb.max_point.z() > max_point.z())
return false;
return true;
}
template <class T>
bool aabb<T>::contains(const vector_type& point) const
{
if (point.x() < min_point.x() || point.x() > max_point.x())
return false;
if (point.y() < min_point.y() || point.y() > max_point.y())
return false;
if (point.z() < min_point.z() || point.z() > max_point.z())
return false;
return true;
}
template <class T>
typename aabb<T>::vector_type aabb<T>::corner(std::size_t index) const noexcept
{
return
{
((index >> 2) & 1) ? max_point[0] : min_point[0],
((index >> 1) & 1) ? max_point[1] : min_point[1],
((index >> 0) & 1) ? max_point[2] : min_point[2]
};
}
} // namespace geom
#endif // ANTKEEPER_GEOM_AABB_HPP

+ 0
- 95
src/engine/geom/bounding-volume.hpp View File

@ -1,95 +0,0 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GEOM_BOUNDING_VOLUME_HPP
#define ANTKEEPER_GEOM_BOUNDING_VOLUME_HPP
#include <engine/math/vector.hpp>
#include <stdexcept>
namespace geom {
template <class T>
struct sphere;
template <class T>
struct aabb;
/// Enumerates bounding volume types.
enum class bounding_volume_type
{
aabb, ///< Indicates the bounding volume is an axis-aligned bounding box.
sphere, ///< Indicates the bounding volume is a sphere.
convex_hull ///< Indicates the bounding volume is a convex hull.
};
/**
* Abstract base class for bounding volumes.
*
* @tparam T Scalar type.
*/
template <class T>
class bounding_volume
{
public:
/// Returns the enumerated type of this bounding volume.
virtual bounding_volume_type get_bounding_volume_type() const = 0;
/// Tests for intersection between this bounding volume and a bounding sphere.
virtual bool intersects(const sphere<T>& sphere) const = 0;
/// Tests for intersection between this bounding volume and an axis-aligned bounding box.
virtual bool intersects(const aabb<T>& aabb) const = 0;
/// Tests whether this bounding volume contains a sphere.
virtual bool contains(const sphere<T>& sphere) const = 0;
/// Tests whether this bounding volume contains an axis-aligned bounding box.
virtual bool contains(const aabb<T>& aabb) const = 0;
/// Tests whether this bounding volume contains a point.
virtual bool contains(const math::vector<T, 3>& point) const = 0;
/// Tests for intersection between this bounding volume and another bounding volume.
bool intersects(const bounding_volume& volume) const;
};
/// Tests for intersection between this bounding volume and another bounding volume.
template <class T>
bool bounding_volume<T>::intersects(const bounding_volume& volume) const
{
const bounding_volume_type type = volume.get_bounding_volume_type();
switch (type)
{
case bounding_volume_type::sphere:
return intersects(static_cast<const sphere<T>&>(volume));
break;
case bounding_volume_type::aabb:
return intersects(static_cast<const aabb<T>&>(volume));
break;
default:
throw std::runtime_error("unimplemented");
break;
}
}
} // namespace geom
#endif // ANTKEEPER_GEOM_BOUNDING_VOLUME_HPP

+ 0
- 143
src/engine/geom/convex-hull.hpp View File

@ -1,143 +0,0 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GEOM_CONVEX_HULL_HPP
#define ANTKEEPER_GEOM_CONVEX_HULL_HPP
#include <engine/geom/bounding-volume.hpp>
#include <engine/geom/plane.hpp>
#include <engine/geom/sphere.hpp>
#include <engine/geom/aabb.hpp>
#include <cstddef>
#include <vector>
namespace geom {
/**
* A plane-bounded convex hull.
*/
template <class T>
struct convex_hull: public bounding_volume<T>
{
/// Vector of planes with descibe the bounds of the convex hull.
std::vector<plane<T>> planes;
/**
* Creates a convex hull.
*
* @param size Number of planes the convex hull should accommodate.
*/
explicit convex_hull(std::size_t size);
/// Creates a convex hull.
convex_hull();
virtual bounding_volume_type get_bounding_volume_type() const;
virtual bool intersects(const sphere<T>& sphere) const;
virtual bool intersects(const aabb<T>& aabb) const;
virtual bool contains(const sphere<T>& sphere) const;
virtual bool contains(const aabb<T>& aabb) const;
virtual bool contains(const math::vector<T, 3>& point) const;
};
template <class T>
convex_hull<T>::convex_hull(std::size_t size):
planes(size, plane<T>())
{}
template <class T>
convex_hull<T>::convex_hull()
{}
template <class T>
inline bounding_volume_type convex_hull<T>::get_bounding_volume_type() const
{
return bounding_volume_type::convex_hull;
}
template <class T>
bool convex_hull<T>::intersects(const sphere<T>& sphere) const
{
for (const plane<T>& plane: planes)
if (plane.signed_distance(sphere.center) < -sphere.radius)
return false;
return true;
}
template <class T>
bool convex_hull<T>::intersects(const aabb<T>& aabb) const
{
math::vector<T, 3> pv;
for (const plane<T>& plane: planes)
{
pv.x() = (plane.normal.x() > T(0)) ? aabb.max_point.x() : aabb.min_point.x();
pv.y() = (plane.normal.y() > T(0)) ? aabb.max_point.y() : aabb.min_point.y();
pv.z() = (plane.normal.z() > T(0)) ? aabb.max_point.z() : aabb.min_point.z();
if (plane.signed_distance(pv) < T(0))
return false;
}
return true;
}
template <class T>
bool convex_hull<T>::contains(const sphere<T>& sphere) const
{
for (const plane<T>& plane: planes)
if (plane.signed_distance(sphere.center) < sphere.radius)
return false;
return true;
}
template <class T>
bool convex_hull<T>::contains(const aabb<T>& aabb) const
{
math::vector<T, 3> pv;
math::vector<T, 3> nv;
for (const plane<T>& plane: planes)
{
pv.x() = (plane.normal.x() > T(0)) ? aabb.max_point.x() : aabb.min_point.x();
pv.y() = (plane.normal.y() > T(0)) ? aabb.max_point.y() : aabb.min_point.y();
pv.z() = (plane.normal.z() > T(0)) ? aabb.max_point.z() : aabb.min_point.z();
nv.x() = (plane.normal.x() < T(0)) ? aabb.max_point.x() : aabb.min_point.x();
nv.y() = (plane.normal.y() < T(0)) ? aabb.max_point.y() : aabb.min_point.y();
nv.z() = (plane.normal.z() < T(0)) ? aabb.max_point.z() : aabb.min_point.z();
if (plane.signed_distance(pv) < T(0) || plane.signed_distance(nv) < T(0))
return false;
}
return true;
}
template <class T>
bool convex_hull<T>::contains(const math::vector<T, 3>& point) const
{
for (const plane<T>& plane: planes)
if (plane.signed_distance(point) < T(0))
return false;
return true;
}
} // namespace geom
#endif // ANTKEEPER_GEOM_CONVEX_HULL_HPP

+ 1
- 21
src/engine/geom/geom.hpp View File

@ -20,27 +20,7 @@
#ifndef ANTKEEPER_GEOM_HPP
#define ANTKEEPER_GEOM_HPP
/// Geometry
/// Geometric algorithms.
namespace geom {}
#include <engine/aabb.hpp>
#include <engine/bounding-volume.hpp>
#include <engine/convex-hull.hpp>
#include <engine/cartesian.hpp>
#include <engine/csg.hpp>
#include <engine/intersection.hpp>
#include <engine/marching-cubes.hpp>
#include <engine/mesh.hpp>
#include <engine/mesh-accelerator.hpp>
#include <engine/mesh-functions.hpp>
#include <engine/morton.hpp>
#include <engine/octree.hpp>
#include <engine/plane.hpp>
#include <engine/projection.hpp>
#include <engine/ray.hpp>
#include <engine/sdf.hpp>
#include <engine/sphere.hpp>
#include <engine/spherical.hpp>
#include <engine/view-frustum.hpp>
#endif // ANTKEEPER_GEOM_HPP

+ 0
- 187
src/engine/geom/intersection.cpp View File

@ -1,187 +0,0 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include <engine/geom/intersection.hpp>
#include <limits>
namespace geom {
std::tuple<bool, float> ray_plane_intersection(const ray<float>& ray, const plane<float>& plane)
{
float denom = math::dot(ray.direction, plane.normal);
if (denom != 0.0f)
{
float t = -(math::dot(ray.origin, plane.normal) + plane.distance) / denom;
if (t >= 0.0f)
{
return std::make_tuple(true, t);
}
}
return std::make_tuple(false, std::numeric_limits<float>::infinity());
}
std::tuple<bool, float, float, float> ray_triangle_intersection(const ray<float>& ray, const float3& a, const float3& b, const float3& c)
{
// Find edges
float3 edge10 = b - a;
float3 edge20 = c - a;
// Calculate determinant
float3 pv = math::cross(ray.direction, edge20);
float det = math::dot(edge10, pv);
if (!det)
{
return std::make_tuple(false, std::numeric_limits<float>::infinity(), 0.0f, 0.0f);
}
float inverse_det = 1.0f / det;
// Calculate u
float3 tv = ray.origin - a;
float u = math::dot(tv, pv) * inverse_det;
if (u < 0.0f || u > 1.0f)
{
return std::make_tuple(false, std::numeric_limits<float>::infinity(), 0.0f, 0.0f);
}
// Calculate v
float3 qv = math::cross(tv, edge10);
float v = math::dot(ray.direction, qv) * inverse_det;
if (v < 0.0f || u + v > 1.0f)
{
return std::make_tuple(false, std::numeric_limits<float>::infinity(), 0.0f, 0.0f);
}
// Calculate t
float t = math::dot(edge20, qv) * inverse_det;
if (t > 0.0f)
{
return std::make_tuple(true, t, u, v);
}
return std::make_tuple(false, std::numeric_limits<float>::infinity(), 0.0f, 0.0f);
}
std::tuple<bool, float, float> ray_aabb_intersection(const ray<float>& ray, const aabb<float>& aabb)
{
float t0 = -std::numeric_limits<float>::infinity();
float t1 = std::numeric_limits<float>::infinity();
for (std::size_t i = 0; i < 3; ++i)
{
if (ray.direction[i] == 0.0f)
{
if (ray.origin[i] < aabb.min_point[i] || ray.origin[i] > aabb.max_point[i])
{
return std::make_tuple(false, std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity());
}
}
else
{
float tmin = (aabb.min_point[i] - ray.origin[i]) / ray.direction[i];
float tmax = (aabb.max_point[i] - ray.origin[i]) / ray.direction[i];
t0 = std::max(t0, std::min(tmin, tmax));
t1 = std::min(t1, std::max(tmin, tmax));
}
}
if (t0 > t1 || t1 < 0.0f)
{
return std::make_tuple(false, std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity());
}
return std::make_tuple(true, t0, t1);
}
std::tuple<bool, float, float, std::size_t, std::size_t> ray_mesh_intersection(const ray<float>& ray, const mesh& mesh)
{
const std::vector<mesh::face*>& triangles = mesh.get_faces();
bool intersection = false;
float t0 = std::numeric_limits<float>::infinity();
float t1 = -std::numeric_limits<float>::infinity();
std::size_t index0 = triangles.size();
std::size_t index1 = triangles.size();
for (std::size_t i = 0; i < triangles.size(); ++i)
{
const mesh::face* triangle = triangles[i];
const float3& a = reinterpret_cast<const float3&>(triangle->edge->vertex->position);
const float3& b = reinterpret_cast<const float3&>(triangle->edge->next->vertex->position);
const float3& c = reinterpret_cast<const float3&>(triangle->edge->previous->vertex->position);
auto result = ray_triangle_intersection(ray, a, b, c);
if (std::get<0>(result))
{
intersection = true;
float t = std::get<1>(result);
if (t < t0)
{
t0 = t;
index0 = i;
}
if (t > t1)
{
t1 = t;
index1 = i;
}
}
}
return std::make_tuple(intersection, t0, t1, index0, index1);
}
bool aabb_aabb_intersection(const aabb<float>& a, const aabb<float>& b)
{
if (a.max_point.x() < b.min_point.x() || a.min_point.x() > b.max_point.x())
return false;
if (a.max_point.y() < b.min_point.y() || a.min_point.y() > b.max_point.y())
return false;
if (a.max_point.z() < b.min_point.z() || a.min_point.z() > b.max_point.z())
return false;
return true;
}
bool aabb_sphere_intersection(const aabb<float>& aabb, const float3& center, float radius)
{
float sqr_distance = 0.0f;
for (int i = 0; i < 3; ++i)
{
float v = center[i];
if (v < aabb.min_point[i])
sqr_distance += (aabb.min_point[i] - v) * (aabb.min_point[i] - v);
if (v > aabb.max_point[i])
sqr_distance += (v - aabb.max_point[i]) * (v - aabb.max_point[i]);
}
return (sqr_distance <= (radius * radius));
}
} // namespace geom

+ 224
- 27
src/engine/geom/intersection.hpp View File

@ -20,55 +20,252 @@
#ifndef ANTKEEPER_GEOM_INTERSECTION_HPP
#define ANTKEEPER_GEOM_INTERSECTION_HPP
#include <engine/geom/aabb.hpp>
#include <engine/geom/mesh.hpp>
#include <engine/geom/plane.hpp>
#include <engine/geom/ray.hpp>
#include <engine/geom/sphere.hpp>
#include <engine/utility/fundamental-types.hpp>
#include <tuple>
#include <engine/geom/primitives/hyperplane.hpp>
#include <engine/geom/primitives/hyperrectangle.hpp>
#include <engine/geom/primitives/hypersphere.hpp>
#include <engine/geom/primitives/ray.hpp>
#include <algorithm>
#include <optional>
namespace geom {
/**
* Tests for intersection between a ray and a plane.
* Ray-hyperplane intersection test.
*
* @param ray Ray to test for intersection.
* @param plane Plane to test for intersection.
* @return Tuple containing a boolean indicating whether intersection occurred, and a float indicating the signed distance along the ray to the point of intersection.
* @param ray Ray.
* @param hyperplane Hyperplane.
*
* @return Distance along the ray to the point of intersection, or `std::nullopt` if no intersection occurred.
*/
std::tuple<bool, float> ray_plane_intersection(const ray<float>& ray, const plane<float>& plane);
/// @{
template <class T, std::size_t N>
[[nodiscard]] constexpr std::optional<T> intersection(const ray<T, N>& ray, const hyperplane<T, N>& hyperplane) noexcept
{
const T cos_theta = math::dot(ray.direction, hyperplane.normal);
if (cos_theta != T{0})
{
const T t = -hyperplane.distance(ray.origin) / cos_theta;
if (t >= T{0})
{
return t;
}
}
return std::nullopt;
}
std::tuple<bool, float, float, float> ray_triangle_intersection(const ray<float>& ray, const float3& a, const float3& b, const float3& c);
template <class T, std::size_t N>
[[nodiscard]] inline constexpr std::optional<T> intersection(const hyperplane<T, N>& hyperplane, const ray<T, N>& ray) noexcept
{
return intersection<T, N>(ray, hyperplane);
}
/// @}
std::tuple<bool, float, float> ray_aabb_intersection(const ray<float>& ray, const aabb<float>& aabb);
/**
* Ray-hyperrectangle intersection test.
*
* @param ray Ray.
* @param hyperrectangle Hyperrectangle.
*
* @return Tuple containing the distances along the ray to the first and second points of intersection, or `std::nullopt` if no intersection occurred.
*/
/// @{
template <class T, std::size_t N>
[[nodiscard]] constexpr std::optional<std::tuple<T, T>> intersection(const ray<T, N>& ray, const hyperrectangle<T, N>& hyperrectangle) noexcept
{
T t0 = -std::numeric_limits<T>::infinity();
T t1 = std::numeric_limits<T>::infinity();
for (std::size_t i = 0; i < N; ++i)
{
if (!ray.direction[i])
{
if (ray.origin[i] < hyperrectangle.min[i] || ray.origin[i] > hyperrectangle.max[i])
{
return std::nullopt;
}
}
else
{
T min = (hyperrectangle.min[i] - ray.origin[i]) / ray.direction[i];
T max = (hyperrectangle.max[i] - ray.origin[i]) / ray.direction[i];
t0 = std::max(t0, std::min(min, max));
t1 = std::min(t1, std::max(min, max));
}
}
if (t0 > t1 || t1 < T{0})
{
return std::nullopt;
}
return std::tuple<T, T>{t0, t1};
}
std::tuple<bool, float, float, std::size_t, std::size_t> ray_mesh_intersection(const ray<float>& ray, const mesh& mesh);
template <class T, std::size_t N>
[[nodiscard]] inline constexpr std::optional<std::tuple<T, T>> intersection(const hyperrectangle<T, N>& hyperrectangle, const ray<T, N>& ray) noexcept
{
return intersection<T, N>(ray, hyperrectangle);
}
/// @}
/**
* Ray-sphere intersection test.
* Ray-hypersphere intersection test.
*
* @param ray Ray.
* @param hypersphere Hypersphere.
*
* @return Tuple containing the distances along the ray to the first and second points of intersection, or `std::nullopt` if no intersection occurred.
*/
template <class T>
std::tuple<bool, T, T> ray_sphere_intersection(const ray<T>& ray, const sphere<T>& sphere)
template <class T, std::size_t N>
[[nodiscard]] std::optional<std::tuple<T, T>> intersection(const ray<T, N>& ray, const hypersphere<T, N>& hypersphere) noexcept
{
const auto d = ray.origin - sphere.center;
const T b = math::dot(d, ray.direction);
const T c = math::dot(d, d) - sphere.radius * sphere.radius;
const math::vector<T, N> displacement = ray.origin - hypersphere.center;
const T b = math::dot(displacement, ray.direction);
const T c = math::sqr_length(displacement) - hypersphere.radius * hypersphere.radius;
T h = b * b - c;
if (h < T(0))
return {false, T(0), T(0)};
if (h < T{0})
{
return std::nullopt;
}
h = std::sqrt(h);
return {true, -b - h, -b + h};
T t0 = -b - h;
T t1 = -b + h;
if (t0 > t1)
{
std::swap(t0, t1);
}
if (t0 < T{0})
{
return std::nullopt;
}
return std::tuple<T, T>{t0, t1};
}
bool aabb_sphere_intersection(const aabb<float>& aabb, const float3& center, float radius);
/**
* Ray-triangle intersection test.
*
* @param ray Ray.
* @param a,b,c Triangle points.
*
* @return Tuple containing the distance along the ray to the point of intersection, followed by two barycentric coordinates of the point of intersection, or `std::nullopt` if no intersection occurred.
*/
template <class T>
[[nodiscard]] std::optional<std::tuple<T, T, T>> intersection(const ray<T, 3>& ray, const math::vector<T, 3>& a, const math::vector<T, 3>& b, const math::vector<T, 3>& c)
{
// Find edges
const math::vector<T, 3> edge10 = b - a;
const math::vector<T, 3> edge20 = c - a;
// Calculate determinant
const math::vector<T, 3> pv = math::cross(ray.direction, edge20);
const T det = math::dot(edge10, pv);
if (!det)
{
return std::nullopt;
}
const T inverse_det = T{1} / det;
// Calculate u
const math::vector<T, 3> tv = ray.origin - a;
const T u = math::dot(tv, pv) * inverse_det;
if (u < T{0} || u > T{1})
{
return std::nullopt;
}
bool aabb_aabb_intersection(const aabb<float>& a, const aabb<float>& b);
// Calculate v
const math::vector<T, 3> qv = math::cross(tv, edge10);
const T v = math::dot(ray.direction, qv) * inverse_det;
if (v < T{0} || u + v > T{1})
{
return std::nullopt;
}
// Calculate t
const T t = math::dot(edge20, qv) * inverse_det;
if (t > T{0})
{
return std::tuple<T, T, T>{t, u, v};
}
return std::nullopt;
}
/**
* Hyperrectangle-hyperrectangle intersection test.
*
* @param a First hyperrectangle.
* @param b Second hyperrectangle.
*
* @return `true` if an intersection occurred, `false` otherwise.
*/
template <class T, std::size_t N>
[[nodiscard]] inline constexpr bool intersection(const hyperrectangle<T, N>& a, const hyperrectangle<T, N>& b) noexcept
{
return a.intersects(b);
}
/**
* Hyperrectangle-hypersphere intersection test.
*
* @param hyperrectangle Hyperrectangle.
* @param hypersphere Hypersphere.
*
* @return `true` if an intersection occurred, `false` otherwise.
*/
/// @{
template <class T, std::size_t N>
[[nodiscard]] constexpr bool intersection(const hyperrectangle<T, N>& hyperrectangle, const hypersphere<T, N>& hypersphere) noexcept
{
T sqr_distance{0};
for (std::size_t i = 0; i < N; ++i)
{
if (hypersphere.center[i] < hyperrectangle.min[i])
{
const T difference = hyperrectangle.min[i] - hypersphere.center[i];
sqr_distance += difference * difference;
}
else if (hypersphere.center[i] > hyperrectangle.max[i])
{
const T difference = hypersphere.center[i] - hyperrectangle.max[i];
sqr_distance += difference * difference;
}
}
return sqr_distance <= hypersphere.radius * hypersphere.radius;
}
template <class T, std::size_t N>
[[nodiscard]] inline constexpr bool intersection(const hypersphere<T, N>& hypersphere, const hyperrectangle<T, N>& hyperrectangle) noexcept
{
return intersection<T, N>(hyperrectangle, hypersphere);
}
/// @}
/**
* Hypersphere-hypersphere intersection test.
*
* @param a First hypersphere.
* @param b Second hypersphere.
*
* @return `true` if an intersection occurred, `false` otherwise.
*/
template <class T, std::size_t N>
[[nodiscard]] inline constexpr bool intersection(const hypersphere<T, N>& a, const hypersphere<T, N>& b) noexcept
{
return a.intersects(b);
}
} // namespace geom
#endif // ANTKEEPER_GEOM_INTERSECTION_HPP

+ 15
- 16
src/engine/geom/mesh-accelerator.cpp View File

@ -34,9 +34,9 @@ void mesh_accelerator::build(const mesh& mesh)
face_map.clear();
// Calculate mesh dimensions
aabb<float> bounds = calculate_bounds(mesh);
float3 mesh_dimensions = bounds.max_point - bounds.min_point;
center_offset = mesh_dimensions * 0.5f - (bounds.min_point + bounds.max_point) * 0.5f;
box<float> bounds = calculate_bounds(mesh);
float3 mesh_dimensions = bounds.max - bounds.min;
center_offset = mesh_dimensions * 0.5f - (bounds.min + bounds.max) * 0.5f;
// Calculate node dimensions at each octree depth
for (auto i = 0; i <= octree_type::max_depth; ++i)
@ -79,7 +79,7 @@ void mesh_accelerator::build(const mesh& mesh)
}
}
std::optional<mesh_accelerator::ray_query_result> mesh_accelerator::query_nearest(const ray<float>& ray) const
std::optional<mesh_accelerator::ray_query_result> mesh_accelerator::query_nearest(const ray<float, 3>& ray) const
{
ray_query_result result;
result.t = std::numeric_limits<float>::infinity();
@ -92,16 +92,13 @@ std::optional mesh_accelerator::query_neares
return std::nullopt;
}
void mesh_accelerator::query_nearest_recursive(float& nearest_t, geom::mesh::face*& nearest_face, typename octree_type::node_type node, const ray<float>& ray) const
void mesh_accelerator::query_nearest_recursive(float& nearest_t, geom::mesh::face*& nearest_face, typename octree_type::node_type node, const ray<float, 3>& ray) const
{
// Get node bounds
const aabb<float> node_bounds = get_node_bounds(node);
// Test for intersection with node bounds
auto aabb_intersection = ray_aabb_intersection(ray, node_bounds);
const box<float> node_bounds = get_node_bounds(node);
// If ray passed through this node
if (std::get<0>(aabb_intersection))
if (intersection(ray, node_bounds))
{
// Test all triangles in the node
if (auto it = face_map.find(node); it != face_map.end())
@ -116,10 +113,10 @@ void mesh_accelerator::query_nearest_recursive(float& nearest_t, geom::mesh::fac
const float3& c = reinterpret_cast<const float3&>(face->edge->previous->vertex->position);
// Test for intersection with triangle
auto triangle_intersection = ray_triangle_intersection(ray, a, b, c);
if (std::get<0>(triangle_intersection))
auto triangle_intersection = intersection(ray, a, b, c);
if (triangle_intersection)
{
float t = std::get<1>(triangle_intersection);
const float t = std::get<0>(*triangle_intersection);
if (t < nearest_t)
{
nearest_t = t;
@ -133,12 +130,14 @@ void mesh_accelerator::query_nearest_recursive(float& nearest_t, geom::mesh::fac
if (!octree.is_leaf(node))
{
for (int i = 0; i < 8; ++i)
{
query_nearest_recursive(nearest_t, nearest_face, octree.child(node, i), ray);
}
}
}
}
aabb<float> mesh_accelerator::get_node_bounds(typename octree_type::node_type node) const
box<float> mesh_accelerator::get_node_bounds(typename octree_type::node_type node) const
{
// Decode Morton location of node
std::uint32_t x, y, z;
@ -150,7 +149,7 @@ aabb mesh_accelerator::get_node_bounds(typename octree_type::node_type no
// Calculate AABB
float3 min_point = (node_location * dimensions) - center_offset;
return aabb<float>{min_point, min_point + dimensions};
return box<float>{min_point, min_point + dimensions};
}
typename mesh_accelerator::octree_type::node_type mesh_accelerator::find_node(const float3& point) const

+ 4
- 4
src/engine/geom/mesh-accelerator.hpp View File

@ -22,7 +22,7 @@
#include <engine/geom/mesh.hpp>
#include <engine/geom/octree.hpp>
#include <engine/geom/aabb.hpp>
#include <engine/geom/primitives/box.hpp>
#include <engine/geom/intersection.hpp>
#include <engine/utility/fundamental-types.hpp>
#include <list>
@ -56,14 +56,14 @@ public:
* @param ray Ray to test for intersection.
* @return Ray query result on intersection, and `std::nullopt` if no intersection occurred.
*/
std::optional<ray_query_result> query_nearest(const ray<float>& ray) const;
std::optional<ray_query_result> query_nearest(const ray<float, 3>& ray) const;
private:
typedef unordered_octree32 octree_type;
aabb<float> get_node_bounds(typename octree_type::node_type node) const;
box<float> get_node_bounds(typename octree_type::node_type node) const;
void query_nearest_recursive(float& nearest_t, geom::mesh::face*& nearest_face, typename octree_type::node_type node, const ray<float>& ray) const;
void query_nearest_recursive(float& nearest_t, geom::mesh::face*& nearest_face, typename octree_type::node_type node, const ray<float, 3>& ray) const;
/// Returns the max-depth node in which the point is located
typename octree_type::node_type find_node(const float3& point) const;

+ 6
- 11
src/engine/geom/mesh-functions.cpp View File

@ -153,27 +153,22 @@ void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const
}
}
aabb<float> calculate_bounds(const mesh& mesh)
box<float> calculate_bounds(const mesh& mesh)
{
float3 bounds_min;
float3 bounds_max;
box<float> bounds;
for (int i = 0; i < 3; ++i)
{
bounds_min[i] = std::numeric_limits<float>::infinity();
bounds_max[i] = -std::numeric_limits<float>::infinity();
bounds.min[i] = std::numeric_limits<float>::infinity();
bounds.max[i] = -std::numeric_limits<float>::infinity();
}
for (const mesh::vertex* vertex: mesh.get_vertices())
{
const auto& position = vertex->position;
for (int i = 0; i < 3; ++i)
{
bounds_min[i] = std::min<float>(bounds_min[i], position[i]);
bounds_max[i] = std::max<float>(bounds_max[i], position[i]);
}
bounds.extend(position);
}
return aabb<float>{bounds_min, bounds_max};
return bounds;
}
mesh::vertex* poke_face(mesh& mesh, std::size_t index)

+ 2
- 2
src/engine/geom/mesh-functions.hpp View File

@ -20,7 +20,7 @@
#ifndef ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP
#define ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP
#include <engine/geom/aabb.hpp>
#include <engine/geom/primitives/box.hpp>
#include <engine/geom/mesh.hpp>
#include <engine/utility/fundamental-types.hpp>
#include <array>
@ -59,7 +59,7 @@ void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const
/**
* Calculates the AABB bounds of a mesh.
*/
aabb<float> calculate_bounds(const mesh& mesh);
box<float> calculate_bounds(const mesh& mesh);
/**
* Triangulates a face by adding a new vertex in the center, then creating triangles between the edges of the original face and the new vertex.

+ 0
- 123
src/engine/geom/plane.hpp View File

@ -1,123 +0,0 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GEOM_PLANE_HPP
#define ANTKEEPER_GEOM_PLANE_HPP
#include <engine/math/vector.hpp>
namespace geom {
/**
* A flat 2-dimensional surface.
*/
template <class T>
struct plane
{
typedef math::vector<T, 3> vector_type;
/// Plane normal vector.
vector_type normal;
/// Plane distance.
T distance;
/**
* Creates a plane given a normal vector and distance.
*/
plane(const vector_type& normal, T distance);
/**
* Creates a plane given a normal vector and offset vector.
*/
plane(const vector_type& normal, const vector_type& offset);
/**
* Creates a plane given three points.
*/
plane(const vector_type& a, const vector_type& b, const vector_type& c);
/**
* Creates a plane given its coefficients.
*
* @param coefficients Vector containing the plane coefficients, A, B, C and D, as x, y, z, and w, respectively.
*/
explicit plane(const math::vector<T, 4>& coefficients);
/// Creates an uninitialized plane.
plane() = default;
/**
* Calculates the signed distance between a plane and a vector.
*
* @param v Vector.
* @return Signed distance between the plane and vector.
*/
T signed_distance(const vector_type& v) const;
/**
* Calculates the point of intersection between three planes.
*/
static vector_type intersection(const plane& p0, const plane& p1, const plane& p2);
};
template <class T>
inline plane<T>::plane(const vector_type& normal, T distance):
normal(normal),
distance(distance)
{}
template <class T>
plane<T>::plane(const vector_type& normal, const vector_type& offset):
normal(normal),
distance(-math::dot(normal, offset))
{}
template <class T>
plane<T>::plane(const vector_type& a, const vector_type& b, const vector_type& c)
{
normal = math::normalize(math::cross(c - b, a - b));
distance = -(math::dot(normal, b));
}
template <class T>
plane<T>::plane(const math::vector<T, 4>& coefficients)
{
const vector_type abc = math::vector<T, 3>(coefficients);
const float inverse_length = T(1) / math::length(abc);
normal = abc * inverse_length;
distance = coefficients[3] * inverse_length;
}
template <class T>
inline T plane<T>::signed_distance(const vector_type& v) const
{
return distance + math::dot(normal, v);
}
template <class T>
typename plane<T>::vector_type plane<T>::intersection(const plane& p0, const plane& p1, const plane& p2)
{
return -(p0.distance * math::cross(p1.normal, p2.normal) + p1.distance * math::cross(p2.normal, p0.normal) + p2.distance * math::cross(p0.normal, p1.normal)) / math::dot(p0.normal, math::cross(p1.normal, p2.normal));
}
} // namespace geom
#endif // ANTKEEPER_GEOM_PLANE_HPP

+ 2
- 2
src/engine/geom/primitives/box.hpp View File

@ -23,7 +23,7 @@
#include <engine/geom/primitives/hyperrectangle.hpp>
namespace geom {
namespace primitive {
namespace primitives {
/**
* 3-dimensional hyperrectangle.
@ -33,7 +33,7 @@ namespace primitive {
template <class T>
using box = hyperrectangle<T, 3>;
} // namespace primitive
} // namespace primitives
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_BOX_HPP

+ 2
- 2
src/engine/geom/primitives/circle.hpp View File

@ -23,7 +23,7 @@
#include <engine/geom/primitives/hypersphere.hpp>
namespace geom {
namespace primitive {
namespace primitives {
/**
* 2-dimensional hypersphere.
@ -33,7 +33,7 @@ namespace primitive {
template <class T>
using circle = hypersphere<T, 2>;
} // namespace primitive
} // namespace primitives
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_CIRCLE_HPP

+ 5
- 2
src/engine/geom/primitives/hyperplane.hpp View File

@ -23,7 +23,7 @@
#include <engine/math/vector.hpp>
namespace geom {
namespace primitive {
namespace primitives {
/**
* *n*-dimensional plane.
@ -93,7 +93,10 @@ struct hyperplane
}
};
} // namespace primitive
} // namespace primitives
using namespace primitives;
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_HYPERPLANE_HPP

+ 22
- 2
src/engine/geom/primitives/hyperrectangle.hpp View File

@ -25,7 +25,7 @@
#include <cmath>
namespace geom {
namespace primitive {
namespace primitives {
/**
* *n*-dimensional axis-aligned rectangle.
@ -203,9 +203,29 @@ struct hyperrectangle
}
return v;
}
/**
* Returns the nth corner of the hyperrectangle.
*
* @param index Index of a corner.
*/
[[nodiscard]] constexpr vector_type corner(std::size_t index) const noexcept
{
vector_type p;
for (std::size_t i = 0; i < N; ++i)
{
p[i] = ((index >> ((N - 1) - i)) & 1) ? max[i] : min[i];
}
return p;
}
};
} // namespace primitive
} // namespace primitives
using namespace primitives;
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_HYPERRECTANGLE_HPP

+ 5
- 2
src/engine/geom/primitives/hypersphere.hpp View File

@ -24,7 +24,7 @@
#include <engine/math/vector.hpp>
namespace geom {
namespace primitive {
namespace primitives {
/**
* *n*-dimensional sphere.
@ -149,7 +149,10 @@ struct hypersphere
}
};
} // namespace primitive
} // namespace primitives
using namespace primitives;
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_HYPERSPHERE_HPP

+ 0
- 207
src/engine/geom/primitives/intersection.hpp View File

@ -1,207 +0,0 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GEOM_PRIMITIVES_INTERSECTION_HPP
#define ANTKEEPER_GEOM_PRIMITIVES_INTERSECTION_HPP
#include <engine/geom/primitives/hyperplane.hpp>
#include <engine/geom/primitives/hyperrectangle.hpp>
#include <engine/geom/primitives/hypersphere.hpp>
#include <engine/geom/primitives/ray.hpp>
#include <algorithm>
#include <optional>
namespace geom {
namespace primitive {
/**
* Ray-hyperplane intersection test.
*
* @param ray Ray.
* @param hyperplane Hyperplane.
*
* @return Distance along the ray to the point of intersection, or `std::nullopt` if no intersection occurred.
*/
/// @{
template <class T, std::size_t N>
constexpr std::optional<T> intersection(const ray<T, N>& ray, const hyperplane<T, N>& hyperplane) noexcept
{
const T cos_theta = math::dot(ray.direction, hyperplane.normal);
if (cos_theta != T{0})
{
const T t = -hyperplane.distance(ray.origin) / cos_theta;
if (t >= T{0})
return t;
}
return std::nullopt;
}
template <class T, std::size_t N>
inline constexpr std::optional<T> intersection(const hyperplane<T, N>& hyperplane, const ray<T, N>& ray) noexcept
{
return intersection<T, N>(ray, hyperplane);
}
/// @}
/**
* Ray-hyperrectangle intersection test.
*
* @param ray Ray.
* @param hyperrectangle Hyperrectangle.
*
* @return Tuple containing the distances along the ray to the first and second points of intersection, or `std::nullopt` if no intersection occurred.
*/
/// @{
template <class T, std::size_t N>
constexpr std::optional<std::tuple<T, T>> intersection(const ray<T, N>& ray, const hyperrectangle<T, N>& hyperrectangle) noexcept
{
T t0 = -std::numeric_limits<T>::infinity();
T t1 = std::numeric_limits<T>::infinity();
for (std::size_t i = 0; i < N; ++i)
{
if (!ray.direction[i])
{
if (ray.origin[i] < hyperrectangle.min[i] || ray.origin[i] > hyperrectangle.max[i])
return std::nullopt;
}
else
{
T min = (hyperrectangle.min[i] - ray.origin[i]) / ray.direction[i];
T max = (hyperrectangle.max[i] - ray.origin[i]) / ray.direction[i];
t0 = std::max(t0, std::min(min, max));
t1 = std::min(t1, std::max(min, max));
}
}
if (t0 > t1 || t1 < T{0})
return std::nullopt;
return {t0, t1};
}
template <class T, std::size_t N>
inline constexpr std::optional<std::tuple<T, T>> intersection(const hyperrectangle<T, N>& hyperrectangle, const ray<T, N>& ray) noexcept
{
return intersection<T, N>(ray, hyperrectangle);
}
/// @}
/**
* Ray-hypersphere intersection test.
*
* @param ray Ray.
* @param hypersphere Hypersphere.
*
* @return Tuple containing the distances along the ray to the first and second points of intersection, or `std::nullopt` if no intersection occurred.
*/
template <class T, std::size_t N>
std::optional<std::tuple<T, T>> intersection(const ray<T, N>& ray, const hypersphere<T, N>& hypersphere) noexcept
{
const math::vector<T, N> displacement = ray.origin - hypersphere.center;
const T b = math::dot(displacement, ray.direction);
const T c = math::sqr_length(displacement) - hypersphere.radius * hypersphere.radius;
T h = b * b - c;
if (h < T{0})
return std::nullopt;
h = std::sqrt(h);
T t0 = -b - h;
T t1 = -b + h;
if (t0 > t1)
std::swap(t0, t1);
if (t0 < T{0})
return std::nullopt;
return std::tuple<T, T>{t0, t1};
}
/**
* Hyperrectangle-hyperrectangle intersection test.
*
* @param a First hyperrectangle.
* @param b Second hyperrectangle.
*
* @return `true` if an intersection occurred, `false` otherwise.
*/
template <class T, std::size_t N>
inline constexpr bool intersection(const hyperrectangle<T, N>& a, const hyperrectangle<T, N>& b) noexcept
{
return a.intersects(b);
}
/**
* Hyperrectangle-hypersphere intersection test.
*
* @param hyperrectangle Hyperrectangle.
* @param hypersphere Hypersphere.
*
* @return `true` if an intersection occurred, `false` otherwise.
*/
/// @{
template <class T, std::size_t N>
constexpr bool intersection(const hyperrectangle<T, N>& hyperrectangle, const hypersphere<T, N>& hypersphere) noexcept
{
T sqr_distance{0};
for (std::size_t i = 0; i < N; ++i)
{
if (hypersphere.center[i] < hyperrectangle.min[i])
{
const T difference = hyperrectangle.min[i] - hypersphere.center[i];
sqr_distance += difference * difference;
}
else if (hypersphere.center[i] > hyperrectangle.max[i])
{
const T difference = hypersphere.center[i] - hyperrectangle.max[i];
sqr_distance += difference * difference;
}
}
return sqr_distance <= hypersphere.radius * hypersphere.radius;
}
template <class T, std::size_t N>
inline constexpr bool intersection(const hypersphere<T, N>& hypersphere, const hyperrectangle<T, N>& hyperrectangle) noexcept
{
return intersection<T, N>(hyperrectangle, hypersphere);
}
/// @}
/**
* Hypersphere-hypersphere intersection test.
*
* @param a First hypersphere.
* @param b Second hypersphere.
*
* @return `true` if an intersection occurred, `false` otherwise.
*/
template <class T, std::size_t N>
inline constexpr bool intersection(const hypersphere<T, N>& a, const hypersphere<T, N>& b) noexcept
{
return a.intersects(b);
}
} // namespace primitive
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_INTERSECTION_HPP

+ 5
- 2
src/engine/geom/primitives/line-segment.hpp View File

@ -23,7 +23,7 @@
#include <engine/math/vector.hpp>
namespace geom {
namespace primitive {
namespace primitives {
/**
* *n*-dimensional line segment.
@ -54,7 +54,10 @@ struct line_segment
}
};
} // namespace primitive
} // namespace primitives
using namespace primitives;
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_LINE_SEGMENT_HPP

+ 2
- 2
src/engine/geom/primitives/line.hpp View File

@ -23,7 +23,7 @@
#include <engine/geom/primitives/hyperplane.hpp>
namespace geom {
namespace primitive {
namespace primitives {
/**
* 2-dimensional hyperplane.
@ -33,7 +33,7 @@ namespace primitive {
template <class T>
using line = hyperplane<T, 2>;
} // namespace primitive
} // namespace primitives
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_LINE_HPP

+ 2
- 2
src/engine/geom/primitives/plane.hpp View File

@ -23,7 +23,7 @@
#include <engine/geom/primitives/hyperplane.hpp>
namespace geom {
namespace primitive {
namespace primitives {
/**
* 3-dimensional hyperplane.
@ -33,7 +33,7 @@ namespace primitive {
template <class T>
using plane = hyperplane<T, 3>;
} // namespace primitive
} // namespace primitives
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_PLANE_HPP

+ 5
- 2
src/engine/geom/primitives/ray.hpp View File

@ -25,7 +25,7 @@
#include <cmath>
namespace geom {
namespace primitive {
namespace primitives {
/**
* Half of a line proceeding from an initial point.
@ -95,7 +95,10 @@ struct ray
}
};
} // namespace primitive
} // namespace primitives
using namespace primitives;
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_RAY_HPP

+ 2
- 2
src/engine/geom/primitives/rectangle.hpp View File

@ -23,7 +23,7 @@
#include <engine/geom/primitives/hyperrectangle.hpp>
namespace geom {
namespace primitive {
namespace primitives {
/**
* 2-dimensional hyperrectangle.
@ -33,7 +33,7 @@ namespace primitive {
template <class T>
using rectangle = hyperrectangle<T, 2>;
} // namespace primitive
} // namespace primitives
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_RECTANGLE_HPP

+ 2
- 2
src/engine/geom/primitives/sphere.hpp View File

@ -23,7 +23,7 @@
#include <engine/geom/primitives/hypersphere.hpp>
namespace geom {
namespace primitive {
namespace primitives {
/**
* 3-dimensional hypersphere.
@ -33,7 +33,7 @@ namespace primitive {
template <class T>
using sphere = hypersphere<T, 3>;
} // namespace primitive
} // namespace primitives
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_SPHERE_HPP

+ 317
- 0
src/engine/geom/primitives/view-frustum.hpp View File

@ -0,0 +1,317 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GEOM_PRIMITIVES_VIEW_FRUSTUM_HPP
#define ANTKEEPER_GEOM_PRIMITIVES_VIEW_FRUSTUM_HPP
#include <engine/math/vector.hpp>
#include <engine/math/matrix.hpp>
#include <engine/geom/primitives/plane.hpp>
#include <engine/geom/primitives/box.hpp>
#include <engine/geom/primitives/sphere.hpp>
namespace geom {
namespace primitives {
/**
* View frustum.
*
* @tparam T Real type.
*/
template <class T>
struct view_frustum
{
/// Vector type.
using vector_type = math::vector<T, 3>;
/// Plane type.
using plane_type = geom::plane<T>;
/// View-projection matrix type.
using matrix_type = math::matrix<T, 4, 4>;
/// Box type.
using box_type = geom::box<T>;
/// Sphere type.
using sphere_type = geom::sphere<T>;
/// Constructs a view frustum
constexpr view_frustum() noexcept = default;
/**
* Constructs a view frustum by extracting planes from view-projection matrix.
*
* @param matrix View-projection matrix from which to extract view frustum planes.
*/
inline explicit constexpr view_frustum(const matrix_type& matrix) noexcept
{
extract(matrix);
}
/// Returns the left clipping plane.
/// @{
[[nodiscard]] inline constexpr const plane_type& left() const noexcept
{
return planes[0];
}
[[nodiscard]] inline constexpr plane_type& left() noexcept
{
return planes[0];
}
/// @}
/// Returns the right clipping plane.
/// @{
[[nodiscard]] inline constexpr const plane_type& right() const noexcept
{
return planes[1];
}
[[nodiscard]] inline constexpr plane_type& right() noexcept
{
return planes[1];
}
/// @}
/// Returns the bottom clipping plane.
/// @{
[[nodiscard]] inline constexpr const plane_type& bottom() const noexcept
{
return planes[2];
}
[[nodiscard]] inline constexpr plane_type& bottom() noexcept
{
return planes[2];
}
/// @}
/// Returns the top clipping plane.
/// @{
[[nodiscard]] inline constexpr const plane_type& top() const noexcept
{
return planes[3];
}
[[nodiscard]] inline constexpr plane_type& top() noexcept
{
return planes[3];
}
/// @}
/// Returns the near clipping plane.
/// @{
[[nodiscard]] inline constexpr const plane_type& near() const noexcept
{
return planes[4];
}
[[nodiscard]] inline constexpr plane_type& near() noexcept
{
return planes[4];
}
/// @}
/// Returns the far clipping plane.
/// @{
[[nodiscard]] inline constexpr const plane_type& far() const noexcept
{
return planes[5];
}
[[nodiscard]] inline constexpr plane_type& far() noexcept
{
return planes[5];
}
/// @}
/**
* Extracts the view frustum planes from a view-projection matrix.
*
* @param matrix View-projection matrix from which to extract view frustum planes.
*/
void extract(const matrix_type& matrix) noexcept
{
for (std::size_t i = 0; i < 6; ++i)
{
plane_type& plane = planes[i];
const std::size_t j = i >> 1;
// Extract plane coefficients
if (i % 2)
{
plane.normal.x() = matrix[0][3] - matrix[0][j];
plane.normal.y() = matrix[1][3] - matrix[1][j];
plane.normal.z() = matrix[2][3] - matrix[2][j];
plane.constant = matrix[3][3] - matrix[3][j];
}
else
{
plane.normal.x() = matrix[0][3] + matrix[0][j];
plane.normal.y() = matrix[1][3] + matrix[1][j];
plane.normal.z() = matrix[2][3] + matrix[2][j];
plane.constant = matrix[3][3] + matrix[3][j];
}
// Normalize plane coefficients
const T inv_length = math::inv_length(plane.normal);
plane.normal *= inv_length;
plane.constant *= inv_length;
}
}
/**
* Tests for intersection between an axis-aligned box and the view frustum.
*
* @param box Box to test for intersection with the view frustum.
*
* @return `true` if the axis-aligned box intersects the view frustum, `false` otherwise.
*/
[[nodiscard]] bool intersects(const box_type& box) const noexcept
{
for (const auto& plane: planes)
{
const vector_type p
{
(plane.normal.x() > T{0}) ? box.max.x() : box.min.x(),
(plane.normal.y() > T{0}) ? box.max.y() : box.min.y(),
(plane.normal.z() > T{0}) ? box.max.z() : box.min.z()
};
if (plane.distance(p) < T{0})
{
return false;
}
}
return true;
}
/**
* Tests for intersection between a sphere and the view frustum.
*
* @param sphere Sphere to test for intersection with the view frustum.
*
* @return `true` if the sphere intersects the view frustum, `false` otherwise.
*/
[[nodiscard]] bool intersects(const sphere_type& sphere) const noexcept
{
for (const auto& plane: planes)
{
if (plane.distance(sphere.center) < -sphere.radius)
{
return false;
}
}
return true;
}
/**
* Tests whether a point is contained within this view frustum.
*
* @param point Point to test for containment.
*
* @return `true` if the point is contained within this view frustum, `false` otherwise.
*/
[[nodiscard]] constexpr bool contains(const vector_type& point) const noexcept
{
for (const auto& plane: planes)
{
if (plane.distance(point) < T{0})
{
return false;
}
}
return true;
}
/**
* Checks if an axis-aligned box is completely contained within the view frustum.
*
* @param box Box to test for containment within the view frustum.
*
* @return `true` if the axis-aligned box is completely contained within the view frustum, `false` otherwise.
*/
[[nodiscard]] bool contains(const box_type& box) const noexcept
{
for (const auto& plane: planes)
{
const vector_type p
{
(plane.normal.x() > T{0}) ? box.max.x() : box.min.x(),
(plane.normal.y() > T{0}) ? box.max.y() : box.min.y(),
(plane.normal.z() > T{0}) ? box.max.z() : box.min.z()
};
const vector_type n
{
(plane.normal.x() < T{0}) ? box.max.x() : box.min.x(),
(plane.normal.y() < T{0}) ? box.max.y() : box.min.y(),
(plane.normal.z() < T{0}) ? box.max.z() : box.min.z()
};
if (plane.distance(p) < T{0} || plane.distance(n) < T{0})
{
return false;
}
}
return true;
}
/**
* Checks if a sphere is completely contained within the view frustum.
*
* @param sphere Sphere to test for containment within the view frustum.
*
* @return `true` if the sphere is completely contained within the view frustum, `false` otherwise.
*/
[[nodiscard]] bool contains(const sphere_type& sphere) const noexcept
{
for (const auto& plane: planes)
{
if (plane.distance(sphere.center) < sphere.radius)
{
return false;
}
}
return true;
}
/**
* View frustum clipping planes.
*
* Clipping planes are stored in the following order:
*
* 1. left
* 2. right
* 3. bottom
* 4. top
* 5. near
* 6. far
*/
plane_type planes[6];
};
} // namespace primitives
using namespace primitives;
} // namespace geom
#endif // ANTKEEPER_GEOM_PRIMITIVES_VIEW_FRUSTUM_HPP

+ 0
- 59
src/engine/geom/ray.hpp View File

@ -1,59 +0,0 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GEOM_RAY_HPP
#define ANTKEEPER_GEOM_RAY_HPP
#include <engine/math/vector.hpp>
namespace geom {
/**
* Half of a line proceeding from an initial point.
*/
template <class T>
struct ray
{
typedef math::vector<T, 3> vector_type;
/// Origin of the ray.
vector_type origin;
/// Normalized direction vector of the ray.
vector_type direction;
/**
* Extrapolates from the ray origin along the ray direction vector.
*
* @param distance Distance to extrapolate.
* @return Extrapolated coordinates.
*/
vector_type extrapolate(T distance) const;
};
template <class T>
inline typename ray<T>::vector_type ray<T>::extrapolate(T distance) const
{
return origin + direction * distance;
}
} // namespace geom
#endif // ANTKEEPER_GEOM_RAY_HPP

+ 13
- 7
src/engine/geom/rect-pack.hpp View File

@ -20,7 +20,7 @@
#ifndef ANTKEEPER_GEOM_RECT_PACK_HPP
#define ANTKEEPER_GEOM_RECT_PACK_HPP
#include <engine/geom/rect.hpp>
#include <engine/geom/primitives/rectangle.hpp>
#include <memory>
namespace geom {
@ -34,16 +34,16 @@ template
struct rect_pack_node
{
/// Scalar type.
typedef T scalar_type;
using scalar_type = T;
/// Rect type.
typedef rect<T> rect_type;
using rect_type = rectangle<T>;
/// Pointers to the two children of the node, if any.
std::unique_ptr<rect_pack_node> children[2];
/// Bounds of the node.
rect_type bounds{T{0}, T{0}, T{0}, T{0}};
rect_type bounds{{T{0}, T{0}}, {T{0}, T{0}}};
/// `true` if the node is occupied, `false` otherwise.
bool occupied{false};
@ -61,10 +61,10 @@ class rect_pack
{
public:
/// Scalar type.
typedef T scalar_type;
using scalar_type = T;
/// Node type.
typedef rect_pack_node<T> node_type;
using node_type = rect_pack_node<T>;
/**
* Creates a rect pack and sets the bounds of the root node.
@ -127,7 +127,7 @@ template
void rect_pack<T>::resize(scalar_type w, scalar_type h)
{
clear();
root.bounds = {T(0), T(0), w, h};
root.bounds = {{T{0}, T{0}}, {w, h}};
}
template <class T>
@ -159,7 +159,9 @@ typename rect_pack::node_type* rect_pack::insert(node_type& node, scalar_t
// Attempt to insert into first child
node_type* result = insert(*node.children[0], w, h);
if (result)
{
return result;
}
// Cannot fit in first child, attempt to insert into second child
return insert(*node.children[1], w, h);
@ -168,7 +170,9 @@ typename rect_pack::node_type* rect_pack::insert(node_type& node, scalar_t
// Abort if node occupied
if (node.occupied)
{
return nullptr;
}
// Determine node dimensions
scalar_type node_w = node.bounds.max.x() - node.bounds.min.x();
@ -176,7 +180,9 @@ typename rect_pack::node_type* rect_pack::insert(node_type& node, scalar_t
// Check if rect is larger than node
if (w > node_w || h > node_h)
{
return nullptr;
}
// Check for a perfect fit
if (w == node_w && h == node_h)

+ 0
- 41
src/engine/geom/rect.hpp View File

@ -1,41 +0,0 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GEOM_RECT_HPP
#define ANTKEEPER_GEOM_RECT_HPP
#include <engine/math/vector.hpp>
namespace geom {
/**
* 2D rectangle.
*/
template <class T>
struct rect
{
typedef math::vector<T, 2> vector_type;
vector_type min;
vector_type max;
};
} // namespace geom
#endif // ANTKEEPER_GEOM_RECT_HPP

+ 0
- 117
src/engine/geom/sphere.hpp View File

@ -1,117 +0,0 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GEOM_SPHERE_HPP
#define ANTKEEPER_GEOM_SPHERE_HPP
#include <engine/geom/bounding-volume.hpp>
#include <engine/geom/aabb.hpp>
#include <engine/math/vector.hpp>
#include <algorithm>
namespace geom {
/**
* Bounding sphere.
*/
template <class T>
struct sphere: public bounding_volume<T>
{
typedef math::vector<T, 3> vector_type;
vector_type center;
T radius;
sphere(const vector_type& center, T radius);
sphere();
virtual bounding_volume_type get_bounding_volume_type() const;
virtual bool intersects(const sphere<T>& sphere) const;
virtual bool intersects(const aabb<T>& aabb) const;
virtual bool contains(const sphere<T>& sphere) const;
virtual bool contains(const aabb<T>& aabb) const;
virtual bool contains(const vector_type& point) const;
};
template <class T>
sphere<T>::sphere(const vector_type& center, T radius):
center(center),
radius(radius)
{}
template <class T>
sphere<T>::sphere()
{}
template <class T>
inline bounding_volume_type sphere<T>::get_bounding_volume_type() const
{
return bounding_volume_type::sphere;
}
template <class T>
bool sphere<T>::intersects(const sphere<T>& sphere) const
{
vector_type d = center - sphere.center;
T r = radius + sphere.radius;
return (math::dot(d, d) <= r * r);
}
template <class T>
bool sphere<T>::intersects(const aabb<T>& aabb) const
{
return aabb.intersects(*this);
}
template <class T>
bool sphere<T>::contains(const sphere<T>& sphere) const
{
T containment_radius = radius - sphere.radius;
if (containment_radius < T(0))
return false;
vector_type d = center - sphere.center;
return (math::dot(d, d) <= containment_radius * containment_radius);
}
template <class T>
bool sphere<T>::contains(const aabb<T>& aabb) const
{
T distance = T(0);
vector_type a = center - aabb.min_point;
vector_type b = center - aabb.max_point;
distance += std::max(a.x() * a.x(), b.x() * b.x());
distance += std::max(a.y() * a.y(), b.y() * b.y());
distance += std::max(a.z() * a.z(), b.z() * b.z());
return (distance <= radius * radius);
}
template <class T>
bool sphere<T>::contains(const vector_type& point) const
{
vector_type d = center - point;
return (math::dot(d, d) <= radius * radius);
}
} // namespace geom
#endif // ANTKEEPER_GEOM_SPHERE_HPP

+ 0
- 190
src/engine/geom/view-frustum.hpp View File

@ -1,190 +0,0 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GEOM_VIEW_FRUSTUM_HPP
#define ANTKEEPER_GEOM_VIEW_FRUSTUM_HPP
#include <engine/geom/convex-hull.hpp>
#include <engine/math/vector.hpp>
#include <engine/math/matrix.hpp>
#include <array>
namespace geom {
/**
* View frustum.
*/
template <class T>
class view_frustum
{
public:
typedef math::vector<T, 3> vector_type;
typedef math::matrix<T, 4, 4> matrix_type;
typedef plane<T> plane_type;
/**
* Creates a view frustum from a view-projection matrix.
*
* @param view_projection View-projection matrix.
*/
explicit view_frustum(const matrix_type& view_projection);
/// Creates an uninitialized view frustum.
view_frustum();
/**
* Recalculates the view frustum from a view-projection matrix.
*
* @param view_projection View-projection matrix.
*/
void set_matrix(const matrix_type& view_projection);
/// Returns a convex hull which describes the bounds of the view frustum.
const convex_hull<T>& get_bounds() const;
/// Returns the left clipping plane.
const plane_type& get_left() const;
/// Returns the right clipping plane.
const plane_type& get_right() const;
/// Returns the bottom clipping plane.
const plane_type& get_bottom() const;
/// Returns the top clipping plane.
const plane_type& get_top() const;
/// Returns the near clipping plane.
const plane_type& get_near() const;
/// Returns the far clipping plane.
const plane_type& get_far() const;
/**
* Returns an array containing the corners of the view frustum bounds.
*
* @return Array containing the corners of the view frustum bounds. Corners are stored in the following order: NTL, NTR, NBL, NBR, FTL, FTR, FBL, FBR; where N is near, F is far, T is top, B is bottom, L is left, and R is right, therefore NTL refers to the corner shared between the near, top, and left clipping planes.
*/
const std::array<vector_type, 8>& get_corners() const;
private:
void recalculate_planes(const matrix_type& view_projection);
void recalculate_corners();
convex_hull<T> bounds;
std::array<vector_type, 8> corners;
};
template <class T>
view_frustum<T>::view_frustum(const matrix_type& view_projection):
bounds(6)
{
set_matrix(view_projection);
}
template <class T>
view_frustum<T>::view_frustum():
view_frustum(math::matrix4<T>::identity())
{}
template <class T>
void view_frustum<T>::set_matrix(const matrix_type& view_projection)
{
recalculate_planes(view_projection);
recalculate_corners();
}
template <class T>
inline const convex_hull<T>& view_frustum<T>::get_bounds() const
{
return bounds;
}
template <class T>
inline const typename view_frustum<T>::plane_type& view_frustum<T>::get_left() const
{
return bounds.planes[0];
}
template <class T>
inline const typename view_frustum<T>::plane_type& view_frustum<T>::get_right() const
{
return bounds.planes[1];
}
template <class T>
inline const typename view_frustum<T>::plane_type& view_frustum<T>::get_bottom() const
{
return bounds.planes[2];
}
template <class T>
inline const typename view_frustum<T>::plane_type& view_frustum<T>::get_top() const
{
return bounds.planes[3];
}
template <class T>
inline const typename view_frustum<T>::plane_type& view_frustum<T>::get_near() const
{
return bounds.planes[4];
}
template <class T>
inline const typename view_frustum<T>::plane_type& view_frustum<T>::get_far() const
{
return bounds.planes[5];
}
template <class T>
inline const std::array<typename view_frustum<T>::vector_type, 8>& view_frustum<T>::get_corners() const
{
return corners;
}
template <class T>
void view_frustum<T>::recalculate_planes(const matrix_type& view_projection)
{
matrix_type transpose = math::transpose(view_projection);
bounds.planes[0] = plane_type(transpose[3] + transpose[0]);
bounds.planes[1] = plane_type(transpose[3] - transpose[0]);
bounds.planes[2] = plane_type(transpose[3] + transpose[1]);
bounds.planes[3] = plane_type(transpose[3] - transpose[1]);
bounds.planes[4] = plane_type(transpose[3] + transpose[2]);
bounds.planes[5] = plane_type(transpose[3] - transpose[2]);
}
template <class T>
void view_frustum<T>::recalculate_corners()
{
/// @TODO THESE CORNERS MAY BE INCORRECT!!!!!!!!!!!
corners[0] = plane_type::intersection(get_near(), get_top(), get_left());
corners[1] = plane_type::intersection(get_near(), get_top(), get_right());
corners[2] = plane_type::intersection(get_near(), get_bottom(), get_left());
corners[3] = plane_type::intersection(get_near(), get_bottom(), get_right());
corners[4] = plane_type::intersection(get_far(), get_top(), get_left());
corners[5] = plane_type::intersection(get_far(), get_top(), get_right());
corners[6] = plane_type::intersection(get_far(), get_bottom(), get_left());
corners[7] = plane_type::intersection(get_far(), get_bottom(), get_right());
}
} // namespace geom
#endif // ANTKEEPER_GEOM_VIEW_FRUSTUM_HPP

+ 31
- 59
src/engine/math/quaternion.hpp View File

@ -24,8 +24,6 @@
#include <engine/math/matrix.hpp>
#include <engine/math/vector.hpp>
#include <cmath>
#include <istream>
#include <ostream>
namespace math {
@ -38,13 +36,13 @@ template
struct quaternion
{
/// Scalar type.
typedef T scalar_type;
using scalar_type = T;
/// Vector type.
typedef vector<T, 3> vector_type;
using vector_type = vector<T, 3>;
/// Rotation matrix type.
typedef matrix<T, 3, 3> matrix_type;
using matrix_type = matrix<T, 3, 3>;
/// Quaternion real part.
scalar_type r;
@ -109,7 +107,7 @@ struct quaternion
*/
[[nodiscard]] static quaternion rotate_x(scalar_type angle)
{
return {std::cos(angle * T(0.5)), std::sin(angle * T(0.5)), T(0), T(0)};
return {std::cos(angle * T{0.5}), std::sin(angle * T{0.5}), T{0}, T{0}};
}
/**
@ -121,7 +119,7 @@ struct quaternion
*/
[[nodiscard]] static quaternion rotate_y(scalar_type angle)
{
return {std::cos(angle * T(0.5)), T(0), std::sin(angle * T(0.5)), T(0)};
return {std::cos(angle * T{0.5}), T{0}, std::sin(angle * T{0.5}), T{0}};
}
/**
@ -132,7 +130,7 @@ struct quaternion
*/
[[nodiscard]] static quaternion rotate_z(scalar_type angle)
{
return {std::cos(angle * T(0.5)), T(0), T(0), std::sin(angle * T(0.5))};
return {std::cos(angle * T{0.5}), T{0}, T{0}, std::sin(angle * T{0.5})};
}
/**
@ -167,9 +165,9 @@ struct quaternion
return
{
T(1) - (yy + zz) * T(2), (xy + zw) * T(2), (xz - yw) * T(2),
(xy - zw) * T(2), T(1) - (xx + zz) * T(2), (yz + xw) * T(2),
(xz + yw) * T(2), (yz - xw) * T(2), T(1) - (xx + yy) * T(2)
T{1} - (yy + zz) * T{2}, (xy + zw) * T{2}, (xz - yw) * T{2},
(xy - zw) * T{2}, T{1} - (xx + zz) * T{2}, (yz + xw) * T{2},
(xz + yw) * T{2}, (yz - xw) * T{2}, T{1} - (xx + yy) * T{2}
};
}
@ -580,7 +578,7 @@ inline constexpr quaternion mul(const quaternion& a, T b) noexcept
template <class T>
constexpr vector<T, 3> mul(const quaternion<T>& a, const vector<T, 3>& b) noexcept
{
return a.i * dot(a.i, b) * T(2) + b * (a.r * a.r - sqr_length(a.i)) + cross(a.i, b) * a.r * T(2);
return a.i * dot(a.i, b) * T{2} + b * (a.r * a.r - sqr_length(a.i)) + cross(a.i, b) * a.r * T{2};
}
template <class T>
@ -598,7 +596,7 @@ inline constexpr quaternion negate(const quaternion& q) noexcept
template <class T>
quaternion<T> nlerp(const quaternion<T>& a, const quaternion<T>& b, T t)
{
return normalize(add(mul(a, T(1) - t), mul(b, t * std::copysign(T(1), dot(a, b)))));
return normalize(add(mul(a, T{1} - t), mul(b, t * std::copysign(T{1}, dot(a, b)))));
}
template <class T>
@ -610,8 +608,7 @@ inline quaternion normalize(const quaternion& q)
template <class T>
quaternion<T> angle_axis(T angle, const vector<T, 3>& axis)
{
angle *= T{0.5};
return {std::cos(angle), axis * std::sin(angle)};
return {std::cos(angle * T{0.5}), axis * std::sin(angle * T{0.5})};
}
template <class T>
@ -626,14 +623,16 @@ template
quaternion<T> slerp(const quaternion<T>& a, const quaternion<T>& b, T t, T error)
{
T cos_theta = dot(a, b);
if (cos_theta > T(1) - error)
if (cos_theta > T{1} - error)
{
return normalize(lerp(a, b, t));
}
cos_theta = std::max<T>(T(-1), std::min<T>(T(1), cos_theta));
cos_theta = std::max<T>(T{-1}, std::min<T>(T{1}, cos_theta));
const T theta = std::acos(cos_theta) * t;
quaternion<T> c = normalize(sub(b, mul(a, cos_theta)));
const quaternion<T> c = normalize(sub(b, mul(a, cos_theta)));
return add(mul(a, std::cos(theta)), mul(c, std::sin(theta)));
}
@ -677,9 +676,13 @@ void swing_twist(const quaternion& q, const vector& a, quaternion& q
const vector<T, 3> qa = mul(q, a);
const vector<T, 3> sa = cross(a, qa);
if (sqr_length(sa) > error)
{
qs = angle_axis(std::acos(dot(a, qa)), sa);
}
else
{
qs = quaternion<T>::identity();
}
}
}
@ -688,12 +691,12 @@ quaternion quaternion_cast(const matrix& m)
{
const T t = trace(m);
if (t > T(0))
if (t > T{0})
{
T s = T(0.5) / std::sqrt(t + T(1));
const T s = T{0.5} / std::sqrt(t + T{1});
return
{
T(0.25) / s,
T{0.25} / s,
(m[1][2] - m[2][1]) * s,
(m[2][0] - m[0][2]) * s,
(m[0][1] - m[1][0]) * s
@ -703,36 +706,36 @@ quaternion quaternion_cast(const matrix& m)
{
if (m[0][0] > m[1][1] && m[0][0] > m[2][2])
{
T s = T(2) * std::sqrt(T(1) + m[0][0] - m[1][1] - m[2][2]);
const T s = T{2} * std::sqrt(T{1} + m[0][0] - m[1][1] - m[2][2]);
return
{
(m[1][2] - m[2][1]) / s,
T(0.25) * s,
T{0.25} * s,
(m[1][0] + m[0][1]) / s,
(m[2][0] + m[0][2]) / s
};
}
else if (m[1][1] > m[2][2])
{
T s = T(2) * std::sqrt(T(1) + m[1][1] - m[0][0] - m[2][2]);
const T s = T{2} * std::sqrt(T{1} + m[1][1] - m[0][0] - m[2][2]);
return
{
(m[2][0] - m[0][2]) / s,
(m[1][0] + m[0][1]) / s,
T(0.25) * s,
T{0.25} * s,
(m[2][1] + m[1][2]) / s
};
}
else
{
T s = T(2) * std::sqrt(T(1) + m[2][2] - m[0][0] - m[1][1]);
const T s = T{2} * std::sqrt(T{1} + m[2][2] - m[0][0] - m[1][1]);
return
{
(m[0][1] - m[1][0]) / s,
(m[2][0] + m[0][2]) / s,
(m[2][1] + m[1][2]) / s,
T(0.25) * s
T{0.25} * s
};
}
}
@ -929,37 +932,6 @@ inline constexpr quaternion& operator/=(quaternion& a, T b) noexcept
}
/// @}
/**
* Writes the real and imaginary parts of a quaternion to an output stream, with each number delimeted by a space.
*
* @param os Output stream.
* @param q Quaternion.
*
* @return Output stream.
*/
template <class T>
std::ostream& operator<<(std::ostream& os, const math::quaternion<T>& q)
{
os << q.r << ' ' << q.i;
return os;
}
/**
* Reads the real and imaginary parts of a quaternion from an input stream, with each number delimeted by a space.
*
* @param is Input stream.
* @param q Quaternion.
*
* @return Input stream.
*/
template <class T>
std::istream& operator>>(std::istream& is, const math::quaternion<T>& q)
{
is >> q.r;
is >> q.i;
return is;
}
} // namespace operators
} // namespace math

+ 1
- 1
src/engine/physics/kinematics/colliders/box-collider.hpp View File

@ -32,7 +32,7 @@ class box_collider: public collider
{
public:
/// Box type.
using box_type = geom::primitive::box<float>;
using box_type = geom::box<float>;
[[nodiscard]] inline constexpr collider_type type() const noexcept override
{

+ 1
- 1
src/engine/physics/kinematics/colliders/plane-collider.hpp View File

@ -32,7 +32,7 @@ class plane_collider: public collider
{
public:
/// Plane type.
using plane_type = geom::primitive::plane<float>;
using plane_type = geom::plane<float>;
[[nodiscard]] inline constexpr collider_type type() const noexcept override
{

+ 1
- 1
src/engine/physics/kinematics/colliders/sphere-collider.hpp View File

@ -32,7 +32,7 @@ class sphere_collider: public collider
{
public:
/// Sphere type.
using sphere_type = geom::primitive::sphere<float>;
using sphere_type = geom::sphere<float>;
[[nodiscard]] inline constexpr collider_type type() const noexcept override
{

+ 0
- 5
src/engine/render/context.hpp View File

@ -20,8 +20,6 @@
#ifndef ANTKEEPER_RENDER_CONTEXT_HPP
#define ANTKEEPER_RENDER_CONTEXT_HPP
#include <engine/geom/plane.hpp>
#include <engine/geom/bounding-volume.hpp>
#include <engine/utility/fundamental-types.hpp>
#include <engine/math/transform-operators.hpp>
#include <engine/render/operation.hpp>
@ -44,9 +42,6 @@ struct context
/// Pointer to the camera.
const scene::camera* camera;
/// Camera culling volume.
const geom::bounding_volume<float>* camera_culling_volume;
/// Collection of scene objects being rendered.
const scene::collection* collection;

+ 1
- 2
src/engine/render/model.cpp View File

@ -239,8 +239,7 @@ std::unique_ptr resource_loader::load(::resource_m
}
// Read model bounds
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(model->get_bounds().min_point.data()), 3);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(model->get_bounds().max_point.data()), 3);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&model->get_bounds()), 6);
// Read material count
std::uint16_t material_count = 0;

+ 2
- 2
src/engine/render/model.hpp View File

@ -21,7 +21,7 @@
#define ANTKEEPER_RENDER_MODEL_HPP
#include <engine/animation/skeleton.hpp>
#include <engine/geom/aabb.hpp>
#include <engine/geom/primitives/box.hpp>
#include <engine/gl/drawing-mode.hpp>
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/vertex-buffer.hpp>
@ -53,7 +53,7 @@ class model
{
public:
/// AABB type.
typedef geom::aabb<float> aabb_type;
using aabb_type = geom::box<float>;
/**
* Constructs a model.

+ 73
- 41
src/engine/render/passes/shadow-map-pass.cpp View File

@ -29,8 +29,7 @@
#include <engine/scene/camera.hpp>
#include <engine/scene/collection.hpp>
#include <engine/scene/light.hpp>
#include <engine/geom/view-frustum.hpp>
#include <engine/geom/aabb.hpp>
#include <engine/geom/primitives/view-frustum.hpp>
#include <engine/config.hpp>
#include <engine/math/interpolation.hpp>
#include <engine/math/vector.hpp>
@ -159,7 +158,7 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, render::
// Calculate viewports for each shadow map
const int shadow_map_resolution = static_cast<int>(light.get_shadow_framebuffer()->get_depth_attachment()->get_width());
const int cascade_resolution = shadow_map_resolution / 2;
const int cascade_resolution = shadow_map_resolution >> 1;
int4 shadow_map_viewports[4];
for (int i = 0; i < 4; ++i)
{
@ -173,16 +172,26 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, render::
viewport[3] = cascade_resolution;
}
// Calculate a view-projection matrix from the directional light's transform
const auto& light_transform = light.get_transform();
float3 forward = light_transform.rotation * config::global_forward;
float3 up = light_transform.rotation * config::global_up;
float4x4 light_view = math::look_at(light_transform.translation, light_transform.translation + forward, up);
float4x4 light_projection = math::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
float4x4 light_view_projection = light_projection * light_view;
// Reverse half z clip-space coordinates of a cube
constexpr math::vector<float, 4> clip_space_cube[8] =
{
{-1, -1, 1, 1}, // NBL
{ 1, -1, 1, 1}, // NBR
{-1, 1, 1, 1}, // NTL
{ 1, 1, 1, 1}, // NTR
{-1, -1, 0, 1}, // FBL
{ 1, -1, 0, 1}, // FBR
{-1, 1, 0, 1}, // FTL
{ 1, 1, 0, 1} // FTR
};
float4x4 cropped_view_projection;
float4x4 model_view_projection;
// Calculate world-space corners of camera view frustum
math::vector<float, 3> view_frustum_corners[8];
for (std::size_t i = 0; i < 8; ++i)
{
math::vector<float, 4> corner = camera.get_inverse_view_projection() * clip_space_cube[i];
view_frustum_corners[i] = math::vector<float, 3>(corner) / corner[3];
}
// Sort render operations
std::sort(std::execution::par_unseq, ctx.operations.begin(), ctx.operations.end(), operation_compare);
@ -195,47 +204,68 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, render::
const int4& viewport = shadow_map_viewports[i];
rasterizer->set_viewport(viewport[0], viewport[1], viewport[2], viewport[3]);
// Calculate projection matrix for view camera subfrustum
const float subfrustum_near = (i) ? cascade_distances[i - 1] : camera.get_clip_near();
const float subfrustum_far = cascade_distances[i];
float4x4 subfrustum_projection = math::perspective_half_z(camera.get_fov(), camera.get_aspect_ratio(), subfrustum_near, subfrustum_far);
// Calculate world-space corners and center of camera subfrustum
const float t_near = (i) ? cascade_distances[i - 1] / camera.get_clip_far() : 0.0f;
const float t_far = cascade_distances[i] / camera.get_clip_far();
math::vector<float, 3> subfrustum_center{0, 0, 0};
math::vector<float, 3> subfrustum_corners[8];
for (std::size_t i = 0; i < 4; ++i)
{
subfrustum_corners[i] = math::lerp(view_frustum_corners[i], view_frustum_corners[i + 4], t_near);
subfrustum_corners[i + 4] = math::lerp(view_frustum_corners[i], view_frustum_corners[i + 4], t_far);
subfrustum_center += subfrustum_corners[i];
subfrustum_center += subfrustum_corners[i + 4];
}
subfrustum_center *= (1.0f / 8.0f);
// Calculate view camera subfrustum
geom::view_frustum<float> subfrustum(subfrustum_projection * camera.get_view());
// Calculate a view-projection matrix from the light's point-of-view
const float3 light_up = light.get_rotation() * config::global_up;
float4x4 light_view = math::look_at(subfrustum_center, subfrustum_center + light.get_direction(), light_up);
float4x4 light_projection = math::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
float4x4 light_view_projection = light_projection * light_view;
// Create AABB containing the view camera subfrustum corners
const std::array<float3, 8>& subfrustum_corners = subfrustum.get_corners();
geom::aabb<float> subfrustum_aabb = {subfrustum_corners[0], subfrustum_corners[0]};
for (int j = 1; j < 8; ++j)
// Calculate AABB of the subfrustum corners in light clip-space
geom::box<float> cropping_bounds;
cropping_bounds.min = {std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()};
cropping_bounds.max = {-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()};
for (std::size_t i = 0; i < 8; ++i)
{
subfrustum_aabb.min_point = math::min(subfrustum_aabb.min_point, subfrustum_corners[j]);
subfrustum_aabb.max_point = math::max(subfrustum_aabb.max_point, subfrustum_corners[j]);
math::vector<float, 4> corner4 = math::vector<float, 4>(subfrustum_corners[i]);
corner4[3] = 1.0f;
corner4 = light_view_projection * corner4;
const math::vector<float, 3> corner3 = math::vector<float, 3>(corner4) / corner4[3];
cropping_bounds.min = math::min(cropping_bounds.min, corner3);
cropping_bounds.max = math::max(cropping_bounds.max, corner3);
}
// Transform subfrustum AABB into the light clip-space
geom::aabb<float> cropping_bounds = geom::aabb<float>::transform(subfrustum_aabb, light_view_projection);
// Quantize clip-space coordinates
const float texel_scale_x = (cropping_bounds.max_point.x() - cropping_bounds.min_point.x()) / static_cast<float>(cascade_resolution);
const float texel_scale_y = (cropping_bounds.max_point.y() - cropping_bounds.min_point.y()) / static_cast<float>(cascade_resolution);
cropping_bounds.min_point.x() = std::floor(cropping_bounds.min_point.x() / texel_scale_x) * texel_scale_x;
cropping_bounds.max_point.x() = std::floor(cropping_bounds.max_point.x() / texel_scale_x) * texel_scale_x;
cropping_bounds.min_point.y() = std::floor(cropping_bounds.min_point.y() / texel_scale_y) * texel_scale_y;
cropping_bounds.max_point.y() = std::floor(cropping_bounds.max_point.y() / texel_scale_y) * texel_scale_y;
const float texel_scale_x = (cropping_bounds.max.x() - cropping_bounds.min.x()) / static_cast<float>(cascade_resolution);
const float texel_scale_y = (cropping_bounds.max.y() - cropping_bounds.min.y()) / static_cast<float>(cascade_resolution);
cropping_bounds.min.x() = std::floor(cropping_bounds.min.x() / texel_scale_x) * texel_scale_x;
cropping_bounds.max.x() = std::floor(cropping_bounds.max.x() / texel_scale_x) * texel_scale_x;
cropping_bounds.min.y() = std::floor(cropping_bounds.min.y() / texel_scale_y) * texel_scale_y;
cropping_bounds.max.y() = std::floor(cropping_bounds.max.y() / texel_scale_y) * texel_scale_y;
// Recalculate light projection matrix with quantized coordinates
/// @NOTE: light z should be modified here to included shadow casters outside the view frustum
// cropping_bounds.min.z() -= 10.0f;
// cropping_bounds.max.z() += 10.0f;
// Crop light projection matrix
light_projection = math::ortho_half_z
(
cropping_bounds.min_point.x(), cropping_bounds.max_point.x(),
cropping_bounds.min_point.y(), cropping_bounds.max_point.y(),
cropping_bounds.min_point.z(), cropping_bounds.max_point.z()
cropping_bounds.min.x(), cropping_bounds.max.x(),
cropping_bounds.min.y(), cropping_bounds.max.y(),
cropping_bounds.min.z(), cropping_bounds.max.z()
);
// Calculate cropped view projection matrix
cropped_view_projection = light_projection * light_view;
// Recalculate light view projection matrix
light_view_projection = light_projection * light_view;
// Calculate world-space to cascade texture-space transformation matrix
cascade_matrices[i] = bias_tile_matrices[i] * cropped_view_projection;
cascade_matrices[i] = bias_tile_matrices[i] * light_view_projection;
for (const render::operation* operation: ctx.operations)
{
@ -244,7 +274,9 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, render::
{
// Skip materials which don't cast shadows
if (material->get_shadow_mode() == material_shadow_mode::none)
{
continue;
}
if (material->is_two_sided() != two_sided)
{
@ -270,7 +302,7 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, render::
}
// Calculate model-view-projection matrix
model_view_projection = cropped_view_projection * operation->transform;
float4x4 model_view_projection = light_view_projection * operation->transform;
// Upload operation-dependent parameters to shader program
if (active_shader_program == unskinned_shader_program.get())

+ 4
- 16
src/engine/render/stages/culling-stage.cpp View File

@ -31,13 +31,8 @@ void culling_stage::execute(render::context& ctx)
// Get all objects in the collection
const auto& objects = ctx.collection->get_objects();
// Get camera culling volume
ctx.camera_culling_volume = ctx.camera->get_culling_mask();
if (!ctx.camera_culling_volume)
{
ctx.camera_culling_volume = &ctx.camera->get_view_frustum().get_bounds();
}
const auto& culling_volume = *ctx.camera_culling_volume;
// Get camera view frustum
const auto& view_frustum = ctx.camera->get_view_frustum();
// Construct mutex to guard set of visible objects
std::mutex mutex;
@ -60,15 +55,8 @@ void culling_stage::execute(render::context& ctx)
//if (!(object->get_layer_mask() & camera_layer_mask))
// return;
// Get object culling volume
const geom::bounding_volume<float>* object_culling_volume = object->get_culling_mask();
if (!object_culling_volume)
{
object_culling_volume = &object->get_bounds();
}
// Cull object if it's outside of the camera culling volume
if (!culling_volume.intersects(*object_culling_volume))
// Cull object if it's outside of the camera view frustum
if (!view_frustum.intersects(object->get_bounds()))
{
return;
}

+ 2
- 4
src/engine/scene/billboard.cpp View File

@ -124,7 +124,7 @@ void billboard::render(render::context& ctx) const
break;
}
m_render_op.depth = ctx.camera->get_view_frustum().get_near().signed_distance(get_translation());
m_render_op.depth = ctx.camera->get_view_frustum().near().distance(get_translation());
ctx.operations.emplace_back(&m_render_op);
}
@ -146,9 +146,7 @@ void billboard::set_billboard_type(billboard_type type)
void billboard::transformed()
{
static const aabb_type untransformed_bounds{{-1, -1, -1}, {1, 1, 1}};
m_bounds = aabb_type::transform(untransformed_bounds, get_transform());
m_bounds = {get_translation() - get_scale(), get_translation() + get_scale()};
if (m_billboard_type == scene::billboard_type::flat)
{

+ 2
- 5
src/engine/scene/billboard.hpp View File

@ -21,7 +21,6 @@
#define ANTKEEPER_SCENE_BILLBOARD_HPP
#include <engine/scene/object.hpp>
#include <engine/geom/aabb.hpp>
#include <engine/math/vector.hpp>
#include <engine/render/material.hpp>
#include <engine/render/operation.hpp>
@ -38,8 +37,6 @@ namespace scene {
class billboard: public object<billboard>
{
public:
using aabb_type = geom::aabb<float>;
/// Constructs a billboard.
billboard();
@ -69,7 +66,7 @@ public:
m_alignment_axis = axis;
}
[[nodiscard]] inline const bounding_volume_type& get_bounds() const noexcept override
[[nodiscard]] inline const aabb_type& get_bounds() const noexcept override
{
return m_bounds;
}
@ -95,7 +92,7 @@ private:
std::unique_ptr<gl::vertex_buffer> m_vbo;
std::unique_ptr<gl::vertex_array> m_vao;
mutable render::operation m_render_op;
aabb_type m_bounds{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}};
aabb_type m_bounds{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}};
billboard_type m_billboard_type{billboard_type::flat};
math::vector<float, 3> m_alignment_axis{0.0f, 1.0f, 0.0f};
};

+ 35
- 15
src/engine/scene/camera.cpp View File

@ -20,15 +20,14 @@
#include <engine/scene/camera.hpp>
#include <engine/math/quaternion.hpp>
#include <engine/math/projection.hpp>
#include <engine/debug/log.hpp>
namespace scene {
geom::primitive::ray<float, 3> camera::pick(const float2& ndc) const
geom::ray<float, 3> camera::pick(const float2& ndc) const
{
const float4x4 inverse_view_projection = math::inverse(m_view_projection);
const float4 near = inverse_view_projection * float4{ndc[0], ndc[1], 1.0f, 1.0f};
const float4 far = inverse_view_projection * float4{ndc[0], ndc[1], 0.0f, 1.0f};
const float4 near = m_inverse_view_projection * float4{ndc[0], ndc[1], 1.0f, 1.0f};
const float4 far = m_inverse_view_projection * float4{ndc[0], ndc[1], 0.0f, 1.0f};
const float3 origin = float3{near[0], near[1], near[2]} / near[3];
const float3 direction = math::normalize(float3{far[0], far[1], far[2]} / far[3] - origin);
@ -59,7 +58,7 @@ float3 camera::unproject(const float3& window, const float4& viewport) const
result[2] = 1.0f - window[2]; // z: [1, 0]
result[3] = 1.0f;
result = math::inverse(m_view_projection) * result;
result = m_inverse_view_projection * result;
return math::vector<float, 3>(result) * (1.0f / result[3]);
}
@ -79,10 +78,10 @@ void camera::set_perspective(float fov, float aspect_ratio, float clip_near, flo
// Recalculate view-projection matrix
m_view_projection = m_projection * m_view;
m_inverse_view_projection = math::inverse(m_view_projection);
// Recalculate view frustum
/// @TODO: this is a hack to fix the half z projection matrix view frustum
m_view_frustum.set_matrix(math::perspective(m_fov, m_aspect_ratio, m_clip_near, m_clip_far) * m_view);
update_frustum();
}
void camera::set_orthographic(float clip_left, float clip_right, float clip_bottom, float clip_top, float clip_near, float clip_far)
@ -102,9 +101,10 @@ void camera::set_orthographic(float clip_left, float clip_right, float clip_bott
// Recalculate view-projection matrix
m_view_projection = m_projection * m_view;
m_inverse_view_projection = math::inverse(m_view_projection);
// Recalculate view frustum
m_view_frustum.set_matrix(m_view_projection);
update_frustum();
}
void camera::set_exposure_value(float ev100)
@ -120,16 +120,36 @@ void camera::transformed()
m_up = get_rotation() * math::vector<float, 3>{0.0f, 1.0f, 0.0f};
m_view = math::look_at(get_translation(), get_translation() + m_forward, m_up);
m_view_projection = m_projection * m_view;
m_inverse_view_projection = math::inverse(m_view_projection);
update_frustum();
}
void camera::update_frustum()
{
// Recalculate view frustum
if (m_orthographic)
m_view_frustum.extract(m_view_projection);
// Reverse half z clip-space coordinates of a cube
constexpr math::vector<float, 4> clip_space_cube[8] =
{
m_view_frustum.set_matrix(m_view_projection);
}
else
{-1, -1, 1, 1}, // NBL
{ 1, -1, 1, 1}, // NBR
{-1, 1, 1, 1}, // NTL
{ 1, 1, 1, 1}, // NTR
{-1, -1, 0, 1}, // FBL
{ 1, -1, 0, 1}, // FBR
{-1, 1, 0, 1}, // FTL
{ 1, 1, 0, 1} // FTR
};
// Update bounds
m_bounds.min = {std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()};
m_bounds.max = {-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()};
for (std::size_t i = 0; i < 8; ++i)
{
/// @TODO: this is a hack to fix the half z projection matrix view frustum
m_view_frustum.set_matrix(math::perspective(m_fov, m_aspect_ratio, m_clip_near, m_clip_far) * m_view);
const math::vector<float, 4> frustum_corner = m_inverse_view_projection * clip_space_cube[i];
m_bounds.extend(math::vector<float, 3>(frustum_corner) / frustum_corner[3]);
}
}

+ 15
- 4
src/engine/scene/camera.hpp View File

@ -21,7 +21,7 @@
#define ANTKEEPER_SCENE_CAMERA_HPP
#include <engine/scene/object.hpp>
#include <engine/geom/view-frustum.hpp>
#include <engine/geom/primitives/view-frustum.hpp>
#include <engine/geom/primitives/ray.hpp>
#include <engine/utility/fundamental-types.hpp>
#include <engine/render/compositor.hpp>
@ -35,6 +35,7 @@ namespace scene {
class camera: public object<camera>
{
public:
/// Camera view frustum type.
using view_frustum_type = geom::view_frustum<float>;
/**
@ -44,7 +45,7 @@ public:
*
* @return Picking ray.
*/
[[nodiscard]] geom::primitive::ray<float, 3> pick(const float2& ndc) const;
[[nodiscard]] geom::ray<float, 3> pick(const float2& ndc) const;
/**
* Maps object coordinates to window coordinates.
@ -133,9 +134,9 @@ public:
return m_composite_index;
}
[[nodiscard]] inline const bounding_volume_type& get_bounds() const noexcept override
[[nodiscard]] inline const aabb_type& get_bounds() const noexcept override
{
return m_view_frustum.get_bounds();
return m_bounds;
}
/// Returns `true` if the camera uses an orthographic projection matrix, `false` otherwise.
@ -222,6 +223,12 @@ public:
return m_view_projection;
}
/// Returns the camera's inverse view-projection matrix.
[[nodiscard]] inline const float4x4& get_inverse_view_projection() const noexcept
{
return m_inverse_view_projection;
}
/// Returns the camera's forward vector.
[[nodiscard]] inline const math::vector<float, 3>& get_forward() const noexcept
{
@ -242,6 +249,7 @@ public:
private:
virtual void transformed();
void update_frustum();
render::compositor* m_compositor{nullptr};
int m_composite_index{0};
@ -262,11 +270,14 @@ private:
float4x4 m_view{float4x4::identity()};
float4x4 m_projection{float4x4::identity()};
float4x4 m_view_projection{float4x4::identity()};
float4x4 m_inverse_view_projection{float4x4::identity()};
math::vector<float, 3> m_forward{0.0f, 0.0f, -1.0f};
math::vector<float, 3> m_up{0.0f, 1.0f, 0.0f};
view_frustum_type m_view_frustum;
aabb_type m_bounds{{0, 0, 0}, {0, 0, 0}};
};
} // namespace scene

+ 1
- 2
src/engine/scene/light.cpp View File

@ -18,13 +18,12 @@
*/
#include <engine/scene/light.hpp>
#include <engine/math/interpolation.hpp>
namespace scene {
void light::transformed()
{
m_bounds = {get_translation(), 0.0f};
m_bounds = {get_translation(), get_translation()};
}
} // namespace scene

+ 2
- 6
src/engine/scene/light.hpp View File

@ -21,7 +21,6 @@
#define ANTKEEPER_SCENE_LIGHT_HPP
#include <engine/scene/object.hpp>
#include <engine/geom/sphere.hpp>
#include <engine/scene/light-type.hpp>
namespace scene {
@ -32,20 +31,17 @@ namespace scene {
class light: public object<light>
{
public:
typedef geom::sphere<float> sphere_type;
/// Returns an enumeration denoting the light object type.
[[nodiscard]] virtual light_type get_light_type() const noexcept = 0;
inline const bounding_volume_type& get_bounds() const noexcept override
inline const aabb_type& get_bounds() const noexcept override
{
return m_bounds;
}
private:
virtual void transformed();
sphere_type m_bounds{{0.0f, 0.0f, 0.0f}, 0.0f};
aabb_type m_bounds{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}};
};
} // namespace scene

+ 3
- 20
src/engine/scene/object.hpp View File

@ -20,7 +20,7 @@
#ifndef ANTKEEPER_SCENE_OBJECT_HPP
#define ANTKEEPER_SCENE_OBJECT_HPP
#include <engine/geom/bounding-volume.hpp>
#include <engine/geom/primitives/box.hpp>
#include <engine/math/vector.hpp>
#include <engine/math/quaternion.hpp>
#include <engine/math/transform-type.hpp>
@ -39,7 +39,7 @@ public:
using vector_type = math::vector<float, 3>;
using quaternion_type = math::quaternion<float>;
using transform_type = math::transform<float>;
using bounding_volume_type = geom::bounding_volume<float>;
using aabb_type = geom::box<float>;
/// Returns the type ID for this scene object type.
virtual const std::size_t get_object_type_id() const noexcept = 0;
@ -100,14 +100,6 @@ public:
transformed();
}
/**
* Sets a culling mask for the object, which will be used for view-frustum culling instead of the object's bounds.
*/
inline void set_culling_mask(const bounding_volume_type* culling_mask) noexcept
{
m_culling_mask = culling_mask;
}
/// Returns whether the scene object is active.
[[nodiscard]] inline bool is_active() const noexcept
{
@ -149,15 +141,7 @@ public:
/**
* Returns the bounds of the object.
*/
[[nodiscard]] virtual const bounding_volume_type& get_bounds() const noexcept = 0;
/**
* Returns the culling mask of the object.
*/
[[nodiscard]] inline const bounding_volume_type* get_culling_mask() const noexcept
{
return m_culling_mask;
}
[[nodiscard]] virtual const aabb_type& get_bounds() const noexcept = 0;
protected:
static std::size_t next_object_type_id();
@ -173,7 +157,6 @@ private:
bool m_active{true};
transform_type m_transform{transform_type::identity};
const bounding_volume_type* m_culling_mask{nullptr};
};
/**

+ 11
- 2
src/engine/scene/static-mesh.cpp View File

@ -83,7 +83,16 @@ void static_mesh::update_bounds()
{
if (m_model)
{
m_bounds = aabb_type::transform(m_model->get_bounds(), get_transform());
// Get model bounds
const auto& model_bounds = m_model->get_bounds();
// Naive algorithm: transform each corner of the model AABB
m_bounds.min = {std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()};
m_bounds.max = {-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()};
for (std::size_t i = 0; i < 8; ++i)
{
m_bounds.extend(get_transform() * model_bounds.corner(i));
}
}
else
{
@ -104,7 +113,7 @@ void static_mesh::transformed()
void static_mesh::render(render::context& ctx) const
{
const float depth = ctx.camera->get_view_frustum().get_near().signed_distance(get_translation());
const float depth = ctx.camera->get_view_frustum().near().distance(get_translation());
for (auto& operation: m_operations)
{
operation.depth = depth;

+ 1
- 4
src/engine/scene/static-mesh.hpp View File

@ -22,7 +22,6 @@
#include <engine/scene/object.hpp>
#include <engine/animation/pose.hpp>
#include <engine/geom/aabb.hpp>
#include <engine/render/model.hpp>
#include <engine/render/operation.hpp>
#include <vector>
@ -35,8 +34,6 @@ namespace scene {
class static_mesh: public object<static_mesh>
{
public:
typedef geom::aabb<float> aabb_type;
/**
* Constructs a static mesh from a model.
*
@ -67,7 +64,7 @@ public:
*/
void reset_materials();
[[nodiscard]] inline const bounding_volume_type& get_bounds() const noexcept override
[[nodiscard]] inline const aabb_type& get_bounds() const noexcept override
{
return m_bounds;
}

+ 13
- 5
src/engine/scene/text.cpp View File

@ -78,7 +78,7 @@ void text::render(render::context& ctx) const
{
if (m_vertex_count)
{
m_render_op.depth = ctx.camera->get_view_frustum().get_near().signed_distance(get_translation());
m_render_op.depth = ctx.camera->get_view_frustum().near().distance(get_translation());
ctx.operations.push_back(&m_render_op);
}
}
@ -129,7 +129,14 @@ void text::set_color(const float4& color)
void text::transformed()
{
m_world_bounds = aabb_type::transform(m_local_bounds, get_transform());
// Naive algorithm: transform each corner of the AABB
m_world_bounds.min = {std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()};
m_world_bounds.max = {-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()};
for (std::size_t i = 0; i < 8; ++i)
{
m_world_bounds.extend(get_transform() * m_local_bounds.corner(i));
}
m_render_op.transform = math::matrix_cast(get_transform());
}
@ -163,7 +170,8 @@ void text::update_content()
float2 pen_position = {0.0f, 0.0f};
// Reset local-space bounds
m_local_bounds = {{0, 0, 0}, {0, 0, 0}};
m_local_bounds.min = {std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), 0.0f};
m_local_bounds.max = {-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), 0.0f};
// Generate vertex data
char32_t previous_code = 0;
@ -233,8 +241,8 @@ void text::update_content()
const float2& position = positions[i];
for (int j = 0; j < 2; ++j)
{
m_local_bounds.min_point[j] = std::min<float>(m_local_bounds.min_point[j], position[j]);
m_local_bounds.max_point[j] = std::max<float>(m_local_bounds.max_point[j], position[j]);
m_local_bounds.min[j] = std::min<float>(m_local_bounds.min[j], position[j]);
m_local_bounds.max[j] = std::max<float>(m_local_bounds.max[j], position[j]);
}
}
}

+ 1
- 4
src/engine/scene/text.hpp View File

@ -21,7 +21,6 @@
#define ANTKEEPER_SCENE_TEXT_HPP
#include <engine/scene/object.hpp>
#include <engine/geom/aabb.hpp>
#include <engine/utility/fundamental-types.hpp>
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/vertex-buffer.hpp>
@ -37,8 +36,6 @@ namespace scene {
class text: public object<text>
{
public:
using aabb_type = geom::aabb<float>;
/// Constructs a text object.
text();
@ -116,7 +113,7 @@ public:
return m_color;
}
[[nodiscard]] inline virtual const bounding_volume_type& get_bounds() const noexcept
[[nodiscard]] inline const aabb_type& get_bounds() const noexcept override
{
return m_world_bounds;
}

+ 9
- 9
src/game/ant/ant-morphogenesis.cpp View File

@ -23,6 +23,7 @@
#include <engine/math/transform-operators.hpp>
#include <engine/math/quaternion.hpp>
#include <engine/debug/log.hpp>
#include <engine/geom/primitives/box.hpp>
#include <unordered_set>
namespace {
@ -137,7 +138,7 @@ void reskin_vertices
*
* @return Bounds of the vertex data.
*/
[[nodiscard]] geom::aabb<float> calculate_bounds
[[nodiscard]] geom::box<float> calculate_bounds
(
const std::byte* vertex_data,
std::size_t vertex_count,
@ -146,17 +147,16 @@ void reskin_vertices
{
const std::byte* position_data = vertex_data + position_attribute.offset;
geom::aabb<float> bounds;
bounds.min_point = {std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()};
bounds.max_point = {-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()};
geom::box<float> bounds
{
{std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()},
{-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()}
};
for (std::size_t i = 0; i < vertex_count; ++i)
{
// Get vertex position
const float3& position = reinterpret_cast<const float3&>(*(position_data + position_attribute.stride * i));
bounds.min_point = math::min(bounds.min_point, position);
bounds.max_point = math::max(bounds.max_point, position);
const float3& position = reinterpret_cast<const float3&>(*(position_data + position_attribute.stride * i));
bounds.extend(position);
}
return bounds;

+ 0
- 39
src/game/components/collision-component.hpp View File

@ -1,39 +0,0 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_COLLISION_COMPONENT_HPP
#define ANTKEEPER_GAME_COLLISION_COMPONENT_HPP
#include <engine/geom/aabb.hpp>
#include <engine/geom/mesh.hpp>
#include <engine/geom/mesh-accelerator.hpp>
struct collision_component
{
geom::mesh* mesh;
geom::aabb<float> bounds;
geom::mesh_accelerator mesh_accelerator;
float radius{0.0f};
};
#endif // ANTKEEPER_GAME_COLLISION_COMPONENT_HPP

+ 1
- 1
src/game/components/picking-component.hpp View File

@ -27,7 +27,7 @@
struct picking_component
{
/// Picking sphere.
geom::primitive::sphere<float> sphere;
geom::sphere<float> sphere;
/// Picking flags.
std::uint32_t flags;

+ 10
- 10
src/game/controls.cpp View File

@ -416,19 +416,19 @@ void enable_menu_controls(::game& ctx)
{
auto [name, value] = ctx.menu_item_texts[i];
const auto& name_bounds = static_cast<const geom::aabb<float>&>(name->get_bounds());
float min_x = name_bounds.min_point.x();
float min_y = name_bounds.min_point.y();
float max_x = name_bounds.max_point.x();
float max_y = name_bounds.max_point.y();
const auto& name_bounds = name->get_bounds();
float min_x = name_bounds.min.x();
float min_y = name_bounds.min.y();
float max_x = name_bounds.max.x();
float max_y = name_bounds.max.y();
if (value)
{
const auto& value_bounds = static_cast<const geom::aabb<float>&>(value->get_bounds());
min_x = std::min<float>(min_x, value_bounds.min_point.x());
min_y = std::min<float>(min_y, value_bounds.min_point.y());
max_x = std::max<float>(max_x, value_bounds.max_point.x());
max_y = std::max<float>(max_y, value_bounds.max_point.y());
const auto& value_bounds = value->get_bounds();
min_x = std::min<float>(min_x, value_bounds.min.x());
min_y = std::min<float>(min_y, value_bounds.min.y());
max_x = std::max<float>(max_x, value_bounds.max.x());
max_y = std::max<float>(max_y, value_bounds.max.y());
}
min_x -= padding;

+ 0
- 1
src/game/game.hpp View File

@ -30,7 +30,6 @@
#include <engine/entity/id.hpp>
#include <engine/entity/registry.hpp>
#include <engine/event/subscription.hpp>
#include <engine/geom/aabb.hpp>
#include <engine/gl/framebuffer.hpp>
#include <engine/gl/rasterizer.hpp>
#include <engine/gl/texture-2d.hpp>

+ 0
- 24
src/game/loaders/entity-archetype-loader.cpp View File

@ -23,7 +23,6 @@
#include <engine/math/angles.hpp>
#include "game/components/atmosphere-component.hpp"
#include "game/components/behavior-component.hpp"
#include "game/components/collision-component.hpp"
#include "game/components/diffuse-reflector-component.hpp"
#include "game/components/terrain-component.hpp"
#include "game/components/transform-component.hpp"
@ -150,27 +149,6 @@ static bool load_component_celestial_body(entity::archetype& archetype, const js
return true;
}
static bool load_component_collision(entity::archetype& archetype, resource_manager& resource_manager, const json& element)
{
::collision_component component;
component.mesh = nullptr;
if (element.contains("file"))
{
//component.mesh = resource_manager.load<geom::mesh>(element["file"].get<std::string>());
}
archetype.stamps.push_back
(
[component](entt::handle& handle)
{
handle.emplace_or_replace<decltype(component)>(component);
}
);
return (component.mesh != nullptr);
}
static bool load_component_diffuse_reflector(entity::archetype& archetype, const json& element)
{
::diffuse_reflector_component component;
@ -291,8 +269,6 @@ static bool load_component(entity::archetype& archetype, resource_manager& resou
return load_component_blackbody(archetype, element.value());
if (element.key() == "celestial_body")
return load_component_celestial_body(archetype, element.value());
if (element.key() == "collision")
return load_component_collision(archetype, resource_manager, element.value());
if (element.key() == "diffuse_reflector")
return load_component_diffuse_reflector(archetype, element.value());
if (element.key() == "model")

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

@ -92,14 +92,14 @@ void align_text(::game& ctx, bool center, bool has_back, float anchor_y)
float row_width = 0.0f;
// Add name width to row width
const auto& name_bounds = static_cast<const geom::aabb<float>&>(name->get_bounds());
row_width += name_bounds.max_point.x() - name_bounds.min_point.x();
const auto& name_bounds = name->get_bounds();
row_width += name_bounds.max.x() - name_bounds.min.x();
if (value)
{
// Add value width to row width
const auto& value_bounds = static_cast<const geom::aabb<float>&>(value->get_bounds());
row_width += value_bounds.max_point.x() - value_bounds.min_point.x();
const auto& value_bounds = value->get_bounds();
row_width += value_bounds.max.x() - value_bounds.min.x();
// Add column spacing to row width
row_width += column_spacing;
@ -133,8 +133,8 @@ void align_text(::game& ctx, bool center, bool has_back, float anchor_y)
if (center || i == ctx.menu_item_texts.size() - 1)
{
const auto& name_bounds = static_cast<const geom::aabb<float>&>(name->get_bounds());
const float name_width = name_bounds.max_point.x() - name_bounds.min_point.x();
const auto& name_bounds = name->get_bounds();
const float name_width = name_bounds.max.x() - name_bounds.min.x();
x = viewport_center.x() - name_width * 0.5f;
}
@ -142,8 +142,8 @@ void align_text(::game& ctx, bool center, bool has_back, float anchor_y)
if (value)
{
const auto& value_bounds = static_cast<const geom::aabb<float>&>(value->get_bounds());
const float value_width = value_bounds.max_point.x() - value_bounds.min_point.x();
const auto& value_bounds = value->get_bounds();
const float value_width = value_bounds.max.x() - value_bounds.min.x();
if (center || i == ctx.menu_item_texts.size() - 1)
x = viewport_center.x() - value_width * 0.5f;

+ 1
- 1
src/game/states/collection-menu-state.hpp View File

@ -49,7 +49,7 @@ private:
std::shared_ptr<event::subscription> mouse_button_pressed_subscription;
std::shared_ptr<event::subscription> window_resized_subscription;
geom::primitive::rectangle<float> box_bounds;
geom::rectangle<float> box_bounds;
int row_count;
int column_count;
int selected_row;

+ 6
- 6
src/game/states/credits-state.cpp View File

@ -46,9 +46,9 @@ credits_state::credits_state(::game& ctx):
credits_text.set_content(get_string(ctx, "credits"));
// Align credits text
const auto& credits_aabb = static_cast<const geom::aabb<float>&>(credits_text.get_bounds());
float credits_w = credits_aabb.max_point.x() - credits_aabb.min_point.x();
float credits_h = credits_aabb.max_point.y() - credits_aabb.min_point.y();
const auto& credits_aabb = credits_text.get_bounds();
float credits_w = credits_aabb.max.x() - credits_aabb.min.x();
float credits_h = credits_aabb.max.y() - credits_aabb.min.y();
credits_text.set_translation({std::round(viewport_center.x() - credits_w * 0.5f), std::round(viewport_center.y() - credits_h * 0.5f), 0.0f});
// Set up animation timing configuration
@ -80,9 +80,9 @@ credits_state::credits_state(::game& ctx):
{
const vec2 viewport_size = vec2(event.window->get_viewport_size());
const vec2 viewport_center = viewport_size * 0.5f;
const auto& credits_aabb = static_cast<const geom::aabb<float>&>(credits_text.get_bounds());
float credits_w = credits_aabb.max_point.x() - credits_aabb.min_point.x();
float credits_h = credits_aabb.max_point.y() - credits_aabb.min_point.y();
const auto& credits_aabb = credits_text.get_bounds();
float credits_w = credits_aabb.max.x() - credits_aabb.min.x();
float credits_h = credits_aabb.max.y() - credits_aabb.min.y();
credits_text.set_translation({std::round(viewport_center.x() - credits_w * 0.5f), std::round(viewport_center.y() - credits_h * 0.5f), 0.0f});
}
);

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

@ -65,9 +65,9 @@ main_menu_state::main_menu_state(::game& ctx, bool fade_in):
title_text->set_color({1.0f, 1.0f, 1.0f, (fade_in) ? 1.0f : 0.0f});
title_text->set_font(&ctx.title_font);
title_text->set_content(get_string(ctx, "title_antkeeper"));
const auto& title_aabb = static_cast<const geom::aabb<float>&>(title_text->get_bounds());
float title_w = title_aabb.max_point.x() - title_aabb.min_point.x();
float title_h = title_aabb.max_point.y() - title_aabb.min_point.y();
const auto& title_aabb = title_text->get_bounds();
float title_w = title_aabb.max.x() - title_aabb.min.x();
float title_h = title_aabb.max.y() - title_aabb.min.y();
title_text->set_translation({std::round(viewport_center.x() - title_w * 0.5f), std::round(viewport_center.y() - title_h * 0.5f + (viewport_size.y() / 3.0f) / 2.0f), 0.0f});
// Add text to UI
@ -288,9 +288,9 @@ main_menu_state::main_menu_state(::game& ctx, bool fade_in):
const vec2 viewport_center = viewport_size * 0.5f;
// Re-align title text
const auto& title_aabb = static_cast<const geom::aabb<float>&>(title_text->get_bounds());
float title_w = title_aabb.max_point.x() - title_aabb.min_point.x();
float title_h = title_aabb.max_point.y() - title_aabb.min_point.y();
const auto& title_aabb = title_text->get_bounds();
float title_w = title_aabb.max.x() - title_aabb.min.x();
float title_h = title_aabb.max.y() - title_aabb.min.y();
title_text->set_translation({std::round(viewport_center.x() - title_w * 0.5f), std::round(viewport_center.y() - title_h * 0.5f + (viewport_size.y() / 3.0f) / 2.0f), 0.0f});
::menu::align_text(ctx, true, false, (-viewport_size.y() / 3.0f) / 2.0f);

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

@ -22,7 +22,6 @@
#include "game/ant/ant-morphogenesis.hpp"
#include "game/ant/ant-phenome.hpp"
#include "game/commands/commands.hpp"
#include "game/components/collision-component.hpp"
#include "game/components/constraint-stack-component.hpp"
#include "game/components/scene-component.hpp"
#include "game/components/picking-component.hpp"
@ -683,11 +682,12 @@ void nest_selection_state::setup_controls()
auto projectile_body = std::make_unique<physics::rigid_body>();
projectile_body->set_position(camera_transform.world.translation);
projectile_body->set_previous_position(camera_transform.world.translation);
projectile_body->set_mass(0.1f);
projectile_body->set_inertia(0.05f);
projectile_body->set_angular_damping(0.5f);
//auto projectile_collider = std::make_shared<physics::box_collider>(float3{-0.5f, -0.5f, -0.5f}, float3{0.5f, 0.5f, 0.5f});
//auto projectile_collider = std::make_shared<physics::box_collider>(float3{-1.0f, -1.0f, -1.0f}, float3{1.0f, 1.0f, 1.0f});
auto projectile_collider = std::make_shared<physics::sphere_collider>(1.0f);

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

@ -163,9 +163,9 @@ nuptial_flight_state::nuptial_flight_state(::game& ctx):
selection_text.set_material(ctx.menu_font_material);
selection_text.set_color({1.0f, 1.0f, 1.0f, 1.0f});
selection_text.set_font(&ctx.menu_font);
const auto& text_aabb = static_cast<const geom::aabb<float>&>(selection_text.get_bounds());
float text_w = text_aabb.max_point.x() - text_aabb.min_point.x();
float text_h = text_aabb.max_point.y() - text_aabb.min_point.y();
const auto& text_aabb = selection_text.get_bounds();
float text_w = text_aabb.max.x() - text_aabb.min.x();
float text_h = text_aabb.max.y() - text_aabb.min.y();
selection_text.set_translation({std::round(viewport_size.x() * 0.5f - text_w * 0.5f), std::round(ctx.menu_font.get_font_metrics().size), 0.0f});
// Add text to UI
@ -564,7 +564,7 @@ void nuptial_flight_state::setup_controls()
};
// Get picking ray from camera
const geom::primitive::ray<float, 3> ray = ctx.surface_camera->pick(mouse_ndc);
const geom::ray<float, 3> ray = ctx.surface_camera->pick(mouse_ndc);
// Pick entity
entity::id picked_eid = ctx.collision_system->pick_nearest(ray, ~selected_picking_flag);
@ -821,7 +821,7 @@ void nuptial_flight_state::enable_controls()
};
// Get picking ray from camera
const geom::primitive::ray<float, 3> ray = ctx.surface_camera->pick(mouse_ndc);
const geom::ray<float, 3> ray = ctx.surface_camera->pick(mouse_ndc);
// Pick entity
entity::id picked_eid = ctx.collision_system->pick_nearest(ray, ~selected_picking_flag);
@ -1101,9 +1101,9 @@ void nuptial_flight_state::select_entity(entity::id entity_id)
}
const auto& viewport_size = ctx.window->get_viewport_size();
const auto& text_aabb = static_cast<const geom::aabb<float>&>(selection_text.get_bounds());
float text_w = text_aabb.max_point.x() - text_aabb.min_point.x();
float text_h = text_aabb.max_point.y() - text_aabb.min_point.y();
const auto& text_aabb = selection_text.get_bounds();
float text_w = text_aabb.max.x() - text_aabb.min.x();
float text_h = text_aabb.max.y() - text_aabb.min.y();
selection_text.set_translation({std::round(viewport_size.x() * 0.5f - text_w * 0.5f), std::round(ctx.menu_font.get_font_metrics().size), 0.0f});
}
}

+ 7
- 7
src/game/systems/astronomy-system.cpp View File

@ -24,6 +24,7 @@
#include "game/components/diffuse-reflector-component.hpp"
#include <engine/geom/intersection.hpp>
#include <engine/geom/cartesian.hpp>
#include <engine/geom/primitives/sphere.hpp>
#include <engine/color/color.hpp>
#include <engine/physics/orbit/orbit.hpp>
#include <engine/physics/time/ut1.hpp>
@ -184,7 +185,7 @@ void astronomy_system::update(float t, float dt)
if (reference_atmosphere)
{
// Construct ray at observer pointing towards the blackbody
const geom::ray<double> ray = {{0, 0, 0}, observer_blackbody_direction_eus};
const geom::ray<double, 3> ray = {{0, 0, 0}, observer_blackbody_direction_eus};
// Integrate atmospheric spectral transmittance factor between observer and blackbody
const double3 transmittance = integrate_transmittance(*observer, *reference_body, *reference_atmosphere, ray);
@ -282,7 +283,7 @@ void astronomy_system::update(float t, float dt)
double3 observer_reflector_transmittance = {1, 1, 1};
if (reference_atmosphere)
{
const geom::ray<double> ray = {{0, 0, 0}, observer_reflector_direction_eus};
const geom::ray<double, 3> ray = {{0, 0, 0}, observer_reflector_direction_eus};
observer_reflector_transmittance = integrate_transmittance(*observer, *reference_body, *reference_atmosphere, ray);
}
@ -582,7 +583,7 @@ void astronomy_system::update_icrf_to_eus(const ::celestial_body_component& body
}
}
double3 astronomy_system::integrate_transmittance(const ::observer_component& observer, const ::celestial_body_component& body, const ::atmosphere_component& atmosphere, geom::ray<double> ray) const
double3 astronomy_system::integrate_transmittance(const ::observer_component& observer, const ::celestial_body_component& body, const ::atmosphere_component& atmosphere, geom::ray<double, 3> ray) const
{
double3 transmittance = {1, 1, 1};
@ -595,11 +596,11 @@ double3 astronomy_system::integrate_transmittance(const ::observer_component& ob
atmosphere_sphere.radius = body.radius + atmosphere.upper_limit;
// Check for intersection between the ray and atmosphere
auto intersection = geom::ray_sphere_intersection(ray, atmosphere_sphere);
if (std::get<0>(intersection))
auto intersection = geom::intersection(ray, atmosphere_sphere);
if (intersection)
{
// Get point of intersection
const double3 intersection_point = ray.extrapolate(std::get<2>(intersection));
const double3 intersection_point = ray.extrapolate(std::get<1>(*intersection));
// Integrate optical of Rayleigh, Mie, and ozone particles
const double optical_depth_r = physics::gas::atmosphere::optical_depth_exp(ray.origin, intersection_point, body.radius, atmosphere.rayleigh_scale_height, transmittance_samples);
@ -618,4 +619,3 @@ double3 astronomy_system::integrate_transmittance(const ::observer_component& ob
return transmittance;
}

+ 2
- 2
src/game/systems/astronomy-system.hpp View File

@ -31,7 +31,7 @@
#include "game/components/atmosphere-component.hpp"
#include "game/components/celestial-body-component.hpp"
#include "game/components/orbit-component.hpp"
#include <engine/geom/ray.hpp>
#include <engine/geom/primitives/ray.hpp>
/**
@ -124,7 +124,7 @@ private:
*
* @return Spectral transmittance factor.
*/
double3 integrate_transmittance(const ::observer_component& observer, const ::celestial_body_component& body, const ::atmosphere_component& atmosphere, geom::ray<double> ray) const;
double3 integrate_transmittance(const ::observer_component& observer, const ::celestial_body_component& body, const ::atmosphere_component& atmosphere, geom::ray<double, 3> ray) const;
/// Time since epoch, in days.
double time_days;

+ 7
- 23
src/game/systems/collision-system.cpp View File

@ -20,9 +20,8 @@
#include "game/systems/collision-system.hpp"
#include "game/components/transform-component.hpp"
#include "game/components/picking-component.hpp"
#include "game/components/collision-component.hpp"
#include "game/components/rigid-body-component.hpp"
#include <engine/geom/primitives/intersection.hpp>
#include <engine/geom/intersection.hpp>
#include <engine/geom/primitives/plane.hpp>
#include <engine/math/transform-operators.hpp>
#include <limits>
@ -31,16 +30,10 @@
collision_system::collision_system(entity::registry& registry):
updatable_system(registry)
{
registry.on_construct<collision_component>().connect<&collision_system::on_collision_construct>(this);
registry.on_update<collision_component>().connect<&collision_system::on_collision_update>(this);
registry.on_destroy<collision_component>().connect<&collision_system::on_collision_destroy>(this);
}
collision_system::~collision_system()
{
registry.on_construct<collision_component>().disconnect<&collision_system::on_collision_construct>(this);
registry.on_update<collision_component>().disconnect<&collision_system::on_collision_update>(this);
registry.on_destroy<collision_component>().disconnect<&collision_system::on_collision_destroy>(this);
}
void collision_system::update(float t, float dt)
@ -48,7 +41,7 @@ void collision_system::update(float t, float dt)
}
entity::id collision_system::pick_nearest(const geom::primitive::ray<float, 3>& ray, std::uint32_t flags) const
entity::id collision_system::pick_nearest(const geom::ray<float, 3>& ray, std::uint32_t flags) const
{
entity::id nearest_eid = entt::null;
float nearest_distance = std::numeric_limits<float>::infinity();
@ -63,14 +56,14 @@ entity::id collision_system::pick_nearest(const geom::primitive::ray&
return;
// Transform picking sphere
const geom::primitive::sphere<float> sphere =
const geom::sphere<float> sphere =
{
transform.world * picking.sphere.center,
picking.sphere.radius * math::max(transform.world.scale)
};
// Test for intersection between ray and sphere
auto result = geom::primitive::intersection(ray, sphere);
auto result = geom::intersection(ray, sphere);
if (result)
{
float t0 = std::get<0>(*result);
@ -88,13 +81,13 @@ entity::id collision_system::pick_nearest(const geom::primitive::ray&
return nearest_eid;
}
entity::id collision_system::pick_nearest(const float3& origin, const float3& normal, std::uint32_t flags) const
entity::id collision_system::pick_nearest(const math::vector<float, 3>& origin, const math::vector<float, 3>& normal, std::uint32_t flags) const
{
entity::id nearest_eid = entt::null;
float nearest_sqr_distance = std::numeric_limits<float>::infinity();
// Construct picking plane
const geom::primitive::plane<float> picking_plane = geom::primitive::plane<float>(origin, normal);
const geom::plane<float> picking_plane = geom::plane<float>(origin, normal);
// For each entity with picking and transform components
registry.view<picking_component, transform_component>().each
@ -106,7 +99,7 @@ entity::id collision_system::pick_nearest(const float3& origin, const float3& no
return;
// Transform picking sphere center
float3 picking_sphere_center = transform.world * picking.sphere.center;
math::vector<float, 3> picking_sphere_center = transform.world * picking.sphere.center;
// Skip entity if picking sphere center has negative distance from picking plane
if (picking_plane.distance(picking_sphere_center) < 0.0f)
@ -127,12 +120,3 @@ entity::id collision_system::pick_nearest(const float3& origin, const float3& no
return nearest_eid;
}
void collision_system::on_collision_construct(entity::registry& registry, entity::id entity_id)
{}
void collision_system::on_collision_update(entity::registry& registry, entity::id entity_id)
{}
void collision_system::on_collision_destroy(entity::registry& registry, entity::id entity_id)
{}

+ 2
- 8
src/game/systems/collision-system.hpp View File

@ -22,7 +22,6 @@
#include "game/systems/updatable-system.hpp"
#include <engine/entity/id.hpp>
#include "game/components/collision-component.hpp"
#include <engine/geom/primitives/ray.hpp>
@ -45,7 +44,7 @@ public:
*
* @return ID of the picked entity, or `entt::null` if no entity was picked.
*/
entity::id pick_nearest(const geom::primitive::ray<float, 3>& ray, std::uint32_t flags) const;
entity::id pick_nearest(const geom::ray<float, 3>& ray, std::uint32_t flags) const;
/**
* Picks the nearest entity with the specified picking flags that has a non-negative distance from a plane.
@ -56,12 +55,7 @@ public:
*
* @return ID of the picked entity, or `entt::null` if no entity was picked.
*/
entity::id pick_nearest(const float3& origin, const float3& normal, std::uint32_t flags) const;
private:
void on_collision_construct(entity::registry& registry, entity::id entity_id);
void on_collision_update(entity::registry& registry, entity::id entity_id);
void on_collision_destroy(entity::registry& registry, entity::id entity_id);
entity::id pick_nearest(const math::vector<float, 3>& origin, const math::vector<float, 3>& normal, std::uint32_t flags) const;
};

+ 0
- 1
src/game/systems/physics-system.cpp View File

@ -18,7 +18,6 @@
*/
#include "game/systems/physics-system.hpp"
#include "game/components/collision-component.hpp"
#include "game/components/rigid-body-component.hpp"
#include "game/components/rigid-body-constraint-component.hpp"
#include "game/components/transform-component.hpp"

+ 2
- 1
src/game/systems/render-system.cpp View File

@ -19,12 +19,13 @@
#include "game/systems/render-system.hpp"
#include "game/components/transform-component.hpp"
#include "game/components/rigid-body-component.hpp"
#include <algorithm>
#include <execution>
render_system::render_system(entity::registry& registry):
updatable_system(registry),
updated_scene_transforms(registry, entt::collector.update<transform_component>().where<scene_component>()),
updated_scene_transforms(registry, entt::collector.update<transform_component>().where<scene_component>(entt::exclude<rigid_body_component>)),
t(0.0),
dt(0.0),
renderer(nullptr)

+ 6
- 158
src/game/systems/subterrain-system.cpp View File

@ -34,158 +34,6 @@
#include <array>
#include <limits>
/**
* An octree containing cubes for the marching cubes algorithm.
*/
struct cube_tree
{
public:
cube_tree(const geom::aabb<float>& bounds, int max_depth);
~cube_tree();
const bool is_leaf() const;
const geom::aabb<float>& get_bounds() const;
/// Subdivides all nodes intersecting with a region to the max depth.
void subdivide_max(const geom::aabb<float>& region);
/// Fills a list with all leaf nodes that intersect with a region.
void query_leaves(std::list<cube_tree*>& nodes, const geom::aabb<float>& region);
void visit_leaves(const geom::aabb<float>& region, const std::function<void(cube_tree&)>& f);
/// Counts then number of nodes in the octree.
std::size_t size() const;
cube_tree* children[8];
float3 corners[8];
float distances[8];
const int max_depth;
const int depth;
const geom::aabb<float> bounds;
private:
cube_tree(const geom::aabb<float>& bounds, int max_depth, int depth);
void subdivide();
};
cube_tree::cube_tree(const geom::aabb<float>& bounds, int max_depth):
cube_tree(bounds, max_depth, 0)
{}
cube_tree::cube_tree(const geom::aabb<float>& bounds, int max_depth, int depth):
bounds(bounds),
max_depth(max_depth),
depth(depth)
{
corners[0] = {bounds.min_point.x(), bounds.min_point.y(), bounds.min_point.z()};
corners[1] = {bounds.max_point.x(), bounds.min_point.y(), bounds.min_point.z()};
corners[2] = {bounds.max_point.x(), bounds.max_point.y(), bounds.min_point.z()};
corners[3] = {bounds.min_point.x(), bounds.max_point.y(), bounds.min_point.z()};
corners[4] = {bounds.min_point.x(), bounds.min_point.y(), bounds.max_point.z()};
corners[5] = {bounds.max_point.x(), bounds.min_point.y(), bounds.max_point.z()};
corners[6] = {bounds.max_point.x(), bounds.max_point.y(), bounds.max_point.z()};
corners[7] = {bounds.min_point.x(), bounds.max_point.y(), bounds.max_point.z()};
for (int i = 0; i < 8; ++i)
{
children[i] = nullptr;
distances[i] = -std::numeric_limits<float>::infinity();
// For outside normals
//distances[i] = std::numeric_limits<float>::infinity();
}
}
cube_tree::~cube_tree()
{
for (cube_tree* child: children)
delete child;
}
void cube_tree::subdivide_max(const geom::aabb<float>& region)
{
if (depth != max_depth && aabb_aabb_intersection(bounds, region))
{
if (is_leaf())
subdivide();
for (cube_tree* child: children)
child->subdivide_max(region);
}
}
void cube_tree::query_leaves(std::list<cube_tree*>& nodes, const geom::aabb<float>& region)
{
if (aabb_aabb_intersection(bounds, region))
{
if (is_leaf())
{
nodes.push_back(this);
}
else
{
for (cube_tree* child: children)
child->query_leaves(nodes, region);
}
}
}
void cube_tree::visit_leaves(const geom::aabb<float>& region, const std::function<void(cube_tree&)>& f)
{
if (aabb_aabb_intersection(bounds, region))
{
if (is_leaf())
{
f(*this);
}
else
{
for (cube_tree* child: children)
child->visit_leaves(region, f);
}
}
}
std::size_t cube_tree::size() const
{
std::size_t node_count = 1;
if (!is_leaf())
{
for (cube_tree* child: children)
node_count += child->size();
}
return node_count;
}
inline const bool cube_tree::is_leaf() const
{
return (children[0] == nullptr);
}
inline const geom::aabb<float>& cube_tree::get_bounds() const
{
return bounds;
}
void cube_tree::subdivide()
{
const float3 center = (bounds.min_point + bounds.max_point) * 0.5f;
for (int i = 0; i < 8; ++i)
{
geom::aabb<float> child_bounds;
for (int j = 0; j < 3; ++j)
{
child_bounds.min_point[j] = std::min<float>(corners[i][j], center[j]);
child_bounds.max_point[j] = std::max<float>(corners[i][j], center[j]);
}
children[i] = new cube_tree(child_bounds, max_depth, depth + 1);
}
}
subterrain_system::subterrain_system(entity::registry& registry, ::resource_manager* resource_manager):
updatable_system(registry),
resource_manager(resource_manager)
@ -263,8 +111,8 @@ subterrain_system::subterrain_system(entity::registry& registry, ::resource_mana
float adjusted_volume_size = std::pow(2.0f, octree_depth) * isosurface_resolution;
// Set subterrain bounds
subterrain_bounds.min_point = float3{-0.5f, -1.0f, -0.5f} * adjusted_volume_size;
subterrain_bounds.max_point = float3{ 0.5f, 0.0f, 0.5f} * adjusted_volume_size;
subterrain_bounds.min = float3{-0.5f, -1.0f, -0.5f} * adjusted_volume_size;
subterrain_bounds.max = float3{ 0.5f, 0.0f, 0.5f} * adjusted_volume_size;
// Set subterrain model bounds
subterrain_model->set_bounds(subterrain_bounds);
@ -369,7 +217,7 @@ void subterrain_system::march(::cube_tree* node)
}
// Get node bounds
const geom::aabb<float>& bounds = node->get_bounds();
const geom::box<float>& bounds = node->get_bounds();
// Polygonize cube
float vertex_buffer[12 * 3];
@ -491,11 +339,11 @@ void subterrain_system::dig(const float3& position, float radius)
{
/*
// Construct region containing the cavity sphere
geom::aabb<float> region = {position, position};
geom::box<float> region = {position, position};
for (int i = 0; i < 3; ++i)
{
region.min_point[i] -= radius + isosurface_resolution;
region.max_point[i] += radius + isosurface_resolution;
region.min[i] -= radius + isosurface_resolution;
region.max[i] += radius + isosurface_resolution;
}
// Subdivide the octree to the maximum depth within the region

+ 1
- 2
src/game/systems/subterrain-system.hpp View File

@ -22,7 +22,6 @@
#include "game/systems/updatable-system.hpp"
#include <engine/geom/mesh.hpp>
#include <engine/geom/aabb.hpp>
#include <engine/scene/collection.hpp>
#include <engine/scene/static-mesh.hpp>
#include <engine/render/model.hpp>
@ -107,7 +106,7 @@ private:
::render::model_group* subterrain_outside_group;
int subterrain_model_vertex_size;
int subterrain_model_vertex_stride;
geom::aabb<float> subterrain_bounds;
geom::box<float> subterrain_bounds;
cube_tree* cube_tree;
std::vector<float3> subterrain_vertices;
std::vector<std::array<std::uint_fast32_t, 3>> subterrain_triangles;

+ 13
- 13
src/game/systems/terrain-system.cpp View File

@ -329,20 +329,20 @@ std::unique_ptr terrain_system::generate_patch_mesh(quadtree_node_ty
const float cell_size = patch_size / static_cast<float>(patch_subdivisions + 1);
// Init patch bounds
geom::aabb<float> patch_bounds;
patch_bounds.min_point.x() = patch_center.x() - patch_size * 0.5f;
patch_bounds.min_point.y() = std::numeric_limits<float>::infinity();
patch_bounds.min_point.z() = patch_center.z() - patch_size * 0.5f;
patch_bounds.max_point.x() = patch_center.x() + patch_size * 0.5f;
patch_bounds.max_point.y() = -std::numeric_limits<float>::infinity();
patch_bounds.max_point.z() = patch_center.z() + patch_size * 0.5f;
geom::box<float> patch_bounds;
patch_bounds.min.x() = patch_center.x() - patch_size * 0.5f;
patch_bounds.min.y() = std::numeric_limits<float>::infinity();
patch_bounds.min.z() = patch_center.z() - patch_size * 0.5f;
patch_bounds.max.x() = patch_center.x() + patch_size * 0.5f;
patch_bounds.max.y() = -std::numeric_limits<float>::infinity();
patch_bounds.max.z() = patch_center.z() + patch_size * 0.5f;
// Calculate positions and UVs of patch vertices and immediately neighboring vertices
float3 first_vertex_position =
{
patch_bounds.min_point.x() - cell_size,
patch_bounds.min.x() - cell_size,
patch_center.y(),
patch_bounds.min_point.z() - cell_size
patch_bounds.min.z() - cell_size
};
float3 vertex_position = first_vertex_position;
for (std::size_t i = 0; i < patch_vertex_buffer.size(); ++i)
@ -354,15 +354,15 @@ std::unique_ptr terrain_system::generate_patch_mesh(quadtree_node_ty
vertex_position.y() = elevation_function(vertex_position.x(), vertex_position.z());
// Update patch bounds
patch_bounds.min_point.y() = std::min(patch_bounds.min_point.y(), vertex_position.y());
patch_bounds.max_point.y() = std::max(patch_bounds.max_point.y(), vertex_position.y());
patch_bounds.min.y() = std::min(patch_bounds.min.y(), vertex_position.y());
patch_bounds.max.y() = std::max(patch_bounds.max.y(), vertex_position.y());
// Update patch vertex position
patch_vertex_buffer[i][j].position = vertex_position;
// Calculate patch vertex UV
patch_vertex_buffer[i][j].uv.x() = (vertex_position.x() - patch_bounds.min_point.x()) / patch_size;
patch_vertex_buffer[i][j].uv.y() = (vertex_position.z() - patch_bounds.min_point.z()) / patch_size;
patch_vertex_buffer[i][j].uv.x() = (vertex_position.x() - patch_bounds.min.x()) / patch_size;
patch_vertex_buffer[i][j].uv.y() = (vertex_position.z() - patch_bounds.min.z()) / patch_size;
// Init patch vertex normal, tangent, and bitangent
patch_vertex_buffer[i][j].normal = {0, 0, 0};

+ 0
- 1
src/game/systems/terrain-system.hpp View File

@ -31,7 +31,6 @@
#include <engine/render/material.hpp>
#include <engine/scene/static-mesh.hpp>
#include <engine/scene/collection.hpp>
#include <engine/geom/view-frustum.hpp>
#include <unordered_map>
#include <stack>

Loading…
Cancel
Save