Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicit color conversion methods #10321

Merged
merged 9 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/bevy_gltf/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -998,7 +998,7 @@ fn load_node(
gltf::khr_lights_punctual::Kind::Directional => {
let mut entity = parent.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
color: Color::from(light.color()),
color: Color::rgb_from_array(light.color()),
// NOTE: KHR_punctual_lights defines the intensity units for directional
// lights in lux (lm/m^2) which is what we need.
illuminance: light.intensity(),
Expand All @@ -1018,7 +1018,7 @@ fn load_node(
gltf::khr_lights_punctual::Kind::Point => {
let mut entity = parent.spawn(PointLightBundle {
point_light: PointLight {
color: Color::from(light.color()),
color: Color::rgb_from_array(light.color()),
// NOTE: KHR_punctual_lights defines the intensity units for point lights in
// candela (lm/sr) which is luminous intensity and we need luminous power.
// For a point light, luminous power = 4 * pi * luminous intensity
Expand All @@ -1044,7 +1044,7 @@ fn load_node(
} => {
let mut entity = parent.spawn(SpotLightBundle {
spot_light: SpotLight {
color: Color::from(light.color()),
color: Color::rgb_from_array(light.color()),
// NOTE: KHR_punctual_lights defines the intensity units for spot lights in
// candela (lm/sr) which is luminous intensity and we need luminous power.
// For a spot light, we map luminous power = 4 * pi * luminous intensity
Expand Down
243 changes: 155 additions & 88 deletions crates/bevy_render/src/color/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub use colorspace::*;
use bevy_math::{Vec3, Vec4};
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use serde::{Deserialize, Serialize};
use std::ops::{Add, AddAssign, Mul, MulAssign};
use std::ops::{Add, Mul, MulAssign};
use thiserror::Error;

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)]
Expand Down Expand Up @@ -1102,69 +1102,184 @@ impl Color {
}
}
}
}

impl Default for Color {
fn default() -> Self {
Color::WHITE
/// New `Color` from `[f32; 4]` (or a type that can be converted into them) with RGB representation in sRGB colorspace.
#[inline]
pub fn rgba_from_array(arr: impl Into<[f32; 4]>) -> Self {
let [r, g, b, a]: [f32; 4] = arr.into();
Color::rgba(r, g, b, a)
}
}

impl AddAssign<Color> for Color {
fn add_assign(&mut self, rhs: Color) {
match self {
/// New `Color` from `[f32; 3]` (or a type that can be converted into them) with RGB representation in sRGB colorspace.
#[inline]
pub fn rgb_from_array(arr: impl Into<[f32; 3]>) -> Self {
let [r, g, b]: [f32; 3] = arr.into();
Color::rgb(r, g, b)
}

/// New `Color` from `[f32; 4]` (or a type that can be converted into them) with RGB representation in linear RGB colorspace.
#[inline]
pub fn rgba_linear_from_array(arr: impl Into<[f32; 4]>) -> Self {
let [r, g, b, a]: [f32; 4] = arr.into();
Color::rgba_linear(r, g, b, a)
}

/// New `Color` from `[f32; 3]` (or a type that can be converted into them) with RGB representation in linear RGB colorspace.
#[inline]
pub fn rgb_linear_from_array(arr: impl Into<[f32; 3]>) -> Self {
let [r, g, b]: [f32; 3] = arr.into();
Color::rgb_linear(r, g, b)
}

/// New `Color` from `[f32; 4]` (or a type that can be converted into them) with HSL representation in sRGB colorspace.
#[inline]
pub fn hsla_from_array(arr: impl Into<[f32; 4]>) -> Self {
let [h, s, l, a]: [f32; 4] = arr.into();
Color::hsla(h, s, l, a)
}

/// New `Color` from `[f32; 3]` (or a type that can be converted into them) with HSL representation in sRGB colorspace.
#[inline]
pub fn hsl_from_array(arr: impl Into<[f32; 3]>) -> Self {
let [h, s, l]: [f32; 3] = arr.into();
Color::hsl(h, s, l)
}

/// New `Color` from `[f32; 4]` (or a type that can be converted into them) with LCH representation in sRGB colorspace.
#[inline]
pub fn lcha_from_array(arr: impl Into<[f32; 4]>) -> Self {
let [l, c, h, a]: [f32; 4] = arr.into();
Color::lcha(l, c, h, a)
}

/// New `Color` from `[f32; 3]` (or a type that can be converted into them) with LCH representation in sRGB colorspace.
#[inline]
pub fn lch_from_array(arr: impl Into<[f32; 3]>) -> Self {
let [l, c, h]: [f32; 3] = arr.into();
Color::lch(l, c, h)
}

/// Convert `Color` to RGBA and return as `Vec4`.
#[inline]
pub fn rgba_to_vec4(&self) -> Vec4 {
let color = self.as_rgba();
match color {
Color::Rgba {
red,
green,
blue,
alpha,
} => {
let rhs = rhs.as_rgba_f32();
*red += rhs[0];
*green += rhs[1];
*blue += rhs[2];
*alpha += rhs[3];
}
} => Vec4::new(red, green, blue, alpha),
_ => unreachable!(),
}
}

/// Convert `Color` to RGBA and return as `Vec3`.
#[inline]
pub fn rgb_to_vec3(&self) -> Vec3 {
let color = self.as_rgba();
match color {
Color::Rgba {
red, green, blue, ..
} => Vec3::new(red, green, blue),
_ => unreachable!(),
}
}

/// Convert `Color` to linear RGBA and return as `Vec4`.
#[inline]
pub fn rgba_linear_to_vec4(&self) -> Vec4 {
let color = self.as_rgba_linear();
match color {
Color::RgbaLinear {
red,
green,
blue,
alpha,
} => {
let rhs = rhs.as_linear_rgba_f32();
*red += rhs[0];
*green += rhs[1];
*blue += rhs[2];
*alpha += rhs[3];
}
} => Vec4::new(red, green, blue, alpha),
_ => unreachable!(),
}
}

/// Convert `Color` to linear RGBA and return as `Vec3`.
#[inline]
pub fn rgb_linear_to_vec3(&self) -> Vec3 {
let color = self.as_rgba_linear();
match color {
Color::RgbaLinear {
red, green, blue, ..
} => Vec3::new(red, green, blue),
_ => unreachable!(),
}
}

/// Convert `Color` to HSLA and return as `Vec4`.
#[inline]
pub fn hsla_to_vec4(&self) -> Vec4 {
let color = self.as_hsla();
match color {
Color::Hsla {
hue,
saturation,
lightness,
alpha,
} => {
let rhs = rhs.as_hsla_f32();
*hue += rhs[0];
*saturation += rhs[1];
*lightness += rhs[2];
*alpha += rhs[3];
}
} => Vec4::new(hue, saturation, lightness, alpha),
_ => unreachable!(),
}
}

/// Convert `Color` to HSLA and return as `Vec3`.
#[inline]
pub fn hsl_to_vec3(&self) -> Vec3 {
let color = self.as_hsla();
match color {
Color::Hsla {
hue,
saturation,
lightness,
..
} => Vec3::new(hue, saturation, lightness),
_ => unreachable!(),
}
}

/// Convert `Color` to LCHA and return as `Vec4`.
#[inline]
pub fn lcha_to_vec4(&self) -> Vec4 {
let color = self.as_lcha();
match color {
Color::Lcha {
lightness,
chroma,
hue,
alpha,
} => {
let rhs = rhs.as_lcha_f32();
*lightness += rhs[0];
*chroma += rhs[1];
*hue += rhs[2];
*alpha += rhs[3];
}
} => Vec4::new(lightness, chroma, hue, alpha),
_ => unreachable!(),
}
}

/// Convert `Color` to LCHA and return as `Vec3`.
#[inline]
pub fn lch_to_vec3(&self) -> Vec3 {
let color = self.as_lcha();
match color {
Color::Lcha {
lightness,
chroma,
hue,
..
} => Vec3::new(lightness, chroma, hue),
_ => unreachable!(),
}
}
}

impl Default for Color {
fn default() -> Self {
Color::WHITE
}
}

impl Add<Color> for Color {
type Output = Color;

Expand Down Expand Up @@ -1219,7 +1334,6 @@ impl Add<Color> for Color {
alpha,
} => {
let rhs = rhs.as_lcha_f32();

Color::Lcha {
lightness: lightness + rhs[0],
chroma: chroma + rhs[1],
Expand All @@ -1231,53 +1345,6 @@ impl Add<Color> for Color {
}
}

impl AddAssign<Vec4> for Color {
fn add_assign(&mut self, rhs: Vec4) {
let rhs: Color = rhs.into();
*self += rhs;
}
}

impl Add<Vec4> for Color {
type Output = Color;

fn add(self, rhs: Vec4) -> Self::Output {
let rhs: Color = rhs.into();
self + rhs
}
}

impl From<Color> for [f32; 4] {
fn from(color: Color) -> Self {
color.as_rgba_f32()
}
}

impl From<[f32; 4]> for Color {
fn from([r, g, b, a]: [f32; 4]) -> Self {
Color::rgba(r, g, b, a)
}
}

impl From<[f32; 3]> for Color {
fn from([r, g, b]: [f32; 3]) -> Self {
Color::rgb(r, g, b)
}
}

impl From<Color> for Vec4 {
fn from(color: Color) -> Self {
let color: [f32; 4] = color.into();
Vec4::new(color[0], color[1], color[2], color[3])
}
}

impl From<Vec4> for Color {
fn from(vec4: Vec4) -> Self {
Color::rgba(vec4.x, vec4.y, vec4.z, vec4.w)
}
}

impl From<Color> for wgpu::Color {
fn from(color: Color) -> Self {
if let Color::RgbaLinear {
Expand Down Expand Up @@ -1909,15 +1976,15 @@ mod tests {
#[test]
fn conversions_vec4() {
let starting_vec4 = Vec4::new(0.4, 0.5, 0.6, 1.0);
let starting_color = Color::from(starting_vec4);
let starting_color = Color::rgba_from_array(starting_vec4);

assert_eq!(starting_vec4, Vec4::from(starting_color));
assert_eq!(starting_vec4, starting_color.rgba_to_vec4());

let transformation = Vec4::new(0.5, 0.5, 0.5, 1.0);

assert_eq!(
starting_color * transformation,
Color::from(starting_vec4 * transformation),
Color::rgba_from_array(starting_vec4 * transformation)
);
}

Expand Down
4 changes: 2 additions & 2 deletions examples/ui/ui_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn update(time: Res<Time>, mut ui_materials: ResMut<Assets<CustomUiMaterial>>) {
for (_, material) in ui_materials.iter_mut() {
// rainbow color effect
let new_color = Color::hsl((time.elapsed_seconds() * 60.0) % 360.0, 1., 0.5);
material.color = new_color.into();
material.color = new_color.rgba_to_vec4();
}
}

Expand Down Expand Up @@ -45,7 +45,7 @@ fn setup(mut commands: Commands, mut ui_materials: ResMut<Assets<CustomUiMateria
..default()
},
material: ui_materials.add(CustomUiMaterial {
color: Color::WHITE.into(),
color: Color::WHITE.rgba_to_vec4(),
}),
..default()
});
Expand Down