Port to Bevy 0.11 (#22)
--------- Co-authored-by: mramirez <ramirezmike2@gmail.com> Co-authored-by: Zain Azam <zainyusufazam@gmail.com>
This commit is contained in:
parent
71add9ecb5
commit
ae5c331450
17
Cargo.toml
17
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bevy_mod_outline"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "A mesh outlining plugin for Bevy."
|
||||
@ -11,27 +11,30 @@ keywords = ["gamedev", "bevy", "outline"]
|
||||
categories = ["game-engines", "rendering"]
|
||||
|
||||
[dependencies]
|
||||
bevy = { version = "0.10", default-features = false, features = [
|
||||
bevy = { version = "0.11", default-features = false, features = [
|
||||
"bevy_asset",
|
||||
"bevy_render",
|
||||
"bevy_pbr",
|
||||
"ktx2",
|
||||
"tonemapping_luts",
|
||||
"zstd",
|
||||
"bevy_core_pipeline",
|
||||
] }
|
||||
bitfield = "0.14"
|
||||
interpolation = "0.2"
|
||||
thiserror = "1.0"
|
||||
wgpu-types = "0.15"
|
||||
wgpu-types = "0.16.1"
|
||||
|
||||
[dev-dependencies]
|
||||
bevy = { version = "0.10", default-features = false, features = [
|
||||
bevy = { version = "0.11", default-features = false, features = [
|
||||
"animation",
|
||||
"bevy_gltf",
|
||||
"bevy_pbr",
|
||||
"bevy_scene",
|
||||
"bevy_winit",
|
||||
"png",
|
||||
"x11",
|
||||
] }
|
||||
bevy_mod_gltf_patched = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["bevy_ui"]
|
||||
@ -56,3 +59,7 @@ path = "examples/animated_fox.rs"
|
||||
[[example]]
|
||||
name = "hollow"
|
||||
path = "examples/hollow.rs"
|
||||
|
||||
[[example]]
|
||||
name = "morph_targets"
|
||||
path = "examples/morph_targets.rs"
|
||||
|
15
README.md
15
README.md
@ -12,7 +12,7 @@ vertex extrusion method.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
bevy_mod_outline = "0.4"
|
||||
bevy_mod_outline = "0.5"
|
||||
```
|
||||
|
||||
## Examples
|
||||
@ -49,14 +49,21 @@ A glTF model with pre-baked outline normals.
|
||||
cargo run --example hollow
|
||||
```
|
||||
|
||||
An animated morphing glTF model with an outline.
|
||||
|
||||
```shell
|
||||
cargo run --example morph_targets
|
||||
```
|
||||
|
||||
## Versions
|
||||
|
||||
| This Version | Bevy version |
|
||||
|--------------|--------------|
|
||||
| 0.1.x | 0.7.x |
|
||||
| 0.2.x | 0.8.x |
|
||||
| 0.3.x | 0.9.x |
|
||||
| 0.5.x | 0.11.x |
|
||||
| 0.4.x | 0.10.x |
|
||||
| 0.3.x | 0.9.x |
|
||||
| 0.2.x | 0.8.x |
|
||||
| 0.1.x | 0.7.x |
|
||||
|
||||
## Features
|
||||
|
||||
|
1055
assets/MorphStressTest.gltf
Normal file
1055
assets/MorphStressTest.gltf
Normal file
File diff suppressed because one or more lines are too long
@ -14,16 +14,17 @@ struct Fox(Handle<AnimationClip>);
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(OutlinePlugin)
|
||||
.add_plugin(AutoGenerateOutlineNormalsPlugin)
|
||||
.add_plugins((
|
||||
DefaultPlugins,
|
||||
OutlinePlugin,
|
||||
AutoGenerateOutlineNormalsPlugin,
|
||||
))
|
||||
.insert_resource(AmbientLight {
|
||||
color: Color::WHITE,
|
||||
brightness: 1.0,
|
||||
})
|
||||
.add_startup_system(setup)
|
||||
.add_system(setup_scene_once_loaded)
|
||||
.add_system(close_on_esc)
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, (setup_scene_once_loaded, close_on_esc))
|
||||
.run();
|
||||
}
|
||||
|
||||
|
@ -1,28 +1,27 @@
|
||||
use std::f32::consts::{PI, TAU};
|
||||
|
||||
use bevy::{prelude::*, scene::SceneInstance, window::close_on_esc};
|
||||
use bevy_mod_gltf_patched::GltfPlugin;
|
||||
use bevy::{gltf::GltfPlugin, prelude::*, scene::SceneInstance, window::close_on_esc};
|
||||
use bevy_mod_outline::*;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
// Disable built-in glTF plugin
|
||||
.add_plugins(DefaultPlugins.build().disable::<bevy::gltf::GltfPlugin>())
|
||||
// Register outline normal vertex attribute with bevy_mod_gltf_patched
|
||||
.add_plugin(
|
||||
GltfPlugin::default()
|
||||
.add_custom_vertex_attribute("_OUTLINE_NORMAL", ATTRIBUTE_OUTLINE_NORMAL),
|
||||
// Register outline normal vertex attribute with glTF plugin
|
||||
.add_plugins(
|
||||
DefaultPlugins.build().set(
|
||||
GltfPlugin::default()
|
||||
.add_custom_vertex_attribute("_OUTLINE_NORMAL", ATTRIBUTE_OUTLINE_NORMAL),
|
||||
),
|
||||
)
|
||||
.add_plugin(OutlinePlugin)
|
||||
.add_plugins(OutlinePlugin)
|
||||
.insert_resource(AmbientLight {
|
||||
color: Color::WHITE,
|
||||
brightness: 1.0,
|
||||
})
|
||||
.add_startup_system(setup)
|
||||
.add_system(setup_scene_once_loaded)
|
||||
.add_system(rotates)
|
||||
.add_system(rotates_hue)
|
||||
.add_system(close_on_esc)
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(
|
||||
Update,
|
||||
(setup_scene_once_loaded, rotates, rotates_hue, close_on_esc),
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
126
examples/morph_targets.rs
Normal file
126
examples/morph_targets.rs
Normal file
@ -0,0 +1,126 @@
|
||||
//! Controls morph targets in a loaded scene.
|
||||
//!
|
||||
//! Illustrates:
|
||||
//!
|
||||
//! - How to access and modify individual morph target weights.
|
||||
//! See the [`update_weights`] system for details.
|
||||
//! - How to read morph target names in [`name_morphs`].
|
||||
//! - How to play morph target animations in [`setup_animations`].
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_mod_outline::{
|
||||
AutoGenerateOutlineNormalsPlugin, OutlineBundle, OutlinePlugin, OutlineVolume,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins((
|
||||
DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
title: "morph targets".to_string(),
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
}),
|
||||
OutlinePlugin,
|
||||
AutoGenerateOutlineNormalsPlugin,
|
||||
))
|
||||
.insert_resource(AmbientLight {
|
||||
brightness: 1.0,
|
||||
..default()
|
||||
})
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, (name_morphs, setup_outlines, setup_animations))
|
||||
.run();
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct MorphData {
|
||||
the_wave: Handle<AnimationClip>,
|
||||
mesh: Handle<Mesh>,
|
||||
}
|
||||
|
||||
fn setup(asset_server: Res<AssetServer>, mut commands: Commands) {
|
||||
commands.insert_resource(MorphData {
|
||||
the_wave: asset_server.load("MorphStressTest.gltf#Animation2"),
|
||||
mesh: asset_server.load("MorphStressTest.gltf#Mesh0/Primitive0"),
|
||||
});
|
||||
commands.spawn(SceneBundle {
|
||||
scene: asset_server.load("MorphStressTest.gltf#Scene0"),
|
||||
..default()
|
||||
});
|
||||
commands.spawn(DirectionalLightBundle {
|
||||
directional_light: DirectionalLight {
|
||||
color: Color::WHITE,
|
||||
illuminance: 19350.0,
|
||||
..default()
|
||||
},
|
||||
transform: Transform::from_rotation(Quat::from_rotation_z(PI / 2.0)),
|
||||
..default()
|
||||
});
|
||||
commands.spawn(Camera3dBundle {
|
||||
transform: Transform::from_xyz(3.0, 2.1, 10.2).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
/// Adds outlines to the meshes.
|
||||
fn setup_outlines(
|
||||
mut commands: Commands,
|
||||
mut has_setup: Local<bool>,
|
||||
meshes: Query<Entity, With<Handle<Mesh>>>,
|
||||
) {
|
||||
if *has_setup {
|
||||
return;
|
||||
}
|
||||
for entity in &meshes {
|
||||
commands.entity(entity).insert(OutlineBundle {
|
||||
outline: OutlineVolume {
|
||||
visible: true,
|
||||
width: 3.0,
|
||||
colour: Color::RED,
|
||||
},
|
||||
..default()
|
||||
});
|
||||
*has_setup = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Plays an [`AnimationClip`] from the loaded [`Gltf`] on the [`AnimationPlayer`] created by the spawned scene.
|
||||
fn setup_animations(
|
||||
mut has_setup: Local<bool>,
|
||||
mut players: Query<(&Name, &mut AnimationPlayer)>,
|
||||
morph_data: Res<MorphData>,
|
||||
) {
|
||||
if *has_setup {
|
||||
return;
|
||||
}
|
||||
for (name, mut player) in &mut players {
|
||||
// The name of the entity in the GLTF scene containing the AnimationPlayer for our morph targets is "Main"
|
||||
if name.as_str() != "Main" {
|
||||
continue;
|
||||
}
|
||||
player.play(morph_data.the_wave.clone()).repeat();
|
||||
*has_setup = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// You can get the target names in their corresponding [`Mesh`].
|
||||
/// They are in the order of the weights.
|
||||
fn name_morphs(
|
||||
mut has_printed: Local<bool>,
|
||||
morph_data: Res<MorphData>,
|
||||
meshes: Res<Assets<Mesh>>,
|
||||
) {
|
||||
if *has_printed {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(mesh) = meshes.get(&morph_data.mesh) else { return };
|
||||
let Some(names) = mesh.morph_target_names() else { return };
|
||||
for name in names {
|
||||
println!(" {name}");
|
||||
}
|
||||
*has_printed = true;
|
||||
}
|
@ -15,11 +15,9 @@ fn main() {
|
||||
App::new()
|
||||
.insert_resource(Msaa::Sample4)
|
||||
.insert_resource(ClearColor(Color::BLACK))
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(OutlinePlugin)
|
||||
.add_startup_system(setup)
|
||||
.add_system(close_on_esc)
|
||||
.add_system(rotates)
|
||||
.add_plugins((DefaultPlugins, OutlinePlugin))
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, (close_on_esc, rotates))
|
||||
.run();
|
||||
}
|
||||
|
||||
|
@ -16,11 +16,9 @@ fn main() {
|
||||
App::new()
|
||||
.insert_resource(Msaa::Sample4)
|
||||
.insert_resource(ClearColor(Color::BLACK))
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(OutlinePlugin)
|
||||
.add_startup_system(setup)
|
||||
.add_system(close_on_esc)
|
||||
.add_system(set_camera_viewports)
|
||||
.add_plugins((DefaultPlugins, OutlinePlugin))
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, (close_on_esc, set_camera_viewports))
|
||||
.run();
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,9 @@ fn main() {
|
||||
App::new()
|
||||
.insert_resource(Msaa::Sample4)
|
||||
.insert_resource(ClearColor(Color::BLACK))
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(OutlinePlugin)
|
||||
.add_startup_system(setup)
|
||||
.add_system(close_on_esc)
|
||||
.add_system(wobble)
|
||||
.add_system(orbit)
|
||||
.add_plugins((DefaultPlugins, OutlinePlugin))
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, (close_on_esc, wobble, orbit))
|
||||
.run();
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,8 @@ pub(crate) fn queue_outline_stencil_mesh(
|
||||
let key = base_key
|
||||
.with_primitive_topology(mesh.primitive_topology)
|
||||
.with_depth_mode(stencil_flags.depth_mode)
|
||||
.with_offset_zero(stencil_uniform.offset == 0.0);
|
||||
.with_offset_zero(stencil_uniform.offset == 0.0)
|
||||
.with_morph_targets(mesh.morph_targets.is_some());
|
||||
let pipeline = pipelines
|
||||
.specialize(&pipeline_cache, &stencil_pipeline, key, &mesh.layout)
|
||||
.unwrap();
|
||||
@ -161,7 +162,8 @@ pub(crate) fn queue_outline_volume_mesh(
|
||||
})
|
||||
.with_depth_mode(volume_flags.depth_mode)
|
||||
.with_offset_zero(volume_uniform.offset == 0.0)
|
||||
.with_hdr_format(view.hdr);
|
||||
.with_hdr_format(view.hdr)
|
||||
.with_morph_targets(mesh.morph_targets.is_some());
|
||||
let pipeline = pipelines
|
||||
.specialize(&pipeline_cache, &outline_pipeline, key, &mesh.layout)
|
||||
.unwrap();
|
||||
|
@ -168,6 +168,6 @@ pub struct AutoGenerateOutlineNormalsPlugin;
|
||||
|
||||
impl Plugin for AutoGenerateOutlineNormalsPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_system(auto_generate_outline_normals);
|
||||
app.add_systems(Update, auto_generate_outline_normals);
|
||||
}
|
||||
}
|
||||
|
95
src/lib.rs
95
src/lib.rs
@ -31,7 +31,7 @@ use bevy::render::render_graph::RenderGraph;
|
||||
use bevy::render::render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions};
|
||||
use bevy::render::render_resource::{SpecializedMeshPipelines, VertexFormat};
|
||||
use bevy::render::view::RenderLayers;
|
||||
use bevy::render::{RenderApp, RenderSet};
|
||||
use bevy::render::{Render, RenderApp, RenderSet};
|
||||
use bevy::transform::TransformSystem;
|
||||
use interpolation::Lerp;
|
||||
|
||||
@ -207,37 +207,55 @@ impl Plugin for OutlinePlugin {
|
||||
Shader::from_wgsl
|
||||
);
|
||||
|
||||
app.add_plugin(ExtractComponentPlugin::<OutlineStencil>::extract_visible())
|
||||
.add_plugin(ExtractComponentPlugin::<OutlineRenderLayers>::default())
|
||||
.add_plugin(UniformComponentPlugin::<OutlineStencilUniform>::default())
|
||||
.add_plugin(UniformComponentPlugin::<OutlineVolumeUniform>::default())
|
||||
.add_plugin(UniformComponentPlugin::<OutlineFragmentUniform>::default())
|
||||
.add_plugin(UniformComponentPlugin::<OutlineViewUniform>::default())
|
||||
.add_system(
|
||||
compute_outline_depth
|
||||
.in_base_set(CoreSet::PostUpdate)
|
||||
.after(TransformSystem::TransformPropagate),
|
||||
)
|
||||
.sub_app_mut(RenderApp)
|
||||
.init_resource::<DrawFunctions<StencilOutline>>()
|
||||
.init_resource::<DrawFunctions<OpaqueOutline>>()
|
||||
.init_resource::<DrawFunctions<TransparentOutline>>()
|
||||
.init_resource::<OutlinePipeline>()
|
||||
.init_resource::<SpecializedMeshPipelines<OutlinePipeline>>()
|
||||
.add_render_command::<StencilOutline, DrawStencil>()
|
||||
.add_render_command::<OpaqueOutline, DrawOutline>()
|
||||
.add_render_command::<TransparentOutline, DrawOutline>()
|
||||
.add_system(extract_outline_view_uniforms.in_schedule(ExtractSchedule))
|
||||
.add_system(extract_outline_stencil_uniforms.in_schedule(ExtractSchedule))
|
||||
.add_system(extract_outline_volume_uniforms.in_schedule(ExtractSchedule))
|
||||
.add_system(sort_phase_system::<StencilOutline>.in_set(RenderSet::PhaseSort))
|
||||
.add_system(sort_phase_system::<OpaqueOutline>.in_set(RenderSet::PhaseSort))
|
||||
.add_system(sort_phase_system::<TransparentOutline>.in_set(RenderSet::PhaseSort))
|
||||
.add_system(queue_outline_view_bind_group.in_set(RenderSet::Queue))
|
||||
.add_system(queue_outline_stencil_bind_group.in_set(RenderSet::Queue))
|
||||
.add_system(queue_outline_volume_bind_group.in_set(RenderSet::Queue))
|
||||
.add_system(queue_outline_stencil_mesh.in_set(RenderSet::Queue))
|
||||
.add_system(queue_outline_volume_mesh.in_set(RenderSet::Queue));
|
||||
app.add_plugins((
|
||||
ExtractComponentPlugin::<OutlineStencil>::extract_visible(),
|
||||
ExtractComponentPlugin::<OutlineRenderLayers>::default(),
|
||||
UniformComponentPlugin::<OutlineStencilUniform>::default(),
|
||||
UniformComponentPlugin::<OutlineVolumeUniform>::default(),
|
||||
UniformComponentPlugin::<OutlineFragmentUniform>::default(),
|
||||
UniformComponentPlugin::<OutlineViewUniform>::default(),
|
||||
))
|
||||
.add_systems(
|
||||
PostUpdate,
|
||||
compute_outline_depth.after(TransformSystem::TransformPropagate),
|
||||
)
|
||||
.sub_app_mut(RenderApp)
|
||||
.init_resource::<DrawFunctions<StencilOutline>>()
|
||||
.init_resource::<DrawFunctions<OpaqueOutline>>()
|
||||
.init_resource::<DrawFunctions<TransparentOutline>>()
|
||||
.init_resource::<SpecializedMeshPipelines<OutlinePipeline>>()
|
||||
.add_render_command::<StencilOutline, DrawStencil>()
|
||||
.add_render_command::<OpaqueOutline, DrawOutline>()
|
||||
.add_render_command::<TransparentOutline, DrawOutline>()
|
||||
.add_systems(ExtractSchedule, extract_outline_view_uniforms)
|
||||
.add_systems(ExtractSchedule, extract_outline_stencil_uniforms)
|
||||
.add_systems(ExtractSchedule, extract_outline_volume_uniforms)
|
||||
.add_systems(
|
||||
Render,
|
||||
sort_phase_system::<StencilOutline>.in_set(RenderSet::PhaseSort),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
sort_phase_system::<OpaqueOutline>.in_set(RenderSet::PhaseSort),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
sort_phase_system::<TransparentOutline>.in_set(RenderSet::PhaseSort),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
queue_outline_view_bind_group.in_set(RenderSet::Queue),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
queue_outline_stencil_bind_group.in_set(RenderSet::Queue),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
queue_outline_volume_bind_group.in_set(RenderSet::Queue),
|
||||
)
|
||||
.add_systems(Render, queue_outline_stencil_mesh.in_set(RenderSet::Queue))
|
||||
.add_systems(Render, queue_outline_volume_mesh.in_set(RenderSet::Queue));
|
||||
|
||||
let world = &mut app.sub_app_mut(RenderApp).world;
|
||||
let node = OutlineNode::new(world);
|
||||
@ -248,16 +266,10 @@ impl Plugin for OutlinePlugin {
|
||||
.get_sub_graph_mut(bevy::core_pipeline::core_3d::graph::NAME)
|
||||
.unwrap();
|
||||
draw_3d_graph.add_node(OUTLINE_PASS_NODE_NAME, node);
|
||||
draw_3d_graph.add_slot_edge(
|
||||
draw_3d_graph.input_node().id,
|
||||
bevy::core_pipeline::core_3d::graph::input::VIEW_ENTITY,
|
||||
OUTLINE_PASS_NODE_NAME,
|
||||
OutlineNode::IN_VIEW,
|
||||
);
|
||||
|
||||
// Run after main 3D pass, but before UI psss
|
||||
draw_3d_graph.add_node_edge(
|
||||
bevy::core_pipeline::core_3d::graph::node::MAIN_PASS,
|
||||
bevy::core_pipeline::core_3d::graph::node::END_MAIN_PASS,
|
||||
OUTLINE_PASS_NODE_NAME,
|
||||
);
|
||||
#[cfg(feature = "bevy_ui")]
|
||||
@ -266,4 +278,9 @@ impl Plugin for OutlinePlugin {
|
||||
bevy::ui::draw_ui_graph::node::UI_PASS,
|
||||
);
|
||||
}
|
||||
|
||||
fn finish(&self, app: &mut App) {
|
||||
app.sub_app_mut(RenderApp)
|
||||
.init_resource::<OutlinePipeline>();
|
||||
}
|
||||
}
|
||||
|
10
src/node.rs
10
src/node.rs
@ -3,7 +3,7 @@ use std::cmp::Reverse;
|
||||
use bevy::ecs::system::lifetimeless::Read;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::camera::ExtractedCamera;
|
||||
use bevy::render::render_graph::{NodeRunError, SlotInfo, SlotType};
|
||||
use bevy::render::render_graph::NodeRunError;
|
||||
use bevy::render::render_phase::{
|
||||
CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem, RenderPhase,
|
||||
};
|
||||
@ -128,8 +128,6 @@ pub(crate) struct OutlineNode {
|
||||
}
|
||||
|
||||
impl OutlineNode {
|
||||
pub(crate) const IN_VIEW: &'static str = "view";
|
||||
|
||||
pub(crate) fn new(world: &mut World) -> Self {
|
||||
Self {
|
||||
query: world.query_filtered(),
|
||||
@ -138,10 +136,6 @@ impl OutlineNode {
|
||||
}
|
||||
|
||||
impl Node for OutlineNode {
|
||||
fn input(&self) -> Vec<SlotInfo> {
|
||||
vec![SlotInfo::new(Self::IN_VIEW, SlotType::Entity)]
|
||||
}
|
||||
|
||||
fn update(&mut self, world: &mut World) {
|
||||
self.query.update_archetypes(world);
|
||||
}
|
||||
@ -152,7 +146,7 @@ impl Node for OutlineNode {
|
||||
render_context: &mut RenderContext,
|
||||
world: &World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
||||
let view_entity = graph.view_entity();
|
||||
let (camera, stencil_phase, opaque_phase, transparent_phase, camera_3d, target, depth) =
|
||||
match self.query.get_manual(world, view_entity) {
|
||||
Ok(query) => query,
|
||||
|
@ -1,14 +1,47 @@
|
||||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::mesh_types
|
||||
#import bevy_render::view View
|
||||
#import bevy_pbr::mesh_types Mesh
|
||||
#import bevy_pbr::mesh_types SkinnedMesh
|
||||
|
||||
struct VertexInput {
|
||||
#ifdef MORPH_TARGETS
|
||||
fn morph_vertex(vertex_in: Vertex) -> Vertex {
|
||||
var vertex = vertex_in;
|
||||
let weight_count = bevy_pbr::morph::layer_count();
|
||||
for (var i: u32 = 0u; i < weight_count; i ++) {
|
||||
let weight = bevy_pbr::morph::weight_at(i);
|
||||
if weight == 0.0 {
|
||||
continue;
|
||||
}
|
||||
vertex.position += weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::position_offset, i);
|
||||
#ifdef VERTEX_NORMALS
|
||||
vertex.normal += weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::normal_offset, i);
|
||||
#endif
|
||||
#ifdef VERTEX_TANGENTS
|
||||
vertex.tangent += vec4(weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::tangent_offset, i), 0.0);
|
||||
#endif
|
||||
}
|
||||
return vertex;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct Vertex {
|
||||
#ifdef VERTEX_POSITIONS
|
||||
@location(0) position: vec3<f32>,
|
||||
#endif
|
||||
#ifndef OFFSET_ZERO
|
||||
@location(1) normal: vec3<f32>,
|
||||
@location(1) outline_normal: vec3<f32>,
|
||||
#endif
|
||||
#ifdef VERTEX_NORMALS
|
||||
@location(2) normal: vec3<f32>,
|
||||
#endif
|
||||
#ifdef VERTEX_TANGENTS
|
||||
@location(3) tangent: vec4<f32>,
|
||||
#endif
|
||||
#ifdef SKINNED
|
||||
@location(2) joint_indexes: vec4<u32>,
|
||||
@location(3) joint_weights: vec4<f32>,
|
||||
@location(5) joint_indices: vec4<u32>,
|
||||
@location(6) joint_weights: vec4<f32>,
|
||||
#endif
|
||||
#ifdef MORPH_TARGETS
|
||||
@builtin(vertex_index) index: u32,
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -30,14 +63,14 @@ struct OutlineVertexUniform {
|
||||
offset: f32,
|
||||
};
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> view: View;
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> mesh: Mesh;
|
||||
|
||||
#ifdef SKINNED
|
||||
@group(1) @binding(1)
|
||||
var<uniform> joint_matrices: SkinnedMesh;
|
||||
#import bevy_pbr::skinning
|
||||
#endif
|
||||
#import bevy_pbr::morph
|
||||
|
||||
@group(2) @binding(0)
|
||||
var<uniform> view_uniform: OutlineViewUniform;
|
||||
@ -60,9 +93,14 @@ fn model_origin_z(plane: vec3<f32>, view_proj: mat4x4<f32>) -> f32 {
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vertex(vertex: VertexInput) -> VertexOutput {
|
||||
fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
|
||||
#ifdef MORPH_TARGETS
|
||||
var vertex = morph_vertex(vertex_no_morph);
|
||||
#else
|
||||
var vertex = vertex_no_morph;
|
||||
#endif
|
||||
#ifdef SKINNED
|
||||
let model = skin_model(vertex.joint_indexes, vertex.joint_weights);
|
||||
let model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights);
|
||||
#else
|
||||
let model = mesh.model;
|
||||
#endif
|
||||
@ -75,7 +113,7 @@ fn vertex(vertex: VertexInput) -> VertexOutput {
|
||||
#ifdef OFFSET_ZERO
|
||||
let out_xy = clip_pos.xy;
|
||||
#else
|
||||
let clip_norm = mat4to3(view.view_proj) * (mat4to3(model) * vertex.normal);
|
||||
let clip_norm = mat4to3(view.view_proj) * (mat4to3(model) * vertex.outline_normal);
|
||||
let ndc_delta = vstage.offset * normalize(clip_norm.xy) * view_uniform.scale * clip_pos.w;
|
||||
let out_xy = clip_pos.xy + ndc_delta;
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use bevy::pbr::{MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS};
|
||||
use bevy::pbr::{setup_morph_and_skinning_defs, MeshPipelineKey};
|
||||
use bevy::prelude::*;
|
||||
use bevy::reflect::TypeUuid;
|
||||
use bevy::render::render_resource::{
|
||||
@ -57,6 +57,7 @@ impl PipelineKey {
|
||||
pub offset_zero, set_offset_zero: 13;
|
||||
pub hdr_format, set_hdr_format: 14;
|
||||
pub opengl_workaround, set_opengl_workaround: 15;
|
||||
pub morph_targets, set_morph_targets: 16;
|
||||
}
|
||||
|
||||
pub(crate) fn new() -> Self {
|
||||
@ -135,6 +136,21 @@ impl PipelineKey {
|
||||
self.set_opengl_workaround(opengl_workaround);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_morph_targets(mut self, morph_targets: bool) -> Self {
|
||||
self.set_morph_targets(morph_targets);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PipelineKey> for MeshPipelineKey {
|
||||
fn from(key: PipelineKey) -> Self {
|
||||
if key.morph_targets() {
|
||||
MeshPipelineKey::empty() | MeshPipelineKey::MORPH_TARGETS
|
||||
} else {
|
||||
MeshPipelineKey::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
@ -223,38 +239,43 @@ impl SpecializedMeshPipeline for OutlinePipeline {
|
||||
fn specialize(
|
||||
&self,
|
||||
key: Self::Key,
|
||||
mesh_layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
let mut targets = vec![];
|
||||
let mut vertex_defs = vec!["MESH_BINDGROUP_1".into()];
|
||||
let mut fragment_defs = vec![];
|
||||
let mut buffer_attrs = Vec::new();
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_POSITION) {
|
||||
vertex_defs.push("VERTEX_POSITIONS".into());
|
||||
buffer_attrs.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_NORMAL) {
|
||||
vertex_defs.push("VERTEX_NORMALS".into());
|
||||
buffer_attrs.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(2));
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_TANGENT) {
|
||||
vertex_defs.push("VERTEX_TANGENTS".into());
|
||||
buffer_attrs.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3));
|
||||
}
|
||||
|
||||
let mut bind_layouts = vec![if key.msaa() == Msaa::Off {
|
||||
self.mesh_pipeline.view_layout.clone()
|
||||
} else {
|
||||
self.mesh_pipeline.view_layout_multisampled.clone()
|
||||
}];
|
||||
let mut buffer_attrs = vec![Mesh::ATTRIBUTE_POSITION.at_shader_location(0)];
|
||||
let mut vertex_defs = vec![
|
||||
ShaderDefVal::Int(
|
||||
"MAX_CASCADES_PER_LIGHT".to_string(),
|
||||
MAX_CASCADES_PER_LIGHT as i32,
|
||||
),
|
||||
ShaderDefVal::Int(
|
||||
"MAX_DIRECTIONAL_LIGHTS".to_string(),
|
||||
MAX_DIRECTIONAL_LIGHTS as i32,
|
||||
),
|
||||
];
|
||||
let mut fragment_defs = vec![];
|
||||
bind_layouts.push(
|
||||
if mesh_layout.contains(Mesh::ATTRIBUTE_JOINT_INDEX)
|
||||
&& mesh_layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
|
||||
{
|
||||
vertex_defs.push(ShaderDefVal::from("SKINNED"));
|
||||
buffer_attrs.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(2));
|
||||
buffer_attrs.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(3));
|
||||
self.mesh_pipeline.skinned_mesh_layout.clone()
|
||||
} else {
|
||||
self.mesh_pipeline.mesh_layout.clone()
|
||||
},
|
||||
);
|
||||
|
||||
bind_layouts.push(setup_morph_and_skinning_defs(
|
||||
&self.mesh_pipeline.mesh_layouts,
|
||||
layout,
|
||||
5,
|
||||
&key.into(),
|
||||
&mut vertex_defs,
|
||||
&mut buffer_attrs,
|
||||
));
|
||||
|
||||
bind_layouts.push(self.outline_view_bind_group_layout.clone());
|
||||
let cull_mode;
|
||||
if key.depth_mode() == DepthMode::Flat {
|
||||
@ -269,7 +290,7 @@ impl SpecializedMeshPipeline for OutlinePipeline {
|
||||
vertex_defs.push(ShaderDefVal::from("OFFSET_ZERO"));
|
||||
} else {
|
||||
buffer_attrs.push(
|
||||
if mesh_layout.contains(ATTRIBUTE_OUTLINE_NORMAL) {
|
||||
if layout.contains(ATTRIBUTE_OUTLINE_NORMAL) {
|
||||
ATTRIBUTE_OUTLINE_NORMAL
|
||||
} else {
|
||||
Mesh::ATTRIBUTE_NORMAL
|
||||
@ -305,7 +326,7 @@ impl SpecializedMeshPipeline for OutlinePipeline {
|
||||
vertex_defs.push(val.clone());
|
||||
fragment_defs.push(val);
|
||||
}
|
||||
let buffers = vec![mesh_layout.get_layout(&buffer_attrs)?];
|
||||
let buffers = vec![layout.get_layout(&buffer_attrs)?];
|
||||
Ok(RenderPipelineDescriptor {
|
||||
vertex: VertexState {
|
||||
shader: OUTLINE_SHADER_HANDLE.typed::<Shader>(),
|
||||
|
Loading…
Reference in New Issue
Block a user