use bevy::{
app::{App, Plugin},
asset::{Assets, HandleUntyped},
core_pipeline::core_2d::Transparent2d,
ecs::{
component::Component,
entity::Entity,
query::With,
system::{Commands, Local, Query, Res, ResMut, Resource},
world::{FromWorld, World},
},
reflect::TypeUuid,
render::{
mesh::Mesh,
render_asset::RenderAssets,
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
render_resource::{
BlendState, ColorTargetState, ColorWrites, FragmentState, FrontFace, MultisampleState,
PipelineCache, PolygonMode, PrimitiveState, RenderPipelineDescriptor, Shader,
SpecializedRenderPipeline, SpecializedRenderPipelines, TextureFormat,
VertexBufferLayout, VertexFormat, VertexState, VertexStepMode,
},
texture::BevyDefault,
view::{ComputedVisibility, ExtractedView, Msaa, ViewTarget, VisibleEntities},
Extract, RenderApp, RenderStage,
},
sprite::{
DrawMesh2d, Mesh2dHandle, Mesh2dPipeline, Mesh2dPipelineKey, Mesh2dUniform,
SetMesh2dBindGroup, SetMesh2dViewBindGroup,
},
utils::FloatOrd,
};
#[derive(Component, Default)]
pub struct Shape;
#[derive(Resource)]
struct ShapePipeline {
mesh2d_pipeline: Mesh2dPipeline,
}
impl FromWorld for ShapePipeline {
fn from_world(world: &mut World) -> Self {
Self {
mesh2d_pipeline: Mesh2dPipeline::from_world(world),
}
}
}
#[allow(clippy::too_many_lines)]
impl SpecializedRenderPipeline for ShapePipeline {
type Key = Mesh2dPipelineKey;
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let formats = vec![
VertexFormat::Float32x3,
VertexFormat::Float32x4,
];
let vertex_layout =
VertexBufferLayout::from_vertex_formats(VertexStepMode::Vertex, formats);
RenderPipelineDescriptor {
vertex: VertexState {
shader: SHAPE_SHADER_HANDLE.typed::<Shader>(),
entry_point: "vertex".into(),
shader_defs: Vec::new(),
buffers: vec![vertex_layout],
},
fragment: Some(FragmentState {
shader: SHAPE_SHADER_HANDLE.typed::<Shader>(),
shader_defs: Vec::new(),
entry_point: "fragment".into(),
targets: vec![Some(ColorTargetState {
format: if key.contains(Mesh2dPipelineKey::HDR) {
ViewTarget::TEXTURE_FORMAT_HDR
} else {
TextureFormat::bevy_default()
},
blend: Some(BlendState::ALPHA_BLENDING),
write_mask: ColorWrites::ALL,
})],
}),
layout: Some(vec![
self.mesh2d_pipeline.view_layout.clone(),
self.mesh2d_pipeline.mesh_layout.clone(),
]),
primitive: PrimitiveState {
front_face: FrontFace::Cw,
cull_mode: None,
unclipped_depth: false,
polygon_mode: PolygonMode::Fill,
conservative: false,
topology: key.primitive_topology(),
strip_index_format: None,
},
depth_stencil: None,
multisample: MultisampleState {
count: key.msaa_samples(),
mask: !0,
alpha_to_coverage_enabled: false,
},
label: Some("shape_pipeline".into()),
}
}
}
type DrawShape = (
SetItemPipeline,
SetMesh2dViewBindGroup<0>,
SetMesh2dBindGroup<1>,
DrawMesh2d,
);
pub struct RenderShapePlugin;
pub const SHAPE_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 868_242_739_331_722_714);
impl Plugin for RenderShapePlugin {
fn build(&self, app: &mut App) {
let mut shaders = app.world.get_resource_mut::<Assets<Shader>>().unwrap();
shaders.set_untracked(
SHAPE_SHADER_HANDLE,
Shader::from_wgsl(include_str!("shape.wgsl")),
);
let render_app = app.get_sub_app_mut(RenderApp).unwrap();
render_app
.add_render_command::<Transparent2d, DrawShape>()
.init_resource::<ShapePipeline>()
.init_resource::<SpecializedRenderPipelines<ShapePipeline>>()
.add_system_to_stage(RenderStage::Extract, extract_shape)
.add_system_to_stage(RenderStage::Queue, queue_shape);
}
}
fn extract_shape(
mut commands: Commands,
mut previous_len: Local<usize>,
query: Extract<Query<(Entity, &ComputedVisibility), With<Shape>>>,
) {
let mut values = Vec::with_capacity(*previous_len);
for (entity, computed_visibility) in query.iter() {
if !computed_visibility.is_visible() {
continue;
}
values.push((entity, (Shape,)));
}
*previous_len = values.len();
commands.insert_or_spawn_batch(values);
}
#[allow(clippy::too_many_arguments)]
fn queue_shape(
transparent_draw_functions: Res<DrawFunctions<Transparent2d>>,
shape_pipeline: Res<ShapePipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<ShapePipeline>>,
mut pipeline_cache: ResMut<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>,
shape: Query<(&Mesh2dHandle, &Mesh2dUniform), With<Shape>>,
mut views: Query<(
&ExtractedView,
&VisibleEntities,
&mut RenderPhase<Transparent2d>,
)>,
) {
if shape.is_empty() {
return;
}
for (view, visible_entities, mut transparent_phase) in views.iter_mut() {
let draw_shape = transparent_draw_functions
.read()
.get_id::<DrawShape>()
.unwrap();
let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples)
| Mesh2dPipelineKey::from_hdr(view.hdr);
for visible_entity in &visible_entities.entities {
if let Ok((mesh2d_handle, mesh2d_uniform)) = shape.get(*visible_entity) {
let mut mesh2d_key = mesh_key;
if let Some(mesh) = render_meshes.get(&mesh2d_handle.0) {
mesh2d_key |=
Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology);
}
let pipeline_id =
pipelines.specialize(&mut pipeline_cache, &shape_pipeline, mesh2d_key);
let mesh_z = mesh2d_uniform.transform.w_axis.z;
transparent_phase.add(Transparent2d {
entity: *visible_entity,
draw_function: draw_shape,
pipeline: pipeline_id,
sort_key: FloatOrd(mesh_z),
batch_range: None,
});
}
}
}
}