diff --git a/Cargo.toml b/Cargo.toml index 0e614a5..f58df5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,27 +11,31 @@ 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" +bevy_mod_gltf_patched = { git = "https://github.com/zainthemaynnn/bevy_mod_gltf_patched/", branch = "bevy-0.11" } [features] default = ["bevy_ui"] diff --git a/examples/animated_fox.rs b/examples/animated_fox.rs index d824a30..7696481 100644 --- a/examples/animated_fox.rs +++ b/examples/animated_fox.rs @@ -14,16 +14,13 @@ struct Fox(Handle); 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(); } diff --git a/examples/hollow.rs b/examples/hollow.rs index c12dcff..7624a29 100644 --- a/examples/hollow.rs +++ b/examples/hollow.rs @@ -9,20 +9,17 @@ fn main() { // Disable built-in glTF plugin .add_plugins(DefaultPlugins.build().disable::()) // Register outline normal vertex attribute with bevy_mod_gltf_patched - .add_plugin( + .add_plugins( 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(); } diff --git a/examples/pieces.rs b/examples/pieces.rs index 48e296f..7f4341f 100644 --- a/examples/pieces.rs +++ b/examples/pieces.rs @@ -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(); } diff --git a/examples/render_layers.rs b/examples/render_layers.rs index 2182372..3dae2ad 100644 --- a/examples/render_layers.rs +++ b/examples/render_layers.rs @@ -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(); } diff --git a/examples/shapes.rs b/examples/shapes.rs index b00f010..96ed028 100644 --- a/examples/shapes.rs +++ b/examples/shapes.rs @@ -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(); } diff --git a/src/draw.rs b/src/draw.rs index f409d72..0e4ef28 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -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(); diff --git a/src/generate.rs b/src/generate.rs index d9ef308..862ebf7 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -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); } } diff --git a/src/lib.rs b/src/lib.rs index fbc1e68..92f5875 100644 --- a/src/lib.rs +++ b/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::::extract_visible()) - .add_plugin(ExtractComponentPlugin::::default()) - .add_plugin(UniformComponentPlugin::::default()) - .add_plugin(UniformComponentPlugin::::default()) - .add_plugin(UniformComponentPlugin::::default()) - .add_plugin(UniformComponentPlugin::::default()) - .add_system( - compute_outline_depth - .in_base_set(CoreSet::PostUpdate) - .after(TransformSystem::TransformPropagate), - ) - .sub_app_mut(RenderApp) - .init_resource::>() - .init_resource::>() - .init_resource::>() - .init_resource::() - .init_resource::>() - .add_render_command::() - .add_render_command::() - .add_render_command::() - .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::.in_set(RenderSet::PhaseSort)) - .add_system(sort_phase_system::.in_set(RenderSet::PhaseSort)) - .add_system(sort_phase_system::.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::::extract_visible(), + ExtractComponentPlugin::::default(), + UniformComponentPlugin::::default(), + UniformComponentPlugin::::default(), + UniformComponentPlugin::::default(), + UniformComponentPlugin::::default(), + )) + .add_systems( + PostUpdate, + compute_outline_depth.after(TransformSystem::TransformPropagate), + ) + .sub_app_mut(RenderApp) + .init_resource::>() + .init_resource::>() + .init_resource::>() + .init_resource::>() + .add_render_command::() + .add_render_command::() + .add_render_command::() + .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::.in_set(RenderSet::PhaseSort), + ) + .add_systems( + Render, + sort_phase_system::.in_set(RenderSet::PhaseSort), + ) + .add_systems( + Render, + sort_phase_system::.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::(); + } } diff --git a/src/node.rs b/src/node.rs index 50349a6..b39624d 100644 --- a/src/node.rs +++ b/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 { - 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, diff --git a/src/outline.wgsl b/src/outline.wgsl index 61b5fe2..750b64b 100644 --- a/src/outline.wgsl +++ b/src/outline.wgsl @@ -1,5 +1,6 @@ -#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 { @location(0) position: vec3, @@ -7,8 +8,8 @@ struct VertexInput { @location(1) normal: vec3, #endif #ifdef SKINNED - @location(2) joint_indexes: vec4, - @location(3) joint_weights: vec4, + @location(5) joint_indices: vec4, + @location(6) joint_weights: vec4, #endif }; @@ -30,14 +31,14 @@ struct OutlineVertexUniform { offset: f32, }; +@group(0) @binding(0) +var view: View; + @group(1) @binding(0) var mesh: Mesh; -#ifdef SKINNED -@group(1) @binding(1) -var joint_matrices: SkinnedMesh; #import bevy_pbr::skinning -#endif +#import bevy_pbr::morph @group(2) @binding(0) var view_uniform: OutlineViewUniform; @@ -62,7 +63,7 @@ fn model_origin_z(plane: vec3, view_proj: mat4x4) -> f32 { @vertex fn vertex(vertex: VertexInput) -> VertexOutput { #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 diff --git a/src/pipeline.rs b/src/pipeline.rs index a64c573..df3036c 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -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 for MeshPipelineKey { + fn from(key: PipelineKey) -> Self { + if key.morph_targets() { + MeshPipelineKey::empty() | MeshPipelineKey::MORPH_TARGETS + } else { + MeshPipelineKey::empty() + } + } } #[derive(Resource)] @@ -231,30 +247,20 @@ impl SpecializedMeshPipeline for OutlinePipeline { } 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 vertex_defs = vec!["MESH_BINDGROUP_1".into()]; 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, + mesh_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 {