use crate::Error;
use bevy::ecs::prelude::Component;
use bevy::math::Vec3;
use bevy::reflect::Reflect;
use bevy::render::color::Color;
use bevy::render::mesh::{Indices, Mesh, VertexAttributeValues};
use bevy::utils::HashMap;
#[derive(Debug, Copy, Clone, Default, Reflect)]
pub enum NormalComputing {
None,
#[default]
SmoothNormals,
FlatNormals,
}
#[derive(Debug, Clone, Component, Default)]
pub struct ClothRendering {
pub vertex_positions: Vec<Vec3>,
pub vertex_uvs: Option<Vec<[f32; 2]>>,
pub vertex_colors: Option<Vec<[f32; 4]>>,
pub indices: Vec<u32>,
pub normal_computing: NormalComputing,
}
impl ClothRendering {
fn face_normal(a: Vec3, b: Vec3, c: Vec3) -> Vec3 {
(b - a).cross(c - a).normalize() }
pub fn init(mesh: &Mesh, normal_computing: NormalComputing) -> Result<Self, Error> {
let vertex_positions = mesh
.attribute(Mesh::ATTRIBUTE_POSITION)
.ok_or_else(|| Error::MissingMeshAttribute("Vertex_Position".to_string()))?;
let vertex_positions: Vec<Vec3> = match vertex_positions {
VertexAttributeValues::Float32x3(v) => v.iter().copied().map(Vec3::from).collect(),
_ => return Err(Error::UnsupportedVertexPositionAttribute),
};
let vertex_count = vertex_positions.len();
let vertex_uvs = mesh
.attribute(Mesh::ATTRIBUTE_UV_0)
.and_then(|attr| match attr {
VertexAttributeValues::Float32x2(v) => Some(v.clone()),
_ => None,
});
let attr_count = vertex_uvs.as_ref().map_or(vertex_count, Vec::len);
if attr_count != vertex_count {
return Err(Error::InvalidMeshAttribute {
attribute: "Vertex_Uv".to_string(),
message: format!("Expected {vertex_count} values, got {attr_count}"),
});
}
let vertex_colors = mesh
.attribute(Mesh::ATTRIBUTE_COLOR)
.and_then(|attr| match attr {
VertexAttributeValues::Float32x4(v) => Some(v.clone()),
VertexAttributeValues::Float32x3(v) => {
Some(v.iter().copied().map(|[r, g, b]| [r, g, b, 1.0]).collect())
}
VertexAttributeValues::Uint8x4(v) => Some(
v.iter()
.copied()
.map(|[r, g, b, a]| Color::rgba_u8(r, g, b, a).as_rgba_f32())
.collect(),
),
_ => None,
});
let attr_count = vertex_colors.as_ref().map_or(vertex_count, Vec::len);
if attr_count != vertex_count {
return Err(Error::InvalidMeshAttribute {
attribute: "Vertex_Color".to_string(),
message: format!("Expected {vertex_count} values, got {attr_count}"),
});
}
let indices = match mesh.indices() {
None => return Err(Error::MissingIndices),
Some(i) => match i {
Indices::U16(v) => v.iter().copied().map(u32::from).collect(),
Indices::U32(v) => v.clone(),
},
};
Ok(Self {
vertex_positions,
vertex_uvs,
vertex_colors,
indices,
normal_computing,
})
}
#[must_use]
pub fn compute_aabb(&self, offset: Option<f32>) -> (Vec3, Vec3) {
const VEC3_MIN: Vec3 = Vec3::from_array([std::f32::MIN, std::f32::MIN, std::f32::MIN]);
const VEC3_MAX: Vec3 = Vec3::from_array([std::f32::MAX, std::f32::MAX, std::f32::MAX]);
let mut minimum = VEC3_MAX;
let mut maximum = VEC3_MIN;
for p in &self.vertex_positions {
minimum = minimum.min(*p);
maximum = maximum.max(*p);
}
let center = 0.5 * (maximum + minimum);
let half_extents = 0.5 * (maximum - minimum) + offset.unwrap_or(0.0);
(center, half_extents)
}
pub fn update_positions(&mut self, vertex_positions: Vec<Vec3>) {
assert_eq!(vertex_positions.len(), self.vertex_positions.len());
self.vertex_positions = vertex_positions;
}
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub fn duplicated_self(&self) -> Self {
let ((vertex_positions, indices), (vertex_uvs, vertex_colors)): ((_, _), (Vec<_>, Vec<_>)) =
self.indices
.iter()
.enumerate()
.map(|(i, indice)| {
(
(self.vertex_positions[*indice as usize], i as u32),
(
self.vertex_uvs.as_ref().map(|v| v[*indice as usize]),
self.vertex_colors.as_ref().map(|v| v[*indice as usize]),
),
)
})
.unzip();
Self {
vertex_positions,
indices,
normal_computing: self.normal_computing,
vertex_uvs: vertex_uvs.into_iter().collect(),
vertex_colors: vertex_colors.into_iter().collect(),
}
}
pub(crate) fn compute_flat_normals(&self) -> Vec<Vec3> {
self.indices
.chunks_exact(3)
.flat_map(|chunk| {
let [a, b, c] =
[chunk[0], chunk[1], chunk[2]].map(|i| self.vertex_positions[i as usize]);
let normal = Self::face_normal(a, b, c);
[normal; 3]
})
.collect()
}
#[allow(clippy::cast_precision_loss)]
pub(crate) fn compute_smooth_normals(&self) -> Vec<Vec3> {
let mut map: HashMap<_, Vec<_>> = HashMap::with_capacity(self.vertex_positions.len());
for chunk in self.indices.chunks_exact(3) {
let [a, b, c] = [chunk[0] as usize, chunk[1] as usize, chunk[2] as usize];
let flat_normal = Self::face_normal(
self.vertex_positions[a],
self.vertex_positions[b],
self.vertex_positions[c],
);
map.entry(a).or_default().push(flat_normal);
map.entry(b).or_default().push(flat_normal);
map.entry(c).or_default().push(flat_normal);
}
(0..self.vertex_positions.len())
.map(|i| {
let sum = map[&i].iter().fold(Vec3::ZERO, |res, v| res + *v);
sum / map[&i].len() as f32
})
.collect()
}
fn vec3_vertex_attr(attr: &[Vec3]) -> Vec<[f32; 3]> {
attr.iter().map(Vec3::to_array).collect()
}
pub fn apply(&self, mesh: &mut Mesh) {
match self.normal_computing {
NormalComputing::None => mesh.insert_attribute(
Mesh::ATTRIBUTE_POSITION,
Self::vec3_vertex_attr(&self.vertex_positions),
),
NormalComputing::SmoothNormals => {
mesh.insert_attribute(
Mesh::ATTRIBUTE_POSITION,
Self::vec3_vertex_attr(&self.vertex_positions),
);
let vertex_normals = self.compute_smooth_normals();
mesh.insert_attribute(
Mesh::ATTRIBUTE_NORMAL,
Self::vec3_vertex_attr(&vertex_normals),
);
}
NormalComputing::FlatNormals => {
let new_self = self.duplicated_self();
mesh.insert_attribute(
Mesh::ATTRIBUTE_POSITION,
Self::vec3_vertex_attr(&new_self.vertex_positions),
);
if let Some(ref attr) = new_self.vertex_uvs {
mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, attr.clone());
}
if let Some(ref attr) = new_self.vertex_colors {
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, attr.clone());
}
let vertex_normals = new_self.compute_flat_normals();
mesh.insert_attribute(
Mesh::ATTRIBUTE_NORMAL,
Self::vec3_vertex_attr(&vertex_normals),
);
mesh.set_indices(Some(Indices::U32(new_self.indices)));
}
}
}
}