use crate::*;
#[derive(Clone, Debug)]
pub struct Image {
pub id: Option<String>,
pub name: Option<String>,
pub format: Option<String>,
pub height: u32,
pub width: u32,
pub depth: u32,
pub asset: Option<Box<Asset>>,
pub source: ImageSource,
pub extra: Vec<Extra>,
}
impl Image {
pub fn new(id: impl Into<String>, name: Option<String>, source: ImageSource) -> Self {
Self {
id: Some(id.into()),
name,
format: None,
height: 0,
width: 0,
depth: 1,
asset: None,
source,
extra: vec![],
}
}
}
impl XNode for Image {
const NAME: &'static str = "image";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
Ok(Image {
id: element.attr("id").map(Into::into),
name: element.attr("name").map(Into::into),
format: element.attr("format").map(Into::into),
height: parse_attr(element.attr("height"))?.unwrap_or(0),
width: parse_attr(element.attr("width"))?.unwrap_or(0),
depth: parse_attr(element.attr("depth"))?.unwrap_or(1),
asset: Asset::parse_opt_box(&mut it)?,
source: parse_one_many(&mut it, ImageSource::parse)?,
extra: Extra::parse_many(it)?,
})
}
}
impl XNodeWrite for Image {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let mut e = Self::elem();
e.opt_attr("id", &self.id);
e.opt_attr("name", &self.name);
e.opt_attr("format", &self.format);
e.def_print_attr("height", self.height, 0);
e.def_print_attr("width", self.width, 0);
e.def_print_attr("depth", self.depth, 1);
let e = e.start(w)?;
self.asset.write_to(w)?;
self.source.write_to(w)?;
self.extra.write_to(w)?;
e.end(w)
}
}
#[derive(Clone, Debug)]
pub enum ImageParam {
NewParam(NewParam),
Image(Image),
}
impl From<Image> for ImageParam {
fn from(v: Image) -> Self {
Self::Image(v)
}
}
impl ImageParam {
pub(crate) fn parse_list(it: &mut ElementIter<'_>) -> Result<Vec<Self>> {
parse_list_many(it, |e| {
Ok(Some(match e.name() {
Image::NAME => Self::Image(Image::parse(e)?),
NewParam::NAME => Self::NewParam(NewParam::parse(e)?),
_ => return Ok(None),
}))
})
}
}
impl XNodeWrite for ImageParam {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
match self {
Self::NewParam(e) => e.write_to(w),
Self::Image(e) => e.write_to(w),
}
}
}
fn parse_hex_array(s: &str) -> Box<[u8]> {
let mut out = vec![];
let mut hi = 0;
let mut odd = false;
for c in s.bytes() {
let c = match c {
b'0'..=b'9' => c - b'0',
b'a'..=b'f' => c - b'a' + 10,
b'A'..=b'F' => c - b'A' + 10,
_ => continue,
};
if odd {
out.push(hi << 4 | c)
} else {
hi = c
}
odd = !odd
}
out.into()
}
#[derive(Clone, Debug)]
pub enum ImageSource {
Data(Box<[u8]>),
InitFrom(Url),
}
impl ImageSource {
pub fn parse(element: &Element) -> Result<Option<Self>> {
Ok(Some(match element.name() {
"data" => {
let s = get_text(element).ok_or("expected text element")?;
ImageSource::Data(parse_hex_array(s))
}
"init_from" => ImageSource::InitFrom(parse_elem(element)?),
_ => return Ok(None),
}))
}
}
impl XNodeWrite for ImageSource {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
match self {
Self::Data(data) => {
let e = ElemBuilder::new("data").start(w)?;
let mut indent = false;
for chunk in data.chunks(40) {
if indent {
w.write_indent()?
}
indent = true;
for &c in chunk {
write!(w.get_mut(), "{:x}", c)?
}
}
e.end(w)
}
Self::InitFrom(url) => ElemBuilder::print("init_from", url, w),
}
}
}
#[derive(Clone, Debug)]
pub struct Sampler2D {
pub source: NameRef<Surface>,
pub wrap_s: WrapMode,
pub wrap_t: WrapMode,
pub min_filter: SamplerFilter,
pub mag_filter: SamplerFilter,
pub mip_filter: SamplerFilter,
pub border_color: Option<Box<[f32; 4]>>,
pub mipmap_max_level: u8,
pub mipmap_bias: f32,
pub extra: Vec<Extra>,
}
impl Sampler2D {
pub fn new(source: String) -> Self {
Self {
source: Ref::new(source),
wrap_s: Default::default(),
wrap_t: Default::default(),
min_filter: Default::default(),
mag_filter: Default::default(),
mip_filter: Default::default(),
border_color: None,
mipmap_max_level: 0,
mipmap_bias: 0.,
extra: vec![],
}
}
}
impl XNode for Sampler2D {
const NAME: &'static str = "sampler2D";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
Ok(Sampler2D {
source: parse_one("source", &mut it, parse_elem)?,
wrap_s: parse_opt("wrap_s", &mut it, parse_elem)?.unwrap_or_default(),
wrap_t: parse_opt("wrap_t", &mut it, parse_elem)?.unwrap_or_default(),
min_filter: parse_opt("minfilter", &mut it, parse_elem)?.unwrap_or_default(),
mag_filter: parse_opt("magfilter", &mut it, parse_elem)?.unwrap_or_default(),
mip_filter: parse_opt("mipfilter", &mut it, parse_elem)?.unwrap_or_default(),
border_color: parse_opt("border_color", &mut it, parse_array_n)?,
mipmap_max_level: parse_opt("mipmap_maxlevel", &mut it, parse_elem)?.unwrap_or(0),
mipmap_bias: parse_opt("mipmap_bias", &mut it, parse_elem)?.unwrap_or(0.),
extra: Extra::parse_many(it)?,
})
}
}
impl XNodeWrite for Sampler2D {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let e = Self::elem().start(w)?;
ElemBuilder::print("source", &self.source, w)?;
ElemBuilder::def_print("wrap_s", self.wrap_s, Default::default(), w)?;
ElemBuilder::def_print("wrap_t", self.wrap_t, Default::default(), w)?;
ElemBuilder::def_print("minfilter", self.min_filter, Default::default(), w)?;
ElemBuilder::def_print("magfilter", self.mag_filter, Default::default(), w)?;
ElemBuilder::def_print("mipfilter", self.mip_filter, Default::default(), w)?;
opt(&self.border_color, |e| {
ElemBuilder::print_arr("border_color", &**e, w)
})?;
ElemBuilder::def_print("mipmap_maxlevel", self.mipmap_max_level, 0, w)?;
ElemBuilder::def_print("mipmap_bias", self.mipmap_bias, 0., w)?;
self.extra.write_to(w)?;
e.end(w)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WrapMode {
Wrap,
Mirror,
Clamp,
Border,
None,
}
impl Default for WrapMode {
fn default() -> Self {
Self::Wrap
}
}
impl FromStr for WrapMode {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"WRAP" => Ok(Self::Wrap),
"MIRROR" => Ok(Self::Mirror),
"CLAMP" => Ok(Self::Clamp),
"BORDER" => Ok(Self::Border),
"NONE" => Ok(Self::None),
_ => Err(()),
}
}
}
impl WrapMode {
pub fn to_str(self) -> &'static str {
match self {
Self::Wrap => "WRAP",
Self::Mirror => "MIRROR",
Self::Clamp => "CLAMP",
Self::Border => "BORDER",
Self::None => "NONE",
}
}
}
impl Display for WrapMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self.to_str(), f)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum SamplerFilter {
None,
Nearest,
Linear,
NearestMipmapNearest,
LinearMipmapNearest,
NearestMipmapLinear,
LinearMipmapLinear,
}
impl Default for SamplerFilter {
fn default() -> Self {
Self::None
}
}
impl FromStr for SamplerFilter {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"NONE" => Ok(Self::None),
"NEAREST" => Ok(Self::Nearest),
"LINEAR" => Ok(Self::Linear),
"NEAREST_MIPMAP_NEAREST" => Ok(Self::NearestMipmapNearest),
"LINEAR_MIPMAP_NEAREST" => Ok(Self::LinearMipmapNearest),
"NEAREST_MIPMAP_LINEAR" => Ok(Self::NearestMipmapLinear),
"LINEAR_MIPMAP_LINEAR" => Ok(Self::LinearMipmapLinear),
_ => Err(()),
}
}
}
impl SamplerFilter {
pub fn to_str(self) -> &'static str {
match self {
Self::None => "NONE",
Self::Nearest => "NEAREST",
Self::Linear => "LINEAR",
Self::NearestMipmapNearest => "NEAREST_MIPMAP_NEAREST",
Self::LinearMipmapNearest => "LINEAR_MIPMAP_NEAREST",
Self::NearestMipmapLinear => "NEAREST_MIPMAP_LINEAR",
Self::LinearMipmapLinear => "LINEAR_MIPMAP_LINEAR",
}
}
}
impl Display for SamplerFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self.to_str(), f)
}
}
#[derive(Clone, Debug)]
pub struct Surface {
pub ty: SurfaceType,
pub init: SurfaceInit,
pub format: Option<String>,
pub format_hint: Option<Box<FormatHint>>,
pub size: Option<Box<[u32; 3]>>,
pub viewport_ratio: Option<Box<[f32; 2]>>,
pub mip_levels: u32,
pub mipmap_generate: bool,
pub extra: Vec<Extra>,
}
impl Surface {
pub fn new(ty: SurfaceType, init: SurfaceInit) -> Self {
Self {
ty,
init,
format: None,
format_hint: None,
size: None,
viewport_ratio: None,
mip_levels: 0,
mipmap_generate: false,
extra: vec![],
}
}
}
impl XNode for Surface {
const NAME: &'static str = "surface";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
let res = Surface {
ty: parse_attr(element.attr("type"))?.ok_or("expected 'type' attr")?,
init: parse_one_many(&mut it, SurfaceInit::parse)?,
format: parse_opt("format", &mut it, parse_text)?,
format_hint: FormatHint::parse_opt_box(&mut it)?,
size: parse_opt("size", &mut it, parse_array_n)?,
viewport_ratio: parse_opt("viewport_ratio", &mut it, parse_array_n)?,
mip_levels: parse_opt("mip_levels", &mut it, parse_elem)?.unwrap_or(0),
mipmap_generate: parse_opt("mipmap_generate", &mut it, parse_elem)?.unwrap_or(false),
extra: Extra::parse_many(it)?,
};
if res.size.is_some() && res.viewport_ratio.is_some() {
return Err("size and viewport_ratio cannot be used together".into());
}
Ok(res)
}
}
impl XNodeWrite for Surface {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let mut e = Self::elem();
e.print_attr("type", self.ty);
let e = e.start(w)?;
self.init.write_to(w)?;
opt(&self.format, |e| ElemBuilder::print_str("format", e, w))?;
self.format_hint.write_to(w)?;
opt(&self.size, |e| ElemBuilder::print_arr("size", &**e, w))?;
opt(&self.viewport_ratio, |e| {
ElemBuilder::print_arr("viewport_ratio", &**e, w)
})?;
ElemBuilder::def_print("mip_levels", self.mip_levels, 0, w)?;
ElemBuilder::def_print("mipmap_generate", self.mipmap_generate, false, w)?;
self.extra.write_to(w)?;
e.end(w)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SurfaceType {
Untyped,
_1D,
_2D,
_3D,
Rect,
Cube,
Depth,
}
impl Default for SurfaceType {
fn default() -> Self {
Self::Untyped
}
}
impl FromStr for SurfaceType {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"UNTYPED" => Ok(Self::Untyped),
"1D" => Ok(Self::_1D),
"2D" => Ok(Self::_2D),
"3D" => Ok(Self::_3D),
"RECT" => Ok(Self::Rect),
"CUBE" => Ok(Self::Cube),
"DEPTH" => Ok(Self::Depth),
_ => Err(()),
}
}
}
impl SurfaceType {
pub fn to_str(self) -> &'static str {
match self {
Self::Untyped => "UNTYPED",
Self::_1D => "1D",
Self::_2D => "2D",
Self::_3D => "3D",
Self::Rect => "RECT",
Self::Cube => "CUBE",
Self::Depth => "DEPTH",
}
}
}
impl Display for SurfaceType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self.to_str(), f)
}
}
#[derive(Clone, Debug)]
pub struct FormatHint {
pub channels: SurfaceChannels,
pub range: SurfaceRange,
pub precision: SurfacePrecision,
pub options: Vec<SurfaceOption>,
pub extra: Vec<Extra>,
}
impl FormatHint {
pub fn new(
channels: SurfaceChannels,
range: SurfaceRange,
precision: SurfacePrecision,
) -> Self {
Self {
channels,
range,
precision,
options: vec![],
extra: vec![],
}
}
}
impl XNode for FormatHint {
const NAME: &'static str = "format_hint";
fn parse(element: &Element) -> Result<Self> {
debug_assert_eq!(element.name(), Self::NAME);
let mut it = element.children().peekable();
Ok(FormatHint {
channels: parse_one("channels", &mut it, parse_elem)?,
range: parse_one("range", &mut it, parse_elem)?,
precision: parse_one("precision", &mut it, parse_elem)?,
options: parse_list("option", &mut it, parse_elem)?,
extra: Extra::parse_many(it)?,
})
}
}
impl XNodeWrite for FormatHint {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
let e = Self::elem().start(w)?;
ElemBuilder::print("channels", &self.channels, w)?;
ElemBuilder::print("range", &self.range, w)?;
ElemBuilder::print("precision", &self.precision, w)?;
many(&self.options, |e| ElemBuilder::print("option", e, w))?;
self.extra.write_to(w)?;
e.end(w)
}
}
#[derive(Clone, Debug)]
pub enum SurfaceInit {
Null,
Target,
From {
mip: u32,
slice: u32,
face: SurfaceFace,
image: NameRef<Image>,
},
}
impl SurfaceInit {
pub fn init_from(image: impl Into<String>) -> Self {
Self::From {
mip: 0,
slice: 0,
face: Default::default(),
image: Ref::new(image.into()),
}
}
pub fn parse(element: &Element) -> Result<Option<Self>> {
Ok(Some(match element.name() {
"init_as_null" => Self::Null,
"init_as_target" => Self::Target,
"init_cube" | "init_volume" | "init_planar" => unimplemented!(),
"init_from" => Self::From {
mip: parse_attr(element.attr("mip"))?.unwrap_or(0),
slice: parse_attr(element.attr("slice"))?.unwrap_or(0),
face: parse_attr(element.attr("face"))?.unwrap_or_default(),
image: parse_elem(element)?,
},
_ => return Ok(None),
}))
}
}
impl XNodeWrite for SurfaceInit {
fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
match self {
Self::Null => ElemBuilder::new("init_as_null").end(w),
Self::Target => ElemBuilder::new("init_as_target").end(w),
&Self::From {
mip,
slice,
face,
ref image,
} => {
let mut e = ElemBuilder::new("init_from");
e.def_print_attr("mip", mip, 0);
e.def_print_attr("slice", slice, 0);
e.def_print_attr("face", face, Default::default());
let e = e.start(w)?;
print_elem(image, w)?;
e.end(w)
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SurfaceFace {
PosX,
NegX,
PosY,
NegY,
PosZ,
NegZ,
}
impl Default for SurfaceFace {
fn default() -> Self {
Self::PosX
}
}
impl FromStr for SurfaceFace {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"POSITIVE_X" => Ok(Self::PosX),
"NEGATIVE_X" => Ok(Self::NegX),
"POSITIVE_Y" => Ok(Self::PosY),
"NEGATIVE_Y" => Ok(Self::NegY),
"POSITIVE_Z" => Ok(Self::PosZ),
"NEGATIVE_Z" => Ok(Self::NegZ),
_ => Err(()),
}
}
}
impl SurfaceFace {
pub fn to_str(self) -> &'static str {
match self {
Self::PosX => "POSITIVE_X",
Self::NegX => "NEGATIVE_X",
Self::PosY => "POSITIVE_Y",
Self::NegY => "NEGATIVE_Y",
Self::PosZ => "POSITIVE_Z",
Self::NegZ => "NEGATIVE_Z",
}
}
}
impl Display for SurfaceFace {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self.to_str(), f)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SurfaceChannels {
RGB,
RGBA,
L,
LA,
D,
XYZ,
XYZW,
}
impl FromStr for SurfaceChannels {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"RGB" => Ok(Self::RGB),
"RGBA" => Ok(Self::RGBA),
"L" => Ok(Self::L),
"LA" => Ok(Self::LA),
"D" => Ok(Self::D),
"XYZ" => Ok(Self::XYZ),
"XYZW" => Ok(Self::XYZW),
_ => Err(()),
}
}
}
impl SurfaceChannels {
pub fn to_str(self) -> &'static str {
match self {
Self::RGB => "RGB",
Self::RGBA => "RGBA",
Self::L => "L",
Self::LA => "LA",
Self::D => "D",
Self::XYZ => "XYZ",
Self::XYZW => "XYZW",
}
}
}
impl Display for SurfaceChannels {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self.to_str(), f)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SurfaceRange {
SNorm,
UNorm,
SInt,
UInt,
Float,
}
impl FromStr for SurfaceRange {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"SNORM" => Ok(Self::SNorm),
"UNORM" => Ok(Self::UNorm),
"SINT" => Ok(Self::SInt),
"UINT" => Ok(Self::UInt),
"FLOAT" => Ok(Self::Float),
_ => Err(()),
}
}
}
impl SurfaceRange {
pub fn to_str(self) -> &'static str {
match self {
Self::SNorm => "SNORM",
Self::UNorm => "UNORM",
Self::SInt => "SINT",
Self::UInt => "UINT",
Self::Float => "FLOAT",
}
}
}
impl Display for SurfaceRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self.to_str(), f)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SurfacePrecision {
Low,
Mid,
High,
}
impl FromStr for SurfacePrecision {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"LOW" => Ok(Self::Low),
"MID" => Ok(Self::Mid),
"HIGH" => Ok(Self::High),
_ => Err(()),
}
}
}
impl SurfacePrecision {
pub fn to_str(self) -> &'static str {
match self {
Self::Low => "LOW",
Self::Mid => "MID",
Self::High => "HIGH",
}
}
}
impl Display for SurfacePrecision {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self.to_str(), f)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SurfaceOption {
SrgbGamma,
Normalized3,
Normalized4,
Compressible,
}
impl FromStr for SurfaceOption {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"SRGB_GAMMA" => Ok(Self::SrgbGamma),
"NORMALIZED3" => Ok(Self::Normalized3),
"NORMALIZED4" => Ok(Self::Normalized4),
"COMPRESSABLE" => Ok(Self::Compressible),
_ => Err(()),
}
}
}
impl SurfaceOption {
pub fn to_str(self) -> &'static str {
match self {
Self::SrgbGamma => "SRGB_GAMMA",
Self::Normalized3 => "NORMALIZED3",
Self::Normalized4 => "NORMALIZED4",
Self::Compressible => "COMPRESSABLE",
}
}
}
impl Display for SurfaceOption {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self.to_str(), f)
}
}