2022-10-23 15:59:18 +11:00
|
|
|
use crate::PreprocessError;
|
|
|
|
use librashader::{ShaderFormat, ShaderParameter};
|
2022-10-22 14:37:47 +11:00
|
|
|
use nom::bytes::complete::{is_not, tag, take_until, take_while};
|
|
|
|
use nom::combinator::map_res;
|
|
|
|
use nom::number::complete::float;
|
|
|
|
use nom::sequence::delimited;
|
2022-10-23 15:59:18 +11:00
|
|
|
use nom::IResult;
|
|
|
|
use std::str::FromStr;
|
2022-11-21 18:13:10 +11:00
|
|
|
use nom::character::complete::multispace1;
|
2022-10-22 12:04:00 +11:00
|
|
|
|
2022-10-22 17:54:06 +11:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) struct ShaderMeta {
|
|
|
|
pub(crate) format: ShaderFormat,
|
|
|
|
pub(crate) parameters: Vec<ShaderParameter>,
|
2022-10-23 15:59:18 +11:00
|
|
|
pub(crate) name: Option<String>,
|
2022-10-22 17:54:06 +11:00
|
|
|
}
|
2022-10-22 14:37:47 +11:00
|
|
|
|
2022-10-23 15:59:18 +11:00
|
|
|
fn parse_parameter_string(input: &str) -> Result<ShaderParameter, PreprocessError> {
|
2022-11-21 18:13:10 +11:00
|
|
|
fn parse_parameter_string_name(input: &str) -> IResult<&str, (&str, &str)> {
|
2022-10-22 14:37:47 +11:00
|
|
|
let (input, _) = tag("#pragma parameter ")(input)?;
|
2022-11-21 18:13:10 +11:00
|
|
|
let (input, name) = take_while(|c| c != ' ' && c != '\t')(input)?;
|
|
|
|
let (input, _) = multispace1(input)?;
|
2022-10-22 14:37:47 +11:00
|
|
|
let (input, description) = delimited(tag("\""), is_not("\""), tag("\""))(input)?;
|
2022-11-21 18:13:10 +11:00
|
|
|
let (input, _) = multispace1(input)?;
|
|
|
|
Ok((
|
|
|
|
input,
|
|
|
|
(name, description)
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_parameter_string_inner<'a, 'b>(name: &'a str, description: &'a str, input: &'b str) -> IResult<&'b str, ShaderParameter> {
|
2022-10-22 14:37:47 +11:00
|
|
|
let (input, initial) = float(input)?;
|
2022-11-21 18:13:10 +11:00
|
|
|
let (input, _) = multispace1(input)?;
|
2022-10-22 14:37:47 +11:00
|
|
|
let (input, minimum) = float(input)?;
|
2022-11-21 18:13:10 +11:00
|
|
|
let (input, _) = multispace1(input)?;
|
2022-10-22 14:37:47 +11:00
|
|
|
let (input, maximum) = float(input)?;
|
2022-11-21 18:13:10 +11:00
|
|
|
let (input, _) = multispace1(input)?;
|
2022-10-22 14:37:47 +11:00
|
|
|
let (input, step) = float(input)?;
|
2022-10-23 15:59:18 +11:00
|
|
|
Ok((
|
|
|
|
input,
|
|
|
|
ShaderParameter {
|
|
|
|
id: name.to_string(),
|
|
|
|
description: description.to_string(),
|
|
|
|
initial,
|
|
|
|
minimum,
|
|
|
|
maximum,
|
|
|
|
step,
|
|
|
|
},
|
|
|
|
))
|
2022-10-22 14:37:47 +11:00
|
|
|
}
|
|
|
|
|
2022-11-21 18:13:10 +11:00
|
|
|
|
|
|
|
let Ok((params, (name, description))) = parse_parameter_string_name(input) else {
|
|
|
|
return Err(PreprocessError::PragmaParseError(input.to_string()));
|
|
|
|
};
|
|
|
|
|
|
|
|
// some shaders do some really funky things with their pragmas so we need to be lenient and ignore
|
|
|
|
// that it can be set at all.
|
|
|
|
if let Ok((_, param)) = parse_parameter_string_inner(name, description, params) {
|
|
|
|
Ok(param)
|
2022-10-22 14:37:47 +11:00
|
|
|
} else {
|
2022-11-21 18:13:10 +11:00
|
|
|
Ok(ShaderParameter {
|
|
|
|
id: name.to_string(),
|
|
|
|
description: description.to_string(),
|
|
|
|
initial: 0f32,
|
|
|
|
minimum: 0f32,
|
|
|
|
maximum: 0f32,
|
|
|
|
step: 0f32,
|
|
|
|
})
|
2022-10-22 14:37:47 +11:00
|
|
|
}
|
2022-11-21 18:13:10 +11:00
|
|
|
|
2022-10-22 14:37:47 +11:00
|
|
|
}
|
|
|
|
|
2022-10-22 17:54:06 +11:00
|
|
|
pub(crate) fn parse_pragma_meta(source: impl AsRef<str>) -> Result<ShaderMeta, PreprocessError> {
|
2022-10-22 14:37:47 +11:00
|
|
|
let source = source.as_ref();
|
|
|
|
let mut parameters: Vec<ShaderParameter> = Vec::new();
|
|
|
|
let mut format = ShaderFormat::default();
|
2022-10-22 17:54:06 +11:00
|
|
|
let mut name = None;
|
2022-10-22 14:37:47 +11:00
|
|
|
for line in source.lines() {
|
|
|
|
if line.starts_with("#pragma parameter ") {
|
|
|
|
let parameter = parse_parameter_string(line)?;
|
|
|
|
if let Some(existing) = parameters.iter().find(|&p| p.id == parameter.id) {
|
|
|
|
if existing != ¶meter {
|
2022-10-23 15:59:18 +11:00
|
|
|
return Err(PreprocessError::DuplicatePragmaError(parameter.id));
|
2022-10-22 14:37:47 +11:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
parameters.push(parameter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if line.starts_with("#pragma format ") {
|
|
|
|
if format != ShaderFormat::Unknown {
|
2022-10-23 15:59:18 +11:00
|
|
|
return Err(PreprocessError::DuplicatePragmaError(line.to_string()));
|
2022-10-22 14:37:47 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
let format_string = line["#pragma format ".len()..].trim();
|
|
|
|
format = ShaderFormat::from_str(&format_string)?;
|
|
|
|
|
|
|
|
if format == ShaderFormat::Unknown {
|
2022-10-23 15:59:18 +11:00
|
|
|
return Err(PreprocessError::UnknownShaderFormat);
|
2022-10-22 14:37:47 +11:00
|
|
|
}
|
|
|
|
}
|
2022-10-22 17:54:06 +11:00
|
|
|
|
|
|
|
if line.starts_with("#pragma name ") {
|
|
|
|
if name.is_some() {
|
|
|
|
return Err(PreprocessError::DuplicatePragmaError(line.to_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
name = Some(line.trim().to_string())
|
|
|
|
}
|
2022-10-22 14:37:47 +11:00
|
|
|
}
|
|
|
|
|
2022-10-23 15:59:18 +11:00
|
|
|
Ok(ShaderMeta {
|
|
|
|
name,
|
|
|
|
format,
|
|
|
|
parameters,
|
|
|
|
})
|
2022-10-22 14:37:47 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use crate::pragma::parse_parameter_string;
|
2022-10-23 15:59:18 +11:00
|
|
|
use librashader::ShaderParameter;
|
2022-10-22 14:37:47 +11:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn parses_parameter_pragma() {
|
|
|
|
assert_eq!(ShaderParameter {
|
|
|
|
id: "exc".to_string(),
|
|
|
|
description: "orizontal correction hack (games where players stay at center)".to_string(),
|
|
|
|
initial: 0.0,
|
|
|
|
minimum: -10.0,
|
|
|
|
maximum: 10.0,
|
|
|
|
step: 0.25
|
|
|
|
}, parse_parameter_string(r#"#pragma parameter exc "orizontal correction hack (games where players stay at center)" 0.0 -10.0 10.0 0.25"#).unwrap())
|
|
|
|
}
|
2022-10-23 15:59:18 +11:00
|
|
|
}
|