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