From 30bd641af46e3ac6f4ec9fab514e1a80b7a54329 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Fri, 13 Dec 2024 21:35:39 +0000 Subject: [PATCH] box-shadow clipping fix (#16790) # Objective Instead of clipping the non-visable sections of box-shadows, the shadow is scaled to fit into the remaining area after clipping because the normalized coordinates that are meant to border the unclipped subsection of the shadow are always set to `[Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y]`, ## Solution Calculate the coordinates for the corners of the visible area. ## Testing Test app: ```rust use bevy::color::palettes::css::RED; use bevy::color::palettes::css::WHITE; use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .run(); } fn setup(mut commands: Commands) { commands.spawn(Camera2d); commands .spawn(Node { ..Default::default() }) .with_children(|commands| { commands .spawn(( Node { width: Val::Px(100.), height: Val::Px(100.), margin: UiRect { left: Val::Px(100.), top: Val::Px(300.), ..Default::default() }, overflow: Overflow::clip(), ..Default::default() }, BackgroundColor(WHITE.into()), )) .with_children(|commands| { commands.spawn(( Node { position_type: PositionType::Absolute, left: Val::Px(50.), top: Val::Px(50.), width: Val::Px(100.), height: Val::Px(100.), ..Default::default() }, BackgroundColor(RED.into()), BoxShadow::from(ShadowStyle { x_offset: Val::ZERO, y_offset: Val::ZERO, spread_radius: Val::Px(50.), blur_radius: Val::Px(6.), ..Default::default() }), )); }); }); } ``` Main: bad_shadow This PR: clipped_shadow --- crates/bevy_ui/src/render/box_shadow.rs | 29 +++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ui/src/render/box_shadow.rs b/crates/bevy_ui/src/render/box_shadow.rs index ce741ce137ec7..40363bf386c48 100644 --- a/crates/bevy_ui/src/render/box_shadow.rs +++ b/crates/bevy_ui/src/render/box_shadow.rs @@ -217,7 +217,7 @@ impl SpecializedRenderPipeline for BoxShadowPipeline { pub struct ExtractedBoxShadow { pub stack_index: u32, pub transform: Mat4, - pub rect: Rect, + pub bounds: Vec2, pub clip: Option, pub camera_entity: Entity, pub color: LinearRgba, @@ -323,10 +323,7 @@ pub fn extract_shadows( transform: transform.compute_matrix() * Mat4::from_translation(offset.extend(0.)), color: drop_shadow.color.into(), - rect: Rect { - min: Vec2::ZERO, - max: shadow_size + 6. * blur_radius, - }, + bounds: shadow_size + 6. * blur_radius, clip: clip.map(|clip| clip.clip), camera_entity, radius, @@ -415,9 +412,7 @@ pub fn prepare_shadows( while item_index < ui_phase.items.len() { let item = &mut ui_phase.items[item_index]; if let Some(box_shadow) = extracted_shadows.box_shadows.get(item.entity()) { - let uinode_rect = box_shadow.rect; - - let rect_size = uinode_rect.size().extend(1.0); + let rect_size = box_shadow.bounds.extend(1.0); // Specify the corners of the node let positions = QUAD_VERTEX_POSITIONS @@ -479,7 +474,23 @@ pub fn prepare_shadows( box_shadow.radius.bottom_left, ]; - let uvs = [Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y]; + let uvs = [ + Vec2::new(positions_diff[0].x, positions_diff[0].y), + Vec2::new( + box_shadow.bounds.x + positions_diff[1].x, + positions_diff[1].y, + ), + Vec2::new( + box_shadow.bounds.x + positions_diff[2].x, + box_shadow.bounds.y + positions_diff[2].y, + ), + Vec2::new( + positions_diff[3].x, + box_shadow.bounds.y + positions_diff[3].y, + ), + ] + .map(|pos| pos / box_shadow.bounds); + for i in 0..4 { ui_meta.vertices.push(BoxShadowVertex { position: positions_clipped[i].into(),