voxelis 25.4.0

Sparse Voxel Octree DAG engine for building worlds, shaping matter, and mastering 3D space — powered by pure Rust.
Documentation
use std::{io::Read, path::Path};

use byteorder::{BigEndian, ReadBytesExt};
use glam::IVec3;
use md5::{Digest, Md5};

use crate::{MaxDepth, world::VoxModel};

use super::{
    Flags,
    consts::{VTM_MAGIC, VTM_VERSION},
};

pub fn import_model_from_vtm<P: AsRef<Path>>(
    path: &P,
    memory_budget: usize,
    target_chunk_world_size: Option<f32>,
) -> VoxModel {
    let mut vox_file = std::fs::File::open(path).unwrap();
    let mut reader = std::io::BufReader::new(&mut vox_file);

    let mut magic = [0u8; VTM_MAGIC.len()];
    reader.read_exact(&mut magic).unwrap();
    assert_eq!(magic, VTM_MAGIC);

    let version = reader.read_u16::<BigEndian>().unwrap();
    assert_eq!(version, VTM_VERSION);

    let flags = reader.read_u16::<BigEndian>().unwrap();
    let flags = Flags::from_bits(flags).unwrap();
    println!("Flags: {:?}", flags);

    let lod_level = reader.read_u8().unwrap();
    println!("LOD Level: {}", lod_level);

    let chunk_world_size = reader.read_f32::<BigEndian>().unwrap();
    println!("Chunk Size: {}m", chunk_world_size);

    let _reserved_1 = reader.read_u32::<BigEndian>().unwrap();
    let _reserved_2 = reader.read_u32::<BigEndian>().unwrap();

    let world_bounds_x = reader.read_i32::<BigEndian>().unwrap();
    let world_bounds_y = reader.read_i32::<BigEndian>().unwrap();
    let world_bounds_z = reader.read_i32::<BigEndian>().unwrap();
    let world_bounds = IVec3::new(world_bounds_x, world_bounds_y, world_bounds_z);

    println!("World bounds: {:?}", world_bounds);

    let name_len = reader.read_u8().unwrap();
    let mut name = vec![0u8; name_len as usize];
    reader.read_exact(&mut name).unwrap();

    println!("Name: {:?}", std::str::from_utf8(&name).unwrap());

    let mut md5_hash = [0u8; 16];
    reader.read_exact(&mut md5_hash).unwrap();

    println!("MD5 Hash: {:0X?}", md5_hash);

    let data_size = reader.read_u32::<BigEndian>().unwrap();
    let mut data = vec![0u8; data_size as usize];
    reader.read_exact(&mut data).unwrap();

    println!("Data: {:?}", data_size);

    let data = if flags.contains(Flags::COMPRESSED) {
        let mut decoder = zstd::stream::Decoder::new(&data[..]).unwrap();
        let mut data = Vec::new();
        std::io::copy(&mut decoder, &mut data).unwrap();

        data
    } else {
        data
    };

    let mut md5_hasher = Md5::new();
    md5_hasher.update(&data);
    let md5_hash_calculated = md5_hasher.finalize();

    assert_eq!(md5_hash, md5_hash_calculated.as_slice());

    println!("MD5 Hash calculated: {:0X?}", md5_hash_calculated);

    let chunk_world_size = target_chunk_world_size.unwrap_or(chunk_world_size);

    let mut model = VoxModel::empty(MaxDepth::new(lod_level), chunk_world_size, memory_budget);
    model.deserialize(&data);

    model
}