use axiolid_core::Scalar;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Blend {
Linear,
Nearest,
None,
}
#[derive(Debug, Clone, PartialEq)]
pub struct AttributeChannel {
pub name: String,
pub values: Vec<Scalar>,
pub width: usize,
pub blend: Blend,
pub corner_indices: Option<Vec<u32>>,
}
impl AttributeChannel {
pub const UNMAPPED: u32 = u32::MAX;
pub fn new(name: impl Into<String>, values: Vec<Scalar>, width: usize, blend: Blend) -> Self {
Self {
name: name.into(),
values,
width,
blend,
corner_indices: None,
}
}
pub fn corner_indexed(
name: impl Into<String>,
values: Vec<Scalar>,
width: usize,
blend: Blend,
corner_indices: Vec<u32>,
) -> Self {
Self {
corner_indices: Some(corner_indices),
..Self::new(name, values, width, blend)
}
}
pub fn is_corner_indexed(&self) -> bool {
self.corner_indices.is_some()
}
pub fn value_count(&self) -> usize {
if self.width == 0 {
return 0;
}
self.values.len() / self.width
}
pub fn vertex_count(&self) -> usize {
self.value_count()
}
pub fn get(&self, vertex: usize) -> Option<&[Scalar]> {
if self.width == 0 {
return None;
}
let start = vertex.checked_mul(self.width)?;
self.values.get(start..start + self.width)
}
pub fn at_corner(&self, mesh_indices: &[u32], corner: usize) -> Option<&[Scalar]> {
let slot = match &self.corner_indices {
Some(corners) => *corners.get(corner)?,
None => *mesh_indices.get(corner)?,
};
if slot == Self::UNMAPPED {
return None;
}
self.get(slot as usize)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AttributeFate {
Preserved,
Interpolated,
Dropped(DropReason),
}
impl AttributeFate {
#[must_use]
pub fn then(self, next: Self) -> Self {
match (self, next) {
(Self::Dropped(reason), _) | (_, Self::Dropped(reason)) => Self::Dropped(reason),
(Self::Preserved, Self::Preserved) => Self::Preserved,
_ => Self::Interpolated,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DropReason {
NotBlendable,
ProviderLimitation,
ConflictingValues,
IncompatibleChannels,
}