Skip to content

Commit 7f908d0

Browse files
committed
Blend modes.
1 parent 8c8a2ba commit 7f908d0

File tree

5 files changed

+47
-44
lines changed

5 files changed

+47
-44
lines changed

crates/processing_pyo3/src/graphics.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ use pyo3::{
2121
use crate::glfw::GlfwContext;
2222
use crate::math::{extract_vec2, extract_vec3, extract_vec4};
2323

24-
// ---------------------------------------------------------------------------
25-
// BlendMode
26-
// ---------------------------------------------------------------------------
27-
2824
#[pyclass(name = "BlendMode")]
2925
#[derive(Clone)]
3026
pub struct PyBlendMode {

crates/processing_render/src/graphics.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,7 @@ impl ProcessingProjection {
132132

133133
impl CameraProjection for ProcessingProjection {
134134
fn get_clip_from_view(&self) -> Mat4 {
135-
// NOTE: near and far are swapped to invert the depth range from [0,1] to [1,0]
136-
// This is for interoperability with Bevy's reverse-Z depth pipeline.
135+
// near/far swapped for Bevy's reverse-Z depth
137136
Mat4::orthographic_rh(
138137
0.0,
139138
self.width,

crates/processing_render/src/material/custom.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::any::TypeId;
1+
use std::any::{Any, TypeId};
22
use std::borrow::Cow;
33
use std::sync::Arc;
44

@@ -11,7 +11,7 @@ wesl::wesl_pkg!(lygia);
1111

1212
use bevy::{
1313
asset::{AsAssetId, AssetEventSystems},
14-
core_pipeline::core_3d::{Opaque3d, Transparent3d},
14+
core_pipeline::core_3d::Opaque3d,
1515
ecs::system::{
1616
SystemParamItem,
1717
lifetimeless::{SRes, SResMut},
@@ -50,8 +50,6 @@ use bevy_naga_reflect::dynamic_shader::DynamicShader;
5050

5151
use bevy::shader::Shader as ShaderAsset;
5252

53-
use std::any::Any;
54-
5553
use crate::material::MaterialValue;
5654
use crate::render::material::UntypedMaterial;
5755
use processing_core::config::{Config, ConfigKey};
@@ -86,7 +84,7 @@ pub struct CustomMaterial {
8684
pub shader_handle: Handle<ShaderAsset>,
8785
pub has_vertex: bool,
8886
pub has_fragment: bool,
89-
pub blend_state: Option<bevy::render::render_resource::BlendState>,
87+
pub blend_state: Option<BlendState>,
9088
}
9189

9290
#[derive(Component)]

crates/processing_render/src/render/command.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ impl From<u8> for BlendMode {
7575
}
7676
}
7777

78-
/// Convert a u8 to a WebGPU BlendFactor.
7978
pub fn blend_factor_from_u8(v: u8) -> BlendFactor {
8079
match v {
8180
0 => BlendFactor::Zero,
@@ -93,7 +92,6 @@ pub fn blend_factor_from_u8(v: u8) -> BlendFactor {
9392
}
9493
}
9594

96-
/// Convert a u8 to a WebGPU BlendOperation.
9795
pub fn blend_op_from_u8(v: u8) -> BlendOperation {
9896
match v {
9997
0 => BlendOperation::Add,
@@ -105,7 +103,6 @@ pub fn blend_op_from_u8(v: u8) -> BlendOperation {
105103
}
106104
}
107105

108-
/// Build a BlendState from individual component parameters.
109106
pub fn custom_blend_state(
110107
color_src: u8,
111108
color_dst: u8,
@@ -150,8 +147,7 @@ impl BlendMode {
150147
}
151148
}
152149

153-
/// Convert to a WebGPU BlendState, matching Processing's OpenGL blend configurations.
154-
/// Returns None for the default Blend mode (standard alpha blending handled by AlphaMode).
150+
/// Returns None for the default Blend mode, letting AlphaMode handle blending.
155151
pub fn to_blend_state(self) -> Option<BlendState> {
156152
use BlendFactor::*;
157153
use BlendOperation::*;

crates/processing_render/src/render/mod.rs

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -358,12 +358,16 @@ pub fn flush_draw_commands(
358358

359359
let material_key = material_key_with_fill(&state);
360360
let material_handle = match &material_key {
361-
MaterialKey::Custom { entity: mat_entity, .. } => {
362-
let Some(handle) = p_material_handles.get(*mat_entity).ok() else {
361+
MaterialKey::Custom { entity: mat_entity, blend_state } => {
362+
let Some(untyped) = p_material_handles.get(*mat_entity).ok() else {
363363
warn!("Could not find material for entity {:?}", mat_entity);
364364
continue;
365365
};
366-
handle.0.clone()
366+
clone_custom_material_with_blend(
367+
&mut res.custom_materials,
368+
&untyped.0,
369+
*blend_state,
370+
)
367371
}
368372
_ => material_key.to_material(&mut res.materials),
369373
};
@@ -478,24 +482,11 @@ fn spawn_mesh(
478482
warn!("Custom material entity {:?} not found", entity);
479483
return;
480484
};
481-
match *blend_state {
482-
// No blend override — use the original handle
483-
None => untyped.0.clone(),
484-
// Blend override — clone the custom material with the blend state
485-
Some(bs) => {
486-
if let Ok(handle) = untyped.0.clone().try_typed::<CustomMaterial>() {
487-
if let Some(original) = res.custom_materials.get(&handle) {
488-
let mut variant = original.clone();
489-
variant.blend_state = Some(bs);
490-
res.custom_materials.add(variant).untyped()
491-
} else {
492-
untyped.0.clone()
493-
}
494-
} else {
495-
untyped.0.clone()
496-
}
497-
}
498-
}
485+
clone_custom_material_with_blend(
486+
&mut res.custom_materials,
487+
&untyped.0,
488+
*blend_state,
489+
)
499490
}
500491
_ => key.to_material(&mut res.materials),
501492
};
@@ -512,8 +503,6 @@ fn spawn_mesh(
512503
fn needs_batch(batch: &BatchState, state: &RenderState, material_key: &MaterialKey) -> bool {
513504
let material_changed = batch.material_key.as_ref() != Some(material_key);
514505
let transform_changed = batch.transform != state.transform.current();
515-
// When a custom blend mode is active, each shape needs its own draw call
516-
// so that shapes composite against each other in draw order.
517506
let requires_separate_draws = state.blend_state.is_some();
518507
material_changed || transform_changed || requires_separate_draws
519508
}
@@ -531,6 +520,27 @@ fn start_batch(
531520
batch.current_mesh = Some(empty_mesh());
532521
}
533522

523+
fn clone_custom_material_with_blend(
524+
custom_materials: &mut Assets<CustomMaterial>,
525+
original: &UntypedHandle,
526+
blend_state: Option<BlendState>,
527+
) -> UntypedHandle {
528+
match blend_state {
529+
None => original.clone(),
530+
Some(bs) => {
531+
let Ok(handle) = original.clone().try_typed::<CustomMaterial>() else {
532+
return original.clone();
533+
};
534+
let Some(original_mat) = custom_materials.get(&handle) else {
535+
return original.clone();
536+
};
537+
let mut variant = original_mat.clone();
538+
variant.blend_state = Some(bs);
539+
custom_materials.add(variant).untyped()
540+
}
541+
}
542+
}
543+
534544
fn material_key_with_color(
535545
key: &MaterialKey,
536546
color: Color,
@@ -641,13 +651,17 @@ fn add_shape3d(
641651
let mesh_handle = res.meshes.add(mesh);
642652
let fill_color = state.fill_color.unwrap_or(Color::WHITE);
643653
let material_handle = match &state.material_key {
644-
MaterialKey::Custom { entity, .. } => match material_handles.get(*entity) {
645-
Ok(handle) => handle.0.clone(),
646-
Err(_) => {
654+
MaterialKey::Custom { entity, .. } => {
655+
let Some(untyped) = material_handles.get(*entity).ok() else {
647656
warn!("Custom material entity {:?} not found", entity);
648657
return;
649-
}
650-
},
658+
};
659+
clone_custom_material_with_blend(
660+
&mut res.custom_materials,
661+
&untyped.0,
662+
state.blend_state,
663+
)
664+
}
651665
// TODO: in 2d, we use vertex colors. `to_material` becomes complicated if we also encode
652666
// a base color in the material, so for simplicity we just create a new material here
653667
// that is unlit and uses the fill color as the base color

0 commit comments

Comments
 (0)