| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
use bevy::{ |
| 9 |
asset::RenderAssetUsages, |
| 10 |
color::palettes::basic::YELLOW, |
| 11 |
core_pipeline::{core_2d::CORE_2D_DEPTH_FORMAT, Core2d, Core2dSystems}, |
| 12 |
ecs::{ |
| 13 |
entity::EntityHash, |
| 14 |
system::{lifetimeless::SRes, SystemParamItem}, |
| 15 |
}, |
| 16 |
math::{ops, FloatOrd}, |
| 17 |
mesh::{BaseMeshPipelineKey, Indices, MeshVertexAttribute, VertexBufferLayout}, |
| 18 |
platform::collections::HashSet, |
| 19 |
prelude::*, |
| 20 |
render::{ |
| 21 |
batching::{no_gpu_preprocessing::batch_and_prepare_sorted_render_phase, GetBatchData}, |
| 22 |
camera::ExtractedCamera, |
| 23 |
diagnostic::RecordDiagnostics as _, |
| 24 |
material_bind_groups::{MaterialBindGroupIndex, MaterialBindGroupSlot, MaterialBindingId}, |
| 25 |
mesh::{ |
| 26 |
allocator::MeshAllocator, MeshMetadataFallbackBuffer, RenderMesh, RenderMeshBufferInfo, |
| 27 |
}, |
| 28 |
render_asset::RenderAssets, |
| 29 |
render_phase::{ |
| 30 |
sort_phase_system, AddRenderCommand, CachedRenderPipelinePhaseItem, DrawFunctionId, |
| 31 |
DrawFunctions, PhaseItem, PhaseItemExtraIndex, RenderCommand, RenderCommandResult, |
| 32 |
SetItemPipeline, SortedPhaseItem, TrackedRenderPass, ViewSortedRenderPhases, |
| 33 |
}, |
| 34 |
render_resource::{ |
| 35 |
BlendState, CachedRenderPipelineId, ColorTargetState, ColorWrites, CompareFunction, |
| 36 |
DepthBiasState, DepthStencilState, Face, FragmentState, MultisampleState, |
| 37 |
PipelineCache, PrimitiveState, PrimitiveTopology, RenderPassDescriptor, |
| 38 |
RenderPipelineDescriptor, SpecializedRenderPipeline, SpecializedRenderPipelines, |
| 39 |
StencilFaceState, StencilState, StoreOp, VertexFormat, VertexState, VertexStepMode, |
| 40 |
}, |
| 41 |
renderer::{RenderContext, ViewQuery}, |
| 42 |
sync_component::{SyncComponent, SyncComponentPlugin}, |
| 43 |
sync_world::{MainEntity, MainEntityHashMap, RenderEntity}, |
| 44 |
view::{ |
| 45 |
ExtractedView, RenderVisibleEntities, RetainedViewEntity, ViewDepthStencilTexture, |
| 46 |
ViewTarget, |
| 47 |
}, |
| 48 |
Extract, Render, RenderApp, RenderStartup, RenderSystems, |
| 49 |
}, |
| 50 |
sprite_render::{ |
| 51 |
extract_2d_meshes, init_mesh_2d_pipeline, Mesh2dBindGroup, Mesh2dPipeline, |
| 52 |
Mesh2dPipelineKey, Mesh2dTransforms, Mesh2dUniform, MeshFlags, RenderMesh2dInstance, |
| 53 |
SetMesh2dViewBindGroup, |
| 54 |
}, |
| 55 |
}; |
| 56 |
use indexmap::IndexMap; |
| 57 |
use std::{f32::consts::PI, ops::Range}; |
| 58 |
|
| 59 |
fn main() { |
| 60 |
App::new() |
| 61 |
.add_plugins((DefaultPlugins, ColoredMesh2dPlugin)) |
| 62 |
.add_systems(Startup, star) |
| 63 |
.run(); |
| 64 |
} |
| 65 |
|
| 66 |
fn star( |
| 67 |
mut commands: Commands, |
| 68 |
|
| 69 |
mut meshes: ResMut<Assets<Mesh>>, |
| 70 |
) { |
| 71 |
|
| 72 |
|
| 73 |
|
| 74 |
|
| 75 |
|
| 76 |
|
| 77 |
|
| 78 |
let mut star = Mesh::new( |
| 79 |
PrimitiveTopology::TriangleList, |
| 80 |
RenderAssetUsages::RENDER_WORLD, |
| 81 |
); |
| 82 |
|
| 83 |
|
| 84 |
|
| 85 |
|
| 86 |
|
| 87 |
|
| 88 |
|
| 89 |
|
| 90 |
|
| 91 |
|
| 92 |
|
| 93 |
|
| 94 |
|
| 95 |
let mut v_pos = vec![[0.0, 0.0, 0.0]]; |
| 96 |
for i in 0..10 { |
| 97 |
|
| 98 |
let a = i as f32 * PI / 5.0; |
| 99 |
|
| 100 |
let r = (1 - i % 2) as f32 * 100.0 + 100.0; |
| 101 |
|
| 102 |
v_pos.push([r * ops::sin(a), r * ops::cos(a), 0.0]); |
| 103 |
} |
| 104 |
|
| 105 |
star.insert_attribute(Mesh::ATTRIBUTE_POSITION, v_pos); |
| 106 |
|
| 107 |
|
| 108 |
let mut v_color: Vec<u32> = vec![LinearRgba::BLACK.as_u32()]; |
| 109 |
v_color.extend_from_slice(&[LinearRgba::from(YELLOW).as_u32(); 10]); |
| 110 |
star.insert_attribute( |
| 111 |
MeshVertexAttribute::new("Vertex_Color", 1, VertexFormat::Uint32), |
| 112 |
v_color, |
| 113 |
); |
| 114 |
|
| 115 |
|
| 116 |
|
| 117 |
|
| 118 |
|
| 119 |
|
| 120 |
|
| 121 |
|
| 122 |
|
| 123 |
|
| 124 |
let mut indices = vec![0, 1, 10]; |
| 125 |
for i in 2..=10 { |
| 126 |
indices.extend_from_slice(&[0, i, i - 1]); |
| 127 |
} |
| 128 |
star.insert_indices(Indices::U32(indices)); |
| 129 |
|
| 130 |
|
| 131 |
commands.spawn(( |
| 132 |
|
| 133 |
ColoredMesh2d, |
| 134 |
|
| 135 |
Mesh2d(meshes.add(star)), |
| 136 |
)); |
| 137 |
|
| 138 |
commands.spawn(Camera2d); |
| 139 |
} |
| 140 |
|
| 141 |
|
| 142 |
#[derive(Component, Default)] |
| 143 |
pub struct ColoredMesh2d; |
| 144 |
|
| 145 |
impl SyncComponent<RenderApp> for ColoredMesh2d { |
| 146 |
type Target = Self; |
| 147 |
} |
| 148 |
|
| 149 |
|
| 150 |
#[derive(Resource)] |
| 151 |
pub struct ColoredMesh2dPipeline { |
| 152 |
|
| 153 |
mesh2d_pipeline: Mesh2dPipeline, |
| 154 |
|
| 155 |
shader: Handle<Shader>, |
| 156 |
} |
| 157 |
|
| 158 |
fn init_colored_mesh_2d_pipeline( |
| 159 |
mut commands: Commands, |
| 160 |
mesh2d_pipeline: Res<Mesh2dPipeline>, |
| 161 |
colored_mesh2d_shader: Res<ColoredMesh2dShader>, |
| 162 |
) { |
| 163 |
commands.insert_resource(ColoredMesh2dPipeline { |
| 164 |
mesh2d_pipeline: mesh2d_pipeline.clone(), |
| 165 |
|
| 166 |
shader: colored_mesh2d_shader.0.clone(), |
| 167 |
}); |
| 168 |
} |
| 169 |
|
| 170 |
|
| 171 |
impl SpecializedRenderPipeline for ColoredMesh2dPipeline { |
| 172 |
type Key = Mesh2dPipelineKey; |
| 173 |
|
| 174 |
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { |
| 175 |
|
| 176 |
|
| 177 |
let formats = vec![ |
| 178 |
|
| 179 |
VertexFormat::Float32x3, |
| 180 |
|
| 181 |
VertexFormat::Uint32, |
| 182 |
]; |
| 183 |
|
| 184 |
let vertex_layout = |
| 185 |
VertexBufferLayout::from_vertex_formats(VertexStepMode::Vertex, formats); |
| 186 |
|
| 187 |
let format = key.target_format(); |
| 188 |
|
| 189 |
RenderPipelineDescriptor { |
| 190 |
vertex: VertexState { |
| 191 |
|
| 192 |
shader: self.shader.clone(), |
| 193 |
|
| 194 |
buffers: vec![vertex_layout], |
| 195 |
..default() |
| 196 |
}, |
| 197 |
fragment: Some(FragmentState { |
| 198 |
|
| 199 |
shader: self.shader.clone(), |
| 200 |
targets: vec![Some(ColorTargetState { |
| 201 |
format, |
| 202 |
blend: Some(BlendState::ALPHA_BLENDING), |
| 203 |
write_mask: ColorWrites::ALL, |
| 204 |
})], |
| 205 |
..default() |
| 206 |
}), |
| 207 |
|
| 208 |
layout: vec![ |
| 209 |
|
| 210 |
self.mesh2d_pipeline.view_layout.clone(), |
| 211 |
|
| 212 |
self.mesh2d_pipeline.mesh_layout.clone(), |
| 213 |
], |
| 214 |
primitive: PrimitiveState { |
| 215 |
cull_mode: Some(Face::Back), |
| 216 |
topology: BaseMeshPipelineKey::from_bits_retain(key.bits()).primitive_topology(), |
| 217 |
strip_index_format: BaseMeshPipelineKey::from_bits_retain(key.bits()) |
| 218 |
.strip_index_format(), |
| 219 |
..default() |
| 220 |
}, |
| 221 |
depth_stencil: Some(DepthStencilState { |
| 222 |
format: CORE_2D_DEPTH_FORMAT, |
| 223 |
depth_write_enabled: Some(false), |
| 224 |
depth_compare: Some(CompareFunction::GreaterEqual), |
| 225 |
stencil: StencilState { |
| 226 |
front: StencilFaceState::IGNORE, |
| 227 |
back: StencilFaceState::IGNORE, |
| 228 |
read_mask: 0, |
| 229 |
write_mask: 0, |
| 230 |
}, |
| 231 |
bias: DepthBiasState { |
| 232 |
constant: 0, |
| 233 |
slope_scale: 0.0, |
| 234 |
clamp: 0.0, |
| 235 |
}, |
| 236 |
}), |
| 237 |
multisample: MultisampleState { |
| 238 |
count: key.msaa_samples(), |
| 239 |
mask: !0, |
| 240 |
alpha_to_coverage_enabled: false, |
| 241 |
}, |
| 242 |
label: Some("colored_mesh2d_pipeline".into()), |
| 243 |
..default() |
| 244 |
} |
| 245 |
} |
| 246 |
} |
| 247 |
|
| 248 |
|
| 249 |
type DrawTransparentColoredMesh2d = ( |
| 250 |
|
| 251 |
SetItemPipeline, |
| 252 |
|
| 253 |
SetMesh2dViewBindGroup<0>, |
| 254 |
|
| 255 |
SetColoredMesh2dBindGroup<1>, |
| 256 |
|
| 257 |
DrawColoredMesh2d, |
| 258 |
); |
| 259 |
|
| 260 |
|
| 261 |
|
| 262 |
const COLORED_MESH2D_SHADER: &str = r" |
| 263 |
|
| 264 |
import bevy_sprite_render::mesh2d::functions as mesh2d_functions; |
| 265 |
|
| 266 |
|
| 267 |
struct Vertex { |
| 268 |
@builtin(instance_index) instance_index: u32, |
| 269 |
@location(0) position: vec3<f32>, |
| 270 |
@location(1) color: u32, |
| 271 |
}; |
| 272 |
|
| 273 |
struct VertexOutput { |
| 274 |
|
| 275 |
@builtin(position) clip_position: vec4<f32>, |
| 276 |
|
| 277 |
@location(0) color: vec4<f32>, |
| 278 |
}; |
| 279 |
|
| 280 |
|
| 281 |
@vertex |
| 282 |
fn vertex(vertex: Vertex) -> VertexOutput { |
| 283 |
var out: VertexOutput; |
| 284 |
|
| 285 |
let model = mesh2d_functions::get_world_from_local(vertex.instance_index); |
| 286 |
out.clip_position = mesh2d_functions::mesh2d_position_local_to_clip(model, vec4<f32>(vertex.position, 1.0)); |
| 287 |
|
| 288 |
out.color = vec4<f32>((vec4<u32>(vertex.color) >> vec4<u32>(0u, 8u, 16u, 24u)) & vec4<u32>(255u)) / 255.0; |
| 289 |
return out; |
| 290 |
} |
| 291 |
|
| 292 |
|
| 293 |
struct FragmentInput { |
| 294 |
|
| 295 |
@location(0) color: vec4<f32>, |
| 296 |
}; |
| 297 |
|
| 298 |
|
| 299 |
@fragment |
| 300 |
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> { |
| 301 |
return in.color; |
| 302 |
} |
| 303 |
"; |
| 304 |
|
| 305 |
|
| 306 |
pub struct ColoredMesh2dPlugin; |
| 307 |
|
| 308 |
|
| 309 |
|
| 310 |
#[derive(Resource)] |
| 311 |
struct ColoredMesh2dShader(Handle<Shader>); |
| 312 |
|
| 313 |
|
| 314 |
#[derive(Resource, Deref, DerefMut, Default)] |
| 315 |
pub struct RenderColoredMesh2dInstances(MainEntityHashMap<RenderMesh2dInstance>); |
| 316 |
|
| 317 |
impl Plugin for ColoredMesh2dPlugin { |
| 318 |
fn build(&self, app: &mut App) { |
| 319 |
|
| 320 |
let mut shaders = app.world_mut().resource_mut::<Assets<Shader>>(); |
| 321 |
|
| 322 |
|
| 323 |
let shader = shaders.add(Shader::from_wesl(COLORED_MESH2D_SHADER, file!())); |
| 324 |
|
| 325 |
app.add_plugins(SyncComponentPlugin::<ColoredMesh2d>::default()); |
| 326 |
|
| 327 |
|
| 328 |
app.get_sub_app_mut(RenderApp) |
| 329 |
.unwrap() |
| 330 |
.init_resource::<DrawFunctions<TransparentColoredMesh2d>>() |
| 331 |
|
| 332 |
|
| 333 |
.init_resource::<ViewSortedRenderPhases<TransparentColoredMesh2d>>() |
| 334 |
|
| 335 |
.init_resource::<SpecializedRenderPipelines<ColoredMesh2dPipeline>>() |
| 336 |
|
| 337 |
.init_resource::<RenderColoredMesh2dInstances>() |
| 338 |
.insert_resource(ColoredMesh2dShader(shader)) |
| 339 |
|
| 340 |
.add_render_command::<TransparentColoredMesh2d, DrawTransparentColoredMesh2d>() |
| 341 |
.add_systems( |
| 342 |
RenderStartup, |
| 343 |
init_colored_mesh_2d_pipeline.after(init_mesh_2d_pipeline), |
| 344 |
) |
| 345 |
.add_systems( |
| 346 |
ExtractSchedule, |
| 347 |
( |
| 348 |
extract_colored_mesh2d.after(extract_2d_meshes), |
| 349 |
extract_colored_mesh2d_camera_phases, |
| 350 |
), |
| 351 |
) |
| 352 |
.add_systems( |
| 353 |
Render, |
| 354 |
( |
| 355 |
sort_phase_system::<TransparentColoredMesh2d>.in_set(RenderSystems::PhaseSort), |
| 356 |
queue_colored_mesh2d.in_set(RenderSystems::QueueMeshes), |
| 357 |
|
| 358 |
batch_and_prepare_sorted_render_phase::< |
| 359 |
TransparentColoredMesh2d, |
| 360 |
ColoredMesh2dPipeline, |
| 361 |
> |
| 362 |
.in_set(RenderSystems::PrepareResources), |
| 363 |
), |
| 364 |
) |
| 365 |
.add_systems( |
| 366 |
Core2d, |
| 367 |
|
| 368 |
main_colored_transparent_pass_2d.in_set(Core2dSystems::MainPass), |
| 369 |
); |
| 370 |
} |
| 371 |
} |
| 372 |
|
| 373 |
|
| 374 |
|
| 375 |
|
| 376 |
|
| 377 |
|
| 378 |
struct TransparentColoredMesh2d { |
| 379 |
sort_key: FloatOrd, |
| 380 |
entity: (Entity, MainEntity), |
| 381 |
pipeline: CachedRenderPipelineId, |
| 382 |
draw_function: DrawFunctionId, |
| 383 |
batch_range: Range<u32>, |
| 384 |
extra_index: PhaseItemExtraIndex, |
| 385 |
|
| 386 |
|
| 387 |
indexed: bool, |
| 388 |
} |
| 389 |
|
| 390 |
impl PhaseItem for TransparentColoredMesh2d { |
| 391 |
#[inline] |
| 392 |
fn entity(&self) -> Entity { |
| 393 |
self.entity.0 |
| 394 |
} |
| 395 |
|
| 396 |
#[inline] |
| 397 |
fn main_entity(&self) -> MainEntity { |
| 398 |
self.entity.1 |
| 399 |
} |
| 400 |
|
| 401 |
#[inline] |
| 402 |
fn draw_function(&self) -> DrawFunctionId { |
| 403 |
self.draw_function |
| 404 |
} |
| 405 |
|
| 406 |
#[inline] |
| 407 |
fn batch_range(&self) -> &Range<u32> { |
| 408 |
&self.batch_range |
| 409 |
} |
| 410 |
|
| 411 |
#[inline] |
| 412 |
fn batch_range_mut(&mut self) -> &mut Range<u32> { |
| 413 |
&mut self.batch_range |
| 414 |
} |
| 415 |
|
| 416 |
#[inline] |
| 417 |
fn extra_index(&self) -> PhaseItemExtraIndex { |
| 418 |
self.extra_index.clone() |
| 419 |
} |
| 420 |
|
| 421 |
#[inline] |
| 422 |
fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) { |
| 423 |
(&mut self.batch_range, &mut self.extra_index) |
| 424 |
} |
| 425 |
} |
| 426 |
|
| 427 |
impl SortedPhaseItem for TransparentColoredMesh2d { |
| 428 |
type SortKey = FloatOrd; |
| 429 |
|
| 430 |
#[inline] |
| 431 |
fn sort_key(&self) -> Self::SortKey { |
| 432 |
self.sort_key |
| 433 |
} |
| 434 |
|
| 435 |
#[inline] |
| 436 |
fn sort(items: &mut IndexMap<(Entity, MainEntity), TransparentColoredMesh2d, EntityHash>) { |
| 437 |
items.sort_by_key(|_, item| item.sort_key()); |
| 438 |
} |
| 439 |
|
| 440 |
fn recalculate_sort_keys( |
| 441 |
_: &mut IndexMap<(Entity, MainEntity), Self, EntityHash>, |
| 442 |
_: &ExtractedView, |
| 443 |
) { |
| 444 |
|
| 445 |
} |
| 446 |
|
| 447 |
fn indexed(&self) -> bool { |
| 448 |
self.indexed |
| 449 |
} |
| 450 |
} |
| 451 |
|
| 452 |
impl CachedRenderPipelinePhaseItem for TransparentColoredMesh2d { |
| 453 |
#[inline] |
| 454 |
fn cached_pipeline(&self) -> CachedRenderPipelineId { |
| 455 |
self.pipeline |
| 456 |
} |
| 457 |
} |
| 458 |
|
| 459 |
impl GetBatchData for ColoredMesh2dPipeline { |
| 460 |
type Param = (SRes<RenderColoredMesh2dInstances>, SRes<MeshAllocator>); |
| 461 |
type BatchSetCompareData = AssetId<Mesh>; |
| 462 |
type BatchCompareData = Option<MaterialBindGroupIndex>; |
| 463 |
type BufferData = Mesh2dUniform; |
| 464 |
|
| 465 |
fn get_batch_data( |
| 466 |
(mesh_instances, mesh_allocator): &SystemParamItem<Self::Param>, |
| 467 |
(_entity, main_entity): (Entity, MainEntity), |
| 468 |
) -> Option<( |
| 469 |
Self::BufferData, |
| 470 |
Option<(Self::BatchSetCompareData, Self::BatchCompareData)>, |
| 471 |
)> { |
| 472 |
let mesh_instance = mesh_instances.get(&main_entity)?; |
| 473 |
let metadata_index = mesh_allocator |
| 474 |
.mesh_metadata_slice(&mesh_instance.mesh_asset_id) |
| 475 |
.map(|mesh_metadata_slice| mesh_metadata_slice.range.start); |
| 476 |
|
| 477 |
Some(( |
| 478 |
Mesh2dUniform::from_components( |
| 479 |
&mesh_instance.transforms, |
| 480 |
MaterialBindGroupSlot(0), |
| 481 |
mesh_instance.tag, |
| 482 |
metadata_index, |
| 483 |
), |
| 484 |
mesh_instance |
| 485 |
.automatic_batching |
| 486 |
.then_some((mesh_instance.mesh_asset_id, None)), |
| 487 |
)) |
| 488 |
} |
| 489 |
} |
| 490 |
|
| 491 |
|
| 492 |
fn extract_colored_mesh2d_camera_phases( |
| 493 |
mut colored_mesh2d_render_phases: ResMut<ViewSortedRenderPhases<TransparentColoredMesh2d>>, |
| 494 |
cameras_2d: Extract<Query<(Entity, &Camera), With<Camera2d>>>, |
| 495 |
mut live_entities: Local<HashSet<RetainedViewEntity>>, |
| 496 |
) { |
| 497 |
live_entities.clear(); |
| 498 |
|
| 499 |
for (main_entity, camera) in &cameras_2d { |
| 500 |
if !camera.is_active { |
| 501 |
continue; |
| 502 |
} |
| 503 |
|
| 504 |
|
| 505 |
let retained_view_entity = RetainedViewEntity::new(main_entity.into(), None, 0); |
| 506 |
|
| 507 |
colored_mesh2d_render_phases.prepare_for_new_frame(retained_view_entity); |
| 508 |
|
| 509 |
live_entities.insert(retained_view_entity); |
| 510 |
} |
| 511 |
|
| 512 |
|
| 513 |
colored_mesh2d_render_phases.retain(|camera_entity, _| live_entities.contains(camera_entity)); |
| 514 |
} |
| 515 |
|
| 516 |
|
| 517 |
pub fn extract_colored_mesh2d( |
| 518 |
mut commands: Commands, |
| 519 |
mut previous_len: Local<usize>, |
| 520 |
|
| 521 |
|
| 522 |
query: Extract< |
| 523 |
Query< |
| 524 |
( |
| 525 |
Entity, |
| 526 |
RenderEntity, |
| 527 |
&ViewVisibility, |
| 528 |
&GlobalTransform, |
| 529 |
&Mesh2d, |
| 530 |
), |
| 531 |
With<ColoredMesh2d>, |
| 532 |
>, |
| 533 |
>, |
| 534 |
mut render_mesh_instances: ResMut<RenderColoredMesh2dInstances>, |
| 535 |
) { |
| 536 |
let mut values = Vec::with_capacity(*previous_len); |
| 537 |
for (entity, render_entity, view_visibility, transform, handle) in &query { |
| 538 |
if !view_visibility.get() { |
| 539 |
continue; |
| 540 |
} |
| 541 |
|
| 542 |
let transforms = Mesh2dTransforms { |
| 543 |
world_from_local: transform.affine().into(), |
| 544 |
flags: MeshFlags::empty().bits(), |
| 545 |
}; |
| 546 |
|
| 547 |
values.push((render_entity, ColoredMesh2d)); |
| 548 |
render_mesh_instances.insert( |
| 549 |
entity.into(), |
| 550 |
RenderMesh2dInstance { |
| 551 |
mesh_asset_id: handle.0.id(), |
| 552 |
transforms, |
| 553 |
|
| 554 |
material_bindings_index: MaterialBindingId::default(), |
| 555 |
automatic_batching: false, |
| 556 |
tag: 0, |
| 557 |
}, |
| 558 |
); |
| 559 |
} |
| 560 |
*previous_len = values.len(); |
| 561 |
commands.try_insert_batch(values); |
| 562 |
} |
| 563 |
|
| 564 |
|
| 565 |
fn queue_colored_mesh2d( |
| 566 |
transparent_draw_functions: Res<DrawFunctions<TransparentColoredMesh2d>>, |
| 567 |
colored_mesh2d_pipeline: Res<ColoredMesh2dPipeline>, |
| 568 |
mut pipelines: ResMut<SpecializedRenderPipelines<ColoredMesh2dPipeline>>, |
| 569 |
pipeline_cache: Res<PipelineCache>, |
| 570 |
render_meshes: Res<RenderAssets<RenderMesh>>, |
| 571 |
render_mesh_instances: Res<RenderColoredMesh2dInstances>, |
| 572 |
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<TransparentColoredMesh2d>>, |
| 573 |
views: Query<(&RenderVisibleEntities, &ExtractedView, &Msaa)>, |
| 574 |
) { |
| 575 |
if render_mesh_instances.is_empty() { |
| 576 |
return; |
| 577 |
} |
| 578 |
|
| 579 |
|
| 580 |
for (visible_entities, view, msaa) in &views { |
| 581 |
let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) |
| 582 |
else { |
| 583 |
continue; |
| 584 |
}; |
| 585 |
|
| 586 |
let draw_colored_mesh2d = transparent_draw_functions |
| 587 |
.read() |
| 588 |
.id::<DrawTransparentColoredMesh2d>(); |
| 589 |
|
| 590 |
let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples()) |
| 591 |
| Mesh2dPipelineKey::from_target_format(view.target_format); |
| 592 |
|
| 593 |
|
| 594 |
let Some(visible_entities) = visible_entities.get::<Mesh2d>() else { |
| 595 |
continue; |
| 596 |
}; |
| 597 |
for (render_entity, visible_entity) in visible_entities.iter_visible() { |
| 598 |
if let Some(mesh_instance) = render_mesh_instances.get(visible_entity) { |
| 599 |
let mesh2d_handle = mesh_instance.mesh_asset_id; |
| 600 |
let mesh2d_transforms = &mesh_instance.transforms; |
| 601 |
|
| 602 |
let mut mesh2d_key = mesh_key; |
| 603 |
let Some(mesh) = render_meshes.get(mesh2d_handle) else { |
| 604 |
continue; |
| 605 |
}; |
| 606 |
mesh2d_key |= Mesh2dPipelineKey::from( |
| 607 |
BaseMeshPipelineKey::from_primitive_topology_and_strip_index( |
| 608 |
mesh.primitive_topology(), |
| 609 |
mesh.index_format(), |
| 610 |
) |
| 611 |
.bits(), |
| 612 |
); |
| 613 |
|
| 614 |
let pipeline_id = |
| 615 |
pipelines.specialize(&pipeline_cache, &colored_mesh2d_pipeline, mesh2d_key); |
| 616 |
|
| 617 |
let mesh_z = mesh2d_transforms.world_from_local.translation.z; |
| 618 |
transparent_phase.add_retained(TransparentColoredMesh2d { |
| 619 |
entity: (*render_entity, *visible_entity), |
| 620 |
draw_function: draw_colored_mesh2d, |
| 621 |
pipeline: pipeline_id, |
| 622 |
|
| 623 |
|
| 624 |
sort_key: FloatOrd(mesh_z), |
| 625 |
|
| 626 |
batch_range: 0..1, |
| 627 |
extra_index: PhaseItemExtraIndex::None, |
| 628 |
indexed: mesh.indexed(), |
| 629 |
}); |
| 630 |
} |
| 631 |
} |
| 632 |
} |
| 633 |
} |
| 634 |
|
| 635 |
|
| 636 |
|
| 637 |
fn main_colored_transparent_pass_2d( |
| 638 |
world: &World, |
| 639 |
view: ViewQuery<( |
| 640 |
&ExtractedCamera, |
| 641 |
&ExtractedView, |
| 642 |
&ViewTarget, |
| 643 |
&ViewDepthStencilTexture, |
| 644 |
)>, |
| 645 |
transparent_phases: Res<ViewSortedRenderPhases<TransparentColoredMesh2d>>, |
| 646 |
mut ctx: RenderContext, |
| 647 |
) { |
| 648 |
let view_entity = view.entity(); |
| 649 |
let (camera, extracted_view, target, depth) = view.into_inner(); |
| 650 |
|
| 651 |
let Some(transparent_phase) = transparent_phases.get(&extracted_view.retained_view_entity) |
| 652 |
else { |
| 653 |
return; |
| 654 |
}; |
| 655 |
|
| 656 |
#[cfg(feature = "trace")] |
| 657 |
let _span = info_span!("main_colored_transparent_pass_2d").entered(); |
| 658 |
|
| 659 |
let diagnostics = ctx.diagnostic_recorder(); |
| 660 |
let diagnostics = diagnostics.as_deref(); |
| 661 |
|
| 662 |
let color_attachments = [Some(target.get_color_attachment())]; |
| 663 |
|
| 664 |
|
| 665 |
|
| 666 |
|
| 667 |
|
| 668 |
|
| 669 |
let depth_stencil_attachment = Some(depth.get_attachment(StoreOp::Store)); |
| 670 |
|
| 671 |
{ |
| 672 |
let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor { |
| 673 |
label: Some("main_colored_transparent_pass_2d"), |
| 674 |
color_attachments: &color_attachments, |
| 675 |
depth_stencil_attachment, |
| 676 |
timestamp_writes: None, |
| 677 |
occlusion_query_set: None, |
| 678 |
multiview_mask: None, |
| 679 |
}); |
| 680 |
let pass_span = diagnostics.pass_span(&mut render_pass, "main_colored_transparent_pass_2d"); |
| 681 |
|
| 682 |
if let Some(viewport) = camera.viewport.as_ref() { |
| 683 |
render_pass.set_camera_viewport(viewport); |
| 684 |
} |
| 685 |
|
| 686 |
if !transparent_phase.items.is_empty() { |
| 687 |
#[cfg(feature = "trace")] |
| 688 |
let _transparent_span = info_span!("colored_transparent_main_pass_2d").entered(); |
| 689 |
if let Err(err) = transparent_phase.render(&mut render_pass, world, view_entity) { |
| 690 |
error!( |
| 691 |
"Error encountered while rendering the colored transparent 2D phase {err:?}" |
| 692 |
); |
| 693 |
} |
| 694 |
} |
| 695 |
|
| 696 |
pass_span.end(&mut render_pass); |
| 697 |
} |
| 698 |
} |
| 699 |
|
| 700 |
|
| 701 |
|
| 702 |
|
| 703 |
|
| 704 |
struct SetColoredMesh2dBindGroup<const I: usize>; |
| 705 |
|
| 706 |
impl<P, const I: usize> RenderCommand<P> for SetColoredMesh2dBindGroup<I> |
| 707 |
where |
| 708 |
P: PhaseItem, |
| 709 |
{ |
| 710 |
type Param = ( |
| 711 |
SRes<Mesh2dBindGroup>, |
| 712 |
SRes<RenderColoredMesh2dInstances>, |
| 713 |
SRes<MeshAllocator>, |
| 714 |
SRes<MeshMetadataFallbackBuffer>, |
| 715 |
); |
| 716 |
type ViewQuery = (); |
| 717 |
type ItemQuery = (); |
| 718 |
|
| 719 |
#[inline] |
| 720 |
fn render<'w>( |
| 721 |
item: &P, |
| 722 |
_view: (), |
| 723 |
_item_query: Option<()>, |
| 724 |
(mesh2d_bind_group, render_mesh2d_instances, mesh_allocator,metadata_fallback_buffer): SystemParamItem< |
| 725 |
'w, |
| 726 |
'_, |
| 727 |
Self::Param, |
| 728 |
>, |
| 729 |
pass: &mut TrackedRenderPass<'w>, |
| 730 |
) -> RenderCommandResult { |
| 731 |
let render_mesh2d_instances = render_mesh2d_instances.into_inner(); |
| 732 |
let mesh_allocator = mesh_allocator.into_inner(); |
| 733 |
let mesh2d_bind_group = mesh2d_bind_group.into_inner(); |
| 734 |
|
| 735 |
let Some(RenderMesh2dInstance { mesh_asset_id, .. }) = |
| 736 |
render_mesh2d_instances.get(&item.main_entity()) |
| 737 |
else { |
| 738 |
return RenderCommandResult::Skip; |
| 739 |
}; |
| 740 |
let metadata_slab_id = mesh_allocator |
| 741 |
.key_to_slab |
| 742 |
.get(&bevy_render::mesh::allocator::MeshAllocationKey::new( |
| 743 |
*mesh_asset_id, |
| 744 |
bevy_render::mesh::allocator::ElementClass::Metadata, |
| 745 |
)) |
| 746 |
.cloned() |
| 747 |
.unwrap_or(metadata_fallback_buffer.slab_id); |
| 748 |
let Some(bind_group) = &mesh2d_bind_group.value.get(&metadata_slab_id) else { |
| 749 |
return RenderCommandResult::Failure( |
| 750 |
"The mesh2d bind group wasn't set in the render phase.", |
| 751 |
); |
| 752 |
}; |
| 753 |
|
| 754 |
let mut dynamic_offsets: [u32; 1] = Default::default(); |
| 755 |
let mut offset_count = 0; |
| 756 |
if let PhaseItemExtraIndex::DynamicOffset(dynamic_offset) = item.extra_index() { |
| 757 |
dynamic_offsets[offset_count] = dynamic_offset; |
| 758 |
offset_count += 1; |
| 759 |
} |
| 760 |
pass.set_bind_group(I, bind_group, &dynamic_offsets[..offset_count]); |
| 761 |
RenderCommandResult::Success |
| 762 |
} |
| 763 |
} |
| 764 |
|
| 765 |
|
| 766 |
struct DrawColoredMesh2d; |
| 767 |
|
| 768 |
impl<P: PhaseItem> RenderCommand<P> for DrawColoredMesh2d { |
| 769 |
type Param = ( |
| 770 |
SRes<RenderAssets<RenderMesh>>, |
| 771 |
SRes<RenderColoredMesh2dInstances>, |
| 772 |
SRes<MeshAllocator>, |
| 773 |
); |
| 774 |
type ViewQuery = (); |
| 775 |
type ItemQuery = (); |
| 776 |
|
| 777 |
#[inline] |
| 778 |
fn render<'w>( |
| 779 |
item: &P, |
| 780 |
_view: (), |
| 781 |
_item_query: Option<()>, |
| 782 |
(meshes, render_mesh2d_instances, mesh_allocator): SystemParamItem<'w, '_, Self::Param>, |
| 783 |
pass: &mut TrackedRenderPass<'w>, |
| 784 |
) -> RenderCommandResult { |
| 785 |
let meshes = meshes.into_inner(); |
| 786 |
let render_mesh2d_instances = render_mesh2d_instances.into_inner(); |
| 787 |
let mesh_allocator = mesh_allocator.into_inner(); |
| 788 |
|
| 789 |
let Some(RenderMesh2dInstance { mesh_asset_id, .. }) = |
| 790 |
render_mesh2d_instances.get(&item.main_entity()) |
| 791 |
else { |
| 792 |
return RenderCommandResult::Skip; |
| 793 |
}; |
| 794 |
let Some(gpu_mesh) = meshes.get(*mesh_asset_id) else { |
| 795 |
return RenderCommandResult::Skip; |
| 796 |
}; |
| 797 |
let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(mesh_asset_id) else { |
| 798 |
return RenderCommandResult::Skip; |
| 799 |
}; |
| 800 |
|
| 801 |
pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..)); |
| 802 |
|
| 803 |
let batch_range = item.batch_range(); |
| 804 |
match &gpu_mesh.buffer_info { |
| 805 |
RenderMeshBufferInfo::Indexed { |
| 806 |
index_format, |
| 807 |
count, |
| 808 |
} => { |
| 809 |
let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(mesh_asset_id) |
| 810 |
else { |
| 811 |
return RenderCommandResult::Skip; |
| 812 |
}; |
| 813 |
|
| 814 |
pass.set_index_buffer(index_buffer_slice.buffer.slice(..), *index_format); |
| 815 |
|
| 816 |
pass.draw_indexed( |
| 817 |
index_buffer_slice.range.start..(index_buffer_slice.range.start + count), |
| 818 |
vertex_buffer_slice.range.start as i32, |
| 819 |
batch_range.clone(), |
| 820 |
); |
| 821 |
} |
| 822 |
RenderMeshBufferInfo::NonIndexed => { |
| 823 |
pass.draw(vertex_buffer_slice.range, batch_range.clone()); |
| 824 |
} |
| 825 |
} |
| 826 |
RenderCommandResult::Success |
| 827 |
} |
| 828 |
} |