use std::collections::{BTreeMap, BTreeSet};
use std::path::Path;
use crate::error::VdbError;
use crate::field::Field;
use crate::field::local_index;
use crate::meta::{Bounds, GridMeta, SDF_KEY};
use crate::topology::{LeafNumber, TileNumber, Topology};
use crate::tree::{Coord, TileLevel};
use crate::tree::{LEAF_LOG2, LOWER_DIM_LOG2, LOWER_SPAN_LOG2, UPPER_DIM_LOG2};
pub use crate::vdb_read::{VdbFile, VdbGrid, from_bytes, read};
pub(crate) use crate::kind::VDB_MAGIC as MAGIC;
const FILE_VERSION: u32 = 225;
const LIBRARY: [u32; 2] = [14, 0];
const UUID: &[u8; 36] = b"00000000-0000-0000-0000-000000000000";
pub(crate) const ACTIVE_MASK: u32 = 2;
const UPPER_PLACES: usize = 1 << 15;
const LOWER_PLACES: usize = 1 << 12;
const LEAF_PLACES: usize = 1 << 9;
const EMPTY_BOX: (Coord, Coord) = ([i32::MAX; 3], [i32::MIN; 3]);
pub trait VdbValue: sealed::VdbWrite {}
mod sealed {
use crate::field::Stored;
pub trait VdbWrite: Stored {
const TREE_TYPE: &'static str;
fn put(self, out: &mut Vec<u8>);
}
}
impl VdbValue for f32 {}
impl VdbValue for f64 {}
impl sealed::VdbWrite for f32 {
const TREE_TYPE: &'static str = "Tree_float_5_4_3";
fn put(self, out: &mut Vec<u8>) {
out.extend(self.to_le_bytes());
}
}
impl sealed::VdbWrite for f64 {
const TREE_TYPE: &'static str = "Tree_double_5_4_3";
fn put(self, out: &mut Vec<u8>) {
out.extend(self.to_le_bytes());
}
}
#[derive(Debug, Clone)]
pub struct VdbEntry {
name: String,
tree_type: &'static str,
body: Vec<u8>,
buffers: Vec<u8>,
}
impl VdbEntry {
pub fn new<T: VdbValue>(meta: &GridMeta, t: &Topology, f: &Field<T>) -> Result<Self, VdbError> {
meta.check()?;
f.belongs_to(t)?;
let nodes = Nodes::of(t)?;
let mut body = Vec::new();
put_u32(&mut body, ACTIVE_MASK);
put_metadata(&mut body, meta, t)?;
put_transform(&mut body, &meta.transform)?;
put_topology(&mut body, &nodes, t, f)?;
let mut buffers = Vec::new();
for n in nodes.leaves() {
put_mask(&mut buffers, LEAF_PLACES, 0..LEAF_PLACES);
buffers.push(0);
let values = f.leaf(t, n)?;
for x in 0..8 {
for y in 0..8 {
for z in 0..8 {
values[local_index([x, y, z])].put(&mut buffers);
}
}
}
}
Ok(Self { name: meta.name.clone(), tree_type: T::TREE_TYPE, body, buffers })
}
}
pub fn to_bytes(entries: &[VdbEntry]) -> Result<Vec<u8>, VdbError> {
let mut names = BTreeSet::new();
if let Some(e) = entries.iter().find(|e| !names.insert(e.name.as_str())) {
return Err(VdbError::DuplicateName(e.name.clone()));
}
let mut out = Vec::new();
out.extend(MAGIC.to_le_bytes());
for x in [FILE_VERSION, LIBRARY[0], LIBRARY[1]] {
put_u32(&mut out, x);
}
out.push(1);
out.extend(UUID);
put_u32(&mut out, 0);
let count = i32::try_from(entries.len()).map_err(|_| VdbError::TooLarge("grid count"))?;
out.extend(count.to_le_bytes());
for e in entries {
put_str(&mut out, &e.name)?;
put_str(&mut out, e.tree_type)?;
put_str(&mut out, "")?;
let body = out.len() + 24;
let buffers = body + e.body.len();
for at in [body, buffers, buffers + e.buffers.len()] {
let at = i64::try_from(at).map_err(|_| VdbError::TooLarge("offset"))?;
out.extend(at.to_le_bytes());
}
out.extend(&e.body);
out.extend(&e.buffers);
}
Ok(out)
}
pub fn write(path: &Path, entries: &[VdbEntry]) -> Result<(), VdbError> {
use std::io::Write;
let bytes = to_bytes(entries)?;
crate::cache::replace(path, |out| Ok::<(), VdbError>(out.write_all(&bytes)?))
}
#[derive(Default)]
struct Lower {
leaves: BTreeMap<usize, LeafNumber>,
tiles: BTreeMap<usize, TileNumber>,
}
#[derive(Default)]
struct Upper {
lowers: BTreeMap<usize, Lower>,
tiles: BTreeMap<usize, TileNumber>,
}
struct Nodes {
root_tiles: BTreeMap<Coord, TileNumber>,
uppers: BTreeMap<Coord, Upper>,
}
impl Nodes {
fn of(t: &Topology) -> Result<Self, VdbError> {
let mut nodes = Self { root_tiles: BTreeMap::new(), uppers: BTreeMap::new() };
for n in t.leaf_numbers() {
let o = t.leaf_origin(n)?;
nodes.lower(o).leaves.insert(place(o, LEAF_LOG2, LOWER_DIM_LOG2), n);
}
for n in t.tile_numbers() {
let (level, o) = t.tile(n)?;
match level {
TileLevel::Root => {
nodes.root_tiles.insert(o, n);
}
TileLevel::Upper => {
nodes.upper(o).tiles.insert(place(o, LOWER_SPAN_LOG2, UPPER_DIM_LOG2), n);
}
TileLevel::Lower => {
nodes.lower(o).tiles.insert(place(o, LEAF_LOG2, LOWER_DIM_LOG2), n);
}
}
}
Ok(nodes)
}
fn upper(&mut self, v: Coord) -> &mut Upper {
self.uppers.entry(TileLevel::Root.origin(v)).or_default()
}
fn lower(&mut self, v: Coord) -> &mut Lower {
self.upper(v).lowers.entry(place(v, LOWER_SPAN_LOG2, UPPER_DIM_LOG2)).or_default()
}
fn leaves(&self) -> impl Iterator<Item = LeafNumber> + '_ {
self.uppers
.values()
.flat_map(|u| u.lowers.values())
.flat_map(|l| l.leaves.values().copied())
}
}
fn place(v: Coord, child: u32, log: u32) -> usize {
let [x, y, z] = v.map(|c| ((c.cast_unsigned() >> child) & ((1 << log) - 1)) as usize);
(x << (2 * log)) | (y << log) | z
}
fn put_mask(out: &mut Vec<u8>, places: usize, on: impl Iterator<Item = usize>) {
let mut words = vec![0_u64; places / 64];
for p in on {
if let Some(w) = words.get_mut(p / 64) {
*w |= 1 << (p % 64);
}
}
for w in words {
out.extend(w.to_le_bytes());
}
}
fn put_values<T: VdbValue>(
out: &mut Vec<u8>,
tiles: &BTreeMap<usize, TileNumber>,
t: &Topology,
f: &Field<T>,
) -> Result<(), VdbError> {
out.push(0);
for n in tiles.values() {
f.tile(t, *n)?.put(out);
}
Ok(())
}
fn put_topology<T: VdbValue>(
out: &mut Vec<u8>,
nodes: &Nodes,
t: &Topology,
f: &Field<T>,
) -> Result<(), VdbError> {
out.extend(1_i32.to_le_bytes());
f.background().put(out);
put_u32(out, u32_of(nodes.root_tiles.len(), "root's tile count")?);
put_u32(out, u32_of(nodes.uppers.len(), "root's node count")?);
for (o, n) in &nodes.root_tiles {
put_coord(out, *o);
f.tile(t, *n)?.put(out);
out.push(1);
}
for (o, u) in &nodes.uppers {
put_coord(out, *o);
put_mask(out, UPPER_PLACES, u.lowers.keys().copied());
put_mask(out, UPPER_PLACES, u.tiles.keys().copied());
put_values(out, &u.tiles, t, f)?;
for l in u.lowers.values() {
put_mask(out, LOWER_PLACES, l.leaves.keys().copied());
put_mask(out, LOWER_PLACES, l.tiles.keys().copied());
put_values(out, &l.tiles, t, f)?;
for _ in l.leaves.values() {
put_mask(out, LEAF_PLACES, 0..LEAF_PLACES);
}
}
}
Ok(())
}
fn put_metadata(out: &mut Vec<u8>, meta: &GridMeta, t: &Topology) -> Result<(), VdbError> {
let class: &[u8] = if meta.extra.contains_key(SDF_KEY) { b"level set" } else { b"unknown" };
let (min, max) = match Bounds::of(t)? {
Bounds::Box { min, max } => (min, max),
Bounds::Empty => EMPTY_BOX,
};
let vec3i = |c: Coord| c.iter().flat_map(|x| x.to_le_bytes()).collect::<Vec<u8>>();
let entries: [(&str, &str, Vec<u8>); 7] = [
("class", "string", class.to_vec()),
("file_bbox_max", "vec3i", vec3i(max)),
("file_bbox_min", "vec3i", vec3i(min)),
("file_compression", "string", b"active values".to_vec()),
("file_mem_bytes", "int64", 0_i64.to_le_bytes().to_vec()),
("file_voxel_count", "int64", voxel_count(t)?.to_le_bytes().to_vec()),
("name", "string", meta.name.as_bytes().to_vec()),
];
put_u32(out, 7);
for (name, kind, value) in entries {
put_str(out, name)?;
put_str(out, kind)?;
put_u32(out, u32_of(value.len(), "metadata value")?);
out.extend(value);
}
Ok(())
}
fn voxel_count(t: &Topology) -> Result<i64, VdbError> {
let too_large = || VdbError::TooLarge("voxel count");
let leaves = i64::try_from(t.leaf_count()).map_err(|_| too_large())?;
let mut count = leaves.checked_mul(512).ok_or_else(too_large)?;
for n in t.tile_numbers() {
let (level, _) = t.tile(n)?;
count = count.checked_add(1_i64 << (3 * level.edge_log2())).ok_or_else(too_large)?;
}
Ok(count)
}
fn put_transform(out: &mut Vec<u8>, a: &[[f64; 4]; 3]) -> Result<(), VdbError> {
put_str(out, "AffineMap")?;
let mut m = [[0.0_f64; 4]; 4];
for (r, row) in a.iter().enumerate() {
for (c, x) in row.iter().take(3).enumerate() {
m[c][r] = *x;
}
m[3][r] = row[3] + 0.5 * (row[0] + row[1] + row[2]);
}
m[3][3] = 1.0;
for x in m.iter().flatten() {
out.extend(x.to_le_bytes());
}
Ok(())
}
fn put_coord(out: &mut Vec<u8>, c: Coord) {
for x in c {
out.extend(x.to_le_bytes());
}
}
fn put_u32(out: &mut Vec<u8>, x: u32) {
out.extend(x.to_le_bytes());
}
fn put_str(out: &mut Vec<u8>, s: &str) -> Result<(), VdbError> {
put_u32(out, u32_of(s.len(), "string's length")?);
out.extend(s.as_bytes());
Ok(())
}
fn u32_of(n: usize, what: &'static str) -> Result<u32, VdbError> {
u32::try_from(n).map_err(|_| VdbError::TooLarge(what))
}