use std::fmt;
use std::sync::Arc;
use crate::asset::{AssetSource, InMemoryAsset};
#[derive(Clone, Debug)]
pub enum ImageData {
#[cfg(feature = "registry")]
Embedded(oxideav_core::VideoFrame),
Source(Arc<dyn AssetSource>),
External { uri: String, mime: Option<String> },
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MagFilter {
Nearest,
Linear,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MinFilter {
Nearest,
Linear,
NearestMipNearest,
LinearMipNearest,
NearestMipLinear,
LinearMipLinear,
}
impl MinFilter {
pub fn uses_mipmaps(&self) -> bool {
!matches!(self, Self::Nearest | Self::Linear)
}
pub fn mipmap_mode(&self) -> MipmapMode {
match self {
Self::Nearest | Self::Linear => MipmapMode::Disabled,
Self::NearestMipNearest | Self::LinearMipNearest => MipmapMode::Nearest,
Self::NearestMipLinear | Self::LinearMipLinear => MipmapMode::Linear,
}
}
pub fn base_filter(&self) -> MagFilter {
match self {
Self::Nearest | Self::NearestMipNearest | Self::NearestMipLinear => MagFilter::Nearest,
Self::Linear | Self::LinearMipNearest | Self::LinearMipLinear => MagFilter::Linear,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MipmapMode {
Disabled,
Nearest,
Linear,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum WrapMode {
ClampToEdge,
MirroredRepeat,
#[default]
Repeat,
}
impl WrapMode {
pub fn wrap(&self, coord: f32) -> f32 {
if !coord.is_finite() {
return 0.0;
}
match self {
Self::ClampToEdge => coord.clamp(0.0, 1.0),
Self::Repeat => coord - coord.floor(),
Self::MirroredRepeat => {
let m = coord - 2.0 * (coord / 2.0).floor();
if m > 1.0 {
2.0 - m
} else {
m
}
}
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Sampler {
pub mag_filter: Option<MagFilter>,
pub min_filter: Option<MinFilter>,
pub wrap_s: WrapMode,
pub wrap_t: WrapMode,
}
impl Sampler {
pub fn default_sampler() -> Self {
Self {
mag_filter: None,
min_filter: None,
wrap_s: WrapMode::Repeat,
wrap_t: WrapMode::Repeat,
}
}
pub fn with_mag_filter(mut self, filter: MagFilter) -> Self {
self.mag_filter = Some(filter);
self
}
pub fn with_min_filter(mut self, filter: MinFilter) -> Self {
self.min_filter = Some(filter);
self
}
pub fn with_wrap(mut self, wrap_s: WrapMode, wrap_t: WrapMode) -> Self {
self.wrap_s = wrap_s;
self.wrap_t = wrap_t;
self
}
pub fn effective_mag_filter(&self) -> MagFilter {
self.mag_filter.unwrap_or(MagFilter::Linear)
}
pub fn effective_min_filter(&self) -> MinFilter {
self.min_filter.unwrap_or(MinFilter::LinearMipLinear)
}
pub fn uses_mipmaps(&self) -> bool {
self.effective_min_filter().uses_mipmaps()
}
pub fn wrap_uv(&self, uv: [f32; 2]) -> [f32; 2] {
[self.wrap_s.wrap(uv[0]), self.wrap_t.wrap(uv[1])]
}
}
pub struct Texture {
pub name: Option<String>,
pub image: ImageData,
pub sampler: Sampler,
}
impl fmt::Debug for Texture {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Texture")
.field("name", &self.name)
.field("image", &self.image)
.field("sampler", &self.sampler)
.finish()
}
}
impl Clone for Texture {
fn clone(&self) -> Self {
Self {
name: self.name.clone(),
image: self.image.clone(),
sampler: self.sampler,
}
}
}
impl Texture {
pub fn from_uri(uri: impl Into<String>) -> Self {
Self {
name: None,
image: ImageData::External {
uri: uri.into(),
mime: None,
},
sampler: Sampler::default_sampler(),
}
}
pub fn from_source(source: Arc<dyn AssetSource>) -> Self {
Self {
name: None,
image: ImageData::Source(source),
sampler: Sampler::default_sampler(),
}
}
pub fn from_encoded(mime: impl Into<String>, bytes: Vec<u8>) -> Self {
let asset = Arc::new(InMemoryAsset {
mime: Some(mime.into()),
bytes,
});
Self::from_source(asset)
}
}