#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Face {
PosX,
NegX,
PosY,
NegY,
PosZ,
NegZ,
}
pub struct QuadTreeNode {
pub face: Face,
pub level: u32,
pub x: u32,
pub y: u32,
pub children: Option<Box<[QuadTreeNode; 4]>>,
}
pub struct LodConfig {
pub max_level: u32,
pub split_distance_factor: f64,
pub base_resolution: u32,
}
impl Default for LodConfig {
fn default() -> Self {
Self {
max_level: 15,
split_distance_factor: 2.0,
base_resolution: 64,
}
}
}
impl QuadTreeNode {
pub fn new(face: Face, level: u32, x: u32, y: u32) -> Self {
Self {
face,
level,
x,
y,
children: None,
}
}
pub fn should_split(&self, camera_distance: f64, config: &LodConfig) -> bool {
if self.level >= config.max_level {
return false;
}
let node_size = 1.0 / (1u64 << self.level) as f64;
camera_distance < node_size * config.split_distance_factor
}
pub fn split(&mut self) {
let nl = self.level + 1;
let nx = self.x * 2;
let ny = self.y * 2;
self.children = Some(Box::new([
QuadTreeNode::new(self.face, nl, nx, ny),
QuadTreeNode::new(self.face, nl, nx + 1, ny),
QuadTreeNode::new(self.face, nl, nx, ny + 1),
QuadTreeNode::new(self.face, nl, nx + 1, ny + 1),
]));
}
pub fn is_leaf(&self) -> bool {
self.children.is_none()
}
}
pub struct LodTerrain {
pub roots: Vec<QuadTreeNode>,
pub config: LodConfig,
}
impl LodTerrain {
pub fn new(config: LodConfig) -> Self {
let faces = [
Face::PosX,
Face::NegX,
Face::PosY,
Face::NegY,
Face::PosZ,
Face::NegZ,
];
let roots = faces
.iter()
.map(|&f| QuadTreeNode::new(f, 0, 0, 0))
.collect();
Self { roots, config }
}
pub fn update(&mut self, camera_pos: [f64; 3]) {
let cam_dist =
(camera_pos[0].powi(2) + camera_pos[1].powi(2) + camera_pos[2].powi(2)).sqrt();
for root in &mut self.roots {
Self::update_node(root, cam_dist, &self.config);
}
}
fn update_node(node: &mut QuadTreeNode, camera_distance: f64, config: &LodConfig) {
if node.should_split(camera_distance, config) {
if node.is_leaf() {
node.split();
}
if let Some(ref mut children) = node.children {
for child in children.iter_mut() {
Self::update_node(child, camera_distance, config);
}
}
}
}
}