celestialsim/
water_runtime.rs1use godot::classes::fast_noise_lite::NoiseType;
12use godot::classes::{
13 FastNoiseLite, MeshInstance3D, Node3D, NoiseTexture2D, Shader, ShaderMaterial, SphereMesh,
14 Texture2D,
15};
16use godot::prelude::*;
17
18use crate::water::proxy_radius;
19
20const WATER_SHADER: &str = "res://addons/celestialsim/water_surface.gdshader";
21
22#[derive(Clone, Copy)]
25pub struct WaterParams {
26 pub deep_color: Color,
27 pub shallow_color: Color,
28 pub wave_strength: f32,
29 pub wave_scale: f32,
30 pub wave_speed: f32,
31 pub underwater_color: Color,
32 pub underwater_density: f32,
33 pub sun_dir: Vector3,
36}
37
38pub struct WaterRuntime {
39 mmi: Gd<MeshInstance3D>,
40 mesh: Gd<SphereMesh>,
41 material: Gd<ShaderMaterial>,
42 proxy_r: f32,
45}
46
47impl WaterRuntime {
48 fn make_wave_normal_tex(seed: i32, frequency: f32) -> Gd<Texture2D> {
52 let mut noise = FastNoiseLite::new_gd();
53 noise.set_noise_type(NoiseType::SIMPLEX_SMOOTH);
54 noise.set_frequency(frequency);
55 noise.set_seed(seed);
56 let mut tex = NoiseTexture2D::new_gd();
57 tex.set_width(256);
58 tex.set_height(256);
59 tex.set_seamless(true);
60 tex.set_as_normal_map(true);
61 tex.set_bump_strength(4.0);
62 tex.set_noise(&noise);
63 tex.upcast()
64 }
65
66 pub fn create(parent: &mut Gd<Node3D>) -> Self {
68 let shader = godot::tools::load::<Shader>(WATER_SHADER);
69 let mut material = ShaderMaterial::new_gd();
70 material.set_shader(&shader);
71 let wave_a = Self::make_wave_normal_tex(1337, 0.015);
74 let wave_b = Self::make_wave_normal_tex(9001, 0.026);
75 material.set_shader_parameter("wave_normal_a", &wave_a.to_variant());
76 material.set_shader_parameter("wave_normal_b", &wave_b.to_variant());
77
78 let mut mesh = SphereMesh::new_gd();
79 mesh.set_radial_segments(24);
82 mesh.set_rings(16);
83 mesh.set_material(&material);
84
85 let mut mmi = MeshInstance3D::new_alloc();
86 mmi.set_mesh(&mesh);
87 mmi.set_cast_shadows_setting(godot::classes::geometry_instance_3d::ShadowCastingSetting::OFF);
90 parent.add_child(&mmi);
91
92 Self { mmi, mesh, material, proxy_r: 0.0 }
93 }
94
95 pub fn update(&mut self, center: Vector3, water_radius: f32, p: WaterParams, visible: bool) {
99 self.mmi.set_visible(visible);
100 if !visible {
101 return;
102 }
103 let pr = proxy_radius(water_radius);
104 if (pr - self.proxy_r).abs() > self.proxy_r.max(1.0) * 1e-3 {
105 self.mesh.set_radius(pr);
106 self.mesh.set_height(pr * 2.0);
107 self.proxy_r = pr;
108 }
109 self.mmi.set_global_position(center);
110
111 let m = &mut self.material;
112 m.set_shader_parameter("planet_center", ¢er.to_variant());
113 m.set_shader_parameter("water_radius", &water_radius.to_variant());
114 m.set_shader_parameter("deep_color", &p.deep_color.to_variant());
115 m.set_shader_parameter("shallow_color", &p.shallow_color.to_variant());
116 m.set_shader_parameter("wave_strength", &p.wave_strength.to_variant());
117 m.set_shader_parameter("wave_scale", &p.wave_scale.to_variant());
118 m.set_shader_parameter("wave_speed", &p.wave_speed.to_variant());
119 m.set_shader_parameter("underwater_color", &p.underwater_color.to_variant());
120 m.set_shader_parameter("underwater_density", &p.underwater_density.to_variant());
121 m.set_shader_parameter("sun_direction", &p.sun_dir.to_variant());
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 #[test]
128 fn proxy_margin_is_applied() {
129 assert!(crate::water::PROXY_MARGIN > 1.0);
131 }
132}