use std::collections::BTreeMap;
use glium::uniforms::{SamplerBehavior, UniformValue};
use sge_color::Color;
use sge_macros::gen_ref_type;
use sge_programs::{GOURAUD_3D_PROGRAM, ProgramRef};
use sge_textures::TextureRef;
use sge_vectors::{Mat3, Mat4, Vec2, Vec3, Vec4};
pub const DEFAULT_MATERIAL: MaterialRef = MaterialRef(0);
pub struct Material {
pub(crate) program: ProgramRef,
pub(crate) uniforms: BTreeMap<String, UniformData>,
pub draw_param_overrides: Option<glium::DrawParameters<'static>>,
}
#[derive(Clone, Copy, PartialEq)]
pub enum UniformData {
Float(f32),
Vec2(Vec2),
Vec3(Vec3),
Vec4(Vec4),
Color(Color),
Texture(TextureRef),
Mat4(Mat4),
Mat3(Mat3),
}
impl Material {
pub fn new(program: ProgramRef) -> Self {
Self {
program,
uniforms: BTreeMap::new(),
draw_param_overrides: None,
}
}
pub fn with_draw_param_overrides(mut self, params: glium::DrawParameters<'static>) -> Self {
self.draw_param_overrides = Some(params);
self
}
pub fn with_float(mut self, name: impl Into<String>, value: f32) -> Self {
self.uniforms.insert(name.into(), UniformData::Float(value));
self
}
pub fn with_vec2(mut self, name: impl Into<String>, value: Vec2) -> Self {
self.uniforms.insert(name.into(), UniformData::Vec2(value));
self
}
pub fn with_vec3(mut self, name: impl Into<String>, value: Vec3) -> Self {
self.uniforms.insert(name.into(), UniformData::Vec3(value));
self
}
pub fn with_vec4(mut self, name: impl Into<String>, value: Vec4) -> Self {
self.uniforms.insert(name.into(), UniformData::Vec4(value));
self
}
pub fn with_mat4(mut self, name: impl Into<String>, value: Mat4) -> Self {
self.uniforms.insert(name.into(), UniformData::Mat4(value));
self
}
pub fn with_mat3(mut self, name: impl Into<String>, value: Mat3) -> Self {
self.uniforms.insert(name.into(), UniformData::Mat3(value));
self
}
pub fn with_texture(mut self, name: impl Into<String>, texture: TextureRef) -> Self {
self.uniforms
.insert(name.into(), UniformData::Texture(texture));
self
}
pub fn with_color(mut self, name: impl Into<String>, color: Color) -> Self {
self.uniforms.insert(name.into(), UniformData::Color(color));
self
}
pub fn set_draw_param_overrides(&mut self, params: glium::DrawParameters<'static>) {
self.draw_param_overrides = Some(params);
}
pub fn set_float(&mut self, name: impl Into<String>, value: f32) {
self.uniforms.insert(name.into(), UniformData::Float(value));
}
pub fn set_vec2(&mut self, name: impl Into<String>, value: Vec2) {
self.uniforms.insert(name.into(), UniformData::Vec2(value));
}
pub fn set_vec3(&mut self, name: impl Into<String>, value: Vec3) {
self.uniforms.insert(name.into(), UniformData::Vec3(value));
}
pub fn set_vec4(&mut self, name: impl Into<String>, value: Vec4) {
self.uniforms.insert(name.into(), UniformData::Vec4(value));
}
pub fn set_mat4(&mut self, name: impl Into<String>, value: Mat4) {
self.uniforms.insert(name.into(), UniformData::Mat4(value));
}
pub fn set_mat3(&mut self, name: impl Into<String>, value: Mat3) {
self.uniforms.insert(name.into(), UniformData::Mat3(value));
}
pub fn set_texture(&mut self, name: impl Into<String>, texture: TextureRef) {
self.uniforms
.insert(name.into(), UniformData::Texture(texture));
}
pub fn set_color(&mut self, name: impl Into<String>, color: Color) {
self.uniforms.insert(name.into(), UniformData::Color(color));
}
pub fn get_uniform(&self, name: impl Into<String>) -> Option<UniformData> {
self.uniforms.get(&name.into()).copied()
}
}
impl UniformData {
fn to_gpu<'a>(self) -> UniformValue<'a> {
match self {
Self::Float(f) => UniformValue::Float(f),
Self::Mat3(m) => UniformValue::Mat3(m.to_cols_array_2d()),
Self::Mat4(m) => UniformValue::Mat4(m.to_cols_array_2d()),
Self::Texture(texture) => {
let texture = texture.get();
let behaviour = SamplerBehavior {
magnify_filter: texture.magnify_filter,
minify_filter: texture.minify_filter,
..Default::default()
};
UniformValue::Texture2d(&texture.gl_texture, Some(behaviour))
}
Self::Vec2(v) => UniformValue::Vec2(v.into()),
Self::Vec3(v) => UniformValue::Vec3(v.into()),
Self::Vec4(v) => UniformValue::Vec4(v.into()),
Self::Color(c) => UniformValue::Vec4(c.for_gpu()),
}
}
}
impl glium::uniforms::Uniforms for Material {
fn visit_values<'a, F: FnMut(&str, UniformValue<'a>)>(&'a self, mut add: F) {
for key in self.uniforms.keys() {
let value = self.uniforms.get(key).unwrap().to_gpu();
add(key, value);
}
}
}
gen_ref_type!(Material, MaterialRef, materials);
pub(crate) fn init_materials() {
init_materials_storage();
let regular_color = Color::hex(0xBBBDBD);
let dark_color = Color::hex(0x333333);
let light_pos = Vec3::new(0.0, 3.0, 1.0);
Material::new(GOURAUD_3D_PROGRAM)
.with_color("regular_color", regular_color)
.with_color("dark_color", dark_color)
.with_vec3("light_pos", light_pos)
.create();
}