Skip to content
This repository has been archived by the owner on Nov 13, 2023. It is now read-only.

Commit

Permalink
add some documentation to the multiresolution module
Browse files Browse the repository at this point in the history
  • Loading branch information
bonsairobo committed Mar 21, 2021
1 parent 7829f18 commit 9595f95
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 16 deletions.
14 changes: 7 additions & 7 deletions crates/building_blocks_storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,13 @@ pub type SmallKeyBuildHasher = ahash::RandomState;

pub mod prelude {
pub use super::{
copy_extent, BytesCompression, Chunk, ChunkHashMapPyramid2, ChunkHashMapPyramid3,
ChunkIndexer, ChunkMapBuilder, ChunkReadStorage, ChunkWriteStorage, Compressed,
CompressibleChunkMap, CompressibleChunkMapReader, CompressibleChunkStorage,
CompressibleChunkStorageReader, Compression, FastCompressibleChunkStorage,
FromBytesCompression, Func, IndexedArray, IsEmpty, IterChunkKeys, Local, LocalChunkCache2,
LocalChunkCache3, OctreeChunkIndex, OctreeNode, OctreeSet, Sd16, Sd8, SerializableChunks,
SignedDistance, Stride, TransformMap,
copy_extent, Chunk, ChunkHashMapPyramid2, ChunkHashMapPyramid3, ChunkMapBuilder,
ChunkReadStorage, ChunkWriteStorage, Compressed, CompressibleChunkMap,
CompressibleChunkMapReader, CompressibleChunkStorage, CompressibleChunkStorageReader,
Compression, FastCompressibleChunkStorage, FromBytesCompression, Func, IndexedArray,
IsEmpty, IterChunkKeys, Local, LocalChunkCache2, LocalChunkCache3, OctreeChunkIndex,
OctreeNode, OctreeSet, PointDownsampler, Sd16, Sd8, SdfMeanDownsampler, SerializableChunks,
SignedDistance, SmallKeyHashMap, Stride, TransformMap, VisitStatus,
};

pub use super::access_traits::*;
Expand Down
69 changes: 69 additions & 0 deletions crates/building_blocks_storage/src/multiresolution.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,72 @@
//! Structures and algorithms for implementing level of detail (LoD).
//!
//! More specifically, this module is called "multiresolution" because it helps with storing voxels of different "sizes" or
//! equivalently different sampling rates. Here's the breakdown of important structures we can use:
//!
//! - [OctreeSet](crate::OctreeSet): a bounded 3D hierarchical bitset, used for spatial indexing tasks
//! - [OctreeChunkIndex](crate::OctreeChunkIndex): an unbounded `OctreeSet` specifically for tracking the presence of chunks
//! - [ChunkPyramid](self::ChunkPyramid): an ordered list of `ChunkMap` "levels", where each level decreases the sampling rate
//! - [ChunkDownsampler](self::ChunkDownsampler): an algorithm for downsampling one chunk
//!
//! You will generally want to have a `ChunkPyramid` and a corresponding `OctreeChunkIndex` that tracks the set of chunks that
//! exist. Each node in the `OctreeChunkIndex` corresponds to a chunk at a particular level, i.e. an `LodChunkKey`. There is
//! currently no enforcement of occupancy in the `ChunkPyramid`.
//!
//! `OctreeChunkIndex` is "unbounded" because it is actually a collection of `OctreeSet`s stored in a map. Each entry of that
//! map is called a "super chunk." You can think if it like a `ChunkMap`, except instead of `Array`s, it stores `OctreeSet`s.
//! Every superchunk is the same shape, and each is resonsible for a sparse set of chunks in a bounded region.
//!
//! You might wonder why the `OctreeChunkIndex` is necessary at all. It's main utility is for optimizing iteration over large
//! regions of the map. Without one, the best you could do is hash every single chunk key that overlaps your query extent to see
//! if it exists in the pyramid. It is also a natural structure for implementing a clipmap.
//!
//! ## Indexing and Downsampling a `ChunkMap`
//!
//! ```
//! # use building_blocks_core::prelude::*;
//! # use building_blocks_storage::prelude::*;
//! # use std::collections::HashSet;
//!
//! // Constructing a pyramid is much the same as constructing a chunk map, except you need to supply a closure to construct
//! // empty storages.
//! let num_lods = 5; // Up to 6 supported for now.
//! let chunk_shape = Point3i::fill(16);
//! let ambient_value = 0;
//! let builder = ChunkMapBuilder3x1::new(chunk_shape, ambient_value);
//! let mut pyramid = ChunkHashMapPyramid3::new(builder, || SmallKeyHashMap::new(), num_lods);
//!
//! // Populate LOD0, the highest resolution.
//! let mut lod0 = pyramid.level_mut(0);
//! let extent = Extent3i::from_min_and_shape(Point3i::ZERO, Point3i::fill(100));
//! lod0.fill_extent(&extent, 1);
//!
//! // Now we index the currently populated set of chunks.
//! let superchunk_shape = Point3i::fill(512);
//! let mut index = OctreeChunkIndex::index_chunk_map(superchunk_shape, lod0);
//!
//! // Just make sure everything's here. A unit test to help you understand this structure.
//! let mut chunk_keys = HashSet::new();
//! index.superchunk_octrees.visit_octrees(
//! &extent,
//! &mut |octree: &OctreeSet| {
//! octree.visit_all_octants_in_preorder(&mut |node: &OctreeNode| {
//! // Chunks are the single-voxel leaves. Remember this octree is indexing in a space where 1 voxel = 1 chunk.
//! if node.octant().is_single_voxel() {
//! // The octree coordinates are downscaled by the chunk shape.
//! chunk_keys.insert(node.octant().minimum() * chunk_shape);
//! }
//! VisitStatus::Continue
//! });
//! }
//! );
//! assert_eq!(chunk_keys, lod0.storage().chunk_keys().cloned().collect());
//!
//! // Now let's downsample those chunks into every level of the pyramid. This goes bottom-up in post-order. The
//! // `PointDownsampler` simply takes one point for each 2x2x2 region being sampled. There is also an `SdfMeanDownsampler` that
//! // works on any voxels types that implement `SignedDistance`. Or you can define your own downsampler!
//! pyramid.downsample_chunks_with_index(&index, &PointDownsampler, &extent);
//! ```
pub mod chunk_pyramid;
pub mod clipmap;
pub mod sampling;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
prelude::*, ArrayNx1, ChunkDownsampler, ChunkMap, ChunkMapBuilder, ChunkMapBuilderNx1,
ChunkMapNx1, FastArrayCompressionNx1, OctreeChunkIndex, OctreeNode, SmallKeyHashMap,
VisitStatus,
ChunkMapNx1, FastArrayCompressionNx1, LodChunkKey, OctreeChunkIndex, OctreeNode,
SmallKeyHashMap, VisitStatus,
};

use building_blocks_core::prelude::*;
Expand Down Expand Up @@ -49,6 +49,17 @@ impl<N, T, Store> ChunkPyramid<N, T, Store> {
}
}

impl<N, T, Store> ChunkPyramid<N, T, Store>
where
PointN<N>: IntegerPoint<N>,
T: Clone,
Store: ChunkReadStorage<N, ArrayNx1<N, T>>,
{
pub fn get_chunk(&self, key: LodChunkKey<N>) -> Option<&ArrayNx1<N, T>> {
self.levels[key.lod as usize].get_chunk(key.chunk_key)
}
}

impl<N, T, Store> ChunkPyramid<N, T, Store>
where
PointN<N>: IntegerPoint<N>,
Expand Down Expand Up @@ -80,6 +91,10 @@ where
self.builder.ambient_value()
}

pub fn get_mut_chunk(&mut self, key: LodChunkKey<N>) -> Option<&mut ArrayNx1<N, T>> {
self.levels[key.lod as usize].get_mut_chunk(key.chunk_key)
}

/// Downsamples the chunk at `src_level` and `src_chunk_key` into the specified destination level `dst_level`.
pub fn downsample_chunk<Samp>(
&mut self,
Expand Down
19 changes: 12 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
//!
//! ![Wireframe](https://media.githubusercontent.com/media/bonsairobo/building-blocks/main/examples/screenshots/wireframe.png)
//!
//! ![LOD Terrain](https://media.githubusercontent.com/media/bonsairobo/building-blocks/main/examples/screenshots/lod_terrain.png)
//! ![LOD
//! Terrain](https://media.githubusercontent.com/media/bonsairobo/building-blocks/main/examples/screenshots/lod_terrain.png)
//!
//! The primary focus is core data structures and algorithms. Features include:
//!
Expand Down Expand Up @@ -96,13 +97,17 @@
//!
//! To learn the basics about lattice maps, start with these doc pages:
//!
//! - [points](https://docs.rs/building_blocks_core/latest/building_blocks_core/point/struct.PointN.html)
//! - [extents](https://docs.rs/building_blocks_core/latest/building_blocks_core/extent/struct.ExtentN.html)
//! - [arrays](https://docs.rs/building_blocks_storage/latest/building_blocks_storage/array/index.html)
//! - [point](https://docs.rs/building_blocks_core/latest/building_blocks_core/point/struct.PointN.html)
//! - [extent](https://docs.rs/building_blocks_core/latest/building_blocks_core/extent/struct.ExtentN.html)
//! - [array](https://docs.rs/building_blocks_storage/latest/building_blocks_storage/array/index.html)
//! - [access traits](https://docs.rs/building_blocks_storage/latest/building_blocks_storage/access/index.html)
//! - [chunk maps](https://docs.rs/building_blocks_storage/latest/building_blocks_storage/chunk_map/index.html)
//! - [transform maps](https://docs.rs/building_blocks_storage/latest/building_blocks_storage/transform_map/index.html)
//! - [fn maps](https://docs.rs/building_blocks_storage/latest/building_blocks_storage/func/index.html)
//! - [chunk map](https://docs.rs/building_blocks_storage/latest/building_blocks_storage/chunk_map/index.html)
//! - [transform map](https://docs.rs/building_blocks_storage/latest/building_blocks_storage/transform_map/index.html)
//! - [fn map](https://docs.rs/building_blocks_storage/latest/building_blocks_storage/func/index.html)
//!
//! After that, you might be interested in hierarchical structures to help you scale your voxel application. For that, you'll
//! want to read the [multiresolution module doc
//! page](https://docs.rs/building_blocks_storage/latest/building_blocks_storage/multiresolution/index.html).
//!
//! ## Benchmarks
//!
Expand Down

0 comments on commit 9595f95

Please sign in to comment.