isomagic 0.1.3

Unused. Contact me for transfer of ownership.
extern crate dot_vox;
extern crate image;
extern crate structopt;
#[macro_use]
extern crate structopt_derive;
#[macro_use]
extern crate error_chain;

use structopt::StructOpt;
use image::{ImageBuffer, Rgba, RgbaImage};
use dot_vox::{DotVoxData, Voxel, Model};

mod enums;
use enums::{Side, View};

use std::path::PathBuf;
use std::fs::create_dir_all;

#[derive(StructOpt)]
struct Options {
    #[structopt(help = "Input .vox model")]
    filename: String,
    #[structopt(short = "m", long = "model", help = "Which model in the voxel file to render [default: all]")]
    model: Option<usize>,
    #[structopt(short = "s", long = "side", help = "Which side of the model to render [default: all]")]
    side: Option<Side>,
    #[structopt(short = "v", long = "view", help = "Which perspective of the model to render [default: all]")]
    view: Option<View>,
    #[structopt(short = "o", long = "output", default_value = ".", help = "The output directory to write files to")]
    output: String
}

error_chain!{
    foreign_links {
        Io(::std::io::Error);
    }
}

fn run() -> Result<()> {
    let options = Options::from_args();

    Renderer::new(&options.filename)
        .chain_err(|| format!("Failed to parse '{}'.", &options.filename))?
        .render_all(options)
}

fn main() {
    if let Err(error) = run() {
        eprintln!("{}", error);

        for cause in error.iter().skip(1) {
            eprintln!("Caused by: {}", cause);
        }
    }
}

struct Renderer {
    vox: DotVoxData
}

impl Renderer {
    fn new(filename: &str) -> Result<Self> {
        Ok(Self {
            vox: dot_vox::load(filename)?
        })
    }

    fn render_all(&mut self, options: Options) -> Result<()> {
        let models = options.model.map(|model| vec![model]).unwrap_or_else(|| (0 .. self.vox.models.len()).collect());
        let sides  = options.side.map(|side| vec![side]).unwrap_or_else(Side::all);
        let views  = options.view.map(|view| vec![view]).unwrap_or_else(View::all);
        
        for model in models {
            for side in &sides {
                for view in &views {
                    if *view == View::Face || (*side != Side::Top && *side != Side::Bottom) {
                        self.render(model, side, view, PathBuf::from(&options.output))?;
                    }
                }
            }
        }

        Ok(())
    }

    fn render(&mut self, model: usize, side: &Side, view: &View, mut output: PathBuf) -> Result<()> {
        let image = match *view {
            View::Face                  => ModelRenderer::new(self, model, 0, 0).render_face(side),
            View::FourtyFive            => ModelRenderer::new(self, model, 0, 1).render_45(side),
            View::FourtyFiveIso         => ModelRenderer::new(self, model, 1, 1).render_45_iso(side),
            View::TwentyTwoPointFive    => ModelRenderer::new(self, model, 1, 2).render_22_5(side),
            View::TwentyTwoPointFiveIso => ModelRenderer::new(self, model, 3, 3).render_22_5_iso(side),
        };

        if !output.exists() {
            create_dir_all(&output).chain_err(|| format!("Failed to create directory '{}'.", output.display()))?;
        }

        output.push(format!("{}_{}_{}.png", side.to_str(), view.to_str(), model));
        image.save(&output).chain_err(|| format!("Failed to save '{}'.", output.display()))?;
        Ok(())
    }
}

struct Size {
    x: u32,
    y: u32,
    z: u32
}

impl Size {
    fn invert_x(&self, voxel: &Voxel) -> u32 {
        self.x - u32::from(voxel.x)
    }

    fn invert_y(&self, voxel: &Voxel) -> u32 {
        self.y - u32::from(voxel.y)
    }

    fn invert_z(&self, voxel: &Voxel) -> u32 {
        self.z - u32::from(voxel.z)
    }
}

struct ModelRenderer<'a> {
    model: &'a mut Model,
    palette: &'a [u32],
    x_padding: u32,
    y_padding: u32
}

impl<'a> ModelRenderer<'a> {
    fn new(renderer: &'a mut Renderer, model: usize, x_padding: u32, y_padding: u32) -> Self {
        Self {
            model: &mut renderer.vox.models[model],
            palette: &renderer.vox.palette,
            x_padding, y_padding
        }
    }

    fn colour(&self, index: u8, subtract: u8) -> Rgba<u8> {
        let colour = self.palette[index as usize - 1];
        let r = (colour % 256) as u8;
        let g = ((colour >> 8)  % 256) as u8;
        let b = ((colour >> 16) % 256) as u8;
        let a = ((colour >> 24) % 256) as u8;

        let r = r.saturating_sub(subtract);
        let g = g.saturating_sub(subtract);
        let b = b.saturating_sub(subtract);

        Rgba {
            data: [r, g, b, a]
        }
    }

    fn create_image<S, X, Y>(&mut self, sort: S, map_x: X, map_y: Y) -> RgbaImage
        where
            S: Fn(&Voxel) -> u32,
            X: Fn(&Voxel) -> u32,
            Y: Fn(&Voxel) -> u32
    {
        self.model.voxels.sort_unstable_by_key(sort);
        
        let width  = self.model.voxels.iter().map(map_x).max().unwrap_or(0) + self.x_padding + 1;
        let height = self.model.voxels.iter().map(map_y).max().unwrap_or(0) + self.y_padding + 1;
        
        ImageBuffer::new(width, height)
    }

    fn size(&self) -> Size {
        Size {
            x: self.model.size.x,
            y: self.model.size.y,
            z: self.model.size.z,
        }
    }

    fn render_face(&mut self, side: &Side) -> RgbaImage {
        let size = self.size();

        match *side {
            Side::Top => self.render_face_closure(
                |voxel| u32::from(voxel.z), |voxel| u32::from(voxel.x), |voxel| size.invert_y(voxel),
            ),
            Side::Front  => self.render_face_closure(
                |voxel| size.invert_y(voxel), |voxel| u32::from(voxel.x), |voxel| size.invert_z(voxel)
            ),
            Side::Left => self.render_face_closure(
                |voxel| size.invert_x(voxel), |voxel| size.invert_y(voxel), |voxel| size.invert_z(voxel)
            ),
            Side::Right => self.render_face_closure(
                |voxel| u32::from(voxel.x), |voxel| u32::from(voxel.y), |voxel| size.invert_z(voxel)
            ),
            Side::Back => self.render_face_closure(
                |voxel| u32::from(voxel.y), |voxel| size.invert_x(voxel), |voxel| size.invert_z(voxel)
            ),
            Side::Bottom => self.render_face_closure(
                |voxel| size.invert_z(voxel), |voxel| u32::from(voxel.x), |voxel| u32::from(voxel.y)
            )
        }
    }

    fn render_face_closure<S, X, Y>(&mut self, sort: S, map_x: X, map_y: Y) -> RgbaImage
        where
            S: Fn(&Voxel) -> u32,
            X: Fn(&Voxel) -> u32,
            Y: Fn(&Voxel) -> u32
    {
        let mut image = self.create_image(sort, &map_x, &map_y);

        for voxel in &self.model.voxels {
            let colour = self.colour(voxel.i, 0);
            image.put_pixel(map_x(voxel), map_y(voxel), colour);
        }

        image
    }

    fn render_45(&mut self, side: &Side) -> RgbaImage {
        let size = self.size();

        match *side {
            Side::Front => self.render_45_closure(
                |voxel| u32::from(voxel.z) + size.invert_y(voxel),
                |voxel| u32::from(voxel.x),
                |voxel| size.invert_z(voxel) + size.invert_y(voxel)
            ),
            Side::Left =>  self.render_45_closure(
                |voxel| u32::from(voxel.z) + size.invert_x(voxel),
                |voxel| size.invert_y(voxel),
                |voxel| size.invert_z(voxel) + size.invert_x(voxel)
            ),
            Side::Right => self.render_45_closure(
                |voxel| u32::from(voxel.z) + u32::from(voxel.x),
                |voxel| u32::from(voxel.y),
                |voxel| size.invert_z(voxel) + u32::from(voxel.x)
            ),
            Side::Back  => self.render_45_closure(
                |voxel| u32::from(voxel.z) + u32::from(voxel.y),
                |voxel| size.invert_x(voxel),
                |voxel| size.invert_z(voxel) + u32::from(voxel.y)
            ),
            _ => unreachable!()
        }
    }


    fn render_45_closure<S, X, Y>(&mut self, sort: S, map_x: X, map_y: Y) -> RgbaImage
        where
            S: Fn(&Voxel) -> u32,
            X: Fn(&Voxel) -> u32,
            Y: Fn(&Voxel) -> u32
    {
        let mut image = self.create_image(sort, &map_x, &map_y);

        for voxel in &self.model.voxels {
            let x = map_x(voxel);
            let y = map_y(voxel);

            let colour = self.colour(voxel.i, 30);
            let colour_lighter = self.colour(voxel.i, 0);

            image.put_pixel(x, y + 1, colour);
            image.put_pixel(x, y, colour_lighter);
        }

        image
    }

    fn render_22_5(&mut self, side: &Side) -> RgbaImage {
        let size = self.size();
        
        match *side {
            Side::Front => self.render_22_5_closure(
                |voxel| u32::from(voxel.z) + size.invert_y(voxel),
                |voxel| u32::from(voxel.x) * 2,
                |voxel| size.invert_z(voxel) * 2 + size.invert_y(voxel)
            ),
            Side::Left => self.render_22_5_closure(
                |voxel| u32::from(voxel.z) + size.invert_x(voxel),
                |voxel| size.invert_y(voxel) * 2,
                |voxel| size.invert_z(voxel) * 2 + size.invert_x(voxel)
            ),
            Side::Right => self.render_22_5_closure(
                |voxel| u32::from(voxel.z) + u32::from(voxel.x),
                |voxel| u32::from(voxel.y) * 2,
                |voxel| size.invert_z(voxel) * 2 + u32::from(voxel.x)
            ),
            Side::Back  => self.render_22_5_closure(
                |voxel| u32::from(voxel.z) + u32::from(voxel.y),
                |voxel| size.invert_x(voxel) * 2,
                |voxel| size.invert_z(voxel) * 2 + u32::from(voxel.y)
            ),
            _ => unreachable!()
        }
    }


    fn render_22_5_closure<S, X, Y>(&mut self, sort: S, map_x: X, map_y: Y) -> RgbaImage
        where
            S: Fn(&Voxel) -> u32,
            X: Fn(&Voxel) -> u32,
            Y: Fn(&Voxel) -> u32
    {
        let mut image = self.create_image(sort, &map_x, &map_y);

        for voxel in &self.model.voxels {
            let x = map_x(voxel);
            let y = map_y(voxel);

            let colour = self.colour(voxel.i, 30);
            let colour_lighter = self.colour(voxel.i, 0);

            image.put_pixel(x,     y,     colour_lighter);
            image.put_pixel(x + 1, y,     colour_lighter);
            image.put_pixel(x,     y + 1, colour);
            image.put_pixel(x + 1, y + 1, colour);
            image.put_pixel(x,     y + 2, colour);
            image.put_pixel(x + 1, y + 2, colour);
        }

        image
    }

    fn render_45_iso(&mut self, side: &Side) -> RgbaImage {
        let size = self.size();

        match *side {
            Side::Front => self.render_45_iso_closure(
                |voxel| u32::from(voxel.z) + size.invert_x(voxel) + size.invert_y(voxel),
                |voxel| u32::from(voxel.x) + size.invert_y(voxel),
                |voxel| size.invert_z(voxel) + size.invert_x(voxel) + size.invert_y(voxel)
            ),
            Side::Left => self.render_45_iso_closure(
                |voxel| u32::from(voxel.z) + size.invert_x(voxel) + u32::from(voxel.y),
                |voxel| size.invert_x(voxel) + size.invert_y(voxel),
                |voxel| size.invert_z(voxel) + size.invert_x(voxel) + u32::from(voxel.y)
            ),
            Side::Right => self.render_45_iso_closure(
                |voxel| u32::from(voxel.z) + u32::from(voxel.x) + size.invert_y(voxel),
                |voxel| u32::from(voxel.x) + u32::from(voxel.y),
                |voxel| size.invert_z(voxel) + u32::from(voxel.x) + size.invert_y(voxel)
            ),
            Side::Back => self.render_45_iso_closure(
                |voxel| u32::from(voxel.z) + u32::from(voxel.x) + u32::from(voxel.y),
                |voxel| size.invert_x(voxel) + u32::from(voxel.y),
                |voxel| size.invert_z(voxel) + u32::from(voxel.x) + u32::from(voxel.y)
            ),
            _ => unreachable!()
        }
    }

    fn render_45_iso_closure<S, X, Y>(&mut self, sort: S, map_x: X, map_y: Y) -> RgbaImage
        where
            S: Fn(&Voxel) -> u32,
            X: Fn(&Voxel) -> u32,
            Y: Fn(&Voxel) -> u32
    {
        let mut image = self.create_image(sort, &map_x, &map_y);

        for voxel in &self.model.voxels {
            let x = map_x(voxel);
            let y = map_y(voxel);

            let colour = self.colour(voxel.i, 30);
            let colour_lighter = self.colour(voxel.i, 15);
            let colour_lightest = self.colour(voxel.i, 0);

            image.put_pixel(x,     y,     colour_lightest);
            image.put_pixel(x + 1, y,     colour_lightest);
            image.put_pixel(x,     y + 1, colour);
            image.put_pixel(x + 1, y + 1, colour_lighter);
        }

        image
    }

    fn render_22_5_iso(&mut self, side: &Side) -> RgbaImage {
        let size = self.size();

        match *side {
            Side::Front => self.render_22_5_iso_closure(
                |voxel| u32::from(voxel.z) + size.invert_x(voxel) + size.invert_y(voxel),
                |voxel| u32::from(voxel.x) * 2 + size.invert_y(voxel) * 2,
                |voxel| size.invert_z(voxel) * 3 + size.invert_x(voxel) + size.invert_y(voxel)
            ),
            Side::Left => self.render_22_5_iso_closure(
                |voxel| u32::from(voxel.z) + size.invert_x(voxel) + u32::from(voxel.y),
                |voxel| size.invert_x(voxel) * 2 + size.invert_y(voxel) * 2,
                |voxel| size.invert_z(voxel) * 3 + size.invert_x(voxel) + u32::from(voxel.y)
            ),
            Side::Right => self.render_22_5_iso_closure(
                |voxel| u32::from(voxel.z) + u32::from(voxel.x) + size.invert_y(voxel),
                |voxel| u32::from(voxel.x) * 2 + u32::from(voxel.y) * 2,
                |voxel| size.invert_z(voxel) * 3 + u32::from(voxel.x) + size.invert_y(voxel)
            ),
            Side::Back => self.render_22_5_iso_closure(
                |voxel| u32::from(voxel.z) + u32::from(voxel.x) + u32::from(voxel.y),
                |voxel| size.invert_x(voxel) * 2 + u32::from(voxel.y) * 2,
                |voxel| size.invert_z(voxel) * 3 + u32::from(voxel.x) + u32::from(voxel.y)
            ),
            _ => unreachable!()
        }
    }

    fn render_22_5_iso_closure<S, X, Y>(&mut self, sort: S, map_x: X, map_y: Y) -> RgbaImage
        where
            S: Fn(&Voxel) -> u32,
            X: Fn(&Voxel) -> u32,
            Y: Fn(&Voxel) -> u32
    {
        let mut image = self.create_image(sort, &map_x, &map_y);

        for voxel in &self.model.voxels {
            let x = map_x(voxel);
            let y = map_y(voxel);

            let colour = self.colour(voxel.i, 30);
            let colour_lighter = self.colour(voxel.i, 15);
            let colour_lightest = self.colour(voxel.i, 0);
            
            image.put_pixel(x,     y,     colour_lightest);
            image.put_pixel(x + 1, y,     colour_lightest);
            image.put_pixel(x + 2, y,     colour_lightest);
            image.put_pixel(x + 3, y,     colour_lightest);

            for y in y + 1 .. y + 4 {
                image.put_pixel(x,     y, colour);
                image.put_pixel(x + 1, y, colour);
                image.put_pixel(x + 2, y, colour_lighter);
                image.put_pixel(x + 3, y, colour_lighter);
            }
        }

        image
    }
}