use crate::math::{Point2u, Vec2, Vector, pt2, vec2};
use crate::util::{
Dims,
buf::{AsSlice2, Buf2, Slice2},
};
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct Tex;
pub type TexCoord = Vec2<Tex>;
impl TexCoord {
pub const fn u(&self) -> f32 {
self.0[0]
}
pub const fn v(&self) -> f32 {
self.0[1]
}
}
#[inline]
pub const fn uv(u: f32, v: f32) -> TexCoord {
Vector::new([u, v])
}
#[derive(Copy, Clone)]
pub struct Texture<D> {
w: f32,
h: f32,
data: D,
}
#[derive(Clone)]
pub struct Atlas<C> {
pub layout: Layout,
pub texture: Texture<Buf2<C>>,
}
#[derive(Copy, Clone, Debug)]
pub enum Layout {
Grid { sub_dims: Dims },
}
impl<D> Texture<D> {
#[inline]
pub fn width(&self) -> f32 {
self.w
}
#[inline]
pub fn height(&self) -> f32 {
self.h
}
pub fn data(&self) -> &D {
&self.data
}
}
impl<C> Atlas<C> {
pub fn new(layout: Layout, texture: Texture<Buf2<C>>) -> Self {
Self { layout, texture }
}
fn rect(&self, i: u32) -> [Point2u; 2] {
match self.layout {
Layout::Grid { sub_dims: (sub_w, sub_h) } => {
let subs_per_row = self.texture.data.width() / sub_w;
let top_left =
pt2(i % subs_per_row * sub_w, i / subs_per_row * sub_h);
[top_left, top_left + vec2(sub_w as i32, sub_h as i32)]
}
}
}
pub fn get(&self, i: u32) -> Texture<Slice2<C>> {
let [p0, p1] = self.rect(i);
self.texture.data.slice(p0..p1).into()
}
pub fn coords(&self, i: u32) -> [TexCoord; 4] {
let tex_w = self.texture.width();
let tex_h = self.texture.height();
let [(x0, y0), (x1, y1)] = self
.rect(i)
.map(|p| (p.x() as f32 / tex_w, p.y() as f32 / tex_h));
[uv(x0, y0), uv(x1, y0), uv(x0, y1), uv(x1, y1)]
}
}
impl<C> From<Buf2<C>> for Texture<Buf2<C>> {
fn from(data: Buf2<C>) -> Self {
Self {
w: data.width() as f32,
h: data.height() as f32,
data,
}
}
}
impl<'a, C> From<Slice2<'a, C>> for Texture<Slice2<'a, C>> {
fn from(data: Slice2<'a, C>) -> Self {
Self {
w: data.width() as f32,
h: data.height() as f32,
data,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct SamplerRepeatPot {
w_mask: u32,
h_mask: u32,
}
impl SamplerRepeatPot {
pub fn new<C>(tex: &Texture<impl AsSlice2<C>>) -> Self {
let w = tex.width() as u32;
let h = tex.height() as u32;
assert!(w.is_power_of_two(), "width must be 2^n, was {w}");
assert!(h.is_power_of_two(), "height must be 2^n, was {h}");
Self { w_mask: w - 1, h_mask: h - 1 }
}
pub fn sample<C: Copy>(
&self,
tex: &Texture<impl AsSlice2<C>>,
tc: TexCoord,
) -> C {
let scaled_uv = uv(tex.width() * tc.u(), tex.height() * tc.v());
self.sample_abs(tex, scaled_uv)
}
pub fn sample_abs<C: Copy>(
&self,
tex: &Texture<impl AsSlice2<C>>,
tc: TexCoord,
) -> C {
use crate::math::float::f32;
let u = f32::floor(tc.u()) as i32 as u32 & self.w_mask;
let v = f32::floor(tc.v()) as i32 as u32 & self.h_mask;
tex.data.as_slice2()[[u, v]]
}
}
#[derive(Copy, Clone, Debug)]
pub struct SamplerClamp;
impl SamplerClamp {
pub fn sample<C: Copy>(
&self,
tex: &Texture<impl AsSlice2<C>>,
tc: TexCoord,
) -> C {
self.sample_abs(tex, uv(tc.u() * tex.w, tc.v() * tex.h))
}
pub fn sample_abs<C: Copy>(
&self,
tex: &Texture<impl AsSlice2<C>>,
tc: TexCoord,
) -> C {
use crate::math::float::f32;
let u = f32::floor(tc.u().clamp(0.0, tex.w - 1.0)) as u32;
let v = f32::floor(tc.v().clamp(0.0, tex.h - 1.0)) as u32;
tex.data.as_slice2()[[u, v]]
}
}
#[derive(Copy, Clone, Debug)]
pub struct SamplerOnce;
impl SamplerOnce {
pub fn sample<C: Copy>(
&self,
tex: &Texture<impl AsSlice2<C>>,
tc: TexCoord,
) -> C {
let scaled_uv = uv(tex.width() * tc.u(), tex.height() * tc.v());
self.sample_abs(tex, scaled_uv)
}
pub fn sample_abs<C: Copy>(
&self,
tex: &Texture<impl AsSlice2<C>>,
tc: TexCoord,
) -> C {
let u = tc.u() as u32;
let v = tc.v() as u32;
let d = tex.data.as_slice2();
debug_assert!(u < d.width(), "u={u}");
debug_assert!(v < d.height(), "v={v}");
d[[u, v]]
}
}
#[cfg(test)]
mod tests {
use alloc::vec;
use crate::math::{Color3, rgb};
use crate::util::buf::Buf2;
use super::*;
#[rustfmt::skip]
fn tex() -> Texture<Buf2<Color3>> {
Texture::from(Buf2::new_from(
(2, 2), vec![
rgb(0xFF, 0, 0),
rgb(0, 0xFF, 0),
rgb(0, 0, 0xFF),
rgb(0xFF, 0xFF, 0),
]
))
}
#[test]
fn sampler_repeat_pot() {
let tex = tex();
let s = SamplerRepeatPot::new(&tex);
assert_eq!(s.sample(&tex, uv(-0.1, 0.0)), rgb(0, 0xFF, 0));
assert_eq!(s.sample(&tex, uv(0.0, -0.1)), rgb(0, 0, 0xFF));
assert_eq!(s.sample(&tex, uv(1.0, 0.0)), rgb(0xFF, 0, 0));
assert_eq!(s.sample(&tex, uv(0.0, 1.0)), rgb(0xFF, 0, 0));
assert_eq!(s.sample(&tex, uv(4.8, 0.2)), rgb(0, 0xFF, 0));
assert_eq!(s.sample(&tex, uv(0.2, 4.8)), rgb(0, 0, 0xFF));
}
#[test]
fn sampler_clamp() {
let tex = tex();
let s = SamplerClamp;
assert_eq!(s.sample(&tex, uv(-1.0, 0.0)), rgb(0xFF, 0, 0));
assert_eq!(s.sample(&tex, uv(0.0, -1.0)), rgb(0xFF, 0, 0));
assert_eq!(s.sample(&tex, uv(1.5, 0.0)), rgb(0, 0xFF, 0));
assert_eq!(s.sample(&tex, uv(0.0, 1.5)), rgb(0, 0, 0xFF));
assert_eq!(s.sample(&tex, uv(1.5, 1.5)), rgb(0xFF, 0xFF, 0));
}
#[test]
fn sampler_once() {
let tex = tex();
let s = SamplerOnce;
assert_eq!(s.sample(&tex, uv(0.0, 0.0)), rgb(0xFF, 0, 0));
assert_eq!(s.sample(&tex, uv(0.5, 0.0)), rgb(0, 0xFF, 0));
assert_eq!(s.sample(&tex, uv(0.0, 0.5)), rgb(0, 0, 0xFF));
assert_eq!(s.sample(&tex, uv(0.5, 0.5)), rgb(0xFF, 0xFF, 0));
}
}