use crate::*;
#[derive(Clone, Debug)]
pub struct Effect {
pub id: String,
pub name: Option<String>,
pub asset: Option<Box<Asset>>,
pub annotate: Vec<Annotate>,
pub image: Vec<Image>,
pub new_param: Vec<NewParam>,
pub profile: Vec<Profile>,
pub extra: Vec<Extra>,
}
impl XNode for Effect {
const NAME: &'static str = "effect";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
let res = Effect {
id: element.attr("id").ok_or("expected id attr")?.into(),
name: element.attr("name").map(Into::into),
asset: Asset::parse_opt_box(&mut it)?,
annotate: Annotate::parse_list(&mut it)?,
image: Image::parse_list(&mut it)?,
new_param: NewParam::parse_list(&mut it)?,
profile: parse_list_many(&mut it, Profile::parse)?,
extra: Extra::parse_many(it)?,
};
if res.profile.is_empty() {
return Err("expected at least one profile".into());
}
Ok(res)
}
}
impl XNodeWrite for Effect {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let mut e = Self::elem();
e.attr("id", &self.id);
e.opt_attr("name", &self.name);
let e = e.start(w)?;
self.asset.write_to(w)?;
self.annotate.write_to(w)?;
self.image.write_to(w)?;
self.new_param.write_to(w)?;
self.profile.write_to(w)?;
self.extra.write_to(w)?;
e.end(w)
}
}
impl Effect {
pub fn new(id: impl Into<String>, technique: TechniqueFx<CommonData>) -> Self {
Self {
id: id.into(),
name: None,
asset: None,
annotate: vec![],
image: vec![],
new_param: vec![],
profile: vec![ProfileCommon::new(technique).into()],
extra: vec![],
}
}
pub fn shader(id: impl Into<String>, shader: impl Into<Shader>) -> Self {
Self::new(id, TechniqueFx::new("common", CommonData::shader(shader)))
}
pub fn get_common_profile(&self) -> Option<&ProfileCommon> {
self.profile.iter().find_map(Profile::as_common)
}
pub fn get_param(&self, sid: &str) -> Option<&NewParam> {
self.new_param.iter().rev().find(|p| p.sid == sid)
}
}
#[derive(Clone, Debug, Default)]
pub struct InstanceEffectData {
pub technique_hint: Vec<TechniqueHint>,
pub set_param: Vec<EffectSetParam>,
}
impl XNodeWrite for InstanceEffectData {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
self.technique_hint.write_to(w)?;
self.set_param.write_to(w)
}
}
impl Instantiate for Effect {
const INSTANCE: &'static str = "instance_effect";
type Data = InstanceEffectData;
fn parse_data(_: &Element, it: &mut ElementIter<'_>) -> Result<Self::Data> {
Ok(InstanceEffectData {
technique_hint: TechniqueHint::parse_list(it)?,
set_param: EffectSetParam::parse_list(it)?,
})
}
fn is_empty(data: &Self::Data) -> bool {
data.technique_hint.is_empty() && data.set_param.is_empty()
}
}
#[derive(Clone, Debug)]
pub struct BindVertexInput {
pub semantic: String,
pub input_semantic: String,
pub input_set: Option<u32>,
}
impl BindVertexInput {
pub fn new(
semantic: impl Into<String>,
input_semantic: impl Into<String>,
input_set: Option<u32>,
) -> Self {
Self {
semantic: semantic.into(),
input_semantic: input_semantic.into(),
input_set,
}
}
}
impl XNode for BindVertexInput {
const NAME: &'static str = "bind_vertex_input";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let semantic = element.attr("semantic");
let input_semantic = element.attr("input_semantic");
Ok(BindVertexInput {
semantic: semantic.ok_or("missing semantic attribute")?.into(),
input_semantic: input_semantic.ok_or("missing input semantic")?.into(),
input_set: parse_attr(element.attr("input_set"))?,
})
}
}
impl XNodeWrite for BindVertexInput {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let mut e = Self::elem();
e.attr("semantic", &self.semantic);
e.attr("input_semantic", &self.input_semantic);
e.opt_print_attr("input_set", &self.input_set);
e.end(w)
}
}
pub trait ProfileData: XNodeWrite + Sized {
fn parse(it: &mut ElementIter<'_>) -> Result<Self>;
}
#[derive(Clone, Debug)]
pub struct TechniqueFx<T> {
pub id: Option<String>,
pub sid: String,
pub asset: Option<Box<Asset>>,
pub data: T,
pub extra: Vec<Extra>,
}
impl<T> TechniqueFx<T> {
pub fn new(sid: impl Into<String>, data: T) -> Self {
Self {
id: None,
sid: sid.into(),
asset: None,
data,
extra: vec![],
}
}
pub fn default(sid: impl Into<String>) -> Self
where
T: Default,
{
Self::new(sid, T::default())
}
}
impl<T: ProfileData> XNode for TechniqueFx<T> {
const NAME: &'static str = "technique";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
Ok(TechniqueFx {
id: element.attr("id").map(Into::into),
sid: element.attr("sid").ok_or("expecting sid attr")?.into(),
asset: Asset::parse_opt_box(&mut it)?,
data: T::parse(&mut it)?,
extra: Extra::parse_many(it)?,
})
}
}
impl<T: ProfileData> XNodeWrite for TechniqueFx<T> {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let mut e = Self::elem();
e.opt_attr("id", &self.id);
e.attr("sid", &self.sid);
let e = e.start(w)?;
self.asset.write_to(w)?;
self.data.write_to(w)?;
self.extra.write_to(w)?;
e.end(w)
}
}
#[derive(Clone, Debug)]
pub struct TechniqueHint {
pub platform: Option<String>,
pub ref_: String,
pub profile: Option<String>,
}
impl TechniqueHint {
pub fn new(
platform: impl Into<String>,
ref_: impl Into<String>,
profile: impl Into<String>,
) -> Self {
Self {
platform: Some(platform.into()),
ref_: ref_.into(),
profile: Some(profile.into()),
}
}
}
impl XNode for TechniqueHint {
const NAME: &'static str = "technique_hint";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
Ok(TechniqueHint {
platform: element.attr("platform").map(Into::into),
ref_: element.attr("ref").ok_or("expected 'ref' attr")?.into(),
profile: element.attr("profile").map(Into::into),
})
}
}
impl XNodeWrite for TechniqueHint {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let mut e = Self::elem();
e.opt_attr("platform", &self.platform);
e.attr("ref", &self.ref_);
e.opt_attr("profile", &self.profile);
e.end(w)
}
}