Initial port to Bevy 0.13.

This commit is contained in:
Robin KAY 2024-02-16 00:30:51 +00:00
parent bcc0cdd86a
commit fffb75b20c
12 changed files with 188 additions and 201 deletions

View File

@ -11,7 +11,7 @@ keywords = ["gamedev", "bevy", "outline"]
categories = ["game-engines", "rendering"] categories = ["game-engines", "rendering"]
[dependencies] [dependencies]
bevy = { version = "0.12", default-features = false, features = [ bevy = { git = "https://github.com/bevyengine/bevy.git", rev = "4ebc560dfb0fee5d3728d1d8f7362f484b784e4c", default-features = false, features = [
"bevy_asset", "bevy_asset",
"bevy_render", "bevy_render",
"bevy_pbr", "bevy_pbr",
@ -24,10 +24,10 @@ bitfield = "0.14"
interpolation = "0.2" interpolation = "0.2"
interpolation_03 = { package = "interpolation", version = "0.3", optional = true } interpolation_03 = { package = "interpolation", version = "0.3", optional = true }
thiserror = "1.0" thiserror = "1.0"
wgpu-types = "0.17" wgpu-types = "0.19"
[dev-dependencies] [dev-dependencies]
bevy = { version = "0.12", default-features = false, features = [ bevy = { git = "https://github.com/bevyengine/bevy.git", rev = "4ebc560dfb0fee5d3728d1d8f7362f484b784e4c", default-features = false, features = [
"animation", "animation",
"bevy_gltf", "bevy_gltf",
"bevy_pbr", "bevy_pbr",

View File

@ -51,7 +51,7 @@ fn setup(
size: 500000.0, size: 500000.0,
subdivisions: 0, subdivisions: 0,
})), })),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), material: materials.add(StandardMaterial::from(Color::rgb(0.3, 0.5, 0.3))),
..default() ..default()
}); });

View File

@ -36,18 +36,15 @@ fn setup(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
commands.insert_resource(MyAssets { commands.insert_resource(MyAssets {
mesh: meshes.add( mesh: meshes.add(Mesh::from(Capsule {
Capsule {
radius: 1.0, radius: 1.0,
rings: 10, rings: 10,
depth: 2.0, depth: 2.0,
latitudes: 15, latitudes: 15,
longitudes: 15, longitudes: 15,
..default() ..default()
} })),
.into(), material: materials.add(StandardMaterial::from(Color::BEIGE)),
),
material: materials.add(Color::BEIGE.into()),
}); });
// Add light source and camera // Add light source and camera

View File

@ -32,15 +32,12 @@ fn setup(
// Add sphere with child meshes sticking out of it // Add sphere with child meshes sticking out of it
commands commands
.spawn(PbrBundle { .spawn(PbrBundle {
mesh: meshes.add( mesh: meshes.add(Mesh::from(UVSphere {
UVSphere {
radius: 0.75, radius: 0.75,
sectors: 30, sectors: 30,
stacks: 30, stacks: 30,
} })),
.into(), material: materials.add(StandardMaterial::from(Color::rgb(0.9, 0.1, 0.1))),
),
material: materials.add(Color::rgb(0.9, 0.1, 0.1).into()),
transform: Transform::from_translation(Vec3::new(0.0, 1.0, 0.0)), transform: Transform::from_translation(Vec3::new(0.0, 1.0, 0.0)),
..default() ..default()
}) })
@ -60,18 +57,15 @@ fn setup(
.with_children(|parent| { .with_children(|parent| {
parent parent
.spawn(PbrBundle { .spawn(PbrBundle {
mesh: meshes.add( mesh: meshes.add(Mesh::from(Capsule {
Capsule {
radius: 0.2, radius: 0.2,
rings: 15, rings: 15,
depth: 1.0, depth: 1.0,
latitudes: 15, latitudes: 15,
longitudes: 15, longitudes: 15,
..Default::default() ..Default::default()
} })),
.into(), material: materials.add(StandardMaterial::from(Color::rgb(0.1, 0.1, 0.9))),
),
material: materials.add(Color::rgb(0.1, 0.1, 0.9).into()),
transform: Transform::from_rotation(Quat::from_axis_angle(Vec3::X, TAU / 4.0)) transform: Transform::from_rotation(Quat::from_axis_angle(Vec3::X, TAU / 4.0))
.with_translation(Vec3::new(0.0, 0.0, 0.75)), .with_translation(Vec3::new(0.0, 0.0, 0.75)),
..default() ..default()
@ -79,16 +73,13 @@ fn setup(
.insert(InheritOutlineBundle::default()); .insert(InheritOutlineBundle::default());
parent parent
.spawn(PbrBundle { .spawn(PbrBundle {
mesh: meshes.add( mesh: meshes.add(Mesh::from(Torus {
Torus {
radius: 0.5, radius: 0.5,
ring_radius: 0.1, ring_radius: 0.1,
subdivisions_segments: 30, subdivisions_segments: 30,
subdivisions_sides: 15, subdivisions_sides: 15,
} })),
.into(), material: materials.add(StandardMaterial::from(Color::rgb(0.1, 0.1, 0.9))),
),
material: materials.add(Color::rgb(0.1, 0.1, 0.9).into()),
transform: Transform::from_rotation(Quat::from_axis_angle(Vec3::Z, TAU / 4.0)) transform: Transform::from_rotation(Quat::from_axis_angle(Vec3::Z, TAU / 4.0))
.with_translation(Vec3::new(0.0, 0.0, -0.75)), .with_translation(Vec3::new(0.0, 0.0, -0.75)),
..default() ..default()
@ -102,7 +93,7 @@ fn setup(
size: 5.0, size: 5.0,
subdivisions: 0, subdivisions: 0,
})), })),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), material: materials.add(StandardMaterial::from(Color::rgb(0.3, 0.5, 0.3))),
..default() ..default()
}); });
commands.spawn(PointLightBundle { commands.spawn(PointLightBundle {

View File

@ -1,7 +1,6 @@
use std::f32::consts::PI; use std::f32::consts::PI;
use bevy::{ use bevy::{
core_pipeline::clear_color::ClearColorConfig,
prelude::{ prelude::{
shape::{Plane, Torus}, shape::{Plane, Torus},
*, *,
@ -45,7 +44,7 @@ fn setup(
subdivisions_segments: 40, subdivisions_segments: 40,
subdivisions_sides: 20, subdivisions_sides: 20,
})), })),
material: materials.add(Color::rgb(0.1, 0.1, 0.9).into()), material: materials.add(StandardMaterial::from(Color::rgb(0.1, 0.1, 0.9))),
transform: Transform::from_rotation(Quat::from_rotation_x(0.5 * PI)) transform: Transform::from_rotation(Quat::from_rotation_x(0.5 * PI))
.with_translation(0.8 * Vec3::Y), .with_translation(0.8 * Vec3::Y),
..default() ..default()
@ -67,7 +66,7 @@ fn setup(
size: 5.0, size: 5.0,
subdivisions: 0, subdivisions: 0,
})), })),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), material: materials.add(StandardMaterial::from(Color::rgb(0.3, 0.5, 0.3))),
..default() ..default()
}); });
commands.spawn(PointLightBundle { commands.spawn(PointLightBundle {
@ -95,9 +94,6 @@ fn setup(
.spawn(Camera3dBundle { .spawn(Camera3dBundle {
camera: Camera { camera: Camera {
order: i, order: i,
..default()
},
camera_3d: Camera3d {
clear_color: if i > 0 { clear_color: if i > 0 {
ClearColorConfig::None ClearColorConfig::None
} else { } else {
@ -105,6 +101,7 @@ fn setup(
}, },
..default() ..default()
}, },
camera_3d: Camera3d { ..default() },
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
..default() ..default()
}) })
@ -117,11 +114,11 @@ fn setup(
} }
fn set_camera_viewports( fn set_camera_viewports(
win_query: Query<(&Window, Changed<Window>), With<PrimaryWindow>>, win_query: Query<Ref<Window>, With<PrimaryWindow>>,
mut query: Query<(&mut Camera, &CameraMode)>, mut query: Query<(&mut Camera, &CameraMode)>,
) { ) {
let (win, win_changed) = win_query.get_single().unwrap(); let win = win_query.get_single().unwrap();
if win_changed { if win.is_changed() {
// Divide window into quadrants // Divide window into quadrants
let size = UVec2::new(win.physical_width() / 2, win.physical_height() / 2); let size = UVec2::new(win.physical_width() / 2, win.physical_height() / 2);
for (mut camera, mode) in query.iter_mut() { for (mut camera, mode) in query.iter_mut() {

View File

@ -38,7 +38,7 @@ fn setup(
commands commands
.spawn(PbrBundle { .spawn(PbrBundle {
mesh: meshes.add(cube_mesh), mesh: meshes.add(cube_mesh),
material: materials.add(Color::rgb(0.1, 0.1, 0.9).into()), material: materials.add(StandardMaterial::from(Color::rgb(0.1, 0.1, 0.9))),
transform: Transform::from_xyz(0.0, 1.0, 0.0), transform: Transform::from_xyz(0.0, 1.0, 0.0),
..default() ..default()
}) })
@ -61,7 +61,7 @@ fn setup(
subdivisions_segments: 20, subdivisions_segments: 20,
subdivisions_sides: 10, subdivisions_sides: 10,
})), })),
material: materials.add(Color::rgb(0.9, 0.1, 0.1).into()), material: materials.add(StandardMaterial::from(Color::rgb(0.9, 0.1, 0.1))),
transform: Transform::from_xyz(0.0, 1.2, 2.0) transform: Transform::from_xyz(0.0, 1.2, 2.0)
.with_rotation(Quat::from_rotation_x(0.5 * PI)), .with_rotation(Quat::from_rotation_x(0.5 * PI)),
..default() ..default()
@ -82,7 +82,7 @@ fn setup(
size: 5.0, size: 5.0,
subdivisions: 0, subdivisions: 0,
})), })),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), material: materials.add(StandardMaterial::from(Color::rgb(0.3, 0.5, 0.3))),
..default() ..default()
}); });
commands.spawn(PointLightBundle { commands.spawn(PointLightBundle {

View File

@ -58,9 +58,13 @@ impl<T: Clone + Default> Sourced<T> {
} }
} }
pub fn is_changed<U: Component>(&self, tuple: Option<(&U, bool)>) -> bool { pub fn is_changed<U: Component>(&self, tuple: &Option<Ref<U>>) -> bool {
tuple.is_some() != matches!(self.source, Source::Set) tuple.is_some() != matches!(self.source, Source::Set)
|| if let Some((_, c)) = tuple { c } else { false } || if let Some(r) = tuple {
r.is_changed()
} else {
false
}
} }
} }
@ -77,11 +81,11 @@ pub(crate) struct ComputedInternal {
pub struct ComputedOutline(pub(crate) Option<ComputedInternal>); pub struct ComputedOutline(pub(crate) Option<ComputedInternal>);
type OutlineComponents<'a> = ( type OutlineComponents<'a> = (
(&'a InheritedVisibility, Changed<InheritedVisibility>), Ref<'a, InheritedVisibility>,
(&'a GlobalTransform, Changed<GlobalTransform>), Ref<'a, GlobalTransform>,
Option<(&'a OutlineVolume, Changed<OutlineVolume>)>, Option<Ref<'a, OutlineVolume>>,
Option<(&'a OutlineStencil, Changed<OutlineStencil>)>, Option<Ref<'a, OutlineStencil>>,
Option<(&'a OutlineMode, Changed<OutlineMode>)>, Option<Ref<'a, OutlineMode>>,
); );
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
@ -150,7 +154,7 @@ fn propagate_computed_outline(
fn update_computed_outline( fn update_computed_outline(
computed: &mut ComputedOutline, computed: &mut ComputedOutline,
((visibility, changed_visibility), (transform, changed_transform), volume, stencil, mode): QueryItem<'_, OutlineComponents>, (visibility, transform, volume, stencil, mode): QueryItem<'_, OutlineComponents>,
parent_computed: &ComputedInternal, parent_computed: &ComputedInternal,
parent_entity: Option<Entity>, parent_entity: Option<Entity>,
force_update: bool, force_update: bool,
@ -158,27 +162,31 @@ fn update_computed_outline(
let changed = force_update let changed = force_update
|| if let ComputedOutline(Some(computed)) = computed { || if let ComputedOutline(Some(computed)) = computed {
computed.inherited_from != parent_entity computed.inherited_from != parent_entity
|| changed_visibility || visibility.is_changed()
|| (changed_transform && matches!(mode, Some((OutlineMode::FlatVertex { .. }, _)))) || (transform.is_changed()
|| computed.volume.is_changed(volume) && mode
|| computed.stencil.is_changed(stencil) .as_ref()
|| computed.mode.is_changed(mode) .map(|r| matches!(r.as_ref(), OutlineMode::FlatVertex { .. }))
.unwrap_or(false))
|| computed.volume.is_changed(&volume)
|| computed.stencil.is_changed(&stencil)
|| computed.mode.is_changed(&mode)
} else { } else {
true true
}; };
if changed { if changed {
*computed = ComputedOutline(Some(ComputedInternal { *computed = ComputedOutline(Some(ComputedInternal {
inherited_from: parent_entity, inherited_from: parent_entity,
volume: if let Some((vol, _)) = volume { volume: if let Some(vol) = volume {
Sourced::set(ComputedVolume { Sourced::set(ComputedVolume {
enabled: visibility.get() && vol.visible && vol.colour.a() != 0.0, enabled: visibility.get() && vol.visible && vol.colour.a() != 0.0,
offset: vol.width, offset: vol.width,
colour: vol.colour.into(), colour: vol.colour.as_linear_rgba_f32().into(),
}) })
} else { } else {
Sourced::inherit(&parent_computed.volume.value) Sourced::inherit(&parent_computed.volume.value)
}, },
stencil: if let Some((sten, _)) = stencil { stencil: if let Some(sten) = stencil {
Sourced::set(ComputedStencil { Sourced::set(ComputedStencil {
enabled: visibility.get() && sten.enabled, enabled: visibility.get() && sten.enabled,
offset: sten.offset, offset: sten.offset,
@ -186,8 +194,8 @@ fn update_computed_outline(
} else { } else {
Sourced::inherit(&parent_computed.stencil.value) Sourced::inherit(&parent_computed.stencil.value)
}, },
mode: if let Some((m, _)) = mode { mode: if let Some(m) = mode {
Sourced::set(match m { Sourced::set(match m.as_ref() {
OutlineMode::FlatVertex { OutlineMode::FlatVertex {
model_origin: origin, model_origin: origin,
} => ComputedMode { } => ComputedMode {

View File

@ -23,18 +23,20 @@
//! [`AutoGenerateOutlineNormalsPlugin`]. //! [`AutoGenerateOutlineNormalsPlugin`].
use bevy::asset::load_internal_asset; use bevy::asset::load_internal_asset;
use bevy::core_pipeline::core_3d::graph::{Labels3d, SubGraph3d};
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::batching::{batch_and_prepare_render_phase, write_batched_instance_buffer}; use bevy::render::batching::{batch_and_prepare_render_phase, write_batched_instance_buffer};
use bevy::render::extract_component::{ use bevy::render::extract_component::{
ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
}; };
use bevy::render::mesh::MeshVertexAttribute; use bevy::render::mesh::MeshVertexAttribute;
use bevy::render::render_graph::RenderGraph; use bevy::render::render_graph::{RenderGraph, RenderLabel};
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, VertexFormat}; use bevy::render::render_resource::{SpecializedMeshPipelines, VertexFormat};
use bevy::render::view::{RenderLayers, VisibilitySystems}; use bevy::render::view::{RenderLayers, VisibilitySystems};
use bevy::render::{Render, RenderApp, RenderSet}; use bevy::render::{Render, RenderApp, RenderSet};
use bevy::transform::TransformSystem; use bevy::transform::TransformSystem;
use bevy::ui::graph::LabelsUi;
use interpolation::Lerp; use interpolation::Lerp;
use crate::draw::{ use crate::draw::{
@ -72,7 +74,10 @@ pub const ATTRIBUTE_OUTLINE_NORMAL: MeshVertexAttribute =
/// ///
/// This node runs after the main 3D passes and before the UI pass. The name can be used to /// This node runs after the main 3D passes and before the UI pass. The name can be used to
/// add additional constraints on node execution order with respect to other passes. /// add additional constraints on node execution order with respect to other passes.
pub const OUTLINE_PASS_NODE_NAME: &str = "bevy_mod_outline_node"; #[derive(Copy, Clone, Debug, RenderLabel, Hash, PartialEq, Eq)]
pub enum LabelsOutline {
OutlinePass,
}
/// A component for stenciling meshes during outline rendering. /// A component for stenciling meshes during outline rendering.
#[derive(Clone, Component)] #[derive(Clone, Component)]
@ -108,7 +113,7 @@ impl Lerp for OutlineStencil {
fn lerp(&self, other: &Self, scalar: &Self::Scalar) -> Self { fn lerp(&self, other: &Self, scalar: &Self::Scalar) -> Self {
OutlineStencil { OutlineStencil {
enabled: lerp_bool(self.enabled, other.enabled, *scalar), enabled: lerp_bool(self.enabled, other.enabled, *scalar),
offset: self.offset.lerp(&other.offset, scalar), offset: self.offset.lerp(other.offset, *scalar),
} }
} }
} }
@ -139,7 +144,7 @@ impl Lerp for OutlineVolume {
fn lerp(&self, other: &Self, scalar: &Self::Scalar) -> Self { fn lerp(&self, other: &Self, scalar: &Self::Scalar) -> Self {
OutlineVolume { OutlineVolume {
visible: lerp_bool(self.visible, other.visible, *scalar), visible: lerp_bool(self.visible, other.visible, *scalar),
width: self.width.lerp(&other.width, scalar), width: self.width.lerp(other.width, *scalar),
colour: { colour: {
let [r, g, b, a] = self let [r, g, b, a] = self
.colour .colour
@ -165,11 +170,11 @@ impl interpolation_03::Lerp for OutlineVolume {
pub struct OutlineRenderLayers(pub RenderLayers); pub struct OutlineRenderLayers(pub RenderLayers);
impl ExtractComponent for OutlineRenderLayers { impl ExtractComponent for OutlineRenderLayers {
type Query = ( type QueryData = (
Option<&'static OutlineRenderLayers>, Option<&'static OutlineRenderLayers>,
Option<&'static RenderLayers>, Option<&'static RenderLayers>,
); );
type Filter = With<ComputedOutline>; type QueryFilter = With<ComputedOutline>;
type Out = Self; type Out = Self;
fn extract_component( fn extract_component(
@ -319,21 +324,13 @@ impl Plugin for OutlinePlugin {
let mut graph = world.resource_mut::<RenderGraph>(); let mut graph = world.resource_mut::<RenderGraph>();
let draw_3d_graph = graph let draw_3d_graph = graph.get_sub_graph_mut(SubGraph3d).unwrap();
.get_sub_graph_mut(bevy::core_pipeline::core_3d::graph::NAME) draw_3d_graph.add_node(LabelsOutline::OutlinePass, node);
.unwrap();
draw_3d_graph.add_node(OUTLINE_PASS_NODE_NAME, node);
// Run after main 3D pass, but before UI psss // Run after main 3D pass, but before UI psss
draw_3d_graph.add_node_edge( draw_3d_graph.add_node_edge(Labels3d::EndMainPass, LabelsOutline::OutlinePass);
bevy::core_pipeline::core_3d::graph::node::END_MAIN_PASS,
OUTLINE_PASS_NODE_NAME,
);
#[cfg(feature = "bevy_ui")] #[cfg(feature = "bevy_ui")]
draw_3d_graph.add_node_edge( draw_3d_graph.add_node_edge(LabelsOutline::OutlinePass, LabelsUi::UiPass);
OUTLINE_PASS_NODE_NAME,
bevy::ui::draw_ui_graph::node::UI_PASS,
);
} }
fn finish(&self, app: &mut App) { fn finish(&self, app: &mut App) {

View File

@ -10,7 +10,7 @@ use bevy::render::render_phase::{
}; };
use bevy::render::render_resource::{ use bevy::render::render_resource::{
CachedRenderPipelineId, LoadOp, Operations, RenderPassDepthStencilAttachment, CachedRenderPipelineId, LoadOp, Operations, RenderPassDepthStencilAttachment,
RenderPassDescriptor, RenderPassDescriptor, StoreOp,
}; };
use bevy::render::view::{ExtractedView, ViewDepthTexture, ViewTarget}; use bevy::render::view::{ExtractedView, ViewDepthTexture, ViewTarget};
use bevy::render::{ use bevy::render::{
@ -217,13 +217,15 @@ impl Node for OutlineNode {
label: Some("outline_stencil_pass"), label: Some("outline_stencil_pass"),
color_attachments: &[], color_attachments: &[],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment { depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &depth.view, view: &depth.view(),
depth_ops: Some(Operations { depth_ops: Some(Operations {
load: camera_3d.depth_load_op.clone().into(), load: camera_3d.depth_load_op.clone().into(),
store: true, store: StoreOp::Store,
}), }),
stencil_ops: None, stencil_ops: None,
}), }),
timestamp_writes: None,
occlusion_query_set: None,
}; };
let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor); let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor);
if let Some(viewport) = camera.viewport.as_ref() { if let Some(viewport) = camera.viewport.as_ref() {
@ -235,18 +237,17 @@ impl Node for OutlineNode {
if !opaque_phase.items.is_empty() { if !opaque_phase.items.is_empty() {
let pass_descriptor = RenderPassDescriptor { let pass_descriptor = RenderPassDescriptor {
label: Some("outline_opaque_pass"), label: Some("outline_opaque_pass"),
color_attachments: &[Some(target.get_color_attachment(Operations { color_attachments: &[Some(target.get_color_attachment())],
load: LoadOp::Load,
store: true,
}))],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment { depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &depth.view, view: &depth.view(),
depth_ops: Some(Operations { depth_ops: Some(Operations {
load: LoadOp::Load, load: LoadOp::Load,
store: true, store: StoreOp::Store,
}), }),
stencil_ops: None, stencil_ops: None,
}), }),
timestamp_writes: None,
occlusion_query_set: None,
}; };
let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor); let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor);
if let Some(viewport) = camera.viewport.as_ref() { if let Some(viewport) = camera.viewport.as_ref() {
@ -258,18 +259,17 @@ impl Node for OutlineNode {
if !transparent_phase.items.is_empty() { if !transparent_phase.items.is_empty() {
let pass_descriptor = RenderPassDescriptor { let pass_descriptor = RenderPassDescriptor {
label: Some("outline_transparent_pass"), label: Some("outline_transparent_pass"),
color_attachments: &[Some(target.get_color_attachment(Operations { color_attachments: &[Some(target.get_color_attachment())],
load: LoadOp::Load,
store: true,
}))],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment { depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &depth.view, view: &depth.view(),
depth_ops: Some(Operations { depth_ops: Some(Operations {
load: LoadOp::Load, load: LoadOp::Load,
store: true, store: StoreOp::Store,
}), }),
stencil_ops: None, stencil_ops: None,
}), }),
timestamp_writes: None,
occlusion_query_set: None,
}; };
let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor); let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor);
if let Some(viewport) = camera.viewport.as_ref() { if let Some(viewport) = camera.viewport.as_ref() {

View File

@ -1,7 +1,6 @@
use std::borrow::Cow; use std::borrow::Cow;
use bevy::ecs::query::QueryItem; use bevy::ecs::system::lifetimeless::SQuery;
use bevy::ecs::system::lifetimeless::Read;
use bevy::ecs::system::SystemParamItem; use bevy::ecs::system::SystemParamItem;
use bevy::pbr::{ use bevy::pbr::{
setup_morph_and_skinning_defs, MeshFlags, MeshPipelineKey, MeshTransforms, MeshUniform, setup_morph_and_skinning_defs, MeshFlags, MeshPipelineKey, MeshTransforms, MeshUniform,
@ -9,11 +8,10 @@ use bevy::pbr::{
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::batching::GetBatchData; use bevy::render::batching::GetBatchData;
use bevy::render::render_resource::{ use bevy::render::render_resource::{
BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState, BindGroupLayout, BindGroupLayoutEntry, BindingType, BlendState, BufferBindingType, BufferSize,
BufferBindingType, BufferSize, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState, Face,
DepthStencilState, Face, FragmentState, FrontFace, MultisampleState, PolygonMode, FragmentState, FrontFace, MultisampleState, PolygonMode, PrimitiveState, PrimitiveTopology,
PrimitiveState, PrimitiveTopology, ShaderDefVal, ShaderSize, ShaderStages, ShaderType, ShaderDefVal, ShaderSize, ShaderStages, ShaderType, StencilState, TextureFormat, VertexState,
StencilState, TextureFormat, VertexState,
}; };
use bevy::render::renderer::RenderDevice; use bevy::render::renderer::RenderDevice;
use bevy::render::settings::WgpuSettings; use bevy::render::settings::WgpuSettings;
@ -168,10 +166,9 @@ impl FromWorld for OutlinePipeline {
let world = world.cell(); let world = world.cell();
let mesh_pipeline = world.get_resource::<MeshPipeline>().unwrap().clone(); let mesh_pipeline = world.get_resource::<MeshPipeline>().unwrap().clone();
let render_device = world.get_resource::<RenderDevice>().unwrap(); let render_device = world.get_resource::<RenderDevice>().unwrap();
let outline_view_bind_group_layout = let outline_view_bind_group_layout = render_device.create_bind_group_layout(
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { "outline_view_bind_group_layout",
label: Some("outline_view_bind_group_layout"), &[
entries: &[
BindGroupLayoutEntry { BindGroupLayoutEntry {
binding: 0, binding: 0,
visibility: ShaderStages::VERTEX, visibility: ShaderStages::VERTEX,
@ -193,20 +190,17 @@ impl FromWorld for OutlinePipeline {
count: None, count: None,
}, },
], ],
}); );
let outline_volume_bind_group_layout = let outline_volume_bind_group_layout = render_device.create_bind_group_layout(
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { "outline_volume_bind_group_layout",
label: Some("outline_volume_bind_group_layout"), &[
entries: &[
BindGroupLayoutEntry { BindGroupLayoutEntry {
binding: 0, binding: 0,
visibility: ShaderStages::VERTEX, visibility: ShaderStages::VERTEX,
ty: BindingType::Buffer { ty: BindingType::Buffer {
ty: BufferBindingType::Uniform, ty: BufferBindingType::Uniform,
has_dynamic_offset: true, has_dynamic_offset: true,
min_binding_size: BufferSize::new( min_binding_size: BufferSize::new(OutlineVolumeUniform::SHADER_SIZE.get()),
OutlineVolumeUniform::SHADER_SIZE.get(),
),
}, },
count: None, count: None,
}, },
@ -223,11 +217,10 @@ impl FromWorld for OutlinePipeline {
count: None, count: None,
}, },
], ],
}); );
let outline_stencil_bind_group_layout = let outline_stencil_bind_group_layout = render_device.create_bind_group_layout(
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { "outline_stencil_bind_group_layout",
label: Some("outline_stencil_bind_group_layout"), &[BindGroupLayoutEntry {
entries: &[BindGroupLayoutEntry {
binding: 0, binding: 0,
visibility: ShaderStages::VERTEX, visibility: ShaderStages::VERTEX,
ty: BindingType::Buffer { ty: BindingType::Buffer {
@ -237,7 +230,7 @@ impl FromWorld for OutlinePipeline {
}, },
count: None, count: None,
}], }],
}); );
OutlinePipeline { OutlinePipeline {
mesh_pipeline, mesh_pipeline,
outline_view_bind_group_layout, outline_view_bind_group_layout,
@ -373,21 +366,20 @@ impl SpecializedMeshPipeline for OutlinePipeline {
} }
impl GetBatchData for OutlinePipeline { impl GetBatchData for OutlinePipeline {
type Param = (); type Param = SQuery<&'static ExtractedOutline>;
type Query = Read<ExtractedOutline>;
type QueryFilter = ();
type CompareData = (); type CompareData = ();
type BufferData = MeshUniform; type BufferData = MeshUniform;
fn get_batch_data( fn get_batch_data(
_: &SystemParamItem<Self::Param>, param: &SystemParamItem<Self::Param>,
outline: &QueryItem<Self::Query>, entity: Entity,
) -> (Self::BufferData, Option<Self::CompareData>) { ) -> Option<(Self::BufferData, Option<Self::CompareData>)> {
let outline = param.get(entity).unwrap();
let ts = MeshTransforms { let ts = MeshTransforms {
transform: (&outline.transform).into(), transform: (&outline.transform).into(),
previous_transform: (&outline.transform).into(), previous_transform: (&outline.transform).into(),
flags: MeshFlags::NONE.bits(), flags: MeshFlags::NONE.bits(),
}; };
((&ts).into(), None) Some((MeshUniform::new(&ts, None), None))
} }
} }

View File

@ -149,20 +149,23 @@ pub(crate) fn prepare_outline_volume_bind_group(
pub(crate) struct SetOutlineStencilBindGroup<const I: usize>(); pub(crate) struct SetOutlineStencilBindGroup<const I: usize>();
impl<const I: usize> RenderCommand<StencilOutline> for SetOutlineStencilBindGroup<I> { impl<const I: usize> RenderCommand<StencilOutline> for SetOutlineStencilBindGroup<I> {
type ViewWorldQuery = (); type ViewQuery = ();
type ItemWorldQuery = Read<DynamicUniformIndex<OutlineStencilUniform>>; type ItemQuery = Read<DynamicUniformIndex<OutlineStencilUniform>>;
type Param = SRes<OutlineStencilBindGroup>; type Param = SRes<OutlineStencilBindGroup>;
fn render<'w>( fn render<'w>(
_item: &StencilOutline, _item: &StencilOutline,
_view_data: (), _view_data: (),
entity_data: &DynamicUniformIndex<OutlineStencilUniform>, entity_data: Option<&DynamicUniformIndex<OutlineStencilUniform>>,
bind_group: SystemParamItem<'w, '_, Self::Param>, bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
let Some(dyn_uniform) = entity_data else {
return RenderCommandResult::Failure;
};
pass.set_bind_group( pass.set_bind_group(
I, I,
&bind_group.into_inner().bind_group, &bind_group.into_inner().bind_group,
&[entity_data.index()], &[dyn_uniform.index()],
); );
RenderCommandResult::Success RenderCommandResult::Success
} }
@ -171,8 +174,8 @@ impl<const I: usize> RenderCommand<StencilOutline> for SetOutlineStencilBindGrou
pub(crate) struct SetOutlineVolumeBindGroup<const I: usize>(); pub(crate) struct SetOutlineVolumeBindGroup<const I: usize>();
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetOutlineVolumeBindGroup<I> { impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetOutlineVolumeBindGroup<I> {
type ViewWorldQuery = (); type ViewQuery = ();
type ItemWorldQuery = ( type ItemQuery = (
Read<DynamicUniformIndex<OutlineVolumeUniform>>, Read<DynamicUniformIndex<OutlineVolumeUniform>>,
Read<DynamicUniformIndex<OutlineFragmentUniform>>, Read<DynamicUniformIndex<OutlineFragmentUniform>>,
); );
@ -180,14 +183,16 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetOutlineVolumeBindGrou
fn render<'w>( fn render<'w>(
_item: &P, _item: &P,
_view_data: (), _view_data: (),
entity_data: ( entity_data: Option<(
&DynamicUniformIndex<OutlineVolumeUniform>, &DynamicUniformIndex<OutlineVolumeUniform>,
&DynamicUniformIndex<OutlineFragmentUniform>, &DynamicUniformIndex<OutlineFragmentUniform>,
), )>,
bind_group: SystemParamItem<'w, '_, Self::Param>, bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
let (vertex, fragment) = entity_data; let Some((vertex, fragment)) = entity_data else {
return RenderCommandResult::Failure;
};
pass.set_bind_group( pass.set_bind_group(
I, I,
&bind_group.into_inner().bind_group, &bind_group.into_inner().bind_group,

View File

@ -82,16 +82,16 @@ pub(crate) fn prepare_outline_view_bind_group(
pub(crate) struct SetOutlineViewBindGroup<const I: usize>(); pub(crate) struct SetOutlineViewBindGroup<const I: usize>();
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetOutlineViewBindGroup<I> { impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetOutlineViewBindGroup<I> {
type ViewWorldQuery = ( type ViewQuery = (
Read<ViewUniformOffset>, Read<ViewUniformOffset>,
Read<DynamicUniformIndex<OutlineViewUniform>>, Read<DynamicUniformIndex<OutlineViewUniform>>,
); );
type ItemWorldQuery = (); type ItemQuery = ();
type Param = SRes<OutlineViewBindGroup>; type Param = SRes<OutlineViewBindGroup>;
fn render<'w>( fn render<'w>(
_item: &P, _item: &P,
(core_view_data, outline_view_data): ROQueryItem<'w, Self::ViewWorldQuery>, (core_view_data, outline_view_data): ROQueryItem<'w, Self::ViewQuery>,
_entity_data: (), _entity_data: Option<()>,
bind_group: SystemParamItem<'w, '_, Self::Param>, bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {