997 lines
27 KiB
Text
997 lines
27 KiB
Text
|
|
########################################################
|
|
# compatibility layer for local weather package
|
|
# Thorsten Renk, March 2011
|
|
########################################################
|
|
|
|
# function purpose
|
|
#
|
|
# setDefaultCloudsOff to remove the standard Flightgear 3d clouds
|
|
# setVisibility to set the visibility to a given value
|
|
# setLift to set lift to given value
|
|
# setRain to set rain to a given value
|
|
# setSnow to set snow to a given value
|
|
# setTurbulence to set turbulence to a given value
|
|
# setTemperature to set temperature to a given value
|
|
# setPressure to set pressure to a given value
|
|
# setDewpoint to set the dewpoint to a given value
|
|
# setLight to set light saturation to given value
|
|
# setWind to set wind
|
|
# setWindSmoothly to set the wind gradually across a second
|
|
# smooth_wind_loop (helper function for setWindSmoothly)
|
|
# create_cloud to place a single cloud into the scenery
|
|
# create_impostor to place an impostor sheet mimicking far clouds into the scene
|
|
# create_cloud_array to place clouds from storage arrays into the scenery
|
|
# get_elevation to get the terrain elevation at given coordinates
|
|
# get_elevation_vector to get terrain elevation at given coordinate vector
|
|
# set_wxradarecho_storm to provide info about a storm to the wxradar
|
|
|
|
|
|
# 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.
|
|
|
|
var weather_dynamics = nil;
|
|
var weather_tile_management = nil;
|
|
var compat_layer = nil;
|
|
var weather_tiles = nil;
|
|
|
|
|
|
_setlistener("/nasal/local_weather/loaded", func {
|
|
|
|
compat_layer = local_weather;
|
|
weather_dynamics = local_weather;
|
|
weather_tile_management = local_weather;
|
|
weather_tiles = local_weather;
|
|
|
|
|
|
var result = "yes";
|
|
|
|
if (1==0) # no compatibility tests for 2.4 binary, it has the required features
|
|
{
|
|
print("Compatibility layer: testing for hard coded support");
|
|
|
|
if (props.globals.getNode("/rendering/scene/saturation", 0) == nil)
|
|
{result = "no"; features.can_set_light = 0;}
|
|
else
|
|
{result = "yes"; features.can_set_light = 1;}
|
|
print("* can set light saturation: "~result);
|
|
|
|
|
|
if (props.globals.getNode("/rendering/scene/scattering", 0) == nil)
|
|
{result = "no"; features.can_set_scattering = 0;}
|
|
else
|
|
{result = "yes"; features.can_set_scattering = 1;}
|
|
print("* can set horizon scattering: "~result);
|
|
|
|
if (props.globals.getNode("/environment/terrain", 0) == nil)
|
|
{result = "no"; features.terrain_presampling = 0;}
|
|
else
|
|
{result = "yes"; features.terrain_presampling = 1;setprop("/environment/terrain/area[0]/enabled",1);}
|
|
print("* hard coded terrain presampling: "~result);
|
|
|
|
if ((props.globals.getNode("/environment/terrain/area[0]/enabled",1).getBoolValue() == 1) and (features.terrain_presampling ==1))
|
|
{result = "yes"; features.terrain_presampling_active = 1;}
|
|
else
|
|
{result = "no"; features.terrain_presampling_active = 0;}
|
|
print("* terrain presampling initialized: "~result);
|
|
|
|
|
|
if (props.globals.getNode("/environment/config/enabled", 0) == nil)
|
|
{result = "no"; features.can_disable_environment = 0;}
|
|
else
|
|
{result = "yes"; features.can_disable_environment = 1;}
|
|
print("* can disable global weather: "~result);
|
|
|
|
|
|
print("Compatibility layer: tests done.");
|
|
}
|
|
|
|
|
|
# features of a 2.4 binary
|
|
|
|
# switch terrainsampler to active, should be initialized
|
|
|
|
|
|
features.can_set_light = 1;
|
|
features.can_set_scattering = 1;
|
|
features.terrain_presampling = 1;
|
|
features.terrain_presampling_active = 1;
|
|
features.can_disable_environment = 1;
|
|
|
|
|
|
|
|
# features of a current GIT binary
|
|
|
|
features.fast_geodinfo = 1;
|
|
|
|
|
|
# do actual startup()
|
|
local_weather.updateMenu();
|
|
local_weather.startup();
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
var setDefaultCloudsOff = func {
|
|
|
|
var layers = props.globals.getNode("/environment/clouds").getChildren("layer");
|
|
|
|
foreach (var l; layers)
|
|
{
|
|
l.getNode("coverage-type").setValue(5);
|
|
}
|
|
|
|
|
|
|
|
# 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
|
|
setprop("/sim/rendering/clouds3d-wrap",0);
|
|
|
|
# rain altitude limit off, detailed precipitation control on
|
|
|
|
props.globals.getNode("/environment/params/use-external-precipitation-level").setBoolValue("true");
|
|
props.globals.getNode("/environment/precipitation-control/detailed-precipitation").setBoolValue("true");
|
|
|
|
|
|
# set skydome unloading off
|
|
|
|
setprop("/sim/rendering/minimum-sky-visibility", 0.0);
|
|
|
|
# just to be sure, set other parameters off
|
|
|
|
compat_layer.setRain(0.0);
|
|
compat_layer.setSnow(0.0);
|
|
compat_layer.setLight(1.0);
|
|
|
|
}
|
|
|
|
|
|
####################################
|
|
# set visibility to given value
|
|
####################################
|
|
|
|
var setVisibility = func (vis) {
|
|
|
|
setprop("/environment/visibility-m",vis);
|
|
|
|
}
|
|
|
|
|
|
var setVisibilitySmoothly = func (vis) {
|
|
|
|
|
|
visibility_target = vis;
|
|
visibility_current = getprop("/environment/visibility-m");
|
|
|
|
if (smooth_visibility_loop_flag == 0)
|
|
{
|
|
smooth_visibility_loop_flag = 1;
|
|
visibility_loop();
|
|
}
|
|
}
|
|
|
|
var visibility_loop = func {
|
|
|
|
if (local_weather.local_weather_running_flag == 0) {return;}
|
|
|
|
if (visibility_target == visibility_current)
|
|
{smooth_visibility_loop_flag = 0; return;}
|
|
|
|
if (visibility_target < visibility_current)
|
|
{
|
|
var vis_goal = visibility_target;
|
|
if (vis_goal < 0.97 * visibility_current) {vis_goal = 0.97 * visibility_current;}
|
|
}
|
|
else
|
|
{
|
|
var vis_goal = visibility_target;
|
|
if (vis_goal > 1.03 * visibility_current) {vis_goal = 1.03 * visibility_current;}
|
|
}
|
|
# print(vis_goal, " ",local_weather.interpolated_conditions.visibility_m );
|
|
if (local_weather.interpolated_conditions.visibility_m > vis_goal)
|
|
{setprop("/environment/visibility-m",vis_goal);}
|
|
visibility_current = vis_goal;
|
|
|
|
settimer( func {visibility_loop(); },0);
|
|
}
|
|
|
|
|
|
####################################
|
|
# set thermal lift to given value
|
|
####################################
|
|
|
|
var setLift = func (lift) {
|
|
|
|
setprop("/environment/local-weather-lift-fps",lift);
|
|
|
|
}
|
|
|
|
####################################
|
|
# set rain properties
|
|
####################################
|
|
|
|
var setRain = func (rain) {
|
|
|
|
setprop("/environment/rain-norm", rain);
|
|
}
|
|
|
|
|
|
var setRainDropletSize = func (size) {
|
|
|
|
setprop("/environment/precipitation-control/rain-droplet-size", size);
|
|
}
|
|
|
|
####################################
|
|
# set snow properties
|
|
####################################
|
|
|
|
var setSnow = func (snow) {
|
|
|
|
setprop("/environment/snow-norm", snow);
|
|
}
|
|
|
|
var setSnowFlakeSize = func (size) {
|
|
|
|
setprop("/environment/precipitation-control/snow-flake-size", size);
|
|
}
|
|
|
|
|
|
|
|
####################################
|
|
# set turbulence to given value
|
|
####################################
|
|
|
|
var setTurbulence = func (turbulence) {
|
|
|
|
var turbulence_scale = getprop("/local-weather/config/turbulence-scale");
|
|
|
|
setprop("/environment/turbulence/magnitude-norm",turbulence * turbulence_scale);
|
|
setprop("/environment/turbulence/rate-hz",3.0);
|
|
}
|
|
|
|
|
|
####################################
|
|
# set temperature to given value
|
|
####################################
|
|
|
|
var setTemperature = func (T) {
|
|
|
|
setprop("/environment/temperature-sea-level-degc",T);
|
|
}
|
|
|
|
####################################
|
|
# set pressure to given value
|
|
####################################
|
|
|
|
var setPressure = func (p) {
|
|
|
|
setprop("/environment/pressure-sea-level-inhg",p);
|
|
}
|
|
|
|
####################################
|
|
# set dewpoint to given value
|
|
####################################
|
|
|
|
var setDewpoint = func (D) {
|
|
|
|
setprop("/environment/dewpoint-sea-level-degc",D);
|
|
}
|
|
|
|
####################################
|
|
# set light saturation to given value
|
|
####################################
|
|
|
|
var setLight = func (s) {
|
|
|
|
setprop("/rendering/scene/saturation",s);
|
|
}
|
|
|
|
var setLightSmoothly = func (s) {
|
|
|
|
light_target = s;
|
|
light_current = getprop("/rendering/scene/saturation");
|
|
|
|
if (smooth_light_loop_flag == 0)
|
|
{
|
|
smooth_light_loop_flag = 1;
|
|
light_loop();
|
|
}
|
|
}
|
|
|
|
var light_loop = func {
|
|
|
|
if (local_weather.local_weather_running_flag == 0) {return;}
|
|
|
|
if (light_target == light_current)
|
|
{smooth_light_loop_flag = 0; return;}
|
|
|
|
if (light_target < light_current)
|
|
{
|
|
var light_goal = light_target;
|
|
if (light_goal < 0.97 * light_current) {light_goal = 0.97 * light_current;}
|
|
}
|
|
else
|
|
{
|
|
var light_goal = light_target;
|
|
if (light_goal > 1.03 * light_current) {light_goal = 1.03 * light_current;}
|
|
}
|
|
|
|
setprop("/rendering/scene/saturation",light_goal);
|
|
light_current = light_goal;
|
|
|
|
settimer( func {light_loop(); },0);
|
|
}
|
|
|
|
|
|
####################################
|
|
# set horizon scattering
|
|
####################################
|
|
|
|
var setScattering = func (s) {
|
|
|
|
setprop("/rendering/scene/scattering",s);
|
|
}
|
|
|
|
####################################
|
|
# set overcast haze
|
|
####################################
|
|
|
|
var setOvercast = func (o) {
|
|
|
|
setprop("/rendering/scene/overcast",o);
|
|
}
|
|
|
|
|
|
####################################
|
|
# set skydome scattering parameters
|
|
####################################
|
|
|
|
var setSkydomeShader = func (r, m, d) {
|
|
|
|
setprop("/sim/rendering/rayleigh", r);
|
|
setprop("/sim/rendering/mie", m);
|
|
setprop("/sim/rendering/dome-density",d);
|
|
}
|
|
|
|
###########################################################
|
|
# set wind to given direction and speed
|
|
###########################################################
|
|
|
|
|
|
var setWind = func (dir, speed) {
|
|
|
|
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
|
|
setprop("/environment/clouds/layer[0]/elevation-ft",0.0);
|
|
|
|
}
|
|
|
|
###########################################################
|
|
# set wind smoothly to given direction and speed
|
|
# interpolating across several frames
|
|
###########################################################
|
|
|
|
var smoothDirection = func (dir0, dir1, factor) {
|
|
|
|
var diff = ( math.mod( dir0 - dir1 + 180 + 360, 360 ) - 180 );
|
|
diff *= factor;
|
|
|
|
return math.mod( 360 + dir1 + ( diff / 2), 360);
|
|
}
|
|
|
|
|
|
var setWindSmoothly = func (dir, speed) {
|
|
|
|
var curDir = getprop("/environment/wind-from-heading-deg");
|
|
var curSpeed = getprop("/environment/wind-speed-kt");
|
|
|
|
dir = math.mod(dir, 360);
|
|
|
|
var newSpeed = (curSpeed * 9 + speed) / 10;
|
|
var newDir = smoothDirection(dir, curDir, 0.2);
|
|
|
|
setWind(newDir, newSpeed);
|
|
|
|
#setWind(dir, speed);
|
|
}
|
|
|
|
|
|
###########################################################
|
|
# place a single cloud
|
|
###########################################################
|
|
|
|
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 d_max = weather_tile_management.cloud_view_distance + 1000.0;
|
|
|
|
# noctilucent clouds should not be deleted with the tile, hence they're assigned to tile zero
|
|
if (find("noctilucent",path) != -1)
|
|
{tile_counter=0;}
|
|
|
|
# check if we deal with a convective cloud - no need to do this any more, convective clouds go via a different system
|
|
|
|
var convective_flag = 0;
|
|
|
|
#if (find("cumulus",path) != -1)
|
|
# {
|
|
# if ((find("alto",path) != -1) or (find("cirro", path) != -1) or (find("strato", path) != -1))
|
|
# {convective_flag = 0;}
|
|
# else if ((find("small",path) != -1) or (find("whisp",path) != -1))
|
|
# {convective_flag = 1;}
|
|
# else if (find("bottom",path) != -1)
|
|
# {convective_flag = 4;}
|
|
# else
|
|
# {convective_flag = 2;}
|
|
#
|
|
# }
|
|
#else if (find("congestus",path) != -1)
|
|
# {
|
|
# if (find("bottom",path) != -1)
|
|
# {convective_flag = 5;}
|
|
# else
|
|
# {convective_flag = 3;}
|
|
# }
|
|
|
|
#print("path: ", path, " flag: ", convective_flag);
|
|
|
|
# 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)
|
|
|
|
|
|
|
|
# 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 = buffered_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)
|
|
break;
|
|
var cl = c.getChild("cloud", i, 1);
|
|
n.getNode("placement-index").setValue(i);
|
|
|
|
var placement_index = i;
|
|
|
|
var model_number = n.getNode("model-placement-index").getValue();
|
|
var m = props.globals.getNode("models", 1);
|
|
for (var i = model_number; 1; i += 1)
|
|
if (m.getChild("model", i, 0) == nil)
|
|
break;
|
|
var model = m.getChild("model", i, 1);
|
|
n.getNode("model-placement-index").setValue(i);
|
|
|
|
|
|
|
|
var latN = cl.getNode("position/latitude-deg", 1); latN.setValue(lat);
|
|
var lonN = cl.getNode("position/longitude-deg", 1); lonN.setValue(long);
|
|
var altN = cl.getNode("position/altitude-ft", 1); altN.setValue(alt);
|
|
var hdgN = cl.getNode("orientation/true-heading-deg", 1); hdgN.setValue(heading);
|
|
|
|
cl.getNode("tile-index",1).setValue(tile_counter);
|
|
|
|
model.getNode("path", 1).setValue(path);
|
|
model.getNode("latitude-deg", 1).setValue(lat);
|
|
model.getNode("longitude-deg", 1).setValue(long);
|
|
model.getNode("elevation-ft", 1).setValue(alt);
|
|
model.getNode("heading-deg", 1).setValue(local_weather.wind.cloudlayer[0]+180.0);
|
|
model.getNode("tile-index",1).setValue(tile_counter);
|
|
model.getNode("speed-kt",1).setValue(local_weather.wind.cloudlayer[1]);
|
|
model.getNode("load", 1).remove();
|
|
|
|
|
|
#model.getNode("latitude-deg-prop", 1).setValue(latN.getPath());
|
|
#model.getNode("longitude-deg-prop", 1).setValue(lonN.getPath());
|
|
#model.getNode("elevation-ft-prop", 1).setValue(altN.getPath());
|
|
#model.getNode("heading-deg-prop", 1).setValue(hdgN.getPath());
|
|
|
|
# sort the cloud into the cloud hash array
|
|
|
|
if (buffer_flag == 1)
|
|
{
|
|
var cs = weather_tile_management.cloudScenery.new(tile_counter, convective_flag, cl, model);
|
|
append(weather_tile_management.cloudSceneryArray,cs);
|
|
}
|
|
|
|
# if weather dynamics is on, also create a timestamp property and sort the cloud hash into quadtree
|
|
|
|
if (local_weather.dynamics_flag == 1)
|
|
{
|
|
cs.timestamp = weather_dynamics.time_lw;
|
|
cs.write_index = placement_index;
|
|
|
|
|
|
if (getprop(lw~"tmp/buffer-status") == "placing")
|
|
{
|
|
var blat = buffered_tile_latitude;
|
|
var blon = buffered_tile_longitude;
|
|
var alpha = buffered_tile_alpha;
|
|
}
|
|
else
|
|
{
|
|
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.cloudQuadtrees[tile_counter-1], cs);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
###########################################################
|
|
# place an impostor sheet
|
|
###########################################################
|
|
|
|
var create_impostor = func(path, lat, long, alt, heading) {
|
|
|
|
var n = props.globals.getNode("local-weather/clouds", 1);
|
|
var model_number = n.getNode("model-placement-index").getValue();
|
|
var m = props.globals.getNode("models", 1);
|
|
for (var i = model_number; 1; i += 1)
|
|
if (m.getChild("model", i, 0) == nil)
|
|
break;
|
|
var model = m.getChild("model", i, 1);
|
|
n.getNode("model-placement-index").setValue(i);
|
|
|
|
|
|
model.getNode("path", 1).setValue(path);
|
|
model.getNode("latitude-deg", 1).setValue(lat);
|
|
model.getNode("longitude-deg", 1).setValue(long);
|
|
model.getNode("elevation-ft", 1).setValue(alt);
|
|
model.getNode("heading-deg", 1).setValue(local_weather.wind.cloudlayer[0]+180.0);
|
|
model.getNode("speed-kt",1).setValue(local_weather.wind.cloudlayer[1]);
|
|
model.getNode("load", 1).remove();
|
|
|
|
|
|
var imp = weather_tile_management.cloudImpostor.new(model);
|
|
append(weather_tile_management.cloudImpostorArray,imp);
|
|
|
|
|
|
}
|
|
|
|
|
|
###########################################################
|
|
# place a model
|
|
###########################################################
|
|
|
|
var place_model = func(path, lat, lon, alt, heading, pitch, yaw) {
|
|
|
|
|
|
|
|
var m = props.globals.getNode("models", 1);
|
|
for (var i = 0; 1; i += 1)
|
|
if (m.getChild("model", i, 0) == nil)
|
|
break;
|
|
var model = m.getChild("model", i, 1);
|
|
|
|
|
|
model.getNode("path", 1).setValue(path);
|
|
model.getNode("latitude-deg", 1).setValue(lat);
|
|
model.getNode("longitude-deg", 1).setValue(lon);
|
|
model.getNode("elevation-ft", 1).setValue(alt);
|
|
model.getNode("heading-deg", 1).setValue(heading);
|
|
model.getNode("pitch-deg", 1).setValue(pitch);
|
|
model.getNode("roll-deg", 1).setValue(yaw);
|
|
model.getNode("load", 1).remove();
|
|
|
|
|
|
}
|
|
|
|
###########################################################
|
|
# place a model with control properties
|
|
###########################################################
|
|
|
|
var place_model_controlled = func(string, path, lat, lon, alt, heading, pitch, roll) {
|
|
|
|
|
|
|
|
var m = props.globals.getNode("models", 1);
|
|
for (var i = 0; 1; i += 1)
|
|
if (m.getChild("model", i, 0) == nil)
|
|
break;
|
|
var model = m.getChild("model", i, 1);
|
|
|
|
|
|
setprop("/local-weather/"~string~"/latitude-deg", lat);
|
|
setprop("/local-weather/"~string~"/longitude-deg", lon);
|
|
setprop("/local-weather/"~string~"/elevation-ft", alt);
|
|
setprop("/local-weather/"~string~"/heading-deg", heading);
|
|
setprop("/local-weather/"~string~"/pitch-deg", pitch);
|
|
setprop("/local-weather/"~string~"/roll-deg", roll);
|
|
|
|
|
|
|
|
var cmodel = props.globals.getNode("/local-weather/"~string, 1);
|
|
var latN = cmodel.getNode("latitude-deg",1);
|
|
var lonN = cmodel.getNode("longitude-deg",1);
|
|
var altN = cmodel.getNode("elevation-ft",1);
|
|
var headN = cmodel.getNode("heading-deg",1);
|
|
var pitchN = cmodel.getNode("pitch-deg",1);
|
|
var rollN = cmodel.getNode("roll-deg",1);
|
|
|
|
|
|
|
|
model.getNode("path", 1).setValue(path);
|
|
model.getNode("latitude-deg-prop", 1).setValue(latN.getPath());
|
|
model.getNode("longitude-deg-prop", 1).setValue(lonN.getPath());
|
|
model.getNode("elevation-ft-prop", 1).setValue(altN.getPath());
|
|
model.getNode("heading-deg-prop", 1).setValue(headN.getPath());
|
|
model.getNode("pitch-deg-prop", 1).setValue(pitchN.getPath());
|
|
model.getNode("roll-deg-prop", 1).setValue(rollN.getPath());
|
|
model.getNode("tile-index",1).setValue(0);
|
|
model.getNode("load", 1).remove();
|
|
|
|
|
|
#return model;
|
|
}
|
|
|
|
|
|
|
|
|
|
###########################################################
|
|
# place a single cloud using hard-coded system
|
|
###########################################################
|
|
|
|
var create_cloud_new = func(c) {
|
|
|
|
|
|
|
|
var tile_counter = getprop(lw~"tiles/tile-counter");
|
|
cloud_index = cloud_index + 1;
|
|
|
|
c.index = tile_counter;
|
|
c.cloud_index = cloud_index;
|
|
|
|
# light must be such that the top of a cloud cannot be darker than the bottom
|
|
|
|
if (c.bottom_shade > c.top_shade) {c.bottom_shade = c.top_shade;}
|
|
c.middle_shade = c.top_shade;
|
|
|
|
# write the actual cloud into the scenery
|
|
|
|
|
|
var p = props.Node.new({ "layer" : 0,
|
|
"index": cloud_index,
|
|
"lat-deg": c.lat,
|
|
"lon-deg": c.lon,
|
|
"min-sprite-width-m": c.min_width,
|
|
"max-sprite-width-m": c.max_width,
|
|
"min-sprite-height-m": c.min_height,
|
|
"max-sprite-height-m": c.max_height,
|
|
"num-sprites": c.n_sprites,
|
|
"min-bottom-lighting-factor": c.bottom_shade,
|
|
"min-middle-lighting-factor": c.middle_shade,
|
|
"min-top-lighting-factor": c.top_shade,
|
|
"alpha-factor": c.alpha_factor,
|
|
"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,
|
|
"min-cloud-width-m": c.min_cloud_width,
|
|
"max-cloud-width-m": c.min_cloud_width,
|
|
"min-cloud-height-m": c.min_cloud_height + c.min_cloud_height * 0.2 * local_weather.height_bias,
|
|
"max-cloud-height-m": c.min_cloud_height + c.min_cloud_height * 0.2 * local_weather.height_bias,
|
|
"z-scale": c.z_scale,
|
|
"height-map-texture": 0,
|
|
"alt-ft" : c.alt });
|
|
fgcommand("add-cloud", p);
|
|
|
|
#print("alt: ", c.alt);
|
|
|
|
# add other management properties to the hash if dynamics is on
|
|
|
|
if (local_weather.dynamics_flag == 1)
|
|
{
|
|
c.timestamp = weather_dynamics.time_lw;
|
|
}
|
|
|
|
|
|
# add cloud to array
|
|
|
|
append(weather_tile_management.cloudArray,c);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
###########################################################
|
|
# place a cloud layer from arrays, split across frames
|
|
###########################################################
|
|
|
|
var create_cloud_array = func (i, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation) {
|
|
|
|
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("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();
|
|
|
|
setprop(lw~"tiles/tile["~dir_index~"]/generated-flag",2);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
var k_max = 30;
|
|
var s = size(clouds_path);
|
|
|
|
if (s < k_max) {k_max = s;}
|
|
|
|
for (var k = 0; k < k_max; k = k+1)
|
|
{
|
|
if (getprop(lw~"config/dynamics-flag") ==1)
|
|
{
|
|
cloud_mean_altitude = local_weather.clouds_mean_alt[s-k-1];
|
|
cloud_flt = local_weather.clouds_flt[s-k-1];
|
|
cloud_evolution_timestamp = local_weather.clouds_evolution_timestamp[s-k-1];
|
|
}
|
|
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]);
|
|
#create_cloud_new(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]);
|
|
}
|
|
|
|
setsize(clouds_path,s-k_max);
|
|
setsize(clouds_lat,s-k_max);
|
|
setsize(clouds_lon,s-k_max);
|
|
setsize(clouds_alt,s-k_max);
|
|
setsize(clouds_orientation,s-k_max);
|
|
|
|
if (getprop(lw~"config/dynamics-flag") ==1)
|
|
{
|
|
setsize(local_weather.clouds_mean_alt,s-k_max);
|
|
setsize(local_weather.clouds_flt,s-k_max);
|
|
setsize(local_weather.clouds_evolution_timestamp,s-k_max);
|
|
}
|
|
|
|
settimer( func {create_cloud_array(i - k, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation ) }, 0 );
|
|
};
|
|
|
|
|
|
var create_new_cloud_array = func (i, cloudArray)
|
|
{
|
|
|
|
|
|
|
|
|
|
if ((i < 0) or (i==0))
|
|
{
|
|
if (local_weather.debug_output_flag == 1)
|
|
{print("Processing add-cloud calls finished!"); }
|
|
return;
|
|
}
|
|
|
|
|
|
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 );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
###########################################################
|
|
# get terrain elevation
|
|
###########################################################
|
|
|
|
var get_elevation = func (lat, lon) {
|
|
|
|
var info = geodinfo(lat, lon);
|
|
if (info != nil) {var elevation = info[0] * local_weather.m_to_ft;}
|
|
else {var elevation = -1.0; }
|
|
|
|
|
|
return elevation;
|
|
}
|
|
|
|
###########################################################
|
|
# get terrain elevation vector
|
|
###########################################################
|
|
|
|
var get_elevation_array = func (lat, lon) {
|
|
|
|
var elevation = [];
|
|
var n = size(lat);
|
|
|
|
|
|
for(var i = 0; i < n; i=i+1)
|
|
{
|
|
append(elevation, get_elevation(lat[i], lon[i]));
|
|
}
|
|
|
|
|
|
return elevation;
|
|
}
|
|
|
|
###########################################################
|
|
# set the wxradar echo of a storm
|
|
###########################################################
|
|
|
|
var set_wxradarecho_storm = func (lat, lon, base, top, radius, ref, turb, type) {
|
|
|
|
# look for the next free index in the wxradar property tree entries
|
|
|
|
var n = props.globals.getNode("/instrumentation/wxradar", 1);
|
|
for (var i = 0; 1; i += 1)
|
|
if (n.getChild("storm", i, 0) == nil)
|
|
break;
|
|
var s = n.getChild("storm", i, 1);
|
|
|
|
|
|
s.getNode("latitude-deg",1).setValue(lat);
|
|
s.getNode("longitude-deg",1).setValue(lon);
|
|
s.getNode("heading-deg",1).setValue(0.0);
|
|
s.getNode("base-altitude-ft",1).setValue(base);
|
|
s.getNode("top-altitude-ft",1).setValue(top);
|
|
s.getNode("radius-nm",1).setValue(radius * m_to_nm);
|
|
s.getNode("reflectivity-norm",1).setValue(ref);
|
|
s.getNode("turbulence-norm",1).setValue(turb);
|
|
s.getNode("type",1).setValue(type);
|
|
s.getNode("show",1).setValue(1);
|
|
}
|
|
|
|
###########################################################
|
|
# remove unused echos
|
|
###########################################################
|
|
|
|
var remove_wxradar_echos = func {
|
|
|
|
var distance_to_remove = 70000.0;
|
|
|
|
var storms = props.globals.getNode("/instrumentation/wxradar", 1).getChildren("storm");
|
|
|
|
var pos = geo.aircraft_position();
|
|
|
|
foreach (s; storms)
|
|
{
|
|
var d_sq = local_weather.calc_d_sq(pos.lat(), pos.lon(), s.getNode("latitude-deg").getValue(), s.getNode("longitude-deg").getValue());
|
|
if (d_sq > distance_to_remove * distance_to_remove)
|
|
{
|
|
s.remove();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
############################################################
|
|
# global variables
|
|
############################################################
|
|
|
|
# conversions
|
|
|
|
var nm_to_m = 1852.00;
|
|
var m_to_nm = 1.0/nm_to_m;
|
|
|
|
# some common abbreviations
|
|
|
|
var lw = "/local-weather/";
|
|
var ec = "/environment/config/";
|
|
|
|
# storage arrays for model vector
|
|
|
|
var mvec = [];
|
|
var msize = 0;
|
|
|
|
# loop flags and variables
|
|
|
|
var smooth_visibility_loop_flag = 0;
|
|
|
|
var visibility_target = 0.0;
|
|
var visibility_current = 0.0;
|
|
|
|
var smooth_light_loop_flag = 0;
|
|
|
|
var light_target = 0.0;
|
|
var light_current = 0.0;
|
|
|
|
# available hard-coded support
|
|
|
|
var features = {};
|
|
|
|
# globals to transmit info if clouds are written from buffer
|
|
|
|
var buffered_tile_latitude = 0.0;
|
|
var buffered_tile_longitude = 0.0;
|
|
var buffered_tile_alpha = 0.0;
|
|
var buffered_tile_index = 0;
|
|
|
|
# globals to handle additional info for Cumulus cloud dynamics
|
|
|
|
var cloud_mean_altitude = 0.0;
|
|
var cloud_flt = 0.0;
|
|
var cloud_evolution_timestamp = 0.0;
|
|
|
|
# globals to handle new cloud indexing
|
|
|
|
var cloud_index = 0;
|