#ifndef O3D_POLYGON_HPP #define O3D_POLYGON_HPP #include #include #include #include #include "math/vector.hpp" #include "math/utils.hpp" #include "o3d/deriv_vertex.hpp" #include "o3d/tri_deriv.hpp" namespace engine::o3d::polygon { template concept PolygonVectorTypeConcept = engine::math::VectorTypeConcept && (std::is_same_v || std::is_same_v); template struct Polygon; class ToPolygon { public: constexpr ToPolygon() {} }; template constexpr Polygon operator|(const engine::o3d::TriangleDerived& triangle_derived, ToPolygon); template class ClipAligned { public: constexpr ClipAligned(float boundary) : m_boundary { boundary } {} constexpr float boundary() const & { return m_boundary; } private: float m_boundary; }; template constexpr Polygon operator|(const Polygon& polygon, ClipAligned clip_aligned); class DivByW { public: constexpr DivByW() {} }; class SignedAreaXY { public: constexpr SignedAreaXY() {} }; template struct Polygon { std::size_t points_count; std::array, max_points_count> points; constexpr Polygon() : points_count { 0 } {} constexpr Polygon(std::size_t points_count, std::array, max_points_count> points) : points_count { points_count }, points { points } {} constexpr Polygon clip_z(float z1, float z2) const & { return *this | ClipAligned(z1) | ClipAligned(z2) ; } constexpr Polygon clip_xy(float x1, float y1, float x2, float y2) const & { return *this | ClipAligned(x1) | ClipAligned(y1) | ClipAligned(x2) | ClipAligned(y2) ; } constexpr Polygon map_xy( const engine::math::Vector2& from1, const engine::math::Vector2& from2, const engine::math::Vector2& to1, const engine::math::Vector2& to2) const & { Polygon polygon; polygon.points_count = points_count; for (std::size_t i = 0; i < points_count; i++) { auto& p = polygon.points[i]; if constexpr (std::is_same_v) { p.vertex = { points[i].vertex.xy().map(from1, from2, to1, to2), points[i].vertex.z, }; } else { p.vertex = { points[i].vertex.xy().map(from1, from2, to1, to2), points[i].vertex.z, points[i].vertex.w, }; } p.b0 = points[i].b0; p.b1 = points[i].b1; } return polygon; } constexpr std::tuple, max_points_count - 2>> to_triangles() const & requires (max_points_count >= 3) { std::array, max_points_count - 2> triangles; if (points_count < 3) return { 0, triangles }; for (std::size_t i = 0; i < points_count - 2; i++) triangles[i] = { points[0], points[i + 1], points[i + 2] }; return { points_count - 2, triangles }; } constexpr std::tuple, 0>> to_triangles() const & { return { 0, {} }; } }; template constexpr Polygon operator|(const engine::o3d::TriangleDerived& triangle_derived, ToPolygon) { return { 3, { triangle_derived.derived_vertex1, triangle_derived.derived_vertex2, triangle_derived.derived_vertex3 } }; } template constexpr Polygon operator|(const Polygon& polygon, ClipAligned clip_aligned) { using transposition = engine::math::vector_coords::transpose; constexpr auto y_coord_id = transposition::template id(); constexpr auto z_coord_id = transposition::template id(); constexpr auto is_in = [](const engine::o3d::DerivedVertex& p, float boundary) constexpr { if constexpr (keep_geq) return p.vertex.template v() >= boundary; else return p.vertex.template v() <= boundary; }; Polygon new_polygon; for (size_t i = 0; i < polygon.points_count; i++) { const auto& prev = polygon.points[(i + polygon.points_count - 1) % polygon.points_count]; const auto& cur = polygon.points[i]; if (is_in(prev, clip_aligned.boundary()) != is_in(cur, clip_aligned.boundary())) { float fac = (clip_aligned.boundary() - prev.vertex.template v()) / (cur.vertex.template v() - prev.vertex.template v()); auto& new_point = new_polygon.points[new_polygon.points_count++]; new_point.vertex.template v() = clip_aligned.boundary(); new_point.vertex.template v() = engine::math::utils::lerp(prev.vertex.template v(), cur.vertex.template v(), fac); if constexpr (std::is_same_v) { // called new_w because it represents w, but because we only need x, y and w, we use // Vector3, and therefore the vector coordinate's name is z float new_w = 1.f / ((1.f - fac) / prev.vertex.z + fac / cur.vertex.z); new_point.vertex.template v() = new_w; new_point.b0 = new_w * engine::math::utils::lerp(prev.b0 / prev.vertex.z, cur.b0 / cur.vertex.z, fac); new_point.b1 = new_w * engine::math::utils::lerp(prev.b1 / prev.vertex.z, cur.b1 / cur.vertex.z, fac); } else { // Vector4 constexpr auto w_coord_id = transposition::template id(); new_point.vertex.template v() = engine::math::utils::lerp(prev.vertex.template v(), cur.vertex.template v(), fac); new_point.vertex.template v() = engine::math::utils::lerp(prev.vertex.template v(), cur.vertex.template v(), fac); new_point.b0 = engine::math::utils::lerp(prev.b0, cur.b0, fac); new_point.b1 = engine::math::utils::lerp(prev.b1, cur.b1, fac); } } if (is_in(cur, clip_aligned.boundary())) new_polygon.points[new_polygon.points_count++] = cur; } return new_polygon; } template constexpr Polygon operator|(const Polygon& polygon, DivByW) { Polygon new_polygon; new_polygon.points_count = polygon.points_count; for (std::size_t i = 0; i < polygon.points_count; i++) new_polygon.points[i] = polygon.points[i].div_by_w(); return new_polygon; } template constexpr float operator|(const Polygon& polygon, SignedAreaXY) { float res = 0.f; if (polygon.points_count > 0) { for (std::size_t i = 0; i < polygon.points_count - 1; i++) res += polygon.points[i].vertex.xy().det(polygon.points[i + 1].vertex.xy()); res += polygon.points[polygon.points_count - 1].vertex.xy().det(polygon.points[0].vertex.xy()); } return .5f * res; } } #endif // O3D_POLYGON_HPP