he3d-core 0.4.0

no_std + alloc core: resources, scene graph, materials, and stable generational IDs
Documentation
use core::ops::{Add, Mul, Sub};

/// A packed 8-bit RGBA color, laid out in RGBA byte order.
/// 一个打包的 8 位 RGBA 颜色,按 RGBA 字节顺序排列。
///
/// This is the storage format for textures and framebuffers.
/// 这是纹理和帧缓冲的存储格式。
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Hash)]
#[repr(C)]
pub struct ColorRgba8 {
   /// Red channel.
   /// 红色通道。
   pub r: u8,
   /// Green channel.
   /// 绿色通道。
   pub g: u8,
   /// Blue channel.
   /// 蓝色通道。
   pub b: u8,
   /// Alpha channel.
   /// 透明通道。
   pub a: u8,
}

// Compile-time layout guarantee: `ColorRgba8` must stay a plain 4-byte RGBA tuple.
// 编译期布局保证:`ColorRgba8` 必须保持为一个普通的 4 字节 RGBA 元组。
// Backends reinterpret its slice as `&[u8]` for framebuffer upload, so adding fields or padding here would silently corrupt memory.
// 后端会将其切片重新解释为 `&[u8]` 以用于帧缓冲上传,因此在此添加字段或填充会静默地破坏内存。
const _: () = assert!(core::mem::size_of::<ColorRgba8>() == 4);

impl ColorRgba8 {
   /// Opaque black.
   /// 不透明黑色。
   pub const BLACK: Self = Self::from_rgba(0, 0, 0, 255);
   /// Opaque white.
   /// 不透明白色。
   pub const WHITE: Self = Self::from_rgba(255, 255, 255, 255);
   /// Fully transparent black.
   /// 完全透明的黑色。
   pub const TRANSPARENT: Self = Self::from_rgba(0, 0, 0, 0);
   /// The conventional "missing texture" color.
   /// 约定的“缺失纹理”颜色。
   pub const MAGENTA: Self = Self::from_rgb(255, 0, 255);

   /// Pack four components into a color.
   /// 将四个分量打包成一个颜色。
   #[inline]
   #[must_use]
   pub const fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
      Self { r, g, b, a }
   }

   /// Pack three components into an opaque color.
   /// 将三个分量打包成一个不透明颜色。
   #[inline]
   #[must_use]
   pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
      Self { r, g, b, a: 255 }
   }

   /// Convert to linear-light components in `[0, 1]`.
   /// 转换为 `[0, 1]` 范围内的线性光分量。
   #[inline]
   #[must_use]
   pub fn to_linear(self) -> Rgb {
      Rgb {
         r: f32::from(self.r) / 255.0,
         g: f32::from(self.g) / 255.0,
         b: f32::from(self.b) / 255.0,
      }
   }
}

/// A linear-light RGB color with `f32` components.
/// 一个使用 `f32` 分量的线性光 RGB 颜色。
///
/// Components are conventionally in `[0, 1]` but are not clamped by default; lighting math may produce values outside the range, and conversion to [`ColorRgba8`] clamps them.
/// 分量通常位于 `[0, 1]` 内,但默认不会截断;光照运算可能产生超出该范围的值,转换为 [`ColorRgba8`] 时会进行截断。
#[derive(Clone, Copy, PartialEq, Debug, Default)]
pub struct Rgb {
   /// Red component.
   /// 红色分量。
   pub r: f32,
   /// Green component.
   /// 绿色分量。
   pub g: f32,
   /// Blue component.
   /// 蓝色分量。
   pub b: f32,
}

impl Rgb {
   /// Pure black.
   /// 纯黑。
   pub const BLACK: Self = Self::new(0.0, 0.0, 0.0);
   /// Pure white.
   /// 纯白。
   pub const WHITE: Self = Self::new(1.0, 1.0, 1.0);

   /// Construct a color from three components.
   /// 用三个分量构造一个颜色。
   #[inline]
   #[must_use]
   pub const fn new(r: f32, g: f32, b: f32) -> Self {
      Self { r, g, b }
   }

   /// Quantize to 8-bit RGBA, truncating toward zero.
   /// 量化到 8 位 RGBA,向零截断。
   ///
   /// The `as u8` cast saturates: values below 0 clamp to 0, values above 1 clamp to 255, and NaN clamps to 0.
   /// `as u8` 转换是饱和的:小于 0 的值截断为 0,大于 1 的值截断为 255,NaN 截断为 0。
   /// Truncation rather than rounding is deliberate: it keeps the mapping from
   /// `[0, 1]` onto `0..=255` monotonic and idempotent, so quantizing an already
   /// quantized colour changes nothing.
   /// 采用截断而非四舍五入是刻意的:它使 `[0, 1]` 到 `0..=255` 的映射保持单调且幂等,
   /// 因此对已量化的颜色再次量化不会改变任何东西。
   #[inline]
   #[must_use]
   // The float→u8 truncation and sign loss are the documented quantization behavior
   // of this function; do not "fix" the casts.
   // float→u8 的截断与符号丢失正是本函数文档化的量化行为,请勿“修正”这些转换。
   #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
   pub fn to_rgba8(self) -> ColorRgba8 {
      ColorRgba8 {
         r: (self.r * 255.0) as u8,
         g: (self.g * 255.0) as u8,
         b: (self.b * 255.0) as u8,
         a: 255,
      }
   }
}

impl Add for Rgb {
   type Output = Self;
   #[inline]
   fn add(self, rhs: Self) -> Self {
      Self::new(self.r + rhs.r, self.g + rhs.g, self.b + rhs.b)
   }
}

impl Sub for Rgb {
   type Output = Self;
   #[inline]
   fn sub(self, rhs: Self) -> Self {
      Self::new(self.r - rhs.r, self.g - rhs.g, self.b - rhs.b)
   }
}

impl Mul<f32> for Rgb {
   type Output = Self;
   #[inline]
   fn mul(self, rhs: f32) -> Self {
      Self::new(self.r * rhs, self.g * rhs, self.b * rhs)
   }
}

/// Component-wise multiplication.
/// 逐分量相乘。
impl Mul for Rgb {
   type Output = Self;
   #[inline]
   fn mul(self, rhs: Self) -> Self {
      Self::new(self.r * rhs.r, self.g * rhs.g, self.b * rhs.b)
   }
}