diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index bd7789ec08..461f647039 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -38,7 +38,8 @@ once_cell = "1.4.0" downcast-rs = "1.1.1" thiserror = "1.0" anyhow = "1.0" +hexasphere = "0.1.5" [features] png = ["image/png"] -hdr = ["image/hdr"] \ No newline at end of file +hdr = ["image/hdr"] diff --git a/crates/bevy_render/src/mesh/mesh.rs b/crates/bevy_render/src/mesh/mesh.rs index 2dbed5a000..958f423f9f 100644 --- a/crates/bevy_render/src/mesh/mesh.rs +++ b/crates/bevy_render/src/mesh/mesh.rs @@ -165,12 +165,16 @@ impl Mesh { } } +/// Generation for some primitive shape meshes. pub mod shape { use super::{Mesh, VertexAttribute}; use crate::pipeline::PrimitiveTopology; use bevy_math::*; + use hexasphere::Hexasphere; + /// A cube. pub struct Cube { + /// Half the side length of the cube. pub size: f32, } @@ -246,8 +250,11 @@ pub mod shape { } } + /// A rectangle on the XY plane. pub struct Quad { + /// Full width and height of the rectangle. pub size: Vec2, + /// Flips the texture coords of the resulting vertices. pub flip: bool, } @@ -341,7 +348,9 @@ pub mod shape { } } + /// A square on the XZ plane. pub struct Plane { + /// The total side length of the square. pub size: f32, } @@ -378,6 +387,70 @@ pub mod shape { } } } + + /// A sphere made from a subdivided Icosahedron. + pub struct Icosphere { + /// The radius of the sphere. + pub radius: f32, + /// The number of subdivisions applied. + pub subdivisions: usize, + } + + impl Default for Icosphere { + fn default() -> Self { + Self { + radius: 1.0, + subdivisions: 5, + } + } + } + + impl From for Mesh { + fn from(sphere: Icosphere) -> Self { + let hexasphere = Hexasphere::new(sphere.subdivisions, |point| { + let inclination = point.z().acos(); + let azumith = point.y().atan2(point.x()); + + let norm_inclination = 1.0 - (inclination / std::f32::consts::PI); + let norm_azumith = (azumith / std::f32::consts::PI) * 0.5; + + [norm_inclination, norm_azumith] + }); + + let raw_points = hexasphere.raw_points(); + + let points = raw_points + .iter() + .map(|&p| { + (p * sphere.radius).into() + }) + .collect::>(); + + let normals = raw_points + .iter() + .copied() + .map(Into::into) + .collect::>(); + + let uvs = hexasphere.raw_data().to_owned(); + + let mut indices = Vec::with_capacity(hexasphere.indices_per_main_triangle() * 20); + + for i in 0..20 { + hexasphere.get_indices(i, &mut indices); + } + + Mesh { + primitive_topology: PrimitiveTopology::TriangleList, + attributes: vec![ + VertexAttribute::position(points), + VertexAttribute::normal(normals), + VertexAttribute::uv(uvs), + ], + indices: Some(indices) + } + } + } } fn remove_current_mesh_resources( diff --git a/examples/3d/3d_scene.rs b/examples/3d/3d_scene.rs index e5a4a6d2cb..a471ace836 100644 --- a/examples/3d/3d_scene.rs +++ b/examples/3d/3d_scene.rs @@ -29,6 +29,13 @@ fn setup( translation: Translation::new(0.0, 1.0, 0.0), ..Default::default() }) + // sphere + .spawn(PbrComponents { + mesh: meshes.add(Mesh::from(shape::Icosphere { subdivisions: 4, radius: 0.5 })), + material: materials.add(Color::rgb(0.1, 0.4, 0.8).into()), + translation: Translation::new(1.5, 1.5, 1.5), + ..Default::default() + }) // light .spawn(LightComponents { translation: Translation::new(4.0, 8.0, 4.0),