From 56afb803580ad274160ca878cd8aee87cc1f51fa Mon Sep 17 00:00:00 2001 From: Frederic Bouvier Date: Tue, 3 Jan 2012 20:21:34 +0100 Subject: [PATCH 1/2] Local Weather 1.4 by Thorsten Renk : fixes lots of graphical errors and transparency issues and provides a consistent GUI. --- Effects/cloud-static.eff | 64 + Effects/rain-layer.eff | 11 +- Nasal/local_weather.nas | 4761 +++++++++++++++++ Nasal/local_weather/cloud_definitions.nas | 135 +- Nasal/local_weather/compat_layer.nas | 70 +- Nasal/local_weather/local_weather.nas | 270 +- Nasal/local_weather/weather_dynamics.nas | 38 +- .../local_weather/weather_tile_management.nas | 21 +- Nasal/local_weather/weather_tiles.nas | 358 +- Shaders/cloud-static.frag | 10 + Shaders/cloud-static.vert | 55 + Shaders/rain-layer.vert | 4 +- gui/dialogs/local_weather_tiles.xml | 309 +- 13 files changed, 5825 insertions(+), 281 deletions(-) create mode 100644 Effects/cloud-static.eff create mode 100644 Nasal/local_weather.nas create mode 100644 Shaders/cloud-static.frag create mode 100644 Shaders/cloud-static.vert diff --git a/Effects/cloud-static.eff b/Effects/cloud-static.eff new file mode 100644 index 000000000..52870a571 --- /dev/null +++ b/Effects/cloud-static.eff @@ -0,0 +1,64 @@ + + + Effects/rain-layer + + + + + + + + /sim/rendering/clouds3d-enable + + 1.0 + + + + + + + true + + 0.5 0.5 0.5 1.0 + 0.5 0.5 0.5 1.0 + off + + + greater + 0.01 + + smooth + + src-alpha + one-minus-src-alpha + + + false + + + 9 + DepthSortedBin + + + 0 + texture[0]/type + texture[0]/image + texture[0]/filter + texture[0]/wrap-s + texture[0]/wrap-t + + + + Shaders/cloud-static.vert + Shaders/cloud-static.frag + + + baseTexture + sampler-2d + 0 + + true + + + diff --git a/Effects/rain-layer.eff b/Effects/rain-layer.eff index f0296c360..828237fb6 100644 --- a/Effects/rain-layer.eff +++ b/Effects/rain-layer.eff @@ -4,6 +4,9 @@ + + /sim/rendering/clouds3d-vis-range + @@ -36,7 +39,7 @@ false - 10 + 9 DepthSortedBin @@ -58,6 +61,12 @@ sampler-2d 0 + + range + float + range + + true diff --git a/Nasal/local_weather.nas b/Nasal/local_weather.nas new file mode 100644 index 000000000..afb649dd2 --- /dev/null +++ b/Nasal/local_weather.nas @@ -0,0 +1,4761 @@ + +######################################################## +# routines to set up, transform and manage local weather +# Thorsten Renk, June 2011 +# thermal model by Patrice Poly, April 2010 +######################################################## + +# function purpose +# +# calc_geo to compute the latitude to meter conversion +# calc_d_sq to compute a distance square in local Cartesian approximation +# effect_volume_loop to check if the aircraft has entered an effect volume +# assemble_effect_array to store the size of the effect volume array +# add_vectors to add two vectors in polar coordinates +# wind_altitude_interpolation to interpolate aloft winds in altitude +# wind_interpolation to interpolate aloft winds in altitude and position +# get_slowdown_fraction to compute the effect of boundary layer wind slowdown +# interpolation_loop to continuously interpolate weather parameters between stations +# thermal_lift_start to start the detailed thermal model +# thermal_lift_loop to manage the detailed thermal lift model +# thermal_lift_stop to end the detailed thermal lift model +# wave_lift_start to start the detailed wave lift model +# wave_lift_loop to manage the detailed wave lift model +# wave_lift_stop to end the detailed wave lift model +# effect_volume_start to manage parameters when an effect volume is entered +# effect_volume_stop to manage parameters when an effect volume is left +# ts_factor (helper function for thermal lift model) +# tl_factor (helper function for thermal lift model) +# calcLift_max to calculate the maximal available thermal lift for given altitude +# calcLift to calculate the thermal lift at aircraft position +# calcWaveLift to calculate wave lift at aircraft position +# create_cloud_vec to place a single cloud into an array to be written later +# clear_all to remove all clouds, effect volumes and weather stations and stop loops +# create_detailed_cumulus_cloud to place multiple cloudlets into a box based on a size parameter +# create_cumulonimbus_cloud to place multiple cloudlets into a box +# create_cumulonimbus_cloud_rain to place multiple cloudlets into a box and add a rain layer beneath +# create_cumosys wrapper to place a convective cloud system based on terrain coverage +# cumulus_loop to place 25 Cumulus clouds each frame +# create_cumulus to place a convective cloud system based on terrain coverage +# recreate_cumulus to respawn convective clouds as part of the convective dynamics algorithm +# cumulus_exclusion_layer to create a layer with 'holes' left for thunderstorm placement +# create_rise_clouds to create a barrier cloud system +# create_streak to create a cloud streak +# create_undulatus to create an undulating cloud pattern +# create_layer to create a cloud layer with optional precipitation +# create_hollow_layer to create a cloud layer in a hollow cylinder (better for performance) +# create_cloudbox to create a sophisticated cumulus cloud with different textures (experimental) +# terrain_presampling_start to initialize terrain presampling +# terrain_presampling_loop to sample 25 terrain points per frame +# terrain_presampling to sample terrain elevation at a random point within specified area +# terrain_presampling_analysis to analyze terrain presampling results +# wave_detection_loop to detect if and where wave lift should be placed (currently unfinished) +# get_convective_altitude to determine the altitude at which a Cumulus cloud is placed +# manage presampling to take proper action when a presampling call has been finished +# set_wind_model_flag to convert the wind model string into an integer flag +# set_texture_mix to determine the texture mix between smooth and rough cloud appearance +# create_effect_volume to create an effect volume +# set_weather_station to specify a weather station for interpolation +# set_atmosphere_ipoint to specify an interpolation point for visibility, haze and shading in the atmosphere +# set_wind_ipoint to set an aloft wind interpolation point +# set_wind_ipoint_metar to set a wind interpolation point from available ground METAR info where aloft is modelled +# showDialog to pop up a dialog window +# readFlags to read configuration flags from the property tree into Nasal variables at startup +# streak_wrapper wrapper to execute streak from menu +# convection wrapper wrapper to execute convective clouds from menu +# barrier wrapper wrapper to execute barrier clouds from menu +# single_cloud_wrapper wrapper to create single cloud from menu +# layer wrapper wrapper to create layer from menu +# box wrapper wrapper to create a cloudbox (experimental) +# set_aloft wrapper wrapper to create aloft winds from menu +# set_tile to call a weather tile creation from menu +# startup to prepare the package at startup +# test to serve as a testbed for new functions + +# object purpose + +# weatherStation to store info about weather conditions +# atmopshereIpoint to store info about haze and light propagation in the atmosphere +# windIpoint to store an interpolation point of the windfield +# effectVolume to store effect volume info and provide methods to move and time-evolve effect volumes +# thermalLift to store thermal info and provide methods to move and time-evolve a thermal +# waveLift to store wave info + + + + +################################### +# geospatial helper functions +################################### + +var calc_geo = func(clat) { + +lon_to_m = math.cos(clat*math.pi/180.0) * lat_to_m; +m_to_lon = 1.0/lon_to_m; + +weather_dynamics.lon_to_m = lon_to_m; +weather_dynamics.m_to_lon = m_to_lon; + +} + + +var calc_d_sq = func (lat1, lon1, lat2, lon2) { + +var x = (lat1 - lat2) * lat_to_m; +var y = (lon1 - lon2) * lon_to_m; + +return (x*x + y*y); +} + + +################################### +# effect volume management loop +################################### + +var effect_volume_loop = func (index, n_active) { + + +if (local_weather_running_flag == 0) {return;} + +var n = 25; + + +var esize = n_effectVolumeArray; + +var viewpos = geo.aircraft_position(); +var active_counter = n_active; + +var i_max = index + n; +if (i_max > esize) {i_max = esize;} + +for (var i = index; i < i_max; i = i+1) + { + var e = effectVolumeArray[i]; + + var flag = 0; # default assumption is that we're not in the volume + + var ealt_min = e.alt_low * ft_to_m; + var ealt_max = e.alt_high * ft_to_m; + + + if ((viewpos.alt() > ealt_min) and (viewpos.alt() < ealt_max)) # we are in the correct alt range + { + # so we load geometry next + + var geometry = e.geometry; + var elat = e.lat; + var elon = e.lon; + var rx = e.r1; + + if (geometry == 1) # we have a cylinder + { + var d_sq = calc_d_sq(viewpos.lat(), viewpos.lon(), elat, elon); + if (d_sq < (rx*rx)) {flag =1;} + } + else if (geometry == 2) # we have an elliptic shape + { + # get orientation + + var ry = e.r2; + var phi = e.phi; + + phi = phi * math.pi/180.0; + + + # first get unrotated coordinates + var xx = (viewpos.lon() - elon) * lon_to_m; + var yy = (viewpos.lat() - elat) * lat_to_m; + + # then rotate to align with the shape + var x = xx * math.cos(phi) - yy * math.sin(phi); + var y = yy * math.cos(phi) + xx * math.sin(phi); + + # then check elliptic condition + if ((x*x)/(rx*rx) + (y*y)/(ry*ry) <1) {flag = 1;} + } + else if (geometry == 3) # we have a rectangular shape + { + # get orientation + + var ry = e.r2; + var phi = e.phi; + + phi = phi * math.pi/180.0; + # first get unrotated coordinates + var xx = (viewpos.lon() - elon) * lon_to_m; + var yy = (viewpos.lat() - elat) * lat_to_m; + # then rotate to align with the shape + var x = xx * math.cos(phi) - yy * math.sin(phi); + var y = yy * math.cos(phi) + xx * math.sin(phi); + # then check rectangle condition + if ((x>-rx) and (x-ry) and (y wind_altitude_array[8]) {var alt_wind = 0.99* wind_altitude_array[8];} +else {var alt_wind = altitude;} + +for (var i = 0; i<9; i=i+1) + {if (alt_wind < wind_altitude_array[i]) {break;}} + + +#var altNodeMin = w.getChild("altitude",i-1); +#var altNodeMax = w.getChild("altitude",i); + +#var vmin = altNodeMin.getNode("windspeed-kt").getValue(); +#var vmax = altNodeMax.getNode("windspeed-kt").getValue(); + +var vmin = w.alt[i-1].v; +var vmax = w.alt[i].v; + +#var dir_min = altNodeMin.getNode("wind-from-heading-deg").getValue(); +#var dir_max = altNodeMax.getNode("wind-from-heading-deg").getValue(); + +var dir_min = w.alt[i-1].d; +var dir_max = w.alt[i].d; + +var f = (alt_wind - wind_altitude_array[i-1])/(wind_altitude_array[i] - wind_altitude_array[i-1]); + +var res = add_vectors(dir_min, (1-f) * vmin, dir_max, f * vmax); + +return res; +} + + +################################### +# windfield spatial interpolation +################################### + +var wind_interpolation = func (lat, lon, alt) { + +var sum_norm = 0; +var sum_wind = [0,0]; + +var wsize = size(windIpointArray); + +for (var i = 0; i < wsize; i=i+1) { + + + var w = windIpointArray[i]; + + var wpos = geo.Coord.new(); + wpos.set_latlon(w.lat,w.lon,1000.0); + + var ppos = geo.Coord.new(); + ppos.set_latlon(lat,lon,1000.0); + + var d = ppos.distance_to(wpos); + if (d <100.0) {d = 100.0;} # to prevent singularity at zero + + sum_norm = sum_norm + (1./d) * w.weight; + + var res = wind_altitude_interpolation(alt,w); + + sum_wind = add_vectors(sum_wind[0], sum_wind[1], res[0], (res[1]/d) * w.weight); + + # gradually fade in the interpolation weight of newly added points to + # avoid sudden jumps + + if (w.weight < 1.0) {w.weight = w.weight + 0.02;} + + } + +sum_wind[1] = sum_wind[1] /sum_norm; + +return sum_wind; +} + + +################################### +# boundary layer computations +################################### + + +var get_slowdown_fraction = func { + +var tile_index = getprop(lw~"tiles/tile[4]/tile-index"); +var altitude_agl = getprop("/position/altitude-agl-ft"); +var altitude = getprop("/position/altitude-ft"); + + + +if (presampling_flag == 0) + { + var base_layer_thickness = 600.0; + var f_slow = 1.0/3.0; + } +else + { + var alt_median = alt_50_array[tile_index - 1]; + var alt_difference = alt_median - (altitude - altitude_agl); + var base_layer_thickness = 150.0; + + # get the boundary layer size dependent on terrain altitude above terrain median + + if (alt_difference > 0.0) # we're low and the boundary layer grows + {var boundary_alt = base_layer_thickness + 0.3 * alt_difference;} + else # the boundary layer shrinks + {var boundary_alt = base_layer_thickness + 0.1 * alt_difference;} + + if (boundary_alt < 50.0){boundary_alt = 50.0;} + if (boundary_alt > 3000.0) {boundary_alt = 3000.0;} + + # get the boundary effect as a function of bounday layer size + + var f_slow = 1.0 - (0.2 + 0.17 * math.ln(boundary_alt/base_layer_thickness)); + } + +if (debug_output_flag == 1) + { + #print("Boundary layer thickness: ",base_layer_thickness); + #print("Boundary layer slowdown: ", f_slow); + } +return f_slow; +} + + +################################### +# interpolation management loop +################################### + +var interpolation_loop = func { + +if (local_weather_running_flag == 0) {return;} + +var viewpos = geo.aircraft_position(); + + + +var vis_before = getprop(lwi~"visibility-m"); + +# if applicable, do some work for fps sampling + +if (fps_control_flag == 1) + { + fps_samples = fps_samples +1; + fps_sum = fps_sum + getprop("/sim/frame-rate"); + } + + +# determine at which distance we no longer keep an interpolation point, needs to be larger for METAR since points are more scarce + +if (metar_flag == 1) + {var distance_to_unload = 250000.0;} +else + {var distance_to_unload = 120000.0;} + +# if we can set environment without a reset, the loop can run a bit faster for smoother interpolation +# so determine the suitable timing + +if (compat_layer.features.can_disable_environment == 1) + {var interpolation_loop_time = 0.2; var vlimit = 1.01;} +else + {var interpolation_loop_time = 1.0; var vlimit = 1.05;} + + +# get an inverse distance weighted average from all defined weather stations + +var sum_alt = 0.0; +var sum_vis = 0.0; +var sum_T = 0.0; +var sum_p = 0.0; +var sum_D = 0.0; +var sum_norm = 0.0; + +var n_stations = size(weatherStationArray); + +for (var i = 0; i < n_stations; i=i+1) { + + s = weatherStationArray[i]; + + + var stpos = geo.Coord.new(); + stpos.set_latlon(s.lat,s.lon,0.0); + + var d = viewpos.distance_to(stpos); + if (d <100.0) {d = 100.0;} # to prevent singularity at zero + + sum_norm = sum_norm + 1./d * s.weight; + + sum_alt = sum_alt + (s.alt/d) * s.weight; + sum_vis = sum_vis + (s.vis/d) * s.weight; + sum_T = sum_T + (s.T/d) * s.weight; + sum_D = sum_D + (s.D/d) * s.weight; + sum_p = sum_p + (s.p/d) * s.weight; + + # gradually fade in the interpolation weight of newly added stations to + # avoid sudden jumps + + if (s.weight < 1.0) {s.weight = s.weight + 0.02;} + + # automatically delete stations out of range + # take care not to unload if weird values appear for a moment + # never unload if only one station left + if ((d > distance_to_unload) and (d < (distance_to_unload + 20000.0)) and (n_stations > 1)) + { + if (debug_output_flag == 1) + {print("Distance to weather station ", d, " m, unloading ...", i);} + weatherStationArray = weather_tile_management.delete_from_vector(weatherStationArray,i); + i = i-1; n_stations = n_stations -1; + } + } + +setprop(lwi~"station-number", i+1); + + +var ialt = sum_alt/sum_norm; +var vis = sum_vis/sum_norm; +var p = sum_p/sum_norm; +var D = sum_D/sum_norm + temperature_offset; +var T = sum_T/sum_norm + temperature_offset; + + +# get an inverse distance weighted average from all defined atmospheric condition points + +sum_norm = 0.0; +var sum_vis_aloft = 0.0; +var sum_vis_alt1 = 0.0; +var sum_vis_ovcst = 0.0; +var sum_ovcst = 0.0; +var sum_ovcst_alt_low = 0.0; +var sum_ovcst_alt_high = 0.0; +var sum_scatt = 0.0; +var sum_scatt_alt_low = 0.0; +var sum_scatt_alt_high = 0.0; + +var n_iPoints = size(atmosphereIpointArray); + +for (var i = 0; i < n_iPoints; i=i+1) { + + a = atmosphereIpointArray[i]; + + + var apos = geo.Coord.new(); + apos.set_latlon(a.lat,a.lon,0.0); + + var d = viewpos.distance_to(apos); + if (d <100.0) {d = 100.0;} # to prevent singularity at zero + + sum_norm = sum_norm + 1./d * a.weight; + sum_vis_aloft = sum_vis_aloft + (a.vis_aloft/d) * a.weight; + sum_vis_alt1 = sum_vis_alt1 + (a.vis_alt1/d) * a.weight; + sum_vis_ovcst = sum_vis_ovcst + (a.vis_ovcst/d) * a.weight; + sum_ovcst = sum_ovcst + (a.ovcst/d) * a.weight; + sum_ovcst_alt_low = sum_ovcst_alt_low + (a.ovcst_alt_low/d) * a.weight; + sum_ovcst_alt_high = sum_ovcst_alt_high + (a.ovcst_alt_high/d) * a.weight; + sum_scatt = sum_scatt + (a.scatt/d) * a.weight; + sum_scatt_alt_low = sum_scatt_alt_low + (a.scatt_alt_low/d) * a.weight; + sum_scatt_alt_high = sum_scatt_alt_high + (a.scatt_alt_high/d) * a.weight; + + # gradually fade in the interpolation weight of newly added stations to + # avoid sudden jumps + + if (a.weight < 1.0) {a.weight = a.weight + 0.02;} + + # automatically delete stations out of range + # take care not to unload if weird values appear for a moment + # never unload if only one station left + if ((d > distance_to_unload) and (d < (distance_to_unload + 20000.0)) and (n_iPoints > 1)) + { + if (debug_output_flag == 1) + {print("Distance to atmosphere interpolation point ", d, " m, unloading ...", i);} + atmosphereIpointArray = weather_tile_management.delete_from_vector(atmosphereIpointArray,i); + i = i-1; n_iPoints = n_iPoints -1; + } + } + +setprop(lwi~"atmosphere-ipoint-number", i+1); + + + +var vis_aloft = sum_vis_aloft/sum_norm; +var vis_alt1 = sum_vis_alt1/sum_norm; +var vis_ovcst = sum_vis_ovcst/sum_norm; +var ovcst_max = sum_ovcst/sum_norm; +var ovcst_alt_low = sum_ovcst_alt_low/sum_norm; +var ovcst_alt_high = sum_ovcst_alt_high/sum_norm; +var scatt_max = sum_scatt/sum_norm; +var scatt_alt_low = sum_scatt_alt_low/sum_norm; +var scatt_alt_high = sum_scatt_alt_high/sum_norm; + + + + +# altitude model for visibility - increase above the lowest inversion layer to simulate ground haze + +vis = vis * ground_haze_factor; + +var altitude = getprop("position/altitude-ft"); +current_mean_terrain_elevation = ialt; + +var alt1 = vis_alt1; +var alt2 = alt1 + 1500.0; + +setprop("/environment/ground-visibility-m",vis); +setprop("/environment/ground-haze-thickness-m",alt2 * ft_to_m); + +# compute the visibility gradients + +if (realistic_visibility_flag == 1) + { + vis_aloft = vis_aloft * 2.0; + vis_ovcst = vis_ovcst * 3.0; + } + +var inc1 = 0.1 * (vis_aloft - vis)/(vis_alt1 - ialt); +var inc2 = 0.9 * (vis_aloft - vis)/1500.0; +var inc3 = (vis_ovcst - vis_aloft)/(ovcst_alt_high - vis_alt1+1500); +var inc4 = 0.5; + + +if (realistic_visibility_flag == 1) + {inc4 = inc4 * 3.0;} + +# compute the visibility + +if (altitude < alt1) + {vis = vis + inc1 * altitude;} +else if (altitude < alt2) + { + vis = vis + inc1 * alt1 + inc2 * (altitude - alt1); + } +else if (altitude < ovcst_alt_high) + { + vis = vis + inc1 * alt1 + inc2 * (alt2-alt1) + inc3 * (altitude - alt2); + } +else if (altitude > ovcst_alt_high) + { + vis = vis + inc1 * alt1 + inc2 * (alt2-alt1) + inc3 * (ovcst_alt_high - alt2) + inc4 * (altitude - ovcst_alt_high); + } + +# limit visibility (otherwise memory consumption may be very bad...) + +if (vis > max_vis_range) + {vis = max_vis_range;} + +# determine scattering shader parameters if scattering shader is on + +if (scattering_shader_flag == 1) + { + var rayleigh = 0.0003 ; + var mie = 0.003; + var density = 0.3; + + + + if (altitude < 30000.0) + { + rayleigh = 0.0004 - altitude/30000.0 * 0.0001; + mie = 0.004 - altitude/30000.0 * 0.001; + } + else if (altitude < 60000.0) + { + rayleigh = 0.0003 - (altitude-30000.0)/30000.0 * 0.0001; + mie = 0.003 - (altitude-30000.0)/30000.0 * 0.001; + } + else if (altitude < 85000.0) + { + rayleigh = 0.0002 - (altitude-60000.0)/25000.0 * 0.0001; + mie = 0.002; + } + else + {rayleigh = 0.0001; mie = 0.002;} + } +# otherwise compute normal skydome shader parameters + + + +# compute the horizon shading + +if (altitude < scatt_alt_low) + { + var scatt = scatt_max; + } +else if (altitude < scatt_alt_high) + { + var scatt = scatt_max + (0.95 - scatt_max) * (altitude - scatt_alt_low)/(scatt_alt_high-scatt_alt_low); + } +else + {var scatt = 0.95;} + + +# compute the overcast haze + +if (altitude < ovcst_alt_low) + { + var ovcst = ovcst_max; + } +else if (altitude < ovcst_alt_high) + { + var ovcst = ovcst_max - ovcst_max * (altitude - ovcst_alt_low)/(ovcst_alt_high-ovcst_alt_low); + } +else + {var ovcst = 0.0;} + + + + +# limit relative changes of the visibility, will make for gradual transitions + +if (vis/vis_before > vlimit) + {vis = vlimit * vis_before;} +else if (vis/vis_before < (2.0-vlimit)) + {vis = (2.0-vlimit) * vis_before;} + + + + +# write all properties into the weather interpolation record in the property tree + +setprop(lwi~"mean-terrain-altitude-ft",ialt); +if (vis > 0.0) {setprop(lwi~"visibility-m",vis);} # a redundancy check +setprop(lwi~"temperature-degc",T); +setprop(lwi~"dewpoint-degc",D); +if (p > 10.0) {setprop(lwi~"pressure-sea-level-inhg",p);} +setprop(lwi~"turbulence",0.0); + + +if (scattering_shader_flag == 1) + { + local_weather.setSkydomeShader(rayleigh, mie, density); + } + +local_weather.setScattering(scatt); +local_weather.setOvercast(ovcst); + + +# now check if an effect volume writes the property and set only if not + +flag = getprop("local-weather/effect-volumes/number-active-vis"); + +if ((flag ==0) and (vis > 0.0) and (getprop(lw~"lift-loop-flag") == 0) and (compat_layer.smooth_visibility_loop_flag == 0)) + { + setprop(lw~"current/visibility-m",vis); + compat_layer.setVisibility(vis); + } + +flag = getprop("local-weather/effect-volumes/number-active-turb"); + +if ((flag ==0)) + { + setprop(lw~"current/turbulence",0.0); + compat_layer.setTurbulence(0.0); + } + + +flag = getprop("local-weather/effect-volumes/number-active-lift"); + +if (flag ==0) + { + setprop(lw~"current/thermal-lift",0.0); + } + +# no need to check for these, as they are not modelled in effect volumes + +setprop(lw~"current/temperature-degc",T); +compat_layer.setTemperature(T); + +setprop(lw~"current/dewpoint-degc", D); +compat_layer.setDewpoint(D); + +if (p>0.0) + { + setprop(lw~"current/pressure-sea-level-inhg",p); + compat_layer.setPressure(p); + } + + +# now determine the local wind + + +var tile_index = getprop(lw~"tiles/tile[4]/tile-index"); + +if (wind_model_flag ==1) # constant + { + var winddir = weather_dynamics.tile_wind_direction[0]; + var windspeed = weather_dynamics.tile_wind_speed[0]; + + wind.cloudlayer = [winddir,windspeed]; + + } +else if (wind_model_flag ==2) # constant in tile + { + var winddir = weather_dynamics.tile_wind_direction[tile_index-1]; + var windspeed = weather_dynamics.tile_wind_speed[tile_index-1]; + + wind.cloudlayer = [winddir, windspeed]; + + } +else if (wind_model_flag ==3) # aloft interpolated, constant in tiles + { + var w = windIpointArray[0]; + var res = wind_altitude_interpolation(altitude,w); + var winddir = res[0]; + var windspeed = res[1]; + + wind.cloudlayer = wind_altitude_interpolation(0.0,w); + + } +else if (wind_model_flag == 5) # aloft waypoint interpolated + { + var res = wind_interpolation(viewpos.lat(), viewpos.lon(), altitude); + + var winddir = res[0]; + var windspeed = res[1]; + + wind.cloudlayer = wind_interpolation(viewpos.lat(), viewpos.lon(), 0.0); + } + + +wind.surface = [wind.cloudlayer[0], wind.cloudlayer[1] * get_slowdown_fraction()]; + +# now do the boundary layer computations + +var altitude_agl = getprop("/position/altitude-agl-ft"); + + +if (presampling_flag == 0) + { + var boundary_alt = 600.0; + var windspeed_ground = windspeed/3.0; + + var f_min = 2.0/3.0; + + if (altitude_agl < boundary_alt) + {var windspeed_current = windspeed_ground + 2.0 * windspeed_ground * (altitude_agl/boundary_alt);} + else + {var windspeed_current = windspeed;} + } +else + { + var alt_median = alt_50_array[tile_index - 1]; + var alt_difference = alt_median - (altitude - altitude_agl); + var base_layer_thickness = 150.0; + + # get the boundary layer size dependent on terrain altitude above terrain median + + if (alt_difference > 0.0) # we're low and the boundary layer grows + {var boundary_alt = base_layer_thickness + 0.3 * alt_difference;} + else # the boundary layer shrinks + {var boundary_alt = base_layer_thickness + 0.1 * alt_difference;} + + if (boundary_alt < 50.0){boundary_alt = 50.0;} + if (boundary_alt > 3000.0) {boundary_alt = 3000.0;} + + # get the boundary effect as a function of bounday layer size + + var f_min = 0.2 + 0.17 * math.ln(boundary_alt/base_layer_thickness); + + + if (altitude_agl < boundary_alt) + { + var windspeed_current = (1-f_min) * windspeed + f_min * windspeed * (altitude_agl/boundary_alt); + } + else + {var windspeed_current = windspeed;} + + } + + +var windspeed_ground = (1.0-f_min) * windspeed; + + +# set the wind hash before gusts, it represents mean wind + +wind.current = [winddir,windspeed_current]; + + + +# determine gusts and turbulence in the bounday layer + +var gust_frequency = getprop(lw~"tmp/gust-frequency-hz"); + + + + +if (gust_frequency > 0.0) + { + var gust_relative_strength = getprop(lw~"tmp/gust-relative-strength"); + var gust_angvar = getprop(lw~"tmp/gust-angular-variation-deg"); + + var winddir_last = getprop(lwi~"wind-from-heading-deg"); + + var alt_scaling_factor = 1.2 * windspeed / 10.0; + if (alt_scaling_factor < 1.0) {alt_scaling_factor = 1.0;} + + # expected mean number of gusts in time interval (should be < 1) + var p_gust = gust_frequency * interpolation_loop_time; + + winddir_change = 0.0; + + if (rand() < p_gust) # we change the offsets for windspeed and direction + { + var alt_fact = 1.0 - altitude_agl/(boundary_alt * alt_scaling_factor); + if (alt_fact < 0.0) {alt_fact = 0.0}; + windspeed_multiplier = (1.0 + ((rand()) * gust_relative_strength * alt_fact)); + winddir_change = alt_fact * (1.0 - 2.0 * rand()) * gust_angvar; + winddir_change = winddir_change * 0.2; # Markov chain parameter, max. change per frame is 1/5 + + # if the Markov chain reaches the boundary, reflect + + #print("Winddir: ", winddir, " winddir_last: ", winddir_last, " winddir_change: ", winddir_change); + if (weather_tile_management.relangle(winddir_last + winddir_change, winddir) > gust_angvar) + {winddir_change = -winddir_change;} + + } + windspeed_current = windspeed_current * windspeed_multiplier; + winddir = winddir_last + winddir_change; + } + + + + + +compat_layer.setWindSmoothly(winddir, windspeed_current); + +setprop(lwi~"wind-from-heading-deg", winddir); +setprop(lwi~"wind-speed-kt",windspeed_current); + +setprop(lw~"current/wind-from-heading-deg",winddir); +setprop(lw~"current/wind-speed-kt",windspeed_current); + + +# hack to get access to the water shader + +setprop("/environment/config/boundary/entry[0]/wind-from-heading-deg",winddir); +setprop("/environment/config/boundary/entry[0]/wind-speed-kt",windspeed_ground); + +# end hack + + +if (getprop(lw~"interpolation-loop-flag") ==1) {settimer(interpolation_loop, interpolation_loop_time);} + +} + +################################### +# thermal lift loop startup +################################### + +var thermal_lift_start = func (ev) { + + +# if another lift loop is already running, do nothing +if (getprop(lw~"lift-loop-flag") == 1) {return;} + +# copy the properties from effect volume to the lift object + +l = thermalLift.new(ev.lat, ev.lon, ev.radius, ev.height, ev.cn, ev.sh, ev.max_lift, ev.f_lift_radius); + +l.index = ev.index; + +if (dynamics_flag == 1) + { + l.timestamp = weather_dynamics.time_lw; + if (dynamical_convection_flag == 1) + { + l.flt = ev.flt; + l.evolution_timestamp = ev.evolution_timestamp; + } + } + + + +thermal = l; + +if (debug_output_flag == 1) + { + print("Entering thermal lift..."); + print("strength: ", thermal.max_lift, " radius: ", thermal.radius); + if (dynamical_convection_flag ==1) + {print("fractional lifetime: ", thermal.flt);} + + } + +# and start the lift loop, unless another one is already running +# so we block overlapping calls + + +setprop(lw~"lift-loop-flag",1); +settimer(thermal_lift_loop,0); + +} + +################################### +# thermal lift loop +################################### + +var thermal_lift_loop = func { + +if (local_weather_running_flag == 0) {return;} + +var apos = geo.aircraft_position(); + +var tlat = thermal.lat; +var tlon = thermal.lon; + +var tpos = geo.Coord.new(); +tpos.set_latlon(tlat,tlon,0.0); + +var d = apos.distance_to(tpos); +var alt = getprop("position/altitude-ft"); + +if (dynamical_convection_flag == 1) + {var flt = thermal.flt;} +else + {var flt = 0.5;} + +var lift = calcLift(d, alt, thermal.radius, thermal.height, thermal.cn, thermal.sh, thermal.max_lift, thermal.f_lift_radius, flt); + +if (getprop(lw~"wave-loop-flag") ==1) + { + lift = lift + getprop(lw~"current/wave-lift"); + } + +# compute a reduction in visibility when entering the cloudbase + +var vis = getprop(lw~"interpolation/visibility-m"); + +if (alt > 0.9 * thermal.height) + { + var visibility_reduction = math.pow((alt - 0.9 * thermal.height)/(0.2 * thermal.height),0.1); + visibility_reduction = visibility_reduction * (1.0 - math.pow(d/(0.8*thermal.radius),14)); + + if (visibility_reduction > 1.0) {visibility_reduction = 1.0;} # this shouldn't ever happen + if (visibility_reduction < 0.0) {visibility_reduction = 0.0;} + vis = vis * (1.0 - 0.98 * visibility_reduction); + + } + +setprop(lw~"current/visibility-m",vis); +compat_layer.setVisibility(vis); + + + + +setprop(lw~"current/thermal-lift",lift); +compat_layer.setLift(lift); + +# if dynamics is on, move the thermal and occasionally compute altitude and age + +if (dynamics_flag == 1) + { + thermal.move(); + + if ((rand() < 0.01) and (presampling_flag == 1)) # check every 100 frames + { + if (dynamical_convection_flag == 1) + { + thermal.correct_altitude_and_age(); + if (thermal.flt > 1.1) + {thermal_lift_stop();} + } + else + { + thermal.correct_altitude(); + } + } + } + + +if (getprop(lw~"lift-loop-flag") ==1) {settimer(thermal_lift_loop, 0);} +} + + + + + +################################### +# thermal lift loop stop +################################### + +var thermal_lift_stop = func { + +setprop(lw~"lift-loop-flag",0); +setprop(lw~"current/thermal-lift",0.0); +compat_layer.setLift(0.0); + +if (debug_output_flag == 1) + { + print("Leaving thermal lift..."); + } + +} + + +################################### +# wave lift loop startup +################################### + +var wave_lift_start = func (ev) { + +# copy the properties from effect volume to the wave object + + +w = waveLift.new (ev.lat, ev.lon, ev.r1, ev.r2, ev.phi, ev.height, ev.max_lift); +w.index = ev.index; +wave = w; + +# and start the lift loop, unless another one is already running +# so we block overlapping calls + +if (getprop(lw~"wave-loop-flag") == 0) +{setprop(lw~"wave-loop-flag",1); settimer(wave_lift_loop,0);} + +} + +################################### +# wave lift loop +################################### + +var wave_lift_loop = func { + +if (local_weather_running_flag == 0) {return;} + +var lat = getprop("position/latitude-deg"); +var lon = getprop("position/longitude-deg"); +var alt = getprop("position/altitude-ft"); + + +var phi = wave.phi * math.pi/180.0; + +var xx = (lon - wave.lon) * lon_to_m; +var yy = (lat - wave.lat) * lat_to_m; + +var x = xx * math.cos(phi) - yy * math.sin(phi); +var y = yy * math.cos(phi) + xx * math.sin(phi); + +var lift = calcWaveLift(x,y,alt); + +# check if we are in a thermal, if so set wave lift and let the thermal lift loop add that + +if (getprop(lw~"lift-loop-flag") ==1) + { + setprop(lw~"current/wave-lift",lift); + } +else + { + setprop(lw~"current/thermal-lift",lift); + } + +if (getprop(lw~"wave-loop-flag") ==1) {settimer(wave_lift_loop, 0);} +} + + + + +################################### +# wave lift loop stop +################################### + +var wave_lift_stop = func { + +setprop(lw~"wave-loop-flag",0); +setprop(lw~"current/thermal-lift",0.0); +} + + + +#################################### +# action taken when in effect volume +#################################### + +var effect_volume_start = func (ev) { + +var cNode = props.globals.getNode(lw~"current"); + + +if (ev.vis_flag ==1) + { + # first store the current setting in case we need to restore on leaving + + var vis = ev.vis; + ev.vis_r = cNode.getNode("visibility-m").getValue(); + + # then set the new value in current and execute change + cNode.getNode("visibility-m").setValue(vis); + #compat_layer.setVisibility(vis); + compat_layer.setVisibilitySmoothly(vis); + + # then count the number of active volumes on entry (we need that to determine + # what to do on exit) + ev.n_entry_vis = getprop(lw~"effect-volumes/number-active-vis"); + + # and add to the counter + setprop(lw~"effect-volumes/number-active-vis",getprop(lw~"effect-volumes/number-active-vis")+1); + } + +if (ev.rain_flag == 1) + { + var rain = ev.rain; + ev.rain_r = cNode.getNode("rain-norm").getValue(); + cNode.getNode("rain-norm").setValue(rain); + compat_layer.setRain(rain); + ev.n_entry_rain = getprop(lw~"effect-volumes/number-active-rain"); + setprop(lw~"effect-volumes/number-active-rain",getprop(lw~"effect-volumes/number-active-rain")+1); + } +if (ev.snow_flag == 1) + { + var snow = ev.snow; + ev.snow_r = cNode.getNode("snow-norm").getValue(); + cNode.getNode("snow-norm").setValue(snow); + compat_layer.setSnow(snow); + ev.n_entry_snow = getprop(lw~"effect-volumes/number-active-snow"); + setprop(lw~"effect-volumes/number-active-snow",getprop(lw~"effect-volumes/number-active-snow")+1); + } +if (ev.turb_flag == 1) + { + var turbulence = ev.turb; + ev.turb_r = cNode.getNode("turbulence").getValue(); + cNode.getNode("turbulence").setValue(turbulence); + compat_layer.setTurbulence(turbulence); + ev.n_entry_turb = getprop(lw~"effect-volumes/number-active-turb"); + setprop(lw~"effect-volumes/number-active-turb",getprop(lw~"effect-volumes/number-active-turb")+1); + } +if (ev.sat_flag == 1) + { + var saturation = ev.sat; + ev.sat_r = getprop("/rendering/scene/saturation"); + compat_layer.setLightSmoothly(saturation); + ev.n_entry_sat = getprop(lw~"effect-volumes/number-active-sat"); + setprop(lw~"effect-volumes/number-active-sat",getprop(lw~"effect-volumes/number-active-sat")+1); + } + +if (ev.lift_flag == 1) + { + var lift = ev.lift; + ev.lift_r = cNode.getNode("thermal-lift").getValue(); + cNode.getNode("thermal-lift").setValue(lift); + compat_layer.setLift(lift); + ev.n_entry_lift = getprop(lw~"effect-volumes/number-active-lift"); + setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1); + } +else if (ev.lift_flag == 2) + { + ev.lift_r = cNode.getNode("thermal-lift").getValue(); + ev.n_entry_lift = getprop(lw~"effect-volumes/number-active-lift"); + setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1); + thermal_lift_start(ev); + } +else if (ev.lift_flag == 3) + { + ev.lift_r = cNode.getNode("thermal-lift").getValue(); + ev.n_entry_lift = getprop(lw~"effect-volumes/number-active-lift"); + setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1); + wave_lift_start(ev); + } + +} + + + +var effect_volume_stop = func (ev) { + +var cNode = props.globals.getNode(lw~"current"); + + +if (ev.vis_flag == 1) + { + + var n_active = getprop(lw~"effect-volumes/number-active-vis"); + + + var n_entry = ev.n_entry_vis; + + # if no other nodes affecting property are active, restore to outside + # else restore settings as they have been when entering the volume when the number + # of active volumes is the same as on entry (i.e. volumes are nested), otherwise + # leave property at current because new definitions are already active and should not + # be cancelled + + if (n_active ==1){var vis = props.globals.getNode(lw~"interpolation/visibility-m").getValue();} + else if ((n_active -1) == n_entry) #{var vis = ev.getNode("restore/visibility-m").getValue();} + {var vis = ev.vis_r;} + else {var vis = cNode.getNode("visibility-m").getValue();} + cNode.getNode("visibility-m").setValue(vis); + #compat_layer.setVisibility(vis); + compat_layer.setVisibilitySmoothly(vis); + + # and subtract from the counter + setprop(lw~"effect-volumes/number-active-vis",getprop(lw~"effect-volumes/number-active-vis")-1); + } +if (ev.rain_flag == 1) + { + var n_active = getprop(lw~"effect-volumes/number-active-rain"); + var n_entry = ev.n_entry_rain; + + if (n_active ==1){var rain = props.globals.getNode(lw~"interpolation/rain-norm").getValue();} + else if ((n_active -1) == n_entry)# {var rain = ev.getNode("restore/rain-norm").getValue();} + {var rain = ev.rain_r;} + else {var rain = cNode.getNode("rain-norm").getValue();} + cNode.getNode("rain-norm").setValue(rain); + compat_layer.setRain(rain); + setprop(lw~"effect-volumes/number-active-rain",getprop(lw~"effect-volumes/number-active-rain")-1); + } + +if (ev.snow_flag == 1) + { + var n_active = getprop(lw~"effect-volumes/number-active-snow"); + var n_entry = ev.n_entry_snow; + + if (n_active ==1){var snow = props.globals.getNode(lw~"interpolation/snow-norm").getValue();} + else if ((n_active -1) == n_entry) + {var snow = ev.snow_r;} + else {var snow = cNode.getNode("snow-norm").getValue();} + cNode.getNode("snow-norm").setValue(snow); + compat_layer.setSnow(snow); + setprop(lw~"effect-volumes/number-active-snow",getprop(lw~"effect-volumes/number-active-snow")-1); + } + +if (ev.turb_flag == 1) + { + var n_active = getprop(lw~"effect-volumes/number-active-turb"); + var n_entry = ev.n_entry_turb; + if (n_active ==1){var turbulence = props.globals.getNode(lw~"interpolation/turbulence").getValue();} + else if ((n_active -1) == n_entry) + {var turbulence = ev.turb_r;} + else {var turbulence = cNode.getNode("turbulence").getValue();} + cNode.getNode("turbulence").setValue(turbulence); + compat_layer.setTurbulence(turbulence); + setprop(lw~"effect-volumes/number-active-turb",getprop(lw~"effect-volumes/number-active-turb")-1); + } + +if (ev.sat_flag == 1) + { + var n_active = getprop(lw~"effect-volumes/number-active-sat"); + var n_entry = ev.n_entry_sat; + if (n_active ==1){var saturation = 1.0;} + else if ((n_active -1) == n_entry) + {var saturation = ev.sat_r;} + else {var saturation = getprop("/rendering/scene/saturation");} + compat_layer.setLightSmoothly(saturation); + setprop(lw~"effect-volumes/number-active-sat",getprop(lw~"effect-volumes/number-active-sat")-1); + } + +if (ev.lift_flag == 1) + { + var n_active = getprop(lw~"effect-volumes/number-active-lift"); + var n_entry = ev.n_entry_lift; + if (n_active ==1){var lift = props.globals.getNode(lw~"interpolation/thermal-lift").getValue();} + else if ((n_active -1) == n_entry) + {var lift = ev.lift_r;} + else {var lift = cNode.getNode("thermal-lift").getValue();} + cNode.getNode("thermal-lift").setValue(lift); + compat_layer.setLift(lift); + setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1); + } +else if (ev.lift_flag == 2) + { + thermal_lift_stop(); + setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1); + } +else if (ev.lift_flag == 3) + { + wave_lift_stop(); + setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1); + } + +} + + + +######################################### +# compute thermal lift in detailed model +######################################### + +var ts_factor = func (t, alt, height) { + +var t1 = 0.1; # fractional time at which lift is fully developed +var t2 = 0.9; # fractional time at which lift starts to decay +var t3 = 1.0; # fractional time at which lift is gone + +# no time dependence modelled yet +# return 1.0; + + + +var t_a = t - (alt/height) * t1 - t1; + +if (t_a<0) {return 0.0;} +else if (t_a 200.0*m_to_ft) and (alt < height)) + {return max_lift;} + +# decreasing lift from cloudbase to 10% above base +else if ((alt > height ) and (alt < height*1.1)) + {return max_lift * 0.5 * (1.0 - math.cos((1.0-10.0*(alt-height)/height)*math.pi));} + +# no lift available above +else {return 0.0;} +} + + + +var calcLift = func (d, alt, R, height, cn, sh, max_lift, f_lift_radius, t) { + +# radius of slice at given altitude +var r_total = (cn + alt/height*(1.0-cn)) * (R - R * (1.0- sh ) * (1.0 - ((2.0*alt/height)-1.0)*((2.0*alt/height)-1.0))); + + +# print("r_total: ", r_total, "d: ",d); +# print("alt: ", alt, "height: ",height); + +# no lift if we're outside the radius or above the thermal +if ((d > r_total) or (alt > 1.1*height)) { return 0.0; } + +# fraction of radius providing lift +var r_lift = f_lift_radius * r_total; + +# print("r_lift: ", r_lift); + +# if we are in the sink portion, get the max. sink for this time and altitude and adjust for actual position +if ((d < r_total ) and (d > r_lift)) + { + var s_max = 0.5 * calcLift_max(alt, max_lift, height) * ts_factor(t, alt, height); + # print("s_max: ", s_max); + return s_max * math.sin(math.pi * (1.0 + (d-r_lift) * (1.0/(r_total - r_lift)))); + } +# else we are in the lift portion, get the max. lift for this time and altitude and adjust for actual position +else + { + var l_max = calcLift_max(alt, max_lift, height) * tl_factor(t, alt, height); + # print("l_max: ", l_max); + return l_max * math.cos(math.pi * (d/(2.0 * r_lift))); + } +} + +######################################### +# compute wave lift in detailed model +######################################### + +var calcWaveLift = func (x,y, alt) { + +var lift = wave.max_lift * math.cos((y/wave.y) * 1.5 * math.pi); + +if (abs(x)/wave.x > 0.9) + { + lift = lift * (abs(x) - 0.9 * wave.x)/(0.1 * wave.x); + } + + + +lift = lift * 2.71828 * math.exp(-alt/wave.height) * alt/wave.height; + +var alt_agl = getprop("/position/altitude-agl-ft"); + +if (alt_agl < 1000.0) + { + lift = lift * (alt_agl/1000.0) * (alt_agl/1000.0); + } + +return lift; +} + + + + + + +########################################################### +# place a single cloud into a vector to be processed +# separately +########################################################### + +var create_cloud_vec = func(path, lat, long, alt, heading) { + +if (path == "new") # we have to switch to new cloud generating routines + { + local_weather.cloudAssembly.lat = lat; + local_weather.cloudAssembly.lon = long; + local_weather.cloudAssembly.alt = alt; + + #print(lat," ",long, " ", alt); + + if (dynamics_flag == 1) + { + local_weather.cloudAssembly.mean_alt = cloud_mean_altitude; + local_weather.cloudAssembly.flt = cloud_fractional_lifetime; + local_weather.cloudAssembly.evolution_timestamp = cloud_evolution_timestamp; + local_weather.cloudAssembly.rel_alt = cloudAssembly.alt - cloud_mean_altitude; + } + #compat_layer.create_cloud_new(local_weather.cloudAssembly); + + append(cloudAssemblyArray,cloudAssembly); + + return; + } + +append(clouds_path,path); +append(clouds_lat,lat); +append(clouds_lon,long); +append(clouds_alt,alt); +append(clouds_orientation,heading); + +# globals (needed for Cumulus clouds) should be set if needed by the main cloud generating call + +if (dynamics_flag ==1) + { + append(clouds_mean_alt, cloud_mean_altitude); + append(clouds_flt, cloud_fractional_lifetime); + append(clouds_evolution_timestamp,cloud_evolution_timestamp); + } + +} +########################################################### +# clear all clouds and effects +########################################################### + +var clear_all = func { + +# clear the clouds and models + +var cloudNode = props.globals.getNode(lw~"clouds", 1); +cloudNode.removeChildren("tile"); + +var modelNode = props.globals.getNode("models", 1).getChildren("model"); + +foreach (var m; modelNode) + { + var l = m.getNode("tile-index",1).getValue(); + if (l != nil) + { + m.remove(); + } + } + + +# remove the hard-coded clouds + +foreach (c; weather_tile_management.cloudArray) + { + c.remove(); + } +setsize(weather_tile_management.cloudArray,0); + +# reset pressure continuity + +weather_tiles.last_pressure = 0.0; + +# stop all loops + +setprop(lw~"effect-loop-flag",0); +setprop(lw~"interpolation-loop-flag",0); +setprop(lw~"tile-loop-flag",0); +setprop(lw~"lift-loop-flag",0); +setprop(lw~"wave-loop-flag",0); +setprop(lw~"dynamics-loop-flag",0); +setprop(lw~"timing-loop-flag",0); +setprop(lw~"buffer-loop-flag",0); +setprop(lw~"housekeeping-loop-flag",0); +setprop(lw~"convective-loop-flag",0); + +weather_dynamics.convective_loop_kill_flag = 1; # long-running loop needs a different scheme to end + +# also remove rain, snow, haze and light effects + +compat_layer.setRain(0.0); +compat_layer.setSnow(0.0); +compat_layer.setLight(1.0); + + +# set placement indices to zero + +setprop(lw~"clouds/placement-index",0); +setprop(lw~"clouds/model-placement-index",0); +setprop(lw~"effect-volumes/effect-placement-index",0); +setprop(lw~"effect-volumes/number",0); +setprop(lw~"tiles/tile-counter",0); + + +# remove any quadtrees and arrays + +settimer ( func { setsize(weather_dynamics.cloudQuadtrees,0);},0.1); # to avoid error generation in this frame +setsize(effectVolumeArray,0); +n_effectVolumeArray = 0; + +# if we have used METAR, we may no longer want to do so + +metar_flag = 0; + + +settimer ( func { + setsize(weather_tile_management.modelArrays,0); + setsize(weather_dynamics.tile_wind_direction,0); + setsize(weather_dynamics.tile_wind_speed,0); + setsize(weather_tile_management.cloudBufferArray,0); + setsize(weather_tile_management.cloudSceneryArray,0); + setsize(alt_20_array,0); + setsize(alt_50_array,0); + setsize(alt_min_array,0); + setsize(alt_mean_array,0); + setsize(weather_dynamics.tile_convective_altitude,0); + setsize(weather_dynamics.tile_convective_strength,0); + setsize(weatherStationArray,0); + setsize(windIpointArray,0); + setsize(atmosphereIpointArray,0); + setprop(lw~"clouds/buffer-count",0); + setprop(lw~"clouds/cloud-scenery-count",0); + weather_tile_management.n_cloudSceneryArray = 0; + compat_layer.setScattering(0.8); + compat_layer.setOvercast(0.0); + setprop(lwi~"ipoint-number",0); + setprop(lwi~"atmosphere-ipoint-number", 0); + },0.1); + +setprop(lw~"tmp/presampling-status", "idle"); + +# reset the random store + +weather_tiles.rnd_store = rand(); + +# default 3d clouds layer wrapping back on, just in case + +setprop("/sim/rendering/clouds3d-wrap",1); + +# indicate that we are no longer running + + +local_weather_running_flag = 0; + +} + + + +########################################################### +# detailed Cumulus clouds created from multiple cloudlets +########################################################### + +var create_detailed_cumulus_cloud = func (lat, lon, alt, size) { + + +var edge_bias = convective_texture_mix; + +size = size + convective_size_bias; + + +if (hardcoded_clouds_flag == 0) + { + if (size > 2.0) + { + if (rand() > (size - 2.0)) + {create_cumulonimbus_cloud(lat, lon, alt, size); } + else + {create_cumulonimbus_cloud_rain(lat, lon, alt, size, 0.1 + 0.2* rand());} + return; + } + + else if (size>1.5) + { + var type = "Congestus"; + var btype = "Congestus bottom"; + var height = 400; + var n = 8; + var n_b = 4; + var x = 1000.0; + var y = 300.0; + var edge = 0.3; + } + + else if (size>1.1) + { + var type = "Cumulus (cloudlet)"; + var btype = "Cumulus bottom"; + var height = 200; + var n = 8; + var n_b = 1; + var x = 400.0; + var y = 200.0; + var edge = 0.3; + } + else if (size>0.8) + { + var type = "Cumulus (cloudlet)"; + var btype = "Cumulus bottom"; + var height = 150; + var n = 6; + var x = 300.0; + var y = 200.0; + var edge = 0.3; + } + else if (size>0.4) + { + var type = "Cumulus (cloudlet)"; + var btype = "Cumulus bottom"; + var height = 100; + var n = 4; + var x = 200.0; + var y = 200.0; + var edge = 1.0; + } + else + { + var type = "Cumulus (whisp)"; + var btype = "Cumulus bottom"; + var height = 100; + var n = 1; + var x = 100.0; + var y = 100.0; + var edge = 1.0; + } + + var alpha = rand() * 180.0; + edge = edge + edge_bias; + create_streak(type,lat,lon, alt+ 0.5* (height +cloud_vertical_size_map["Cumulus"] * ft_to_m), height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + + # for large clouds, add a bottom + + if ((size > 1.1) and (edge < 0.4)) + { + + create_streak(btype,lat,lon, alt, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0); + } + + + + } +else + { + if (size > 2.0) + { + if (rand() > (size - 2.0)) + {create_cumulonimbus_cloud(lat, lon, alt, size); } + else + {create_cumulonimbus_cloud_rain(lat, lon, alt, size, 0.1 + 0.2* rand());} + return; + } + + else if (size>1.5) + { + var type = "Congestus"; + + var height = 400; + var n = 3; + var x = 700.0; + var y = 200.0; + var edge = 0.2; + + var alpha = rand() * 180.0; + edge = edge + edge_bias; + + create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Congestus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + + var type = "Cu (volume)"; + var height = 400; + var n = 10; + var x = 1400.0; + var y = 400.0; + var edge = 0.2; + + edge = edge + edge_bias; + + create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Congestus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + + var btype = "Congestus bottom"; + var n_b = 6; + + create_streak(btype,lat,lon, alt -offset_map["Congestus"] -200.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0); + + } + else if (size>1.1) + { + var type = "Cumulus (cloudlet)"; + var btype = "Cumulus bottom"; + var height = 200; + var n = 6; + var n_b = 2; + var x = 900.0; + var y = 200.0; + var edge = 0.2; + + var alpha = rand() * 180.0; + edge = edge + edge_bias; + create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + + create_streak(btype,lat,lon, alt -offset_map["Cumulus"] - 200.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0); + + } + else if (size>0.8) + { + var type = "Cumulus (cloudlet)"; + var height = 150; + var n = 4; + var x = 300.0; + var y = 300.0; + var edge = 0.3; + + var alpha = rand() * 180.0; + edge = edge + edge_bias; + create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + + n = 2; + x = 700.0; + y = 200.0; + edge = 1.0; + create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + } + + else if (size>0.4) + { + var type = "Cumulus (cloudlet)"; + var height = 100; + var n = 2; + var x = 600.0; + var y = 100.0; + var edge = 1.0; + + var alpha = rand() * 180.0; + edge = edge + edge_bias; + create_streak(type,lat,lon, alt+ 0.3* (height)-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + } + else + { + var type = "Cumulus (whisp)"; + var height = 100; + var n = 1; + var x = 100.0; + var y = 100.0; + var edge = 1.0; + + var alpha = rand() * 180.0; + edge = edge + edge_bias; + create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + } + + } + + + +} + +########################################################### +# detailed small Cumulonimbus clouds created from multiple cloudlets +########################################################### + +var create_cumulonimbus_cloud = func(lat, lon, alt, size) { + +if (hardcoded_clouds_flag == 1) + {create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 3.4, 8, 0.8, 0.1, 8);} +else + {create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 1.4, 4, 0.9, 0.2, 8);} + + + +} + +########################################################### +# detailed small Cumulonimbus and rain created from multiple cloudlets +########################################################### + +var create_cumulonimbus_cloud_rain = func(lat, lon, alt, size, rain) { + +if (hardcoded_clouds_flag == 1) + {create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 3.4, 8, 0.8, 0.1, 8);} +else + {create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 1.4, 4, 0.9, 0.2, 8);} + + +# place a rain texture + +var path = "Models/Weather/rain2.xml"; +if (thread_flag == 1) + {create_cloud_vec(path, lat, lon, alt, 0.0);} + else + {compat_layer.create_cloud(path, lat, lon, alt, 0.0);} + + + +# and some rain underneath + +create_effect_volume(1, lat, lon, 2000.0, 2000.0, 0.0, 0.0, alt+1000.0, 8000.0 + 8000.0 * rand(), rain, -1, -1, -1 ,1,-1 ); + + +} + + +########################################################### +# wrappers for convective cloud system to distribute +# call across several frames if needed +########################################################### + +var create_cumosys = func (blat, blon, balt, nc, size) { + +# realistic Cumulus has somewhat larger models, so compensate to get the same coverage +if (detailed_clouds_flag == 1) + {nc = int(0.7 * nc);} + +nc = int(nc / cumulus_efficiency_factor); + +if (thread_flag == 1) + {setprop(lw~"tmp/convective-status", "computing"); + cumulus_loop(blat, blon, balt, nc, size);} + +else + {create_cumulus(blat, blon, balt, nc, size); + if (debug_output_flag == 1) + {print("Convective system done!");} + } +} + + + +var cumulus_loop = func (blat, blon, balt, nc, size) { + +if (local_weather_running_flag == 0) {return;} + +if (local_weather.features.fast_geodinfo == 0) + {var n = int(25/cumulus_efficiency_factor);} +else + {var n = int(200/cumulus_efficiency_factor);} + +if (nc < 0) + { + if (debug_output_flag == 1) + {print("Convective system done!");} + setprop(lw~"tmp/convective-status", "idle"); + assemble_effect_array(); + convective_size_bias = 0.0; + return; + } + +create_cumulus(blat, blon, balt, n, size); + +settimer( func {cumulus_loop(blat, blon, balt, nc-n, size) },0); +} + +########################################################### +# place a convective cloud system +########################################################### + +var create_cumulus = func (blat, blon, balt, nc, size) { + + + +var path = "Models/Weather/blank.ac"; +var i = 0; +var p = 0.0; +var rn = 0.0; +var place_lift_flag = 0; +var strength = 0.0; +var detail_flag = detailed_clouds_flag; + +var alpha = getprop(lw~"tmp/tile-orientation-deg") * math.pi/180.0; # the tile orientation + +if (detailed_terrain_interaction_flag == 1) + { + var tile_index = getprop(lw~"tiles/tile-counter"); + #var alt_min = alt_min_array[tile_index-1]; + #var alt_mean = alt_mean_array[tile_index -1]; + #var alt_median = alt_50_array[tile_index -1]; + #var alt_base = alt_20_array[tile-index -1]; + #var alt_min = getprop(lw~"tmp/tile-alt-min-ft"); + #var alt_mean = getprop(lw~"tmp/tile-alt-mean-ft"); + #var alt_median = getprop(lw~"tmp/tile-alt-median-ft"); + #var alt_base = getprop(lw~"tmp/tile-alt-offset-ft"); + } + +var sec_to_rad = 2.0 * math.pi/86400; # conversion factor for sinusoidal dependence on daytime + +calc_geo(blat); + +# get the local time of the day in seconds + +var t = getprop("sim/time/utc/day-seconds"); +t = t + getprop("sim/time/local-offset"); + +# print("t is now:", t); + +# and make a simple sinusoidal model of thermal strength + +# daily variation in number of thermals, peaks at noon +var t_factor1 = 0.5 * (1.0-math.cos((t * sec_to_rad))); + +# daily variation in strength of thermals, peaks around 15:30 +var t_factor2 = 0.5 * (1.0-math.cos((t * sec_to_rad)-0.9)); + + +# number of possible thermals equals overall strength times daily variation times geographic variation +# this is a proxy for solar thermal energy + +nc = t_factor1 * nc * math.cos(blat/180.0*math.pi); + +var thermal_conditions = getprop(lw~"config/thermal-properties"); + + +while (i < nc) { + + p = 0.0; + place_lift_flag = 0; + strength = 0.0; + + # pick a trial position inside the tile and rotate by tile orientation angle + var x = (2.0 * rand() - 1.0) * size; + var y = (2.0 * rand() - 1.0) * size; + + var lat = blat + (y * math.cos(alpha) - x * math.sin(alpha)) * m_to_lat; + var lon = blon + (x * math.cos(alpha) + y * math.sin(alpha)) * m_to_lon; + + # now check ground cover type on chosen spot + var info = geodinfo(lat, lon); + + if (info != nil) { + var elevation = info[0] * m_to_ft; + if (info[1] != nil){ + var landcover = info[1].names[0]; + if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];} + else {print(p, " ", info[1].names[0]);} + }} + else { + print("No terrain loaded!"); + continue; + } + + + # apply some optional corrections, biases clouds towards higher elevations + + var terrain_altitude_factor = 1.0; + var terrain_strength_factor = 1.0; + + if (detailed_terrain_interaction_flag == 1) + { + + terrain_altitude_factor = get_terrain_altitude_factor(tile_index, balt, elevation); + terrain_strength_factor = get_terrain_strength_factor(terrain_altitude_factor); + + } + + + # then decide if the thermal energy at the spot generates an updraft and a cloud + + if (rand() < (p * cumulus_efficiency_factor * terrain_altitude_factor)) # we decide to place a cloud at this spot + { + + + # check if we have a terrain elevation analysis available and can use a + # detailed placement altitude correction + + if (presampling_flag == 1) + { + + if (detailed_terrain_interaction_flag == 1) + { + var grad = get_terrain_gradient(lat, lon, elevation, alpha, 1000.0); + } + else + {var grad = 0.0;} + + + var place_alt = get_convective_altitude(balt, elevation, getprop(lw~"tiles/tile-counter"), grad); + } + else {var place_alt = balt;} + + # no cloud placement into the ground + if (place_alt < elevation) {continue;} + + # if we're in a lee, we may not want to place the cloud + + if (detailed_terrain_interaction_flag == 1) + { + var p_lee_suppression = get_lee_bias(grad); + if (rand() > p_lee_suppression) {continue;} + } + + + # now decide on the strength of the thermal at the spot and on cloud size + + var rn = rand(); + strength = (1.5 * rn + (2.0 * p * terrain_strength_factor)) * t_factor2; + if (strength > 1.0) + { + # we place a large cloud, and we generate lift + path = select_cloud_model("Cumulus","large"); place_lift_flag = 1; + } + else {path = select_cloud_model("Cumulus","small");} + + # the terrain effect cannot create Cb development, so we have to curb + # the strength if it would not have been Cb otherwise + + if (strength > 2.0) + { + if (((1.5 * rn + (2.0 * p)) * t_factor2) < 2.0) + {strength = 1.7 + rand() * 0.2;} + } + + + cloud_mean_altitude = place_alt; + cloud_fractional_lifetime = rand(); + cloud_evolution_timestamp = weather_dynamics.time_lw; + + if (generate_thermal_lift_flag != 3) # no clouds if we produce blue thermals + { + if (thread_flag == 1) + { + if (detail_flag == 0){create_cloud_vec(path,lat,lon, place_alt, 0.0);} + else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);} + } + else + { + if (detail_flag == 0){compat_layer.create_cloud(path, lat, lon, place_alt, 0.0);} + else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);} + } + } + + # now see if we need to create a thermal - first check the flag + if (generate_thermal_lift_flag == 1) # thermal by constant + { + # now check if convection is strong + if (place_lift_flag == 1) + { + var lift = 3.0 + 10.0 * (strength -1.0); + var radius = 500 + 500 * rand(); + #print("Lift: ", lift * ft_to_m - 1.0); + create_effect_volume(1, lat, lon, radius, radius, 0.0, 0.0, place_alt+500.0, -1, -1, -1, -1, lift, 1,-1); + } # end if place_lift_flag + } # end if generate-thermal-lift-flag + else if ((generate_thermal_lift_flag == 2) or (generate_thermal_lift_flag == 3)) # thermal by function + { + + if (place_lift_flag == 1) + { + var lift = (3.0 + 10.0 * (strength -1.0))/thermal_conditions; + var radius = (500 + 500 * rand())*thermal_conditions; + + create_effect_volume(1, lat, lon, 1.1*radius, 1.1*radius, 0.0, 0.0, place_alt*1.15, -1, -1, -1, lift*0.03, lift, -2,-1); + } # end if place_lift_flag + + } # end if generate-thermal-lift-flag + + + } # end if rand < p + i = i + 1; + } # end while + +} + + + + + +################################################################# +# respawn convective clouds to compensate for decay +# the difference being that new clouds get zero fractional +# lifetime and are placed based on terrain with a different weight +################################################################## + +var recreate_cumulus = func (blat, blon, balt, alpha, nc, size, tile_index) { + +var path = "Models/Weather/blank.ac"; +var i = 0; +var p = 0.0; +var rn = 0.0; +var place_lift_flag = 0; +var strength = 0.0; +var detail_flag = detailed_clouds_flag; + +alpha = alpha * math.pi/180.0; # the tile orientation + +var sec_to_rad = 2.0 * math.pi/86400; # conversion factor for sinusoidal dependence on daytime + +# current aircraft position + +var alat = getprop("position/latitude-deg"); +var alon = getprop("position/longitude-deg"); + +# get the local time of the day in seconds + +var t = getprop("sim/time/utc/day-seconds"); +t = t + getprop("sim/time/local-offset"); + + +# and make a simple sinusoidal model of thermal strength + +# daily variation in number of thermals, peaks at noon +var t_factor1 = 0.5 * (1.0-math.cos((t * sec_to_rad))); + +# daily variation in strength of thermals, peaks around 15:30 +var t_factor2 = 0.5 * (1.0-math.cos((t * sec_to_rad)-0.9)); + + +# number of possible thermals equals overall strength times daily variation times geographic variation +# this is a proxy for solar thermal energy + +nc = t_factor1 * nc * math.cos(blat/180.0*math.pi); + +var thermal_conditions = getprop(lw~"config/thermal-properties"); + + +while (i < nc) { + + p = 0.0; + place_lift_flag = 0; + strength = 0.0; + + # pick a trial position inside the tile and rotate by tile orientation angle + var x = (2.0 * rand() - 1.0) * size; + var y = (2.0 * rand() - 1.0) * size; + + var lat = blat + (y * math.cos(alpha) - x * math.sin(alpha)) * m_to_lat; + var lon = blon + (x * math.cos(alpha) + y * math.sin(alpha)) * m_to_lon; + + # check if the cloud would be spawned in visual range, if not don't bother + var d_sq = calc_d_sq(alat, alon, lat, lon); + + if (math.sqrt(d_sq) > weather_tile_management.cloud_view_distance) + {i = i+1; continue;} + + # now check ground cover type on chosen spot + var info = geodinfo(lat, lon); + + if (info != nil) { + var elevation = info[0] * m_to_ft; + if (info[1] != nil){ + var landcover = info[1].names[0]; + if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];} + else {print(p, " ", info[1].names[0]);} + }} + else {continue;} + + + # apply some optional corrections, biases clouds towards higher elevations + + var terrain_altitude_factor = 1.0; + var terrain_strength_factor = 1.0; + + if (detailed_terrain_interaction_flag == 1) + { + terrain_altitude_factor = get_terrain_altitude_factor(tile_index, balt, elevation); + terrain_strength_factor = get_terrain_strength_factor(terrain_altitude_factor); + } + + + + + # check if to place a cloud with weight sqrt(p), the lifetime gets another sqrt(p) factor + + if (rand() > math.sqrt(p * cumulus_efficiency_factor * terrain_altitude_factor)) + {i=i+1; continue;} + + + # then calculate the strength of the updraft + + strength = (1.5 * rand() + (2.0 * p * terrain_strength_factor)) * t_factor2; # the strength of thermal activity at the spot + if (strength > 1.0) + { + path = select_cloud_model("Cumulus","large"); place_lift_flag = 1; + } + else {path = select_cloud_model("Cumulus","small");} + + if (presampling_flag == 1) + { + var place_alt = get_convective_altitude(balt, elevation, tile_index,0.0); + } + else {var place_alt = balt;} + + + # no cloud placement into the ground + if (place_alt < elevation) {continue;} + + # if we're in a lee, we may not want to place the cloud + + if (detailed_terrain_interaction_flag == 1) + { + var p_lee_suppression = get_lee_bias(grad); + if (rand() > math.sqrt(p_lee_suppression)) {continue;} + } + + cloud_mean_altitude = place_alt; + cloud_fractional_lifetime = 0.0; + cloud_evolution_timestamp = weather_dynamics.time_lw; + + compat_layer.cloud_mean_altitude = place_alt; + compat_layer.cloud_flt = cloud_fractional_lifetime; + compat_layer.cloud_evolution_timestamp = cloud_evolution_timestamp; + + if (generate_thermal_lift_flag != 3) # no clouds if we produce blue thermals + { + if (thread_flag == 1) + { + thread_flag = 0; # create clouds immediately + if (detail_flag == 0){compat_layer.create_cloud(path,lat,lon, place_alt, 0.0);} + else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);} + thread_flag = 1; # and restore threading + } + else + { + if (detail_flag == 0){compat_layer.create_cloud(path, lat, lon, place_alt, 0.0);} + else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);} + } + } + + if (generate_thermal_lift_flag == 1) # thermal by constant + { + if (place_lift_flag == 1) + { + var lift = 3.0 + 10.0 * (strength -1.0); + var radius = 500 + 500 * rand(); + create_effect_volume(1, lat, lon, radius, radius, 0.0, 0.0, place_alt+500.0, -1, -1, -1, -1, lift, 1,-1); + } # end if place_lift_flag + } # end if generate-thermal-lift-flag + else if ((generate_thermal_lift_flag == 2) or (generate_thermal_lift_flag == 3)) # thermal by function + { + if (place_lift_flag == 1) + { + var lift = (3.0 + 10.0 * (strength -1.0))/thermal_conditions; + var radius = (500 + 500 * rand())*thermal_conditions; + + create_effect_volume(1, lat, lon, 1.1*radius, 1.1*radius, 0.0, 0.0, place_alt*1.15, -1, -1, -1, lift*0.03, lift, -2,-1); + } # end if place_lift_flag + + } # end if generate-thermal-lift-flag + + + i = i + 1; + } # end while + +} + + + + + +########################################################### +# place a Cumulus layer with excluded regions +# to avoid placing cumulus underneath a thunderstorm +########################################################### + +var cumulus_exclusion_layer = func (blat, blon, balt, n, size_x, size_y, alpha, s_min, s_max, n_ex, exlat, exlon, exrad) { + + +var strength = 0; +var flag = 1; +var phi = alpha * math.pi/180.0; + +var detail_flag = detailed_clouds_flag; + +if (detail_flag == 1) {var i_max = int(0.25*n);} else {var i_max = int(1.0*n);} + + + +for (var i =0; i< i_max; i=i+1) + { + var x = (2.0 * rand() - 1.0) * size_x; + var y = (2.0 * rand() - 1.0) * size_y; + + var lat = blat + (y * math.cos(phi) - x * math.sin(phi)) * m_to_lat; + var lon = blon + (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon; + + flag = 1; + + for (var j=0; j 1.0) {var path = select_cloud_model("Cumulus","large"); } + else {var path = select_cloud_model("Cumulus","small");} + + if (thread_flag == 1) + { + if (detail_flag == 0){create_cloud_vec(path,lat,lon, balt, 0.0);} + else {create_detailed_cumulus_cloud(lat, lon, balt, strength);} + } + else + { + if (detail_flag == 0){compat_layer.create_cloud(path, lat, lon, balt, 0.0);} + else {create_detailed_cumulus_cloud(lat, lon, balt, strength);} + } + + } # end if flag + + } # end for i + +} + + +########################################################### +# place a barrier cloud system +########################################################### + +var create_rise_clouds = func (blat, blon, balt, nc, size, winddir, dist) { + +var path = "Models/Weather/blank.ac"; +var i = 0; +var p = 0.0; +var rn = 0.0; +var nsample = 10; +var counter = 0; +var dir = (winddir + 180.0) * math.pi/180.0; +var step = dist/nsample; + +calc_geo(blat); + +while (i < nc) { + + counter = counter + 1; + p = 0.0; + + var x = (2.0 * rand() - 1.0) * size; + var y = (2.0 * rand() - 1.0) * size; + + var lat = blat + y * m_to_lat; + var lon = blon + x * m_to_lon; + + var elevation = compat_layer.get_elevation(lat, lon); + + #print("elevation: ", elevation, "balt: ", balt); + + if ((elevation < balt) and (elevation != -1.0)) + { + for (var j = 0; j balt) + { + p = 1.0 - j * (1.0/nsample); + #p = 1.0; + break; + } + + } + } + if (counter > 500) {print("Cannot place clouds - exiting..."); i = nc;} + if (rand() < p) + { + path = select_cloud_model("Stratus (structured)","large"); + compat_layer.create_cloud(path, lat, lon, balt, 0.0); + counter = 0; + i = i+1; + } + + } # end while + +} + + +########################################################### +# place a cloud streak +########################################################### + +var create_streak = func (type, blat, blong, balt, alt_var, nx, xoffset, edgex, x_var, ny, yoffset, edgey, y_var, direction, tri) { + +var flag = 0; +var path = "Models/Weather/blank.ac"; +calc_geo(blat); +var dir = direction * math.pi/180.0; + +var ymin = -0.5 * ny * yoffset; +var xmin = -0.5 * nx * xoffset; +var xinc = xoffset * (tri-1.0) /ny; + +var jlow = int(nx*edgex); +var ilow = int(ny*edgey); + + +for (var i=0; i(nx-jlow-1))) and ((i(ny-ilow-1)))) # select a small or no cloud + { + if (rn > 2.0) {flag = 1;} else {path = select_cloud_model(type,"small");} + } + if ((j(nx-jlow-1)) or (i(ny-ilow-1))) + { + if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"small");} + } + else { # select a large cloud + if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"large");} + } + + + if (flag==0){ + if (thread_flag == 1) + {create_cloud_vec(path, lat, long, alt, 0.0);} + else + {compat_layer.create_cloud(path, lat, long, alt, 0.0);} + + + } + } + + } + +} + +########################################################### +# place an undulatus pattern +########################################################### + +var create_undulatus = func (type, blat, blong, balt, alt_var, nx, xoffset, edgex, x_var, ny, yoffset, edgey, y_var, und_strength, direction, tri) { + +var flag = 0; +var path = "Models/Weather/blank.ac"; +calc_geo(blat); +var dir = direction * math.pi/180.0; + +var ymin = -0.5 * ny * yoffset; +var xmin = -0.5 * nx * xoffset; +var xinc = xoffset * (tri-1.0) /ny; + +var jlow = int(nx*edgex); +var ilow = int(ny*edgey); + +var und = 0.0; +var und_array = []; + +for (var i=0; i(nx-jlow-1))) and ((i(ny-ilow-1)))) # select a small or no cloud + { + if (rn > 2.0) {flag = 1;} else {path = select_cloud_model(type,"small");} + } + if ((j(nx-jlow-1)) or (i(ny-ilow-1))) + { + if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"small");} + } + else { # select a large cloud + if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"large");} + } + + + if (flag==0){ + if (thread_flag == 1) + {create_cloud_vec(path, lat, long, alt, 0.0);} + else + {compat_layer.create_cloud(path, lat, long, alt, 0.0);} + + + } + } + + } + +} + + +########################################################### +# place a cloud layer +########################################################### + +var create_layer = func (type, blat, blon, balt, bthick, rx, ry, phi, density, edge, rainflag, rain_density) { + + +var i = 0; +var area = math.pi * rx * ry; +var circ = math.pi * (rx + ry); # that's just an approximation +var n = int(area/80000000.0 * 100 * density); +var m = int(circ/63000.0 * 40 * rain_density); +var path = "Models/Weather/blank.ac"; + +#print("density: ",n); + +phi = phi * math.pi/180.0; + +if (contains(cloud_vertical_size_map, type)) + {var alt_offset = cloud_vertical_size_map[type]/2.0 * m_to_ft;} + else {var alt_offset = 0.0;} + +while(i ((1.0 - edge) * (1.0- edge))) + { + if (rand() > 0.4) { + path = select_cloud_model(type,"small"); + if (thread_flag == 1) + {create_cloud_vec(path, lat, lon, alt, 0.0);} + else + {compat_layer.create_cloud(path, lat, lon, alt, 0.0);} + } + } + else { + path = select_cloud_model(type,"large"); + if (thread_flag == 1) + {create_cloud_vec(path, lat, lon, alt, 0.0);} + else + {compat_layer.create_cloud(path, lat, lon, alt, 0.0);} + } + i = i + 1; + } + } + +i = 0; + +if (rainflag ==1){ + +if (local_weather.hardcoded_clouds_flag == 1) {balt = balt + local_weather.offset_map[type]; } + + while(i (gap_fraction * gap_fraction))) + { + var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi)); + var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi)); + if (res > ((1.0 - edge) * (1.0- edge))) + { + if (rand() > 0.4) { + path = select_cloud_model(type,"small"); + compat_layer.create_cloud(path, lat, lon, alt, 0.0); + } + } + else { + path = select_cloud_model(type,"large"); + if (thread_flag == 1) + {create_cloud_vec(path, lat, lon, alt, 0.0);} + else + {compat_layer.create_cloud(path, lat, lon, alt, 0.0);} + } + i = i + 1; + } + else # we are in the central gap region + { + i = i + 1; + } + } + +i = 0; + + +} + + + + +########################################################### +# place a cloud box +########################################################### + + +var create_cloudbox = func (type, blat, blon, balt, dx,dy,dz,n, f_core, r_core, h_core, n_core, f_bottom, h_bottom, n_bottom) { + +var phi = 0; + +# first get core coordinates + +var core_dx = dx * f_core; +var core_dy = dy * f_core; +var core_dz = dz * h_core; + +var core_x_offset = (1.0 * rand() - 0.5) * ((dx - core_dx) * r_core); +var core_y_offset = (1.0 * rand() - 0.5) * ((dy - core_dy) * r_core); + +# get the bottom geometry + +var bottom_dx = dx * f_bottom; +var bottom_dy = dy * f_bottom; +var bottom_dz = dz * h_bottom; + +var bottom_offset = 400.0; # in practice, need a small shift + +# fill the main body of the box + +for (var i=0; i core_x_offset - 0.5 * core_dx) and (x < core_x_offset + 0.5 * core_dx)) + { + if ((y > core_y_offset - 0.5 * core_dy) and (y < core_y_offset + 0.5 * core_dy)) + { + i = i -1; + continue; + } + } + + var alt = balt + bottom_dz + bottom_offset + dz * rand(); + + var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi)); + var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi)); + + var path = select_cloud_model(type,"standard"); + + + create_cloud_vec(path, lat, lon, alt, 0.0); + + } + +# fill the core region + +for (var i=0; i 0.2) # we have below 20 fps + {n = 5;} + else if (dt > 0.1) # we have below 10 fps + {n = 10;} + else if (dt > 0.05) # we have below 5 fps + {n = 15;} + } +else + { + n = 250; n_out = 250; + } + +if (nc <= 0) # we're done and may analyze the result + { + terrain_presampling_analysis(); + if (debug_output_flag == 1) + {print("Presampling done!");} + setprop(lw~"tmp/presampling-status", "finished"); + return; + } + +terrain_presampling(blat, blon, n, size, alpha); + +settimer( func {terrain_presampling_loop(blat, blon, nc-n_out, size, alpha) },0); +} + + +########################################################### +# terrain presampling routine +########################################################### + +var terrain_presampling = func (blat, blon, ntries, size, alpha) { + +var phi = alpha * math.pi/180.0; +var elevation = 0.0; + +var lat_vec = []; +var lon_vec = []; +var lat_lon_vec = []; + + +for (var i=0; i int(0.5 *n_tot)) {alt_med = i * 500.0; break;} + } + + sum = 0; + for (var i=0; i<20;i=i+1) + { + sum = sum + terrain_n[i]; + if (sum > int(0.3 *n_tot)) {alt_20 = i * 500.0; break;} + } + + + for (var i=0; i<20;i=i+1) {alt_mean = alt_mean + terrain_n[i] * i * 500.0;} + alt_mean = alt_mean/n_tot; + + for (var i=0; i<20;i=i+1) {if (terrain_n[i] > 0) {alt_min = i * 500.0; break;}} + + var n_max = 0; + sum = 0; + + for (var i=0; i<19;i=i+1) + { + sum = sum + terrain_n[i]; + if (terrain_n[i] > n_max) {n_max = terrain_n[i];} + if ((n_max > terrain_n[i+1]) and (sum > int(0.3*n_tot))) + {alt_low_min = i * 500; break;} + } + } +else + { + #print("Hard-coded sampling..."); + var n_tot = getprop("/environment/terrain/area[0]/input/max-samples"); + var alt_mean = getprop("/environment/terrain/area[0]/output/alt-mean-ft"); + var alt_med = getprop("/environment/terrain/area[0]/output/alt-median-ft"); + var alt_min = getprop("/environment/terrain/area[0]/output/alt-min-ft"); + var alt_20 = getprop("/environment/terrain/area[0]/output/alt-offset-ft"); + } + +if (debug_output_flag == 1) + {print("Terrain presampling analysis results:"); + print("total: ",n_tot," mean: ",alt_mean," median: ",alt_med," min: ",alt_min, " alt_20: ", alt_20);} + + + +setprop(lw~"tmp/tile-alt-offset-ft",alt_20); +setprop(lw~"tmp/tile-alt-median-ft",alt_med); +setprop(lw~"tmp/tile-alt-min-ft",alt_min); +setprop(lw~"tmp/tile-alt-mean-ft",alt_mean); +setprop(lw~"tmp/tile-alt-layered-ft",0.5 * (alt_min + alt_20)); + +append(alt_50_array, alt_med); +append(alt_20_array, alt_20); +append(alt_min_array, alt_min); +append(alt_mean_array, alt_mean); +} + + + +########################################################### +# wave conditions search +########################################################### + +var wave_detection_loop = func (blat, blon, nx, alpha) { + +if (local_weather_running_flag == 0) {return;} + +var phi = alpha * math.pi/180.0; +var elevation = 0.0; +var ny = 20; + + +for (var i=0; i 1.0) {fraction = 1.0;} # no placement above maximum shift +if (fraction < 0.0) {fraction = 0.0;} # no downward shift + +# get the cloud base + +var cloudbase = balt - alt_offset; + +var alt_above_terrain = balt - elevation; + +# the shift strength is weakened if the layer is high above base elevation +# the reference altitude is 1000 ft, anything higher has less sensitivity to terrain + +var shift_strength = 1000.0/alt_above_terrain; + +if (shift_strength > 1.0) {shift_strength = 1.0;} # no enhancement for very low layers +if (shift_strength < 0.0) {shift_strength = 1.0;} # this shouldn't happen, but just in case... + +if (alt_diff > alt_variation) {alt_diff = alt_variation;} # maximal shift is given by alt_variation + +# print("balt: ", balt, " new alt: ", balt + shift_strength * alt_diff * fraction); + +return balt + shift_strength * alt_diff * fraction; + +} + + +########################################################### +# detailed terrain gradient determination in wind direction +########################################################### + + +var get_terrain_gradient = func (lat, lon, elevation1, phi, dist) { + + +# get the first elevation +# var elevation1 = compat_layer.get_elevation(lat,lon); + +# look upwind to learn about the history of the cloud +var elevation2 = compat_layer.get_elevation(lat+weather_tiles.get_lat(0.0,dist,phi), lon+weather_tiles.get_lon(0.0,dist,phi)); + +return (elevation2 - elevation1)/(dist * m_to_ft); +} + +########################################################### +# enhancement of the placement altitude due to terrain +########################################################### + +var get_gradient_factor = func (grad) { + +if (grad > 0.0) + {return 1.0;} +else + { + return 1.0 -2.0 * grad; + } +} + + +########################################################### +# suppression of placement in lee terrain +########################################################### + +var get_lee_bias = func (grad) { + + +if ((local_weather.wind_model_flag == 1) or (local_weather.wind_model_flag == 3)) + { + var windspeed = tile_wind_speed[0]; + } + else if ((local_weather.wind_model_flag ==2) or (local_weather.wind_model_flag == 4) or (local_weather.wind_model_flag == 5)) + { + var windspeed = tile_wind_speed[tile_index-1]; + } + + +if (grad < 0.0) + {return 1.0;} +else + { + var lee_bias = 1.0 - (grad * 0.2 * windspeed); + } +if (lee_bias < 0.2) {lee_bias = 0.2;} + +return lee_bias; +} + +########################################################### +# enhancement of Cumulus in above average altitude +########################################################### + + +var get_terrain_altitude_factor = func (tile_index, balt, elevation) { + + +var alt_mean = alt_mean_array[tile_index -1]; +var alt_base = alt_20_array[tile_index -1]; + +var alt_layer = balt - alt_base; +var alt_above_terrain = balt - elevation; +var alt_above_mean = balt - alt_mean; + +# the cloud may still be above terrain even if the layer altitude is negative, but we want to avoid neg. factors here + +if (alt_above_terrain < 0.0) {alt_above_terrain = 0.0;} + +var norm_alt_diff = (alt_above_mean - alt_above_terrain)/alt_layer; + +if (norm_alt_diff > 0.0) + { + var terrain_altitude_factor = 1.0 + 2.0 * norm_alt_diff; + } + else + { + var terrain_altitude_factor = 1.0/(1.0 - 5.0 * norm_alt_diff); + } + +if (terrain_altitude_factor > 3.0) {terrain_altitude_factor = 3.0;} +if (terrain_altitude_factor < 0.1) {terrain_altitude_factor = 0.1;} + +return terrain_altitude_factor; +} + + +var get_terrain_strength_factor = func (terrain_altitude_factor) { + +return 1.0+ (0.5 * (terrain_altitude_factor-1.0)); + +} + + +########################################################### +# terrain presampling listener dispatcher +########################################################### + +var manage_presampling = func { + + + +var status = getprop(lw~"tmp/presampling-status"); + + +# we only take action when the analysis is done +if (status != "finished") {return;} + +if (getprop(lw~"tiles/tile-counter") == 0) # we deal with a tile setup call from the menu + { + set_tile(); + } +else # the tile setup call came from weather_tile_management + { + var lat = getprop(lw~"tiles/tmp/latitude-deg"); + var lon = getprop(lw~"tiles/tmp/longitude-deg"); + var code = getprop(lw~"tiles/tmp/code"); + var dir_index = getprop(lw~"tiles/tmp/dir-index"); + + weather_tile_management.generate_tile(code, lat, lon, dir_index); + } + + +# set status to idle again + +setprop(lw~"tmp/presampling-status", "idle"); + +} + + +########################################################### +# hardcoded terrain presampling listener dispatcher +########################################################### + +var manage_hardcoded_presampling = func { + +var status = getprop("/environment/terrain/area[0]/enabled"); + +print("Hard-coded terrain presampling status: ", status); + +# no action unless the sampler has finished +if (status ==0) {return;} + +# no action if the sampler hasn't been started + +if (getprop(lw~"tmp/presampling-status") != "sampling") {return;} + +terrain_presampling_analysis(); +if (debug_output_flag == 1) + {print("Presampling done!");} +setprop(lw~"tmp/presampling-status", "finished"); + + +} + +########################################################### +# set wind model flag +########################################################### + +var set_wind_model_flag = func { + +var wind_model = getprop(lw~"config/wind-model"); + +if (wind_model == "constant") {wind_model_flag = 1;} +else if (wind_model == "constant in tile") {wind_model_flag =2;} +else if (wind_model == "aloft interpolated") {wind_model_flag =3; } +else if (wind_model == "airmass interpolated") {wind_model_flag =4;} +else if (wind_model == "aloft waypoints") {wind_model_flag =5;} +else {print("Wind model not implemented!"); wind_model_flag =1;} + + +} + + +########################################################### +# set texture mix for convective clouds +########################################################### + +var set_texture_mix = func { + +var thermal_properties = getprop(lw~"config/thermal-properties"); + + +convective_texture_mix = -(thermal_properties - 1.0) * 0.4; + +if (convective_texture_mix < -0.2) {convective_texture_mix = -0.2;} +if (convective_texture_mix > 0.2) {convective_texture_mix = 0.2;} +} + +########################################################### +# create an effect volume +########################################################### + +var create_effect_volume = func (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag, sat) { + + +var ev = effectVolume.new (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag, sat); +ev.index = getprop(lw~"tiles/tile-counter"); +ev.active_flag = 0; + + +if (vis < 0.0) {ev.vis_flag = 0;} else {ev.vis_flag = 1;} +if (rain < 0.0) {ev.rain_flag = 0;} else {ev.rain_flag = 1;} +if (snow < 0.0) {ev.snow_flag = 0;} else {ev.snow_flag = 1;} +if (turb < 0.0) {ev.turb_flag = 0;} else {ev.turb_flag = 1;} +if (lift_flag == 0.0) {ev.lift_flag = 0;} else {ev.lift_flag = 1;} +if (sat < 0.0) {ev.sat_flag = 0;} else {ev.sat_flag = 1;} +if (sat > 1.0) {sat = 1.0;} + +if (lift_flag == -2) # we create a thermal by function + { + ev.lift_flag = 2; + ev.radius = 0.8 * r1; + ev.height = alt_high * 0.87; + ev.cn = 0.7 + rand() * 0.2; + ev.sh = 0.7 + rand() * 0.2; + ev.max_lift = lift; + ev.f_lift_radius = 0.7 + rand() * 0.2; + if (dynamics_flag == 1) # globals set by the convective system + { + ev.flt = cloud_fractional_lifetime; + ev.evolution_timestamp = cloud_evolution_timestamp; + } + } + +if (lift_flag == -3) # we create a wave lift + { + ev.lift_flag = 3; + ev.height = 10000.0; # scale height in ft + ev.max_lift = lift; + ev.index = 0; # static objects are assigned tile id zero + } + +# set a timestamp if needed + +if (dynamics_flag == 1) + { + ev.timestamp = weather_dynamics.time_lw; + } + +# and add to the counter +setprop(lw~"effect-volumes/number",getprop(lw~"effect-volumes/number")+1); + +append(effectVolumeArray,ev); +} + + + + + +########################################################### +# set a weather station for interpolation +########################################################### + +var set_weather_station = func (lat, lon, alt, vis, T, D, p) { + +var s = weatherStation.new (lat, lon, alt, vis, T, D, p); +s.index = getprop(lw~"tiles/tile-counter"); +s.weight = 0.02; + +# set a timestamp if needed + +if (dynamics_flag == 1) + { + s.timestamp = weather_dynamics.time_lw; + } +append(weatherStationArray,s); + +} + + +########################################################### +# set an atmosphere condition point for interpolation +########################################################### + +var set_atmosphere_ipoint = func (lat, lon, vis_aloft, vis_alt1, vis_ovcst, ovcst,ovcst_alt_low, ovcst_alt_high, scatt, scatt_alt_low, scatt_alt_high) { + +var a = atmosphereIpoint.new (lat, lon, vis_aloft, vis_alt1, vis_ovcst, ovcst, ovcst_alt_low, ovcst_alt_high, scatt, scatt_alt_low, scatt_alt_high); +a.index = getprop(lw~"tiles/tile-counter"); +a.weight = 0.02; + +# set a timestamp if needed + +if (dynamics_flag == 1) + { + a.timestamp = weather_dynamics.time_lw; + } +append(atmosphereIpointArray,a); + +} + + +########################################################### +# set a wind interpolation point +########################################################### + +var set_wind_ipoint = func (lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8) { + +var w = windIpoint.new(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8); + +append(windIpointArray, w); + + +} + + +########################################################### +# set a wind interpolation point from ground METAR data +########################################################### + +var set_wind_ipoint_metar = func (lat, lon, d0, v0) { + +# insert a plausible pattern of aloft winds based on ground info + + +# direction of Coriolis deflection depends on hemisphere +if (lat >0.0) {var dsign = -1.0;} else {var dsign = 1.0;} + + +var v1 = v0 * (1.0 + rand() * 0.1); +var d1 = d0 + dsign * 2.0 * rand(); + +var v2 = v0 * (1.2 + rand() * 0.2); +var d2 = d0 + dsign * (3.0 * rand() + 2.0); + +var v3 = v0 * (1.3 + rand() * 0.4) + 5.0; +var d3 = d0 + dsign * (3.0 * rand() + dsign * 4.0); + +var v4 = v0 * (1.7 + rand() * 0.5) + 10.0; +var d4 = d0 + dsign * (4.0 * rand() + dsign * 8.0); + +var v5 = v0 * (1.7 + rand() * 0.5) + 20.0; +var d5 = d0 + dsign * (4.0 * rand() + dsign * 10.0); + +var v6 = v0 * (1.7 + rand() * 0.5) + 40.0; +var d6 = d0 + dsign * (4.0 * rand() + dsign * 12.0); + +var v7 = v0 * (2.0 + rand() * 0.7) + 50.0; +var d7 = d0 + dsign * (4.0 * rand() + dsign * 13.0); + +var v8 = v0 * (2.0 + rand() * 0.7) + 55.0;; +var d8 = d0 + dsign * (5.0 * rand() + dsign * 14.0); + +var w = windIpoint.new(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8); + +append(windIpointArray, w); + + + +} + +########################################################### +# helper to show additional dialogs +########################################################### + +var showDialog = func (name) { + +fgcommand("dialog-show", props.Node.new({"dialog-name":name})); + +} + + +########################################################### +# helper to transfer configuration flags in menu to Nasal +########################################################### + +var readFlags = func { + +# thermal lift must be 1 for constant thermals (obsolete), 2 for thermals by model (menu default) +# and 3 for blue thermals (set internally inside the tile only) + +if (getprop(lw~"config/generate-thermal-lift-flag") ==1) {generate_thermal_lift_flag = 2;} + else {generate_thermal_lift_flag = 0}; + +thread_flag = getprop(lw~"config/thread-flag"); +# dynamics_flag = getprop(lw~"config/dynamics-flag"); +presampling_flag = getprop(lw~"config/presampling-flag"); +detailed_clouds_flag = getprop(lw~"config/detailed-clouds-flag"); +dynamical_convection_flag = getprop(lw~"config/dynamical-convection-flag"); +debug_output_flag = getprop(lw~"config/debug-output-flag"); +fps_control_flag = getprop(lw~"config/fps-control-flag"); +realistic_visibility_flag = getprop(lw~"config/realistic-visibility-flag"); +detailed_terrain_interaction_flag = getprop(lw~"config/detailed-terrain-interaction-flag"); +scattering_shader_flag = getprop("/sim/rendering/scattering-shader"); + +} + +########################################################### +# wrappers to call functions from the local weather menu bar +########################################################### + +var streak_wrapper = func { + +thread_flag = 0; +dynamics_flag = 0; +presampling_flag = 0; + +var array = []; +append(weather_tile_management.modelArrays,array); +setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1); + +var lat = getprop("position/latitude-deg"); +var lon = getprop("position/longitude-deg"); +var type = getprop("/local-weather/tmp/cloud-type"); +var alt = getprop("/local-weather/tmp/alt"); +var nx = getprop("/local-weather/tmp/nx"); +var xoffset = getprop("/local-weather/tmp/xoffset"); +var xedge = getprop("/local-weather/tmp/xedge"); +var ny = getprop("/local-weather/tmp/ny"); +var yoffset = getprop("/local-weather/tmp/yoffset"); +var yedge = getprop("/local-weather/tmp/yedge"); +var dir = getprop("/local-weather/tmp/dir"); +var tri = getprop("/local-weather/tmp/tri"); +var rnd_alt = getprop("/local-weather/tmp/rnd-alt"); +var rnd_pos_x = getprop("/local-weather/tmp/rnd-pos-x"); +var rnd_pos_y = getprop("/local-weather/tmp/rnd-pos-y"); + +create_streak(type,lat,lon,alt,rnd_alt,nx,xoffset,xedge,rnd_pos_x,ny,yoffset,yedge,rnd_pos_y,dir,tri); +} + + +var convection_wrapper = func { + +thread_flag = 0; +dynamics_flag = 0; +presampling_flag = 0; + + +var array = []; +append(weather_tile_management.modelArrays,array); +setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1); + +var lat = getprop("position/latitude-deg"); +var lon = getprop("position/longitude-deg"); +var alt = getprop("/local-weather/tmp/conv-alt"); +var size = getprop("/local-weather/tmp/conv-size"); +var strength = getprop("/local-weather/tmp/conv-strength"); + +var n = int(10 * size * size * strength); +create_cumosys(lat,lon,alt,n, size*1000.0); + +} + +var barrier_wrapper = func { + + +thread_flag = 0; +dynamics_flag = 0; +presampling_flag = 0; + + +var array = []; +append(weather_tile_management.modelArrays,array); +setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1); + +var lat = getprop("position/latitude-deg"); +var lon = getprop("position/longitude-deg"); +var alt = getprop("/local-weather/tmp/bar-alt"); +var n = getprop("/local-weather/tmp/bar-n"); +var dir = getprop("/local-weather/tmp/bar-dir"); +var dist = getprop("/local-weather/tmp/bar-dist") * 1000.0; +var size = getprop("/local-weather/tmp/bar-size") * 1000.0; + +create_rise_clouds(lat, lon, alt, n, size, dir, dist); + +} + +var single_cloud_wrapper = func { + +thread_flag = 0; +dynamics_flag = 0; +presampling_flag = 0; + + + +var array = []; +append(weather_tile_management.modelArrays,array); +setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1); + +var type = getprop("/local-weather/tmp/scloud-type"); +var subtype = getprop("/local-weather/tmp/scloud-subtype"); +var lat = getprop("/local-weather/tmp/scloud-lat"); +var lon = getprop("/local-weather/tmp/scloud-lon"); +var alt = getprop("/local-weather/tmp/scloud-alt"); +var heading = getprop("/local-weather/tmp/scloud-dir"); + +var path = select_cloud_model(type,subtype); + +compat_layer.create_cloud(path, lat, lon, alt, heading); + +} + +var layer_wrapper = func { + +thread_flag = 0; +dynamics_flag = 0; +presampling_flag = 0; + + +var array = []; +append(weather_tile_management.modelArrays,array); +setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1); + +var lat = getprop("position/latitude-deg"); +var lon = getprop("position/longitude-deg"); +var type = getprop(lw~"tmp/layer-type"); +var rx = getprop(lw~"tmp/layer-rx") * 1000.0; +var ry = getprop(lw~"tmp/layer-ry") * 1000.0; +var phi = getprop(lw~"tmp/layer-phi"); +var alt = getprop(lw~"tmp/layer-alt"); +var thick = getprop(lw~"tmp/layer-thickness"); +var density = getprop(lw~"tmp/layer-density"); +var edge = getprop(lw~"tmp/layer-edge"); +var rain_flag = getprop(lw~"tmp/layer-rain-flag"); +var rain_density = getprop(lw~"tmp/layer-rain-density"); + +create_layer(type, lat, lon, alt, thick, rx, ry, phi, density, edge, rain_flag, rain_density); + +} + +var box_wrapper = func { + +thread_flag = 0; +dynamics_flag = 0; +presampling_flag = 0; + + +setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1); + +var lat = getprop("position/latitude-deg"); +var lon = getprop("position/longitude-deg"); +var alt = getprop("position/altitude-ft"); +var x = getprop(lw~"tmp/box-x-m"); +var y = getprop(lw~"tmp/box-y-m"); +var z = getprop(lw~"tmp/box-alt-ft"); +var n = getprop(lw~"tmp/box-n"); +var f_core = getprop(lw~"tmp/box-core-fraction"); +var r_core = getprop(lw~"tmp/box-core-offset"); +var h_core = getprop(lw~"tmp/box-core-height"); +var n_core = getprop(lw~"tmp/box-core-n"); +var f_bottom = getprop(lw~"tmp/box-bottom-fraction"); +var h_bottom = getprop(lw~"tmp/box-bottom-thickness"); +var n_bottom = getprop(lw~"tmp/box-bottom-n"); + +var type = "Box_test"; + + + +create_cloudbox(type, lat, lon, alt, x,y,z,n, f_core, r_core, h_core, n_core, f_bottom, h_bottom, n_bottom); + +} + + +var set_aloft_wrapper = func { + + + +var lat = getprop(lw~"tmp/ipoint-latitude-deg"); +var lon = getprop(lw~"tmp/ipoint-longitude-deg"); + +var d0 = getprop(lw~"tmp/FL0-wind-from-heading-deg"); +var v0 = getprop(lw~"tmp/FL0-windspeed-kt"); + +var d1 = getprop(lw~"tmp/FL50-wind-from-heading-deg"); +var v1 = getprop(lw~"tmp/FL50-windspeed-kt"); + +var d2 = getprop(lw~"tmp/FL100-wind-from-heading-deg"); +var v2 = getprop(lw~"tmp/FL100-windspeed-kt"); + +var d3 = getprop(lw~"tmp/FL180-wind-from-heading-deg"); +var v3 = getprop(lw~"tmp/FL180-windspeed-kt"); + +var d4 = getprop(lw~"tmp/FL240-wind-from-heading-deg"); +var v4 = getprop(lw~"tmp/FL240-windspeed-kt"); + +var d5 = getprop(lw~"tmp/FL300-wind-from-heading-deg"); +var v5 = getprop(lw~"tmp/FL300-windspeed-kt"); + +var d6 = getprop(lw~"tmp/FL340-wind-from-heading-deg"); +var v6 = getprop(lw~"tmp/FL340-windspeed-kt"); + +var d7 = getprop(lw~"tmp/FL390-wind-from-heading-deg"); +var v7 = getprop(lw~"tmp/FL390-windspeed-kt"); + +var d8 = getprop(lw~"tmp/FL450-wind-from-heading-deg"); +var v8 = getprop(lw~"tmp/FL450-windspeed-kt"); + +set_wind_ipoint(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8); + +if (wind_model_flag == 5) +{setprop(lwi~"ipoint-number", getprop(lwi~"ipoint-number") + 1);} + +} + +#################################### +# tile setup call wrapper +#################################### + +var set_tile = func { + +# check if another instance of local weather is running already + + +if (local_weather_running_flag == 1) + { + setprop("/sim/messages/pilot", "Local weather: Local weather is already running, use Clear/End before restarting. Aborting..."); + return; + } + +local_weather_startup_flag = 1; + + +var type = getprop("/local-weather/tmp/tile-type"); + +# set tile center coordinates to current position + +var lat = getprop("position/latitude-deg"); +var lon = getprop("position/longitude-deg"); + +setprop(lw~"tiles/tmp/latitude-deg",lat); +setprop(lw~"tiles/tmp/longitude-deg",lon); +setprop(lw~"tiles/tmp/dir-index",4); + +readFlags(); + +# check consistency of flags + +if (dynamical_convection_flag == 1) + { + if (dynamics_flag == 0) + { + print("Dynamical convection needs weather dynamics to run! Aborting..."); + setprop("/sim/messages/pilot", "Local weather: dynamical convection needs weather dynamics to run! Aborting..."); + return; + } + if (presampling_flag == 0) + { + print("Dynamical convection needs terrain presampling to run! Aborting..."); + setprop("/sim/messages/pilot", "Local weather: dynamical convection needs terrain presampling to run! Aborting..."); + return; + } + } + +if (detailed_terrain_interaction_flag == 1) + { + if (presampling_flag == 0) + { + print("Terrain effect needs terrain presampling to run! Aborting..."); + setprop("/sim/messages/pilot", "Local weather: terrain effect needs terrain presampling to run! Aborting..."); + return; + } + } + + +# if we can do so, we switch global weather and METAR parsing in environment off at this point + +if (compat_layer.features.can_disable_environment ==1) + { + props.globals.getNode("/environment/config/enabled").setBoolValue(0); + props.globals.getNode("/environment/params/metar-updates-environment").setBoolValue(0); + } + + +# switch off normal 3d clouds + +local_weather.setDefaultCloudsOff(); + +# now see if we need to presample the terrain + +if ((presampling_flag == 1) and (getprop(lw~"tmp/presampling-status") == "idle")) + { + terrain_presampling_start(lat, lon, 1000, 40000, getprop(lw~"tmp/tile-orientation-deg")); + return; + } + + +# indicate that we're up and running + +local_weather_startup_flag = 0; +local_weather_running_flag = 1; + +# see if we use METAR for weather setup + +if ((getprop("/environment/metar/valid") == 1) and (getprop(lw~"tmp/tile-management") == "METAR")) + { + type = "METAR"; + metar_flag = 1; + + setprop(lw~"METAR/station-id","METAR"); + + + + } +else if ((getprop("/environment/metar/valid") == 0) and (getprop(lw~"tmp/tile-management") == "METAR")) + { + print("No METAR available, aborting..."); + setprop("/sim/messages/pilot", "Local weather: No METAR available! Aborting..."); + return; + } + + +# see if we need to create an aloft wind interpolation structure + +if ((wind_model_flag == 3) or ((wind_model_flag ==5) and (getprop(lwi~"ipoint-number") == 0))) + { + if (metar_flag != 1) + {set_aloft_wrapper();} + } + + +# prepare the first tile wind field + +if (metar_flag == 1) # the winds from current METAR are used + { + + # METAR reports ground winds, we want to set aloft, so we need to compute the local boundary layer + # need to set the tile index for this + setprop(lw~"tiles/tile[4]/tile-index",1); + + var boundary_correction = 1.0/get_slowdown_fraction(); + var metar_base_wind_deg = getprop("environment/metar/base-wind-dir-deg"); + var metar_base_wind_speed = boundary_correction * getprop("environment/metar/base-wind-speed-kt"); + + # set the wind hash for the new scheme + + wind.cloudlayer = [metar_base_wind_deg,metar_base_wind_speed]; + wind.surface = [metar_base_wind_deg,metar_base_wind_speed/boundary_correction]; + wind.current = wind.surface; + + + if ((wind_model_flag == 1) or (wind_model_flag == 2)) + { + append(weather_dynamics.tile_wind_direction, metar_base_wind_deg); + append(weather_dynamics.tile_wind_speed, metar_base_wind_speed); + setprop(lw~"tmp/tile-orientation-deg",metar_base_wind_deg); + } + else if (wind_model_flag == 5) + { + var station_lat = getprop("/environment/metar/station-latitude-deg"); + var station_lon = getprop("/environment/metar/station-longitude-deg"); + + set_wind_ipoint_metar(station_lat, station_lon, metar_base_wind_deg, metar_base_wind_speed); + + var res = wind_interpolation(lat,lon,0.0); + + append(weather_dynamics.tile_wind_direction,res[0]); + append(weather_dynamics.tile_wind_speed,res[1]); + setprop(lw~"tmp/tile-orientation-deg", weather_dynamics.tile_wind_direction[0]); + } + else + { + print("Wind model currently not supported with live data!"); + setprop("/sim/messages/pilot", "Local weather: Wind model currently not supported with live data! Aborting..."); + return; + } + } +else + { + setprop(lw~"tiles/tile[4]/tile-index",1); + var boundary_correction = get_slowdown_fraction(); + + if (wind_model_flag == 5) # it needs to be interpolated + { + var res = wind_interpolation(lat,lon,0.0); + + append(weather_dynamics.tile_wind_direction,res[0]); + append(weather_dynamics.tile_wind_speed,res[1]); + + # set the wind hash for the new scheme + + wind.surface = [res[0],res[1] * boundary_correction]; + wind.cloudlayer = res; + wind.current = wind.surface; + + + } + else if (wind_model_flag == 3) # it comes from a different menu + { + append(weather_dynamics.tile_wind_direction, getprop(lw~"tmp/FL0-wind-from-heading-deg")); + append(weather_dynamics.tile_wind_speed, getprop(lw~"tmp/FL0-windspeed-kt")); + + # set the wind hash for the new scheme + wind.cloudlayer = [getprop(lw~"tmp/FL0-wind-from-heading-deg"),getprop(lw~"tmp/FL0-windspeed-kt") ]; + wind.surface = [wind.cloudlayer[0],wind.cloudlayer[1] * boundary_correction]; + wind.current = wind.surface; + + } + else # it comes from the standard menu + { + append(weather_dynamics.tile_wind_direction, getprop(lw~"tmp/tile-orientation-deg")); + append(weather_dynamics.tile_wind_speed, getprop(lw~"tmp/windspeed-kt")); + + # set the wind hash for the new scheme + wind.cloudlayer = [getprop(lw~"tmp/tile-orientation-deg"),getprop(lw~"tmp/windspeed-kt")]; + wind.surface = [wind.cloudlayer[0],wind.cloudlayer[1] * boundary_correction]; + wind.current = wind.surface; + + } + + # when the aloft wind menu is used, the lowest winds should be taken from there + # so we need to overwrite the setting from the tile generating menu in this case + # otherwise the wrong orientation is built + + + if (wind_model_flag ==3) + { + setprop(lw~"tmp/tile-orientation-deg", getprop(lw~"tmp/FL0-wind-from-heading-deg")); + } + else if (wind_model_flag == 5) + { + setprop(lw~"tmp/tile-orientation-deg", weather_dynamics.tile_wind_direction[0]); + } +} + +# create all the neighbouring tile coordinate sets + +weather_tile_management.create_neighbours(lat,lon,getprop(lw~"tmp/tile-orientation-deg")); + + + + +setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1); + + +# see if we need to generate a quadtree structure for clouds + +if (dynamics_flag ==1) + { + var quadtree = []; + weather_dynamics.generate_quadtree_structure(0, quadtree); + append(weather_dynamics.cloudQuadtrees,quadtree); + } + + + + +if (type == "High-pressure-core") + {weather_tiles.set_high_pressure_core_tile();} +else if (type == "High-pressure") + {weather_tiles.set_high_pressure_tile();} +else if (type == "High-pressure-border") + {weather_tiles.set_high_pressure_border_tile();} +else if (type == "Low-pressure-border") + {weather_tiles.set_low_pressure_border_tile();} +else if (type == "Low-pressure") + {weather_tiles.set_low_pressure_tile();} +else if (type == "Low-pressure-core") + {weather_tiles.set_low_pressure_core_tile();} +else if (type == "Cold-sector") + {weather_tiles.set_cold_sector_tile();} +else if (type == "Warm-sector") + {weather_tiles.set_warm_sector_tile();} +else if (type == "Tropical") + {weather_tiles.set_tropical_weather_tile();} +else if (type == "Coldfront") + {weather_tiles.set_coldfront_tile();} +else if (type == "Warmfront") + {weather_tiles.set_warmfront1_tile();} +else if (type == "Warmfront-2") + {weather_tiles.set_warmfront2_tile();} +else if (type == "Warmfront-3") + {weather_tiles.set_warmfront3_tile();} +else if (type == "Warmfront-4") + {weather_tiles.set_warmfront4_tile();} +else if (type == "METAR") + {weather_tiles.set_METAR_tile();} +else if (type == "Altocumulus sky") + {weather_tiles.set_altocumulus_tile();setprop(lw~"tiles/code","altocumulus_sky");} +else if (type == "Broken layers") + {weather_tiles.set_broken_layers_tile();setprop(lw~"tiles/code","broken_layers");} +else if (type == "Cold front") + {weather_tiles.set_coldfront_tile();setprop(lw~"tiles/code","coldfront");} +else if (type == "Cirrus sky") + {weather_tiles.set_cirrus_sky_tile();setprop(lw~"tiles/code","cirrus_sky");} +else if (type == "Fair weather") + {setprop(lw~"tiles/code","cumulus_sky");weather_tiles.set_fair_weather_tile();} +else if (type == "Glider's sky") + {setprop(lw~"tiles/code","gliders_sky");weather_tiles.set_gliders_sky_tile();} +else if (type == "Blue thermals") + {setprop(lw~"tiles/code","blue_thermals");weather_tiles.set_blue_thermals_tile();} +else if (type == "Incoming rainfront") + {weather_tiles.set_rainfront_tile();setprop(lw~"tiles/code","rainfront");} +else if (type == "8/8 stratus sky") + {weather_tiles.set_overcast_stratus_tile();setprop(lw~"tiles/code","overcast_stratus");} +else if (type == "Test tile") + {weather_tiles.set_4_8_stratus_tile();setprop(lw~"tiles/code","test");} +else if (type == "Summer rain") + {weather_tiles.set_summer_rain_tile();setprop(lw~"tiles/code","summer_rain");} +else + {print("Tile not implemented.");setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")-1);return();} + + +# mark tile as active + +append(weather_tile_management.active_tile_list,1); + +# start tile management loop if needed + +if (getprop(lw~"tmp/tile-management") != "single tile") { + if (getprop(lw~"tile-loop-flag") == 0) + { + setprop(lw~"tiles/tile[4]/code",getprop(lw~"tiles/code")); + setprop(lw~"tile-loop-flag",1); + weather_tile_management.tile_management_loop();} + } + +# start the interpolation loop + +if (getprop(lw~"interpolation-loop-flag") == 0) +{setprop(lw~"interpolation-loop-flag",1); local_weather.interpolation_loop();} + +# start the effect volume loop + +if (getprop(lw~"effect-loop-flag") == 0) +{setprop(lw~"effect-loop-flag",1); local_weather.effect_volume_loop(0,0);} + +# start weather dynamics loops if needed + +if (getprop(lw~"timing-loop-flag") == 0) + {setprop(lw~"timing-loop-flag",1); local_weather.timing_loop();} + +if (dynamics_flag ==1) + { + + + if (getprop(lw~"dynamics-loop-flag") == 0) + { + setprop(lw~"dynamics-loop-flag",1); + weather_dynamics.quadtree_loop(); + weather_dynamics.weather_dynamics_loop(0,0); + } + if ((getprop(lw~"convective-loop-flag") == 0) and (getprop(lw~"config/dynamical-convection-flag") ==1)) + { + setprop(lw~"convective-loop-flag",1); + weather_dynamics.convective_loop(); + } + } + + + + +# and start the buffer loop and housekeeping loop if needed + +if (buffer_flag == 1) + { + if (getprop(lw~"buffer-loop-flag") == 0) + { + setprop(lw~"buffer-loop-flag",1); weather_tile_management.buffer_loop(0); + setprop(lw~"housekeeping-loop-flag",1); weather_tile_management.housekeeping_loop(0,0); + } + } + + + +# weather_tile_management.watchdog_loop(); + +} + + +################################################# +# Anything that needs to run at startup goes here +################################################# + +var startup = func { +print("Loading local weather routines..."); + +# get local Cartesian geometry + +var lat = getprop("position/latitude-deg"); +var lon = getprop("position/longitude-deg"); +calc_geo(lat); + + +# copy weather properties at startup to local weather + +setprop(lw~"interpolation/visibility-m",getprop(ec~"boundary/entry[0]/visibility-m")); +setprop(lw~"interpolation/pressure-sea-level-inhg",getprop(ec~"boundary/entry[0]/pressure-sea-level-inhg")); +setprop(lw~"interpolation/temperature-degc",getprop(ec~"boundary/entry[0]/temperature-degc")); +setprop(lw~"interpolation/wind-from-heading-deg",getprop(ec~"boundary/entry[0]/wind-from-heading-deg")); +setprop(lw~"interpolation/wind-speed-kt",getprop(ec~"boundary/entry[0]/wind-speed-kt")); +setprop(lw~"interpolation/turbulence",getprop(ec~"boundary/entry[0]/turbulence/magnitude-norm")); + +setprop(lw~"interpolation/rain-norm",0.0); +setprop(lw~"interpolation/snow-norm",0.0); +setprop(lw~"interpolation/thermal-lift",0.0); + + +# before interpolation starts, these are also initially current + +setprop(lw~"current/visibility-m",getprop(lwi~"visibility-m")); +setprop(lw~"current/pressure-sea-level-inhg",getprop(lw~"interpolation/pressure-sea-level-inhg")); +setprop(lw~"current/temperature-degc",getprop(lw~"interpolation/temperature-degc")); +setprop(lw~"current/wind-from-heading-deg",getprop(lw~"interpolation/wind-from-heading-deg")); +setprop(lw~"current/wind-speed-kt",getprop(lw~"interpolation/wind-speed-kt")); +setprop(lw~"current/rain-norm",getprop(lw~"interpolation/rain-norm")); +setprop(lw~"current/snow-norm",getprop(lw~"interpolation/snow-norm")); +setprop(lw~"current/thermal-lift",getprop(lw~"interpolation/thermal-lift")); +setprop(lw~"current/turbulence",getprop(lwi~"turbulence")); + +# create default properties for METAR system, should be overwritten by real-weather-fetch + +setprop(lw~"METAR/latitude-deg",lat); +setprop(lw~"METAR/longitude-deg",lon); +setprop(lw~"METAR/altitude-ft",0.0); +setprop(lw~"METAR/wind-direction-deg",0.0); +setprop(lw~"METAR/wind-strength-kt",10.0); +setprop(lw~"METAR/visibility-m",17000.0); +setprop(lw~"METAR/rain-norm",0.0); +setprop(lw~"METAR/snow-norm",0.0); +setprop(lw~"METAR/temperature-degc",10.0); +setprop(lw~"METAR/dewpoint-degc",7.0); +setprop(lw~"METAR/pressure-inhg",29.92); +setprop(lw~"METAR/thunderstorm-flag",0); +setprop(lw~"METAR/layer[0]/cover-oct",4); +setprop(lw~"METAR/layer[0]/alt-agl-ft", 3000.0); +setprop(lw~"METAR/layer[1]/cover-oct",0); +setprop(lw~"METAR/layer[1]/alt-agl-ft", 20000.0); +setprop(lw~"METAR/layer[2]/cover-oct",0); +setprop(lw~"METAR/layer[2]/alt-agl-ft", 20000.0); +setprop(lw~"METAR/layer[3]/cover-oct",0); +setprop(lw~"METAR/layer[3]/alt-agl-ft", 20000.0); +setprop(lw~"METAR/available-flag",1); + + + + +# set listeners + +setlistener(lw~"tmp/thread-status", func {var s = size(clouds_path); compat_layer.create_cloud_array(s, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation); }); +setlistener(lw~"tmp/convective-status", func {var s = size(clouds_path); compat_layer.create_cloud_array(s, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation); }); +setlistener(lw~"tmp/effect-thread-status", func {var s = size(effects_geo); effect_placement_loop(s); }); +setlistener(lw~"tmp/presampling-status", func {manage_presampling(); }); + +setlistener(lw~"config/wind-model", func {set_wind_model_flag();}); +setlistener(lw~"config/thermal-properties", func {set_texture_mix();}); + +setlistener(lw~"config/clouds-in-dynamics-loop", func {weather_dynamics.max_clouds_in_loop = int(getprop(lw~"config/clouds-in-dynamics-loop"));}); + +setlistener(lw~"config/clouds-visible-range-m", func {weather_tile_management.cloud_view_distance = getprop(lw~"config/clouds-visible-range-m");}); +setlistener(lw~"config/distance-to-load-tile-m", func {setprop(lw~"config/distance-to-remove-tile-m",getprop(lw~"config/distance-to-load-tile-m") + 500.0);}); + +setlistener(lw~"config/fps-control-flag", func {fps_control_flag = getprop(lw~"config/fps-control-flag");}); +setlistener(lw~"config/target-framerate", func {target_framerate = getprop(lw~"config/target-framerate");}); + +setlistener(lw~"config/small-scale-persistence", func {weather_tiles.small_scale_persistence = getprop(lw~"config/small-scale-persistence");}); +setlistener(lw~"config/ground-haze-factor", func {ground_haze_factor = getprop(lw~"config/ground-haze-factor");}); +setlistener(lw~"config/max-vis-range-m", func {max_vis_range = getprop(lw~"config/max-vis-range-m");}); + +setlistener(lw~"config/temperature-offset-degc", func {temperature_offset = getprop(lw~"config/temperature-offset-degc");}); + +setlistener("/sim/rendering/scattering-shader", func {scattering_shader_flag = getprop("/sim/rendering/scattering-shader"); }); +} + + +##################################################### +# Standard test call (for development and debug only) +##################################################### + +var test = func { + +var lat = getprop("position/latitude-deg"); +var lon = getprop("position/longitude-deg"); + +thread_flag = 0; +dynamics_flag = 0; +presampling_flag = 0; + + +#if (compat_layer.features.can_disable_environment ==1) +# { +# props.globals.getNode("/environment/config/enabled").setBoolValue(0); +# props.globals.getNode("/environment/params/metar-updates-environment").setBoolValue(0); +# } +# +#compat_layer.setDefaultCloudsOff(); + +#var array = []; +#append(weather_tile_management.modelArrays,array); +#setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1); + + +#var pos = geo.aircraft_position(); + +#debug.dump(geodinfo(lat, lon)); + +#create_cumulonimbus_cloud(lat, lon, 6000.0, 2.5); + +# geo.put_model("Models/Astro/Earth.ac",lat, lon); + +#setprop("/environment/terrain/area[0]/input/latitude-deg", lat ); +#setprop("/environment/terrain/area[0]/input/longitude-deg", lon ); + +#setprop("/environment/terrain/area[0]/output/valid", 0 ); + +elttest(); + +} + + +var elttest = func { + +var lat_uncertainty = 0.001; +var lon_uncertainty = 0.001; + +#var lat = getprop("/position/latitude-deg") + lat_uncertainty * 0.5 - rand(); +#var lon = getprop("/position/longitude-deg") + lon_uncertainty * 0.5 - rand(); +var lat = getprop("/position/latitude-string"); +var lon = getprop("/position/longitude-string"); +var aircraft = getprop("sim/description"); +var callsign = getprop("sim/multiplay/callsign"); +var help_string = "ELT AutoMessage: " ~ aircraft ~ " " ~ callsign ~ " " ~lat~" LAT "~lon~" LON, requesting SAR service"; + +setprop("/sim/multiplay/chat", help_string); +} + + +################################################################# +# object classes +################################################################# + +var weatherStation = { + new: func (lat, lon, alt, vis, T, D, p) { + var s = { parents: [weatherStation] }; + s.lat = lat; + s.lon = lon; + s.alt = alt; + s.vis = vis; + s.T = T; + s.D = D; + s.p = p; + s.scattering = 0.8; + return s; + }, + move: func { + var windfield = weather_dynamics.get_windfield(me.index); + var dt = weather_dynamics.time_lw - me.timestamp; + me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat; + me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon; + me.timestamp = weather_dynamics.time_lw; + }, +}; + + +var atmosphereIpoint = { + new: func (lat, lon, vis_aloft, vis_alt1, vis_ovcst, ovcst, ovcst_alt_low, ovcst_alt_high, scatt, scatt_alt_low, scatt_alt_high){ + var a = { parents: [atmosphereIpoint] }; + a.lat = lat; + a.lon = lon; + a.vis_aloft = vis_aloft; + a.vis_alt1 = vis_alt1; + a.vis_ovcst = vis_ovcst; + a.ovcst = ovcst; + a.ovcst_alt_low = ovcst_alt_low; + a.ovcst_alt_high = ovcst_alt_high; + a.scatt = scatt; + a.scatt_alt_low = scatt_alt_low; + a.scatt_alt_high = scatt_alt_high; + return a; + }, + move: func { + var windfield = weather_dynamics.get_windfield(me.index); + var dt = weather_dynamics.time_lw - me.timestamp; + me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat; + me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon; + me.timestamp = weather_dynamics.time_lw; + }, +}; + + +var windIpoint = { + new: func (lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8) { + var w = { parents: [windIpoint] }; + w.lat = lat; + w.lon = lon; + + altvec = []; + + var wv = windVec.new(d0, v0); + append(altvec,wv); + + wv = windVec.new(d1, v1); + append(altvec, wv); + + wv = windVec.new(d2, v2); + append(altvec, wv); + + wv = windVec.new(d3, v3); + append(altvec, wv); + + wv = windVec.new(d4, v4); + append(altvec, wv); + + wv = windVec.new(d5, v5); + append(altvec, wv); + + wv = windVec.new(d6, v6); + append(altvec, wv); + + wv = windVec.new(d7, v7); + append(altvec, wv); + + wv = windVec.new(d8, v8); + append(altvec, wv); + + w.alt = altvec; + + w.weight = 0.02; + return w; + }, +}; + +var windVec = { + new: func (d, v) { + var wv = { parents: [windVec] }; + wv.d = d; + wv.v = v; + return wv; + }, + +}; + + + + + +var effectVolume = { + new: func (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag, sat) { + var e = { parents: [effectVolume] }; + e.geometry = geometry; + e.lat = lat; + e.lon = lon; + e.r1 = r1; + e.r2 = r2; + e.phi = phi; + e.alt_low = alt_low; + e.alt_high = alt_high; + e.vis = vis; + e.rain = rain; + e.snow = snow; + e.turb = turb; + e.lift = lift; + e.lift_flag = lift_flag; + e.sat = sat; + return e; + }, + move: func { + var windfield = weather_dynamics.get_windfield(me.index); + var dt = weather_dynamics.time_lw - me.timestamp; + me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat; + me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon; + me.timestamp = weather_dynamics.time_lw; + }, + correct_altitude: func { + var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + alt_20_array[me.index-1]; + var elevation = compat_layer.get_elevation(me.lat, me.lon); + me.alt_high = local_weather.get_convective_altitude(convective_alt, elevation, me.index,0.0) *1.15; + me.height = me.alt_high * 0.87; + }, + correct_altitude_and_age: func { + var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + local_weather.alt_20_array[me.index-1]; + var elevation = -1.0; var p_cover = 0.2; + var info = geodinfo(me.lat, me.lon); + if (info != nil) + { + elevation = info[0] * local_weather.m_to_ft; + if (info[1] != nil) + { + var landcover = info[1].names[0]; + if (contains(landcover_map,landcover)) {p_cover = landcover_map[landcover];} + else {p_cover = 0.2;} + } + } + me.alt_high = get_convective_altitude(convective_alt, elevation, me.index,0.0) * 1.15; + me.height = me.alt_high * 0.87; + var current_lifetime = math.sqrt(p_cover)/math.sqrt(0.35) * weather_dynamics.cloud_convective_lifetime_s; + var fractional_increase = (weather_dynamics.time_lw - me.evolution_timestamp)/current_lifetime; + me.flt = me.flt + fractional_increase; + me.evolution_timestamp = weather_dynamics.time_lw; + }, + get_distance: func { + var lat = getprop("position/latitude-deg"); + var lon = getprop("position/longitude-deg"); + return math.sqrt(calc_d_sq(lat, lon, me.lat, me.lon)); + }, +}; + + +var thermalLift = { + new: func (lat, lon, radius, height, cn, sh, max_lift, f_lift_radius) { + var l = { parents: [thermalLift] }; + l.lat = lat; + l.lon = lon; + l.radius = radius; + l.height = height; + l.cn = cn; + l.sh = sh; + l.max_lift = max_lift; + l.f_lift_radius = f_lift_radius; + return l; + }, + move: func { + var windfield = weather_dynamics.get_windfield(me.index); + var dt = weather_dynamics.time_lw - me.timestamp; + me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat; + me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon; + me.timestamp = weather_dynamics.time_lw; + }, + correct_altitude: func { + var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + alt_20_array[me.index-1]; + var elevation = compat_layer.get_elevation(me.lat, me.lon); + me.height = local_weather.get_convective_altitude(convective_alt, elevation, me.index,0.0); + }, + correct_altitude_and_age: func { + var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + local_weather.alt_20_array[me.index-1]; + var elevation = -1.0; var p_cover = 0.2; + var info = geodinfo(me.lat, me.lon); + if (info != nil) + { + elevation = info[0] * local_weather.m_to_ft; + if (info[1] != nil) + { + var landcover = info[1].names[0]; + if (contains(landcover_map,landcover)) {p_cover = landcover_map[landcover];} + else {p_cover = 0.2;} + } + } + me.height = get_convective_altitude(convective_alt, elevation, me.index,0.0); + var current_lifetime = math.sqrt(p_cover)/math.sqrt(0.35) * weather_dynamics.cloud_convective_lifetime_s; + var fractional_increase = (weather_dynamics.time_lw - me.evolution_timestamp)/current_lifetime; + me.flt = me.flt + fractional_increase; + me.evolution_timestamp = weather_dynamics.time_lw; + }, + +}; + + +var waveLift = { + new: func (lat, lon, x, y, phi, height, max_lift) { + var w = { parents: [waveLift] }; + w.lat = lat; + w.lon = lon; + w.x = x; + w.y = y; + w.phi = phi; + w.height = height; + w.max_lift = max_lift; + w.phi = getprop(lw~"tmp/tile-orientation-deg"); + return w; + }, + +}; + + + +################################################################# +# global variable, property creation and the startup listener +################################################################# + +var rad_E = 6378138.12; # earth radius +var lat_to_m = 110952.0; # latitude degrees to meters +var m_to_lat = 9.01290648208234e-06; # meters to latitude degrees +var ft_to_m = 0.30480; +var m_to_ft = 1.0/ft_to_m; + +var lon_to_m = 0.0; # needs to be calculated dynamically +var m_to_lon = 0.0; # we do this on startup + +# some common abbreviations + +var lw = "/local-weather/"; +var lwi = "/local-weather/interpolation/"; +var ec = "/environment/config/"; + +# a hash map of the strength for convection associated with terrain types + +var landcover_map = {BuiltUpCover: 0.35, Town: 0.35, Freeway:0.35, BarrenCover:0.3, HerbTundraCover: 0.25, GrassCover: 0.2, CropGrassCover: 0.2, EvergreenBroadCover: 0.2, EvergreenNeedleCover: 0.2, Sand: 0.25, Grass: 0.2, Ocean: 0.01, Marsh: 0.05, Lake: 0.01, ShrubCover: 0.15, Landmass: 0.2, CropWoodCover: 0.15, MixedForestCover: 0.15, DryCropPastureCover: 0.25, MixedCropPastureCover: 0.2, IrrCropPastureCover: 0.15, DeciduousBroadCover: 0.1, DeciduousNeedleCover: 0.1, Bog: 0.05, pa_taxiway : 0.35, pa_tiedown: 0.35, pc_taxiway: 0.35, pc_tiedown: 0.35, Glacier: 0.03, SnowCover: 0.04, DryLake: 0.3, IntermittentStream: 0.2, DryCrop: 0.2, Lava: 0.3, GolfCourse: 0.2, Rock: 0.3, Construction: 0.35}; + +# a hash map of average vertical cloud model sizes + +var cloud_vertical_size_map = {Altocumulus: 700.0, Cumulus: 600.0, Congestus: 2000.0, Nimbus: 1000.0, Stratus: 800.0, Stratus_structured: 600.0, Stratus_thin: 400.0, Cirrocumulus: 200.0, Cb_box: 2000.0}; + +# a hash map of offsets for the new cloud rendering system + +var offset_map = {Nimbus: 2800.0, Stratus: 2000.0, Stratus_thin: 2500.0, Cirrostratus: 4500.0, Stratus_structured: 1800.0, Stratus_alt: 600.0, Cumulus: 200.0, Congestus: 600.0 }; + +# the array of aloft wind interpolation altitudes + +var wind_altitude_array = [0.0, 5000.0, 10000.0, 18000.0, 24000.0, 30000.0, 34000.0, 39000.0, 45000.0]; + +# storage arrays for cloud generation + +var clouds_path = []; +var clouds_lat = []; +var clouds_lon = []; +var clouds_alt = []; +var clouds_orientation = []; + + + + +# storage array for assembled clouds + +var cloudAssemblyArray = []; + +# additional info needed for dynamical clouds: the base altitude around which cloudlets are distributed +# and the fractional lifetime + +var clouds_mean_alt = []; +var clouds_flt = []; +var clouds_evolution_timestamp = []; + + +# storage arrays for terrain presampling and results by tile + +var terrain_n = []; +var alt_50_array = []; +var alt_20_array = []; +var alt_min_array = []; +var alt_mean_array = []; + +# array of currently existing effect volumes + +var effectVolumeArray = []; +var n_effectVolumeArray = 0; + +# the thermal and the wave hash + +var thermal = {}; +var wave = {}; + +# the wind hash stores the current winds + +var wind = {surface: [0.0,0.0] , cloudlayer: [0.0,0.0], current: [0.0,0.0]}; + + + +# arrays of currently existing weather stations, wind interpolation and atmospheric condition points + +var weatherStationArray = []; +var windIpointArray = []; +var atmosphereIpointArray = []; + + +# a flag for the wind model (so we don't have to do string comparisons all the time) +# 1: constant 2: constant in tile 3: aloft interpolated 4: airmass interpolated + +var wind_model_flag = 1; + +# globals governing properties of the Cumulus system + +var convective_texture_mix = 0.0; +var convective_size_bias = 0.0; +var cumulus_efficiency_factor = 1.0; +var cloud_mean_altitude = 0.0; + +# globals keeping track of the lifetime when building a Cumulus from individual cloudlets + +var cloud_fractional_lifetime = 0.0; +var cloud_evolution_timestamp = 0.0; + +# globals propagating gust information inside the interpolation loop + +var windspeed_multiplier = 1.0; +var winddir_change = 0.0; + +# global flags mirroring property tree menu settings + +var generate_thermal_lift_flag = 0; +var thread_flag = 1; +var dynamics_flag = 1; +var presampling_flag = 1; +var detailed_clouds_flag = 1; +var dynamical_convection_flag = 1; +var debug_output_flag = 1; +var metar_flag = 0; +var local_weather_running_flag = 0; +var local_weather_startup_flag = 0; +var fps_control_flag = 0; +var buffer_flag = 1; +var detailed_terrain_interaction_flag = 1; +var hardcoded_clouds_flag = 1; +var realistic_visibility_flag = 0; +var scattering_shader_flag = 0; + +var ground_haze_factor = 1.0; +var max_vis_range = 120000.0; +var temperature_offset = 0.0; + + +# globals for framerate controlled cloud management + +var fps_average = 0.0; +var fps_samples = 0; +var fps_sum = 0.0; +var target_framerate = 25.0; + +# set all sorts of default properties for the menu + + +setprop(lw~"tmp/scloud-lat",getprop("position/latitude-deg")); +setprop(lw~"tmp/scloud-lon",getprop("position/longitude-deg")); +setprop(lw~"tmp/tile-alt-median-ft",0.0); +setprop(lw~"tmp/tile-alt-min-ft",0.0); +setprop(lw~"tmp/last-reading-pos-del",0); +setprop(lw~"tmp/last-reading-pos-mod",0); +setprop(lw~"tmp/thread-status", "idle"); +setprop(lw~"tmp/convective-status", "idle"); +setprop(lw~"tmp/presampling-status", "idle"); +setprop(lw~"tmp/buffer-status", "idle"); +setprop(lw~"tmp/buffer-tile-index", 0); +setprop(lw~"tmp/ipoint-latitude-deg",getprop("position/latitude-deg")); +setprop(lw~"tmp/ipoint-longitude-deg",getprop("position/longitude-deg")); + + + +# set the default loop flags to loops inactive + + +setprop(lw~"effect-loop-flag",0); +setprop(lw~"interpolation-loop-flag",0); +setprop(lw~"tile-loop-flag",0); +setprop(lw~"lift-loop-flag",0); +setprop(lw~"wave-loop-flag",0); +setprop(lw~"buffer-loop-flag",0); +setprop(lw~"housekeeping-loop-flag",0); +setprop(lw~"convective-loop-flag",0); + +# create other management properties + +#setprop(lw~"clouds/cloud-number",0); +setprop(lw~"clouds/placement-index",0); +setprop(lw~"clouds/model-placement-index",0); +setprop(lw~"effect-volumes/effect-placement-index",0); + +# create properties for effect volume management + +setprop(lw~"effect-volumes/number",0); +setprop(lw~"effect-volumes/number-active-vis",0); +setprop(lw~"effect-volumes/number-active-rain",0); +setprop(lw~"effect-volumes/number-active-snow",0); +setprop(lw~"effect-volumes/number-active-turb",0); +setprop(lw~"effect-volumes/number-active-lift",0); +setprop(lw~"effect-volumes/number-active-sat",0); + +setprop(lw~"config/max-vis-range-m", 120000.0); +setprop(lw~"config/temperature-offset-degc", 0.0); + +# create properties for tile management + +setprop(lw~"tiles/tile-counter",0); + +# create properties for wind + +setprop(lwi~"ipoint-number",0); + +var updateMenu = func { + var isEnabled = getprop("/nasal/local_weather/enabled"); + gui.menuEnable("local_weather", isEnabled); + gui.menuEnable("local_weather_tiles", isEnabled); +} + +_setlistener("/nasal/local_weather/enabled", updateMenu); + + diff --git a/Nasal/local_weather/cloud_definitions.nas b/Nasal/local_weather/cloud_definitions.nas index c85db3a3c..495fd4f6b 100644 --- a/Nasal/local_weather/cloud_definitions.nas +++ b/Nasal/local_weather/cloud_definitions.nas @@ -485,7 +485,7 @@ else if (type == "Stratocumulus bottom"){ { cloudAssembly = local_weather.cloud.new(type, subtype); - mult = 1.0; + # characterize the basic texture sheet cloudAssembly.texture_sheet = "/Models/Weather/cumulus_bottom_sheet1.rgb"; @@ -520,6 +520,46 @@ else if (type == "Stratocumulus bottom"){ } else if (type == "Cumulonimbus (cloudlet)"){ + +# new code + + if (local_weather.hardcoded_clouds_flag == 1) + { + cloudAssembly = local_weather.cloud.new(type, subtype); + + # characterize the basic texture sheet + + + cloudAssembly.num_tex_x = 2; + cloudAssembly.num_tex_y = 2; + + if (rand() < 0.5) + {cloudAssembly.texture_sheet = "/Models/Weather/cumulonimbus_sheet2.rgb";} + else + {cloudAssembly.texture_sheet = "/Models/Weather/cumulonimbus_sheet1.rgb";} + + var mult = 1.0; + if (subtype == "small") {mult = 0.7;} + + #characterize the cloud + cloudAssembly.bottom_shade = 0.6; + cloudAssembly.n_sprites = 5; + cloudAssembly.min_width = 1700.0 * mult; + cloudAssembly.max_width = 2200.0 * mult; + cloudAssembly.min_height = 1700.0 * mult; + cloudAssembly.max_height = 2200.0 * mult; + cloudAssembly.min_cloud_width = 3500.0 * mult; + cloudAssembly.min_cloud_height = 3500.0 * mult; + cloudAssembly.z_scale = 1.0; + + #signal that new routines are used + path = "new"; + + } + else + { + + if (subtype == "small") { if (rn > 0.875) {path = "Models/Weather/cumulonimbus_sl1.xml";} else if (rn > 0.75) {path = "Models/Weather/cumulonimbus_sl2.xml";} @@ -540,6 +580,7 @@ else if (type == "Cumulonimbus (cloudlet)"){ else if (rn > 0.125) {path = "Models/Weather/cumulonimbus_sl7.xml";} else {path = "Models/Weather/cumulonimbus_sl8.xml";} } + } } @@ -885,24 +926,34 @@ else if (type == "Stratus") { { cloudAssembly = local_weather.cloud.new(type, subtype); - if (subtype == "small") {var mult = 0.8;} - else {var mult = 1.0;} - - # characterize the basic texture sheet - cloudAssembly.texture_sheet = "/Models/Weather/stratus_sheet1.rgb"; - cloudAssembly.num_tex_x = 3; - cloudAssembly.num_tex_y = 2; + if (subtype == "small") + { + var mult = 0.8; + cloudAssembly.texture_sheet = "/Models/Weather/cirrocumulus_sheet1.rgb"; + cloudAssembly.num_tex_x = 3; + cloudAssembly.num_tex_y = 3; + cloudAssembly.n_sprites = 10; + cloudAssembly.z_scale = 0.6; + } + else + { + var mult = 1.0; + cloudAssembly.texture_sheet = "/Models/Weather/stratus_sheet1.rgb"; + cloudAssembly.num_tex_x = 3; + cloudAssembly.num_tex_y = 2; + cloudAssembly.n_sprites = 10; + cloudAssembly.z_scale = 0.4; + } #characterize the cloud cloudAssembly.bottom_shade = 0.4; - cloudAssembly.n_sprites = 20; - cloudAssembly.min_width = 1900.0 * mult; - cloudAssembly.max_width = 2400.0 * mult; - cloudAssembly.min_height = 1900.0 * mult; - cloudAssembly.max_height = 2400.0 * mult; - cloudAssembly.min_cloud_width = 5000.0 * mult; + cloudAssembly.min_width = 2000.0 * mult; + cloudAssembly.max_width = 2500.0 * mult; + cloudAssembly.min_height = 2000.0 * mult; + cloudAssembly.max_height = 2500.0 * mult; + cloudAssembly.min_cloud_width = 5000.0; cloudAssembly.min_cloud_height = 1.1 * cloudAssembly.max_height; - cloudAssembly.z_scale = 0.4; + #signal that new routines are used path = "new"; @@ -957,11 +1008,11 @@ else if (type == "Stratus (thin)") { #characterize the cloud cloudAssembly.bottom_shade = 0.8; - cloudAssembly.min_width = 1500.0 * mult; - cloudAssembly.max_width = 2000.0 * mult; - cloudAssembly.min_height = 1500.0 * mult; - cloudAssembly.max_height = 2000.0 * mult; - cloudAssembly.min_cloud_width = 2500.0; + cloudAssembly.min_width = 1900.0 * mult; + cloudAssembly.max_width = 2400.0 * mult; + cloudAssembly.min_height = 1900.0 * mult; + cloudAssembly.max_height = 2400.0 * mult; + cloudAssembly.min_cloud_width = 4200.0; cloudAssembly.min_cloud_height = 50.0; @@ -1055,6 +1106,49 @@ else if (type == "Fog (thin)") { } } else if (type == "Fog (thick)") { + + # new code + + if (local_weather.hardcoded_clouds_flag ==1) + { + cloudAssembly = local_weather.cloud.new(type, subtype); + + if (subtype == "small") + { + var mult = 0.8; + cloudAssembly.texture_sheet = "/Models/Weather/stratus_sheet1.rgb"; + cloudAssembly.num_tex_x = 3; + cloudAssembly.num_tex_y = 2; + cloudAssembly.n_sprites = 5; + cloudAssembly.z_scale = 1.0; + } + else + { + var mult = 1.0; + cloudAssembly.texture_sheet = "/Models/Weather/stratus_sheet1.rgb"; + cloudAssembly.num_tex_x = 3; + cloudAssembly.num_tex_y = 2; + cloudAssembly.n_sprites = 5; + cloudAssembly.z_scale = 1.0; + } + + #characterize the cloud + cloudAssembly.bottom_shade = 0.4; + cloudAssembly.min_width = 2000.0 * mult; + cloudAssembly.max_width = 2500.0 * mult; + cloudAssembly.min_height = 2000.0 * mult; + cloudAssembly.max_height = 2500.0 * mult; + cloudAssembly.min_cloud_width = 5000.0; + cloudAssembly.min_cloud_height = 1.1 * cloudAssembly.max_height; + + + #signal that new routines are used + path = "new"; + } + + else # old code + { + if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/stratus_thick1.xml";} else if (rn > 0.6) {path = "Models/Weather/stratus_thick2.xml";} @@ -1068,6 +1162,7 @@ else if (type == "Fog (thick)") { else if (rn > 0.4) {path = "Models/Weather/stratus_thick3.xml";} else if (rn > 0.2) {path = "Models/Weather/stratus_thick4.xml";} else {path = "Models/Weather/stratus_thick5.xml";} + } } } else if (type == "Test") {path="Models/Weather/single_cloud.xml";} diff --git a/Nasal/local_weather/compat_layer.nas b/Nasal/local_weather/compat_layer.nas index 4038a9a0e..a0b4e985d 100644 --- a/Nasal/local_weather/compat_layer.nas +++ b/Nasal/local_weather/compat_layer.nas @@ -170,7 +170,7 @@ else if (local_weather.hardcoded_clouds_flag == 1) { - # we store that information ourselves, so this should be zero + # we store that information ourselves, so this should be zero, but rain forces us to go for an offset setprop("/environment/clouds/layer[0]/elevation-ft",0.0); # layer wrapping off @@ -347,6 +347,7 @@ else var setTemperature = func (T) { + if (features.can_disable_environment == 1) { setprop("/environment/temperature-sea-level-degc",T); @@ -503,6 +504,14 @@ if (features.can_disable_environment == 1) setprop("/environment/wind-from-heading-deg",dir); setprop("/environment/wind-speed-kt",speed); } + +# this is needed to trigger the cloud drift to pick up the new wind setting +if (local_weather.hardcoded_clouds_flag == 1) + { + setprop("/environment/clouds/layer[0]/elevation-ft",0.0); + } + + else { # this is a workaround for systems which lack hard-coded support @@ -767,7 +776,10 @@ var p = props.Node.new({ "layer" : 0, "min-sprite-height-m": c.min_height, "max-sprite-height-m": c.max_height, "num-sprites": c.n_sprites, - "bottom-shade": c.bottom_shade, + "min-bottom-lighting-factor": c.bottom_shade, + "min-middle-lighting-factor": 0.9, + "min-top-lighting-factor": 1.0, + "min-shade-lighting-factor": c.bottom_shade, "texture": c.texture_sheet, "num-textures-x": c.num_tex_x, "num-textures-y": c.num_tex_y, @@ -777,10 +789,10 @@ var p = props.Node.new({ "layer" : 0, "max-cloud-height-m": c.min_cloud_height, "z-scale": c.z_scale, "height-map-texture": 0, - "alt-ft" : c.alt}); + "alt-ft" : c.alt }); fgcommand("add-cloud", p); -# print("alt: ", c.alt); +#print("alt: ", c.alt); # add other management properties to the hash if dynamics is on @@ -799,6 +811,10 @@ append(weather_tile_management.cloudArray,c); + + + + ########################################################### # place a cloud layer from arrays, split across frames ########################################################### @@ -811,13 +827,22 @@ if ((i < 0) or (i==0)) { if (local_weather.debug_output_flag == 1) {print("Cloud placement from array finished!"); } + + # then place all clouds using the new rendering system + if (local_weather.hardcoded_clouds_flag == 1) + { + var s = size(local_weather.cloudAssemblyArray); + create_new_cloud_array(s,cloudAssemblyArray); + } + setprop(lw~"tmp/thread-status", "idle"); # now set flag that tile has been completely processed var dir_index = props.globals.getNode(lw~"tiles/tmp/dir-index").getValue(); - props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("generated-flag").setValue(2); - + #props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("generated-flag").setValue(2); + setprop(lw~"tiles/tile["~dir_index~"]/generated-flag",2); + return; } @@ -856,9 +881,42 @@ settimer( func {create_cloud_array(i - k, clouds_path, clouds_lat, clouds_lon, c }; +var create_new_cloud_array = func (i, cloudArray) +{ +#if (getprop(lw~"tmp/thread-status") != "placing") {return;} +#if (getprop(lw~"tmp/convective-status") != "idle") {return;} + +if ((i < 0) or (i==0)) + { + if (local_weather.debug_output_flag == 1) + {print("Processing add-cloud calls finished!"); } + return; + } + +#print("Hello world! i is now: ",i); + +var k_max = 20; +var s = size(cloudArray); + +if (s < k_max) {k_max = s;} + +for (var k = 0; k < k_max; k = k+1) + { + local_weather.create_cloud_new(cloudArray[s-k-1]); + #print(cloudArray[s-k-1].alt); + } + +setsize(cloudArray,s-k_max); + + + +settimer( func {create_new_cloud_array(i - k, cloudArray) }, 0 ); +} + + diff --git a/Nasal/local_weather/local_weather.nas b/Nasal/local_weather/local_weather.nas index 185945f77..e6548b40f 100644 --- a/Nasal/local_weather/local_weather.nas +++ b/Nasal/local_weather/local_weather.nas @@ -410,8 +410,8 @@ else if (debug_output_flag == 1) { - print("Boundary layer thickness: ",base_layer_thickness); - print("Boundary layer slowdown: ", f_slow); + #print("Boundary layer thickness: ",base_layer_thickness); + #print("Boundary layer slowdown: ", f_slow); } return f_slow; } @@ -509,8 +509,8 @@ setprop(lwi~"station-number", i+1); var ialt = sum_alt/sum_norm; var vis = sum_vis/sum_norm; var p = sum_p/sum_norm; -var D = sum_D/sum_norm; -var T = sum_T/sum_norm; +var D = sum_D/sum_norm + temperature_offset; +var T = sum_T/sum_norm + temperature_offset; # get an inverse distance weighted average from all defined atmospheric condition points @@ -586,6 +586,7 @@ var scatt_alt_high = sum_scatt_alt_high/sum_norm; # altitude model for visibility - increase above the lowest inversion layer to simulate ground haze +vis = vis * ground_haze_factor; var altitude = getprop("position/altitude-ft"); current_mean_terrain_elevation = ialt; @@ -593,6 +594,8 @@ current_mean_terrain_elevation = ialt; var alt1 = vis_alt1; var alt2 = alt1 + 1500.0; +setprop("/environment/ground-visibility-m",vis); +setprop("/environment/ground-haze-thickness-m",alt2 * ft_to_m); # compute the visibility gradients @@ -628,10 +631,10 @@ else if (altitude > ovcst_alt_high) vis = vis + inc1 * alt1 + inc2 * (alt2-alt1) + inc3 * (ovcst_alt_high - alt2) + inc4 * (altitude - ovcst_alt_high); } -# limit visibility (otherwise memory consumption is very bad...) +# limit visibility (otherwise memory consumption may be very bad...) -if (vis > 120000.0) - {vis = 120000.0;} +if (vis > max_vis_range) + {vis = max_vis_range;} # determine scattering shader parameters if scattering shader is on @@ -641,6 +644,8 @@ if (scattering_shader_flag == 1) var mie = 0.003; var density = 0.3; + + if (altitude < 30000.0) { rayleigh = 0.0004 - altitude/30000.0 * 0.0001; @@ -660,37 +665,37 @@ if (scattering_shader_flag == 1) {rayleigh = 0.0001; mie = 0.002;} } # otherwise compute normal skydome shader parameters -else + + + +# compute the horizon shading + +if (altitude < scatt_alt_low) { - - # compute the horizon shading - - if (altitude < scatt_alt_low) - { - var scatt = scatt_max; - } - else if (altitude < scatt_alt_high) - { - var scatt = scatt_max + (0.95 - scatt_max) * (altitude - scatt_alt_low)/(scatt_alt_high-scatt_alt_low); - } - else - {var scatt = 0.95;} + var scatt = scatt_max; + } +else if (altitude < scatt_alt_high) + { + var scatt = scatt_max + (0.95 - scatt_max) * (altitude - scatt_alt_low)/(scatt_alt_high-scatt_alt_low); + } +else + {var scatt = 0.95;} # compute the overcast haze - if (altitude < ovcst_alt_low) - { - var ovcst = ovcst_max; - } - else if (altitude < ovcst_alt_high) - { - var ovcst = ovcst_max - ovcst_max * (altitude - ovcst_alt_low)/(ovcst_alt_high-ovcst_alt_low); - } - else - {var ovcst = 0.0;} - +if (altitude < ovcst_alt_low) + { + var ovcst = ovcst_max; + } +else if (altitude < ovcst_alt_high) + { + var ovcst = ovcst_max - ovcst_max * (altitude - ovcst_alt_low)/(ovcst_alt_high-ovcst_alt_low); } +else + {var ovcst = 0.0;} + + # limit relative changes of the visibility, will make for gradual transitions @@ -717,11 +722,10 @@ if (scattering_shader_flag == 1) { local_weather.setSkydomeShader(rayleigh, mie, density); } -else - { - local_weather.setScattering(scatt); - local_weather.setOvercast(ovcst); - } + +local_weather.setScattering(scatt); +local_weather.setOvercast(ovcst); + # now check if an effect volume writes the property and set only if not @@ -766,18 +770,24 @@ if (p>0.0) # now determine the local wind -#var tile_index = props.globals.getNode(lw~"tiles").getChild("tile",4).getNode("tile-index").getValue(); + var tile_index = getprop(lw~"tiles/tile[4]/tile-index"); if (wind_model_flag ==1) # constant { var winddir = weather_dynamics.tile_wind_direction[0]; var windspeed = weather_dynamics.tile_wind_speed[0]; + + wind.cloudlayer = [winddir,windspeed]; + } else if (wind_model_flag ==2) # constant in tile { var winddir = weather_dynamics.tile_wind_direction[tile_index-1]; var windspeed = weather_dynamics.tile_wind_speed[tile_index-1]; + + wind.cloudlayer = [winddir, windspeed]; + } else if (wind_model_flag ==3) # aloft interpolated, constant in tiles { @@ -785,6 +795,9 @@ else if (wind_model_flag ==3) # aloft interpolated, constant in tiles var res = wind_altitude_interpolation(altitude,w); var winddir = res[0]; var windspeed = res[1]; + + wind.cloudlayer = wind_altitude_interpolation(0.0,w); + } else if (wind_model_flag == 5) # aloft waypoint interpolated { @@ -792,9 +805,13 @@ else if (wind_model_flag == 5) # aloft waypoint interpolated var winddir = res[0]; var windspeed = res[1]; + + wind.cloudlayer = wind_interpolation(viewpos.lat(), viewpos.lon(), 0.0); } +wind.surface = [wind.cloudlayer[0], wind.cloudlayer[1] * get_slowdown_fraction()]; + # now do the boundary layer computations var altitude_agl = getprop("/position/altitude-agl-ft"); @@ -843,6 +860,15 @@ else } +var windspeed_ground = (1.0-f_min) * windspeed; + + +# set the wind hash before gusts, it represents mean wind + +wind.current = [winddir,windspeed_current]; + + + # determine gusts and turbulence in the bounday layer var gust_frequency = getprop(lw~"tmp/gust-frequency-hz"); @@ -897,6 +923,13 @@ setprop(lw~"current/wind-from-heading-deg",winddir); setprop(lw~"current/wind-speed-kt",windspeed_current); +# hack to get access to the water shader + +setprop("/environment/config/boundary/entry[0]/wind-from-heading-deg",winddir); +setprop("/environment/config/boundary/entry[0]/wind-speed-kt",windspeed_ground); + +# end hack + if (getprop(lw~"interpolation-loop-flag") ==1) {settimer(interpolation_loop, interpolation_loop_time);} @@ -1472,14 +1505,18 @@ if (path == "new") # we have to switch to new cloud generating routines local_weather.cloudAssembly.lon = long; local_weather.cloudAssembly.alt = alt; + #print(lat," ",long, " ", alt); + if (dynamics_flag == 1) { local_weather.cloudAssembly.mean_alt = cloud_mean_altitude; local_weather.cloudAssembly.flt = cloud_fractional_lifetime; local_weather.cloudAssembly.evolution_timestamp = cloud_evolution_timestamp; - local_weather.cloudAssembly.rel_alt = c.alt - c.mean_alt; + local_weather.cloudAssembly.rel_alt = cloudAssembly.alt - cloud_mean_altitude; } - compat_layer.create_cloud_new(local_weather.cloudAssembly); + #compat_layer.create_cloud_new(local_weather.cloudAssembly); + + append(cloudAssemblyArray,cloudAssembly); return; } @@ -1736,7 +1773,7 @@ else var alpha = rand() * 180.0; edge = edge + edge_bias; - create_streak(type,lat,lon, alt+ 0.3* (height )-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Congestus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); var type = "Cu (volume)"; var height = 400; @@ -1747,12 +1784,12 @@ else edge = edge + edge_bias; - create_streak(type,lat,lon, alt+ 0.3* (height )-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Congestus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); var btype = "Congestus bottom"; var n_b = 6; - create_streak(btype,lat,lon, alt - 1100.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0); + create_streak(btype,lat,lon, alt -offset_map["Congestus"] -200.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0); } else if (size>1.1) @@ -1768,9 +1805,9 @@ else var alpha = rand() * 180.0; edge = edge + edge_bias; - create_streak(type,lat,lon, alt+ 0.3* (height )-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); - create_streak(btype,lat,lon, alt -600.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0); + create_streak(btype,lat,lon, alt -offset_map["Cumulus"] - 200.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0); } else if (size>0.8) @@ -1784,13 +1821,13 @@ else var alpha = rand() * 180.0; edge = edge + edge_bias; - create_streak(type,lat,lon, alt+ 0.3* (height )-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); n = 2; x = 700.0; y = 200.0; edge = 1.0; - create_streak(type,lat,lon, alt+ 0.3* (height )-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); } else if (size>0.4) @@ -1804,7 +1841,7 @@ else var alpha = rand() * 180.0; edge = edge + edge_bias; - create_streak(type,lat,lon, alt+ 0.3* (height)-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + create_streak(type,lat,lon, alt+ 0.3* (height)-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); } else { @@ -1817,7 +1854,7 @@ else var alpha = rand() * 180.0; edge = edge + edge_bias; - create_streak(type,lat,lon, alt+ 0.3* (height )-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); + create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0); } } @@ -1833,7 +1870,7 @@ else var create_cumulonimbus_cloud = func(lat, lon, alt, size) { if (hardcoded_clouds_flag == 1) - {create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 3.4, 8, 0.9, 0.2, 8);} + {create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 3.4, 8, 0.8, 0.1, 8);} else {create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 1.4, 4, 0.9, 0.2, 8);} @@ -1848,7 +1885,7 @@ else var create_cumulonimbus_cloud_rain = func(lat, lon, alt, size, rain) { if (hardcoded_clouds_flag == 1) - {create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 3.4, 8, 0.9, 0.2, 8);} + {create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 3.4, 8, 0.8, 0.1, 8);} else {create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 1.4, 4, 0.9, 0.2, 8);} @@ -1939,18 +1976,21 @@ var detail_flag = detailed_clouds_flag; var alpha = getprop(lw~"tmp/tile-orientation-deg") * math.pi/180.0; # the tile orientation -if (detailed_terrain_interaction_flag == 1) - { - var tile_index = getprop(lw~"tiles/tile-counter"); +var tile_index = getprop(lw~"tiles/tile-counter"); +var alt_base = alt_20_array[tile_index -1]; + +#if (detailed_terrain_interaction_flag == 1) +# { + #var tile_index = getprop(lw~"tiles/tile-counter"); #var alt_min = alt_min_array[tile_index-1]; #var alt_mean = alt_mean_array[tile_index -1]; #var alt_median = alt_50_array[tile_index -1]; - #var alt_base = alt_20_array[tile-index -1]; + #var alt_base = alt_20_array[tile_index -1]; #var alt_min = getprop(lw~"tmp/tile-alt-min-ft"); #var alt_mean = getprop(lw~"tmp/tile-alt-mean-ft"); #var alt_median = getprop(lw~"tmp/tile-alt-median-ft"); #var alt_base = getprop(lw~"tmp/tile-alt-offset-ft"); - } +# } var sec_to_rad = 2.0 * math.pi/86400; # conversion factor for sinusoidal dependence on daytime @@ -2003,7 +2043,13 @@ while (i < nc) { if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];} else {print(p, " ", info[1].names[0]);} }} - else {continue;} + else { + # to avoid gaps, we create default clouds + + p = p + 0.1; + var elevation = alt_base; + # continue; + } # apply some optional corrections, biases clouds towards higher elevations @@ -2056,8 +2102,10 @@ while (i < nc) { } - # now decide on the strength of the thermal - strength = (1.5 * rand() + (2.0 * p * terrain_strength_factor)) * t_factor2; # the strength of thermal activity at the spot + # now decide on the strength of the thermal at the spot and on cloud size + + var rn = rand(); + strength = (1.5 * rn + (2.0 * p * terrain_strength_factor)) * t_factor2; if (strength > 1.0) { # we place a large cloud, and we generate lift @@ -2065,7 +2113,15 @@ while (i < nc) { } else {path = select_cloud_model("Cumulus","small");} - + # the terrain effect cannot create Cb development, so we have to curb + # the strength if it would not have been Cb otherwise + + if (strength > 2.0) + { + if (((1.5 * rn + (2.0 * p)) * t_factor2) < 2.0) + {strength = 1.7 + rand() * 0.2;} + } + cloud_mean_altitude = place_alt; cloud_fractional_lifetime = rand(); @@ -2168,6 +2224,7 @@ nc = t_factor1 * nc * math.cos(blat/180.0*math.pi); var thermal_conditions = getprop(lw~"config/thermal-properties"); +var alt_base = alt_20_array[tile_index -1]; while (i < nc) { @@ -2198,7 +2255,13 @@ while (i < nc) { if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];} else {print(p, " ", info[1].names[0]);} }} - else {continue;} + else { + # to avoid gaps, we create default clouds + + p = p + 0.1; + var elevation = alt_base; + # continue; + } # apply some optional corrections, biases clouds towards higher elevations @@ -2578,6 +2641,8 @@ var n = int(area/80000000.0 * 100 * density); var m = int(circ/63000.0 * 40 * rain_density); var path = "Models/Weather/blank.ac"; +#print("density: ",n); + phi = phi * math.pi/180.0; if (contains(cloud_vertical_size_map, type)) @@ -2599,7 +2664,10 @@ while(i 0.4) { path = select_cloud_model(type,"small"); - compat_layer.create_cloud(path, lat, lon, alt, 0.0); + if (thread_flag == 1) + {create_cloud_vec(path, lat, lon, alt, 0.0);} + else + {compat_layer.create_cloud(path, lat, lon, alt, 0.0);} } } else { @@ -2617,6 +2685,8 @@ i = 0; if (rainflag ==1){ +if (local_weather.hardcoded_clouds_flag == 1) {balt = balt + local_weather.offset_map[type]; } + while(i 3.0 * current_visibility) - {distance_to_load = 3.0 * current_visibility;} -if (distance_to_load < 29000.0) - {distance_to_load = 29000.0;} +var distance_to_load = current_visibility; + +if (distance_to_load > 65000.0) {distance_to_load = 65000.0;} +if (distance_to_load < 29000.0) {distance_to_load = 29000.0;} + + +#if (distance_to_load > 3.0 * current_visibility) +# {distance_to_load = 3.0 * current_visibility;} +#if (distance_to_load < 29000.0) +# {distance_to_load = 29000.0;} + +var distance_to_remove = distance_to_load + 500.0; # check here if we have a new weather station if METAR is running @@ -473,6 +481,7 @@ if (getprop(lw~"tmp/tile-management") == "repeat tile") else if (code == "cold_sector") {weather_tiles.set_cold_sector_tile();} else if (code == "warm_sector") {weather_tiles.set_warm_sector_tile();} else if (code == "tropical_weather") {weather_tiles.set_tropical_weather_tile();} + else if (code == "thunderstorms") {weather_tiles.set_thunderstorms_tile();} else if (code == "test") {weather_tiles.set_4_8_stratus_tile();} else { diff --git a/Nasal/local_weather/weather_tiles.nas b/Nasal/local_weather/weather_tiles.nas index b7dea927b..bacacaed8 100644 --- a/Nasal/local_weather/weather_tiles.nas +++ b/Nasal/local_weather/weather_tiles.nas @@ -96,22 +96,29 @@ calc_geo(blat); # first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure) -local_weather.set_weather_station(blat, blon, alt_offset, 20000.0, 14.0, 12.0, 29.78); +local_weather.set_weather_station(blat, blon, alt_offset, 30000.0, 14.0, 12.0, 29.78); -#strength = 0.5; +alt_offset = 0.0; + +#var strength = 0.5; #local_weather.create_cumosys(blat,blon, 3000.0, get_n(strength), 20000.0); #create_2_8_altocumulus_streaks(blat, blon, 12000+alt_offset, alpha) ; #create_2_8_altocumulus_streaks(blat, blon, 12000+alt_offset, alpha) ; #create_6_8_stratus(blat, blon, 3000+alt_offset, alpha) ; #create_4_8_tstratus_patches(blat, blon, 5000+alt_offset, alpha) ; -#create_4_8_sstratus_patches(blat, blon, 12000+alt_offset, alpha) ; -#create_4_8_cirrostratus_patches(blat, blon, 20000+alt_offset, alpha) ; +#create_4_8_sstratus_patches(blat, blon, 5000+alt_offset, alpha) ; +#create_4_8_cirrostratus_patches(blat, blon, 5000+alt_offset, alpha) ; #create_4_8_cirrocumulus_streaks(blat, blon, 10000.0 + alt_offset, alpha); -create_4_8_alttstratus_streaks(blat, blon, 5000+alt_offset, alpha) ; +#create_4_8_alttstratus_streaks(blat, blon, 5000+alt_offset, alpha) ; #create_2_8_cirrocumulus_patches(blat, blon, 13000+alt_offset, alpha) ; -#create_8_8_nimbus_rain(blat, blon, 5000+alt_offset, alpha, 0.3) ; + +#create_8_8_nimbus_rain(blat, blon, 3000+alt_offset, alpha, 0.3) ; +#create_8_8_tstratus(blat, blon, 5000+alt_offset, alpha); +#create_8_8_cirrostratus(blat, blon, 5000+alt_offset, alpha); +create_thunderstorm_scenario (blat, blon, 3000.0, alpha); +#create_big_thunderstorm (blat, blon, 3000.0, alpha); #create_4_8_altocumulus_perlucidus(blat, blon, 5000+alt_offset, alpha) ; @@ -121,7 +128,7 @@ create_4_8_alttstratus_streaks(blat, blon, 5000+alt_offset, alpha) ; -local_weather.set_atmosphere_ipoint(blat, blon, 45000.0, 10000.0, 45000.0, 0.0, 25000.0, 30000.0, 0.9, 10000.0, 11000.0); +local_weather.set_atmosphere_ipoint(blat, blon, 45000.0, 15000.0, 45000.0, 0.0, 15000.0, 17000.0, 0.8, 12000.0, 17000.0); append(weather_dynamics.tile_convective_altitude,3000.0); @@ -160,12 +167,14 @@ calc_geo(blat); # get probabilistic values for the weather parameters -var vis = 30000.0 + rand() * 15000.0; +var vis = 25000.0 + rand() * 10000.0; var T = 20.0 + rand() * 10.0; var spread = 14.0 + 8.0 * rand(); var D = T - spread; var p = 1025.0 + rand() * 6.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # and set them at the tile center local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg); @@ -298,12 +307,14 @@ calc_geo(blat); # get probabilistic values for the weather parameters -var vis = 25000.0 + rand() * 15000.0; +var vis = 20000.0 + rand() * 10000.0; var T = 15.0 + rand() * 10.0; var spread = 10.0 + 4.0 * rand(); var D = T - spread; var p = 1019.0 + rand() * 6.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # and set them at the tile center local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg); @@ -445,12 +456,14 @@ calc_geo(blat); # get probabilistic values for the weather parameters -var vis = 20000.0 + rand() * 12000.0; +var vis = 15000.0 + rand() * 7000.0; var T = 12.0 + rand() * 10.0; var spread = 7.0 + 4.0 * rand(); var D = T - spread; var p = 1013.0 + rand() * 6.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # and set them at the tile center local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg); @@ -653,12 +666,14 @@ calc_geo(blat); # get probabilistic values for the weather parameters -var vis = 12000.0 + rand() * 9000.0; +var vis = 8000.0 + rand() * 8000.0; var T = 10.0 + rand() * 10.0; var spread = 6.0 + 2.0 * rand(); var D = T - spread; var p = 1007.0 + rand() * 6.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # and set them at the tile center local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg); @@ -802,7 +817,7 @@ var alpha = getprop(lw~"tmp/tile-orientation-deg"); var phi = alpha * math.pi/180.0; -if (getprop(lw~"tmp/presampling-flag") == 0) +if (local_weather.presampling_flag == 0) {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} else {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} @@ -815,13 +830,13 @@ calc_geo(blat); # get probabilistic values for the weather parameters -var vis = 9000.0 + rand() * 10000.0; +var vis = 8000.0 + rand() * 5000.0; var T = 5.0 + rand() * 10.0; var spread = 5.0 + 2.0 * rand(); var D = T - spread; var p = 1001.0 + rand() * 6.0; p = adjust_p(p); - +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} # and set them at the tile center local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg); @@ -832,11 +847,15 @@ var strength = 0.0; var rn = rand(); + + if (rand() < small_scale_persistence) {rn = rnd_store;} else {rnd_store = rn;} +rn = 0.1; + if (rn > 0.8) { @@ -849,7 +868,10 @@ if (rn > 0.8) y = 2.0 * (rand()-0.5) * 11000.0; var beta = rand() * 360.0; - local_weather.create_layer("Nimbus", blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 1, 1.0); + var alt_eff = alt; + if (local_weather.hardcoded_clouds_flag == 1) {alt_eff = alt_eff - 3000.0;} + + local_weather.create_layer("Nimbus", blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt_eff+alt_offset, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 1, 1.0); local_weather.create_effect_volume(2, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 10000.0, 6000.0, beta, 0.0, alt + alt_offset, 5000.0, 0.3, -1, -1, -1,0,-1 ); local_weather.create_effect_volume(2, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 9000.0, 5000.0, beta, 0.0, alt+alt_offset-300.0, 1500.0, 0.5, -1, -1, -1,0,-1 ); @@ -857,7 +879,7 @@ if (rn > 0.8) y = 2.0 * (rand()-0.5) * 11000.0; var beta = rand() * 360.0; - local_weather.create_layer("Nimbus", blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset, 500.0, 10000.0, 6000.0, beta, 1.0, 0.2, 1, 1.0); + local_weather.create_layer("Nimbus", blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt_eff+alt_offset, 500.0, 10000.0, 6000.0, beta, 1.0, 0.2, 1, 1.0); local_weather.create_effect_volume(2, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 9000.0, 5000.0, beta, 0.0, alt + alt_offset, 5000.0, 0.3, -1, -1, -1,0 ,-1); local_weather.create_effect_volume(2, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 8000.0, 4000.0, beta, 0.0, alt+alt_offset-300.0, 1500.0, 0.5, -1, -1, -1,0,-1 ); @@ -952,7 +974,7 @@ var lon = 0.0; var alpha = getprop(lw~"tmp/tile-orientation-deg"); var phi = alpha * math.pi/180.0; -if (getprop(lw~"tmp/presampling-flag") == 0) +if (local_weather.presampling_flag == 0) {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} else {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} @@ -971,6 +993,8 @@ var spread = 4.5 + 1.0 * rand(); var D = T - spread; var p = 995.0 + rand() * 6.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # and set them at the tile center local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg); @@ -980,6 +1004,7 @@ local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_i var alt = spread * 400.0 + local_weather.cloud_vertical_size_map["Nimbus"] * 0.5 * m_to_ft; var strength = 0.0; +#var alt = 3000.0; create_8_8_nimbus_rain(blat, blon, alt+alt_offset, alpha,0.4 + rand()*0.2); @@ -1036,12 +1061,14 @@ calc_geo(blat); # get probabilistic values for the weather parameters -var vis = 45000.0 + rand() * 20000.0; +var vis = 40000.0 + rand() * 15000.0; var T = 8.0 + rand() * 8.0; var spread = 7.0 + 3.0 * rand(); var D = T - spread; var p = 1005.0 + rand() * 10.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # and set them at the tile center local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg); @@ -1131,12 +1158,14 @@ calc_geo(blat); # get probabilistic values for the weather parameters -var vis = 12000.0 + rand() * 10000.0; +var vis = 8000.0 + rand() * 5000.0; var T = 16.0 + rand() * 10.0; var spread = 6.0 + 3.0 * rand(); var D = T - spread; var p = 1005.0 + rand() * 10.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # and set them at the tile center local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg); @@ -1273,6 +1302,8 @@ var spread = 8.0 + 2.0 * rand(); var D = T - spread; var p = 970 + rand() * 10.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure) local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg); @@ -1374,6 +1405,82 @@ tile_finished(); } + + +#################################### +# Thunderstorms +#################################### + +var set_thunderstorms_tile = func { + +setprop(lw~"tiles/code","thunderstorms"); + +tile_start(); + +var x = 0.0; +var y = 0.0; +var lat = 0.0; +var lon = 0.0; + + +var alpha = getprop(lw~"tmp/tile-orientation-deg"); +var phi = alpha * math.pi/180.0; +var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft"); + +# get tile center coordinates + +var blat = getprop(lw~"tiles/tmp/latitude-deg"); +var blon = getprop(lw~"tiles/tmp/longitude-deg"); +calc_geo(blat); + +# get probabilistic values for the weather parameters + +var vis = 9000.0 + rand() * 10000.0; +var T = 10.0 + rand() * 15.0; +var spread = 8.0 + 2.0 * rand(); +var D = T - spread; +var p = 1000 + rand() * 10.0; p = adjust_p(p); + +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + +# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure) + +local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg); + +# altitude for the lowest layer +var alt = spread * 400.0; +var strength = 0.0; + +# bias Cumulus clouds towards larger sizes due to lots of water vapour +local_weather.convective_size_bias = 0.3 + rand() * 0.3; + + +# and specify the atmosphere + local_weather.set_atmosphere_ipoint(blat, blon, vis + 12000.0, alt+alt_offset, vis + 20000.0, 0.0, alt+alt_offset +20000.0, alt+alt_offset + 25000.0, 0.65, alt+alt_offset, alt+alt_offset + 2500.0); + +var rn = rand(); + + +if (rand() < small_scale_persistence) + {rn = rnd_store;} +else + {rnd_store = rn;} + +create_thunderstorm_scenario (blat, blon, alt + alt_offset, alpha); + +# store convective altitude and strength + +append(weather_dynamics.tile_convective_altitude,alt); +append(weather_dynamics.tile_convective_strength,strength); + +tile_finished(); + +} + + + + + #################################### # Coldfront #################################### @@ -1410,6 +1517,8 @@ var spread = 8.0 + 2.0 * rand(); var D = T - spread; var p = 1005 + rand() * 10.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # first weather info for tile (lat, lon, visibility, temperature, dew point, pressure) # after the front @@ -1525,7 +1634,7 @@ var lon = 0.0; var alpha = getprop(lw~"tmp/tile-orientation-deg"); var phi = alpha * math.pi/180.0; -if (getprop(lw~"tmp/presampling-flag") == 0) +if (local_weather.presampling_flag == 0) {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} else {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} @@ -1544,6 +1653,8 @@ var spread = 9.0 + 4.0 * rand(); var D = T - spread; var p = 1005 + rand() * 10.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # first weather info for tile (lat, lon, visibility, temperature, dew point, pressure) # after the front @@ -1587,7 +1698,10 @@ for (var i=0; i<6; i=i+1) var x = 2.0 * (rand()-0.5) * 15000; var y = 2.0 * (rand()-0.5) * 10000 + 10000; var beta = (rand() -0.5) * 180.0; - local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 18000 + alt + alt_offset,300.0,4,2300.0,0.2,600.0,4,2300.0,0.2,600.0,alpha+beta,1.0); + var alt_shift = 0.0; + if (local_weather.hardcoded_clouds_flag == 1) {alt_shift = local_weather.offset_map["Cirrostratus"];} + + local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 18000 + alt + alt_offset - alt_shift,300.0,4,2300.0,0.2,600.0,4,2300.0,0.2,600.0,alpha+beta,1.0); } @@ -1625,7 +1739,7 @@ var lon = 0.0; var alpha = getprop(lw~"tmp/tile-orientation-deg"); var phi = alpha * math.pi/180.0; -if (getprop(lw~"tmp/presampling-flag") == 0) +if (local_weather.presampling_flag == 0) {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} else {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} @@ -1644,6 +1758,8 @@ var spread = 8.0 + 2.0 * rand(); var D = T - spread; var p = 1005 + rand() * 10.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # first weather info for tile (lat, lon, visibility, temperature, dew point, pressure) # after the front @@ -1668,12 +1784,15 @@ var strength = 0.0; # followed by random patches of Cirrostratus +var alt_shift = 0.0; +if (local_weather.hardcoded_clouds_flag == 1) {alt_shift = local_weather.offset_map["Cirrostratus"];} + for (var i=0; i<3; i=i+1) { var x = 2.0 * (rand()-0.5) * 18000; var y = 2.0 * (rand()-0.5) * 5000 - 15000; var beta = (rand() -0.5) * 180.0; - local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 15000 + alt + alt_offset,300.0,4,2300.0,0.2,600.0,4,2300.0,0.2,600.0,alpha+beta,1.0); + local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 15000 + alt + alt_offset - alt_shift,300.0,4,2300.0,0.2,600.0,4,2300.0,0.2,600.0,alpha+beta,1.0); } @@ -1742,7 +1861,7 @@ var lon = 0.0; var alpha = getprop(lw~"tmp/tile-orientation-deg"); var phi = alpha * math.pi/180.0; -if (getprop(lw~"tmp/presampling-flag") == 0) +if (local_weather.presampling_flag == 0) {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} else {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} @@ -1761,6 +1880,8 @@ var spread = 7.0 + 2.0 * rand(); var D = T - spread; var p = 1005 + rand() * 10.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # first weather info for tile (lat, lon, visibility, temperature, dew point, pressure) # after the front @@ -1849,7 +1970,7 @@ var lon = 0.0; var alpha = getprop(lw~"tmp/tile-orientation-deg"); var phi = alpha * math.pi/180.0; -if (getprop(lw~"tmp/presampling-flag") == 0) +if (local_weather.presampling_flag == 0) {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} else {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} @@ -1868,6 +1989,8 @@ var spread = 5.0 + 2.0 * rand(); var D = T - spread; var p = 1005 + rand() * 10.0; p = adjust_p(p); +if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + # first weather info for tile (lat, lon, visibility, temperature, dew point, pressure) # after the front @@ -1951,7 +2074,10 @@ var lon = 0.0; var alpha = getprop("/environment/metar/base-wind-dir-deg"); var phi = alpha * math.pi/180.0; -var metar_alt_offset = 700.0 + getprop("/environment/metar/station-elevation-ft"); + +# it seems more recent Flightgear versions have absolute altitude +# var metar_alt_offset = 700.0 + getprop("/environment/metar/station-elevation-ft"); +var metar_alt_offset = 700.0; # print("metar_alt_offset", metar_alt_offset); @@ -2263,11 +2389,13 @@ var set_METAR_weather_station = func { if (is_visibility_max == 1) { - if (p * inhg_to_hp < 1000.0) {vis = 10000.0 + 5000 * rand();} - else if (p * inhg_to_hp < 1010.0) {vis = 15000.0 + 7000 * rand();} - else if (p * inhg_to_hp < 1020.0) {vis = 22000.0 + 14000.0 * rand();} - else {vis = 30000.0 + 15000.0 * rand();} - } + if (p * inhg_to_hp < 1000.0) {vis = 10000.0 + 5000 * rand();} + else if (p * inhg_to_hp < 1010.0) {vis = 15000.0 + 7000 * rand();} + else if (p * inhg_to_hp < 1020.0) {vis = 22000.0 + 14000.0 * rand();} + else {vis = 30000.0 + 15000.0 * rand();} + + if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;} + } @@ -2385,32 +2513,31 @@ var set_METAR_weather_station = func { # mid-level cloud setup calls #################################### -var create_8_8_stratus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;} - -local_weather.create_streak("Stratus",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0); - -} var create_8_8_tstratus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 650.0;} - -local_weather.create_streak("Stratus (thin)",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0); - +if (local_weather.hardcoded_clouds_flag == 1) + { + alt = alt - local_weather.offset_map["Stratus_thin"]; + +local_weather.create_streak("Stratus (thin)",lat, lon, alt,500.0,40,1000.0,0.0,400.0,40,1000.0,0.0,400.0,alpha,1.0); + } +else + { + local_weather.create_streak("Stratus (thin)",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0); + } } var create_8_8_cirrostratus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1800.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Cirrostratus"];} local_weather.create_streak("Cirrostratus",lat,lon,alt,500.0,30,1250.0,0.0,400.0,30,1250.0,0.0,400.0,alpha,1.0); } var create_8_8_nimbus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;} local_weather.create_streak("Nimbus",lat, lon, alt,500.0,32,1250.0,0.0,200.0,32,1250.0,0.0,200.0,alpha,1.0); @@ -2419,9 +2546,11 @@ local_weather.create_streak("Nimbus",lat, lon, alt,500.0,32,1250.0,0.0,200.0,32, var create_8_8_nimbus_var1 = func (lat, lon, alt, alpha) { +if (local_weather.hardcoded_clouds_flag == 1) {var alt_eff = alt - local_weather.offset_map["Nimbus"]; } + var phi = alpha * math.pi/180.0; -local_weather.create_streak("Nimbus",lat, lon, alt,500.0,35,1111.0,0.0,200.0,35,1111.0,0.0,200.0,alpha,1.0); +local_weather.create_streak("Nimbus",lat, lon, alt_eff,500.0,35,1150.0,0.0,200.0,35,1150.0,0.0,200.0,alpha,1.0); for (var i = 0; i < 3; i=i+1) { @@ -2434,9 +2563,11 @@ for (var i = 0; i < 3; i=i+1) var create_8_8_nimbus_var2 = func (lat, lon, alt, alpha) { +if (local_weather.hardcoded_clouds_flag == 1) {var alt_eff = alt - local_weather.offset_map["Nimbus"]; } + var phi = alpha * math.pi/180.0; -local_weather.create_streak("Nimbus",lat, lon, alt,500.0,35,1111.0,0.0,200.0,35,1111.0,0.0,200.0,alpha,1.0); +local_weather.create_streak("Nimbus",lat, lon, alt_eff,500.0,35,1150.0,0.0,200.0,35,1150.0,0.0,200.0,alpha,1.0); for (var i=0; i<8; i=i+1) { @@ -2452,10 +2583,12 @@ for (var i=0; i<8; i=i+1) var create_8_8_nimbus_var3 = func (lat, lon, alt, alpha) { +if (local_weather.hardcoded_clouds_flag == 1) {var alt_eff = alt - local_weather.offset_map["Nimbus"]; } + var phi = alpha * math.pi/180.0; -local_weather.create_streak("Nimbus",lat, lon, alt,500.0,35,1111.0,0.0,200.0,35,1111.0,0.0,200.0,alpha,1.0); +local_weather.create_streak("Nimbus",lat, lon, alt_eff,500.0,35,1150.0,0.0,200.0,35,1150.0,0.0,200.0,alpha,1.0); for (var i=0; i<6; i=i+1) { @@ -2465,21 +2598,17 @@ for (var i=0; i<6; i=i+1) local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt+1600.0,300.0,6,1200.0,0.2,700.0,6,1200.0,0.2,700.0,alpha+beta,1.0); } -# reduced visibility in layer -#local_weather.create_effect_volume(3, lat, lon, 20000.0, 20000.0, alpha, alt-1500.0, alt+900.0, 2000.0, -1 , -1, -1, -1,0 ,-1); -# cloud shade -#local_weather.create_effect_volume(3, lat, lon, 20000.0, 20000.0, alpha, 0.0, alt, -1, -1 , -1, -1, -1,0 ,0.8); - } var create_8_8_nimbus_rain = func (lat, lon, alt, alpha, rain) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 3000.0;} + if (local_weather.detailed_clouds_flag == 0) {local_weather.create_streak("Nimbus",lat, lon, alt,500.0,32,1250.0,0.0,200.0,32,1250.0,0.0,200.0,alpha,1.0);} else { + #print(local_weather.offset_map["Nimbus"]); var rn = rand(); if (rn > 0.66) {create_8_8_nimbus_var1(lat, lon, alt, alpha);} else if (rn > 0.33) {create_8_8_nimbus_var2(lat, lon, alt, alpha);} @@ -2499,12 +2628,19 @@ else } +var create_8_8_stratus = func (lat, lon, alt, alpha) { + +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus"];} + +local_weather.create_streak("Stratus",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0); +} + var create_8_8_stratus_rain = func (lat, lon, alt, alpha, rain) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;} -local_weather.create_streak("Stratus",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0); +create_8_8_stratus(lat, lon, alt, alpha); + if (rain > 0.1) { @@ -2520,9 +2656,22 @@ else var create_6_8_stratus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;} +if (local_weather.hardcoded_clouds_flag == 1) + { + alt = alt - local_weather.offset_map["Stratus"]; + + for (var i = 0; i < 20; i = i + 1) + { + var phi = alpha * math.pi/180.0; + var x = 2.0 * (rand()-0.5) * 18000; + var y = 2.0 * (rand()-0.5) * 18000; -local_weather.create_streak("Stratus",lat, lon, alt,500.0,20,0.0,0.2,20000.0,20,0.0,0.2,20000.0,alpha,1.0); + local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,500.0,5,2300.0,0.2,500.0,5,2300.0,0.2,500.0,alpha,1.0); + } + + } +else + {local_weather.create_streak("Stratus",lat, lon, alt,500.0,20,0.0,0.2,20000.0,20,0.0,0.2,20000.0,alpha,1.0);} } @@ -2532,7 +2681,9 @@ var create_6_8_nimbus_rain = func (lat, lon, alt, alpha, rain) { var phi = alpha * math.pi/180.0; -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 3000.0;} +var alt_cloud = alt; + +if (local_weather.hardcoded_clouds_flag == 1) {alt_cloud = alt_cloud - 3000.0;} for (var i = 0; i < 3; i = i + 1) { @@ -2540,7 +2691,7 @@ for (var i = 0; i < 3; i = i + 1) var y = 2.0 * (rand()-0.5) * 12000.0; var beta = rand() * 360.0; - local_weather.create_layer("Nimbus", lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 1, 1.0); + local_weather.create_layer("Nimbus", lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt_cloud, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 1, 1.0); if (rain > 0.1) { @@ -2562,7 +2713,9 @@ var create_6_8_stratus_rain = func (lat, lon, alt, alpha, rain) { var phi = alpha * math.pi/180.0; -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;} +var alt_cloud = alt; + +if (local_weather.hardcoded_clouds_flag == 1) {alt_cloud = alt_cloud - local_weather.offset_map["Stratus"];} for (var i = 0; i < 3; i = i + 1) { @@ -2570,7 +2723,7 @@ for (var i = 0; i < 3; i = i + 1) var y = 2.0 * (rand()-0.5) * 12000.0; var beta = rand() * 360.0; - local_weather.create_layer("Stratus", lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 0, 0.0); + local_weather.create_layer("Stratus", lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt_cloud, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 0, 0.0); if (rain > 0.1) { @@ -2589,21 +2742,21 @@ for (var i = 0; i < 3; i = i + 1) var create_6_8_stratus_undulatus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus"];} local_weather.create_undulatus("Stratus",lat, lon, alt,300.0,10,4000.0,0.1,400.0,50,800.0,0.1,100.0, 1000.0, alpha,1.0); } var create_6_8_tstratus_undulatus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 650.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_thin"];} local_weather.create_undulatus("Stratus (thin)",lat, lon, alt,300.0,10,4000.0,0.1,400.0,50,800.0,0.1,100.0, 1000.0, alpha,1.0); } var create_6_8_cirrostratus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1800.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Cirrostratus"];} local_weather.create_streak("Cirrostratus",lat,lon,alt,500.0,24,1500.0,0.0,900.0,24,1500.0,0.0,900.0,alpha,1.0); } @@ -2633,7 +2786,7 @@ else var create_4_8_stratus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus"];} var phi = alpha * math.pi/180.0; var x = 2.0 * (rand()-0.5) * 15000; @@ -2662,7 +2815,7 @@ var create_4_8_stratus_patches = func (lat, lon, alt, alpha) { var phi = alpha * math.pi/180.0; -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus"];} for (var i=0; i<16; i=i+1) { @@ -2679,7 +2832,7 @@ var create_4_8_tstratus_patches = func (lat, lon, alt, alpha) { var phi = alpha * math.pi/180.0; -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 650.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_thin"];} for (var i=0; i<22; i=i+1) { @@ -2701,7 +2854,7 @@ var create_4_8_sstratus_patches = func (lat, lon, alt, alpha) { var phi = alpha * math.pi/180.0; -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 800.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_structured"];} for (var i=0; i<22; i=i+1) { @@ -2719,7 +2872,7 @@ for (var i=0; i<22; i=i+1) var create_4_8_cirrostratus_patches = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1800.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Cirrostratus"];} var phi = alpha * math.pi/180.0; @@ -2736,7 +2889,7 @@ for (var i=0; i<6; i=i+1) var create_4_8_cirrostratus_undulatus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1800.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Cirrostratus"];} local_weather.create_undulatus("Cirrostratus",lat, lon, alt,300.0,5,8000.0,0.1,400.0,40,1000.0,0.1,100.0, 1500.0, alpha,1.0); } @@ -2744,7 +2897,7 @@ local_weather.create_undulatus("Cirrostratus",lat, lon, alt,300.0,5,8000.0,0.1,4 var create_4_8_stratus_undulatus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus"];} var phi = alpha * math.pi/180.0; var x = 2.0 * (rand()-0.5) * 5000; @@ -2760,7 +2913,7 @@ local_weather.create_streak("Stratus",lat+get_lat(x,y-4000,phi), lon+get_lon(x,y var create_4_8_tstratus_undulatus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 650.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_thin"];} var phi = alpha * math.pi/180.0; var x = 2.0 * (rand()-0.5) * 5000; @@ -2776,7 +2929,7 @@ local_weather.create_streak("Stratus (thin)",lat+get_lat(x,y-4000,phi), lon+get_ var create_4_8_sstratus_undulatus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 800.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_structured"];} var phi = alpha * math.pi/180.0; var x = 2.0 * (rand()-0.5) * 5000; @@ -2858,7 +3011,7 @@ for (var i=0; i<20; i=i+1) var create_4_8_alttstratus_streaks = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 300.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_alt"];} var phi = alpha * math.pi/180.0; @@ -2878,7 +3031,7 @@ for (var i=0; i<10; i=i+1) var create_4_8_alttstratus_patches = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 300.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_alt"];} var phi = alpha * math.pi/180.0; @@ -2909,7 +3062,7 @@ else var create_2_8_stratus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus"];} var phi = alpha * math.pi/180.0; @@ -2926,7 +3079,7 @@ for (var i=0; i<8; i=i+1) var create_2_8_tstratus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 650.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_thin"];} var phi = alpha * math.pi/180.0; @@ -2944,7 +3097,7 @@ for (var i=0; i<8; i=i+1) var create_2_8_sstratus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 800.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_structured"];} var phi = alpha * math.pi/180.0; @@ -2967,7 +3120,7 @@ for (var i=0; i<8; i=i+1) var create_2_8_sstratus_streak = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 800.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_structured"];} var phi = alpha * math.pi/180.0; @@ -2981,7 +3134,7 @@ local_weather.create_streak("Stratus (structured)",lat+get_lat(x,y,phi), lon+get var create_2_8_cirrostratus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1800.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Cirrostratus"];} var phi = alpha * math.pi/180.0; @@ -3027,7 +3180,7 @@ local_weather.create_streak("Cirrus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), var create_2_8_alttstratus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 300.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_alt"];} var phi = alpha * math.pi/180.0; @@ -3113,7 +3266,7 @@ local_weather.create_streak("Cirrus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), var create_1_8_cirrostratus_undulatus = func (lat, lon, alt, alpha) { -if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1800.0;} +if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Cirrostratus"];} local_weather.create_undulatus("Cirrostratus",lat, lon, alt,300.0,1,8000.0,0.0,400.0,40,1000.0,0.1,100.0, 1500.0, alpha,1.0); } @@ -3265,9 +3418,12 @@ var create_small_thunderstorm = func(lat, lon, alt, alpha) { var scale = 0.7 + rand() * 0.3; -local_weather.create_layer("Stratus", lat, lon, alt, 1000.0, 4000.0 * scale, 4000.0 * scale, 0.0, 1.0, 0.3, 1, 1.0); +var alt_eff = alt; +if (local_weather.hardcoded_clouds_flag == 1) {alt_eff = alt - local_weather.offset_map["Stratus"] - 2000.0; } -local_weather.create_layer("Cumulonimbus (cloudlet)", lat, lon, alt+2000, 15000.0, 3000.0 * scale, 3000.0 * scale, 0.0, 2.0, 0.0, 0, 0.0); +local_weather.create_layer("Stratus", lat, lon, alt_eff, 1000.0, 4000.0 * scale, 4000.0 * scale, 0.0, 1.0, 0.3, 1, 1.0); + +local_weather.create_layer("Cumulonimbus (cloudlet)", lat, lon, alt_eff+3000.0, 12000.0, 3000.0 * scale, 3000.0 * scale, 0.0, 2.0, 0.0, 0, 0.0); # set the exclusion region for the Cumulus layer append(elat, lat); append(elon, lon); append(erad, 4000.0 * scale * 1.2); @@ -3282,15 +3438,20 @@ var create_medium_thunderstorm = func(lat, lon, alt, alpha) { var scale = 0.7 + rand() * 0.3; -local_weather.create_layer("Nimbus", lat, lon, alt, 500.0, 6000.0 * scale, 6000.0 * scale, 0.0, 1.0, 0.3, 1, 1.5); +var alt_eff = alt; +if (local_weather.hardcoded_clouds_flag == 1) {alt_eff = alt - local_weather.offset_map["Nimbus"] - 2000.0; } -#local_weather.create_layer("Stratus", lat, lon, alt+1500, 1000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0); -local_weather.create_hollow_layer("Stratus", lat, lon, alt+1500, 1000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 1.0, 0.3, 0.5); +local_weather.create_layer("Nimbus", lat, lon, alt_eff, 500.0, 6000.0 * scale, 6000.0 * scale, 0.0, 1.0, 0.3, 1, 1.5); -local_weather.create_layer("Fog (thick)", lat, lon, alt+4000, 6000.0, 3400.0 * scale, 3400.0 * scale, 0.0, 1.5, 0.3, 0, 0.0); +#local_weather.create_layer("Stratus", lat, lon, alt_eff+1500, 1000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0); -local_weather.create_layer("Cumulonimbus (cloudlet)", lat, lon, alt+10000, 10000.0, 3600.0 * scale, 3600.0 * scale, 0.0, 1.2, 0.0, 0, 0.0); +local_weather.create_hollow_layer("Stratus", lat, lon, alt_eff+1500, 1000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 1.0, 0.3, 0.5); + +local_weather.create_layer("Fog (thick)", lat, lon, alt_eff+4000, 6000.0, 3400.0 * scale, 3400.0 * scale, 0.0, 1.5, 0.3, 0, 0.0); + + +local_weather.create_layer("Cumulonimbus (cloudlet)", lat, lon, alt_eff+10000, 10000.0, 3600.0 * scale, 3600.0 * scale, 0.0, 1.2, 0.0, 0, 0.0); # set the exclusion region for the Cumulus layer append(elat, lat); append(elon, lon); append(erad, 6000.0 * scale * 1.2); @@ -3307,23 +3468,26 @@ var phi = alpha * math.pi/180.0; var scale = 0.8; -local_weather.create_layer("Nimbus", lat, lon, alt, 500.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.25, 1, 1.5); +var alt_eff = alt; +if (local_weather.hardcoded_clouds_flag == 1) {alt_eff = alt - local_weather.offset_map["Nimbus"]; } + +local_weather.create_layer("Nimbus", lat, lon, alt_eff, 500.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.25, 1, 1.5); #local_weather.create_layer("Stratus", lat, lon, alt+1500, 1000.0, 7200.0 * scale, 7200.0 * scale, 0.0, 1.0, 0.3, 0, 0.0); -local_weather.create_hollow_layer("Stratus", lat, lon, alt+1500, 1000.0, 7200.0 * scale, 7200.0 * scale, 0.0, 1.0, 0.3, 0.7); +local_weather.create_hollow_layer("Stratus", lat, lon, alt_eff+1500, 1000.0, 7200.0 * scale, 7200.0 * scale, 0.0, 1.0, 0.3, 0.7); -local_weather.create_layer("Fog (thick)", lat, lon, alt+5000, 3000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 0.7, 0.3, 0, 0.0); +local_weather.create_layer("Fog (thick)", lat, lon, alt_eff+5000, 3000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 0.7, 0.3, 0, 0.0); -local_weather.create_layer("Fog (thick)", lat+get_lat(0,-1000,phi), lon+get_lon(0,-1000,phi), alt+12000, 4000.0, 6300.0 * scale, 6300.0 * scale, 0.0, 0.7, 0.3, 0, 0.0); +local_weather.create_layer("Fog (thick)", lat+get_lat(0,-1000,phi), lon+get_lon(0,-1000,phi), alt_eff+12000, 4000.0, 6300.0 * scale, 6300.0 * scale, 0.0, 0.7, 0.3, 0, 0.0); #local_weather.create_layer("Stratus", lat+get_lat(0,-2000,phi), lon+get_lon(0,-2000,phi), alt+17000, 1000.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0); -local_weather.create_hollow_layer("Stratus", lat+get_lat(0,-2000,phi), lon+get_lon(0,-2000,phi), alt+17000, 1000.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.3, 0.5); +local_weather.create_hollow_layer("Stratus", lat+get_lat(0,-2000,phi), lon+get_lon(0,-2000,phi), alt_eff+17000, 1000.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.3, 0.5); -#local_weather.create_layer("Stratus", lat+get_lat(0,-3000,phi), lon+get_lon(0,-3000,phi), alt+20000, 1000.0, 9500.0 * scale, 9500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0); -local_weather.create_hollow_layer("Stratus", lat+get_lat(0,-3000,phi), lon+get_lon(0,-3000,phi), alt+20000, 1000.0, 9500.0 * scale, 9500.0 * scale, 0.0, 1.0, 0.3, 0.5); +#local_weather.create_layer("Stratus", lat+get_lat(0,-3000,phi), lon+get_lon(0,-3000,phi), alt_eff+20000, 1000.0, 9500.0 * scale, 9500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0); +local_weather.create_hollow_layer("Stratus", lat+get_lat(0,-3000,phi), lon+get_lon(0,-3000,phi), alt_eff+20000, 1000.0, 9500.0 * scale, 9500.0 * scale, 0.0, 1.0, 0.3, 0.5); -local_weather.create_layer("Stratus (thin)", lat+get_lat(0,-4000,phi), lon+get_lon(0,-4000,phi), alt+24000, 1000.0, 11500.0 * scale, 11500.0 * scale, 0.0, 2.0, 0.3, 0, 0.0); +local_weather.create_layer("Stratus (thin)", lat+get_lat(0,-4000,phi), lon+get_lon(0,-4000,phi), alt_eff+24000, 1000.0, 11500.0 * scale, 11500.0 * scale, 0.0, 2.0, 0.0, 0, 0.0); # set the exclusion region for the Cumulus layer append(elat, lat); append(elon, lon); append(erad, 7500.0 * scale * 1.2); @@ -3420,6 +3584,8 @@ var lon_to_m = 0.0; # needs to be calculated dynamically var m_to_lon = 0.0; # we do this on startup var lw = "/local-weather/"; +var realistic_visibility_multiplyer = 1.5; + var small_scale_persistence = getprop(lw~"config/small-scale-persistence"); var rnd_store = rand(); diff --git a/Shaders/cloud-static.frag b/Shaders/cloud-static.frag new file mode 100644 index 000000000..b51ff3af4 --- /dev/null +++ b/Shaders/cloud-static.frag @@ -0,0 +1,10 @@ +uniform sampler2D baseTexture; +varying float fogFactor; + +void main(void) +{ + vec4 base = texture2D( baseTexture, gl_TexCoord[0].st); + vec4 finalColor = base * gl_Color; + gl_FragColor.rgb = mix(gl_Fog.color.rgb, finalColor.rgb, fogFactor ); + gl_FragColor.a = mix(0.0, finalColor.a, fogFactor); +} diff --git a/Shaders/cloud-static.vert b/Shaders/cloud-static.vert new file mode 100644 index 000000000..96c2ec80a --- /dev/null +++ b/Shaders/cloud-static.vert @@ -0,0 +1,55 @@ +// -*-C++-*- +#version 120 + +varying float fogFactor; + + +float shade = 0.8; +float cloud_height = 1000.0; + +void main(void) +{ + + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + //gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndexX, textureIndexY, 0.0, 0.0); + vec4 ep = gl_ModelViewMatrixInverse * vec4(0.0,0.0,0.0,1.0); + vec4 l = gl_ModelViewMatrixInverse * vec4(0.0,0.0,1.0,1.0); + vec3 u = normalize(ep.xyz - l.xyz); + + gl_Position = vec4(0.0, 0.0, 0.0, 1.0); + gl_Position.x = gl_Vertex.x; + gl_Position.y += gl_Vertex.y; + gl_Position.z += gl_Vertex.z; + gl_Position.xyz += gl_Color.xyz; + + + + // Determine a lighting normal based on the vertex position from the + // center of the cloud, so that sprite on the opposite side of the cloud to the sun are darker. + float n = dot(normalize(-gl_LightSource[0].position.xyz), + normalize(mat3x3(gl_ModelViewMatrix) * (- gl_Position.xyz)));; + + // Determine the position - used for fog and shading calculations + vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Position); + float fogCoord = abs(ecPosition.z); + float fract = smoothstep(0.0, cloud_height, gl_Position.z + cloud_height); + + + gl_Position = gl_ModelViewProjectionMatrix * gl_Position; + +// Determine the shading of the sprite based on its vertical position and position relative to the sun. + n = min(smoothstep(-0.5, 0.0, n), fract); +// Determine the shading based on a mixture from the backlight to the front + vec4 backlight = gl_LightSource[0].diffuse * shade; + + gl_FrontColor = mix(backlight, gl_LightSource[0].diffuse, n); + gl_FrontColor += gl_FrontLightModelProduct.sceneColor; + + // As we get within 100m of the sprite, it is faded out. Equally at large distances it also fades out. + gl_FrontColor.a = min(smoothstep(100.0, 250.0, fogCoord), 1.0 - smoothstep(70000.0, 75000.0, fogCoord)); + gl_BackColor = gl_FrontColor; + + // Fog doesn't affect rain as much as other objects. + fogFactor = exp( -gl_Fog.density * fogCoord * 0.4); + fogFactor = clamp(fogFactor, 0.0, 1.0); +} diff --git a/Shaders/rain-layer.vert b/Shaders/rain-layer.vert index 52c4dbd5c..ef72bf493 100644 --- a/Shaders/rain-layer.vert +++ b/Shaders/rain-layer.vert @@ -3,6 +3,8 @@ varying float fogFactor; +uniform float range; // From /sim/rendering/clouds3d-vis-range + float shade = 0.8; float cloud_height = 1000.0; @@ -45,7 +47,7 @@ void main(void) gl_FrontColor += gl_FrontLightModelProduct.sceneColor; // As we get within 100m of the sprite, it is faded out. Equally at large distances it also fades out. - gl_FrontColor.a = min(smoothstep(100.0, 250.0, fogCoord), 1.0 - smoothstep(40000.0, 45000.0, fogCoord)); + gl_FrontColor.a = min(smoothstep(100.0, 250.0, fogCoord), 1.0 - smoothstep(range*0.9, range, fogCoord)); gl_BackColor = gl_FrontColor; // Fog doesn't affect rain as much as other objects. diff --git a/gui/dialogs/local_weather_tiles.xml b/gui/dialogs/local_weather_tiles.xml index aacc2d387..cbf4acfe1 100644 --- a/gui/dialogs/local_weather_tiles.xml +++ b/gui/dialogs/local_weather_tiles.xml @@ -5,7 +5,7 @@ local_weather_tiles - 310 + 470 385 false @@ -35,6 +35,7 @@ Coldfront Warmfront Tropical + Thunderstorms Test tile dialog-apply @@ -49,6 +50,24 @@ --> + + + 300 + 330 + + + + + + + 390 + 330 + 50 + 25 + /local-weather/tmp/tile-alt-offset-ft + + + 5 295 @@ -77,34 +96,19 @@ /local-weather/tmp/windspeed-kt - - 155 - 295 - - - - - - - 240 - 295 - 50 - 25 - /local-weather/tmp/tile-alt-offset-ft - + - - 5 - 265 + 157 + 295 - 65 - 265 + 225 + 295 50 20 0.0 @@ -118,15 +122,15 @@ - 112 - 265 + 275 + 295 - 167 - 265 + 333 + 295 50 20 0.0 @@ -138,15 +142,15 @@ - 215 - 265 + 380 + 295 - 240 - 265 + 410 + 295 50 20 0.0 @@ -162,14 +166,14 @@ 5 - 235 + 265 150 - 235 + 265 140 25 true @@ -185,15 +189,32 @@ + + 300 + 265 + + + + + + + 390 + 265 + 50 + 25 + /local-weather/config/temperature-offset-degc + + + 5 - 205 + 235 150 - 205 + 235 140 25 true @@ -207,33 +228,10 @@ - - 10 - 175 - 15 - 15 - - /local-weather/config/presampling-flag - - dialog-apply - - - - - 150 - 175 - 15 - 15 - - /local-weather/config/generate-thermal-lift-flag - - dialog-apply - - 10 - 150 + 205 15 15 @@ -243,7 +241,7 @@ - + - + - + - 10 - 100 + 150 + 205 15 15 @@ -292,8 +290,8 @@ - 150 - 100 + 290 + 205 15 15 @@ -303,21 +301,46 @@ - + 10 - 75 + 180 + 15 + 15 + + /local-weather/config/generate-thermal-lift-flag + + dialog-apply + + + + + 150 + 180 + 15 + 15 + + /local-weather/config/presampling-flag + + dialog-apply + + + + + + 5 + 150 - 10 - 50 + 160 + 150 - 80 - 50 + 230 + 150 90 20 0.3 @@ -329,11 +352,147 @@ - 180 - 50 + 330 + 150 + + + 5 + 125 + + + + + 160 + 125 + + + + + 230 + 125 + 90 + 20 + 0.1 + 1.0 + /local-weather/config/ground-haze-factor + + dialog-apply + + + + + 330 + 125 + + + + + + 5 + 100 + + + + + 160 + 100 + + + + + 230 + 100 + 90 + 20 + 30000.0 + 140000.0 + /local-weather/config/max-vis-range-m + + dialog-apply + + + + + 330 + 100 + + + + + + 5 + 75 + + + + + + 15 + 45 + + + + + 78 + 60 + + + + + 60 + 45 + 100 + 20 + 0.5 + 2.0 + /local-weather/config/large-scale-persistence + + dialog-apply + + + + + 165 + 45 + + + + + + 205 + 45 + + + + + 257 + 60 + + + + + 250 + 45 + 100 + 20 + 0.0 + 1.0 + /local-weather/config/small-scale-persistence + + dialog-apply + + + + + 355 + 45 + + + + 10 10 From b426b4dbed805964f178bc00796ca89822336ba6 Mon Sep 17 00:00:00 2001 From: Gijs de Rooy Date: Tue, 3 Jan 2012 21:35:26 +0100 Subject: [PATCH 2/2] Transmit door-positions over MP. --- Aircraft/c172p/Models/c172p.xml | 9 +++++++++ Aircraft/c172p/c172p-set.xml | 3 +++ 2 files changed, 12 insertions(+) diff --git a/Aircraft/c172p/Models/c172p.xml b/Aircraft/c172p/Models/c172p.xml index 94eb6af57..2f8f51e54 100644 --- a/Aircraft/c172p/Models/c172p.xml +++ b/Aircraft/c172p/Models/c172p.xml @@ -7,6 +7,15 @@ -3.0 -0.065 + + + + var rplayer = cmdarg(); + rplayer.getNode("sim/model/door-positions/rightDoor/position-norm", 1).alias(rplayer.getNode("sim/multiplay/generic/float[0]")); + rplayer.getNode("sim/model/door-positions/leftDoor/position-norm", 1).alias(rplayer.getNode("sim/multiplay/generic/float[1]")); + rplayer.getNode("sim/model/door-positions/baggageDoor/position-norm", 1).alias(rplayer.getNode("sim/multiplay/generic/float[2]")); + + diff --git a/Aircraft/c172p/c172p-set.xml b/Aircraft/c172p/c172p-set.xml index 8a5eeb9b9..36c7867cd 100644 --- a/Aircraft/c172p/c172p-set.xml +++ b/Aircraft/c172p/c172p-set.xml @@ -100,6 +100,9 @@ Started October 23 2001 by John Check, fgpanels@rockfish.net 1 + + + 0 0 0