mirror of
https://github.com/bevyengine/bevy
synced 2024-11-24 21:53:07 +00:00
introduction of ConvexPolygon
and ConvexPolygonMeshBuilder
(#15544)
# Objective - As discussed on [Discord](https://discord.com/channels/691052431525675048/1203087353850364004/1285300659746246849), implement a `ConvexPolygon` 2D math primitive and associated mesh builder. - The original goal was to have a mesh builder for the simplest (i.e. convex) polygons. ## Solution - The `ConvexPolygon` is created from its vertices. - The convexity of the polygon is checked when created via `new()` by verifying that the winding order of all the triangles formed with adjacent vertices is the same. - The `ConvexPolygonMeshBuilder` uses an anchor vertex and goes through every adjacent pair of vertices in the polygon to form triangles that fill up the polygon. ## Testing - Tested locally with my own simple `ConvexPolygonMeshBuilder` usage.
This commit is contained in:
parent
b48f9e2a4b
commit
3d8e56f766
3 changed files with 114 additions and 3 deletions
|
@ -1,4 +1,5 @@
|
|||
use core::f32::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_3, PI};
|
||||
use thiserror::Error;
|
||||
|
||||
use super::{Measured2d, Primitive2d, WindingOrder};
|
||||
use crate::{
|
||||
|
@ -1603,6 +1604,67 @@ impl<const N: usize> Polygon<N> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A convex polygon with `N` vertices.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
|
||||
#[cfg_attr(
|
||||
all(feature = "serialize", feature = "bevy_reflect"),
|
||||
reflect(Serialize, Deserialize)
|
||||
)]
|
||||
pub struct ConvexPolygon<const N: usize> {
|
||||
/// The vertices of the [`ConvexPolygon`].
|
||||
#[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
|
||||
pub vertices: [Vec2; N],
|
||||
}
|
||||
impl<const N: usize> Primitive2d for ConvexPolygon<N> {}
|
||||
|
||||
/// An error that happens when creating a [`ConvexPolygon`].
|
||||
#[derive(Error, Debug, Clone)]
|
||||
pub enum ConvexPolygonError {
|
||||
/// The created polygon is not convex.
|
||||
#[error("The created polygon is not convex")]
|
||||
Concave,
|
||||
}
|
||||
|
||||
impl<const N: usize> ConvexPolygon<N> {
|
||||
fn triangle_winding_order(
|
||||
&self,
|
||||
a_index: usize,
|
||||
b_index: usize,
|
||||
c_index: usize,
|
||||
) -> WindingOrder {
|
||||
let a = self.vertices[a_index];
|
||||
let b = self.vertices[b_index];
|
||||
let c = self.vertices[c_index];
|
||||
Triangle2d::new(a, b, c).winding_order()
|
||||
}
|
||||
|
||||
/// Create a [`ConvexPolygon`] from its `vertices`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`ConvexPolygonError::Concave`] if the `vertices` do not form a convex polygon.
|
||||
pub fn new(vertices: [Vec2; N]) -> Result<Self, ConvexPolygonError> {
|
||||
let polygon = Self::new_unchecked(vertices);
|
||||
let ref_winding_order = polygon.triangle_winding_order(N - 1, 0, 1);
|
||||
for i in 1..N {
|
||||
let winding_order = polygon.triangle_winding_order(i - 1, i, (i + 1) % N);
|
||||
if winding_order != ref_winding_order {
|
||||
return Err(ConvexPolygonError::Concave);
|
||||
}
|
||||
}
|
||||
Ok(polygon)
|
||||
}
|
||||
|
||||
/// Create a [`ConvexPolygon`] from its `vertices`, without checks.
|
||||
/// Use this version only if you know that the `vertices` make up a convex polygon.
|
||||
#[inline(always)]
|
||||
pub fn new_unchecked(vertices: [Vec2; N]) -> Self {
|
||||
Self { vertices }
|
||||
}
|
||||
}
|
||||
|
||||
/// A polygon with a variable number of vertices, allocated on the heap
|
||||
/// in a `Box<[Vec2]>`.
|
||||
///
|
||||
|
|
|
@ -7,8 +7,8 @@ use super::{Extrudable, MeshBuilder, Meshable};
|
|||
use bevy_math::{
|
||||
ops,
|
||||
primitives::{
|
||||
Annulus, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Rectangle,
|
||||
RegularPolygon, Rhombus, Triangle2d, Triangle3d, WindingOrder,
|
||||
Annulus, Capsule2d, Circle, CircularSector, CircularSegment, ConvexPolygon, Ellipse,
|
||||
Rectangle, RegularPolygon, Rhombus, Triangle2d, Triangle3d, WindingOrder,
|
||||
},
|
||||
FloatExt, Vec2,
|
||||
};
|
||||
|
@ -398,6 +398,55 @@ impl From<CircularSegment> for Mesh {
|
|||
}
|
||||
}
|
||||
|
||||
/// A builder used for creating a [`Mesh`] with a [`ConvexPolygon`] shape.
|
||||
pub struct ConvexPolygonMeshBuilder<const N: usize> {
|
||||
pub vertices: [Vec2; N],
|
||||
}
|
||||
|
||||
impl<const N: usize> Meshable for ConvexPolygon<N> {
|
||||
type Output = ConvexPolygonMeshBuilder<N>;
|
||||
|
||||
fn mesh(&self) -> Self::Output {
|
||||
Self::Output {
|
||||
vertices: self.vertices,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> MeshBuilder for ConvexPolygonMeshBuilder<N> {
|
||||
fn build(&self) -> Mesh {
|
||||
let mut indices = Vec::with_capacity((N - 2) * 3);
|
||||
let mut positions = Vec::with_capacity(N);
|
||||
|
||||
for vertex in self.vertices {
|
||||
positions.push([vertex.x, vertex.y, 0.0]);
|
||||
}
|
||||
for i in 2..N as u32 {
|
||||
indices.extend_from_slice(&[0, i - 1, i]);
|
||||
}
|
||||
Mesh::new(
|
||||
PrimitiveTopology::TriangleList,
|
||||
RenderAssetUsages::default(),
|
||||
)
|
||||
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
|
||||
.with_inserted_indices(Indices::U32(indices))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Extrudable for ConvexPolygonMeshBuilder<N> {
|
||||
fn perimeter(&self) -> Vec<PerimeterSegment> {
|
||||
vec![PerimeterSegment::Flat {
|
||||
indices: (0..N as u32).chain([0]).collect(),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<ConvexPolygon<N>> for Mesh {
|
||||
fn from(polygon: ConvexPolygon<N>) -> Self {
|
||||
polygon.mesh().build()
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder used for creating a [`Mesh`] with a [`RegularPolygon`] shape.
|
||||
pub struct RegularPolygonMeshBuilder {
|
||||
circumradius: f32,
|
||||
|
|
|
@ -67,7 +67,7 @@ impl PerimeterSegment {
|
|||
}
|
||||
}
|
||||
|
||||
/// A trait for required for implementing `Meshable` for `Extrusion<T>`.
|
||||
/// A trait required for implementing `Meshable` for `Extrusion<T>`.
|
||||
///
|
||||
/// ## Warning
|
||||
///
|
||||
|
|
Loading…
Reference in a new issue