radar-gi/src/components/program.rs
2024-07-18 20:52:15 +08:00

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");
}
}