use crate::mesh3d::Vec3;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Light3D {
pub position: Vec3,
pub color: [f32; 3],
pub intensity: f32,
}
impl Light3D {
pub fn new(position: Vec3, color: [f32; 3], intensity: f32) -> Self {
Self {
position,
color,
intensity,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Material {
pub ambient: [f32; 3],
pub diffuse: [f32; 3],
pub specular: [f32; 3],
pub shininess: f32,
}
impl Material {
pub fn default() -> Self {
Self {
ambient: [0.2, 0.2, 0.2],
diffuse: [0.8, 0.8, 0.8],
specular: [1.0, 1.0, 1.0],
shininess: 32.0,
}
}
pub fn metal() -> Self {
Self {
ambient: [0.1, 0.1, 0.1],
diffuse: [0.5, 0.5, 0.5],
specular: [1.0, 1.0, 1.0],
shininess: 128.0,
}
}
pub fn plastic() -> Self {
Self {
ambient: [0.3, 0.3, 0.3],
diffuse: [0.7, 0.7, 0.7],
specular: [0.5, 0.5, 0.5],
shininess: 16.0,
}
}
}
pub struct PhongShader {
pub lights: Vec<Light3D>,
pub ambient_light: [f32; 3],
}
impl PhongShader {
pub fn new() -> Self {
Self {
lights: Vec::new(),
ambient_light: [0.1, 0.1, 0.1],
}
}
pub fn add_light(&mut self, light: Light3D) {
self.lights.push(light);
}
pub fn calculate_lighting(
&self,
position: &Vec3,
normal: &Vec3,
view_dir: &Vec3,
material: &Material,
base_color: [u8; 4],
) -> [u8; 4] {
let mut final_color = [
material.ambient[0] * self.ambient_light[0],
material.ambient[1] * self.ambient_light[1],
material.ambient[2] * self.ambient_light[2],
];
for light in &self.lights {
let light_dir = Vec3::new(
light.position.x - position.x,
light.position.y - position.y,
light.position.z - position.z,
)
.normalize();
let diff = normal.dot(&light_dir).max(0.0);
final_color[0] += material.diffuse[0] * diff * light.color[0] * light.intensity;
final_color[1] += material.diffuse[1] * diff * light.color[1] * light.intensity;
final_color[2] += material.diffuse[2] * diff * light.color[2] * light.intensity;
let reflect_dir = self.reflect(&light_dir.normalize(), normal);
let spec = reflect_dir.dot(view_dir).max(0.0).powf(material.shininess);
final_color[0] += material.specular[0] * spec * light.color[0] * light.intensity;
final_color[1] += material.specular[1] * spec * light.color[1] * light.intensity;
final_color[2] += material.specular[2] * spec * light.color[2] * light.intensity;
}
let r = (base_color[0] as f32 * final_color[0].min(1.0)) as u8;
let g = (base_color[1] as f32 * final_color[1].min(1.0)) as u8;
let b = (base_color[2] as f32 * final_color[2].min(1.0)) as u8;
[r, g, b, base_color[3]]
}
fn reflect(&self, incident: &Vec3, normal: &Vec3) -> Vec3 {
let dot = incident.dot(normal);
Vec3::new(
incident.x - 2.0 * dot * normal.x,
incident.y - 2.0 * dot * normal.y,
incident.z - 2.0 * dot * normal.z,
)
}
}
pub struct ToonShader {
pub levels: u8,
pub outline_thickness: f32,
pub outline_color: [u8; 4],
}
impl ToonShader {
pub fn new(levels: u8) -> Self {
Self {
levels,
outline_thickness: 0.1,
outline_color: [0, 0, 0, 255],
}
}
pub fn with_outline(mut self, thickness: f32, color: [u8; 4]) -> Self {
self.outline_thickness = thickness;
self.outline_color = color;
self
}
pub fn apply(&self, color: [u8; 4], light_intensity: f32) -> [u8; 4] {
let step = 1.0 / self.levels as f32;
let quantized = (light_intensity / step).floor() * step;
[
(color[0] as f32 * quantized) as u8,
(color[1] as f32 * quantized) as u8,
(color[2] as f32 * quantized) as u8,
color[3],
]
}
pub fn should_outline(&self, normal_dot_view: f32) -> bool {
normal_dot_view.abs() < self.outline_thickness
}
}
pub struct WireframeShader {
pub line_color: [u8; 4],
pub fill_color: [u8; 4],
pub line_width: f32,
}
impl WireframeShader {
pub fn new() -> Self {
Self {
line_color: [255, 255, 255, 255],
fill_color: [50, 50, 50, 255],
line_width: 2.0,
}
}
pub fn with_colors(mut self, line_color: [u8; 4], fill_color: [u8; 4]) -> Self {
self.line_color = line_color;
self.fill_color = fill_color;
self
}
pub fn with_line_width(mut self, width: f32) -> Self {
self.line_width = width;
self
}
}
pub struct FresnelShader {
pub rim_color: [f32; 3],
pub rim_power: f32,
pub rim_intensity: f32,
}
impl FresnelShader {
pub fn new() -> Self {
Self {
rim_color: [1.0, 1.0, 1.0],
rim_power: 3.0,
rim_intensity: 1.0,
}
}
pub fn calculate(&self, normal: &Vec3, view_dir: &Vec3, base_color: [u8; 4]) -> [u8; 4] {
let fresnel = (1.0 - normal.dot(view_dir).abs()).powf(self.rim_power) * self.rim_intensity;
let r = (base_color[0] as f32 + self.rim_color[0] * fresnel * 255.0).min(255.0) as u8;
let g = (base_color[1] as f32 + self.rim_color[1] * fresnel * 255.0).min(255.0) as u8;
let b = (base_color[2] as f32 + self.rim_color[2] * fresnel * 255.0).min(255.0) as u8;
[r, g, b, base_color[3]]
}
}
pub struct NormalMapShader {
pub bump_strength: f32,
}
impl NormalMapShader {
pub fn new(bump_strength: f32) -> Self {
Self { bump_strength }
}
pub fn perturb_normal(&self, normal: &Vec3, u: f32, v: f32) -> Vec3 {
let bump_x = (u * 10.0).sin() * self.bump_strength;
let bump_y = (v * 10.0).sin() * self.bump_strength;
Vec3::new(
normal.x + bump_x,
normal.y + bump_y,
normal.z,
).normalize()
}
}
pub struct ReflectionShader {
pub reflectivity: f32,
pub environment_color: [f32; 3],
}
impl ReflectionShader {
pub fn new() -> Self {
Self {
reflectivity: 0.5,
environment_color: [0.5, 0.7, 1.0],
}
}
pub fn calculate(&self, normal: &Vec3, view_dir: &Vec3, base_color: [u8; 4]) -> [u8; 4] {
let reflect_dir = self.reflect(view_dir, normal);
let reflection_factor = reflect_dir.y.max(0.0) * self.reflectivity;
let r = ((base_color[0] as f32 * (1.0 - reflection_factor)) +
(self.environment_color[0] * 255.0 * reflection_factor)).min(255.0) as u8;
let g = ((base_color[1] as f32 * (1.0 - reflection_factor)) +
(self.environment_color[1] * 255.0 * reflection_factor)).min(255.0) as u8;
let b = ((base_color[2] as f32 * (1.0 - reflection_factor)) +
(self.environment_color[2] * 255.0 * reflection_factor)).min(255.0) as u8;
[r, g, b, base_color[3]]
}
fn reflect(&self, incident: &Vec3, normal: &Vec3) -> Vec3 {
let dot = incident.dot(normal);
Vec3::new(
incident.x - 2.0 * dot * normal.x,
incident.y - 2.0 * dot * normal.y,
incident.z - 2.0 * dot * normal.z,
)
}
}
pub struct FogShader {
pub fog_color: [u8; 4],
pub fog_density: f32,
pub fog_start: f32,
pub fog_end: f32,
}
impl FogShader {
pub fn new() -> Self {
Self {
fog_color: [200, 200, 220, 255],
fog_density: 0.05,
fog_start: 10.0,
fog_end: 50.0,
}
}
pub fn apply(&self, color: [u8; 4], distance: f32) -> [u8; 4] {
let fog_factor = if distance < self.fog_start {
0.0
} else if distance > self.fog_end {
1.0
} else {
(distance - self.fog_start) / (self.fog_end - self.fog_start)
};
let fog_factor = fog_factor * self.fog_density;
let fog_factor = fog_factor.clamp(0.0, 1.0);
[
((color[0] as f32 * (1.0 - fog_factor)) + (self.fog_color[0] as f32 * fog_factor)) as u8,
((color[1] as f32 * (1.0 - fog_factor)) + (self.fog_color[1] as f32 * fog_factor)) as u8,
((color[2] as f32 * (1.0 - fog_factor)) + (self.fog_color[2] as f32 * fog_factor)) as u8,
color[3],
]
}
}