Change outline normal generator to use face normals.
This commit is contained in:
parent
cf7358cc44
commit
a55c60fd21
|
@ -1,6 +1,9 @@
|
||||||
use bevy::{
|
use bevy::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
render::{mesh::VertexAttributeValues, render_resource::VertexFormat},
|
render::{
|
||||||
|
mesh::VertexAttributeValues,
|
||||||
|
render_resource::{PrimitiveTopology, VertexFormat},
|
||||||
|
},
|
||||||
utils::{FloatOrd, HashMap, HashSet},
|
utils::{FloatOrd, HashMap, HashSet},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,6 +12,8 @@ use crate::ATTRIBUTE_OUTLINE_NORMAL;
|
||||||
/// 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 {
|
||||||
|
#[error("unsupported primitive topology '{0:?}'")]
|
||||||
|
UnsupportedPrimitiveTopology(PrimitiveTopology),
|
||||||
#[error("missing vertex attributes '{0}'")]
|
#[error("missing vertex attributes '{0}'")]
|
||||||
MissingVertexAttribute(&'static str),
|
MissingVertexAttribute(&'static str),
|
||||||
#[error("the '{0}' vertex attribute should have {1:?} format, but had {2:?} format")]
|
#[error("the '{0}' vertex attribute should have {1:?} format, but had {2:?} format")]
|
||||||
|
@ -17,7 +22,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 regular normals.
|
/// Generates outline normals for the mesh from the face normals.
|
||||||
///
|
///
|
||||||
/// 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
|
||||||
|
@ -27,13 +32,17 @@ pub trait OutlineMeshExt {
|
||||||
/// perpendicular to the surface of the mesh, this technique may result in non-uniform
|
/// perpendicular to the surface of the mesh, this technique may result in non-uniform
|
||||||
/// outline thickness.
|
/// outline thickness.
|
||||||
///
|
///
|
||||||
/// This function will silently do nothing if the outline normals would be equal to the
|
/// This function only supports meshes with TriangleList topology.
|
||||||
/// regular normals.
|
|
||||||
fn generate_outline_normals(&mut self) -> Result<(), GenerateOutlineNormalsError>;
|
fn generate_outline_normals(&mut self) -> Result<(), GenerateOutlineNormalsError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutlineMeshExt for Mesh {
|
impl OutlineMeshExt for Mesh {
|
||||||
fn generate_outline_normals(&mut self) -> Result<(), GenerateOutlineNormalsError> {
|
fn generate_outline_normals(&mut self) -> Result<(), GenerateOutlineNormalsError> {
|
||||||
|
if self.primitive_topology() != PrimitiveTopology::TriangleList {
|
||||||
|
return Err(GenerateOutlineNormalsError::UnsupportedPrimitiveTopology(
|
||||||
|
self.primitive_topology(),
|
||||||
|
));
|
||||||
|
}
|
||||||
let positions = match self.attribute(Mesh::ATTRIBUTE_POSITION).ok_or(
|
let positions = match self.attribute(Mesh::ATTRIBUTE_POSITION).ok_or(
|
||||||
GenerateOutlineNormalsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name),
|
GenerateOutlineNormalsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name),
|
||||||
)? {
|
)? {
|
||||||
|
@ -44,39 +53,45 @@ impl OutlineMeshExt for Mesh {
|
||||||
v.into(),
|
v.into(),
|
||||||
)),
|
)),
|
||||||
}?;
|
}?;
|
||||||
let normals = match self.attribute(Mesh::ATTRIBUTE_NORMAL).ok_or(
|
let mut map = HashMap::<[FloatOrd; 3], Vec3>::with_capacity(positions.len());
|
||||||
GenerateOutlineNormalsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name),
|
let mut proc = |p0: Vec3, p1: Vec3, p2: Vec3| {
|
||||||
)? {
|
let face_normal = (p1 - p0).cross(p2 - p0).normalize_or_zero();
|
||||||
VertexAttributeValues::Float32x3(n) => Ok(n),
|
for (cp0, cp1, cp2) in [(p0, p1, p2), (p1, p2, p0), (p2, p0, p1)] {
|
||||||
v => Err(GenerateOutlineNormalsError::InvalidVertexAttributeFormat(
|
let angle = (cp1 - cp0).angle_between(cp2 - cp0);
|
||||||
Mesh::ATTRIBUTE_NORMAL.name,
|
let n = map
|
||||||
VertexFormat::Float32x3,
|
.entry([FloatOrd(cp0.x), FloatOrd(cp0.y), FloatOrd(cp0.z)])
|
||||||
v.into(),
|
.or_default();
|
||||||
)),
|
*n += angle * face_normal;
|
||||||
}?;
|
}
|
||||||
let mut map = HashMap::with_capacity(positions.len());
|
};
|
||||||
let mut modified = false;
|
if let Some(indices) = self.indices() {
|
||||||
for (p, n) in positions.iter().zip(normals.iter()) {
|
let mut it = indices.iter();
|
||||||
let key = [FloatOrd(p[0]), FloatOrd(p[1]), FloatOrd(p[2])];
|
while let (Some(i0), Some(i1), Some(i2)) = (it.next(), it.next(), it.next()) {
|
||||||
let value = Vec3::from_array(*n);
|
proc(
|
||||||
map.entry(key)
|
Vec3::from_array(positions[i0]),
|
||||||
.and_modify(|e| {
|
Vec3::from_array(positions[i1]),
|
||||||
modified = true;
|
Vec3::from_array(positions[i2]),
|
||||||
*e += value
|
);
|
||||||
})
|
}
|
||||||
.or_insert(value);
|
} else {
|
||||||
}
|
let mut it = positions.iter();
|
||||||
if modified {
|
while let (Some(p0), Some(p1), Some(p2)) = (it.next(), it.next(), it.next()) {
|
||||||
let mut outlines = Vec::with_capacity(positions.len());
|
proc(
|
||||||
for p in positions.iter() {
|
Vec3::from_array(*p0),
|
||||||
let key = [FloatOrd(p[0]), FloatOrd(p[1]), FloatOrd(p[2])];
|
Vec3::from_array(*p1),
|
||||||
outlines.push(map.get(&key).unwrap().normalize_or_zero().to_array());
|
Vec3::from_array(*p2),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
self.insert_attribute(
|
|
||||||
ATTRIBUTE_OUTLINE_NORMAL,
|
|
||||||
VertexAttributeValues::Float32x3(outlines),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
let mut outlines = Vec::with_capacity(positions.len());
|
||||||
|
for p in positions.iter() {
|
||||||
|
let key = [FloatOrd(p[0]), FloatOrd(p[1]), FloatOrd(p[2])];
|
||||||
|
outlines.push(map.get(&key).unwrap().normalize_or_zero().to_array());
|
||||||
|
}
|
||||||
|
self.insert_attribute(
|
||||||
|
ATTRIBUTE_OUTLINE_NORMAL,
|
||||||
|
VertexAttributeValues::Float32x3(outlines),
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue