Last active
December 13, 2025 03:12
-
-
Save Kielan/950923c538785c3ef2b07242bbea977f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use bevy::prelude::*; | |
| #[derive(Resource, Clone)] | |
| pub struct PlanetData { | |
| pub lod_focus: Vec3, | |
| // add radius, noise params, max lod, etc | |
| } | |
| #[derive(Component)] | |
| pub struct Planet; | |
| #[derive(Component)] | |
| pub struct PlanetMeshFace { | |
| pub normal: Vec3, | |
| pub name: &'static str, | |
| } | |
| const PLANET_FACES: [(Vec3, &str); 6] = [ | |
| (Vec3::Y, "Top"), | |
| (-Vec3::Y, "Bot"), | |
| (-Vec3::X, "Left"), | |
| (Vec3::X, "Right"), | |
| (-Vec3::Z, "Back"), | |
| (Vec3::Z, "Front"), | |
| ]; | |
| pub fn spawn_planet( | |
| mut commands: Commands, | |
| ) { | |
| commands | |
| .spawn(( | |
| Planet, | |
| Transform::default(), | |
| GlobalTransform::default(), | |
| )) | |
| .with_children(|parent| { | |
| for (normal, name) in PLANET_FACES { | |
| parent.spawn(( | |
| PlanetMeshFace { normal, name }, | |
| Transform::default(), | |
| GlobalTransform::default(), | |
| )); | |
| } | |
| }); | |
| } | |
| pub fn regenerate_faces_on_planet_data_change( | |
| planet_data: Res<PlanetData>, | |
| mut faces: Query<&PlanetMeshFace>, | |
| ) { | |
| if !planet_data.is_changed() { | |
| return; | |
| } | |
| for face in &mut faces { | |
| regenerate_mesh(face, &planet_data); | |
| } | |
| } | |
| fn regenerate_mesh(face: &PlanetMeshFace, planet_data: &PlanetData) { | |
| // Equivalent of: | |
| // child._regenerate_mesh(planet_data) | |
| // Typical steps: | |
| // - Build quadtree | |
| // - Project cube → sphere | |
| // - LOD based on planet_data.lod_focus | |
| // - Write Mesh | |
| } | |
| #[derive(Component)] | |
| pub struct Player; | |
| pub fn update_lod_focus_from_player( | |
| player: Query<&GlobalTransform, With<Player>>, | |
| mut planet_data: ResMut<PlanetData>, | |
| ) { | |
| if let Ok(player_transform) = player.get_single() { | |
| planet_data.lod_focus = player_transform.translation(); | |
| } | |
| } | |
| pub struct PlanetPlugin; | |
| impl Plugin for PlanetPlugin { | |
| fn build(&self, app: &mut App) { | |
| app | |
| .insert_resource(PlanetData { | |
| lod_focus: Vec3::ZERO, | |
| }) | |
| .add_startup_system(spawn_planet) | |
| .add_system(update_lod_focus_from_player) | |
| .add_system(regenerate_faces_on_planet_data_change); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #[derive(Clone)] | |
| pub struct LodLevel { | |
| pub distance: f32, | |
| pub resolution: u32, | |
| } | |
| pub trait PlanetNoise: Send + Sync { | |
| fn get_noise_3d(&self, p: Vec3) -> f32; | |
| fn is_base_layer(&self) -> bool; | |
| fn amplitude(&self) -> f32; | |
| fn min_height(&self) -> f32; | |
| } | |
| use bevy::prelude::*; | |
| #[derive(Resource)] | |
| pub struct PlanetData { | |
| pub radius: f32, | |
| pub lod_focus: Vec3, | |
| pub max_lod: usize, | |
| pub lod_levels: Vec<LodLevel>, | |
| /// Noise layers | |
| pub planet_noise: Vec<Box<dyn PlanetNoise>>, | |
| pub min_height: f32, | |
| pub max_height: f32, | |
| /// Gradient texture (optional) | |
| pub planet_color: Option<Handle<Image>>, | |
| } | |
| impl Default for PlanetData { | |
| fn default() -> Self { | |
| let lod_levels = vec![ | |
| LodLevel { distance: 500.0, resolution: 2 }, | |
| LodLevel { distance: 50.0, resolution: 2 }, | |
| LodLevel { distance: 25.0, resolution: 3 }, | |
| LodLevel { distance: 10.0, resolution: 4 }, | |
| LodLevel { distance: 1.0, resolution: 10 }, | |
| ]; | |
| Self { | |
| radius: 1.0, | |
| lod_focus: Vec3::ZERO, | |
| max_lod: lod_levels.len() - 1, | |
| lod_levels, | |
| planet_noise: Vec::new(), | |
| min_height: 9999.0, | |
| max_height: 0.0, | |
| planet_color: None, | |
| } | |
| } | |
| } | |
| impl PlanetData { | |
| pub fn point_on_planet(&self, point_on_sphere: Vec3) -> Vec3 { | |
| let mut elevation = 0.0; | |
| let mut base_layer_mask = 0.0; | |
| if !self.planet_noise.is_empty() { | |
| // Base layers | |
| for n in &self.planet_noise { | |
| if n.is_base_layer() { | |
| let mut level_base_elevation = | |
| n.get_noise_3d(point_on_sphere * 100.0); | |
| level_base_elevation = | |
| (level_base_elevation + 1.0) / 2.0 * n.amplitude(); | |
| level_base_elevation = | |
| (level_base_elevation - n.min_height()).max(0.0); | |
| base_layer_mask += level_base_elevation; | |
| } | |
| } | |
| // All layers | |
| for n in &self.planet_noise { | |
| let mut level_elevation = | |
| n.get_noise_3d(point_on_sphere * 100.0); | |
| level_elevation = | |
| (level_elevation + 1.0) / 2.0 * n.amplitude(); | |
| level_elevation = | |
| (level_elevation - n.min_height()).max(0.0) | |
| * base_layer_mask; | |
| elevation += level_elevation; | |
| } | |
| } | |
| point_on_sphere * self.radius * (elevation + 1.0) | |
| } | |
| } | |
| pub fn planet_data_changed( | |
| planet_data: Res<PlanetData>, | |
| ) { | |
| if planet_data.is_changed() { | |
| // Regenerate meshes / LODs | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment