Skip to content

Commit c7898b0

Browse files
calculate flat normals for mesh if missing
1 parent f520a34 commit c7898b0

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

crates/bevy_gltf/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ bevy_render = { path = "../bevy_render", version = "0.4.0" }
2424
bevy_transform = { path = "../bevy_transform", version = "0.4.0" }
2525
bevy_math = { path = "../bevy_math", version = "0.4.0" }
2626
bevy_scene = { path = "../bevy_scene", version = "0.4.0" }
27+
bevy_log = { path = "../bevy_log", version = "0.4.0" }
2728

2829
# other
2930
gltf = { version = "0.15.2", default-features = false, features = ["utils", "names", "KHR_materials_unlit"] }

crates/bevy_gltf/src/loader.rs

+6
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ async fn load_gltf<'a, 'b>(
146146
mesh.set_indices(Some(Indices::U32(indices.into_u32().collect())));
147147
};
148148

149+
if mesh.attribute(Mesh::ATTRIBUTE_NORMAL).is_none() {
150+
bevy_log::info!("missing normals, computing them as flat");
151+
mesh.duplicate_vertices();
152+
mesh.compute_flat_normals();
153+
}
154+
149155
let mesh = load_context.set_labeled_asset(&primitive_label, LoadedAsset::new(mesh));
150156
primitives.push(super::GltfPrimitive {
151157
mesh,

crates/bevy_render/src/mesh/mesh.rs

+96
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ impl VertexAttributeValues {
6565
self.len() == 0
6666
}
6767

68+
fn as_float3(&self) -> Option<&[[f32; 3]]> {
69+
match self {
70+
VertexAttributeValues::Float3(values) => Some(values),
71+
_ => None,
72+
}
73+
}
74+
6875
// TODO: add vertex format as parameter here and perform type conversions
6976
/// Flattens the VertexAttributeArray into a sequence of bytes. This is
7077
/// useful for serialization and sending to the GPU.
@@ -194,6 +201,29 @@ pub enum Indices {
194201
U32(Vec<u32>),
195202
}
196203

204+
impl Indices {
205+
fn iter(&self) -> impl Iterator<Item = usize> + '_ {
206+
match self {
207+
Indices::U16(vec) => IndicesIter::U16(vec.iter()),
208+
Indices::U32(vec) => IndicesIter::U32(vec.iter()),
209+
}
210+
}
211+
}
212+
enum IndicesIter<'a> {
213+
U16(std::slice::Iter<'a, u16>),
214+
U32(std::slice::Iter<'a, u32>),
215+
}
216+
impl Iterator for IndicesIter<'_> {
217+
type Item = usize;
218+
219+
fn next(&mut self) -> Option<Self::Item> {
220+
match self {
221+
IndicesIter::U16(iter) => iter.next().map(|val| *val as usize),
222+
IndicesIter::U32(iter) => iter.next().map(|val| *val as usize),
223+
}
224+
}
225+
}
226+
197227
impl From<&Indices> for IndexFormat {
198228
fn from(indices: &Indices) -> Self {
199229
match indices {
@@ -369,6 +399,72 @@ impl Mesh {
369399

370400
attributes_interleaved_buffer
371401
}
402+
403+
/// Duplicates the vertex attributes so that no vertices are shared.
404+
///
405+
/// Does nothing if no [Indices] are set.
406+
pub fn duplicate_vertices(&mut self) {
407+
fn duplicate<T: Copy>(values: &[T], indices: impl Iterator<Item = usize>) -> Vec<T> {
408+
indices.map(|i| values[i]).collect()
409+
}
410+
411+
assert!(
412+
matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
413+
"can only duplicate vertices for `TriangleList`s"
414+
);
415+
416+
let indices = match self.indices.take() {
417+
Some(indices) => indices,
418+
None => return,
419+
};
420+
for (_, attributes) in self.attributes.iter_mut() {
421+
let indices = indices.iter();
422+
match attributes {
423+
VertexAttributeValues::Float(vec) => *vec = duplicate(&vec, indices),
424+
VertexAttributeValues::Int(vec) => *vec = duplicate(&vec, indices),
425+
VertexAttributeValues::Uint(vec) => *vec = duplicate(&vec, indices),
426+
VertexAttributeValues::Float2(vec) => *vec = duplicate(&vec, indices),
427+
VertexAttributeValues::Int2(vec) => *vec = duplicate(&vec, indices),
428+
VertexAttributeValues::Uint2(vec) => *vec = duplicate(&vec, indices),
429+
VertexAttributeValues::Float3(vec) => *vec = duplicate(&vec, indices),
430+
VertexAttributeValues::Int3(vec) => *vec = duplicate(&vec, indices),
431+
VertexAttributeValues::Uint3(vec) => *vec = duplicate(&vec, indices),
432+
VertexAttributeValues::Int4(vec) => *vec = duplicate(&vec, indices),
433+
VertexAttributeValues::Uint4(vec) => *vec = duplicate(&vec, indices),
434+
VertexAttributeValues::Float4(vec) => *vec = duplicate(&vec, indices),
435+
VertexAttributeValues::Uchar4Norm(vec) => *vec = duplicate(&vec, indices),
436+
}
437+
}
438+
}
439+
440+
/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
441+
/// This [duplicates the vertices](Mesh::duplicate_vertices), so any [`Indices`] will be gone if set.
442+
pub fn compute_flat_normals(&mut self) {
443+
if self.indices().is_some() {
444+
self.duplicate_vertices();
445+
self.compute_flat_normals();
446+
return;
447+
}
448+
449+
let positions = self
450+
.attribute(Mesh::ATTRIBUTE_POSITION)
451+
.unwrap()
452+
.as_float3()
453+
.expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");
454+
455+
let normals: Vec<_> = positions
456+
.chunks_exact(3)
457+
.map(|p| face_normal(p[0], p[1], p[2]))
458+
.flat_map(|normal| std::array::IntoIter::new([normal, normal, normal]))
459+
.collect();
460+
461+
self.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
462+
}
463+
}
464+
465+
fn face_normal(a: [f32; 3], b: [f32; 3], c: [f32; 3]) -> [f32; 3] {
466+
let (a, b, c) = (Vec3::from(a), Vec3::from(b), Vec3::from(c));
467+
(b - a).cross(c - a).into()
372468
}
373469

374470
fn remove_resource_save(

0 commit comments

Comments
 (0)