Add optional vertex attribute for outline normals.
This commit is contained in:
parent
3e8c7e69a2
commit
5c5c4dc5d9
|
@ -15,6 +15,7 @@ bevy = { version = "0.8", default-features = false, features = [
|
|||
"bevy_asset",
|
||||
"render",
|
||||
] }
|
||||
thiserror = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
bevy = { version = "0.8", default-features = false, features = [
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use std::f32::consts::{PI, TAU};
|
||||
|
||||
use bevy::prelude::{shape::Torus, *};
|
||||
use bevy::prelude::{
|
||||
shape::{Cube, Torus},
|
||||
*,
|
||||
};
|
||||
|
||||
use bevy_mod_outline::*;
|
||||
|
||||
|
@ -33,14 +36,11 @@ fn setup(
|
|||
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
|
||||
..default()
|
||||
});
|
||||
let mut cube_mesh = Mesh::from(Cube { size: 1.0 });
|
||||
cube_mesh.generate_outline_normals().unwrap();
|
||||
commands
|
||||
.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(Torus {
|
||||
radius: 0.6,
|
||||
ring_radius: 0.2,
|
||||
subdivisions_segments: 20,
|
||||
subdivisions_sides: 10,
|
||||
})),
|
||||
mesh: meshes.add(cube_mesh),
|
||||
material: materials.add(Color::rgb(0.1, 0.1, 0.9).into()),
|
||||
transform: Transform::from_xyz(0.0, 1.0, 0.0),
|
||||
..default()
|
||||
|
|
68
src/lib.rs
68
src/lib.rs
|
@ -4,10 +4,12 @@ use bevy::prelude::*;
|
|||
use bevy::render::extract_component::{
|
||||
ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
|
||||
};
|
||||
use bevy::render::mesh::{MeshVertexAttribute, VertexAttributeValues};
|
||||
use bevy::render::render_graph::RenderGraph;
|
||||
use bevy::render::render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions};
|
||||
use bevy::render::render_resource::SpecializedMeshPipelines;
|
||||
use bevy::render::render_resource::{SpecializedMeshPipelines, VertexFormat};
|
||||
use bevy::render::{RenderApp, RenderStage};
|
||||
use bevy::utils::{FloatOrd, HashMap};
|
||||
|
||||
use crate::draw::{queue_outline_mesh, queue_outline_stencil_mesh, DrawOutline, DrawStencil};
|
||||
use crate::node::{OpaqueOutline, OutlineNode, StencilOutline, TransparentOutline};
|
||||
|
@ -27,7 +29,14 @@ mod view_uniforms;
|
|||
|
||||
// See https://alexanderameye.github.io/notes/rendering-outlines/
|
||||
|
||||
/// A component for stenciling meshes during outline rendering
|
||||
/// The direction to extrude the vertex when rendering the outline.
|
||||
pub const ATTRIBUTE_OUTLINE_NORMAL: MeshVertexAttribute = MeshVertexAttribute::new(
|
||||
"Outline_Normal",
|
||||
1585570526414773879,
|
||||
VertexFormat::Float32x3,
|
||||
);
|
||||
|
||||
/// A component for stenciling meshes during outline rendering.
|
||||
#[derive(Component, Default)]
|
||||
pub struct OutlineStencil;
|
||||
|
||||
|
@ -49,6 +58,61 @@ pub struct Outline {
|
|||
pub colour: Color,
|
||||
}
|
||||
|
||||
/// Failed to generate outline normals for the mesh.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum GenerateOutlineNormalsError {
|
||||
#[error("missing vertex attributes '{0}'")]
|
||||
MissingVertexAttribute(&'static str),
|
||||
#[error("the '{0}' vertex attribute should have {1:?} format, but had {2:?} format")]
|
||||
InvalidVertexAttributeFormat(&'static str, VertexFormat, VertexFormat),
|
||||
}
|
||||
|
||||
pub trait OutlineMeshExt {
|
||||
/// Generates outline normals for the mesh by normalising the sum of the regular normals.
|
||||
fn generate_outline_normals(&mut self) -> Result<(), GenerateOutlineNormalsError>;
|
||||
}
|
||||
|
||||
impl OutlineMeshExt for Mesh {
|
||||
fn generate_outline_normals(&mut self) -> Result<(), GenerateOutlineNormalsError> {
|
||||
let positions = match self.attribute(Mesh::ATTRIBUTE_POSITION).ok_or(
|
||||
GenerateOutlineNormalsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name),
|
||||
)? {
|
||||
VertexAttributeValues::Float32x3(p) => Ok(p),
|
||||
v => Err(GenerateOutlineNormalsError::InvalidVertexAttributeFormat(
|
||||
Mesh::ATTRIBUTE_POSITION.name,
|
||||
VertexFormat::Float32x3,
|
||||
v.into(),
|
||||
)),
|
||||
}?;
|
||||
let normals = match self.attribute(Mesh::ATTRIBUTE_NORMAL).ok_or(
|
||||
GenerateOutlineNormalsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name),
|
||||
)? {
|
||||
VertexAttributeValues::Float32x3(n) => Ok(n),
|
||||
v => Err(GenerateOutlineNormalsError::InvalidVertexAttributeFormat(
|
||||
Mesh::ATTRIBUTE_NORMAL.name,
|
||||
VertexFormat::Float32x3,
|
||||
v.into(),
|
||||
)),
|
||||
}?;
|
||||
let mut map = HashMap::with_capacity(positions.len());
|
||||
for (p, n) in positions.iter().zip(normals.iter()) {
|
||||
let key = [FloatOrd(p[0]), FloatOrd(p[1]), FloatOrd(p[2])];
|
||||
let value = Vec3::from_array(*n);
|
||||
map.entry(key).and_modify(|e| *e += value).or_insert(value);
|
||||
}
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds support for rendering outlines.
|
||||
pub struct OutlinePlugin;
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ use bevy::{
|
|||
|
||||
use crate::uniforms::{OutlineFragmentUniform, OutlineVertexUniform};
|
||||
use crate::view_uniforms::OutlineViewUniform;
|
||||
use crate::ATTRIBUTE_OUTLINE_NORMAL;
|
||||
|
||||
pub const COMMON_SHADER_HANDLE: HandleUntyped =
|
||||
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 9448276477068917228);
|
||||
|
@ -108,7 +109,7 @@ impl SpecializedMeshPipeline for OutlinePipeline {
|
|||
fn specialize(
|
||||
&self,
|
||||
(key, pass_type): Self::Key,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
mesh_layout: &MeshVertexBufferLayout,
|
||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
let mut targets = vec![];
|
||||
let mut bind_layouts = vec![
|
||||
|
@ -135,10 +136,17 @@ impl SpecializedMeshPipeline for OutlinePipeline {
|
|||
|
||||
bind_layouts.push(self.outline_view_bind_group_layout.clone());
|
||||
bind_layouts.push(self.outline_bind_group_layout.clone());
|
||||
buffer_attrs.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1));
|
||||
buffer_attrs.push(
|
||||
if mesh_layout.contains(ATTRIBUTE_OUTLINE_NORMAL) {
|
||||
ATTRIBUTE_OUTLINE_NORMAL
|
||||
} else {
|
||||
Mesh::ATTRIBUTE_NORMAL
|
||||
}
|
||||
.at_shader_location(1),
|
||||
);
|
||||
}
|
||||
}
|
||||
let buffers = vec![layout.get_layout(&buffer_attrs)?];
|
||||
let buffers = vec![mesh_layout.get_layout(&buffer_attrs)?];
|
||||
Ok(RenderPipelineDescriptor {
|
||||
vertex: VertexState {
|
||||
shader: shader.clone().typed::<Shader>(),
|
||||
|
|
Loading…
Reference in New Issue