Change normal generator to use vertex normals if available, but keep face weighting.
This commit is contained in:
parent
5d49ad8f3c
commit
3f304437cd
@ -1,7 +1,7 @@
|
|||||||
use bevy::{
|
use bevy::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
render::{
|
render::{
|
||||||
mesh::VertexAttributeValues,
|
mesh::{Indices, VertexAttributeValues},
|
||||||
render_resource::{PrimitiveTopology, VertexFormat},
|
render_resource::{PrimitiveTopology, VertexFormat},
|
||||||
},
|
},
|
||||||
utils::{FloatOrd, HashMap, HashSet},
|
utils::{FloatOrd, HashMap, HashSet},
|
||||||
@ -9,6 +9,44 @@ use bevy::{
|
|||||||
|
|
||||||
use crate::ATTRIBUTE_OUTLINE_NORMAL;
|
use crate::ATTRIBUTE_OUTLINE_NORMAL;
|
||||||
|
|
||||||
|
enum IndexIterator<'a> {
|
||||||
|
ExplicitU16(std::slice::Iter<'a, u16>),
|
||||||
|
ExplicitU32(std::slice::Iter<'a, u32>),
|
||||||
|
Implicit(std::ops::Range<usize>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Mesh> for IndexIterator<'a> {
|
||||||
|
fn from(value: &'a Mesh) -> Self {
|
||||||
|
match value.indices() {
|
||||||
|
Some(Indices::U16(vec)) => IndexIterator::ExplicitU16(vec.iter()),
|
||||||
|
Some(Indices::U32(vec)) => IndexIterator::ExplicitU32(vec.iter()),
|
||||||
|
None => IndexIterator::Implicit(0..value.count_vertices()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for IndexIterator<'_> {
|
||||||
|
type Item = usize;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self {
|
||||||
|
IndexIterator::ExplicitU16(iter) => iter.next().map(|val| *val as usize),
|
||||||
|
IndexIterator::ExplicitU32(iter) => iter.next().map(|val| *val as usize),
|
||||||
|
IndexIterator::Implicit(iter) => iter.next(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
match self {
|
||||||
|
IndexIterator::ExplicitU16(iter) => iter.size_hint(),
|
||||||
|
IndexIterator::ExplicitU32(iter) => iter.size_hint(),
|
||||||
|
IndexIterator::Implicit(iter) => iter.size_hint(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExactSizeIterator for IndexIterator<'_> {}
|
||||||
|
|
||||||
/// Failed to generate outline normals for the mesh.
|
/// Failed to generate outline normals for the mesh.
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum GenerateOutlineNormalsError {
|
pub enum GenerateOutlineNormalsError {
|
||||||
@ -22,7 +60,7 @@ pub enum GenerateOutlineNormalsError {
|
|||||||
|
|
||||||
/// Extension methods for [`Mesh`].
|
/// Extension methods for [`Mesh`].
|
||||||
pub trait OutlineMeshExt {
|
pub trait OutlineMeshExt {
|
||||||
/// Generates outline normals for the mesh from the face normals.
|
/// Generates outline normals for the mesh.
|
||||||
///
|
///
|
||||||
/// Vertex extrusion only works for meshes with smooth surface normals. Hard edges cause
|
/// Vertex extrusion only works for meshes with smooth surface normals. Hard edges cause
|
||||||
/// visual artefacts. This function generates faux-smooth normals for outlining purposes
|
/// visual artefacts. This function generates faux-smooth normals for outlining purposes
|
||||||
@ -53,34 +91,29 @@ impl OutlineMeshExt for Mesh {
|
|||||||
v.into(),
|
v.into(),
|
||||||
)),
|
)),
|
||||||
}?;
|
}?;
|
||||||
let mut map = HashMap::<[FloatOrd; 3], Vec3>::with_capacity(positions.len());
|
let normals = match self.attribute(Mesh::ATTRIBUTE_NORMAL) {
|
||||||
let mut proc = |p0: Vec3, p1: Vec3, p2: Vec3| {
|
Some(VertexAttributeValues::Float32x3(p)) => Some(p),
|
||||||
let face_normal = (p1 - p0).cross(p2 - p0).normalize_or_zero();
|
_ => None,
|
||||||
for (cp0, cp1, cp2) in [(p0, p1, p2), (p1, p2, p0), (p2, p0, p1)] {
|
|
||||||
let angle = (cp1 - cp0).angle_between(cp2 - cp0);
|
|
||||||
let n = map
|
|
||||||
.entry([FloatOrd(cp0.x), FloatOrd(cp0.y), FloatOrd(cp0.z)])
|
|
||||||
.or_default();
|
|
||||||
*n += angle * face_normal;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if let Some(indices) = self.indices() {
|
let mut map = HashMap::<[FloatOrd; 3], Vec3>::with_capacity(positions.len());
|
||||||
let mut it = indices.iter();
|
let mut it = IndexIterator::from(&*self);
|
||||||
while let (Some(i0), Some(i1), Some(i2)) = (it.next(), it.next(), it.next()) {
|
while let (Some(i0), Some(i1), Some(i2)) = (it.next(), it.next(), it.next()) {
|
||||||
proc(
|
for (j0, j1, j2) in [(i0, i1, i2), (i1, i2, i0), (i2, i0, i1)] {
|
||||||
Vec3::from_array(positions[i0]),
|
let p0 = Vec3::from(positions[j0]);
|
||||||
Vec3::from_array(positions[i1]),
|
let p1 = Vec3::from(positions[j1]);
|
||||||
Vec3::from_array(positions[i2]),
|
let p2 = Vec3::from(positions[j2]);
|
||||||
);
|
let angle = (p1 - p0).angle_between(p2 - p0);
|
||||||
}
|
let n = map
|
||||||
} else {
|
.entry([FloatOrd(p0.x), FloatOrd(p0.y), FloatOrd(p0.z)])
|
||||||
let mut it = positions.iter();
|
.or_default();
|
||||||
while let (Some(p0), Some(p1), Some(p2)) = (it.next(), it.next(), it.next()) {
|
*n += angle
|
||||||
proc(
|
* if let Some(ns) = normals {
|
||||||
Vec3::from_array(*p0),
|
// Use vertex normal
|
||||||
Vec3::from_array(*p1),
|
Vec3::from(ns[j0])
|
||||||
Vec3::from_array(*p2),
|
} else {
|
||||||
);
|
// Calculate face normal
|
||||||
|
(p1 - p0).cross(p2 - p0).normalize_or_zero()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut outlines = Vec::with_capacity(positions.len());
|
let mut outlines = Vec::with_capacity(positions.len());
|
||||||
|
Loading…
Reference in New Issue
Block a user