1111#include " vsg_gui_component.h"
1212#include " vsg_lighting.h"
1313#include " vsg_materials.h"
14+ #include " vsg_radiation_surface.h"
1415#include " vsg_water_surface.h"
1516
17+ #include < hydroc/waves/regular_wave.h>
18+ #include < hydroc/waves/irregular_wave.h>
19+
1620#include < iostream>
1721#include < memory>
1822
@@ -124,49 +128,35 @@ void GUIImplVSG::EnsureWaterSurface() {
124128 return ;
125129 }
126130
127- // Check if we have a valid wave model (not NoWave).
128- bool has_waves = wave_model_ && wave_model_->GetWaveMode () != WaveMode::noWaveCIC;
129-
130131 // If already initialized for this visual system, nothing to do.
131132 if (animated_water_->IsInitializedFor (pVis.get ())) {
132133 return ;
133134 }
134135
135136 if constexpr (kDebugWaveSurface ) {
136- std::cout << " [WaterSurface] EnsureWaterSurface: has_waves= " << has_waves
137- << " kForceWaterSurface =" << kForceWaterSurface << std::endl;
137+ bool has_waves = wave_model_ && wave_model_-> GetWaveMode () != WaveMode::noWaveCIC;
138+ std::cout << " [WaterSurface] EnsureWaterSurface: has_waves =" << has_waves << std::endl;
138139 }
139140
140- // Handle animated or static water surface.
141- if (has_waves || kForceWaterSurface ) {
142- // Initialize animated water surface directly in VSG scene.
143- // Must be done after pVis->Initialize() which happens in Init().
144- // Use viewer_settings_ resolution if available.
145- int resolution = (viewer_settings_) ? viewer_settings_->grid_resolution : 0 ;
146- animated_water_->Initialize (pVis.get (), resolution);
147-
148- if (animated_water_->IsInitialized ()) {
149- // Print status: wave pointer status.
150- std::cout << " [WaterSurface] wave_ptr=" << (wave_model_ ? " ok" : " null" );
151- if (wave_model_) {
152- std::cout << " mode=" << static_cast <int >(wave_model_->GetWaveMode ());
153- }
154- std::cout << " " << animated_water_->GetStatusString () << std::endl;
155-
156- // Initial update at current system time.
157- double t = system_->GetChTime ();
158- animated_water_->Update (wave_model_, t);
159- } else {
160- if constexpr (kDebugWaveSurface ) {
161- std::cout << " [WaterSurface] Initialize() failed!" << std::endl;
162- }
141+ // Always create animated water surface (supports waves + radiation viz).
142+ // Static plane fallback removed - animated water handles all cases.
143+ int resolution = (viewer_settings_) ? viewer_settings_->grid_resolution : 0 ;
144+ animated_water_->Initialize (pVis.get (), resolution);
145+
146+ if (animated_water_->IsInitialized ()) {
147+ std::cout << " [WaterSurface] wave_ptr=" << (wave_model_ ? " ok" : " null" );
148+ if (wave_model_) {
149+ std::cout << " mode=" << static_cast <int >(wave_model_->GetWaveMode ());
150+ }
151+ std::cout << " " << animated_water_->GetStatusString () << std::endl;
152+
153+ // Initial update at current system time.
154+ double t = system_->GetChTime ();
155+ animated_water_->Update (wave_model_, t);
156+ } else {
157+ if constexpr (kDebugWaveSurface ) {
158+ std::cout << " [WaterSurface] Initialize() failed!" << std::endl;
163159 }
164- } else if (!water_surface_created_) {
165- // No waves and not forcing: add static water plane.
166- auto water_plane = CreateStaticWaterPlane ();
167- system_->AddBody (water_plane);
168- water_surface_created_ = true ;
169- std::cout << " [WaterSurface] static plane created (no wave model)" << std::endl;
170160 }
171161}
172162
@@ -194,6 +184,14 @@ bool GUIImplVSG::IsRunning(double timestep) {
194184 // VSG vertex buffers are marked dirty() in Update() for GPU re-upload.
195185 if (animated_water_ && animated_water_->IsInitialized ()) {
196186 double t = system_ ? system_->GetChTime () : 0.0 ;
187+
188+ // Update radiation source body state if radiation viz is enabled.
189+ // Chooses the first non-water body in the system as the source.
190+ // NOTE: This is visualization-only and does NOT affect physics.
191+ if (viewer_settings_ && viewer_settings_->show_radiation_viz && system_) {
192+ UpdateRadiationSourceBody (t);
193+ }
194+
197195 // Update() handles null wave_model_ gracefully (keeps surface flat).
198196 // Pass viewer_settings_ for scale multiplier and throttle.
199197 animated_water_->Update (wave_model_, t, viewer_settings_.get ());
@@ -206,5 +204,95 @@ bool GUIImplVSG::IsRunning(double timestep) {
206204 return true ;
207205}
208206
207+ void GUIImplVSG::UpdateRadiationSourceBody (double t) {
208+ if (!animated_water_ || !system_) {
209+ return ;
210+ }
211+
212+ // Update global params.
213+ if (viewer_settings_) {
214+ RadiationSurfaceViz::Params rad_params;
215+ rad_params.visual_scale = static_cast <double >(viewer_settings_->radiation_visual_scale );
216+ rad_params.wave_period = 8.0 ; // Default; should match body oscillation frequency
217+
218+ // Get wave properties from wave model if available.
219+ if (wave_model_) {
220+ // Water depth and gravity (available in all wave models).
221+ if (wave_model_->water_depth_ > 0.0 ) {
222+ rad_params.water_depth = wave_model_->water_depth_ ;
223+ }
224+ rad_params.gravity = wave_model_->g_ ;
225+
226+ // Wave period from RegularWave.
227+ if (wave_model_->GetWaveMode () == WaveMode::regular) {
228+ auto * reg_wave = dynamic_cast <RegularWave*>(wave_model_.get ());
229+ if (reg_wave && reg_wave->regular_wave_omega_ > 0.0 ) {
230+ constexpr double kTwoPi = 2.0 * 3.14159265358979323846 ;
231+ rad_params.wave_period = kTwoPi / reg_wave->regular_wave_omega_ ;
232+ }
233+ }
234+ // Peak period from IrregularWaves (JONSWAP).
235+ else if (wave_model_->GetWaveMode () == WaveMode::irregular) {
236+ auto * irreg_wave = dynamic_cast <IrregularWaves*>(wave_model_.get ());
237+ if (irreg_wave) {
238+ // Note: IrregularWaves doesn't expose params_ directly.
239+ // For now, keep default. Could add a GetPeakPeriod() accessor.
240+ }
241+ }
242+ // NoWave mode (decay tests): wave_period should ideally be set to the
243+ // body's natural period T_n ≈ 2π√((M+A_∞)/K_hs). Using default 8s as fallback.
244+ }
245+
246+ animated_water_->GetRadiationViz ().SetParams (rad_params);
247+ }
248+
249+ // Log source bodies once.
250+ static bool logged_sources = false ;
251+
252+ // Iterate over ALL moving bodies and update their radiation state.
253+ for (auto & body : system_->GetBodies ()) {
254+ if (!body) {
255+ continue ;
256+ }
257+
258+ const std::string& name = body->GetName ();
259+
260+ // Skip water surfaces and ground bodies.
261+ if (name == " water_surface" || name == " animated_water_surface" ||
262+ name == " ground" || name == " floor" || name.empty ()) {
263+ continue ;
264+ }
265+
266+ // Skip fixed bodies.
267+ if (body->IsFixed ()) {
268+ continue ;
269+ }
270+
271+ // Get body motion state.
272+ const chrono::ChVector3d pos = body->GetPos ();
273+ const chrono::ChVector3d vel = body->GetPosDt ();
274+ const chrono::ChVector3d ang_vel = body->GetAngVelLocal ();
275+
276+ // Estimate body radius from AABB (rough approximation).
277+ double radius = 5.0 ; // Default
278+ auto aabb = body->GetTotalAABB ();
279+ if (aabb.max .x () > aabb.min .x ()) {
280+ double dx = aabb.max .x () - aabb.min .x ();
281+ double dy = aabb.max .y () - aabb.min .y ();
282+ radius = std::max (dx, dy) / 2.0 ;
283+ radius = std::max (radius, 1.0 ); // Minimum 1m
284+ }
285+
286+ // Update radiation viz for this body.
287+ animated_water_->GetRadiationViz ().SetSourceState (name, pos, vel, ang_vel, radius, t);
288+
289+ if (!logged_sources) {
290+ std::cout << " [RadiationViz] Source: " << name << " (r=" << radius << " m)" << std::endl;
291+ }
292+ }
293+
294+ logged_sources = true ;
295+ }
296+
209297} // namespace gui
210298} // namespace hydroc
0 commit comments