use std::collections::HashMap;
use derivative::Derivative;
use crate::math::Matrix44;
use crate::renderer::{
gl,
Effect,
Texture,
Uniform,
Vertex,
};
fn gl_check_error(line: u32) {
unsafe {
let e = gl::GetError();
match e {
gl::NO_ERROR => {},
e => {
tracing::error!("gl error: {} in line {}", e, line);
},
}
}
}
#[derive(Derivative)]
#[derivative(Debug)]
#[repr(C)]
pub struct Material {
#[derivative(Debug = "ignore")]
vertices: Vec<Vertex>,
buffer: gl::types::GLuint,
vao: gl::types::GLuint,
layer_id: u8,
effect_id: u16,
texture_hwids: Vec<u16>,
effect_name: String,
texture_name: String,
key: u128,
uniforms: HashMap<String, Uniform>,
mvp_matrix: Matrix44,
}
impl Material {
pub fn new(layer_id: u8, effect: &Effect, textures: Vec<&Texture>) -> Self {
let texture_hwids = textures.iter().map(|&t| t.hwid()).collect();
let key = Material::calculate_key(layer_id, effect.id(), &texture_hwids);
let texture_name = textures
.iter()
.map(|&t| t.name().to_owned())
.collect::<Vec<_>>()
.join(" ");
let mut s = Self {
vertices: Vec::new(),
buffer: 0xffffffff,
vao: 0xffffffff,
layer_id: layer_id,
effect_id: effect.id(),
texture_hwids: texture_hwids,
effect_name: effect.name().to_string(),
texture_name: texture_name,
key: key,
uniforms: HashMap::new(),
mvp_matrix: Matrix44::identity(),
};
unsafe {
gl::GenVertexArrays(1, &mut s.vao);
gl::GenBuffers(1, &mut s.buffer);
}
s
}
pub fn calculate_key(layer_id: u8, effect_id: u16, texture_hwids: &Vec<u16>) -> u128 {
if texture_hwids.len() > 4 {
panic!("Too many texture channels. {}", texture_hwids.len());
}
if effect_id > 0xff {
panic!("Too many effects. Got id {}", &effect_id);
}
let mut r = 0u128;
let mut shift = 0;
for i in 0..4 {
if let Some(texture_hwid) = texture_hwids.get(i) {
r |= (*texture_hwid as u128 & 0xffff) << shift;
}
shift += 16;
}
r | ((effect_id as u128 & 0xff) << (shift)) | ((layer_id as u128 & 0x000f) << (8 + shift))
}
pub fn can_render(&self, key: u128) -> bool {
self.key == key
}
pub fn key(&self) -> u128 {
self.key
}
pub fn layer_id(&self) -> u8 {
self.layer_id
}
pub fn effect_id(&self) -> u16 {
self.effect_id
}
pub fn effect_name(&self) -> &str {
&self.effect_name
}
pub fn texture_name(&self) -> &str {
&self.texture_name
}
pub fn clear(&mut self) {
self.vertices.clear();
}
pub fn add_vertex(&mut self, vertex: &Vertex) {
self.vertices.push(*vertex);
}
pub fn set_uniform(&mut self, name: &str, value: &Uniform) {
self.uniforms.insert(name.to_owned(), *value);
}
pub fn set_mvp_matrix(&mut self, mvp_matrix: &Matrix44) {
self.mvp_matrix = *mvp_matrix;
}
pub fn render(&mut self, effect: &mut Effect) -> u32 {
let vertex_count = self.vertices.len();
if vertex_count == 0 {
return 0;
}
gl_check_error(line!());
let vertex_count = unsafe {
gl::Enable(gl::BLEND);
gl::BindVertexArray(self.vao);
gl::BindBuffer(gl::ARRAY_BUFFER, self.buffer);
let vertex_size = (core::mem::size_of::<f32>() * (3 + 2 + 4)) as isize; gl::BufferData(
gl::ARRAY_BUFFER,
vertex_size * vertex_count as isize,
self.vertices.as_ptr() as *const core::ffi::c_void,
gl::STATIC_DRAW, );
let attrib_pos_index = 0;
let attrib_tex_coords_index = 1;
let attrib_color_index = 2;
gl::EnableVertexAttribArray(attrib_pos_index);
gl::VertexAttribPointer(
attrib_pos_index,
3,
gl::FLOAT,
gl::FALSE,
vertex_size as i32,
0 as *const _,
);
gl::EnableVertexAttribArray(attrib_tex_coords_index);
gl::VertexAttribPointer(
attrib_tex_coords_index,
2,
gl::FLOAT,
gl::FALSE,
vertex_size as i32,
(3 * 4) as *const _,
);
gl::EnableVertexAttribArray(attrib_color_index);
gl::VertexAttribPointer(
attrib_color_index,
4,
gl::FLOAT,
gl::FALSE,
vertex_size as i32,
((3 + 2) * 4) as *const _,
);
effect.r#use();
for (n, v) in self.uniforms.iter() {
match effect.program().lookup_uniform(&n) {
Some(l) => {
match v {
Uniform::F32(v) => {
gl::Uniform1f(l, *v);
},
Uniform::MATRIX44(mtx44) => {
gl::UniformMatrix4fv(l, 1, 0, mtx44.as_ptr() as *const _);
},
}
},
None => {
println!("Warning: Location of {} not found {:?}", &n, &effect);
},
}
}
for (n, l) in effect.program().uniforms_iter() {
match n.as_str() {
"texture0\0" => {
gl::Uniform1i(*l, 0); },
"texture1\0" => {
gl::Uniform1i(*l, 1); },
"modelViewProjectionMatrix\0" => {
gl::UniformMatrix4fv(*l, 1, 0, self.mvp_matrix.as_ptr() as *const _);
},
_ => {
todo!("handle uniform {:?}", &n);
},
}
}
for (i, hwid) in self.texture_hwids.iter().enumerate() {
gl::ActiveTexture(gl::TEXTURE0 + i as u32);
gl::BindTexture(gl::TEXTURE_2D, *hwid as u32);
}
gl::ActiveTexture(gl::TEXTURE0);
gl::DrawArrays(gl::TRIANGLES, 0, vertex_count as i32);
vertex_count as u32
};
gl_check_error(line!());
vertex_count
}
}