use glam::*;
use image::GenericImageView;
use std::{
collections::HashMap,
fmt::Display,
ops::{Add, DivAssign, Index, IndexMut, MulAssign, SubAssign},
ops::{AddAssign, Div, Mul, Sub},
path::Path,
path::PathBuf,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct Material {
pub name: String,
pub color: [f32; 4],
pub absorption: [f32; 4],
pub specular: [f32; 4],
pub metallic: f32,
pub subsurface: f32,
pub specular_f: f32,
pub roughness: f32,
pub specular_tint: f32,
pub anisotropic: f32,
pub sheen: f32,
pub sheen_tint: f32,
pub clearcoat: f32,
pub clearcoat_gloss: f32,
pub transmission: f32,
pub eta: f32,
pub custom0: f32,
pub custom1: f32,
pub custom2: f32,
pub custom3: f32,
pub diffuse_tex: i16,
pub normal_tex: i16,
pub metallic_roughness_tex: i16,
pub emissive_tex: i16,
pub sheen_tex: i16,
}
impl Default for Material {
fn default() -> Self {
Self {
name: String::new(),
color: [1.0; 4],
absorption: [0.0; 4],
specular: [1.0; 4],
metallic: 0.0,
subsurface: 0.0,
specular_f: 0.5,
roughness: 0.0,
specular_tint: 0.0,
anisotropic: 0.0,
sheen: 0.0,
sheen_tint: 0.0,
clearcoat: 0.0,
clearcoat_gloss: 1.0,
transmission: 0.0,
eta: 1.0,
custom0: 0.0,
custom1: 0.0,
custom2: 0.0,
custom3: 0.0,
diffuse_tex: -1,
normal_tex: -1,
metallic_roughness_tex: -1,
emissive_tex: -1,
sheen_tex: -1,
}
}
}
impl Display for Material {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Material {{ name: {}, color: {}, absorption: {}, specular: {}, metallic: {}, subsurface: {}, specular_f: {}, roughness: {}, specular_tint: {}, anisotropic: {}, sheen: {}, sheen_tint: {}, clearcoat: {}, clearcoat_gloss: {}, transmission: {}, eta: {}, custom0: {}, custom1: {}, custom2: {}, custom3: {}, diffuse_tex: {}, normal_tex: {}, metallic_roughness_tex: {}, emissive_tex: {}, sheen_tex: {} }}",
self.name,
format!("r: {}, g: {}, b: {}, a: {}", self.color[0], self.color[1], self.color[2], self.color[3]),
format!("r: {}, g: {}, b: {}, a: {}", self.absorption[0], self.absorption[1], self.absorption[2], self.absorption[3]),
format!("r: {}, g: {}, b: {}, a: {}", self.specular[0], self.specular[1], self.specular[2], self.specular[3]),
self.metallic,
self.subsurface,
self.specular_f,
self.roughness,
self.specular_tint,
self.anisotropic,
self.sheen,
self.sheen_tint,
self.clearcoat,
self.clearcoat_gloss,
self.transmission,
self.eta,
self.custom0,
self.custom1,
self.custom2,
self.custom3,
self.diffuse_tex,
self.normal_tex,
self.metallic_roughness_tex,
self.emissive_tex,
self.sheen_tex,
)
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct MaterialList {
materials: Vec<Material>,
tex_path_mapping: HashMap<PathBuf, usize>,
textures: Vec<Texture>,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Flip {
None,
FlipU,
FlipV,
FlipUV,
}
impl Default for Flip {
fn default() -> Self {
Self::None
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub enum TextureSource {
Loaded(Texture),
Filesystem(PathBuf, Flip),
}
impl Display for MaterialList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"MaterialList: {{ materials: {}, textures: {} }}",
self.materials.len(),
self.textures.len()
)
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum TextureFormat {
R,
RG,
RGB,
RGBA,
BGR,
BGRA,
R16,
RG16,
RGB16,
RGBA16,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone)]
#[repr(C, align(16))]
pub struct Pixel {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl Default for Pixel {
fn default() -> Self {
Self {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
}
}
}
impl From<[f32; 4]> for Pixel {
fn from(c: [f32; 4]) -> Self {
Self {
r: c[0],
g: c[1],
b: c[2],
a: c[3],
}
}
}
impl Into<[f32; 4]> for Pixel {
fn into(self) -> [f32; 4] {
[self.r, self.g, self.b, self.a]
}
}
impl Pixel {
pub fn new() -> Self {
Self::default()
}
pub fn zero() -> Self {
Self::from([0.0; 4])
}
pub fn one() -> Self {
Self::from([1.0; 4])
}
pub fn from_bgra(pixel: u32) -> Self {
let r = pixel.overflowing_shr(16).0 & 255;
let g = pixel.overflowing_shr(8).0 & 255;
let b = pixel & 255;
let a = pixel.overflowing_shr(24).0 & 255;
let r = r as f32 / 255.0;
let g = g as f32 / 255.0;
let b = b as f32 / 255.0;
let a = a as f32 / 255.0;
Self { r, g, b, a }
}
pub fn from_rgba(pixel: u32) -> Self {
let r = pixel.overflowing_shr(0).0 & 255;
let g = pixel.overflowing_shr(8).0 & 255;
let b = pixel.overflowing_shr(16).0 & 255;
let a = pixel.overflowing_shr(24).0 & 255;
let r = r as f32 / 255.0;
let g = g as f32 / 255.0;
let b = b as f32 / 255.0;
let a = a as f32 / 255.0;
Self { r, g, b, a }
}
pub fn scaled(self, factor: f32) -> Self {
Self {
r: self.r * factor,
g: self.g * factor,
b: self.b * factor,
a: self.a * factor,
}
}
pub fn sqrt(self) -> Self {
Self {
r: if self.r <= 0.0 { 0.0 } else { self.r.sqrt() },
g: if self.g <= 0.0 { 0.0 } else { self.g.sqrt() },
b: if self.b <= 0.0 { 0.0 } else { self.b.sqrt() },
a: if self.a <= 0.0 { 0.0 } else { self.a.sqrt() },
}
}
pub fn pow(self, exp: f32) -> Self {
Self {
r: if self.r <= 0.0 { 0.0 } else { self.r.powf(exp) },
g: if self.g <= 0.0 { 0.0 } else { self.g.powf(exp) },
b: if self.b <= 0.0 { 0.0 } else { self.b.powf(exp) },
a: if self.a <= 0.0 { 0.0 } else { self.a.powf(exp) },
}
}
}
impl Add<Pixel> for Pixel {
type Output = Self;
fn add(self, rhs: Pixel) -> Self::Output {
Self {
r: self.r + rhs.r,
g: self.g + rhs.g,
b: self.b + rhs.b,
a: self.a + rhs.a,
}
}
}
impl Sub<Pixel> for Pixel {
type Output = Self;
fn sub(self, rhs: Pixel) -> Self::Output {
Self {
r: self.r - rhs.r,
g: self.g - rhs.g,
b: self.b - rhs.b,
a: self.a - rhs.a,
}
}
}
impl Div<Pixel> for Pixel {
type Output = Self;
fn div(self, rhs: Pixel) -> Self::Output {
Self {
r: self.r / rhs.r,
g: self.g / rhs.g,
b: self.b / rhs.b,
a: self.a / rhs.a,
}
}
}
impl Mul<Pixel> for Pixel {
type Output = Self;
fn mul(self, rhs: Pixel) -> Self::Output {
Self {
r: self.r * rhs.r,
g: self.g * rhs.g,
b: self.b * rhs.b,
a: self.a * rhs.a,
}
}
}
impl AddAssign<Pixel> for Pixel {
fn add_assign(&mut self, rhs: Pixel) {
self.r += rhs.r;
self.g += rhs.g;
self.b += rhs.b;
self.a += rhs.a;
}
}
impl SubAssign<Pixel> for Pixel {
fn sub_assign(&mut self, rhs: Pixel) {
self.r -= rhs.r;
self.g -= rhs.g;
self.b -= rhs.b;
self.a -= rhs.a;
}
}
impl DivAssign<Pixel> for Pixel {
fn div_assign(&mut self, rhs: Pixel) {
self.r /= rhs.r;
self.g /= rhs.g;
self.b /= rhs.b;
self.a /= rhs.a;
}
}
impl MulAssign<Pixel> for Pixel {
fn mul_assign(&mut self, rhs: Pixel) {
self.r *= rhs.r;
self.g *= rhs.g;
self.b *= rhs.b;
self.a *= rhs.a;
}
}
impl Index<usize> for Pixel {
type Output = f32;
fn index(&self, index: usize) -> &Self::Output {
match index {
0 => &self.r,
1 => &self.g,
2 => &self.b,
3 => &self.a,
_ => panic!("invalid index {}", index),
}
}
}
impl IndexMut<usize> for Pixel {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match index {
0 => &mut self.r,
1 => &mut self.g,
2 => &mut self.b,
3 => &mut self.a,
_ => panic!("invalid index {}", index),
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct Texture {
pub data: Vec<u32>,
pub width: u32,
pub height: u32,
pub mip_levels: u32,
}
impl Default for Texture {
fn default() -> Self {
let mut data = Vec::with_capacity(64 * 64);
for y in 0..64 {
for x in 0..64 {
let r = x as f32 / 64.0_f32;
let g = y as f32 / 64.0_f32;
let b = 0.2;
let r = (r * 255.0) as u32;
let g = (g * 255.0) as u32;
let b = (b * 255.0) as u32;
let a = 255_u32;
data.push((a << 24) + ((r >> 2) << 16) + ((g >> 2) << 8) + (b >> 2));
}
}
assert_eq!(data.len(), 64 * 64);
Self {
data,
width: 64,
height: 64,
mip_levels: 1,
}
}
}
impl Display for Texture {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Texture {{ data: {} bytes, width: {}, height: {}, mip_levels: {} }}",
self.data.len() * std::mem::size_of::<u32>(),
self.width,
self.height,
self.mip_levels
)
}
}
impl Texture {
pub const MIP_LEVELS: usize = 5;
pub fn generate_mipmaps(&mut self, levels: usize) {
if self.mip_levels == levels as u32 {
return;
}
self.mip_levels = levels as u32;
self.data.resize(self.required_texel_count(levels), 0);
let mut src_offset = 0;
let mut dst_offset = src_offset + self.width as usize * self.height as usize;
let mut pw: usize = self.width as usize;
let mut w: usize = self.width as usize >> 1;
let mut h: usize = self.height as usize >> 1;
for _ in 1..levels {
let max_dst_offset = dst_offset + (w * h);
debug_assert!(max_dst_offset <= self.data.len());
for y in 0..h {
for x in 0..w {
let src0 = self.data[x * 2 + (y * 2) * pw + src_offset];
let src1 = self.data[x * 2 + 1 + (y * 2) * pw + src_offset];
let src2 = self.data[x * 2 + (y * 2 + 1) * pw + src_offset];
let src3 = self.data[x * 2 + 1 + (y * 2 + 1) * pw + src_offset];
let a = ((src0 >> 24) & 255)
.min((src1 >> 24) & 255)
.min(((src2 >> 24) & 255).min((src3 >> 24) & 255));
let r = ((src0 >> 16) & 255)
+ ((src1 >> 16) & 255)
+ ((src2 >> 16) & 255)
+ ((src3 >> 16) & 255);
let g = ((src0 >> 8) & 255)
+ ((src1 >> 8) & 255)
+ ((src2 >> 8) & 255)
+ ((src3 >> 8) & 255);
let b = (src0 & 255) + (src1 & 255) + (src2 & 255) + (src3 & 255);
self.data[dst_offset + x + y * w] =
(a << 24) + ((r >> 2) << 16) + ((g >> 2) << 8) + (b >> 2);
}
}
src_offset = dst_offset;
dst_offset += w * h;
pw = w;
w >>= 1;
h >>= 1;
}
}
pub fn texel_count(width: u32, height: u32, levels: usize) -> usize {
let mut w = width;
let mut h = height;
let mut needed = 0;
for _ in 0..levels {
needed += w * h;
w >>= 1;
h >>= 1;
}
needed as usize
}
fn required_texel_count(&self, levels: usize) -> usize {
let mut w = self.width;
let mut h = self.height;
let mut needed = 0;
for _ in 0..levels {
needed += w * h;
w >>= 1;
h >>= 1;
}
needed as usize
}
pub fn sample_uv_rgba(&self, uv: [f32; 2]) -> [f32; 4] {
let x = (self.width as f32 * uv[0]) as i32;
let y = (self.height as f32 * uv[1]) as i32;
let x = x.min(self.width as i32 - 1).max(0) as usize;
let y = y.min(self.height as i32).max(0) as usize;
let texel = self.data[y * self.width as usize + x];
let blue = (texel & 0xFF) as f32 / 255.0;
let green = ((texel.overflowing_shr(8).0) & 0xFF) as f32 / 255.0;
let red = ((texel.overflowing_shr(16).0) & 0xFF) as f32 / 255.0;
let alpha = ((texel.overflowing_shr(24).0) & 0xFF) as f32 / 255.0;
[red, green, blue, alpha]
}
pub fn sample_uv_bgra(&self, uv: [f32; 2]) -> [f32; 4] {
let x = (self.width as f32 * uv[0]) as i32;
let y = (self.height as f32 * uv[1]) as i32;
let x = x.min(self.width as i32 - 1).max(0) as usize;
let y = y.min(self.height as i32).max(0) as usize;
let texel = self.data[y * self.width as usize + x];
let blue = (texel & 0xFF) as f32 / 255.0;
let green = ((texel.overflowing_shr(8).0) & 0xFF) as f32 / 255.0;
let red = ((texel.overflowing_shr(16).0) & 0xFF) as f32 / 255.0;
let alpha = ((texel.overflowing_shr(24).0) & 0xFF) as f32 / 255.0;
[blue, green, red, alpha]
}
pub fn sample_uv_rgb(&self, uv: [f32; 2]) -> [f32; 3] {
let x = (self.width as f32 * uv[0]) as i32;
let y = (self.height as f32 * uv[1]) as i32;
let x = x.min(self.width as i32 - 1).max(0) as usize;
let y = y.min(self.height as i32).max(0) as usize;
let texel = self.data[y * self.width as usize + x];
let blue = (texel & 0xFF) as f32 / 255.0;
let green = ((texel.overflowing_shr(8).0) & 0xFF) as f32 / 255.0;
let red = ((texel.overflowing_shr(16).0) & 0xFF) as f32 / 255.0;
[red, green, blue]
}
pub fn sample_uv_bgr(&self, uv: [f32; 2]) -> [f32; 3] {
let x = (self.width as f32 * uv[0]) as i32;
let y = (self.height as f32 * uv[1]) as i32;
let x = x.min(self.width as i32 - 1).max(0) as usize;
let y = y.min(self.height as i32).max(0) as usize;
let texel = self.data[y * self.width as usize + x];
let blue = (texel & 0xFF) as f32 / 255.0;
let green = ((texel.overflowing_shr(8).0) & 0xFF) as f32 / 255.0;
let red = ((texel.overflowing_shr(16).0) & 0xFF) as f32 / 255.0;
[blue, green, red]
}
pub fn get_texel(&self, x: usize, y: usize) -> u32 {
let x = (x as i32).min(self.width as i32 - 1).max(0) as usize;
let y = (y as i32).min(self.height as i32).max(0) as usize;
self.data[y * self.width as usize + x]
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn offset_for_level(&self, mip_level: usize) -> usize {
assert!(mip_level <= self.mip_levels as usize);
let mut offset = 0;
for i in 0..mip_level {
let (w, h) = self.mip_level_width_height(i);
offset += w * h;
}
offset
}
pub fn mip_level_width(&self, mip_level: usize) -> usize {
let mut w = self.width as usize;
for _ in 0..mip_level {
w >>= 1;
}
w
}
pub fn mip_level_height(&self, mip_level: usize) -> usize {
let mut h = self.height as usize;
for _ in 0..mip_level {
h >>= 1;
}
h
}
pub fn mip_level_width_height(&self, mip_level: usize) -> (usize, usize) {
let mut w = self.width as usize;
let mut h = self.height as usize;
if mip_level == 0 {
return (w, h);
}
for _ in 0..mip_level {
w >>= 1;
h >>= 1
}
(w, h)
}
pub fn resized(&self, width: u32, height: u32) -> Texture {
let mut image: image::ImageBuffer<image::Bgra<u8>, Vec<u8>> =
image::ImageBuffer::new(self.width, self.height);
image.copy_from_slice(unsafe {
std::slice::from_raw_parts(
self.data.as_ptr() as *const u8,
(self.width * self.height) as usize * std::mem::size_of::<u32>(),
)
});
let image =
image::imageops::resize(&image, width, height, image::imageops::FilterType::Nearest);
let mut data = vec![0; (width * height) as usize];
data.copy_from_slice(unsafe {
std::slice::from_raw_parts(image.as_ptr() as *const u32, (width * height) as usize)
});
Texture {
data,
width: width as u32,
height: height as u32,
mip_levels: 1,
}
}
pub fn load<T: AsRef<Path>>(path: T, flip: Flip) -> Result<Self, ()> {
if !path.as_ref().exists() {
return Err(());
}
let img = match image::open(path) {
Ok(img) => img,
Err(_) => return Err(()),
};
let img = match flip {
Flip::None => img,
Flip::FlipU => img.fliph(),
Flip::FlipV => img.flipv(),
Flip::FlipUV => img.fliph().flipv(),
};
let (width, height) = (img.width(), img.height());
let mut data = vec![0 as u32; (width * height) as usize];
let bgra_image = img.to_bgra8();
data.copy_from_slice(unsafe {
std::slice::from_raw_parts(bgra_image.as_ptr() as *const u32, (width * height) as usize)
});
Ok(Texture {
width,
height,
data,
mip_levels: 1,
})
}
pub fn flipped(&mut self, flip: Flip) -> Self {
let mut img: image::ImageBuffer<image::Bgra<u8>, Vec<u8>> =
image::ImageBuffer::new(self.width, self.height);
img.copy_from_slice(unsafe {
std::slice::from_raw_parts(
self.data.as_ptr() as *const u8,
(self.width * self.height) as usize * std::mem::size_of::<u32>(),
)
});
let img = image::DynamicImage::ImageBgra8(img);
let img = match flip {
Flip::None => img,
Flip::FlipU => img.fliph(),
Flip::FlipV => img.flipv(),
Flip::FlipUV => img.fliph().flipv(),
};
let mut data = vec![0; (self.width * self.height) as usize];
data.copy_from_slice(unsafe {
std::slice::from_raw_parts(
img.as_bgra8().unwrap().as_ptr() as *const u32,
(self.width * self.height) as usize,
)
});
Texture {
data,
width: self.width,
height: self.height,
mip_levels: 1,
}
}
pub fn merge(r: Option<&Self>, g: Option<&Self>, b: Option<&Self>, a: Option<&Self>) -> Self {
let mut wh = None;
let mut assert_tex_wh = |t: Option<&Self>| {
if let Some(t) = t {
if let Some((width, height)) = wh {
assert_eq!(width, t.width);
assert_eq!(height, t.height);
} else {
wh = Some((t.width, t.height));
}
}
};
assert_tex_wh(r);
assert_tex_wh(g);
assert_tex_wh(b);
assert_tex_wh(a);
let (width, height) = wh.unwrap();
let sample = |t: Option<&Self>, index: usize| {
if let Some(t) = t {
t.data[index] & 255
} else {
0
}
};
let mut data = vec![0_u32; (width * height) as usize];
for y in 0..height {
for x in 0..width {
let index = (y * height + x) as usize;
let r = sample(r, index);
let g = sample(g, index);
let b = sample(b, index);
let a = sample(a, index);
data[index] = (a << 24) + (r << 16) + (g << 8) + b;
}
}
Texture {
data,
width,
height,
mip_levels: 1,
}
}
pub fn from_bytes(
bytes: &[u8],
width: u32,
height: u32,
format: TextureFormat,
stride: usize,
) -> Self {
if format == TextureFormat::BGRA && stride == 4 {
return Texture {
data: unsafe {
std::slice::from_raw_parts(
bytes.as_ptr() as *mut u32,
(width * height) as usize,
)
}
.to_vec(),
width,
height,
mip_levels: 1,
};
}
let mut data = vec![0 as u32; (width * height) as usize];
for y in 0..height {
for x in 0..width {
let index = (x + y * width) as usize;
let orig_index = index * stride;
let (r, g, b, a) = match format {
TextureFormat::R => (bytes[orig_index] as u32, 0, 0, 0),
TextureFormat::RG => (bytes[orig_index] as u32, 0, 0, 0),
TextureFormat::BGR => (
bytes[orig_index + 2] as u32,
bytes[orig_index + 1] as u32,
bytes[orig_index] as u32,
0,
),
TextureFormat::RGB => (
bytes[orig_index] as u32,
bytes[orig_index + 1] as u32,
bytes[orig_index + 2] as u32,
0,
),
TextureFormat::RGBA => (
bytes[orig_index] as u32,
bytes[orig_index + 1] as u32,
bytes[orig_index + 2] as u32,
bytes[orig_index + 3] as u32,
),
TextureFormat::BGRA => (
bytes[orig_index + 2] as u32,
bytes[orig_index + 1] as u32,
bytes[orig_index] as u32,
bytes[orig_index + 3] as u32,
),
TextureFormat::R16 => (
(bytes[orig_index] as u32) + ((bytes[orig_index + 1] as u32) << 16),
0,
0,
0,
),
TextureFormat::RG16 => (
(bytes[orig_index] as u32) + ((bytes[orig_index + 1] as u32) << 16),
(bytes[orig_index + 2] as u32) + ((bytes[orig_index + 3] as u32) << 16),
0,
0,
),
TextureFormat::RGB16 => (
(bytes[orig_index] as u32) + ((bytes[orig_index + 1] as u32) << 16),
(bytes[orig_index + 2] as u32) + ((bytes[orig_index + 3] as u32) << 16),
(bytes[orig_index + 4] as u32) + ((bytes[orig_index + 5] as u32) << 16),
0,
),
TextureFormat::RGBA16 => (
(bytes[orig_index] as u32) + ((bytes[orig_index + 1] as u32) << 16),
(bytes[orig_index + 2] as u32) + ((bytes[orig_index + 3] as u32) << 16),
(bytes[orig_index + 4] as u32) + ((bytes[orig_index + 5] as u32) << 16),
(bytes[orig_index + 6] as u32) + ((bytes[orig_index + 7] as u32) << 16),
),
};
data[index] = (a << 24) + (r << 16) + (g << 8) + b;
}
}
Texture {
data,
width,
height,
mip_levels: 1,
}
}
pub fn transformed<C>(mut self, cb: C) -> Texture
where
C: Fn(Pixel) -> Pixel,
{
for pixel in self.data.iter_mut() {
let cb = &cb;
let p: u32 = *pixel;
let r: u32 = p.overflowing_shr(16).0 & 255;
let g: u32 = p.overflowing_shr(8).0 & 255;
let b: u32 = p & 255;
let a: u32 = p.overflowing_shr(24).0 & 255;
let r: f32 = r as f32 / 255.0;
let g: f32 = g as f32 / 255.0;
let b: f32 = b as f32 / 255.0;
let a: f32 = a as f32 / 255.0;
let colors: Pixel = cb(Pixel::from([r, g, b, a]));
let r: u32 = (colors.r.min(1.0).max(0.0) * 255.0) as u32;
let g: u32 = (colors.g.min(1.0).max(0.0) * 255.0) as u32;
let b: u32 = (colors.b.min(1.0).max(0.0) * 255.0) as u32;
let a: u32 = (colors.a.min(1.0).max(0.0) * 255.0) as u32;
*pixel = (a << 24) + (r << 16) + (g << 8) + b;
}
self
}
}
impl<T: AsRef<Path>> From<T> for Texture {
fn from(path: T) -> Self {
Self::load(path, Flip::default()).unwrap()
}
}
impl Default for MaterialList {
fn default() -> Self {
Self::new()
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct TextureDescriptor {
pub albedo: Option<TextureSource>,
pub normal: Option<TextureSource>,
pub metallic_roughness_map: Option<TextureSource>,
pub emissive_map: Option<TextureSource>,
pub sheen_map: Option<TextureSource>,
}
impl TextureDescriptor {
pub fn new() -> Self {
Self::default()
}
pub fn with_albedo(mut self, tex: TextureSource) -> Self {
self.albedo = Some(tex);
self
}
pub fn with_normal(mut self, tex: TextureSource) -> Self {
self.normal = Some(tex);
self
}
pub fn with_metallic_roughness(mut self, tex: TextureSource) -> Self {
self.metallic_roughness_map = Some(tex);
self
}
pub fn with_metallic_roughness_maps(
mut self,
metallic: TextureSource,
roughness: TextureSource,
) -> Self {
let metallic = match metallic {
TextureSource::Loaded(t) => t,
TextureSource::Filesystem(path, flip) => Texture::load(path, flip).unwrap(),
};
let roughness = match roughness {
TextureSource::Loaded(t) => t,
TextureSource::Filesystem(path, flip) => Texture::load(path, flip).unwrap(),
};
let tex = Texture::merge(Some(&metallic), Some(&roughness), None, None);
self.metallic_roughness_map = Some(TextureSource::Loaded(tex));
self
}
pub fn with_emissive(mut self, tex: TextureSource) -> Self {
self.emissive_map = Some(tex);
self
}
pub fn with_sheen(mut self, tex: TextureSource) -> Self {
self.sheen_map = Some(tex);
self
}
}
impl Default for TextureDescriptor {
fn default() -> Self {
Self {
albedo: None,
normal: None,
metallic_roughness_map: None,
emissive_map: None,
sheen_map: None,
}
}
}
#[allow(dead_code)]
impl MaterialList {
pub fn new() -> MaterialList {
let materials = Vec::new();
let textures = Vec::new();
MaterialList {
materials,
tex_path_mapping: HashMap::new(),
textures,
}
}
pub fn add<B: Into<[f32; 3]>>(
&mut self,
color: B,
roughness: f32,
specular: B,
transmission: f32,
) -> usize {
let material = Material {
color: Vec3::from(color.into()).extend(1.0).into(),
roughness,
specular: Vec3::from(specular.into()).extend(1.0).into(),
transmission,
..Material::default()
};
self.push(material)
}
pub fn add_with_maps(
&mut self,
color: Vec3,
roughness: f32,
specular: Vec3,
transmission: f32,
textures: TextureDescriptor,
) -> usize {
let albedo = textures.albedo;
let normal = textures.normal;
let metallic_roughness_map = textures.metallic_roughness_map;
let emissive_map = textures.emissive_map;
let sheen_map = textures.sheen_map;
let mut material = Material::default();
material.color = color.extend(1.0).into();
material.specular = specular.extend(1.0).into();
material.roughness = roughness;
material.transmission = transmission;
let diffuse_tex = if let Some(albedo) = albedo {
match albedo {
TextureSource::Loaded(tex) => self.push_texture(tex) as i32,
TextureSource::Filesystem(path, flip) => {
self.get_texture_index(&path, flip).unwrap_or_else(|_| -1)
}
}
} else {
-1
};
let normal_tex = if let Some(normal) = normal {
match normal {
TextureSource::Loaded(tex) => self.push_texture(tex) as i32,
TextureSource::Filesystem(path, flip) => {
self.get_texture_index(&path, flip).unwrap_or_else(|_| -1)
}
}
} else {
-1
};
let metallic_roughness_tex = if let Some(metallic_roughness_map) = metallic_roughness_map {
match metallic_roughness_map {
TextureSource::Loaded(tex) => self.push_texture(tex) as i32,
TextureSource::Filesystem(path, flip) => {
self.get_texture_index(&path, flip).unwrap_or_else(|_| -1)
}
}
} else {
-1
};
let emissive_tex = if let Some(emissive_map) = emissive_map {
match emissive_map {
TextureSource::Loaded(tex) => self.push_texture(tex) as i32,
TextureSource::Filesystem(path, flip) => {
self.get_texture_index(&path, flip).unwrap_or_else(|_| -1)
}
}
} else {
-1
};
let sheen_tex = if let Some(sheen_map) = sheen_map {
match sheen_map {
TextureSource::Loaded(tex) => self.push_texture(tex) as i32,
TextureSource::Filesystem(path, flip) => {
self.get_texture_index(&path, flip).unwrap_or_else(|_| -1)
}
}
} else {
-1
};
material.diffuse_tex = diffuse_tex as i16;
material.normal_tex = normal_tex as i16;
material.metallic_roughness_tex = metallic_roughness_tex as i16;
material.emissive_tex = emissive_tex as i16;
material.sheen_tex = sheen_tex as i16;
self.push(material)
}
pub fn push(&mut self, mat: Material) -> usize {
let i = self.materials.len();
self.materials.push(mat);
i
}
pub fn push_texture(&mut self, mut texture: Texture) -> usize {
if texture.width < 64 || texture.height < 64 {
texture = texture.resized(64.max(texture.width), 64.max(texture.height));
}
texture.generate_mipmaps(Texture::MIP_LEVELS);
let i = self.textures.len();
self.textures.push(texture);
i
}
pub fn get_texture_index<T: AsRef<Path> + Copy>(
&mut self,
path: T,
flip: Flip,
) -> Result<i32, i32> {
if let Some(id) = self.tex_path_mapping.get(path.as_ref()) {
return Ok((*id) as i32);
}
return match Texture::load(path, flip) {
Ok(mut tex) => {
if tex.width < 64 || tex.height < 64 {
tex = tex.resized(64.max(tex.width), 64.max(tex.height));
}
tex.generate_mipmaps(Texture::MIP_LEVELS);
let index = self.textures.len();
self.textures.push(tex);
self.tex_path_mapping
.insert(path.as_ref().to_path_buf(), index);
Ok(index as i32)
}
Err(_) => Err(-1),
};
}
pub fn get_default(&self) -> usize {
0
}
pub fn is_empty(&self) -> bool {
self.materials.is_empty()
}
pub fn len(&self) -> usize {
self.materials.len()
}
pub fn len_textures(&self) -> usize {
self.textures.len()
}
pub fn as_slice(&self) -> &[Material] {
self.materials.as_slice()
}
pub fn take(self) -> (Vec<Material>, Vec<Texture>) {
(self.materials, self.textures)
}
}
impl Index<usize> for MaterialList {
type Output = Material;
fn index(&self, index: usize) -> &Self::Output {
&self.materials[index]
}
}
impl IndexMut<usize> for MaterialList {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.materials[index]
}
}