#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Face {
PosX,
NegX,
PosY,
NegY,
PosZ,
NegZ,
}
impl Face {
pub fn all() -> [Face; 6] {
[
Face::PosX,
Face::NegX,
Face::PosY,
Face::NegY,
Face::PosZ,
Face::NegZ,
]
}
}
pub struct LodConfig {
pub max_level: u32,
pub split_distance_factor: f64,
pub base_size_m: f64,
}
impl LodConfig {
pub fn default_mercury() -> Self {
Self {
max_level: 14,
split_distance_factor: 2.0,
base_size_m: crate::physics::orbit::MERCURY_RADIUS * 2.0,
}
}
pub fn node_size(&self, level: u32) -> f64 {
self.base_size_m / (1u64 << level) as f64
}
pub fn should_split(&self, level: u32, distance_m: f64) -> bool {
if level >= self.max_level {
return false;
}
distance_m < self.node_size(level) * self.split_distance_factor
}
}
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 split(&mut self) {
let l = self.level + 1;
let bx = self.x * 2;
let by = self.y * 2;
self.children = Some(Box::new([
QuadTreeNode::new(self.face, l, bx, by),
QuadTreeNode::new(self.face, l, bx + 1, by),
QuadTreeNode::new(self.face, l, bx, by + 1),
QuadTreeNode::new(self.face, l, bx + 1, by + 1),
]));
}
pub fn leaf_count(&self) -> usize {
match &self.children {
None => 1,
Some(c) => c.iter().map(|n| n.leaf_count()).sum(),
}
}
}
pub struct LodTerrain {
pub roots: Vec<QuadTreeNode>,
pub config: LodConfig,
}
impl LodTerrain {
pub fn new(config: LodConfig) -> Self {
let roots = Face::all()
.iter()
.map(|&f| QuadTreeNode::new(f, 0, 0, 0))
.collect();
Self { roots, config }
}
pub fn total_leaves(&self) -> usize {
self.roots.iter().map(|r| r.leaf_count()).sum()
}
}
pub fn lod_level(distance: f64) -> u8 {
if distance < 1_000.0 {
5
} else if distance < 10_000.0 {
4
} else if distance < 100_000.0 {
3
} else if distance < 1_000_000.0 {
2
} else {
1
}
}