Skip to main content

voxquant_dotvox/
lib.rs

1//! `MagicaVoxel` support for [`voxquant_core`] through the [`dot_vox`](https://docs.rs/dot_vox/latest/dot_vox/) crate
2use anyhow::Result;
3use clap::{Args, ValueEnum};
4use glam::{Mat4, Vec4};
5use std::fmt;
6use std::path::Path;
7use voxquant_core::{Format, OutputFormat, VoxelizationConfig, scene::Scene};
8
9mod serialization;
10mod voxelization;
11
12/// Determines the algorithm that assigns color indices to
13/// generated voxel colors.
14#[derive(Debug, Clone, Copy, ValueEnum)]
15pub enum ColorMode {
16    /// The palette will be static, it will use the default palette defined
17    /// by this crate (NOT the default magicavoxel palette!)
18    #[value(name = "static")]
19    Static,
20    /// The palette will be determined using the colors present within the
21    /// generated model. A quantization algorithm will assign colors to
22    /// make the output file colors as accurate as possible
23    #[cfg(feature = "dynamic_palette")]
24    #[value(name = "dynamic")]
25    Dynamic,
26}
27
28impl fmt::Display for ColorMode {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        match self {
31            Self::Static => f.write_str("static"),
32            #[cfg(feature = "dynamic_palette")]
33            Self::Dynamic => f.write_str("dynamic"),
34        }
35    }
36}
37
38#[profiling::function]
39fn voxelize_and_save(
40    scene: Scene,
41    format_config: &DotVoxConfig,
42    voxelization_config: &VoxelizationConfig,
43    output: &Path,
44) -> Result<()> {
45    let largest_dim = scene.bounds.size().max_element();
46    let scale = voxelization_config.res as f32 / largest_dim;
47
48    let voxel_bounds_size = scene.bounds.size() * scale;
49
50    let center_offset = -(voxel_bounds_size / 2.0).round().as_ivec3() + 128;
51
52    match format_config.color {
53        ColorMode::Static => {
54            let data = voxelization::voxelize(
55                &scene,
56                voxelization_config.res,
57                voxelization_config.mode,
58                !format_config.no_optimization,
59            );
60
61            serialization::save_vox_static(data, output, center_offset)?;
62        }
63        #[cfg(feature = "dynamic_palette")]
64        ColorMode::Dynamic => {
65            let data = voxelization::voxelize(
66                &scene,
67                voxelization_config.res,
68                voxelization_config.mode,
69                !format_config.no_optimization,
70            );
71
72            serialization::save_vox_dynamic(data, output, center_offset)?;
73        }
74    }
75
76    Ok(())
77}
78
79/// Config for the [`DotVox`] voxelizer.
80#[derive(Debug, Args)]
81#[command(next_help_heading = "`.vox` format options")]
82pub struct DotVoxConfig {
83    /// The palette generation mode. Dynamic palette looks
84    /// much better, but the static palette is much faster.
85    ///
86    /// Dynamic palette is only enabled if the feature `dynamic_palette`
87    /// is enabled (the feature is enabled by default)
88    #[cfg_attr(feature = "dynamic_palette", arg(long, default_value_t = ColorMode::Dynamic))]
89    #[cfg_attr(not(feature = "dynamic_palette"), arg(long, default_value_t = ColorMode::Static))]
90    pub color: ColorMode,
91
92    /// With this option, if two triangles share a voxel,
93    /// both voxels will be present in the output file
94    /// (magicavoxel will likely present the last one)
95    #[arg(long, default_value_t = false)]
96    pub no_optimization: bool,
97}
98
99/// The definition of the output format.
100pub struct DotVox;
101
102impl Format for DotVox {
103    // Z: up, Y: forward, X: right
104    const BASIS: Mat4 = Mat4::from_cols(
105        Vec4::new(1.0, 0.0, 0.0, 0.0),
106        Vec4::new(0.0, 0.0, 1.0, 0.0),
107        Vec4::new(0.0, 1.0, 0.0, 0.0),
108        Vec4::new(0.0, 0.0, 0.0, 1.0),
109    );
110}
111
112impl OutputFormat for DotVox {
113    type Config = DotVoxConfig;
114
115    fn voxelize_and_save(
116        scene: Scene,
117        output: &Path,
118        format_config: Self::Config,
119        voxelization_config: &VoxelizationConfig,
120    ) -> Result<()> {
121        voxelize_and_save(scene, &format_config, voxelization_config, output)
122    }
123}