bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
36.39k stars 3.59k forks source link

Support more complicated BoxShadows #16496

Open JMS55 opened 15 hours ago

JMS55 commented 15 hours ago

What problem does this solve or what need does it fill?

Allow us to do things like https://www.tyleo.com/html-glass.html.

What solution would you like?

ickshonpe commented 1 hour ago

Inset box shadows

I only included drop shadow support as I wanted to keep the initial PR really simple but inset shadows should be really easy to add. If there's anyone looking for an idea for a simple side project, a 3rd party inset shadow crate shouldn't take much effort. I might make one myself if I run out of bevy PR ideas.

Per-side and multiple box shadows

The plugin just draws blurred rects from a list so we have support for per-side and multiple box shadows already, sort of. That requires users to write their own component and extraction function though, which isn't great.

Clipping

UI clipping only supports right angled, axis-aligned rectangles. It doesn't consider border radius at all, I guess we need some of compositing to implement it properly?

Eliptical border radius

Another missing feature for shadows we need is elliptical border radius support. The box shadow plugin only supports uniform scaling of node's shadows because inon-uniform scaling would require eliptical border radius support to draw distorted curved borders.

API

Something like this maybe:

#[derive(Component, Default, Clone, Debug, Reflect)]
#[reflect(Component, Default)]
#[cfg_attr(
    feature = "serialize",
    derive(serde::Serialize, serde::Deserialize),
    reflect(Serialize, Deserialize)
)]
pub struct BoxShadow {
    /// Shadows drawn according to their order in the vector, from back to front
    shadows: Vec<ShadowStyle>,
}

// #[derive(Component, Copy, Clone, Debug, Default, PartialEq, Eq, Reflect)]
// #[reflect(Component, Default, Debug, PartialEq)]

#[derive(Copy, Clone, Debug, PartialEq, Reflect)]
pub enum ShadowStyle {
    /// Shadow drawn beneath a UI node.
    /// Position and size relative to the corresponding UI node.
    Drop {
        /// The shadow's color
        color: Color,
        /// Horizontal offset
        x_offset: Val,
        /// Vertical offset
        y_offset: Val,
        /// How much the shadow should spread outward.
        ///
        /// Negative values will make the shadow shrink inwards.
        /// Percentage values are based on the width of the UI node.
        spread_radius: Val,
        /// Blurriness of the shadow
        blur_radius: Val,
    },
    /// Shadow drawn beneath its corresponding UI node.
    /// Position is relative to its `Node` but with independent size and radius.
    DropFree {
        /// The shadow's color
        color: Color,
        /// Horizontal offset
        x_offset: Val,
        /// Vertical offset
        y_offset: Val,
        /// Shadow width,
        width: Val,
        /// Shadow height
        height: Val,
        /// radius of the corners
        radius: BorderRadius,
        /// Blurriness of the shadow
        blur_radius: Val,
    },
    /// Inner shadow drawn on top of the node and inside the node's padding box, starting at the border-padding edge.
    Inset {
        /// Horizontal offset.
        /// Negative values move the shadow to the left, positive to the right.
        x_offset: Val,
        /// Vertical offset.
        /// Negative values move the shadow to the left, positive to the right.
        y_offset: Val,
        /// Controls the size of the shadow. Positive values increase the size of the shadow,
        /// covering more of the padding box.
        spread_radius: Val,
        /// Blurriness of the shadow
        blur_radius: Val,
    },
}

impl Default for ShadowStyle {
    fn default() -> Self {
        Self::Drop {
            color: Color::BLACK,
            x_offset: Val::Percent(20.),
            y_offset: Val::Percent(20.),
            spread_radius: Val::ZERO,
            blur_radius: Val::Percent(10.),
        }
    }
}