use crate::*;
#[derive(Clone, Debug)]
pub struct Render {
pub camera_node: UrlRef<Node>,
pub layers: Vec<String>,
pub instance_effect: Option<Instance<Effect>>,
}
impl Render {
pub fn new(camera_node: Url, layers: Vec<String>, instance_effect: Url) -> Self {
Self {
camera_node: Ref::new(camera_node),
layers,
instance_effect: Some(Instance::new(instance_effect)),
}
}
}
impl XNode for Render {
const NAME: &'static str = "render";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
let res = Render {
camera_node: parse_attr(element.attr("camera_node"))?
.ok_or("missing camera_node attr")?,
layers: parse_list("layer", &mut it, parse_text)?,
instance_effect: Instance::parse_opt(&mut it)?,
};
finish(res, it)
}
}
impl XNodeWrite for Render {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let mut e = Self::elem();
e.print_attr("camera_node", &self.camera_node);
let e = e.start(w)?;
many(&self.layers, |e| ElemBuilder::print_str("layer", e, w))?;
self.instance_effect.write_to(w)?;
e.end(w)
}
}
#[derive(Clone, Debug)]
pub enum Shader {
Blinn(Blinn),
Constant(ConstantFx),
Lambert(Lambert),
Phong(Phong),
}
impl From<Blinn> for Shader {
fn from(v: Blinn) -> Self {
Self::Blinn(v)
}
}
impl From<ConstantFx> for Shader {
fn from(v: ConstantFx) -> Self {
Self::Constant(v)
}
}
impl From<Lambert> for Shader {
fn from(v: Lambert) -> Self {
Self::Lambert(v)
}
}
impl From<Phong> for Shader {
fn from(v: Phong) -> Self {
Self::Phong(v)
}
}
impl Shader {
pub fn parse(e: &Element) -> Result<Option<Self>> {
Ok(Some(match e.name() {
Blinn::NAME => Self::Blinn(Blinn::parse(e)?),
ConstantFx::NAME => Self::Constant(ConstantFx::parse(e)?),
Lambert::NAME => Self::Lambert(Lambert::parse(e)?),
Phong::NAME => Self::Phong(Phong::parse(e)?),
_ => return Ok(None),
}))
}
pub fn on_textures<'a, E>(
&'a self,
f: &mut impl FnMut(&'a Texture) -> Result<(), E>,
) -> Result<(), E> {
match self {
Self::Blinn(s) => s.on_textures(f),
Self::Constant(s) => s.on_textures(f),
Self::Lambert(s) => s.on_textures(f),
Self::Phong(s) => s.on_textures(f),
}
}
}
impl XNodeWrite for Shader {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
match self {
Self::Blinn(e) => e.write_to(w),
Self::Constant(e) => e.write_to(w),
Self::Lambert(e) => e.write_to(w),
Self::Phong(e) => e.write_to(w),
}
}
}
#[derive(Clone, Default, Debug)]
pub struct Blinn {
pub emission: Option<WithSid<ColorParam>>,
pub ambient: Option<WithSid<ColorParam>>,
pub diffuse: Option<WithSid<ColorParam>>,
pub specular: Option<WithSid<ColorParam>>,
pub shininess: Option<WithSid<FloatParam>>,
pub reflective: Option<WithSid<ColorParam>>,
pub reflectivity: Option<WithSid<FloatParam>>,
pub transparent: Option<WithSid<ColorParam>>,
pub transparency: Option<WithSid<FloatParam>>,
pub index_of_refraction: Option<WithSid<FloatParam>>,
}
impl XNode for Blinn {
const NAME: &'static str = "blinn";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
Ok(Blinn {
emission: parse_opt("emission", &mut it, WithSid::parse)?,
ambient: parse_opt("ambient", &mut it, WithSid::parse)?,
diffuse: parse_opt("diffuse", &mut it, WithSid::parse)?,
specular: parse_opt("specular", &mut it, WithSid::parse)?,
shininess: parse_opt("shininess", &mut it, WithSid::parse)?,
reflective: parse_opt("reflective", &mut it, WithSid::parse)?,
reflectivity: parse_opt("reflectivity", &mut it, WithSid::parse)?,
transparent: parse_opt("transparent", &mut it, WithSid::parse)?,
transparency: parse_opt("transparency", &mut it, WithSid::parse)?,
index_of_refraction: parse_opt("index_of_refraction", &mut it, WithSid::parse)?,
})
}
}
impl XNodeWrite for Blinn {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let e = Self::elem().start(w)?;
WithSid::write_opt(&self.emission, "emission", w)?;
WithSid::write_opt(&self.ambient, "ambient", w)?;
WithSid::write_opt(&self.diffuse, "diffuse", w)?;
WithSid::write_opt(&self.specular, "specular", w)?;
WithSid::write_opt(&self.shininess, "shininess", w)?;
WithSid::write_opt(&self.reflective, "reflective", w)?;
WithSid::write_opt(&self.reflectivity, "reflectivity", w)?;
WithSid::write_opt(&self.transparent, "transparent", w)?;
WithSid::write_opt(&self.transparency, "transparency", w)?;
WithSid::write_opt(&self.index_of_refraction, "index_of_refraction", w)?;
e.end(w)
}
}
impl Blinn {
pub fn on_textures<'a, E>(
&'a self,
f: &mut impl FnMut(&'a Texture) -> Result<(), E>,
) -> Result<(), E> {
on_color_as_texture(&self.emission, f)?;
on_color_as_texture(&self.ambient, f)?;
on_color_as_texture(&self.diffuse, f)?;
on_color_as_texture(&self.specular, f)?;
on_color_as_texture(&self.reflective, f)?;
on_color_as_texture(&self.transparent, f)
}
}
#[derive(Clone, Default, Debug)]
pub struct ConstantFx {
pub emission: Option<WithSid<ColorParam>>,
pub reflective: Option<WithSid<ColorParam>>,
pub reflectivity: Option<WithSid<FloatParam>>,
pub transparent: Option<WithSid<ColorParam>>,
pub transparency: Option<WithSid<FloatParam>>,
pub index_of_refraction: Option<WithSid<FloatParam>>,
}
impl XNode for ConstantFx {
const NAME: &'static str = "constant";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
Ok(ConstantFx {
emission: parse_opt("emission", &mut it, WithSid::parse)?,
reflective: parse_opt("reflective", &mut it, WithSid::parse)?,
reflectivity: parse_opt("reflectivity", &mut it, WithSid::parse)?,
transparent: parse_opt("transparent", &mut it, WithSid::parse)?,
transparency: parse_opt("transparency", &mut it, WithSid::parse)?,
index_of_refraction: parse_opt("index_of_refraction", &mut it, WithSid::parse)?,
})
}
}
impl XNodeWrite for ConstantFx {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let e = Self::elem().start(w)?;
WithSid::write_opt(&self.emission, "emission", w)?;
WithSid::write_opt(&self.reflective, "reflective", w)?;
WithSid::write_opt(&self.reflectivity, "reflectivity", w)?;
WithSid::write_opt(&self.transparent, "transparent", w)?;
WithSid::write_opt(&self.transparency, "transparency", w)?;
WithSid::write_opt(&self.index_of_refraction, "index_of_refraction", w)?;
e.end(w)
}
}
impl ConstantFx {
pub fn on_textures<'a, E>(
&'a self,
f: &mut impl FnMut(&'a Texture) -> Result<(), E>,
) -> Result<(), E> {
on_color_as_texture(&self.emission, f)?;
on_color_as_texture(&self.reflective, f)?;
on_color_as_texture(&self.transparent, f)
}
}
#[derive(Clone, Default, Debug)]
pub struct Lambert {
pub emission: Option<WithSid<ColorParam>>,
pub ambient: Option<WithSid<ColorParam>>,
pub diffuse: Option<WithSid<ColorParam>>,
pub reflective: Option<WithSid<ColorParam>>,
pub reflectivity: Option<WithSid<FloatParam>>,
pub transparent: Option<WithSid<ColorParam>>,
pub transparency: Option<WithSid<FloatParam>>,
pub index_of_refraction: Option<WithSid<FloatParam>>,
}
impl XNode for Lambert {
const NAME: &'static str = "lambert";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
Ok(Lambert {
emission: parse_opt("emission", &mut it, WithSid::parse)?,
ambient: parse_opt("ambient", &mut it, WithSid::parse)?,
diffuse: parse_opt("diffuse", &mut it, WithSid::parse)?,
reflective: parse_opt("reflective", &mut it, WithSid::parse)?,
reflectivity: parse_opt("reflectivity", &mut it, WithSid::parse)?,
transparent: parse_opt("transparent", &mut it, WithSid::parse)?,
transparency: parse_opt("transparency", &mut it, WithSid::parse)?,
index_of_refraction: parse_opt("index_of_refraction", &mut it, WithSid::parse)?,
})
}
}
impl XNodeWrite for Lambert {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let e = Self::elem().start(w)?;
WithSid::write_opt(&self.emission, "emission", w)?;
WithSid::write_opt(&self.ambient, "ambient", w)?;
WithSid::write_opt(&self.diffuse, "diffuse", w)?;
WithSid::write_opt(&self.reflective, "reflective", w)?;
WithSid::write_opt(&self.reflectivity, "reflectivity", w)?;
WithSid::write_opt(&self.transparent, "transparent", w)?;
WithSid::write_opt(&self.transparency, "transparency", w)?;
WithSid::write_opt(&self.index_of_refraction, "index_of_refraction", w)?;
e.end(w)
}
}
impl Lambert {
pub fn on_textures<'a, E>(
&'a self,
f: &mut impl FnMut(&'a Texture) -> Result<(), E>,
) -> Result<(), E> {
on_color_as_texture(&self.emission, f)?;
on_color_as_texture(&self.ambient, f)?;
on_color_as_texture(&self.diffuse, f)?;
on_color_as_texture(&self.reflective, f)?;
on_color_as_texture(&self.transparent, f)
}
}
#[derive(Clone, Default, Debug)]
pub struct Phong {
pub emission: Option<WithSid<ColorParam>>,
pub ambient: Option<WithSid<ColorParam>>,
pub diffuse: Option<WithSid<ColorParam>>,
pub specular: Option<WithSid<ColorParam>>,
pub shininess: Option<WithSid<FloatParam>>,
pub reflective: Option<WithSid<ColorParam>>,
pub reflectivity: Option<WithSid<FloatParam>>,
pub transparent: Option<WithSid<ColorParam>>,
pub transparency: Option<WithSid<FloatParam>>,
pub index_of_refraction: Option<WithSid<FloatParam>>,
}
impl XNode for Phong {
const NAME: &'static str = "phong";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
Ok(Phong {
emission: parse_opt("emission", &mut it, WithSid::parse)?,
ambient: parse_opt("ambient", &mut it, WithSid::parse)?,
diffuse: parse_opt("diffuse", &mut it, WithSid::parse)?,
specular: parse_opt("specular", &mut it, WithSid::parse)?,
shininess: parse_opt("shininess", &mut it, WithSid::parse)?,
reflective: parse_opt("reflective", &mut it, WithSid::parse)?,
reflectivity: parse_opt("reflectivity", &mut it, WithSid::parse)?,
transparent: parse_opt("transparent", &mut it, WithSid::parse)?,
transparency: parse_opt("transparency", &mut it, WithSid::parse)?,
index_of_refraction: parse_opt("index_of_refraction", &mut it, WithSid::parse)?,
})
}
}
impl XNodeWrite for Phong {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let e = Self::elem().start(w)?;
WithSid::write_opt(&self.emission, "emission", w)?;
WithSid::write_opt(&self.ambient, "ambient", w)?;
WithSid::write_opt(&self.diffuse, "diffuse", w)?;
WithSid::write_opt(&self.specular, "specular", w)?;
WithSid::write_opt(&self.shininess, "shininess", w)?;
WithSid::write_opt(&self.reflective, "reflective", w)?;
WithSid::write_opt(&self.reflectivity, "reflectivity", w)?;
WithSid::write_opt(&self.transparent, "transparent", w)?;
WithSid::write_opt(&self.transparency, "transparency", w)?;
WithSid::write_opt(&self.index_of_refraction, "index_of_refraction", w)?;
e.end(w)
}
}
impl Phong {
pub fn on_textures<'a, E>(
&'a self,
f: &mut impl FnMut(&'a Texture) -> Result<(), E>,
) -> Result<(), E> {
on_color_as_texture(&self.emission, f)?;
on_color_as_texture(&self.ambient, f)?;
on_color_as_texture(&self.diffuse, f)?;
on_color_as_texture(&self.specular, f)?;
on_color_as_texture(&self.reflective, f)?;
on_color_as_texture(&self.transparent, f)
}
}
#[derive(Clone, Default, Debug)]
pub struct WithSid<T> {
sid: Option<String>,
data: T,
}
impl<T> Deref for WithSid<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
pub(crate) use private::CanWithSid;
pub(crate) mod private {
use super::*;
pub trait CanWithSid: XNodeWrite + Sized {
fn parse(element: &Element) -> Result<Option<Self>>;
fn write_with_sid<W: Write>(&self, sid: &Option<String>, w: &mut XWriter<W>) -> Result<()>;
}
}
impl<T> From<T> for WithSid<T> {
fn from(data: T) -> Self {
Self::new(data)
}
}
impl<T> WithSid<T> {
pub fn new(data: T) -> Self {
Self { sid: None, data }
}
#[allow(clippy::self_named_constructors)]
pub fn with_sid(sid: impl Into<String>, data: T) -> Self {
Self {
sid: Some(sid.into()),
data,
}
}
}
impl<T: CanWithSid> WithSid<T> {
pub fn parse(element: &Element) -> Result<Self> {
let mut it = element.children().peekable();
parse_one_many(&mut it, |e| {
Ok(T::parse(e)?.map(|data| Self {
sid: e.attr("sid").map(Into::into),
data,
}))
})
}
fn write_opt(this: &Option<Self>, name: &str, w: &mut XWriter<impl Write>) -> Result<()> {
opt(this, |this| {
let elem = ElemBuilder::new(name).start(w)?;
this.write_to(w)?;
elem.end(w)
})
}
}
impl<T: CanWithSid> XNodeWrite for WithSid<T> {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
self.data.write_with_sid(&self.sid, w)
}
}
#[derive(Clone, Debug)]
pub enum ColorParam {
Color(Box<[f32; 4]>),
Param(Box<str>),
Texture(Box<Texture>),
}
impl From<[f32; 4]> for ColorParam {
fn from(rgba: [f32; 4]) -> Self {
Self::color(rgba)
}
}
impl From<[f32; 4]> for WithSid<ColorParam> {
fn from(rgba: [f32; 4]) -> Self {
WithSid::new(rgba.into())
}
}
impl From<Texture> for ColorParam {
fn from(tex: Texture) -> Self {
Self::Texture(Box::new(tex))
}
}
impl From<Texture> for WithSid<ColorParam> {
fn from(tex: Texture) -> Self {
WithSid::new(tex.into())
}
}
impl ColorParam {
pub fn color(rgba: [f32; 4]) -> Self {
Self::Color(Box::new(rgba))
}
}
impl CanWithSid for ColorParam {
fn parse(e: &Element) -> Result<Option<Self>> {
Ok(Some(match e.name() {
"color" => Self::Color(parse_array_n(e)?),
Param::NAME => Self::Param(e.attr("ref").ok_or("expected ref attr")?.into()),
Texture::NAME => Self::Texture(Texture::parse_box(e)?),
_ => return Ok(None),
}))
}
fn write_with_sid<W: Write>(&self, sid: &Option<String>, w: &mut XWriter<W>) -> Result<()> {
match self {
Self::Color(arr) => {
let mut e = ElemBuilder::new("color");
e.opt_attr("sid", sid);
let e = e.start(w)?;
print_arr(&**arr, w)?;
e.end(w)
}
Self::Param(ref_) => {
let mut e = ElemBuilder::new(Param::NAME);
e.opt_attr("sid", sid);
e.attr("ref", ref_);
e.end(w)
}
Self::Texture(e) => e.write_to(w),
}
}
}
impl XNodeWrite for ColorParam {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
self.write_with_sid(&None, w)
}
}
impl ColorParam {
pub fn as_texture(&self) -> Option<&Texture> {
match self {
ColorParam::Texture(tex) => Some(tex),
_ => None,
}
}
pub fn as_color(&self) -> Option<&[f32; 4]> {
match self {
ColorParam::Color(c) => Some(c),
_ => None,
}
}
}
#[derive(Clone, Debug)]
pub enum FloatParam {
Float(f32),
Param(Box<str>),
}
impl From<f32> for FloatParam {
fn from(val: f32) -> Self {
Self::Float(val)
}
}
impl From<f32> for WithSid<FloatParam> {
fn from(val: f32) -> Self {
WithSid::new(val.into())
}
}
impl CanWithSid for FloatParam {
fn parse(e: &Element) -> Result<Option<Self>> {
Ok(Some(match e.name() {
"float" => Self::Float(parse_elem(e)?),
Param::NAME => Self::Param(e.attr("ref").ok_or("expected ref attr")?.into()),
_ => return Ok(None),
}))
}
fn write_with_sid<W: Write>(&self, sid: &Option<String>, w: &mut XWriter<W>) -> Result<()> {
match self {
Self::Float(val) => {
let mut e = ElemBuilder::new("float");
e.opt_attr("sid", sid);
let e = e.start(w)?;
print_elem(val, w)?;
e.end(w)
}
Self::Param(ref_) => {
let mut e = ElemBuilder::new(Param::NAME);
e.opt_attr("sid", sid);
e.attr("ref", ref_);
e.end(w)
}
}
}
}
impl XNodeWrite for FloatParam {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
self.write_with_sid(&None, w)
}
}
#[derive(Clone, Debug)]
pub struct Texture {
pub texture: String,
pub texcoord: String,
pub extra: Option<Box<Extra>>,
}
impl Texture {
pub fn new(texture: impl Into<String>, texcoord: impl Into<String>) -> Self {
Self {
texture: texture.into(),
texcoord: texcoord.into(),
extra: None,
}
}
fn write_with_sid<W: Write>(&self, sid: &Option<String>, w: &mut XWriter<W>) -> Result<()> {
let mut e = Self::elem();
e.opt_attr("sid", sid);
e.attr("texture", &self.texture);
e.attr("texcoord", &self.texcoord);
if let Some(extra) = &self.extra {
let e = e.start(w)?;
extra.write_to(w)?;
e.end(w)
} else {
e.end(w)
}
}
}
impl XNode for Texture {
const NAME: &'static str = "texture";
fn parse(e: &Element) -> Result<Self> {
let mut it = e.children().peekable();
let res = Texture {
texture: e.attr("texture").ok_or("expected texture attr")?.into(),
texcoord: e.attr("texcoord").ok_or("expected texcoord attr")?.into(),
extra: Extra::parse_opt_box(&mut it)?,
};
finish(res, it)
}
}
impl XNodeWrite for Texture {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
self.write_with_sid(&None, w)
}
}
fn on_color_as_texture<'a, E>(
opt: &'a Option<WithSid<ColorParam>>,
f: &mut impl FnMut(&'a Texture) -> Result<(), E>,
) -> Result<(), E> {
if let Some(WithSid {
data: ColorParam::Texture(tex),
..
}) = opt
{
f(tex)?
}
Ok(())
}