150 lines
4.0 KiB
Rust
150 lines
4.0 KiB
Rust
use glow::{HasContext, NativeUniformLocation};
|
|
|
|
use super::shader::Shader;
|
|
use super::snippets::{CodeType, Snippet};
|
|
use crate::components::CodeComponent;
|
|
use crate::graphics::transforms::{viewport::Viewport, Transform};
|
|
|
|
#[derive(Debug)]
|
|
pub struct Program {
|
|
version: &'static str,
|
|
vertex: Shader,
|
|
fragment: Shader,
|
|
|
|
geometry: Option<Shader>,
|
|
pub native_program: Option<<glow::Context as HasContext>::Program>,
|
|
}
|
|
|
|
impl Program {
|
|
pub fn new(
|
|
vertex: Shader,
|
|
fragment: Shader,
|
|
geometry: Option<Shader>,
|
|
version: &'static str,
|
|
) -> Self {
|
|
let res = Self {
|
|
version,
|
|
vertex,
|
|
fragment,
|
|
geometry,
|
|
native_program: None,
|
|
};
|
|
|
|
res
|
|
}
|
|
|
|
pub fn vertex(&self) -> &Shader {
|
|
&self.vertex
|
|
}
|
|
|
|
pub fn fragment(&self) -> &Shader {
|
|
&self.fragment
|
|
}
|
|
|
|
pub fn set_hook(&mut self, hook: &str, code: &Snippet) {
|
|
self.vertex.set_hook(hook, code);
|
|
self.fragment.set_hook(hook, code);
|
|
self.geometry.as_mut().map(|g| g.set_hook(hook, code));
|
|
}
|
|
|
|
pub fn set_transform<T: Transform>(&mut self, value: &T) {
|
|
self.set_hook("transform", value.snippet());
|
|
}
|
|
|
|
pub fn set_viewport(&mut self, viewport: &Viewport) {
|
|
self.set_hook("viewport", viewport.snippet());
|
|
}
|
|
|
|
pub fn compile(&mut self, gl: &glow::Context) -> crate::errors::Result<()> {
|
|
use crate::errors::Error;
|
|
|
|
self.vertex.define("_GLUMPY__VERTEX_SHADER__");
|
|
self.fragment.define("_GLUMPY__FRAGMENT_SHADER__");
|
|
|
|
unsafe {
|
|
let program = gl.create_program().map_err(|e| Error::InvalidProgram(e))?;
|
|
|
|
let vertex_shader = self.vertex.compile(&self.version, gl);
|
|
let fragment_shader = self.fragment.compile(&self.version, gl);
|
|
|
|
gl.attach_shader(program, vertex_shader);
|
|
gl.attach_shader(program, fragment_shader);
|
|
|
|
// Geom Shader
|
|
let geom_shader = if let Some(geometry) = self.geometry.as_mut() {
|
|
geometry.define("_GLUMPY__GEOMETRY_SHADER__");
|
|
let geometry_shader = geometry.compile(&self.version, gl);
|
|
|
|
gl.attach_shader(program, geometry_shader);
|
|
|
|
Some(geometry_shader)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
gl.link_program(program);
|
|
|
|
if !gl.get_program_link_status(program) {
|
|
return Err(Error::InvalidProgram(gl.get_program_info_log(program)));
|
|
}
|
|
|
|
gl.detach_shader(program, vertex_shader);
|
|
gl.detach_shader(program, fragment_shader);
|
|
if let Some(_) = self.geometry.as_ref() {
|
|
gl.detach_shader(program, geom_shader.unwrap());
|
|
gl.delete_shader(geom_shader.unwrap());
|
|
}
|
|
|
|
gl.delete_shader(vertex_shader);
|
|
gl.delete_shader(fragment_shader);
|
|
|
|
gl.use_program(Some(program));
|
|
|
|
self.native_program = Some(program);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn destroy(&self, gl: &glow::Context) {
|
|
unsafe {
|
|
self.native_program.map(|p| gl.delete_program(p));
|
|
}
|
|
}
|
|
|
|
pub fn get_uniform_location(
|
|
&self,
|
|
gl: &glow::Context,
|
|
name: &str,
|
|
) -> Option<NativeUniformLocation> {
|
|
self.native_program
|
|
.as_ref()
|
|
.map(|p| unsafe {
|
|
let location = gl.get_uniform_location(*p, name);
|
|
location
|
|
})
|
|
.flatten()
|
|
}
|
|
}
|
|
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_program() {
|
|
let vertex = Shader::new(
|
|
glow::VERTEX_SHADER,
|
|
CodeType::<&'static str>::Path("agg-fast-path.vert".into()),
|
|
)
|
|
.unwrap();
|
|
|
|
let fragment = Shader::new(
|
|
glow::FRAGMENT_SHADER,
|
|
CodeType::<&'static str>::Path("agg-fast-path.frag".into()),
|
|
)
|
|
.unwrap();
|
|
|
|
let program = Program::new(vertex, fragment, None, "330 core");
|
|
}
|
|
}
|