Port to Bevy 0.12 (#25)

This commit is contained in:
Robin KAY 2023-11-08 22:29:32 +00:00 committed by GitHub
parent 6dcf604b93
commit ddb40d0c2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 194 additions and 72 deletions

View File

@ -11,7 +11,7 @@ keywords = ["gamedev", "bevy", "outline"]
categories = ["game-engines", "rendering"]
[dependencies]
bevy = { version = "0.11", default-features = false, features = [
bevy = { version = "0.12", default-features = false, features = [
"bevy_asset",
"bevy_render",
"bevy_pbr",
@ -23,10 +23,10 @@ bevy = { version = "0.11", default-features = false, features = [
bitfield = "0.14"
interpolation = "0.2"
thiserror = "1.0"
wgpu-types = "0.16.1"
wgpu-types = "0.17"
[dev-dependencies]
bevy = { version = "0.11", default-features = false, features = [
bevy = { version = "0.12", default-features = false, features = [
"animation",
"bevy_gltf",
"bevy_pbr",

View File

@ -59,7 +59,7 @@ pub(crate) fn queue_outline_stencil_mesh(
if !view_mask.intersects(outline_mask) {
continue; // Layer not enabled
}
let Some(mesh) = render_meshes.get(&outline.mesh) else {
let Some(mesh) = render_meshes.get(outline.mesh_id) else {
continue; // No mesh
};
let key = base_key
@ -78,6 +78,8 @@ pub(crate) fn queue_outline_stencil_mesh(
pipeline,
draw_function: draw_stencil,
distance,
batch_range: 0..0,
dynamic_offset: None,
});
}
}
@ -135,7 +137,7 @@ pub(crate) fn queue_outline_volume_mesh(
if !view_mask.intersects(outline_mask) {
continue; // Layer not enabled
}
let Some(mesh) = render_meshes.get(&outline.mesh) else {
let Some(mesh) = render_meshes.get(outline.mesh_id) else {
continue; // No mesh
};
let transparent = fragment_uniform.colour[3] < 1.0;
@ -162,6 +164,8 @@ pub(crate) fn queue_outline_volume_mesh(
pipeline,
draw_function: draw_transparent_outline,
distance,
batch_range: 0..0,
dynamic_offset: None,
});
} else {
opaque_phase.add(OpaqueOutline {
@ -169,6 +173,8 @@ pub(crate) fn queue_outline_volume_mesh(
pipeline,
draw_function: draw_opaque_outline,
distance,
batch_range: 0..0,
dynamic_offset: None,
});
}
}

View File

@ -138,22 +138,23 @@ impl OutlineMeshExt for Mesh {
fn auto_generate_outline_normals(
mut meshes: ResMut<Assets<Mesh>>,
mut events: EventReader<'_, '_, AssetEvent<Mesh>>,
mut squelch: Local<HashSet<Handle<Mesh>>>,
mut squelch: Local<HashSet<AssetId<Mesh>>>,
) {
for event in events.iter() {
for event in events.read() {
match event {
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
if squelch.contains(handle) {
AssetEvent::Added { id } | AssetEvent::Modified { id } => {
if squelch.contains(id) {
// Suppress modification events created by this system
squelch.remove(handle);
} else if let Some(mesh) = meshes.get_mut(handle) {
squelch.remove(id);
} else if let Some(mesh) = meshes.get_mut(*id) {
let _ = mesh.generate_outline_normals();
squelch.insert(handle.clone_weak());
squelch.insert(*id);
}
}
AssetEvent::Removed { handle } => {
squelch.remove(handle);
AssetEvent::Removed { id } => {
squelch.remove(id);
}
_ => {}
}
}
}

View File

@ -24,6 +24,7 @@
use bevy::asset::load_internal_asset;
use bevy::prelude::*;
use bevy::render::batching::{batch_and_prepare_render_phase, write_batched_instance_buffer};
use bevy::render::extract_component::{
ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
};
@ -42,11 +43,12 @@ use crate::draw::{
use crate::node::{OpaqueOutline, OutlineNode, StencilOutline, TransparentOutline};
use crate::pipeline::{OutlinePipeline, FRAGMENT_SHADER_HANDLE, OUTLINE_SHADER_HANDLE};
use crate::uniforms::{
extract_outline_uniforms, queue_outline_stencil_bind_group, queue_outline_volume_bind_group,
set_outline_visibility, OutlineFragmentUniform, OutlineStencilUniform, OutlineVolumeUniform,
extract_outline_uniforms, prepare_outline_stencil_bind_group,
prepare_outline_volume_bind_group, set_outline_visibility, OutlineFragmentUniform,
OutlineStencilUniform, OutlineVolumeUniform,
};
use crate::view_uniforms::{
extract_outline_view_uniforms, queue_outline_view_bind_group, OutlineViewUniform,
extract_outline_view_uniforms, prepare_outline_view_bind_group, OutlineViewUniform,
};
mod computed;
@ -258,6 +260,20 @@ impl Plugin for OutlinePlugin {
.add_systems(
Render,
(
prepare_outline_view_bind_group,
prepare_outline_stencil_bind_group,
prepare_outline_volume_bind_group,
)
.in_set(RenderSet::PrepareBindGroups),
)
.add_systems(
Render,
(queue_outline_stencil_mesh, queue_outline_volume_mesh).in_set(RenderSet::QueueMeshes),
)
.add_systems(
Render,
(
sort_phase_system::<StencilOutline>,
sort_phase_system::<OpaqueOutline>,
sort_phase_system::<TransparentOutline>,
)
@ -266,15 +282,16 @@ impl Plugin for OutlinePlugin {
.add_systems(
Render,
(
queue_outline_view_bind_group,
queue_outline_stencil_bind_group,
queue_outline_volume_bind_group,
batch_and_prepare_render_phase::<StencilOutline, OutlinePipeline>,
batch_and_prepare_render_phase::<OpaqueOutline, OutlinePipeline>,
batch_and_prepare_render_phase::<TransparentOutline, OutlinePipeline>,
)
.in_set(RenderSet::Queue),
.in_set(RenderSet::PrepareResources),
)
.add_systems(
Render,
(queue_outline_stencil_mesh, queue_outline_volume_mesh).in_set(RenderSet::Queue),
write_batched_instance_buffer::<OutlinePipeline>
.in_set(RenderSet::PrepareResourcesFlush),
);
let world = &mut app.sub_app_mut(RenderApp).world;

View File

@ -1,4 +1,5 @@
use std::cmp::Reverse;
use std::ops::Range;
use bevy::ecs::system::lifetimeless::Read;
use bevy::prelude::*;
@ -16,6 +17,7 @@ use bevy::render::{
render_graph::{Node, RenderGraphContext},
renderer::RenderContext,
};
use bevy::utils::nonmax::NonMaxU32;
use bevy::utils::FloatOrd;
pub(crate) struct StencilOutline {
@ -23,6 +25,8 @@ pub(crate) struct StencilOutline {
pub pipeline: CachedRenderPipelineId,
pub entity: Entity,
pub draw_function: DrawFunctionId,
pub batch_range: Range<u32>,
pub dynamic_offset: Option<NonMaxU32>,
}
impl PhaseItem for StencilOutline {
@ -40,6 +44,22 @@ impl PhaseItem for StencilOutline {
fn draw_function(&self) -> bevy::render::render_phase::DrawFunctionId {
self.draw_function
}
fn batch_range(&self) -> &std::ops::Range<u32> {
&self.batch_range
}
fn batch_range_mut(&mut self) -> &mut std::ops::Range<u32> {
&mut self.batch_range
}
fn dynamic_offset(&self) -> Option<bevy::utils::nonmax::NonMaxU32> {
self.dynamic_offset
}
fn dynamic_offset_mut(&mut self) -> &mut Option<bevy::utils::nonmax::NonMaxU32> {
&mut self.dynamic_offset
}
}
impl CachedRenderPipelinePhaseItem for StencilOutline {
@ -54,6 +74,8 @@ pub(crate) struct OpaqueOutline {
pub pipeline: CachedRenderPipelineId,
pub entity: Entity,
pub draw_function: DrawFunctionId,
pub batch_range: Range<u32>,
pub dynamic_offset: Option<NonMaxU32>,
}
impl PhaseItem for OpaqueOutline {
@ -71,6 +93,22 @@ impl PhaseItem for OpaqueOutline {
fn draw_function(&self) -> bevy::render::render_phase::DrawFunctionId {
self.draw_function
}
fn batch_range(&self) -> &Range<u32> {
&self.batch_range
}
fn batch_range_mut(&mut self) -> &mut Range<u32> {
&mut self.batch_range
}
fn dynamic_offset(&self) -> Option<NonMaxU32> {
self.dynamic_offset
}
fn dynamic_offset_mut(&mut self) -> &mut Option<NonMaxU32> {
&mut self.dynamic_offset
}
}
impl CachedRenderPipelinePhaseItem for OpaqueOutline {
@ -85,6 +123,8 @@ pub(crate) struct TransparentOutline {
pub pipeline: CachedRenderPipelineId,
pub entity: Entity,
pub draw_function: DrawFunctionId,
pub batch_range: Range<u32>,
pub dynamic_offset: Option<NonMaxU32>,
}
impl PhaseItem for TransparentOutline {
@ -102,6 +142,22 @@ impl PhaseItem for TransparentOutline {
fn draw_function(&self) -> bevy::render::render_phase::DrawFunctionId {
self.draw_function
}
fn batch_range(&self) -> &Range<u32> {
&self.batch_range
}
fn batch_range_mut(&mut self) -> &mut Range<u32> {
&mut self.batch_range
}
fn dynamic_offset(&self) -> Option<NonMaxU32> {
self.dynamic_offset
}
fn dynamic_offset_mut(&mut self) -> &mut Option<NonMaxU32> {
&mut self.dynamic_offset
}
}
impl CachedRenderPipelinePhaseItem for TransparentOutline {

View File

@ -1,10 +1,13 @@
#import bevy_render::view View
#import bevy_pbr::mesh_types Mesh
#import bevy_pbr::mesh_types SkinnedMesh
#import bevy_render::view::View
#import bevy_render::maths
#import bevy_pbr::mesh_types::Mesh
#import bevy_pbr::mesh_types::SkinnedMesh
#import bevy_pbr::mesh_functions
struct Vertex {
#ifdef VERTEX_POSITIONS
@location(0) position: vec3<f32>,
@builtin(instance_index) instance_index: u32,
#endif
#ifndef OFFSET_ZERO
@location(1) outline_normal: vec3<f32>,
@ -39,9 +42,7 @@ struct OutlineVertexUniform {
@group(0) @binding(0)
var<uniform> view: View;
@group(1) @binding(0)
var<uniform> mesh: Mesh;
#import bevy_pbr::mesh_bindings
#import bevy_pbr::skinning
#import bevy_pbr::morph
@ -90,7 +91,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
#ifdef SKINNED
let model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights);
#else
let model = mesh.model;
let model = bevy_pbr::mesh_functions::get_model_matrix(vertex_no_morph.instance_index);
#endif
let clip_pos = view.view_proj * (model * vec4<f32>(vertex.position, 1.0));
#ifdef OFFSET_ZERO

View File

@ -1,8 +1,14 @@
use std::borrow::Cow;
use bevy::pbr::{setup_morph_and_skinning_defs, MeshPipelineKey};
use bevy::ecs::query::QueryItem;
use bevy::ecs::system::lifetimeless::Read;
use bevy::ecs::system::SystemParamItem;
use bevy::pbr::{
setup_morph_and_skinning_defs, MeshFlags, MeshPipelineKey, MeshPipelineViewLayoutKey,
MeshTransforms, MeshUniform,
};
use bevy::prelude::*;
use bevy::reflect::TypeUuid;
use bevy::render::batching::GetBatchData;
use bevy::render::render_resource::{
BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState,
BufferBindingType, BufferSize, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState,
@ -11,6 +17,7 @@ use bevy::render::render_resource::{
TextureFormat, VertexState,
};
use bevy::render::renderer::RenderDevice;
use bevy::render::settings::WgpuSettings;
use bevy::render::texture::BevyDefault;
use bevy::render::view::ViewTarget;
use bevy::{
@ -23,18 +30,20 @@ use bevy::{
},
};
use bitfield::{bitfield_bitrange, bitfield_fields};
use wgpu_types::{Backends, PushConstantRange};
use crate::uniforms::{
DepthMode, OutlineFragmentUniform, OutlineStencilUniform, OutlineVolumeUniform,
DepthMode, ExtractedOutline, OutlineFragmentUniform, OutlineStencilUniform,
OutlineVolumeUniform,
};
use crate::view_uniforms::OutlineViewUniform;
use crate::ATTRIBUTE_OUTLINE_NORMAL;
pub(crate) const OUTLINE_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2101625026478770097);
pub(crate) const OUTLINE_SHADER_HANDLE: Handle<Shader> =
Handle::weak_from_u128(2101625026478770097);
pub(crate) const FRAGMENT_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 12033806834125368121);
pub(crate) const FRAGMENT_SHADER_HANDLE: Handle<Shader> =
Handle::weak_from_u128(12033806834125368121);
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum PassType {
@ -245,11 +254,14 @@ impl SpecializedMeshPipeline for OutlinePipeline {
buffer_attrs.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
}
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 bind_layouts = vec![self
.mesh_pipeline
.get_view_layout(if key.msaa() == Msaa::Off {
MeshPipelineViewLayoutKey::empty()
} else {
MeshPipelineViewLayoutKey::MULTISAMPLED
})
.clone()];
bind_layouts.push(setup_morph_and_skinning_defs(
&self.mesh_pipeline.mesh_layouts,
@ -308,15 +320,23 @@ impl SpecializedMeshPipeline for OutlinePipeline {
}
}
let buffers = vec![layout.get_layout(&buffer_attrs)?];
let mut push_constant_ranges = Vec::with_capacity(1);
// Proxy for webgl feature flag in bevy
if WgpuSettings::default().backends == Some(Backends::GL) {
push_constant_ranges.push(PushConstantRange {
stages: ShaderStages::VERTEX,
range: 0..4,
});
}
Ok(RenderPipelineDescriptor {
vertex: VertexState {
shader: OUTLINE_SHADER_HANDLE.typed::<Shader>(),
shader: OUTLINE_SHADER_HANDLE,
entry_point: "vertex".into(),
shader_defs: vertex_defs,
buffers,
},
fragment: Some(FragmentState {
shader: FRAGMENT_SHADER_HANDLE.typed::<Shader>(),
shader: FRAGMENT_SHADER_HANDLE,
shader_defs: fragment_defs,
entry_point: "fragment".into(),
targets,
@ -343,8 +363,28 @@ impl SpecializedMeshPipeline for OutlinePipeline {
mask: !0,
alpha_to_coverage_enabled: false,
},
push_constant_ranges: default(),
push_constant_ranges,
label: Some(Cow::Borrowed("outline_pipeline")),
})
}
}
impl GetBatchData for OutlinePipeline {
type Param = ();
type Query = Read<ExtractedOutline>;
type QueryFilter = ();
type CompareData = ();
type BufferData = MeshUniform;
fn get_batch_data(
_: &SystemParamItem<Self::Param>,
outline: &QueryItem<Self::Query>,
) -> (Self::BufferData, Option<Self::CompareData>) {
let ts = MeshTransforms {
transform: (&outline.transform).into(),
previous_transform: (&outline.transform).into(),
flags: MeshFlags::NONE.bits(),
};
((&ts).into(), None)
}
}

View File

@ -3,11 +3,12 @@ use bevy::{
lifetimeless::{Read, SRes},
SystemParamItem,
},
math::Affine3A,
prelude::*,
render::{
extract_component::{ComponentUniforms, DynamicUniformIndex},
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::{BindGroup, BindGroupDescriptor, BindGroupEntry, ShaderType},
render_resource::{BindGroup, BindGroupEntry, ShaderType},
renderer::RenderDevice,
Extract,
},
@ -18,7 +19,8 @@ use crate::{node::StencilOutline, pipeline::OutlinePipeline, ComputedOutline};
#[derive(Component)]
pub(crate) struct ExtractedOutline {
pub depth_mode: DepthMode,
pub mesh: Handle<Mesh>,
pub transform: Affine3A,
pub mesh_id: AssetId<Mesh>,
}
#[derive(Clone, Component, ShaderType)]
@ -57,13 +59,11 @@ pub(crate) struct OutlineVolumeBindGroup {
pub bind_group: BindGroup,
}
pub(crate) fn set_outline_visibility(
mut query: Query<(&mut ComputedVisibility, &ComputedOutline)>,
) {
pub(crate) fn set_outline_visibility(mut query: Query<(&mut ViewVisibility, &ComputedOutline)>) {
for (mut visibility, computed) in query.iter_mut() {
if let ComputedOutline(Some(computed)) = computed {
if computed.volume.value.enabled || computed.stencil.value.enabled {
visibility.set_visible_in_view();
visibility.set();
}
}
}
@ -72,14 +72,15 @@ pub(crate) fn set_outline_visibility(
#[allow(clippy::type_complexity)]
pub(crate) fn extract_outline_uniforms(
mut commands: Commands,
query: Extract<Query<(Entity, &ComputedOutline, &Handle<Mesh>)>>,
query: Extract<Query<(Entity, &ComputedOutline, &GlobalTransform, &Handle<Mesh>)>>,
) {
for (entity, computed, mesh) in query.iter() {
for (entity, computed, transform, mesh) in query.iter() {
let cmds = &mut commands.get_or_spawn(entity);
if let ComputedOutline(Some(computed)) = computed {
cmds.insert(ExtractedOutline {
depth_mode: computed.mode.value.depth_mode,
mesh: mesh.clone_weak(),
transform: transform.affine(),
mesh_id: mesh.id(),
});
if computed.volume.value.enabled {
cmds.insert(OutlineVolumeUniform {
@ -100,26 +101,26 @@ pub(crate) fn extract_outline_uniforms(
}
}
pub(crate) fn queue_outline_stencil_bind_group(
pub(crate) fn prepare_outline_stencil_bind_group(
mut commands: Commands,
render_device: Res<RenderDevice>,
outline_pipeline: Res<OutlinePipeline>,
vertex: Res<ComponentUniforms<OutlineStencilUniform>>,
) {
if let Some(vertex_binding) = vertex.binding() {
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
let bind_group = render_device.create_bind_group(
Some("outline_stencil_bind_group"),
&outline_pipeline.outline_stencil_bind_group_layout,
&[BindGroupEntry {
binding: 0,
resource: vertex_binding.clone(),
}],
label: Some("outline_stencil_bind_group"),
layout: &outline_pipeline.outline_stencil_bind_group_layout,
});
);
commands.insert_resource(OutlineStencilBindGroup { bind_group });
}
}
pub(crate) fn queue_outline_volume_bind_group(
pub(crate) fn prepare_outline_volume_bind_group(
mut commands: Commands,
render_device: Res<RenderDevice>,
outline_pipeline: Res<OutlinePipeline>,
@ -127,8 +128,10 @@ pub(crate) fn queue_outline_volume_bind_group(
fragment: Res<ComponentUniforms<OutlineFragmentUniform>>,
) {
if let (Some(vertex_binding), Some(fragment_binding)) = (vertex.binding(), fragment.binding()) {
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
entries: &[
let bind_group = render_device.create_bind_group(
"outline_volume_bind_group",
&outline_pipeline.outline_volume_bind_group_layout,
&[
BindGroupEntry {
binding: 0,
resource: vertex_binding.clone(),
@ -138,9 +141,7 @@ pub(crate) fn queue_outline_volume_bind_group(
resource: fragment_binding.clone(),
},
],
label: Some("outline_volume_bind_group"),
layout: &outline_pipeline.outline_volume_bind_group_layout,
});
);
commands.insert_resource(OutlineVolumeBindGroup { bind_group });
}
}

View File

@ -6,7 +6,7 @@ use bevy::render::render_phase::{
PhaseItem, RenderCommand, RenderCommandResult, RenderPhase, TrackedRenderPass,
};
use bevy::render::render_resource::ShaderType;
use bevy::render::render_resource::{BindGroup, BindGroupDescriptor, BindGroupEntry};
use bevy::render::render_resource::{BindGroup, BindGroupEntry};
use bevy::render::renderer::RenderDevice;
use bevy::render::view::RenderLayers;
use bevy::render::Extract;
@ -49,21 +49,21 @@ pub(crate) fn extract_outline_view_uniforms(
}
}
pub(crate) fn queue_outline_view_bind_group(
pub(crate) fn prepare_outline_view_bind_group(
mut commands: Commands,
render_device: Res<RenderDevice>,
outline_pipeline: Res<OutlinePipeline>,
view_uniforms: Res<ComponentUniforms<OutlineViewUniform>>,
) {
if let Some(view_binding) = view_uniforms.binding() {
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
let bind_group = render_device.create_bind_group(
"outline_view_bind_group",
&outline_pipeline.outline_view_bind_group_layout,
&[BindGroupEntry {
binding: 0,
resource: view_binding.clone(),
}],
label: Some("outline_view_bind_group"),
layout: &outline_pipeline.outline_view_bind_group_layout,
});
);
commands.insert_resource(OutlineViewBindGroup { bind_group });
}
}