use crate::std::{
texture::{Sampler, SamplerComparison},
vector::Vec4f,
};
#[derive(Clone)]
pub struct TextureData1D<T> {
pub mips: std::vec::Vec<std::vec::Vec<[T; 4]>>,
}
impl<T: Default + Copy> TextureData1D<T> {
pub fn new(width: u32) -> Self {
let pixels: std::vec::Vec<[T; 4]> = (0..width)
.map(|_| std::array::from_fn(|_| T::default()))
.collect();
Self { mips: vec![pixels] }
}
}
impl<T> TextureData1D<T> {
pub fn width(&self, level: u32) -> u32 {
self.mips
.get(level as usize)
.map(|m| m.len() as u32)
.unwrap_or(0)
}
pub fn num_levels(&self) -> u32 {
self.mips.len() as u32
}
pub fn get_pixel(&self, x: u32, level: u32) -> Option<&[T; 4]> {
self.mips.get(level as usize)?.get(x as usize)
}
pub fn set_pixel(&mut self, x: u32, level: u32, value: [T; 4]) {
if let Some(mip) = self.mips.get_mut(level as usize)
&& let Some(pixel) = mip.get_mut(x as usize)
{
*pixel = value;
}
}
}
#[derive(Clone)]
pub struct TextureData2D<T> {
pub mips: std::vec::Vec<std::vec::Vec<std::vec::Vec<[T; 4]>>>,
}
impl<T: Default + Clone> TextureData2D<T> {
pub fn new(width: u32, height: u32) -> Self {
let pixels: std::vec::Vec<std::vec::Vec<[T; 4]>> = (0..height)
.map(|_| {
(0..width)
.map(|_| std::array::from_fn(|_| T::default()))
.collect()
})
.collect();
Self { mips: vec![pixels] }
}
}
impl<T> TextureData2D<T> {
pub fn dimensions(&self, level: u32) -> (u32, u32) {
self.mips
.get(level as usize)
.map(|m| {
let height = m.len() as u32;
let width = m.first().map(|r| r.len() as u32).unwrap_or(0);
(width, height)
})
.unwrap_or((0, 0))
}
pub fn num_levels(&self) -> u32 {
self.mips.len() as u32
}
pub fn get_pixel(&self, x: u32, y: u32, level: u32) -> Option<&[T; 4]> {
self.mips
.get(level as usize)?
.get(y as usize)?
.get(x as usize)
}
pub fn set_pixel(&mut self, x: u32, y: u32, level: u32, value: [T; 4]) {
if let Some(mip) = self.mips.get_mut(level as usize)
&& let Some(row) = mip.get_mut(y as usize)
&& let Some(pixel) = row.get_mut(x as usize)
{
*pixel = value;
}
}
}
impl<T: Default + Clone> TextureData2D<T> {
pub fn generate_mips(&mut self)
where
T: Copy + Into<f32> + From<f32>,
{
let (mut w, mut h) = self.dimensions(0);
while w > 1 || h > 1 {
let new_w = Ord::max(w / 2, 1);
let new_h = Ord::max(h / 2, 1);
let prev_level = self.mips.len() - 1;
let mut new_mip: std::vec::Vec<std::vec::Vec<[T; 4]>> = (0..new_h)
.map(|_| {
(0..new_w)
.map(|_| std::array::from_fn(|_| T::default()))
.collect()
})
.collect();
for y in 0..new_h {
for x in 0..new_w {
let mut sum = [0.0f32; 4];
let mut count = 0;
for dy in 0..2 {
for dx in 0..2 {
let sx = Ord::min(x * 2 + dx, w - 1);
let sy = Ord::min(y * 2 + dy, h - 1);
if let Some(pixel) = self.mips[prev_level]
.get(sy as usize)
.and_then(|row| row.get(sx as usize))
{
for c in 0..4 {
sum[c] += pixel[c].into();
}
count += 1;
}
}
}
if count > 0 {
let avg = [
T::from(sum[0] / count as f32),
T::from(sum[1] / count as f32),
T::from(sum[2] / count as f32),
T::from(sum[3] / count as f32),
];
new_mip[y as usize][x as usize] = avg;
}
}
}
self.mips.push(new_mip);
w = new_w;
h = new_h;
}
}
}
#[derive(Clone)]
pub struct TextureData2DArray<T> {
pub layers: std::vec::Vec<TextureData2D<T>>,
}
impl<T: Default + Copy> TextureData2DArray<T> {
pub fn new(width: u32, height: u32, layers: u32) -> Self {
Self {
layers: (0..layers)
.map(|_| TextureData2D::new(width, height))
.collect(),
}
}
}
impl<T> TextureData2DArray<T> {
pub fn num_layers(&self) -> u32 {
self.layers.len() as u32
}
pub fn dimensions(&self, level: u32) -> (u32, u32) {
self.layers
.first()
.map(|l| l.dimensions(level))
.unwrap_or((0, 0))
}
pub fn num_levels(&self) -> u32 {
self.layers.first().map(|l| l.num_levels()).unwrap_or(0)
}
}
#[derive(Clone)]
pub struct TextureData3D<T> {
pub mips: std::vec::Vec<std::vec::Vec<std::vec::Vec<std::vec::Vec<[T; 4]>>>>,
}
impl<T: Default + Copy> TextureData3D<T> {
pub fn new(width: u32, height: u32, depth: u32) -> Self {
let volume: std::vec::Vec<std::vec::Vec<std::vec::Vec<[T; 4]>>> = (0..depth)
.map(|_| {
(0..height)
.map(|_| {
(0..width)
.map(|_| std::array::from_fn(|_| T::default()))
.collect()
})
.collect()
})
.collect();
Self { mips: vec![volume] }
}
}
impl<T> TextureData3D<T> {
pub fn dimensions(&self, level: u32) -> (u32, u32, u32) {
self.mips
.get(level as usize)
.map(|m| {
let depth = m.len() as u32;
let height = m.first().map(|s| s.len() as u32).unwrap_or(0);
let width = m
.first()
.and_then(|s| s.first())
.map(|r| r.len() as u32)
.unwrap_or(0);
(width, height, depth)
})
.unwrap_or((0, 0, 0))
}
pub fn num_levels(&self) -> u32 {
self.mips.len() as u32
}
pub fn get_pixel(&self, x: u32, y: u32, z: u32, level: u32) -> Option<&[T; 4]> {
self.mips
.get(level as usize)?
.get(z as usize)?
.get(y as usize)?
.get(x as usize)
}
pub fn set_pixel(&mut self, x: u32, y: u32, z: u32, level: u32, value: [T; 4]) {
if let Some(mip) = self.mips.get_mut(level as usize)
&& let Some(slice) = mip.get_mut(z as usize)
&& let Some(row) = slice.get_mut(y as usize)
&& let Some(pixel) = row.get_mut(x as usize)
{
*pixel = value;
}
}
}
#[derive(Clone)]
pub struct TextureDataCube<T> {
pub faces: [TextureData2D<T>; 6],
}
impl<T: Default + Copy> TextureDataCube<T> {
pub fn new(size: u32) -> Self {
Self {
faces: std::array::from_fn(|_| TextureData2D::new(size, size)),
}
}
}
impl<T> TextureDataCube<T> {
pub fn dimensions(&self, level: u32) -> (u32, u32) {
self.faces[0].dimensions(level)
}
pub fn num_levels(&self) -> u32 {
self.faces[0].num_levels()
}
}
#[derive(Clone)]
pub struct TextureDataCubeArray<T> {
pub cubes: std::vec::Vec<TextureDataCube<T>>,
}
impl<T: Default + Copy> TextureDataCubeArray<T> {
pub fn new(size: u32, layers: u32) -> Self {
Self {
cubes: (0..layers).map(|_| TextureDataCube::new(size)).collect(),
}
}
}
impl<T> TextureDataCubeArray<T> {
pub fn num_layers(&self) -> u32 {
self.cubes.len() as u32
}
pub fn dimensions(&self, level: u32) -> (u32, u32) {
self.cubes
.first()
.map(|c| c.dimensions(level))
.unwrap_or((0, 0))
}
pub fn num_levels(&self) -> u32 {
self.cubes.first().map(|c| c.num_levels()).unwrap_or(0)
}
}
#[derive(Clone)]
pub struct TextureDataMultisampled2D<T> {
pub width: u32,
pub height: u32,
pub sample_count: u32,
pub samples: std::vec::Vec<std::vec::Vec<std::vec::Vec<[T; 4]>>>,
}
impl<T: Default + Clone> TextureDataMultisampled2D<T> {
pub fn new(width: u32, height: u32, sample_count: u32) -> Self {
let samples: std::vec::Vec<std::vec::Vec<std::vec::Vec<[T; 4]>>> = (0..height)
.map(|_| {
(0..width)
.map(|_| {
(0..sample_count)
.map(|_| std::array::from_fn(|_| T::default()))
.collect()
})
.collect()
})
.collect();
Self {
width,
height,
sample_count,
samples,
}
}
}
impl<T> TextureDataMultisampled2D<T> {
pub fn dimensions(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn num_samples(&self) -> u32 {
self.sample_count
}
pub fn get_sample(&self, x: u32, y: u32, sample: u32) -> Option<&[T; 4]> {
self.samples
.get(y as usize)?
.get(x as usize)?
.get(sample as usize)
}
}
#[derive(Clone)]
pub struct TextureDataDepth2D {
pub mips: std::vec::Vec<std::vec::Vec<std::vec::Vec<f32>>>,
}
impl TextureDataDepth2D {
pub fn new(width: u32, height: u32) -> Self {
let row = vec![0.0f32; width as usize];
let pixels = vec![row; height as usize];
Self { mips: vec![pixels] }
}
pub fn dimensions(&self, level: u32) -> (u32, u32) {
self.mips
.get(level as usize)
.map(|m| {
let height = m.len() as u32;
let width = m.first().map(|r| r.len() as u32).unwrap_or(0);
(width, height)
})
.unwrap_or((0, 0))
}
pub fn num_levels(&self) -> u32 {
self.mips.len() as u32
}
pub fn get_depth(&self, x: u32, y: u32, level: u32) -> Option<f32> {
self.mips
.get(level as usize)?
.get(y as usize)?
.get(x as usize)
.copied()
}
}
#[derive(Clone)]
pub struct TextureDataDepth2DArray {
pub layers: std::vec::Vec<TextureDataDepth2D>,
}
impl TextureDataDepth2DArray {
pub fn new(width: u32, height: u32, layers: u32) -> Self {
Self {
layers: (0..layers)
.map(|_| TextureDataDepth2D::new(width, height))
.collect(),
}
}
pub fn num_layers(&self) -> u32 {
self.layers.len() as u32
}
pub fn dimensions(&self, level: u32) -> (u32, u32) {
self.layers
.first()
.map(|l| l.dimensions(level))
.unwrap_or((0, 0))
}
pub fn num_levels(&self) -> u32 {
self.layers.first().map(|l| l.num_levels()).unwrap_or(0)
}
}
#[derive(Clone)]
pub struct TextureDataDepthCube {
pub faces: [TextureDataDepth2D; 6],
}
impl TextureDataDepthCube {
pub fn new(size: u32) -> Self {
Self {
faces: std::array::from_fn(|_| TextureDataDepth2D::new(size, size)),
}
}
pub fn dimensions(&self, level: u32) -> (u32, u32) {
self.faces[0].dimensions(level)
}
pub fn num_levels(&self) -> u32 {
self.faces[0].num_levels()
}
}
#[derive(Clone)]
pub struct TextureDataDepthCubeArray {
pub cubes: std::vec::Vec<TextureDataDepthCube>,
}
impl TextureDataDepthCubeArray {
pub fn new(size: u32, layers: u32) -> Self {
Self {
cubes: (0..layers)
.map(|_| TextureDataDepthCube::new(size))
.collect(),
}
}
pub fn num_layers(&self) -> u32 {
self.cubes.len() as u32
}
pub fn dimensions(&self, level: u32) -> (u32, u32) {
self.cubes
.first()
.map(|c| c.dimensions(level))
.unwrap_or((0, 0))
}
pub fn num_levels(&self) -> u32 {
self.cubes.first().map(|c| c.num_levels()).unwrap_or(0)
}
}
#[derive(Clone)]
pub struct TextureDataDepthMultisampled2D {
pub width: u32,
pub height: u32,
pub sample_count: u32,
pub samples: std::vec::Vec<std::vec::Vec<std::vec::Vec<f32>>>,
}
impl TextureDataDepthMultisampled2D {
pub fn new(width: u32, height: u32, sample_count: u32) -> Self {
let samples_per_pixel = vec![0.0f32; sample_count as usize];
let row = vec![samples_per_pixel; width as usize];
let pixels = vec![row; height as usize];
Self {
width,
height,
sample_count,
samples: pixels,
}
}
pub fn dimensions(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn num_samples(&self) -> u32 {
self.sample_count
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum FilterMode {
#[default]
Nearest,
Linear,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum AddressMode {
#[default]
ClampToEdge,
Repeat,
MirrorRepeat,
}
#[derive(Clone, Debug)]
pub struct SamplerState {
pub mag_filter: FilterMode,
pub min_filter: FilterMode,
pub mipmap_filter: FilterMode,
pub address_mode_u: AddressMode,
pub address_mode_v: AddressMode,
pub address_mode_w: AddressMode,
pub lod_min_clamp: f32,
pub lod_max_clamp: f32,
}
impl Default for SamplerState {
fn default() -> Self {
Self {
mag_filter: FilterMode::Nearest,
min_filter: FilterMode::Nearest,
mipmap_filter: FilterMode::Nearest,
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
address_mode_w: AddressMode::ClampToEdge,
lod_min_clamp: 0.0,
lod_max_clamp: 1000.0,
}
}
}
impl SamplerState {
pub fn new() -> Self {
Self::default()
}
pub fn linear() -> Self {
Self {
mag_filter: FilterMode::Linear,
min_filter: FilterMode::Linear,
mipmap_filter: FilterMode::Linear,
..Default::default()
}
}
pub fn apply_address_mode(mode: AddressMode, coord: f32) -> f32 {
match mode {
AddressMode::ClampToEdge => coord.clamp(0.0, 1.0),
AddressMode::Repeat => coord.rem_euclid(1.0),
AddressMode::MirrorRepeat => {
let t = coord.rem_euclid(2.0);
if t > 1.0 { 2.0 - t } else { t }
}
}
}
}
#[derive(Clone, Debug)]
pub struct SamplerComparisonState {
pub sampler: SamplerState,
pub compare: CompareFunction,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum CompareFunction {
Never,
Less,
Equal,
#[default]
LessEqual,
Greater,
NotEqual,
GreaterEqual,
Always,
}
impl CompareFunction {
pub fn compare(self, sample: f32, reference: f32) -> f32 {
let result = match self {
CompareFunction::Never => false,
CompareFunction::Less => reference < sample,
CompareFunction::Equal => (sample - reference).abs() < f32::EPSILON,
CompareFunction::LessEqual => reference <= sample,
CompareFunction::Greater => reference > sample,
CompareFunction::NotEqual => (sample - reference).abs() >= f32::EPSILON,
CompareFunction::GreaterEqual => reference >= sample,
CompareFunction::Always => true,
};
if result { 1.0 } else { 0.0 }
}
}
impl Default for SamplerComparisonState {
fn default() -> Self {
Self {
sampler: SamplerState::default(),
compare: CompareFunction::LessEqual,
}
}
}
pub trait TextureDimensionsQuery {
type Output;
fn query_dimensions(&self, level: u32) -> Self::Output;
}
pub trait TextureNumLayersQuery {
fn query_num_layers(&self) -> u32;
}
pub trait TextureNumLevelsQuery {
fn query_num_levels(&self) -> u32;
}
pub trait TextureNumSamplesQuery {
fn query_num_samples(&self) -> u32;
}
pub trait TextureLoad<Coords, Level> {
type Output;
fn load(&self, coords: Coords, level: Level) -> Self::Output;
}
pub trait TextureLoadMultisampled<Coords, SampleIndex> {
type Output;
fn load_multisampled(&self, coords: Coords, sample_index: SampleIndex) -> Self::Output;
}
pub trait TextureLoadArray<Coords, ArrayIndex, Level> {
type Output;
fn load_array(&self, coords: Coords, array_index: ArrayIndex, level: Level) -> Self::Output;
}
pub trait TextureSample<Coords> {
type Output;
fn sample(&self, sampler: &Sampler, coords: Coords) -> Self::Output;
}
pub trait TextureSampleOffset<Coords, Offset> {
type Output;
fn sample_offset(&self, sampler: &Sampler, coords: Coords, offset: Offset) -> Self::Output;
}
pub trait TextureSampleArray<Coords, ArrayIndex> {
type Output;
fn sample_array(
&self,
sampler: &Sampler,
coords: Coords,
array_index: ArrayIndex,
) -> Self::Output;
}
pub trait TextureSampleLevel<Coords, Level> {
type Output;
fn sample_level(&self, sampler: &Sampler, coords: Coords, level: Level) -> Self::Output;
}
pub trait TextureSampleBias<Coords, Bias> {
type Output;
fn sample_bias(&self, sampler: &Sampler, coords: Coords, bias: Bias) -> Self::Output;
}
pub trait TextureSampleGrad<Coords, Ddx, Ddy> {
type Output;
fn sample_grad(&self, sampler: &Sampler, coords: Coords, ddx: Ddx, ddy: Ddy) -> Self::Output;
}
pub trait TextureSampleCompare<Coords, DepthRef> {
fn sample_compare(
&self,
sampler: &SamplerComparison,
coords: Coords,
depth_ref: DepthRef,
) -> f32;
}
pub trait TextureSampleCompareLevel<Coords, DepthRef> {
fn sample_compare_level(
&self,
sampler: &SamplerComparison,
coords: Coords,
depth_ref: DepthRef,
) -> f32;
}
pub trait TextureSampleBaseClampToEdge<Coords> {
type Output;
fn sample_base_clamp_to_edge(&self, sampler: &Sampler, coords: Coords) -> Self::Output;
}
pub trait TextureGather<Coords> {
type Output;
fn gather(&self, component: u32, sampler: &Sampler, coords: Coords) -> Self::Output;
}
pub trait TextureGatherDepth<Coords> {
fn gather_depth(&self, sampler: &Sampler, coords: Coords) -> Vec4f;
}
pub trait TextureGatherCompare<Coords, DepthRef> {
fn gather_compare(
&self,
sampler: &SamplerComparison,
coords: Coords,
depth_ref: DepthRef,
) -> Vec4f;
}
pub trait TextureSampleArrayOffset<Coords, ArrayIndex, Offset> {
type Output;
fn sample_array_offset(
&self,
sampler: &Sampler,
coords: Coords,
array_index: ArrayIndex,
offset: Offset,
) -> Self::Output;
}
pub trait TextureSampleLevelOffset<Coords, Level, Offset> {
type Output;
fn sample_level_offset(
&self,
sampler: &Sampler,
coords: Coords,
level: Level,
offset: Offset,
) -> Self::Output;
}
pub trait TextureSampleLevelArray<Coords, ArrayIndex, Level> {
type Output;
fn sample_level_array(
&self,
sampler: &Sampler,
coords: Coords,
array_index: ArrayIndex,
level: Level,
) -> Self::Output;
}
pub trait TextureSampleLevelArrayOffset<Coords, ArrayIndex, Level, Offset> {
type Output;
fn sample_level_array_offset(
&self,
sampler: &Sampler,
coords: Coords,
array_index: ArrayIndex,
level: Level,
offset: Offset,
) -> Self::Output;
}
pub trait TextureSampleBiasOffset<Coords, Bias, Offset> {
type Output;
fn sample_bias_offset(
&self,
sampler: &Sampler,
coords: Coords,
bias: Bias,
offset: Offset,
) -> Self::Output;
}
pub trait TextureSampleBiasArray<Coords, ArrayIndex, Bias> {
type Output;
fn sample_bias_array(
&self,
sampler: &Sampler,
coords: Coords,
array_index: ArrayIndex,
bias: Bias,
) -> Self::Output;
}
pub trait TextureSampleBiasArrayOffset<Coords, ArrayIndex, Bias, Offset> {
type Output;
fn sample_bias_array_offset(
&self,
sampler: &Sampler,
coords: Coords,
array_index: ArrayIndex,
bias: Bias,
offset: Offset,
) -> Self::Output;
}
pub trait TextureSampleGradOffset<Coords, Ddx, Ddy, Offset> {
type Output;
fn sample_grad_offset(
&self,
sampler: &Sampler,
coords: Coords,
ddx: Ddx,
ddy: Ddy,
offset: Offset,
) -> Self::Output;
}
pub trait TextureSampleGradArray<Coords, ArrayIndex, Ddx, Ddy> {
type Output;
fn sample_grad_array(
&self,
sampler: &Sampler,
coords: Coords,
array_index: ArrayIndex,
ddx: Ddx,
ddy: Ddy,
) -> Self::Output;
}
pub trait TextureSampleGradArrayOffset<Coords, ArrayIndex, Ddx, Ddy, Offset> {
type Output;
fn sample_grad_array_offset(
&self,
sampler: &Sampler,
coords: Coords,
array_index: ArrayIndex,
ddx: Ddx,
ddy: Ddy,
offset: Offset,
) -> Self::Output;
}
pub trait TextureSampleCompareOffset<Coords, DepthRef, Offset> {
fn sample_compare_offset(
&self,
sampler: &SamplerComparison,
coords: Coords,
depth_ref: DepthRef,
offset: Offset,
) -> f32;
}
pub trait TextureSampleCompareArray<Coords, ArrayIndex, DepthRef> {
fn sample_compare_array(
&self,
sampler: &SamplerComparison,
coords: Coords,
array_index: ArrayIndex,
depth_ref: DepthRef,
) -> f32;
}
pub trait TextureSampleCompareArrayOffset<Coords, ArrayIndex, DepthRef, Offset> {
fn sample_compare_array_offset(
&self,
sampler: &SamplerComparison,
coords: Coords,
array_index: ArrayIndex,
depth_ref: DepthRef,
offset: Offset,
) -> f32;
}
pub trait TextureSampleCompareLevelOffset<Coords, DepthRef, Offset> {
fn sample_compare_level_offset(
&self,
sampler: &SamplerComparison,
coords: Coords,
depth_ref: DepthRef,
offset: Offset,
) -> f32;
}
pub trait TextureSampleCompareLevelArray<Coords, ArrayIndex, DepthRef> {
fn sample_compare_level_array(
&self,
sampler: &SamplerComparison,
coords: Coords,
array_index: ArrayIndex,
depth_ref: DepthRef,
) -> f32;
}
pub trait TextureSampleCompareLevelArrayOffset<Coords, ArrayIndex, DepthRef, Offset> {
fn sample_compare_level_array_offset(
&self,
sampler: &SamplerComparison,
coords: Coords,
array_index: ArrayIndex,
depth_ref: DepthRef,
offset: Offset,
) -> f32;
}
pub trait TextureGatherOffset<Coords, Offset> {
type Output;
fn gather_offset(
&self,
component: u32,
sampler: &Sampler,
coords: Coords,
offset: Offset,
) -> Self::Output;
}
pub trait TextureGatherArray<Coords, ArrayIndex> {
type Output;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Coords,
array_index: ArrayIndex,
) -> Self::Output;
}
pub trait TextureGatherArrayOffset<Coords, ArrayIndex, Offset> {
type Output;
fn gather_array_offset(
&self,
component: u32,
sampler: &Sampler,
coords: Coords,
array_index: ArrayIndex,
offset: Offset,
) -> Self::Output;
}
pub trait TextureGatherDepthOffset<Coords, Offset> {
fn gather_depth_offset(&self, sampler: &Sampler, coords: Coords, offset: Offset) -> Vec4f;
}
pub trait TextureGatherDepthArray<Coords, ArrayIndex> {
fn gather_depth_array(
&self,
sampler: &Sampler,
coords: Coords,
array_index: ArrayIndex,
) -> Vec4f;
}
pub trait TextureGatherDepthArrayOffset<Coords, ArrayIndex, Offset> {
fn gather_depth_array_offset(
&self,
sampler: &Sampler,
coords: Coords,
array_index: ArrayIndex,
offset: Offset,
) -> Vec4f;
}
pub trait TextureGatherCompareOffset<Coords, DepthRef, Offset> {
fn gather_compare_offset(
&self,
sampler: &SamplerComparison,
coords: Coords,
depth_ref: DepthRef,
offset: Offset,
) -> Vec4f;
}
pub trait TextureGatherCompareArray<Coords, ArrayIndex, DepthRef> {
fn gather_compare_array(
&self,
sampler: &SamplerComparison,
coords: Coords,
array_index: ArrayIndex,
depth_ref: DepthRef,
) -> Vec4f;
}
pub trait TextureGatherCompareArrayOffset<Coords, ArrayIndex, DepthRef, Offset> {
fn gather_compare_array_offset(
&self,
sampler: &SamplerComparison,
coords: Coords,
array_index: ArrayIndex,
depth_ref: DepthRef,
offset: Offset,
) -> Vec4f;
}
pub trait TextureStore<Coords, Value> {
fn store(&self, coords: Coords, value: Value);
}
pub trait TextureStoreArray<Coords, ArrayIndex, Value> {
fn store_array(&self, coords: Coords, array_index: ArrayIndex, value: Value);
}
pub trait TextureLoadStorage<Coords> {
type Output;
fn load_storage(&self, coords: Coords) -> Self::Output;
}
pub trait TextureLoadStorageArray<Coords, ArrayIndex> {
type Output;
fn load_storage_array(&self, coords: Coords, array_index: ArrayIndex) -> Self::Output;
}
pub trait IntoCoord {
fn into_i32(self) -> i32;
fn into_u32(self) -> u32;
}
impl IntoCoord for i32 {
fn into_i32(self) -> i32 {
self
}
fn into_u32(self) -> u32 {
self.max(0) as u32
}
}
impl IntoCoord for u32 {
fn into_i32(self) -> i32 {
self as i32
}
fn into_u32(self) -> u32 {
self
}
}
pub trait IntoLevel {
fn into_level(self) -> u32;
}
impl IntoLevel for i32 {
fn into_level(self) -> u32 {
self.max(0) as u32
}
}
impl IntoLevel for u32 {
fn into_level(self) -> u32 {
self
}
}
pub fn texture_dimensions<T: TextureDimensionsQuery>(t: &T) -> T::Output {
t.query_dimensions(0)
}
pub fn texture_dimensions_level<T: TextureDimensionsQuery>(
t: &T,
level: impl IntoLevel,
) -> T::Output {
t.query_dimensions(level.into_level())
}
pub fn texture_num_layers<T: TextureNumLayersQuery>(t: &T) -> u32 {
t.query_num_layers()
}
pub fn texture_num_levels<T: TextureNumLevelsQuery>(t: &T) -> u32 {
t.query_num_levels()
}
pub fn texture_num_samples<T: TextureNumSamplesQuery>(t: &T) -> u32 {
t.query_num_samples()
}
pub fn texture_load<T, C, L>(t: &T, coords: C, level: L) -> T::Output
where
T: TextureLoad<C, L>,
{
t.load(coords, level)
}
pub fn texture_load_multisampled<T, C, S>(t: &T, coords: C, sample_index: S) -> T::Output
where
T: TextureLoadMultisampled<C, S>,
{
t.load_multisampled(coords, sample_index)
}
pub fn texture_load_array<T, C, A, L>(t: &T, coords: C, array_index: A, level: L) -> T::Output
where
T: TextureLoadArray<C, A, L>,
{
t.load_array(coords, array_index, level)
}
pub fn texture_sample<T, C>(t: &T, s: &Sampler, coords: C) -> T::Output
where
T: TextureSample<C>,
{
t.sample(s, coords)
}
pub fn texture_sample_offset<T, C, O>(t: &T, s: &Sampler, coords: C, offset: O) -> T::Output
where
T: TextureSampleOffset<C, O>,
{
t.sample_offset(s, coords, offset)
}
pub fn texture_sample_array<T, C, A>(t: &T, s: &Sampler, coords: C, array_index: A) -> T::Output
where
T: TextureSampleArray<C, A>,
{
t.sample_array(s, coords, array_index)
}
pub fn texture_sample_level<T, C, L>(t: &T, s: &Sampler, coords: C, level: L) -> T::Output
where
T: TextureSampleLevel<C, L>,
{
t.sample_level(s, coords, level)
}
pub fn texture_sample_bias<T, C, B>(t: &T, s: &Sampler, coords: C, bias: B) -> T::Output
where
T: TextureSampleBias<C, B>,
{
t.sample_bias(s, coords, bias)
}
pub fn texture_sample_grad<T, C, D>(t: &T, s: &Sampler, coords: C, ddx: D, ddy: D) -> T::Output
where
T: TextureSampleGrad<C, D, D>,
{
t.sample_grad(s, coords, ddx, ddy)
}
pub fn texture_sample_compare<T, C, R>(t: &T, s: &SamplerComparison, coords: C, depth_ref: R) -> f32
where
T: TextureSampleCompare<C, R>,
{
t.sample_compare(s, coords, depth_ref)
}
pub fn texture_sample_compare_level<T, C, R>(
t: &T,
s: &SamplerComparison,
coords: C,
depth_ref: R,
) -> f32
where
T: TextureSampleCompareLevel<C, R>,
{
t.sample_compare_level(s, coords, depth_ref)
}
pub fn texture_sample_base_clamp_to_edge<T, C>(t: &T, s: &Sampler, coords: C) -> T::Output
where
T: TextureSampleBaseClampToEdge<C>,
{
t.sample_base_clamp_to_edge(s, coords)
}
pub fn texture_gather<T, C>(component: impl IntoCoord, t: &T, s: &Sampler, coords: C) -> T::Output
where
T: TextureGather<C>,
{
t.gather(component.into_u32(), s, coords)
}
pub fn texture_gather_depth<T, C>(t: &T, s: &Sampler, coords: C) -> Vec4f
where
T: TextureGatherDepth<C>,
{
t.gather_depth(s, coords)
}
pub fn texture_gather_compare<T, C, R>(
t: &T,
s: &SamplerComparison,
coords: C,
depth_ref: R,
) -> Vec4f
where
T: TextureGatherCompare<C, R>,
{
t.gather_compare(s, coords, depth_ref)
}
pub fn texture_sample_array_offset<T, C, A, O>(
t: &T,
s: &Sampler,
coords: C,
array_index: A,
offset: O,
) -> T::Output
where
T: TextureSampleArrayOffset<C, A, O>,
{
t.sample_array_offset(s, coords, array_index, offset)
}
pub fn texture_sample_level_offset<T, C, L, O>(
t: &T,
s: &Sampler,
coords: C,
level: L,
offset: O,
) -> T::Output
where
T: TextureSampleLevelOffset<C, L, O>,
{
t.sample_level_offset(s, coords, level, offset)
}
pub fn texture_sample_level_array<T, C, A, L>(
t: &T,
s: &Sampler,
coords: C,
array_index: A,
level: L,
) -> T::Output
where
T: TextureSampleLevelArray<C, A, L>,
{
t.sample_level_array(s, coords, array_index, level)
}
pub fn texture_sample_level_array_offset<T, C, A, L, O>(
t: &T,
s: &Sampler,
coords: C,
array_index: A,
level: L,
offset: O,
) -> T::Output
where
T: TextureSampleLevelArrayOffset<C, A, L, O>,
{
t.sample_level_array_offset(s, coords, array_index, level, offset)
}
pub fn texture_sample_bias_offset<T, C, B, O>(
t: &T,
s: &Sampler,
coords: C,
bias: B,
offset: O,
) -> T::Output
where
T: TextureSampleBiasOffset<C, B, O>,
{
t.sample_bias_offset(s, coords, bias, offset)
}
pub fn texture_sample_bias_array<T, C, A, B>(
t: &T,
s: &Sampler,
coords: C,
array_index: A,
bias: B,
) -> T::Output
where
T: TextureSampleBiasArray<C, A, B>,
{
t.sample_bias_array(s, coords, array_index, bias)
}
pub fn texture_sample_bias_array_offset<T, C, A, B, O>(
t: &T,
s: &Sampler,
coords: C,
array_index: A,
bias: B,
offset: O,
) -> T::Output
where
T: TextureSampleBiasArrayOffset<C, A, B, O>,
{
t.sample_bias_array_offset(s, coords, array_index, bias, offset)
}
pub fn texture_sample_grad_offset<T, C, D, O>(
t: &T,
s: &Sampler,
coords: C,
ddx: D,
ddy: D,
offset: O,
) -> T::Output
where
T: TextureSampleGradOffset<C, D, D, O>,
{
t.sample_grad_offset(s, coords, ddx, ddy, offset)
}
pub fn texture_sample_grad_array<T, C, A, D>(
t: &T,
s: &Sampler,
coords: C,
array_index: A,
ddx: D,
ddy: D,
) -> T::Output
where
T: TextureSampleGradArray<C, A, D, D>,
{
t.sample_grad_array(s, coords, array_index, ddx, ddy)
}
pub fn texture_sample_grad_array_offset<T, C, A, D, O>(
t: &T,
s: &Sampler,
coords: C,
array_index: A,
ddx: D,
ddy: D,
offset: O,
) -> T::Output
where
T: TextureSampleGradArrayOffset<C, A, D, D, O>,
{
t.sample_grad_array_offset(s, coords, array_index, ddx, ddy, offset)
}
pub fn texture_sample_compare_offset<T, C, R, O>(
t: &T,
s: &SamplerComparison,
coords: C,
depth_ref: R,
offset: O,
) -> f32
where
T: TextureSampleCompareOffset<C, R, O>,
{
t.sample_compare_offset(s, coords, depth_ref, offset)
}
pub fn texture_sample_compare_array<T, C, A, R>(
t: &T,
s: &SamplerComparison,
coords: C,
array_index: A,
depth_ref: R,
) -> f32
where
T: TextureSampleCompareArray<C, A, R>,
{
t.sample_compare_array(s, coords, array_index, depth_ref)
}
pub fn texture_sample_compare_array_offset<T, C, A, R, O>(
t: &T,
s: &SamplerComparison,
coords: C,
array_index: A,
depth_ref: R,
offset: O,
) -> f32
where
T: TextureSampleCompareArrayOffset<C, A, R, O>,
{
t.sample_compare_array_offset(s, coords, array_index, depth_ref, offset)
}
pub fn texture_sample_compare_level_offset<T, C, R, O>(
t: &T,
s: &SamplerComparison,
coords: C,
depth_ref: R,
offset: O,
) -> f32
where
T: TextureSampleCompareLevelOffset<C, R, O>,
{
t.sample_compare_level_offset(s, coords, depth_ref, offset)
}
pub fn texture_sample_compare_level_array<T, C, A, R>(
t: &T,
s: &SamplerComparison,
coords: C,
array_index: A,
depth_ref: R,
) -> f32
where
T: TextureSampleCompareLevelArray<C, A, R>,
{
t.sample_compare_level_array(s, coords, array_index, depth_ref)
}
pub fn texture_sample_compare_level_array_offset<T, C, A, R, O>(
t: &T,
s: &SamplerComparison,
coords: C,
array_index: A,
depth_ref: R,
offset: O,
) -> f32
where
T: TextureSampleCompareLevelArrayOffset<C, A, R, O>,
{
t.sample_compare_level_array_offset(s, coords, array_index, depth_ref, offset)
}
pub fn texture_gather_offset<T, C, O>(
component: impl IntoCoord,
t: &T,
s: &Sampler,
coords: C,
offset: O,
) -> T::Output
where
T: TextureGatherOffset<C, O>,
{
t.gather_offset(component.into_u32(), s, coords, offset)
}
pub fn texture_gather_array<T, C, A>(
component: impl IntoCoord,
t: &T,
s: &Sampler,
coords: C,
array_index: A,
) -> T::Output
where
T: TextureGatherArray<C, A>,
{
t.gather_array(component.into_u32(), s, coords, array_index)
}
pub fn texture_gather_array_offset<T, C, A, O>(
component: impl IntoCoord,
t: &T,
s: &Sampler,
coords: C,
array_index: A,
offset: O,
) -> T::Output
where
T: TextureGatherArrayOffset<C, A, O>,
{
t.gather_array_offset(component.into_u32(), s, coords, array_index, offset)
}
pub fn texture_gather_depth_offset<T, C, O>(t: &T, s: &Sampler, coords: C, offset: O) -> Vec4f
where
T: TextureGatherDepthOffset<C, O>,
{
t.gather_depth_offset(s, coords, offset)
}
pub fn texture_gather_depth_array<T, C, A>(t: &T, s: &Sampler, coords: C, array_index: A) -> Vec4f
where
T: TextureGatherDepthArray<C, A>,
{
t.gather_depth_array(s, coords, array_index)
}
pub fn texture_gather_depth_array_offset<T, C, A, O>(
t: &T,
s: &Sampler,
coords: C,
array_index: A,
offset: O,
) -> Vec4f
where
T: TextureGatherDepthArrayOffset<C, A, O>,
{
t.gather_depth_array_offset(s, coords, array_index, offset)
}
pub fn texture_gather_compare_offset<T, C, R, O>(
t: &T,
s: &SamplerComparison,
coords: C,
depth_ref: R,
offset: O,
) -> Vec4f
where
T: TextureGatherCompareOffset<C, R, O>,
{
t.gather_compare_offset(s, coords, depth_ref, offset)
}
pub fn texture_gather_compare_array<T, C, A, R>(
t: &T,
s: &SamplerComparison,
coords: C,
array_index: A,
depth_ref: R,
) -> Vec4f
where
T: TextureGatherCompareArray<C, A, R>,
{
t.gather_compare_array(s, coords, array_index, depth_ref)
}
pub fn texture_gather_compare_array_offset<T, C, A, R, O>(
t: &T,
s: &SamplerComparison,
coords: C,
array_index: A,
depth_ref: R,
offset: O,
) -> Vec4f
where
T: TextureGatherCompareArrayOffset<C, A, R, O>,
{
t.gather_compare_array_offset(s, coords, array_index, depth_ref, offset)
}
pub fn texture_store<T, C, V>(t: &T, coords: C, value: V)
where
T: TextureStore<C, V>,
{
t.store(coords, value)
}
pub fn texture_store_array<T, C, A, V>(t: &T, coords: C, array_index: A, value: V)
where
T: TextureStoreArray<C, A, V>,
{
t.store_array(coords, array_index, value)
}
pub fn texture_load_storage<T, C>(t: &T, coords: C) -> T::Output
where
T: TextureLoadStorage<C>,
{
t.load_storage(coords)
}
pub fn texture_load_storage_array<T, C, A>(t: &T, coords: C, array_index: A) -> T::Output
where
T: TextureLoadStorageArray<C, A>,
{
t.load_storage_array(coords, array_index)
}
fn bilinear_interpolate<T: Copy + Default + Into<f32> + From<f32>>(
tl: [T; 4],
tr: [T; 4],
bl: [T; 4],
br: [T; 4],
fx: f32,
fy: f32,
) -> [T; 4] {
std::array::from_fn(|i| {
let top = tl[i].into() * (1.0 - fx) + tr[i].into() * fx;
let bottom = bl[i].into() * (1.0 - fx) + br[i].into() * fx;
T::from(top * (1.0 - fy) + bottom * fy)
})
}
fn normalized_to_texel(coord: f32, size: u32) -> (u32, f32) {
let scaled = coord * size as f32;
let texel = (scaled.floor() as i32).clamp(0, (size as i32) - 1) as u32;
let frac = scaled.fract().abs();
(texel, frac)
}
pub(crate) fn sample_texture_2d<T: Copy + Default + Into<f32> + From<f32>>(
data: &TextureData2D<T>,
sampler_state: &SamplerState,
u: f32,
v: f32,
level: u32,
) -> [T; 4] {
let u = SamplerState::apply_address_mode(sampler_state.address_mode_u, u);
let v = SamplerState::apply_address_mode(sampler_state.address_mode_v, v);
let (width, height) = data.dimensions(level);
if width == 0 || height == 0 {
return std::array::from_fn(|_| T::default());
}
match sampler_state.min_filter {
FilterMode::Nearest => {
let x = Ord::min((u * width as f32).floor() as u32, width - 1);
let y = Ord::min((v * height as f32).floor() as u32, height - 1);
data.get_pixel(x, y, level)
.copied()
.unwrap_or_else(|| std::array::from_fn(|_| T::default()))
}
FilterMode::Linear => {
let (x0, fx) = normalized_to_texel(u - 0.5 / width as f32, width);
let (y0, fy) = normalized_to_texel(v - 0.5 / height as f32, height);
let x1 = Ord::min(x0 + 1, width - 1);
let y1 = Ord::min(y0 + 1, height - 1);
let default_pixel = || std::array::from_fn(|_| T::default());
let tl = data
.get_pixel(x0, y0, level)
.copied()
.unwrap_or_else(default_pixel);
let tr = data
.get_pixel(x1, y0, level)
.copied()
.unwrap_or_else(default_pixel);
let bl = data
.get_pixel(x0, y1, level)
.copied()
.unwrap_or_else(default_pixel);
let br = data
.get_pixel(x1, y1, level)
.copied()
.unwrap_or_else(default_pixel);
bilinear_interpolate(tl, tr, bl, br, fx, fy)
}
}
}
pub(crate) fn sample_depth_texture_2d(
data: &TextureDataDepth2D,
sampler_state: &SamplerState,
u: f32,
v: f32,
level: u32,
) -> f32 {
let u = SamplerState::apply_address_mode(sampler_state.address_mode_u, u);
let v = SamplerState::apply_address_mode(sampler_state.address_mode_v, v);
let (width, height) = data.dimensions(level);
if width == 0 || height == 0 {
return 0.0;
}
match sampler_state.min_filter {
FilterMode::Nearest => {
let x = Ord::min((u * width as f32).floor() as u32, width - 1);
let y = Ord::min((v * height as f32).floor() as u32, height - 1);
data.get_depth(x, y, level).unwrap_or(0.0)
}
FilterMode::Linear => {
let (x0, fx) = normalized_to_texel(u - 0.5 / width as f32, width);
let (y0, fy) = normalized_to_texel(v - 0.5 / height as f32, height);
let x1 = Ord::min(x0 + 1, width - 1);
let y1 = Ord::min(y0 + 1, height - 1);
let tl = data.get_depth(x0, y0, level).unwrap_or(0.0);
let tr = data.get_depth(x1, y0, level).unwrap_or(0.0);
let bl = data.get_depth(x0, y1, level).unwrap_or(0.0);
let br = data.get_depth(x1, y1, level).unwrap_or(0.0);
let top = tl * (1.0 - fx) + tr * fx;
let bottom = bl * (1.0 - fx) + br * fx;
top * (1.0 - fy) + bottom * fy
}
}
}
pub(crate) fn gather_4_depth_texels(
data: &TextureDataDepth2D,
sampler_state: &SamplerState,
u: f32,
v: f32,
) -> [f32; 4] {
let u = SamplerState::apply_address_mode(sampler_state.address_mode_u, u);
let v = SamplerState::apply_address_mode(sampler_state.address_mode_v, v);
let (width, height) = data.dimensions(0);
if width == 0 || height == 0 {
return [0.0; 4];
}
let (x0, _fx) = normalized_to_texel(u - 0.5 / width as f32, width);
let (y0, _fy) = normalized_to_texel(v - 0.5 / height as f32, height);
let x1 = Ord::min(x0 + 1, width - 1);
let y1 = Ord::min(y0 + 1, height - 1);
let tl = data.get_depth(x0, y0, 0).unwrap_or(0.0); let tr = data.get_depth(x1, y0, 0).unwrap_or(0.0); let bl = data.get_depth(x0, y1, 0).unwrap_or(0.0); let br = data.get_depth(x1, y1, 0).unwrap_or(0.0);
[bl, br, tr, tl]
}
#[allow(clippy::too_many_arguments)]
fn trilinear_interpolate<T: Copy + Default + Into<f32> + From<f32>>(
c000: [T; 4],
c100: [T; 4],
c010: [T; 4],
c110: [T; 4],
c001: [T; 4],
c101: [T; 4],
c011: [T; 4],
c111: [T; 4],
fx: f32,
fy: f32,
fz: f32,
) -> [T; 4] {
let lerp = |a: f32, b: f32, t: f32| a * (1.0 - t) + b * t;
std::array::from_fn(|i| {
let top = lerp(
lerp(c000[i].into(), c100[i].into(), fx),
lerp(c010[i].into(), c110[i].into(), fx),
fy,
);
let bottom = lerp(
lerp(c001[i].into(), c101[i].into(), fx),
lerp(c011[i].into(), c111[i].into(), fx),
fy,
);
T::from(lerp(top, bottom, fz))
})
}
pub(crate) fn sample_texture_1d<T: Copy + Default + Into<f32> + From<f32>>(
data: &TextureData1D<T>,
sampler_state: &SamplerState,
u: f32,
level: u32,
) -> [T; 4] {
let u = SamplerState::apply_address_mode(sampler_state.address_mode_u, u);
let width = data.width(level);
if width == 0 {
return std::array::from_fn(|_| T::default());
}
match sampler_state.min_filter {
FilterMode::Nearest => {
let x = Ord::min((u * width as f32).floor() as u32, width - 1);
data.get_pixel(x, level)
.copied()
.unwrap_or_else(|| std::array::from_fn(|_| T::default()))
}
FilterMode::Linear => {
let (x0, fx) = normalized_to_texel(u - 0.5 / width as f32, width);
let x1 = Ord::min(x0 + 1, width - 1);
let left = data
.get_pixel(x0, level)
.copied()
.unwrap_or_else(|| std::array::from_fn(|_| T::default()));
let right = data
.get_pixel(x1, level)
.copied()
.unwrap_or_else(|| std::array::from_fn(|_| T::default()));
std::array::from_fn(|i| T::from(left[i].into() * (1.0 - fx) + right[i].into() * fx))
}
}
}
pub(crate) fn sample_texture_3d<T: Copy + Default + Into<f32> + From<f32>>(
data: &TextureData3D<T>,
sampler_state: &SamplerState,
u: f32,
v: f32,
w: f32,
level: u32,
) -> [T; 4] {
let u = SamplerState::apply_address_mode(sampler_state.address_mode_u, u);
let v = SamplerState::apply_address_mode(sampler_state.address_mode_v, v);
let w = SamplerState::apply_address_mode(sampler_state.address_mode_w, w);
let (width, height, depth) = data.dimensions(level);
if width == 0 || height == 0 || depth == 0 {
return std::array::from_fn(|_| T::default());
}
match sampler_state.min_filter {
FilterMode::Nearest => {
let x = Ord::min((u * width as f32).floor() as u32, width - 1);
let y = Ord::min((v * height as f32).floor() as u32, height - 1);
let z = Ord::min((w * depth as f32).floor() as u32, depth - 1);
data.get_pixel(x, y, z, level)
.copied()
.unwrap_or_else(|| std::array::from_fn(|_| T::default()))
}
FilterMode::Linear => {
let (x0, fx) = normalized_to_texel(u - 0.5 / width as f32, width);
let (y0, fy) = normalized_to_texel(v - 0.5 / height as f32, height);
let (z0, fz) = normalized_to_texel(w - 0.5 / depth as f32, depth);
let x1 = Ord::min(x0 + 1, width - 1);
let y1 = Ord::min(y0 + 1, height - 1);
let z1 = Ord::min(z0 + 1, depth - 1);
let default_pixel = || std::array::from_fn(|_| T::default());
let c000 = data
.get_pixel(x0, y0, z0, level)
.copied()
.unwrap_or_else(default_pixel);
let c100 = data
.get_pixel(x1, y0, z0, level)
.copied()
.unwrap_or_else(default_pixel);
let c010 = data
.get_pixel(x0, y1, z0, level)
.copied()
.unwrap_or_else(default_pixel);
let c110 = data
.get_pixel(x1, y1, z0, level)
.copied()
.unwrap_or_else(default_pixel);
let c001 = data
.get_pixel(x0, y0, z1, level)
.copied()
.unwrap_or_else(default_pixel);
let c101 = data
.get_pixel(x1, y0, z1, level)
.copied()
.unwrap_or_else(default_pixel);
let c011 = data
.get_pixel(x0, y1, z1, level)
.copied()
.unwrap_or_else(default_pixel);
let c111 = data
.get_pixel(x1, y1, z1, level)
.copied()
.unwrap_or_else(default_pixel);
trilinear_interpolate(c000, c100, c010, c110, c001, c101, c011, c111, fx, fy, fz)
}
}
}
fn select_cube_face(dx: f32, dy: f32, dz: f32) -> (usize, f32, f32) {
let ax = dx.abs();
let ay = dy.abs();
let az = dz.abs();
let ma = ax.max(ay).max(az);
if ma == 0.0 {
return (0, 0.5, 0.5);
}
if ax == ma {
if dx >= 0.0 {
(0, 0.5 - dz / (2.0 * ma), 0.5 - dy / (2.0 * ma))
} else {
(1, 0.5 + dz / (2.0 * ma), 0.5 - dy / (2.0 * ma))
}
} else if ay == ma {
if dy >= 0.0 {
(2, 0.5 + dx / (2.0 * ma), 0.5 + dz / (2.0 * ma))
} else {
(3, 0.5 + dx / (2.0 * ma), 0.5 - dz / (2.0 * ma))
}
} else {
if dz >= 0.0 {
(4, 0.5 + dx / (2.0 * ma), 0.5 - dy / (2.0 * ma))
} else {
(5, 0.5 - dx / (2.0 * ma), 0.5 - dy / (2.0 * ma))
}
}
}
pub(crate) fn sample_texture_cube<T: Copy + Default + Into<f32> + From<f32>>(
data: &TextureDataCube<T>,
sampler_state: &SamplerState,
dx: f32,
dy: f32,
dz: f32,
level: u32,
) -> [T; 4] {
let (face, u, v) = select_cube_face(dx, dy, dz);
sample_texture_2d(&data.faces[face], sampler_state, u, v, level)
}
pub(crate) fn sample_texture_cube_array<T: Copy + Default + Into<f32> + From<f32>>(
data: &TextureDataCubeArray<T>,
sampler_state: &SamplerState,
dx: f32,
dy: f32,
dz: f32,
array_index: u32,
level: u32,
) -> [T; 4] {
let layer = (array_index as usize).min(data.cubes.len().saturating_sub(1));
data.cubes
.get(layer)
.map(|cube| sample_texture_cube(cube, sampler_state, dx, dy, dz, level))
.unwrap_or_else(|| std::array::from_fn(|_| T::default()))
}
pub(crate) fn gather_texture_2d<T: Copy + Default>(
data: &TextureData2D<T>,
sampler_state: &SamplerState,
u: f32,
v: f32,
component: u32,
) -> [T; 4] {
let (w, h) = data.dimensions(0);
if w == 0 || h == 0 {
return std::array::from_fn(|_| T::default());
}
let u = SamplerState::apply_address_mode(sampler_state.address_mode_u, u);
let v = SamplerState::apply_address_mode(sampler_state.address_mode_v, v);
let texel_x = (u * w as f32 - 0.5).floor();
let texel_y = (v * h as f32 - 0.5).floor();
let c = component.min(3) as usize;
let get = |x: f32, y: f32| -> T {
let xi = (x as i32).clamp(0, w as i32 - 1) as u32;
let yi = (y as i32).clamp(0, h as i32 - 1) as u32;
data.get_pixel(xi, yi, 0).map(|p| p[c]).unwrap_or_default()
};
[
get(texel_x, texel_y + 1.0),
get(texel_x + 1.0, texel_y + 1.0),
get(texel_x + 1.0, texel_y),
get(texel_x, texel_y),
]
}
pub(crate) fn gather_texture_cube<T: Copy + Default>(
data: &TextureDataCube<T>,
sampler_state: &SamplerState,
dx: f32,
dy: f32,
dz: f32,
component: u32,
) -> [T; 4] {
let (face, u, v) = select_cube_face(dx, dy, dz);
gather_texture_2d(&data.faces[face], sampler_state, u, v, component)
}
pub(crate) fn gather_texture_cube_array<T: Copy + Default>(
data: &TextureDataCubeArray<T>,
sampler_state: &SamplerState,
dx: f32,
dy: f32,
dz: f32,
array_index: u32,
component: u32,
) -> [T; 4] {
let layer = (array_index as usize).min(data.cubes.len().saturating_sub(1));
data.cubes
.get(layer)
.map(|cube| gather_texture_cube(cube, sampler_state, dx, dy, dz, component))
.unwrap_or_else(|| std::array::from_fn(|_| T::default()))
}
pub(crate) fn gather_texture_2d_array<T: Copy + Default>(
data: &TextureData2DArray<T>,
sampler_state: &SamplerState,
u: f32,
v: f32,
array_index: u32,
component: u32,
) -> [T; 4] {
let layer = (array_index as usize).min(data.layers.len().saturating_sub(1));
data.layers
.get(layer)
.map(|layer_data| gather_texture_2d(layer_data, sampler_state, u, v, component))
.unwrap_or_else(|| std::array::from_fn(|_| T::default()))
}
pub(crate) fn gather_depth_texture_cube(
data: &TextureDataDepthCube,
sampler_state: &SamplerState,
dx: f32,
dy: f32,
dz: f32,
) -> [f32; 4] {
let (face, u, v) = select_cube_face(dx, dy, dz);
gather_4_depth_texels(&data.faces[face], sampler_state, u, v)
}
pub(crate) fn gather_depth_texture_cube_array(
data: &TextureDataDepthCubeArray,
sampler_state: &SamplerState,
dx: f32,
dy: f32,
dz: f32,
array_index: u32,
) -> [f32; 4] {
let layer = (array_index as usize).min(data.cubes.len().saturating_sub(1));
data.cubes
.get(layer)
.map(|cube| gather_depth_texture_cube(cube, sampler_state, dx, dy, dz))
.unwrap_or([0.0, 0.0, 0.0, 0.0])
}