#[derive(Debug, Clone, Copy, PartialEq, Eq)]
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]>>,
}
impl QuadTreeNode {
pub fn new(face: Face, level: u32, x: u32, y: u32) -> Self {
Self {
face,
level,
x,
y,
children: None,
}
}
pub fn is_leaf(&self) -> bool {
self.children.is_none()
}
pub fn center_on_sphere(&self, radius: f64) -> (f64, f64, f64) {
let size = 1.0 / (1u32 << self.level) as f64;
let cx = self.x as f64 * size + size * 0.5;
let cy = self.y as f64 * size + size * 0.5;
let (u, v) = (cx * 2.0 - 1.0, cy * 2.0 - 1.0);
let (x, y, z) = match self.face {
Face::PosX => (1.0, v, -u),
Face::NegX => (-1.0, v, u),
Face::PosY => (u, 1.0, -v),
Face::NegY => (u, -1.0, v),
Face::PosZ => (u, v, 1.0),
Face::NegZ => (-u, v, -1.0),
};
let len = (x * x + y * y + z * z).sqrt();
(x / len * radius, y / len * radius, z / len * radius)
}
}
pub struct LodConfig {
pub max_level: u32,
pub split_distance_factor: f64,
}
impl Default for LodConfig {
fn default() -> Self {
Self {
max_level: 14,
split_distance_factor: 2.0,
}
}
}
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
.into_iter()
.map(|f| QuadTreeNode::new(f, 0, 0, 0))
.collect();
Self { roots, config }
}
pub fn update(&mut self, camera: (f64, f64, f64)) {
for root in &mut self.roots {
Self::update_node(root, camera, crate::MARS_RADIUS, &self.config);
}
}
fn update_node(
node: &mut QuadTreeNode,
camera: (f64, f64, f64),
radius: f64,
config: &LodConfig,
) {
if node.level >= config.max_level {
return;
}
let center = node.center_on_sphere(radius);
let dx = camera.0 - center.0;
let dy = camera.1 - center.1;
let dz = camera.2 - center.2;
let dist = (dx * dx + dy * dy + dz * dz).sqrt();
let size = radius * 2.0 / (1u32 << node.level) as f64;
if dist < size * config.split_distance_factor {
if node.children.is_none() {
let l = node.level + 1;
let bx = node.x * 2;
let by = node.y * 2;
node.children = Some(Box::new([
QuadTreeNode::new(node.face, l, bx, by),
QuadTreeNode::new(node.face, l, bx + 1, by),
QuadTreeNode::new(node.face, l, bx, by + 1),
QuadTreeNode::new(node.face, l, bx + 1, by + 1),
]));
}
if let Some(ref mut children) = node.children {
for child in children.iter_mut() {
Self::update_node(child, camera, radius, config);
}
}
} else {
node.children = None;
}
}
pub fn leaf_count(&self) -> usize {
self.roots.iter().map(Self::count_leaves).sum()
}
fn count_leaves(node: &QuadTreeNode) -> usize {
match &node.children {
None => 1,
Some(children) => children.iter().map(Self::count_leaves).sum(),
}
}
}