16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-18 00:49:19 +02:00

Merge branch 'master' into faster-parser

This commit is contained in:
Christopher Kwiatkowski
2026-05-04 11:39:19 +02:00
committed by GitHub
19 changed files with 10742 additions and 9166 deletions

View File

@@ -1755,6 +1755,8 @@ class TMoverParameters
bool HasCamshaft{false};
int DynamicBrakeType = 0; /*patrz dbrake_**/
int DynamicBrakeAmpmeters = 2; /*liczba amperomierzy przy hamowaniu ED*/
bool SplitEDPneumaticBrake = false; /*czy hamulec ED i pneumatyczny pomocniczy są rozdzielone na osobne nastawniki*/
int DynamicBrakeCtrlPosNo = 10; /*ilosc pozycji nastawnika hamulca ED (DBPN, domyslnie 10)*/
double DynamicBrakeRes = 5.8; /*rezystancja oporników przy hamowaniu ED*/
double DynamicBrakeRes1 = 5.8; /*rezystancja oporników przy hamowaniu ED - 1 tryb*/
double DynamicBrakeRes2 = 5.8; /*rezystancja oporników przy hamowaniu ED - 2 tryb*/
@@ -2031,6 +2033,7 @@ class TMoverParameters
int ManualBrakePos = 0; /*nastawa hamulca recznego*/
double LocalBrakePosA = 0.0; /*nastawa hamulca pomocniczego*/
double LocalBrakePosAEIM = 0.0; /*pozycja hamulca pomocniczego ep dla asynchronicznych ezt*/
double DynamicBrakeCtrlPos = 0.0; /*nastawa nastawnika hamulca elektrodynamicznego (0..1) - aktywne tylko gdy SplitEDPneumaticBrake*/
bool UniversalBrakeButtonActive[3] = {false, false, false}; /* brake button pressed */
/*
int BrakeStatus = b_off; //0 - odham, 1 - ham., 2 - uszk., 4 - odluzniacz, 8 - antyposlizg, 16 - uzyte EP, 32 - pozycja R, 64 - powrot z R
@@ -2381,6 +2384,12 @@ class TMoverParameters
/*! ABu 010205: - skrajne polozenia ham. pomocniczego*/
bool IncManualBrakeLevel(int CtrlSpeed);
bool DecManualBrakeLevel(int CtrlSpeed);
/*! oddzielny nastawnik hamulca elektrodynamicznego (gdy SplitEDPneumaticBrake)*/
bool IncDynamicBrakeLevel(float const CtrlSpeed);
bool DecDynamicBrakeLevel(float const CtrlSpeed);
bool DynamicBrakeLevelSet(double Position);
double DynamicBrakeRatio(void) const;
bool DynamicBrakeAvailable(void) const; /*czy ED dostepny w aktualnym oknie predkosci Vh0..Vh1*/
bool DynamicBrakeSwitch(bool Switch);
bool RadiostopSwitch(bool Switch);
bool AlarmChainSwitch(bool const State);

View File

@@ -3883,6 +3883,79 @@ bool TMoverParameters::DecLocalBrakeLevel(float const CtrlSpeed)
return DBL;
}
// *************************************************************************************************
// zwiększenie nastawy nastawnika hamulca elektrodynamicznego (DynamicBrakeCtrl)
// uzywane gdy SplitEDPneumaticBrake = true
// *************************************************************************************************
bool TMoverParameters::IncDynamicBrakeLevel(float const CtrlSpeed)
{
if (false == SplitEDPneumaticBrake)
return false;
if (DynamicBrakeCtrlPosNo < 1)
return false;
if (DynamicBrakeCtrlPos < 1.0)
{
DynamicBrakeCtrlPos = std::min(1.0, DynamicBrakeCtrlPos + (double)CtrlSpeed / (double)DynamicBrakeCtrlPosNo);
return true;
}
return false;
}
// *************************************************************************************************
// zmniejszenie nastawy nastawnika hamulca elektrodynamicznego (DynamicBrakeCtrl)
// *************************************************************************************************
bool TMoverParameters::DecDynamicBrakeLevel(float const CtrlSpeed)
{
if (false == SplitEDPneumaticBrake)
return false;
if (DynamicBrakeCtrlPosNo < 1)
return false;
if (DynamicBrakeCtrlPos > 0.0)
{
DynamicBrakeCtrlPos = std::max(0.0, DynamicBrakeCtrlPos - (double)CtrlSpeed / (double)DynamicBrakeCtrlPosNo);
return true;
}
return false;
}
// *************************************************************************************************
// bezposrednie ustawienie nastawnika hamulca elektrodynamicznego (DynamicBrakeCtrl)
// *************************************************************************************************
bool TMoverParameters::DynamicBrakeLevelSet(double Position)
{
if (false == SplitEDPneumaticBrake)
return false;
DynamicBrakeCtrlPos = clamp(Position, 0.0, 1.0);
return true;
}
// *************************************************************************************************
// odczyt nastawy nastawnika hamulca elektrodynamicznego (DynamicBrakeCtrl)
// zwraca 0 jezeli SplitEDPneumaticBrake nie jest aktywne (dla zachowania starej logiki)
// *************************************************************************************************
double TMoverParameters::DynamicBrakeRatio(void) const
{
if (false == SplitEDPneumaticBrake)
return 0.0;
return clamp(DynamicBrakeCtrlPos, 0.0, 1.0);
}
// *************************************************************************************************
// czy hamulec ED jest aktualnie dostepny w oknie predkosci Vh0..Vh1
// *************************************************************************************************
bool TMoverParameters::DynamicBrakeAvailable(void) const
{
double const vh0{eimc[eimc_p_Vh0]};
double const vh1{eimc[eimc_p_Vh1]};
if (vh1 <= 0.001)
{
// brak zdefiniowanej strefy - ED dziala zawsze
return true;
}
// strefa wylaczenia jest definiowana przez Vh0 (minimum); powyzej Vh0 ED dziala
return (Vel >= vh0);
}
// *************************************************************************************************
// Q: 20160711
// zwiększenie nastawy hamulca ręcznego
@@ -4439,7 +4512,13 @@ void TMoverParameters::UpdatePipePressure(double dt)
dpLocalValve = LocHandle->GetPF(std::max(lbpa, LocalBrakePosAEIM), Hamulec->GetBCP(), ScndPipePress, dt, 0);
}
else
dpLocalValve = LocHandle->GetPF(LocalBrakePosAEIM, Hamulec->GetBCP(), ScndPipePress, dt, 0);
{
// in EIM stock the pneumatic local brake is normally driven only by the MED algorithm
// (LocalBrakePosAEIM). When SplitEDPneumaticBrake is active the dedicated LocalBrake
// lever should apply pneumatic pressure on the locomotive directly, bypassing MED.
double lbpa = SplitEDPneumaticBrake ? LocalBrakePosA : 0.0;
dpLocalValve = LocHandle->GetPF(std::max(lbpa, LocalBrakePosAEIM), Hamulec->GetBCP(), ScndPipePress, dt, 0);
}
LockPipe = PipePress < (LockPipe ? LockPipeOff : LockPipeOn);
bool lock_new = (LockPipe && !UnlockPipe && (BrakeCtrlPosR > HandleUnlock)) || ((EmergencyCutsOffHandle) && (EmergencyValveFlow > 0)); // new simple codition based on .fiz
@@ -6233,14 +6312,19 @@ double TMoverParameters::TractionForce(double dt)
}
double edBCP = Hamulec->GetEDBCP();
auto const localbrakeactive{(CabOccupied != 0) && (LocHandle->GetCP() > 0.25)};
// gdy SplitEDPneumaticBrake jest aktywne, hamulec pomocniczy (LocalBrake) NIE wymusza
// zalaczenia hamulca elektrodynamicznego - ED jest sterowany wlasnym nastawnikiem
auto const dynbrakectrlactive{SplitEDPneumaticBrake && (DynamicBrakeRatio() > 0.01) && DynamicBrakeAvailable()};
auto const localbrakeforED{SplitEDPneumaticBrake ? false : localbrakeactive};
if ((false == Doors.instances[side::left].is_closed) || (false == Doors.instances[side::right].is_closed) ||
(Doors.permit_needed && (Doors.instances[side::left].open_permit || Doors.instances[side::right].open_permit)))
{
DynamicBrakeFlag = true;
}
else if (((edBCP < 0.25) && (false == localbrakeactive) && (AnPos < 0.01)) || ((edBCP < 0.25) && (ShuntModeAllow) && (LocalBrakePosA < 0.01)))
else if (((edBCP < 0.25) && (false == localbrakeforED) && (false == dynbrakectrlactive) && (AnPos < 0.01)) ||
((edBCP < 0.25) && (ShuntModeAllow) && (false == dynbrakectrlactive) && (LocalBrakePosA < 0.01)))
DynamicBrakeFlag = false;
else if ((((BrakePress > 0.25) && (edBCP > 0.25) || localbrakeactive)) || (AnPos > 0.02))
else if ((((BrakePress > 0.25) && (edBCP > 0.25) || localbrakeforED)) || (AnPos > 0.02) || dynbrakectrlactive)
DynamicBrakeFlag = true;
edBCP = Hamulec->GetEDBCP() * eimc[eimc_p_abed]; // stala napedu
if ((DynamicBrakeFlag))
@@ -6267,8 +6351,21 @@ double TMoverParameters::TractionForce(double dt)
}
else
{
PosRatio = -std::max(std::min(edBCP * 1.0 / MaxBrakePress[0], 1.0), AnPos) * std::max(0.0, std::min(1.0, (Vel - eimc[eimc_p_Vh0]) / (eimc[eimc_p_Vh1] - eimc[eimc_p_Vh0])));
eimv[eimv_Fzad] = -std::min(1.0, std::max(LocalBrakeRatio(), edBCP / MaxBrakePress[0]));
// strefa wylaczenia ED (Vh0..Vh1) - mnoznik 0..1 zaleznie od predkosci
double const vhRamp = std::max(0.0, std::min(1.0, (Vel - eimc[eimc_p_Vh0]) / (eimc[eimc_p_Vh1] - eimc[eimc_p_Vh0])));
if (SplitEDPneumaticBrake)
{
// w trybie split LocalBrake nie zaleca ED; uzywany jest DynamicBrakeCtrl
// oraz zwykle zapotrzebowanie z hamulca zespolonego (edBCP)
double const edLeverDemand = DynamicBrakeRatio();
PosRatio = -std::max(std::max(edLeverDemand, std::min(edBCP / MaxBrakePress[0], 1.0)), AnPos) * vhRamp;
eimv[eimv_Fzad] = -std::min(1.0, std::max(edLeverDemand, edBCP / MaxBrakePress[0]));
}
else
{
PosRatio = -std::max(std::min(edBCP * 1.0 / MaxBrakePress[0], 1.0), AnPos) * vhRamp;
eimv[eimv_Fzad] = -std::min(1.0, std::max(LocalBrakeRatio(), edBCP / MaxBrakePress[0]));
}
}
tmp = 5;
}
@@ -7231,10 +7328,26 @@ void TMoverParameters::CheckEIMIC(double dt)
{
double offset = EIMCtrlAdditionalZeros ? 1.0 : 0.0;
double multiplier = (EIMCtrlEmergency ? 1.0 : 0.0) + offset;
// gdy SplitEDPneumaticBrake jest wlaczone, sterowanie ujemne (hamowanie ED) bierzemy
// z osobnego nastawnika DynamicBrakeCtrl, a nie z hamulca pomocniczego (LocalBrake).
// Dodatkowo zapotrzebowanie ED jest ograniczone przez strefe Vh0..Vh1: powyzej Vh1 - pelny ED,
// pomiedzy Vh1 a Vh0 - liniowe zanikanie, ponizej Vh0 - zerowe (brak hamowania pneumatycznego
// jako uzupelnienia, poniewaz LocalBrake jest oddzielny i operuje wylacznie pneumatyka).
double brakeDemand = SplitEDPneumaticBrake ? DynamicBrakeRatio() : LocalBrakeRatio();
if (SplitEDPneumaticBrake)
{
double const vh0 = eimc[eimc_p_Vh0];
double const vh1 = eimc[eimc_p_Vh1];
if (vh1 > vh0 + 0.001)
{
double const vhRamp = clamp((Vel - vh0) / (vh1 - vh0), 0.0, 1.0);
brakeDemand *= vhRamp;
}
}
switch (EIMCtrlType)
{
case 0:
eimic = (LocalBrakeRatio() > 0.01 ? -LocalBrakeRatio() : eimic_analog > 0.01 ? eimic_analog : (double)MainCtrlPos / (double)MainCtrlPosNo);
eimic = (brakeDemand > 0.01 ? -brakeDemand : eimic_analog > 0.01 ? eimic_analog : (double)MainCtrlPos / (double)MainCtrlPosNo);
if (EIMCtrlAdditionalZeros || EIMCtrlEmergency)
{
if (eimic > 0.001)
@@ -7348,7 +7461,9 @@ void TMoverParameters::CheckEIMIC(double dt)
eimic = 0; // shut off retarder
if ((UniCtrlIntegratedBrakeCtrl == false) && (UniCtrlIntegratedLocalBrakeCtrl == false))
{
eimic = (LocalBrakeRatio() > 0.01 ? -LocalBrakeRatio() : eimic);
// w trybie SplitEDPneumaticBrake hamowanie ED przychodzi z osobnego nastawnika,
// LocalBrake operuje wylacznie pneumatycznym hamulcem lokomotywy
eimic = (brakeDemand > 0.01 ? -brakeDemand : eimic);
}
}
if (LocHandleTimeTraxx)
@@ -10709,6 +10824,18 @@ void TMoverParameters::LoadFIZ_Cntrl(std::string const &line)
extract_value(DynamicBrakeAmpmeters, "DBAM", line, "");
}
// split ED/pneumatic brake control: when true the local brake operates only on the
// pneumatic locomotive cylinder while electrodynamic braking is commanded by a
// separate "dynamicbrakectrl" lever (or the negative range of jointctrl).
extract_value(SplitEDPneumaticBrake, "SplitEDPneumaticBrake", line, "");
// number of stepped positions for the dedicated ED brake controller (default 10)
DynamicBrakeCtrlPosNo = 10;
extract_value(DynamicBrakeCtrlPosNo, "DBPN", line, "");
if (DynamicBrakeCtrlPosNo < 1)
{
DynamicBrakeCtrlPosNo = 10;
}
extract_value(MainCtrlPosNo, "MCPN", line, "");
extract_value(ScndCtrlPosNo, "SCPN", line, "");
extract_value(ScndInMain, "SCIM", line, "");

View File

@@ -319,6 +319,12 @@ int eu07_application::init(int Argc, char *Argv[])
return result;
}
// configure the OS console according to Globals.ShowSystemConsole.
// must run AFTER init_settings (so the ini-loaded value is honoured) and
// BEFORE the first WriteLog below (so colored \033[...] sequences emitted
// by utilities/Logs.cpp land in a console with VT processing enabled).
init_console();
WriteLog("Starting MaSzyna rail vehicle simulator (release: " + Global.asVersion + ")");
WriteLog("For online documentation and additional files refer to: http://eu07.pl");
WriteLog("Authors: Marcin_EU, McZapkie, ABu, Winger, Tolaris, nbmx, OLO_EU, Bart, Quark-t, "
@@ -978,6 +984,102 @@ void eu07_application::init_debug()
#endif
}
// Honours Globals.ShowSystemConsole (default: true).
//
// Windows: if the toggle is on, ensure a console is attached (allocate one when
// the binary was linked as the WINDOWS subsystem and none is inherited), then
// reopen the C stdio streams onto it and turn on ENABLE_VIRTUAL_TERMINAL_PROCESSING
// so the ANSI colour escapes used by utilities/Logs.cpp render correctly.
// If the toggle is off, hide the console window (or free an allocated one) so
// the GUI launches without a stray cmd window.
//
// Other platforms: no-op. stdout is the launching terminal (or already detached);
// hiding it on Linux/macOS would mean redirecting to /dev/null and is left to the
// user's shell (e.g. "./eu07 >/dev/null 2>&1").
void eu07_application::init_console()
{
#ifdef _WIN32
HWND consoleWnd = ::GetConsoleWindow();
const bool hadConsole = (consoleWnd != nullptr);
if (Global.ShowSystemConsole)
{
if (!hadConsole)
{
// no console inherited (e.g. WINDOWS subsystem build, or launched
// detached) -- create one and wire stdio to it so printf in
// utilities/Logs.cpp actually reaches the user
if (::AllocConsole())
{
FILE *fp = nullptr;
freopen_s(&fp, "CONOUT$", "w", stdout);
freopen_s(&fp, "CONOUT$", "w", stderr);
freopen_s(&fp, "CONIN$", "r", stdin);
// keep C++ streams in sync with the redirected C streams
std::ios::sync_with_stdio(true);
std::cout.clear();
std::cerr.clear();
std::clog.clear();
std::cin.clear();
consoleWnd = ::GetConsoleWindow();
}
}
// enable ANSI virtual-terminal processing so the colour escapes in
// utilities/Logs.cpp ("\033[1;37;41m...", "\033[32m...") render as
// colours instead of literal garbage on the conhost.
//
// IMPORTANT: do NOT rely on GetStdHandle(STD_OUTPUT_HANDLE). After
// AllocConsole + freopen_s the CRT may have rewired things, and on a
// console-subsystem build the std handle can also point to a redirected
// pipe/file rather than the actual console screen buffer -- in either
// case SetConsoleMode silently fails and the escape bytes leak through
// as text ("←[32m" etc). Open CONOUT$ directly to get the real screen
// buffer handle, then flip VT on it.
auto enable_vt = [](const char *devName) {
HANDLE h = ::CreateFileA(
devName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
0,
nullptr);
if (h == INVALID_HANDLE_VALUE)
return;
DWORD mode = 0;
if (::GetConsoleMode(h, &mode))
{
::SetConsoleMode(h, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT);
}
::CloseHandle(h);
};
// CONOUT$ is the console's active screen buffer; enabling VT on it
// affects everything that ends up being written there, regardless of
// which FILE* / std handle the writer used
enable_vt("CONOUT$");
if (consoleWnd)
{
::ShowWindow(consoleWnd, SW_SHOW);
// give the console a recognisable title
::SetConsoleTitleA("MaSzyna log");
}
}
else
{
// user wants no console window -- hide whatever we have
if (consoleWnd)
{
::ShowWindow(consoleWnd, SW_HIDE);
}
// if we'd allocated one ourselves on a previous run path we'd FreeConsole here,
// but on first init the console (if any) was inherited from the launcher and
// belongs to it -- just hiding the window is the least-surprising behaviour
}
#endif
}
void eu07_application::init_files()
{

View File

@@ -101,6 +101,7 @@ private:
// methods
bool needs_ogl() const;
void init_debug();
void init_console();
void init_files();
int init_settings( int Argc, char *Argv[] );
int init_locale();

View File

@@ -17,11 +17,16 @@ gl::cubemap::~cubemap()
void gl::cubemap::alloc(GLint format, int width, int height, GLenum components, GLenum type)
{
glBindTexture(GL_TEXTURE_CUBE_MAP, *this);
for (GLuint tgt = GL_TEXTURE_CUBE_MAP_POSITIVE_X; tgt <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; tgt++)
glTexImage2D(tgt, 0, format, width, height, 0, components, type, nullptr);
glBindTexture(GL_TEXTURE_CUBE_MAP, *this);
for (GLuint tgt = GL_TEXTURE_CUBE_MAP_POSITIVE_X; tgt <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; tgt++)
glTexImage2D(tgt, 0, format, width, height, 0, components, type, nullptr);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
// make the cubemap sample-complete with no mipmaps required.
// generate_mipmaps() will later override MIN_FILTER once mips actually exist.
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
void gl::cubemap::bind(int unit)

View File

@@ -30,6 +30,13 @@ commanddescription_sequence Commands_descriptions = {
{"mastercontrollerdecrease", command_target::vehicle, command_mode::oneoff},
{"mastercontrollerdecreasefast", command_target::vehicle, command_mode::oneoff},
{"mastercontrollerset", command_target::vehicle, command_mode::oneoff},
{"dynamicbrakecontrollerincrease", command_target::vehicle, command_mode::oneoff},
{"dynamicbrakecontrollerdecrease", command_target::vehicle, command_mode::oneoff},
{"dynamicbrakecontrollerset", command_target::vehicle, command_mode::oneoff},
{"dynamicbrakecontrollerincreasefast", command_target::vehicle, command_mode::oneoff},
{"dynamicbrakecontrollerdecreasefast", command_target::vehicle, command_mode::oneoff},
{"secondcontrollerincrease", command_target::vehicle, command_mode::oneoff},
{"secondcontrollerincreasefast", command_target::vehicle, command_mode::oneoff},
{"secondcontrollerdecrease", command_target::vehicle, command_mode::oneoff},
@@ -410,6 +417,11 @@ std::unordered_map<std::string, user_command> commandMap = {
{"mastercontrollerdecrease", user_command::mastercontrollerdecrease},
{"mastercontrollerdecreasefast", user_command::mastercontrollerdecreasefast},
{"mastercontrollerset", user_command::mastercontrollerset},
{"dynamicbrakecontrollerincrease", user_command::dynamicbrakecontrollerincrease},
{"dynamicbrakecontrollerdecrease", user_command::dynamicbrakecontrollerdecrease},
{"dynamicbrakecontrollerset", user_command::dynamicbrakecontrollerset},
{"dynamicbrakecontrollerincreasefast", user_command::dynamicbrakecontrollerincreasefast},
{"dynamicbrakecontrollerdecreasefast", user_command::dynamicbrakecontrollerdecreasefast},
{"secondcontrollerincrease", user_command::secondcontrollerincrease},
{"secondcontrollerincreasefast", user_command::secondcontrollerincreasefast},
{"secondcontrollerdecrease", user_command::secondcontrollerdecrease},

View File

@@ -24,6 +24,11 @@ enum class user_command
mastercontrollerdecrease,
mastercontrollerdecreasefast,
mastercontrollerset,
dynamicbrakecontrollerincrease,
dynamicbrakecontrollerdecrease,
dynamicbrakecontrollerset,
dynamicbrakecontrollerincreasefast,
dynamicbrakecontrollerdecreasefast,
secondcontrollerincrease,
secondcontrollerincreasefast,
secondcontrollerdecrease,

View File

@@ -32,6 +32,13 @@ driverkeyboard_input::default_bindings() {
{ user_command::mastercontrollerdecrease, {GLFW_KEY_KP_SUBTRACT, ""} },
{ user_command::mastercontrollerdecreasefast, {GLFW_KEY_KP_SUBTRACT | keymodifier::shift, ""} },
// mastercontrollerset,
{ user_command::dynamicbrakecontrollerincrease, {GLFW_KEY_KP_3 | keymodifier::control, ""} },
{ user_command::dynamicbrakecontrollerdecrease, {GLFW_KEY_KP_9 | keymodifier::control, ""} },
// mastercontrollerset,
{ user_command::dynamicbrakecontrollerincreasefast, {GLFW_KEY_KP_3 | keymodifier::control | keymodifier::shift, ""} },
{ user_command::dynamicbrakecontrollerdecreasefast, {GLFW_KEY_KP_9 | keymodifier::control | keymodifier::shift, ""} },
{ user_command::secondcontrollerincrease, {GLFW_KEY_KP_DIVIDE, ""} },
{ user_command::secondcontrollerincreasefast, {GLFW_KEY_KP_DIVIDE | keymodifier::shift, ""} },
{ user_command::secondcontrollerdecrease, {GLFW_KEY_KP_MULTIPLY, ""} },

View File

@@ -534,6 +534,9 @@ drivermouse_input::default_bindings() {
{ "mainctrl:", {
user_command::mastercontrollerset,
user_command::none } },
{ "dynamicbrakectrl:", {
user_command::dynamicbrakecontrollerset,
user_command::none } },
{ "scndctrl:", {
user_command::secondcontrollerset,
user_command::none } },

View File

@@ -312,15 +312,37 @@ void python_taskqueue::exit()
m_condition.notify_all();
// let them free up their shit before we proceed
m_workers = {};
// get rid of the leftover tasks
// with the workers dead we don't have to worry about concurrent access anymore
for (auto task : m_tasks.data)
// drop any pending render/upload tasks. the workers are joined at this
// point so the locks are strictly defensive, but they cost nothing and
// document intent. clearing the deque also actually releases the tasks,
// which the previous code did not do (cancel() is a no-op stub).
{
task->cancel();
std::lock_guard<std::mutex> lock(m_tasks.mutex);
for (auto &task : m_tasks.data)
{
task->cancel();
}
m_tasks.data.clear();
}
// take a bow
{
std::lock_guard<std::mutex> lock(m_uploadtasks.mutex);
m_uploadtasks.data.clear();
}
// reclaim cached python objects while the interpreter is still alive,
// so no Py_DECREF lands on a finalized interpreter during later teardown
acquire_lock();
for (auto &entry : m_renderers)
{
Py_XDECREF(entry.second);
}
m_renderers.clear();
Py_XDECREF(m_stderr);
m_stderr = nullptr;
Py_XDECREF(m_main);
m_main = nullptr;
// take a bow
Py_Finalize();
m_initialized = false;
}
// adds specified task along with provided collection of data to the work queue. returns true on success
@@ -518,8 +540,10 @@ void python_taskqueue::run(GLFWwindow *Context, rendertask_sequence &Tasks, uplo
// TBD, TODO: add some idle time between tasks in case we're on a single thread cpu?
} while (task != nullptr);
// if there's nothing left to do wait until there is
// but check every now and then on your own to minimize potential deadlock situations
Condition.wait_for(std::chrono::seconds(5));
// short timeout: notify_all() in exit() can race with the worker's
// transition into the wait, and the run loop also drives prompt
// shutdown checks
Condition.wait_for(std::chrono::milliseconds(250));
}
// clean up thread state data
PyEval_AcquireLock();
@@ -527,6 +551,12 @@ void python_taskqueue::run(GLFWwindow *Context, rendertask_sequence &Tasks, uplo
PyThreadState_Clear(threadstate);
PyThreadState_Delete(threadstate);
PyEval_ReleaseLock();
// detach the GL context before the worker terminates; some drivers
// (NVIDIA on X11, certain Mesa/Wayland configs) hang in process teardown
// if a context is still current on a dying thread
if (Context)
glfwMakeContextCurrent(nullptr);
}
void python_taskqueue::update()

View File

@@ -8,6 +8,14 @@ vec3 envmap_color( vec3 normal )
vec3 refvec = reflect(f_pos.xyz, normal); // view space
refvec = vec3(inv_view * vec4(refvec, 0.0)); // world space
vec3 envcolor = texture(envmap, refvec).rgb;
// Sanitize. The env cubemap can briefly contain NaN/Inf after a
// reflection-pass regeneration (m_empty_cubemap was bound during the
// face render and on NVIDIA can sample as undefined). Without this
// guard, a single bad texel propagates through env_reflection in
// apply_lights() and produces one-frame black flashes on glossy
// (esp. specgloss) surfaces.
if (any(isnan(envcolor)) || any(isinf(envcolor))) envcolor = vec3(0.0);
envcolor = max(envcolor, vec3(0.0));
#else
vec3 envcolor = vec3(0.5);
#endif
@@ -21,8 +29,13 @@ vec3 envmap_color_lod(vec3 fragnormal, float lod)
#if ENVMAP_ENABLED
vec3 refvec = reflect(f_pos.xyz, fragnormal); // view space — matches envmap_color exactly
refvec = vec3(inv_view * vec4(refvec, 0.0)); // world space — was missing
return textureLod(envmap, refvec, lod).rgb;
vec3 envcolor = textureLod(envmap, refvec, lod).rgb;
// See envmap_color() above — same NaN/Inf sanitize, also needed here
// because mipmap generation propagates a single NaN texel across the
// whole mip chain.
if (any(isnan(envcolor)) || any(isinf(envcolor))) envcolor = vec3(0.0);
return max(envcolor, vec3(0.0));
#else
return vec3(0.5); // was vec3(0.0), match the non-LOD fallback
#endif
}
}

View File

@@ -46,6 +46,17 @@ vec4 tonemap(vec4 x)
// with pureWhite=1.0 it collapses to identity (L*(1+L)/(1+L) = L) and
// just clips HDR>1.0 at the framebuffer -> washed-out / burnt look.
// ACES gives a smooth highlight shoulder + slight toe contrast.
return FBOUT(vec4(ACESFilm(x.rgb), x.a));
// Last-line-of-defense sanitize. ACESFilm has the form
// (x*(a*x+b)) / (x*(c*x+d)+e)
// which maps NaN -> NaN and +Inf -> NaN (Inf/Inf). Either turns the
// pixel black after framebuffer clamp. A negative HDR input feeds a
// negative numerator/denominator and can produce non-physical output
// that also looks like a black flash. Clamp to a sensible range
// before the curve so a single bad upstream pixel can't escape.
vec3 hdr = x.rgb;
hdr = mix(hdr, vec3(0.0), vec3(any(isnan(hdr)) || any(isinf(hdr))));
hdr = max(hdr, vec3(0.0));
return FBOUT(vec4(ACESFilm(hdr), x.a));
//return FBOUT(vec4(reinhard(x.rgb), x.a));
}

View File

@@ -1086,6 +1086,12 @@ bool global_settings::ConfigParseDebug(cParser& Parser, const std::string& token
return true;
}
if (token == "showsystemconsole")
{
ParseOne(Parser, ShowSystemConsole);
return true;
}
if (token == "shakefactor")
{
ParseOne(Parser, ShakingMultiplierBF);
@@ -1516,6 +1522,7 @@ global_settings::export_as_text( std::ostream &Output ) const {
export_as_text( Output, "fullphysics", FullPhysics );
export_as_text( Output, "debuglog", iWriteLogEnabled );
export_as_text( Output, "multiplelogs", MultipleLogs );
export_as_text( Output, "showsystemconsole", ShowSystemConsole );
export_as_text( Output, "logs.filter", DisabledLogTypes );
Output
<< "mousescale "

View File

@@ -86,6 +86,7 @@ struct global_settings {
bool priorityLoadText3D{false}; // ladowanie T3D priorytetowo
int iWriteLogEnabled{ 3 }; // maska bitowa: 1-zapis do pliku, 2-okienko, 4-nazwy torów
bool MultipleLogs{ false };
bool ShowSystemConsole{ false }; // toggle visibility of the OS console window with log output
unsigned int DisabledLogTypes{ 0 };
bool ParserLogIncludes{ true };
// simulation

View File

@@ -190,17 +190,16 @@ bool cParser::getTokens(unsigned int Count, bool ToLower, const char *Break)
this->str("");
this->clear();
*/
std::string token;
for (unsigned int i = tokens.size(); i < Count; ++i)
{
readToken(token, ToLower, Break);
if (token.empty())
std::string token = readToken(ToLower, Break);
if (true == token.empty())
{
// no more tokens
break;
}
tokens.emplace_back(std::move(token));
// collect parameters
tokens.emplace_back(token);
/*
if (i == 0)
this->str(token);
@@ -219,6 +218,18 @@ bool cParser::getTokens(unsigned int Count, bool ToLower, const char *Break)
return true;
}
std::string cParser::readTokenFromDelegate(bool ToLower, const char *Break)
{
if (!mIncludeParser)
return {};
std::string token = mIncludeParser->readToken(ToLower, Break);
if (token.empty())
{
mIncludeParser = nullptr;
}
return token;
}
std::string cParser::readTokenFromStream(bool ToLower, const char *Break)
{
std::string token;
@@ -268,7 +279,7 @@ void cParser::stripFirstTokenBOM(std::string& token, bool ToLower, const char* B
// if first "token" was standalone BOM, read the next real token (avoid recursion)
while (token.empty() && mStream->peek() != EOF) {
readToken(token, ToLower, Break);
token = readToken(ToLower, Break);
// readToken will not re-enter BOM stripping because mFirstToken is now false
break;
}
@@ -310,7 +321,7 @@ void cParser::skipIncludeBlock() {
// mimic original: while token != "end" readToken(true)
std::string t;
do {
readToken(t, true);
t = readToken(true);
} while (t != "end" && !t.empty());
}
@@ -355,81 +366,72 @@ void cParser::startIncludeFromParser(cParser& srcParser, bool ToLower, std::stri
bool cParser::handleIncludeIfPresent(std::string& token, bool ToLower, const char* Break) {
// token-mode include: token == "include"
if (expandIncludes && token == "include") {
std::string includefile;
if (allowRandomIncludes)
includefile = deserialize_random_set(*this);
else
readToken(includefile, ToLower);
std::string includefile =
allowRandomIncludes ? deserialize_random_set(*this) : readToken(ToLower);
startIncludeFromParser(*this, ToLower, std::move(includefile));
// after processing include, return next token from current parser
readToken(token, ToLower, Break);
token = readToken(ToLower, Break);
return true;
}
// line-mode HACK: Break == "\n\r" and line begins with "include"
if ((std::strcmp(Break, "\n\r") == 0) && token.compare(0, 7, "include") == 0) {
cParser includeparser(token.substr(7));
std::string includefile;
if (allowRandomIncludes)
includefile = deserialize_random_set(includeparser);
else
includeparser.readToken(includefile, ToLower);
std::string includefile =
allowRandomIncludes ? deserialize_random_set(includeparser) : includeparser.readToken(ToLower);
startIncludeFromParser(includeparser, ToLower, std::move(includefile));
readToken(token, ToLower, Break);
token = readToken(ToLower, Break);
return true;
}
return false;
}
void cParser::readToken(std::string &out, bool ToLower, const char *Break)
std::string cParser::readToken(bool ToLower, const char *Break)
{
if (mIncludeParser)
std::string token;
token = readTokenFromDelegate(ToLower, Break);
if (token.empty())
{
mIncludeParser->readToken(out, ToLower, Break);
if (out.empty())
{
mIncludeParser = nullptr;
out = readTokenFromStream(ToLower, Break);
}
}
else
{
out = readTokenFromStream(ToLower, Break);
token = readTokenFromStream(ToLower, Break);
}
stripFirstTokenBOM(out, ToLower, Break);
stripFirstTokenBOM(token, ToLower, Break);
substituteParameters(out, ToLower);
substituteParameters(token, ToLower);
handleIncludeIfPresent(out, ToLower, Break);
handleIncludeIfPresent(token, ToLower, Break);
return token;
}
std::vector<std::string> cParser::readParameters(cParser &Input)
{
std::vector<std::string> includeparameters;
std::string parameter;
Input.readToken(parameter, false); // w parametrach nie zmniejszamy
std::string parameter = Input.readToken(false); // w parametrach nie zmniejszamy
while ((parameter.empty() == false) && (parameter != "end"))
{
includeparameters.emplace_back(parameter);
Input.readToken(parameter, false);
parameter = Input.readToken(false);
}
return includeparameters;
}
std::string cParser::readQuotes(char const Quote)
{ // read the stream until specified char or stream end
std::string token;
std::string token = "";
char c{0};
bool escaped = false;
while (mStream->get(c))
while (mStream->peek() != EOF)
{ // get all chars until the quote mark
c = mStream->get();
if (escaped)
{
escaped = false;
@@ -455,11 +457,13 @@ std::string cParser::readQuotes(char const Quote)
void cParser::skipComment(std::string const &Endmark)
{ // pobieranie znaków aż do znalezienia znacznika końca
std::string input;
std::string input = "";
char c{0};
auto const endmarksize = Endmark.size();
while (mStream->get(c))
while (mStream->peek() != EOF)
{
// o ile nie koniec pliku
c = mStream->get(); // pobranie znaku
if (c == '\n')
{
// update line counter
@@ -550,8 +554,8 @@ std::size_t cParser::count()
size_t count{0};
do
{
token.clear();
readToken(token, false);
token = "";
token = readToken(false);
++count;
} while (false == token.empty());

View File

@@ -45,14 +45,11 @@ class cParser //: public std::stringstream
inline
void
ignoreToken() {
std::string out;
readToken(out); };
readToken(); };
inline
bool
expectToken( std::string const &Value ) {
std::string out;
readToken(out);
return out == Value; };
return readToken() == Value; };
inline
bool
eof() {
@@ -69,6 +66,7 @@ class cParser //: public std::stringstream
return m_autoclear; }
bool
getTokens( unsigned int Count = 1, bool ToLower = true, char const *Break = "\n\r\t ;" );
std::string readTokenFromDelegate(bool ToLower, const char *Break);
std::string readTokenFromStream(bool ToLower, const char *Break);
void stripFirstTokenBOM(std::string &token, bool ToLower, const char *Break);
void substituteParameters(std::string &token, bool ToLower);
@@ -105,7 +103,7 @@ class cParser //: public std::stringstream
void startIncludeFromParser(cParser &srcParser, bool ToLower, std::string includefile);
bool handleIncludeIfPresent(std::string &token, bool ToLower, const char *Break);
// methods:
void readToken(std::string& out, bool ToLower = true, const char *Break = "\n\r\t ;");
std::string readToken(bool ToLower = true, const char *Break = "\n\r\t ;");
static std::vector<std::string> readParameters( cParser &Input );
std::string readQuotes( char const Quote = '\"' );
void skipComment( std::string const &Endmark );

View File

@@ -293,7 +293,7 @@ material_data::assign( std::string const &Replacableskin ) {
// check for the pipe method first
if( contains( Replacableskin, '|' ) ) {
cParser nameparser( Replacableskin );
nameparser.getTokens( 4, true, "|" );
nameparser.getTokens( 4, false, "|" );
int skinindex = 0;
std::string texturename; nameparser >> texturename;
while( ( texturename != "" ) && ( skinindex < 4 ) ) {

File diff suppressed because it is too large Load Diff

View File

@@ -236,11 +236,19 @@ class TTrain {
static void OnCommand_aidriverenable( TTrain *Train, command_data const &Command );
static void OnCommand_aidriverdisable( TTrain *Train, command_data const &Command );
static void OnCommand_jointcontrollerset( TTrain *Train, command_data const &Command );
static void OnCommand_mastercontrollerincrease( TTrain *Train, command_data const &Command );
static void OnCommand_mastercontrollerincreasefast( TTrain *Train, command_data const &Command );
static void OnCommand_mastercontrollerdecrease( TTrain *Train, command_data const &Command );
static void OnCommand_mastercontrollerdecreasefast( TTrain *Train, command_data const &Command );
static void OnCommand_mastercontrollerset( TTrain *Train, command_data const &Command );
static void OnCommand_DynamicBrakeControllerIncrease( TTrain *Train, command_data const &Command );
static void OnCommand_DynamicBrakeControllerIncreaseFast( TTrain *Train, command_data const &Command );
static void OnCommand_DynamicBrakeControllerDecrease( TTrain *Train, command_data const &Command );
static void OnCommand_DynamicBrakeControllerDecreaseFast( TTrain *Train, command_data const &Command );
static void OnCommand_DynamicBrakeControllerSet( TTrain *Train, command_data const &Command );
static void OnCommand_secondcontrollerincrease( TTrain *Train, command_data const &Command );
static void OnCommand_secondcontrollerincreasefast( TTrain *Train, command_data const &Command );
static void OnCommand_secondcontrollerdecrease( TTrain *Train, command_data const &Command );