mmd 0.0.6

Miku Miku Dance format parser for rust programming language
use crate::{Config, Error};
use enumflags2::BitFlags;
use itertools::Itertools;
use std::convert::TryFrom;
use std::fmt::{Debug, Display, Formatter};

#[derive(BitFlags, Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum DrawingFlags {
  NoCull = 0b00000001,
  GroundShadow = 0b00000010,
  DrawShadow = 0b00000100,
  ReceiveShadow = 0b00001000,
  HasEdge = 0b00010000,
  VertexColor = 0b00100000,
  PointDrawing = 0b01000000,
  LineDrawing = 0b10000000,
}

struct DrawingFlagsFmt(BitFlags<DrawingFlags>);

impl Display for DrawingFlagsFmt {
  fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
    write!(f, "{}", self.0.iter().map(|v| format!("{:?}", v)).join("|"))
  }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum EnvironmentBlendMode {
  Disabled = 0,
  Multiply = 1,
  Additive = 2,
  AdditionalVec4 = 3,
}

impl Display for EnvironmentBlendMode {
  fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
    match self {
      EnvironmentBlendMode::Disabled => write!(f, "disabled"),
      EnvironmentBlendMode::Multiply => write!(f, "*"),
      EnvironmentBlendMode::Additive => write!(f, "+"),
      EnvironmentBlendMode::AdditionalVec4 => write!(f, "in vertex"),
    }
  }
}

impl TryFrom<u8> for EnvironmentBlendMode {
  type Error = Error;

  fn try_from(value: u8) -> Result<Self, Self::Error> {
    Ok(match value {
      0 => EnvironmentBlendMode::Disabled,
      1 => EnvironmentBlendMode::Multiply,
      2 => EnvironmentBlendMode::Additive,
      3 => EnvironmentBlendMode::AdditionalVec4,
      e => return Err(Error::InvalidEnvironmentBlendMode(e)),
    })
  }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Toon<C: Config> {
  Texture(C::TextureIndex),
  Internal(u8),
}

impl<C: Config> Display for Toon<C>
where
  C::TextureIndex: Display,
{
  fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
    match self {
      Toon::Texture(t) => write!(f, "texture({})", t),
      Toon::Internal(i) => write!(f, "internal({})", i),
    }
  }
}

pub struct Material<C: Config> {
  pub local_name: String,
  pub universal_name: String,
  pub diffuse_color: C::Vec4,
  pub specular_color: C::Vec3,
  pub specular_strength: f32,
  pub ambient_color: C::Vec3,
  pub draw_flags: BitFlags<DrawingFlags>,
  pub edge_color: C::Vec4,
  pub edge_scale: f32,
  pub texture_index: C::TextureIndex,
  pub environment_index: C::TextureIndex,
  pub environment_blend_mode: EnvironmentBlendMode,
  pub toon: Toon<C>,
  pub metadata: String,
  pub surface_count: i32,
}

impl<C: Config> Display for Material<C>
where
  C::Vec3: Display,
  C::Vec4: Display,
  C::TextureIndex: Display,
{
  fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
    write!(
      f,
      r"local name: {}, universal name: {}
diffuse: {}, specular: {}/{}, ambient: {}, flags: {}
edge: {}/{}, texture: {}, environment: {}/{},
toon: {}, metadata: {}, surfaces: {}",
      self.local_name,
      self.universal_name,
      self.diffuse_color,
      self.specular_color,
      self.specular_strength,
      self.ambient_color,
      DrawingFlagsFmt(self.draw_flags),
      self.edge_color,
      self.edge_scale,
      self.texture_index,
      self.environment_index,
      self.environment_blend_mode,
      self.toon,
      self.metadata,
      self.surface_count
    )
  }
}