From ec6969853067d6b0cec94cd6559a91ceacd3141e Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 16 Jul 2026 18:07:53 +0800 Subject: [PATCH] feat(planets): spawn_planet_system with deterministic ChaCha8Rng seeding --- src/render/plugin.rs | 9 ++++- src/scene/planets.rs | 82 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/src/render/plugin.rs b/src/render/plugin.rs index 9ef5352..1938029 100644 --- a/src/render/plugin.rs +++ b/src/render/plugin.rs @@ -96,6 +96,7 @@ impl Plugin for BlackHolePlugin { app.init_resource::() .init_resource::() .init_resource::() + .init_resource::() .add_plugins(Material2dPlugin::::default()) .add_plugins(Material2dPlugin::::default()) .add_plugins(Material2dPlugin::::default()) @@ -110,7 +111,7 @@ impl Plugin for BlackHolePlugin { // Runs in PreStartup, before setup_primary_egui_context_system. .add_systems(bevy::prelude::PreStartup, disable_egui_auto_context) .add_systems(Startup, spawn_fullscreen_quad) - .add_systems(Startup, crate::scene::planets::spawn_default_planet) + .add_systems(Startup, crate::scene::planets::spawn_planet_system) .add_systems( Update, ( @@ -120,6 +121,12 @@ impl Plugin for BlackHolePlugin { nudge_camera, ), ) + .add_systems(Update, crate::scene::planets::spawn_planet_system) + .add_systems( + Update, + crate::scene::planets::orbit_system + .before(crate::scene::planets::upload_planets), + ) .add_systems(Update, crate::scene::planets::upload_planets) .add_systems(Update, rebuild_bloom) // bevy_egui 0.41 requires UI systems to run inside the egui context diff --git a/src/scene/planets.rs b/src/scene/planets.rs index 0ea35fa..7ff64df 100644 --- a/src/scene/planets.rs +++ b/src/scene/planets.rs @@ -2,6 +2,8 @@ use std::f32::consts::{PI, TAU}; use bevy::prelude::*; use bevy::render::storage::ShaderBuffer; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; use crate::render::material::{SphereData, MAX_PLANETS}; @@ -27,6 +29,10 @@ pub struct OrbitParams { pub phase: f32, } +/// UI 改了种子/count/k 时置位, spawn_planet_system 检测到就重生整个行星系统. +#[derive(Resource, Default)] +pub struct PlanetSystemDirty(pub bool); + /// 由轨道根数 + 当前 (模拟)时间 + 自旋, 计算行星世界空间位置. /// 纯函数: 无 Bevy 依赖, 可独立测试. /// @@ -134,14 +140,74 @@ pub fn upload_planets( } } -/// Spawns a default test planet behind/above the hole so lensing is visible. -pub fn spawn_default_planet(mut commands: Commands) { - commands.spawn(Planet { - center: Vec3::new(0.0, 2.0, -25.0), - radius: 2.0, - color: Vec3::new(0.3, 0.5, 1.0), - emissive: false, - }); +/// (重)生成行星系统. 检测 PlanetSystemDirty: 若置位, 先 despawn 所有现有 +/// (Planet, OrbitParams), 再用 ChaCha8Rng + params.planet_seed 重新随机生成. +/// 确定性种子 → 同种子给同布局, 方便调试/截图/测试. +/// +/// 同时注册在 Startup 和 Update: Startup 首帧靠 "existing 为空" 门控首次生成; +/// Update 路径靠 dirty flag 门控重生. 不会每帧重建. +pub fn spawn_planet_system( + mut commands: Commands, + params: Res, + mut dirty: ResMut, + existing: Query>, +) { + // 只在 dirty, 或现有行星为零 (首帧/Startup) 时重生. + if !dirty.0 && !existing.is_empty() { + return; + } + // despawn 现有行星 + for entity in &existing { + commands.entity(entity).despawn(); + } + dirty.0 = false; + + if !params.planets_enabled { + return; + } + + let mut rng = ChaCha8Rng::seed_from_u64(params.planet_seed as u64); + let max = crate::render::material::MAX_PLANETS as u32; + for _ in 0..params.planet_count_target.min(max) { + let inclination = rng.gen_range(0.0..PI); + let longitude = rng.gen_range(0.0..TAU); + let phase = rng.gen_range(0.0..TAU); + let radius_factor = rng.gen_range(2.0..4.0); + // 颜色: 暖色行星 (橙/红/黄系), 避开蓝色 (易与背景星混淆) + let hue = rng.gen_range(0.02..0.13); + let color = hsv_to_rgb(hue, rng.gen_range(0.5..0.9), rng.gen_range(0.7..1.0)); + commands.spawn(( + OrbitParams { + radius_factor, + inclination, + longitude_of_node: longitude, + phase, + }, + Planet { + center: Vec3::ZERO, // 首帧由 orbit_system 填 + radius: rng.gen_range(0.8..1.6), + color, + emissive: false, + }, + )); + } +} + +/// HSV → RGB (h,s,v ∈ [0,1]). 行星颜色用. +fn hsv_to_rgb(h: f32, s: f32, v: f32) -> Vec3 { + let i = (h * 6.0).floor() as i32 % 6; + let f = h * 6.0 - (h * 6.0).floor(); + let p = v * (1.0 - s); + let q = v * (1.0 - f * s); + let t = v * (1.0 - (1.0 - f) * s); + match i { + 0 => Vec3::new(v, t, p), + 1 => Vec3::new(q, v, p), + 2 => Vec3::new(p, v, t), + 3 => Vec3::new(p, q, v), + 4 => Vec3::new(t, p, v), + _ => Vec3::new(v, p, q), + } } #[cfg(test)]