2023-04-21 00:15:42 +00:00
|
|
|
# Copyright (C) 2023 Fernando García Liñán
|
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
# Utilities for the HDR Pipeline
|
|
|
|
#
|
|
|
|
# Shaders usually need to be fed uniforms that are either too expensive to
|
|
|
|
# compute on the GPU or can be precomputed once per frame. In this file we
|
|
|
|
# transform values from already existing properties into data that can be used
|
|
|
|
# by the shaders directly.
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
2023-05-02 23:57:20 +00:00
|
|
|
################################################################################
|
|
|
|
# Atmosphere
|
|
|
|
################################################################################
|
2024-01-08 17:26:24 +00:00
|
|
|
|
|
|
|
setlistener("/environment/aerosols/type", func(p) {
|
|
|
|
var type = p.getValue();
|
|
|
|
if (type == 7) { # Rural
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-absorption-cross-section[0]", 5.0393e-23);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-absorption-cross-section[1]", 8.0765e-23);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-absorption-cross-section[2]", 1.3823e-22);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-absorption-cross-section[3]", 2.3383e-22);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-scattering-cross-section[0]", 2.6004e-22);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-scattering-cross-section[1]", 2.4844e-22);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-scattering-cross-section[2]", 2.8362e-22);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-scattering-cross-section[3]", 2.7494e-22);
|
2024-01-08 17:33:17 +00:00
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-base-density", 8.5440e15);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-relative-background-density", 2.3408e-13);
|
2024-01-08 17:26:24 +00:00
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-scale-height", 0.73);
|
|
|
|
} else { # Urban (default)
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-absorption-cross-section[0]", 2.8722e-24);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-absorption-cross-section[1]", 4.6168e-24);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-absorption-cross-section[2]", 7.9706e-24);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-absorption-cross-section[3]", 1.3578e-23);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-scattering-cross-section[0]", 1.5908e-22);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-scattering-cross-section[1]", 1.7711e-22);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-scattering-cross-section[2]", 2.0942e-22);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-scattering-cross-section[3]", 2.4033e-22);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-base-density", 1.3681e17);
|
2024-01-08 17:33:17 +00:00
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-relative-background-density", 1.4618e-14);
|
2024-01-08 17:26:24 +00:00
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-scale-height", 0.73);
|
|
|
|
}
|
|
|
|
}, 1, 0);
|
|
|
|
|
|
|
|
setlistener("/environment/aerosols/turbidity", func(p) {
|
|
|
|
setprop("/sim/rendering/hdr/atmos/aerosol-turbidity", p.getValue());
|
|
|
|
}, 1, 0);
|
|
|
|
|
|
|
|
setlistener("/environment/ground-albedo", func(p) {
|
|
|
|
var albedo = p.getValue();
|
|
|
|
# Use the same albedo value for all wavelengths
|
|
|
|
for (var i = 0; i < 4; i += 1) {
|
|
|
|
setprop("/sim/rendering/hdr/atmos/ground-albedo[" ~ i ~ "]", albedo);
|
|
|
|
}
|
|
|
|
}, 1, 0);
|
|
|
|
|
|
|
|
# Update fog density when the visibility changes
|
|
|
|
setlistener("/environment/visibility-m", func {
|
|
|
|
var max = 30000000.0;
|
|
|
|
var visibility = getprop("/environment/visibility-m");
|
|
|
|
var vis = math.min(visibility, max);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/fog-density", max*math.pow(0.99937, vis));
|
|
|
|
}, 1, 0);
|
|
|
|
|
|
|
|
setlistener("/environment/fog-height-falloff", func(p) {
|
|
|
|
setprop("/sim/rendering/hdr/atmos/fog-scale-height", p.getValue());
|
|
|
|
}, 1, 0);
|
|
|
|
|
|
|
|
setlistener("/environment/fog-height-offset", func(p) {
|
|
|
|
setprop("/sim/rendering/hdr/atmos/fog-height-offset", p.getValue());
|
|
|
|
}, 1, 0);
|
|
|
|
|
|
|
|
# Ozone concentration depends on the month of the year
|
|
|
|
setlistener("/sim/time/utc/month", func {
|
|
|
|
var ozone_mean_monthly_dobson = [
|
|
|
|
347.0, # January
|
|
|
|
370.0, # February
|
|
|
|
381.0, # March
|
|
|
|
384.0, # April
|
|
|
|
372.0, # May
|
|
|
|
352.0, # June
|
|
|
|
333.0, # July
|
|
|
|
317.0, # August
|
|
|
|
298.0, # September
|
|
|
|
285.0, # October
|
|
|
|
290.0, # November
|
|
|
|
315.0 # December
|
|
|
|
];
|
|
|
|
var month = getprop("/sim/time/utc/month") or 1;
|
|
|
|
var index = math.clamp(month - 1, 0, 11);
|
|
|
|
setprop("/sim/rendering/hdr/atmos/ozone-mean-dobson", ozone_mean_monthly_dobson[index]);
|
|
|
|
}, 1, 0);
|
2023-05-02 23:57:20 +00:00
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# Environment map
|
|
|
|
################################################################################
|
2023-10-05 03:56:51 +00:00
|
|
|
|
2023-05-04 03:53:49 +00:00
|
|
|
var envmap_frame_listener = nil;
|
2023-10-09 03:52:11 +00:00
|
|
|
var envmap_updating = false;
|
|
|
|
var envmap_current_face = 0;
|
|
|
|
var envmap_prefiltering_active = false;
|
|
|
|
var envmap_instant_update_active = false;
|
2023-10-05 03:56:51 +00:00
|
|
|
|
2023-10-09 03:52:11 +00:00
|
|
|
var envmap_reset_internal_variables = func {
|
|
|
|
if (envmap_frame_listener != nil) {
|
|
|
|
removelistener(envmap_frame_listener);
|
|
|
|
envmap_frame_listener = nil;
|
|
|
|
}
|
|
|
|
envmap_updating = false;
|
|
|
|
envmap_current_face = 0;
|
|
|
|
envmap_prefiltering_active = false;
|
|
|
|
envmap_instant_update_active = false;
|
|
|
|
}
|
2023-10-05 03:56:51 +00:00
|
|
|
|
|
|
|
var envmap_set_render_face = func(face, active) {
|
|
|
|
setprop("/sim/rendering/hdr/envmap/should-render-face-" ~ face, active);
|
|
|
|
}
|
|
|
|
|
|
|
|
var envmap_set_render_all_faces = func(active) {
|
|
|
|
for (var i = 0; i < 6; i += 1) {
|
|
|
|
envmap_set_render_face(i, active);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var envmap_set_prefilter = func(active) {
|
|
|
|
setprop("/sim/rendering/hdr/envmap/should-prefilter", active);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Update all the faces of the environment cubemap in a single frame
|
2023-10-09 03:52:11 +00:00
|
|
|
var envmap_update_instant_callback = func {
|
|
|
|
if (!envmap_instant_update_active) {
|
|
|
|
# This is the first time this callback has been called so enable
|
|
|
|
# rendering for all faces.
|
2023-10-05 03:56:51 +00:00
|
|
|
envmap_set_render_all_faces(true);
|
|
|
|
envmap_set_prefilter(true);
|
2023-10-09 03:52:11 +00:00
|
|
|
envmap_instant_update_active = true;
|
|
|
|
} else {
|
|
|
|
# This is the second time this callback has been called. Disable
|
|
|
|
# rendering for all faces.
|
|
|
|
envmap_set_render_all_faces(false);
|
|
|
|
envmap_set_prefilter(false);
|
|
|
|
envmap_reset_internal_variables();
|
2023-10-05 03:56:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Update a single cubemap face each frame. The prefiltering step is also done
|
|
|
|
# in a separate step, totalling 7 frames to update the entire environment map.
|
2023-10-09 03:52:11 +00:00
|
|
|
var envmap_update_progressive_callback = func {
|
|
|
|
if (envmap_prefiltering_active) {
|
2023-05-04 03:53:49 +00:00
|
|
|
# Last frame we activated the prefiltering, which is the last step.
|
|
|
|
# Now disable it and reset all variables for the next update cycle.
|
2023-10-05 03:56:51 +00:00
|
|
|
envmap_set_prefilter(false);
|
2023-10-09 03:52:11 +00:00
|
|
|
envmap_reset_internal_variables();
|
2023-05-04 03:53:49 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-10-09 03:52:11 +00:00
|
|
|
if (envmap_current_face < 6) {
|
2023-05-02 23:57:20 +00:00
|
|
|
# Render the current face
|
2023-10-09 03:52:11 +00:00
|
|
|
envmap_set_render_face(envmap_current_face, true);
|
2023-05-02 23:57:20 +00:00
|
|
|
}
|
2023-10-09 03:52:11 +00:00
|
|
|
if (envmap_current_face > 0) {
|
2023-05-02 23:57:20 +00:00
|
|
|
# Stop rendering the previous face
|
2023-10-09 03:52:11 +00:00
|
|
|
envmap_set_render_face(envmap_current_face - 1, false);
|
2023-05-02 23:57:20 +00:00
|
|
|
}
|
2023-10-09 03:52:11 +00:00
|
|
|
if (envmap_current_face < 6) {
|
2023-05-02 23:57:20 +00:00
|
|
|
# Go to next face and update it next frame
|
2023-10-09 03:52:11 +00:00
|
|
|
envmap_current_face += 1;
|
2023-05-02 23:57:20 +00:00
|
|
|
} else {
|
2023-05-04 03:53:49 +00:00
|
|
|
# We have finished updating all faces. Reset the face counter and
|
|
|
|
# prefilter the envmap.
|
2023-10-09 03:52:11 +00:00
|
|
|
envmap_current_face = 0;
|
|
|
|
envmap_prefiltering_active = true;
|
2023-10-05 03:56:51 +00:00
|
|
|
envmap_set_prefilter(true);
|
2023-05-02 23:57:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-05 03:56:51 +00:00
|
|
|
var update_envmap = func(instant = false) {
|
2023-10-09 03:52:11 +00:00
|
|
|
if (getprop("/sim/rendering/hdr/envmap/update-continuously"))
|
|
|
|
return;
|
2023-10-05 03:56:51 +00:00
|
|
|
|
2023-10-09 03:52:11 +00:00
|
|
|
if (envmap_updating) {
|
|
|
|
# We were already updating the envmap. If the requested update is
|
|
|
|
# instant, cancel the ongoing update and start over. If it is a
|
|
|
|
# progressive update, let the ongoing one finish and do nothing.
|
|
|
|
if (instant) {
|
|
|
|
envmap_set_render_all_faces(false);
|
|
|
|
envmap_set_prefilter(false);
|
|
|
|
envmap_reset_internal_variables();
|
|
|
|
envmap_updating = true;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
2023-10-05 03:56:51 +00:00
|
|
|
} else {
|
2023-10-09 03:52:11 +00:00
|
|
|
envmap_updating = true;
|
2023-05-02 23:57:20 +00:00
|
|
|
}
|
2023-10-05 03:56:51 +00:00
|
|
|
|
2023-10-09 03:52:11 +00:00
|
|
|
var callback = instant ? envmap_update_instant_callback
|
|
|
|
: envmap_update_progressive_callback;
|
2023-10-05 03:56:51 +00:00
|
|
|
# We use a listener to the frame signal because it is guaranteed to be
|
|
|
|
# fired at a defined moment, while a settimer() with interval 0 might
|
|
|
|
# not if subsystems are re-ordered.
|
|
|
|
envmap_frame_listener = setlistener("/sim/signals/frame", callback);
|
2023-05-02 23:57:20 +00:00
|
|
|
}
|
|
|
|
|
2023-10-09 03:52:11 +00:00
|
|
|
# Continuous update
|
|
|
|
setlistener("/sim/rendering/hdr/envmap/update-continuously", func(p) {
|
|
|
|
if (p.getValue()) {
|
|
|
|
envmap_set_render_all_faces(true);
|
|
|
|
envmap_set_prefilter(true);
|
|
|
|
envmap_reset_internal_variables();
|
|
|
|
} else {
|
|
|
|
envmap_set_render_all_faces(false);
|
|
|
|
envmap_set_prefilter(false);
|
|
|
|
}
|
|
|
|
}, 1, 0);
|
|
|
|
|
2023-05-04 03:53:49 +00:00
|
|
|
# Manual update
|
|
|
|
setlistener("/sim/rendering/hdr/envmap/force-update", func(p) {
|
|
|
|
if (p.getValue()) {
|
2023-10-05 03:56:51 +00:00
|
|
|
update_envmap(true);
|
2023-05-04 03:53:49 +00:00
|
|
|
p.setValue(false);
|
|
|
|
}
|
|
|
|
}, 0, 0);
|
|
|
|
|
|
|
|
# Automatically update the envmap every so often
|
|
|
|
var envmap_timer = maketimer(getprop("/sim/rendering/hdr/envmap/update-rate-s"),
|
2023-10-09 03:52:11 +00:00
|
|
|
func {
|
|
|
|
update_envmap(false);
|
|
|
|
});
|
2023-05-04 03:53:49 +00:00
|
|
|
envmap_timer.simulatedTime = true;
|
2023-05-02 23:57:20 +00:00
|
|
|
|
2023-05-04 03:53:49 +00:00
|
|
|
# Start updating when the FDM is initialized
|
2023-05-02 23:57:20 +00:00
|
|
|
setlistener("/sim/signals/fdm-initialized", func {
|
2023-10-09 03:52:11 +00:00
|
|
|
update_envmap(true);
|
2023-05-04 03:53:49 +00:00
|
|
|
envmap_timer.start();
|
2023-05-02 23:57:20 +00:00
|
|
|
# Do a single update after 5 seconds when most of the scenery is loaded
|
2023-10-09 03:52:11 +00:00
|
|
|
settimer(func {
|
|
|
|
update_envmap(true);
|
|
|
|
}, 5);
|
2023-05-02 23:57:20 +00:00
|
|
|
});
|
|
|
|
|
2023-10-05 03:56:51 +00:00
|
|
|
# If the update rate is modified at runtime, force an envmap update and restart
|
|
|
|
# the timer with the new period.
|
2023-05-04 03:53:49 +00:00
|
|
|
setlistener("/sim/rendering/hdr/envmap/update-rate-s", func(p) {
|
|
|
|
if (envmap_timer.isRunning) {
|
2023-10-09 03:52:11 +00:00
|
|
|
update_envmap(false);
|
2023-05-04 03:53:49 +00:00
|
|
|
envmap_timer.restart(p.getValue());
|
2023-05-02 23:57:20 +00:00
|
|
|
}
|
2023-05-04 03:53:49 +00:00
|
|
|
});
|
|
|
|
|
2023-10-09 03:52:11 +00:00
|
|
|
# Update the envmap at a much faster rate when time warp is active
|
|
|
|
var envmap_warp_listener = nil;
|
|
|
|
var prev_warp = getprop("/sim/time/warp");
|
2023-10-05 03:56:51 +00:00
|
|
|
|
2023-10-09 03:52:11 +00:00
|
|
|
var envmap_warp_listener_callback = func {
|
|
|
|
removelistener(envmap_warp_listener);
|
|
|
|
envmap_warp_timer.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
var envmap_warp_timer_callback = func {
|
|
|
|
# Check if the time warp has ended so we can stop updating the envmap so
|
|
|
|
# regularly.
|
|
|
|
var current_warp = getprop("/sim/time/warp");
|
|
|
|
if (current_warp == prev_warp) {
|
|
|
|
# Time warp has ended, so stop the timer and reattach the listener
|
|
|
|
envmap_warp_timer.stop();
|
|
|
|
envmap_warp_listener = setlistener("/sim/time/warp",
|
|
|
|
envmap_warp_listener_callback, 0, 0);
|
|
|
|
# Do a final update to make sure the envmap corresponds to the new
|
|
|
|
# time of day.
|
|
|
|
update_envmap(true);
|
2023-05-04 03:53:49 +00:00
|
|
|
} else {
|
2023-10-09 03:52:11 +00:00
|
|
|
# Time warp is still going so store the previous warp value and update
|
|
|
|
# the envmap.
|
|
|
|
prev_warp = current_warp;
|
|
|
|
update_envmap(true);
|
2023-05-04 03:53:49 +00:00
|
|
|
}
|
2023-10-09 03:52:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var envmap_warp_timer = maketimer(0.5, envmap_warp_timer_callback);
|
|
|
|
envmap_warp_timer.simulatedTime = true;
|
|
|
|
|
|
|
|
envmap_warp_listener = setlistener("/sim/time/warp",
|
|
|
|
envmap_warp_listener_callback, 0, 0);
|
2023-10-05 03:56:51 +00:00
|
|
|
|
|
|
|
# Update the envmap when the view changes
|
|
|
|
setlistener("/sim/current-view/view-number", func {
|
|
|
|
update_envmap(true);
|
|
|
|
}, 0, 0);
|