ling_graphics/
material.rs1use crate::color::Color;
2
3#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
5pub struct TextureData {
6 pub width: usize,
7 pub height: usize,
8 pub data: Vec<u8>,
10}
11
12impl TextureData {
13 pub fn new(width: usize, height: usize) -> Self {
14 Self { width, height, data: vec![0u8; width * height * 4] }
15 }
16
17 pub fn from_color(width: usize, height: usize, color: Color) -> Self {
18 let bytes = color.to_rgba_bytes();
19 let data = bytes.iter().cloned().cycle().take(width * height * 4).collect();
20 Self { width, height, data }
21 }
22
23 pub fn set_pixel(&mut self, x: usize, y: usize, color: Color) {
24 if x >= self.width || y >= self.height { return; }
25 let idx = (y * self.width + x) * 4;
26 let b = color.to_rgba_bytes();
27 self.data[idx..idx + 4].copy_from_slice(&b);
28 }
29
30 pub fn get_pixel(&self, x: usize, y: usize) -> Color {
31 if x >= self.width || y >= self.height { return Color::TRANSPARENT; }
32 let idx = (y * self.width + x) * 4;
33 Color::new(
34 self.data[idx] as f32 / 255.0,
35 self.data[idx + 1] as f32 / 255.0,
36 self.data[idx + 2] as f32 / 255.0,
37 self.data[idx + 3] as f32 / 255.0,
38 )
39 }
40
41 pub fn sample(&self, u: f32, v: f32) -> Color {
43 let u = u.fract().abs();
44 let v = v.fract().abs();
45 let fx = u * (self.width as f32 - 1.0);
46 let fy = v * (self.height as f32 - 1.0);
47 let x0 = fx as usize;
48 let y0 = fy as usize;
49 let x1 = (x0 + 1).min(self.width - 1);
50 let y1 = (y0 + 1).min(self.height - 1);
51 let tx = fx - x0 as f32;
52 let ty = fy - y0 as f32;
53 let c00 = self.get_pixel(x0, y0);
54 let c10 = self.get_pixel(x1, y0);
55 let c01 = self.get_pixel(x0, y1);
56 let c11 = self.get_pixel(x1, y1);
57 let top = c00.lerp(c10, tx);
58 let bottom = c01.lerp(c11, tx);
59 top.lerp(bottom, ty)
60 }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
66pub enum AlphaMode {
67 Opaque,
68 Mask { cutoff: f32 },
70 Blend,
72 Premultiplied,
74}
75
76#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
79pub struct Material {
80 pub albedo_color: Color,
81 pub albedo_texture: Option<TextureData>,
82 pub normal_texture: Option<TextureData>,
83 pub metallic: f32,
84 pub roughness: f32,
85 pub emissive: Color,
86 pub alpha_mode: AlphaMode,
87 pub tint: Color,
89 pub double_sided: bool,
90}
91
92impl Material {
93 pub fn new(color: Color) -> Self {
94 Self {
95 albedo_color: color,
96 albedo_texture: None,
97 normal_texture: None,
98 metallic: 0.0,
99 roughness: 0.8,
100 emissive: Color::BLACK,
101 alpha_mode: AlphaMode::Opaque,
102 tint: Color::WHITE,
103 double_sided: false,
104 }
105 }
106
107 pub fn with_texture(mut self, tex: TextureData) -> Self {
108 self.albedo_texture = Some(tex);
109 self
110 }
111
112 pub fn with_metallic_roughness(mut self, m: f32, r: f32) -> Self {
113 self.metallic = m;
114 self.roughness = r;
115 self
116 }
117
118 pub fn with_alpha(mut self, mode: AlphaMode) -> Self {
119 self.alpha_mode = mode;
120 self
121 }
122
123 pub fn with_emissive(mut self, c: Color) -> Self {
124 self.emissive = c;
125 self
126 }
127
128 pub fn sample_albedo(&self, u: f32, v: f32) -> Color {
130 let base = match &self.albedo_texture {
131 Some(tex) => tex.sample(u, v),
132 None => self.albedo_color,
133 };
134 base * self.tint
135 }
136}
137
138impl Default for Material {
139 fn default() -> Self { Self::new(Color::WHITE) }
140}