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",
|
"bevy_asset",
|
||||||
"render",
|
"render",
|
||||||
] }
|
] }
|
||||||
|
thiserror = "1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bevy = { version = "0.8", default-features = false, features = [
|
bevy = { version = "0.8", default-features = false, features = [
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
use std::f32::consts::{PI, TAU};
|
use std::f32::consts::{PI, TAU};
|
||||||
|
|
||||||
use bevy::prelude::{shape::Torus, *};
|
use bevy::prelude::{
|
||||||
|
shape::{Cube, Torus},
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
|
||||||
use bevy_mod_outline::*;
|
use bevy_mod_outline::*;
|
||||||
|
|
||||||
@ -33,14 +36,11 @@ fn setup(
|
|||||||
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
|
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
|
let mut cube_mesh = Mesh::from(Cube { size: 1.0 });
|
||||||
|
cube_mesh.generate_outline_normals().unwrap();
|
||||||
commands
|
commands
|
||||||
.spawn_bundle(PbrBundle {
|
.spawn_bundle(PbrBundle {
|
||||||
mesh: meshes.add(Mesh::from(Torus {
|
mesh: meshes.add(cube_mesh),
|
||||||
radius: 0.6,
|
|
||||||
ring_radius: 0.2,
|
|
||||||
subdivisions_segments: 20,
|
|
||||||
subdivisions_sides: 10,
|
|
||||||
})),
|
|
||||||
material: materials.add(Color::rgb(0.1, 0.1, 0.9).into()),
|
material: materials.add(Color::rgb(0.1, 0.1, 0.9).into()),
|
||||||
transform: Transform::from_xyz(0.0, 1.0, 0.0),
|
transform: Transform::from_xyz(0.0, 1.0, 0.0),
|
||||||
..default()
|
..default()
|
||||||
|
68
src/lib.rs
68
src/lib.rs
@ -4,10 +4,12 @@ use bevy::prelude::*;
|
|||||||
use bevy::render::extract_component::{
|
use bevy::render::extract_component::{
|
||||||
ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
|
ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
|
||||||
};
|
};
|
||||||
|
use bevy::render::mesh::{MeshVertexAttribute, VertexAttributeValues};
|
||||||
use bevy::render::render_graph::RenderGraph;
|
use bevy::render::render_graph::RenderGraph;
|
||||||
use bevy::render::render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions};
|
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::render::{RenderApp, RenderStage};
|
||||||
|
use bevy::utils::{FloatOrd, HashMap};
|
||||||
|
|
||||||
use crate::draw::{queue_outline_mesh, queue_outline_stencil_mesh, DrawOutline, DrawStencil};
|
use crate::draw::{queue_outline_mesh, queue_outline_stencil_mesh, DrawOutline, DrawStencil};
|
||||||
use crate::node::{OpaqueOutline, OutlineNode, StencilOutline, TransparentOutline};
|
use crate::node::{OpaqueOutline, OutlineNode, StencilOutline, TransparentOutline};
|
||||||
@ -27,7 +29,14 @@ mod view_uniforms;
|
|||||||
|
|
||||||
// See https://alexanderameye.github.io/notes/rendering-outlines/
|
// 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)]
|
#[derive(Component, Default)]
|
||||||
pub struct OutlineStencil;
|
pub struct OutlineStencil;
|
||||||
|
|
||||||
@ -49,6 +58,61 @@ pub struct Outline {
|
|||||||
pub colour: Color,
|
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.
|
/// Adds support for rendering outlines.
|
||||||
pub struct OutlinePlugin;
|
pub struct OutlinePlugin;
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ use bevy::{
|
|||||||
|
|
||||||
use crate::uniforms::{OutlineFragmentUniform, OutlineVertexUniform};
|
use crate::uniforms::{OutlineFragmentUniform, OutlineVertexUniform};
|
||||||
use crate::view_uniforms::OutlineViewUniform;
|
use crate::view_uniforms::OutlineViewUniform;
|
||||||
|
use crate::ATTRIBUTE_OUTLINE_NORMAL;
|
||||||
|
|
||||||
pub const COMMON_SHADER_HANDLE: HandleUntyped =
|
pub const COMMON_SHADER_HANDLE: HandleUntyped =
|
||||||
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 9448276477068917228);
|
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 9448276477068917228);
|
||||||
@ -108,7 +109,7 @@ impl SpecializedMeshPipeline for OutlinePipeline {
|
|||||||
fn specialize(
|
fn specialize(
|
||||||
&self,
|
&self,
|
||||||
(key, pass_type): Self::Key,
|
(key, pass_type): Self::Key,
|
||||||
layout: &MeshVertexBufferLayout,
|
mesh_layout: &MeshVertexBufferLayout,
|
||||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||||
let mut targets = vec![];
|
let mut targets = vec![];
|
||||||
let mut bind_layouts = 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_view_bind_group_layout.clone());
|
||||||
bind_layouts.push(self.outline_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 {
|
Ok(RenderPipelineDescriptor {
|
||||||
vertex: VertexState {
|
vertex: VertexState {
|
||||||
shader: shader.clone().typed::<Shader>(),
|
shader: shader.clone().typed::<Shader>(),
|
||||||
|
Loading…
Reference in New Issue
Block a user