diff --git a/Effects/cloud-impostor.eff b/Effects/cloud-impostor.eff
new file mode 100644
index 000000000..13c675304
--- /dev/null
+++ b/Effects/cloud-impostor.eff
@@ -0,0 +1,168 @@
+
+
+ Effects/cloud-impostor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /sim/rendering/shaders/skydome
+ /sim/rendering/clouds3d-enable
+
+ 1.0
+
+
+
+
+
+
+ true
+
+ 0.5 0.5 0.5 1.0
+ 0.5 0.5 0.5 1.0
+ off
+
+
+ greater
+ 0.01
+
+ smooth
+
+ src-alpha
+ one-minus-src-alpha
+
+
+ false
+
+
+ 9
+ DepthSortedBin
+
+
+ 0
+
+
+
+
+
+
+
+
+ Shaders/cloud-impostor-lightfield.vert
+ Shaders/cloud-static-lightfield.frag
+
+
+ baseTexture
+ sampler-2d
+ 0
+
+
+ range
+ float
+
+
+
+ terminator
+ float
+
+
+
+ altitude
+ float
+
+
+
+ cloud_self_shading
+ float
+
+
+
+ moonlight
+ float
+
+
+
+ air_pollution
+ float
+
+
+ true
+
+
+
+
+
+ /sim/rendering/clouds3d-enable
+
+ 1.0
+
+
+
+
+
+
+ true
+
+ 0.5 0.5 0.5 1.0
+ 0.5 0.5 0.5 1.0
+ off
+
+
+ greater
+ 0.01
+
+ smooth
+
+ src-alpha
+ one-minus-src-alpha
+
+
+ false
+
+
+ 9
+ DepthSortedBin
+
+
+ 0
+
+
+
+
+
+
+
+
+ Shaders/cloud-static.vert
+ Shaders/cloud-static.frag
+
+
+ baseTexture
+ sampler-2d
+ 0
+
+
+ terminator
+ float
+
+
+
+ altitude
+ float
+
+
+ true
+
+
+
diff --git a/Nasal/local_weather/cloud_definitions.nas b/Nasal/local_weather/cloud_definitions.nas
index c204ccccf..4765380dd 100644
--- a/Nasal/local_weather/cloud_definitions.nas
+++ b/Nasal/local_weather/cloud_definitions.nas
@@ -536,6 +536,27 @@ else if (type=="Noctilucent") {
else if (rn > 0.25) {path = "Models/Weather/noctilucent9.xml";}
else {path = "Models/Weather/noctilucent10.xml";}
}
+else if (type=="Impostor sheet") {
+ if (subtype=="Nimbus") {
+ if (rn>0.0) {path = "Models/Weather/impostor_nimbus.xml";}
+ }
+ else if (subtype=="broken") {
+ if (rn>0.5) {path = "Models/Weather/impostor_broken1.xml";}
+ else if (rn>0.0) {path = "Models/Weather/impostor_broken2.xml";}
+ }
+ else if (subtype=="scattered") {
+ if (rn>0.6) {path = "Models/Weather/impostor_scattered1.xml";}
+ else if (rn>0.4) {path = "Models/Weather/impostor_scattered2.xml";}
+ else if (rn > 0.2) {path = "Models/Weather/impostor_few1.xml";}
+ else {path = "Models/Weather/impostor_few2.xml";}
+ }
+ else if (subtype=="few") {
+ if (rn>0.7) {path = "Models/Weather/impostor_few1.xml";}
+ else if (rn>0.4) {path = "Models/Weather/impostor_few2.xml";}
+ else if (rn>0.3) {path = "Models/Weather/impostor_scattered2.xml";}
+ else {path = "void";}
+ }
+ }
else if (type == "Cirrocumulus (cloudlet)") {
cloudAssembly = local_weather.cloud.new(type, subtype);
diff --git a/Nasal/local_weather/compat_layer.nas b/Nasal/local_weather/compat_layer.nas
index bee026932..655d0da6c 100644
--- a/Nasal/local_weather/compat_layer.nas
+++ b/Nasal/local_weather/compat_layer.nas
@@ -20,6 +20,7 @@
# 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
@@ -542,7 +543,36 @@ if (local_weather.dynamics_flag == 1)
}
+###########################################################
+# 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);
+
+
+}
###########################################################
diff --git a/Nasal/local_weather/local_weather.nas b/Nasal/local_weather/local_weather.nas
index 61a3a6e82..094639ab4 100644
--- a/Nasal/local_weather/local_weather.nas
+++ b/Nasal/local_weather/local_weather.nas
@@ -1687,6 +1687,9 @@ settimer ( func { setsize(weather_dynamics.cloudQuadtrees,0);},0.1); # to avoid
setsize(effectVolumeArray,0);
n_effectVolumeArray = 0;
+# remove any impostors
+
+weather_tile_management.remove_impostors();
# clear any wxradar echos
@@ -3676,6 +3679,12 @@ if (compat_layer.features.can_disable_environment ==1)
local_weather.setDefaultCloudsOff();
+# read max. visibility range and set far camera clipping
+
+max_vis_range = math.exp(getprop(lw~"config/aux-max-vis-range-m"));
+setprop(lw~"config/max-vis-range-m",max_vis_range);
+if (max_vis_range>120000.0){setprop("/sim/rendering/camera-group/zfar",max_vis_range);}
+
# now see if we need to presample the terrain
if ((presampling_flag == 1) and (getprop(lw~"tmp/presampling-status") == "idle"))
@@ -3972,6 +3981,8 @@ local_weather.init_sea_colors();
# start the mask loop
#local_weather.init_mask();
+# create impostors - this should only happen when sufficiently high in air
+weather_tile_management.create_impostors();
# weather_tile_management.watchdog_loop();
diff --git a/Nasal/local_weather/weather_tile_management.nas b/Nasal/local_weather/weather_tile_management.nas
index ab9162aa4..b4f3f3dde 100644
--- a/Nasal/local_weather/weather_tile_management.nas
+++ b/Nasal/local_weather/weather_tile_management.nas
@@ -14,6 +14,8 @@
# create_neighbours to initialize the 8 neighbours of the initial tile
# buffer_loop to manage the buffering of faraway clouds in an array
# housekeeping_loop to shift clouds from the scenery into the buffer
+# remove_impostors to delete a ring of impostors to mimick distant clouds
+# create_impostors to create a ring of impostors to mimick distant clouds
# watchdog loop (debug helping structure)
# calc_geo to get local Cartesian geometry for latitude conversion
# get_lat to get latitude from Cartesian coordinates
@@ -27,6 +29,7 @@
# cloud to provide the data hash for the new cloud rendering system
# cloudBuffer to store a cloud in a Nasal buffer, to provide methods to move it
# cloudScenery to store info for clouds in scenery, to provide methods to move and evolve them
+# cloudImpostor to provide the hash data for an impostor cloud sheet
###################################
@@ -876,6 +879,11 @@ if (system_rotation_angle > 0.0)
create_neighbour(lat, lon, 9, alpha);
rotate_tile_scheme(system_rotation_angle);
}
+
+# ready the system for creating impostors
+
+impostor_trigger = 1;
+
}
@@ -1190,6 +1198,57 @@ if (getprop(lw~"housekeeping-loop-flag") ==1) {settimer( func {housekeeping_loop
}
+###############################
+# impostor handline routines
+###############################
+
+var impostor_trigger = 0;
+
+var impostor_type_map = {"low_pressure_core" : "Nimbus", "low_pressure" : "broken", "low_pressure_border": "broken", "high_pressure_border" : "scattered", "high_pressure" : "few", "high_pressure_core": "few"};
+
+var remove_impostors = func {
+
+foreach (entry;cloudImpostorArray)
+ {
+ entry.removeNodes();
+ }
+setsize(cloudImpostorArray,0);
+}
+
+var create_impostors = func {
+
+var visibility = getprop("/environment/visibility-m");
+var cloud_range = getprop("/sim/rendering/clouds3d-vis-range");
+
+if ((visibility < 80000.0) or (cloud_range < 70000.0)) {return;}
+
+if (visibility < cloud_range) {var range = visibility;}
+else {var range = cloud_range;}
+
+var n = int ((range - 60000.0)/40000.0);
+
+var lat = getprop(lw~"tiles/tile[4]/latitude-deg");
+var lon = getprop(lw~"tiles/tile[4]/longitude-deg");
+var alpha = getprop(lw~"tiles/tile[4]/orientation-deg");
+var code = getprop(lw~"tiles/tile[4]/code");
+var index = getprop(lw~"tiles/tile[4]/tile-index");
+var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");
+var alt = weather_dynamics.tile_convective_altitude[index-1] + 1000.0 + alt_offset;
+
+if (contains(impostor_type_map,code)) {var type = impostor_type_map[code];}
+else {var type = "scattered";}
+
+if (local_weather.debug_output_flag == 1)
+ {
+ printf("Creating impostor ring...");
+ print(lat, " ", lon, " ", alt, " ", alpha, " ", type, " ", n);
+ }
+
+weather_tiles.create_impostor_ring(lat, lon, alt, alpha, type, n);
+}
+
+
+
###############################
# watchdog loop for debugging
###############################
@@ -1369,6 +1428,21 @@ var cloudBuffer = {
};
+var cloudImpostorArray = [];
+
+var cloudImpostor = {
+ new: func(modelNode) {
+ var c = { parents: [cloudImpostor] };
+ c.modelNode = modelNode;
+ return c;
+ },
+ removeNodes: func {
+ me.modelNode.remove();
+ },
+};
+
+
+
var cloudSceneryArray = [];
var n_cloudSceneryArray = 0;
diff --git a/Nasal/local_weather/weather_tiles.nas b/Nasal/local_weather/weather_tiles.nas
index 77576174d..49fabeca1 100644
--- a/Nasal/local_weather/weather_tiles.nas
+++ b/Nasal/local_weather/weather_tiles.nas
@@ -78,6 +78,15 @@ 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);}
+# generate impostor ring if applicable
+
+if (impostor_trigger == 1)
+ {
+ weather_tile_management.remove_impostors();
+ weather_tile_management.create_impostors();
+ impostor_trigger = 0;
+ }
+
}
@@ -4418,6 +4427,38 @@ for (var i=0; i<2; i=i+1)
}
+var create_impostor_ring = func (lat, lon, alt, alpha, type, n) {
+
+var path = local_weather.select_cloud_model("Impostor sheet", type);
+var phi = alpha * math.pi/180.0;
+
+var limit = 4 + 2 * n;
+
+for (var i = 0; i< limit+1; i=i+1)
+ {
+ for (var j = 0; j 6000.0)
+ {
+ x=x*1000.0; y=y*1000.0;
+ path = local_weather.select_cloud_model("Impostor sheet", type);
+ if (path != "void")
+ {
+ var rnd = rand();
+ if (rnd > 0.75) {alpha = alpha + 90.0;}
+ compat_layer.create_impostor(path, lat + get_lat(x,y,phi), lon+get_lon(x,y,phi), alt ,alpha);
+ }
+ }
+ }
+ }
+
+
+}
+
+
###################
# helper functions
###################
diff --git a/Shaders/cloud-impostor-lightfield.vert b/Shaders/cloud-impostor-lightfield.vert
new file mode 100644
index 000000000..0992cfd65
--- /dev/null
+++ b/Shaders/cloud-impostor-lightfield.vert
@@ -0,0 +1,146 @@
+// -*-C++-*-
+#version 120
+
+varying float fogFactor;
+varying vec3 hazeColor;
+
+uniform float terminator;
+uniform float altitude;
+uniform float cloud_self_shading;
+uniform float moonlight;
+uniform float air_pollution;
+uniform float range;
+
+const float shade = 1.0;
+const float cloud_height = 1000.0;
+const float EarthRadius = 5800000.0;
+
+// light_func is a generalized logistic function fit to the light intensity as a function
+// of scaled terminator position obtained from Flightgear core
+
+float light_func (in float x, in float a, in float b, in float c, in float d, in float e)
+{
+x = x-0.5;
+
+// use the asymptotics to shorten computations
+if (x > 30.0) {return e;}
+if (x < -15.0) {return 0.03;}
+
+
+return e / pow((1.0 + a * exp(-b * (x-c)) ),(1.0/d));
+}
+
+void main(void)
+{
+
+ vec3 shadedFogColor = vec3 (0.65, 0.67, 0.78);
+ vec3 moonLightColor = vec3 (0.095, 0.095, 0.15) * moonlight;
+
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+ //gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndexX, textureIndexY, 0.0, 0.0);
+ vec4 ep = gl_ModelViewMatrixInverse * vec4(0.0,0.0,0.0,1.0);
+ vec4 l = gl_ModelViewMatrixInverse * vec4(0.0,0.0,1.0,1.0);
+ vec3 u = normalize(ep.xyz - l.xyz);
+
+ gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
+ gl_Position.x = gl_Vertex.x;
+ gl_Position.y += gl_Vertex.y;
+ gl_Position.z += gl_Vertex.z;
+ gl_Position.xyz += gl_Color.xyz;
+
+
+ // Determine a lighting normal based on the vertex position from the
+ // center of the cloud, so that sprite on the opposite side of the cloud to the sun are darker.
+ float n = dot(normalize(-gl_LightSource[0].position.xyz),
+ normalize(mat3x3(gl_ModelViewMatrix) * (- gl_Position.xyz)));;
+
+ // Determine the position - used for fog and shading calculations
+ vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Position);
+ float fogCoord = abs(ecPosition.z);
+ float fract = smoothstep(0.0, cloud_height, gl_Position.z + cloud_height);
+
+ vec3 relVector = gl_Position.xyz - ep.xyz;
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Position;
+
+ // Light at the final position
+
+ // first obtain normal to sun position
+
+ vec3 lightFull = (gl_ModelViewMatrixInverse * gl_LightSource[0].position).xyz;
+ vec3 lightHorizon = normalize(vec3(lightFull.x,lightFull.y, 0.0));
+
+ // yprime is the distance of the vertex into sun direction, corrected for altitude
+ //float vertex_alt = max(altitude * 0.30480 + relVector.z,100.0);
+ float vertex_alt = altitude + relVector.z;
+ float yprime = -dot(relVector, lightHorizon);
+ float yprime_alt = yprime -sqrt(2.0 * EarthRadius * vertex_alt);
+
+ // compute the light at the position
+ vec4 light_diffuse;
+
+ float lightArg = (terminator-yprime_alt)/100000.0;
+
+ light_diffuse.b = light_func(lightArg -1.2 * air_pollution, 1.330e-05, 0.264, 2.227, 1.08e-05, 1.0);
+ light_diffuse.g = light_func(lightArg -0.6 * air_pollution, 3.931e-06, 0.264, 3.827, 7.93e-06, 1.0);
+ light_diffuse.r = light_func(lightArg, 8.305e-06, 0.161, 3.827, 3.04e-05, 1.0);
+ light_diffuse.a = 1.0;
+
+ //float light_intensity = light_func(lightArg, 8.305e-06, 0.161, 3.827, 3.04e-05, 1.0);
+ //vec4 light_diffuse = vec4 (0.57, 0.57, 0.9, 1.0);
+ //light_diffuse.rgb = light_intensity * light_diffuse.rgb;
+
+ // two times terminator width governs how quickly light fades into shadow
+ float terminator_width = 200000.0;
+ float earthShade = 0.9 * smoothstep(terminator_width+ terminator, -terminator_width + terminator, yprime_alt) + 0.1;
+
+ //float intensity = length(light_diffuse.rgb);
+ float intensity = (1.0 - (0.5 * (1.0 - earthShade))) * length(light_diffuse.rgb);
+ //light_diffuse.rgb = intensity * normalize(mix(light_diffuse.rgb, shadedFogColor, (1.0 - smoothstep(0.5,0.9, cloud_self_shading ))));
+ light_diffuse.rgb = intensity * normalize(mix(light_diffuse.rgb, shadedFogColor, (1.0 - smoothstep(0.5,0.9, cloud_self_shading ))));
+ if (earthShade < 0.6)
+ {
+ intensity = length(light_diffuse.rgb);
+ light_diffuse.rgb = intensity * normalize(mix(light_diffuse.rgb, shadedFogColor, 1.0 -smoothstep(0.1, 0.6,earthShade ) ));
+ }
+
+ // Determine the shading of the sprite based on its vertical position and position relative to the sun.
+ // n = min(smoothstep(-0.5, 0.0, n), fract);
+// Determine the shading based on a mixture from the backlight to the front
+ //vec4 backlight = light_diffuse * shade;
+
+ gl_FrontColor = light_diffuse;//mix(backlight, light_diffuse, n);
+ //gl_FrontColor += gl_FrontLightModelProduct.sceneColor;
+
+ // As we get within 100m of the sprite, it is faded out. Equally at large distances it also fades out.
+ gl_FrontColor.a = min(smoothstep(100.0, 250.0, fogCoord), 1.0 - smoothstep(0.9 * range, range, fogCoord));
+ // During the day, noctilucent clouds are invisible
+ //gl_FrontColor.a = gl_FrontColor.a * (1.0 - smoothstep(3.0,5.0,lightArg));
+
+
+ // Fog doesn't affect rain as much as other objects.
+ //fogFactor = exp( -gl_Fog.density * fogCoord * 0.4);
+ //fogFactor = clamp(fogFactor, 0.0, 1.0);
+
+float fadeScale = 0.05 + 0.2 * log(fogCoord/1000.0);
+ if (fadeScale < 0.05) fadeScale = 0.05;
+ fogFactor = exp( -gl_Fog.density * 0.5* fogCoord * fadeScale);
+
+ hazeColor = light_diffuse.rgb;
+ hazeColor.r = hazeColor.r * 0.83;
+ hazeColor.g = hazeColor.g * 0.9;
+
+ // in sunset or sunrise conditions, do extra shading of clouds
+
+
+
+
+
+
+
+ //hazeColor = hazeColor * earthShade;
+ //gl_FrontColor.rgb = gl_FrontColor.rgb * earthShade;
+ gl_FrontColor.rgb = gl_FrontColor.rgb + moonLightColor * (1.0 - smoothstep(0.4, 0.5, earthShade));
+ hazeColor.rgb = hazeColor.rgb + moonLightColor * (1.0 - smoothstep(0.4, 0.5, earthShade));
+ gl_BackColor = gl_FrontColor;
+
+}
diff --git a/gui/dialogs/rendering.xml b/gui/dialogs/rendering.xml
index 05d1f2617..3209a8dcf 100644
--- a/gui/dialogs/rendering.xml
+++ b/gui/dialogs/rendering.xml
@@ -616,7 +616,7 @@
cloud-vis-range
1000.0
- 45000.0
+ 150000.0
/sim/rendering/clouds3d-vis-range
dialog-apply