Use LinearRgba in bevy_gizmos
internals (#12128)
# Objective This PR arose as part of the migration process for `bevy_color`: see #12056. While examining how `bevy_gizmos` stores color types internally, I found that rather than storing a `Color` internally, it actually stores a `[f32;4]` for a linear RGB, type aliased to a `ColorItem`. While we don't *have* to clean this up to complete the migration, now that we have explicit strong typing for linear color types we should use them rather than replicating this idea throughout the codebase. ## Solution - Added `LinearRgba::NAN`, for when you want to do cursed rendering things. - Replaced the internal color representation in `bevy_gizmo`: this was `ColorItem`, but is now `LinearRgba`. - `LinearRgba` is now `Pod`, enabling us to use the same fast `bytemuck` bit twiddling tricks that we were using before. This requires: 1. Forcing `LinearRgba` to be `repr(C)` 2. Implementing `Zeroable`, and unsafe trait which defines what the struct looks like when all values are zero. 3. Implementing `Pod`, a marker trait with stringent safety requirements that is required for "plain old data". --------- Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
This commit is contained in:
parent
abc6f983ce
commit
5860e01432
@ -13,6 +13,7 @@ bevy_math = { path = "../bevy_math", version = "0.14.0-dev" }
|
|||||||
bevy_reflect = { path = "../bevy_reflect", version = "0.14.0-dev", features = [
|
bevy_reflect = { path = "../bevy_reflect", version = "0.14.0-dev", features = [
|
||||||
"bevy",
|
"bevy",
|
||||||
] }
|
] }
|
||||||
|
bytemuck = "1"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
wgpu = { version = "0.19.1", default-features = false }
|
wgpu = { version = "0.19.1", default-features = false }
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
use crate::{color_difference::EuclideanDistance, Alpha, Luminance, Mix, StandardColor};
|
use crate::{color_difference::EuclideanDistance, Alpha, Luminance, Mix, StandardColor};
|
||||||
use bevy_math::Vec4;
|
use bevy_math::Vec4;
|
||||||
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
|
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Linear RGB color with alpha.
|
/// Linear RGB color with alpha.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)]
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)]
|
||||||
#[reflect(PartialEq, Serialize, Deserialize)]
|
#[reflect(PartialEq, Serialize, Deserialize)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct LinearRgba {
|
pub struct LinearRgba {
|
||||||
/// The red channel. [0.0, 1.0]
|
/// The red channel. [0.0, 1.0]
|
||||||
pub red: f32,
|
pub red: f32,
|
||||||
@ -44,6 +46,18 @@ impl LinearRgba {
|
|||||||
alpha: 0.0,
|
alpha: 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// An invalid color.
|
||||||
|
///
|
||||||
|
/// This type can be used to represent an invalid color value;
|
||||||
|
/// in some rendering applications the color will be ignored,
|
||||||
|
/// enabling performant hacks like hiding lines by setting their color to `INVALID`.
|
||||||
|
pub const NAN: Self = Self {
|
||||||
|
red: f32::NAN,
|
||||||
|
green: f32::NAN,
|
||||||
|
blue: f32::NAN,
|
||||||
|
alpha: f32::NAN,
|
||||||
|
};
|
||||||
|
|
||||||
/// Construct a new [`LinearRgba`] color from components.
|
/// Construct a new [`LinearRgba`] color from components.
|
||||||
pub const fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
|
pub const fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -279,6 +293,33 @@ impl encase::private::CreateFrom for LinearRgba {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Zeroable`] type is one whose bytes can be filled with zeroes while remaining valid.
|
||||||
|
///
|
||||||
|
/// SAFETY: [`LinearRgba`] is inhabited
|
||||||
|
/// SAFETY: [`LinearRgba`]'s all-zero bit pattern is a valid value
|
||||||
|
unsafe impl Zeroable for LinearRgba {
|
||||||
|
fn zeroed() -> Self {
|
||||||
|
LinearRgba {
|
||||||
|
red: 0.0,
|
||||||
|
green: 0.0,
|
||||||
|
blue: 0.0,
|
||||||
|
alpha: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`Pod`] trait is [`bytemuck`]'s marker for types that can be safely transmuted from a byte array.
|
||||||
|
///
|
||||||
|
/// It is intended to only be implemented for types which are "Plain Old Data".
|
||||||
|
///
|
||||||
|
/// SAFETY: [`LinearRgba`] is inhabited.
|
||||||
|
/// SAFETY: [`LinearRgba`] permits any bit value.
|
||||||
|
/// SAFETY: [`LinearRgba`] does not have padding bytes.
|
||||||
|
/// SAFETY: all of the fields of [`LinearRgba`] are [`Pod`], as f32 is [`Pod`].
|
||||||
|
/// SAFETY: [`LinearRgba`] is `repr(C)`
|
||||||
|
/// SAFETY: [`LinearRgba`] does not permit interior mutability.
|
||||||
|
unsafe impl Pod for LinearRgba {}
|
||||||
|
|
||||||
impl encase::ShaderSize for LinearRgba {}
|
impl encase::ShaderSize for LinearRgba {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -17,6 +17,7 @@ webgpu = []
|
|||||||
bevy_pbr = { path = "../bevy_pbr", version = "0.14.0-dev", optional = true }
|
bevy_pbr = { path = "../bevy_pbr", version = "0.14.0-dev", optional = true }
|
||||||
bevy_sprite = { path = "../bevy_sprite", version = "0.14.0-dev", optional = true }
|
bevy_sprite = { path = "../bevy_sprite", version = "0.14.0-dev", optional = true }
|
||||||
bevy_app = { path = "../bevy_app", version = "0.14.0-dev" }
|
bevy_app = { path = "../bevy_app", version = "0.14.0-dev" }
|
||||||
|
bevy_color = { path = "../bevy_color", version = "0.14.0-dev" }
|
||||||
bevy_ecs = { path = "../bevy_ecs", version = "0.14.0-dev" }
|
bevy_ecs = { path = "../bevy_ecs", version = "0.14.0-dev" }
|
||||||
bevy_math = { path = "../bevy_math", version = "0.14.0-dev" }
|
bevy_math = { path = "../bevy_math", version = "0.14.0-dev" }
|
||||||
bevy_asset = { path = "../bevy_asset", version = "0.14.0-dev" }
|
bevy_asset = { path = "../bevy_asset", version = "0.14.0-dev" }
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
use std::{iter, marker::PhantomData};
|
use std::{iter, marker::PhantomData};
|
||||||
|
|
||||||
use crate::circles::DEFAULT_CIRCLE_SEGMENTS;
|
use crate::circles::DEFAULT_CIRCLE_SEGMENTS;
|
||||||
|
use bevy_color::LinearRgba;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Tick,
|
component::Tick,
|
||||||
system::{Deferred, ReadOnlySystemParam, Res, Resource, SystemBuffer, SystemMeta, SystemParam},
|
system::{Deferred, ReadOnlySystemParam, Res, Resource, SystemBuffer, SystemMeta, SystemParam},
|
||||||
@ -19,14 +20,13 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
type PositionItem = [f32; 3];
|
type PositionItem = [f32; 3];
|
||||||
type ColorItem = [f32; 4];
|
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
pub(crate) struct GizmoStorage<T: GizmoConfigGroup> {
|
pub(crate) struct GizmoStorage<T: GizmoConfigGroup> {
|
||||||
pub list_positions: Vec<PositionItem>,
|
pub(crate) list_positions: Vec<PositionItem>,
|
||||||
pub list_colors: Vec<ColorItem>,
|
pub(crate) list_colors: Vec<LinearRgba>,
|
||||||
pub strip_positions: Vec<PositionItem>,
|
pub(crate) strip_positions: Vec<PositionItem>,
|
||||||
pub strip_colors: Vec<ColorItem>,
|
pub(crate) strip_colors: Vec<LinearRgba>,
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,9 +104,9 @@ where
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct GizmoBuffer<T: GizmoConfigGroup> {
|
struct GizmoBuffer<T: GizmoConfigGroup> {
|
||||||
list_positions: Vec<PositionItem>,
|
list_positions: Vec<PositionItem>,
|
||||||
list_colors: Vec<ColorItem>,
|
list_colors: Vec<LinearRgba>,
|
||||||
strip_positions: Vec<PositionItem>,
|
strip_positions: Vec<PositionItem>,
|
||||||
strip_colors: Vec<ColorItem>,
|
strip_colors: Vec<LinearRgba>,
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,10 +244,8 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
|
|||||||
}
|
}
|
||||||
self.extend_strip_positions(positions);
|
self.extend_strip_positions(positions);
|
||||||
let len = self.buffer.strip_positions.len();
|
let len = self.buffer.strip_positions.len();
|
||||||
self.buffer
|
self.buffer.strip_colors.resize(len - 1, color.into());
|
||||||
.strip_colors
|
self.buffer.strip_colors.push(LinearRgba::NAN);
|
||||||
.resize(len - 1, color.as_linear_rgba_f32());
|
|
||||||
self.buffer.strip_colors.push([f32::NAN; 4]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw a line in 3D made of straight segments between the points, with a color gradient.
|
/// Draw a line in 3D made of straight segments between the points, with a color gradient.
|
||||||
@ -287,11 +285,11 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
|
|||||||
|
|
||||||
for (position, color) in points {
|
for (position, color) in points {
|
||||||
strip_positions.push(position.to_array());
|
strip_positions.push(position.to_array());
|
||||||
strip_colors.push(color.as_linear_rgba_f32());
|
strip_colors.push(color.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
strip_positions.push([f32::NAN; 3]);
|
strip_positions.push([f32::NAN; 3]);
|
||||||
strip_colors.push([f32::NAN; 4]);
|
strip_colors.push(LinearRgba::NAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw a wireframe sphere in 3D made out of 3 circles around the axes.
|
/// Draw a wireframe sphere in 3D made out of 3 circles around the axes.
|
||||||
@ -583,14 +581,14 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
|
|||||||
fn extend_list_colors(&mut self, colors: impl IntoIterator<Item = LegacyColor>) {
|
fn extend_list_colors(&mut self, colors: impl IntoIterator<Item = LegacyColor>) {
|
||||||
self.buffer
|
self.buffer
|
||||||
.list_colors
|
.list_colors
|
||||||
.extend(colors.into_iter().map(|color| color.as_linear_rgba_f32()));
|
.extend(colors.into_iter().map(LinearRgba::from));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn add_list_color(&mut self, color: LegacyColor, count: usize) {
|
fn add_list_color(&mut self, color: LegacyColor, count: usize) {
|
||||||
self.buffer
|
self.buffer
|
||||||
.list_colors
|
.list_colors
|
||||||
.extend(iter::repeat(color.as_linear_rgba_f32()).take(count));
|
.extend(iter::repeat(LinearRgba::from(color)).take(count));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -52,6 +52,7 @@ pub mod prelude {
|
|||||||
use aabb::AabbGizmoPlugin;
|
use aabb::AabbGizmoPlugin;
|
||||||
use bevy_app::{App, Last, Plugin};
|
use bevy_app::{App, Last, Plugin};
|
||||||
use bevy_asset::{load_internal_asset, Asset, AssetApp, Assets, Handle};
|
use bevy_asset::{load_internal_asset, Asset, AssetApp, Assets, Handle};
|
||||||
|
use bevy_color::LinearRgba;
|
||||||
use bevy_core::cast_slice;
|
use bevy_core::cast_slice;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
@ -309,7 +310,7 @@ struct LineGizmoUniform {
|
|||||||
#[derive(Asset, Debug, Default, Clone, TypePath)]
|
#[derive(Asset, Debug, Default, Clone, TypePath)]
|
||||||
struct LineGizmo {
|
struct LineGizmo {
|
||||||
positions: Vec<[f32; 3]>,
|
positions: Vec<[f32; 3]>,
|
||||||
colors: Vec<[f32; 4]>,
|
colors: Vec<LinearRgba>,
|
||||||
/// Whether this gizmo's topology is a line-strip or line-list
|
/// Whether this gizmo's topology is a line-strip or line-list
|
||||||
strip: bool,
|
strip: bool,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user