diff --git a/crates/bevy_render/src/mesh/mesh/mod.rs b/crates/bevy_render/src/mesh/mesh/mod.rs index d7fa777a9f..88a99b7973 100644 --- a/crates/bevy_render/src/mesh/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mesh/mod.rs @@ -213,7 +213,7 @@ impl Mesh { attribute: MeshVertexAttribute, values: impl Into, ) { - let mut values = values.into(); + let values = values.into(); let values_format = VertexFormat::from(&values); if values_format != attribute.format { panic!( @@ -222,17 +222,6 @@ impl Mesh { ); } - // validate attributes - if attribute.id == Self::ATTRIBUTE_JOINT_WEIGHT.id { - let VertexAttributeValues::Float32x4(ref mut values) = values else { - unreachable!() // we confirmed the format above - }; - for value in values.iter_mut().filter(|v| *v == &[0.0, 0.0, 0.0, 0.0]) { - // zero weights are invalid - value[0] = 1.0; - } - } - self.attributes .insert(attribute.id, MeshAttributeData { attribute, values }); } @@ -630,6 +619,31 @@ impl Mesh { pub fn morph_target_names(&self) -> Option<&[String]> { self.morph_target_names.as_deref() } + + /// Normalize joint weights so they sum to 1. + pub fn normalize_joint_weights(&mut self) { + if let Some(joints) = self.attribute_mut(Self::ATTRIBUTE_JOINT_WEIGHT) { + let VertexAttributeValues::Float32x4(ref mut joints) = joints else { + panic!("unexpected joint weight format"); + }; + + for weights in joints.iter_mut() { + // force negative weights to zero + weights.iter_mut().for_each(|w| *w = w.max(0.0)); + + let sum: f32 = weights.iter().sum(); + if sum == 0.0 { + // all-zero weights are invalid + weights[0] = 1.0; + } else { + let recip = sum.recip(); + for weight in weights.iter_mut() { + *weight *= recip; + } + } + } + } + } } #[derive(Debug, Clone)]