use std::borrow::Cow;
#[derive(Debug, Clone)]
pub enum ShaderAsset {
Path(&'static str),
Source {
file_name: &'static str,
fragment: &'static str,
},
Stored(&'static str),
}
impl ShaderAsset {
pub fn fragment_source(&self) -> Cow<'static, str> {
match self {
ShaderAsset::Path(path) => {
Cow::Owned(std::fs::read_to_string(path)
.unwrap_or_else(|e| panic!("Failed to read shader file '{}': {}", path, e)))
}
ShaderAsset::Source { fragment, .. } => Cow::Borrowed(fragment),
ShaderAsset::Stored(name) => {
let mgr = crate::renderer::MATERIAL_MANAGER.lock().unwrap();
match mgr.get_source(name) {
Some(src) => Cow::Owned(src.to_string()),
None => {
eprintln!("Shader storage '{}' not found, using fallback", name);
Cow::Borrowed(crate::renderer::DEFAULT_FRAGMENT_SHADER)
}
}
}
}
}
pub fn cache_key(&self) -> &str {
match self {
ShaderAsset::Path(path) => path,
ShaderAsset::Source { file_name, .. } => file_name,
ShaderAsset::Stored(name) => name,
}
}
}
#[derive(Debug, Clone)]
pub struct ShaderConfig {
pub fragment: Cow<'static, str>,
pub uniforms: Vec<ShaderUniform>,
pub name: String,
}
#[derive(Debug, Clone)]
pub struct ShaderUniform {
pub name: String,
pub value: ShaderUniformValue,
}
#[derive(Debug, Clone)]
pub enum ShaderUniformValue {
Float(f32),
Vec2([f32; 2]),
Vec3([f32; 3]),
Vec4([f32; 4]),
Int(i32),
Mat4([[f32; 4]; 4]),
}
impl From<f32> for ShaderUniformValue {
fn from(v: f32) -> Self {
ShaderUniformValue::Float(v)
}
}
impl From<[f32; 2]> for ShaderUniformValue {
fn from(v: [f32; 2]) -> Self {
ShaderUniformValue::Vec2(v)
}
}
impl From<[f32; 3]> for ShaderUniformValue {
fn from(v: [f32; 3]) -> Self {
ShaderUniformValue::Vec3(v)
}
}
impl From<[f32; 4]> for ShaderUniformValue {
fn from(v: [f32; 4]) -> Self {
ShaderUniformValue::Vec4(v)
}
}
impl From<i32> for ShaderUniformValue {
fn from(v: i32) -> Self {
ShaderUniformValue::Int(v)
}
}
impl From<[[f32; 4]; 4]> for ShaderUniformValue {
fn from(v: [[f32; 4]; 4]) -> Self {
ShaderUniformValue::Mat4(v)
}
}
pub struct ShaderBuilder<'a> {
source: &'a ShaderAsset,
uniforms: Vec<ShaderUniform>,
}
impl<'a> ShaderBuilder<'a> {
pub(crate) fn new(source: &'a ShaderAsset) -> Self {
Self {
source,
uniforms: Vec::new(),
}
}
pub fn uniform(&mut self, name: &str, value: impl Into<ShaderUniformValue>) -> &mut Self {
self.uniforms.push(ShaderUniform {
name: name.to_string(),
value: value.into(),
});
self
}
pub(crate) fn into_config(&mut self) -> ShaderConfig {
ShaderConfig {
fragment: self.source.fragment_source(),
uniforms: std::mem::take(&mut self.uniforms),
name: self.source.cache_key().to_string(),
}
}
}