Skip to content

Commit 6f33765

Browse files
committed
Add vertex color rendering from example
1 parent 084343f commit 6f33765

File tree

9 files changed

+327
-71
lines changed

9 files changed

+327
-71
lines changed

src/draw/colored_mesh.rs

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
use bevy::{
2+
core::FloatOrd,
3+
core_pipeline::Transparent2d,
4+
prelude::*,
5+
reflect::TypeUuid,
6+
render::{
7+
render_asset::RenderAssets,
8+
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
9+
render_resource::{
10+
BlendState, ColorTargetState, ColorWrites, Face, FragmentState, FrontFace,
11+
MultisampleState, PolygonMode, PrimitiveState, RenderPipelineCache,
12+
RenderPipelineDescriptor, SpecializedPipeline, SpecializedPipelines, TextureFormat,
13+
VertexAttribute, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode,
14+
},
15+
texture::BevyDefault,
16+
view::VisibleEntities,
17+
RenderApp, RenderStage,
18+
},
19+
sprite::{
20+
DrawMesh2d, Mesh2dHandle, Mesh2dPipeline, Mesh2dPipelineKey, Mesh2dUniform,
21+
SetMesh2dBindGroup, SetMesh2dViewBindGroup,
22+
},
23+
};
24+
use bevy_inspector_egui::Inspectable;
25+
26+
/// Handle to the custom shader with a unique random ID.
27+
pub const COLORED_MESH_SHADER_HANDLE: HandleUntyped =
28+
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 13518715925425351754);
29+
30+
/// A marker component for colored 2d meshes.
31+
#[derive(Debug, Default, Component, Inspectable)]
32+
pub struct ColoredMesh;
33+
34+
/// Bundle for easy construction of colored meshes.
35+
#[derive(Default, Bundle)]
36+
pub struct ColoredMeshBundle {
37+
colored_mesh: ColoredMesh,
38+
handle: Mesh2dHandle,
39+
transform: Transform,
40+
global_transform: GlobalTransform,
41+
visibility: Visibility,
42+
computed_visibility: ComputedVisibility,
43+
}
44+
45+
impl ColoredMeshBundle {
46+
/// Create a new bundle.
47+
pub fn new(position: Vec2, mesh: Handle<Mesh>) -> Self {
48+
Self {
49+
handle: Mesh2dHandle(mesh),
50+
transform: Transform::from_xyz(position.x, position.y, 0.0),
51+
..Default::default()
52+
}
53+
}
54+
}
55+
56+
/// Custom pipeline for 2d meshes with vertex colors.
57+
pub struct ColoredMeshPipeline {
58+
/// This pipeline wraps the standard [`Mesh2dPipeline`].
59+
mesh2d_pipeline: Mesh2dPipeline,
60+
}
61+
62+
impl FromWorld for ColoredMeshPipeline {
63+
fn from_world(world: &mut World) -> Self {
64+
Self {
65+
mesh2d_pipeline: Mesh2dPipeline::from_world(world),
66+
}
67+
}
68+
}
69+
70+
// We implement `SpecializedPipeline` to customize the default rendering from `Mesh2dPipeline`.
71+
impl SpecializedPipeline for ColoredMeshPipeline {
72+
type Key = Mesh2dPipelineKey;
73+
74+
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
75+
// Customize how to store the meshes' vertex attributes in the vertex buffer
76+
// Our meshes only have position and color
77+
let vertex_attributes = vec![
78+
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
79+
VertexAttribute {
80+
format: VertexFormat::Float32x3,
81+
// this offset is the size of the color attribute, which is stored first
82+
offset: 16,
83+
// position is available at location 0 in the shader
84+
shader_location: 0,
85+
},
86+
// Color
87+
VertexAttribute {
88+
format: VertexFormat::Float32x4,
89+
offset: 0,
90+
shader_location: 1,
91+
},
92+
];
93+
// This is the sum of the size of position and color attributes (12 + 16 = 28)
94+
let vertex_array_stride = 28;
95+
96+
RenderPipelineDescriptor {
97+
vertex: VertexState {
98+
// Use our custom shader
99+
shader: COLORED_MESH_SHADER_HANDLE.typed::<Shader>(),
100+
entry_point: "vertex".into(),
101+
shader_defs: Vec::new(),
102+
// Use our custom vertex buffer
103+
buffers: vec![VertexBufferLayout {
104+
array_stride: vertex_array_stride,
105+
step_mode: VertexStepMode::Vertex,
106+
attributes: vertex_attributes,
107+
}],
108+
},
109+
fragment: Some(FragmentState {
110+
// Use our custom shader
111+
shader: COLORED_MESH_SHADER_HANDLE.typed::<Shader>(),
112+
shader_defs: Vec::new(),
113+
entry_point: "fragment".into(),
114+
targets: vec![ColorTargetState {
115+
format: TextureFormat::bevy_default(),
116+
blend: Some(BlendState::ALPHA_BLENDING),
117+
write_mask: ColorWrites::ALL,
118+
}],
119+
}),
120+
// Use the two standard uniforms for 2d meshes
121+
layout: Some(vec![
122+
// Bind group 0 is the view uniform
123+
self.mesh2d_pipeline.view_layout.clone(),
124+
// Bind group 1 is the mesh uniform
125+
self.mesh2d_pipeline.mesh_layout.clone(),
126+
]),
127+
primitive: PrimitiveState {
128+
front_face: FrontFace::Ccw,
129+
cull_mode: Some(Face::Back),
130+
unclipped_depth: false,
131+
polygon_mode: PolygonMode::Fill,
132+
conservative: false,
133+
topology: key.primitive_topology(),
134+
strip_index_format: None,
135+
},
136+
depth_stencil: None,
137+
multisample: MultisampleState {
138+
count: key.msaa_samples(),
139+
mask: !0,
140+
alpha_to_coverage_enabled: false,
141+
},
142+
label: Some("colored_mesh_pipeline".into()),
143+
}
144+
}
145+
}
146+
147+
/// Specify how to render a colored 2d mesh.
148+
type DrawColoredMesh = (
149+
// Set the pipeline
150+
SetItemPipeline,
151+
// Set the view uniform as bind group 0
152+
SetMesh2dViewBindGroup<0>,
153+
// Set the mesh uniform as bind group 1
154+
SetMesh2dBindGroup<1>,
155+
// Draw the mesh
156+
DrawMesh2d,
157+
);
158+
159+
/// Plugin that renders [`ColoredMesh`]s.
160+
pub struct ColoredMeshPlugin;
161+
162+
impl Plugin for ColoredMeshPlugin {
163+
fn build(&self, app: &mut App) {
164+
// Load our custom shader
165+
let mut shaders = app.world.get_resource_mut::<Assets<Shader>>().unwrap();
166+
shaders.set_untracked(
167+
COLORED_MESH_SHADER_HANDLE,
168+
Shader::from_wgsl(include_str!("colored_mesh.wgsl")),
169+
);
170+
171+
// Register our custom draw function and pipeline, and add our render systems
172+
let render_app = app.get_sub_app_mut(RenderApp).unwrap();
173+
render_app
174+
.add_render_command::<Transparent2d, DrawColoredMesh>()
175+
.init_resource::<ColoredMeshPipeline>()
176+
.init_resource::<SpecializedPipelines<ColoredMeshPipeline>>()
177+
.add_system_to_stage(RenderStage::Extract, extract_colored_mesh)
178+
.add_system_to_stage(RenderStage::Queue, queue_colored_mesh);
179+
}
180+
}
181+
182+
/// Extract the [`ColoredMesh`] marker component into the render app
183+
pub fn extract_colored_mesh(
184+
mut commands: Commands,
185+
mut previous_len: Local<usize>,
186+
query: Query<(Entity, &ComputedVisibility), With<ColoredMesh>>,
187+
) {
188+
let mut values = Vec::with_capacity(*previous_len);
189+
for (entity, computed_visibility) in query.iter() {
190+
if !computed_visibility.is_visible {
191+
continue;
192+
}
193+
values.push((entity, (ColoredMesh,)));
194+
}
195+
*previous_len = values.len();
196+
commands.insert_or_spawn_batch(values);
197+
}
198+
199+
/// Queue the 2d meshes marked with [`ColoredMesh`] using our custom pipeline and draw function.
200+
#[allow(clippy::too_many_arguments)]
201+
pub fn queue_colored_mesh(
202+
transparent_draw_functions: Res<DrawFunctions<Transparent2d>>,
203+
colored_mesh_pipeline: Res<ColoredMeshPipeline>,
204+
mut pipelines: ResMut<SpecializedPipelines<ColoredMeshPipeline>>,
205+
mut pipeline_cache: ResMut<RenderPipelineCache>,
206+
msaa: Res<Msaa>,
207+
render_meshes: Res<RenderAssets<Mesh>>,
208+
colored_mesh: Query<(&Mesh2dHandle, &Mesh2dUniform), With<ColoredMesh>>,
209+
mut views: Query<(&VisibleEntities, &mut RenderPhase<Transparent2d>)>,
210+
) {
211+
if colored_mesh.is_empty() {
212+
return;
213+
}
214+
// Iterate each view (a camera is a view)
215+
for (visible_entities, mut transparent_phase) in views.iter_mut() {
216+
let draw_colored_mesh = transparent_draw_functions
217+
.read()
218+
.get_id::<DrawColoredMesh>()
219+
.unwrap();
220+
221+
let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples);
222+
223+
// Queue all entities visible to that view
224+
for visible_entity in &visible_entities.entities {
225+
if let Ok((mesh2d_handle, mesh2d_uniform)) = colored_mesh.get(*visible_entity) {
226+
// Get our specialized pipeline
227+
let mut mesh2d_key = mesh_key;
228+
if let Some(mesh) = render_meshes.get(&mesh2d_handle.0) {
229+
mesh2d_key |=
230+
Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology);
231+
}
232+
233+
let pipeline_id =
234+
pipelines.specialize(&mut pipeline_cache, &colored_mesh_pipeline, mesh2d_key);
235+
236+
let mesh_z = mesh2d_uniform.transform.w_axis.z;
237+
transparent_phase.add(Transparent2d {
238+
entity: *visible_entity,
239+
draw_function: draw_colored_mesh,
240+
pipeline: pipeline_id,
241+
// The 2d render items are sorted according to their z value before rendering,
242+
// in order to get correct transparency
243+
sort_key: FloatOrd(mesh_z),
244+
// This material is not batched
245+
batch_range: None,
246+
});
247+
}
248+
}
249+
}
250+
}

src/draw/colored_mesh.wgsl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Import the standard 2d mesh uniforms and set their bind groups
2+
#import bevy_sprite::mesh2d_view_bind_group
3+
[[group(0), binding(0)]]
4+
var<uniform> view: View;
5+
6+
#import bevy_sprite::mesh2d_struct
7+
[[group(1), binding(0)]]
8+
var<uniform> mesh: Mesh2d;
9+
10+
// The structure of the vertex buffer is as specified in `specialize()`
11+
struct Vertex {
12+
[[location(0)]] position: vec3<f32>;
13+
[[location(1)]] color: vec4<f32>;
14+
};
15+
16+
struct VertexOutput {
17+
// The vertex shader must set the on-screen position of the vertex
18+
[[builtin(position)]] clip_position: vec4<f32>;
19+
// We pass the vertex color to the framgent shader in location 0
20+
[[location(0)]] color: vec4<f32>;
21+
};
22+
23+
/// Entry point for the vertex shader.
24+
[[stage(vertex)]]
25+
fn vertex(vertex: Vertex) -> VertexOutput {
26+
var out: VertexOutput;
27+
// Project the world position of the mesh into screen position
28+
out.clip_position = view.view_proj * mesh.model * vec4<f32>(vertex.position, 1.0);
29+
out.color = vertex.color;
30+
31+
return out;
32+
}
33+
34+
// The input of the fragment shader must correspond to the output of the vertex shader for all `location`s
35+
struct FragmentInput {
36+
// The color is interpolated between vertices by default
37+
[[location(0)]] color: vec4<f32>;
38+
};
39+
40+
/// Entry point for the fragment shader.
41+
[[stage(fragment)]]
42+
fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
43+
return in.color;
44+
}

src/draw/mesh.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ fn convert_buffers_into_mesh(
185185
mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, vertices);
186186

187187
// Set the colors
188-
//mesh.set_attribute(Mesh::ATTRIBUTE_COLOR, colors);
188+
mesh.set_attribute(Mesh::ATTRIBUTE_COLOR, colors);
189189

190190
mesh
191191
}
@@ -304,7 +304,7 @@ impl<'l> Iterator for PathConvIter<'l> {
304304
/// Convert an SVG color to a Bevy color.
305305
fn svg_color_to_bevy(paint: &Paint, opacity: u8) -> [f32; 4] {
306306
return match paint {
307-
Paint::Color(color) => dbg!(Color::rgba_u8(color.red, color.green, color.blue, opacity)),
307+
Paint::Color(color) => Color::rgba_u8(color.red, color.green, color.blue, opacity),
308308
// We only support plain colors
309309
_ => Color::default(),
310310
}

src/draw/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
pub mod colored_mesh;
12
pub mod mesh;
23
pub mod svg;
34

45
use self::svg::SvgAssetLoader;
56
use bevy::prelude::{AddAsset, App, Msaa, Plugin};
7+
use colored_mesh::ColoredMeshPlugin;
68

79
/// The plugin to manage rendering.
810
pub struct DrawPlugin;
@@ -12,6 +14,7 @@ impl Plugin for DrawPlugin {
1214
// Smooth anti aliasing
1315
app.insert_resource(Msaa { samples: 4 })
1416
.init_asset_loader::<SvgAssetLoader>()
17+
.add_plugin(ColoredMeshPlugin)
1518
.add_startup_system(svg::setup);
1619
}
1720
}

src/draw/svg.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use anyhow::{Context, Error};
22
use bevy::{
33
asset::{AssetLoader, BoxedFuture, LoadContext, LoadedAsset},
4-
prelude::{AssetServer, Color, Commands, Handle, Mesh, Res},
4+
prelude::{AssetServer, Commands, Handle, Mesh, Res},
55
};
66
use bevy_inspector_egui::Inspectable;
7-
use usvg::{Options, Paint, Tree};
7+
use usvg::{Options, Tree};
88

99
#[derive(Debug, Clone, Inspectable)]
1010
pub struct AlliedCharacterSvg(#[inspectable(ignore)] pub Handle<Mesh>);

0 commit comments

Comments
 (0)