use super::*;
#[derive(Default)]
pub(super) struct LibraryEffects<'a> {
pub(super) effects: HashMap<&'a str, Effect<'a>>,
}
pub(super) struct Effect<'a> {
pub(super) id: &'a str,
pub(super) profile: ProfileCommon<'a>,
}
pub(super) struct ProfileCommon<'a> {
pub(super) surfaces: HashMap<&'a str, Surface<'a>>,
pub(super) samplers: HashMap<&'a str, Sampler<'a>>,
pub(super) technique: Technique<'a>,
}
pub(super) struct Technique<'a> {
#[allow(dead_code)] pub(super) ty: ShadeType,
pub(super) emission: ColorAndTexture<'a>,
pub(super) ambient: ColorAndTexture<'a>,
pub(super) diffuse: ColorAndTexture<'a>,
pub(super) specular: ColorAndTexture<'a>,
pub(super) reflective: ColorAndTexture<'a>,
pub(super) transparent: ColorAndTexture<'a>,
pub(super) has_transparency: bool,
pub(super) rgb_transparency: bool,
pub(super) invert_transparency: bool,
pub(super) shininess: f32,
pub(super) reflectivity: f32,
pub(super) transparency: f32,
pub(super) index_of_refraction: f32,
pub(super) double_sided: bool,
pub(super) bump: Texture<'a>,
pub(super) wireframe: bool,
pub(super) faceted: bool,
}
pub(super) struct Surface<'a> {
pub(super) init_from: Uri<'a, Image<'a>>,
}
pub(super) struct Sampler<'a> {
pub(super) source: &'a str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(super) enum ShadeType {
Constant,
Lambert,
Phong,
Blinn,
}
pub(super) struct ColorAndTexture<'a> {
pub(super) color: Color4,
pub(super) texture: Texture<'a>,
}
impl ColorAndTexture<'_> {
fn new(color: Color4) -> Self {
Self {
color,
texture: Texture {
texture: "",
},
}
}
}
pub(super) struct Texture<'a> {
pub(super) texture: &'a str,
}
pub(super) fn parse_library_effects<'a>(
cx: &mut Context<'a>,
node: xml::Node<'a, '_>,
) -> io::Result<()> {
debug_assert_eq!(node.tag_name().name(), "library_effects");
for child in node.element_children() {
match child.tag_name().name() {
"effect" => {
let effect = parse_effect(cx, child)?;
cx.library_effects.effects.insert(effect.id, effect);
}
"asset" | "extra" => { }
_ => return Err(error::unexpected_child_elem(child)),
}
}
Ok(())
}
fn parse_effect<'a>(cx: &mut Context<'a>, node: xml::Node<'a, '_>) -> io::Result<Effect<'a>> {
debug_assert_eq!(node.tag_name().name(), "effect");
let id = node.required_attribute("id")?;
let mut profile = None;
for child in node.element_children() {
if child.tag_name().name() == "profile_COMMON" {
profile = Some(parse_profile_common(cx, child)?);
}
}
let profile = match profile {
Some(profile) => profile,
None => return Err(error::exactly_one_elem(node, "profile_COMMON")),
};
Ok(Effect {
id,
profile,
})
}
fn parse_profile_common<'a>(
cx: &mut Context<'a>,
node: xml::Node<'a, '_>,
) -> io::Result<ProfileCommon<'a>> {
debug_assert_eq!(node.tag_name().name(), "profile_COMMON");
let mut surfaces = HashMap::default();
let mut samplers = HashMap::default();
let mut technique = None;
for child in node.element_children() {
match child.tag_name().name() {
"newparam" => {
parse_newparam(cx, child, &mut surfaces, &mut samplers)?;
}
"technique" => {
for t in child.element_children() {
let name = t.tag_name().name();
match name {
"constant" | "lambert" | "phong" | "blinn" => {
technique = Some(parse_technique(t, name.parse().unwrap())?);
}
"asset" | "extra" => { }
_ => {}
}
}
}
"asset" | "extra" => { }
_ => return Err(error::unexpected_child_elem(child)),
}
}
let technique = match technique {
Some(technique) => technique,
None => return Err(error::exactly_one_elem(node, "technique")),
};
Ok(ProfileCommon {
surfaces,
samplers,
technique,
})
}
fn parse_newparam<'a>(
_cx: &mut Context<'a>,
node: xml::Node<'a, '_>,
surfaces: &mut HashMap<&'a str, Surface<'a>>,
samplers: &mut HashMap<&'a str, Sampler<'a>>,
) -> io::Result<()> {
debug_assert_eq!(node.tag_name().name(), "newparam");
let sid = node.required_attribute("sid")?;
for child in node.element_children() {
match child.tag_name().name() {
"surface" => {
if let Some(init) = child.child("init_from") {
surfaces.insert(
sid,
Surface {
init_from: Uri::from_id(init.trimmed_text()),
},
);
}
}
"sampler2D" => {
if let Some(source) = child.child("source") {
samplers.insert(
sid,
Sampler {
source: source.trimmed_text(),
},
);
}
}
_ => return Err(error::unexpected_child_elem(child)),
}
}
Ok(())
}
impl FromStr for ShadeType {
type Err = io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"constant" => Self::Constant,
"lambert" => Self::Lambert,
"phong" => Self::Phong,
"blinn" => Self::Blinn,
_ => bail!("unknown shade type {:?}", s),
})
}
}
fn parse_technique<'a>(node: xml::Node<'a, '_>, ty: ShadeType) -> io::Result<Technique<'a>> {
debug_assert_eq!(node.tag_name().name().parse::<ShadeType>().unwrap(), ty);
let mut effect = Technique::new(ty);
for child in node.element_children() {
let name = child.tag_name().name();
match name {
"emission" => {
parse_effect_color(
child,
&mut effect.emission.color,
&mut effect.emission.texture,
)?;
}
"ambient" => {
parse_effect_color(
child,
&mut effect.ambient.color,
&mut effect.ambient.texture,
)?;
}
"diffuse" => {
parse_effect_color(
child,
&mut effect.diffuse.color,
&mut effect.diffuse.texture,
)?;
}
"specular" => {
parse_effect_color(
child,
&mut effect.specular.color,
&mut effect.specular.texture,
)?;
}
"reflective" => {
parse_effect_color(
child,
&mut effect.reflective.color,
&mut effect.reflective.texture,
)?;
}
"transparent" => {
effect.has_transparency = true;
if let Some(opaque) = child.parse_attribute::<Opaque>("opaque")? {
effect.rgb_transparency = opaque.rgb_transparency();
effect.invert_transparency = opaque.invert_transparency();
}
parse_effect_color(
child,
&mut effect.transparent.color,
&mut effect.transparent.texture,
)?;
}
"shininess" => {
if let Some(n) = parse_effect_float(child)? {
effect.shininess = n;
}
}
"reflectivity" => {
if let Some(n) = parse_effect_float(child)? {
effect.reflectivity = n;
}
}
"transparency" => {
if let Some(n) = parse_effect_float(child)? {
effect.transparency = n;
}
}
"index_of_refraction" => {
if let Some(n) = parse_effect_float(child)? {
effect.index_of_refraction = n;
}
}
"double_sided" => {
effect.double_sided = node.parse_required_attribute(name)?;
}
"bump" => {
let mut dummy = [0.; 4];
parse_effect_color(child, &mut dummy, &mut effect.bump)?;
}
"wireframe" => {
effect.wireframe = node.parse_required_attribute(name)?;
}
"faceted" => {
effect.faceted = node.parse_required_attribute(name)?;
}
_ => {}
}
}
Ok(effect)
}
impl Technique<'_> {
fn new(ty: ShadeType) -> Self {
Self {
ty,
emission: ColorAndTexture::new([0.0, 0.0, 0.0, 1.0]),
ambient: ColorAndTexture::new([0.1, 0.1, 0.1, 1.0]),
diffuse: ColorAndTexture::new([0.6, 0.6, 0.6, 1.0]),
specular: ColorAndTexture::new([0.4, 0.4, 0.4, 1.0]),
transparent: ColorAndTexture::new([1.0, 1.0, 1.0, 1.0]),
reflective: ColorAndTexture::new([0.0, 0.0, 0.0, 1.0]),
shininess: 10.0,
index_of_refraction: 1.0,
reflectivity: 0.0,
transparency: 1.0,
has_transparency: false,
rgb_transparency: false,
invert_transparency: false,
double_sided: false,
bump: Texture {
texture: "",
},
wireframe: false,
faceted: false,
}
}
}
fn parse_effect_color<'a>(
node: xml::Node<'a, '_>,
color: &mut Color4,
texture: &mut Texture<'a>,
) -> io::Result<()> {
for child in node.element_children() {
match child.tag_name().name() {
"color" => {
let content = xml::comma_to_period(child.trimmed_text());
let mut iter = xml::parse_float_array_exact(&content, 4);
let map_err = |e| {
format_err!(
"{e} in <{}> element ({})",
child.tag_name().name(),
child.text_location(),
)
};
let r = iter.next().unwrap().map_err(map_err)?;
let g = iter.next().unwrap().map_err(map_err)?;
let b = iter.next().unwrap().map_err(map_err)?;
let a = iter.next().unwrap().map_err(map_err)?;
*color = [r, g, b, a];
}
"texture" => {
let _texcoord = child.required_attribute("texcoord")?;
*texture = Texture {
texture: child.required_attribute("texture")?,
};
}
"param" => {
}
_ => {}
}
}
Ok(())
}
fn parse_effect_float(node: xml::Node<'_, '_>) -> io::Result<Option<f32>> {
let mut float = None;
for child in node.element_children() {
match child.tag_name().name() {
"float" => {
let content = xml::comma_to_period(child.trimmed_text());
float = Some(
float::parse(content.as_bytes())
.ok_or_else(|| format_err!("error while parsing a float"))?,
);
}
"param" => {
}
_ => return Err(error::unexpected_child_elem(child)),
}
}
Ok(float)
}
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Eq)]
enum Opaque {
A_ZERO,
A_ONE,
RGB_ZERO,
RGB_ONE,
}
impl Opaque {
fn rgb_transparency(self) -> bool {
matches!(self, Self::RGB_ZERO | Self::RGB_ONE)
}
fn invert_transparency(self) -> bool {
matches!(self, Self::RGB_ZERO | Self::A_ZERO)
}
}
impl FromStr for Opaque {
type Err = io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"A_ZERO" => Self::A_ZERO,
"A_ONE" => Self::A_ONE,
"RGB_ZERO" => Self::RGB_ZERO,
"RGB_ONE" => Self::RGB_ONE,
_ => bail!("unknown opaque type {:?}", s),
})
}
}