class_name WaterSurfaceMotion extends MeshInstance3D const DEFAULT_AMPLITUDE: float = 0.055 const DEFAULT_CYCLE_SECONDS: float = 7.0 const DEFAULT_SECONDARY_SWELL: float = 0.18 const DEFAULT_PHASE_OFFSET: float = 0.0 @export_range(0.0, 0.2, 0.005, "suffix:m") var amplitude: float = ( DEFAULT_AMPLITUDE ) @export_range(1.0, 30.0, 0.1, "suffix:s") var cycle_seconds: float = ( DEFAULT_CYCLE_SECONDS ) @export_range(0.0, 0.5, 0.01) var secondary_swell: float = ( DEFAULT_SECONDARY_SWELL ) @export_range(0.0, TAU, 0.01, "radians") var phase_offset: float = 0.0 var _base_height: float func _ready() -> void: _base_height = position.y set_process(amplitude > 0.0) func _process(_delta: float) -> void: position.y = _base_height + calculate_height_offset( _current_time_seconds(), amplitude, cycle_seconds, secondary_swell, phase_offset, ) static func get_default_height_offset() -> float: return calculate_height_offset( _current_time_seconds(), DEFAULT_AMPLITUDE, DEFAULT_CYCLE_SECONDS, DEFAULT_SECONDARY_SWELL, DEFAULT_PHASE_OFFSET, ) static func calculate_height_offset( time_seconds: float, motion_amplitude: float, motion_cycle_seconds: float, motion_secondary_swell: float, motion_phase_offset: float, ) -> float: var safe_cycle := maxf(motion_cycle_seconds, 0.001) var phase := ( fposmod(time_seconds, safe_cycle) / safe_cycle * TAU + motion_phase_offset ) var swell := ( sin(phase) + sin(phase * 2.0 + 1.1) * motion_secondary_swell ) / (1.0 + motion_secondary_swell) return swell * motion_amplitude static func _current_time_seconds() -> float: return float(Time.get_ticks_msec()) * 0.001