Skip to content

Commit

Permalink
Added the ability to map texture texels
Browse files Browse the repository at this point in the history
  • Loading branch information
zesterer committed Nov 2, 2023
1 parent 782e689 commit d90b67f
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 20 deletions.
31 changes: 16 additions & 15 deletions examples/texture_mapping.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use euc::{Buffer2d, Nearest, Pipeline, Sampler, Target, Texture, TriangleList};
use image::RgbaImage;
use euc::{Buffer2d, Pipeline, Sampler, Target, Texture, TriangleList};
use minifb::{Key, Window, WindowOptions};
use vek::{Mat4, Rgba, Vec2, Vec3, Vec4};

struct Cube<'r> {
struct Cube<'r, S> {
mvp: Mat4<f32>,
positions: &'r [Vec4<f32>],
uvs: &'r [Vec2<f32>],
sampler: &'r Nearest<RgbaImage>,
sampler: S,
}

impl<'r> Pipeline<'r> for Cube<'r> {
impl<'r, S: Sampler<2, Index = f32, Sample = Rgba<f32>>> Pipeline<'r> for Cube<'r, S> {
type Vertex = usize;
type VertexData = Vec2<f32>;
type Primitives = TriangleList;
Expand All @@ -26,8 +25,8 @@ impl<'r> Pipeline<'r> for Cube<'r> {
}

#[inline]
fn fragment(&self, v_uv: Self::VertexData) -> Self::Fragment {
Rgba::from(self.sampler.sample(v_uv.into_array()).0).map(|e: u8| e as f32)
fn fragment(&self, uv: Self::VertexData) -> Self::Fragment {
self.sampler.sample(uv.into_array())
}

fn blend(&self, _: Self::Pixel, color: Self::Fragment) -> Self::Pixel {
Expand All @@ -41,6 +40,7 @@ fn main() {
let mut color = Buffer2d::fill([w, h], 0);
let mut depth = Buffer2d::fill([w, h], 1.0);

// Vertex positions
let positions = [
// z = 1
Vec4::new(-1.0, -1.0, 1.0, 1.0),
Expand Down Expand Up @@ -73,6 +73,7 @@ fn main() {
Vec4::new(-1.0, 1.0, -1.0, 1.0),
Vec4::new(-1.0, 1.0, 1.0, 1.0),
];
// Vertex texture coordinates
let uvs = [
// z = 1
Vec2::new(0.0, 1.0),
Expand Down Expand Up @@ -106,14 +107,14 @@ fn main() {
Vec2::new(1.0, 1.0),
];

let texture = match image::open("examples/data/rust.png") {
Ok(image) => image.to_rgba8(),
Err(err) => {
eprintln!("{}", err);
return;
}
};
let sampler = texture.nearest();
// Load a texture from disk
let texture = image::open("examples/data/rust.png").unwrap().to_rgba8();

// Create a sampler from the texture. Because the underlying texture is a bitmap, we map its texels to a
// floating-point color. From here, we allow it to be bilinearly interpolated by the shader.
let sampler = texture
.map(|pixel| Rgba::from(pixel.0).map(|e: u8| e as f32))
.linear();

let mut win = Window::new("Texture Mapping", w, h, WindowOptions::default()).unwrap();

Expand Down
4 changes: 4 additions & 0 deletions src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use micromath::F32Ext;

/// Defines how a [`Pipeline`] will interact with the depth target.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct DepthMode {
/// The test, if any, that occurs when comparing the depth of the new fragment with that of the current depth.
pub test: Option<Ordering>,
Expand Down Expand Up @@ -53,6 +54,7 @@ impl DepthMode {

/// Defines how a [`Pipeline`] will interact with the pixel target.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct PixelMode {
/// Whether the fragment's pixel should be written to the pixel target.
pub write: bool,
Expand Down Expand Up @@ -89,6 +91,7 @@ pub enum YAxisDirection {
}

/// The configuration of the coordinate system used by a pipeline.
#[non_exhaustive]
pub struct CoordinateMode {
pub handedness: Handedness,
pub y_axis_direction: YAxisDirection,
Expand All @@ -97,6 +100,7 @@ pub struct CoordinateMode {

/// The anti-aliasing mode used by a pipeline.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum AaMode {
/// No anti-aliasing.
None,
Expand Down
7 changes: 2 additions & 5 deletions src/sampler/linear.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ where

#[inline(always)]
fn sample(&self, index: [Self::Index; 2]) -> Self::Sample {
assert!(index[0] <= 1.0, "{:?}", index);
assert!(index[1] <= 1.0, "{:?}", index);

let size = self.raw_texture().size();
let size_f32 = size.map(|e| e as f32);
// Index in texture coordinates
Expand All @@ -43,14 +40,14 @@ where
// Find interpolation values
let fract = index_tex.map(|e| e.fract());

assert!(
debug_assert!(
posi[0] < size[0],
"pos: {:?}, sz: {:?}, idx: {:?}",
posi,
size,
index
);
assert!(
debug_assert!(
posi[1] < size[1],
"pos: {:?}, sz: {:?}, idx: {:?}",
posi,
Expand Down
54 changes: 54 additions & 0 deletions src/texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ pub trait Texture<const N: usize> {
phantom: PhantomData,
}
}

/// Map the texels of this texture to another type using a mapping function.
fn map<F, U>(self, f: F) -> Map<Self, F, U>
where
F: Fn(Self::Texel) -> U,
Self: Sized,
{
Map {
tex: self,
f,
phantom: PhantomData,
}
}
}

impl<'a, T: Texture<N>, const N: usize> Texture<N> for &'a T {
Expand Down Expand Up @@ -119,6 +132,47 @@ impl<'a, T: Texture<N>, const N: usize> Texture<N> for &'a mut T {
}
}

#[derive(Debug)]
pub struct Map<T, F, U> {
tex: T,
f: F,
phantom: PhantomData<U>,
}

impl<T: Copy, F: Copy, U> Copy for Map<T, F, U> {}
impl<T: Clone, F: Clone, U> Clone for Map<T, F, U> {
fn clone(&self) -> Self {
Self {
tex: self.tex.clone(),
f: self.f.clone(),
phantom: PhantomData,
}
}
}

impl<'a, T: Texture<N>, U: Clone, F: Fn(T::Texel) -> U, const N: usize> Texture<N>
for Map<T, F, U>
{
type Index = T::Index;
type Texel = U;
#[inline(always)]
fn size(&self) -> [Self::Index; N] {
self.tex.size()
}
#[inline(always)]
fn preferred_axes(&self) -> Option<[usize; N]> {
self.tex.preferred_axes()
}
#[inline(always)]
fn read(&self, index: [Self::Index; N]) -> Self::Texel {
(self.f)(self.tex.read(index))
}
#[inline(always)]
unsafe fn read_unchecked(&self, index: [Self::Index; N]) -> Self::Texel {
(self.f)(self.tex.read_unchecked(index))
}
}

// impl<'a, T: Clone, F: Fn([usize; N]) -> T, const N: usize> Texture<N> for (F, [usize; N], PhantomData<T>) {
// type Index = usize;
// type Texel = T;
Expand Down

0 comments on commit d90b67f

Please sign in to comment.