mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 06:19:19 +02:00
* refactor(sonar): use override instead of redundant virtual (cpp:S3471) * refactor(sonar): remove empty statements, one declaration per statement (cpp:S1116, cpp:S1659) * refactor(sonar): drop redundant lambda return types (cpp:S3574) * refactor(sonar): prefer using over typedef (cpp:S5416) * refactor(sonar): use =default for trivial special members (cpp:S3490) * refactor(sonar): use nullptr literal (cpp:S4962) * refactor(sonar): remove redundant access specifiers (cpp:S3539) * refactor(sonar): remove redundant casts (cpp:S1905) * refactor(sonar): use std::scoped_lock instead of std::lock_guard (cpp:S5997) * refactor(sonar): construct elements in place with emplace_back (cpp:S6003) * refactor(sonar): make narrowing conversions explicit (cpp:S5276) * refactor(sonar): mark unused parameters (cpp:S1172) Unused names are commented out; parameters used only on some platforms are marked [[maybe_unused]]. * refactor(sonar): declare non-mutating member functions const (cpp:S5817) Virtual functions are left untouched so overrides keep matching. * refactor(sonar): merge collapsible nested if statements (cpp:S1066) Comments that preceded the inner if are kept above the merged statement. * refactor(sonar): scope single-use variables in if init-statements (cpp:S6004) * fix(sonar): give polymorphic base classes virtual destructors (cpp:S1235) * refactor(sonar): use try_emplace for map insertion (cpp:S6030) * refactor(sonar): use auto where the type is spelled out on the right (cpp:S5827) * refactor(sonar): remove redundant parentheses (cpp:S1110) * refactor(sonar): document intentionally empty functions (cpp:S1186) * refactor(sonar): remove dead stores (cpp:S1854) Side effects (e.g. LISTLINE++) are kept. * refactor(sonar): remove unused variables, captures and private fields (cpp:S1481, cpp:S1068) * refactor(sonar): use structured bindings for pair access (cpp:S6005) * refactor(sonar): use range-based loops and ranges algorithms (cpp:S5566) * refactor(sonar): split chained assignments (cpp:S1121) * refactor(sonar): add default cases to switch statements (cpp:S3562) * refactor(sonar): use std::size for array lengths (cpp:S7127) * refactor(sonar): use std::ranges algorithms and std::erase(_if) (cpp:S6197) * refactor(sonar): access static members through the class (cpp:S2209) * refactor(sonar): add braces around nested if-else (cpp:S5261) * refactor(sonar): avoid side effects inside typeid operands (cpp:S5279) * refactor(sonar): drop top-level const from by-value return types (cpp:S5951) * refactor(sonar): declare variables separately from their type definitions (cpp:S3646) * refactor(sonar): declare loop counters in the for statement (cpp:S5955) * refactor(sonar): rely on class template argument deduction (cpp:S6012) * refactor(sonar): inherit base constructors instead of forwarding them (cpp:S5952) * refactor(sonar): rename locals that shadow outer declarations (cpp:S1117) Only local variables and definition parameters are renamed; shadowing struct fields and enumerators in headers are left for a separate change. * refactor(sonar): reduce enum qualification with using enum (cpp:S6177) Applied only where no unqualified name in the block could be captured by the introduced enumerators. * refactor(sonar): mark converting constructors explicit (cpp:S1709) sound_source and the gl::object conversion operators stay implicit; they are relied upon by copy-list-initialisation and GL calls. * refactor(sonar): initialise members in initialiser lists or declarations (cpp:S3230) * refactor(sonar): make read-only local references and pointers const (cpp:S5350) * refactor(sonar): take read-only pointer and reference parameters as const (cpp:S995) TTrain command handlers keep their signature, it is fixed by the handler table. * refactor(sonar): pass expensive-to-copy parameters by const reference (cpp:S1238) Signatures declared in files touched by PR 206 (Driver.h, EU07.cpp) and virtual overrides are left unchanged. * refactor(sonar): use transparent comparators for string-keyed maps (cpp:S6045) std::unordered_map instances and map types exposed in headers used by other modules are left as they are. * fix(sonar): do not use UI text as a printf format string (cpp:S5281) Text lines containing '%' would otherwise be interpreted as format specifiers by ImGui::TextColored. * refactor(sonar): use std::bind_front for callbacks (cpp:S5995) * refactor(sonar): drop const_cast on string literals passed to the Python C API (cpp:S859) * refactor(sonar): take read-only string parameters as std::string_view (cpp:S6009) The Windows messaging helpers keep std::string, they need c_str(). * refactor(sonar): make read-only global tables const (cpp:S5421) Mutable global state (flags, timers, singletons) is left as is. * refactor(sonar): replace single-case switch statements with if (cpp:S1301) * refactor(sonar): format UUIDs and log timestamps with std::format (cpp:S6494, cpp:S5945) * refactor(sonar): use std::array for local fixed-size arrays (cpp:S5945) Arrays that are class members or part of headers are left unchanged. * fix(sonar): test the bearing damage bit, not bit 0 (cpp:S872) 'DamageFlag & dtrain_bearing > 0' parsed as 'DamageFlag & (dtrain_bearing > 0)', i.e. DamageFlag & 1, so bearing friction reacted to the wrong damage flag. * refactor(sonar): assorted small fixes - empty() instead of size() comparisons (cpp:S1155) - member initialisers in declaration order (cpp:S3229) - no redundant c_str() when a std::string is accepted (cpp:S7121) - contains() on associative containers (cpp:S6171) - const reference instead of a copying range-for (cpp:S6032) - starts_with() for prefix checks (cpp:S6178) - no std::move on temporaries (cpp:S5274) - has_value() on std::optional (cpp:S7172) - bool literals for bool members (cpp:S5820) - inline only on the definition (cpp:S3548) - explicit base access specifier (cpp:S5965) - explicit element access instead of dereferencing an array (cpp:S7116) - rely on the C++20 rewritten operator!= (cpp:S6186) * refactor(sonar): more small fixes - std::format instead of iostream manipulators in to_string/to_hex_str (cpp:S6495); output verified identical, negative hex values keep the unsigned form - remove an empty compound statement (cpp:S108) - drop an always-false unsigned < 0 branch (cpp:S1768) - explicit lambda capture (cpp:S3608) - std::make_unique (cpp:S5950) * chore: strip stray carriage returns left in TTrain constructor comments * fix: keep quantize() in its own value type The S5276 change cast the result of the generic quantize<Type_>() to unsigned int, which truncated float/double callers (shadow angle slider, editor rotation snapping, AI speed control steps) and broke negative values. * fix(sonar): use integer loop counters in segment sampling (cpp:S2193) * refactor(sonar): clean up issues reported on PR 209 - remove locals left unused after the dead-store cleanup (cpp:S1481, cpp:S1854) - unnamed unused parameter (cpp:S1172) - single-use declarations moved into if init-statements (cpp:S6004) - auto for casted initialisers (cpp:S5827) - const references and pointers where nothing is modified (cpp:S5350) - rename structured bindings shadowed by inner declarations (cpp:S1117)
265 lines
10 KiB
C++
265 lines
10 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "simulation/simulationenvironment.h"
|
|
|
|
#include "simulation/simulationsounds.h"
|
|
#include "utilities/Globals.h"
|
|
#include "utilities/Timer.h"
|
|
|
|
namespace simulation {
|
|
|
|
world_environment Environment;
|
|
|
|
} // simulation
|
|
|
|
void
|
|
world_environment::on_daylight_change() {
|
|
|
|
if( Global.FakeLight ) {
|
|
// for fake daylight enter fixed hour
|
|
time( 10, 30, 0 );
|
|
}
|
|
else {
|
|
// local clock based calculation
|
|
time();
|
|
}
|
|
}
|
|
|
|
// calculates current season of the year based on set simulation date
|
|
void
|
|
world_environment::compute_season( int const Yearday ) {
|
|
|
|
using dayseasonpair = std::pair<int, std::string>;
|
|
|
|
std::vector<dayseasonpair> seasonsequence {
|
|
{ 65, "winter:" },
|
|
{ 158, "spring:" },
|
|
{ 252, "summer:" },
|
|
{ 341, "autumn:" },
|
|
{ 366, "winter:" } };
|
|
auto const lookup =
|
|
std::ranges::lower_bound(
|
|
seasonsequence,
|
|
std::clamp( Yearday, 1, seasonsequence.back().first ),
|
|
{}, &dayseasonpair::first );
|
|
|
|
Global.Season = lookup->second;
|
|
// season can affect the weather so if it changes, re-calculate weather as well
|
|
compute_weather();
|
|
}
|
|
|
|
// calculates current weather
|
|
void
|
|
world_environment::compute_weather() const {
|
|
|
|
Global.Weather = Global.Overcast <= 0.10 ? "clear:" :
|
|
Global.Overcast <= 0.50 ? "scattered:" :
|
|
Global.Overcast <= 0.90 ? "broken:" :
|
|
Global.Overcast <= 1.00 ? "overcast:" :
|
|
|
|
Global.AirTemperature > 1 ? "rain:" :
|
|
"snow:";
|
|
|
|
Global.fTurbidity = Global.Overcast <= 0.10 ? 3 :
|
|
Global.Overcast <= 0.20 ? 4 :
|
|
Global.Overcast <= 0.30 ? 5 :
|
|
Global.Overcast <= 0.40 ? 5 :
|
|
Global.Overcast <= 0.50 ? 5 :
|
|
Global.Overcast <= 0.60 ? 5 :
|
|
Global.Overcast <= 0.70 ? 6 :
|
|
Global.Overcast <= 0.80 ? 7 :
|
|
Global.Overcast <= 0.90 ? 8 :
|
|
9;
|
|
}
|
|
|
|
void
|
|
world_environment::init() {
|
|
|
|
m_sun.init();
|
|
m_moon.init();
|
|
m_stars.init();
|
|
m_clouds.Init();
|
|
m_precipitation.init();
|
|
{
|
|
auto const rainsoundoverride { simulation::Sound_overrides.find( "weather.rainsound:" ) };
|
|
m_rainsound.deserialize(
|
|
rainsoundoverride != simulation::Sound_overrides.end() ? rainsoundoverride->second : "rain-sound-loop",
|
|
sound_type::single );
|
|
}
|
|
m_wind = basic_wind{
|
|
static_cast<float>( Random( 0, 360 ) ),
|
|
static_cast<float>( Random( -5, 5 ) ),
|
|
static_cast<float>( Random( -2, 4 ) ),
|
|
static_cast<float>( Random( -1, 1 ) ),
|
|
static_cast<float>( Random( 5, 20 ) ),
|
|
{} };
|
|
}
|
|
|
|
void
|
|
world_environment::update() {
|
|
// move celestial bodies...
|
|
m_sun.update();
|
|
m_moon.update();
|
|
// ...determine source of key light and adjust global state accordingly...
|
|
// diffuse (sun) intensity goes down after twilight, and reaches minimum 18 degrees below horizon
|
|
float twilightfactor = std::clamp( -m_sun.getAngle(), 0.0f, 18.0f ) / 18.0f;
|
|
// NOTE: sun light receives extra padding to prevent moon from kicking in too soon
|
|
auto const sunlightlevel = m_sun.getIntensity() + 0.05f * ( 1.f - twilightfactor );
|
|
auto const moonlightlevel = m_moon.getIntensity() * 0.65f; // scaled down by arbitrary factor, it's pretty bright otherwise
|
|
|
|
// ...update skydome to match the current sun position as well...
|
|
// twilight factor can be reset later down, so we do it here while it's still reflecting state of the sun
|
|
// turbidity varies from 2-3 during the day based on overcast, 3-4 after sunset to deal with sunlight bleeding too much into the sky from below horizon
|
|
m_skydome.SetTurbidity(
|
|
2.25f
|
|
+ std::clamp( Global.Overcast, 0.f, 1.f )
|
|
+ std::lerp( 0.f, 1.f, std::clamp( twilightfactor * 1.5f, 0.f, 1.f ) ) );
|
|
m_skydome.SetOvercastFactor( Global.Overcast );
|
|
m_skydome.Update( m_sun.getDirection() );
|
|
|
|
float keylightintensity;
|
|
glm::vec3 keylightcolor;
|
|
Global.SunAngle = m_sun.getAngle();
|
|
if (moonlightlevel > sunlightlevel && Global.SunAngle < -20)
|
|
{
|
|
// rare situations when the moon is brighter than the sun, typically at night
|
|
Global.SunAngle = m_moon.getAngle();
|
|
Global.DayLight.position = m_moon.getDirection();
|
|
Global.DayLight.direction = -1.0f * m_moon.getDirection();
|
|
keylightintensity = moonlightlevel;
|
|
m_lightintensity = moonlightlevel;
|
|
// if the moon is up, it overrides the twilight
|
|
twilightfactor = 0.0f;
|
|
keylightcolor = glm::vec3( 1.0f, 0.9490196f, 0.7921569f );
|
|
}
|
|
else {
|
|
// regular situation with sun as the key light
|
|
Global.SunAngle = m_sun.getAngle();
|
|
Global.DayLight.position = m_sun.getDirection();
|
|
Global.DayLight.direction = -1.0f * m_sun.getDirection();
|
|
keylightintensity = sunlightlevel;
|
|
m_lightintensity = 1.0f;
|
|
// include 'golden hour' effect in twilight lighting
|
|
float const duskfactor = 1.25f - std::clamp( Global.SunAngle, 0.0f, 18.0f ) / 18.0f;
|
|
keylightcolor = glm::mix(
|
|
glm::vec3( 1.0f, 0.9490196f, 0.9058824f ), // 255, 242, 231 / 255
|
|
glm::vec3( 0.9215686f, 0.4705882f, 0.1411765f ), // 235, 120, 36 / 255
|
|
duskfactor );
|
|
}
|
|
// ...retrieve current sky colour and brightness...
|
|
auto const skydomecolour = m_skydome.GetAverageColor();
|
|
auto const skydomehsv = colors::RGBtoHSV( skydomecolour );
|
|
// sun strength is reduced by overcast level
|
|
keylightintensity *= 1.0f - std::min(1.f, Global.Overcast) * 0.65f;
|
|
|
|
// intensity combines intensity of the sun and the light reflected by the sky dome
|
|
// it'd be more technically correct to have just the intensity of the sun here,
|
|
// but whether it'd _look_ better is something to be tested
|
|
auto const intensity = std::min( 1.15f * ( 0.05f + keylightintensity + skydomehsv.z ), 1.25f );
|
|
// the impact of sun component is reduced proportionally to overcast level, as overcast increases role of ambient light
|
|
auto const diffuselevel = std::lerp( keylightintensity, intensity * ( 1.0f - twilightfactor ), 1.0f - std::min( 1.f, Global.Overcast ) * 0.75f );
|
|
// ...update light colours and intensity.
|
|
keylightcolor = keylightcolor * diffuselevel;
|
|
Global.DayLight.diffuse = glm::vec4( keylightcolor, Global.DayLight.diffuse.a );
|
|
Global.DayLight.specular = glm::vec4( keylightcolor * 0.85f, diffuselevel );
|
|
|
|
// tonal impact of skydome color is inversely proportional to how high the sun is above the horizon
|
|
// (this is pure conjecture, aimed more to 'look right' than be accurate)
|
|
float const ambienttone = std::clamp( 1.0f - Global.SunAngle / 90.0f, 0.0f, 1.0f );
|
|
float const ambientintensitynightfactor = 1.f - 0.75f * std::clamp( -m_sun.getAngle(), 0.0f, 18.0f ) / 18.0f;
|
|
Global.DayLight.ambient[ 0 ] = std::lerp( skydomehsv.z, skydomecolour.r, ambienttone ) * ambientintensitynightfactor;
|
|
Global.DayLight.ambient[ 1 ] = std::lerp( skydomehsv.z, skydomecolour.g, ambienttone ) * ambientintensitynightfactor;
|
|
Global.DayLight.ambient[ 2 ] = std::lerp( skydomehsv.z, skydomecolour.b, ambienttone ) * ambientintensitynightfactor;
|
|
|
|
Global.fLuminance = intensity;
|
|
|
|
// update the fog. setting it to match the average colour of the sky dome is cheap
|
|
// but quite effective way to make the distant items blend with background better
|
|
Global.FogColor = m_skydome.GetAverageHorizonColor() * keylightcolor *
|
|
std::clamp((float)Global.fLuminance, 0.f, 1.f);
|
|
|
|
|
|
// weather-related simulation factors
|
|
Global.FrictionWeatherFactor = Global.Weather == "rain:" ? 0.85f : Global.Weather == "snow:" ? 0.75f : 1.0f;
|
|
|
|
Global.Period = m_sun.getAngle() > -12.0f ? "day:" : "night:";
|
|
|
|
if( true == (FreeFlyModeFlag || Global.CabWindowOpen)
|
|
&& Global.Weather == "rain:" ) {
|
|
if( m_rainsound.is_combined() ) {
|
|
m_rainsound.pitch( static_cast<float>(Global.Overcast - 1.0) );
|
|
}
|
|
m_rainsound
|
|
.gain( m_rainsound.m_amplitudeoffset + m_rainsound.m_amplitudefactor * 1.f )
|
|
.play( sound_flags::exclusive | sound_flags::looping );
|
|
}
|
|
else {
|
|
m_rainsound.stop();
|
|
}
|
|
|
|
update_wind();
|
|
}
|
|
|
|
void
|
|
world_environment::update_precipitation() {
|
|
|
|
m_precipitation.update();
|
|
}
|
|
|
|
void
|
|
world_environment::update_moon() {
|
|
|
|
m_moon.update( true );
|
|
}
|
|
|
|
void
|
|
world_environment::update_wind() {
|
|
|
|
auto const timedelta{ static_cast<float>( Timer::GetDeltaTime() ) };
|
|
|
|
m_wind.change_time -= timedelta;
|
|
if( m_wind.change_time < 0 ) {
|
|
m_wind.change_time = static_cast<float>(Random( 5, 15 ));
|
|
m_wind.velocity_change = static_cast<float>(Random( -0.2, 0.2 ));
|
|
if( Random() < 0.05 ) {
|
|
// changes in wind direction should be less frequent than changes in wind speed
|
|
// TBD, TODO: configuration-driven direction change frequency
|
|
m_wind.azimuth_change = static_cast<float>(Random( -5, 5 ));
|
|
}
|
|
else {
|
|
// keep direction change periods short, to avoid too drastic changes in direction
|
|
m_wind.azimuth_change = 0.0;
|
|
}
|
|
}
|
|
// TBD, TODO: wind configuration
|
|
m_wind.azimuth = clamp_circular( m_wind.azimuth + m_wind.azimuth_change * timedelta );
|
|
// HACK: negative part of range allows for some quiet periods, without active wind
|
|
m_wind.velocity = std::clamp( m_wind.velocity + m_wind.velocity_change * timedelta, -2.f, 4.f );
|
|
// convert to force vector
|
|
auto const polarangle { glm::radians( 90.f ) }; // theta
|
|
auto const azimuthalangle{ glm::radians( m_wind.azimuth ) }; // phi
|
|
// convert spherical coordinates to opengl coordinates
|
|
m_wind.vector =
|
|
std::max( 0.f, m_wind.velocity )
|
|
* glm::vec3(
|
|
std::sin( polarangle ) * std::sin( azimuthalangle ) * -1,
|
|
std::cos( polarangle ),
|
|
std::sin( polarangle ) * std::cos( azimuthalangle ) );
|
|
}
|
|
|
|
void
|
|
world_environment::time( int const Hour, int const Minute, int const Second ) {
|
|
|
|
m_sun.setTime( Hour, Minute, Second );
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|