pmx_parser 0.2.0

load and save .pmx model file.
Documentation
use std::io::{Read, Write};

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

use crate::error::PmxError;
use crate::header::Header;
use crate::kits::{read_f32x3, read_f32x4, read_vec, write_f32x3, write_f32x4};
use crate::TextureIndex;

#[derive(Default, Debug, Clone, PartialEq)]
pub struct Materials {
    pub materials: Vec<Material>,
}

impl Materials {
    pub fn count(&self) -> u32 {
        self.materials.len() as u32
    }
    pub fn read<R: Read>(header: &Header, read: &mut R) -> Result<Self, PmxError> {
        Ok(Self {
            materials: read_vec(read, |read| Material::read(header, read))?,
        })
    }
    pub fn write<W: Write>(&self, header: &Header, write: &mut W) -> Result<(), PmxError> {
        write.write_u32::<LittleEndian>(self.count())?;
        for i in &self.materials {
            i.write(header, write)?;
        }
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct Material {
    pub name: String,
    pub name_en: String,
    pub diffuse: [f32; 4],
    pub specular: [f32; 4],
    pub ambient: [f32; 3],
    pub flags: MaterialFlags,
    pub edge_color: [f32; 4],
    pub edge_size: f32,
    pub texture_index: TextureIndex,
    pub env_texture_index: TextureIndex,
    pub mix: Mix,
    pub toon_texture: ToonTexture,
    pub comment: String,
    pub element_count: u32,
}

impl Material {
    pub fn read<R: Read>(header: &Header, read: &mut R) -> Result<Self, PmxError> {
        Ok(Self {
            name: header.encoding.read(read)?,
            name_en: header.encoding.read(read)?,
            diffuse: read_f32x4(read)?,
            specular: read_f32x4(read)?,
            ambient: read_f32x3(read)?,
            flags: MaterialFlags::from_bits_retain(read.read_u8()?),
            edge_color: read_f32x4(read)?,
            edge_size: read.read_f32::<LittleEndian>()?,
            texture_index: header.texture_index.read(read)?,
            env_texture_index: header.texture_index.read(read)?,
            mix: Mix::try_from(read.read_u8()?)?,
            toon_texture: ToonTexture::read(header, read)?,
            comment: header.encoding.read(read)?,
            element_count: read.read_u32::<LittleEndian>()?,
        })
    }

    pub fn write<W: Write>(&self, header: &Header, write: &mut W) -> Result<(), PmxError> {
        header.encoding.write(write, self.name.as_str())?;
        header.encoding.write(write, self.name_en.as_str())?;
        write_f32x4(write, self.diffuse)?;
        write_f32x4(write, self.specular)?;
        write_f32x3(write, self.ambient)?;
        write.write_u8(self.flags.bits())?;
        write_f32x4(write, self.edge_color)?;
        write.write_f32::<LittleEndian>(self.edge_size)?;
        header.texture_index.write(write, self.texture_index)?;
        header.texture_index.write(write, self.env_texture_index)?;
        write.write_u8(self.mix as u8)?;
        self.toon_texture.write(header, write)?;
        header.encoding.write(write, self.comment.as_str())?;
        write.write_u32::<LittleEndian>(self.element_count)?;
        Ok(())
    }
}

bitflags::bitflags! {
    #[derive(Debug, Copy, Clone, Eq, PartialEq)]
    pub struct MaterialFlags: u8 {
        const DISABLE_CULLING = 0x01;
        const GROUND_SHADOW = 0x02;
        const DRAW_SHADOW = 0x04;
        const RECEIVE_SHADOW = 0x08;
        const HAS_EDGE = 0x10;
        const VERTEX_COLOR = 0x20;
        const POINT_DRAW = 0x40;
        const LINE_DRAW =  0x80;
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u8)]
pub enum Mix {
    No = 0x00,
    Mul = 0x01,
    Add = 0x02,
    SubTexture = 0x03,
}

impl TryFrom<u8> for Mix {
    type Error = PmxError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0x00 => Ok(Self::No),
            0x01 => Ok(Self::Mul),
            0x02 => Ok(Self::Add),
            0x03 => Ok(Self::SubTexture),
            _ => Err(PmxError::MixError),
        }
    }
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ToonTexture {
    TextureIndex(TextureIndex),
    CommonIndex(u8),
}

impl ToonTexture {
    pub fn read<R: Read>(header: &Header, read: &mut R) -> Result<Self, PmxError> {
        let t = read.read_u8()?;
        match t {
            0x00 => Ok(Self::TextureIndex(header.texture_index.read(read)?)),
            0x01 => Ok(Self::CommonIndex(read.read_u8()?)),
            _ => Err(PmxError::ToonError),
        }
    }
    pub fn write<W: Write>(&self, header: &Header, write: &mut W) -> Result<(), PmxError> {
        match *self {
            ToonTexture::TextureIndex(texture_index) => {
                write.write_u8(0x00)?;
                header.texture_index.write(write, texture_index)?
            }
            ToonTexture::CommonIndex(common_index) => {
                write.write_u8(0x01)?;
                write.write_u8(common_index)?;
            }
        }
        Ok(())
    }
}