diff --git a/Docs/README.local_weather.html b/Docs/README.local_weather.html index a2afa3485..505308079 100644 --- a/Docs/README.local_weather.html +++ b/Docs/README.local_weather.html @@ -7,7 +7,7 @@ -

Local Weather Package - v0.8

+

Local Weather Package - v0.85

1. Introduction

@@ -15,7 +15,7 @@ The aim of a local weather system is to simulate weather phenomena tied to speci This is in contrast to the current (v.2.0.0) weather system of Flightgear where weather changes affect the weather everywhere in the simulated world and are (with few exceptions) not tied to specific locations. In such a system, it is impossible to observe e.g. the approach of a rainfront while flying in sunshine.

-The local weather package ultimately aims to provide the functionality to simulate such local phenomena. In version 0.8, the package supplies various cloud placement algorithms, as well as local control over most major weather parameters (wind, visibility, pressure, temperature, rain, snow, thermal lift, turbulence...) through interpolation routines and event volumes. The dynamics of the different systems is tied together - clouds and weather effects drift in the specified wind field. The package also contains a fairly detailed algorithm to generate convective clouds and thermals with a realistic distribution. Unfortunately, as of v0.8, there is no interaction yet between the windfield and the cloud-generating algorithms, i.e. while the placement algorithms create a realistic configuration of thermals and convective clouds, the wind will simply move this configuration, not create, destroy or move clouds in altitude dynamically (which would be realistic).

+The local weather package ultimately aims to provide the functionality to simulate such local phenomena. In version 0.85, the package supplies various cloud placement algorithms, as well as local control over most major weather parameters (wind, visibility, pressure, temperature, rain, snow, thermal lift, turbulence...) through interpolation routines and event volumes. The dynamics of the different systems is tied together - clouds and weather effects drift in the specified wind field. The package also contains a fairly detailed algorithm to generate convective clouds and thermals with a realistic distribution. Unfortunately, as of v0.85, there is no interaction yet between the windfield and the cloud-generating algorithms, i.e. while the placement algorithms create a realistic configuration of thermals and convective clouds, the wind will simply move this configuration, not create, destroy or move clouds in altitude dynamically (which would be realistic).

For long-range flights, the system automatically provides transitions between different weather patterns like fronts and low and high pressure areas. However, basically all features currently present can and will eventually be improved.

@@ -24,7 +24,7 @@ For long-range flights, the system automatically provides transitions between di The package needs to be unpacked in the Flightgear root directory. It writes content into the Nasal/, gui/, gui/dialogs/, Shaders, Effects/, Docs/, and Models/Weather/ subdirectories. The installation does not overwrite any of the default Flightgear files, but to be accessible from the menu, one must copy gui/menubar.xml.alt to the default menubar.xml or copy the last two lines of the environemnt menu calling local_weather and local_weather_tiles into the default file.

-This adds the items Local Weather and Local weather tiles to the Environment menu when Flightgear is up. Most of the basic functionality is contained in local_weather.nas which is loaded at startup and identifies itself with a message, but does not start any functions unless called from the GUI.

+This adds the items Local Weather, Local Weather Tiles and Local Weather Settings to the Environment menu when Flightgear is up. Most of the basic functionality is contained in local_weather.nas which is loaded at startup and identifies itself with a message, but does not start any functions unless called from the GUI.

3. Functionality

@@ -80,8 +80,11 @@ The picture illustrates the result of a layer generation call for Nimbostratus c

+

Cloudbox

+The cloudbox placement is an experimental routine allowing to define a cloud core, border and bottom region and to place different texture types into these regions. The underlying idea is that this would generate better Cumulus clouds from small texture bits. However, currently the Cumulus clouds are generated by multiple layers of whole cloud photographs, creating a more realistic impression. Thus, the cloudbox is not actively developed further.

+

Tile placement

The second menu is used to place complete weather tiles based on low-level calls. It is intended for the user to automatically create the various weather patterns during flight.

@@ -90,7 +93,7 @@ The second menu is used to place complete weather tiles based on low-level calls

-The dropdown menu is used to select the type of weather tile to build. The menu contains two groups of tiles - the first are classified by airmass, whereas the last two are scenarios intended for soaring.

+Weather is created in a series of 40x40 km squares, called tiles. Tiles are classified by airmass, such that the sequence of tiles can describe for example the transition from a high pressure area to a low pressure area. The dropdown menu is used to select the type of weather tile to build initially. The menu contains two groups of tiles - the first are classified by airmass, whereas the last two are scenarios intended for soaring.

Below are entries for three parameters. The first two are the simplified version of wind direction and speed for the user who is not interested in specifying many different wind interpolation points. The third parameter, the altitude offset, is to manually adjust the altitude level of clouds in the absence of terrain presampling. Cloud layer placement calls are then specified for absolute altitudes and calibrated at sea level. As a result, layers are placed too low in mountainous terrain, hence the need for an offset. The offset may at present also be useful for dynamical weather, as convective clouds with terrain presampling follow terrain altitude, which looks strange when the clouds are allowed to drift in the wind without altitude correction.

@@ -108,7 +111,7 @@ Below the menu are five tickboxes. 'Terrain presampling' finds the distribution The option 'dynamical weather' ties all clouds and weather effects to the windfield. If that option is not chosen, the wind is still generated according to the chosen model, but only felt by the aircraft. This makes e.g. soaring unrealistic, as the aircraft continuously drifts out of a static thermal below a static cap cloud. When 'dynamical weather' is selected, aircraft, cloud and thermal are all displaced by the wind.

-The slider 'Thermal properties' is only relevant for soaring scenarios. It governs the rato of maximum lift to radius of a thermal. A setting close to 'low convection' creates large thermals with relatively small lift and virtually no turbulence, a setting close to 'rough day' creates very narrow, turbulent thermals with large lift. Unless thermals are placed, no other weather tile is affected by the settings.

+The slider 'Thermal properties' is mainly relevant for soaring scenarios. It governs the rato of maximum lift to radius of a thermal. A setting close to 'low convection' creates large thermals with relatively small lift and virtually no turbulence, a setting close to 'rough day' creates very narrow, turbulent thermals with large lift. However, it also affects the Cumulus textures to be used. 'low convection' creates well-formed, smooth Cumuli whereas 'rough day' biases the texture selection towards more rugged and diffuse clouds.

The button 'Show winds' brings up the detailed wind menu which is needed for the wind models 'aloft interpolated' and 'aloft waypoints':

@@ -132,9 +135,32 @@ The following pictures show the results of tile setups 'Low-pressure-border' and

+

Performance settings

+ + +The performance setting menu is available from the main menubar. It controls the allocation of system resources to the various tasks.

+ +

+ +

+ +The first part controls the creation of new weather tiles, the second part controls the ranges up to which clouds inside a generated weather tile are shown as part of the scenery (clouds created but not shown are stored and processed in a buffer array). Clouds are visible if and only if they are 1) part of a created tile and 2) closer than the buffering range. This is illustrated in the following figure: Tiles are only created if their center is within range, clouds are only visible if the cloud model itself is within range, the resulting region dependent on the two ranges in which clouds are displayed is shown in red.

+ +

+ +

+ +From this, it is apparent that the two ranges should not be drastically different. Setting tile creation range to 55 km implies lots of work trying to asses the properties of faraway terrain to generate suitable cloud distributions, setting the buffering range to 15 km means that most of these clouds are never visible. Note that in low visibility, a large tile creation range can be problematic, as Flightgear may not have loaded the terrain. Note also that setting the tile creation range above 40 km means a slow startup of the system, as it needs to create 5 tiles on startup instead of just a single one.

+ +There are additional options to do asymmetric buffering, i.e. to 'cut' out a wedge in the rear of the aircraft in which the tile creation radius or the cloud visibility radius are lower. For example, setting the buffering ratio to 0.2 and the angle to 180 degrees corresponds to a very agressive buffering in which clouds in the rear hemisphere of the aircraft are almost absent and shown only to 20% of the set distance. This may increase performance by 20-30%, but only in straight flight - in fast turns, lots of clouds have to be shuffled from the buffer into the scenery and back, which is actually slower than never trying to cut a wedge at all. Also, agressive asymmetric buffering is not nice in external views as the reference is always the aircraft course.

+ +The last option controls the amout of resources allocated to making clouds drift in the wind if dynamical weather is on. In order to avoid freezes, the weather dynamics system processes only a fixed number of clouds per frame inside the field of view. That means that faraway clouds do not necessarily move even with dynamical weather on. The slider indirectly controls the distance out to which clouds are being processed.

+ +All performance setting menu-options work at runtime, but are processed over time rather than instantaneously, i.e. it takes 10-20 seconds till the new balance between buffered clouds and active clouds is reached after the slider is moved.

+

4. Cloud models

-The package contains a number of different cloud models, both static ones for Cirrus and Cirrocumulus clouds as well as rotated ones for Altocumulus, Cirrostratus, Cumulus, Cumulonimbus, Stratus and Nimbostratus cloudlet models. Neither the cloud textures, nor the models nor the transformations are perfected, and any aspect can be improved. Currently the standard clouds cannot quite reach the sophistication of the shader-based standard 3-d clouds of Flightgear, but the detailed Cumulus clouds are on the verge of catching up.

+The package contains a number of different cloud models, both static ones for Cirrus and Cirrocumulus clouds as well as rotated ones for Altocumulus, Cirrostratus, Cumulus, Cumulonimbus, Stratus and Nimbostratus cloudlet models. Neither the cloud textures, nor the models nor the transformations are perfected, and any aspect can be improved.

@@ -186,7 +212,7 @@ Effect volumes are always specified between a minimum and a maximum altitude, an where geometry is a flag (1: circular, 2: elliptical and 3: rectangular), lat and lon are the latitude and longitude, r1 and r2 are the two size parameters for the elliptic or rectangular shape (for the circular shape, only the first is used), phi is the rotation angle of the shape (not used for circular shape), alt_low and alt_high are the altitude boundaries, vis, rain, snow, turb and lift are weather parameters which are either set to the value they should assume, or to -1 if they are not to be used, or to -2 if a function instead of a parameter is to be used. Since thermal lift can be set to negative values in a sink, a separate flag is provided in this case.

-In version 0.8, thermal lift is implemented by function. There is no easy way to implement any weather parameter by function in an effect volume, as this requires some amount of Nasal coding. +In version 0.85, thermal lift is implemented by function. There is no easy way to implement any weather parameter by function in an effect volume, as this requires some amount of Nasal coding.

6. Wind models and dynamical weather

@@ -243,19 +269,18 @@ To make your own tile visible, an entry in the menu gui/dialogs/local_weather

9. Performance tuning

-With default settings, the local weather package generates a 40x40 km weather tile when the aircraft is closer than 35 km to the tile center and unloads it when the aircraft is more than 37 km away. This means that the system can generate at most 4 tiles at once and clouds are visible for at least 15 km and up to 30 km (the latter number determined by fading in the shaders). However, rendering and managing multiple overcast cloud layers in a region of 80x80 km is a significant drain on performance. For older systems, a few things can be tried:

+With default settings, the local weather package generates a 40x40 km weather tile when the aircraft is closer than 39 km to the tile center and unloads it when the aircraft is more than 39.5 km away. This means that the system can generate at most 4 tiles at once and clouds are visible for at least 19 km and up to 45 km (the latter number determined by fading in the shaders). However, rendering and managing multiple overcast cloud layers in a region of 80x80 km is a significant drain on performance. For older systems, a few things can be done:

diff --git a/Docs/menu1.jpg b/Docs/menu1.jpg index 743325572..69f89877e 100644 Binary files a/Docs/menu1.jpg and b/Docs/menu1.jpg differ diff --git a/Docs/menu4.jpg b/Docs/menu4.jpg new file mode 100644 index 000000000..4394ba1b3 Binary files /dev/null and b/Docs/menu4.jpg differ diff --git a/Docs/tiles.gif b/Docs/tiles.gif new file mode 100644 index 000000000..1dba02ba0 Binary files /dev/null and b/Docs/tiles.gif differ diff --git a/Nasal/compat_layer.nas b/Nasal/compat_layer.nas index afe3582b8..a91a0ccd7 100644 --- a/Nasal/compat_layer.nas +++ b/Nasal/compat_layer.nas @@ -26,6 +26,117 @@ # get_elevation_vector to get terrain elevation at given coordinate vector + +# This file contains portability wrappers for the local weather system: +# http://wiki.flightgear.org/index.php/A_local_weather_system +# +# This module is intended to provide a certain degree of backward compatibility for past +# FlightGear releases, while sketching out the low level APIs used and required by the +# local weather system, as these +# are being added to FlightGear. +# +# This file contains various workarounds for doing things that are currently not yet directly +# supported by the core FlightGear/Nasal APIs (fgfs 2.0). +# +# Some of these workarounds are purely implemented in Nasal space, and may thus not provide sufficient +# performance in some situations. +# +# The goal is to move all such workarounds eventually into this module, so that the high level weather modules +# only refer to this "compatibility layer" (using an "ideal API"), while this module handles +# implementation details +# and differences among different versions of FlightGear, so that key APIs can be ported to C++ space +# for the sake +# of improving runtime performance and efficiency. +# +# This provides an abstraction layer that isolates the rest of the local weather system from low +# level implementation details. +# +# C++ developers who want to help improve the local weather system (or the FlightGear/Nasal +# interface in general) should +# check out this file (as well as the wiki page) for APIs or features that shall eventually be +# re/implemented in C++ space for +# improving the local weather system. +# +# +# This module provides a handful of helpers for dynamically querying the Nasal API of the running fgfs binary, +# so that it can make use of new APIs (where available), while still working with older fgfs versions. +# +# Note: The point of these helpers is that they should really only be used +# by this module, and not in other parts/files of the +# local weather system. Any hard coded special cases should be moved into this module. +# +# The compatibility layer is currently work in progress and will be extended as new Nasal +# APIs are being added to FlightGear. + +########################################### +# header checking availability of functions +########################################### + + +var has_symbol = func(s) contains(globals,s); +var is_function = func(s) typeof(globals[s])=='func'; +var has_function = func(f) has_symbol(f) and is_function(f); + +# try to call a function with given parameters +# save exceptions to err vector +# returns 0 for no exceptions (exceptions vector is empty) +# returns >=1 for exception occurred (i.e. unsupported API call) + + +var try_call = func(f, params) { +var err=[]; +call(globals[f], params, nil,nil,err); # see http://plausible.org/nasal/lib.html +return size(err); +}; + + +var query = func(api,params) { + if ( has_function(api) ) { + return try_call(api, params ); + } + return 1; # fail +} + +var patches = { geodinfo: "http://flightgear.org/forums/viewtopic.php?f=5&t=7358&st=0&sk=t&sd=a&start=90#p82805", }; + +# query fgfs binary for required APIs and set values in this hash +var features = {}; + + +#fixme: compare results from new and old API +var check_geodinfo_vec = func { + var err=[]; + + if ( query('geodinfo',[ [37.618,-122.374],1000])==0 ) { + printf("geodinfo found"); # now try to use it + var ksfo=[37.618, -122.374]; + var alt=10000; + # see if it returns a vector or not + call( func { print (alt); (typeof(geodinfo(ksfo,alt))=='vector')?return:die(); }, [], caller()[0],nil,err); + print('-','geodinfo:', (size(err) >=1) ? "Vector support unavailable" : "Vector support available"); + if(size(err) and contains(patches,'geodinfo')) print('---> A patch is available at ', patches['geodinfo']); + + return size(err)?0:1; + } + return 0; +} + +_setlistener("/sim/signals/nasal-dir-initialized", func { + print ("Compatibility layer: Checking available Nasal APIs:"); + print ("(this may cause harmless error messages when hard-coded support is lacking)"); + print ("##########################################"); + features.geodinfo_supports_vectors= check_geodinfo_vec (); + print("features.geodinfo_supports_vectors=", features.geodinfo_supports_vectors); + print ("##########################################"); + print("Compatibility checks done."); +}); + +# this is now where we can simply refer to features.geodinfo_supports_vectors +# for checking if vector support is available or not - to use the most appropriate +# APIs + + + #################################### # set visibility to given value #################################### @@ -223,10 +334,47 @@ settimer( func {smooth_wind_loop(vx,vy,vx_old,vy_old,counter-1, count_max); },ti var create_cloud = func(path, lat, long, alt, heading) { var tile_counter = getprop(lw~"tiles/tile-counter"); +var buffer_flag = getprop(lw~"config/buffer-flag"); +var dynamics_flag = getprop(lw~"config/dynamics-flag"); +var d_max = weather_tile_management.cloud_view_distance + 1000.0; + + +# first check if the cloud should be stored in the buffer +# we keep it if it is in visual range or at high altitude (where visual range is different) + +if (buffer_flag == 1) + { + # calculate the distance to the aircraft + var pos = geo.aircraft_position(); + var cpos = geo.Coord.new(); + cpos.set_latlon(lat,long,0.0); + var d = pos.distance_to(cpos); + + if ((d > d_max) and (alt < 20000.0)) # we buffer the cloud + { + var b = weather_tile_management.cloudBuffer.new(lat, long, alt, path, heading, tile_counter); + if (dynamics_flag ==1) {b.timestamp = weather_dynamics.time_lw;} + append(weather_tile_management.cloudBufferArray,b); + return; + } + } + +# now check if we are writing from the buffer, in this case change tile index +# to buffered one + +if (getprop(lw~"tmp/buffer-status") == "placing") + { + tile_counter = getprop(lw~"tmp/buffer-tile-index"); + } + + +# if the cloud is not buffered, get property tree nodes and write it +# into the scenery var n = props.globals.getNode("local-weather/clouds", 1); var c = n.getChild("tile",tile_counter,1); + var cloud_number = n.getNode("placement-index").getValue(); for (var i = cloud_number; 1; i += 1) if (c.getChild("cloud", i, 0) == nil) @@ -266,20 +414,27 @@ model.getNode("load", 1).remove(); n.getNode("cloud-number").setValue(n.getNode("cloud-number").getValue()+1); -# sort the model nodes into a vector +# sort the model node into a vector for easy deletion -append(weather_tile_management.modelArrays[tile_counter-1],model); +# append(weather_tile_management.modelArrays[tile_counter-1],model); +# sort the cloud into the cloud hash array -# if weather dynamics is on, also create a timestamp property and sort into quadtree +if ((buffer_flag == 1) and (getprop(lw~"tmp/tile-management") != "single tile")) + { + var cs = weather_tile_management.cloudScenery.new(tile_counter, cl, model); + append(weather_tile_management.cloudSceneryArray,cs); + } -if (getprop(lw~"config/dynamics-flag") == 1) +# if weather dynamics is on, also create a timestamp property and sort the cloud node into quadtree + +#if (getprop(lw~"config/dynamics-flag") == 1) +if (dynamics_flag == 1) { cl.getNode("timestamp-sec",1).setValue(weather_dynamics.time_lw); var blat = getprop(lw~"tiles/tmp/latitude-deg"); var blon = getprop(lw~"tiles/tmp/longitude-deg"); var alpha = getprop(lw~"tmp/tile-orientation-deg"); - #weather_dynamics.sort_into_quadtree(blat, blon, alpha, lat, long, weather_dynamics.cloudQuadtree, cl); weather_dynamics.sort_into_quadtree(blat, blon, alpha, lat, long, weather_dynamics.cloudQuadtrees[tile_counter-1], cl); } @@ -298,6 +453,12 @@ if ((i < 0) or (i==0)) { print("Cloud placement from array finished!"); 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(); + # print("dir_index: ",dir_index); + props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("generated-flag").setValue(2); + return; } @@ -309,8 +470,6 @@ if (s < k_max) {k_max = s;} for (var k = 0; k < k_max; k = k+1) { - #print(s, " ", k, " ", s-k-1, " ", clouds_path[s-k-1]); - #create_cloud(clouds_type[s-k-1], clouds_path[s-k-1], clouds_lat[s-k-1], clouds_lon[s-k-1], clouds_alt[s-k-1], 0.0, 0); create_cloud(clouds_path[s-k-1], clouds_lat[s-k-1], clouds_lon[s-k-1], clouds_alt[s-k-1], clouds_orientation[s-k-1]); } @@ -412,6 +571,15 @@ else if (status == "idle") # we initialize the loop { mvec = weather_tile_management.modelArrays[index-1]; msize = size(mvec); + if (msize == 0) + { + print("Tile deletion loop finished!"); + setprop(lw~"tmp/thread-status", "idle"); + setprop(lw~"clouds/placement-index",0); + setprop(lw~"clouds/model-placement-index",0); + setsize(weather_tile_management.modelArrays[index-1],0); + return; + } setprop(lw~"tmp/last-reading-pos-mod", msize); setprop(lw~"tmp/thread-status", "removing"); } @@ -475,9 +643,16 @@ var get_elevation_array = func (lat, lon) { var elevation = []; var n = size(lat); -for(var i = 0; i < n; i=i+1) +if (features.geodinfo_supports_vectors == 0) { - append(elevation, get_elevation(lat[i], lon[i])); + for(var i = 0; i < n; i=i+1) + { + append(elevation, get_elevation(lat[i], lon[i])); + } + } +else + { + elevation = geodinfo(lat,10000); } return elevation; diff --git a/Nasal/local_weather.nas b/Nasal/local_weather.nas index 14994d132..21badfc29 100644 --- a/Nasal/local_weather.nas +++ b/Nasal/local_weather.nas @@ -445,18 +445,18 @@ else if (wind_model_flag == 5) # aloft waypoint interpolated var altitude_agl = getprop("/position/altitude-agl-ft"); -if (getprop("tmp/presampling-flag") == 0) + +if (getprop(lw~"tmp/presampling-flag") == 0) { var boundary_alt = 600.0; - var windspeed_ground = windspeed/3.0; if (altitude_agl < boundary_alt) - {var windspeed_current = windspeed_ground + 2.0 * windspeed_ground * (altitude_agl/boundary_alt);} + {var windspeed_current = windspeed_ground + 2.0 * windspeed_ground * (altitude_agl/boundary_alt);} else {var windspeed_current = windspeed;} } -else +else { var alt_median = alt_50_array[tile_index - 1]; var alt_difference = alt_median - (altitude - altitude_agl); @@ -1022,13 +1022,18 @@ else if ((type == "Cumulonimbus") or (type == "Cumulonimbus (rain)")) { } else if (type == "Cirrus") { if (subtype == "large") { - if (rn > 0.66) {path = "Models/Weather/cirrus1.xml";} - else if (rn > 0.33) {path = "Models/Weather/cirrus2.xml";} - else {path = "Models/Weather/cirrus3.xml";} + if (rn > 0.833) {path = "Models/Weather/cirrus1.xml";} + else if (rn > 0.666) {path = "Models/Weather/cirrus2.xml";} + else if (rn > 0.5) {path = "Models/Weather/cirrus3.xml";} + else if (rn > 0.333) {path = "Models/Weather/cirrus4.xml";} + else if (rn > 0.166) {path = "Models/Weather/cirrus5.xml";} + else {path = "Models/Weather/cirrus6.xml";} } else if (subtype == "small") { - if (rn > 0.5) {path = "Models/Weather/cirrus_amorphous1.xml";} - else {path = "Models/Weather/cirrus_amorphous2.xml";} + if (rn > 0.75) {path = "Models/Weather/cirrus_amorphous1.xml";} + else if (rn > 0.5) {path = "Models/Weather/cirrus_amorphous2.xml";} + else if (rn > 0.25) {path = "Models/Weather/cirrus_amorphous3.xml";} + else {path = "Models/Weather/cirrus_amorphous4.xml";} } } else if (type == "Cirrocumulus") { @@ -1037,8 +1042,14 @@ else if (type == "Cirrocumulus") { else {path = "Models/Weather/cirrocumulus2.xml";} } else if (subtype == "large") { - if (rn > 0.5) {path = "Models/Weather/cirrocumulus1.xml";} - else {path = "Models/Weather/cirrocumulus4.xml";} + if (rn > 0.875) {path = "Models/Weather/cirrocumulus1.xml";} + else if (rn > 0.750){path = "Models/Weather/cirrocumulus4.xml";} + else if (rn > 0.625){path = "Models/Weather/cirrocumulus5.xml";} + else if (rn > 0.500){path = "Models/Weather/cirrocumulus6.xml";} + else if (rn > 0.385){path = "Models/Weather/cirrocumulus7.xml";} + else if (rn > 0.250){path = "Models/Weather/cirrocumulus8.xml";} + else if (rn > 0.125){path = "Models/Weather/cirrocumulus9.xml";} + else {path = "Models/Weather/cirrocumulus10.xml";} } } else if (type == "Cirrocumulus (cloudlet)") { @@ -1238,7 +1249,7 @@ setprop(lwi~"ipoint-number",0); weather_tiles.last_pressure = 0.0; -# stop the effect loop and the interpolation loop, make sure thermal generation is off +# stop all loops make sure thermal generation is off setprop(lw~"effect-loop-flag",0); setprop(lw~"interpolation-loop-flag",0); @@ -1246,6 +1257,8 @@ setprop(lw~"tile-loop-flag",0); setprop(lw~"lift-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~"tmp/generate-thermal-lift-flag",0); # also remove rain and snow effects @@ -1261,13 +1274,22 @@ 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; -setsize(weather_tile_management.modelArrays,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); + setprop(lw~"clouds/buffer-count",0); + setprop(lw~"clouds/cloud-scenery-count",0); + },1.1); } @@ -1280,7 +1302,10 @@ var create_detailed_cumulus_cloud = func (lat, lon, alt, size) { #print(size); -var edge_bias = 0.0; +var edge_bias = convective_texture_mix; + +#print("edge_bias: ",edge_bias); + var size_bias = 0.0; if (size > 2.0) @@ -2015,7 +2040,24 @@ else var terrain_presampling_loop = func (blat, blon, nc, size, alpha) { -var n = 25; # number of geoinfo calls per frame +if (compat_layer.features.geodinfo_supports_vectors == 0) + {var n = 25;} # number of geoinfo calls per frame +else + {var n = 25;} # vector call + +var n_out = 25; + +# dynamically drop accuracy if framerate is low + +var dt = getprop("/sim/time/delta-sec"); + +if (dt > 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;} + if (nc <= 0) # we're done and may analyze the result { @@ -2027,7 +2069,7 @@ if (nc <= 0) # we're done and may analyze the result terrain_presampling(blat, blon, n, size, alpha); -settimer( func {terrain_presampling_loop(blat, blon, nc-n, size, alpha) },0); +settimer( func {terrain_presampling_loop(blat, blon, nc-n_out, size, alpha) },0); } @@ -2042,21 +2084,34 @@ var elevation = 0.0; var lat_vec = []; var lon_vec = []; +var lat_lon_vec = []; for (var i=0; i 0.2) {convective_texture_mix = 0.2;} +} + ########################################################### # create an effect volume ########################################################### @@ -2618,7 +2688,7 @@ 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); # now see if we need to presample the terrain @@ -2792,6 +2862,19 @@ if (getprop(lw~"config/dynamics-flag") ==1) } +# and start the buffer loop and housekeeping loop if needed + +if (getprop(lw~"config/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); + } + } + +#weather_tile_management.watchdog_loop(); + } @@ -2868,7 +2951,12 @@ setlistener(lw~"tmp/effect-thread-status", func {var s = size(effects_geo); eff 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);}); } @@ -2881,7 +2969,18 @@ var test = func { var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); -showDialog("local_weather_winds"); + + +var v = [1,2,3,4,5,6,7]; + +print(v[2]); +v = weather_tile_management.delete_from_vector(v, 2); +v = weather_tile_management.delete_from_vector(v, 2); + +for (var i = 0; i < size(v); i = i + 1) + { + print(v[i]); + } #weather_dynamics.cos_beta = 1; #weather_dynamics.sin_beta = 0; @@ -2912,9 +3011,9 @@ showDialog("local_weather_winds"); -# terrain_presampling_start(lat, lon, 1000, 20000, 0.0); +#terrain_presampling_start(lat, lon, 10000, 20000, 0.0); -# test: 8 identical position tuples for KSFO +#test: 8 identical position tuples for KSFO #var p=[ 37.6189722, -122.3748889, 37.6189722, -122.3748889, # 37.6289722, -122.3748889, 37.6189722, -122.3648889, # 37.6389722, -122.3748889, 37.6189722, -122.3548889, @@ -2975,7 +3074,7 @@ var clouds_orientation = []; var terrain_n = []; var alt_50_array = []; - +var alt_20_array = []; # array of currently existing effect volumes @@ -2987,6 +3086,10 @@ var n_effectVolumeArray = 0; var wind_model_flag = 1; +# a global determining the relative amount of different textures in detailed convective clouds + +var convective_texture_mix = 0.0; + # set all sorts of default properties for the menu @@ -3054,6 +3157,8 @@ 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/FL0-wind-from-heading-deg",0.0); setprop(lw~"tmp/FL0-windspeed-kt",8.0); setprop(lw~"tmp/FL50-wind-from-heading-deg",2.0); @@ -3078,12 +3183,21 @@ setprop(lw~"tmp/ipoint-longitude-deg",getprop("position/longitude-deg")); # set config values -setprop(lw~"config/distance-to-load-tile-m",35000.0); -setprop(lw~"config/distance-to-remove-tile-m",37000.0); +#setprop(lw~"config/distance-to-load-tile-m",35000.0); +#setprop(lw~"config/distance-to-remove-tile-m",37000.0); +setprop(lw~"config/distance-to-load-tile-m",39000.0); +setprop(lw~"config/distance-to-remove-tile-m",39500.0); setprop(lw~"config/detailed-clouds-flag",1); setprop(lw~"config/dynamics-flag",1); setprop(lw~"config/thermal-properties",1.0); setprop(lw~"config/wind-model","constant"); +setprop(lw~"config/buffer-flag",1); +setprop(lw~"config/asymmetric-reduction",0.7); +setprop(lw~"config/clouds-visible-range-m",30000.0); +setprop(lw~"config/asymmetric-buffering-flag",0); +setprop(lw~"config/asymmetric-buffering-reduction",0.3); +setprop(lw~"config/asymmetric-buffering-angle-deg",90.0); +setprop(lw~"config/clouds-in-dynamics-loop",250); # set the default loop flags to loops inactive @@ -3092,6 +3206,8 @@ 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~"buffer-loop-flag",0); +setprop(lw~"housekeeping-loop-flag",0); # create other management properties diff --git a/Nasal/weather_dynamics.nas b/Nasal/weather_dynamics.nas index 3f838d820..c92e12095 100644 --- a/Nasal/weather_dynamics.nas +++ b/Nasal/weather_dynamics.nas @@ -102,7 +102,7 @@ foreach (t; tiles) { var generated_flag = t.getNode("generated-flag").getValue(); - if (generated_flag == 1) + if ((generated_flag == 1) or (generated_flag ==2)) { var index = t.getNode("tile-index").getValue(); current_tile_index_wd = index; @@ -127,9 +127,9 @@ foreach (t; tiles) # if there are plenty of moving clouds nearby, no one pays attention to the small motion of distant clouds # price to pay is that some clouds appear to jump once they get into range -if (cloud_counter < 150) {view_distance = view_distance * 1.1;} -else if (cloud_counter > 250) {view_distance = view_distance * 0.9;} -if (view_distance > 30000.0) {view_distance = 30000.0;} +if (cloud_counter < 0.5 * max_clouds_in_loop) {view_distance = view_distance * 1.1;} +else if (cloud_counter > max_clouds_in_loop) {view_distance = view_distance * 0.9;} +if (view_distance > weather_tile_management.cloud_view_distance) {view_distance = weather_tile_management.cloud_view_distance;} #print(cloud_counter, " ", view_distance/1000.0); @@ -477,7 +477,6 @@ return xy_vec; } - ################################ # globals, constants, properties ################################ @@ -505,6 +504,7 @@ var lw = "/local-weather/"; # globals var time_lw = 0.0; +var max_clouds_in_loop = 250; # the quadtree structure diff --git a/Nasal/weather_tile_management.nas b/Nasal/weather_tile_management.nas index 605278025..b6090f3f9 100644 --- a/Nasal/weather_tile_management.nas +++ b/Nasal/weather_tile_management.nas @@ -12,6 +12,7 @@ # copy_entry to copy tile information from one node to another # create_neighbour to set up information for a new neighbouring tile # create_neighbours to initialize the 8 neighbours of the initial tile +# buffer_loop to manage the buffering of faraway clouds in an array # calc_geo to get local Cartesian geometry for latitude conversion # get_lat to get latitude from Cartesian coordinates # get_lon to get longitude from Cartesian coordinates @@ -31,9 +32,17 @@ var d_min = 100000.0; var i_min = 0; var distance_to_load = getprop(lw~"config/distance-to-load-tile-m"); var distance_to_remove = getprop(lw~"config/distance-to-remove-tile-m"); +var current_visibility = getprop(lw~"interpolation/visibility-m"); var current_heading = getprop("orientation/heading-deg"); var loading_flag = getprop(lw~"tmp/asymmetric-tile-loading-flag"); -var this_frame_generated_flag = 0; # use this flag to avoid overlapping tile generation calls +var this_frame_action_flag = 0; # use this flag to avoid overlapping tile operations + +setsize(active_tile_list,0); + +if (distance_to_load > 3.0 * current_visibility) + {distance_to_load = 3.0 * current_visibility;} +if (distance_to_load < 25000.0) + {distance_to_load = 25000.0;} foreach (var t; tNode) { @@ -43,12 +52,16 @@ foreach (var t; tNode) { if (d < d_min) {d_min = d; i_min = i;} var flag = t.getNode("generated-flag").getValue(); + if ((flag ==2) or (flag ==1)) {append(active_tile_list,t.getNode("tile-index").getValue());} + var dir = viewpos.course_to(tpos); var d_load = distance_to_load; var d_remove = distance_to_remove; if (loading_flag == 1) { - if ((abs(dir-current_heading) > 135.0) and (abs(dir-current_heading) < 225.0)) + var angle = abs(dir-current_heading); + #if (i==7) {print(angle);} + if ((angle > 135.0) and (angle < 225.0)) { d_load = 0.7 * d_load; d_remove = 0.7 * d_remove; @@ -59,13 +72,15 @@ foreach (var t; tNode) { # and if no other tile has been generated in this loop cycle # and the thread and convective system are idle # (we want to avoid overlapping tile generation) - if ((d < distance_to_load) and (flag==0) and (this_frame_generated_flag == 0) and (getprop(lw~"tmp/thread-status") == "idle") and (getprop(lw~"tmp/convective-status") == "idle")) + + if ((d < d_load) and (flag==0) and (this_frame_action_flag == 0) and (getprop(lw~"tmp/thread-status") == "idle") and (getprop(lw~"tmp/convective-status") == "idle") and (getprop(lw~"tmp/presampling-status") == "idle")) { + this_frame_action_flag = 1; setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1); print("Building tile unique index ",getprop(lw~"tiles/tile-counter"), " in direction ",i); - generate_tile(code, tpos.lat(), tpos.lon(),i); + append(active_tile_list,getprop(lw~"tiles/tile-counter")); - if (getprop(lw~"config/dynamics-flag") == 1) + if (getprop(lw~"config/dynamics-flag") == 1) { var quadtree = []; weather_dynamics.generate_quadtree_structure(0, quadtree); @@ -76,24 +91,50 @@ foreach (var t; tNode) { t.getNode("timestamp-sec").setValue(weather_dynamics.time_lw); t.getNode("tile-index",1).setValue(getprop(lw~"tiles/tile-counter")); - this_frame_generated_flag = 1; + generate_tile(code, tpos.lat(), tpos.lon(),i); + } - if ((d > distance_to_remove) and (flag==1)) # the tile needs to be deleted if it exists + if ((d > d_remove) and (flag == 2) and (this_frame_action_flag == 0)) # the tile needs to be deleted if it exists { - print("Removing tile, unique index ", t.getNode("tile-index").getValue()); + print("Removing tile, unique index ", t.getNode("tile-index").getValue()," direction ",i); remove_tile(t.getNode("tile-index").getValue()); t.getNode("generated-flag").setValue(0); + this_frame_action_flag = 1; } i = i + 1; } # end foreach #print("Minimum distance to: ",i_min); - if (i_min != 4) # we've entered a different tile + var presampling_status = getprop(lw~"tmp/presampling-status"); + var convective_status = getprop(lw~"tmp/convective-status"); + var thread_status = getprop(lw~"tmp/thread-status"); + + if ((presampling_status == "idle") and (convective_status == "idle") and (thread_status == "idle")) { + var system_status = "idle"; + } + else + {system_status = "computing";} + + # and (this_frame_action_flag == 0) and (presampling_status == "idle") and (convective_status=="idle")) + + # check if we've entered a different tile and if no operation is in progress + + # var gen_flag = tNode[i_min].getNode("generated-flag").getValue(); + if ((i_min != 4) and (system_status == "idle")) + { + + var gen_flag = tNode[i_min].getNode("generated-flag").getValue(); + if (gen_flag != 2){ + print("Tile direction ",i_min, " not generated!"); + print("Flag: ",gen_flag); + } + print("Changing active tile to direction ", i_min); change_active_tile(i_min); + } @@ -109,17 +150,21 @@ if (getprop(lw~"tile-loop-flag") ==1) {settimer(tile_management_loop, 5.0);} var generate_tile = func (code, lat, lon, dir_index) { -# make sure the last tile call has finished, otherwise put on hold -#if ((getprop(lw~"tmp/thread-status") != "idle") or (getprop(lw~"tmp/convective-status") != "idle")) -# { -# print("Tile generation overlap, delaying..."); -# settimer( func {generate_tile(code, lat, lon, dir_index);}, 1); -# } +# the code should never be NIL, but this appears to happen under certain conditions +# so just to be on the safe side make sure it is set to current tile code if +# it actually is NIL + +if (code == "") + { + print("No tile code - falling back on default!"); + code = getprop(lw~"tiles/code"); + } setprop(lw~"tiles/tmp/latitude-deg", lat); setprop(lw~"tiles/tmp/longitude-deg",lon); setprop(lw~"tiles/tmp/code",code); +setprop(lw~"tiles/tmp/dir-index",dir_index); # do windspeed and orientation before presampling check, but test not to do it again @@ -145,7 +190,7 @@ if (((getprop(lw~"tmp/presampling-flag") == 1) and (getprop(lw~"tmp/presampling- var windspeed = getprop(lw~"tmp/windspeed-kt"); windspeed = windspeed + 2.0 * (rand()-0.5) * 2.0; if (windspeed < 0) {windspeed = rand();} - setprop(lw~"tmp/windspeed-kt"); + setprop(lw~"tmp/windspeed-kt", windspeed); # store the tile orientation and wind strength in an array for fast processing @@ -180,7 +225,6 @@ if (((getprop(lw~"tmp/presampling-flag") == 1) and (getprop(lw~"tmp/presampling- if ((getprop(lw~"tmp/presampling-flag") == 1) and (getprop(lw~"tmp/presampling-status") == "idle")) { local_weather.terrain_presampling_start(lat, lon, 1000, 40000, getprop(lw~"tmp/tile-orientation-deg")); - setprop(lw~"tiles/tmp/dir-index",dir_index); return; } @@ -338,7 +382,22 @@ else if (getprop(lw~"tmp/tile-management") == "realistic weather") var remove_tile = func (index) { -compat_layer.remove_clouds(index); +# remove tile from active list + +var s = size(active_tile_list); + +for (var j = 0; j < s; j=j+1) + { + if (index == active_tile_list[j]) + { + active_tile_list = delete_from_vector(active_tile_list,j); + break; + } + } + +settimer( func { props.globals.getNode("local-weather/clouds", 1).removeChild("tile",index) },100); + +#compat_layer.remove_clouds(index); var effectNode = props.globals.getNode("local-weather/effect-volumes").getChildren("effect-volume"); @@ -365,7 +424,7 @@ setprop(lw~"effect-volumes/effect-placement-index",0); if (getprop(lw~"config/dynamics-flag") ==1) { - setsize(weather_dynamics.cloudQuadtrees[index-1],0); + settimer( func {setsize(weather_dynamics.cloudQuadtrees[index-1],0);},1.0); } # rebuild effect volume vector @@ -505,9 +564,16 @@ t.getNode("latitude-deg").setValue(f.getNode("latitude-deg").getValue()); t.getNode("longitude-deg").setValue(f.getNode("longitude-deg").getValue()); t.getNode("generated-flag").setValue(f.getNode("generated-flag").getValue()); t.getNode("tile-index").setValue(f.getNode("tile-index").getValue()); -t.getNode("code").setValue(f.getNode("code").getValue()); t.getNode("timestamp-sec").setValue(f.getNode("timestamp-sec").getValue()); t.getNode("orientation-deg").setValue(f.getNode("orientation-deg").getValue()); +t.getNode("code").setValue(f.getNode("code").getValue()); + +#if (f.getNode("code").getValue() == "") +# {print("Empty tile code copying from ", from_index," to ", to_index, "!");} + +#if (f.getNode("code").getValue() != "") # we don't copy an empty code, that can trigger errors +# {t.getNode("code").setValue(f.getNode("code").getValue());} + } ##################################### @@ -530,11 +596,16 @@ if ((index == 6) or (index == 7) or (index == 8)) {y = -40000.0;} var t = props.globals.getNode(lw~"tiles").getChild("tile",index,0); +# use the last built tile code as default, in case a tile isn't formed when reached, +# the code is not empty but has a plausible value + +var default_code = getprop(lw~"tiles/code"); + t.getNode("latitude-deg",1).setValue(blat + get_lat(x,y,phi)); t.getNode("longitude-deg",1).setValue(blon + get_lon(x,y,phi)); t.getNode("generated-flag",1).setValue(0); t.getNode("tile-index",1).setValue(-1); -t.getNode("code",1).setValue(""); +t.getNode("code",1).setValue(default_code); t.getNode("timestamp-sec",1).setValue(weather_dynamics.time_lw); t.getNode("orientation-deg",1).setValue(0.0); } @@ -636,6 +707,233 @@ setprop(lw~"tiles/tile[8]/timestamp-sec",weather_dynamics.time_lw); setprop(lw~"tiles/tile[8]/orientation-deg",0.0); } + +############################### +# buffer loop +############################### + +var buffer_loop = func (index) { + +var n = 5; +var n_max = size(cloudBufferArray); +var s = size(active_tile_list); + +setprop(lw~"clouds/buffer-count",n_max); + +# don't do anything as long as the buffer is empty + +if (n_max == 0) # nothing to do, loop over + {if (getprop(lw~"buffer-loop-flag") ==1) {settimer( func {buffer_loop(index)}, 0);} return;} + +# don't process the buffer if a tile call is writing clouds into the scenery + +if (getprop(lw~"tmp/thread-status") == "placing") + {if (getprop(lw~"buffer-loop-flag") ==1) {settimer( func {buffer_loop(index)}, 0);} return;} + +# lock the system status for buffer operations and get flags + +setprop(lw~"tmp/buffer-status", "placing"); +var asymmetric_buffering_flag = getprop(lw~"config/asymmetric-buffering-flag"); + +if (asymmetric_buffering_flag ==1) + { + var buffering_angle = getprop(lw~"config/asymmetric-buffering-angle-deg"); + var buffering_reduction = getprop(lw~"config/asymmetric-buffering-reduction"); + var current_heading = getprop("orientation/heading-deg"); + } + +# now process the buffer + + +if (index > n_max-1) {index = 0;} + +var i_max = index + n; +if (i_max > n_max) {i_max = n_max;} + +for (var i = index; i < i_max; i = i+1) + { + var c = cloudBufferArray[i]; + + # check if the cloud is still part of an active tile, if not remove from buffer + + + var flag = 0; + for (var j = 0; j < s; j = j+1) + { + if (active_tile_list[j] == c.index) {flag = 1; break;} + } + + if (flag == 0) + { + cloudBufferArray = delete_from_vector(cloudBufferArray,i); + i = i -1; i_max = i_max - 1; n_max = n_max - 1; + continue; + } + + # if wind drift is on, move the cloud + + if (getprop(lw~"config/dynamics-flag") == 1) + { + c.move(); + } + + + # check distance and decide if the cloud should be created + + var d = c.get_distance(); + var d_comp = cloud_view_distance + 1000.0; + + if (asymmetric_buffering_flag == 1) + { + var dir = c.get_course(); + var angle = abs(dir-current_heading); + if ((angle > 180.0 - 0.5 * buffering_angle) and (angle < 180 + 0.5 * buffering_angle)) + { + d_comp = buffering_reduction * d_comp; + } + } + + + + if (d < d_comp) # insert the cloud into scenery and delete from buffer + { + setprop(lw~"tmp/buffer-tile-index",c.index); + compat_layer.create_cloud(c.path, c.lat, c.lon, c.alt, c.orientation); + cloudBufferArray = delete_from_vector(cloudBufferArray,i); + i = i -1; i_max = i_max - 1; n_max = n_max - 1; + deleted_flag = 1; + } + + + + + } # end for i + + +# unlock the system status for buffer operations + +setprop(lw~"tmp/buffer-status", "idle"); + +if (getprop(lw~"buffer-loop-flag") ==1) {settimer( func {buffer_loop(i)}, 0);} +} + + +############################### +# housekeeping loop +############################### + +var housekeeping_loop = func (index) { + +var n = 5; +var n_max = size(cloudSceneryArray); +var s = size(active_tile_list); + +setprop(lw~"clouds/cloud-scenery-count",n_max); + +# don't do anything as long as the array is empty + +if (n_max == 0) # nothing to do, loop over + {if (getprop(lw~"housekeeping-loop-flag") ==1) {settimer( func {housekeeping_loop(index)}, 0);} return;} + +# parse the flags + +var asymmetric_buffering_flag = getprop(lw~"config/asymmetric-buffering-flag"); + +if (asymmetric_buffering_flag ==1) + { + var buffering_angle = getprop(lw~"config/asymmetric-buffering-angle-deg"); + var buffering_reduction = getprop(lw~"config/asymmetric-buffering-reduction"); + var current_heading = getprop("orientation/heading-deg"); + } + +# now process the array + +if (index > n_max-1) {index = 0;} + +var i_max = index + n; +if (i_max > n_max) {i_max = n_max;} + +for (var i = index; i < i_max; i = i+1) + { + var c = cloudSceneryArray[i]; + + var flag = 0; + + for (var j = 0; j < s; j = j+1) + { + if (active_tile_list[j] == c.index) {flag = 1; break;} + } + + if (flag == 0) + { + c.removeNodes(); + cloudSceneryArray = delete_from_vector(cloudSceneryArray,i); + i = i -1; i_max = i_max - 1; n_max = n_max - 1; + continue; + } + + var d = c.get_distance(); + var alt = c.get_altitude(); + + d_comp = cloud_view_distance + 1000.0; + + + if (asymmetric_buffering_flag == 1) + { + var dir = c.get_course(); + var angle = abs(dir-current_heading); + if ((angle > 180.0 - 0.5 * buffering_angle) and (angle < 180 + 0.5 * buffering_angle)) + { + d_comp = buffering_reduction * d_comp; + } + } + + if ((d > d_comp) and (alt < 20000.0)) + { + append(cloudBufferArray,c.to_buffer()); + cloudSceneryArray = delete_from_vector(cloudSceneryArray,i); + i = i -1; i_max = i_max - 1; n_max = n_max - 1; + continue; + } + + } + + +if (getprop(lw~"housekeeping-loop-flag") ==1) {settimer( func {housekeeping_loop(i)}, 0);} +} + + +############################### +# watchdog loop for debugging +############################### + + +var watchdog_loop = func { + +var tNode = props.globals.getNode(lw~"tiles", 1).getChildren("tile"); + +var i = 0; + +print("===================="); + +foreach(t; tNode) + { + var code = t.getNode("code").getValue(); + var index = t.getNode("tile-index").getValue(); + var flag = t.getNode("generated-flag").getValue(); + + print(i,": code: ", code, " unique id: ", index, " flag: ", flag); + + i = i + 1; + } + +print("===================="); + +settimer(watchdog_loop, 10.0); +} + + + ################### # global variables ################### @@ -652,9 +950,109 @@ var lon_to_m = 0.0; #local_weather.lon_to_m; var m_to_lon = 0.0; # local_weather.m_to_lon; var lw = "/local-weather/"; +var cloud_view_distance = 20000.0; var modelArrays = []; +var active_tile_list = []; + + +##################################################### +# hashes to manage clouds in scenery or in the buffer +##################################################### + +var cloudBufferArray = []; + +var cloudBuffer = { + new: func(lat, lon, alt, path, orientation, index) { + var c = { parents: [cloudBuffer] }; + c.lat = lat; + c.lon = lon; + c.alt = alt; + c.path = path; + c.orientation = orientation; + c.index = index; + return c; + }, + get_distance: func { + var pos = geo.aircraft_position(); + var cpos = geo.Coord.new(); + cpos.set_latlon(me.lat,me.lon,0.0); + return pos.distance_to(cpos); + }, + get_course: func { + var pos = geo.aircraft_position(); + var cpos = geo.Coord.new(); + cpos.set_latlon(me.lat,me.lon,0.0); + return pos.course_to(cpos); + }, + show: func { + print("lat: ",me.lat," lon: ",me.lon," alt: ",me.alt); + print("path: ",me.path); + + }, + 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 cloudSceneryArray = []; + +var cloudScenery = { + new: func(index, cloudNode, modelNode) { + var c = { parents: [cloudScenery] }; + c.index = index; + c.cloudNode = cloudNode; + c.modelNode = modelNode; + return c; + }, + removeNodes: func { + me.modelNode.remove(); + me.cloudNode.remove(); + }, + to_buffer: func { + var lat = me.cloudNode.getNode("position/latitude-deg").getValue(); + var lon = me.cloudNode.getNode("position/longitude-deg").getValue(); + var alt = me.cloudNode.getNode("position/altitude-ft").getValue(); + var path = me.modelNode.getNode("path").getValue(); + var orientation = me.cloudNode.getNode("orientation/true-heading-deg").getValue(); + var b = cloudBuffer.new(lat, lon, alt, path, orientation, me.index); + + if (getprop(lw~"config/dynamics-flag") == 1) + { + var timestamp = me.cloudNode.getNode("timestamp-sec").getValue(); + b.timestamp = timestamp; + } + + me.removeNodes(); + return b; + }, + get_distance: func { + var pos = geo.aircraft_position(); + var cpos = geo.Coord.new(); + var lat = me.cloudNode.getNode("position/latitude-deg").getValue(); + var lon = me.cloudNode.getNode("position/longitude-deg").getValue(); + cpos.set_latlon(lat,lon,0.0); + return pos.distance_to(cpos); + }, + get_course: func { + var pos = geo.aircraft_position(); + var cpos = geo.Coord.new(); + var lat = me.cloudNode.getNode("position/latitude-deg").getValue(); + var lon = me.cloudNode.getNode("position/longitude-deg").getValue(); + cpos.set_latlon(lat,lon,0.0); + return pos.course_to(cpos); + }, + get_altitude: func { + return me.cloudNode.getNode("position/altitude-ft").getValue(); + }, +}; ################### # helper functions @@ -675,3 +1073,15 @@ var get_lon = func (x,y,phi) { return (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon; } + + +var delete_from_vector = func(vec, index) { + +var n = index+1; + +var vec_end = subvec(vec, n); + +setsize(vec, n-1); +return vec~vec_end; + +} diff --git a/Nasal/weather_tiles.nas b/Nasal/weather_tiles.nas index 84a9a0ced..a7b7bc33d 100644 --- a/Nasal/weather_tiles.nas +++ b/Nasal/weather_tiles.nas @@ -21,8 +21,16 @@ var tile_start = func { +# set thread lock if (getprop(lw~"tmp/thread-flag") == 1){setprop(lw~"tmp/thread-status","computing");} +# set the tile code +var current_code = getprop(lw~"tiles/code"); +var dir_index = getprop(lw~"tiles/tmp/dir-index"); +props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("code").setValue(current_code); + + + # generate a handling array for models var array = []; @@ -39,14 +47,18 @@ setprop(lw~"clouds/placement-index",0); setsize(elat,0); setsize(elon,0); setsize(erad,0); var dir_index = getprop(lw~"tiles/tmp/dir-index"); -props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("code").setValue(current_code); +#props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("code").setValue(current_code); local_weather.assemble_effect_array(); print("Finished setting up tile type ",current_code, " in direction ",dir_index); -if (getprop(lw~"tmp/thread-flag") == 1){setprop(lw~"tmp/thread-status","placing");} -else {local_weather.assemble_effect_array();} # otherwise this is done at the end of the placement loop +if (getprop(lw~"tmp/thread-flag") == 1) + {setprop(lw~"tmp/thread-status","placing");} +else # without worker threads, tile generation is complete at this point + {props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("generated-flag").setValue(2);} + + } @@ -59,10 +71,10 @@ else {local_weather.assemble_effect_array();} # otherwise this is done at the en var set_4_8_stratus_tile = func { -tile_start(); - setprop(lw~"tiles/code","test"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -87,6 +99,8 @@ local_weather.set_weather_station(blat, blon, 20000.0, 14.0, 12.0, 29.78); create_4_8_cirrocumulus_bank(blat, blon, 6000.0, 0.0); +#create_detailed_stratocumulus_bank(blat, blon,5000.0+alt_offset,0.0); + tile_finished(); @@ -99,10 +113,10 @@ tile_finished(); var set_high_pressure_core_tile = func { -tile_start(); - setprop(lw~"tiles/code","high_pressure_core"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -148,10 +162,10 @@ tile_finished(); var set_high_pressure_tile = func { -tile_start(); - setprop(lw~"tiles/code","high_pressure"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -186,36 +200,32 @@ var alt = spread * 1000; var rn = rand(); -if (rn > 0.5) + +if (rn > 0.66) { # cloud scenario 1: possible Cirrus over Cumulus var strength = 0.2 + rand() * 0.4; local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0); - # if cumulus are weak, increase likelihood of Cirrus (the logic is the other way round - # but this makes no difference and is easier to implement) + # one or two Cirrus clouds - # try to place two spatially separated Cirrus clouds - - if (rand() > 2.0 * strength) - { - x = 2000.0 + rand() * 16000.0; - y = 2.0 * (rand()-0.5) * 18000; - alt = 25000.0 + rand() * 5000.0; - var path = local_weather.select_cloud_model("Cirrus", "small"); - compat_layer.create_cloud(path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset,alpha); - } - - if (rand() > 2.0 * strength) + x = 2000.0 + rand() * 16000.0; + y = 2.0 * (rand()-0.5) * 18000; + alt = 25000.0 + rand() * 5000.0; + var path = local_weather.select_cloud_model("Cirrus", "small"); + compat_layer.create_cloud(path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset,alpha); + + if (rand() > 0.5) { x = -2000.0 - rand() * 16000.0; y = 2.0 * (rand()-0.5) * 18000; alt = 25000.0 + rand() * 5000.0; var path = local_weather.select_cloud_model("Cirrus", "small"); compat_layer.create_cloud(path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset,alpha); - } + } + } -else if (rn > 0.0) +else if (rn > 0.33) { # cloud scenario 2: Cirrostratus over weak Cumulus @@ -225,6 +235,22 @@ else if (rn > 0.0) create_2_8_cirrostratus(blat, blon, alt+alt_offset+25000.0, alpha); } +else if (rn > 0.0) + { + # cloud scenario 3: Cirrocumulus sheet over Cumulus + + var strength = 0.2 + rand() * 0.2; + local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0); + + x = 2.0 * (rand()-0.5) * 5000; + y = 2.0 * (rand()-0.5) * 5000; + + + var path = local_weather.select_cloud_model("Cirrocumulus", "large"); + compat_layer.create_cloud(path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset +24000,alpha); + + } + tile_finished(); } @@ -236,10 +262,10 @@ tile_finished(); var set_high_pressure_border_tile = func { -tile_start(); - setprop(lw~"tiles/code","high_pressure_border"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -340,17 +366,23 @@ else if (rn > 0.166) } else if (rn > 0.0) { - # cloud scenario 6: Cirrocumulus + # cloud scenario 6: Cirrocumulus sheets var strength = 0.2 + rand() * 0.2; local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0); - x = 2.0 * (rand()-0.5) * 5000; - y = 2.0 * (rand()-0.5) * 5000; + for (var i = 0; i < 2; i = i + 1) + { + x = 2.0 * (rand()-0.5) * 10000; + y = -6000 + i * 12000 + 2.0 * (rand()-0.5) * 1000; - var path = local_weather.select_cloud_model("Cirrocumulus", "large"); - compat_layer.create_cloud(path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset +22000,alpha); + var beta = rand() * 90; + var alt_variation = rand() * 2000; + + var path = local_weather.select_cloud_model("Cirrocumulus", "large"); + compat_layer.create_cloud(path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset +20000+ alt_variation,alpha+ beta); + } } @@ -366,10 +398,10 @@ tile_finished(); var set_low_pressure_border_tile = func { -tile_start(); - setprop(lw~"tiles/code","low_pressure_border"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -408,7 +440,7 @@ var alt = spread * 1000.0; var rn = rand(); -if (rn > 0.75) +if (rn > 0.8) { # cloud scenario 1: a low 4/8 stratus patches, thin patches above @@ -416,7 +448,7 @@ if (rn > 0.75) create_4_8_stratus_patches(blat, blon, alt+alt_offset,alpha); create_4_8_tstratus_patches(blat, blon, alt+alt_offset+6000,alpha); } -else if (rn > 0.5) +else if (rn > 0.6) { # cloud scenario 2: a low 4/8 undulatus, thin patches above @@ -424,25 +456,33 @@ else if (rn > 0.5) create_4_8_sstratus_undulatus(blat, blon, alt+alt_offset,alpha); create_2_8_tstratus(blat, blon, alt+alt_offset+7000,alpha); } -else if (rn > 0.25) +else if (rn > 0.4) { # cloud scenario 3: low Stratocumulus - alt = alt + local_weather.cloud_vertical_size_map["Stratus"] * 0.5 * m_to_ft; - create_stratocumulus_bank(blat, blon, alt+alt_offset,alpha); - create_2_8_sstratus(blat, blon, alt+alt_offset+3000,alpha); + # alt = alt + local_weather.cloud_vertical_size_map["Cumulus"] * 0.5 * m_to_ft; + create_detailed_stratocumulus_bank(blat, blon, alt+alt_offset,alpha); + create_2_8_sstratus(blat, blon, alt+alt_offset+6000,alpha); create_2_8_tstratus(blat, blon, alt+alt_offset+9000,alpha); } -else if (rn > 0.0) +else if (rn > 0.2) { # cloud scenario 4: dense low Stratocumulus - alt = alt + local_weather.cloud_vertical_size_map["Stratus"] * 0.5 * m_to_ft; - create_stratocumulus_bank(blat, blon, alt+alt_offset,alpha); - create_stratocumulus_bank(blat, blon, alt+alt_offset,alpha); + # alt = alt + local_weather.cloud_vertical_size_map["Cumulus"] * 0.5 * m_to_ft; + create_detailed_stratocumulus_bank(blat, blon, alt+alt_offset,alpha); + create_detailed_stratocumulus_bank(blat, blon, alt+alt_offset,alpha); create_2_8_sstratus(blat, blon, alt+alt_offset+8000,alpha); } +else if (rn > 0.0) + { + # cloud scenario 5: Cirrocumulus over 4/8 Stratus + alt = alt + local_weather.cloud_vertical_size_map["Stratus"] * 0.5 * m_to_ft; + create_4_8_sstratus_patches(blat, blon, alt+alt_offset,alpha); + + create_4_8_cirrocumulus_bank(blat, blon, alt+alt_offset + 12000.0, alpha); + } tile_finished(); @@ -454,10 +494,10 @@ tile_finished(); var set_low_pressure_tile = func { -tile_start(); - setprop(lw~"tiles/code","low_pressure"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -565,10 +605,10 @@ tile_finished(); var set_low_pressure_core_tile = func { -tile_start(); - setprop(lw~"tiles/code","low_pressure_core"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -633,10 +673,10 @@ tile_finished(); var set_cold_sector_tile = func { -tile_start(); - setprop(lw~"tiles/code","cold_sector"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -668,14 +708,37 @@ var alt = spread * 1000.0; var rn = rand(); +#rn = 0.1; -if (rn > 0.0) +if (rn > 0.5) { # cloud scenario 1: strong Cumulus development var strength = 0.8 + rand() * 0.2; local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0); } +else if (rn > 0.0) + { + # cloud scenario 2: Cirrocumulus sheets over Cumulus + + var strength = 0.6 + rand() * 0.2; + local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0); + + for (var i = 0; i < 2; i = i + 1) + { + x = 2.0 * (rand()-0.5) * 10000; + y = -6000 + i * 12000 + 2.0 * (rand()-0.5) * 1000; + + var beta = rand() * 90; + var alt_variation = rand() * 2000; + + var path = local_weather.select_cloud_model("Cirrocumulus", "large"); + compat_layer.create_cloud(path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset +20000+ alt_variation,alpha+ beta); + } + + } + + tile_finished(); } @@ -687,10 +750,10 @@ tile_finished(); var set_warm_sector_tile = func { -tile_start(); - setprop(lw~"tiles/code","warm_sector"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -789,10 +852,10 @@ tile_finished(); var set_tropical_weather_tile = func { -tile_start(); - setprop(lw~"tiles/code","tropical_weather"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -907,10 +970,10 @@ tile_finished(); var set_coldfront_tile = func { -tile_start(); - setprop(lw~"tiles/code","coldfront"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -976,7 +1039,7 @@ y = 5000.0; var strength = 0.3; var n = int(4000 * strength) * 0.2; -local_weather.cumulus_exclusion_layer(blat+get_lat(x,y,phi), blon+ get_lon(x,y,phi), alt+alt_offset, n, 20000.0, 10000.0, alpha, 2.1,2.5 , size(elat), elat, elon, erad); +local_weather.cumulus_exclusion_layer(blat+get_lat(x,y,phi), blon+ get_lon(x,y,phi), alt+alt_offset, n, 20000.0, 10000.0, alpha, 1.5,2.5 , size(elat), elat, elon, erad); # then leading and traling Cumulus @@ -985,14 +1048,14 @@ y = 15500.0; strength = 1.0; n = int(4000 * strength) * 0.15; -local_weather.cumulus_exclusion_layer(blat+get_lat(x,y,phi), blon+ get_lon(x,y,phi), alt+alt_offset, n, 20000.0, 2000.0, alpha, 0.5,1.4 , size(elat), elat, elon, erad); +local_weather.cumulus_exclusion_layer(blat+get_lat(x,y,phi), blon+ get_lon(x,y,phi), alt+alt_offset, n, 20000.0, 2000.0, alpha, 0.5,1.8 , size(elat), elat, elon, erad); x = 0.0; y = -5500.0; strength = 1.0; n = int(4000 * strength) * 0.15; -local_weather.cumulus_exclusion_layer(blat+get_lat(x,y,phi), blon+ get_lon(x,y,phi), alt+alt_offset, n, 20000.0, 2000.0, alpha, 0.5,1.4 , size(elat), elat, elon, erad); +local_weather.cumulus_exclusion_layer(blat+get_lat(x,y,phi), blon+ get_lon(x,y,phi), alt+alt_offset, n, 20000.0, 2000.0, alpha, 0.5,1.8 , size(elat), elat, elon, erad); # finally some thin stratus underneath the Cumulus @@ -1028,10 +1091,10 @@ tile_finished(); var set_warmfront1_tile = func { -tile_start(); - setprop(lw~"tiles/code","warmfront1"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -1084,11 +1147,11 @@ var alt = spread * 1000.0; # high Cirrus leading -x = 2.0 * (rand()-0.5) * 3000; -y = 2.0 * (rand()-0.5) * 3000 - 12000.0; +x = 2.0 * (rand()-0.5) * 1000; +y = 2.0 * (rand()-0.5) * 1000 - 9000.0; -local_weather.create_streak("Cirrus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 25000.0+alt+alt_offset,1500.0,3,11000.0,0.0, 800.0, 1,8000.0,0.0,10000.0,alpha ,1.0); +local_weather.create_streak("Cirrus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 25000.0+alt+alt_offset,1500.0,3,11000.0,0.0, 3000.0, 2,11000.0,0.0,3000.0,alpha ,1.0); # followed by random patches of Cirrostratus @@ -1114,10 +1177,10 @@ tile_finished(); var set_warmfront2_tile = func { -tile_start(); - setprop(lw~"tiles/code","warmfront2"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -1223,10 +1286,10 @@ tile_finished(); var set_warmfront3_tile = func { -tile_start(); - setprop(lw~"tiles/code","warmfront3"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -1320,10 +1383,10 @@ tile_finished(); var set_warmfront4_tile = func { -tile_start(); - setprop(lw~"tiles/code","warmfront4"); +tile_start(); + var x = 0.0; var y = 0.0; var lat = 0.0; @@ -1405,242 +1468,11 @@ tile_finished(); } -#################################### -# Altocumulus sky -# cloud count 1400/tile -#################################### -var set_altocumulus_tile = func { -tile_start(); -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); - -# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure) - -local_weather.set_weather_station(blat, blon, 35000.0, 22.0, 14.0, 30.02); - -# then draw the Altocumulus streaks, dense at 15.000 ft, sparse at 17.000 ft - -local_weather.create_streak("Altocumulus",blat, blon, 15000.0+alt_offset,1500.0,40,1000.0,0.2,800.0, 40,1000.0,0.2,800.0,alpha ,1.0); -local_weather.create_streak("Altocumulus",blat, blon, 17000.0+alt_offset,1500.0,18,2000.0,0.35,800.0,18,2000.0,0.35,800.0,alpha,1.0); - - -tile_finished(); - -} - - - - -#################################### -# Overcast stratus sky -# cloud count 1000/tile -#################################### - -var set_overcast_stratus_tile = func { - -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); - -# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure) - -local_weather.set_weather_station(blat, blon, 10000.0, 14.0, 12.0, 29.78); - -# then draw the Stratus layers - -var size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus"]; -local_weather.create_streak("Stratus",blat, blon, 1500.0+alt_offset+size_offset,0.0,32,1250.0,0.2,600.0, 32,1250.0,0.2,600.0,0.0 ,1.0); - - -size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus_structured"]; -local_weather.create_streak("Stratus (structured)",blat, blon, 5000.0+alt_offset+size_offset,0.0,16,2500.0,0.3,1200.0,16,2500.0,0.3,1200.0,0.0,1.0); - - - -# reduce visibility even more below lowest layer -# and add a slight drizzle by a nested effect volume - -local_weather.create_effect_volume(3, blat, blon, 18000.0, 18000.0, 0.0, 0.0, 1800.0, 8000.0, -1, -1, -1, -1, 0); -local_weather.create_effect_volume(3, blat, blon, 14000.0, 14000.0, 0.0, 0.0, 1500.0, 6000.0, 0.1, -1, -1, -1,0 ); - - -tile_finished(); - -} - - -#################################### -# Incoming rainfront -# cloud count 1500/tile -#################################### - -var set_rainfront_tile = func { - -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); - -# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure) -local_weather.set_weather_station(blat, blon, 9000.0, 14.0, 12.0, 990 * hp_to_inhg); - -x = 15000.0; y = 20000.0; -local_weather.set_weather_station(blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 6000.0, 12.0, 10.0, 985 * hp_to_inhg); - -x = -15000.0; y = 20000.0; -local_weather.set_weather_station(blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 6000.0, 12.0, 10.0, 990 * hp_to_inhg); - -# draw two Stratus layers - -x = 0.0; y = -15000.0; -var size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus"]; -local_weather.create_streak("Stratus",blat, blon, 3000.0+alt_offset+size_offset,500.0,17,2500.0,0.2,1100.0, 6,2000.0,0.2,1100.0,alpha ,1.0); - - -x = 0.0; y = 0.0; -var size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus"]; -local_weather.create_streak("Stratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 2000.0+alt_offset+size_offset,300.0,40,1000.0,0.2,600.0, 10,1000.0,0.2,600.0,alpha ,1.0); - - -# and a Nimbus layer with precipitation - -x = 0.0; y = 15000.0; -var size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Nimbus"]; -local_weather.create_layer("Nimbus", blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 1000.0+alt_offset, 500.0, 22000.0, 13000.0, alpha, 1.0, 0.2, 1, 1.0); - -# set visibility and rain inside the precipitation area - -local_weather.create_effect_volume(2, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 19000.0, 10000.0, alpha, 0.0, 2000.0, 5000.0, 0.1, -1, -1, -1,0 ); -local_weather.create_effect_volume(2, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 16000.0, 7000.0, alpha, 0.0, 1500.0, 1500.0, 0.5, -1, -1, -1,0 ); - -# set visibility good above the clouds - -local_weather.create_effect_volume(3, blat, blon, 20000.0, 20000.0, 0.0, 2100.0, 85000.0, 18000.0, -1, -1, -1, -1,0 ); - -tile_finished(); - - - -} - - -#################################### -# Broken layers -# cloud count 550/tile -#################################### - -var set_broken_layers_tile = func { - -tile_start(); - - -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); - -# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure) -local_weather.set_weather_station(blat, blon, 20000.0, 14.0, 12.0, 1005 * hp_to_inhg); - -# set the broken stratus layers - -size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus_structured"]; - -local_weather.create_streak("Stratus (structured)",blat, blon, 4000.0+alt_offset+size_offset,1000.0,22,0.0,0.3,20000.0,22,0.0,0.3,20000.0,0.0,1.0); - - -local_weather.create_streak("Stratus (structured)",blat, blon, 6000.0+alt_offset+size_offset,1000.0,16,0.0,0.4,20000.0,16,0.0,0.4,20000.0,0.0,1.0); - - -local_weather.create_streak("Stratus (structured)",blat, blon, 7000.0+alt_offset+size_offset,1000.0,11,0.0,0.5,20000.0,11,0.0,0.5,20000.0,0.0,1.0); - - -tile_finished(); - - -} - - -#################################### -# Fair weather and Cumulus -# cloud count max. 800/tile -#################################### - -var set_fair_weather_tile = func { - -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); - -# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure) -local_weather.set_weather_station(blat, blon, 35000.0, 20.0, 16.0, 1018 * hp_to_inhg); - - -# add convective clouds - - -var strength = 1.0; -var n = int(4000 * strength); # calculate the number of placement tries from tile size 20x20km and strength -local_weather.create_cumosys(blat,blon, 3000.0 + alt_offset, n, 20000.0); - -tile_finished(); - - -} #################################### @@ -1649,6 +1481,8 @@ tile_finished(); var set_gliders_sky_tile = func { +setprop(lw~"tiles/code","glides_sky"); + tile_start(); var x = 0.0; @@ -1691,6 +1525,8 @@ tile_finished(); var set_blue_thermals_tile = func { +setprop(lw~"tiles/code","blue_thermals"); + tile_start(); var x = 0.0; @@ -1728,121 +1564,7 @@ tile_finished(); } -#################################### -# Summer rain -# cloud count max. 1200/tile -#################################### -var set_summer_rain_tile = func { - -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); - -# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure) -local_weather.set_weather_station(blat, blon, 25000.0, 25.0, 22.0, 1013 * hp_to_inhg); - -# then add some developing thunderstorms -local_weather.create_streak("Cumulonimbus (rain)",blat, blon, 3000.0+alt_offset,0.0,3,0.0,1.0,20000.0,3,0.0,1.0,20000.0,0.0,1.0); - - - -# add overdeveloped convective clouds - -var strength = 1.5; -var n = int(4000 * strength); # calculate the number of placement tries from tile size 20x20km and strength -local_weather.create_cumosys(blat,blon, 3000.0+alt_offset,n, 20000.0); - -tile_finished(); - - -} - - - -#################################### -# Cirrus sky -#################################### - -var set_cirrus_sky_tile = func { - -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); - -# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure) - -local_weather.set_weather_station(blat, blon, 20000.0, 20.0, 16.0, 29.80); - -# visibility is slightly worse north, pressure is lower, so set additional stations - -x = -10000.0; y = 18000.0; -lat = blat + (y * math.cos(phi) - x * math.sin(phi)) * m_to_lat; -lon = blon + (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon; -local_weather.set_weather_station(lat, lon, 18000.0, 20.0, 16.0, 29.75); - -x = 10000.0; y = 18000.0; -lat = blat + (y * math.cos(phi) - x * math.sin(phi)) * m_to_lat; -lon = blon + (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon; -local_weather.set_weather_station(lat, lon, 18000.0, 20.0, 16.0, 29.75); - -# now set up the clouds - -x = 0.0; y = 0.0; -compat_layer.create_cloud("Models/Weather/cirrus1.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 28000.0 + alt_offset,alpha); - -x = 7500.0; y = 1000.0; -compat_layer.create_cloud("Models/Weather/cirrus2.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 28500.0 + alt_offset,alpha); - -x = 16000.0; y = -1000.0; -compat_layer.create_cloud("Models/Weather/cirrus1.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 29500.0 + alt_offset,alpha); - -x = -16000.0; y = 2500.0; -compat_layer.create_cloud("Models/Weather/cirrus1.xml",blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 30000.0 + alt_offset,alpha); - -x = 7000.0; y = 8000.0; -compat_layer.create_cloud("Models/Weather/cirrocumulus1.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 22000.0 + alt_offset,alpha); - -x = -3000.0; y = 9000.0; -compat_layer.create_cloud("Models/Weather/cirrocumulus1.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 21000.0 + alt_offset,alpha); - -x = -1000.0; y = 14000.0; -compat_layer.create_cloud("Models/Weather/cirrocumulus2.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 20000.0 + alt_offset,alpha); - -# add moderately strong convective clouds - -var strength = 0.4; -var n = int(4000 * strength); # calculate the number of placement tries from tile size 20x20km and strength -local_weather.create_cumosys(blat,blon, 4000.0+alt_offset,n, 20000.0); - -tile_finished(); -} #################################### @@ -1851,6 +1573,9 @@ tile_finished(); var set_METAR_tile = func { + +setprop(lw~"tiles/code","METAR"); # to be replaced + tile_start(); var x = 0.0; @@ -1858,7 +1583,7 @@ var y = 0.0; var lat = 0.0; var lon = 0.0; -setprop(lw~"tiles/code","METAR"); # to be replaced + var alpha = getprop(lw~"METAR/wind-direction-deg"); var phi = alpha * math.pi/180.0; @@ -2163,10 +1888,10 @@ var create_4_8_cirrocumulus_bank = func (lat, lon, alt, alpha) { var phi = alpha * math.pi/180.0; var x = 2.0 * (rand()-0.5) * 5000; var y = 2.0 * (rand()-0.5) * 5000; -var tri = 1.4 + 0.6 *rand(); +var tri = 1.5 + 1.5 *rand(); var beta = (rand() -0.5) * 60.0; -local_weather.create_streak("Cirrocumulus (cloudlet)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,900.0,15,750.0,0.2,400.0,24,750.0,0.2,400.0,alpha+90.0+beta,tri); +local_weather.create_streak("Cirrocumulus (cloudlet)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,400.0,12,750.0,0.25,400.0,24,750.0,0.2,400.0,alpha+90.0+beta,tri); } @@ -2179,7 +1904,7 @@ var y = 2.0 * (rand()-0.5) * 5000; var tri = 1.4 + 0.6 *rand(); var beta = (rand() -0.5) * 60.0; -local_weather.create_streak("Cirrocumulus (cloudlet)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,900.0,25,300.0,0.0,900.0,15,1400.0,0.0,300.0,alpha+90.0+beta,tri); +local_weather.create_streak("Cirrocumulus (cloudlet)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,400.0,25,300.0,0.0,900.0,15,1400.0,0.0,300.0,alpha+90.0+beta,tri); } @@ -2293,6 +2018,28 @@ local_weather.create_streak("Cumulus",lat+get_lat(x,y-6000,phi), lon+get_lon(x,y } +var create_detailed_stratocumulus_bank = func (lat, lon, alt, alpha) { + +var phi = alpha * math.pi/180.0; +var x = 2.0 * (rand()-0.5) * 6000; +var y = 2.0 * (rand()-0.5) * 6000; +var tri = 1.5 + 1.5*rand(); +var beta = (rand() -0.5) * 60.0; + +var alt_offset = 0.5 * local_weather.cloud_vertical_size_map["Cumulus"] * ft_to_m; + +local_weather.create_streak("Congestus",lat+get_lat(x,y+7500,phi), lon+get_lon(x,y+7500,phi), alt + alt_offset,500.0,12,1000.0,0.1,400.0,15,1000.0,0.1,400.0,alpha+90.0+beta,tri); + +local_weather.create_streak("Congestus",lat+get_lat(x,y-7500,phi), lon+get_lon(x,y-7500,phi), alt + alt_offset,500.0,12,1000.0,0.1,400.0,15,1000.0,0.1,400.0,alpha+270.0+beta,tri); + + +local_weather.create_streak("Congestus bottom",lat+get_lat(x,y+5250,phi), lon+get_lon(x,y+5250,phi), alt,0.0,10,700.0,0.2,400.0,15,700.0,0.0,400.0,alpha+90.0+beta,tri); + +local_weather.create_streak("Congestus bottom",lat+get_lat(x,y-5250,phi), lon+get_lon(x,y-5250,phi), alt,0.0,10,700.0,0.2,400.0,15,700.0,0.0,400.0,alpha+270.0+beta,tri); + +} + + var create_cloud_bank = func (type, lat, lon, alt, x1, x2, height, n, alpha) { local_weather.create_streak(type,lat,lon, alt+ 0.5* height,height,n,0.0,0.0,x1,1,0.0,0.0,x2,alpha,1.0); diff --git a/Shaders/clouds-layered.vert b/Shaders/clouds-layered.vert index 8846f0ee5..bb78d1c0e 100644 --- a/Shaders/clouds-layered.vert +++ b/Shaders/clouds-layered.vert @@ -54,7 +54,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, 300.0, fogCoord), 1 - smoothstep(25000.0, 30000.0, fogCoord)); + gl_FrontColor.a = min(smoothstep(100.0, 300.0, fogCoord), 1 - smoothstep(40000.0, 45000.0, fogCoord)); gl_BackColor = gl_FrontColor; // Fog doesn't affect clouds as much as other objects. diff --git a/Shaders/clouds-thick.vert b/Shaders/clouds-thick.vert index 996208295..a25af4ec8 100644 --- a/Shaders/clouds-thick.vert +++ b/Shaders/clouds-thick.vert @@ -3,7 +3,7 @@ varying float fogFactor; -float shade = 0.6; +float shade = 0.4; float cloud_height = 1000.0; void main(void) @@ -39,6 +39,7 @@ void main(void) // 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); float fract = smoothstep(0.0, cloud_height, gl_Position.z + cloud_height); // Final position of the sprite @@ -53,7 +54,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(10.0, 100.0, fogCoord), 1 - smoothstep(15000.0, 20000.0, fogCoord)); + gl_FrontColor.a = min(smoothstep(10.0, 100.0, fogCoord), 1 - smoothstep(40000.0, 45000.0, fogCoord)); gl_BackColor = gl_FrontColor; // Fog doesn't affect clouds as much as other objects. diff --git a/Shaders/clouds-thin.vert b/Shaders/clouds-thin.vert index a1731e96c..d424c6e0f 100644 --- a/Shaders/clouds-thin.vert +++ b/Shaders/clouds-thin.vert @@ -63,7 +63,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(10.0, 100.0, fogCoord), 1 - smoothstep(15000.0, 20000.0, fogCoord)); + gl_FrontColor.a = min(smoothstep(10.0, 100.0, fogCoord), 1 - smoothstep(40000.0, 45000.0, fogCoord)); gl_BackColor = gl_FrontColor; // Fog doesn't affect clouds as much as other objects. diff --git a/Shaders/clouds-thinlayer.vert b/Shaders/clouds-thinlayer.vert index 4f500a151..bcc6e791e 100644 --- a/Shaders/clouds-thinlayer.vert +++ b/Shaders/clouds-thinlayer.vert @@ -54,7 +54,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, 300.0, fogCoord), 1 - smoothstep(25000.0, 30000.0, fogCoord)); + gl_FrontColor.a = min(smoothstep(100.0, 300.0, fogCoord), 1 - smoothstep(40000.0, 45000.0, fogCoord)); gl_BackColor = gl_FrontColor; // Fog doesn't affect clouds as much as other objects. diff --git a/gui/dialogs/local_weather.xml b/gui/dialogs/local_weather.xml index fefc07748..6561adba8 100644 --- a/gui/dialogs/local_weather.xml +++ b/gui/dialogs/local_weather.xml @@ -241,6 +241,7 @@ /local-weather/tmp/cloud-type Altocumulus Cirrus + Cirrocumulus (cloudlet) Cirrostratus Cumulus Cumulus (cloudlet) diff --git a/gui/dialogs/local_weather_config.xml b/gui/dialogs/local_weather_config.xml new file mode 100644 index 000000000..74eb35605 --- /dev/null +++ b/gui/dialogs/local_weather_config.xml @@ -0,0 +1,273 @@ + + + + + + + local_weather_config + 400 + 400 + false + + + + 5 + 370 + + + + + 60 + 340 + + + + + 10 + 320 + + + + + 60 + 320 + 100 + 20 + 25000.0 + 55000.0 + /local-weather/config/distance-to-load-tile-m + + dialog-apply + + + + + 170 + 320 + + + + + + 250 + 350 + 15 + 15 + + /local-weather/tmp/asymmetric-tile-loading-flag + + dialog-apply + + + + + 220 + 320 + + + + + 250 + 320 + 100 + 20 + 0.5 + 1.0 + /local-weather/config/asymmetric-reduction + + dialog-apply + + + + + 360 + 320 + + + + + 5 + 280 + + + + + + 60 + 260 + + + + + 10 + 240 + + + + + 60 + 240 + 100 + 20 + 15000.0 + 45000.0 + /local-weather/config/clouds-visible-range-m + + dialog-apply + + + + + 170 + 240 + + + + + + 250 + 270 + 15 + 15 + + /local-weather/config/asymmetric-buffering-flag + + dialog-apply + + + + + 220 + 240 + + + + + 250 + 240 + 100 + 20 + 0.2 + 1.0 + /local-weather/config/asymmetric-buffering-reduction + + dialog-apply + + + + + 360 + 240 + + + + + + 170 + 210 + + + + + 220 + 210 + + + + + 250 + 210 + 100 + 20 + 0.0 + 180.0 + /local-weather/config/asymmetric-buffering-angle-deg + + dialog-apply + + + + + 360 + 210 + + + + + + + + 5 + 160 + + + + + + 60 + 140 + + + + + 10 + 120 + + + + + 60 + 120 + 100 + 20 + 100 + 400 + /local-weather/config/clouds-in-dynamics-loop + + dialog-apply + + + + + 170 + 120 + + + + + + + + 10 + 10 + + + + + + + + + + + diff --git a/gui/menubar.xml.alt b/gui/menubar.xml.alt index cf844318d..043aa5332 100644 --- a/gui/menubar.xml.alt +++ b/gui/menubar.xml.alt @@ -333,6 +333,13 @@ local_weather_tiles + + + + dialog-show + local_weather_config + +