Skip to content

Commit

Permalink
Explicit color conversion methods (bevyengine#10321)
Browse files Browse the repository at this point in the history
# Objective

Closes bevyengine#10319 

## Changelog
* Added a new `Color::rgba_from_array([f32; 4]) -> Color` method.
* Added a new `Color::rgb_from_array([f32; 3]) -> Color` method.
* Added a new `Color::rgba_linear_from_array([f32; 4]) -> Color` method.
* Added a new `Color::rgb_linear_from_array([f32; 3]) -> Color` method.
* Added a new `Color::hsla_from_array([f32; 4]) -> Color` method.
* Added a new `Color::hsl_from_array([f32; 3]) -> Color` method.
* Added a new `Color::lcha_from_array([f32; 4]) -> Color` method.
* Added a new `Color::lch_from_array([f32; 3]) -> Color` method.
* Added a new `Color::rgba_to_vec4(&self) -> Vec4` method.
* Added a new `Color::rgba_to_array(&self) -> [f32; 4]` method.
* Added a new `Color::rgb_to_vec3(&self) -> Vec3` method.
* Added a new `Color::rgb_to_array(&self) -> [f32; 3]` method.
* Added a new `Color::rgba_linear_to_vec4(&self) -> Vec4` method.
* Added a new `Color::rgba_linear_to_array(&self) -> [f32; 4]` method.
* Added a new `Color::rgb_linear_to_vec3(&self) -> Vec3` method.
* Added a new `Color::rgb_linear_to_array(&self) -> [f32; 3]` method.
* Added a new `Color::hsla_to_vec4(&self) -> Vec4` method.
* Added a new `Color::hsla_to_array(&self) -> [f32; 4]` method.
* Added a new `Color::hsl_to_vec3(&self) -> Vec3` method.
* Added a new `Color::hsl_to_array(&self) -> [f32; 3]` method.
* Added a new `Color::lcha_to_vec4(&self) -> Vec4` method.
* Added a new `Color::lcha_to_array(&self) -> [f32; 4]` method.
* Added a new `Color::lch_to_vec3(&self) -> Vec3` method.
* Added a new `Color::lch_to_array(&self) -> [f32; 3]` method.

## Migration Guide
`Color::from(Vec4)` is now `Color::rgba_from_array(impl Into<[f32; 4]>)`
`Vec4::from(Color)` is now `Color::rgba_to_vec4(&self)`

Before:
```rust
let color_vec4 = Vec4::new(0.5, 0.5, 0.5);
let color_from_vec4 = Color::from(color_vec4);

let color_array = [0.5, 0.5, 0.5];
let color_from_array = Color::from(color_array);
```
After:
```rust
let color_vec4 = Vec4::new(0.5, 0.5, 0.5);
let color_from_vec4 = Color::rgba_from_array(color_vec4);

let color_array = [0.5, 0.5, 0.5];
let color_from_array = Color::rgba_from_array(color_array);
```
  • Loading branch information
st0rmbtw authored and Ray Redondo committed Jan 9, 2024
1 parent 462dff1 commit 76c6bb0
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 93 deletions.
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

0 comments on commit 76c6bb0

Please sign in to comment.