marss 0.0.3

Mars celestial simulation crate for the MilkyWay SolarSystem workspace
Documentation
#[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(),
        }
    }
}