use bevy::{
asset::uuid_handle,
core_pipeline::core_2d::CORE_2D_DEPTH_FORMAT,
image::BevyDefault,
mesh::VertexBufferLayout,
prelude::{Component, FromWorld, Handle, Resource, Shader, World},
render::{
globals::GlobalsUniform,
render_resource::{
BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendComponent,
BlendFactor, BlendOperation, BlendState, BufferBindingType, ColorTargetState,
ColorWrites, CompareFunction, DepthBiasState, DepthStencilState, Face, FragmentState,
FrontFace, MultisampleState, PolygonMode, PrimitiveState, PrimitiveTopology,
RenderPipelineDescriptor, SamplerBindingType, ShaderStages, ShaderType,
SpecializedRenderPipeline, StencilFaceState, StencilState, TextureFormat,
TextureSampleType, TextureViewDimension, VertexFormat, VertexState, VertexStepMode,
},
view::{ViewTarget, ViewUniform},
},
};
use crate::map::{HexCoordSystem, IsoCoordSystem, TilemapType};
use super::{chunk::TilemapUniformData, prepare::MeshUniform};
pub const TILEMAP_SHADER_VERTEX: Handle<Shader> =
uuid_handle!("915ef471-58b4-4431-acae-f38b41969a9e");
pub const TILEMAP_SHADER_FRAGMENT: Handle<Shader> =
uuid_handle!("bf34308e-69df-4da8-b0ff-816042c716c8");
#[derive(Clone, Resource)]
pub struct TilemapPipeline {
pub view_layout: BindGroupLayoutDescriptor,
pub material_layout: BindGroupLayoutDescriptor,
pub mesh_layout: BindGroupLayoutDescriptor,
}
impl FromWorld for TilemapPipeline {
fn from_world(_world: &mut World) -> Self {
let view_layout = BindGroupLayoutDescriptor::new(
"tilemap_view_layout",
&[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(ViewUniform::min_size()),
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::VERTEX_FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: Some(GlobalsUniform::min_size()),
},
count: None,
},
],
);
let mesh_layout = BindGroupLayoutDescriptor::new(
"tilemap_mesh_layout",
&[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(MeshUniform::min_size()),
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::VERTEX_FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(TilemapUniformData::min_size()),
},
count: None,
},
],
);
#[cfg(not(feature = "atlas"))]
let material_layout = BindGroupLayoutDescriptor::new(
"tilemap_material_layout",
&[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2Array,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
],
);
#[cfg(feature = "atlas")]
let material_layout = BindGroupLayoutDescriptor::new(
"tilemap_material_layout",
&[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
],
);
TilemapPipeline {
view_layout,
material_layout,
mesh_layout,
}
}
}
#[derive(Debug, Component, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TilemapPipelineKey {
pub msaa: u32,
pub map_type: TilemapType,
pub hdr: bool,
}
impl SpecializedRenderPipeline for TilemapPipeline {
type Key = TilemapPipelineKey;
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let mut shader_defs = Vec::new();
#[cfg(feature = "atlas")]
shader_defs.push("ATLAS".into());
let mesh_string = match key.map_type {
TilemapType::Square => "SQUARE",
TilemapType::Isometric(coord_system) => match coord_system {
IsoCoordSystem::Diamond => "ISO_DIAMOND",
IsoCoordSystem::Staggered => "ISO_STAGGERED",
},
TilemapType::Hexagon(coord_system) => match coord_system {
HexCoordSystem::Column => "COLUMN_HEX",
HexCoordSystem::ColumnEven => "COLUMN_EVEN_HEX",
HexCoordSystem::ColumnOdd => "COLUMN_ODD_HEX",
HexCoordSystem::Row => "ROW_HEX",
HexCoordSystem::RowEven => "ROW_EVEN_HEX",
HexCoordSystem::RowOdd => "ROW_ODD_HEX",
},
};
shader_defs.push(mesh_string.into());
let formats = vec![
VertexFormat::Float32x4,
VertexFormat::Float32x4,
VertexFormat::Float32x4,
];
let vertex_layout =
VertexBufferLayout::from_vertex_formats(VertexStepMode::Vertex, formats);
RenderPipelineDescriptor {
vertex: VertexState {
shader: TILEMAP_SHADER_VERTEX,
entry_point: Some("vertex".into()),
shader_defs: shader_defs.clone(),
buffers: vec![vertex_layout],
},
fragment: Some(FragmentState {
shader: TILEMAP_SHADER_FRAGMENT,
shader_defs,
entry_point: Some("fragment".into()),
targets: vec![Some(ColorTargetState {
format: if key.hdr {
ViewTarget::TEXTURE_FORMAT_HDR
} else {
TextureFormat::bevy_default()
},
blend: Some(BlendState {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
}),
write_mask: ColorWrites::ALL,
})],
}),
layout: vec![
self.view_layout.clone(),
self.mesh_layout.clone(),
self.material_layout.clone(),
],
primitive: PrimitiveState {
conservative: false,
cull_mode: Some(Face::Back),
front_face: FrontFace::Ccw,
polygon_mode: PolygonMode::Fill,
strip_index_format: None,
topology: PrimitiveTopology::TriangleList,
unclipped_depth: false,
},
depth_stencil: Some(DepthStencilState {
format: CORE_2D_DEPTH_FORMAT,
depth_write_enabled: false,
depth_compare: CompareFunction::GreaterEqual,
stencil: StencilState {
front: StencilFaceState::IGNORE,
back: StencilFaceState::IGNORE,
read_mask: 0,
write_mask: 0,
},
bias: DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: MultisampleState {
count: key.msaa,
mask: !0,
alpha_to_coverage_enabled: false,
},
label: Some("tilemap_pipeline".into()),
push_constant_ranges: vec![],
zero_initialize_workgroup_memory: false,
}
}
}