Skip to content

Primitive 3d custom tangents #17691

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions crates/bevy_mesh/src/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,35 @@ impl Mesh {
.insert(attribute.id, MeshAttributeData { attribute, values });
}

/// Sets the data for a vertex attribute (position, normal, etc.) if not empty.
/// The name will often be one of the associated constants such
/// as [`Mesh::ATTRIBUTE_POSITION`].
///
/// `Aabb` of entities with modified mesh are not updated automatically.
///
/// # Panics
/// Panics when the format of the values does not match the attribute's format.
#[inline]
pub fn insert_attribute_if_not_empty(
&mut self,
attribute: MeshVertexAttribute,
values: impl Into<VertexAttributeValues>,
) {
let values = values.into();
if !values.is_empty() {
let values_format = VertexFormat::from(&values);
if values_format != attribute.format {
panic!(
"Failed to insert attribute. Invalid attribute format for {}. Given format is {values_format:?} but expected {:?}",
attribute.name, attribute.format
);
}

self.attributes
.insert(attribute.id, MeshAttributeData { attribute, values });
}
}

/// Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal, etc.).
/// The name will often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].
///
Expand All @@ -257,6 +286,27 @@ impl Mesh {
self
}

/// Consumes the mesh and returns a mesh with data set for a vertex attribute (position, normal, etc.)
/// if it is not empty.
/// The name will often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`].
///
/// (Alternatively, you can use [`Mesh::insert_attribute`] to mutate an existing mesh in-place)
///
/// `Aabb` of entities with modified mesh are not updated automatically.
///
/// # Panics
/// Panics when the format of the values does not match the attribute's format.
#[must_use]
#[inline]
pub fn with_inserted_attribute_if_not_empty(
mut self,
attribute: MeshVertexAttribute,
values: impl Into<VertexAttributeValues>,
) -> Self {
self.insert_attribute_if_not_empty(attribute, values);
self
}

/// Removes the data for a vertex attribute
pub fn remove_attribute(
&mut self,
Expand Down
10 changes: 10 additions & 0 deletions crates/bevy_mesh/src/primitives/dim3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,13 @@ pub use sphere::*;
pub use tetrahedron::*;
pub use torus::*;
pub use triangle3d::*;

use bevy_math::{Dir3, Vec3};

fn calculate_tangents_around_axis(normals: &[[f32; 3]], tangent_axis: &Dir3) -> Vec<[f32; 4]> {
normals
.iter()
.map(|normal| Vec3::from_array(*normal))
.map(|normal| tangent_axis.cross(normal).extend(1.).to_array())
.collect()
}
28 changes: 26 additions & 2 deletions crates/bevy_mesh/src/primitives/dim3/sphere.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::Sphere};
use bevy_math::{ops, primitives::Sphere, Dir3};
use bevy_reflect::prelude::*;
use core::f32::consts::PI;
use hexasphere::shapes::IcoSphere;
Expand Down Expand Up @@ -55,6 +55,8 @@ pub struct SphereMeshBuilder {
pub sphere: Sphere,
/// The type of sphere mesh that will be built.
pub kind: SphereKind,
/// Axis around which the tangents will be calculated
pub tangent_axis: Option<Dir3>,
}

impl SphereMeshBuilder {
Expand All @@ -64,6 +66,7 @@ impl SphereMeshBuilder {
Self {
sphere: Sphere { radius },
kind,
tangent_axis: None,
}
}

Expand All @@ -74,6 +77,13 @@ impl SphereMeshBuilder {
self
}

/// Sets the tangent axis that will be used to calculate mesh tangents.
#[inline]
pub const fn with_tangent_axis(mut self, axis: Dir3) -> Self {
self.tangent_axis.replace(axis);
self
}

/// Creates an icosphere mesh with the given number of subdivisions.
///
/// The number of faces quadruples with each subdivision.
Expand Down Expand Up @@ -151,14 +161,21 @@ impl SphereMeshBuilder {

let indices = Indices::U32(indices);

let tangent = if let Some(tangent_axis) = self.tangent_axis {
super::calculate_tangents_around_axis(normals.as_slice(), &tangent_axis)
} else {
vec![]
};

Ok(Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(indices)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, points)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs))
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
.with_inserted_attribute_if_not_empty(Mesh::ATTRIBUTE_TANGENT, tangent))
}

/// Creates a UV sphere [`Mesh`] with the given number of
Expand Down Expand Up @@ -220,6 +237,12 @@ impl SphereMeshBuilder {
}
}

let tangents = if let Some(tangent_axis) = self.tangent_axis {
super::calculate_tangents_around_axis(normals.as_slice(), &tangent_axis)
} else {
vec![]
};

Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
Expand All @@ -228,6 +251,7 @@ impl SphereMeshBuilder {
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
.with_inserted_attribute_if_not_empty(Mesh::ATTRIBUTE_TANGENT, tangents)
}
}

Expand Down