earths 0.0.4

High-fidelity Earth simulation engine — orbit, atmosphere, geology, hydrology, biosphere, terrain, lighting, rendering, satellites, and temporal systems with full scientific coupling
Documentation
use sciforge::hub::prelude::constants::elements::{atomic_mass, electronegativity};

#[derive(Debug, Clone)]
pub struct PbrMaterial {
    pub name: &'static str,
    pub albedo: [f32; 4],
    pub roughness: f32,
    pub metallic: f32,
    pub normal_strength: f32,
    pub emissive: [f32; 3],
    pub ior: f32,
    pub subsurface: f32,
}

fn metallic_from_iron(fe_wt_frac: f64) -> f32 {
    let m_fe = atomic_mass(26);
    let m_o = atomic_mass(8);
    let fe_fraction_in_feo = m_fe / (m_fe + m_o);
    let pure_fe = fe_wt_frac * fe_fraction_in_feo;
    let en = electronegativity(26).unwrap_or(1.83);
    let metallic_scale = (1.0 - en / 4.0).max(0.0);
    (pure_fe * metallic_scale).clamp(0.0, 1.0) as f32
}

impl PbrMaterial {
    pub fn deep_ocean() -> Self {
        Self {
            name: "deep_ocean",
            albedo: [0.006, 0.018, 0.065, 1.0],
            roughness: 0.02,
            metallic: 0.02,
            normal_strength: 1.0,
            emissive: [0.0; 3],
            ior: 1.333,
            subsurface: 0.4,
        }
    }
    pub fn shallow_ocean() -> Self {
        Self {
            name: "shallow_ocean",
            albedo: [0.04, 0.12, 0.18, 1.0],
            roughness: 0.05,
            metallic: 0.02,
            normal_strength: 0.8,
            emissive: [0.0; 3],
            ior: 1.333,
            subsurface: 0.6,
        }
    }
    pub fn grassland() -> Self {
        Self {
            name: "grassland",
            albedo: [0.12, 0.36, 0.08, 1.0],
            roughness: 0.85,
            metallic: 0.0,
            normal_strength: 0.8,
            emissive: [0.0; 3],
            ior: 1.45,
            subsurface: 0.3,
        }
    }
    pub fn desert_sand() -> Self {
        Self {
            name: "desert_sand",
            albedo: [0.76, 0.60, 0.36, 1.0],
            roughness: 0.95,
            metallic: metallic_from_iron(0.02),
            normal_strength: 0.6,
            emissive: [0.0; 3],
            ior: 1.544,
            subsurface: 0.0,
        }
    }
    pub fn fresh_snow() -> Self {
        Self {
            name: "fresh_snow",
            albedo: [0.95, 0.95, 0.97, 1.0],
            roughness: 0.3,
            metallic: 0.0,
            normal_strength: 0.3,
            emissive: [0.0; 3],
            ior: 1.309,
            subsurface: 0.8,
        }
    }
    pub fn granite() -> Self {
        Self {
            name: "granite",
            albedo: [0.40, 0.36, 0.33, 1.0],
            roughness: 0.88,
            metallic: metallic_from_iron(0.015),
            normal_strength: 1.0,
            emissive: [0.0; 3],
            ior: 1.544,
            subsurface: 0.0,
        }
    }
    pub fn basalt() -> Self {
        Self {
            name: "basalt",
            albedo: [0.15, 0.13, 0.12, 1.0],
            roughness: 0.92,
            metallic: metallic_from_iron(0.08),
            normal_strength: 1.0,
            emissive: [0.0; 3],
            ior: 1.58,
            subsurface: 0.0,
        }
    }
    pub fn volcanic_lava() -> Self {
        Self {
            name: "volcanic_lava",
            albedo: [0.15, 0.08, 0.05, 1.0],
            roughness: 0.7,
            metallic: metallic_from_iron(0.10),
            normal_strength: 1.0,
            emissive: [0.8, 0.2, 0.0],
            ior: 1.50,
            subsurface: 0.0,
        }
    }
    pub fn tropical_forest() -> Self {
        Self {
            name: "tropical_forest",
            albedo: [0.04, 0.18, 0.02, 1.0],
            roughness: 0.92,
            metallic: 0.0,
            normal_strength: 0.9,
            emissive: [0.0; 3],
            ior: 1.45,
            subsurface: 0.35,
        }
    }
    pub fn boreal_forest() -> Self {
        Self {
            name: "boreal_forest",
            albedo: [0.05, 0.12, 0.04, 1.0],
            roughness: 0.90,
            metallic: 0.0,
            normal_strength: 0.85,
            emissive: [0.0; 3],
            ior: 1.45,
            subsurface: 0.25,
        }
    }
    pub fn glacier_ice() -> Self {
        Self {
            name: "glacier_ice",
            albedo: [0.70, 0.85, 0.92, 0.85],
            roughness: 0.1,
            metallic: 0.04,
            normal_strength: 0.5,
            emissive: [0.0; 3],
            ior: 1.309,
            subsurface: 0.9,
        }
    }
    pub fn clay_soil() -> Self {
        Self {
            name: "clay_soil",
            albedo: [0.42, 0.28, 0.18, 1.0],
            roughness: 0.95,
            metallic: metallic_from_iron(0.04),
            normal_strength: 0.7,
            emissive: [0.0; 3],
            ior: 1.50,
            subsurface: 0.1,
        }
    }
    pub fn limestone() -> Self {
        let m_ca = atomic_mass(20);
        let m_c = atomic_mass(6);
        let m_o = atomic_mass(8);
        let _ = m_ca + m_c + 3.0 * m_o;
        Self {
            name: "limestone",
            albedo: [0.82, 0.80, 0.74, 1.0],
            roughness: 0.80,
            metallic: 0.0,
            normal_strength: 0.9,
            emissive: [0.0; 3],
            ior: 1.486,
            subsurface: 0.05,
        }
    }
    pub fn coral_reef() -> Self {
        Self {
            name: "coral_reef",
            albedo: [0.55, 0.40, 0.30, 1.0],
            roughness: 0.75,
            metallic: 0.0,
            normal_strength: 1.0,
            emissive: [0.0; 3],
            ior: 1.486,
            subsurface: 0.15,
        }
    }

    pub fn all_earth() -> Vec<Self> {
        vec![
            Self::deep_ocean(),
            Self::shallow_ocean(),
            Self::grassland(),
            Self::desert_sand(),
            Self::fresh_snow(),
            Self::granite(),
            Self::basalt(),
            Self::volcanic_lava(),
            Self::tropical_forest(),
            Self::boreal_forest(),
            Self::glacier_ice(),
            Self::clay_soil(),
            Self::limestone(),
            Self::coral_reef(),
        ]
    }

    pub fn fresnel_r0(&self) -> f64 {
        ((self.ior as f64 - 1.0) / (self.ior as f64 + 1.0)).powi(2)
    }
}