From 05b448094c298945c9ad6113e4e75aa24c7005c9 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Thu, 29 Apr 2021 00:27:38 +0200 Subject: [PATCH] Add ModalDownsampler --- .../src/multiresolution/chunk_pyramid.rs | 30 ++++++++++++- .../src/multiresolution/sampling.rs | 44 +++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/crates/building_blocks_storage/src/multiresolution/chunk_pyramid.rs b/crates/building_blocks_storage/src/multiresolution/chunk_pyramid.rs index ad122bcb..95505ba5 100644 --- a/crates/building_blocks_storage/src/multiresolution/chunk_pyramid.rs +++ b/crates/building_blocks_storage/src/multiresolution/chunk_pyramid.rs @@ -376,7 +376,7 @@ pub type CompressibleChunkPyramid3 = CompressibleChunkPyramid<[i32; 3], B #[cfg(test)] mod tests { use super::*; - use crate::{Sd8, SdfMeanDownsampler, TransformMap}; + use crate::{ModalDownsampler, Sd8, SdfMeanDownsampler, TransformMap}; #[test] fn downsample_destination_for_one_level_up() { @@ -463,4 +463,32 @@ mod tests { &lod0_extent, ); } + + #[test] + fn downsample_modal_chunks_with_index() { + let num_lods = 2; + let chunk_shape = Point3i::fill(16); + let superchunk_shape = Point3i::fill(32); + + let lod0_extent = + Extent3i::from_min_and_shape(Point3i::ZERO, superchunk_shape); + + // Build a chunk map for LOD0. + let ambient = 0; + let pyramid_builder = ChunkMapBuilder3x1::new(chunk_shape, ambient); + let mut pyramid = ChunkPyramid3::new(pyramid_builder, || SmallKeyHashMap::new(), num_lods); + let lod0 = pyramid.level_mut(0); + lod0.fill_extent(&Extent3i::from_min_and_shape(Point3i::ZERO, chunk_shape + PointN([chunk_shape.x(), chunk_shape.y(), 0])), 1); + *lod0.get_mut(chunk_shape) = 1; + + let index = OctreeChunkIndex::index_chunk_map(superchunk_shape, &lod0); + + pyramid.downsample_chunks_with_index( + &index, + &ModalDownsampler, + &lod0_extent, + ); + + assert_eq!(pyramid.level(1).get(PointN([0, 0, 0])), 1); + } } diff --git a/crates/building_blocks_storage/src/multiresolution/sampling.rs b/crates/building_blocks_storage/src/multiresolution/sampling.rs index edbe7e2d..ac9da455 100644 --- a/crates/building_blocks_storage/src/multiresolution/sampling.rs +++ b/crates/building_blocks_storage/src/multiresolution/sampling.rs @@ -1,3 +1,5 @@ +use std::{collections::HashMap, hash::Hash}; + use crate::{prelude::*, ArrayIndexer, ArrayNx1}; use building_blocks_core::prelude::*; @@ -44,6 +46,48 @@ where } } +/// A `ChunkDownsampler` that selects the most frequent (i.e. modal) voxel type from each `2x2x2` region, discarding the rest. +pub struct ModalDownsampler; + +impl ChunkDownsampler for ModalDownsampler +where + N: ArrayIndexer, + PointN: IntegerPoint, + T: 'static + Copy + Eq + Hash + std::fmt::Debug, + Src: Get, Item = T> + IndexedArray, +{ + fn downsample( + &self, + src_chunk: &Src, + dst_chunk: &mut ArrayNx1, + dst_min: Local, + lod_delta: u8, + ) { + // PERF: the access pattern here might not be very cache friendly + + debug_assert!(lod_delta > 0); + let lod_delta = lod_delta as i32; + + let lod_scale_factor = 1 << lod_delta; + let src_shape_per_point = PointN::fill(lod_scale_factor); + + let dst_shape = src_chunk.extent().shape >> lod_delta; + debug_assert!(dst_shape > PointN::ZERO); + + for p_dst in ExtentN::from_min_and_shape(PointN::ZERO, dst_shape).iter_points() { + let src_min = p_dst << lod_delta; + let src_extent = ExtentN::from_min_and_shape(src_min, src_shape_per_point); + + let mut frequencies = HashMap::new(); + for p_src in src_extent.iter_points() { + *frequencies.entry(src_chunk.get(Local(p_src))).or_insert(0) += 1; + } + let (voxel, _frequency) = frequencies.iter().max_by(|a, b| a.1.cmp(b.1)).unwrap(); + *dst_chunk.get_mut(Local(dst_min.0 + p_dst)) = *voxel; + } + } +} + /// A `ChunkDownsampler` that takes the mean of each `2x2x2` region of a signed distance field. It also renormalizes the values /// to lie in the range `[-1.0, 1.0]`. pub struct SdfMeanDownsampler;