diff --git a/benches/benches/bevy_camera/primitives.rs b/benches/benches/bevy_camera/primitives.rs index 153fe9e7ca78e..90ee8f5d96b87 100644 --- a/benches/benches/bevy_camera/primitives.rs +++ b/benches/benches/bevy_camera/primitives.rs @@ -1,6 +1,6 @@ use bevy_camera::primitives::{Aabb, Frustum, Sphere}; use bevy_math::{ - primitives::{HalfSpace, ViewFrustum}, + camera::{HalfSpace, ViewFrustum}, Affine3A, Quat, Vec3, Vec3A, Vec4, }; use core::hint::black_box; diff --git a/crates/bevy_camera/src/primitives.rs b/crates/bevy_camera/src/primitives.rs index bb42023f9e5ab..bc97856791273 100644 --- a/crates/bevy_camera/src/primitives.rs +++ b/crates/bevy_camera/src/primitives.rs @@ -4,7 +4,7 @@ use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{component::Component, entity::EntityHashMap, reflect::ReflectComponent}; use bevy_math::{ bounding::{Aabb3d, BoundingVolume}, - primitives::{HalfSpace, ViewFrustum}, + camera::{HalfSpace, ViewFrustum}, Affine3A, Mat3A, Vec3, Vec3A, }; use bevy_mesh::{Mesh, VertexAttributeValues}; diff --git a/crates/bevy_camera/src/projection.rs b/crates/bevy_camera/src/projection.rs index 85d849d6557c7..cc073f11fedd0 100644 --- a/crates/bevy_camera/src/projection.rs +++ b/crates/bevy_camera/src/projection.rs @@ -4,9 +4,7 @@ use core::ops::{Deref, DerefMut}; use crate::{primitives::Frustum, visibility::VisibilitySystems}; use bevy_app::{App, Plugin, PostUpdate}; use bevy_ecs::prelude::*; -use bevy_math::{ - ops, primitives::ViewFrustum, proj, vec4, AspectRatio, Mat4, Rect, Vec2, Vec3A, Vec4, -}; +use bevy_math::{camera::ViewFrustum, ops, proj, vec4, AspectRatio, Mat4, Rect, Vec2, Vec3A, Vec4}; use bevy_reflect::{std_traits::ReflectDefault, Reflect, ReflectDeserialize, ReflectSerialize}; use bevy_transform::{components::GlobalTransform, TransformSystems}; use derive_more::derive::From; diff --git a/crates/bevy_gizmos/src/gizmos.rs b/crates/bevy_gizmos/src/gizmos.rs index 02f13609e3470..0588ec737d242 100644 --- a/crates/bevy_gizmos/src/gizmos.rs +++ b/crates/bevy_gizmos/src/gizmos.rs @@ -288,7 +288,9 @@ where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - pub(crate) enabled: bool, + // needs to be public for [`bevy_shapes`] + /// whether or not this buffer will be rendered + pub enabled: bool, /// The positions of line segment endpoints. pub list_positions: Vec, /// The colors of line segment endpoints. diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index 212f04a13a780..79f6c81f3731c 100755 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -33,7 +33,6 @@ pub mod frustum; pub mod gizmos; mod global; pub mod grid; -pub mod primitives; pub mod retained; pub mod rounded_box; mod simplex_stroke_font; @@ -65,7 +64,6 @@ pub mod prelude { }, gizmos::Gizmos, global::gizmo, - primitives::{dim2::GizmoPrimitive2d, dim3::GizmoPrimitive3d}, retained::Gizmo, AppGizmoBuilder, GizmoAsset, }; diff --git a/crates/bevy_gizmos_render/Cargo.toml b/crates/bevy_gizmos_render/Cargo.toml index 12c0e103446b6..7b233408cdf52 100644 --- a/crates/bevy_gizmos_render/Cargo.toml +++ b/crates/bevy_gizmos_render/Cargo.toml @@ -30,6 +30,7 @@ bevy_utils = { path = "../bevy_utils", version = "0.20.0-dev" } bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.20.0-dev" } bevy_transform = { path = "../bevy_transform", version = "0.20.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.20.0-dev" } +bevy_shapes = { path = "../bevy_shapes", version = "0.20.0-dev", features = [ "meshing" ] } bevy_color = { path = "../bevy_color", version = "0.20.0-dev" } bevy_material = { path = "../bevy_material", version = "0.20.0-dev" } bevy_log = { path = "../bevy_log", version = "0.20.0-dev" } diff --git a/crates/bevy_gizmos_render/src/transform_gizmo_render.rs b/crates/bevy_gizmos_render/src/transform_gizmo_render.rs index 51d474bce1c85..0154392749745 100644 --- a/crates/bevy_gizmos_render/src/transform_gizmo_render.rs +++ b/crates/bevy_gizmos_render/src/transform_gizmo_render.rs @@ -18,12 +18,10 @@ use bevy_ecs::{ schedule::IntoScheduleConfigs, system::{Commands, Query, Res, ResMut}, }; -use bevy_math::{ - primitives::{Cone, Cuboid, Cylinder, Torus}, - Quat, Vec3, -}; -use bevy_mesh::{Mesh, Mesh3d, MeshBuilder, Meshable}; +use bevy_math::{Quat, Vec3}; +use bevy_mesh::{Mesh, Mesh3d, Meshable}; use bevy_pbr::{MeshMaterial3d, StandardMaterial}; +use bevy_shapes::primitives::{Cone, Cuboid, Cylinder, Torus}; use bevy_transform::{ components::{GlobalTransform, Transform}, systems::propagate_transforms_for, @@ -167,36 +165,30 @@ fn spawn_gizmo_meshes( }; // Pre-create meshes - let shaft_mesh = meshes.add(Cylinder::new(SHAFT_RADIUS, SHAFT_LENGTH).mesh().build()); - let cone_mesh = meshes.add(Cone::new(CONE_RADIUS, CONE_HEIGHT).mesh().build()); - let scale_cube_mesh = meshes.add( - Cuboid::new(SCALE_CUBE_SIZE, SCALE_CUBE_SIZE, SCALE_CUBE_SIZE) - .mesh() - .build(), - ); + let shaft_mesh = meshes.add(Cylinder::new(SHAFT_RADIUS, SHAFT_LENGTH).mesh()); + let cone_mesh = meshes.add(Cone::new(CONE_RADIUS, CONE_HEIGHT).mesh()); + let scale_cube_mesh = + meshes.add(Cuboid::new(SCALE_CUBE_SIZE, SCALE_CUBE_SIZE, SCALE_CUBE_SIZE).mesh()); let rotate_torus_mesh = meshes.add( Torus { minor_radius: 0.015, major_radius: ROTATE_RING_RADIUS, } - .mesh() - .build(), + .mesh(), ); let view_circle_mesh = meshes.add( Torus { minor_radius: VIEW_CIRCLE_MINOR, major_radius: VIEW_CIRCLE_MAJOR, } - .mesh() - .build(), + .mesh(), ); let view_ring_mesh = meshes.add( Torus { minor_radius: VIEW_RING_MINOR, major_radius: VIEW_RING_MAJOR, } - .mesh() - .build(), + .mesh(), ); // Axis rotations: cylinder default is Y-up diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 70b9bc34bad59..285669868d54c 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -127,14 +127,17 @@ shader_format_wesl = ["bevy_shader/shader_format_wesl"] serialize = [ "bevy_a11y?/serialize", "bevy_color?/serialize", + "bevy_dev_tools?/serialize", "bevy_ecs/serialize", "bevy_image?/serialize", "bevy_input/serialize", "bevy_math/serialize", + "bevy_shapes/serialize", "bevy_world_serialization?/serialize", "bevy_time/serialize", "bevy_transform/serialize", "bevy_ui?/serialize", + "bevy_utils/serialize", "bevy_window?/serialize", "bevy_winit?/serialize", "bevy_platform/serialize", @@ -416,6 +419,7 @@ std = [ "bevy_input/std", "bevy_input_focus?/std", "bevy_math/std", + "bevy_shapes/std", "bevy_platform/std", "bevy_reflect/std", "bevy_state?/std", @@ -505,6 +509,10 @@ bevy_math = { path = "../bevy_math", version = "0.20.0-dev", default-features = "bevy_reflect", "nostd-libm", ] } +bevy_shapes = { path = "../bevy_shapes", version = "0.20.0-dev", default-features = false, features = [ + "bevy_reflect", + "nostd-libm", +] } bevy_platform = { path = "../bevy_platform", version = "0.20.0-dev", default-features = false, features = [ "alloc", ] } diff --git a/crates/bevy_internal/src/lib.rs b/crates/bevy_internal/src/lib.rs index bc278b9b4f125..1452f0342e242 100644 --- a/crates/bevy_internal/src/lib.rs +++ b/crates/bevy_internal/src/lib.rs @@ -84,6 +84,7 @@ pub use bevy_scene as scene; pub use bevy_settings as settings; #[cfg(feature = "bevy_shader")] pub use bevy_shader as shader; +pub use bevy_shapes as shapes; #[cfg(feature = "bevy_solari")] pub use bevy_solari as solari; #[cfg(feature = "bevy_sprite")] diff --git a/crates/bevy_internal/src/prelude.rs b/crates/bevy_internal/src/prelude.rs index 6b03a6adf9057..928d417697e35 100644 --- a/crates/bevy_internal/src/prelude.rs +++ b/crates/bevy_internal/src/prelude.rs @@ -114,3 +114,6 @@ pub use crate::picking::prelude::*; #[doc(hidden)] #[cfg(feature = "bevy_clipboard")] pub use crate::clipboard::prelude::*; + +#[doc(hidden)] +pub use crate::shapes::prelude::*; diff --git a/crates/bevy_light/Cargo.toml b/crates/bevy_light/Cargo.toml index f9e85c8b99dfd..5c92d85640503 100644 --- a/crates/bevy_light/Cargo.toml +++ b/crates/bevy_light/Cargo.toml @@ -26,10 +26,12 @@ bevy_color = { path = "../bevy_color", version = "0.20.0-dev", features = [ ] } bevy_gizmos = { path = "../bevy_gizmos", version = "0.20.0-dev", optional = true } bevy_log = { path = "../bevy_log", version = "0.20.0-dev" } +bevy_shapes = { path = "../bevy_shapes", version = "0.20.0-dev", optional = true } +bevy_gizmos = { path = "../bevy_gizmos", version = "0.20.0-dev", optional = true } # other tracing = { version = "0.1", default-features = false } -wgpu-types = { version = "29.0.3", default-features = false } +wgpu-types = { version = "30", default-features = false } half = "2.7.1" smallvec = { version = "1", default-features = false } @@ -38,6 +40,7 @@ default = [] experimental_pbr_pcss = [] webgl = [] webgpu = [] +bevy_gizmos = ["bevy_shapes?/gizmos", "dep:bevy_gizmos", "dep:bevy_shapes"] [lints] workspace = true diff --git a/crates/bevy_light/src/cluster/assign.rs b/crates/bevy_light/src/cluster/assign.rs index 8be51681ac313..cdb2f70490910 100644 --- a/crates/bevy_light/src/cluster/assign.rs +++ b/crates/bevy_light/src/cluster/assign.rs @@ -11,8 +11,8 @@ use bevy_ecs::{ system::{Local, Query, Res}, }; use bevy_math::{ + camera::HalfSpace, ops::{self, sin_cos}, - primitives::HalfSpace, Mat4, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles as _, Vec4, Vec4Swizzles as _, }; use bevy_transform::components::GlobalTransform; diff --git a/crates/bevy_light/src/directional_light.rs b/crates/bevy_light/src/directional_light.rs index e65a1e1b2024e..f703bdde515f3 100644 --- a/crates/bevy_light/src/directional_light.rs +++ b/crates/bevy_light/src/directional_light.rs @@ -7,7 +7,7 @@ use bevy_camera::{ use bevy_color::Color; use bevy_ecs::prelude::*; use bevy_image::Image; -use bevy_math::primitives::ViewFrustum; +use bevy_math::camera::ViewFrustum; use bevy_reflect::prelude::*; use bevy_transform::components::Transform; use tracing::warn; diff --git a/crates/bevy_light/src/gizmos.rs b/crates/bevy_light/src/gizmos.rs index 681082a9ea7f5..94d884252c26d 100644 --- a/crates/bevy_light/src/gizmos.rs +++ b/crates/bevy_light/src/gizmos.rs @@ -2,7 +2,8 @@ use core::f32::consts::PI; -use bevy_gizmos::primitives::dim3::GizmoPrimitive3d; +use bevy_shapes::gizmos::GizmoPrimitive3d; +use bevy_shapes::primitives::{Cone, Sphere}; use crate::{DirectionalLight, PointLight, RectLight, SpotLight}; use bevy_app::{Plugin, PostUpdate}; @@ -18,11 +19,7 @@ use bevy_ecs::{ schedule::IntoScheduleConfigs, system::{Query, Res}, }; -use bevy_math::{ - ops, - primitives::{Cone, Sphere}, - Isometry3d, Quat, Vec3, -}; +use bevy_math::{ops, Isometry3d, Quat, Vec3}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_transform::{components::GlobalTransform, TransformSystems}; diff --git a/crates/bevy_light/src/point_light.rs b/crates/bevy_light/src/point_light.rs index 940e124f0cf04..7e230f7fe8879 100644 --- a/crates/bevy_light/src/point_light.rs +++ b/crates/bevy_light/src/point_light.rs @@ -6,7 +6,7 @@ use bevy_camera::{ use bevy_color::Color; use bevy_ecs::prelude::*; use bevy_image::Image; -use bevy_math::{primitives::ViewFrustum, proj}; +use bevy_math::{camera::ViewFrustum, proj}; use bevy_reflect::prelude::*; use bevy_transform::components::{GlobalTransform, Transform}; diff --git a/crates/bevy_light/src/spot_light.rs b/crates/bevy_light/src/spot_light.rs index 0b23653928c02..9d0d04c83df88 100644 --- a/crates/bevy_light/src/spot_light.rs +++ b/crates/bevy_light/src/spot_light.rs @@ -6,7 +6,7 @@ use bevy_camera::{ use bevy_color::Color; use bevy_ecs::prelude::*; use bevy_image::Image; -use bevy_math::{primitives::ViewFrustum, proj, Affine3A, Dir3, Mat3, Mat4, Vec3}; +use bevy_math::{camera::ViewFrustum, proj, Affine3A, Dir3, Mat3, Mat4, Vec3}; use bevy_reflect::prelude::*; use bevy_transform::components::{GlobalTransform, Transform}; diff --git a/crates/bevy_math/src/bounding/bounded2d/mod.rs b/crates/bevy_math/src/bounding/bounded2d/mod.rs index 40920402ab2d9..6d88efae728dd 100644 --- a/crates/bevy_math/src/bounding/bounded2d/mod.rs +++ b/crates/bevy_math/src/bounding/bounded2d/mod.rs @@ -1,5 +1,3 @@ -mod primitive_impls; - use super::{BoundingVolume, IntersectsVolume}; use crate::{ ops, @@ -469,8 +467,6 @@ mod aabb2d_tests { } } -use crate::primitives::Circle; - /// A bounding circle #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr( @@ -486,8 +482,8 @@ use crate::primitives::Circle; pub struct BoundingCircle { /// The center of the bounding circle pub center: Vec2, - /// The circle - pub circle: Circle, + /// The radius of the circle + pub radius: f32, } impl BoundingCircle { @@ -495,10 +491,7 @@ impl BoundingCircle { #[inline] pub const fn new(center: Vec2, radius: f32) -> Self { debug_assert!(radius >= 0.); - Self { - center, - circle: Circle { radius }, - } + Self { center, radius } } /// Computes a [`BoundingCircle`] containing the given set of points, @@ -526,7 +519,7 @@ impl BoundingCircle { /// Get the radius of the bounding circle #[inline] pub const fn radius(&self) -> f32 { - self.circle.radius + self.radius } /// Computes the smallest [`Aabb2d`] containing this [`BoundingCircle`]. @@ -544,7 +537,20 @@ impl BoundingCircle { /// Otherwise, it will be inside the circle and returned as is. #[inline] pub fn closest_point(&self, point: Vec2) -> Vec2 { - self.circle.closest_point(point - self.center) + self.center + let centered_point = point - self.center; + let distance_squared = centered_point.length_squared(); + + let closest_point = if distance_squared <= self.radius.squared() { + // The point is inside the circle. + point + } else { + // The point is outside the circle. + // Find the closest point on the perimeter of the circle. + let dir_to_point = point / ops::sqrt(distance_squared); + self.radius * dir_to_point + }; + + closest_point + self.center } } diff --git a/crates/bevy_math/src/bounding/bounded3d/mod.rs b/crates/bevy_math/src/bounding/bounded3d/mod.rs index fa5aaf17b9f40..9d8233a624c99 100644 --- a/crates/bevy_math/src/bounding/bounded3d/mod.rs +++ b/crates/bevy_math/src/bounding/bounded3d/mod.rs @@ -1,12 +1,8 @@ -mod extrusion; -mod primitive_impls; - use glam::Mat3; use super::{BoundingVolume, IntersectsVolume}; use crate::{ ops::{self, FloatPow}, - primitives::Cuboid, Isometry3d, Quat, Vec3A, }; @@ -17,8 +13,6 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; -pub use extrusion::BoundedExtrusion; - /// Computes the geometric center of the given set of points. #[inline] fn point_cloud_3d_center(points: impl Iterator>) -> Vec3A { @@ -128,15 +122,6 @@ impl Aabb3d { } } -impl From for Aabb3d { - fn from(value: Cuboid) -> Self { - Aabb3d { - min: (-value.half_size).into(), - max: value.half_size.into(), - } - } -} - impl BoundingVolume for Aabb3d { type Translation = Vec3A; type Rotation = Quat; @@ -495,8 +480,6 @@ mod aabb3d_tests { } } -use crate::primitives::Sphere; - /// A bounding sphere #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr( @@ -513,7 +496,7 @@ pub struct BoundingSphere { /// The center of the bounding sphere pub center: Vec3A, /// The sphere - pub sphere: Sphere, + pub radius: f32, } impl BoundingSphere { @@ -522,7 +505,7 @@ impl BoundingSphere { debug_assert!(radius >= 0.); Self { center: center.into(), - sphere: Sphere { radius }, + radius, } } @@ -554,7 +537,7 @@ impl BoundingSphere { /// Get the radius of the bounding sphere #[inline] pub const fn radius(&self) -> f32 { - self.sphere.radius + self.radius } /// Computes the smallest [`Aabb3d`] containing this [`BoundingSphere`]. @@ -637,9 +620,7 @@ impl BoundingVolume for BoundingSphere { debug_assert!(amount >= 0.); Self { center: self.center, - sphere: Sphere { - radius: self.radius() + amount, - }, + radius: self.radius() + amount, } } @@ -650,9 +631,7 @@ impl BoundingVolume for BoundingSphere { debug_assert!(self.radius() >= amount); Self { center: self.center, - sphere: Sphere { - radius: self.radius() - amount, - }, + radius: self.radius() - amount, } } diff --git a/crates/bevy_math/src/bounding/raycast2d.rs b/crates/bevy_math/src/bounding/raycast2d.rs index aa8fcf2591dcc..875372daa9ecc 100644 --- a/crates/bevy_math/src/bounding/raycast2d.rs +++ b/crates/bevy_math/src/bounding/raycast2d.rs @@ -172,7 +172,7 @@ impl BoundingCircleCast { /// Get the distance at which the [`BoundingCircle`]s collide, if at all. pub fn circle_collision_at(&self, mut circle: BoundingCircle) -> Option { circle.center -= self.circle.center; - circle.circle.radius += self.circle.radius(); + circle.radius += self.circle.radius(); self.ray.circle_intersection_at(&circle) } } diff --git a/crates/bevy_math/src/bounding/raycast3d.rs b/crates/bevy_math/src/bounding/raycast3d.rs index ad21766c742e3..37fc602c7401b 100644 --- a/crates/bevy_math/src/bounding/raycast3d.rs +++ b/crates/bevy_math/src/bounding/raycast3d.rs @@ -183,7 +183,7 @@ impl BoundingSphereCast { /// Get the distance at which the [`BoundingSphere`]s collide, if at all. pub fn sphere_collision_at(&self, mut sphere: BoundingSphere) -> Option { sphere.center -= self.sphere.center; - sphere.sphere.radius += self.sphere.radius(); + sphere.radius += self.sphere.radius(); self.ray.sphere_intersection_at(&sphere) } } diff --git a/crates/bevy_math/src/primitives/half_space.rs b/crates/bevy_math/src/camera/half_space.rs similarity index 98% rename from crates/bevy_math/src/primitives/half_space.rs rename to crates/bevy_math/src/camera/half_space.rs index a955b8b0a6bb9..5c3bb8a440207 100644 --- a/crates/bevy_math/src/primitives/half_space.rs +++ b/crates/bevy_math/src/camera/half_space.rs @@ -27,7 +27,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; /// It includes all the points from the bisecting plane towards `NEG_Z`, and the distance /// from the plane to the origin is `-8.0` along `NEG_Z`. /// -/// It is used to define a [`ViewFrustum`](crate::primitives::ViewFrustum), +/// It is used to define a [`ViewFrustum`](crate::camera::ViewFrustum), /// but is also a useful mathematical primitive for rendering tasks such as light computation. #[derive(Clone, Copy, Debug, Default, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] @@ -109,7 +109,7 @@ mod half_space_tests { use approx::assert_relative_eq; use super::HalfSpace; - use crate::{Vec3, Vec4}; + use bevy_math::{Vec3, Vec4}; #[test] fn intersection_point() { diff --git a/crates/bevy_math/src/camera/mod.rs b/crates/bevy_math/src/camera/mod.rs new file mode 100644 index 0000000000000..e3ef8c5545ccb --- /dev/null +++ b/crates/bevy_math/src/camera/mod.rs @@ -0,0 +1,7 @@ +//! Camera related mathematical primitives like [`HalfSpace`]es and [`ViewFrustum`]s + +mod half_space; +pub use half_space::*; + +mod view_frustum; +pub use view_frustum::*; diff --git a/crates/bevy_math/src/primitives/view_frustum.rs b/crates/bevy_math/src/camera/view_frustum.rs similarity index 98% rename from crates/bevy_math/src/primitives/view_frustum.rs rename to crates/bevy_math/src/camera/view_frustum.rs index 604aee6f155aa..356bd3f2004cb 100644 --- a/crates/bevy_math/src/primitives/view_frustum.rs +++ b/crates/bevy_math/src/camera/view_frustum.rs @@ -1,4 +1,5 @@ -use crate::{primitives::HalfSpace, Mat4, Vec3, Vec4}; +use crate::camera::HalfSpace; +use crate::{Mat4, Vec3, Vec4}; #[cfg(feature = "bevy_reflect")] use bevy_reflect::{std_traits::ReflectDefault, Reflect}; @@ -114,7 +115,8 @@ mod view_frustum_tests { use approx::assert_relative_eq; use super::ViewFrustum; - use crate::{primitives::HalfSpace, Vec3, Vec4}; + use crate::camera::HalfSpace; + use bevy_math::{Vec3, Vec4}; #[test] fn cuboid_frustum_corners() { diff --git a/crates/bevy_math/src/direction.rs b/crates/bevy_math/src/direction.rs index 3ddbb5734f658..0cf777051618a 100644 --- a/crates/bevy_math/src/direction.rs +++ b/crates/bevy_math/src/direction.rs @@ -1,7 +1,4 @@ -use crate::{ - primitives::{Primitive2d, Primitive3d}, - Quat, Rot2, Vec2, Vec3, Vec3A, Vec4, -}; +use crate::{Quat, Rot2, Vec2, Vec3, Vec3A, Vec4}; use core::f32::consts::FRAC_1_SQRT_2; use core::fmt; @@ -94,7 +91,6 @@ fn assert_is_normalized(message: &str, length_squared: f32) { )] #[doc(alias = "Direction2d")] pub struct Dir2(Vec2); -impl Primitive2d for Dir2 {} impl Dir2 { /// A unit vector pointing along the positive X axis. @@ -405,7 +401,6 @@ impl approx::UlpsEq for Dir2 { )] #[doc(alias = "Direction3d")] pub struct Dir3(Vec3); -impl Primitive3d for Dir3 {} impl Dir3 { /// A unit vector pointing along the positive X axis. @@ -809,7 +804,6 @@ impl approx::UlpsEq for Dir3 { )] #[doc(alias = "Direction3dA")] pub struct Dir3A(Vec3A); -impl Primitive3d for Dir3A {} impl Dir3A { /// A unit vector pointing along the positive X axis. diff --git a/crates/bevy_math/src/lib.rs b/crates/bevy_math/src/lib.rs index 5495740c610d2..626db7d6c56d3 100644 --- a/crates/bevy_math/src/lib.rs +++ b/crates/bevy_math/src/lib.rs @@ -29,6 +29,7 @@ extern crate alloc; mod affine3; mod aspect_ratio; pub mod bounding; +pub mod camera; pub mod common_traits; mod compass; pub mod cubic_splines; @@ -37,7 +38,6 @@ mod float_ord; mod isometry; mod mat3; pub mod ops; -pub mod primitives; mod ray; mod rects; mod rotation2d; @@ -66,7 +66,7 @@ pub use rotation2d::Rot2; pub use curve::Curve; #[cfg(feature = "rand")] -pub use sampling::{FromRng, ShapeSample}; +pub use sampling::FromRng; /// The math prelude. /// @@ -74,15 +74,14 @@ pub use sampling::{FromRng, ShapeSample}; pub mod prelude { #[doc(hidden)] pub use crate::{ - bvec2, bvec3, bvec3a, bvec4, bvec4a, + bvec2, bvec3, bvec3a, bvec4, bvec4a, camera, cubic_splines::{CubicNurbsError, CubicSegment, RationalSegment}, direction::{Dir2, Dir3, Dir3A}, - ivec2, ivec3, ivec4, mat2, mat3, mat3a, mat4, ops, - primitives::*, - quat, uvec2, uvec3, uvec4, vec2, vec3, vec3a, vec4, BVec2, BVec3, BVec3A, BVec4, BVec4A, - EulerRot, FloatExt, IRect, IVec2, IVec3, IVec4, Isometry2d, Isometry3d, Mat2, Mat3, Mat3A, - Mat4, Quat, Ray2d, Ray3d, Rect, Rot2, StableInterpolate, URect, UVec2, UVec3, UVec4, Vec2, - Vec2Swizzles, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles, + ivec2, ivec3, ivec4, mat2, mat3, mat3a, mat4, ops, quat, uvec2, uvec3, uvec4, vec2, vec3, + vec3a, vec4, BVec2, BVec3, BVec3A, BVec4, BVec4A, EulerRot, FloatExt, IRect, IVec2, IVec3, + IVec4, Isometry2d, Isometry3d, Mat2, Mat3, Mat3A, Mat4, Quat, Ray2d, Ray3d, Rect, Rot2, + StableInterpolate, URect, UVec2, UVec3, UVec4, Vec2, Vec2Swizzles, Vec3, Vec3A, + Vec3Swizzles, Vec4, Vec4Swizzles, }; #[doc(hidden)] @@ -91,7 +90,7 @@ pub mod prelude { #[doc(hidden)] #[cfg(feature = "rand")] - pub use crate::sampling::{FromRng, ShapeSample}; + pub use crate::sampling::FromRng; #[cfg(feature = "alloc")] #[doc(hidden)] diff --git a/crates/bevy_math/src/ray.rs b/crates/bevy_math/src/ray.rs index 0240ba2a71353..34298e85bc958 100644 --- a/crates/bevy_math/src/ray.rs +++ b/crates/bevy_math/src/ray.rs @@ -1,8 +1,4 @@ -use crate::{ - ops, - primitives::{InfinitePlane3d, Plane2d}, - Dir2, Dir3, Vec2, Vec3, -}; +use crate::{ops, Dir2, Dir3, Vec2, Vec3}; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; @@ -45,10 +41,10 @@ impl Ray2d { /// /// Use [`Ray2d::plane_intersection_point`] to get the intersection point directly. #[inline] - pub fn intersect_plane(&self, plane_origin: Vec2, plane: Plane2d) -> Option { - let denominator = plane.normal.dot(*self.direction); + pub fn intersect_plane(&self, plane_origin: Vec2, plane_normal: Dir2) -> Option { + let denominator = plane_normal.dot(*self.direction); if ops::abs(denominator) > f32::EPSILON { - let distance = (plane_origin - self.origin).dot(*plane.normal) / denominator; + let distance = (plane_origin - self.origin).dot(*plane_normal) / denominator; if distance > f32::EPSILON { return Some(distance); } @@ -60,8 +56,8 @@ impl Ray2d { /// /// Calls [`Ray2d::get_point`] on the result of [`Ray2d::intersect_plane`]. #[inline] - pub fn plane_intersection_point(&self, plane_origin: Vec2, plane: Plane2d) -> Option { - self.intersect_plane(plane_origin, plane) + pub fn plane_intersection_point(&self, plane_origin: Vec2, plane_normal: Dir2) -> Option { + self.intersect_plane(plane_origin, plane_normal) .map(|distance| self.get_point(distance)) } } @@ -102,10 +98,10 @@ impl Ray3d { /// /// Use [`Ray3d::plane_intersection_point`] to get the intersection point directly. #[inline] - pub fn intersect_plane(&self, plane_origin: Vec3, plane: InfinitePlane3d) -> Option { - let denominator = plane.normal.dot(*self.direction); + pub fn intersect_plane(&self, plane_origin: Vec3, plane_normal: Dir3) -> Option { + let denominator = plane_normal.dot(*self.direction); if ops::abs(denominator) > f32::EPSILON { - let distance = (plane_origin - self.origin).dot(*plane.normal) / denominator; + let distance = (plane_origin - self.origin).dot(*plane_normal) / denominator; if distance > f32::EPSILON { return Some(distance); } @@ -117,12 +113,8 @@ impl Ray3d { /// /// Calls [`Ray3d::get_point`] on the result of [`Ray3d::intersect_plane`]. #[inline] - pub fn plane_intersection_point( - &self, - plane_origin: Vec3, - plane: InfinitePlane3d, - ) -> Option { - self.intersect_plane(plane_origin, plane) + pub fn plane_intersection_point(&self, plane_origin: Vec3, plane_normal: Dir3) -> Option { + self.intersect_plane(plane_origin, plane_normal) .map(|distance| self.get_point(distance)) } } @@ -136,38 +128,29 @@ mod tests { let ray = Ray2d::new(Vec2::ZERO, Dir2::Y); // Orthogonal, and test that an inverse plane_normal has the same result - assert_eq!( - ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::Y)), - Some(1.0) - ); - assert_eq!( - ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::NEG_Y)), - Some(1.0) - ); - assert!(ray - .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::Y)) - .is_none()); - assert!(ray - .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::NEG_Y)) - .is_none()); + assert_eq!(ray.intersect_plane(Vec2::Y, Dir2::Y), Some(1.0)); + assert_eq!(ray.intersect_plane(Vec2::Y, Dir2::NEG_Y), Some(1.0)); + assert!(ray.intersect_plane(Vec2::NEG_Y, Dir2::Y).is_none()); + assert!(ray.intersect_plane(Vec2::NEG_Y, Dir2::NEG_Y).is_none()); // Diagonal assert_eq!( - ray.intersect_plane(Vec2::Y, Plane2d::new(Vec2::ONE)), + ray.intersect_plane(Vec2::Y, Dir2::new(Vec2::ONE).unwrap()), Some(1.0) ); assert!(ray - .intersect_plane(Vec2::NEG_Y, Plane2d::new(Vec2::ONE)) + .intersect_plane(Vec2::NEG_Y, Dir2::new(Vec2::ONE).unwrap()) .is_none()); // Parallel - assert!(ray - .intersect_plane(Vec2::X, Plane2d::new(Vec2::X)) - .is_none()); + assert!(ray.intersect_plane(Vec2::X, Dir2::X).is_none()); // Parallel with simulated rounding error assert!(ray - .intersect_plane(Vec2::X, Plane2d::new(Vec2::X + Vec2::Y * f32::EPSILON)) + .intersect_plane( + Vec2::X, + Dir2::new(Vec2::X + Vec2::Y * f32::EPSILON).unwrap() + ) .is_none()); } @@ -176,40 +159,28 @@ mod tests { let ray = Ray3d::new(Vec3::ZERO, Dir3::Z); // Orthogonal, and test that an inverse plane_normal has the same result - assert_eq!( - ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::Z)), - Some(1.0) - ); - assert_eq!( - ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::NEG_Z)), - Some(1.0) - ); - assert!(ray - .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::Z)) - .is_none()); - assert!(ray - .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::NEG_Z)) - .is_none()); + assert_eq!(ray.intersect_plane(Vec3::Z, Dir3::Z), Some(1.0)); + assert_eq!(ray.intersect_plane(Vec3::Z, Dir3::NEG_Z), Some(1.0)); + assert!(ray.intersect_plane(Vec3::NEG_Z, Dir3::Z).is_none()); + assert!(ray.intersect_plane(Vec3::NEG_Z, Dir3::NEG_Z).is_none()); // Diagonal assert_eq!( - ray.intersect_plane(Vec3::Z, InfinitePlane3d::new(Vec3::ONE)), + ray.intersect_plane(Vec3::Z, Dir3::new(Vec3::ONE).unwrap()), Some(1.0) ); assert!(ray - .intersect_plane(Vec3::NEG_Z, InfinitePlane3d::new(Vec3::ONE)) + .intersect_plane(Vec3::NEG_Z, Dir3::new(Vec3::ONE).unwrap()) .is_none()); // Parallel - assert!(ray - .intersect_plane(Vec3::X, InfinitePlane3d::new(Vec3::X)) - .is_none()); + assert!(ray.intersect_plane(Vec3::X, Dir3::X).is_none()); // Parallel with simulated rounding error assert!(ray .intersect_plane( Vec3::X, - InfinitePlane3d::new(Vec3::X + Vec3::Z * f32::EPSILON) + Dir3::new(Vec3::X + Vec3::Z * f32::EPSILON).unwrap() ) .is_none()); } diff --git a/crates/bevy_math/src/sampling/mod.rs b/crates/bevy_math/src/sampling/mod.rs index 78db92588b1f6..ecbb44515e05e 100644 --- a/crates/bevy_math/src/sampling/mod.rs +++ b/crates/bevy_math/src/sampling/mod.rs @@ -2,12 +2,5 @@ //! //! To use this, the "rand" feature must be enabled. -#[cfg(feature = "alloc")] -pub mod mesh_sampling; -pub mod shape_sampling; pub mod standard; - -#[cfg(feature = "alloc")] -pub use mesh_sampling::*; -pub use shape_sampling::*; pub use standard::*; diff --git a/crates/bevy_math/src/sampling/standard.rs b/crates/bevy_math/src/sampling/standard.rs index fdf7d4cee781b..49446c13aac69 100644 --- a/crates/bevy_math/src/sampling/standard.rs +++ b/crates/bevy_math/src/sampling/standard.rs @@ -21,12 +21,10 @@ //! let many_random_directions: Vec = rng.sample_iter(StandardUniform).take(5).collect(); //! ``` -use core::f32::consts::TAU; +use core::f32::consts::{PI, TAU}; -use crate::{ - primitives::{Circle, Sphere}, - Dir2, Dir3, Dir3A, Quat, Rot2, ShapeSample, Vec3A, -}; +use crate::{ops, Dir2, Dir3, Dir3A, Quat, Rot2}; +use glam::{Vec2, Vec3}; use rand::{ distr::{Distribution, StandardUniform}, RngExt, @@ -53,23 +51,34 @@ where } } +fn sample_unit_circle(rng: &mut R) -> Vec2 { + let theta = rng.random_range(0.0..TAU); + let (sin, cos) = ops::sin_cos(theta); + Vec2::new(cos, sin) +} + impl Distribution for StandardUniform { #[inline] fn sample(&self, rng: &mut R) -> Dir2 { - let circle = Circle::new(1.0); - let vector = circle.sample_boundary(rng); - Dir2::new_unchecked(vector) + Dir2::new_unchecked(sample_unit_circle(rng)) } } impl FromRng for Dir2 {} +fn sample_unit_sphere(rng: &mut R) -> Vec3 { + let z = rng.random_range(-1f32..=1f32); + let (a_sin, a_cos) = ops::sin_cos(rng.random_range(-PI..=PI)); + let c = ops::sqrt(1f32 - z * z); + let x = a_sin * c; + let y = a_cos * c; + Vec3::new(x, y, z) +} + impl Distribution for StandardUniform { #[inline] fn sample(&self, rng: &mut R) -> Dir3 { - let sphere = Sphere::new(1.0); - let vector = sphere.sample_boundary(rng); - Dir3::new_unchecked(vector) + Dir3::new_unchecked(sample_unit_sphere(rng)) } } @@ -78,9 +87,7 @@ impl FromRng for Dir3 {} impl Distribution for StandardUniform { #[inline] fn sample(&self, rng: &mut R) -> Dir3A { - let sphere = Sphere::new(1.0); - let vector: Vec3A = sphere.sample_boundary(rng).into(); - Dir3A::new_unchecked(vector) + Dir3A::new_unchecked(sample_unit_sphere(rng).into()) } } diff --git a/crates/bevy_mesh/src/mesh.rs b/crates/bevy_mesh/src/mesh.rs index 1f14a8e1a25b3..f107e642ab5a3 100644 --- a/crates/bevy_mesh/src/mesh.rs +++ b/crates/bevy_mesh/src/mesh.rs @@ -17,7 +17,6 @@ use alloc::collections::BTreeMap; use bevy_asset::{Asset, RenderAssetUsages}; use bevy_math::{ bounding::{Aabb2d, Aabb3d}, - primitives::Triangle3d, *, }; use bevy_platform::collections::{hash_map, HashMap}; @@ -2537,17 +2536,15 @@ impl Mesh { /// /// [primitive topology]: PrimitiveTopology /// [triangles]: Triangle3d - pub fn triangles(&self) -> Result + '_, MeshTrianglesError> { + pub fn triangles(&self) -> Result + '_, MeshTrianglesError> { fn indices_to_triangle + Copy>( vertices: &[[f32; 3]], indices: &[T; 3], - ) -> Option { + ) -> Option<[Vec3; 3]> { let vert0 = Vec3::from(*vertices.get(indices[0].try_into().ok()?)?); let vert1 = Vec3::from(*vertices.get(indices[1].try_into().ok()?)?); let vert2 = Vec3::from(*vertices.get(indices[2].try_into().ok()?)?); - Some(Triangle3d { - vertices: [vert0, vert1, vert2], - }) + Some([vert0, vert1, vert2]) } let position_data = self.try_attribute(Mesh::ATTRIBUTE_POSITION)?; @@ -2677,6 +2674,160 @@ impl Mesh { } } +// implement common mesh constructors +impl Mesh { + /// Creates a rectangular quad mesh centered at the origin. + /// + /// The resulting mesh lies in the *XY* plane with normals pointing along the + /// positive *Z* axis. `half_size` specifies half of the quad's width and height, + /// so the full dimensions are `half_size * 2.0`. + /// + /// The mesh consists of four vertices and two triangles, and includes position, + /// normal, and UV attributes. + pub fn quad_mesh(half_size: Vec2) -> Self { + let [hw, hh] = half_size.to_array(); + let positions = alloc::vec![ + [hw, hh, 0.0], + [-hw, hh, 0.0], + [-hw, -hh, 0.0], + [hw, -hh, 0.0], + ]; + let normals = alloc::vec![[0.0, 0.0, 1.0]; 4]; + let uvs = alloc::vec![[1.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]]; + let indices = Indices::U32(alloc::vec![0, 1, 2, 0, 2, 3]); + + Mesh::new( + PrimitiveTopology::TriangleList, + RenderAssetUsages::default(), + ) + .with_inserted_indices(indices) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + } + + /// Creates a subdivided plane mesh. + /// + /// The plane is centered at the origin and has a total size of + /// `half_size * 2.0`. It is initially generated in the *XZ* plane and then + /// rotated so that its normal matches `normal`. + /// + /// `subdivisions_x` and `subdivisions_z` control the number of subdivisions + /// along each axis. A value of `0` produces a single quad. + pub fn plane_mesh( + normal: Dir3, + half_size: Vec2, + subdivisions_x: u32, + subdivisions_z: u32, + ) -> Self { + let z_vertex_count = subdivisions_z + 2; + let x_vertex_count = subdivisions_x + 2; + let num_vertices = (z_vertex_count * x_vertex_count) as usize; + let num_indices = ((z_vertex_count - 1) * (x_vertex_count - 1) * 6) as usize; + + let mut positions: Vec = Vec::with_capacity(num_vertices); + let mut normals: Vec<[f32; 3]> = Vec::with_capacity(num_vertices); + let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(num_vertices); + let mut indices: Vec = Vec::with_capacity(num_indices); + + let rotation = Quat::from_rotation_arc(Vec3::Y, *normal); + let size = half_size * 2.0; + + for z in 0..z_vertex_count { + for x in 0..x_vertex_count { + let tx = x as f32 / (x_vertex_count - 1) as f32; + let tz = z as f32 / (z_vertex_count - 1) as f32; + let pos = rotation * Vec3::new((-0.5 + tx) * size.x, 0.0, (-0.5 + tz) * size.y); + positions.push(pos); + normals.push(normal.to_array()); + uvs.push([tx, tz]); + } + } + + for z in 0..z_vertex_count - 1 { + for x in 0..x_vertex_count - 1 { + let quad = z * x_vertex_count + x; + indices.push(quad + x_vertex_count + 1); + indices.push(quad + 1); + indices.push(quad + x_vertex_count); + indices.push(quad); + indices.push(quad + x_vertex_count); + indices.push(quad + 1); + } + } + + Mesh::new( + PrimitiveTopology::TriangleList, + RenderAssetUsages::default(), + ) + .with_inserted_indices(Indices::U32(indices)) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + } + + pub fn cuboid_mesh(half_size: Vec3) -> Self { + let min = -half_size; + let max = half_size; + + // Suppose Y-up right hand, and camera look from +Z to -Z + let vertices = &[ + // Front + ([min.x, min.y, max.z], [0.0, 0.0, 1.0], [0.0, 0.0]), + ([max.x, min.y, max.z], [0.0, 0.0, 1.0], [1.0, 0.0]), + ([max.x, max.y, max.z], [0.0, 0.0, 1.0], [1.0, 1.0]), + ([min.x, max.y, max.z], [0.0, 0.0, 1.0], [0.0, 1.0]), + // Back + ([min.x, max.y, min.z], [0.0, 0.0, -1.0], [1.0, 0.0]), + ([max.x, max.y, min.z], [0.0, 0.0, -1.0], [0.0, 0.0]), + ([max.x, min.y, min.z], [0.0, 0.0, -1.0], [0.0, 1.0]), + ([min.x, min.y, min.z], [0.0, 0.0, -1.0], [1.0, 1.0]), + // Right + ([max.x, min.y, min.z], [1.0, 0.0, 0.0], [0.0, 0.0]), + ([max.x, max.y, min.z], [1.0, 0.0, 0.0], [1.0, 0.0]), + ([max.x, max.y, max.z], [1.0, 0.0, 0.0], [1.0, 1.0]), + ([max.x, min.y, max.z], [1.0, 0.0, 0.0], [0.0, 1.0]), + // Left + ([min.x, min.y, max.z], [-1.0, 0.0, 0.0], [1.0, 0.0]), + ([min.x, max.y, max.z], [-1.0, 0.0, 0.0], [0.0, 0.0]), + ([min.x, max.y, min.z], [-1.0, 0.0, 0.0], [0.0, 1.0]), + ([min.x, min.y, min.z], [-1.0, 0.0, 0.0], [1.0, 1.0]), + // Top + ([max.x, max.y, min.z], [0.0, 1.0, 0.0], [1.0, 0.0]), + ([min.x, max.y, min.z], [0.0, 1.0, 0.0], [0.0, 0.0]), + ([min.x, max.y, max.z], [0.0, 1.0, 0.0], [0.0, 1.0]), + ([max.x, max.y, max.z], [0.0, 1.0, 0.0], [1.0, 1.0]), + // Bottom + ([max.x, min.y, max.z], [0.0, -1.0, 0.0], [0.0, 0.0]), + ([min.x, min.y, max.z], [0.0, -1.0, 0.0], [1.0, 0.0]), + ([min.x, min.y, min.z], [0.0, -1.0, 0.0], [1.0, 1.0]), + ([max.x, min.y, min.z], [0.0, -1.0, 0.0], [0.0, 1.0]), + ]; + + let positions: Vec<_> = vertices.iter().map(|(p, _, _)| *p).collect(); + let normals: Vec<_> = vertices.iter().map(|(_, n, _)| *n).collect(); + let uvs: Vec<_> = vertices.iter().map(|(_, _, uv)| *uv).collect(); + + let indices = Indices::U32(alloc::vec![ + 0, 1, 2, 2, 3, 0, // front + 4, 5, 6, 6, 7, 4, // back + 8, 9, 10, 10, 11, 8, // right + 12, 13, 14, 14, 15, 12, // left + 16, 17, 18, 18, 19, 16, // top + 20, 21, 22, 22, 23, 20, // bottom + ]); + + Mesh::new( + PrimitiveTopology::TriangleList, + RenderAssetUsages::default(), + ) + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) + .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) + .with_inserted_indices(indices) + } +} + #[cfg(feature = "morph")] impl Mesh { /// Whether this mesh has morph targets. @@ -3049,7 +3200,6 @@ mod tests { use crate::{MeshAttributeCompressionFlags, MeshVertexAttribute, PrimitiveTopology}; use bevy_asset::RenderAssetUsages; use bevy_math::bounding::Aabb3d; - use bevy_math::primitives::Triangle3d; use bevy_math::{Vec3, Vec3A}; use bevy_transform::components::Transform; @@ -3340,22 +3490,18 @@ mod tests { mesh.insert_indices(Indices::U32(vec![0, 1, 2, 2, 3, 0])); assert_eq!( vec![ - Triangle3d { - vertices: [ - Vec3::new(0., 0., 0.), - Vec3::new(1., 0., 0.), - Vec3::new(1., 1., 0.), - ] - }, - Triangle3d { - vertices: [ - Vec3::new(1., 1., 0.), - Vec3::new(0., 1., 0.), - Vec3::new(0., 0., 0.), - ] - } + [ + Vec3::new(0., 0., 0.), + Vec3::new(1., 0., 0.), + Vec3::new(1., 1., 0.), + ], + [ + Vec3::new(1., 1., 0.), + Vec3::new(0., 1., 0.), + Vec3::new(0., 0., 0.), + ] ], - mesh.triangles().unwrap().collect::>() + mesh.triangles().unwrap().collect::>() ); } @@ -3387,20 +3533,12 @@ mod tests { mesh.insert_indices(Indices::U32(vec![0, 1, 2, 3, 4, 5])); assert_eq!( vec![ - Triangle3d { - vertices: [positions[0], positions[1], positions[2]] - }, - Triangle3d { - vertices: [positions[2], positions[1], positions[3]] - }, - Triangle3d { - vertices: [positions[2], positions[3], positions[4]] - }, - Triangle3d { - vertices: [positions[4], positions[3], positions[5]] - }, + [positions[0], positions[1], positions[2]], + [positions[2], positions[1], positions[3]], + [positions[2], positions[3], positions[4]], + [positions[4], positions[3], positions[5]] ], - mesh.triangles().unwrap().collect::>() + mesh.triangles().unwrap().collect::>() ); } diff --git a/crates/bevy_mesh/src/primitives/dim3/cuboid.rs b/crates/bevy_mesh/src/primitives/dim3/cuboid.rs deleted file mode 100644 index 40a7cd45d4433..0000000000000 --- a/crates/bevy_mesh/src/primitives/dim3/cuboid.rs +++ /dev/null @@ -1,99 +0,0 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; -use bevy_asset::RenderAssetUsages; -use bevy_math::{primitives::Cuboid, Vec3}; -use bevy_reflect::prelude::*; - -/// A builder used for creating a [`Mesh`] with a [`Cuboid`] shape. -#[derive(Clone, Copy, Debug, Reflect)] -#[reflect(Default, Debug, Clone)] -pub struct CuboidMeshBuilder { - half_size: Vec3, -} - -impl Default for CuboidMeshBuilder { - /// Returns the default [`CuboidMeshBuilder`] with a width, height, and depth of `1.0`. - fn default() -> Self { - Self { - half_size: Vec3::splat(0.5), - } - } -} - -impl MeshBuilder for CuboidMeshBuilder { - fn build(&self) -> Mesh { - let min = -self.half_size; - let max = self.half_size; - - // Suppose Y-up right hand, and camera look from +Z to -Z - let vertices = &[ - // Front - ([min.x, min.y, max.z], [0.0, 0.0, 1.0], [0.0, 0.0]), - ([max.x, min.y, max.z], [0.0, 0.0, 1.0], [1.0, 0.0]), - ([max.x, max.y, max.z], [0.0, 0.0, 1.0], [1.0, 1.0]), - ([min.x, max.y, max.z], [0.0, 0.0, 1.0], [0.0, 1.0]), - // Back - ([min.x, max.y, min.z], [0.0, 0.0, -1.0], [1.0, 0.0]), - ([max.x, max.y, min.z], [0.0, 0.0, -1.0], [0.0, 0.0]), - ([max.x, min.y, min.z], [0.0, 0.0, -1.0], [0.0, 1.0]), - ([min.x, min.y, min.z], [0.0, 0.0, -1.0], [1.0, 1.0]), - // Right - ([max.x, min.y, min.z], [1.0, 0.0, 0.0], [0.0, 0.0]), - ([max.x, max.y, min.z], [1.0, 0.0, 0.0], [1.0, 0.0]), - ([max.x, max.y, max.z], [1.0, 0.0, 0.0], [1.0, 1.0]), - ([max.x, min.y, max.z], [1.0, 0.0, 0.0], [0.0, 1.0]), - // Left - ([min.x, min.y, max.z], [-1.0, 0.0, 0.0], [1.0, 0.0]), - ([min.x, max.y, max.z], [-1.0, 0.0, 0.0], [0.0, 0.0]), - ([min.x, max.y, min.z], [-1.0, 0.0, 0.0], [0.0, 1.0]), - ([min.x, min.y, min.z], [-1.0, 0.0, 0.0], [1.0, 1.0]), - // Top - ([max.x, max.y, min.z], [0.0, 1.0, 0.0], [1.0, 0.0]), - ([min.x, max.y, min.z], [0.0, 1.0, 0.0], [0.0, 0.0]), - ([min.x, max.y, max.z], [0.0, 1.0, 0.0], [0.0, 1.0]), - ([max.x, max.y, max.z], [0.0, 1.0, 0.0], [1.0, 1.0]), - // Bottom - ([max.x, min.y, max.z], [0.0, -1.0, 0.0], [0.0, 0.0]), - ([min.x, min.y, max.z], [0.0, -1.0, 0.0], [1.0, 0.0]), - ([min.x, min.y, min.z], [0.0, -1.0, 0.0], [1.0, 1.0]), - ([max.x, min.y, min.z], [0.0, -1.0, 0.0], [0.0, 1.0]), - ]; - - let positions: Vec<_> = vertices.iter().map(|(p, _, _)| *p).collect(); - let normals: Vec<_> = vertices.iter().map(|(_, n, _)| *n).collect(); - let uvs: Vec<_> = vertices.iter().map(|(_, _, uv)| *uv).collect(); - - let indices = Indices::U32(vec![ - 0, 1, 2, 2, 3, 0, // front - 4, 5, 6, 6, 7, 4, // back - 8, 9, 10, 10, 11, 8, // right - 12, 13, 14, 14, 15, 12, // left - 16, 17, 18, 18, 19, 16, // top - 20, 21, 22, 22, 23, 20, // bottom - ]); - - Mesh::new( - PrimitiveTopology::TriangleList, - RenderAssetUsages::default(), - ) - .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) - .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) - .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) - .with_inserted_indices(indices) - } -} - -impl Meshable for Cuboid { - type Output = CuboidMeshBuilder; - - fn mesh(&self) -> Self::Output { - CuboidMeshBuilder { - half_size: self.half_size, - } - } -} - -impl From for Mesh { - fn from(cuboid: Cuboid) -> Self { - cuboid.mesh().build() - } -} diff --git a/crates/bevy_mesh/src/primitives/mod.rs b/crates/bevy_mesh/src/primitives/mod.rs index 02aa0f1caf41e..be2096304a338 100644 --- a/crates/bevy_mesh/src/primitives/mod.rs +++ b/crates/bevy_mesh/src/primitives/mod.rs @@ -1,13 +1,14 @@ -//! Mesh generation for [primitive shapes](bevy_math::primitives). +//! Mesh generation traits. //! -//! Primitives that support meshing implement the [`Meshable`] trait. -//! Calling [`mesh`](Meshable::mesh) will return either a [`Mesh`] or a builder -//! that can be used to specify shape-specific configuration for creating the [`Mesh`]. +//! Anything that support meshing implement the [`Meshable`] trait. +//! Calling [`mesh`](Meshable::mesh) will return a [`Mesh`] while calling +//! [`mesh_builder`](Meshable::mesh_builder) returns a builder that can be used to specify +//! shape-specific configuration for creating the [`Mesh`]. //! //! ``` //! # use bevy_asset::Assets; //! # use bevy_ecs::prelude::ResMut; -//! # use bevy_math::prelude::Circle; +//! # use bevy_shapes::prelude::Circle; //! # use bevy_mesh::*; //! # //! # fn setup(mut meshes: ResMut>) { @@ -15,19 +16,10 @@ //! let circle = meshes.add(Circle { radius: 25.0 }); //! //! // Specify number of vertices -//! let circle = meshes.add(Circle { radius: 25.0 }.mesh().resolution(64)); +//! let circle = meshes.add(Circle { radius: 25.0 }.mesh_builder().resolution(64)); //! # } //! ``` -mod dim2; -pub use dim2::*; - -mod dim3; -pub use dim3::*; - -mod extrusion; -pub use extrusion::*; - use super::Mesh; /// A trait for shapes that can be turned into a [`Mesh`]. @@ -35,8 +27,13 @@ pub trait Meshable { /// The output of [`Self::mesh`]. This will be a [`MeshBuilder`] used for creating a [`Mesh`]. type Output: MeshBuilder; + /// Creates a [`MeshBuilder`] for a shape. + fn mesh_builder(&self) -> Self::Output; + /// Creates a [`Mesh`] for a shape. - fn mesh(&self) -> Self::Output; + fn mesh(&self) -> Mesh { + self.mesh_builder().build() + } } /// A trait used to build [`Mesh`]es from a configuration diff --git a/crates/bevy_pbr/src/decal/forward.rs b/crates/bevy_pbr/src/decal/forward.rs index f00c667d6d2ca..1e07a375db598 100644 --- a/crates/bevy_pbr/src/decal/forward.rs +++ b/crates/bevy_pbr/src/decal/forward.rs @@ -7,8 +7,8 @@ use bevy_asset::{Asset, Assets, Handle}; use bevy_ecs::{ component::Component, lifecycle::HookContext, resource::Resource, world::DeferredWorld, }; -use bevy_math::{prelude::Rectangle, Quat, Vec2, Vec3}; -use bevy_mesh::{Mesh, Mesh3d, MeshBuilder, MeshVertexBufferLayoutRef, Meshable}; +use bevy_math::{Quat, Vec2, Vec3}; +use bevy_mesh::{Mesh, Mesh3d, MeshVertexBufferLayoutRef}; use bevy_reflect::{Reflect, TypePath}; use bevy_render::{ render_asset::RenderAssets, @@ -28,14 +28,12 @@ impl Plugin for ForwardDecalPlugin { fn build(&self, app: &mut App) { load_shader_library!(app, "forward_decal.wgsl"); - let mesh = app.world_mut().resource_mut::>().add( - Rectangle::from_size(Vec2::ONE) - .mesh() - .build() + let mesh = app.world_mut().resource_mut::>().add({ + Mesh::quad_mesh(Vec2::ONE) .rotated_by(Quat::from_rotation_arc(Vec3::Z, Vec3::Y)) .with_generated_tangents() - .unwrap(), - ); + .unwrap() + }); app.insert_resource(ForwardDecalMesh(mesh)); diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 1f22f733e9e28..d20061bf4bb14 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -32,9 +32,8 @@ use bevy_material::{ MaterialProperties, }; use bevy_math::{ - ops, - primitives::{HalfSpace, ViewFrustum}, - proj, Mat4, UVec4, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles, + camera::{HalfSpace, ViewFrustum}, + ops, proj, Mat4, UVec4, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles, }; use bevy_mesh::{Mesh3d, MeshVertexBufferLayoutRef}; use bevy_platform::collections::{HashMap, HashSet}; diff --git a/crates/bevy_pbr/src/volumetric_fog/mod.rs b/crates/bevy_pbr/src/volumetric_fog/mod.rs index fc1f084c278ab..79548559375a7 100644 --- a/crates/bevy_pbr/src/volumetric_fog/mod.rs +++ b/crates/bevy_pbr/src/volumetric_fog/mod.rs @@ -37,11 +37,8 @@ use bevy_core_pipeline::{ }; use bevy_ecs::{resource::Resource, schedule::IntoScheduleConfigs as _}; use bevy_light::FogVolume; -use bevy_math::{ - primitives::{Cuboid, Plane3d}, - Vec2, Vec3, -}; -use bevy_mesh::{Mesh, Meshable}; +use bevy_math::{Dir3, Vec2, Vec3}; +use bevy_mesh::Mesh; use bevy_render::{ render_resource::SpecializedRenderPipelines, sync_component::{SyncComponent, SyncComponentPlugin}, @@ -67,8 +64,8 @@ impl Plugin for VolumetricFogPlugin { embedded_asset!(app, "volumetric_fog.wgsl"); let mut meshes = app.world_mut().resource_mut::>(); - let plane_mesh = meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE).mesh()); - let cube_mesh = meshes.add(Cuboid::new(1.0, 1.0, 1.0).mesh()); + let plane_mesh = meshes.add(Mesh::plane_mesh(Dir3::Z, Vec2::ONE, 0, 0)); + let cube_mesh = meshes.add(Mesh::cuboid_mesh(Vec3::ONE)); app.add_plugins(SyncComponentPlugin::::default()); diff --git a/crates/bevy_shapes/Cargo.toml b/crates/bevy_shapes/Cargo.toml new file mode 100644 index 0000000000000..dc7d674b39750 --- /dev/null +++ b/crates/bevy_shapes/Cargo.toml @@ -0,0 +1,83 @@ +[package] +description = "Provides primitive shapes for Bevy Engine" +edition = "2024" +homepage = "https://bevy.org" +keywords = ["bevy"] +license = "MIT OR Apache-2.0" +name = "bevy_shapes" +repository = "https://github.com/bevyengine/bevy" +rust-version = "1.94.0" +version = "0.20.0-dev" + +[dependencies] +approx = { default-features = false, optional = true, version = "0.5" } +arrayvec = { default-features = false, optional = true, version = "0.7" } +bevy_asset = { default-features = false, optional = true, path = "../bevy_asset" } +bevy_color = { default-features = false, optional = true, path = "../bevy_color" } +bevy_gizmos = { default-features = false, optional = true, path = "../bevy_gizmos" } +bevy_math = { default-features = false, path = "../bevy_math" } +bevy_mesh = { default-features = false, optional = true, path = "../bevy_mesh" } +bevy_reflect = { default-features = false, features = [ + "glam", +], optional = true, path = "../bevy_reflect", version = "0.20.0-dev" } +derive_more = { default-features = false, features = [ + "from", + "into", +], version = "2" } +hexasphere = { default-features = false, optional = true, version = "19.0.0" } +rand = { default-features = false, optional = true, version = "0.10" } +rand_distr = { optional = true, version = "0.6" } +serde = { default-features = false, features = [ + "derive", +], optional = true, version = "1" } +thiserror = { default-features = false, version = "2" } +wgpu-types = { default-features = false, optional = true, version = "29.0.3" } + +[dev-dependencies] +bevy_math = { default-features = false, features = [ + "approx", +], path = "../bevy_math" } +bevy_platform = { default-features = false, path = "../bevy_platform" } +chacha20 = { default-features = false, features = ["rng"], version = "0.10.0" } + +[features] +alloc = ["bevy_math/alloc"] +default = [ + "bevy_math/default", + "std", + # all extensions are available (opt-out) + "bounding", + "extrusion", + "gizmos", + "meshing", + "sampling", +] +std = ["alloc", "bevy_math/std"] + +approx = ["bevy_math/approx", "dep:approx"] +bevy_reflect = ["alloc", "bevy_math/bevy_reflect", "dep:bevy_reflect"] +serialize = ["bevy_math/serialize", "dep:serde"] + +bounding = ["dep:arrayvec"] +extrusion = ["meshing"] +gizmos = ["dep:bevy_color", "dep:bevy_gizmos"] +meshing = [ + "alloc", + "bevy_reflect", + "dep:bevy_asset", + "dep:bevy_mesh", + "dep:hexasphere", + "dep:wgpu-types", +] +sampling = ["bevy_math/rand", "dep:rand", "dep:rand_distr"] + +[lints] +workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--generate-link-to-definition", + "--generate-macro-expansion", + "-Zunstable-options", +] diff --git a/crates/bevy_shapes/LICENSE-APACHE b/crates/bevy_shapes/LICENSE-APACHE new file mode 100644 index 0000000000000..d9a10c0d8e868 --- /dev/null +++ b/crates/bevy_shapes/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/crates/bevy_shapes/LICENSE-MIT b/crates/bevy_shapes/LICENSE-MIT new file mode 100644 index 0000000000000..9cf106272ac3b --- /dev/null +++ b/crates/bevy_shapes/LICENSE-MIT @@ -0,0 +1,19 @@ +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/bevy_shapes/README.md b/crates/bevy_shapes/README.md new file mode 100644 index 0000000000000..b8ee9946c6811 --- /dev/null +++ b/crates/bevy_shapes/README.md @@ -0,0 +1,7 @@ +# Bevy Shapes + +[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/bevyengine/bevy#license) +[![Crates.io](https://img.shields.io/crates/v/bevy_shapes.svg)](https://crates.io/crates/bevy_shapes) +[![Downloads](https://img.shields.io/crates/d/bevy_shapes.svg)](https://crates.io/crates/bevy_shapes) +[![Docs](https://docs.rs/bevy_shapes/badge.svg)](https://docs.rs/bevy_shapes/latest/bevy_shapes/) +[![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy) diff --git a/crates/bevy_shapes/clippy.toml b/crates/bevy_shapes/clippy.toml new file mode 100644 index 0000000000000..c1f67e044d8ec --- /dev/null +++ b/crates/bevy_shapes/clippy.toml @@ -0,0 +1,39 @@ +disallowed-methods = [ + { path = "f32::powi", reason = "use ops::FloatPow::squared, ops::FloatPow::cubed, or ops::powf instead for libm determinism" }, + { path = "f32::log", reason = "use ops::ln, ops::log2, or ops::log10 instead for libm determinism" }, + { path = "f32::abs_sub", reason = "deprecated and deeply confusing method" }, + { path = "f32::powf", reason = "use ops::powf instead for libm determinism" }, + { path = "f32::exp", reason = "use ops::exp instead for libm determinism" }, + { path = "f32::exp2", reason = "use ops::exp2 instead for libm determinism" }, + { path = "f32::ln", reason = "use ops::ln instead for libm determinism" }, + { path = "f32::log2", reason = "use ops::log2 instead for libm determinism" }, + { path = "f32::log10", reason = "use ops::log10 instead for libm determinism" }, + { path = "f32::cbrt", reason = "use ops::cbrt instead for libm determinism" }, + { path = "f32::hypot", reason = "use ops::hypot instead for libm determinism" }, + { path = "f32::sin", reason = "use ops::sin instead for libm determinism" }, + { path = "f32::cos", reason = "use ops::cos instead for libm determinism" }, + { path = "f32::tan", reason = "use ops::tan instead for libm determinism" }, + { path = "f32::asin", reason = "use ops::asin instead for libm determinism" }, + { path = "f32::acos", reason = "use ops::acos instead for libm determinism" }, + { path = "f32::atan", reason = "use ops::atan instead for libm determinism" }, + { path = "f32::atan2", reason = "use ops::atan2 instead for libm determinism" }, + { path = "f32::sin_cos", reason = "use ops::sin_cos instead for libm determinism" }, + { path = "f32::exp_m1", reason = "use ops::exp_m1 instead for libm determinism" }, + { path = "f32::ln_1p", reason = "use ops::ln_1p instead for libm determinism" }, + { path = "f32::sinh", reason = "use ops::sinh instead for libm determinism" }, + { path = "f32::cosh", reason = "use ops::cosh instead for libm determinism" }, + { path = "f32::tanh", reason = "use ops::tanh instead for libm determinism" }, + { path = "f32::asinh", reason = "use ops::asinh instead for libm determinism" }, + { path = "f32::acosh", reason = "use ops::acosh instead for libm determinism" }, + { path = "f32::atanh", reason = "use ops::atanh instead for libm determinism" }, + # These methods have defined precision, but are only available from the standard library, + # not in core. Using these substitutes allows for no_std compatibility. + { path = "f32::rem_euclid", reason = "use ops::rem_euclid instead for no_std compatibility" }, + { path = "f32::abs", reason = "use ops::abs instead for no_std compatibility" }, + { path = "f32::sqrt", reason = "use ops::sqrt instead for no_std compatibility" }, + { path = "f32::copysign", reason = "use ops::copysign instead for no_std compatibility" }, + { path = "f32::round", reason = "use ops::round instead for no_std compatibility" }, + { path = "f32::floor", reason = "use ops::floor instead for no_std compatibility" }, + { path = "f32::ceil", reason = "use ops::ceil instead for no_std compatibility" }, + { path = "f32::fract", reason = "use ops::fract instead for no_std compatibility" }, +] diff --git a/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs b/crates/bevy_shapes/src/bounding/dim2.rs similarity index 98% rename from crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs rename to crates/bevy_shapes/src/bounding/dim2.rs index 8e45e645bfeb5..092596d3ae4ed 100644 --- a/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs +++ b/crates/bevy_shapes/src/bounding/dim2.rs @@ -1,13 +1,12 @@ //! Contains [`Bounded2d`] implementations for [geometric primitives](crate::primitives). -use crate::{ - bounding::BoundingVolume, - ops, - primitives::{ - Annulus, Arc2d, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Line2d, - Plane2d, Primitive2d, Rectangle, RegularPolygon, Rhombus, Ring, Segment2d, Triangle2d, - }, - Dir2, Isometry2d, Mat2, Rot2, Vec2, +use crate::primitives::{ + Annulus, Arc2d, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Line2d, Plane2d, + Primitive2d, Rectangle, RegularPolygon, Rhombus, Ring, Segment2d, Triangle2d, +}; +use bevy_math::{ + bounding::{Aabb2d, Bounded2d, BoundingCircle, BoundingVolume}, + ops, Dir2, Isometry2d, Mat2, Rot2, Vec2, }; use core::f32::consts::{FRAC_PI_2, PI, TAU}; @@ -16,8 +15,6 @@ use crate::primitives::{ConvexPolygon, Polygon, Polyline2d}; use arrayvec::ArrayVec; -use super::{Aabb2d, Bounded2d, BoundingCircle}; - impl Bounded2d for Circle { fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { let isometry = isometry.into(); @@ -445,17 +442,15 @@ mod tests { use std::println; use approx::assert_abs_diff_eq; - use glam::Vec2; - use crate::{ + use crate::primitives::{ + Annulus, Arc2d, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Line2d, + Plane2d, Polygon, Polyline2d, Rectangle, RegularPolygon, Rhombus, Segment2d, Triangle2d, + }; + use bevy_math::{ bounding::Bounded2d, ops::{self, FloatPow}, - primitives::{ - Annulus, Arc2d, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Line2d, - Plane2d, Polygon, Polyline2d, Rectangle, RegularPolygon, Rhombus, Segment2d, - Triangle2d, - }, - Dir2, Isometry2d, Rot2, + Dir2, Isometry2d, Rot2, Vec2, }; #[test] diff --git a/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs b/crates/bevy_shapes/src/bounding/dim3.rs similarity index 96% rename from crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs rename to crates/bevy_shapes/src/bounding/dim3.rs index 7cf118e4d97e8..72171c5f86838 100644 --- a/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs +++ b/crates/bevy_shapes/src/bounding/dim3.rs @@ -1,20 +1,17 @@ //! Contains [`Bounded3d`] implementations for [geometric primitives](crate::primitives). -use crate::{ - bounding::{Bounded2d, BoundingCircle, BoundingVolume}, - ops, - primitives::{ - Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, InfinitePlane3d, Line3d, Segment3d, - Sphere, Torus, Triangle2d, Triangle3d, - }, - Isometry2d, Isometry3d, Mat3, Vec2, Vec3, Vec3A, +use crate::primitives::{ + Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, InfinitePlane3d, Line3d, Segment3d, Sphere, + Torus, Triangle2d, Triangle3d, +}; +use bevy_math::{ + bounding::{Aabb3d, Bounded2d, Bounded3d, BoundingCircle, BoundingSphere, BoundingVolume}, + ops, Isometry2d, Isometry3d, Mat3, Vec2, Vec3, Vec3A, }; #[cfg(feature = "alloc")] use crate::primitives::Polyline3d; -use super::{Aabb3d, Bounded3d, BoundingSphere}; - impl Bounded3d for Sphere { fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { let isometry = isometry.into(); @@ -203,11 +200,11 @@ impl Bounded3d for Cone { // Because of circular symmetry, we can use the bounding circle of the triangle // for the bounding sphere of the cone. - let BoundingCircle { circle, center } = triangle.bounding_circle(Isometry2d::IDENTITY); + let BoundingCircle { radius, center } = triangle.bounding_circle(Isometry2d::IDENTITY); BoundingSphere::new( isometry.rotation * Vec3A::from(center.extend(0.0)) + isometry.translation, - circle.radius, + radius, ) } } @@ -366,16 +363,14 @@ impl Bounded3d for Triangle3d { #[cfg(test)] mod tests { - use crate::{bounding::BoundingVolume, ops, Isometry3d}; - use glam::{Quat, Vec3, Vec3A}; - - use crate::{ - bounding::Bounded3d, - primitives::{ - Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, InfinitePlane3d, Line3d, Polyline3d, - Segment3d, Sphere, Torus, Triangle3d, - }, - Dir3, + use bevy_math::{ + bounding::{Bounded3d, BoundingVolume}, + ops, Dir3, Isometry3d, Quat, Vec3, Vec3A, + }; + + use crate::primitives::{ + Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, InfinitePlane3d, Line3d, Polyline3d, + Segment3d, Sphere, Torus, Triangle3d, }; #[test] @@ -645,7 +640,7 @@ mod tests { Vec3::ZERO.into(), "incorrect bounding sphere center" ); - assert_eq!(bs.sphere.radius, 0.0, "incorrect bounding sphere radius"); + assert_eq!(bs.radius(), 0.0, "incorrect bounding sphere radius"); let dup_degenerate_triangle = Triangle3d::new(Vec3::ZERO, Vec3::X, Vec3::X); let bs = dup_degenerate_triangle.bounding_sphere(Isometry3d::IDENTITY); @@ -654,7 +649,7 @@ mod tests { Vec3::new(0.5, 0.0, 0.0).into(), "incorrect bounding sphere center" ); - assert_eq!(bs.sphere.radius, 0.5, "incorrect bounding sphere radius"); + assert_eq!(bs.radius(), 0.5, "incorrect bounding sphere radius"); let br = dup_degenerate_triangle.aabb_3d(Isometry3d::IDENTITY); assert_eq!( br.center(), @@ -674,7 +669,7 @@ mod tests { Vec3::ZERO.into(), "incorrect bounding sphere center" ); - assert_eq!(bs.sphere.radius, 1.0, "incorrect bounding sphere radius"); + assert_eq!(bs.radius(), 1.0, "incorrect bounding sphere radius"); let br = collinear_degenerate_triangle.aabb_3d(Isometry3d::IDENTITY); assert_eq!( br.center(), diff --git a/crates/bevy_math/src/bounding/bounded3d/extrusion.rs b/crates/bevy_shapes/src/bounding/extrusion.rs similarity index 95% rename from crates/bevy_math/src/bounding/bounded3d/extrusion.rs rename to crates/bevy_shapes/src/bounding/extrusion.rs index e37783576caac..7a5d25a36ff46 100644 --- a/crates/bevy_math/src/bounding/bounded3d/extrusion.rs +++ b/crates/bevy_shapes/src/bounding/extrusion.rs @@ -1,24 +1,19 @@ -use core::f32::consts::FRAC_PI_2; +//! Implement [`BoundedExtrusion`] for all the primitives in `bevy_shapes` -use glam::{Vec2, Vec3A, Vec3Swizzles}; +use core::f32::consts::FRAC_PI_2; -use crate::{ - bounding::{BoundingCircle, BoundingVolume}, - ops, - primitives::{ - Capsule2d, Cuboid, Cylinder, Ellipse, Extrusion, Line2d, Primitive2d, Rectangle, - RegularPolygon, Ring, Segment2d, Triangle2d, - }, - Isometry2d, Isometry3d, Quat, Rot2, +use crate::primitives::{ + Capsule2d, Circle, Cuboid, Cylinder, Ellipse, Extrusion, Line2d, Primitive2d, Rectangle, + RegularPolygon, Ring, Segment2d, Triangle2d, +}; +use bevy_math::{ + bounding::{Aabb3d, Bounded2d, Bounded3d, BoundingCircle, BoundingSphere, BoundingVolume}, + ops, Isometry2d, Isometry3d, Quat, Rot2, Vec2, Vec3A, Vec3Swizzles, }; #[cfg(feature = "alloc")] use crate::primitives::{Polygon, Polyline2d}; -use crate::{bounding::Bounded2d, primitives::Circle}; - -use super::{Aabb3d, Bounded3d, BoundingSphere}; - impl BoundedExtrusion for Circle { fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { // Reference: http://iquilezles.org/articles/diskbbox/ @@ -245,10 +240,7 @@ pub trait BoundedExtrusion: Primitive2d + Bounded2d { // Since each of the extrusions bases will have the same distance from its center, // and they are just shifted along the Z-axis, the minimum bounding sphere will be the bounding sphere // of the cylinder defined by the two bounding circles of the bases for any base shape - let BoundingCircle { - center, - circle: Circle { radius }, - } = self.bounding_circle(Isometry2d::IDENTITY); + let BoundingCircle { center, radius } = self.bounding_circle(Isometry2d::IDENTITY); let radius = ops::hypot(radius, half_depth); let center = isometry * Vec3A::from(center.extend(0.)); @@ -260,16 +252,13 @@ pub trait BoundedExtrusion: Primitive2d + Bounded2d { mod tests { use core::f32::consts::FRAC_PI_4; - use glam::{EulerRot, Quat, Vec2, Vec3, Vec3A}; - - use crate::{ + use crate::primitives::{ + Capsule2d, Circle, Ellipse, Extrusion, Line2d, Polygon, Polyline2d, Rectangle, + RegularPolygon, Segment2d, Triangle2d, + }; + use bevy_math::{ bounding::{Bounded3d, BoundingVolume}, - ops, - primitives::{ - Capsule2d, Circle, Ellipse, Extrusion, Line2d, Polygon, Polyline2d, Rectangle, - RegularPolygon, Segment2d, Triangle2d, - }, - Dir2, Isometry3d, + ops, Dir2, EulerRot, Isometry3d, Quat, Vec2, Vec3, Vec3A, }; #[test] diff --git a/crates/bevy_shapes/src/bounding/mod.rs b/crates/bevy_shapes/src/bounding/mod.rs new file mode 100644 index 0000000000000..38b8cf89c1011 --- /dev/null +++ b/crates/bevy_shapes/src/bounding/mod.rs @@ -0,0 +1,13 @@ +//! This module contains the implementations for bounding traits from the [`bevy_math`] crate for +//! working with primitive shapes +//! +//! There are four traits used: +//! - [`BoundingVolume`] is a generic abstraction for any bounding volume +//! - [`IntersectsVolume`] abstracts intersection tests against a [`BoundingVolume`] +//! - [`Bounded2d`]/[`Bounded3d`] are abstractions for shapes to generate [`BoundingVolume`]s + +mod dim2; +mod dim3; + +mod extrusion; +pub use extrusion::*; diff --git a/crates/bevy_gizmos/src/primitives/dim2.rs b/crates/bevy_shapes/src/gizmos/dim2.rs similarity index 97% rename from crates/bevy_gizmos/src/primitives/dim2.rs rename to crates/bevy_shapes/src/gizmos/dim2.rs index a8f25dcac78d8..afda137c53887 100644 --- a/crates/bevy_gizmos/src/primitives/dim2.rs +++ b/crates/bevy_shapes/src/gizmos/dim2.rs @@ -2,19 +2,16 @@ use core::f32::consts::{FRAC_PI_2, PI}; -use super::helpers::*; +use crate::gizmos::helpers::*; -use bevy_color::Color; -use bevy_math::{ - primitives::{ - Annulus, Arc2d, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Line2d, - Plane2d, Polygon, Polyline2d, Primitive2d, Rectangle, RegularPolygon, Rhombus, Segment2d, - Triangle2d, - }, - Dir2, Isometry2d, Rot2, Vec2, +use crate::primitives::{ + Annulus, Arc2d, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Line2d, Plane2d, + Polygon, Polyline2d, Primitive2d, Rectangle, RegularPolygon, Rhombus, Segment2d, Triangle2d, }; +use bevy_color::Color; +use bevy_math::{Dir2, Isometry2d, Rot2, Vec2}; -use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; +use bevy_gizmos::{circles::Ellipse2dBuilder, gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; // some magic number since using directions as offsets will result in lines of length 1 pixel const MIN_LINE_LEN: f32 = 50.0; @@ -108,7 +105,7 @@ where Clear: 'static + Send + Sync, { type Output<'a> - = crate::circles::Ellipse2dBuilder<'a, Config, Clear> + = Ellipse2dBuilder<'a, Config, Clear> where Self: 'a; @@ -215,7 +212,7 @@ where Clear: 'static + Send + Sync, { type Output<'a> - = crate::circles::Ellipse2dBuilder<'a, Config, Clear> + = Ellipse2dBuilder<'a, Config, Clear> where Self: 'a; @@ -293,8 +290,8 @@ where inner_radius: primitive.inner_circle.radius, outer_radius: primitive.outer_circle.radius, color: color.into(), - inner_resolution: crate::circles::DEFAULT_CIRCLE_RESOLUTION, - outer_resolution: crate::circles::DEFAULT_CIRCLE_RESOLUTION, + inner_resolution: DEFAULT_CIRCLE_RESOLUTION, + outer_resolution: DEFAULT_CIRCLE_RESOLUTION, } } } diff --git a/crates/bevy_gizmos/src/primitives/dim3.rs b/crates/bevy_shapes/src/gizmos/dim3.rs similarity index 87% rename from crates/bevy_gizmos/src/primitives/dim3.rs rename to crates/bevy_shapes/src/gizmos/dim3.rs index b4dd923a7b009..080c2011fc01f 100644 --- a/crates/bevy_gizmos/src/primitives/dim3.rs +++ b/crates/bevy_shapes/src/gizmos/dim3.rs @@ -2,16 +2,14 @@ use super::helpers::*; -use bevy_color::Color; -use bevy_math::{ - primitives::{ - Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d, Polyline3d, - Primitive3d, Segment3d, Sphere, Tetrahedron, Torus, Triangle3d, - }, - Dir3, Isometry3d, Quat, UVec2, Vec2, Vec3, +use crate::primitives::{ + Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d, Polyline3d, Primitive3d, + Segment3d, Sphere, Tetrahedron, Torus, Triangle3d, }; +use bevy_color::Color; +use bevy_math::{Dir3, Isometry3d, Quat, UVec2, Vec2, Vec3}; -use crate::{circles::SphereBuilder, gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; +use bevy_gizmos::{circles::SphereBuilder, gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; const DEFAULT_RESOLUTION: u32 = 5; // length used to simulate infinite lines @@ -517,20 +515,28 @@ where let [upper_center, lower_center] = [-1.0, 1.0] .map(|sign| Vec3::Y * sign * self.half_length) .map(|vec3| self.isometry * vec3); - let [upper_points, lower_points] = [-1.0, 1.0] - .map(|sign| Vec3::Y * sign * self.half_length) - .map(|vec3| { - circle_coordinates_closed(self.radius, self.resolution) - .map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + vec3) - .map(|vec3| self.isometry * vec3) - .collect::>() - }); - upper_points.iter().skip(1).copied().for_each(|start| { + // This prevents allocations (no need for alloc crate) and cloning. + let half_length = self.half_length; + let radius = self.radius; + let resolution = self.resolution; + let isometry = self.isometry; + let mk_upper_lower = move || { + [-1.0, 1.0] + .map(|sign| Vec3::Y * sign * half_length) + .map(|vec3| { + circle_coordinates_closed(radius, resolution) + .map(move |vec2| Vec3::new(vec2.x, 0.0, vec2.y) + vec3) + .map(move |vec3| isometry * vec3) + }) + }; + + let [upper_points, lower_points] = mk_upper_lower(); + upper_points.skip(1).for_each(|start| { self.gizmos .short_arc_3d_between(upper_center, start, upper_apex, self.color); }); - lower_points.iter().skip(1).copied().for_each(|start| { + lower_points.skip(1).for_each(|start| { self.gizmos .short_arc_3d_between(lower_center, start, lower_apex, self.color); }); @@ -550,7 +556,8 @@ where self.color, ); - let connection_lines = upper_points.into_iter().zip(lower_points).skip(1); + let [upper_points, lower_points] = mk_upper_lower(); + let connection_lines = upper_points.zip(lower_points).skip(1); connection_lines.for_each(|(start, end)| { self.gizmos.line(start, end, self.color); }); @@ -655,24 +662,26 @@ where let half_height = self.height * 0.5; let apex = self.isometry * (Vec3::Y * half_height); let circle_center = half_height * Vec3::NEG_Y; - let circle_coords = circle_coordinates_closed(self.radius, self.height_resolution) - .map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + circle_center) - .map(|vec3| self.isometry * vec3) - .collect::>(); + let circle_coords = || { + circle_coordinates_closed(self.radius, self.height_resolution) + .map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + circle_center) + .map(|vec3| self.isometry * vec3) + }; // connections to apex - circle_coords - .iter() + circle_coords() .skip(1) - .map(|vec3| (*vec3, apex)) + .map(|vec3| (vec3, apex)) .for_each(|(start, end)| { self.gizmos.line(start, end, self.color); }); // base circle - circle_coords.array_windows().for_each(|&[start, end]| { - self.gizmos.line(start, end, self.color); - }); + circle_coords() + .zip(circle_coords().skip(1)) + .for_each(|(start, end)| { + self.gizmos.line(start, end, self.color); + }); } } @@ -752,21 +761,24 @@ where } let half_height = self.height * 0.5; - let [upper_points, lower_points] = [(-1.0, self.radius_bottom), (1.0, self.radius_top)] - .map(|(sign, radius)| { + let mk_upper_lower = || { + [(-1.0, self.radius_bottom), (1.0, self.radius_top)].map(|(sign, radius)| { let translation = Vec3::Y * sign * half_height; circle_coordinates_closed(radius, self.resolution) - .map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + translation) + .map(move |vec2| Vec3::new(vec2.x, 0.0, vec2.y) + translation) .map(|vec3| self.isometry * vec3) - .collect::>() - }); + }) + }; + let [upper_points1, lower_points1] = mk_upper_lower(); + let [upper_points2, lower_points2] = mk_upper_lower(); - let upper_lines = upper_points.array_windows(); - let lower_lines = lower_points.array_windows(); - upper_lines.chain(lower_lines).for_each(|&[start, end]| { + let upper_lines = upper_points1.zip(upper_points2.skip(1)); + let lower_lines = lower_points1.zip(lower_points2.skip(1)); + upper_lines.chain(lower_lines).for_each(|(start, end)| { self.gizmos.line(start, end, self.color); }); + let [upper_points, lower_points] = mk_upper_lower(); let connection_lines = upper_points.into_iter().zip(lower_points).skip(1); connection_lines.for_each(|(start, end)| { self.gizmos.line(start, end, self.color); @@ -856,27 +868,32 @@ where } // draw 4 circles with major_radius - let [inner, outer, top, bottom] = [ - (self.major_radius - self.minor_radius, 0.0), - (self.major_radius + self.minor_radius, 0.0), - (self.major_radius, self.minor_radius), - (self.major_radius, -self.minor_radius), - ] - .map(|(radius, height)| { - let translation = height * Vec3::Y; - circle_coordinates_closed(radius, self.major_resolution) - .map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + translation) - .map(|vec3| self.isometry * vec3) - .collect::>() - }); - - [&inner, &outer, &top, &bottom] - .iter() - .flat_map(|points| points.array_windows()) - .for_each(|&[start, end]| { + let mk_all_sides = || { + [ + (self.major_radius - self.minor_radius, 0.0), + (self.major_radius + self.minor_radius, 0.0), + (self.major_radius, self.minor_radius), + (self.major_radius, -self.minor_radius), + ] + .map(|(radius, height)| { + let translation = height * Vec3::Y; + circle_coordinates_closed(radius, self.major_resolution) + .map(move |vec2| Vec3::new(vec2.x, 0.0, vec2.y) + translation) + .map(|vec3| self.isometry * vec3) + }) + }; + let [inner1, outer1, top1, bottom1] = mk_all_sides(); + let [inner2, outer2, top2, bottom2] = mk_all_sides(); + inner1 + .zip(inner2.skip(1)) + .chain(outer1.zip(outer2.skip(1))) + .chain(top1.zip(top2.skip(1))) + .chain(bottom1.zip(bottom2.skip(1))) + .for_each(|(start, end)| { self.gizmos.line(start, end, self.color); }); + let [inner, outer, top, bottom] = mk_all_sides(); inner .into_iter() .zip(top) diff --git a/crates/bevy_gizmos/src/primitives/helpers.rs b/crates/bevy_shapes/src/gizmos/helpers.rs similarity index 94% rename from crates/bevy_gizmos/src/primitives/helpers.rs rename to crates/bevy_shapes/src/gizmos/helpers.rs index 37253b14a9ac9..27554f39021dc 100644 --- a/crates/bevy_gizmos/src/primitives/helpers.rs +++ b/crates/bevy_shapes/src/gizmos/helpers.rs @@ -1,7 +1,11 @@ +//! Helper functions for primitives gizmos rendering + use core::f32::consts::TAU; use bevy_math::{ops, Vec2}; +pub(crate) const DEFAULT_CIRCLE_RESOLUTION: u32 = 32; + /// Calculates the `nth` coordinate of a circle. /// /// Given a circle's radius and its resolution, this function computes the position diff --git a/crates/bevy_gizmos/src/primitives/mod.rs b/crates/bevy_shapes/src/gizmos/mod.rs similarity index 71% rename from crates/bevy_gizmos/src/primitives/mod.rs rename to crates/bevy_shapes/src/gizmos/mod.rs index 419ef50217450..4e22a3bcdf79a 100644 --- a/crates/bevy_gizmos/src/primitives/mod.rs +++ b/crates/bevy_shapes/src/gizmos/mod.rs @@ -1,5 +1,9 @@ //! A module for rendering each of the 2D and 3D [`bevy_math::primitives`] with [`Gizmos`](`crate::prelude::Gizmos`). -pub mod dim2; -pub mod dim3; +mod dim2; +pub use dim2::*; + +mod dim3; +pub use dim3::*; + pub(crate) mod helpers; diff --git a/crates/bevy_shapes/src/lib.rs b/crates/bevy_shapes/src/lib.rs new file mode 100644 index 0000000000000..a2b1bc3b064e0 --- /dev/null +++ b/crates/bevy_shapes/src/lib.rs @@ -0,0 +1,97 @@ +//! # Bevy Shapes +//! +//! Geometric primitives and shape-related traits for Bevy. +//! +//! This crate provides the complete collection of 2D and 3D geometric primitives +//! used throughout the Bevy ecosystem, along with implementations of common traits +//! for operations such as meshing, bounding volumes, sampling, and debug rendering. +//! +//! ## Feature flags +//! +//! Additional functionality is available through optional features: +//! +//! - `meshing` — Generate meshes from supported primitives. +//! - `bounding` — Compute bounding volumes. +//! - `sampling` — Sample points on or within primitives. +//! - `gizmos` — Draw primitives using Bevy Gizmos. +//! +//! ## Examples +//! +//! Constructing a primitive: +//! +//! ``` +//! use bevy_shapes::primitives::Circle; +//! +//! let circle = Circle::new(1.0); +//! ``` +//! +//! Many traits are implemented behind feature flags: +//! +//! ```ignore +//! use bevy_shapes::primitives::Sphere; +//! use bevy_shapes::meshing::Meshable; +//! +//! let mesh = sphere.mesh(); +//! ``` +//! +//! Most users will interact with the types in the [`primitives`] module, while +//! optional modules provide additional functionality for those primitives. +#![forbid(unsafe_code)] +#![cfg_attr( + any(docsrs, docsrs_dep), + expect( + internal_features, + reason = "rustdoc_internals is needed for fake_variadic" + ) +)] +#![cfg_attr(any(docsrs, docsrs_dep), feature(rustdoc_internals))] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![doc( + html_logo_url = "https://bevy.org/assets/icon.png", + html_favicon_url = "https://bevy.org/assets/icon.png" +)] +#![no_std] + +#[cfg(feature = "std")] +extern crate std; + +#[cfg(feature = "alloc")] +extern crate alloc; + +pub mod primitives; + +#[cfg(feature = "meshing")] +pub mod meshing; + +#[cfg(feature = "bounding")] +pub mod bounding; + +#[cfg(feature = "sampling")] +pub mod sampling; + +#[cfg(feature = "gizmos")] +pub mod gizmos; + +/// The shapes prelude. +/// +/// This includes the most common types in this crate, re-exported for your convenience. +pub mod prelude { + #[doc(hidden)] + pub use crate::primitives::*; + + #[doc(hidden)] + #[cfg(feature = "meshing")] + pub use crate::meshing::*; + + #[doc(hidden)] + #[cfg(feature = "bounding")] + pub use crate::bounding::*; + + #[doc(hidden)] + #[cfg(feature = "sampling")] + pub use crate::sampling::*; + + #[doc(hidden)] + #[cfg(feature = "gizmos")] + pub use crate::gizmos::*; +} diff --git a/crates/bevy_mesh/src/primitives/dim2.rs b/crates/bevy_shapes/src/meshing/dim2.rs similarity index 77% rename from crates/bevy_mesh/src/primitives/dim2.rs rename to crates/bevy_shapes/src/meshing/dim2.rs index 7ad0fc3826a5d..716887e7856d7 100644 --- a/crates/bevy_mesh/src/primitives/dim2.rs +++ b/crates/bevy_shapes/src/meshing/dim2.rs @@ -1,23 +1,20 @@ use core::f32::consts::FRAC_PI_2; use core::mem; -use crate::{primitives::dim3::triangle3d, Indices, Mesh, PerimeterSegment, VertexAttributeValues}; use bevy_asset::RenderAssetUsages; +use bevy_mesh::{Indices, Mesh, MeshBuilder, Meshable, VertexAttributeValues}; -use super::{Extrudable, MeshBuilder, Meshable}; -use bevy_math::prelude::Polyline2d; -use bevy_math::{ - ops, - primitives::{ - Annulus, Capsule2d, Circle, CircularSector, CircularSegment, ConvexPolygon, Ellipse, - Primitive2d, Rectangle, RegularPolygon, Rhombus, Ring, Segment2d, Triangle2d, Triangle3d, - WindingOrder, - }, - FloatExt, Vec2, Vec3, +use crate::primitives::{ + Annulus, Capsule2d, Circle, CircularSector, CircularSegment, ConvexPolygon, Ellipse, + Polyline2d, Primitive2d, Rectangle, RegularPolygon, Rhombus, Ring, Segment2d, Triangle2d, + Triangle3d, WindingOrder, }; +use bevy_math::{ops, FloatExt, Vec2, Vec3}; use bevy_reflect::prelude::*; use wgpu_types::PrimitiveTopology; +use alloc::vec::Vec; + /// A builder used for creating a [`Mesh`] with a [`Circle`] shape. #[derive(Clone, Copy, Debug, Reflect)] #[reflect(Default, Debug, Clone)] @@ -61,26 +58,16 @@ impl CircleMeshBuilder { impl MeshBuilder for CircleMeshBuilder { fn build(&self) -> Mesh { Ellipse::new(self.circle.radius, self.circle.radius) - .mesh() + .mesh_builder() .resolution(self.resolution) .build() } } -impl Extrudable for CircleMeshBuilder { - fn perimeter(&self) -> Vec { - vec![PerimeterSegment::Smooth { - first_normal: Vec2::Y, - last_normal: Vec2::Y, - indices: (0..self.resolution).chain([0]).collect(), - }] - } -} - impl Meshable for Circle { type Output = CircleMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { CircleMeshBuilder { circle: *self, ..Default::default() @@ -89,8 +76,8 @@ impl Meshable for Circle { } impl From for Mesh { - fn from(circle: Circle) -> Self { - circle.mesh().build() + fn from(value: Circle) -> Self { + value.mesh() } } @@ -179,7 +166,7 @@ impl MeshBuilder for CircularSectorMeshBuilder { let resolution = self.resolution as usize; let mut indices = Vec::with_capacity((resolution - 1) * 3); let mut positions = Vec::with_capacity(resolution + 1); - let normals = vec![[0.0, 0.0, 1.0]; resolution + 1]; + let normals = alloc::vec![[0.0, 0.0, 1.0]; resolution + 1]; let mut uvs = Vec::with_capacity(resolution + 1); let CircularMeshUvMode::Mask { angle: uv_angle } = self.uv_mode; @@ -221,28 +208,10 @@ impl MeshBuilder for CircularSectorMeshBuilder { } } -impl Extrudable for CircularSectorMeshBuilder { - fn perimeter(&self) -> Vec { - let (sin, cos) = ops::sin_cos(self.sector.arc.half_angle); - let first_normal = Vec2::new(sin, cos); - let last_normal = Vec2::new(-sin, cos); - vec![ - PerimeterSegment::Flat { - indices: vec![self.resolution, 0, 1], - }, - PerimeterSegment::Smooth { - first_normal, - last_normal, - indices: (1..=self.resolution).collect(), - }, - ] - } -} - impl Meshable for CircularSector { type Output = CircularSectorMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { CircularSectorMeshBuilder { sector: *self, ..Default::default() @@ -251,11 +220,8 @@ impl Meshable for CircularSector { } impl From for Mesh { - /// Converts this sector into a [`Mesh`] using a default [`CircularSectorMeshBuilder`]. - /// - /// See the documentation of [`CircularSectorMeshBuilder`] for more details. - fn from(sector: CircularSector) -> Self { - sector.mesh().build() + fn from(value: CircularSector) -> Self { + value.mesh() } } @@ -317,7 +283,7 @@ impl MeshBuilder for CircularSegmentMeshBuilder { let resolution = self.resolution as usize; let mut indices = Vec::with_capacity((resolution - 1) * 3); let mut positions = Vec::with_capacity(resolution + 1); - let normals = vec![[0.0, 0.0, 1.0]; resolution + 1]; + let normals = alloc::vec![[0.0, 0.0, 1.0]; resolution + 1]; let mut uvs = Vec::with_capacity(resolution + 1); let CircularMeshUvMode::Mask { angle: uv_angle } = self.uv_mode; @@ -368,28 +334,10 @@ impl MeshBuilder for CircularSegmentMeshBuilder { } } -impl Extrudable for CircularSegmentMeshBuilder { - fn perimeter(&self) -> Vec { - let (sin, cos) = ops::sin_cos(self.segment.arc.half_angle); - let first_normal = Vec2::new(sin, cos); - let last_normal = Vec2::new(-sin, cos); - vec![ - PerimeterSegment::Flat { - indices: vec![self.resolution, 0, 1], - }, - PerimeterSegment::Smooth { - first_normal, - last_normal, - indices: (1..=self.resolution).collect(), - }, - ] - } -} - impl Meshable for CircularSegment { type Output = CircularSegmentMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { CircularSegmentMeshBuilder { segment: *self, ..Default::default() @@ -398,11 +346,8 @@ impl Meshable for CircularSegment { } impl From for Mesh { - /// Converts this sector into a [`Mesh`] using a default [`CircularSegmentMeshBuilder`]. - /// - /// See the documentation of [`CircularSegmentMeshBuilder`] for more details. - fn from(segment: CircularSegment) -> Self { - segment.mesh().build() + fn from(value: CircularSegment) -> Self { + value.mesh() } } @@ -413,25 +358,32 @@ impl From for Mesh { #[derive(Clone, Debug, Reflect)] #[reflect(Debug, Clone)] pub struct ConvexPolygonMeshBuilder { + /// The verticies of the convex polygons pub vertices: Vec, } impl Meshable for ConvexPolygon { type Output = ConvexPolygonMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { Self::Output { vertices: self.vertices().to_vec(), } } } +impl From for Mesh { + fn from(value: ConvexPolygon) -> Self { + value.mesh() + } +} + impl MeshBuilder for ConvexPolygonMeshBuilder { fn build(&self) -> Mesh { let len = self.vertices.len(); let mut indices = Vec::with_capacity((len - 2) * 3); let mut positions = Vec::with_capacity(len); - let normals = vec![[0.0, 0.0, 1.0]; len]; + let normals = alloc::vec![[0.0, 0.0, 1.0]; len]; let mut uvs = Vec::with_capacity(len); let mut min = Vec2::splat(f32::INFINITY); @@ -464,26 +416,12 @@ impl MeshBuilder for ConvexPolygonMeshBuilder { } } -impl Extrudable for ConvexPolygonMeshBuilder { - fn perimeter(&self) -> Vec { - vec![PerimeterSegment::Flat { - indices: (0..self.vertices.len() as u32).chain([0]).collect(), - }] - } -} - -impl From for Mesh { - fn from(polygon: ConvexPolygon) -> Self { - polygon.mesh().build() - } -} - /// A builder used for creating a [`Mesh`] with a [`RegularPolygon`] shape. #[derive(Clone, Copy, Debug, Reflect)] #[reflect(Default, Debug, Clone)] pub struct RegularPolygonMeshBuilder { circumradius: f32, - sides: u32, + pub(crate) sides: u32, } impl Default for RegularPolygonMeshBuilder { @@ -520,7 +458,7 @@ impl RegularPolygonMeshBuilder { impl Meshable for RegularPolygon { type Output = RegularPolygonMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { Self::Output { circumradius: self.circumcircle.radius, sides: self.sides, @@ -528,30 +466,22 @@ impl Meshable for RegularPolygon { } } +impl From for Mesh { + fn from(value: RegularPolygon) -> Self { + value.mesh() + } +} + impl MeshBuilder for RegularPolygonMeshBuilder { fn build(&self) -> Mesh { // The ellipse mesh is just a regular polygon with two radii Ellipse::new(self.circumradius, self.circumradius) - .mesh() + .mesh_builder() .resolution(self.sides) .build() } } -impl Extrudable for RegularPolygonMeshBuilder { - fn perimeter(&self) -> Vec { - vec![PerimeterSegment::Flat { - indices: (0..self.sides).chain([0]).collect(), - }] - } -} - -impl From for Mesh { - fn from(polygon: RegularPolygon) -> Self { - polygon.mesh().build() - } -} - /// A builder used for creating a [`Mesh`] with an [`Ellipse`] shape. #[derive(Clone, Copy, Debug, Reflect)] #[reflect(Default, Debug, Clone)] @@ -597,7 +527,7 @@ impl MeshBuilder for EllipseMeshBuilder { let resolution = self.resolution as usize; let mut indices = Vec::with_capacity((resolution - 2) * 3); let mut positions = Vec::with_capacity(resolution); - let normals = vec![[0.0, 0.0, 1.0]; resolution]; + let normals = alloc::vec![[0.0, 0.0, 1.0]; resolution]; let mut uvs = Vec::with_capacity(resolution); // Add pi/2 so that there is a vertex at the top (sin is 1.0 and cos is 0.0) @@ -630,20 +560,10 @@ impl MeshBuilder for EllipseMeshBuilder { } } -impl Extrudable for EllipseMeshBuilder { - fn perimeter(&self) -> Vec { - vec![PerimeterSegment::Smooth { - first_normal: Vec2::Y, - last_normal: Vec2::Y, - indices: (0..self.resolution).chain([0]).collect(), - }] - } -} - impl Meshable for Ellipse { type Output = EllipseMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { EllipseMeshBuilder { ellipse: *self, ..Default::default() @@ -652,8 +572,8 @@ impl Meshable for Ellipse { } impl From for Mesh { - fn from(ellipse: Ellipse) -> Self { - ellipse.mesh().build() + fn from(value: Ellipse) -> Self { + value.mesh() } } @@ -674,7 +594,7 @@ impl Segment2dMeshBuilder { impl MeshBuilder for Segment2dMeshBuilder { fn build(&self) -> Mesh { let positions = self.segment.vertices.map(|v| v.extend(0.0)).to_vec(); - let indices = Indices::U32(vec![0, 1]); + let indices = Indices::U32(alloc::vec![0, 1]); Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) @@ -685,15 +605,14 @@ impl MeshBuilder for Segment2dMeshBuilder { impl Meshable for Segment2d { type Output = Segment2dMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { Segment2dMeshBuilder::new(*self) } } impl From for Mesh { - /// Converts this segment into a [`Mesh`] using a default [`Segment2dMeshBuilder`]. - fn from(segment: Segment2d) -> Self { - segment.mesh().build() + fn from(value: Segment2d) -> Self { + value.mesh() } } @@ -728,7 +647,7 @@ impl MeshBuilder for Polyline2dMeshBuilder { impl Meshable for Polyline2d { type Output = Polyline2dMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { Polyline2dMeshBuilder { polyline: self.clone(), } @@ -736,8 +655,8 @@ impl Meshable for Polyline2d { } impl From for Mesh { - fn from(polyline: Polyline2d) -> Self { - polyline.mesh().build() + fn from(value: Polyline2d) -> Self { + value.mesh() } } @@ -789,7 +708,7 @@ impl MeshBuilder for AnnulusMeshBuilder { let mut indices = Vec::with_capacity(self.resolution as usize * 6); let mut positions = Vec::with_capacity(num_vertices); let mut uvs = Vec::with_capacity(num_vertices); - let normals = vec![[0.0, 0.0, 1.0]; num_vertices]; + let normals = alloc::vec![[0.0, 0.0, 1.0]; num_vertices]; // We have one more set of vertices than might be naïvely expected; // the vertices at `start_angle` are duplicated for the purposes of UV @@ -839,28 +758,10 @@ impl MeshBuilder for AnnulusMeshBuilder { } } -impl Extrudable for AnnulusMeshBuilder { - fn perimeter(&self) -> Vec { - let vert_count = 2 * self.resolution; - vec![ - PerimeterSegment::Smooth { - first_normal: Vec2::NEG_Y, - last_normal: Vec2::NEG_Y, - indices: (0..vert_count).step_by(2).chain([0]).rev().collect(), // Inner hole - }, - PerimeterSegment::Smooth { - first_normal: Vec2::Y, - last_normal: Vec2::Y, - indices: (1..vert_count).step_by(2).chain([1]).collect(), // Outer perimeter - }, - ] - } -} - impl Meshable for Annulus { type Output = AnnulusMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { AnnulusMeshBuilder { annulus: *self, ..Default::default() @@ -869,8 +770,8 @@ impl Meshable for Annulus { } impl From for Mesh { - fn from(annulus: Annulus) -> Self { - annulus.mesh().build() + fn from(value: Annulus) -> Self { + value.mesh() } } @@ -915,15 +816,15 @@ impl RhombusMeshBuilder { impl MeshBuilder for RhombusMeshBuilder { fn build(&self) -> Mesh { let [hhd, vhd] = [self.half_diagonals.x, self.half_diagonals.y]; - let positions = vec![ + let positions = alloc::vec![ [hhd, 0.0, 0.0], [0.0, vhd, 0.0], [-hhd, 0.0, 0.0], [0.0, -vhd, 0.0], ]; - let normals = vec![[0.0, 0.0, 1.0]; 4]; - let uvs = vec![[1.0, 0.5], [0.5, 0.0], [0.0, 0.5], [0.5, 1.0]]; - let indices = Indices::U32(vec![2, 0, 1, 2, 3, 0]); + let normals = alloc::vec![[0.0, 0.0, 1.0]; 4]; + let uvs = alloc::vec![[1.0, 0.5], [0.5, 0.0], [0.0, 0.5], [0.5, 1.0]]; + let indices = Indices::U32(alloc::vec![2, 0, 1, 2, 3, 0]); Mesh::new( PrimitiveTopology::TriangleList, @@ -936,18 +837,10 @@ impl MeshBuilder for RhombusMeshBuilder { } } -impl Extrudable for RhombusMeshBuilder { - fn perimeter(&self) -> Vec { - vec![PerimeterSegment::Flat { - indices: vec![0, 1, 2, 3, 0], - }] - } -} - impl Meshable for Rhombus { type Output = RhombusMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { Self::Output { half_diagonals: self.half_diagonals, } @@ -955,8 +848,8 @@ impl Meshable for Rhombus { } impl From for Mesh { - fn from(rhombus: Rhombus) -> Self { - rhombus.mesh().build() + fn from(value: Rhombus) -> Self { + value.mesh() } } @@ -964,7 +857,7 @@ impl From for Mesh { #[derive(Clone, Copy, Debug, Default, Reflect)] #[reflect(Default, Debug, Clone)] pub struct Triangle2dMeshBuilder { - triangle: Triangle2d, + pub(crate) triangle: Triangle2d, } impl Triangle2dMeshBuilder { @@ -979,19 +872,25 @@ impl Triangle2dMeshBuilder { impl Meshable for Triangle2d { type Output = Triangle2dMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { Self::Output { triangle: *self } } } +impl From for Mesh { + fn from(value: Triangle2d) -> Self { + value.mesh() + } +} + impl MeshBuilder for Triangle2dMeshBuilder { fn build(&self) -> Mesh { let vertices_3d = self.triangle.vertices.map(|v| v.extend(0.)); let positions: Vec<_> = vertices_3d.into(); - let normals = vec![[0.0, 0.0, 1.0]; 3]; + let normals = alloc::vec![[0.0, 0.0, 1.0]; 3]; - let uvs: Vec<_> = triangle3d::uv_coords(&Triangle3d::new( + let uvs: Vec<_> = crate::meshing::triangle3d::uv_coords(&Triangle3d::new( vertices_3d[0], vertices_3d[1], vertices_3d[2], @@ -1000,9 +899,9 @@ impl MeshBuilder for Triangle2dMeshBuilder { let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise; let indices = if is_ccw { - Indices::U32(vec![0, 1, 2]) + Indices::U32(alloc::vec![0, 1, 2]) } else { - Indices::U32(vec![2, 1, 0]) + Indices::U32(alloc::vec![2, 1, 0]) }; Mesh::new( @@ -1016,27 +915,6 @@ impl MeshBuilder for Triangle2dMeshBuilder { } } -impl Extrudable for Triangle2dMeshBuilder { - fn perimeter(&self) -> Vec { - let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise; - if is_ccw { - vec![PerimeterSegment::Flat { - indices: vec![0, 1, 2, 0], - }] - } else { - vec![PerimeterSegment::Flat { - indices: vec![2, 1, 0, 2], - }] - } - } -} - -impl From for Mesh { - fn from(triangle: Triangle2d) -> Self { - triangle.mesh().build() - } -} - /// A builder used for creating a [`Mesh`] with a [`Rectangle`] shape. #[derive(Clone, Copy, Debug, Reflect)] #[reflect(Default, Debug, Clone)] @@ -1071,40 +949,14 @@ impl RectangleMeshBuilder { impl MeshBuilder for RectangleMeshBuilder { fn build(&self) -> Mesh { - let [hw, hh] = [self.half_size.x, self.half_size.y]; - let positions = vec![ - [hw, hh, 0.0], - [-hw, hh, 0.0], - [-hw, -hh, 0.0], - [hw, -hh, 0.0], - ]; - let normals = vec![[0.0, 0.0, 1.0]; 4]; - let uvs = vec![[1.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]]; - let indices = Indices::U32(vec![0, 1, 2, 0, 2, 3]); - - Mesh::new( - PrimitiveTopology::TriangleList, - RenderAssetUsages::default(), - ) - .with_inserted_indices(indices) - .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) - .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) - .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) - } -} - -impl Extrudable for RectangleMeshBuilder { - fn perimeter(&self) -> Vec { - vec![PerimeterSegment::Flat { - indices: vec![0, 1, 2, 3, 0], - }] + Mesh::quad_mesh(self.half_size) } } impl Meshable for Rectangle { type Output = RectangleMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { RectangleMeshBuilder { half_size: self.half_size, } @@ -1112,8 +964,8 @@ impl Meshable for Rectangle { } impl From for Mesh { - fn from(rectangle: Rectangle) -> Self { - rectangle.mesh().build() + fn from(value: Rectangle) -> Self { + value.mesh() } } @@ -1168,7 +1020,7 @@ impl MeshBuilder for Capsule2dMeshBuilder { // Six extra indices for the two triangles between the semicircles let mut indices = Vec::with_capacity((resolution as usize - 2) * 2 * 3 + 6); let mut positions = Vec::with_capacity(vertex_count as usize); - let normals = vec![[0.0, 0.0, 1.0]; vertex_count as usize]; + let normals = alloc::vec![[0.0, 0.0, 1.0]; vertex_count as usize]; let mut uvs = Vec::with_capacity(vertex_count as usize); let radius = self.capsule.radius; @@ -1235,36 +1087,10 @@ impl MeshBuilder for Capsule2dMeshBuilder { } } -impl Extrudable for Capsule2dMeshBuilder { - fn perimeter(&self) -> Vec { - let resolution = self.resolution; - let top_semi_indices = (0..resolution).collect(); - let bottom_semi_indices = (resolution..(2 * resolution)).collect(); - vec![ - PerimeterSegment::Smooth { - first_normal: Vec2::X, - last_normal: Vec2::NEG_X, - indices: top_semi_indices, - }, // Top semi-circle - PerimeterSegment::Flat { - indices: vec![resolution - 1, resolution], - }, // Left edge - PerimeterSegment::Smooth { - first_normal: Vec2::NEG_X, - last_normal: Vec2::X, - indices: bottom_semi_indices, - }, // Bottom semi-circle - PerimeterSegment::Flat { - indices: vec![2 * resolution - 1, 0], - }, // Right edge - ] - } -} - impl Meshable for Capsule2d { type Output = Capsule2dMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { Capsule2dMeshBuilder { capsule: *self, ..Default::default() @@ -1273,8 +1099,8 @@ impl Meshable for Capsule2d { } impl From for Mesh { - fn from(capsule: Capsule2d) -> Self { - capsule.mesh().build() + fn from(value: Capsule2d) -> Self { + value.mesh() } } @@ -1283,7 +1109,9 @@ pub struct RingMeshBuilder

where P: Primitive2d + Meshable, { + /// The outside shape of the [`Ring`], determined by a [`Primitive2d`] mesh builder pub outer_shape_builder: P::Output, + /// The inside shape of the [`Ring`], determined by a [`Primitive2d`] mesh builder pub inner_shape_builder: P::Output, } @@ -1294,8 +1122,8 @@ where /// Create a new `RingMeshBuilder

` from a given `Ring

` shape. pub fn new(ring: &Ring

) -> Self { Self { - outer_shape_builder: ring.outer_shape.mesh(), - inner_shape_builder: ring.inner_shape.mesh(), + outer_shape_builder: ring.outer_shape.mesh_builder(), + inner_shape_builder: ring.inner_shape.mesh_builder(), } } @@ -1306,7 +1134,7 @@ where self } - fn get_vertex_attributes(&self) -> Option { + pub(crate) fn get_vertex_attributes(&self) -> Option { fn get_positions(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 3]>> { if let VertexAttributeValues::Float32x3(data) = mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION)? @@ -1362,13 +1190,13 @@ where } } -struct RingMeshBuilderVertexAttributes { - outer_positions: Vec<[f32; 3]>, - inner_positions: Vec<[f32; 3]>, - outer_normals: Vec<[f32; 3]>, - inner_normals: Vec<[f32; 3]>, - outer_uvs: Vec<[f32; 2]>, - inner_uvs: Vec<[f32; 2]>, +pub(crate) struct RingMeshBuilderVertexAttributes { + pub(crate) outer_positions: Vec<[f32; 3]>, + pub(crate) inner_positions: Vec<[f32; 3]>, + pub(crate) outer_normals: Vec<[f32; 3]>, + pub(crate) inner_normals: Vec<[f32; 3]>, + pub(crate) outer_uvs: Vec<[f32; 2]>, + pub(crate) inner_uvs: Vec<[f32; 2]>, } impl

MeshBuilder for RingMeshBuilder

@@ -1455,79 +1283,13 @@ where } } -impl

Extrudable for RingMeshBuilder

-where - P: Primitive2d + Meshable, - P::Output: Extrudable, -{ - /// A list of the indices each representing a part of the perimeter of the mesh. - /// - /// # Panics - /// - /// Panics if the following assumptions are not met. - /// - /// It is assumed that the inner and outer meshes have the same number of vertices. - /// If not, then the [`MeshBuilder`] of the underlying 2d primitive has generated - /// a different number of vertices for the inner and outer instances of the primitive. - /// - /// It is assumed that the `primitive_topology` of the mesh returned by - /// the underlying builder is [`PrimitiveTopology::TriangleList`] - /// and that the mesh has [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes. - fn perimeter(&self) -> Vec { - let outer_vertex_count = self - .get_vertex_attributes() - .filter(|r| r.outer_positions.len() == r.inner_positions.len()) - .expect("The inner and outer meshes should have the same number of vertices, and have required attributes") - .outer_positions - .len(); - - let mut outer_perimeter = self.outer_shape_builder.perimeter(); - let inner_perimeter = - self.inner_shape_builder - .perimeter() - .into_iter() - .rev() - .map(|segment| match segment { - PerimeterSegment::Smooth { - first_normal, - last_normal, - mut indices, - } => PerimeterSegment::Smooth { - first_normal: -last_normal, - last_normal: -first_normal, - indices: { - let outer_perimeter_vertex_count = outer_vertex_count as u32; - indices.reverse(); - for i in &mut indices { - *i += outer_perimeter_vertex_count; - } - indices - }, - }, - PerimeterSegment::Flat { mut indices } => PerimeterSegment::Flat { - indices: { - let outer_perimeter_vertex_count = outer_vertex_count as u32; - indices.reverse(); - for i in &mut indices { - *i += outer_perimeter_vertex_count; - } - indices - }, - }, - }); - - outer_perimeter.extend(inner_perimeter); - outer_perimeter - } -} - impl

Meshable for Ring

where P: Primitive2d + Meshable, { type Output = RingMeshBuilder

; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { RingMeshBuilder::new(self) } } @@ -1536,21 +1298,18 @@ impl

From> for Mesh where P: Primitive2d + Meshable, { - fn from(ring: Ring

) -> Self { - ring.mesh().build() + fn from(value: Ring

) -> Self { + value.mesh() } } #[cfg(test)] mod tests { - use bevy_math::{ - prelude::Annulus, - primitives::{ConvexPolygon, RegularPolygon}, - FloatOrd, Vec2, - }; + use crate::primitives::{Annulus, ConvexPolygon, RegularPolygon}; + use bevy_math::{FloatOrd, Vec2}; use bevy_platform::collections::HashSet; - use crate::{Mesh, MeshBuilder, Meshable, VertexAttributeValues}; + use bevy_mesh::{Mesh, MeshBuilder, Meshable, VertexAttributeValues}; fn count_distinct_positions(points: &[[f32; 3]]) -> usize { let mut map = >::default(); @@ -1562,7 +1321,7 @@ mod tests { #[test] fn test_annulus() { - let mesh = Annulus::new(1.0, 1.2).mesh().resolution(16).build(); + let mesh = Annulus::new(1.0, 1.2).mesh_builder().resolution(16).build(); assert_eq!( 32, @@ -1590,7 +1349,7 @@ mod tests { #[test] fn test_regular_polygon() { - let mut mesh = Mesh::from(RegularPolygon::new(7.0, 4)); + let mut mesh = RegularPolygon::new(7.0, 4).mesh(); let Some(VertexAttributeValues::Float32x3(mut positions)) = mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION) @@ -1629,7 +1388,7 @@ mod tests { #[test] fn test_convex_polygon() { - let polygon = ConvexPolygon::new(vec![ + let polygon = ConvexPolygon::new(alloc::vec![ Vec2::new(-2.0, -1.0), Vec2::new(2.0, -1.0), Vec2::new(1.0, 3.0), @@ -1637,7 +1396,7 @@ mod tests { ]) .unwrap(); - let mut mesh = Mesh::from(polygon); + let mut mesh = polygon.mesh(); let Some(VertexAttributeValues::Float32x3(mut positions)) = mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION) diff --git a/crates/bevy_mesh/src/primitives/dim3/capsule.rs b/crates/bevy_shapes/src/meshing/dim3/capsule.rs similarity index 94% rename from crates/bevy_mesh/src/primitives/dim3/capsule.rs rename to crates/bevy_shapes/src/meshing/dim3/capsule.rs index f46ebce0d15a7..47e7a076515c9 100644 --- a/crates/bevy_mesh/src/primitives/dim3/capsule.rs +++ b/crates/bevy_shapes/src/meshing/dim3/capsule.rs @@ -1,8 +1,11 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::primitives::Capsule3d; use bevy_asset::RenderAssetUsages; -use bevy_math::{ops, primitives::Capsule3d, Vec2, Vec3}; +use bevy_math::{ops, Vec2, Vec3}; +use bevy_mesh::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_reflect::prelude::*; +use alloc::vec::Vec; + /// Manner in which UV coordinates are distributed vertically. #[derive(Clone, Copy, Debug, Default, Reflect)] #[reflect(Default, Debug, Clone)] @@ -132,9 +135,9 @@ impl MeshBuilder for Capsule3dMeshBuilder { // Initialize arrays. let vert_len = (vert_offset_south_cap + longitudes) as usize; - let mut vs: Vec = vec![Vec3::ZERO; vert_len]; - let mut vts: Vec = vec![Vec2::ZERO; vert_len]; - let mut vns: Vec = vec![Vec3::ZERO; vert_len]; + let mut vs: Vec = alloc::vec![Vec3::ZERO; vert_len]; + let mut vts: Vec = alloc::vec![Vec2::ZERO; vert_len]; + let mut vns: Vec = alloc::vec![Vec3::ZERO; vert_len]; let to_theta = 2.0 * core::f32::consts::PI / longitudes as f32; let to_phi = core::f32::consts::PI / latitudes as f32; @@ -149,9 +152,9 @@ impl MeshBuilder for Capsule3dMeshBuilder { let vt_aspect_north = 1.0 - vt_aspect_ratio; let vt_aspect_south = vt_aspect_ratio; - let mut theta_cartesian: Vec = vec![Vec2::ZERO; longitudes as usize]; - let mut rho_theta_cartesian: Vec = vec![Vec2::ZERO; longitudes as usize]; - let mut s_texture_cache: Vec = vec![0.0; lonsp1 as usize]; + let mut theta_cartesian: Vec = alloc::vec![Vec2::ZERO; longitudes as usize]; + let mut rho_theta_cartesian: Vec = alloc::vec![Vec2::ZERO; longitudes as usize]; + let mut s_texture_cache: Vec = alloc::vec![0.0; lonsp1 as usize]; for j in 0..longitudes as usize { let jf = j as f32; @@ -295,7 +298,7 @@ impl MeshBuilder for Capsule3dMeshBuilder { let tri_offset_south_cap = tri_offset_south_hemi + hemi_lons; let fs_len = tri_offset_south_cap + lons3; - let mut tris: Vec = vec![0; fs_len as usize]; + let mut tris: Vec = alloc::vec![0; fs_len as usize]; // Polar caps. let mut i = 0; @@ -421,7 +424,7 @@ impl MeshBuilder for Capsule3dMeshBuilder { impl Meshable for Capsule3d { type Output = Capsule3dMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { Capsule3dMeshBuilder { capsule: *self, ..Default::default() @@ -430,7 +433,7 @@ impl Meshable for Capsule3d { } impl From for Mesh { - fn from(capsule: Capsule3d) -> Self { - capsule.mesh().build() + fn from(value: Capsule3d) -> Self { + value.mesh() } } diff --git a/crates/bevy_mesh/src/primitives/dim3/cone.rs b/crates/bevy_shapes/src/meshing/dim3/cone.rs similarity index 94% rename from crates/bevy_mesh/src/primitives/dim3/cone.rs rename to crates/bevy_shapes/src/meshing/dim3/cone.rs index d06a57f832ea5..e3c0751ac19b0 100644 --- a/crates/bevy_mesh/src/primitives/dim3/cone.rs +++ b/crates/bevy_shapes/src/meshing/dim3/cone.rs @@ -1,6 +1,8 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::primitives::Cone; +use alloc::vec::Vec; use bevy_asset::RenderAssetUsages; -use bevy_math::{ops, primitives::Cone, Vec3}; +use bevy_math::{ops, Vec3}; +use bevy_mesh::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_reflect::prelude::*; /// Anchoring options for [`ConeMeshBuilder`] @@ -107,7 +109,7 @@ impl MeshBuilder for ConeMeshBuilder { // of the normal. This allows us to normalize vertex normals efficiently. let normal_slope = self.cone.radius / self.cone.height; // Equivalent to Vec2::new(1.0, slope).length().recip() - let normalization_factor = (1.0 + normal_slope * normal_slope).sqrt().recip(); + let normalization_factor = ops::sqrt(1.0 + normal_slope * normal_slope).recip(); // How much the angle changes at each step let step_theta = core::f32::consts::TAU / self.resolution as f32; @@ -174,7 +176,7 @@ impl MeshBuilder for ConeMeshBuilder { impl Meshable for Cone { type Output = ConeMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { ConeMeshBuilder { cone: *self, ..Default::default() @@ -183,15 +185,16 @@ impl Meshable for Cone { } impl From for Mesh { - fn from(cone: Cone) -> Self { - cone.mesh().build() + fn from(value: Cone) -> Self { + value.mesh() } } #[cfg(test)] mod tests { - use crate::{Mesh, MeshBuilder, Meshable, VertexAttributeValues}; - use bevy_math::{primitives::Cone, Vec2}; + use crate::primitives::Cone; + use bevy_math::Vec2; + use bevy_mesh::{Mesh, MeshBuilder, Meshable, VertexAttributeValues}; /// Rounds floats to handle floating point error in tests. fn round_floats(points: &mut [[f32; N]]) { @@ -211,7 +214,7 @@ mod tests { radius: 0.5, height: 1.0, } - .mesh() + .mesh_builder() .resolution(4) .build(); diff --git a/crates/bevy_mesh/src/primitives/dim3/conical_frustum.rs b/crates/bevy_shapes/src/meshing/dim3/conical_frustum.rs similarity index 95% rename from crates/bevy_mesh/src/primitives/dim3/conical_frustum.rs rename to crates/bevy_shapes/src/meshing/dim3/conical_frustum.rs index 8c69378c01e64..1b1c510f0e79e 100644 --- a/crates/bevy_mesh/src/primitives/dim3/conical_frustum.rs +++ b/crates/bevy_shapes/src/meshing/dim3/conical_frustum.rs @@ -1,6 +1,8 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::primitives::ConicalFrustum; +use alloc::vec::Vec; use bevy_asset::RenderAssetUsages; -use bevy_math::{ops, primitives::ConicalFrustum, Vec3}; +use bevy_math::{ops, Vec3}; +use bevy_mesh::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_reflect::prelude::*; /// A builder used for creating a [`Mesh`] with a [`ConicalFrustum`] shape. @@ -169,7 +171,7 @@ impl MeshBuilder for ConicalFrustumMeshBuilder { impl Meshable for ConicalFrustum { type Output = ConicalFrustumMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { ConicalFrustumMeshBuilder { frustum: *self, ..Default::default() @@ -178,7 +180,7 @@ impl Meshable for ConicalFrustum { } impl From for Mesh { - fn from(frustum: ConicalFrustum) -> Self { - frustum.mesh().build() + fn from(value: ConicalFrustum) -> Self { + value.mesh() } } diff --git a/crates/bevy_shapes/src/meshing/dim3/cuboid.rs b/crates/bevy_shapes/src/meshing/dim3/cuboid.rs new file mode 100644 index 0000000000000..aa3a8fa5b4e58 --- /dev/null +++ b/crates/bevy_shapes/src/meshing/dim3/cuboid.rs @@ -0,0 +1,42 @@ +use crate::primitives::Cuboid; +use bevy_math::Vec3; +use bevy_mesh::{Mesh, MeshBuilder, Meshable}; +use bevy_reflect::prelude::*; + +/// A builder used for creating a [`Mesh`] with a [`Cuboid`] shape. +#[derive(Clone, Copy, Debug, Reflect)] +#[reflect(Default, Debug, Clone)] +pub struct CuboidMeshBuilder { + half_size: Vec3, +} + +impl Default for CuboidMeshBuilder { + /// Returns the default [`CuboidMeshBuilder`] with a width, height, and depth of `1.0`. + fn default() -> Self { + Self { + half_size: Vec3::splat(0.5), + } + } +} + +impl MeshBuilder for CuboidMeshBuilder { + fn build(&self) -> Mesh { + Mesh::cuboid_mesh(self.half_size) + } +} + +impl Meshable for Cuboid { + type Output = CuboidMeshBuilder; + + fn mesh_builder(&self) -> Self::Output { + CuboidMeshBuilder { + half_size: self.half_size, + } + } +} + +impl From for Mesh { + fn from(value: Cuboid) -> Self { + value.mesh() + } +} diff --git a/crates/bevy_mesh/src/primitives/dim3/cylinder.rs b/crates/bevy_shapes/src/meshing/dim3/cylinder.rs similarity index 96% rename from crates/bevy_mesh/src/primitives/dim3/cylinder.rs rename to crates/bevy_shapes/src/meshing/dim3/cylinder.rs index 7b1b45974ea62..18ddc54be7c40 100644 --- a/crates/bevy_mesh/src/primitives/dim3/cylinder.rs +++ b/crates/bevy_shapes/src/meshing/dim3/cylinder.rs @@ -1,6 +1,8 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::primitives::Cylinder; +use alloc::vec::Vec; use bevy_asset::RenderAssetUsages; -use bevy_math::{ops, primitives::Cylinder}; +use bevy_math::ops; +use bevy_mesh::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_reflect::prelude::*; /// Anchoring options for [`CylinderMeshBuilder`] @@ -207,7 +209,7 @@ impl MeshBuilder for CylinderMeshBuilder { impl Meshable for Cylinder { type Output = CylinderMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { CylinderMeshBuilder { cylinder: *self, ..Default::default() @@ -216,7 +218,7 @@ impl Meshable for Cylinder { } impl From for Mesh { - fn from(cylinder: Cylinder) -> Self { - cylinder.mesh().build() + fn from(value: Cylinder) -> Self { + value.mesh() } } diff --git a/crates/bevy_mesh/src/primitives/dim3/mod.rs b/crates/bevy_shapes/src/meshing/dim3/mod.rs similarity index 100% rename from crates/bevy_mesh/src/primitives/dim3/mod.rs rename to crates/bevy_shapes/src/meshing/dim3/mod.rs diff --git a/crates/bevy_mesh/src/primitives/dim3/plane.rs b/crates/bevy_shapes/src/meshing/dim3/plane.rs similarity index 69% rename from crates/bevy_mesh/src/primitives/dim3/plane.rs rename to crates/bevy_shapes/src/meshing/dim3/plane.rs index e937905584f9a..21cecf33a2fd0 100644 --- a/crates/bevy_mesh/src/primitives/dim3/plane.rs +++ b/crates/bevy_shapes/src/meshing/dim3/plane.rs @@ -1,6 +1,6 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; -use bevy_asset::RenderAssetUsages; -use bevy_math::{primitives::Plane3d, Dir3, Quat, Vec2, Vec3}; +use crate::primitives::Plane3d; +use bevy_math::{Dir3, Vec2}; +use bevy_mesh::{Mesh, MeshBuilder, Meshable}; use bevy_reflect::prelude::*; /// A builder used for creating a [`Mesh`] with a [`Plane3d`] shape. @@ -140,57 +140,19 @@ impl PlaneMeshBuilder { impl MeshBuilder for PlaneMeshBuilder { fn build(&self) -> Mesh { - let z_vertex_count = self.subdivisions_z + 2; - let x_vertex_count = self.subdivisions_x + 2; - let num_vertices = (z_vertex_count * x_vertex_count) as usize; - let num_indices = ((z_vertex_count - 1) * (x_vertex_count - 1) * 6) as usize; - - let mut positions: Vec = Vec::with_capacity(num_vertices); - let mut normals: Vec<[f32; 3]> = Vec::with_capacity(num_vertices); - let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(num_vertices); - let mut indices: Vec = Vec::with_capacity(num_indices); - - let rotation = Quat::from_rotation_arc(Vec3::Y, *self.plane.normal); - let size = self.plane.half_size * 2.0; - - for z in 0..z_vertex_count { - for x in 0..x_vertex_count { - let tx = x as f32 / (x_vertex_count - 1) as f32; - let tz = z as f32 / (z_vertex_count - 1) as f32; - let pos = rotation * Vec3::new((-0.5 + tx) * size.x, 0.0, (-0.5 + tz) * size.y); - positions.push(pos); - normals.push(self.plane.normal.to_array()); - uvs.push([tx, tz]); - } - } - - for z in 0..z_vertex_count - 1 { - for x in 0..x_vertex_count - 1 { - let quad = z * x_vertex_count + x; - indices.push(quad + x_vertex_count + 1); - indices.push(quad + 1); - indices.push(quad + x_vertex_count); - indices.push(quad); - indices.push(quad + x_vertex_count); - indices.push(quad + 1); - } - } - - Mesh::new( - PrimitiveTopology::TriangleList, - RenderAssetUsages::default(), + Mesh::plane_mesh( + self.plane.normal, + self.plane.half_size, + self.subdivisions_x, + self.subdivisions_z, ) - .with_inserted_indices(Indices::U32(indices)) - .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions) - .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) - .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs) } } impl Meshable for Plane3d { type Output = PlaneMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { PlaneMeshBuilder { plane: *self, subdivisions_x: 0, @@ -200,7 +162,7 @@ impl Meshable for Plane3d { } impl From for Mesh { - fn from(plane: Plane3d) -> Self { - plane.mesh().build() + fn from(value: Plane3d) -> Self { + value.mesh() } } diff --git a/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs b/crates/bevy_shapes/src/meshing/dim3/polyline3d.rs similarity index 80% rename from crates/bevy_mesh/src/primitives/dim3/polyline3d.rs rename to crates/bevy_shapes/src/meshing/dim3/polyline3d.rs index 4d13112579f09..6d4520ac11276 100644 --- a/crates/bevy_mesh/src/primitives/dim3/polyline3d.rs +++ b/crates/bevy_shapes/src/meshing/dim3/polyline3d.rs @@ -1,6 +1,7 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::primitives::Polyline3d; +use alloc::vec::Vec; use bevy_asset::RenderAssetUsages; -use bevy_math::primitives::Polyline3d; +use bevy_mesh::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_reflect::prelude::*; /// A builder used for creating a [`Mesh`] with a [`Polyline3d`] shape. @@ -29,7 +30,7 @@ impl MeshBuilder for Polyline3dMeshBuilder { impl Meshable for Polyline3d { type Output = Polyline3dMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { Polyline3dMeshBuilder { polyline: self.clone(), } @@ -37,7 +38,7 @@ impl Meshable for Polyline3d { } impl From for Mesh { - fn from(polyline: Polyline3d) -> Self { - polyline.mesh().build() + fn from(value: Polyline3d) -> Self { + value.mesh() } } diff --git a/crates/bevy_mesh/src/primitives/dim3/segment3d.rs b/crates/bevy_shapes/src/meshing/dim3/segment3d.rs similarity index 74% rename from crates/bevy_mesh/src/primitives/dim3/segment3d.rs rename to crates/bevy_shapes/src/meshing/dim3/segment3d.rs index d032285283afb..292a9c69f40c1 100644 --- a/crates/bevy_mesh/src/primitives/dim3/segment3d.rs +++ b/crates/bevy_shapes/src/meshing/dim3/segment3d.rs @@ -1,6 +1,7 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::primitives::Segment3d; +use alloc::vec::Vec; use bevy_asset::RenderAssetUsages; -use bevy_math::primitives::Segment3d; +use bevy_mesh::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_reflect::prelude::*; /// A builder used for creating a [`Mesh`] with a [`Segment3d`] shape. @@ -13,7 +14,7 @@ pub struct Segment3dMeshBuilder { impl MeshBuilder for Segment3dMeshBuilder { fn build(&self) -> Mesh { let positions: Vec<_> = self.segment.vertices.into(); - let indices = Indices::U32(vec![0, 1]); + let indices = Indices::U32(alloc::vec![0, 1]); Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default()) .with_inserted_indices(indices) @@ -24,27 +25,27 @@ impl MeshBuilder for Segment3dMeshBuilder { impl Meshable for Segment3d { type Output = Segment3dMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { Segment3dMeshBuilder { segment: *self } } } impl From for Mesh { - fn from(segment: Segment3d) -> Self { - segment.mesh().build() + fn from(value: Segment3d) -> Self { + value.mesh() } } #[cfg(test)] mod tests { use super::*; - use crate::Meshable; use bevy_math::Vec3; + use bevy_mesh::{MeshBuilder, Meshable}; #[test] fn segment3d_mesh_builder() { let segment = Segment3d::new(Vec3::ZERO, Vec3::X); - let mesh = segment.mesh().build(); + let mesh = segment.mesh_builder().build(); assert_eq!(mesh.attribute(Mesh::ATTRIBUTE_POSITION).unwrap().len(), 2); assert_eq!(mesh.indices().unwrap().len(), 2); } diff --git a/crates/bevy_mesh/src/primitives/dim3/sphere.rs b/crates/bevy_shapes/src/meshing/dim3/sphere.rs similarity index 96% rename from crates/bevy_mesh/src/primitives/dim3/sphere.rs rename to crates/bevy_shapes/src/meshing/dim3/sphere.rs index 5938526457992..48e0a1e5f2901 100644 --- a/crates/bevy_mesh/src/primitives/dim3/sphere.rs +++ b/crates/bevy_shapes/src/meshing/dim3/sphere.rs @@ -1,6 +1,8 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::primitives::Sphere; +use alloc::vec::Vec; use bevy_asset::RenderAssetUsages; -use bevy_math::{ops, primitives::Sphere}; +use bevy_math::ops; +use bevy_mesh::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_reflect::prelude::*; use core::f32::consts::PI; use hexasphere::shapes::IcoSphere; @@ -141,7 +143,7 @@ impl SphereMeshBuilder { .map(Into::into) .collect::>(); - let uvs = generated.raw_data().to_owned(); + let uvs = generated.raw_data().to_vec(); let mut indices = Vec::with_capacity(generated.indices_per_main_triangle() * 20); @@ -253,7 +255,7 @@ impl MeshBuilder for SphereMeshBuilder { impl Meshable for Sphere { type Output = SphereMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { SphereMeshBuilder { sphere: *self, ..Default::default() @@ -262,7 +264,7 @@ impl Meshable for Sphere { } impl From for Mesh { - fn from(sphere: Sphere) -> Self { - sphere.mesh().build() + fn from(value: Sphere) -> Self { + value.mesh() } } diff --git a/crates/bevy_mesh/src/primitives/dim3/tetrahedron.rs b/crates/bevy_shapes/src/meshing/dim3/tetrahedron.rs similarity index 75% rename from crates/bevy_mesh/src/primitives/dim3/tetrahedron.rs rename to crates/bevy_shapes/src/meshing/dim3/tetrahedron.rs index 529805d9a603f..65d6b5b562aac 100644 --- a/crates/bevy_mesh/src/primitives/dim3/tetrahedron.rs +++ b/crates/bevy_shapes/src/meshing/dim3/tetrahedron.rs @@ -1,7 +1,10 @@ -use super::triangle3d; -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::{ + meshing::triangle3d, + primitives::{Tetrahedron, Triangle3d}, +}; +use alloc::vec::Vec; use bevy_asset::RenderAssetUsages; -use bevy_math::primitives::{Tetrahedron, Triangle3d}; +use bevy_mesh::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_reflect::prelude::*; /// A builder used for creating a [`Mesh`] with a [`Tetrahedron`] shape. @@ -21,9 +24,9 @@ impl MeshBuilder for TetrahedronMeshBuilder { faces.iter_mut().for_each(Triangle3d::reverse); } - let mut positions = vec![]; - let mut normals = vec![]; - let mut uvs = vec![]; + let mut positions = alloc::vec![]; + let mut normals = alloc::vec![]; + let mut uvs = alloc::vec![]; // Each face is meshed as a `Triangle3d`, and we just shove the data into the // vertex attributes sequentially. @@ -31,14 +34,14 @@ impl MeshBuilder for TetrahedronMeshBuilder { positions.extend(face.vertices); let face_normal = triangle3d::normal_vec(&face); - normals.extend(vec![face_normal; 3]); + normals.extend(alloc::vec![face_normal; 3]); let face_uvs = triangle3d::uv_coords(&face); uvs.extend(face_uvs); } // There are four faces and none of them share vertices. - let indices = Indices::U32(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + let indices = Indices::U32(alloc::vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); Mesh::new( PrimitiveTopology::TriangleList, @@ -54,13 +57,13 @@ impl MeshBuilder for TetrahedronMeshBuilder { impl Meshable for Tetrahedron { type Output = TetrahedronMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { TetrahedronMeshBuilder { tetrahedron: *self } } } impl From for Mesh { - fn from(tetrahedron: Tetrahedron) -> Self { - tetrahedron.mesh().build() + fn from(value: Tetrahedron) -> Self { + value.mesh() } } diff --git a/crates/bevy_mesh/src/primitives/dim3/torus.rs b/crates/bevy_shapes/src/meshing/dim3/torus.rs similarity index 95% rename from crates/bevy_mesh/src/primitives/dim3/torus.rs rename to crates/bevy_shapes/src/meshing/dim3/torus.rs index 6f370c13418ca..6d4aeff113b11 100644 --- a/crates/bevy_mesh/src/primitives/dim3/torus.rs +++ b/crates/bevy_shapes/src/meshing/dim3/torus.rs @@ -1,6 +1,8 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::primitives::Torus; +use alloc::vec::Vec; use bevy_asset::RenderAssetUsages; -use bevy_math::{ops, primitives::Torus, Vec3}; +use bevy_math::{ops, Vec3}; +use bevy_mesh::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_reflect::prelude::*; use core::ops::RangeInclusive; @@ -161,7 +163,7 @@ impl MeshBuilder for TorusMeshBuilder { impl Meshable for Torus { type Output = TorusMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { TorusMeshBuilder { torus: *self, ..Default::default() @@ -170,7 +172,7 @@ impl Meshable for Torus { } impl From for Mesh { - fn from(torus: Torus) -> Self { - torus.mesh().build() + fn from(value: Torus) -> Self { + value.mesh() } } diff --git a/crates/bevy_mesh/src/primitives/dim3/triangle3d.rs b/crates/bevy_shapes/src/meshing/dim3/triangle3d.rs similarity index 89% rename from crates/bevy_mesh/src/primitives/dim3/triangle3d.rs rename to crates/bevy_shapes/src/meshing/dim3/triangle3d.rs index e35f272ab9bb1..4ee587b8c5cac 100644 --- a/crates/bevy_mesh/src/primitives/dim3/triangle3d.rs +++ b/crates/bevy_shapes/src/meshing/dim3/triangle3d.rs @@ -1,6 +1,8 @@ -use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; +use crate::primitives::Triangle3d; +use alloc::vec::Vec; use bevy_asset::RenderAssetUsages; -use bevy_math::{primitives::Triangle3d, Vec3}; +use bevy_math::{ops, Vec3}; +use bevy_mesh::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology}; use bevy_reflect::prelude::*; /// A builder used for creating a [`Mesh`] with a [`Triangle3d`] shape. @@ -17,9 +19,9 @@ impl MeshBuilder for Triangle3dMeshBuilder { // Every vertex has the normal of the face of the triangle (or zero if the triangle is degenerate). let normal: Vec3 = normal_vec(&self.triangle); - let normals = vec![normal; 3]; + let normals = alloc::vec![normal; 3]; - let indices = Indices::U32(vec![0, 1, 2]); + let indices = Indices::U32(alloc::vec![0, 1, 2]); Mesh::new( PrimitiveTopology::TriangleList, @@ -35,11 +37,17 @@ impl MeshBuilder for Triangle3dMeshBuilder { impl Meshable for Triangle3d { type Output = Triangle3dMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { Triangle3dMeshBuilder { triangle: *self } } } +impl From for Mesh { + fn from(value: Triangle3d) -> Self { + value.mesh() + } +} + /// The normal of a [`Triangle3d`] with zeroing so that a [`Vec3`] is always obtained for meshing. #[inline] pub(crate) fn normal_vec(triangle: &Triangle3d) -> Vec3 { @@ -70,7 +78,7 @@ pub(crate) fn uv_coords(triangle: &Triangle3d) -> [[f32; 2]; 3] { // Obtuse triangle leaning to the left => x direction extends to the left, shifting a from 0. if offset < 0. { let total_length = 1. - offset; - let a_uv = [offset.abs() / total_length, 0.]; + let a_uv = [ops::abs(offset) / total_length, 0.]; let b_uv = [1., 0.]; let c_uv = [0., 1.]; @@ -94,16 +102,10 @@ pub(crate) fn uv_coords(triangle: &Triangle3d) -> [[f32; 2]; 3] { } } -impl From for Mesh { - fn from(triangle: Triangle3d) -> Self { - triangle.mesh().build() - } -} - #[cfg(test)] mod tests { use super::uv_coords; - use bevy_math::primitives::Triangle3d; + use crate::primitives::Triangle3d; #[test] fn uv_test() { diff --git a/crates/bevy_mesh/src/primitives/extrusion.rs b/crates/bevy_shapes/src/meshing/extrusion.rs similarity index 67% rename from crates/bevy_mesh/src/primitives/extrusion.rs rename to crates/bevy_shapes/src/meshing/extrusion.rs index 9df4821eb392b..f87a0554ad91b 100644 --- a/crates/bevy_mesh/src/primitives/extrusion.rs +++ b/crates/bevy_shapes/src/meshing/extrusion.rs @@ -1,10 +1,15 @@ -use bevy_math::{ - primitives::{Annulus, Capsule2d, Circle, Ellipse, Extrusion, Primitive2d}, - Vec2, Vec3, +use crate::meshing::{ + AnnulusMeshBuilder, Capsule2dMeshBuilder, CircleMeshBuilder, CircularSectorMeshBuilder, + CircularSegmentMeshBuilder, ConvexPolygonMeshBuilder, EllipseMeshBuilder, RectangleMeshBuilder, + RegularPolygonMeshBuilder, RhombusMeshBuilder, RingMeshBuilder, Triangle2dMeshBuilder, }; +use crate::primitives::{ + Annulus, Capsule2d, Circle, Ellipse, Extrusion, Primitive2d, WindingOrder, +}; +use alloc::vec::Vec; +use bevy_math::{ops, Vec2, Vec3}; -use super::{MeshBuilder, Meshable}; -use crate::{Indices, Mesh, PrimitiveTopology, VertexAttributeValues}; +use bevy_mesh::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology, VertexAttributeValues}; /// A type representing a segment of the perimeter of an extrudable mesh. pub enum PerimeterSegment { @@ -92,23 +97,37 @@ where { type Output = ExtrusionBuilder

; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { ExtrusionBuilder { - base_builder: self.base_shape.mesh(), + base_builder: self.base_shape.mesh_builder(), half_depth: self.half_depth, segments: 1, } } } +impl

From> for Mesh +where + P: Primitive2d + Meshable, + P::Output: Extrudable, +{ + fn from(value: Extrusion

) -> Mesh { + value.mesh() + } +} + /// A builder used for creating a [`Mesh`] with an [`Extrusion`] shape. pub struct ExtrusionBuilder

where P: Primitive2d + Meshable, P::Output: Extrudable, { + /// The base mesh builder for a [`Primitive2d`] which is going to be extruded pub base_builder: P::Output, + /// The extrusion length halved. It is applied in both directions 'up' and 'down' from the base + /// shape pub half_depth: f32, + /// The number of segments along the depth of the extrusion pub segments: usize, } @@ -120,7 +139,7 @@ where /// Create a new `ExtrusionBuilder

` from a given `base_shape` and the full `depth` of the extrusion. pub fn new(base_shape: &P, depth: f32) -> Self { Self { - base_builder: base_shape.mesh(), + base_builder: base_shape.mesh_builder(), half_depth: depth / 2., segments: 1, } @@ -300,7 +319,7 @@ where let n = Vec3::from_array([b[1] - a[1], a[0] - b[0], 0.]) .normalize_or_zero() .to_array(); - normals.extend_from_slice(&vec![n; 2 * layers]); + normals.extend_from_slice(&alloc::vec![n; 2 * layers]); // Add the indices for the vertices created above to the mesh. for i in 0..self.segments as u32 { @@ -339,7 +358,7 @@ where let uv_y = 0.5 + 0.5 * i / self.segments as f32; uvs.push([uv_start, uv_y]); } - normals.extend_from_slice(&vec![ + normals.extend_from_slice(&alloc::vec![ first_normal.extend(0.).to_array(); layers ]); @@ -376,7 +395,7 @@ where .extend(0.) .to_array() }; - normals.extend_from_slice(&vec![n; layers]); + normals.extend_from_slice(&alloc::vec![n; layers]); } // If there is a last vertex, we need to add it and its counterparts on each layer. @@ -391,7 +410,7 @@ where let uv_y = 0.5 + 0.5 * i / self.segments as f32; uvs.push([uv_start + uv_segment_delta, uv_y]); } - normals.extend_from_slice(&vec![ + normals.extend_from_slice(&alloc::vec![ last_normal.extend(0.).to_array(); layers ]); @@ -430,12 +449,215 @@ where } } -impl

From> for Mesh +impl Extrudable for CircleMeshBuilder { + fn perimeter(&self) -> Vec { + alloc::vec![PerimeterSegment::Smooth { + first_normal: Vec2::Y, + last_normal: Vec2::Y, + indices: (0..self.resolution).chain([0]).collect(), + }] + } +} + +impl Extrudable for CircularSectorMeshBuilder { + fn perimeter(&self) -> Vec { + let (sin, cos) = ops::sin_cos(self.sector.arc.half_angle); + let first_normal = Vec2::new(sin, cos); + let last_normal = Vec2::new(-sin, cos); + alloc::vec![ + PerimeterSegment::Flat { + indices: alloc::vec![self.resolution, 0, 1], + }, + PerimeterSegment::Smooth { + first_normal, + last_normal, + indices: (1..=self.resolution).collect(), + }, + ] + } +} + +impl Extrudable for CircularSegmentMeshBuilder { + fn perimeter(&self) -> Vec { + let (sin, cos) = ops::sin_cos(self.segment.arc.half_angle); + let first_normal = Vec2::new(sin, cos); + let last_normal = Vec2::new(-sin, cos); + alloc::vec![ + PerimeterSegment::Flat { + indices: alloc::vec![self.resolution, 0, 1], + }, + PerimeterSegment::Smooth { + first_normal, + last_normal, + indices: (1..=self.resolution).collect(), + }, + ] + } +} + +impl Extrudable for ConvexPolygonMeshBuilder { + fn perimeter(&self) -> Vec { + alloc::vec![PerimeterSegment::Flat { + indices: (0..self.vertices.len() as u32).chain([0]).collect(), + }] + } +} + +impl Extrudable for RegularPolygonMeshBuilder { + fn perimeter(&self) -> Vec { + alloc::vec![PerimeterSegment::Flat { + indices: (0..self.sides).chain([0]).collect(), + }] + } +} + +impl Extrudable for EllipseMeshBuilder { + fn perimeter(&self) -> Vec { + alloc::vec![PerimeterSegment::Smooth { + first_normal: Vec2::Y, + last_normal: Vec2::Y, + indices: (0..self.resolution).chain([0]).collect(), + }] + } +} + +impl Extrudable for AnnulusMeshBuilder { + fn perimeter(&self) -> Vec { + let vert_count = 2 * self.resolution; + alloc::vec![ + PerimeterSegment::Smooth { + first_normal: Vec2::NEG_Y, + last_normal: Vec2::NEG_Y, + indices: (0..vert_count).step_by(2).chain([0]).rev().collect(), // Inner hole + }, + PerimeterSegment::Smooth { + first_normal: Vec2::Y, + last_normal: Vec2::Y, + indices: (1..vert_count).step_by(2).chain([1]).collect(), // Outer perimeter + }, + ] + } +} + +impl Extrudable for RhombusMeshBuilder { + fn perimeter(&self) -> Vec { + alloc::vec![PerimeterSegment::Flat { + indices: alloc::vec![0, 1, 2, 3, 0], + }] + } +} + +impl Extrudable for Triangle2dMeshBuilder { + fn perimeter(&self) -> Vec { + let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise; + if is_ccw { + alloc::vec![PerimeterSegment::Flat { + indices: alloc::vec![0, 1, 2, 0], + }] + } else { + alloc::vec![PerimeterSegment::Flat { + indices: alloc::vec![2, 1, 0, 2], + }] + } + } +} + +impl Extrudable for RectangleMeshBuilder { + fn perimeter(&self) -> Vec { + alloc::vec![PerimeterSegment::Flat { + indices: alloc::vec![0, 1, 2, 3, 0], + }] + } +} + +impl Extrudable for Capsule2dMeshBuilder { + fn perimeter(&self) -> Vec { + let resolution = self.resolution; + let top_semi_indices = (0..resolution).collect(); + let bottom_semi_indices = (resolution..(2 * resolution)).collect(); + alloc::vec![ + PerimeterSegment::Smooth { + first_normal: Vec2::X, + last_normal: Vec2::NEG_X, + indices: top_semi_indices, + }, // Top semi-circle + PerimeterSegment::Flat { + indices: alloc::vec![resolution - 1, resolution], + }, // Left edge + PerimeterSegment::Smooth { + first_normal: Vec2::NEG_X, + last_normal: Vec2::X, + indices: bottom_semi_indices, + }, // Bottom semi-circle + PerimeterSegment::Flat { + indices: alloc::vec![2 * resolution - 1, 0], + }, // Right edge + ] + } +} + +impl

Extrudable for RingMeshBuilder

where P: Primitive2d + Meshable, P::Output: Extrudable, { - fn from(value: Extrusion

) -> Self { - value.mesh().build() + /// A list of the indices each representing a part of the perimeter of the mesh. + /// + /// # Panics + /// + /// Panics if the following assumptions are not met. + /// + /// It is assumed that the inner and outer meshes have the same number of vertices. + /// If not, then the [`MeshBuilder`] of the underlying 2d primitive has generated + /// a different number of vertices for the inner and outer instances of the primitive. + /// + /// It is assumed that the `primitive_topology` of the mesh returned by + /// the underlying builder is [`PrimitiveTopology::TriangleList`] + /// and that the mesh has [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes. + fn perimeter(&self) -> Vec { + let outer_vertex_count = self + .get_vertex_attributes() + .filter(|r| r.outer_positions.len() == r.inner_positions.len()) + .expect("The inner and outer meshes should have the same number of vertices, and have required attributes") + .outer_positions + .len(); + + let mut outer_perimeter = self.outer_shape_builder.perimeter(); + let inner_perimeter = + self.inner_shape_builder + .perimeter() + .into_iter() + .rev() + .map(|segment| match segment { + PerimeterSegment::Smooth { + first_normal, + last_normal, + mut indices, + } => PerimeterSegment::Smooth { + first_normal: -last_normal, + last_normal: -first_normal, + indices: { + let outer_perimeter_vertex_count = outer_vertex_count as u32; + indices.reverse(); + for i in &mut indices { + *i += outer_perimeter_vertex_count; + } + indices + }, + }, + PerimeterSegment::Flat { mut indices } => PerimeterSegment::Flat { + indices: { + let outer_perimeter_vertex_count = outer_vertex_count as u32; + indices.reverse(); + for i in &mut indices { + *i += outer_perimeter_vertex_count; + } + indices + }, + }, + }); + + outer_perimeter.extend(inner_perimeter); + outer_perimeter } } diff --git a/crates/bevy_shapes/src/meshing/mod.rs b/crates/bevy_shapes/src/meshing/mod.rs new file mode 100644 index 0000000000000..5c96550a6df41 --- /dev/null +++ b/crates/bevy_shapes/src/meshing/mod.rs @@ -0,0 +1,12 @@ +//! [`Meshable`](bevy_mesh::Meshable) implementation for [primitive shapes](bevy_shapes::primitives). + +mod dim2; +pub use dim2::*; + +mod dim3; +pub use dim3::*; + +#[cfg(feature = "extrusion")] +mod extrusion; +#[cfg(feature = "extrusion")] +pub use extrusion::*; diff --git a/crates/bevy_math/src/primitives/dim2.rs b/crates/bevy_shapes/src/primitives/dim2.rs similarity index 99% rename from crates/bevy_math/src/primitives/dim2.rs rename to crates/bevy_shapes/src/primitives/dim2.rs index b156b459221bc..07240b1c5c1a9 100644 --- a/crates/bevy_math/src/primitives/dim2.rs +++ b/crates/bevy_shapes/src/primitives/dim2.rs @@ -4,9 +4,9 @@ use derive_more::derive::From; use thiserror::Error; use super::{Measured2d, Primitive2d, WindingOrder}; -use crate::{ +use crate::primitives::Inset; +use bevy_math::{ ops::{self, FloatPow}, - primitives::Inset, Dir2, InvalidDirectionError, Isometry2d, Ray2d, Rot2, Vec2, }; diff --git a/crates/bevy_math/src/primitives/dim3.rs b/crates/bevy_shapes/src/primitives/dim3.rs similarity index 99% rename from crates/bevy_math/src/primitives/dim3.rs rename to crates/bevy_shapes/src/primitives/dim3.rs index 8764efcff4ce5..2713da80a5784 100644 --- a/crates/bevy_math/src/primitives/dim3.rs +++ b/crates/bevy_shapes/src/primitives/dim3.rs @@ -1,16 +1,16 @@ use core::f32::consts::{FRAC_PI_3, PI}; use super::{Circle, Measured2d, Measured3d, Primitive2d, Primitive3d}; -use crate::{ +use bevy_math::{ ops::{self, FloatPow}, Dir3, InvalidDirectionError, Isometry3d, Mat3, Ray3d, Vec2, Vec3, }; +use bevy_math::Quat; #[cfg(feature = "bevy_reflect")] use bevy_reflect::{std_traits::ReflectDefault, Reflect}; #[cfg(all(feature = "serialize", feature = "bevy_reflect"))] use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; -use glam::Quat; #[cfg(feature = "alloc")] use alloc::vec::Vec; @@ -1579,8 +1579,8 @@ mod tests { // Reference values were computed by hand and/or with external tools use super::*; - use crate::{InvalidDirectionError, Quat}; use approx::assert_relative_eq; + use bevy_math::{InvalidDirectionError, Quat}; #[test] fn direction_creation() { diff --git a/crates/bevy_math/src/primitives/inset.rs b/crates/bevy_shapes/src/primitives/inset.rs similarity index 94% rename from crates/bevy_math/src/primitives/inset.rs rename to crates/bevy_shapes/src/primitives/inset.rs index 7690593ad5c66..abbe2c8cbcfdf 100644 --- a/crates/bevy_math/src/primitives/inset.rs +++ b/crates/bevy_shapes/src/primitives/inset.rs @@ -1,11 +1,7 @@ -use crate::{ - ops, - primitives::{ - Capsule2d, Circle, CircularSegment, Primitive2d, Rectangle, RegularPolygon, Rhombus, - Triangle2d, - }, - Vec2, +use crate::primitives::{ + Capsule2d, Circle, CircularSegment, Primitive2d, Rectangle, RegularPolygon, Rhombus, Triangle2d, }; +use bevy_math::{ops, Vec2}; /// A primitive that can be resized uniformly. /// diff --git a/crates/bevy_shapes/src/primitives/math.rs b/crates/bevy_shapes/src/primitives/math.rs new file mode 100644 index 0000000000000..e01c381c208a3 --- /dev/null +++ b/crates/bevy_shapes/src/primitives/math.rs @@ -0,0 +1,6 @@ +use crate::primitives::{Primitive2d, Primitive3d}; +use bevy_math::{Dir2, Dir3, Dir3A}; + +impl Primitive2d for Dir2 {} +impl Primitive3d for Dir3 {} +impl Primitive3d for Dir3A {} diff --git a/crates/bevy_math/src/primitives/mod.rs b/crates/bevy_shapes/src/primitives/mod.rs similarity index 94% rename from crates/bevy_math/src/primitives/mod.rs rename to crates/bevy_shapes/src/primitives/mod.rs index c2cb17f963123..0622f34898a86 100644 --- a/crates/bevy_math/src/primitives/mod.rs +++ b/crates/bevy_shapes/src/primitives/mod.rs @@ -8,11 +8,8 @@ mod dim3; pub use dim3::*; mod inset; pub use inset::*; -mod half_space; +mod math; mod polygon; -pub use half_space::*; -mod view_frustum; -pub use view_frustum::*; /// A marker trait for 2D primitives pub trait Primitive2d {} diff --git a/crates/bevy_math/src/primitives/polygon.rs b/crates/bevy_shapes/src/primitives/polygon.rs similarity index 99% rename from crates/bevy_math/src/primitives/polygon.rs rename to crates/bevy_shapes/src/primitives/polygon.rs index 71197d3fd8101..71ef421c7251b 100644 --- a/crates/bevy_math/src/primitives/polygon.rs +++ b/crates/bevy_shapes/src/primitives/polygon.rs @@ -5,7 +5,7 @@ use { core::cmp::Ordering, }; -use crate::Vec2; +use bevy_math::Vec2; #[derive(Debug, Clone, Copy)] #[cfg(feature = "alloc")] @@ -323,7 +323,8 @@ pub fn is_polygon_simple(vertices: &[Vec2]) -> bool { #[cfg(test)] mod tests { - use crate::{primitives::polygon::is_polygon_simple, Vec2}; + use crate::primitives::polygon::is_polygon_simple; + use bevy_math::Vec2; #[test] fn complex_polygon() { diff --git a/crates/bevy_math/src/sampling/mesh_sampling.rs b/crates/bevy_shapes/src/sampling/mesh_sampling.rs similarity index 95% rename from crates/bevy_math/src/sampling/mesh_sampling.rs rename to crates/bevy_shapes/src/sampling/mesh_sampling.rs index 33b55a80b7d6d..612ab3e4947d2 100644 --- a/crates/bevy_math/src/sampling/mesh_sampling.rs +++ b/crates/bevy_shapes/src/sampling/mesh_sampling.rs @@ -1,10 +1,9 @@ //! Functionality related to random sampling from triangle meshes. -use crate::{ - primitives::{Measured2d, Triangle3d}, - ShapeSample, Vec3, -}; +use crate::primitives::{Measured2d, Triangle3d}; +use crate::sampling::ShapeSample; use alloc::vec::Vec; +use bevy_math::Vec3; use rand::RngExt; use rand_distr::{ weighted::{Error as WeightedError, WeightedAliasIndex}, diff --git a/crates/bevy_shapes/src/sampling/mod.rs b/crates/bevy_shapes/src/sampling/mod.rs new file mode 100644 index 0000000000000..a6b37ac19547a --- /dev/null +++ b/crates/bevy_shapes/src/sampling/mod.rs @@ -0,0 +1,11 @@ +//! This module contains tools related to random sampling. +//! +//! To use this, the "rand" feature must be enabled. + +#[cfg(feature = "meshing")] +pub mod mesh_sampling; +pub mod shape_sampling; + +#[cfg(feature = "meshing")] +pub use mesh_sampling::*; +pub use shape_sampling::*; diff --git a/crates/bevy_math/src/sampling/shape_sampling.rs b/crates/bevy_shapes/src/sampling/shape_sampling.rs similarity index 99% rename from crates/bevy_math/src/sampling/shape_sampling.rs rename to crates/bevy_shapes/src/sampling/shape_sampling.rs index a2e02675c9ce8..fa5b4b15de61c 100644 --- a/crates/bevy_math/src/sampling/shape_sampling.rs +++ b/crates/bevy_shapes/src/sampling/shape_sampling.rs @@ -40,7 +40,8 @@ use core::f32::consts::{FRAC_PI_2, PI, TAU}; -use crate::{ops, primitives::*, NormedVectorSpace, ScalarField, Vec2, Vec3}; +use crate::primitives::*; +use bevy_math::{ops, NormedVectorSpace, ScalarField, Vec2, Vec3}; use rand::{ distr::{ uniform::SampleUniform, diff --git a/crates/bevy_sprite_render/src/sprite_mesh/mod.rs b/crates/bevy_sprite_render/src/sprite_mesh/mod.rs index a8238a5a09157..a6d05e8b5977f 100644 --- a/crates/bevy_sprite_render/src/sprite_mesh/mod.rs +++ b/crates/bevy_sprite_render/src/sprite_mesh/mod.rs @@ -9,8 +9,8 @@ use bevy_ecs::{ use bevy_asset::{Assets, Handle}; use bevy_image::TextureAtlasLayout; -use bevy_math::{primitives::Rectangle, vec2}; -use bevy_mesh::{Mesh, Mesh2d, MeshAttributeCompressionFlags, MeshBuilder, Meshable}; +use bevy_math::Vec2; +use bevy_mesh::{Mesh, Mesh2d, MeshAttributeCompressionFlags}; use bevy_platform::collections::HashMap; use bevy_shader::load_shader_library; @@ -50,9 +50,7 @@ fn add_mesh( ) { let quad = quad.get_or_insert_with(|| { meshes.add( - Rectangle::from_size(vec2(1.0, 1.0)) - .mesh() - .build() + Mesh::quad_mesh(Vec2::ONE) .with_removed_attribute(Mesh::ATTRIBUTE_NORMAL) .compressed_mesh( MeshAttributeCompressionFlags::COMPRESS_POSITION diff --git a/crates/bevy_sprite_render/src/tilemap_chunk/mod.rs b/crates/bevy_sprite_render/src/tilemap_chunk/mod.rs index d8353466ff7b4..55f1f529125c3 100644 --- a/crates/bevy_sprite_render/src/tilemap_chunk/mod.rs +++ b/crates/bevy_sprite_render/src/tilemap_chunk/mod.rs @@ -15,7 +15,7 @@ use bevy_ecs::{ world::DeferredWorld, }; use bevy_image::Image; -use bevy_math::{primitives::Rectangle, UVec2}; +use bevy_math::UVec2; use bevy_mesh::{Mesh, Mesh2d}; use bevy_platform::collections::HashMap; use bevy_reflect::{prelude::*, Reflect}; @@ -173,7 +173,7 @@ fn on_insert_tilemap_chunk(mut world: DeferredWorld, HookContext { entity, .. }: mesh.clone() } else { let mut meshes = world.resource_mut::>(); - meshes.add(Rectangle::from_size(mesh_size.as_vec2())) + meshes.add(Mesh::quad_mesh(mesh_size.as_vec2())) }; let mut images = world.resource_mut::>(); diff --git a/examples/2d/mesh2d_arcs.rs b/examples/2d/mesh2d_arcs.rs index af9c3ec5638b8..702c34aa71c41 100644 --- a/examples/2d/mesh2d_arcs.rs +++ b/examples/2d/mesh2d_arcs.rs @@ -10,7 +10,6 @@ use bevy::{ bounding::{Bounded2d, BoundingVolume}, Isometry2d, }, - mesh::{CircularMeshUvMode, CircularSectorMeshBuilder, CircularSegmentMeshBuilder}, prelude::*, }; diff --git a/examples/3d/3d_shapes.rs b/examples/3d/3d_shapes.rs index 5edb6c777f3cc..6fb89a532e8b7 100644 --- a/examples/3d/3d_shapes.rs +++ b/examples/3d/3d_shapes.rs @@ -73,8 +73,8 @@ fn setup( meshes.add(Cylinder::default()), meshes.add(Cone::default()), meshes.add(ConicalFrustum::default()), - meshes.add(Sphere::default().mesh().ico(5).unwrap()), - meshes.add(Sphere::default().mesh().uv(32, 18)), + meshes.add(Sphere::default().mesh_builder().ico(5).unwrap()), + meshes.add(Sphere::default().mesh_builder().uv(32, 18)), meshes.add(Segment3d::default()), meshes.add(Polyline3d::new(vec![ Vec3::new(-0.5, 0.0, 0.0), @@ -195,7 +195,14 @@ fn setup( // ground plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(50.0, 50.0).subdivisions(10))), + Mesh3d( + meshes.add( + Plane3d::default() + .mesh_builder() + .size(50.0, 50.0) + .subdivisions(10), + ), + ), MeshMaterial3d(materials.add(Color::from(SILVER))), )); diff --git a/examples/3d/3d_viewport_to_world.rs b/examples/3d/3d_viewport_to_world.rs index 231306bd2b6dc..2b165e34ed01f 100644 --- a/examples/3d/3d_viewport_to_world.rs +++ b/examples/3d/3d_viewport_to_world.rs @@ -22,7 +22,7 @@ fn draw_cursor( // Calculate a ray pointing from the camera into the world based on the cursor's position. && let Ok(ray) = camera.viewport_to_world(camera_transform, cursor_position) // Calculate if and where the ray is hitting the ground plane. - && let Some(point) = ray.plane_intersection_point(ground.translation(), InfinitePlane3d::new(ground.up())) + && let Some(point) = ray.plane_intersection_point(ground.translation(), ground.up()) { // Draw a circle just above the ground plane at that position. gizmos.circle( @@ -46,7 +46,7 @@ fn setup( ) { // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(20., 20.))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(20., 20.))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), Ground, )); diff --git a/examples/3d/anti_aliasing.rs b/examples/3d/anti_aliasing.rs index 703920b1e5050..36a13bf935298 100644 --- a/examples/3d/anti_aliasing.rs +++ b/examples/3d/anti_aliasing.rs @@ -418,7 +418,7 @@ fn setup( ) { // Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(20.0, 20.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(20.0, 20.0))), MeshMaterial3d(materials.add(Color::srgb(0.1, 0.2, 0.1))), )); diff --git a/examples/3d/auto_exposure.rs b/examples/3d/auto_exposure.rs index 0e640fa0c7e77..c88d53874cf58 100644 --- a/examples/3d/auto_exposure.rs +++ b/examples/3d/auto_exposure.rs @@ -13,7 +13,7 @@ use bevy::{ light::Skybox, - math::{cubic_splines::LinearSpline, primitives::Plane3d, vec2}, + math::{cubic_splines::LinearSpline, vec2}, post_process::auto_exposure::{ AutoExposure, AutoExposureCompensationCurve, AutoExposurePlugin, }, @@ -70,7 +70,7 @@ fn setup( normal: -Dir3::Z, half_size: Vec2::new(2.0, 0.5), } - .mesh(), + .mesh_builder(), )); // Build a dimly lit box around the camera, with a slot to see the bright skybox. diff --git a/examples/3d/blend_modes.rs b/examples/3d/blend_modes.rs index 41a91d112115c..32edc41c83b56 100644 --- a/examples/3d/blend_modes.rs +++ b/examples/3d/blend_modes.rs @@ -31,7 +31,7 @@ fn setup( asset_server: Res, ) { let base_color = Color::srgb(0.9, 0.2, 0.3); - let icosphere_mesh = meshes.add(Sphere::new(0.9).mesh().ico(7).unwrap()); + let icosphere_mesh = meshes.add(Sphere::new(0.9).mesh_builder().ico(7).unwrap()); // Opaque let opaque = commands @@ -122,7 +122,7 @@ fn setup( let black_material = materials.add(Color::BLACK); let white_material = materials.add(Color::WHITE); - let plane_mesh = meshes.add(Plane3d::default().mesh().size(2.0, 2.0)); + let plane_mesh = meshes.add(Plane3d::default().mesh_builder().size(2.0, 2.0)); for x in -3..4 { for z in -3..4 { diff --git a/examples/3d/bloom_3d.rs b/examples/3d/bloom_3d.rs index 982b59745fd45..c0993a067b89c 100644 --- a/examples/3d/bloom_3d.rs +++ b/examples/3d/bloom_3d.rs @@ -52,7 +52,7 @@ fn setup_scene( ..default() }); - let mesh = meshes.add(Sphere::new(0.4).mesh().ico(5).unwrap()); + let mesh = meshes.add(Sphere::new(0.4).mesh_builder().ico(5).unwrap()); for x in -5..5 { for z in -5..5 { diff --git a/examples/3d/camera_sub_view.rs b/examples/3d/camera_sub_view.rs index b6f9a8f39c342..dfc3fe6792d81 100644 --- a/examples/3d/camera_sub_view.rs +++ b/examples/3d/camera_sub_view.rs @@ -32,7 +32,7 @@ fn setup( // Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); diff --git a/examples/3d/clearcoat.rs b/examples/3d/clearcoat.rs index 9368b9cc50ea4..be28058538186 100644 --- a/examples/3d/clearcoat.rs +++ b/examples/3d/clearcoat.rs @@ -83,7 +83,7 @@ fn create_sphere_mesh(meshes: &mut Assets) -> Handle { // We're going to use normal maps, so make sure we've generated tangents, or // else the normal maps won't show up. - let mut sphere_mesh = Sphere::new(1.0).mesh().build(); + let mut sphere_mesh = Sphere::new(1.0).mesh_builder().build(); sphere_mesh .generate_tangents() .expect("Failed to generate tangents"); diff --git a/examples/3d/clustered_decal_maps.rs b/examples/3d/clustered_decal_maps.rs index 85c0daf9dcd56..87f3f3943e266 100644 --- a/examples/3d/clustered_decal_maps.rs +++ b/examples/3d/clustered_decal_maps.rs @@ -196,7 +196,7 @@ fn spawn_plane_mesh( normal: Dir3::NEG_Z, half_size: Vec2::splat(PLANE_HALF_SIZE), } - .mesh() + .mesh_builder() .build() .with_duplicated_vertices() .with_computed_flat_normals() diff --git a/examples/3d/contact_shadows.rs b/examples/3d/contact_shadows.rs index bb3dc82903b17..d2b79ab921e1e 100644 --- a/examples/3d/contact_shadows.rs +++ b/examples/3d/contact_shadows.rs @@ -229,7 +229,7 @@ fn setup(mut commands: Commands, asset_server: Res) { ); commands.spawn(( - Mesh3d(asset_server.add(Circle::default().mesh().into())), + Mesh3d(asset_server.add(Circle::default().mesh_builder().into())), MeshMaterial3d(asset_server.add(StandardMaterial { base_color: Color::srgb(0.06, 0.06, 0.06), ..default() diff --git a/examples/3d/deferred_rendering.rs b/examples/3d/deferred_rendering.rs index 87fea95aa39d0..8e9adfd726e8d 100644 --- a/examples/3d/deferred_rendering.rs +++ b/examples/3d/deferred_rendering.rs @@ -88,12 +88,12 @@ fn setup( // Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(50.0, 50.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(50.0, 50.0))), MeshMaterial3d(forward_mat_h.clone()), )); let cube_h = meshes.add(Cuboid::new(0.1, 0.1, 0.1)); - let sphere_h = meshes.add(Sphere::new(0.125).mesh().uv(32, 18)); + let sphere_h = meshes.add(Sphere::new(0.125).mesh_builder().uv(32, 18)); // Cubes commands.spawn(( diff --git a/examples/3d/irradiance_volumes.rs b/examples/3d/irradiance_volumes.rs index ac1c837ddc237..3647e33579619 100644 --- a/examples/3d/irradiance_volumes.rs +++ b/examples/3d/irradiance_volumes.rs @@ -469,9 +469,7 @@ fn handle_mouse_clicks( let Ok(ray) = camera.viewport_to_world(camera_transform, mouse_position) else { return; }; - let Some(plane_intersection) = - ray.plane_intersection_point(Vec3::ZERO, InfinitePlane3d::new(Vec3::Y)) - else { + let Some(plane_intersection) = ray.plane_intersection_point(Vec3::ZERO, Dir3::Y) else { return; }; // Move all the main objects. @@ -492,7 +490,7 @@ impl FromWorld for ExampleAssets { AnimationGraph::from_clip(fox_animation.clone()); ExampleAssets { - main_sphere: world.add_asset(Sphere::default().mesh().uv(32, 18)), + main_sphere: world.add_asset(Sphere::default().mesh_builder().uv(32, 18)), fox: world.load_asset(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")), main_sphere_material: world.add_asset(Color::from(SILVER)), main_scene: world.load_asset( diff --git a/examples/3d/light_probe_blending.rs b/examples/3d/light_probe_blending.rs index 535e11accf10b..e6b79b251a3f3 100644 --- a/examples/3d/light_probe_blending.rs +++ b/examples/3d/light_probe_blending.rs @@ -271,7 +271,7 @@ fn spawn_reflective_sphere( material: Handle, ) { // Create a mesh. - let sphere = meshes.add(Sphere::default().mesh().uv(32, 18)); + let sphere = meshes.add(Sphere::default().mesh_builder().uv(32, 18)); // Spawn the sphere. commands.spawn(( @@ -296,7 +296,7 @@ fn spawn_reflective_prism( Cuboid { half_size: vec3(2.0, 1.0, 10.0), } - .mesh() + .mesh_builder() .build() // We use flat normals so that the surface appears flat, not curved. .with_duplicated_vertices() diff --git a/examples/3d/lighting.rs b/examples/3d/lighting.rs index 2fa77fb037cba..adf389a992562 100644 --- a/examples/3d/lighting.rs +++ b/examples/3d/lighting.rs @@ -48,7 +48,7 @@ fn setup( ) { // ground plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(10.0, 10.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(10.0, 10.0))), MeshMaterial3d(materials.add(StandardMaterial { base_color: Color::WHITE, perceptual_roughness: 1.0, @@ -109,7 +109,7 @@ fn setup( )); // sphere commands.spawn(( - Mesh3d(meshes.add(Sphere::new(0.5).mesh().uv(32, 18))), + Mesh3d(meshes.add(Sphere::new(0.5).mesh_builder().uv(32, 18))), MeshMaterial3d(materials.add(StandardMaterial { base_color: LIMEGREEN.into(), ..default() @@ -136,7 +136,7 @@ fn setup( }, Transform::from_xyz(1.0, 2.0, 0.0), children![( - Mesh3d(meshes.add(Sphere::new(0.1).mesh().uv(32, 18))), + Mesh3d(meshes.add(Sphere::new(0.1).mesh_builder().uv(32, 18))), MeshMaterial3d(materials.add(StandardMaterial { base_color: RED.into(), emissive: LinearRgba::new(4.0, 0.0, 0.0, 0.0), @@ -177,7 +177,7 @@ fn setup( }, Transform::from_xyz(0.0, 4.0, 0.0), children![( - Mesh3d(meshes.add(Sphere::new(0.1).mesh().uv(32, 18))), + Mesh3d(meshes.add(Sphere::new(0.1).mesh_builder().uv(32, 18))), MeshMaterial3d(materials.add(StandardMaterial { base_color: BLUE.into(), emissive: LinearRgba::new(0.0, 0.0, 713.0, 0.0), diff --git a/examples/3d/meshlet.rs b/examples/3d/meshlet.rs index 892d0cd515c0b..866f822c1bf5c 100644 --- a/examples/3d/meshlet.rs +++ b/examples/3d/meshlet.rs @@ -107,7 +107,7 @@ fn setup( } commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(standard_materials.add(StandardMaterial { base_color: Color::WHITE, perceptual_roughness: 1.0, diff --git a/examples/3d/mirror.rs b/examples/3d/mirror.rs index 584aa12a8a634..d4fa2b63621c1 100644 --- a/examples/3d/mirror.rs +++ b/examples/3d/mirror.rs @@ -298,7 +298,7 @@ fn spawn_mirror( .with_translation(MIRROR_POSITION); commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(1.0, 1.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(1.0, 1.0))), MeshMaterial3d(screen_space_texture_materials.add(ExtendedMaterial { base: StandardMaterial { base_color: Color::BLACK, @@ -465,7 +465,7 @@ fn move_fox_on_mouse_down( let Ok(ray) = camera.viewport_to_world(camera_transform, mouse_position) else { return; }; - let Some(ray_distance) = ray.intersect_plane(Vec3::ZERO, InfinitePlane3d::new(Vec3::Y)) else { + let Some(ray_distance) = ray.intersect_plane(Vec3::ZERO, Dir3::Y) else { return; }; let plane_intersection = ray.origin + ray.direction.normalize() * ray_distance; diff --git a/examples/3d/occlusion_culling.rs b/examples/3d/occlusion_culling.rs index c9b9fd9cfa2d9..8e70a83d4663e 100644 --- a/examples/3d/occlusion_culling.rs +++ b/examples/3d/occlusion_culling.rs @@ -285,7 +285,7 @@ fn spawn_small_cubes( // Create the sphere mesh, and extract the positions of its vertices. let sphere = Sphere::new(OUTER_RADIUS) - .mesh() + .mesh_builder() .ico(OUTER_SUBDIVISION_COUNT) .unwrap(); let sphere_positions = sphere.attribute(Mesh::ATTRIBUTE_POSITION).unwrap(); diff --git a/examples/3d/order_independent_transparency.rs b/examples/3d/order_independent_transparency.rs index c5ffb518414c7..6222e6ce05425 100644 --- a/examples/3d/order_independent_transparency.rs +++ b/examples/3d/order_independent_transparency.rs @@ -387,7 +387,7 @@ fn spawn_spheres(commands: &mut Commands, resources: &mut SceneResources) { let offset = Vec3::new(0.0, 0.0, 0.0); - let sphere_handle = meshes.add(Sphere::new(2.0).mesh()); + let sphere_handle = meshes.add(Sphere::new(2.0).mesh_builder()); let alpha = 0.25; @@ -431,7 +431,7 @@ fn spawn_quads(commands: &mut Commands, resources: &mut SceneResources) { let meshes = &mut resources.meshes; let materials = &mut resources.materials; - let quad_handle = meshes.add(Rectangle::new(3.0, 3.0).mesh()); + let quad_handle = meshes.add(Rectangle::new(3.0, 3.0).mesh_builder()); let render_layers = RenderLayers::layer(1); let xform = |x, y, z| { Transform::from_rotation(Quat::from_rotation_y(0.5)) @@ -499,8 +499,8 @@ fn spawn_occlusion_test(commands: &mut Commands, resources: &mut SceneResources) let meshes = &mut resources.meshes; let materials = &mut resources.materials; - let sphere_handle = meshes.add(Sphere::new(1.0).mesh()); - let cube_handle = meshes.add(Cuboid::from_size(Vec3::ONE).mesh()); + let sphere_handle = meshes.add(Sphere::new(1.0).mesh_builder()); + let cube_handle = meshes.add(Cuboid::from_size(Vec3::ONE).mesh_builder()); let cube_material = materials.add(Color::srgb(0.8, 0.7, 0.6)); let render_layers = RenderLayers::layer(1); diff --git a/examples/3d/orthographic.rs b/examples/3d/orthographic.rs index 1349e49c394b9..8efb6e801daca 100644 --- a/examples/3d/orthographic.rs +++ b/examples/3d/orthographic.rs @@ -30,7 +30,7 @@ fn setup( // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); // cubes diff --git a/examples/3d/parallax_mapping.rs b/examples/3d/parallax_mapping.rs index ef7e7cb4c1fb5..445701fc00a45 100644 --- a/examples/3d/parallax_mapping.rs +++ b/examples/3d/parallax_mapping.rs @@ -223,7 +223,7 @@ fn setup( )); // represent the light source as a sphere - let mesh = meshes.add(Sphere::new(0.05).mesh().ico(3).unwrap()); + let mesh = meshes.add(Sphere::new(0.05).mesh_builder().ico(3).unwrap()); // light commands.spawn(( @@ -237,7 +237,7 @@ fn setup( // Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(10.0, 10.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(10.0, 10.0))), MeshMaterial3d(materials.add(StandardMaterial { // standard material derived from dark green, but // with roughness and reflectance set. diff --git a/examples/3d/pccm.rs b/examples/3d/pccm.rs index 79f8ffb80b2f8..81f407c16626c 100644 --- a/examples/3d/pccm.rs +++ b/examples/3d/pccm.rs @@ -110,7 +110,7 @@ fn spawn_inner_cube( Cuboid { half_size: Vec3::new(5.0, 1.0, 2.0), } - .mesh() + .mesh_builder() .build() .with_duplicated_vertices() .with_computed_flat_normals(), diff --git a/examples/3d/rect_light.rs b/examples/3d/rect_light.rs index e09ca2e341c2f..b017b64e15713 100644 --- a/examples/3d/rect_light.rs +++ b/examples/3d/rect_light.rs @@ -40,7 +40,7 @@ fn setup( }); commands.insert_resource(FloorMaterial(floor_material.clone())); commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(20.0, 20.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(20.0, 20.0))), MeshMaterial3d(floor_material), )); diff --git a/examples/3d/reflection_probes.rs b/examples/3d/reflection_probes.rs index e216af22d5a17..a805fa30539c7 100644 --- a/examples/3d/reflection_probes.rs +++ b/examples/3d/reflection_probes.rs @@ -137,7 +137,7 @@ fn spawn_sphere( app_status: &AppStatus, ) { // Create a sphere mesh. - let sphere_mesh = meshes.add(Sphere::new(1.0).mesh().ico(7).unwrap()); + let sphere_mesh = meshes.add(Sphere::new(1.0).mesh_builder().ico(7).unwrap()); // Create a sphere. commands.spawn(( diff --git a/examples/3d/rotate_environment_map.rs b/examples/3d/rotate_environment_map.rs index a9ddecc6c5886..6ead18e4209d1 100644 --- a/examples/3d/rotate_environment_map.rs +++ b/examples/3d/rotate_environment_map.rs @@ -51,7 +51,7 @@ fn create_sphere_mesh(meshes: &mut Assets) -> Handle { // We're going to use normal maps, so make sure we've generated tangents, or // else the normal maps won't show up. - let mut sphere_mesh = Sphere::new(1.0).mesh().build(); + let mut sphere_mesh = Sphere::new(1.0).mesh_builder().build(); sphere_mesh .generate_tangents() .expect("Failed to generate tangents"); diff --git a/examples/3d/shadow_biases.rs b/examples/3d/shadow_biases.rs index 6bbb74bed45f6..b6ba7520b1095 100644 --- a/examples/3d/shadow_biases.rs +++ b/examples/3d/shadow_biases.rs @@ -91,7 +91,13 @@ fn setup( // ground plane let plane_size = 2.0 * spawn_plane_depth; commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(plane_size, plane_size))), + Mesh3d( + meshes.add( + Plane3d::default() + .mesh_builder() + .size(plane_size, plane_size), + ), + ), MeshMaterial3d(white_handle), )); diff --git a/examples/3d/shadow_caster_receiver.rs b/examples/3d/shadow_caster_receiver.rs index 00c4242e506bd..4a1b75498aafe 100644 --- a/examples/3d/shadow_caster_receiver.rs +++ b/examples/3d/shadow_caster_receiver.rs @@ -56,7 +56,7 @@ fn setup( // floating plane - initially not a shadow receiver and not a caster commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(20.0, 20.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(20.0, 20.0))), MeshMaterial3d(materials.add(Color::from(LIME))), Transform::from_xyz(0.0, 1.0, -10.0), NotShadowCaster, @@ -65,7 +65,7 @@ fn setup( // lower ground plane - initially a shadow receiver commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(20.0, 20.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(20.0, 20.0))), MeshMaterial3d(white_handle), )); diff --git a/examples/3d/solari.rs b/examples/3d/solari.rs index 6cc31fec18fcd..b81b72e1a4839 100644 --- a/examples/3d/solari.rs +++ b/examples/3d/solari.rs @@ -215,7 +215,7 @@ fn setup_many_lights( let mut rng = ChaCha8Rng::seed_from_u64(42); let mut plane_mesh = Plane3d::default() - .mesh() + .mesh_builder() .size(400.0, 400.0) .build() .with_generated_tangents() @@ -229,14 +229,14 @@ fn setup_many_lights( let plane_mesh = meshes.add(plane_mesh); let cube_mesh = meshes.add( Cuboid::default() - .mesh() + .mesh_builder() .build() .with_generated_tangents() .unwrap(), ); let sphere_mesh = meshes.add( Sphere::new(1.0) - .mesh() + .mesh_builder() .build() .with_generated_tangents() .unwrap(), diff --git a/examples/3d/specular_tint.rs b/examples/3d/specular_tint.rs index 81655cfebd570..fc71dbdf35529 100644 --- a/examples/3d/specular_tint.rs +++ b/examples/3d/specular_tint.rs @@ -102,7 +102,7 @@ fn setup( // Spawn the sphere. commands.spawn(( Transform::from_rotation(Quat::from_rotation_x(PI * 0.5)), - Mesh3d(meshes.add(Sphere::default().mesh().uv(32, 18))), + Mesh3d(meshes.add(Sphere::default().mesh_builder().uv(32, 18))), MeshMaterial3d(standard_materials.add(StandardMaterial { // We want only reflected specular light here, so we set the base // color as black. diff --git a/examples/3d/spherical_area_lights.rs b/examples/3d/spherical_area_lights.rs index f4b244debf0cc..24d7764642b0a 100644 --- a/examples/3d/spherical_area_lights.rs +++ b/examples/3d/spherical_area_lights.rs @@ -26,7 +26,7 @@ fn setup( // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(100.0, 100.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(100.0, 100.0))), MeshMaterial3d(materials.add(StandardMaterial { base_color: Color::srgb(0.2, 0.2, 0.2), perceptual_roughness: 0.08, @@ -39,7 +39,7 @@ fn setup( let radius_range = 0.0..0.4; let pos_len = position_range.end - position_range.start; let radius_len = radius_range.end - radius_range.start; - let mesh = meshes.add(Sphere::new(1.0).mesh().uv(120, 64)); + let mesh = meshes.add(Sphere::new(1.0).mesh_builder().uv(120, 64)); for i in 0..COUNT { let percent = i as f32 / COUNT as f32; diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index 33d77830f13b8..ef3163c9c2bd1 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -23,7 +23,7 @@ fn setup( ) { // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(100.0, 100.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(100.0, 100.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); diff --git a/examples/3d/spotlight.rs b/examples/3d/spotlight.rs index 38e38f5a769d5..8e5ed71f70217 100644 --- a/examples/3d/spotlight.rs +++ b/examples/3d/spotlight.rs @@ -42,7 +42,7 @@ fn setup( ) { // ground plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(100.0, 100.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(100.0, 100.0))), MeshMaterial3d(materials.add(Color::WHITE)), Movable, )); @@ -71,8 +71,8 @@ fn setup( .take(40), ); - let sphere_mesh = meshes.add(Sphere::new(0.05).mesh().uv(32, 18)); - let sphere_mesh_direction = meshes.add(Sphere::new(0.1).mesh().uv(32, 18)); + let sphere_mesh = meshes.add(Sphere::new(0.05).mesh_builder().uv(32, 18)); + let sphere_mesh_direction = meshes.add(Sphere::new(0.1).mesh_builder().uv(32, 18)); let red_emissive = materials.add(StandardMaterial { base_color: RED.into(), emissive: LinearRgba::new(1.0, 0.0, 0.0, 0.0), diff --git a/examples/3d/ssao.rs b/examples/3d/ssao.rs index 3de273ed71889..98c502ea1f5bf 100644 --- a/examples/3d/ssao.rs +++ b/examples/3d/ssao.rs @@ -58,7 +58,7 @@ fn setup( Transform::from_xyz(1.0, 0.0, 0.0), )); commands.spawn(( - Mesh3d(meshes.add(Sphere::new(0.4).mesh().uv(72, 36))), + Mesh3d(meshes.add(Sphere::new(0.4).mesh_builder().uv(72, 36))), MeshMaterial3d(materials.add(StandardMaterial { base_color: Color::srgb(0.4, 0.4, 0.4), perceptual_roughness: 1.0, diff --git a/examples/3d/transmission.rs b/examples/3d/transmission.rs index 3e969ffdfa3ac..bf57585e90a86 100644 --- a/examples/3d/transmission.rs +++ b/examples/3d/transmission.rs @@ -64,10 +64,10 @@ fn setup( mut materials: ResMut>, asset_server: Res, ) { - let icosphere_mesh = meshes.add(Sphere::new(0.9).mesh().ico(7).unwrap()); + let icosphere_mesh = meshes.add(Sphere::new(0.9).mesh_builder().ico(7).unwrap()); let cube_mesh = meshes.add(Cuboid::new(0.7, 0.7, 0.7)); - let plane_mesh = meshes.add(Plane3d::default().mesh().size(2.0, 2.0)); - let cylinder_mesh = meshes.add(Cylinder::new(0.5, 2.0).mesh().resolution(50)); + let plane_mesh = meshes.add(Plane3d::default().mesh_builder().size(2.0, 2.0)); + let cylinder_mesh = meshes.add(Cylinder::new(0.5, 2.0).mesh_builder().resolution(50)); // Cube #1 commands.spawn(( diff --git a/examples/3d/transparency_3d.rs b/examples/3d/transparency_3d.rs index c12ff500a595c..fa678cd32f8e9 100644 --- a/examples/3d/transparency_3d.rs +++ b/examples/3d/transparency_3d.rs @@ -19,13 +19,13 @@ fn setup( ) { // Opaque plane, uses `alpha_mode: Opaque` by default commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(6.0, 6.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(6.0, 6.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); // Transparent sphere, uses `alpha_mode: Mask(f32)` commands.spawn(( - Mesh3d(meshes.add(Sphere::new(0.5).mesh().ico(3).unwrap())), + Mesh3d(meshes.add(Sphere::new(0.5).mesh_builder().ico(3).unwrap())), MeshMaterial3d(materials.add(StandardMaterial { // Alpha channel of the color controls transparency. // We set it to 0.0 here, because it will be changed over time in the @@ -42,7 +42,7 @@ fn setup( // Transparent unlit sphere, uses `alpha_mode: Mask(f32)` commands.spawn(( - Mesh3d(meshes.add(Sphere::new(0.5).mesh().ico(3).unwrap())), + Mesh3d(meshes.add(Sphere::new(0.5).mesh_builder().ico(3).unwrap())), MeshMaterial3d(materials.add(StandardMaterial { base_color: Color::srgba(0.2, 0.7, 0.1, 0.0), alpha_mode: AlphaMode::Mask(0.1), @@ -75,7 +75,7 @@ fn setup( // Opaque sphere commands.spawn(( - Mesh3d(meshes.add(Sphere::new(0.5).mesh().ico(3).unwrap())), + Mesh3d(meshes.add(Sphere::new(0.5).mesh_builder().ico(3).unwrap())), MeshMaterial3d(materials.add(Color::srgb(0.7, 0.2, 0.1))), Transform::from_xyz(0.0, 0.5, -1.5), )); diff --git a/examples/3d/two_passes.rs b/examples/3d/two_passes.rs index 7e6658a066556..d8d4ee6aa3541 100644 --- a/examples/3d/two_passes.rs +++ b/examples/3d/two_passes.rs @@ -17,7 +17,7 @@ fn setup( ) { // Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); diff --git a/examples/3d/vertex_colors.rs b/examples/3d/vertex_colors.rs index 7550192ac94f9..74293dbfeb225 100644 --- a/examples/3d/vertex_colors.rs +++ b/examples/3d/vertex_colors.rs @@ -17,7 +17,7 @@ fn setup( ) { // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); // cube diff --git a/examples/3d/visibility_range.rs b/examples/3d/visibility_range.rs index 2ce0d221eb9c8..de4899884b267 100644 --- a/examples/3d/visibility_range.rs +++ b/examples/3d/visibility_range.rs @@ -118,7 +118,7 @@ fn setup( ) { // Spawn a plane. commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(50.0, 50.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(50.0, 50.0))), MeshMaterial3d(materials.add(Color::srgb(0.1, 0.2, 0.1))), )); diff --git a/examples/3d/wireframe.rs b/examples/3d/wireframe.rs index 74376ab0b4c87..a60bf0462cab2 100644 --- a/examples/3d/wireframe.rs +++ b/examples/3d/wireframe.rs @@ -98,7 +98,7 @@ fn setup( // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(materials.add(Color::from(BLUE))), // You can insert this component without the `Wireframe` component // to override the color of the global wireframe for this mesh diff --git a/examples/animation/animated_mesh.rs b/examples/animation/animated_mesh.rs index 544f828ba609b..399f9f8ca3ea6 100644 --- a/examples/animation/animated_mesh.rs +++ b/examples/animation/animated_mesh.rs @@ -111,7 +111,7 @@ fn setup_camera_and_environment( // Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(500000.0, 500000.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(500000.0, 500000.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); diff --git a/examples/animation/animated_mesh_control.rs b/examples/animation/animated_mesh_control.rs index 412b80ad8df96..b80b11b717a69 100644 --- a/examples/animation/animated_mesh_control.rs +++ b/examples/animation/animated_mesh_control.rs @@ -56,7 +56,7 @@ fn setup( // Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(500000.0, 500000.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(500000.0, 500000.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); diff --git a/examples/animation/animated_mesh_events.rs b/examples/animation/animated_mesh_events.rs index b4b924b4de0cf..ec4a13a7b1a7b 100644 --- a/examples/animation/animated_mesh_events.rs +++ b/examples/animation/animated_mesh_events.rs @@ -105,7 +105,7 @@ fn setup( // Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(500000.0, 500000.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(500000.0, 500000.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); diff --git a/examples/animation/eased_motion.rs b/examples/animation/eased_motion.rs index 3a946199a1740..5b38a1b76c5bf 100644 --- a/examples/animation/eased_motion.rs +++ b/examples/animation/eased_motion.rs @@ -64,7 +64,7 @@ fn setup( // Ground plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(50., 50.))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(50., 50.))), MeshMaterial3d(materials.add(Color::from(SILVER))), )); diff --git a/examples/asset/compressed_image_saver.rs b/examples/asset/compressed_image_saver.rs index d727a02044f49..748a77bf6707c 100644 --- a/examples/asset/compressed_image_saver.rs +++ b/examples/asset/compressed_image_saver.rs @@ -51,11 +51,11 @@ fn spawn_scene( let diffuse_env_map = asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"); let specular_env_map = asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"); - let floor_mesh = meshes.add(Circle::new(4.0).mesh().resolution(128).build()); + let floor_mesh = meshes.add(Circle::new(4.0).mesh_builder().resolution(128).build()); let sphere_mesh = meshes.add( Sphere::new(1.0) - .mesh() + .mesh_builder() .kind(SphereKind::Ico { subdivisions: 50 }) .build() .with_generated_tangents() diff --git a/examples/asset/multi_asset_sync.rs b/examples/asset/multi_asset_sync.rs index 1e52d0061b823..2eed4ed440233 100644 --- a/examples/asset/multi_asset_sync.rs +++ b/examples/asset/multi_asset_sync.rs @@ -207,7 +207,7 @@ fn setup_scene( // Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(50000.0, 50000.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(50000.0, 50000.0))), MeshMaterial3d(materials.add(Color::srgb(0.7, 0.2, 0.2))), Loading, )); @@ -231,7 +231,7 @@ fn wait_on_load( ) { // Change color of plane to green commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(50000.0, 50000.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(50000.0, 50000.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), Transform::from_translation(Vec3::Z * -0.01), )); diff --git a/examples/audio/spatial_audio_3d.rs b/examples/audio/spatial_audio_3d.rs index b921fe6fcfd10..a1f144d947599 100644 --- a/examples/audio/spatial_audio_3d.rs +++ b/examples/audio/spatial_audio_3d.rs @@ -26,7 +26,7 @@ fn setup( // sound emitter commands.spawn(( - Mesh3d(meshes.add(Sphere::new(0.2).mesh().uv(32, 18))), + Mesh3d(meshes.add(Sphere::new(0.2).mesh_builder().uv(32, 18))), MeshMaterial3d(materials.add(Color::from(BLUE))), Transform::from_xyz(0.0, 0.0, 0.0), Emitter::default(), diff --git a/examples/camera/camera_orbit.rs b/examples/camera/camera_orbit.rs index a4d2fafd3001d..b79fc4ef13f4a 100644 --- a/examples/camera/camera_orbit.rs +++ b/examples/camera/camera_orbit.rs @@ -56,7 +56,7 @@ fn setup( commands.spawn(( Name::new("Plane"), - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(materials.add(StandardMaterial { base_color: Color::srgb(0.3, 0.5, 0.3), // Turning off culling keeps the plane visible when viewed from beneath. diff --git a/examples/camera/projection_zoom.rs b/examples/camera/projection_zoom.rs index 3797249f33ca5..5b503496fff5d 100644 --- a/examples/camera/projection_zoom.rs +++ b/examples/camera/projection_zoom.rs @@ -70,7 +70,7 @@ fn setup( commands.spawn(( Name::new("Plane"), - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(materials.add(StandardMaterial { base_color: Color::srgb(0.3, 0.5, 0.3), // Turning off culling keeps the plane visible when viewed from beneath. diff --git a/examples/ecs/error_handling.rs b/examples/ecs/error_handling.rs index ca52b4f6dbad8..6d509e1f8ced0 100644 --- a/examples/ecs/error_handling.rs +++ b/examples/ecs/error_handling.rs @@ -2,7 +2,6 @@ //! syntax. use bevy::ecs::{entity::SpawnError, error::warn, world::DeferredWorld}; -use bevy::math::sampling::UniformMeshSampler; use bevy::prelude::*; use chacha20::ChaCha8Rng; @@ -66,7 +65,7 @@ fn setup( // Make a plane for establishing space. commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(12.0, 12.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(12.0, 12.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), Transform::from_xyz(0.0, -2.5, 0.0), )); @@ -87,7 +86,7 @@ fn setup( )); // Create a new sphere mesh: - let mut sphere_mesh = Sphere::new(1.0).mesh().ico(7)?; + let mut sphere_mesh = Sphere::new(1.0).mesh_builder().ico(7)?; sphere_mesh.generate_tangents()?; // Spawn the mesh into the scene: @@ -98,11 +97,13 @@ fn setup( )); // Generate random sample points: - let triangles = sphere_mesh.triangles()?; + let triangles = sphere_mesh + .triangles()? + .map(|vertices| Triangle3d { vertices }); let distribution = UniformMeshSampler::try_new(triangles)?; // Setup sample points: - let point_mesh = meshes.add(Sphere::new(0.01).mesh().ico(3)?); + let point_mesh = meshes.add(Sphere::new(0.01).mesh_builder().ico(3)?); let point_material = materials.add(StandardMaterial { base_color: Srgba::RED.into(), emissive: LinearRgba::rgb(1.0, 0.0, 0.0), diff --git a/examples/ecs/iter_combinations.rs b/examples/ecs/iter_combinations.rs index 0e998ed508b7f..c41d4cce4548d 100644 --- a/examples/ecs/iter_combinations.rs +++ b/examples/ecs/iter_combinations.rs @@ -41,7 +41,7 @@ fn generate_bodies( mut meshes: ResMut>, mut materials: ResMut>, ) { - let mesh = meshes.add(Sphere::new(1.0).mesh().ico(3).unwrap()); + let mesh = meshes.add(Sphere::new(1.0).mesh_builder().ico(3).unwrap()); let color_range = 0.5..1.0; let vel_range = -0.5..0.5; @@ -94,7 +94,7 @@ fn generate_bodies( commands .spawn(( BodyBundle { - mesh: Mesh3d(meshes.add(Sphere::new(1.0).mesh().ico(5).unwrap())), + mesh: Mesh3d(meshes.add(Sphere::new(1.0).mesh_builder().ico(5).unwrap())), material: MeshMaterial3d(materials.add(StandardMaterial { base_color: ORANGE_RED.into(), emissive: LinearRgba::from(ORANGE_RED) * 2., diff --git a/examples/gizmos/3d_gizmos.rs b/examples/gizmos/3d_gizmos.rs index 9da5f5a30a72a..07efd9d197b84 100644 --- a/examples/gizmos/3d_gizmos.rs +++ b/examples/gizmos/3d_gizmos.rs @@ -56,7 +56,7 @@ fn setup( )); // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); // cube diff --git a/examples/gizmos/axes.rs b/examples/gizmos/axes.rs index 71fd1ece46882..daf6920371c6b 100644 --- a/examples/gizmos/axes.rs +++ b/examples/gizmos/axes.rs @@ -86,7 +86,7 @@ fn setup( // A plane to give a sense of place commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(20., 20.))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(20., 20.))), MeshMaterial3d(materials.add(Color::srgb(0.1, 0.1, 0.1))), Transform::from_xyz(0., -2., 0.), )); diff --git a/examples/gizmos/transform_gizmo.rs b/examples/gizmos/transform_gizmo.rs index ee21a21d1152d..99cce5eb339f7 100644 --- a/examples/gizmos/transform_gizmo.rs +++ b/examples/gizmos/transform_gizmo.rs @@ -49,7 +49,7 @@ fn setup( // Ground plane (not pickable) commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(10.0, 10.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(10.0, 10.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.3, 0.3))), Pickable::IGNORE, )); diff --git a/examples/gltf/gltf_extension_animation_graph.rs b/examples/gltf/gltf_extension_animation_graph.rs index c5d89ff646d90..db6386bfb4c85 100644 --- a/examples/gltf/gltf_extension_animation_graph.rs +++ b/examples/gltf/gltf_extension_animation_graph.rs @@ -99,7 +99,7 @@ fn setup_camera_and_environment( // Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(500000.0, 500000.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(500000.0, 500000.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); diff --git a/examples/large_scenes/mipmap_generator/examples/test_compression.rs b/examples/large_scenes/mipmap_generator/examples/test_compression.rs index 8d890de3d006d..76e3d14c1a1f8 100644 --- a/examples/large_scenes/mipmap_generator/examples/test_compression.rs +++ b/examples/large_scenes/mipmap_generator/examples/test_compression.rs @@ -65,7 +65,7 @@ fn setup( let mut mat_rgba = StandardMaterial::from(images.add(image_rgba)); mat_rgba.unlit = true; - let plane_h = meshes.add(Plane3d::default().mesh().size(20.0, 30.0)); + let plane_h = meshes.add(Plane3d::default().mesh_builder().size(20.0, 30.0)); // planes commands.spawn(( diff --git a/examples/large_scenes/mipmap_generator/examples/test_image.rs b/examples/large_scenes/mipmap_generator/examples/test_image.rs index 250a63441e36a..bb75b7dc7d4e3 100644 --- a/examples/large_scenes/mipmap_generator/examples/test_image.rs +++ b/examples/large_scenes/mipmap_generator/examples/test_image.rs @@ -28,7 +28,7 @@ fn setup( // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(20.0, 20.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(20.0, 20.0))), MeshMaterial3d(materials.add(StandardMaterial::from(images.add(image)))), )); // light diff --git a/examples/math/custom_primitives.rs b/examples/math/custom_primitives.rs index 5db89f0be85f5..6a8c2d3066278 100644 --- a/examples/math/custom_primitives.rs +++ b/examples/math/custom_primitives.rs @@ -12,12 +12,9 @@ use bevy::{ color::palettes::css::{RED, WHITE}, input::common_conditions::{input_just_pressed, input_toggle_active}, math::{ - bounding::{ - Aabb2d, Bounded2d, Bounded3d, BoundedExtrusion, BoundingCircle, BoundingVolume, - }, + bounding::{Aabb2d, Bounded2d, Bounded3d, BoundingCircle, BoundingVolume}, Isometry2d, }, - mesh::{Extrudable, ExtrusionBuilder, PerimeterSegment}, prelude::*, }; @@ -179,7 +176,7 @@ fn setup( // Spawn the 2D heart commands.spawn(( // We can use the methods defined on the `MeshBuilder` to customize the mesh. - Mesh3d(meshes.add(HEART.mesh().resolution(50))), + Mesh3d(meshes.add(HEART.mesh_builder().resolution(50))), MeshMaterial3d(materials.add(StandardMaterial { emissive: RED.into(), base_color: RED.into(), @@ -194,7 +191,7 @@ fn setup( // Spawn the 2D heart ring commands.spawn(( // We can use the methods defined on the `MeshBuilder` to customize the mesh. - Mesh3d(meshes.add(RING.mesh().with_inner(|heart| heart.resolution(50)))), + Mesh3d(meshes.add(RING.mesh_builder().with_inner(|heart| heart.resolution(50)))), MeshMaterial3d(materials.add(StandardMaterial { emissive: RED.into(), base_color: RED.into(), @@ -209,7 +206,7 @@ fn setup( // Spawn an extrusion of the heart commands.spawn(( // We can set a custom resolution for the round parts of the extrusion as well. - Mesh3d(meshes.add(EXTRUSION.mesh().resolution(50))), + Mesh3d(meshes.add(EXTRUSION.mesh_builder().resolution(50))), MeshMaterial3d(materials.add(StandardMaterial { base_color: RED.into(), ..Default::default() @@ -226,7 +223,7 @@ fn setup( Mesh3d( meshes.add( RING_EXTRUSION - .mesh() + .mesh_builder() .with_inner(|ring| ring.with_inner(|heart| heart.resolution(50))), ), ), @@ -473,7 +470,7 @@ impl Meshable for Heart { // The `MeshBuilder` can be used to create the actual mesh for that primitive. type Output = HeartMeshBuilder; - fn mesh(&self) -> Self::Output { + fn mesh_builder(&self) -> Self::Output { Self::Output { heart: *self, resolution: 32, diff --git a/examples/math/random_sampling.rs b/examples/math/random_sampling.rs index 7aa1b16daf1fa..eeacd527cef20 100644 --- a/examples/math/random_sampling.rs +++ b/examples/math/random_sampling.rs @@ -3,7 +3,6 @@ use bevy::{ input::mouse::{AccumulatedMouseMotion, MouseButtonInput}, math::prelude::*, - mesh::SphereKind, prelude::*, }; use chacha20::ChaCha8Rng; @@ -59,7 +58,7 @@ fn setup( // Make a plane for establishing space. commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(12.0, 12.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(12.0, 12.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), Transform::from_xyz(0.0, -2.5, 0.0), )); @@ -98,7 +97,7 @@ fn setup( commands.insert_resource(PointMesh( meshes.add( Sphere::new(0.03) - .mesh() + .mesh_builder() .kind(SphereKind::Ico { subdivisions: 3 }), ), )); diff --git a/examples/math/render_primitives.rs b/examples/math/render_primitives.rs index 4d7a3117bfa31..b0dd474e4e396 100644 --- a/examples/math/render_primitives.rs +++ b/examples/math/render_primitives.rs @@ -523,26 +523,26 @@ fn spawn_primitive_2d( }; let convex_polygon = ConvexPolygon::new(CONVEX_POLYGON_VERTICES).unwrap(); [ - Some(RECTANGLE.mesh().build()), - Some(CIRCLE.mesh().build()), - Some(ELLIPSE.mesh().build()), - Some(TRIANGLE_2D.mesh().build()), + Some(RECTANGLE.mesh_builder().build()), + Some(CIRCLE.mesh_builder().build()), + Some(ELLIPSE.mesh_builder().build()), + Some(TRIANGLE_2D.mesh_builder().build()), None, // plane None, // line - Some(SEGMENT_2D.mesh().build()), - Some(polyline_2d.mesh().build()), + Some(SEGMENT_2D.mesh_builder().build()), + Some(polyline_2d.mesh_builder().build()), None, // polygon - Some(convex_polygon.mesh().build()), - Some(REGULAR_POLYGON.mesh().build()), - Some(CAPSULE_2D.mesh().build()), + Some(convex_polygon.mesh_builder().build()), + Some(REGULAR_POLYGON.mesh_builder().build()), + Some(CAPSULE_2D.mesh_builder().build()), None, // cylinder None, // cone None, // conical frustum - Some(ANNULUS.mesh().build()), + Some(ANNULUS.mesh_builder().build()), None, // tetrahedron None, // arc - Some(CIRCULAR_SECTOR.mesh().build()), - Some(CIRCULAR_SEGMENT.mesh().build()), + Some(CIRCULAR_SECTOR.mesh_builder().build()), + Some(CIRCULAR_SEGMENT.mesh_builder().build()), ] .into_iter() .zip(PrimitiveSelected::ALL) @@ -574,23 +574,23 @@ fn spawn_primitive_3d( vertices: POLYLINE_3D_VERTICES.to_vec(), }; [ - Some(CUBOID.mesh().build()), - Some(SPHERE.mesh().build()), + Some(CUBOID.mesh_builder().build()), + Some(SPHERE.mesh_builder().build()), None, // ellipse - Some(TRIANGLE_3D.mesh().build()), - Some(PLANE_3D.mesh().build()), + Some(TRIANGLE_3D.mesh_builder().build()), + Some(PLANE_3D.mesh_builder().build()), None, // line - Some(SEGMENT_3D.mesh().build()), - Some(polyline_3d.mesh().build()), + Some(SEGMENT_3D.mesh_builder().build()), + Some(polyline_3d.mesh_builder().build()), None, // polygon None, // convex polygon None, // regular polygon - Some(CAPSULE_3D.mesh().build()), - Some(CYLINDER.mesh().build()), - Some(CONE.mesh().build()), - Some(CONICAL_FRUSTUM.mesh().build()), - Some(TORUS.mesh().build()), - Some(TETRAHEDRON.mesh().build()), + Some(CAPSULE_3D.mesh_builder().build()), + Some(CYLINDER.mesh_builder().build()), + Some(CONE.mesh_builder().build()), + Some(CONICAL_FRUSTUM.mesh_builder().build()), + Some(TORUS.mesh_builder().build()), + Some(TETRAHEDRON.mesh_builder().build()), None, // arc None, // circular sector None, // circular segment diff --git a/examples/mobile/src/lib.rs b/examples/mobile/src/lib.rs index 894a4673966dd..f29504516751c 100644 --- a/examples/mobile/src/lib.rs +++ b/examples/mobile/src/lib.rs @@ -104,7 +104,7 @@ fn setup_scene( // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(materials.add(Color::srgb(0.1, 0.2, 0.1))), )); // cube @@ -115,7 +115,7 @@ fn setup_scene( )); // sphere commands.spawn(( - Mesh3d(meshes.add(Sphere::new(0.5).mesh().ico(4).unwrap())), + Mesh3d(meshes.add(Sphere::new(0.5).mesh_builder().ico(4).unwrap())), MeshMaterial3d(materials.add(Color::srgb(0.1, 0.4, 0.8))), Transform::from_xyz(1.5, 1.5, 1.5), )); diff --git a/examples/movement/smooth_follow.rs b/examples/movement/smooth_follow.rs index a41fcfddfa255..1f73271bf7f74 100644 --- a/examples/movement/smooth_follow.rs +++ b/examples/movement/smooth_follow.rs @@ -46,7 +46,7 @@ fn setup( ) { // A plane: commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(12.0, 12.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(12.0, 12.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.15, 0.3))), Transform::from_xyz(0.0, -2.5, 0.0), )); diff --git a/examples/picking/custom_hit_data.rs b/examples/picking/custom_hit_data.rs index dabbfa159eca9..dca61435043fa 100644 --- a/examples/picking/custom_hit_data.rs +++ b/examples/picking/custom_hit_data.rs @@ -70,7 +70,10 @@ fn setup_scene( ) { let shapes: [(Mesh, Color); 3] = [ (Cuboid::default().into(), RED.into()), - (Sphere::default().mesh().ico(2).unwrap(), GREEN.into()), + ( + Sphere::default().mesh_builder().ico(2).unwrap(), + GREEN.into(), + ), (Cylinder::default().into(), BLUE.into()), ]; @@ -87,7 +90,7 @@ fn setup_scene( } commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(30.0, 30.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(30.0, 30.0))), MeshMaterial3d(materials.add(Color::from(DARK_GRAY))), Pickable::IGNORE, )); diff --git a/examples/picking/mesh_picking.rs b/examples/picking/mesh_picking.rs index 4766d43a0a86c..fa62c480fe452 100644 --- a/examples/picking/mesh_picking.rs +++ b/examples/picking/mesh_picking.rs @@ -59,8 +59,8 @@ fn setup_scene( meshes.add(Cylinder::default()), meshes.add(Cone::default()), meshes.add(ConicalFrustum::default()), - meshes.add(Sphere::default().mesh().ico(5).unwrap()), - meshes.add(Sphere::default().mesh().uv(32, 18)), + meshes.add(Sphere::default().mesh_builder().ico(5).unwrap()), + meshes.add(Sphere::default().mesh_builder().uv(32, 18)), ]; let extrusions = [ @@ -121,7 +121,14 @@ fn setup_scene( // Ground commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(50.0, 50.0).subdivisions(10))), + Mesh3d( + meshes.add( + Plane3d::default() + .mesh_builder() + .size(50.0, 50.0) + .subdivisions(10), + ), + ), MeshMaterial3d(ground_matl.clone()), Pickable::IGNORE, // Disable picking for the ground plane. )); diff --git a/examples/shader/extended_material_bindless.rs b/examples/shader/extended_material_bindless.rs index a9d9c35e6de89..0035e4fc4756f 100644 --- a/examples/shader/extended_material_bindless.rs +++ b/examples/shader/extended_material_bindless.rs @@ -4,7 +4,6 @@ use std::f32::consts::FRAC_PI_2; use bevy::{ color::palettes::{css::RED, tailwind::GRAY_600}, - mesh::{SphereKind, SphereMeshBuilder}, pbr::{ExtendedMaterial, MaterialExtension, MeshMaterial3d}, prelude::*, render::render_resource::{AsBindGroup, ShaderType}, diff --git a/examples/shader/shader_material_screenspace_texture.rs b/examples/shader/shader_material_screenspace_texture.rs index 2b439f7347e7e..0034217d88579 100644 --- a/examples/shader/shader_material_screenspace_texture.rs +++ b/examples/shader/shader_material_screenspace_texture.rs @@ -26,7 +26,7 @@ fn setup( mut standard_materials: ResMut>, ) { commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(standard_materials.add(Color::srgb(0.3, 0.5, 0.3))), )); commands.spawn((PointLight::default(), Transform::from_xyz(4.0, 8.0, 4.0))); diff --git a/examples/shader/shader_prepass.rs b/examples/shader/shader_prepass.rs index 24acaeb0e6b65..eca9f338fdc55 100644 --- a/examples/shader/shader_prepass.rs +++ b/examples/shader/shader_prepass.rs @@ -60,7 +60,7 @@ fn setup( // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(std_materials.add(Color::srgb(0.3, 0.5, 0.3))), )); diff --git a/examples/stress_tests/many_foxes.rs b/examples/stress_tests/many_foxes.rs index 2dfd082a6770c..b88cfc15b8c03 100644 --- a/examples/stress_tests/many_foxes.rs +++ b/examples/stress_tests/many_foxes.rs @@ -238,7 +238,7 @@ fn setup( // Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5000.0, 5000.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5000.0, 5000.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); diff --git a/examples/stress_tests/many_lights.rs b/examples/stress_tests/many_lights.rs index 5f3111494a121..1768b5aa3698b 100644 --- a/examples/stress_tests/many_lights.rs +++ b/examples/stress_tests/many_lights.rs @@ -51,7 +51,7 @@ fn setup( const N_LIGHTS: usize = 100_000; commands.spawn(( - Mesh3d(meshes.add(Sphere::new(RADIUS).mesh().ico(9).unwrap())), + Mesh3d(meshes.add(Sphere::new(RADIUS).mesh_builder().ico(9).unwrap())), MeshMaterial3d(materials.add(Color::WHITE)), Transform::from_scale(Vec3::NEG_ONE), )); diff --git a/examples/testbed/3d.rs b/examples/testbed/3d.rs index c4553dbb09c89..c4814192c03ba 100644 --- a/examples/testbed/3d.rs +++ b/examples/testbed/3d.rs @@ -132,7 +132,7 @@ mod light { mut materials: ResMut>, ) { commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(10.0, 10.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(10.0, 10.0))), MeshMaterial3d(materials.add(StandardMaterial { base_color: Color::WHITE, perceptual_roughness: 1.0, @@ -236,7 +236,7 @@ mod bloom { ..default() }); - let mesh = meshes.add(Sphere::new(0.5).mesh().ico(5).unwrap()); + let mesh = meshes.add(Sphere::new(0.5).mesh_builder().ico(5).unwrap()); for z in -2..3_i32 { let material = match (z % 2).abs() { diff --git a/examples/transforms/align.rs b/examples/transforms/align.rs index 68a9eb3aaab47..9cd6a223a3f15 100644 --- a/examples/transforms/align.rs +++ b/examples/transforms/align.rs @@ -60,7 +60,7 @@ fn setup( // A plane that we can sit on top of commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(100.0, 100.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(100.0, 100.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), Transform::from_xyz(0., -2., 0.), )); diff --git a/examples/transforms/transform.rs b/examples/transforms/transform.rs index 858e848edd572..11bd3e6a908a4 100644 --- a/examples/transforms/transform.rs +++ b/examples/transforms/transform.rs @@ -45,7 +45,7 @@ fn setup( ) { // Add an object (sphere) for visualizing scaling. commands.spawn(( - Mesh3d(meshes.add(Sphere::new(3.0).mesh().ico(32).unwrap())), + Mesh3d(meshes.add(Sphere::new(3.0).mesh_builder().ico(32).unwrap())), MeshMaterial3d(materials.add(Color::from(YELLOW))), Transform::from_translation(Vec3::ZERO), Center { diff --git a/examples/usage/debug_frustum_culling.rs b/examples/usage/debug_frustum_culling.rs index 008481b489041..a18d39fdbb4c7 100644 --- a/examples/usage/debug_frustum_culling.rs +++ b/examples/usage/debug_frustum_culling.rs @@ -184,7 +184,7 @@ fn setup( Mesh3d( meshes.add( Plane3d::default() - .mesh() + .mesh_builder() .size(SHAPE_RING_RADIUS * 4., SHAPE_RING_RADIUS * 4.), ), ), @@ -192,7 +192,7 @@ fn setup( )); // Blue Wall Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5., 5.))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5., 5.))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.3, 0.5))), Transform::from_xyz(20., 2.5, 10.).with_rotation(Quat::from_rotation_z(PI / 2.)), )); @@ -230,7 +230,7 @@ fn setup( meshes.add(Cuboid { half_size: Vec3::new(1., 0.1, 2.), }), - meshes.add(Sphere::default().mesh().ico(5).unwrap()), + meshes.add(Sphere::default().mesh_builder().ico(5).unwrap()), ]; let shapes_len = shapes.len() as f32; let mut shape_ring = commands.spawn((Transform::default(), Visibility::default(), ShapeRing)); diff --git a/examples/window/screenshot.rs b/examples/window/screenshot.rs index c864f5763f5ca..330489d15ba72 100644 --- a/examples/window/screenshot.rs +++ b/examples/window/screenshot.rs @@ -54,7 +54,7 @@ fn setup( ) { // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); // cube diff --git a/tests/3d/test_invalid_skinned_mesh.rs b/tests/3d/test_invalid_skinned_mesh.rs index cbd1082fcdb37..c0e56306b7fab 100644 --- a/tests/3d/test_invalid_skinned_mesh.rs +++ b/tests/3d/test_invalid_skinned_mesh.rs @@ -80,7 +80,14 @@ fn setup_environment( // Add a plane behind the meshes so we can see the shadows. commands.spawn(( Transform::from_xyz(0.0, 0.0, -1.0), - Mesh3d(mesh_assets.add(Plane3d::default().mesh().size(100.0, 100.0).normal(Dir3::Z))), + Mesh3d( + mesh_assets.add( + Plane3d::default() + .mesh_builder() + .size(100.0, 100.0) + .normal(Dir3::Z), + ), + ), MeshMaterial3d(material_assets.add(StandardMaterial { base_color: Color::srgb(0.05, 0.05, 0.15), reflectance: 0.2, @@ -211,7 +218,14 @@ fn setup_meshes( // Add a square behind the mesh to distinguish it from the other meshes. commands.spawn(( Transform::from_xyz(transform.translation.x, transform.translation.y, -0.8), - Mesh3d(mesh_assets.add(Plane3d::default().mesh().size(4.3, 4.3).normal(Dir3::Z))), + Mesh3d( + mesh_assets.add( + Plane3d::default() + .mesh_builder() + .size(4.3, 4.3) + .normal(Dir3::Z), + ), + ), MeshMaterial3d(background_material_handle.clone()), )); } diff --git a/tests/window/minimizing.rs b/tests/window/minimizing.rs index 8099c27a76365..f706935ce83b2 100644 --- a/tests/window/minimizing.rs +++ b/tests/window/minimizing.rs @@ -34,7 +34,7 @@ fn setup_3d( ) { // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); // cube diff --git a/tests/window/resizing.rs b/tests/window/resizing.rs index d0b0cb8aaf354..e595d752c065e 100644 --- a/tests/window/resizing.rs +++ b/tests/window/resizing.rs @@ -110,7 +110,7 @@ fn setup_3d( ) { // plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), + Mesh3d(meshes.add(Plane3d::default().mesh_builder().size(5.0, 5.0))), MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), )); // cube