use crate::{
core::{
Color,
types::{Double, ResourceId, ResourceIdCollection, ResourceIndexCollection},
},
threemf_namespaces::MATERIAL_NS,
};
#[cfg(feature = "write")]
use instant_xml::ToXml;
#[cfg(feature = "memory-optimized-read")]
use instant_xml::FromXml;
#[cfg(feature = "speed-optimized-read")]
use serde::{self, Deserialize};
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(from = "String"))]
#[cfg_attr(feature = "memory-optimized-read", derive(FromXml))]
#[cfg_attr(feature = "write", derive(ToXml))]
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(scalar, ns(MATERIAL_NS), rename_all = "lowercase")
)]
pub enum TileStyle {
#[default]
Wrap,
Mirror,
Clamp,
None,
}
impl From<String> for TileStyle {
fn from(value: String) -> Self {
match value.to_ascii_lowercase().as_str() {
"wrap" => TileStyle::Wrap,
"mirror" => TileStyle::Mirror,
"clamp" => TileStyle::Clamp,
"none" => TileStyle::None,
_ => TileStyle::Wrap,
}
}
}
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(from = "String"))]
#[cfg_attr(feature = "memory-optimized-read", derive(FromXml))]
#[cfg_attr(feature = "write", derive(ToXml))]
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(scalar, ns(MATERIAL_NS), rename_all = "lowercase")
)]
pub enum Filter {
#[default]
Auto,
Linear,
Nearest,
}
impl From<String> for Filter {
fn from(value: String) -> Self {
match value.to_ascii_lowercase().as_str() {
"auto" => Filter::Auto,
"linear" => Filter::Linear,
"nearest" => Filter::Nearest,
_ => Filter::Auto,
}
}
}
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(from = "String"))]
#[cfg_attr(feature = "memory-optimized-read", derive(FromXml))]
#[cfg_attr(feature = "write", derive(ToXml))]
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(scalar, ns(MATERIAL_NS), rename_all = "lowercase")
)]
pub enum BlendMethod {
#[default]
Mix,
Multiply,
}
impl From<String> for BlendMethod {
fn from(value: String) -> Self {
match value.to_ascii_lowercase().as_str() {
"mix" => BlendMethod::Mix,
"multiply" => BlendMethod::Multiply,
_ => BlendMethod::Mix,
}
}
}
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(rename = "colorgroup"))]
#[cfg_attr(feature = "memory-optimized-read", derive(FromXml))]
#[cfg_attr(feature = "write", derive(ToXml))]
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(ns(MATERIAL_NS), rename = "colorgroup")
)]
pub struct ColorGroup {
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub id: ResourceId,
#[cfg_attr(feature = "speed-optimized-read", serde(default, rename = "color"))]
pub color: Vec<ColorElement>,
}
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(rename = "color"))]
#[cfg_attr(feature = "memory-optimized-read", derive(FromXml))]
#[cfg_attr(feature = "write", derive(ToXml))]
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(ns(MATERIAL_NS), rename = "color")
)]
pub struct ColorElement {
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub color: Color,
}
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(rename = "texture2dgroup"))]
#[cfg_attr(feature = "memory-optimized-read", derive(FromXml))]
#[cfg_attr(feature = "write", derive(ToXml))]
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(ns(MATERIAL_NS), rename = "texture2dgroup")
)]
pub struct Texture2DGroup {
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub id: ResourceId,
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute, rename = "texid")
)]
pub texid: ResourceId,
#[cfg_attr(feature = "speed-optimized-read", serde(default, rename = "tex2coord"))]
pub tex2coord: Vec<Tex2Coord>,
}
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(rename = "tex2coord"))]
#[cfg_attr(feature = "write", derive(ToXml))]
#[derive(Debug, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "write", xml(ns(MATERIAL_NS), rename = "tex2coord"))]
pub struct Tex2Coord {
#[cfg_attr(feature = "write", xml(attribute))]
pub u: Double,
#[cfg_attr(feature = "write", xml(attribute))]
pub v: Double,
}
#[cfg(feature = "memory-optimized-read")]
impl<'xml> FromXml<'xml> for Tex2Coord {
#[inline]
fn matches(id: ::instant_xml::Id<'_>, _: Option<::instant_xml::Id<'_>>) -> bool {
id == ::instant_xml::Id {
ns: MATERIAL_NS,
name: "tex2coord",
}
}
fn deserialize<'cx>(
into: &mut Self::Accumulator,
_: &'static str,
deserializer: &mut ::instant_xml::Deserializer<'cx, 'xml>,
) -> ::std::result::Result<(), ::instant_xml::Error> {
use ::instant_xml::Error;
use ::instant_xml::de::Node;
let mut u: f64 = 0.0;
let mut v: f64 = 0.0;
while let Some(node) = deserializer.next() {
let node = node?;
match node {
Node::Attribute(attr) => {
let id = deserializer.attribute_id(&attr)?;
match id.name.as_bytes().first() {
Some(b'u') => {
u = lexical_core::parse(attr.value.as_bytes()).unwrap_or_default()
}
Some(b'v') => {
v = lexical_core::parse(attr.value.as_bytes()).unwrap_or_default()
}
_ => {}
};
}
Node::Open(data) => {
let mut nested = deserializer.nested(data);
nested.ignore()?;
}
Node::Text(_) => {}
_ => {
return Err(Error::UnexpectedNode("Unexpected".to_owned()));
}
}
}
*into = Some(Self {
u: Double::new(u),
v: Double::new(v),
});
Ok(())
}
type Accumulator = Option<Self>;
const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element;
}
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(rename = "compositematerials"))]
#[cfg_attr(feature = "memory-optimized-read", derive(FromXml))]
#[cfg_attr(feature = "write", derive(ToXml))]
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(ns(MATERIAL_NS), rename = "compositematerials")
)]
pub struct CompositeMaterials {
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub id: ResourceId,
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute, rename = "matid")
)]
pub matid: ResourceId,
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub matindices: ResourceIndexCollection,
#[cfg_attr(feature = "speed-optimized-read", serde(default, rename = "composite"))]
pub composite: Vec<Composite>,
}
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(rename = "composite"))]
#[derive(Debug, PartialEq, Clone)]
pub struct Composite {
#[cfg_attr(
feature = "speed-optimized-read",
serde(deserialize_with = "deserialize_composite_values")
)]
pub values: Vec<Double>,
}
#[cfg(feature = "speed-optimized-read")]
fn deserialize_composite_values<'de, D>(deserializer: D) -> Result<Vec<Double>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = <String as serde::Deserialize>::deserialize(deserializer)?;
if s.is_empty() {
return Ok(Vec::new());
}
s.split_whitespace()
.map(|v: &str| {
v.parse::<f64>()
.map(Double::new)
.map_err(|e| serde::de::Error::custom(format!("Invalid f64 value: {}", e)))
})
.collect::<Result<_, _>>()
}
#[cfg(feature = "write")]
impl ToXml for Composite {
fn serialize<W: std::fmt::Write + ?Sized>(
&self,
field: Option<instant_xml::Id<'_>>,
serializer: &mut instant_xml::Serializer<W>,
) -> Result<(), instant_xml::Error> {
let ns = match field {
Some(id) => {
serializer.write_start(id.name, id.ns)?;
id.ns
}
None => {
serializer.write_start("composite", MATERIAL_NS)?;
"" }
};
if !self.values.is_empty() {
let values_str = self
.values
.iter()
.map(|d| d.value().to_string())
.collect::<Vec<_>>()
.join(" ");
serializer.write_attr("values", ns, &values_str)?;
}
serializer.end_empty()?;
Ok(())
}
fn present(&self) -> bool {
true
}
}
#[cfg(feature = "memory-optimized-read")]
impl<'xml> instant_xml::FromXml<'xml> for Composite {
#[inline]
fn matches(id: instant_xml::Id<'_>, _field: Option<instant_xml::Id<'_>>) -> bool {
id == ::instant_xml::Id {
ns: MATERIAL_NS,
name: "composite",
}
}
fn deserialize<'cx>(
into: &mut Self::Accumulator,
field: &'static str,
deserializer: &mut instant_xml::Deserializer<'cx, 'xml>,
) -> Result<(), instant_xml::Error> {
if into.is_some() {
return Err(instant_xml::Error::DuplicateValue(field));
}
let mut values: Vec<Double> = Vec::new();
while let Some(node) = deserializer.next() {
let node = node?;
match node {
instant_xml::de::Node::Attribute(attr) => {
let id = deserializer.attribute_id(&attr)?;
if id.name == "values" && !attr.value.is_empty() {
values = attr
.value
.split_whitespace()
.map(|s| {
let v: f64 = lexical_core::parse(s.as_bytes()).unwrap_or(0.0);
Double::new(v)
})
.collect();
}
}
instant_xml::de::Node::Open(data) => {
let mut nested = deserializer.nested(data);
nested.ignore()?;
}
_ => {}
}
}
*into = Some(Composite { values });
Ok(())
}
type Accumulator = Option<Self>;
const KIND: instant_xml::Kind = instant_xml::Kind::Element;
}
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(rename = "multiproperties"))]
#[cfg_attr(feature = "memory-optimized-read", derive(FromXml))]
#[cfg_attr(feature = "write", derive(ToXml))]
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(ns(MATERIAL_NS), rename = "multiproperties")
)]
pub struct MultiProperties {
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub id: ResourceId,
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub pids: ResourceIdCollection,
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub blendmethods: Option<String>,
#[cfg_attr(feature = "speed-optimized-read", serde(default, rename = "multi"))]
pub multi: Vec<Multi>,
}
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(rename = "multi"))]
#[cfg_attr(feature = "memory-optimized-read", derive(FromXml))]
#[cfg_attr(feature = "write", derive(ToXml))]
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(ns(MATERIAL_NS), rename = "multi")
)]
pub struct Multi {
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub pindices: ResourceIndexCollection,
}
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(from = "String"))]
#[derive(Debug, PartialEq, Clone)]
pub enum TextureContentType {
Jpeg,
Png,
}
#[cfg(feature = "write")]
impl ToXml for TextureContentType {
fn serialize<W: std::fmt::Write + ?Sized>(
&self,
_field: Option<instant_xml::Id<'_>>,
serializer: &mut instant_xml::Serializer<W>,
) -> Result<(), instant_xml::Error> {
let value = self.to_str();
serializer.write_str(&value)?;
Ok(())
}
}
#[cfg(feature = "memory-optimized-read")]
impl<'xml> FromXml<'xml> for TextureContentType {
#[inline]
fn matches(id: instant_xml::Id<'_>, field: Option<instant_xml::Id<'_>>) -> bool {
if let Some(field_id) = field {
id == field_id
} else {
false
}
}
fn deserialize<'cx>(
into: &mut Self::Accumulator,
field: &'static str,
deserializer: &mut instant_xml::Deserializer<'cx, 'xml>,
) -> Result<(), instant_xml::Error> {
if into.is_some() {
return Err(instant_xml::Error::DuplicateValue(field));
}
if let Some(value) = deserializer.take_str()? {
if let Some(content_type) = Self::from_str(&value) {
*into = Some(content_type);
} else {
return Err(instant_xml::Error::MissingValue(
"Failed to parse texture content type",
));
}
}
Ok(())
}
type Accumulator = Option<Self>;
const KIND: instant_xml::Kind = instant_xml::Kind::Scalar;
}
impl From<String> for TextureContentType {
fn from(value: String) -> Self {
match Self::from_str(value.as_ref()) {
Some(value) => value,
None => Self::Jpeg,
}
}
}
impl TextureContentType {
pub fn to_str(&self) -> &str {
match self {
TextureContentType::Jpeg => "image/jpeg",
TextureContentType::Png => "image/png",
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(value: &str) -> Option<Self> {
match value {
"image/jpeg" => Some(Self::Jpeg),
"image/png" => Some(Self::Png),
_ => None,
}
}
}
#[cfg_attr(feature = "speed-optimized-read", derive(Deserialize))]
#[cfg_attr(feature = "speed-optimized-read", serde(rename = "texture2d"))]
#[cfg_attr(feature = "memory-optimized-read", derive(FromXml))]
#[cfg_attr(feature = "write", derive(ToXml))]
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(ns(MATERIAL_NS), rename = "texture2d")
)]
pub struct Texture2D {
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub id: ResourceId,
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub path: String,
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub contenttype: TextureContentType,
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub tilestyleu: Option<TileStyle>,
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub tilestylev: Option<TileStyle>,
#[cfg_attr(
any(feature = "write", feature = "memory-optimized-read"),
xml(attribute)
)]
pub filter: Option<Filter>,
}
#[cfg(feature = "write")]
#[cfg(test)]
mod write_tests {
use instant_xml::to_string;
use pretty_assertions::assert_eq;
use crate::core::types::{Double, ResourceIdCollection, ResourceIndexCollection};
use crate::threemf_namespaces::MATERIAL_NS;
use super::*;
#[test]
pub fn toxml_color_test() {
let xml_string = format!("<color xmlns=\"{}\" color=\"#FF8000FF\" />", MATERIAL_NS);
let color = ColorElement {
color: Color::from_hex("#FF8000").unwrap(),
};
let color_string = to_string(&color).unwrap();
assert_eq!(color_string, xml_string);
}
#[test]
pub fn toxml_color_group_test() {
let xml_string = format!(
"<colorgroup xmlns=\"{}\" id=\"1\"><color color=\"#FF0000FF\" /><color color=\"#00FF00FF\" /></colorgroup>",
MATERIAL_NS
);
let colorgroup = ColorGroup {
id: 1,
color: vec![
ColorElement {
color: Color::from_hex("#FF0000").unwrap(),
},
ColorElement {
color: Color::from_hex("#00FF00").unwrap(),
},
],
};
let colorgroup_string = to_string(&colorgroup).unwrap();
assert_eq!(colorgroup_string, xml_string);
}
#[test]
pub fn toxml_tex2coord_test() {
let xml_string = format!(
"<tex2coord xmlns=\"{}\" u=\"0.5\" v=\"0.25\" />",
MATERIAL_NS
);
let tex2coord = Tex2Coord {
u: 0.5.into(),
v: 0.25.into(),
};
let tex2coord_string = to_string(&tex2coord).unwrap();
assert_eq!(tex2coord_string, xml_string);
}
#[test]
pub fn toxml_texture2d_group_test() {
let xml_string = format!(
"<texture2dgroup xmlns=\"{}\" id=\"2\" texid=\"1\"><tex2coord u=\"0\" v=\"0\" /><tex2coord u=\"1\" v=\"1\" /></texture2dgroup>",
MATERIAL_NS
);
let texture2dgroup = Texture2DGroup {
id: 2,
texid: 1,
tex2coord: vec![
Tex2Coord {
u: 0.0.into(),
v: 0.0.into(),
},
Tex2Coord {
u: 1.0.into(),
v: 1.0.into(),
},
],
};
let texture2dgroup_string = to_string(&texture2dgroup).unwrap();
assert_eq!(texture2dgroup_string, xml_string);
}
#[test]
pub fn toxml_composite_test() {
let xml_string = format!("<composite xmlns=\"{}\" values=\"0.3 0.7\" />", MATERIAL_NS);
let composite = Composite {
values: vec![Double::new(0.3), Double::new(0.7)],
};
let composite_string = to_string(&composite).unwrap();
assert_eq!(composite_string, xml_string);
}
#[test]
pub fn toxml_composite_1_0_test() {
let xml_string = format!("<composite xmlns=\"{}\" values=\"1 0\" />", MATERIAL_NS);
let composite = Composite {
values: vec![Double::new(1.0), Double::new(0.0)],
};
let composite_string = to_string(&composite).unwrap();
assert_eq!(composite_string, xml_string);
}
#[test]
pub fn toxml_composite_materials_test() {
let xml_string = format!(
"<compositematerials xmlns=\"{}\" id=\"1\" matid=\"10\" matindices=\"0 1\"><composite values=\"1 0\" /><composite values=\"0.5 0.5\" /></compositematerials>",
MATERIAL_NS
);
let compositematerials = CompositeMaterials {
id: 1,
matid: 10,
matindices: ResourceIndexCollection::from(vec![0, 1]),
composite: vec![
Composite {
values: vec![Double::new(1.0), Double::new(0.0)],
},
Composite {
values: vec![Double::new(0.5), Double::new(0.5)],
},
],
};
let compositematerials_string = to_string(&compositematerials).unwrap();
assert_eq!(compositematerials_string, xml_string);
}
#[test]
pub fn toxml_multi_test() {
let xml_string = format!("<multi xmlns=\"{}\" pindices=\"0 1\" />", MATERIAL_NS);
let multi = Multi {
pindices: ResourceIndexCollection::from(vec![0, 1]),
};
let multi_string = to_string(&multi).unwrap();
assert_eq!(multi_string, xml_string);
}
#[test]
pub fn toxml_multi_properties_test() {
let xml_string = format!(
"<multiproperties xmlns=\"{}\" id=\"1\" pids=\"10 20 30\" blendmethods=\"mix multiply\"><multi pindices=\"0 0 0\" /><multi pindices=\"1 2 3\" /></multiproperties>",
MATERIAL_NS
);
let multiproperties = MultiProperties {
id: 1,
pids: ResourceIdCollection::from(vec![10, 20, 30]),
blendmethods: Some("mix multiply".to_owned()),
multi: vec![
Multi {
pindices: ResourceIndexCollection::from(vec![0, 0, 0]),
},
Multi {
pindices: ResourceIndexCollection::from(vec![1, 2, 3]),
},
],
};
let multiproperties_string = to_string(&multiproperties).unwrap();
assert_eq!(multiproperties_string, xml_string);
}
#[test]
pub fn toxml_texture2d_test() {
let xml_string = format!(
"<texture2d xmlns=\"{}\" id=\"1\" path=\"/3D/texture.png\" contenttype=\"image/png\" tilestyleu=\"wrap\" tilestylev=\"mirror\" filter=\"linear\" />",
MATERIAL_NS
);
let texture2d = Texture2D {
id: 1,
path: "/3D/texture.png".to_owned(),
contenttype: TextureContentType::Png,
tilestyleu: Some(TileStyle::Wrap),
tilestylev: Some(TileStyle::Mirror),
filter: Some(Filter::Linear),
};
let texture2d_string = to_string(&texture2d).unwrap();
assert_eq!(texture2d_string, xml_string);
}
#[test]
pub fn toxml_texture2d_defaults_test() {
let xml_string = format!(
"<texture2d xmlns=\"{}\" id=\"1\" path=\"/3D/texture.jpg\" contenttype=\"image/jpeg\" />",
MATERIAL_NS
);
let texture2d = Texture2D {
id: 1,
path: "/3D/texture.jpg".to_owned(),
contenttype: TextureContentType::Jpeg,
tilestyleu: None,
tilestylev: None,
filter: None,
};
let texture2d_string = to_string(&texture2d).unwrap();
assert_eq!(texture2d_string, xml_string);
}
#[derive(Debug, ToXml, PartialEq, Eq)]
#[xml(ns(MATERIAL_NS))]
struct EnumTestType {
tilestyle: Vec<TileStyle>,
filter: Vec<Filter>,
blendmethod: Vec<BlendMethod>,
}
#[test]
pub fn toxml_material_enums_test() {
let xml_string = format!(
"<EnumTestType xmlns=\"{}\"><tilestyle>wrap</tilestyle><tilestyle>mirror</tilestyle><tilestyle>clamp</tilestyle><tilestyle>none</tilestyle><filter>auto</filter><filter>linear</filter><filter>nearest</filter><blendmethod>mix</blendmethod><blendmethod>multiply</blendmethod></EnumTestType>",
MATERIAL_NS
);
let enum_test = EnumTestType {
tilestyle: vec![
TileStyle::Wrap,
TileStyle::Mirror,
TileStyle::Clamp,
TileStyle::None,
],
filter: vec![Filter::Auto, Filter::Linear, Filter::Nearest],
blendmethod: vec![BlendMethod::Mix, BlendMethod::Multiply],
};
let enum_test_string = to_string(&enum_test).unwrap();
assert_eq!(enum_test_string, xml_string);
}
}
#[cfg(feature = "memory-optimized-read")]
#[cfg(test)]
mod memory_optimized_read_tests {
use instant_xml::from_str;
use pretty_assertions::assert_eq;
use crate::core::types::{Double, ResourceIdCollection, ResourceIndexCollection};
use crate::threemf_namespaces::MATERIAL_NS;
use super::*;
#[test]
pub fn fromxml_color_test() {
let xml_string = format!("<color xmlns=\"{}\" color=\"#FF8000\" />", MATERIAL_NS);
let color = from_str::<ColorElement>(&xml_string).unwrap();
assert_eq!(
color,
ColorElement {
color: Color::from_hex("#FF8000FF").unwrap(),
}
);
}
#[test]
pub fn fromxml_color_group_test() {
let xml_string = format!(
"<colorgroup xmlns=\"{}\" id=\"1\"><color color=\"#FF0000\" /><color color=\"#00FF00\" /></colorgroup>",
MATERIAL_NS
);
let colorgroup = from_str::<ColorGroup>(&xml_string).unwrap();
assert_eq!(
colorgroup,
ColorGroup {
id: 1,
color: vec![
ColorElement {
color: Color::from_hex("#FF0000").unwrap(),
},
ColorElement {
color: Color::from_hex("#00FF00").unwrap(),
},
],
}
);
}
#[test]
pub fn fromxml_tex2coord_test() {
let xml_string = format!(
"<tex2coord xmlns=\"{}\" u=\"0.5\" v=\"0.25\" />",
MATERIAL_NS
);
let tex2coord = from_str::<Tex2Coord>(&xml_string).unwrap();
assert_eq!(
tex2coord,
Tex2Coord {
u: 0.5.into(),
v: 0.25.into(),
}
);
}
#[test]
pub fn fromxml_texture2d_group_test() {
let xml_string = format!(
"<texture2dgroup xmlns=\"{}\" id=\"2\" texid=\"1\"><tex2coord u=\"0\" v=\"0\" /><tex2coord u=\"1\" v=\"1\" /></texture2dgroup>",
MATERIAL_NS
);
let texture2dgroup = from_str::<Texture2DGroup>(&xml_string).unwrap();
assert_eq!(
texture2dgroup,
Texture2DGroup {
id: 2,
texid: 1,
tex2coord: vec![
Tex2Coord {
u: 0.0.into(),
v: 0.0.into()
},
Tex2Coord {
u: 1.0.into(),
v: 1.0.into()
},
],
}
);
}
#[test]
pub fn fromxml_composite_test() {
let xml_string = format!("<composite xmlns=\"{}\" values=\"0.3 0.7\" />", MATERIAL_NS);
let composite = from_str::<Composite>(&xml_string).unwrap();
assert_eq!(
composite,
Composite {
values: vec![Double::new(0.3), Double::new(0.7)],
}
);
}
#[test]
pub fn fromxml_composite_materials_test() {
let xml_string = format!(
"<compositematerials xmlns=\"{}\" id=\"1\" matid=\"10\" matindices=\"0 1\"><composite values=\"1.0 0.0\" /><composite values=\"0.5 0.5\" /></compositematerials>",
MATERIAL_NS
);
let compositematerials = from_str::<CompositeMaterials>(&xml_string).unwrap();
assert_eq!(
compositematerials,
CompositeMaterials {
id: 1,
matid: 10,
matindices: ResourceIndexCollection::from(vec![0, 1]),
composite: vec![
Composite {
values: vec![Double::new(1.0), Double::new(0.0)]
},
Composite {
values: vec![Double::new(0.5), Double::new(0.5)]
},
],
}
);
}
#[test]
pub fn fromxml_multi_test() {
let xml_string = format!("<multi xmlns=\"{}\" pindices=\"0 1\" />", MATERIAL_NS);
let multi = from_str::<Multi>(&xml_string).unwrap();
assert_eq!(
multi,
Multi {
pindices: ResourceIndexCollection::from(vec![0, 1]),
}
);
}
#[test]
pub fn fromxml_multi_properties_test() {
let xml_string = format!(
"<multiproperties xmlns=\"{}\" id=\"1\" pids=\"10 20 30\" blendmethods=\"mix multiply\"><multi pindices=\"0 0 0\" /><multi pindices=\"1 2 3\" /></multiproperties>",
MATERIAL_NS
);
let multiproperties = from_str::<MultiProperties>(&xml_string).unwrap();
assert_eq!(
multiproperties,
MultiProperties {
id: 1,
pids: ResourceIdCollection::from(vec![10, 20, 30]),
blendmethods: Some("mix multiply".to_owned()),
multi: vec![
Multi {
pindices: ResourceIndexCollection::from(vec![0, 0, 0])
},
Multi {
pindices: ResourceIndexCollection::from(vec![1, 2, 3])
},
],
}
);
}
#[test]
pub fn fromxml_texture2d_test() {
let xml_string = format!(
"<texture2d xmlns=\"{}\" id=\"1\" path=\"/3D/texture.png\" contenttype=\"image/png\" tilestyleu=\"wrap\" tilestylev=\"mirror\" filter=\"linear\" />",
MATERIAL_NS
);
let texture2d = from_str::<Texture2D>(&xml_string).unwrap();
assert_eq!(texture2d.id, 1);
assert_eq!(texture2d.path, "/3D/texture.png");
assert_eq!(texture2d.contenttype, TextureContentType::Png);
}
#[test]
pub fn fromxml_texture2d_defaults_test() {
let xml_string = format!(
"<texture2d xmlns=\"{}\" id=\"1\" path=\"/3D/texture.jpg\" contenttype=\"image/jpeg\" />",
MATERIAL_NS
);
let texture2d = from_str::<Texture2D>(&xml_string).unwrap();
assert_eq!(
texture2d,
Texture2D {
id: 1,
path: "/3D/texture.jpg".to_owned(),
contenttype: TextureContentType::Jpeg,
tilestyleu: None,
tilestylev: None,
filter: None,
}
);
}
#[derive(FromXml, Debug, PartialEq, Eq)]
#[xml(ns(MATERIAL_NS))]
struct EnumTestType {
tilestyle: Vec<TileStyle>,
filter: Vec<Filter>,
blendmethod: Vec<BlendMethod>,
}
#[test]
pub fn fromxml_material_enums_test() {
let xml_string = format!(
"<EnumTestType xmlns=\"{}\"><tilestyle>wrap</tilestyle><tilestyle>mirror</tilestyle><tilestyle>clamp</tilestyle><tilestyle>none</tilestyle><filter>auto</filter><filter>linear</filter><filter>nearest</filter><blendmethod>mix</blendmethod><blendmethod>multiply</blendmethod></EnumTestType>",
MATERIAL_NS
);
let enum_test = from_str::<EnumTestType>(&xml_string).unwrap();
assert_eq!(
enum_test,
EnumTestType {
tilestyle: vec![
TileStyle::Wrap,
TileStyle::Mirror,
TileStyle::Clamp,
TileStyle::None,
],
filter: vec![Filter::Auto, Filter::Linear, Filter::Nearest],
blendmethod: vec![BlendMethod::Mix, BlendMethod::Multiply],
}
);
}
}
#[cfg(feature = "speed-optimized-read")]
#[cfg(test)]
mod speed_optimized_read_tests {
use pretty_assertions::assert_eq;
use serde_roxmltree::from_str;
use crate::threemf_namespaces::MATERIAL_NS;
use super::*;
#[test]
pub fn fromxml_color_test() {
let xml_string = format!("<color xmlns=\"{}\" color=\"#FF8000\" />", MATERIAL_NS);
let color = from_str::<ColorElement>(&xml_string).unwrap();
assert_eq!(
color,
ColorElement {
color: Color::from_hex("#FF8000FF").unwrap(),
}
);
}
#[test]
pub fn fromxml_color_group_test() {
let xml_string = format!(
"<colorgroup xmlns=\"{}\" id=\"1\"><color color=\"#FF0000\" /><color color=\"#00FF00\" /></colorgroup>",
MATERIAL_NS
);
let colorgroup = from_str::<ColorGroup>(&xml_string).unwrap();
assert_eq!(
colorgroup,
ColorGroup {
id: 1,
color: vec![
ColorElement {
color: Color::from_hex("#FF0000").unwrap(),
},
ColorElement {
color: Color::from_hex("#00FF00").unwrap(),
},
],
}
);
}
#[test]
pub fn fromxml_tex2coord_test() {
let xml_string = format!(
"<tex2coord xmlns=\"{}\" u=\"0.5\" v=\"0.25\" />",
MATERIAL_NS
);
let tex2coord = from_str::<Tex2Coord>(&xml_string).unwrap();
assert_eq!(
tex2coord,
Tex2Coord {
u: 0.5.into(),
v: 0.25.into(),
}
);
}
#[test]
pub fn fromxml_texture2d_group_test() {
let xml_string = format!(
"<texture2dgroup xmlns=\"{}\" id=\"2\" texid=\"1\"><tex2coord u=\"0\" v=\"0\" /><tex2coord u=\"1\" v=\"1\" /></texture2dgroup>",
MATERIAL_NS
);
let texture2dgroup = from_str::<Texture2DGroup>(&xml_string).unwrap();
assert_eq!(
texture2dgroup,
Texture2DGroup {
id: 2,
texid: 1,
tex2coord: vec![
Tex2Coord {
u: 0.0.into(),
v: 0.0.into()
},
Tex2Coord {
u: 1.0.into(),
v: 1.0.into()
},
],
}
);
}
#[test]
pub fn fromxml_composite_materials_test() {
let xml_string = format!(
"<compositematerials xmlns=\"{}\" id=\"1\" matid=\"10\" matindices=\"0 1\"><composite values=\"1.0 0.0\" /><composite values=\"0.5 0.5\" /></compositematerials>",
MATERIAL_NS
);
let compositematerials = from_str::<CompositeMaterials>(&xml_string).unwrap();
assert_eq!(
compositematerials,
CompositeMaterials {
id: 1,
matid: 10,
matindices: ResourceIndexCollection::from(vec![0, 1]),
composite: vec![
Composite {
values: vec![Double::new(1.0), Double::new(0.0)]
},
Composite {
values: vec![Double::new(0.5), Double::new(0.5)]
},
],
}
);
}
#[test]
pub fn fromxml_multi_test() {
let xml_string = format!("<multi xmlns=\"{}\" pindices=\"0 1\" />", MATERIAL_NS);
let multi = from_str::<Multi>(&xml_string).unwrap();
assert_eq!(
multi,
Multi {
pindices: ResourceIndexCollection::from(vec![0, 1]),
}
);
}
#[test]
pub fn fromxml_multi_properties_test() {
let xml_string = format!(
"<multiproperties xmlns=\"{}\" id=\"1\" pids=\"10 20 30\" blendmethods=\"mix multiply\"><multi pindices=\"0 0 0\" /><multi pindices=\"1 2 3\" /></multiproperties>",
MATERIAL_NS
);
let multiproperties = from_str::<MultiProperties>(&xml_string).unwrap();
assert_eq!(
multiproperties,
MultiProperties {
id: 1,
pids: ResourceIdCollection::from(vec![10, 20, 30]),
blendmethods: Some("mix multiply".to_owned()),
multi: vec![
Multi {
pindices: ResourceIndexCollection::from(vec![0, 0, 0])
},
Multi {
pindices: ResourceIndexCollection::from(vec![1, 2, 3])
},
],
}
);
}
#[test]
pub fn fromxml_texture2d_test() {
let xml_string = format!(
"<texture2d xmlns=\"{}\" id=\"1\" path=\"/3D/texture.png\" contenttype=\"image/png\" tilestyleu=\"wrap\" tilestylev=\"mirror\" filter=\"linear\" />",
MATERIAL_NS
);
let texture2d = from_str::<Texture2D>(&xml_string).unwrap();
assert_eq!(texture2d.id, 1);
assert_eq!(texture2d.path, "/3D/texture.png");
assert_eq!(texture2d.contenttype, TextureContentType::Png);
}
#[test]
pub fn fromxml_resources_with_compositematerials_test() {
let xml_string = format!(
r##"<compositematerials xmlns="{}" id="1" matid="10" matindices="0 1"><composite values="1.0 0.0" /><composite values="0.5 0.5" /></compositematerials>"##,
MATERIAL_NS
);
let resources = from_str::<CompositeMaterials>(&xml_string).unwrap();
assert_eq!(
resources,
CompositeMaterials {
id: 1,
matid: 10,
matindices: ResourceIndexCollection::from(vec![0, 1]),
composite: vec![
Composite {
values: vec![Double::new(1.0), Double::new(0.0)]
},
Composite {
values: vec![Double::new(0.5), Double::new(0.5)]
},
],
},
);
}
#[test]
pub fn fromxml_texture2d_defaults_test() {
let xml_string = format!(
"<texture2d xmlns=\"{}\" id=\"1\" path=\"/3D/texture.jpg\" contenttype=\"image/jpeg\" />",
MATERIAL_NS
);
let texture2d = from_str::<Texture2D>(&xml_string).unwrap();
assert_eq!(
texture2d,
Texture2D {
id: 1,
path: "/3D/texture.jpg".to_owned(),
contenttype: TextureContentType::Jpeg,
tilestyleu: None,
tilestylev: None,
filter: None,
}
);
}
#[derive(Deserialize, Debug, PartialEq, Eq)]
struct EnumTestType {
tilestyle: Vec<TileStyle>,
filter: Vec<Filter>,
blendmethod: Vec<BlendMethod>,
}
#[test]
pub fn fromxml_material_enums_test() {
let xml_string = r#"<EnumTestType xmlns="http://schemas.microsoft.com/3dmanufacturing/material/2015/02"><tilestyle>wrap</tilestyle><tilestyle>mirror</tilestyle><tilestyle>clamp</tilestyle><tilestyle>none</tilestyle><filter>auto</filter><filter>linear</filter><filter>nearest</filter><blendmethod>mix</blendmethod><blendmethod>multiply</blendmethod></EnumTestType>"#;
let enum_test = from_str::<EnumTestType>(xml_string).unwrap();
assert_eq!(
enum_test,
EnumTestType {
tilestyle: vec![
TileStyle::Wrap,
TileStyle::Mirror,
TileStyle::Clamp,
TileStyle::None,
],
filter: vec![Filter::Auto, Filter::Linear, Filter::Nearest],
blendmethod: vec![BlendMethod::Mix, BlendMethod::Multiply],
}
);
}
}