diff --git a/CMakeLists.txt b/CMakeLists.txt index 21ca183..73e1a79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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") diff --git a/src/engine/app/display.hpp b/src/engine/app/display.hpp index f35c406..2f9a46f 100644 --- a/src/engine/app/display.hpp +++ b/src/engine/app/display.hpp @@ -59,7 +59,7 @@ public: * * @param bounds Bounds of the display, in display units. */ - inline void set_bounds(const geom::primitive::rectangle& bounds) noexcept + inline void set_bounds(const geom::rectangle& 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& bounds) noexcept + inline void set_usable_bounds(const geom::rectangle& 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& get_bounds() const noexcept + [[nodiscard]] inline const geom::rectangle& 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& get_usable_bounds() const noexcept + [[nodiscard]] inline const geom::rectangle& 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 m_bounds{0}; - geom::primitive::rectangle m_usable_bounds{0}; + geom::rectangle m_bounds{0}; + geom::rectangle m_usable_bounds{0}; int m_refresh_rate{0}; float m_dpi{0.0f}; display_orientation m_orientation{0}; diff --git a/src/engine/geom/aabb.hpp b/src/engine/geom/aabb.hpp deleted file mode 100644 index deabf04..0000000 --- a/src/engine/geom/aabb.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_GEOM_AABB_HPP -#define ANTKEEPER_GEOM_AABB_HPP - -#include -#include -#include -#include -#include -#include - -namespace geom { - -/** - * Axis-aligned bounding box. - */ -template -struct aabb: public bounding_volume -{ - typedef math::vector vector_type; - typedef math::matrix matrix_type; - typedef math::transform 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& sphere) const; - virtual bool intersects(const aabb& aabb) const; - virtual bool contains(const sphere& sphere) const; - virtual bool contains(const aabb& 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 -aabb aabb::transform(const aabb& a, const transform_type& t) -{ - vector_type min_point = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; - vector_type max_point = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::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(min_point[j], transformed_corner[j]); - max_point[j] = std::max(max_point[j], transformed_corner[j]); - } - } - - return {min_point, max_point}; -} - -template -aabb aabb::transform(const aabb& a, const matrix_type& m) -{ - vector_type min_point = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; - vector_type max_point = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; - - for (std::size_t i = 0; i < 8; ++i) - { - vector_type corner = a.corner(i); - math::vector transformed_corner = math::mul(m, math::vector{corner.x(), corner.y(), corner.z(), T{1}}); - - for (std::size_t j = 0; j < 3; ++j) - { - min_point[j] = std::min(min_point[j], transformed_corner[j]); - max_point[j] = std::max(max_point[j], transformed_corner[j]); - } - } - - return {min_point, max_point}; -} - -template -aabb::aabb(const vector_type& min_point, const vector_type& max_point): - min_point(min_point), - max_point(max_point) -{} - -template -aabb::aabb() -{} - -template -inline bounding_volume_type aabb::get_bounding_volume_type() const -{ - return bounding_volume_type::aabb; -} - -template -bool aabb::intersects(const sphere& sphere) const -{ - const vector_type radius_vector = {sphere.radius, sphere.radius, sphere.radius}; - return aabb(min_point - radius_vector, max_point + radius_vector).contains(sphere.center); -} - -template -bool aabb::intersects(const aabb& 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 -bool aabb::contains(const sphere& 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 -bool aabb::contains(const aabb& 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 -bool aabb::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 -typename aabb::vector_type aabb::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 - diff --git a/src/engine/geom/bounding-volume.hpp b/src/engine/geom/bounding-volume.hpp deleted file mode 100644 index ec53fa0..0000000 --- a/src/engine/geom/bounding-volume.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_GEOM_BOUNDING_VOLUME_HPP -#define ANTKEEPER_GEOM_BOUNDING_VOLUME_HPP - -#include -#include - -namespace geom { - -template -struct sphere; -template -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 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& sphere) const = 0; - - /// Tests for intersection between this bounding volume and an axis-aligned bounding box. - virtual bool intersects(const aabb& aabb) const = 0; - - /// Tests whether this bounding volume contains a sphere. - virtual bool contains(const sphere& sphere) const = 0; - - /// Tests whether this bounding volume contains an axis-aligned bounding box. - virtual bool contains(const aabb& aabb) const = 0; - - /// Tests whether this bounding volume contains a point. - virtual bool contains(const math::vector& 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 -bool bounding_volume::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&>(volume)); - break; - - case bounding_volume_type::aabb: - return intersects(static_cast&>(volume)); - break; - - default: - throw std::runtime_error("unimplemented"); - break; - } -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_BOUNDING_VOLUME_HPP diff --git a/src/engine/geom/convex-hull.hpp b/src/engine/geom/convex-hull.hpp deleted file mode 100644 index 5e46246..0000000 --- a/src/engine/geom/convex-hull.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_GEOM_CONVEX_HULL_HPP -#define ANTKEEPER_GEOM_CONVEX_HULL_HPP - -#include -#include -#include -#include -#include -#include - -namespace geom { - -/** - * A plane-bounded convex hull. - */ -template -struct convex_hull: public bounding_volume -{ - /// Vector of planes with descibe the bounds of the convex hull. - std::vector> 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& sphere) const; - virtual bool intersects(const aabb& aabb) const; - virtual bool contains(const sphere& sphere) const; - virtual bool contains(const aabb& aabb) const; - virtual bool contains(const math::vector& point) const; -}; - -template -convex_hull::convex_hull(std::size_t size): - planes(size, plane()) -{} - -template -convex_hull::convex_hull() -{} - -template -inline bounding_volume_type convex_hull::get_bounding_volume_type() const -{ - return bounding_volume_type::convex_hull; -} - -template -bool convex_hull::intersects(const sphere& sphere) const -{ - for (const plane& plane: planes) - if (plane.signed_distance(sphere.center) < -sphere.radius) - return false; - return true; -} - -template -bool convex_hull::intersects(const aabb& aabb) const -{ - math::vector pv; - for (const plane& 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 -bool convex_hull::contains(const sphere& sphere) const -{ - for (const plane& plane: planes) - if (plane.signed_distance(sphere.center) < sphere.radius) - return false; - return true; -} - -template -bool convex_hull::contains(const aabb& aabb) const -{ - math::vector pv; - math::vector nv; - - for (const plane& 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 -bool convex_hull::contains(const math::vector& point) const -{ - for (const plane& plane: planes) - if (plane.signed_distance(point) < T(0)) - return false; - - return true; -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_CONVEX_HULL_HPP - diff --git a/src/engine/geom/geom.hpp b/src/engine/geom/geom.hpp index a86abb4..8ecfea3 100644 --- a/src/engine/geom/geom.hpp +++ b/src/engine/geom/geom.hpp @@ -20,27 +20,7 @@ #ifndef ANTKEEPER_GEOM_HPP #define ANTKEEPER_GEOM_HPP -/// Geometry +/// Geometric algorithms. namespace geom {} -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #endif // ANTKEEPER_GEOM_HPP diff --git a/src/engine/geom/intersection.cpp b/src/engine/geom/intersection.cpp deleted file mode 100644 index 22810f4..0000000 --- a/src/engine/geom/intersection.cpp +++ /dev/null @@ -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 . - */ - -#include -#include - -namespace geom { - -std::tuple ray_plane_intersection(const ray& ray, const plane& 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::infinity()); -} - -std::tuple ray_triangle_intersection(const ray& 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::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::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::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::infinity(), 0.0f, 0.0f); -} - -std::tuple ray_aabb_intersection(const ray& ray, const aabb& aabb) -{ - float t0 = -std::numeric_limits::infinity(); - float t1 = std::numeric_limits::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::infinity(), std::numeric_limits::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::infinity(), std::numeric_limits::infinity()); - } - - return std::make_tuple(true, t0, t1); -} - -std::tuple ray_mesh_intersection(const ray& ray, const mesh& mesh) -{ - const std::vector& triangles = mesh.get_faces(); - - bool intersection = false; - float t0 = std::numeric_limits::infinity(); - float t1 = -std::numeric_limits::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(triangle->edge->vertex->position); - const float3& b = reinterpret_cast(triangle->edge->next->vertex->position); - const float3& c = reinterpret_cast(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& a, const aabb& 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& 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 diff --git a/src/engine/geom/intersection.hpp b/src/engine/geom/intersection.hpp index 83bd0f5..7a3c21a 100644 --- a/src/engine/geom/intersection.hpp +++ b/src/engine/geom/intersection.hpp @@ -20,55 +20,252 @@ #ifndef ANTKEEPER_GEOM_INTERSECTION_HPP #define ANTKEEPER_GEOM_INTERSECTION_HPP -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include 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 ray_plane_intersection(const ray& ray, const plane& plane); +/// @{ +template +[[nodiscard]] constexpr std::optional intersection(const ray& ray, const hyperplane& 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 ray_triangle_intersection(const ray& ray, const float3& a, const float3& b, const float3& c); +template +[[nodiscard]] inline constexpr std::optional intersection(const hyperplane& hyperplane, const ray& ray) noexcept +{ + return intersection(ray, hyperplane); +} +/// @} -std::tuple ray_aabb_intersection(const ray& ray, const aabb& 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 +[[nodiscard]] constexpr std::optional> intersection(const ray& ray, const hyperrectangle& hyperrectangle) noexcept +{ + T t0 = -std::numeric_limits::infinity(); + T t1 = std::numeric_limits::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{t0, t1}; +} -std::tuple ray_mesh_intersection(const ray& ray, const mesh& mesh); +template +[[nodiscard]] inline constexpr std::optional> intersection(const hyperrectangle& hyperrectangle, const ray& ray) noexcept +{ + return intersection(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 -std::tuple ray_sphere_intersection(const ray& ray, const sphere& sphere) +template +[[nodiscard]] std::optional> intersection(const ray& ray, const hypersphere& 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 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{t0, t1}; } -bool aabb_sphere_intersection(const aabb& 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 +[[nodiscard]] std::optional> intersection(const ray& ray, const math::vector& a, const math::vector& b, const math::vector& c) +{ + // Find edges + const math::vector edge10 = b - a; + const math::vector edge20 = c - a; + + // Calculate determinant + const math::vector 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 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& a, const aabb& b); + // Calculate v + const math::vector 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, 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 +[[nodiscard]] inline constexpr bool intersection(const hyperrectangle& a, const hyperrectangle& 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 +[[nodiscard]] constexpr bool intersection(const hyperrectangle& hyperrectangle, const hypersphere& 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 +[[nodiscard]] inline constexpr bool intersection(const hypersphere& hypersphere, const hyperrectangle& hyperrectangle) noexcept +{ + return intersection(hyperrectangle, hypersphere); +} +/// @} + +/** + * Hypersphere-hypersphere intersection test. + * + * @param a First hypersphere. + * @param b Second hypersphere. + * + * @return `true` if an intersection occurred, `false` otherwise. + */ +template +[[nodiscard]] inline constexpr bool intersection(const hypersphere& a, const hypersphere& b) noexcept +{ + return a.intersects(b); +} } // namespace geom #endif // ANTKEEPER_GEOM_INTERSECTION_HPP - diff --git a/src/engine/geom/mesh-accelerator.cpp b/src/engine/geom/mesh-accelerator.cpp index d59ebdf..cf2918b 100644 --- a/src/engine/geom/mesh-accelerator.cpp +++ b/src/engine/geom/mesh-accelerator.cpp @@ -34,9 +34,9 @@ void mesh_accelerator::build(const mesh& mesh) face_map.clear(); // Calculate mesh dimensions - aabb 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 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::query_nearest(const ray& ray) const +std::optional mesh_accelerator::query_nearest(const ray& ray) const { ray_query_result result; result.t = std::numeric_limits::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& ray) const +void mesh_accelerator::query_nearest_recursive(float& nearest_t, geom::mesh::face*& nearest_face, typename octree_type::node_type node, const ray& ray) const { // Get node bounds - const aabb node_bounds = get_node_bounds(node); - - // Test for intersection with node bounds - auto aabb_intersection = ray_aabb_intersection(ray, node_bounds); - + const box 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(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 mesh_accelerator::get_node_bounds(typename octree_type::node_type node) const +box 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{min_point, min_point + dimensions}; + return box{min_point, min_point + dimensions}; } typename mesh_accelerator::octree_type::node_type mesh_accelerator::find_node(const float3& point) const diff --git a/src/engine/geom/mesh-accelerator.hpp b/src/engine/geom/mesh-accelerator.hpp index 6ae344e..273a57a 100644 --- a/src/engine/geom/mesh-accelerator.hpp +++ b/src/engine/geom/mesh-accelerator.hpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include #include @@ -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 query_nearest(const ray& ray) const; + std::optional query_nearest(const ray& ray) const; private: typedef unordered_octree32 octree_type; - aabb get_node_bounds(typename octree_type::node_type node) const; + box 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& ray) const; + void query_nearest_recursive(float& nearest_t, geom::mesh::face*& nearest_face, typename octree_type::node_type node, const ray& ray) const; /// Returns the max-depth node in which the point is located typename octree_type::node_type find_node(const float3& point) const; diff --git a/src/engine/geom/mesh-functions.cpp b/src/engine/geom/mesh-functions.cpp index 50c5921..b19c735 100644 --- a/src/engine/geom/mesh-functions.cpp +++ b/src/engine/geom/mesh-functions.cpp @@ -153,27 +153,22 @@ void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const } } -aabb calculate_bounds(const mesh& mesh) +box calculate_bounds(const mesh& mesh) { - float3 bounds_min; - float3 bounds_max; + box bounds; for (int i = 0; i < 3; ++i) { - bounds_min[i] = std::numeric_limits::infinity(); - bounds_max[i] = -std::numeric_limits::infinity(); + bounds.min[i] = std::numeric_limits::infinity(); + bounds.max[i] = -std::numeric_limits::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(bounds_min[i], position[i]); - bounds_max[i] = std::max(bounds_max[i], position[i]); - } + bounds.extend(position); } - return aabb{bounds_min, bounds_max}; + return bounds; } mesh::vertex* poke_face(mesh& mesh, std::size_t index) diff --git a/src/engine/geom/mesh-functions.hpp b/src/engine/geom/mesh-functions.hpp index c1fbfd8..dc027fa 100644 --- a/src/engine/geom/mesh-functions.hpp +++ b/src/engine/geom/mesh-functions.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP #define ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP -#include +#include #include #include #include @@ -59,7 +59,7 @@ void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const /** * Calculates the AABB bounds of a mesh. */ -aabb calculate_bounds(const mesh& mesh); +box 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. diff --git a/src/engine/geom/plane.hpp b/src/engine/geom/plane.hpp deleted file mode 100644 index 9723153..0000000 --- a/src/engine/geom/plane.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_GEOM_PLANE_HPP -#define ANTKEEPER_GEOM_PLANE_HPP - -#include - -namespace geom { - -/** - * A flat 2-dimensional surface. - */ -template -struct plane -{ - typedef math::vector 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& 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 -inline plane::plane(const vector_type& normal, T distance): - normal(normal), - distance(distance) -{} - -template -plane::plane(const vector_type& normal, const vector_type& offset): - normal(normal), - distance(-math::dot(normal, offset)) -{} - -template -plane::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 -plane::plane(const math::vector& coefficients) -{ - const vector_type abc = math::vector(coefficients); - const float inverse_length = T(1) / math::length(abc); - - normal = abc * inverse_length; - distance = coefficients[3] * inverse_length; -} - -template -inline T plane::signed_distance(const vector_type& v) const -{ - return distance + math::dot(normal, v); -} - -template -typename plane::vector_type plane::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 diff --git a/src/engine/geom/primitives/box.hpp b/src/engine/geom/primitives/box.hpp index 7f1f9b3..344f6dc 100644 --- a/src/engine/geom/primitives/box.hpp +++ b/src/engine/geom/primitives/box.hpp @@ -23,7 +23,7 @@ #include namespace geom { -namespace primitive { +namespace primitives { /** * 3-dimensional hyperrectangle. @@ -33,7 +33,7 @@ namespace primitive { template using box = hyperrectangle; -} // namespace primitive +} // namespace primitives } // namespace geom #endif // ANTKEEPER_GEOM_PRIMITIVES_BOX_HPP diff --git a/src/engine/geom/primitives/circle.hpp b/src/engine/geom/primitives/circle.hpp index 5642aa0..0ff3f41 100644 --- a/src/engine/geom/primitives/circle.hpp +++ b/src/engine/geom/primitives/circle.hpp @@ -23,7 +23,7 @@ #include namespace geom { -namespace primitive { +namespace primitives { /** * 2-dimensional hypersphere. @@ -33,7 +33,7 @@ namespace primitive { template using circle = hypersphere; -} // namespace primitive +} // namespace primitives } // namespace geom #endif // ANTKEEPER_GEOM_PRIMITIVES_CIRCLE_HPP diff --git a/src/engine/geom/primitives/hyperplane.hpp b/src/engine/geom/primitives/hyperplane.hpp index aede887..eb07615 100644 --- a/src/engine/geom/primitives/hyperplane.hpp +++ b/src/engine/geom/primitives/hyperplane.hpp @@ -23,7 +23,7 @@ #include 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 diff --git a/src/engine/geom/primitives/hyperrectangle.hpp b/src/engine/geom/primitives/hyperrectangle.hpp index 9d7f500..64364ac 100644 --- a/src/engine/geom/primitives/hyperrectangle.hpp +++ b/src/engine/geom/primitives/hyperrectangle.hpp @@ -25,7 +25,7 @@ #include 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 diff --git a/src/engine/geom/primitives/hypersphere.hpp b/src/engine/geom/primitives/hypersphere.hpp index b7fe8a2..b7de49b 100644 --- a/src/engine/geom/primitives/hypersphere.hpp +++ b/src/engine/geom/primitives/hypersphere.hpp @@ -24,7 +24,7 @@ #include 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 diff --git a/src/engine/geom/primitives/intersection.hpp b/src/engine/geom/primitives/intersection.hpp deleted file mode 100644 index 97c94f6..0000000 --- a/src/engine/geom/primitives/intersection.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVES_INTERSECTION_HPP -#define ANTKEEPER_GEOM_PRIMITIVES_INTERSECTION_HPP - -#include -#include -#include -#include -#include -#include - -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 -constexpr std::optional intersection(const ray& ray, const hyperplane& 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 -inline constexpr std::optional intersection(const hyperplane& hyperplane, const ray& ray) noexcept -{ - return intersection(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 -constexpr std::optional> intersection(const ray& ray, const hyperrectangle& hyperrectangle) noexcept -{ - T t0 = -std::numeric_limits::infinity(); - T t1 = std::numeric_limits::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 -inline constexpr std::optional> intersection(const hyperrectangle& hyperrectangle, const ray& ray) noexcept -{ - return intersection(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 -std::optional> intersection(const ray& ray, const hypersphere& hypersphere) noexcept -{ - const math::vector 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{t0, t1}; -} - -/** - * Hyperrectangle-hyperrectangle intersection test. - * - * @param a First hyperrectangle. - * @param b Second hyperrectangle. - * - * @return `true` if an intersection occurred, `false` otherwise. - */ -template -inline constexpr bool intersection(const hyperrectangle& a, const hyperrectangle& 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 -constexpr bool intersection(const hyperrectangle& hyperrectangle, const hypersphere& 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 -inline constexpr bool intersection(const hypersphere& hypersphere, const hyperrectangle& hyperrectangle) noexcept -{ - return intersection(hyperrectangle, hypersphere); -} -/// @} - -/** - * Hypersphere-hypersphere intersection test. - * - * @param a First hypersphere. - * @param b Second hypersphere. - * - * @return `true` if an intersection occurred, `false` otherwise. - */ -template -inline constexpr bool intersection(const hypersphere& a, const hypersphere& b) noexcept -{ - return a.intersects(b); -} - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVES_INTERSECTION_HPP diff --git a/src/engine/geom/primitives/line-segment.hpp b/src/engine/geom/primitives/line-segment.hpp index 9d63a71..508d856 100644 --- a/src/engine/geom/primitives/line-segment.hpp +++ b/src/engine/geom/primitives/line-segment.hpp @@ -23,7 +23,7 @@ #include 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 diff --git a/src/engine/geom/primitives/line.hpp b/src/engine/geom/primitives/line.hpp index ef97e5a..8a527e2 100644 --- a/src/engine/geom/primitives/line.hpp +++ b/src/engine/geom/primitives/line.hpp @@ -23,7 +23,7 @@ #include namespace geom { -namespace primitive { +namespace primitives { /** * 2-dimensional hyperplane. @@ -33,7 +33,7 @@ namespace primitive { template using line = hyperplane; -} // namespace primitive +} // namespace primitives } // namespace geom #endif // ANTKEEPER_GEOM_PRIMITIVES_LINE_HPP diff --git a/src/engine/geom/primitives/plane.hpp b/src/engine/geom/primitives/plane.hpp index a49fbf8..faa12d5 100644 --- a/src/engine/geom/primitives/plane.hpp +++ b/src/engine/geom/primitives/plane.hpp @@ -23,7 +23,7 @@ #include namespace geom { -namespace primitive { +namespace primitives { /** * 3-dimensional hyperplane. @@ -33,7 +33,7 @@ namespace primitive { template using plane = hyperplane; -} // namespace primitive +} // namespace primitives } // namespace geom #endif // ANTKEEPER_GEOM_PRIMITIVES_PLANE_HPP diff --git a/src/engine/geom/primitives/ray.hpp b/src/engine/geom/primitives/ray.hpp index 7bc0610..51ea2d2 100644 --- a/src/engine/geom/primitives/ray.hpp +++ b/src/engine/geom/primitives/ray.hpp @@ -25,7 +25,7 @@ #include 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 diff --git a/src/engine/geom/primitives/rectangle.hpp b/src/engine/geom/primitives/rectangle.hpp index 692f466..f43aa2a 100644 --- a/src/engine/geom/primitives/rectangle.hpp +++ b/src/engine/geom/primitives/rectangle.hpp @@ -23,7 +23,7 @@ #include namespace geom { -namespace primitive { +namespace primitives { /** * 2-dimensional hyperrectangle. @@ -33,7 +33,7 @@ namespace primitive { template using rectangle = hyperrectangle; -} // namespace primitive +} // namespace primitives } // namespace geom #endif // ANTKEEPER_GEOM_PRIMITIVES_RECTANGLE_HPP diff --git a/src/engine/geom/primitives/sphere.hpp b/src/engine/geom/primitives/sphere.hpp index d4b8464..e5df04f 100644 --- a/src/engine/geom/primitives/sphere.hpp +++ b/src/engine/geom/primitives/sphere.hpp @@ -23,7 +23,7 @@ #include namespace geom { -namespace primitive { +namespace primitives { /** * 3-dimensional hypersphere. @@ -33,7 +33,7 @@ namespace primitive { template using sphere = hypersphere; -} // namespace primitive +} // namespace primitives } // namespace geom #endif // ANTKEEPER_GEOM_PRIMITIVES_SPHERE_HPP diff --git a/src/engine/geom/primitives/view-frustum.hpp b/src/engine/geom/primitives/view-frustum.hpp new file mode 100644 index 0000000..0610c29 --- /dev/null +++ b/src/engine/geom/primitives/view-frustum.hpp @@ -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 . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVES_VIEW_FRUSTUM_HPP +#define ANTKEEPER_GEOM_PRIMITIVES_VIEW_FRUSTUM_HPP + +#include +#include +#include +#include +#include + +namespace geom { +namespace primitives { + +/** + * View frustum. + * + * @tparam T Real type. + */ +template +struct view_frustum +{ + /// Vector type. + using vector_type = math::vector; + + /// Plane type. + using plane_type = geom::plane; + + /// View-projection matrix type. + using matrix_type = math::matrix; + + /// Box type. + using box_type = geom::box; + + /// Sphere type. + using sphere_type = geom::sphere; + + /// 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 diff --git a/src/engine/geom/ray.hpp b/src/engine/geom/ray.hpp deleted file mode 100644 index 026c6b2..0000000 --- a/src/engine/geom/ray.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_GEOM_RAY_HPP -#define ANTKEEPER_GEOM_RAY_HPP - -#include - -namespace geom { - -/** - * Half of a line proceeding from an initial point. - */ -template -struct ray -{ - typedef math::vector 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 -inline typename ray::vector_type ray::extrapolate(T distance) const -{ - return origin + direction * distance; -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_RAY_HPP - diff --git a/src/engine/geom/rect-pack.hpp b/src/engine/geom/rect-pack.hpp index 90c6a9a..3c55c0e 100644 --- a/src/engine/geom/rect-pack.hpp +++ b/src/engine/geom/rect-pack.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_GEOM_RECT_PACK_HPP #define ANTKEEPER_GEOM_RECT_PACK_HPP -#include +#include #include 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 rect_type; + using rect_type = rectangle; /// Pointers to the two children of the node, if any. std::unique_ptr 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 node_type; + using node_type = rect_pack_node; /** * Creates a rect pack and sets the bounds of the root node. @@ -127,7 +127,7 @@ template void rect_pack::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 @@ -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) diff --git a/src/engine/geom/rect.hpp b/src/engine/geom/rect.hpp deleted file mode 100644 index 57e1438..0000000 --- a/src/engine/geom/rect.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_GEOM_RECT_HPP -#define ANTKEEPER_GEOM_RECT_HPP - -#include - -namespace geom { - -/** - * 2D rectangle. - */ -template -struct rect -{ - typedef math::vector vector_type; - - vector_type min; - vector_type max; -}; - -} // namespace geom - -#endif // ANTKEEPER_GEOM_RECT_HPP diff --git a/src/engine/geom/sphere.hpp b/src/engine/geom/sphere.hpp deleted file mode 100644 index 51f5982..0000000 --- a/src/engine/geom/sphere.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_GEOM_SPHERE_HPP -#define ANTKEEPER_GEOM_SPHERE_HPP - -#include -#include -#include -#include - -namespace geom { - -/** - * Bounding sphere. - */ -template -struct sphere: public bounding_volume -{ - typedef math::vector 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& sphere) const; - virtual bool intersects(const aabb& aabb) const; - virtual bool contains(const sphere& sphere) const; - virtual bool contains(const aabb& aabb) const; - virtual bool contains(const vector_type& point) const; -}; - -template -sphere::sphere(const vector_type& center, T radius): - center(center), - radius(radius) -{} - -template -sphere::sphere() -{} - -template -inline bounding_volume_type sphere::get_bounding_volume_type() const -{ - return bounding_volume_type::sphere; -} - -template -bool sphere::intersects(const sphere& sphere) const -{ - vector_type d = center - sphere.center; - T r = radius + sphere.radius; - return (math::dot(d, d) <= r * r); -} - -template -bool sphere::intersects(const aabb& aabb) const -{ - return aabb.intersects(*this); -} - -template -bool sphere::contains(const sphere& 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 -bool sphere::contains(const aabb& 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 -bool sphere::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 diff --git a/src/engine/geom/view-frustum.hpp b/src/engine/geom/view-frustum.hpp deleted file mode 100644 index b9ecd9f..0000000 --- a/src/engine/geom/view-frustum.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_GEOM_VIEW_FRUSTUM_HPP -#define ANTKEEPER_GEOM_VIEW_FRUSTUM_HPP - -#include -#include -#include -#include - -namespace geom { - -/** - * View frustum. - */ -template -class view_frustum -{ -public: - typedef math::vector vector_type; - typedef math::matrix matrix_type; - typedef plane 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& 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& get_corners() const; - -private: - void recalculate_planes(const matrix_type& view_projection); - void recalculate_corners(); - - convex_hull bounds; - std::array corners; -}; - -template -view_frustum::view_frustum(const matrix_type& view_projection): - bounds(6) -{ - set_matrix(view_projection); -} - -template -view_frustum::view_frustum(): - view_frustum(math::matrix4::identity()) -{} - -template -void view_frustum::set_matrix(const matrix_type& view_projection) -{ - recalculate_planes(view_projection); - recalculate_corners(); -} - -template -inline const convex_hull& view_frustum::get_bounds() const -{ - return bounds; -} - -template -inline const typename view_frustum::plane_type& view_frustum::get_left() const -{ - return bounds.planes[0]; -} - -template -inline const typename view_frustum::plane_type& view_frustum::get_right() const -{ - return bounds.planes[1]; -} - -template -inline const typename view_frustum::plane_type& view_frustum::get_bottom() const -{ - return bounds.planes[2]; -} - -template -inline const typename view_frustum::plane_type& view_frustum::get_top() const -{ - return bounds.planes[3]; -} - -template -inline const typename view_frustum::plane_type& view_frustum::get_near() const -{ - return bounds.planes[4]; -} - -template -inline const typename view_frustum::plane_type& view_frustum::get_far() const -{ - return bounds.planes[5]; -} - -template -inline const std::array::vector_type, 8>& view_frustum::get_corners() const -{ - return corners; -} - -template -void view_frustum::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 -void view_frustum::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 - diff --git a/src/engine/math/quaternion.hpp b/src/engine/math/quaternion.hpp index 0d65aa1..291d1af 100644 --- a/src/engine/math/quaternion.hpp +++ b/src/engine/math/quaternion.hpp @@ -24,8 +24,6 @@ #include #include #include -#include -#include namespace math { @@ -38,13 +36,13 @@ template struct quaternion { /// Scalar type. - typedef T scalar_type; + using scalar_type = T; /// Vector type. - typedef vector vector_type; + using vector_type = vector; /// Rotation matrix type. - typedef matrix matrix_type; + using matrix_type = matrix; /// 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 constexpr vector mul(const quaternion& a, const vector& 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 @@ -598,7 +596,7 @@ inline constexpr quaternion negate(const quaternion& q) noexcept template quaternion nlerp(const quaternion& a, const quaternion& 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 @@ -610,8 +608,7 @@ inline quaternion normalize(const quaternion& q) template quaternion angle_axis(T angle, const vector& 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 @@ -626,14 +623,16 @@ template quaternion slerp(const quaternion& a, const quaternion& 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(-1), std::min(T(1), cos_theta)); + cos_theta = std::max(T{-1}, std::min(T{1}, cos_theta)); + const T theta = std::acos(cos_theta) * t; - quaternion c = normalize(sub(b, mul(a, cos_theta))); + const quaternion 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 qa = mul(q, a); const vector sa = cross(a, qa); if (sqr_length(sa) > error) + { qs = angle_axis(std::acos(dot(a, qa)), sa); + } else + { qs = quaternion::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 -std::ostream& operator<<(std::ostream& os, const math::quaternion& 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 -std::istream& operator>>(std::istream& is, const math::quaternion& q) -{ - is >> q.r; - is >> q.i; - return is; -} - } // namespace operators } // namespace math diff --git a/src/engine/physics/kinematics/colliders/box-collider.hpp b/src/engine/physics/kinematics/colliders/box-collider.hpp index ae197de..8f75f81 100644 --- a/src/engine/physics/kinematics/colliders/box-collider.hpp +++ b/src/engine/physics/kinematics/colliders/box-collider.hpp @@ -32,7 +32,7 @@ class box_collider: public collider { public: /// Box type. - using box_type = geom::primitive::box; + using box_type = geom::box; [[nodiscard]] inline constexpr collider_type type() const noexcept override { diff --git a/src/engine/physics/kinematics/colliders/plane-collider.hpp b/src/engine/physics/kinematics/colliders/plane-collider.hpp index 43f6c96..282b4e1 100644 --- a/src/engine/physics/kinematics/colliders/plane-collider.hpp +++ b/src/engine/physics/kinematics/colliders/plane-collider.hpp @@ -32,7 +32,7 @@ class plane_collider: public collider { public: /// Plane type. - using plane_type = geom::primitive::plane; + using plane_type = geom::plane; [[nodiscard]] inline constexpr collider_type type() const noexcept override { diff --git a/src/engine/physics/kinematics/colliders/sphere-collider.hpp b/src/engine/physics/kinematics/colliders/sphere-collider.hpp index 3ae5aed..bb47294 100644 --- a/src/engine/physics/kinematics/colliders/sphere-collider.hpp +++ b/src/engine/physics/kinematics/colliders/sphere-collider.hpp @@ -32,7 +32,7 @@ class sphere_collider: public collider { public: /// Sphere type. - using sphere_type = geom::primitive::sphere; + using sphere_type = geom::sphere; [[nodiscard]] inline constexpr collider_type type() const noexcept override { diff --git a/src/engine/render/context.hpp b/src/engine/render/context.hpp index 23d8a59..61f3ac4 100644 --- a/src/engine/render/context.hpp +++ b/src/engine/render/context.hpp @@ -20,8 +20,6 @@ #ifndef ANTKEEPER_RENDER_CONTEXT_HPP #define ANTKEEPER_RENDER_CONTEXT_HPP -#include -#include #include #include #include @@ -44,9 +42,6 @@ struct context /// Pointer to the camera. const scene::camera* camera; - /// Camera culling volume. - const geom::bounding_volume* camera_culling_volume; - /// Collection of scene objects being rendered. const scene::collection* collection; diff --git a/src/engine/render/model.cpp b/src/engine/render/model.cpp index a306422..61978c5 100644 --- a/src/engine/render/model.cpp +++ b/src/engine/render/model.cpp @@ -239,8 +239,7 @@ std::unique_ptr resource_loader::load(::resource_m } // Read model bounds - ctx.read32(reinterpret_cast(model->get_bounds().min_point.data()), 3); - ctx.read32(reinterpret_cast(model->get_bounds().max_point.data()), 3); + ctx.read32(reinterpret_cast(&model->get_bounds()), 6); // Read material count std::uint16_t material_count = 0; diff --git a/src/engine/render/model.hpp b/src/engine/render/model.hpp index dd2793b..7c52163 100644 --- a/src/engine/render/model.hpp +++ b/src/engine/render/model.hpp @@ -21,7 +21,7 @@ #define ANTKEEPER_RENDER_MODEL_HPP #include -#include +#include #include #include #include @@ -53,7 +53,7 @@ class model { public: /// AABB type. - typedef geom::aabb aabb_type; + using aabb_type = geom::box; /** * Constructs a model. diff --git a/src/engine/render/passes/shadow-map-pass.cpp b/src/engine/render/passes/shadow-map-pass.cpp index ab2c3d9..9f4eec8 100644 --- a/src/engine/render/passes/shadow-map-pass.cpp +++ b/src/engine/render/passes/shadow-map-pass.cpp @@ -29,8 +29,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -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(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 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 view_frustum_corners[8]; + for (std::size_t i = 0; i < 8; ++i) + { + math::vector corner = camera.get_inverse_view_projection() * clip_space_cube[i]; + view_frustum_corners[i] = math::vector(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 subfrustum_center{0, 0, 0}; + math::vector 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 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& subfrustum_corners = subfrustum.get_corners(); - geom::aabb 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 cropping_bounds; + cropping_bounds.min = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; + cropping_bounds.max = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::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 corner4 = math::vector(subfrustum_corners[i]); + corner4[3] = 1.0f; + corner4 = light_view_projection * corner4; + + const math::vector corner3 = math::vector(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 cropping_bounds = geom::aabb::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(cascade_resolution); - const float texel_scale_y = (cropping_bounds.max_point.y() - cropping_bounds.min_point.y()) / static_cast(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(cascade_resolution); + const float texel_scale_y = (cropping_bounds.max.y() - cropping_bounds.min.y()) / static_cast(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()) diff --git a/src/engine/render/stages/culling-stage.cpp b/src/engine/render/stages/culling-stage.cpp index b118444..77b44ef 100644 --- a/src/engine/render/stages/culling-stage.cpp +++ b/src/engine/render/stages/culling-stage.cpp @@ -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* 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; } diff --git a/src/engine/scene/billboard.cpp b/src/engine/scene/billboard.cpp index 828472f..0af1553 100644 --- a/src/engine/scene/billboard.cpp +++ b/src/engine/scene/billboard.cpp @@ -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) { diff --git a/src/engine/scene/billboard.hpp b/src/engine/scene/billboard.hpp index d394305..f182db5 100644 --- a/src/engine/scene/billboard.hpp +++ b/src/engine/scene/billboard.hpp @@ -21,7 +21,6 @@ #define ANTKEEPER_SCENE_BILLBOARD_HPP #include -#include #include #include #include @@ -38,8 +37,6 @@ namespace scene { class billboard: public object { public: - using aabb_type = geom::aabb; - /// 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 m_vbo; std::unique_ptr 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 m_alignment_axis{0.0f, 1.0f, 0.0f}; }; diff --git a/src/engine/scene/camera.cpp b/src/engine/scene/camera.cpp index 29d2be7..6812073 100644 --- a/src/engine/scene/camera.cpp +++ b/src/engine/scene/camera.cpp @@ -20,15 +20,14 @@ #include #include #include +#include namespace scene { -geom::primitive::ray camera::pick(const float2& ndc) const +geom::ray 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(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{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 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::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; + m_bounds.max = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::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 frustum_corner = m_inverse_view_projection * clip_space_cube[i]; + m_bounds.extend(math::vector(frustum_corner) / frustum_corner[3]); } } diff --git a/src/engine/scene/camera.hpp b/src/engine/scene/camera.hpp index 0c8ca6a..178fd6b 100644 --- a/src/engine/scene/camera.hpp +++ b/src/engine/scene/camera.hpp @@ -21,7 +21,7 @@ #define ANTKEEPER_SCENE_CAMERA_HPP #include -#include +#include #include #include #include @@ -35,6 +35,7 @@ namespace scene { class camera: public object { public: + /// Camera view frustum type. using view_frustum_type = geom::view_frustum; /** @@ -44,7 +45,7 @@ public: * * @return Picking ray. */ - [[nodiscard]] geom::primitive::ray pick(const float2& ndc) const; + [[nodiscard]] geom::ray 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& 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 m_forward{0.0f, 0.0f, -1.0f}; math::vector 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 diff --git a/src/engine/scene/light.cpp b/src/engine/scene/light.cpp index 0fab0fb..7204e20 100644 --- a/src/engine/scene/light.cpp +++ b/src/engine/scene/light.cpp @@ -18,13 +18,12 @@ */ #include -#include namespace scene { void light::transformed() { - m_bounds = {get_translation(), 0.0f}; + m_bounds = {get_translation(), get_translation()}; } } // namespace scene diff --git a/src/engine/scene/light.hpp b/src/engine/scene/light.hpp index fa8bffc..17fdc1a 100644 --- a/src/engine/scene/light.hpp +++ b/src/engine/scene/light.hpp @@ -21,7 +21,6 @@ #define ANTKEEPER_SCENE_LIGHT_HPP #include -#include #include namespace scene { @@ -32,20 +31,17 @@ namespace scene { class light: public object { public: - typedef geom::sphere 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 diff --git a/src/engine/scene/object.hpp b/src/engine/scene/object.hpp index 3e32816..03ecb1e 100644 --- a/src/engine/scene/object.hpp +++ b/src/engine/scene/object.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_SCENE_OBJECT_HPP #define ANTKEEPER_SCENE_OBJECT_HPP -#include +#include #include #include #include @@ -39,7 +39,7 @@ public: using vector_type = math::vector; using quaternion_type = math::quaternion; using transform_type = math::transform; - using bounding_volume_type = geom::bounding_volume; + using aabb_type = geom::box; /// 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}; }; /** diff --git a/src/engine/scene/static-mesh.cpp b/src/engine/scene/static-mesh.cpp index d063d38..f6b7d89 100644 --- a/src/engine/scene/static-mesh.cpp +++ b/src/engine/scene/static-mesh.cpp @@ -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::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; + m_bounds.max = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::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; diff --git a/src/engine/scene/static-mesh.hpp b/src/engine/scene/static-mesh.hpp index 054eb87..c26c78d 100644 --- a/src/engine/scene/static-mesh.hpp +++ b/src/engine/scene/static-mesh.hpp @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -35,8 +34,6 @@ namespace scene { class static_mesh: public object { public: - typedef geom::aabb 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; } diff --git a/src/engine/scene/text.cpp b/src/engine/scene/text.cpp index 5d22c44..a767a1b 100644 --- a/src/engine/scene/text.cpp +++ b/src/engine/scene/text.cpp @@ -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::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; + m_world_bounds.max = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::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::infinity(), std::numeric_limits::infinity(), 0.0f}; + m_local_bounds.max = {-std::numeric_limits::infinity(), -std::numeric_limits::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(m_local_bounds.min_point[j], position[j]); - m_local_bounds.max_point[j] = std::max(m_local_bounds.max_point[j], position[j]); + m_local_bounds.min[j] = std::min(m_local_bounds.min[j], position[j]); + m_local_bounds.max[j] = std::max(m_local_bounds.max[j], position[j]); } } } diff --git a/src/engine/scene/text.hpp b/src/engine/scene/text.hpp index c4ddb91..899eaba 100644 --- a/src/engine/scene/text.hpp +++ b/src/engine/scene/text.hpp @@ -21,7 +21,6 @@ #define ANTKEEPER_SCENE_TEXT_HPP #include -#include #include #include #include @@ -37,8 +36,6 @@ namespace scene { class text: public object { public: - using aabb_type = geom::aabb; - /// 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; } diff --git a/src/game/ant/ant-morphogenesis.cpp b/src/game/ant/ant-morphogenesis.cpp index e57b54f..1be3989 100644 --- a/src/game/ant/ant-morphogenesis.cpp +++ b/src/game/ant/ant-morphogenesis.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace { @@ -137,7 +138,7 @@ void reskin_vertices * * @return Bounds of the vertex data. */ -[[nodiscard]] geom::aabb calculate_bounds +[[nodiscard]] geom::box 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 bounds; - bounds.min_point = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; - bounds.max_point = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; + geom::box bounds + { + {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}, + {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()} + }; for (std::size_t i = 0; i < vertex_count; ++i) { - // Get vertex position - const float3& position = reinterpret_cast(*(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(*(position_data + position_attribute.stride * i)); + bounds.extend(position); } return bounds; diff --git a/src/game/components/collision-component.hpp b/src/game/components/collision-component.hpp deleted file mode 100644 index edb88b5..0000000 --- a/src/game/components/collision-component.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_GAME_COLLISION_COMPONENT_HPP -#define ANTKEEPER_GAME_COLLISION_COMPONENT_HPP - -#include -#include -#include - - -struct collision_component -{ - geom::mesh* mesh; - geom::aabb bounds; - geom::mesh_accelerator mesh_accelerator; - - float radius{0.0f}; -}; - - -#endif // ANTKEEPER_GAME_COLLISION_COMPONENT_HPP - diff --git a/src/game/components/picking-component.hpp b/src/game/components/picking-component.hpp index f38197c..ae677fb 100644 --- a/src/game/components/picking-component.hpp +++ b/src/game/components/picking-component.hpp @@ -27,7 +27,7 @@ struct picking_component { /// Picking sphere. - geom::primitive::sphere sphere; + geom::sphere sphere; /// Picking flags. std::uint32_t flags; diff --git a/src/game/controls.cpp b/src/game/controls.cpp index b668de0..2aacda9 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -416,19 +416,19 @@ void enable_menu_controls(::game& ctx) { auto [name, value] = ctx.menu_item_texts[i]; - const auto& name_bounds = static_cast&>(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&>(value->get_bounds()); - min_x = std::min(min_x, value_bounds.min_point.x()); - min_y = std::min(min_y, value_bounds.min_point.y()); - max_x = std::max(max_x, value_bounds.max_point.x()); - max_y = std::max(max_y, value_bounds.max_point.y()); + const auto& value_bounds = value->get_bounds(); + min_x = std::min(min_x, value_bounds.min.x()); + min_y = std::min(min_y, value_bounds.min.y()); + max_x = std::max(max_x, value_bounds.max.x()); + max_y = std::max(max_y, value_bounds.max.y()); } min_x -= padding; diff --git a/src/game/game.hpp b/src/game/game.hpp index 1ea7618..86cce1e 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff --git a/src/game/loaders/entity-archetype-loader.cpp b/src/game/loaders/entity-archetype-loader.cpp index 2942c90..9470ac2 100644 --- a/src/game/loaders/entity-archetype-loader.cpp +++ b/src/game/loaders/entity-archetype-loader.cpp @@ -23,7 +23,6 @@ #include #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(element["file"].get()); - } - - archetype.stamps.push_back - ( - [component](entt::handle& handle) - { - handle.emplace_or_replace(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") diff --git a/src/game/menu.cpp b/src/game/menu.cpp index 57c4ae6..6fcb7b5 100644 --- a/src/game/menu.cpp +++ b/src/game/menu.cpp @@ -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&>(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&>(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&>(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&>(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; diff --git a/src/game/states/collection-menu-state.hpp b/src/game/states/collection-menu-state.hpp index 5a82d78..8848fea 100644 --- a/src/game/states/collection-menu-state.hpp +++ b/src/game/states/collection-menu-state.hpp @@ -49,7 +49,7 @@ private: std::shared_ptr mouse_button_pressed_subscription; std::shared_ptr window_resized_subscription; - geom::primitive::rectangle box_bounds; + geom::rectangle box_bounds; int row_count; int column_count; int selected_row; diff --git a/src/game/states/credits-state.cpp b/src/game/states/credits-state.cpp index cdc1afb..f3df44a 100644 --- a/src/game/states/credits-state.cpp +++ b/src/game/states/credits-state.cpp @@ -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&>(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&>(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}); } ); diff --git a/src/game/states/main-menu-state.cpp b/src/game/states/main-menu-state.cpp index 02d2b15..d098b7f 100644 --- a/src/game/states/main-menu-state.cpp +++ b/src/game/states/main-menu-state.cpp @@ -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&>(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&>(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); diff --git a/src/game/states/nest-selection-state.cpp b/src/game/states/nest-selection-state.cpp index 9a146e0..6a7172d 100644 --- a/src/game/states/nest-selection-state.cpp +++ b/src/game/states/nest-selection-state.cpp @@ -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(); 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(float3{-0.5f, -0.5f, -0.5f}, float3{0.5f, 0.5f, 0.5f}); + //auto projectile_collider = std::make_shared(float3{-1.0f, -1.0f, -1.0f}, float3{1.0f, 1.0f, 1.0f}); auto projectile_collider = std::make_shared(1.0f); diff --git a/src/game/states/nuptial-flight-state.cpp b/src/game/states/nuptial-flight-state.cpp index 2832f8c..f953640 100644 --- a/src/game/states/nuptial-flight-state.cpp +++ b/src/game/states/nuptial-flight-state.cpp @@ -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&>(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 ray = ctx.surface_camera->pick(mouse_ndc); + const geom::ray ray = ctx.surface_camera->pick(mouse_ndc); // Pick entity entity::id picked_eid = ctx.collision_system->pick_nearest(ray, ~selected_picking_flag); @@ -821,7 +821,7 @@ void nuptial_flight_state::enable_controls() }; // Get picking ray from camera - const geom::primitive::ray ray = ctx.surface_camera->pick(mouse_ndc); + const geom::ray ray = ctx.surface_camera->pick(mouse_ndc); // Pick entity entity::id picked_eid = ctx.collision_system->pick_nearest(ray, ~selected_picking_flag); @@ -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&>(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}); } } diff --git a/src/game/systems/astronomy-system.cpp b/src/game/systems/astronomy-system.cpp index 98aaf28..90ff52d 100644 --- a/src/game/systems/astronomy-system.cpp +++ b/src/game/systems/astronomy-system.cpp @@ -24,6 +24,7 @@ #include "game/components/diffuse-reflector-component.hpp" #include #include +#include #include #include #include @@ -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 ray = {{0, 0, 0}, observer_blackbody_direction_eus}; + const geom::ray 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 ray = {{0, 0, 0}, observer_reflector_direction_eus}; + const geom::ray 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 ray) const +double3 astronomy_system::integrate_transmittance(const ::observer_component& observer, const ::celestial_body_component& body, const ::atmosphere_component& atmosphere, geom::ray 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; } - diff --git a/src/game/systems/astronomy-system.hpp b/src/game/systems/astronomy-system.hpp index 3fe64fa..3c19cd6 100644 --- a/src/game/systems/astronomy-system.hpp +++ b/src/game/systems/astronomy-system.hpp @@ -31,7 +31,7 @@ #include "game/components/atmosphere-component.hpp" #include "game/components/celestial-body-component.hpp" #include "game/components/orbit-component.hpp" -#include +#include /** @@ -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 ray) const; + double3 integrate_transmittance(const ::observer_component& observer, const ::celestial_body_component& body, const ::atmosphere_component& atmosphere, geom::ray ray) const; /// Time since epoch, in days. double time_days; diff --git a/src/game/systems/collision-system.cpp b/src/game/systems/collision-system.cpp index 88dcfb7..3b0da65 100644 --- a/src/game/systems/collision-system.cpp +++ b/src/game/systems/collision-system.cpp @@ -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 +#include #include #include #include @@ -31,16 +30,10 @@ collision_system::collision_system(entity::registry& registry): updatable_system(registry) { - registry.on_construct().connect<&collision_system::on_collision_construct>(this); - registry.on_update().connect<&collision_system::on_collision_update>(this); - registry.on_destroy().connect<&collision_system::on_collision_destroy>(this); } collision_system::~collision_system() { - registry.on_construct().disconnect<&collision_system::on_collision_construct>(this); - registry.on_update().disconnect<&collision_system::on_collision_update>(this); - registry.on_destroy().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& ray, std::uint32_t flags) const +entity::id collision_system::pick_nearest(const geom::ray& ray, std::uint32_t flags) const { entity::id nearest_eid = entt::null; float nearest_distance = std::numeric_limits::infinity(); @@ -63,14 +56,14 @@ entity::id collision_system::pick_nearest(const geom::primitive::ray& return; // Transform picking sphere - const geom::primitive::sphere sphere = + const geom::sphere 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& origin, const math::vector& normal, std::uint32_t flags) const { entity::id nearest_eid = entt::null; float nearest_sqr_distance = std::numeric_limits::infinity(); // Construct picking plane - const geom::primitive::plane picking_plane = geom::primitive::plane(origin, normal); + const geom::plane picking_plane = geom::plane(origin, normal); // For each entity with picking and transform components registry.view().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 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) -{} - diff --git a/src/game/systems/collision-system.hpp b/src/game/systems/collision-system.hpp index 44d9d28..f216120 100644 --- a/src/game/systems/collision-system.hpp +++ b/src/game/systems/collision-system.hpp @@ -22,7 +22,6 @@ #include "game/systems/updatable-system.hpp" #include -#include "game/components/collision-component.hpp" #include @@ -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& ray, std::uint32_t flags) const; + entity::id pick_nearest(const geom::ray& 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& origin, const math::vector& normal, std::uint32_t flags) const; }; diff --git a/src/game/systems/physics-system.cpp b/src/game/systems/physics-system.cpp index a054af3..fa952f0 100644 --- a/src/game/systems/physics-system.cpp +++ b/src/game/systems/physics-system.cpp @@ -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" diff --git a/src/game/systems/render-system.cpp b/src/game/systems/render-system.cpp index 4a35758..8422e51 100644 --- a/src/game/systems/render-system.cpp +++ b/src/game/systems/render-system.cpp @@ -19,12 +19,13 @@ #include "game/systems/render-system.hpp" #include "game/components/transform-component.hpp" +#include "game/components/rigid-body-component.hpp" #include #include render_system::render_system(entity::registry& registry): updatable_system(registry), - updated_scene_transforms(registry, entt::collector.update().where()), + updated_scene_transforms(registry, entt::collector.update().where(entt::exclude)), t(0.0), dt(0.0), renderer(nullptr) diff --git a/src/game/systems/subterrain-system.cpp b/src/game/systems/subterrain-system.cpp index 2aebcf3..f3c6f65 100644 --- a/src/game/systems/subterrain-system.cpp +++ b/src/game/systems/subterrain-system.cpp @@ -34,158 +34,6 @@ #include #include - -/** - * An octree containing cubes for the marching cubes algorithm. - */ -struct cube_tree -{ -public: - cube_tree(const geom::aabb& bounds, int max_depth); - ~cube_tree(); - - const bool is_leaf() const; - const geom::aabb& get_bounds() const; - - /// Subdivides all nodes intersecting with a region to the max depth. - void subdivide_max(const geom::aabb& region); - - /// Fills a list with all leaf nodes that intersect with a region. - void query_leaves(std::list& nodes, const geom::aabb& region); - void visit_leaves(const geom::aabb& region, const std::function& 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 bounds; - -private: - cube_tree(const geom::aabb& bounds, int max_depth, int depth); - void subdivide(); -}; - -cube_tree::cube_tree(const geom::aabb& bounds, int max_depth): - cube_tree(bounds, max_depth, 0) -{} - -cube_tree::cube_tree(const geom::aabb& 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::infinity(); - - // For outside normals - //distances[i] = std::numeric_limits::infinity(); - } -} - -cube_tree::~cube_tree() -{ - for (cube_tree* child: children) - delete child; -} - -void cube_tree::subdivide_max(const geom::aabb& 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& nodes, const geom::aabb& 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& region, const std::function& 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& 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 child_bounds; - for (int j = 0; j < 3; ++j) - { - child_bounds.min_point[j] = std::min(corners[i][j], center[j]); - child_bounds.max_point[j] = std::max(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& bounds = node->get_bounds(); + const geom::box& 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 region = {position, position}; + geom::box 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 diff --git a/src/game/systems/subterrain-system.hpp b/src/game/systems/subterrain-system.hpp index 13edf62..404a37f 100644 --- a/src/game/systems/subterrain-system.hpp +++ b/src/game/systems/subterrain-system.hpp @@ -22,7 +22,6 @@ #include "game/systems/updatable-system.hpp" #include -#include #include #include #include @@ -107,7 +106,7 @@ private: ::render::model_group* subterrain_outside_group; int subterrain_model_vertex_size; int subterrain_model_vertex_stride; - geom::aabb subterrain_bounds; + geom::box subterrain_bounds; cube_tree* cube_tree; std::vector subterrain_vertices; std::vector> subterrain_triangles; diff --git a/src/game/systems/terrain-system.cpp b/src/game/systems/terrain-system.cpp index f821473..48529e8 100644 --- a/src/game/systems/terrain-system.cpp +++ b/src/game/systems/terrain-system.cpp @@ -329,20 +329,20 @@ std::unique_ptr terrain_system::generate_patch_mesh(quadtree_node_ty const float cell_size = patch_size / static_cast(patch_subdivisions + 1); // Init patch bounds - geom::aabb patch_bounds; - patch_bounds.min_point.x() = patch_center.x() - patch_size * 0.5f; - patch_bounds.min_point.y() = std::numeric_limits::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::infinity(); - patch_bounds.max_point.z() = patch_center.z() + patch_size * 0.5f; + geom::box patch_bounds; + patch_bounds.min.x() = patch_center.x() - patch_size * 0.5f; + patch_bounds.min.y() = std::numeric_limits::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::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}; diff --git a/src/game/systems/terrain-system.hpp b/src/game/systems/terrain-system.hpp index 6761f3a..62fa178 100644 --- a/src/game/systems/terrain-system.hpp +++ b/src/game/systems/terrain-system.hpp @@ -31,7 +31,6 @@ #include #include #include -#include #include #include