use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use glium;
use glium::uniforms::Uniforms;
use {Screen, ScreenType};
use errors::{ProcessingErr, ErrorReadingIncludeLineInShader};
#[derive(Clone, Debug)]
pub struct ShaderInfo<U: Uniforms> {
shader_idx: usize,
uniforms: Option<U>,
}
#[macro_export]
macro_rules! create_uniforms {
($screen:ident) => {
{
let m: [[f32; 4]; 4] = $screen.matrices.curr_matrix.into();
let m = [[m[0][0], m[0][1], m[0][2], m[0][3]],
[m[1][0], m[1][1], m[1][2], m[1][3]],
[m[2][0], m[2][1], m[2][2], m[2][3]],
[m[3][0], m[3][1], m[3][2], m[3][3]]];
uniform!{MVP: m}
}
};
($screen:ident, $($uniformName:ident: $value:expr),+) => {
{
let m: [[f32; 4]; 4] = $screen.matrices.curr_matrix.into();
let m = [[m[0][0], m[0][1], m[0][2], m[0][3]],
[m[1][0], m[1][1], m[1][2], m[1][3]],
[m[2][0], m[2][1], m[2][2], m[2][3]],
[m[3][0], m[3][1], m[3][2], m[3][3]]];
uniform!{$($uniformName: $value,)+ MVP: m}
}
}
}
impl<U: Uniforms> ShaderInfo<U> {
pub fn new(idx: usize, uniforms: Option<U>) -> Self {
ShaderInfo {
shader_idx: idx,
uniforms: uniforms,
}
}
pub fn set(&mut self, uniforms: U) {
self.uniforms = Some(uniforms);
}
pub fn get_idx(&self) -> usize {
self.shader_idx
}
pub fn get_uniforms(&self) -> &U { self.uniforms.as_ref().unwrap()
}
}
enum DrawType {
POINT,
LINE,
TRIANGLE,
}
impl<'a> Screen<'a> {
pub fn load_frag_shader<U: Uniforms>(
&mut self,
frag_filename: &str,
uniforms: U,
) -> Result<ShaderInfo<U>, ProcessingErr> {
let fsh = parse_includes(frag_filename)?;
let mut ff = File::create("full.frag").map_err(|e| ProcessingErr::FullShaderNoCreate(e))?;
ff.write_all(fsh.as_bytes()).map_err(|e| ProcessingErr::FullShaderNoWrite(e))?;
ff.flush().map_err(|e| ProcessingErr::FullShaderNoCreate(e))?;
let vsh = "
#version "
.to_owned() + &self.glsl_version +
"
in vec3 position;
in vec4 color;
out vec4 vColor;
out vec3 Position;
uniform \
mat4 MVP;
void main() {
vColor = color;
Position = position;
gl_Position = MVP \
* vec4(position, 1.0);
}
";
let program = match self.display {
ScreenType::Window(ref d) => {
match glium::Program::new(
d,
glium::program::ProgramCreationInput::SourceCode {
vertex_shader: &vsh,
tessellation_control_shader: None,
tessellation_evaluation_shader: None,
geometry_shader: None,
fragment_shader: &fsh,
transform_feedback_varyings: None,
outputs_srgb: true,
uses_point_size: true,
},
) {
Ok(res) => res,
Err(e) => return Err(ProcessingErr::ShaderCompileFail(e))
}
}
ScreenType::Headless(ref d) => {
match glium::Program::new(
d,
glium::program::ProgramCreationInput::SourceCode {
vertex_shader: &vsh,
tessellation_control_shader: None,
tessellation_evaluation_shader: None,
geometry_shader: None,
fragment_shader: &fsh,
transform_feedback_varyings: None,
outputs_srgb: true,
uses_point_size: true,
},
) {
Ok(res) => res,
Err(e) => return Err(ProcessingErr::ShaderCompileFail(e))
}
}
};
self.shader_bank.push(program);
Ok(ShaderInfo {
shader_idx: self.shader_bank.len() - 1,
uniforms: Some(uniforms),
})
}
#[inline]
pub fn shader<U: Uniforms>(&mut self, which_shader: &ShaderInfo<U>) {
self.alternate_shader = which_shader.shader_idx;
self.using_alternate_shader = true;
self.curr_shader = which_shader.shader_idx;
}
#[inline]
pub fn reset_shader(&mut self) {
self.curr_shader = 0;
self.using_alternate_shader = false;
}
}
fn parse_includes(filename: &str) -> Result<String, ProcessingErr> {
let ff = File::open(filename).map_err(|e| ProcessingErr::ShaderNotFound(e))?;
let mut total_contents: Vec<String> = Vec::with_capacity(1);
let mut line_num = 0;
for line in BufReader::new(ff).lines() {
line_num += 1;
let l = line.map_err(|e| ProcessingErr::ErrorReadingShader(line_num, e))?;
if l.starts_with("#include") {
let mut lparts = l.split('<');
let ifname = lparts.nth(1).ok_or(
ProcessingErr::ErrorReadingShader(line_num,
std::io::Error::new(std::io::ErrorKind::InvalidInput,
ErrorReadingIncludeLineInShader::new(
"It is possible that the name for the include file is missing."
)
)
)
)?;
let ln = ifname.len();
let mut dat = File::open(&ifname[0..ln - 1]).map_err(|e| ProcessingErr::IncludeNotFound(e))?;
let mut contents = String::new();
dat.read_to_string(&mut contents).map_err(|e| ProcessingErr::ErrorReadingInclude(e))?;
total_contents.push(contents);
} else {
total_contents.push(l);
}
}
Ok(total_contents.join("\n"))
}