From f48b976ed0055cd7d97bc4a5a051fbdd092700af Mon Sep 17 00:00:00 2001 From: Kamil Lewan Date: Sat, 16 Jan 2021 21:21:13 +0100 Subject: [PATCH 01/88] Update "Dynamic libraries" secion - small fix in formating: end `/` - add serialport link - add `asio` lib --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6a60f43f..2e15e1bd 100644 --- a/README.md +++ b/README.md @@ -25,14 +25,15 @@ List of requirements for compiling executable. For usage/runtime requirements [s **Note:** There's some issue in our build system. If link error occurs, use `-DGLFW3_LIBRARIES=''` in CMake. - [GLM](https://glm.g-truc.net/) (0.9.9.0) -- serialport (0.1.1) +- [serialport](https://sigrok.org/wiki/Libserialport) (0.1.1) - [sndfile](https://github.com/erikd/libsndfile) (1.0.28) -- [LuajIT](http://luajit.org/) (2.0.5) -- [GLEW](http://glew.sourceforge.net/) (2.1.0) +- [LuajIT](http://luajit.org) (2.0.5) +- [GLEW](http://glew.sourceforge.net) (2.1.0) - [PNG](http://www.libpng.org/pub/png/libpng.html) (1.6.34) -- [OpenAL](https://www.openal.org/) (1.18.2) +- [OpenAL](https://www.openal.org) (1.18.2) - pthread - [Python 2.7](https://www.python.org) +- [asio](https://think-async.com/Asio) (1.12) ### **3. OpenGL 3.0.** From 84980a44849819af9dda126e6f496c8326d0cfa4 Mon Sep 17 00:00:00 2001 From: jakubg1 <24206305+jakubg1@users.noreply.github.com> Date: Sun, 3 Mar 2024 03:00:47 +0100 Subject: [PATCH 02/88] Improve the keymapper The keymapper, available as one of the options in the starting menu when the EXE is run without a scenery specified, has undergone a few changes: - Key descriptions are now parsed the way Starter does and are displayed in the window. They are also no longer consumed when saving to a file. - Unbound keys are now also displayed as well. Not all of them though; some entries won't exist if they are not specified in the eu07_input-keyboard.ini file. - You can now unbind a key by pressing F10. The Parser has got a new boolean option: `skipComments`, true by default to leave current behavior unchanged. When set to false, comments will be parsed as ordinary tokens. --- driverkeyboardinput.cpp | 292 ++++++++++++++++++++-------------------- editorkeyboardinput.cpp | 12 +- keyboardinput.cpp | 69 ++++++---- keyboardinput.h | 2 +- launcher/keymapper.cpp | 27 +++- parser.cpp | 2 +- parser.h | 1 + 7 files changed, 222 insertions(+), 183 deletions(-) diff --git a/driverkeyboardinput.cpp b/driverkeyboardinput.cpp index 16b4a468..a093f023 100644 --- a/driverkeyboardinput.cpp +++ b/driverkeyboardinput.cpp @@ -24,265 +24,265 @@ void driverkeyboard_input::default_bindings() { m_bindingsetups = { - { user_command::aidriverenable, GLFW_KEY_Q | keymodifier::shift }, - { user_command::aidriverdisable, GLFW_KEY_Q }, + { user_command::aidriverenable, {GLFW_KEY_Q | keymodifier::shift, ""} }, + { user_command::aidriverdisable, {GLFW_KEY_Q, ""} }, // jointcontrollerset, - { user_command::mastercontrollerincrease, GLFW_KEY_KP_ADD }, - { user_command::mastercontrollerincreasefast, GLFW_KEY_KP_ADD | keymodifier::shift }, - { user_command::mastercontrollerdecrease, GLFW_KEY_KP_SUBTRACT }, - { user_command::mastercontrollerdecreasefast, GLFW_KEY_KP_SUBTRACT | keymodifier::shift }, + { user_command::mastercontrollerincrease, {GLFW_KEY_KP_ADD, ""} }, + { user_command::mastercontrollerincreasefast, {GLFW_KEY_KP_ADD | keymodifier::shift, ""} }, + { user_command::mastercontrollerdecrease, {GLFW_KEY_KP_SUBTRACT, ""} }, + { user_command::mastercontrollerdecreasefast, {GLFW_KEY_KP_SUBTRACT | keymodifier::shift, ""} }, // mastercontrollerset, - { user_command::secondcontrollerincrease, GLFW_KEY_KP_DIVIDE }, - { user_command::secondcontrollerincreasefast, GLFW_KEY_KP_DIVIDE | keymodifier::shift }, - { user_command::secondcontrollerdecrease, GLFW_KEY_KP_MULTIPLY }, - { user_command::secondcontrollerdecreasefast, GLFW_KEY_KP_MULTIPLY | 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, ""} }, + { user_command::secondcontrollerdecreasefast, {GLFW_KEY_KP_MULTIPLY | keymodifier::shift, ""} }, // secondcontrollerset, - { user_command::mucurrentindicatorothersourceactivate, GLFW_KEY_Z | keymodifier::shift }, - { user_command::independentbrakeincrease, GLFW_KEY_KP_1 }, - { user_command::independentbrakeincreasefast, GLFW_KEY_KP_1 | keymodifier::shift }, - { user_command::independentbrakedecrease, GLFW_KEY_KP_7 }, - { user_command::independentbrakedecreasefast, GLFW_KEY_KP_7 | keymodifier::shift }, + { user_command::mucurrentindicatorothersourceactivate, {GLFW_KEY_Z | keymodifier::shift, ""} }, + { user_command::independentbrakeincrease, {GLFW_KEY_KP_1, ""} }, + { user_command::independentbrakeincreasefast, {GLFW_KEY_KP_1 | keymodifier::shift, ""} }, + { user_command::independentbrakedecrease, {GLFW_KEY_KP_7, ""} }, + { user_command::independentbrakedecreasefast, {GLFW_KEY_KP_7 | keymodifier::shift, ""} }, // independentbrakeset, - { user_command::independentbrakebailoff, GLFW_KEY_KP_4 }, + { user_command::independentbrakebailoff, {GLFW_KEY_KP_4, ""} }, // universalbrakebutton1, // universalbrakebutton2, // universalbrakebutton3, - { user_command::trainbrakeincrease, GLFW_KEY_KP_3 }, - { user_command::trainbrakedecrease, GLFW_KEY_KP_9 }, + { user_command::trainbrakeincrease, {GLFW_KEY_KP_3, ""} }, + { user_command::trainbrakedecrease, {GLFW_KEY_KP_9, ""} }, // trainbrakeset, - { user_command::trainbrakecharging, GLFW_KEY_KP_DECIMAL }, - { user_command::trainbrakerelease, GLFW_KEY_KP_6 }, - { user_command::trainbrakefirstservice, GLFW_KEY_KP_8 }, - { user_command::trainbrakeservice, GLFW_KEY_KP_5 }, - { user_command::trainbrakefullservice, GLFW_KEY_KP_2 }, - { user_command::trainbrakehandleoff, GLFW_KEY_KP_5 | keymodifier::control }, - { user_command::trainbrakeemergency, GLFW_KEY_KP_0 }, - { user_command::trainbrakebasepressureincrease, GLFW_KEY_KP_3 | keymodifier::control }, - { user_command::trainbrakebasepressuredecrease, GLFW_KEY_KP_9 | keymodifier::control }, - { user_command::trainbrakebasepressurereset, GLFW_KEY_KP_6 | keymodifier::control }, - { user_command::trainbrakeoperationtoggle, GLFW_KEY_KP_4 | keymodifier::control }, - { user_command::manualbrakeincrease, GLFW_KEY_KP_1 | keymodifier::control }, - { user_command::manualbrakedecrease, GLFW_KEY_KP_7 | keymodifier::control }, - { user_command::alarmchaintoggle, GLFW_KEY_B | keymodifier::shift | keymodifier::control }, + { user_command::trainbrakecharging, {GLFW_KEY_KP_DECIMAL, ""} }, + { user_command::trainbrakerelease, {GLFW_KEY_KP_6, ""} }, + { user_command::trainbrakefirstservice, {GLFW_KEY_KP_8, ""} }, + { user_command::trainbrakeservice, {GLFW_KEY_KP_5, ""} }, + { user_command::trainbrakefullservice, {GLFW_KEY_KP_2, ""} }, + { user_command::trainbrakehandleoff, {GLFW_KEY_KP_5 | keymodifier::control, ""} }, + { user_command::trainbrakeemergency, {GLFW_KEY_KP_0, ""} }, + { user_command::trainbrakebasepressureincrease, {GLFW_KEY_KP_3 | keymodifier::control, ""} }, + { user_command::trainbrakebasepressuredecrease, {GLFW_KEY_KP_9 | keymodifier::control, ""} }, + { user_command::trainbrakebasepressurereset, {GLFW_KEY_KP_6 | keymodifier::control, ""} }, + { user_command::trainbrakeoperationtoggle, {GLFW_KEY_KP_4 | keymodifier::control, ""} }, + { user_command::manualbrakeincrease, {GLFW_KEY_KP_1 | keymodifier::control, ""} }, + { user_command::manualbrakedecrease, {GLFW_KEY_KP_7 | keymodifier::control, ""} }, + { user_command::alarmchaintoggle, {GLFW_KEY_B | keymodifier::shift | keymodifier::control, ""} }, // alarmchainenable, // alarmchaindisable, - { user_command::wheelspinbrakeactivate, GLFW_KEY_KP_ENTER }, - { user_command::sandboxactivate, GLFW_KEY_S | keymodifier::shift }, + { user_command::wheelspinbrakeactivate, {GLFW_KEY_KP_ENTER, ""} }, + { user_command::sandboxactivate, {GLFW_KEY_S | keymodifier::shift, ""} }, // autosandboxtoggle, // autosandboxactivate, // autosandboxdeactivate, - { user_command::reverserincrease, GLFW_KEY_D }, - { user_command::reverserdecrease, GLFW_KEY_R }, + { user_command::reverserincrease, {GLFW_KEY_D, ""} }, + { user_command::reverserdecrease, {GLFW_KEY_R, ""} }, // reverserforwardhigh, // reverserforward, // reverserneutral, // reverserbackward, - { user_command::waterpumpbreakertoggle, GLFW_KEY_W | keymodifier::control }, + { user_command::waterpumpbreakertoggle, {GLFW_KEY_W | keymodifier::control, ""} }, // waterpumpbreakerclose, // waterpumpbreakeropen, - { user_command::waterpumptoggle, GLFW_KEY_W }, + { user_command::waterpumptoggle, {GLFW_KEY_W, ""} }, // waterpumpenable, // waterpumpdisable, - { user_command::waterheaterbreakertoggle, GLFW_KEY_W | keymodifier::control | keymodifier::shift }, + { user_command::waterheaterbreakertoggle, {GLFW_KEY_W | keymodifier::control | keymodifier::shift, ""} }, // waterheaterbreakerclose, // waterheaterbreakeropen, - { user_command::waterheatertoggle, GLFW_KEY_W | keymodifier::shift }, + { user_command::waterheatertoggle, {GLFW_KEY_W | keymodifier::shift, ""} }, // waterheaterenable, // waterheaterdisable, - { user_command::watercircuitslinktoggle, GLFW_KEY_H | keymodifier::shift }, + { user_command::watercircuitslinktoggle, {GLFW_KEY_H | keymodifier::shift, ""} }, // watercircuitslinkenable, // watercircuitslinkdisable, - { user_command::fuelpumptoggle, GLFW_KEY_F }, + { user_command::fuelpumptoggle, {GLFW_KEY_F, ""} }, // fuelpumpenable, // fuelpumpdisable, - { user_command::oilpumptoggle, GLFW_KEY_F | keymodifier::shift }, + { user_command::oilpumptoggle, {GLFW_KEY_F | keymodifier::shift, ""} }, // oilpumpenable, // oilpumpdisable, - { user_command::linebreakertoggle, GLFW_KEY_M }, + { user_command::linebreakertoggle, {GLFW_KEY_M, ""} }, // linebreakeropen, // linebreakerclose, - { user_command::convertertoggle, GLFW_KEY_X }, + { user_command::convertertoggle, {GLFW_KEY_X, ""} }, // converterenable, // converterdisable, - { user_command::convertertogglelocal, GLFW_KEY_X | keymodifier::shift }, - { user_command::converteroverloadrelayreset, GLFW_KEY_N | keymodifier::control }, - { user_command::compressortoggle, GLFW_KEY_C }, + { user_command::convertertogglelocal, {GLFW_KEY_X | keymodifier::shift, ""} }, + { user_command::converteroverloadrelayreset, {GLFW_KEY_N | keymodifier::control, ""} }, + { user_command::compressortoggle, {GLFW_KEY_C, ""} }, // compressorenable, // compressordisable, - { user_command::compressortogglelocal, GLFW_KEY_C | keymodifier::shift }, + { user_command::compressortogglelocal, {GLFW_KEY_C | keymodifier::shift, ""} }, // compressorpresetactivatenext, // compressorpresetactivateprevious, // compressorpresetactivatedefault, - { user_command::motoroverloadrelaythresholdtoggle, GLFW_KEY_F | keymodifier::control }, + { user_command::motoroverloadrelaythresholdtoggle, {GLFW_KEY_F | keymodifier::control, ""} }, // motoroverloadrelaythresholdsetlow, // motoroverloadrelaythresholdsethigh, - { user_command::motoroverloadrelayreset, GLFW_KEY_N }, + { user_command::motoroverloadrelayreset, {GLFW_KEY_N, ""} }, // universalrelayreset1, // universalrelayreset2, // universalrelayreset3, - { user_command::notchingrelaytoggle, GLFW_KEY_G }, - { user_command::epbrakecontroltoggle, GLFW_KEY_Z | keymodifier::control }, - { user_command::trainbrakeoperationmodeincrease, GLFW_KEY_KP_2 | keymodifier::control }, - { user_command::trainbrakeoperationmodedecrease, GLFW_KEY_KP_8 | keymodifier::control }, - { user_command::brakeactingspeedincrease, GLFW_KEY_B | keymodifier::shift }, - { user_command::brakeactingspeeddecrease, GLFW_KEY_B }, + { user_command::notchingrelaytoggle, {GLFW_KEY_G, ""} }, + { user_command::epbrakecontroltoggle, {GLFW_KEY_Z | keymodifier::control, ""} }, + { user_command::trainbrakeoperationmodeincrease, {GLFW_KEY_KP_2 | keymodifier::control, ""} }, + { user_command::trainbrakeoperationmodedecrease, {GLFW_KEY_KP_8 | keymodifier::control, ""} }, + { user_command::brakeactingspeedincrease, {GLFW_KEY_B | keymodifier::shift, ""} }, + { user_command::brakeactingspeeddecrease, {GLFW_KEY_B, ""} }, // brakeactingspeedsetcargo, // brakeactingspeedsetpassenger, // brakeactingspeedsetrapid, - { user_command::brakeloadcompensationincrease, GLFW_KEY_H | keymodifier::shift | keymodifier::control }, - { user_command::brakeloadcompensationdecrease, GLFW_KEY_H | keymodifier::control }, - { user_command::mubrakingindicatortoggle, GLFW_KEY_L | keymodifier::shift }, - { user_command::alerteracknowledge, GLFW_KEY_SPACE }, - { user_command::hornlowactivate, GLFW_KEY_A }, - { user_command::hornhighactivate, GLFW_KEY_S }, - { user_command::whistleactivate, GLFW_KEY_Z }, - { user_command::radiotoggle, GLFW_KEY_R | keymodifier::control }, + { user_command::brakeloadcompensationincrease, {GLFW_KEY_H | keymodifier::shift | keymodifier::control, ""} }, + { user_command::brakeloadcompensationdecrease, {GLFW_KEY_H | keymodifier::control, ""} }, + { user_command::mubrakingindicatortoggle, {GLFW_KEY_L | keymodifier::shift, ""} }, + { user_command::alerteracknowledge, {GLFW_KEY_SPACE, ""} }, + { user_command::hornlowactivate, {GLFW_KEY_A, ""} }, + { user_command::hornhighactivate, {GLFW_KEY_S, ""} }, + { user_command::whistleactivate, {GLFW_KEY_Z, ""} }, + { user_command::radiotoggle, {GLFW_KEY_R | keymodifier::control, ""} }, // radioenable // radiodisable - { user_command::radiochannelincrease, GLFW_KEY_EQUAL }, - { user_command::radiochanneldecrease, GLFW_KEY_MINUS }, + { user_command::radiochannelincrease, {GLFW_KEY_EQUAL, ""} }, + { user_command::radiochanneldecrease, {GLFW_KEY_MINUS, ""} }, // radiochannelset - { user_command::radiostopsend, GLFW_KEY_PAUSE | keymodifier::shift | keymodifier::control }, + { user_command::radiostopsend, {GLFW_KEY_PAUSE | keymodifier::shift | keymodifier::control, ""} }, // radiostopenable // radiostopdisable - { user_command::radiostoptest, GLFW_KEY_R | keymodifier::shift | keymodifier::control }, - { user_command::radiocall3send, GLFW_KEY_BACKSPACE }, + { user_command::radiostoptest, {GLFW_KEY_R | keymodifier::shift | keymodifier::control, ""} }, + { user_command::radiocall3send, {GLFW_KEY_BACKSPACE, ""} }, // radiovolumeincrease, // radiovolumedecrease, // radiovolumeset, - { user_command::cabchangeforward, GLFW_KEY_HOME }, - { user_command::cabchangebackward, GLFW_KEY_END }, + { user_command::cabchangeforward, {GLFW_KEY_HOME, ""} }, + { user_command::cabchangebackward, {GLFW_KEY_END, ""} }, // viewturn, // movehorizontal, // movehorizontalfast, // movevertical, // moveverticalfast, - { user_command::moveleft, GLFW_KEY_LEFT }, - { user_command::moveright, GLFW_KEY_RIGHT }, - { user_command::moveforward, GLFW_KEY_UP }, - { user_command::moveback, GLFW_KEY_DOWN }, - { user_command::moveup, GLFW_KEY_PAGE_UP }, - { user_command::movedown, GLFW_KEY_PAGE_DOWN }, - { user_command::nearestcarcouplingincrease, GLFW_KEY_INSERT }, - { user_command::nearestcarcouplingdisconnect, GLFW_KEY_DELETE }, - { user_command::nearestcarcoupleradapterattach, GLFW_KEY_INSERT | keymodifier::control }, - { user_command::nearestcarcoupleradapterremove, GLFW_KEY_DELETE | keymodifier::control }, - { user_command::occupiedcarcouplingdisconnect, GLFW_KEY_DELETE | keymodifier::shift }, - { user_command::doortoggleleft, GLFW_KEY_COMMA }, - { user_command::doortoggleright, GLFW_KEY_PERIOD }, - { user_command::doorpermitleft, GLFW_KEY_COMMA | keymodifier::shift }, - { user_command::doorpermitright, GLFW_KEY_PERIOD | keymodifier::shift }, - { user_command::doorpermitpresetactivatenext, GLFW_KEY_PERIOD | keymodifier::shift | keymodifier::control }, - { user_command::doorpermitpresetactivateprevious, GLFW_KEY_COMMA | keymodifier::shift | keymodifier::control }, + { user_command::moveleft, {GLFW_KEY_LEFT, "Move left"} }, + { user_command::moveright, {GLFW_KEY_RIGHT, "Move right"} }, + { user_command::moveforward, {GLFW_KEY_UP, "Move forwards"} }, + { user_command::moveback, {GLFW_KEY_DOWN, "Move backwards"} }, + { user_command::moveup, {GLFW_KEY_PAGE_UP, "Move up"} }, + { user_command::movedown, {GLFW_KEY_PAGE_DOWN, "Move down"} }, + { user_command::nearestcarcouplingincrease, {GLFW_KEY_INSERT, ""} }, + { user_command::nearestcarcouplingdisconnect, {GLFW_KEY_DELETE, ""} }, + { user_command::nearestcarcoupleradapterattach, {GLFW_KEY_INSERT | keymodifier::control, ""} }, + { user_command::nearestcarcoupleradapterremove, {GLFW_KEY_DELETE | keymodifier::control, ""} }, + { user_command::occupiedcarcouplingdisconnect, {GLFW_KEY_DELETE | keymodifier::shift, ""} }, + { user_command::doortoggleleft, {GLFW_KEY_COMMA, ""} }, + { user_command::doortoggleright, {GLFW_KEY_PERIOD, ""} }, + { user_command::doorpermitleft, {GLFW_KEY_COMMA | keymodifier::shift, ""} }, + { user_command::doorpermitright, {GLFW_KEY_PERIOD | keymodifier::shift, ""} }, + { user_command::doorpermitpresetactivatenext, {GLFW_KEY_PERIOD | keymodifier::shift | keymodifier::control, ""} }, + { user_command::doorpermitpresetactivateprevious, {GLFW_KEY_COMMA | keymodifier::shift | keymodifier::control, ""} }, // dooropenleft, // dooropenright, - { user_command::dooropenall, GLFW_KEY_SLASH | keymodifier::shift }, + { user_command::dooropenall, {GLFW_KEY_SLASH | keymodifier::shift, ""} }, // doorcloseleft, // doorcloseright, - { user_command::doorcloseall, GLFW_KEY_SLASH | keymodifier::control }, + { user_command::doorcloseall, {GLFW_KEY_SLASH | keymodifier::control, ""} }, // doorsteptoggle, - { user_command::doormodetoggle, GLFW_KEY_SLASH | keymodifier::shift | keymodifier::control }, + { user_command::doormodetoggle, {GLFW_KEY_SLASH | keymodifier::shift | keymodifier::control, ""} }, // mirrorstoggle, - { user_command::departureannounce, GLFW_KEY_SLASH }, - { user_command::doorlocktoggle, GLFW_KEY_S | keymodifier::control }, - { user_command::pantographcompressorvalvetoggle, GLFW_KEY_V | keymodifier::control }, + { user_command::departureannounce, {GLFW_KEY_SLASH, ""} }, + { user_command::doorlocktoggle, {GLFW_KEY_S | keymodifier::control, ""} }, + { user_command::pantographcompressorvalvetoggle, {GLFW_KEY_V | keymodifier::control, ""} }, // pantographcompressorvalveenable, // pantographcompressorvalvedisable, - { user_command::pantographcompressoractivate, GLFW_KEY_V | keymodifier::shift }, - { user_command::pantographtogglefront, GLFW_KEY_P }, - { user_command::pantographtogglerear, GLFW_KEY_O }, + { user_command::pantographcompressoractivate, {GLFW_KEY_V | keymodifier::shift, ""} }, + { user_command::pantographtogglefront, {GLFW_KEY_P, ""} }, + { user_command::pantographtogglerear, {GLFW_KEY_O, ""} }, // pantographraisefront, // pantographraiserear, // pantographlowerfront, // pantographlowerrear, - { user_command::pantographlowerall, GLFW_KEY_P | keymodifier::control }, - { user_command::pantographselectnext, GLFW_KEY_P | keymodifier::shift }, - { user_command::pantographselectprevious, GLFW_KEY_O | keymodifier::shift }, - { user_command::pantographtoggleselected, GLFW_KEY_O | keymodifier::shift | keymodifier::control }, + { user_command::pantographlowerall, {GLFW_KEY_P | keymodifier::control, ""} }, + { user_command::pantographselectnext, {GLFW_KEY_P | keymodifier::shift, ""} }, + { user_command::pantographselectprevious, {GLFW_KEY_O | keymodifier::shift, ""} }, + { user_command::pantographtoggleselected, {GLFW_KEY_O | keymodifier::shift | keymodifier::control, ""} }, // pantographraiseselected, // pantographlowerselected, // pantographvalvesupdate, // pantographvalvesoff, - { user_command::heatingtoggle, GLFW_KEY_H }, + { user_command::heatingtoggle, {GLFW_KEY_H, ""} }, // heatingenable, // heatingdisable, - { user_command::lightspresetactivatenext, GLFW_KEY_T | keymodifier::shift }, - { user_command::lightspresetactivateprevious, GLFW_KEY_T }, - { user_command::headlighttoggleleft, GLFW_KEY_Y }, + { user_command::lightspresetactivatenext, {GLFW_KEY_T | keymodifier::shift, ""} }, + { user_command::lightspresetactivateprevious, {GLFW_KEY_T, ""} }, + { user_command::headlighttoggleleft, {GLFW_KEY_Y, ""} }, // headlightenableleft, // headlightdisableleft, - { user_command::headlighttoggleright, GLFW_KEY_I }, + { user_command::headlighttoggleright, {GLFW_KEY_I, ""} }, // headlightenableright, // headlightdisableright, - { user_command::headlighttoggleupper, GLFW_KEY_U }, + { user_command::headlighttoggleupper, {GLFW_KEY_U, ""} }, // headlightenableupper, // headlightdisableupper, - { user_command::redmarkertoggleleft, GLFW_KEY_Y | keymodifier::shift }, + { user_command::redmarkertoggleleft, {GLFW_KEY_Y | keymodifier::shift, ""} }, // redmarkerenableleft, // redmarkerdisableleft, - { user_command::redmarkertoggleright, GLFW_KEY_I | keymodifier::shift }, + { user_command::redmarkertoggleright, {GLFW_KEY_I | keymodifier::shift, ""} }, // redmarkerenableright, // redmarkerdisableright, - { user_command::headlighttogglerearleft, GLFW_KEY_Y | keymodifier::control }, + { user_command::headlighttogglerearleft, {GLFW_KEY_Y | keymodifier::control, ""} }, // headlightenablerearleft // headlightdisablerearleft - { user_command::headlighttogglerearright, GLFW_KEY_I | keymodifier::control }, + { user_command::headlighttogglerearright, {GLFW_KEY_I | keymodifier::control, ""} }, // headlightenablerearright // headlightdisablerearright - { user_command::headlighttogglerearupper, GLFW_KEY_U | keymodifier::control }, + { user_command::headlighttogglerearupper, {GLFW_KEY_U | keymodifier::control, ""} }, // headlightenablerearupper // headlightdisablerearupper - { user_command::redmarkertogglerearleft, GLFW_KEY_Y | keymodifier::control | keymodifier::shift }, + { user_command::redmarkertogglerearleft, {GLFW_KEY_Y | keymodifier::control | keymodifier::shift, ""} }, // redmarkerenablerearleft // redmarkerdisablerearleft - { user_command::redmarkertogglerearright, GLFW_KEY_I | keymodifier::control | keymodifier::shift }, + { user_command::redmarkertogglerearright, {GLFW_KEY_I | keymodifier::control | keymodifier::shift, ""} }, // redmarkerenablerearright // redmarkerdisablerearright - { user_command::redmarkerstoggle, GLFW_KEY_E | keymodifier::shift }, - { user_command::endsignalstoggle, GLFW_KEY_E }, - { user_command::headlightsdimtoggle, GLFW_KEY_L | keymodifier::control }, + { user_command::redmarkerstoggle, {GLFW_KEY_E | keymodifier::shift, ""} }, + { user_command::endsignalstoggle, {GLFW_KEY_E, ""} }, + { user_command::headlightsdimtoggle, {GLFW_KEY_L | keymodifier::control, ""} }, // headlightsdimenable, // headlightsdimdisable, - { user_command::motorconnectorsopen, GLFW_KEY_L }, + { user_command::motorconnectorsopen, {GLFW_KEY_L, ""} }, // motorconnectorsclose, - { user_command::motordisconnect, GLFW_KEY_E | keymodifier::control }, - { user_command::interiorlighttoggle, GLFW_KEY_APOSTROPHE }, + { user_command::motordisconnect, {GLFW_KEY_E | keymodifier::control, ""} }, + { user_command::interiorlighttoggle, {GLFW_KEY_APOSTROPHE, ""} }, // interiorlightenable, // interiorlightdisable, - { user_command::interiorlightdimtoggle, GLFW_KEY_APOSTROPHE | keymodifier::control }, + { user_command::interiorlightdimtoggle, {GLFW_KEY_APOSTROPHE | keymodifier::control, ""} }, // interiorlightdimenable, // interiorlightdimdisable, // compartmentlightstoggle, // compartmentlightsenable, // compartmentlightsdisable, - { user_command::instrumentlighttoggle, GLFW_KEY_SEMICOLON }, + { user_command::instrumentlighttoggle, {GLFW_KEY_SEMICOLON, ""} }, // instrumentlightenable, // instrumentlightdisable, - { user_command::dashboardlighttoggle, GLFW_KEY_SEMICOLON | keymodifier::shift }, + { user_command::dashboardlighttoggle, {GLFW_KEY_SEMICOLON | keymodifier::shift, ""} }, // dashboardlightenable // dashboardlightdisable - { user_command::timetablelighttoggle, GLFW_KEY_APOSTROPHE | keymodifier::shift }, + { user_command::timetablelighttoggle, {GLFW_KEY_APOSTROPHE | keymodifier::shift, ""} }, // timetablelightenable // timetablelightdisable - { user_command::generictoggle0, GLFW_KEY_0 }, - { user_command::generictoggle1, GLFW_KEY_1 }, - { user_command::generictoggle2, GLFW_KEY_2 }, - { user_command::generictoggle3, GLFW_KEY_3 }, - { user_command::generictoggle4, GLFW_KEY_4 }, - { user_command::generictoggle5, GLFW_KEY_5 }, - { user_command::generictoggle6, GLFW_KEY_6 }, - { user_command::generictoggle7, GLFW_KEY_7 }, - { user_command::generictoggle8, GLFW_KEY_8 }, - { user_command::generictoggle9, GLFW_KEY_9 }, - { user_command::batterytoggle, GLFW_KEY_J }, + { user_command::generictoggle0, {GLFW_KEY_0, ""} }, + { user_command::generictoggle1, {GLFW_KEY_1, ""} }, + { user_command::generictoggle2, {GLFW_KEY_2, ""} }, + { user_command::generictoggle3, {GLFW_KEY_3, ""} }, + { user_command::generictoggle4, {GLFW_KEY_4, ""} }, + { user_command::generictoggle5, {GLFW_KEY_5, ""} }, + { user_command::generictoggle6, {GLFW_KEY_6, ""} }, + { user_command::generictoggle7, {GLFW_KEY_7, ""} }, + { user_command::generictoggle8, {GLFW_KEY_8, ""} }, + { user_command::generictoggle9, {GLFW_KEY_9, ""} }, + { user_command::batterytoggle, {GLFW_KEY_J, ""} }, // batteryenable, // batterydisable, // cabactivationtoggle, // cabactivationenable, // cabactivationdisable, - { user_command::motorblowerstogglefront, GLFW_KEY_N | keymodifier::shift }, - { user_command::motorblowerstogglerear, GLFW_KEY_M | keymodifier::shift }, - { user_command::motorblowersdisableall, GLFW_KEY_M | keymodifier::control }, + { user_command::motorblowerstogglefront, {GLFW_KEY_N | keymodifier::shift, ""} }, + { user_command::motorblowerstogglerear, {GLFW_KEY_M | keymodifier::shift, ""} }, + { user_command::motorblowersdisableall, {GLFW_KEY_M | keymodifier::control, ""} }, // coolingfanstoggle, // tempomattoggle, // springbraketoggle, @@ -308,14 +308,14 @@ driverkeyboard_input::default_bindings() { // speedcontrolbutton8, // speedcontrolbutton9, // admin_timejump, - { user_command::timejumplarge, GLFW_KEY_F1 | keymodifier::control }, - { user_command::timejumpsmall, GLFW_KEY_F1 | keymodifier::shift }, + { user_command::timejumplarge, {GLFW_KEY_F1 | keymodifier::control, "Big time jump (Debug Mode only)"} }, + { user_command::timejumpsmall, {GLFW_KEY_F1 | keymodifier::shift, "Small time jump (Debug Mode only)"} }, // admin_vehiclemove, - { user_command::vehiclemoveforwards, GLFW_KEY_LEFT_BRACKET | keymodifier::control }, - { user_command::vehiclemovebackwards, GLFW_KEY_RIGHT_BRACKET | keymodifier::control }, - { user_command::vehicleboost, GLFW_KEY_TAB | keymodifier::control }, - { user_command::debugtoggle, GLFW_KEY_F12 | keymodifier::control | keymodifier::shift }, - { user_command::pausetoggle, GLFW_KEY_ESCAPE } + { user_command::vehiclemoveforwards, {GLFW_KEY_LEFT_BRACKET | keymodifier::control, "Move the train forwards (Debug Mode only)"} }, + { user_command::vehiclemovebackwards, {GLFW_KEY_RIGHT_BRACKET | keymodifier::control, "Move the train backwards (Debug Mode only)"} }, + { user_command::vehicleboost, {GLFW_KEY_TAB | keymodifier::control, "Boost the train (Debug Mode only)"} }, + { user_command::debugtoggle, {GLFW_KEY_F12 | keymodifier::control | keymodifier::shift, "Toggle Debug Mode"} }, + { user_command::pausetoggle, {GLFW_KEY_ESCAPE, "Pause the game"} } }; } diff --git a/editorkeyboardinput.cpp b/editorkeyboardinput.cpp index 139e2f53..154af92b 100644 --- a/editorkeyboardinput.cpp +++ b/editorkeyboardinput.cpp @@ -25,12 +25,12 @@ void editorkeyboard_input::default_bindings() { m_bindingsetups = { - { user_command::moveleft, GLFW_KEY_LEFT }, - { user_command::moveright, GLFW_KEY_RIGHT }, - { user_command::moveforward, GLFW_KEY_UP }, - { user_command::moveback, GLFW_KEY_DOWN }, - { user_command::moveup, GLFW_KEY_PAGE_UP }, - { user_command::movedown, GLFW_KEY_PAGE_DOWN }, + { user_command::moveleft, {GLFW_KEY_LEFT, "Move left"} }, + { user_command::moveright, {GLFW_KEY_RIGHT, "Move right"} }, + { user_command::moveforward, {GLFW_KEY_UP, "Move forwards"} }, + { user_command::moveback, {GLFW_KEY_DOWN, "Move backwards"} }, + { user_command::moveup, {GLFW_KEY_PAGE_UP, "Move up"} }, + { user_command::movedown, {GLFW_KEY_PAGE_DOWN, "Move down"} }, }; } diff --git a/keyboardinput.cpp b/keyboardinput.cpp index 0b74c9fa..70445d18 100644 --- a/keyboardinput.cpp +++ b/keyboardinput.cpp @@ -122,6 +122,7 @@ bool keyboard_input::recall_bindings() { cParser bindingparser( "eu07_input-keyboard.ini", cParser::buffer_FILE ); + bindingparser.skipComments = false; if( false == bindingparser.ok() ) { return false; } @@ -147,6 +148,7 @@ keyboard_input::recall_bindings() { std::string bindingentry; bindingparser >> bindingentry; cParser entryparser( bindingentry ); + entryparser.skipComments = false; if( true == entryparser.getTokens( 1, true, "\n\r\t " ) ) { std::string commandname; @@ -158,32 +160,48 @@ keyboard_input::recall_bindings() { WriteLog( "Keyboard binding defined for unknown command, \"" + commandname + "\"" ); } else { - int binding{ 0 }; + int keycode = 0; + std::string description = ""; + bool descriptionStarted = false; while( entryparser.getTokens( 1, true, "\n\r\t " ) ) { std::string bindingkeyname; entryparser >> bindingkeyname; - if( bindingkeyname == "shift" ) { binding |= keymodifier::shift; } - else if( bindingkeyname == "ctrl" ) { binding |= keymodifier::control; } - else if( bindingkeyname == "none" ) { binding = 0; } - else { - // regular key, convert it to glfw key code - auto const keylookup = nametokeymap.find( bindingkeyname ); - if( keylookup == nametokeymap.end() ) { - - WriteLog( "Keyboard binding included unrecognized key, \"" + bindingkeyname + "\"" ); + // Parse command description, starting with "//". + // TODO: At some point, rewind this and add translation keys instead for multilingual support. + // This can't be done now as by default this would destroy all command descriptions, + // which are used by Starter. + // Do this when szczawik's Starter becomes deprecated or implements command descriptions in some other way. + WriteLog("got: \"" + bindingkeyname + "\""); + if (descriptionStarted) { + if (description.size() > 0) { + description += " "; } + description += bindingkeyname; + } else if (bindingkeyname == "//") { + descriptionStarted = true; + } else { + if( bindingkeyname == "shift" ) { keycode |= keymodifier::shift; } + else if( bindingkeyname == "ctrl" ) { keycode |= keymodifier::control; } + else if( bindingkeyname == "none" ) { keycode = 0; } else { - // replace any existing binding, preserve modifiers - // (protection from cases where there's more than one key listed in the entry) - binding = keylookup->second | ( binding & 0xffff0000 ); + // regular key, convert it to glfw key code + auto const keylookup = nametokeymap.find( bindingkeyname ); + if( keylookup == nametokeymap.end() ) { + + WriteLog( "Keyboard binding included unrecognized key, \"" + bindingkeyname + "\"" ); + } + else { + // replace any existing binding, preserve modifiers + // (protection from cases where there's more than one key listed in the entry) + keycode = keylookup->second | ( keycode & 0xffff0000 ); + } } } - if( ( binding & 0xffff ) != 0 ) { - m_bindingsetups.insert_or_assign(lookup->second, binding); - } + std::tuple binding{keycode, description}; + m_bindingsetups.insert_or_assign(lookup->second, binding); } } } @@ -202,20 +220,25 @@ void keyboard_input::dump_bindings() return; } - for (const std::pair &binding : m_bindingsetups) { + for (const std::pair> &binding : m_bindingsetups) { stream << simulation::Commands_descriptions[static_cast(binding.first)].name << ' '; - auto it = keytonamemap.find(binding.second & 0xFFFF); + int keycode = std::get(binding.second); + auto it = keytonamemap.find(keycode & 0xFFFF); if (it != keytonamemap.end()) { - if (binding.second & keymodifier::control) + if (keycode & keymodifier::control) stream << "ctrl "; - if (binding.second & keymodifier::shift) + if (keycode & keymodifier::shift) stream << "shift "; - stream << it->second << "\r\n"; + stream << it->second; } else { - stream << "none\r\n"; + stream << "none"; } + + std::string description = std::get(binding.second); + if (description.size() > 0) + stream << " // " << description << "\r\n"; } } @@ -295,7 +318,7 @@ keyboard_input::bind() { for( auto const &bindingsetup : m_bindingsetups ) { - m_bindings[ bindingsetup.second ] = bindingsetup.first; + m_bindings[ std::get(bindingsetup.second) ] = bindingsetup.first; } // cache movement key bindings diff --git a/keyboardinput.h b/keyboardinput.h index 0fc92d80..efb4921f 100644 --- a/keyboardinput.h +++ b/keyboardinput.h @@ -31,7 +31,7 @@ class keyboard_input { public: // types - using bindingsetup_sequence = std::map; + using bindingsetup_sequence = std::map>; enum keymodifier : int { diff --git a/launcher/keymapper.cpp b/launcher/keymapper.cpp index b952e739..e3209e1c 100644 --- a/launcher/keymapper.cpp +++ b/launcher/keymapper.cpp @@ -21,8 +21,15 @@ bool ui::keymapper_panel::key(int key) key |= keyboard_input::keymodifier::shift; if (Global.ctrlState) key |= keyboard_input::keymodifier::control; + // F10 unbinds the key. + if (it->second.compare("f10") == 0) + key = 0; - keyboard.bindings().insert_or_assign(bind_active, key); + // Replace the binding with a new key. + auto it2 = keyboard.bindings().find(bind_active); + if (it2 != keyboard.bindings().end()) { + it2->second = std::tuple(key, std::get(it2->second)); + } bind_active = user_command::none; keyboard.bind(); @@ -45,18 +52,26 @@ void ui::keymapper_panel::render() if (ImGui::Button(STR_C("Save"))) keyboard.dump_bindings(); - for (const std::pair &binding : keyboard.bindings()) { + for (const std::pair> &binding : keyboard.bindings()) { + // Binding ID ImGui::AlignTextToFramePadding(); ImGui::Text(simulation::Commands_descriptions[static_cast(binding.first)].name.c_str()); + // Binding description + std::string description = std::get(binding.second); + ImGui::SameLine(260); + ImGui::Text((description.size() > 0 ? description : "(No description)").c_str()); + + // Binding key button + int keycode = std::get(binding.second); std::string label; - if (binding.second & keyboard_input::keymodifier::control) + if (keycode & keyboard_input::keymodifier::control) label += "ctrl + "; - if (binding.second & keyboard_input::keymodifier::shift) + if (keycode & keyboard_input::keymodifier::shift) label += "shift + "; - auto it = keyboard.keytonamemap.find(binding.second & 0xFFFF); + auto it = keyboard.keytonamemap.find(keycode & 0xFFFF); if (it != keyboard.keytonamemap.end()) label += it->second; @@ -64,7 +79,7 @@ void ui::keymapper_panel::render() bool styles_pushed = false; if (bind_active == binding.first) { - label = "?"; + label = "Press key (F10 to unbind)"; ImVec4 active_col = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive); ImGui::PushStyleColor(ImGuiCol_Button, active_col); styles_pushed = true; diff --git a/parser.cpp b/parser.cpp index 91b12c7d..f854b8e2 100644 --- a/parser.cpp +++ b/parser.cpp @@ -202,7 +202,7 @@ std::string cParser::readToken( bool ToLower, const char *Break ) { token += c; if( findQuotes( token ) ) // do glue together words enclosed in quotes continue; - if( trimComments( token ) ) // don't glue together words separated with comment + if( skipComments && trimComments( token ) ) // don't glue together words separated with comment break; } if( c == '\n' ) { diff --git a/parser.h b/parser.h index c96e4a29..4fd5fd2e 100644 --- a/parser.h +++ b/parser.h @@ -92,6 +92,7 @@ class cParser //: public std::stringstream int LineMain() const; bool expandIncludes = true; bool allowRandomIncludes = false; + bool skipComments = true; private: // methods: From 4d00977fcaedd2db54d3efbb556448c254b2ddf5 Mon Sep 17 00:00:00 2001 From: jakubg1 <24206305+jakubg1@users.noreply.github.com> Date: Sun, 3 Mar 2024 03:48:36 +0100 Subject: [PATCH 03/88] Update STBI from v2.22 to v2.29 This fixes crashes when trying to load corrupted .bmp files. --- stb/stb_image.h | 1193 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 814 insertions(+), 379 deletions(-) diff --git a/stb/stb_image.h b/stb/stb_image.h index e23f4a17..a632d543 100644 --- a/stb/stb_image.h +++ b/stb/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.22 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.29 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,9 +48,16 @@ LICENSE RECENT REVISION HISTORY: + 2.29 (2023-05-xx) optimizations + 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning 2.22 (2019-03-04) gif fixes, fix warnings 2.21 (2019-02-25) fix typo in comment - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings @@ -85,34 +92,42 @@ RECENT REVISION HISTORY: Jeremy Sawicki (handle all ImageNet JPGs) Optimizations & bugfixes Mikhail Morozov (1-bit BMP) Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine + Arseny Kapoulkine Simon Breuss (16-bit PNM) John-Mark Allen Carmelo J Fdez-Aguera Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan - Dave Moore Roy Eltham Hayaki Saito Nathan Reed - Won Chun Luke Graham Johan Duparc Nick Verigakis - the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar - Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex - Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 - Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw - Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus - Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo - Christian Floisand Kevin Schmidt JR Smith github:darealshinji - Blazej Dariusz Roszkowski github:Michaelangel007 + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + Jacko Dirks + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. */ #ifndef STBI_INCLUDE_STB_IMAGE_H #define STBI_INCLUDE_STB_IMAGE_H -#define STBI_ONLY_JPEG -#define STBI_ONLY_BMP - // DOCUMENTATION // // Limitations: @@ -127,7 +142,7 @@ RECENT REVISION HISTORY: // // ... x = width, y = height, n = # 8-bit components per pixel ... // // ... replace '0' with '1'..'4' to force that many components per pixel // // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) +// stbi_image_free(data); // // Standard parameters: // int *x -- outputs image width in pixels @@ -166,6 +181,32 @@ RECENT REVISION HISTORY: // // Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. // +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// // =========================================================================== // // UNICODE: @@ -271,11 +312,10 @@ RECENT REVISION HISTORY: // // iPhone PNG support: // -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). // // Call stbi_set_unpremultiply_on_load(1) as well to force a divide per // pixel to remove any premultiplied alpha *only* if the image file explicitly @@ -317,7 +357,14 @@ RECENT REVISION HISTORY: // - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still // want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB // - +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. #ifndef STBI_NO_STDIO #include @@ -436,7 +483,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f); // get a VERY brief reason for failure -// NOT THREADSAFE +// on most compilers (and ALL modern mainstream compilers) this is threadsafe STBIDEF const char *stbi_failure_reason (void); // free the loaded image -- this is just free() @@ -469,6 +516,13 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); // flip the image vertically, so the first pixel in the output array is the bottom left STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + // ZLIB client - used by PNG, available for other purposes STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); @@ -565,8 +619,25 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define stbi_inline __forceinline #endif +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif -#ifdef _MSC_VER + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#if defined(_MSC_VER) || defined(__SYMBIAN32__) typedef unsigned short stbi__uint16; typedef signed short stbi__int16; typedef unsigned int stbi__uint32; @@ -595,7 +666,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #ifdef STBI_HAS_LROTL #define stbi_lrot(x,y) _lrotl(x,y) #else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) #endif #if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) @@ -709,14 +780,21 @@ static int stbi__sse2_available(void) #ifdef STBI_NEON #include -// assume GCC or Clang on ARM targets +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #endif +#endif #ifndef STBI_SIMD_ALIGN #define STBI_SIMD_ALIGN(type, name) type name #endif +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + /////////////////////////////////////////////// // // stbi__context struct and start_xxx functions @@ -734,6 +812,7 @@ typedef struct int read_from_callbacks; int buflen; stbi_uc buffer_start[128]; + int callback_already_read; stbi_uc *img_buffer, *img_buffer_end; stbi_uc *img_buffer_original, *img_buffer_original_end; @@ -747,6 +826,7 @@ static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) { s->io.read = NULL; s->read_from_callbacks = 0; + s->callback_already_read = 0; s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; } @@ -758,7 +838,8 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void * s->io_user_data = user; s->buflen = sizeof(s->buffer_start); s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); s->img_buffer_original_end = s->img_buffer_end; } @@ -772,12 +853,17 @@ static int stbi__stdio_read(void *user, char *data, int size) static void stbi__stdio_skip(void *user, int n) { + int ch; fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } } static int stbi__stdio_eof(void *user) { - return feof((FILE*) user); + return feof((FILE*) user) || ferror((FILE *) user); } static stbi_io_callbacks stbi__stdio_callbacks = @@ -873,21 +959,27 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__pnm_test(stbi__context *s); static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); #endif -// this is not threadsafe -static const char *stbi__g_failure_reason; +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; STBIDEF const char *stbi_failure_reason(void) { return stbi__g_failure_reason; } +#ifndef STBI_NO_FAILURE_STRINGS static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } +#endif static void *stbi__malloc(size_t size) { @@ -926,11 +1018,13 @@ static int stbi__mul2sizes_valid(int a, int b) return a <= INT_MAX/b; } +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow static int stbi__mad2sizes_valid(int a, int b, int add) { return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); } +#endif // returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow static int stbi__mad3sizes_valid(int a, int b, int c, int add) @@ -940,7 +1034,7 @@ static int stbi__mad3sizes_valid(int a, int b, int c, int add) } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && @@ -948,12 +1042,14 @@ static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) } #endif +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) { if (!stbi__mad2sizes_valid(a, b, add)) return NULL; return stbi__malloc(a*b + add); } +#endif static void *stbi__malloc_mad3(int a, int b, int c, int add) { @@ -961,7 +1057,7 @@ static void *stbi__malloc_mad3(int a, int b, int c, int add) return stbi__malloc(a*b*c + add); } -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; @@ -969,6 +1065,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) } #endif +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; +} + +// returns 1 if the product of two ints fits in a signed short, 0 on overflow. +static int stbi__mul2shorts_valid(int a, int b) +{ + if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char @@ -997,13 +1110,29 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif -static int stbi__vertically_flip_on_load = 0; +static int stbi__vertically_flip_on_load_global = 0; STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { - stbi__vertically_flip_on_load = flag_true_if_should_flip; + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; } +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields @@ -1011,9 +1140,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order ri->num_channels = 0; - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) #ifndef STBI_NO_PNG if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); #endif @@ -1025,10 +1153,19 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re #endif #ifndef STBI_NO_PSD if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); #endif #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif #ifndef STBI_NO_PNM if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); #endif @@ -1113,8 +1250,8 @@ static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int byt stbi_uc *bytes = (stbi_uc *)image; for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; } } #endif @@ -1127,8 +1264,10 @@ static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, if (result == NULL) return NULL; + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + if (ri.bits_per_channel != 8) { - STBI_ASSERT(ri.bits_per_channel == 16); result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 8; } @@ -1151,8 +1290,10 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, if (result == NULL) return NULL; + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + if (ri.bits_per_channel != 16) { - STBI_ASSERT(ri.bits_per_channel == 8); result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 16; } @@ -1180,12 +1321,12 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in #ifndef STBI_NO_STDIO -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); #endif -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); @@ -1195,16 +1336,16 @@ STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wch static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) - return 0; - - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) return 0; -#if _MSC_VER >= 1400 + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) f = 0; #else @@ -1302,15 +1443,15 @@ STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *u STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - + stbi__context s; + stbi__start_mem(&s,buffer,len); + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); } - return result; + return result; } #endif @@ -1455,6 +1596,7 @@ enum static void stbi__refill_buffer(stbi__context *s) { int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); if (n == 0) { // at end of file, treat same as if from memory, but need to handle case // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file @@ -1479,6 +1621,9 @@ stbi_inline static stbi_uc stbi__get8(stbi__context *s) return 0; } +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else stbi_inline static int stbi__at_eof(stbi__context *s) { if (s->io.read) { @@ -1490,9 +1635,14 @@ stbi_inline static int stbi__at_eof(stbi__context *s) return s->img_buffer >= s->img_buffer_end; } +#endif +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else static void stbi__skip(stbi__context *s, int n) { + if (n == 0) return; // already there! if (n < 0) { s->img_buffer = s->img_buffer_end; return; @@ -1507,7 +1657,11 @@ static void stbi__skip(stbi__context *s, int n) } s->img_buffer += n; } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { if (s->io.read) { @@ -1531,18 +1685,27 @@ static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) } else return 0; } +#endif +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else static int stbi__get16be(stbi__context *s) { int z = stbi__get8(s); return (z << 8) + stbi__get8(s); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } +#endif #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing @@ -1558,13 +1721,16 @@ static int stbi__get16le(stbi__context *s) static stbi__uint32 stbi__get32le(stbi__context *s) { stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; } #endif #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else ////////////////////////////////////////////////////////////////////////////// // // generic converter from built-in img_n to req_comp @@ -1580,7 +1746,11 @@ static stbi_uc stbi__compute_y(int r, int g, int b) { return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1616,7 +1786,7 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } @@ -1624,12 +1794,20 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r STBI_FREE(data); return good; } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1665,7 +1843,7 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } @@ -1673,6 +1851,7 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r STBI_FREE(data); return good; } +#endif #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) @@ -1825,9 +2004,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) int i,j,k=0; unsigned int code; // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } h->size[k] = 0; // compute actual symbols (from jpeg spec) @@ -1952,6 +2134,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) // convert the huffman code to the symbol id c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); // convert the id to a symbol @@ -1970,14 +2154,14 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) unsigned int k; int sgn; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); + return k + (stbi__jbias[n] & (sgn - 1)); } // get some unsigned bits @@ -1985,6 +2169,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { unsigned int k; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing k = stbi_lrot(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; @@ -1996,6 +2181,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) { unsigned int k; if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing k = j->code_buffer; j->code_buffer <<= 1; --j->code_bits; @@ -2027,14 +2213,16 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); // 0 all the ac values now so we can do it 32-bits at a time memset(data,0,64*sizeof(data[0])); diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); data[0] = (short) (dc * dequant[0]); // decode AC components, see JPEG spec @@ -2048,6 +2236,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; // decode into unzigzag'd location @@ -2084,11 +2273,14 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ // first scan for DC coefficient, must be first memset(data,0,64*sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * (1 << j->succ_low)); } else { // refinement scan for DC coefficient if (stbi__jpeg_get_bit(j)) @@ -2122,10 +2314,11 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); + data[zig] = (short) ((r >> 8) * (1 << shift)); } else { int rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); @@ -2143,7 +2336,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ } else { k += r; zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); } } } while (k <= j->spec_end); @@ -2942,6 +3135,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m) sizes[i] = stbi__get8(z->s); n += sizes[i]; } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! L -= 17; if (tc == 0) { if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; @@ -3074,6 +3268,8 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); c = stbi__get8(s); if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); s->img_n = c; @@ -3105,6 +3301,13 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; } + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + // compute interleaved mcu info z->img_h_max = h_max; z->img_v_max = v_max; @@ -3182,6 +3385,28 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) return 1; } +static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +{ + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + stbi_uc x = stbi__get8(j->s); + while (x == 0xff) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + // decode image to YCbCr format static int stbi__decode_jpeg_image(stbi__jpeg *j) { @@ -3198,25 +3423,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) if (!stbi__process_scan_header(j)) return 0; if (!stbi__parse_entropy_coded_data(j)) return 0; if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } + j->marker = stbi__skip_jpeg_junk_at_end(j); // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); stbi__uint32 NL = stbi__get16be(j->s); if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); } else { - if (!stbi__process_marker(j, m)) return 0; + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); } - m = stbi__get_marker(j); } if (j->progressive) stbi__jpeg_finish(j); @@ -3660,6 +3882,10 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp else decode_n = z->s->img_n; + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + // resample and color-convert { int k; @@ -3802,6 +4028,8 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re { unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); STBI_NOTUSED(ri); j->s = s; stbi__setup_jpeg(j); @@ -3814,6 +4042,8 @@ static int stbi__jpeg_test(stbi__context *s) { int r; stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); @@ -3838,6 +4068,8 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { int result; stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); STBI_FREE(j); @@ -3857,6 +4089,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) // fast-way is faster to check than jpeg huffman, but slow way is slower #define STBI__ZFAST_BITS 9 // accelerate all cases in default tables #define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet // zlib-style huffman encoding // (jpegs packs from left, zlib from right, so can't share code) @@ -3866,8 +4099,8 @@ typedef struct stbi__uint16 firstcode[16]; int maxcode[17]; stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; } stbi__zhuffman; stbi_inline static int stbi__bitreverse16(int n) @@ -3944,6 +4177,7 @@ typedef struct { stbi_uc *zbuffer, *zbuffer_end; int num_bits; + int hit_zeof_once; stbi__uint32 code_buffer; char *zout; @@ -3954,16 +4188,23 @@ typedef struct stbi__zhuffman z_length, z_distance; } stbi__zbuf; +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) { - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; + return stbi__zeof(z) ? 0 : *z->zbuffer++; } static void stbi__fill_bits(stbi__zbuf *z) { do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); @@ -3988,10 +4229,11 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) for (s=STBI__ZFAST_BITS+1; ; ++s) if (k < z->maxcode[s]) break; - if (s == 16) return -1; // invalid code! + if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. a->code_buffer >>= s; a->num_bits -= s; return z->value[b]; @@ -4000,7 +4242,23 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { int b,s; - if (a->num_bits < 16) stbi__fill_bits(a); + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + if (!a->hit_zeof_once) { + // This is the first time we hit eof, insert 16 extra padding btis + // to allow us to keep going; if we actually consume any of them + // though, that is invalid data. This is caught later. + a->hit_zeof_once = 1; + a->num_bits += 16; // add 16 implicit zero bits + } else { + // We already inserted our extra 16 padding bits and are again + // out, this stream is actually prematurely terminated. + return -1; + } + } else { + stbi__fill_bits(a); + } + } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { s = b >> 9; @@ -4014,13 +4272,16 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes { char *q; - int cur, limit, old_limit; + unsigned int cur, limit, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = old_limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); limit *= 2; + } q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); STBI_NOTUSED(old_limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); @@ -4061,17 +4322,25 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) int len,dist; if (z == 256) { a->zout = zout; + if (a->hit_zeof_once && a->num_bits < 16) { + // The first time we hit zeof, we inserted 16 extra zero bits into our bit + // buffer so the decoder can just do its speculative decoding. But if we + // actually consumed any of those bits (which is the case when num_bits < 16), + // the stream actually read past the end so it is malformed. + return stbi__err("unexpected end","Corrupt PNG"); + } return 1; } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data z -= 257; len = stbi__zlength_base[z]; if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data dist = stbi__zdist_base[z]; if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { + if (len > a->zout_end - zout) { if (!stbi__zexpand(a, zout, len)) return 0; zout = a->zout; } @@ -4118,11 +4387,12 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) c = stbi__zreceive(a,2)+3; if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); fill = lencodes[n-1]; - } else if (c == 17) + } else if (c == 17) { c = stbi__zreceive(a,3)+3; - else { - STBI_ASSERT(c == 18); + } else if (c == 18) { c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); } if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); memset(lencodes+n, fill, c); @@ -4148,7 +4418,7 @@ static int stbi__parse_uncompressed_block(stbi__zbuf *a) a->code_buffer >>= 8; a->num_bits -= 8; } - STBI_ASSERT(a->num_bits == 0); + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); // now fill header the normal way while (k < 4) header[k++] = stbi__zget8(a); @@ -4170,6 +4440,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) int cm = cmf & 15; /* int cinfo = cmf >> 4; */ int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png @@ -4177,7 +4448,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) return 1; } -static const stbi_uc stbi__zdefault_length[288] = +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = { 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, @@ -4213,6 +4484,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) if (!stbi__parse_zlib_header(a)) return 0; a->num_bits = 0; a->code_buffer = 0; + a->hit_zeof_once = 0; do { final = stbi__zreceive(a,1); type = stbi__zreceive(a,2); @@ -4223,7 +4495,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) } else { if (type == 1) { // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; } else { if (!stbi__compute_huffman_codes(a)) return 0; @@ -4368,9 +4640,8 @@ enum { STBI__F_up=2, STBI__F_avg=3, STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first + // synthetic filter used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first }; static stbi_uc first_row_filter[5] = @@ -4379,29 +4650,56 @@ static stbi_uc first_row_filter[5] = STBI__F_sub, STBI__F_none, STBI__F_avg_first, - STBI__F_paeth_first + STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub }; static int stbi__paeth(int a, int b, int c) { - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; + // This formulation looks very different from the reference in the PNG spec, but is + // actually equivalent and has favorable data dependencies and admits straightforward + // generation of branch-free code, which helps performance significantly. + int thresh = c*3 - (a + b); + int lo = a < b ? a : b; + int hi = a < b ? b : a; + int t0 = (hi <= thresh) ? lo : c; + int t1 = (thresh <= lo) ? hi : t0; + return t1; } static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; +// adds an extra all-255 alpha channel +// dest == src is legal +// img_n must be 1 or 3 +static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) +{ + int i; + // must process data backwards since we allow dest==src + if (img_n == 1) { + for (i=x-1; i >= 0; --i) { + dest[i*2+1] = 255; + dest[i*2+0] = src[i]; + } + } else { + STBI_ASSERT(img_n == 3); + for (i=x-1; i >= 0; --i) { + dest[i*4+3] = 255; + dest[i*4+2] = src[i*3+2]; + dest[i*4+1] = src[i*3+1]; + dest[i*4+0] = src[i*3+0]; + } + } +} + // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { - int bytes = (depth == 16? 2 : 1); + int bytes = (depth == 16 ? 2 : 1); stbi__context *s = a->s; stbi__uint32 i,j,stride = x*out_n*bytes; stbi__uint32 img_len, img_width_bytes; + stbi_uc *filter_buf; + int all_ok = 1; int k; int img_n = s->img_n; // copy it into a local for later @@ -4413,8 +4711,11 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); + // note: error exits here don't need to clean up a->out individually, + // stbi__do_png always does on error. if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); + if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG"); img_len = (img_width_bytes + 1) * y; // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, @@ -4422,189 +4723,137 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r // so just check for raw_len < img_len always. if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + // Allocate two scan lines worth of filter workspace buffer. + filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0); + if (!filter_buf) return stbi__err("outofmem", "Out of memory"); + + // Filtering for low-bit-depth images + if (depth < 8) { + filter_bytes = 1; + width = img_width_bytes; + } + for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; + // cur/prior filter buffers alternate + stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes; + stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes; + stbi_uc *dest = a->out + stride*j; + int nk = width * filter_bytes; int filter = *raw++; - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; + // check filter type + if (filter > 4) { + all_ok = stbi__err("invalid filter","Corrupt PNG"); + break; } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } + // perform actual filtering + switch (filter) { + case STBI__F_none: + memcpy(cur, raw, nk); + break; + case STBI__F_sub: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); + break; + case STBI__F_up: + for (k = 0; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); + break; + case STBI__F_avg: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); + break; + case STBI__F_paeth: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes])); + break; + case STBI__F_avg_first: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); + break; } - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } + raw += nk; - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + // expand decoded bits in cur to dest, also adding an extra alpha channel if desired + if (depth < 8) { stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + stbi_uc *in = cur; + stbi_uc *out = dest; + stbi_uc inb = 0; + stbi__uint32 nsmp = x*img_n; - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - + // expand bits to bytes first if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); + for (i=0; i < nsmp; ++i) { + if ((i & 1) == 0) inb = *in++; + *out++ = scale * (inb >> 4); + inb <<= 4; } - if (k > 0) *cur++ = scale * ((*in >> 4) ); } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); + for (i=0; i < nsmp; ++i) { + if ((i & 3) == 0) inb = *in++; + *out++ = scale * (inb >> 6); + inb <<= 2; } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); + } else { + STBI_ASSERT(depth == 1); + for (i=0; i < nsmp; ++i) { + if ((i & 7) == 0) inb = *in++; + *out++ = scale * (inb >> 7); + inb <<= 1; } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; + + // insert alpha=255 values if desired + if (img_n != out_n) + stbi__create_png_alpha_expand8(dest, dest, x, img_n); + } else if (depth == 8) { + if (img_n == out_n) + memcpy(dest, cur, x*img_n); + else + stbi__create_png_alpha_expand8(dest, cur, x, img_n); + } else if (depth == 16) { + // convert the image data from big-endian to platform-native + stbi__uint16 *dest16 = (stbi__uint16*)dest; + stbi__uint32 nsmp = x*img_n; + + if (img_n == out_n) { + for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) + *dest16 = (cur[0] << 8) | cur[1]; + } else { + STBI_ASSERT(img_n+1 == out_n); if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; + for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = 0xffff; } } else { STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; + for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = (cur[2] << 8) | cur[3]; + dest16[2] = (cur[4] << 8) | cur[5]; + dest16[3] = 0xffff; } } } } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } } + STBI_FREE(filter_buf); + if (!all_ok) return 0; + return 1; } @@ -4619,6 +4868,7 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3 // de-interlacing final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; @@ -4739,19 +4989,46 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int return 1; } -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) { - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; } STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) { - stbi__de_iphone_flag = flag_true_if_should_convert; + stbi__de_iphone_flag_global = flag_true_if_should_convert; } +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + static void stbi__de_iphone(stbi__png *z) { stbi__context *s = z->s; @@ -4826,8 +5103,10 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); first = 0; if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); @@ -4839,14 +5118,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!pal_img_n) { s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; } else { // if paletted, then pal_n is our final components, and // img_n is # components to decompress/filter. s->img_n = 1; if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS } + // even with SCAN_header, have to scan to see if we have a tRNS break; } @@ -4878,6 +5156,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } if (z->depth == 16) { for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is } else { @@ -4890,7 +5170,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) case STBI__PNG_TYPE('I','D','A','T'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { stbi__uint32 idata_limit_old = idata_limit; @@ -4944,6 +5230,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) ++s->img_n; } STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); return 1; } @@ -4974,10 +5262,12 @@ static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, st void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) + if (p->depth <= 8) ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; else - ri->bits_per_channel = p->depth; + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { @@ -5082,7 +5372,7 @@ static int stbi__high_bit(unsigned int z) if (z >= 0x00100) { n += 8; z >>= 8; } if (z >= 0x00010) { n += 4; z >>= 4; } if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1; z >>= 1; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } return n; } @@ -5113,7 +5403,7 @@ static int stbi__shiftsigned(unsigned int v, int shift, int bits) v <<= -shift; else v >>= shift; - STBI_ASSERT(v >= 0 && v < 256); + STBI_ASSERT(v < 256); v >>= (8-bits); STBI_ASSERT(bits >= 0 && bits <= 8); return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; @@ -5123,8 +5413,35 @@ typedef struct { int bpp, offset, hsz; unsigned int mr,mg,mb,ma, all_a; + int extra_read; } stbi__bmp_data; +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) { int hsz; @@ -5135,6 +5452,9 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->offset = stbi__get32le(s); info->hsz = hsz = stbi__get32le(s); info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { @@ -5149,6 +5469,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) if (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel stbi__get32le(s); // discard sizeof stbi__get32le(s); // discard hres stbi__get32le(s); // discard vres @@ -5163,21 +5485,12 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) } if (info->bpp == 16 || info->bpp == 32) { if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } + stbi__bmp_set_mask_defaults(info, compress); } else if (compress == 3) { info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); + info->extra_read += 12; // not documented, but generated by photoshop and handled by mspaint if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? @@ -5187,6 +5500,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) return stbi__errpuc("bad BMP", "bad BMP"); } } else { + // V4/V5 header int i; if (hsz != 108 && hsz != 124) return stbi__errpuc("bad BMP", "bad BMP"); @@ -5194,6 +5508,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); stbi__get32le(s); // discard color space for (i=0; i < 12; ++i) stbi__get32le(s); // discard color space parameters @@ -5226,6 +5542,9 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req flip_vertically = ((int) s->img_y) > 0; s->img_y = abs((int) s->img_y); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + mr = info.mr; mg = info.mg; mb = info.mb; @@ -5234,13 +5553,35 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz == 12) { if (info.bpp < 24) - psize = (info.offset - 14 - 24) / 3; + psize = (info.offset - info.extra_read - 24) / 3; } else { if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); + } } - s->img_n = ma ? 4 : 3; + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else @@ -5262,7 +5603,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } - stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); if (info.bpp == 1) width = (s->img_x + 7) >> 3; else if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.bpp == 8) width = s->img_x; @@ -5311,7 +5652,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; - stbi__skip(s, info.offset - 14 - info.hsz); + stbi__skip(s, info.offset - info.extra_read - info.hsz); if (info.bpp == 24) width = 3 * s->img_x; else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; @@ -5329,6 +5670,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } } for (j=0; j < (int) s->img_y; ++j) { if (easy) { @@ -5550,6 +5892,11 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req int RLE_repeating = 0; int read_next_pixel = 1; STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -5590,6 +5937,11 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // do I need to load a palette? if ( tga_indexed) { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette @@ -5713,6 +6065,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); // OK, done return tga_data; } @@ -5797,6 +6150,9 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req h = stbi__get32be(s); w = stbi__get32be(s); + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + // Make sure the depth is 8 bits. bitdepth = stbi__get16be(s); if (bitdepth != 8 && bitdepth != 16) @@ -6151,6 +6507,10 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c x = stbi__get16be(s); y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); @@ -6160,6 +6520,7 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c // intermediate buffer is RGBA result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { @@ -6198,7 +6559,7 @@ typedef struct int w,h; stbi_uc *out; // output buffer (always 4 components) stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; + stbi_uc *history; int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; @@ -6259,6 +6620,9 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in g->ratio = stbi__get8(s); g->transparent = -1; + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments if (is_info) return 1; @@ -6272,6 +6636,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); if (!stbi__gif_header(s, g, comp, 1)) { STBI_FREE(g); stbi__rewind( s ); @@ -6286,7 +6651,7 @@ static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; - int idx; + int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty @@ -6295,12 +6660,12 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) if (g->cur_y >= g->max_y) return; - idx = g->cur_x + g->cur_y; + idx = g->cur_x + g->cur_y; p = &g->out[idx]; - g->history[idx / 4] = 1; + g->history[idx / 4] = 1; c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; + if (c[3] > 128) { // don't render transparent pixels; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; @@ -6409,14 +6774,14 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) // two back is the image from two frames ago, used for a very specific disposal format static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { - int dispose; - int first_frame; - int pi; - int pcount; + int dispose; + int first_frame; + int pi; + int pcount; STBI_NOTUSED(req_comp); // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; + first_frame = 0; if (g->out == 0) { if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) @@ -6428,17 +6793,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i if (!g->out || !g->background || !g->history) return stbi__errpuc("outofmem", "Out of memory"); - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. + // color refers to the color that was there the previous frame. memset(g->out, 0x00, 4 * pcount); memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; + first_frame = 1; } else { - // second frame - how do we dispoase of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; if ((dispose == 3) && (two_back == 0)) { dispose = 2; // if I don't have an image to revert back to, default to the old background @@ -6447,32 +6812,32 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i if (dispose == 3) { // use previous graphic for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); } } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); } } } else { - // This is a non-disposal case eithe way, so just + // This is a non-disposal case eithe way, so just // leave the pixels as is, and they will become the new background // 1: do not dispose // 0: not specified. } - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); } - // clear my history; + // clear my history; memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame for (;;) { - int tag = stbi__get8(s); + int tag = stbi__get8(s); switch (tag) { case 0x2C: /* Image Descriptor */ { @@ -6517,19 +6882,19 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i } else if (g->flags & 0x80) { g->color_table = (stbi_uc *) g->pal; } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - + return stbi__errpuc("missing color table", "Corrupt GIF"); + o = stbi__process_gif_raster(s, g); if (!o) return NULL; - // if this was the first frame, - pcount = g->w * g->h; + // if this was the first frame, + pcount = g->w * g->h; if (first_frame && (g->bgindex > 0)) { // if first frame, any pixel not drawn to gets the background color for (pi = 0; pi < pcount; ++pi) { if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); } } } @@ -6540,7 +6905,7 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i case 0x21: // Comment Extension. { int len; - int ext = stbi__get8(s); + int ext = stbi__get8(s); if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { @@ -6549,23 +6914,23 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i // unset old transparent if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } + g->pal[g->transparent][3] = 255; + } if (g->eflags & 0x01) { g->transparent = stbi__get8(s); if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; + g->pal[g->transparent][3] = 0; } } else { // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; + stbi__skip(s, 1); + g->transparent = -1; } } else { stbi__skip(s, len); break; } - } + } while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); } @@ -6581,18 +6946,35 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i } } +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { - int layers = 0; + int layers = 0; stbi_uc *u = 0; stbi_uc *out = 0; - stbi_uc *two_back = 0; + stbi_uc *two_back = 0; stbi__gif g; - int stride; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + memset(&g, 0, sizeof(g)); if (delays) { - *delays = 0; + *delays = 0; } do { @@ -6602,44 +6984,61 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, if (u) { *x = g.w; *y = g.h; - ++layers; - stride = g.w * g.h * 4; - + ++layers; + stride = g.w * g.h * 4; + if (out) { - out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); } } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); } } - memcpy( out + ((layers - 1) * stride), u, stride ); + memcpy( out + ((layers - 1) * stride), u, stride ); if (layers >= 2) { - two_back = out - 2 * stride; + two_back = out - 2 * stride; } if (delays) { - (*delays)[layers - 1U] = g.delay; + (*delays)[layers - 1U] = g.delay; } } - } while (u != 0); + } while (u != 0); - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); - // do the final conversion after loading everything; + // do the final conversion after loading everything; if (req_comp && req_comp != 4) out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - *z = layers; + *z = layers; return out; } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); + return stbi__errpuc("not GIF", "Image was not as a gif type."); } } @@ -6657,7 +7056,7 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req *y = g.h; // moved conversion to after successful load so that the same - // can be done for multiple frames. + // can be done for multiple frames. if (req_comp && req_comp != 4) u = stbi__convert_format(u, 4, req_comp, g.w, g.h); } else if (g.out) { @@ -6665,9 +7064,9 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req STBI_FREE(g.out); } - // free buffers needed for multiple frame loading; + // free buffers needed for multiple frame loading; STBI_FREE(g.history); - STBI_FREE(g.background); + STBI_FREE(g.background); return u; } @@ -6792,6 +7191,9 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re token += 3; width = (int) strtol(token, NULL, 10); + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + *x = width; *y = height; @@ -6860,12 +7262,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re // Run value = stbi__get8(s); count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } @@ -6934,12 +7336,18 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) info.all_a = 255; p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) + if (p == NULL) { + stbi__rewind( s ); return 0; + } if (x) *x = s->img_x; if (y) *y = s->img_y; - if (comp) *comp = info.ma ? 4 : 3; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } return 1; } #endif @@ -6997,8 +7405,8 @@ static int stbi__psd_is16(stbi__context *s) stbi__rewind( s ); return 0; } - (void) stbi__get32be(s); - (void) stbi__get32be(s); + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); depth = stbi__get16be(s); if (depth != 16) { stbi__rewind( s ); @@ -7077,7 +7485,6 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) // Known limitations: // Does not support comments in the header section // Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel #ifndef STBI_NO_PNM @@ -7098,22 +7505,33 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *out; STBI_NOTUSED(ri); - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) return 0; + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) return stbi__errpuc("too large", "PNM too large"); - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } if (out == NULL) return out; // stbi__convert_format frees input on failure } return out; @@ -7150,6 +7568,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c) while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { value = value*10 + (*c - '0'); *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); } return value; @@ -7180,17 +7600,29 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) stbi__pnm_skip_whitespace(s, &c); *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; else - return 1; + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; } #endif @@ -7246,6 +7678,9 @@ static int stbi__is_16_main(stbi__context *s) if (stbi__psd_is16(s)) return 1; #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif return 0; } @@ -7325,7 +7760,7 @@ STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user /* revision history: - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug From c9ccdda80634fc7a9a9f98ef0aa7a1515dd3ba1f Mon Sep 17 00:00:00 2001 From: jakubg1 <24206305+jakubg1@users.noreply.github.com> Date: Sun, 3 Mar 2024 04:04:42 +0100 Subject: [PATCH 04/88] Fix loading mini textures with periods Some textures, like textures/mini/9-107.13, had incorrectly part of their name removed (truncated to textures/mini/9-107) and thus were failing to load. The workaround was to add an explicit ".bmp" extension for that to be removed instead. --- launcher/textures_scanner.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/textures_scanner.cpp b/launcher/textures_scanner.cpp index 6d575244..1a511774 100644 --- a/launcher/textures_scanner.cpp +++ b/launcher/textures_scanner.cpp @@ -95,7 +95,7 @@ void ui::vehicles_bank::parse_category_entry(const std::string ¶m) std::string mini; std::getline(stream, mini, ','); - category_icons.emplace(ctx_type, "textures/mini/" + ToLower(mini)); + category_icons.emplace(ctx_type, "textures/mini/" + ToLower(mini) + ".bmp"); } void ui::vehicles_bank::parse_controllable_entry(const std::string &target, const std::string ¶m) @@ -122,12 +122,12 @@ void ui::vehicles_bank::parse_texture_info(const std::string &target, const std: set.meta = meta; if (!mini.empty()) - group_icons.emplace(mini, std::move(deferred_image("textures/mini/" + ToLower(mini)))); + group_icons.emplace(mini, std::move(deferred_image("textures/mini/" + ToLower(mini) + ".bmp"))); if (!miniplus.empty()) - set.mini = std::move(deferred_image("textures/mini/" + ToLower(miniplus))); + set.mini = std::move(deferred_image("textures/mini/" + ToLower(miniplus) + ".bmp")); else if (!mini.empty()) - set.mini = std::move(deferred_image("textures/mini/" + ToLower(mini))); + set.mini = std::move(deferred_image("textures/mini/" + ToLower(mini) + ".bmp")); set.skin = ToLower(target); erase_extension(set.skin); From 13d9a91632b562902230ae0ba61902265031036d Mon Sep 17 00:00:00 2001 From: jakubg1 <24206305+jakubg1@users.noreply.github.com> Date: Wed, 13 Mar 2024 03:25:19 +0100 Subject: [PATCH 05/88] Improve pantograph selector - A new parameter has been added to .fiz file format, in the Switches: section: - PantographPresetDefault [0] - The 0-based index of a pantograph preset that should be chosen upon the vehicle creation. 0 by default mimicks the current behavior. - Fixed the selected pantograph configuration not being respected until the configuration has been changed. --- McZapkie/MOVER.h | 1 + McZapkie/Mover.cpp | 1 + Train.cpp | 6 ++++++ Train.h | 4 +++- 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 2702aee1..f93b2e59 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1205,6 +1205,7 @@ public: bool UniCtrlIntegratedLocalBrakeCtrl = false; /*zintegrowany nastawnik JH obsluguje hamowanie hamulcem pomocniczym*/ int UniCtrlNoPowerPos{ 0 }; // cached highesr position not generating traction force std::pair> PantsPreset { "0132", { 0, 0 } }; // pantograph preset switches; .first holds possible setups as chars, .second holds currently selected preset in each cab + int PantsPresetDefault = 0; // default pantograph preset, this is not updated during simulation /*-sekcja parametrow dla lokomotywy elektrycznej*/ TSchemeTable RList; /*lista rezystorow rozruchowych i polaczen silnikow, dla dizla: napelnienia*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index ae3180f5..a395051d 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -11100,6 +11100,7 @@ void TMoverParameters::LoadFIZ_Switches( std::string const &Input ) { std::remove( std::begin( presets ), std::end( presets ), '|' ), std::end( presets ) ); } + extract_value( PantsPresetDefault, "PantographPresetDefault", Input, "" ); } void TMoverParameters::LoadFIZ_MotorParamTable( std::string const &Input ) { diff --git a/Train.cpp b/Train.cpp index 5e1b28f8..187b2545 100644 --- a/Train.cpp +++ b/Train.cpp @@ -637,6 +637,12 @@ bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d) DynamicObject->Mechanik->sync_consist_reversers(); } + // Set the default pantograph preset and update pantographs' valves accordingly. + change_pantograph_selection(mvOccupied->PantsPresetDefault); + // Avoid double update if the default is other than 0. + if (mvOccupied->PantsPresetDefault == 0) + update_pantograph_valves(); + return true; } diff --git a/Train.h b/Train.h index 15bd2a03..4e2afda1 100644 --- a/Train.h +++ b/Train.h @@ -211,12 +211,14 @@ class TTrain { void update_sounds( double const Deltatime ); void update_sounds_runningnoise( sound_source &Sound ); void update_sounds_radio(); + // Translates the given cab to an end ID: cab `1` and engine compartment (`0`) translate to `end::front`, cab `2` translates to `end::rear` inline end cab_to_end( int const End ) const { return ( End == 2 ? end::rear : end::front ); } + // Translates the currently occupied cab to an end ID: cab `1` and engine compartment (`0`) translate to `end::front`, cab `2` translates to `end::rear` inline end cab_to_end() const { return cab_to_end( iCabn ); } @@ -815,7 +817,7 @@ public: // reszta może by?publiczna */ // McZapkie: opis kabiny - obszar poruszania sie mechanika oraz zajetosc std::array Cabine; // przedzial maszynowy, kabina 1 (A), kabina 2 (B) - int iCabn { 0 }; // 0: mid, 1: front, 2: rear + int iCabn { 0 }; // the cab number the player is currently inside of; 0: mid, 1: front, 2: rear bool is_cab_initialized { false }; // McZapkie: do poruszania sie po kabinie Math3D::vector3 pMechSittingPosition; // ABu 180404 From 7560829d1659883825421f4a105db4add0996a72 Mon Sep 17 00:00:00 2001 From: jakubg1 <24206305+jakubg1@users.noreply.github.com> Date: Mon, 1 Apr 2024 19:40:32 +0200 Subject: [PATCH 06/88] Launcher: Display vehicles by groups by default This matches the Starter. --- launcher/vehicle_picker.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/vehicle_picker.h b/launcher/vehicle_picker.h index 8ff2c80d..ed850d11 100644 --- a/launcher/vehicle_picker.h +++ b/launcher/vehicle_picker.h @@ -19,7 +19,7 @@ private: std::shared_ptr selected_vehicle; const std::string *selected_group = nullptr; const skin_set *selected_skinset = nullptr; - bool display_by_groups = false; + bool display_by_groups = true; deferred_image placeholder_mini; std::array search_query = { 0 }; From fb3c0c2d6d9c5ff0f84d7716423b67ddee360276 Mon Sep 17 00:00:00 2001 From: jakubg1 <24206305+jakubg1@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:24:41 +0200 Subject: [PATCH 07/88] Remove debug output --- keyboardinput.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/keyboardinput.cpp b/keyboardinput.cpp index 70445d18..6fcdede8 100644 --- a/keyboardinput.cpp +++ b/keyboardinput.cpp @@ -173,7 +173,6 @@ keyboard_input::recall_bindings() { // This can't be done now as by default this would destroy all command descriptions, // which are used by Starter. // Do this when szczawik's Starter becomes deprecated or implements command descriptions in some other way. - WriteLog("got: \"" + bindingkeyname + "\""); if (descriptionStarted) { if (description.size() > 0) { description += " "; From dba0a035fd0d5e110b7a61e1b2bb0d62736dfac9 Mon Sep 17 00:00:00 2001 From: Wls50 <167411000+wls50@users.noreply.github.com> Date: Sun, 30 Jun 2024 16:13:19 +0200 Subject: [PATCH 08/88] add user data vector to basic_vertex update t3d, e3d & sbt specs accordingly update node model to shape conversion to support indexed meshes --- Model3d.cpp | 89 +++++++++++++++++++++++++++++++--------- Model3d.h | 2 +- geometrybank.cpp | 43 +++++++++++++++++-- geometrybank.h | 13 ++++-- opengl33geometrybank.cpp | 9 ++-- openglgeometrybank.cpp | 8 ++-- scene.cpp | 2 +- scenenode.cpp | 59 +++++++++++++------------- shaders/vertex.vert | 3 ++ vertex.h | 1 + 10 files changed, 162 insertions(+), 67 deletions(-) diff --git a/Model3d.cpp b/Model3d.cpp index 7fbd655a..3cbdae24 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -209,6 +209,21 @@ inline void readMatrix(cParser &parser, float4x4 &matrix) parser >> matrix(x)[y]; }; +template void UserdataParse(cParser &parser, gfx::basic_vertex &vertex) +{ + parser.getTokens(4); + union + { + T InType; + float FloatType; + } buf{}; + for (int i = 0; i < 4; ++i) + { + parser >> buf.InType; + vertex.user_data[i] = buf.FloatType; + } +} + std::pair TSubModel::Load( cParser &parser, bool dynamic ) { // Ra: VBO tworzone na poziomie modelu, a nie submodeli auto token { parser.getToken() }; @@ -478,8 +493,32 @@ std::pair TSubModel::Load( cParser &parser, bool dynamic ) } if (eType < TP_ROTATOR) { // wczytywanie wierzchołków - token = parser.getToken(); - if( token == "numindices:" ) // optional block, potentially preceeding vertex list + token = parser.getToken(); + enum class UserDataType + { + None, + Float, + Int, + UInt, + Count + } user_data = UserDataType::None; + if (token == "userdata:") + { + const static std::map type_mapping{{"float", UserDataType::Float}, {"int", UserDataType::Int}, {"uint", UserDataType::UInt}}; + const std::string type = parser.getToken(); + + if (auto it = type_mapping.find(type); it != type_mapping.end()) + { + user_data = it->second; + } + else + { + user_data = UserDataType::None; + } + + token = parser.getToken(); + } + if( token == "numindices:" ) // optional block, potentially preceeding vertex list { m_geometry.index_count = parser.getToken( false ); Indices.resize( m_geometry.index_count ); @@ -662,7 +701,17 @@ std::pair TSubModel::Load( cParser &parser, bool dynamic ) m_geometry.index_count = Indices.size(); m_geometry.vertex_count = Vertices.size(); } - } + if (user_data != UserDataType::None) + { + const static std::array(UserDataType::Count)> parsers{nullptr, &UserdataParse, &UserdataParse, + &UserdataParse}; + auto vertices { std::begin( Vertices ) }; + for( auto idx = 0; idx < m_geometry.vertex_count; ++idx ) { + auto vertex { vertices + idx }; + parsers[static_cast(user_data)](parser, *vertex); + } + } + } else // gdy brak wierzchołków { eType = TP_ROTATOR; // submodel pomocniczy, ma tylko macierz przekształcenia @@ -1163,25 +1212,25 @@ void TSubModel::RaAnimation(glm::mat4 &m, TAnimType a) //--------------------------------------------------------------------------- -void TSubModel::serialize_geometry( std::ostream &Output, bool const Packed, bool const Indexed ) const { +void TSubModel::serialize_geometry( std::ostream &Output, bool const Packed, bool const Indexed, bool const UserData ) const { if( Child ) { - Child->serialize_geometry( Output, Packed, Indexed ); + Child->serialize_geometry( Output, Packed, Indexed, UserData ); } if( m_geometry.handle != null_handle ) { if( Packed ) { for( auto const &vertex : GfxRenderer->Vertices( m_geometry.handle ) ) { - vertex.serialize_packed( Output, Indexed ); + vertex.serialize_packed( Output, Indexed, UserData ); } } else { for( auto const &vertex : GfxRenderer->Vertices( m_geometry.handle ) ) { - vertex.serialize( Output, Indexed ); + vertex.serialize( Output, Indexed, UserData ); } } } if( Next ) { - Next->serialize_geometry( Output, Packed, Indexed ); + Next->serialize_geometry( Output, Packed, Indexed, UserData ); } }; @@ -1682,20 +1731,20 @@ void TModel3d::SaveToBinFile(std::string const &FileName) Root->serialize_indices( s, indexsize ); if (!(Global.iConvertModels & 8)) { - sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '1' ) ); + sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '1' + 4 ) ); sn_utils::ls_uint32( s, 8 + m_vertexcount * 20 ); - Root->serialize_geometry( s, true, true ); + Root->serialize_geometry( s, true, true, true ); } else { - sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '2' ) ); + sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '2' + 4 ) ); sn_utils::ls_uint32( s, 8 + m_vertexcount * 48 ); - Root->serialize_geometry( s, false, true ); + Root->serialize_geometry( s, false, true, true ); } } else { - sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' ) ); + sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' + 4 ) ); sn_utils::ls_uint32( s, 8 + m_vertexcount * 32 ); - Root->serialize_geometry( s, false, false ); + Root->serialize_geometry( s, false, false, true ); } if (textures.size()) @@ -1785,6 +1834,7 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) std::streampos end = s.tellg() + (std::streampos)size; bool hastangents { false }; + bool hasuserdata {false}; while (s.tellg() < end) { @@ -1828,17 +1878,18 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) return (Left.first) < (Right.first); } ); // once sorted we can grab geometry as it comes, and assign it to the chunks it belongs to size_t const vertextype { ( ( ( type & 0xFF000000 ) >> 24 ) - '0' ) }; - hastangents = ( vertextype > 0 ); + hastangents = ( (vertextype & 3) > 0 ); + hasuserdata = (vertextype & 4); for( auto const &submodeloffset : submodeloffsets ) { auto &submodel { Root[ submodeloffset.second ] }; auto const &submodelgeometry { submodel.m_geometry }; submodel.Vertices.resize( submodelgeometry.vertex_count ); m_vertexcount += submodelgeometry.vertex_count; - switch( vertextype ) { + switch( vertextype & 3 ) { case 0: { // legacy vnt0 format for( auto &vertex : submodel.Vertices ) { - vertex.deserialize( s, hastangents ); + vertex.deserialize( s, hastangents, hasuserdata ); if( submodel.eType < TP_ROTATOR ) { // normal vectors debug routine if( ( false == submodel.m_normalizenormals ) @@ -1853,14 +1904,14 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) case 1: { // expanded chunk formats for( auto &vertex : submodel.Vertices ) { - vertex.deserialize_packed( s, hastangents ); + vertex.deserialize_packed( s, hastangents, hasuserdata ); } break; } case 2: { // expanded chunk formats for( auto &vertex : submodel.Vertices ) { - vertex.deserialize( s, hastangents ); + vertex.deserialize( s, hastangents, hasuserdata ); } break; } diff --git a/Model3d.h b/Model3d.h index 256c583a..e31425ad 100644 --- a/Model3d.h +++ b/Model3d.h @@ -243,7 +243,7 @@ public: std::vector&, std::vector&, std::vector&); - void serialize_geometry( std::ostream &Output, bool const Packed, bool const Indexed ) const; + void serialize_geometry( std::ostream &Output, bool const Packed, bool const Indexed, bool const UserData ) const; int index_size() const; void serialize_indices( std::ostream &Output, int const Size ) const; // places contained geometry in provided ground node diff --git a/geometrybank.cpp b/geometrybank.cpp index 47dd316c..cb6b3c1e 100644 --- a/geometrybank.cpp +++ b/geometrybank.cpp @@ -9,6 +9,7 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "geometrybank.h" +#include "vertex.h" #include "sn_utils.h" #include "Logs.h" @@ -16,8 +17,30 @@ http://mozilla.org/MPL/2.0/. namespace gfx { +basic_vertex basic_vertex::convert(world_vertex const &world, glm::dvec3 const& origin) +{ + basic_vertex vertex{}; + vertex.position = static_cast(world.position - origin); + vertex.normal = world.normal; + vertex.texture = world.texture; + vertex.user_data = world.user_data; + return vertex; +} + +world_vertex basic_vertex::to_world(const glm::dvec3 &origin) const +{ + world_vertex vertex{}; + + vertex.position = static_cast(position) + origin; + vertex.normal = normal; + vertex.texture = texture; + vertex.user_data = user_data; + + return vertex; +} + void -basic_vertex::serialize( std::ostream &s, bool const Tangent ) const { +basic_vertex::serialize( std::ostream &s, bool const Tangent, bool const UserData ) const { sn_utils::s_vec3( s, position ); sn_utils::s_vec3( s, normal ); @@ -26,10 +49,13 @@ basic_vertex::serialize( std::ostream &s, bool const Tangent ) const { if( Tangent ) { sn_utils::s_vec4( s, tangent ); } + if( UserData ) { + sn_utils::s_vec4( s, user_data ); + } } void -basic_vertex::deserialize( std::istream &s, bool const Tangent ) { +basic_vertex::deserialize( std::istream &s, bool const Tangent, bool const UserData ) { position = sn_utils::d_vec3( s ); normal = sn_utils::d_vec3( s ); @@ -38,10 +64,13 @@ basic_vertex::deserialize( std::istream &s, bool const Tangent ) { if( Tangent ) { tangent = sn_utils::d_vec4( s ); } + if( UserData ) { + user_data = sn_utils::d_vec4( s ); + } } void -basic_vertex::serialize_packed( std::ostream &s, bool const Tangent ) const { +basic_vertex::serialize_packed( std::ostream &s, bool const Tangent, bool const UserData ) const { sn_utils::ls_uint64( s, glm::packHalf4x16( { position, 0.f } ) ); sn_utils::ls_uint32( s, glm::packSnorm3x10_1x2( { normal, 0.f } ) ); @@ -50,10 +79,13 @@ basic_vertex::serialize_packed( std::ostream &s, bool const Tangent ) const { if( Tangent ) { sn_utils::ls_uint32( s, glm::packSnorm3x10_1x2( tangent ) ); } + if( UserData ) { + sn_utils::ls_uint64( s, glm::packHalf4x16( user_data )); + } } void -basic_vertex::deserialize_packed( std::istream &s, bool const Tangent ) { +basic_vertex::deserialize_packed( std::istream &s, bool const Tangent, bool const UserData ) { position = glm::unpackHalf4x16( sn_utils::ld_uint64( s ) ); normal = glm::unpackSnorm3x10_1x2( sn_utils::ld_uint32( s ) ); @@ -62,6 +94,9 @@ basic_vertex::deserialize_packed( std::istream &s, bool const Tangent ) { if( Tangent ) { tangent = glm::unpackSnorm3x10_1x2( sn_utils::ld_uint32( s ) ); } + if( UserData ) { + user_data = glm::unpackHalf4x16( sn_utils::ld_uint64( s ) ); + } } // based on diff --git a/geometrybank.h b/geometrybank.h index 2f067388..5f54146a 100644 --- a/geometrybank.h +++ b/geometrybank.h @@ -11,6 +11,8 @@ http://mozilla.org/MPL/2.0/. #include "ResourceManager.h" +struct world_vertex; + namespace gfx { struct basic_vertex { @@ -19,15 +21,18 @@ struct basic_vertex { glm::vec3 normal; // 3d space glm::vec2 texture; // uv space glm::vec4 tangent; // xyz - tangent, w - handedness + glm::vec4 user_data; // user data (for color or additional uv channels, not subject to post-processing) basic_vertex() = default; basic_vertex( glm::vec3 Position, glm::vec3 Normal, glm::vec2 Texture ) : position( Position ), normal( Normal ), texture( Texture ) {} - void serialize( std::ostream&, bool const Tangent = false ) const; - void deserialize( std::istream&, bool const Tangent = false ); - void serialize_packed( std::ostream&, bool const Tangent = false ) const; - void deserialize_packed( std::istream&, bool const Tangent = false ); + static basic_vertex convert(world_vertex const &world, glm::dvec3 const &origin); + world_vertex to_world(glm::dvec3 const &origin = glm::dvec3(0.)) const; + void serialize( std::ostream&, bool const Tangent = false, bool const UserData = false ) const; + void deserialize( std::istream&, bool const Tangent = false, bool const UserData = false ); + void serialize_packed( std::ostream&, bool const Tangent = false, bool const UserData = false ) const; + void deserialize_packed( std::istream&, bool const Tangent = false, bool const UserData = false ); }; // data streams carried in a vertex diff --git a/opengl33geometrybank.cpp b/opengl33geometrybank.cpp index 62ff4f47..e5dd6f31 100644 --- a/opengl33geometrybank.cpp +++ b/opengl33geometrybank.cpp @@ -101,11 +101,12 @@ void opengl33_vaogeometrybank::setup_buffer() void opengl33_vaogeometrybank::setup_attrib(size_t offset) { - m_vao->setup_attrib( *m_vertexbuffer, 0, 3, GL_FLOAT, sizeof( basic_vertex ), 0 * sizeof( float ) + offset * sizeof( basic_vertex ) ); + m_vao->setup_attrib( *m_vertexbuffer, 0, 3, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, position) + offset * sizeof( basic_vertex ) ); // NOTE: normal and color streams share the data - m_vao->setup_attrib( *m_vertexbuffer, 1, 3, GL_FLOAT, sizeof( basic_vertex ), 3 * sizeof( float ) + offset * sizeof( basic_vertex ) ); - m_vao->setup_attrib( *m_vertexbuffer, 2, 2, GL_FLOAT, sizeof( basic_vertex ), 6 * sizeof( float ) + offset * sizeof( basic_vertex ) ); - m_vao->setup_attrib( *m_vertexbuffer, 3, 4, GL_FLOAT, sizeof( basic_vertex ), 8 * sizeof( float ) + offset * sizeof( basic_vertex ) ); + m_vao->setup_attrib( *m_vertexbuffer, 1, 3, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, normal) + offset * sizeof( basic_vertex ) ); + m_vao->setup_attrib( *m_vertexbuffer, 2, 2, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, texture) + offset * sizeof( basic_vertex ) ); + m_vao->setup_attrib( *m_vertexbuffer, 3, 4, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, tangent) + offset * sizeof( basic_vertex ) ); + m_vao->setup_attrib( *m_vertexbuffer, 4, 4, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, user_data) + offset * sizeof( basic_vertex ) ); } // draw() subclass details diff --git a/openglgeometrybank.cpp b/openglgeometrybank.cpp index 961b2716..43aeb0f3 100644 --- a/openglgeometrybank.cpp +++ b/openglgeometrybank.cpp @@ -227,7 +227,7 @@ void opengl_vbogeometrybank::bind_streams( gfx::stream_units const &Units, unsigned int const Streams, size_t offset ) { if( Streams & gfx::stream::position ) { - ::glVertexPointer( 3, GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast( sizeof( gfx::basic_vertex ) * offset ) ); + ::glVertexPointer( 3, GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast( offsetof(gfx::basic_vertex, position) + sizeof( gfx::basic_vertex ) * offset ) ); ::glEnableClientState( GL_VERTEX_ARRAY ); } else { @@ -235,14 +235,14 @@ opengl_vbogeometrybank::bind_streams( gfx::stream_units const &Units, unsigned i } // NOTE: normal and color streams share the data, making them effectively mutually exclusive if( Streams & gfx::stream::normal ) { - ::glNormalPointer( GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast( 12 + sizeof( gfx::basic_vertex ) * offset ) ); + ::glNormalPointer( GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast( offsetof(gfx::basic_vertex, normal) + sizeof( gfx::basic_vertex ) * offset ) ); ::glEnableClientState( GL_NORMAL_ARRAY ); } else { ::glDisableClientState( GL_NORMAL_ARRAY ); } if( Streams & gfx::stream::color ) { - ::glColorPointer( 3, GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast( 12 + sizeof( gfx::basic_vertex ) * offset ) ); + ::glColorPointer( 3, GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast( offsetof(gfx::basic_vertex, normal) + sizeof( gfx::basic_vertex ) * offset ) ); ::glEnableClientState( GL_COLOR_ARRAY ); } else { @@ -251,7 +251,7 @@ opengl_vbogeometrybank::bind_streams( gfx::stream_units const &Units, unsigned i if( Streams & gfx::stream::texture ) { for( auto unit : Units.texture ) { ::glClientActiveTexture( GL_TEXTURE0 + unit ); - ::glTexCoordPointer( 2, GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast( 24 + sizeof( gfx::basic_vertex ) * offset ) ); + ::glTexCoordPointer( 2, GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast( offsetof(gfx::basic_vertex, texture) + sizeof( gfx::basic_vertex ) * offset ) ); ::glEnableClientState( GL_TEXTURE_COORD_ARRAY ); } m_activetexturearrays = Units.texture; diff --git a/scene.cpp b/scene.cpp index 6ca35dc0..9eac1af6 100644 --- a/scene.cpp +++ b/scene.cpp @@ -26,7 +26,7 @@ namespace scene { std::string const EU07_FILEEXTENSION_REGION { ".sbt" }; std::uint32_t const EU07_FILEHEADER { MAKE_ID4( 'E','U','0','7' ) }; -std::uint32_t const EU07_FILEVERSION_REGION { MAKE_ID4( 'S', 'B', 'T', 1 ) }; +std::uint32_t const EU07_FILEVERSION_REGION { MAKE_ID4( 'S', 'B', 'T', '2' ) }; // potentially activates event handler with the same name as provided node, and within handler activation range void diff --git a/scenenode.cpp b/scenenode.cpp index a8f186b7..9d809d7a 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -81,11 +81,8 @@ shape_node::shapenode_data::serialize( std::ostream &Output ) const { // vertex count, followed by vertex data sn_utils::ls_uint32( Output, vertices.size() ); for( auto const &vertex : vertices ) { - gfx::basic_vertex( - glm::vec3{ vertex.position - origin }, - vertex.normal, - vertex.texture ) - .serialize( Output ); + gfx::basic_vertex::convert(vertex, origin) + .serialize( Output, false, true ); } } @@ -112,10 +109,8 @@ shape_node::shapenode_data::deserialize( std::istream &Input ) { vertices.resize( sn_utils::ld_uint32( Input ) ); gfx::basic_vertex localvertex; for( auto &vertex : vertices ) { - localvertex.deserialize( Input ); - vertex.position = origin + glm::dvec3{ localvertex.position }; - vertex.normal = localvertex.normal; - vertex.texture = localvertex.texture; + localvertex.deserialize( Input, false, true ); + vertex = localvertex.to_world(origin); } } @@ -353,23 +348,30 @@ shape_node::convert( TSubModel const *Submodel ) { int vertexcount { 0 }; std::vector importedvertices; world_vertex vertex, vertex1, vertex2; - for( auto const &sourcevertex : GfxRenderer->Vertices( Submodel->m_geometry.handle ) ) { - vertex.position = sourcevertex.position; - vertex.normal = sourcevertex.normal; - vertex.texture = sourcevertex.texture; - if( vertexcount == 0 ) { vertex1 = vertex; } - else if( vertexcount == 1 ) { vertex2 = vertex; } - else if( vertexcount >= 2 ) { - if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) { - importedvertices.emplace_back( vertex1 ); - importedvertices.emplace_back( vertex2 ); - importedvertices.emplace_back( vertex ); - } - // start a new triangle - vertexcount = -1; - } - ++vertexcount; - } + if(!GfxRenderer->Indices(Submodel->m_geometry.handle).empty()){ + const auto& vertices = GfxRenderer->Vertices(Submodel->m_geometry.handle); + for(const auto index : GfxRenderer->Indices(Submodel->m_geometry.handle)){ + vertex = vertices[index].to_world(); + importedvertices.emplace_back(vertex); + } + } + else{ + for( auto const &sourcevertex : GfxRenderer->Vertices( Submodel->m_geometry.handle ) ) { + vertex = sourcevertex.to_world(); + if( vertexcount == 0 ) { vertex1 = vertex; } + else if( vertexcount == 1 ) { vertex2 = vertex; } + else if( vertexcount >= 2 ) { + if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) { + importedvertices.emplace_back( vertex1 ); + importedvertices.emplace_back( vertex2 ); + importedvertices.emplace_back( vertex ); + } + // start a new triangle + vertexcount = -1; + } + ++vertexcount; + } + } if( true == importedvertices.empty() ) { return *this; } @@ -423,10 +425,7 @@ shape_node::create_geometry( gfx::geometrybank_handle const &Bank ) { gfx::vertex_array vertices; vertices.reserve( m_data.vertices.size() ); for( auto const &vertex : m_data.vertices ) { - vertices.emplace_back( - vertex.position - m_data.origin, - vertex.normal, - vertex.texture ); + vertices.emplace_back(gfx::basic_vertex::convert(vertex, m_data.origin)); } m_data.geometry = GfxRenderer->Insert( vertices, Bank, GL_TRIANGLES ); std::vector().swap( m_data.vertices ); // hipster shrink_to_fit diff --git a/shaders/vertex.vert b/shaders/vertex.vert index 4d274e71..feadbe1e 100644 --- a/shaders/vertex.vert +++ b/shaders/vertex.vert @@ -2,6 +2,7 @@ layout(location = 0) in vec3 v_vert; layout(location = 1) in vec3 v_normal; layout(location = 2) in vec2 v_coord; layout(location = 3) in vec4 v_tangent; +layout(location = 4) in vec4 v_userdata; #include @@ -18,6 +19,7 @@ out vec4 f_clip_future_pos; //out vec3 TangentLightPos; //out vec3 TangentViewPos; out vec3 TangentFragPos; +out vec4 UserData; void main() { @@ -43,4 +45,5 @@ void main() // TangentLightPos = TBN * f_light_pos.xyz; // TangentViewPos = TBN * vec3(0.0, 0.0, 0.0); TangentFragPos = TBN * f_pos.xyz; + UserData = v_userdata; } diff --git a/vertex.h b/vertex.h index 91bf1ca0..849b3f1b 100644 --- a/vertex.h +++ b/vertex.h @@ -18,6 +18,7 @@ struct world_vertex { glm::dvec3 position; glm::vec3 normal; glm::vec2 texture; + glm::vec4 user_data; // overloads // operator+ From 27efcdccfa4535d0627203b8a0d047eb0c42b244 Mon Sep 17 00:00:00 2001 From: jakubg1 <24206305+jakubg1@users.noreply.github.com> Date: Wed, 3 Jul 2024 22:55:43 +0200 Subject: [PATCH 09/88] Fix timetable window being cut off on large font sizes --- driveruipanels.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/driveruipanels.cpp b/driveruipanels.cpp index eeddbed4..cf1d893a 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -485,11 +485,13 @@ timetable_panel::render() { | ImGuiWindowFlags_NoCollapse | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 ); + // HACK: Make sure the timetable window is of correct width when using a larger font size. + float horizontalScale = Global.ui_fontsize / 13; if( size.x > 0 ) { - ImGui::SetNextWindowSize( ImVec2S( size.x, size.y ) ); + ImGui::SetNextWindowSize( ImVec2S( size.x * horizontalScale, size.y ) ); } if( size_min.x > 0 ) { - ImGui::SetNextWindowSizeConstraints( ImVec2S( size_min.x, size_min.y ), ImVec2S( size_max.x, size_max.y ) ); + ImGui::SetNextWindowSizeConstraints( ImVec2S( size_min.x * horizontalScale, size_min.y ), ImVec2S( size_max.x * horizontalScale, size_max.y ) ); } auto const panelname { ( title.empty() ? From 561b84a3290eab8f636c6cb0b8aafac88f66f780 Mon Sep 17 00:00:00 2001 From: Wls50 <167411000+wls50@users.noreply.github.com> Date: Sat, 6 Jul 2024 16:04:35 +0200 Subject: [PATCH 10/88] move vertex user data to a separate array update vao creation to support optional vertex data update mesh serialization to account for new user data location change t3d spec: userdata:{float|uint|int} to userdata:bool --- Model3d.cpp | 121 +++++++++++++++++++++++---------------- Model3d.h | 3 + Track.cpp | 88 ++++++++++++++-------------- Traction.cpp | 4 +- geometrybank.cpp | 114 +++++++++++++++++++++++------------- geometrybank.h | 55 +++++++++++------- nullrenderer.h | 22 +++---- opengl33geometrybank.cpp | 22 +++++-- opengl33geometrybank.h | 4 ++ opengl33renderer.cpp | 25 +++++--- opengl33renderer.h | 15 +++-- openglrenderer.cpp | 33 ++++++----- openglrenderer.h | 15 +++-- renderer.h | 14 +++-- scene.cpp | 8 ++- scenenode.cpp | 55 +++++++++++++----- scenenode.h | 2 + widgets/map.cpp | 3 +- 18 files changed, 374 insertions(+), 229 deletions(-) diff --git a/Model3d.cpp b/Model3d.cpp index 3cbdae24..081577a0 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -209,7 +209,7 @@ inline void readMatrix(cParser &parser, float4x4 &matrix) parser >> matrix(x)[y]; }; -template void UserdataParse(cParser &parser, gfx::basic_vertex &vertex) +template void UserdataParse(cParser &parser, gfx::vertex_userdata &vertex) { parser.getTokens(4); union @@ -220,7 +220,7 @@ template void UserdataParse(cParser &parser, gfx::basic_vertex &ver for (int i = 0; i < 4; ++i) { parser >> buf.InType; - vertex.user_data[i] = buf.FloatType; + vertex.data[i] = buf.FloatType; } } @@ -494,27 +494,10 @@ std::pair TSubModel::Load( cParser &parser, bool dynamic ) if (eType < TP_ROTATOR) { // wczytywanie wierzchołków token = parser.getToken(); - enum class UserDataType - { - None, - Float, - Int, - UInt, - Count - } user_data = UserDataType::None; + bool has_userdata = false; if (token == "userdata:") { - const static std::map type_mapping{{"float", UserDataType::Float}, {"int", UserDataType::Int}, {"uint", UserDataType::UInt}}; - const std::string type = parser.getToken(); - - if (auto it = type_mapping.find(type); it != type_mapping.end()) - { - user_data = it->second; - } - else - { - user_data = UserDataType::None; - } + has_userdata = parser.getToken(); token = parser.getToken(); } @@ -695,20 +678,19 @@ std::pair TSubModel::Load( cParser &parser, bool dynamic ) } } Vertices.resize( m_geometry.vertex_count ); // in case we had some degenerate triangles along the way - gfx::calculate_indices( Indices, Vertices, transformscalestack ); + gfx::calculate_indices( Indices, Vertices, Userdata, transformscalestack ); gfx::calculate_tangents( Vertices, Indices, GL_TRIANGLES ); // update values potentially changed by indexing m_geometry.index_count = Indices.size(); m_geometry.vertex_count = Vertices.size(); } - if (user_data != UserDataType::None) + if (has_userdata) { - const static std::array(UserDataType::Count)> parsers{nullptr, &UserdataParse, &UserdataParse, - &UserdataParse}; - auto vertices { std::begin( Vertices ) }; + Userdata.resize(m_geometry.vertex_count); + auto userdata { std::begin( Userdata ) }; for( auto idx = 0; idx < m_geometry.vertex_count; ++idx ) { - auto vertex { vertices + idx }; - parsers[static_cast(user_data)](parser, *vertex); + auto vertex { userdata + idx }; + UserdataParse(parser, *vertex); } } } @@ -1219,13 +1201,33 @@ void TSubModel::serialize_geometry( std::ostream &Output, bool const Packed, boo } if( m_geometry.handle != null_handle ) { if( Packed ) { - for( auto const &vertex : GfxRenderer->Vertices( m_geometry.handle ) ) { - vertex.serialize_packed( Output, Indexed, UserData ); - } + auto vertices = GfxRenderer->Vertices( m_geometry.handle ); + auto userdatas = GfxRenderer->UserData( m_geometry.handle ); + bool has_userdata = !userdatas.empty(); + for( int i = 0; i < vertices.size(); ++i ) { + vertices[i].serialize_packed( Output, Indexed ); + if (UserData) + { + if (has_userdata) + userdatas[i].serialize_packed(Output); + else + gfx::vertex_userdata{}.serialize_packed( Output ); + } + } } else { - for( auto const &vertex : GfxRenderer->Vertices( m_geometry.handle ) ) { - vertex.serialize( Output, Indexed, UserData ); + auto vertices = GfxRenderer->Vertices( m_geometry.handle ); + auto userdatas = GfxRenderer->UserData( m_geometry.handle ); + bool has_userdata = !userdatas.empty(); + for( int i = 0; i < vertices.size(); ++i ) { + vertices[i].serialize( Output, Indexed ); + if (UserData) + { + if (has_userdata) + userdatas[i].serialize(Output); + else + gfx::vertex_userdata{}.serialize( Output ); + } } } } @@ -1306,7 +1308,7 @@ TSubModel::create_geometry( std::size_t &Indexoffset, std::size_t &Vertexoffset, eType < TP_ROTATOR ? eType : GL_POINTS ); - m_geometry.handle = GfxRenderer->Insert( Indices, Vertices, Bank, type ); + m_geometry.handle = GfxRenderer->Insert( Indices, Vertices, Userdata, Bank, type ); } if( m_geometry.handle != 0 ) { @@ -1705,6 +1707,7 @@ void TModel3d::SaveToBinFile(std::string const &FileName) sn_utils::ls_uint32(s, MAKE_ID4('E', '3', 'D', '0')); auto const e3d_spos = s.tellp(); sn_utils::ls_uint32(s, 0); + bool has_any_userdata = Root->HasAnyVertexUserData(); { sn_utils::ls_uint32(s, MAKE_ID4('S', 'U', 'B', '0')); @@ -1731,20 +1734,26 @@ void TModel3d::SaveToBinFile(std::string const &FileName) Root->serialize_indices( s, indexsize ); if (!(Global.iConvertModels & 8)) { - sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '1' + 4 ) ); + int modeltype = 1; + if(has_any_userdata) modeltype |= 4; + sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' + modeltype ) ); sn_utils::ls_uint32( s, 8 + m_vertexcount * 20 ); - Root->serialize_geometry( s, true, true, true ); + Root->serialize_geometry( s, true, true, has_any_userdata ); } else { - sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '2' + 4 ) ); + int modeltype = 2; + if(has_any_userdata) modeltype |= 4; + sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' + modeltype ) ); sn_utils::ls_uint32( s, 8 + m_vertexcount * 48 ); - Root->serialize_geometry( s, false, true, true ); + Root->serialize_geometry( s, false, true, has_any_userdata ); } } else { - sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' + 4 ) ); + int modeltype = 0; + if(has_any_userdata) modeltype |= 4; + sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' + modeltype ) ); sn_utils::ls_uint32( s, 8 + m_vertexcount * 32 ); - Root->serialize_geometry( s, false, false, true ); + Root->serialize_geometry( s, false, false, has_any_userdata ); } if (textures.size()) @@ -1884,16 +1893,20 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) auto &submodel { Root[ submodeloffset.second ] }; auto const &submodelgeometry { submodel.m_geometry }; submodel.Vertices.resize( submodelgeometry.vertex_count ); + if(hasuserdata) + submodel.Userdata.resize( submodelgeometry.vertex_count ); m_vertexcount += submodelgeometry.vertex_count; switch( vertextype & 3 ) { case 0: { // legacy vnt0 format - for( auto &vertex : submodel.Vertices ) { - vertex.deserialize( s, hastangents, hasuserdata ); + for( int i = 0; i < submodel.Vertices.size(); ++i ) { + submodel.Vertices[i].deserialize( s, hastangents ); + if(hasuserdata) + submodel.Userdata[i].deserialize( s ); if( submodel.eType < TP_ROTATOR ) { // normal vectors debug routine if( ( false == submodel.m_normalizenormals ) - && ( std::abs( glm::length2( vertex.normal ) - 1.0f ) > 0.01f ) ) { + && ( std::abs( glm::length2( submodel.Vertices[i].normal ) - 1.0f ) > 0.01f ) ) { submodel.m_normalizenormals = TSubModel::normalize; // we don't know if uniform scaling would suffice WriteLog( "Bad model: non-unit normal vector(s) encountered during sub-model geometry deserialization", logtype::model ); } @@ -1903,15 +1916,19 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) } case 1: { // expanded chunk formats - for( auto &vertex : submodel.Vertices ) { - vertex.deserialize_packed( s, hastangents, hasuserdata ); + for( int i = 0; i < submodel.Vertices.size(); ++i ) { + submodel.Vertices[i].deserialize_packed( s, hastangents ); + if(hasuserdata) + submodel.Userdata[i].deserialize_packed( s ); } break; } case 2: { // expanded chunk formats - for( auto &vertex : submodel.Vertices ) { - vertex.deserialize( s, hastangents, hasuserdata ); + for( int i = 0; i < submodel.Vertices.size(); ++i ) { + submodel.Vertices[i].deserialize( s, hastangents ); + if(hasuserdata) + submodel.Userdata[i].deserialize( s ); } break; } @@ -2042,7 +2059,7 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) if( false == hastangents ) { gfx::calculate_tangents( Root[i].Vertices, Root[i].Indices, type ); } - Root[i].m_geometry.handle = GfxRenderer->Insert( Root[i].Indices, Root[i].Vertices, m_geometrybank, type ); + Root[i].m_geometry.handle = GfxRenderer->Insert( Root[i].Indices, Root[i].Vertices, Root[i].Userdata, m_geometrybank, type ); } } @@ -2181,6 +2198,14 @@ void TSubModel::BinInit(TSubModel *s, float4x4 *m, std::vector *t, normalize ); } } +} + +bool TSubModel::HasAnyVertexUserData() const +{ + for(const TSubModel* sm = this; sm; sm = sm->Next){ + if(!sm->Userdata.empty() || (sm->Child && sm->Child->HasAnyVertexUserData())) return true; + } + return false; }; void TModel3d::LoadFromBinFile(std::string const &FileName, bool dynamic) diff --git a/Model3d.h b/Model3d.h index e31425ad..b767b3f8 100644 --- a/Model3d.h +++ b/Model3d.h @@ -85,6 +85,8 @@ private: public: // chwilowo TAnimType b_Anim{ TAnimType::at_None }; + bool HasAnyVertexUserData() const; + private: uint32_t iFlags{ 0x0200 }; // bit 9=1: submodel został utworzony a nie ustawiony na wczytany plik // flagi informacyjne: @@ -144,6 +146,7 @@ public: // chwilowo float3 v_TransVector { 0.0f, 0.0f, 0.0f }; geometry_data m_geometry { /*this,*/ { 0, 0 }, 0, 0, 0, 0 }; gfx::vertex_array Vertices; + gfx::userdata_array Userdata; gfx::index_array Indices; float m_boundingradius { 0 }; std::uintptr_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację diff --git a/Track.cpp b/Track.cpp index 9cf67496..ec859e91 100644 --- a/Track.cpp +++ b/Track.cpp @@ -1140,6 +1140,7 @@ void TTrack::create_map_geometry(std::vector &Bank, const gfx { if (iCategoryFlag != 1) return; // only tracks for now + gfx::userdata_array empty_userdata{}; switch (eType) { @@ -1148,7 +1149,7 @@ void TTrack::create_map_geometry(std::vector &Bank, const gfx Segment->render_lines(vertices, 0.5f); std::copy(vertices.begin(), vertices.end(), std::back_inserter(Bank)); - extra_map_geometry = GfxRenderer->Insert(vertices, Extra, GL_LINES); + extra_map_geometry = GfxRenderer->Insert(vertices, empty_userdata, Extra, GL_LINES); break; } @@ -1157,12 +1158,12 @@ void TTrack::create_map_geometry(std::vector &Bank, const gfx SwitchExtension->Segments[0]->render_lines(vertices, 0.5f); std::copy(vertices.begin(), vertices.end(), std::back_inserter(Bank)); - SwitchExtension->map_geometry[0] = GfxRenderer->Insert(vertices, Extra, GL_LINES); + SwitchExtension->map_geometry[0] = GfxRenderer->Insert(vertices, empty_userdata, Extra, GL_LINES); vertices.clear(); SwitchExtension->Segments[1]->render_lines(vertices, 0.5f); std::copy(vertices.begin(), vertices.end(), std::back_inserter(Bank)); - SwitchExtension->map_geometry[1] = GfxRenderer->Insert(vertices, Extra, GL_LINES); + SwitchExtension->map_geometry[1] = GfxRenderer->Insert(vertices, empty_userdata, Extra, GL_LINES); break; } default: @@ -1303,7 +1304,7 @@ glm::vec3 TTrack::get_nearest_point(const glm::dvec3 &point) const // wypełnianie tablic VBO void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { - + gfx::userdata_array empty_userdata; switch (iCategoryFlag & 15) { case 1: // tor @@ -1323,11 +1324,11 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { gfx::vertex_array vertices; Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid > 0, texturelength); if( ( Bank != 0 ) && ( true == Geometry2.empty() ) ) { - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); } if( ( Bank == 0 ) && ( false == Geometry2.empty() ) ) { // special variant, replace existing data for a turntable track - GfxRenderer->Replace( vertices, Geometry2[ 0 ], GL_TRIANGLE_STRIP ); + GfxRenderer->Replace(vertices, empty_userdata, Geometry2[0], GL_TRIANGLE_STRIP); } } if (m_material1) @@ -1337,18 +1338,18 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { gfx::vertex_array vertices; if( ( Bank != 0 ) && ( true == Geometry1.empty() ) ) { Segment->RenderLoft( vertices, m_origin, rpts1, iTrapezoid > 0, texturelength ); - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); // reuse the scratchpad Segment->RenderLoft( vertices, m_origin, rpts2, iTrapezoid > 0, texturelength ); - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); } if( ( Bank == 0 ) && ( false == Geometry1.empty() ) ) { // special variant, replace existing data for a turntable track Segment->RenderLoft( vertices, m_origin, rpts1, iTrapezoid > 0, texturelength ); - GfxRenderer->Replace( vertices, Geometry1[ 0 ], GL_TRIANGLE_STRIP ); + GfxRenderer->Replace(vertices, empty_userdata, Geometry1[0], GL_TRIANGLE_STRIP); vertices.clear(); // reuse the scratchpad Segment->RenderLoft( vertices, m_origin, rpts2, iTrapezoid > 0, texturelength ); - GfxRenderer->Replace( vertices, Geometry1[ 1 ], GL_TRIANGLE_STRIP ); + GfxRenderer->Replace(vertices, empty_userdata, Geometry1[1], GL_TRIANGLE_STRIP); } } break; @@ -1373,21 +1374,21 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { // composed from two parts: transition from blade to regular rail, and regular rail SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, true, texturelength, 1.0, 0, bladelength / 2, { SwitchExtension->fOffset2, SwitchExtension->fOffset2 / 2 } ); SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, bladelength / 2, bladelength, { SwitchExtension->fOffset2 / 2, 0.f } ); - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); // fixed parts SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, bladelength ); - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); if( jointlength > 0 ) { // part of the diverging rail touched by wheels of vehicle going straight SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, 0, jointlength ); - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } // other rail, full length SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength ); - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } if( m_material2 ) { @@ -1396,15 +1397,15 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { // composed from two parts: transition from blade to regular rail, and regular rail SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, true, texturelength, 1.0, 0, bladelength / 2, { -fMaxOffset + SwitchExtension->fOffset1, ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2 } ); SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, bladelength / 2, bladelength, { ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2, 0.f } ); - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); // fixed parts SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, bladelength ); - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); // diverging rail, potentially minus part touched by wheels of vehicle going straight SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, jointlength ); - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } } @@ -1417,22 +1418,22 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { // composed from two parts: transition from blade to regular rail, and regular rail SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, true, texturelength, 1.0, 0, bladelength / 2, { -SwitchExtension->fOffset2, -SwitchExtension->fOffset2 / 2 } ); SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, bladelength / 2, bladelength, { -SwitchExtension->fOffset2 / 2, 0.f } ); - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); // fixed parts // prawa szyna za iglicą SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, bladelength ); - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); if( jointlength > 0 ) { // part of the diverging rail touched by wheels of vehicle going straight SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, 0, jointlength ); - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } // other rail, full length SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength ); - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } if( m_material2 ) { @@ -1441,16 +1442,16 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { // composed from two parts: transition from blade to regular rail, and regular rail SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, true, texturelength, 1.0, 0, bladelength / 2, { fMaxOffset - SwitchExtension->fOffset1, ( fMaxOffset - SwitchExtension->fOffset1 ) / 2 } ); SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, bladelength / 2, bladelength, { ( fMaxOffset - SwitchExtension->fOffset1 ) / 2, 0.f } ); - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); // fixed parts // lewa szyna za iglicą SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, bladelength ); - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); // diverging rail, potentially minus part touched by wheels of vehicle going straight SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, jointlength ); - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } } @@ -1459,7 +1460,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { if( true == Global.CreateSwitchTrackbeds ) { gfx::vertex_array vertices; create_switch_trackbed( vertices ); - SwitchExtension->Geometry3 = GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ); + SwitchExtension->Geometry3 = GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP); vertices.clear(); } @@ -1482,7 +1483,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { auto const texturelength { texture_length( m_material1 ) }; gfx::vertex_array vertices; Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid > 0, texturelength); - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); } if (m_material2) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) @@ -1495,13 +1496,13 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { if( ( fTexHeight1 >= 0.0 ) || ( slop != 0.0 ) ) { // tylko jeśli jest z prawej Segment->RenderLoft( vertices, m_origin, rpts1, iTrapezoid > 0, texturelength ); - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) { // tylko jeśli jest z lewej Segment->RenderLoft( vertices, m_origin, rpts2, iTrapezoid > 0, texturelength ); - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } } @@ -1578,22 +1579,22 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) { SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); if( true == render ) { - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } SwitchExtension->Segments[ 3 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); if( true == render ) { - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } SwitchExtension->Segments[ 4 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); if( true == render ) { - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } SwitchExtension->Segments[ 5 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); if( true == render ) { - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } } @@ -1603,17 +1604,17 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) { SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); // z P2 do P4 if( true == render ) { - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); // z P4 do P3=P1 (odwrócony) if( true == render ) { - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, true, texturelength, 1.0, 0, 0, {}, &b, render ); // z P1 do P2 if( true == render ) { - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } } @@ -1668,7 +1669,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { cosa0 * u + sina0 * v + 0.5, -sina0 * u + cosa0 * v + 0.5 } ); } - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_FAN ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_FAN)); } break; } // tt_cross @@ -1687,7 +1688,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { { // tworzenie trójkątów nawierzchni szosy gfx::vertex_array vertices; Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid > 0, fTexLength); - Geometry1.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry1.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); } if (m_material2) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) @@ -1695,10 +1696,10 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { create_road_side_profile( rpts1, rpts2, bpts1 ); gfx::vertex_array vertices; Segment->RenderLoft( vertices, m_origin, rpts1, iTrapezoid > 0, fTexLength ); - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); Segment->RenderLoft( vertices, m_origin, rpts2, iTrapezoid > 0, fTexLength ); - Geometry2.emplace_back( GfxRenderer->Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + Geometry2.emplace_back(GfxRenderer->Insert(vertices, empty_userdata, Bank, GL_TRIANGLE_STRIP)); vertices.clear(); } } @@ -1925,6 +1926,7 @@ void TTrack::RaAnimListAdd(TTrack *t) TTrack * TTrack::RaAnimate() { // wykonanie rekurencyjne animacji, wywoływane przed wyświetleniem sektora // zwraca wskaźnik toru wymagającego dalszej animacji + gfx::userdata_array empty_userdata; if( SwitchExtension->pNextAnim ) SwitchExtension->pNextAnim = SwitchExtension->pNextAnim->RaAnimate(); bool m = true; // animacja trwa @@ -1976,7 +1978,7 @@ TTrack * TTrack::RaAnimate() // composed from two parts: transition from blade to regular rail, and regular rail SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, true, texturelength, 1.0, 0, bladelength / 2, { SwitchExtension->fOffset2, SwitchExtension->fOffset2 / 2 } ); SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, bladelength / 2, bladelength, { SwitchExtension->fOffset2 / 2, 0.f } ); - GfxRenderer->Replace( vertices, Geometry1[ 0 ], GL_TRIANGLE_STRIP ); + GfxRenderer->Replace(vertices, empty_userdata, Geometry1[0], GL_TRIANGLE_STRIP); vertices.clear(); } if( m_material2 ) { @@ -1985,7 +1987,7 @@ TTrack * TTrack::RaAnimate() // composed from two parts: transition from blade to regular rail, and regular rail SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, true, texturelength, 1.0, 0, bladelength / 2, { -fMaxOffset + SwitchExtension->fOffset1, ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2 } ); SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, bladelength / 2, bladelength, { ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2, 0.f } ); - GfxRenderer->Replace( vertices, Geometry2[ 0 ], GL_TRIANGLE_STRIP ); + GfxRenderer->Replace(vertices, empty_userdata, Geometry2[0], GL_TRIANGLE_STRIP); vertices.clear(); } } @@ -1996,7 +1998,7 @@ TTrack * TTrack::RaAnimate() // composed from two parts: transition from blade to regular rail, and regular rail SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, true, texturelength, 1.0, 0, bladelength / 2, { -SwitchExtension->fOffset2, -SwitchExtension->fOffset2 / 2 } ); SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, false, texturelength, 1.0, bladelength / 2, bladelength, { -SwitchExtension->fOffset2 / 2, 0.f } ); - GfxRenderer->Replace( vertices, Geometry1[ 0 ], GL_TRIANGLE_STRIP ); + GfxRenderer->Replace(vertices, empty_userdata, Geometry1[0], GL_TRIANGLE_STRIP); vertices.clear(); } if( m_material2 ) { @@ -2005,7 +2007,7 @@ TTrack * TTrack::RaAnimate() // composed from two parts: transition from blade to regular rail, and regular rail SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, true, texturelength, 1.0, 0, bladelength / 2, { fMaxOffset - SwitchExtension->fOffset1, ( fMaxOffset - SwitchExtension->fOffset1 ) / 2 } ); SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, false, texturelength, 1.0, bladelength / 2, bladelength, { ( fMaxOffset - SwitchExtension->fOffset1 ) / 2, 0.f } ); - GfxRenderer->Replace( vertices, Geometry2[ 0 ], GL_TRIANGLE_STRIP ); + GfxRenderer->Replace(vertices, empty_userdata, Geometry2[0], GL_TRIANGLE_STRIP); vertices.clear(); } } diff --git a/Traction.cpp b/Traction.cpp index 89f7580b..1a2811c9 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -342,7 +342,9 @@ TTraction::create_geometry( gfx::geometrybank_handle const &Bank ) { } auto const elementcount = vertices.size() / 2; - m_geometry = GfxRenderer->Insert( vertices, Bank, GL_LINES ); + + gfx::userdata_array empty_userdata{}; + m_geometry = GfxRenderer->Insert( vertices, empty_userdata, Bank, GL_LINES ); return elementcount; } diff --git a/geometrybank.cpp b/geometrybank.cpp index cb6b3c1e..4371bb3f 100644 --- a/geometrybank.cpp +++ b/geometrybank.cpp @@ -23,7 +23,6 @@ basic_vertex basic_vertex::convert(world_vertex const &world, glm::dvec3 const& vertex.position = static_cast(world.position - origin); vertex.normal = world.normal; vertex.texture = world.texture; - vertex.user_data = world.user_data; return vertex; } @@ -34,13 +33,12 @@ world_vertex basic_vertex::to_world(const glm::dvec3 &origin) const vertex.position = static_cast(position) + origin; vertex.normal = normal; vertex.texture = texture; - vertex.user_data = user_data; return vertex; } void -basic_vertex::serialize( std::ostream &s, bool const Tangent, bool const UserData ) const { +basic_vertex::serialize( std::ostream &s, bool const Tangent ) const { sn_utils::s_vec3( s, position ); sn_utils::s_vec3( s, normal ); @@ -49,13 +47,10 @@ basic_vertex::serialize( std::ostream &s, bool const Tangent, bool const UserDat if( Tangent ) { sn_utils::s_vec4( s, tangent ); } - if( UserData ) { - sn_utils::s_vec4( s, user_data ); - } } void -basic_vertex::deserialize( std::istream &s, bool const Tangent, bool const UserData ) { +basic_vertex::deserialize( std::istream &s, bool const Tangent ) { position = sn_utils::d_vec3( s ); normal = sn_utils::d_vec3( s ); @@ -64,13 +59,10 @@ basic_vertex::deserialize( std::istream &s, bool const Tangent, bool const UserD if( Tangent ) { tangent = sn_utils::d_vec4( s ); } - if( UserData ) { - user_data = sn_utils::d_vec4( s ); - } } void -basic_vertex::serialize_packed( std::ostream &s, bool const Tangent, bool const UserData ) const { +basic_vertex::serialize_packed( std::ostream &s, bool const Tangent ) const { sn_utils::ls_uint64( s, glm::packHalf4x16( { position, 0.f } ) ); sn_utils::ls_uint32( s, glm::packSnorm3x10_1x2( { normal, 0.f } ) ); @@ -79,13 +71,10 @@ basic_vertex::serialize_packed( std::ostream &s, bool const Tangent, bool const if( Tangent ) { sn_utils::ls_uint32( s, glm::packSnorm3x10_1x2( tangent ) ); } - if( UserData ) { - sn_utils::ls_uint64( s, glm::packHalf4x16( user_data )); - } } void -basic_vertex::deserialize_packed( std::istream &s, bool const Tangent, bool const UserData ) { +basic_vertex::deserialize_packed( std::istream &s, bool const Tangent ) { position = glm::unpackHalf4x16( sn_utils::ld_uint64( s ) ); normal = glm::unpackSnorm3x10_1x2( sn_utils::ld_uint32( s ) ); @@ -94,9 +83,6 @@ basic_vertex::deserialize_packed( std::istream &s, bool const Tangent, bool cons if( Tangent ) { tangent = glm::unpackSnorm3x10_1x2( sn_utils::ld_uint32( s ) ); } - if( UserData ) { - user_data = glm::unpackHalf4x16( sn_utils::ld_uint64( s ) ); - } } // based on @@ -218,14 +204,18 @@ void calculate_tangents(vertex_array &vertices, index_array const &indices, int } } -void calculate_indices( index_array &Indices, vertex_array &Vertices, float tolerancescale ) { +void calculate_indices( index_array &Indices, vertex_array &Vertices, userdata_array &Userdata, float tolerancescale ) { Indices.resize( Vertices.size() ); std::iota( std::begin( Indices ), std::end( Indices ), 0 ); // gather instances of used vertices, replace the original vertex bank with it after you're done - vertex_array indexedvertices; + vertex_array indexedvertices{}; + userdata_array indexeduserdata{}; indexedvertices.reserve( std::max( 100, Vertices.size() / 3 ) ); // optimistic guesstimate, but should reduce re-allocation somewhat - auto const matchtolerance { 1e-5f * tolerancescale }; + bool has_userdata = !Userdata.empty(); + if (has_userdata) + indexeduserdata.reserve(std::max(100, Userdata.size() / 3)); + auto const matchtolerance { 1e-5f * tolerancescale }; for( auto idx = 0; idx < Indices.size(); ++idx ) { if( Indices[ idx ] < idx ) { // this index is pointing to a vertex out of linear order, i.e. it's an already processed duplicate we can skip @@ -235,31 +225,38 @@ void calculate_indices( index_array &Indices, vertex_array &Vertices, float tole Indices[ idx ] = indexedvertices.size(); // see if there's any pointers in the remaining index subrange to similar enough vertices // if found, remap these to use our current vertex instead - auto vertex { Vertices[ idx ] }; + const auto& vertex { Vertices[ idx ] }; + const auto* userdata{ has_userdata ? &Userdata[idx] : nullptr }; auto matchiter { std::cbegin( Vertices ) + idx }; + auto matchuserdata = userdata; for( auto matchidx = idx + 1; matchidx < Indices.size() && matchidx < idx + Global.iConvertIndexRange; ++matchidx ) { ++matchiter; + ++matchuserdata; if( ( glm::all( glm::epsilonEqual( vertex.position, matchiter->position, matchtolerance ) ) ) && ( glm::all( glm::epsilonEqual( vertex.normal, matchiter->normal, matchtolerance ) ) ) - && ( glm::all( glm::epsilonEqual( vertex.texture, matchiter->texture, matchtolerance ) ) ) ) { + && ( glm::all( glm::epsilonEqual( vertex.texture, matchiter->texture, matchtolerance ) ) ) + && ( !userdata || glm::all( glm::epsilonEqual( userdata->data, matchuserdata->data, matchtolerance ) ) ) ) { Indices[ matchidx ] = Indices[ idx ]; } } indexedvertices.emplace_back( vertex ); + if(userdata) + indexeduserdata.emplace_back( *userdata ); } // done indexing, swap the source vertex bank with the processed one Vertices.swap( indexedvertices ); + Userdata.swap( indexeduserdata ); } // generic geometry bank class, allows storage, update and drawing of geometry chunks // creates a new geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL gfx::geometry_handle -geometry_bank::create( gfx::vertex_array &Vertices, unsigned int const Type ) { +geometry_bank::create( gfx::vertex_array &Vertices, userdata_array &Userdata, unsigned int const Type ) { - if( true == Vertices.empty() ) { return { 0, 0 }; } + if(Vertices.empty()) { return { 0, 0 }; } - m_chunks.emplace_back( Vertices, Type ); + m_chunks.emplace_back( Vertices, Userdata, Type ); // NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication gfx::geometry_handle chunkhandle { 0, static_cast(m_chunks.size()) }; // template method implementation @@ -270,11 +267,11 @@ geometry_bank::create( gfx::vertex_array &Vertices, unsigned int const Type ) { // creates a new indexed geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL gfx::geometry_handle -geometry_bank::create( gfx::index_array &Indices, gfx::vertex_array &Vertices, unsigned int const Type ) { +geometry_bank::create( gfx::index_array &Indices, gfx::vertex_array &Vertices, userdata_array &Userdata, unsigned int const Type ) { - if( true == Vertices.empty() ) { return { 0, 0 }; } + if(Vertices.empty()) { return { 0, 0 }; } - m_chunks.emplace_back( Indices, Vertices, Type ); + m_chunks.emplace_back( Indices, Vertices, Userdata, Type ); // NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication gfx::geometry_handle chunkhandle { 0, static_cast(m_chunks.size()) }; // template method implementation @@ -285,7 +282,7 @@ geometry_bank::create( gfx::index_array &Indices, gfx::vertex_array &Vertices, u // replaces data of specified chunk with the supplied vertex data, starting from specified offset bool -geometry_bank::replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset ) { +geometry_bank::replace( gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset ) { if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return false; } @@ -295,13 +292,16 @@ geometry_bank::replace( gfx::vertex_array &Vertices, gfx::geometry_handle const && ( Vertices.size() == chunk.vertices.size() ) ) { // check first if we can get away with a simple swap... chunk.vertices.swap( Vertices ); + chunk.userdata.swap( Userdata ); } else { // ...otherwise we need to do some legwork // NOTE: if the offset is larger than existing size of the chunk, it'll bridge the gap with 'blank' vertices // TBD: we could bail out with an error instead if such request occurs chunk.vertices.resize( Offset + Vertices.size(), gfx::basic_vertex() ); + chunk.userdata.resize( Offset + Userdata.size(), gfx::vertex_userdata() ); chunk.vertices.insert( std::end( chunk.vertices ), std::begin( Vertices ), std::end( Vertices ) ); + chunk.userdata.insert( std::end( chunk.userdata ), std::begin( Userdata ), std::end( Userdata ) ); } // template method implementation replace_( Geometry ); @@ -311,11 +311,11 @@ geometry_bank::replace( gfx::vertex_array &Vertices, gfx::geometry_handle const // adds supplied vertex data at the end of specified chunk bool -geometry_bank::append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry ) { +geometry_bank::append( gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometry_handle const &Geometry ) { if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return false; } - return replace( Vertices, Geometry, gfx::geometry_bank::chunk( Geometry ).vertices.size() ); + return replace( Vertices, Userdata, Geometry, gfx::geometry_bank::chunk( Geometry ).vertices.size() ); } // draws geometry stored in specified chunk @@ -346,6 +346,13 @@ geometry_bank::vertices( gfx::geometry_handle const &Geometry ) const { return geometry_bank::chunk( Geometry ).vertices; } +// provides direct access to vertex user data of specfied chunk +userdata_array const & +geometry_bank::userdata( gfx::geometry_handle const &Geometry ) const { + + return geometry_bank::chunk( Geometry ).userdata; +} + // geometry bank manager, holds collection of geometry banks // performs a resource sweep @@ -366,9 +373,9 @@ geometrybank_manager::register_bank(std::unique_ptr bank) { // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL gfx::geometry_handle -geometrybank_manager::create_chunk(vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) { +geometrybank_manager::create_chunk(vertex_array &Vertices, userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type ) { - auto const newchunkhandle = bank( Geometry ).first->create( Vertices, Type ); + auto const newchunkhandle = bank( Geometry ).first->create( Vertices, Userdata, Type ); if( newchunkhandle.chunk != 0 ) { return { Geometry.bank, newchunkhandle.chunk }; } else { return { 0, 0 }; } @@ -376,9 +383,9 @@ geometrybank_manager::create_chunk(vertex_array &Vertices, gfx::geometrybank_han // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL gfx::geometry_handle -geometrybank_manager::create_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, unsigned int const Type ) { +geometrybank_manager::create_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, unsigned int const Type ) { - auto const newchunkhandle = bank( Geometry ).first->create( Indices, Vertices, Type ); + auto const newchunkhandle = bank( Geometry ).first->create( Indices, Vertices, Userdata, Type ); if( newchunkhandle.chunk != 0 ) { return { Geometry.bank, newchunkhandle.chunk }; } else { return { 0, 0 }; } @@ -386,16 +393,16 @@ geometrybank_manager::create_chunk( gfx::index_array &Indices, gfx::vertex_array // replaces data of specified chunk with the supplied vertex data, starting from specified offset bool -geometrybank_manager::replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset ) { +geometrybank_manager::replace( gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset ) { - return bank( Geometry ).first->replace( Vertices, Geometry, Offset ); + return bank( Geometry ).first->replace( Vertices, Userdata, Geometry, Offset ); } // adds supplied vertex data at the end of specified chunk bool -geometrybank_manager::append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry ) { +geometrybank_manager::append( gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometry_handle const &Geometry ) { - return bank( Geometry ).first->append( Vertices, Geometry ); + return bank( Geometry ).first->append( Vertices, Userdata, Geometry ); } // draws geometry stored in specified chunk void @@ -423,4 +430,31 @@ geometrybank_manager::vertices( gfx::geometry_handle const &Geometry ) const { return bank( Geometry ).first->vertices( Geometry ); } +// provides direct access to vertex user data of specfied chunk +gfx::userdata_array const & +geometrybank_manager::userdata( gfx::geometry_handle const &Geometry ) const { + + return bank( Geometry ).first->userdata( Geometry ); +} + +void vertex_userdata::serialize(std::ostream &s) const +{ + sn_utils::s_vec4(s, data); +} + +void vertex_userdata::deserialize(std::istream &s) +{ + data = sn_utils::d_vec4(s); +} + +void vertex_userdata::serialize_packed(std::ostream &s) const +{ + sn_utils::ls_uint64(s, glm::packHalf4x16(data)); +} + +void vertex_userdata::deserialize_packed(std::istream &s) +{ + data = glm::unpackHalf4x16(sn_utils::ld_uint64(s)); +} + } // namespace gfx diff --git a/geometrybank.h b/geometrybank.h index 5f54146a..e12d51ba 100644 --- a/geometrybank.h +++ b/geometrybank.h @@ -21,7 +21,6 @@ struct basic_vertex { glm::vec3 normal; // 3d space glm::vec2 texture; // uv space glm::vec4 tangent; // xyz - tangent, w - handedness - glm::vec4 user_data; // user data (for color or additional uv channels, not subject to post-processing) basic_vertex() = default; basic_vertex( glm::vec3 Position, glm::vec3 Normal, glm::vec2 Texture ) : @@ -29,10 +28,18 @@ struct basic_vertex { {} static basic_vertex convert(world_vertex const &world, glm::dvec3 const &origin); world_vertex to_world(glm::dvec3 const &origin = glm::dvec3(0.)) const; - void serialize( std::ostream&, bool const Tangent = false, bool const UserData = false ) const; - void deserialize( std::istream&, bool const Tangent = false, bool const UserData = false ); - void serialize_packed( std::ostream&, bool const Tangent = false, bool const UserData = false ) const; - void deserialize_packed( std::istream&, bool const Tangent = false, bool const UserData = false ); + void serialize( std::ostream&, bool const Tangent = false ) const; + void deserialize( std::istream&, bool const Tangent = false ); + void serialize_packed( std::ostream&, bool const Tangent = false ) const; + void deserialize_packed( std::istream&, bool const Tangent = false ); +}; + +struct vertex_userdata{ + glm::vec4 data; // user data (for color or additional uv channels, not subject to post-processing) + void serialize( std::ostream& ) const; + void deserialize( std::istream& ); + void serialize_packed( std::ostream& ) const; + void deserialize_packed( std::istream& ); }; // data streams carried in a vertex @@ -55,10 +62,11 @@ struct stream_units { using basic_index = std::uint32_t; using vertex_array = std::vector; +using userdata_array = std::vector; using index_array = std::vector; void calculate_tangents( vertex_array &vertices, index_array const &indices, int const type ); -void calculate_indices( index_array &Indices, vertex_array &Vertices, float tolerancescale = 1.0f ); +void calculate_indices( index_array &Indices, vertex_array &Vertices, userdata_array &Userdata, float tolerancescale = 1.0f ); // generic geometry bank class, allows storage, update and drawing of geometry chunks @@ -102,13 +110,13 @@ public: // methods: // creates a new geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL - auto create( gfx::vertex_array &Vertices, unsigned int const Type ) -> gfx::geometry_handle; + auto create( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, unsigned int const Type ) -> gfx::geometry_handle; // creates a new indexed geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL - auto create( gfx::index_array &Indices, gfx::vertex_array &Vertices, unsigned int const Type ) -> gfx::geometry_handle; + auto create( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, unsigned int const Type ) -> gfx::geometry_handle; // replaces vertex data of specified chunk with the supplied data, starting from specified offset - auto replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool; + auto replace( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool; // adds supplied vertex data at the end of specified chunk - auto append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry ) -> bool; + auto append( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, gfx::geometry_handle const &Geometry ) -> bool; // draws geometry stored in specified chunk auto draw( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams = basic_streams ) -> std::size_t; // draws geometry stored in supplied list of chunks @@ -122,8 +130,10 @@ public: void release(); // provides direct access to index data of specfied chunk auto indices( gfx::geometry_handle const &Geometry ) const -> gfx::index_array const &; - // provides direct access to vertex data of specfied chunk - auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &; + // provides direct access to vertex data of specfied chunk + auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &; + // provides direct access to vertex user data of specfied chunk + auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &; protected: // types: @@ -131,17 +141,20 @@ protected: unsigned int type; // kind of geometry used by the chunk gfx::vertex_array vertices; // geometry data gfx::index_array indices; // index data + gfx::userdata_array userdata; // NOTE: constructor doesn't copy provided geometry data, but moves it - geometry_chunk( gfx::vertex_array &Vertices, unsigned int Type ) : + geometry_chunk( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, unsigned int Type ) : type( Type ) { vertices.swap( Vertices ); + userdata.swap( Userdata ); } // NOTE: constructor doesn't copy provided geometry data, but moves it - geometry_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, unsigned int Type ) : + geometry_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, unsigned int Type ) : type( Type ) { vertices.swap( Vertices ); + userdata.swap( Userdata ); indices.swap( Indices ); } }; @@ -186,13 +199,13 @@ public: // registers a new geometry bank. returns: handle to the bank auto register_bank(std::unique_ptr bank) -> gfx::geometrybank_handle; // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL - auto create_chunk( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle; + auto create_chunk( gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle; // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL - auto create_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, unsigned int const Type ) -> gfx::geometry_handle; + auto create_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, unsigned int const Type ) -> gfx::geometry_handle; // replaces data of specified chunk with the supplied vertex data, starting from specified offset - auto replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool; + auto replace( gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool; // adds supplied vertex data at the end of specified chunk - auto append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry ) -> bool; + auto append( gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry ) -> bool; // draws geometry stored in specified chunk void draw( gfx::geometry_handle const &Geometry, unsigned int const Streams = basic_streams ); template @@ -202,8 +215,10 @@ public: ++First; } } // provides direct access to index data of specfied chunk auto indices( gfx::geometry_handle const &Geometry ) const -> gfx::index_array const &; - // provides direct access to vertex data of specfied chunk - auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &; + // provides direct access to vertex data of specfied chunk + auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &; + // provides direct access to vertex data of specfied chunk + auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &; // sets target texture unit for the texture data stream auto units() -> gfx::stream_units & { return m_units; } // provides access to primitives count diff --git a/nullrenderer.h b/nullrenderer.h index 71f76eb1..b0b8696c 100644 --- a/nullrenderer.h +++ b/nullrenderer.h @@ -66,28 +66,25 @@ public: gfx::geometrybank_handle Create_Bank() override { return m_geometry.register_bank(std::make_unique()); } // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL - gfx::geometry_handle - Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override { return m_geometry.create_chunk( Indices, Vertices, Geometry, Type ); } + gfx::geometry_handle Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override { + return m_geometry.create_chunk( Indices, Vertices, Userdata, Geometry, Type ); } // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL - gfx::geometry_handle - Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override { + gfx::geometry_handle Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override { gfx::calculate_tangents(Vertices, gfx::index_array(), Type); - return m_geometry.create_chunk(Vertices, Geometry, Type); + return m_geometry.create_chunk(Vertices, Userdata, Geometry, Type); } // replaces data of specified chunk with the supplied vertex data, starting from specified offset - bool - Replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset = 0 ) override { + bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override { gfx::calculate_tangents(Vertices, gfx::index_array(), Type); - return m_geometry.replace(Vertices, Geometry, Offset); + return m_geometry.replace(Vertices, Userdata, Geometry, Offset); } // adds supplied vertex data at the end of specified chunk - bool - Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) override { + bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override { gfx::calculate_tangents(Vertices, gfx::index_array(), Type); - return m_geometry.append(Vertices, Geometry); + return m_geometry.append(Vertices, Userdata, Geometry); } // provides direct access to index data of specfied chunk gfx::index_array const & @@ -95,6 +92,9 @@ public: // provides direct access to vertex data of specfied chunk gfx::vertex_array const & Vertices( gfx::geometry_handle const &Geometry ) const override { return m_geometry.vertices(Geometry); } + // provides direct access to vertex data of specfied chunk + gfx::userdata_array const & + UserData( gfx::geometry_handle const &Geometry ) const override { return m_geometry.userdata(Geometry); } // material methods material_handle Fetch_Material( std::string const &Filename, bool const Loadnow = true ) override { diff --git a/opengl33geometrybank.cpp b/opengl33geometrybank.cpp index e5dd6f31..63f0ef8c 100644 --- a/opengl33geometrybank.cpp +++ b/opengl33geometrybank.cpp @@ -53,6 +53,7 @@ void opengl33_vaogeometrybank::setup_buffer() vertexcount{ 0 }, indexcount{ 0 }; auto chunkiterator = m_chunks.cbegin(); + bool has_userdata{ false }; for( auto &chunkrecord : m_chunkrecords ) { // fill records for all chunks, based on the chunk data chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one @@ -61,7 +62,9 @@ void opengl33_vaogeometrybank::setup_buffer() vertexcount += chunkrecord.vertex_count; chunkrecord.index_offset = indexcount; chunkrecord.index_count = chunkiterator->indices.size(); + chunkrecord.has_userdata = !chunkiterator->userdata.empty(); indexcount += chunkrecord.index_count; + has_userdata |= chunkrecord.has_userdata; ++chunkiterator; } // the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist @@ -89,13 +92,17 @@ void opengl33_vaogeometrybank::setup_buffer() m_vertexbuffer.emplace(); // NOTE: we're using static_draw since it's generally true for all we have implemented at the moment // TODO: allow to specify usage hint at the object creation, and pass it here - m_vertexbuffer->allocate( gl::buffer::ARRAY_BUFFER, vertexcount * sizeof( gfx::basic_vertex ), GL_STATIC_DRAW ); + const int vertex_size = has_userdata ? sizeof( gfx::basic_vertex ) + sizeof(gfx::vertex_userdata) : sizeof(gfx::basic_vertex); + m_vertexbuffer->allocate( gl::buffer::ARRAY_BUFFER, static_cast(vertexcount * vertex_size), GL_STATIC_DRAW ); if( ::glGetError() == GL_OUT_OF_MEMORY ) { ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" ); throw std::bad_alloc(); } + m_vertex_count = static_cast(vertexcount); setup_attrib(); + if(has_userdata) + setup_userdata(); } void @@ -106,9 +113,12 @@ opengl33_vaogeometrybank::setup_attrib(size_t offset) m_vao->setup_attrib( *m_vertexbuffer, 1, 3, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, normal) + offset * sizeof( basic_vertex ) ); m_vao->setup_attrib( *m_vertexbuffer, 2, 2, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, texture) + offset * sizeof( basic_vertex ) ); m_vao->setup_attrib( *m_vertexbuffer, 3, 4, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, tangent) + offset * sizeof( basic_vertex ) ); - m_vao->setup_attrib( *m_vertexbuffer, 4, 4, GL_FLOAT, sizeof( basic_vertex ), offsetof(basic_vertex, user_data) + offset * sizeof( basic_vertex ) ); } +void opengl33_vaogeometrybank::setup_userdata(size_t offset) { + const size_t offset_evaluated = m_vertex_count * sizeof(basic_vertex) + offset * sizeof( vertex_userdata ); + m_vao->setup_attrib( *m_vertexbuffer, 4, 4, GL_FLOAT, sizeof( vertex_userdata ), offsetof(vertex_userdata, data) + offset_evaluated );} + // draw() subclass details // NOTE: units and stream parameters are unused, but they're part of (legacy) interface // TBD: specialized bank/manager pair without the cruft? @@ -123,14 +133,18 @@ opengl33_vaogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stre return 0; auto const &chunk = gfx::geometry_bank::chunk( Geometry ); - if( false == chunkrecord.is_good ) { + if( !chunkrecord.is_good ) { m_vao->bind(); // we may potentially need to upload new buffer data before we can draw it if( chunkrecord.index_count > 0 ) { m_indexbuffer->upload( gl::buffer::ELEMENT_ARRAY_BUFFER, chunk.indices.data(), chunkrecord.index_offset * sizeof( gfx::basic_index ), chunkrecord.index_count * sizeof( gfx::basic_index ) ); } m_vertexbuffer->upload( gl::buffer::ARRAY_BUFFER, chunk.vertices.data(), chunkrecord.vertex_offset * sizeof( gfx::basic_vertex ), chunkrecord.vertex_count * sizeof( gfx::basic_vertex ) ); - chunkrecord.is_good = true; + if(chunkrecord.has_userdata) + m_vertexbuffer->upload(gl::buffer::ARRAY_BUFFER, chunk.userdata.data(), + static_cast(m_vertex_count * sizeof(gfx::basic_vertex) + chunkrecord.vertex_offset * sizeof(gfx::vertex_userdata)), + static_cast(chunkrecord.vertex_count * sizeof(gfx::vertex_userdata))); + chunkrecord.is_good = true; } // render if( chunkrecord.index_count > 0 ) { diff --git a/opengl33geometrybank.h b/opengl33geometrybank.h index 809479a6..f6f8af0a 100644 --- a/opengl33geometrybank.h +++ b/opengl33geometrybank.h @@ -37,6 +37,7 @@ private: std::size_t vertex_count{ 0 }; // size of the chunk in the last established buffer std::size_t index_offset{ 0 }; std::size_t index_count{ 0 }; + bool has_userdata{ false }; bool is_good{ false }; // true if local content of the chunk matches the data on the opengl end }; @@ -59,6 +60,8 @@ private: setup_buffer(); void setup_attrib(size_t offset = 0); + void + setup_userdata(size_t offset = 0); void delete_buffer(); @@ -67,6 +70,7 @@ private: std::optional m_indexbuffer; // index buffer data on the opengl end std::optional m_vao; chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order + int m_vertex_count; }; } // namespace gfx diff --git a/opengl33renderer.cpp b/opengl33renderer.cpp index a03fff5a..281e362f 100644 --- a/opengl33renderer.cpp +++ b/opengl33renderer.cpp @@ -111,7 +111,8 @@ bool opengl33_renderer::Init(GLFWwindow *Window) gfx::vertex_array billboard_array{ {{-size, size, 0.f}, glm::vec3(), {1.f, 1.f}}, {{size, size, 0.f}, glm::vec3(), {0.f, 1.f}}, {{-size, -size, 0.f}, glm::vec3(), {1.f, 0.f}}, {{size, -size, 0.f}, glm::vec3(), {0.f, 0.f}}}; - m_billboardgeometry = m_geometry.create_chunk(billboard_array, geometrybank, GL_TRIANGLE_STRIP); + gfx::userdata_array userdata{}; + m_billboardgeometry = m_geometry.create_chunk(billboard_array, userdata, geometrybank, GL_TRIANGLE_STRIP); m_empty_vao = std::make_unique(); try @@ -1961,34 +1962,34 @@ gfx::geometrybank_handle opengl33_renderer::Create_Bank() } // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL -gfx::geometry_handle opengl33_renderer::Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) +gfx::geometry_handle opengl33_renderer::Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) { // NOTE: we expect indexed geometry to come with calculated tangents - return m_geometry.create_chunk( Indices, Vertices, Geometry, Type ); + return m_geometry.create_chunk( Indices, Vertices, Userdata, Geometry, Type ); } // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL -gfx::geometry_handle opengl33_renderer::Insert(gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type) +gfx::geometry_handle opengl33_renderer::Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) { gfx::calculate_tangents(Vertices, gfx::index_array(), Type); - return m_geometry.create_chunk(Vertices, Geometry, Type); + return m_geometry.create_chunk(Vertices, Userdata, Geometry, Type); } // replaces data of specified chunk with the supplied vertex data, starting from specified offset -bool opengl33_renderer::Replace(gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset) +bool opengl33_renderer::Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset) { gfx::calculate_tangents(Vertices, gfx::index_array(), Type); - return m_geometry.replace(Vertices, Geometry, Offset); + return m_geometry.replace(Vertices, Userdata, Geometry, Offset); } // adds supplied vertex data at the end of specified chunk -bool opengl33_renderer::Append(gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type) +bool opengl33_renderer::Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) { gfx::calculate_tangents(Vertices, gfx::index_array(), Type); - return m_geometry.append(Vertices, Geometry); + return m_geometry.append(Vertices, Userdata, Geometry); } // provides direct access to index data of specfied chunk @@ -2003,6 +2004,11 @@ gfx::vertex_array const &opengl33_renderer::Vertices(gfx::geometry_handle const return m_geometry.vertices(Geometry); } +gfx::userdata_array const &opengl33_renderer::UserData(const gfx::geometry_handle &Geometry) const +{ + return m_geometry.userdata(Geometry); +} + // material methods material_handle opengl33_renderer::Fetch_Material(std::string const &Filename, bool const Loadnow) { @@ -4829,3 +4835,4 @@ std::unique_ptr opengl33_renderer::create_func() } bool opengl33_renderer::renderer_register = gfx_renderer_factory::get_instance()->register_backend("modern", opengl33_renderer::create_func); + diff --git a/opengl33renderer.h b/opengl33renderer.h index 6317ca45..fc24fab6 100644 --- a/opengl33renderer.h +++ b/opengl33renderer.h @@ -58,23 +58,22 @@ class opengl33_renderer : public gfx_renderer { gfx::geometrybank_handle Create_Bank() override; // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL - gfx::geometry_handle - Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override; + gfx::geometry_handle Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override; // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL - gfx::geometry_handle - Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override; + gfx::geometry_handle Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override; // replaces data of specified chunk with the supplied vertex data, starting from specified offset - bool - Replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset = 0 ) override; + bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override; // adds supplied vertex data at the end of specified chunk - bool - Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) override; + bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override; // provides direct access to index data of specfied chunk gfx::index_array const & Indices( gfx::geometry_handle const &Geometry ) const override; // provides direct access to vertex data of specfied chunk gfx::vertex_array const & Vertices( gfx::geometry_handle const &Geometry ) const override; + // provides direct access to vertex data of specfied chunk + gfx::userdata_array const & + UserData( gfx::geometry_handle const &Geometry ) const override; // material methods material_handle Fetch_Material( std::string const &Filename, bool const Loadnow = true ) override; diff --git a/openglrenderer.cpp b/openglrenderer.cpp index 0cf6f257..ab89953e 100644 --- a/openglrenderer.cpp +++ b/openglrenderer.cpp @@ -306,7 +306,8 @@ opengl_renderer::Init( GLFWwindow *Window ) { { { size, size, 0.f }, glm::vec3(), { 0.f, 1.f } }, { { -size, -size, 0.f }, glm::vec3(), { 1.f, 0.f } }, { { size, -size, 0.f }, glm::vec3(), { 0.f, 0.f } } }; - m_billboardgeometry = m_geometry.create_chunk(billboard_array, geometrybank, GL_TRIANGLE_STRIP); + gfx::userdata_array userdata{}; + m_billboardgeometry = m_geometry.create_chunk(billboard_array, userdata, geometrybank, GL_TRIANGLE_STRIP); // prepare debug mode objects //m_quadric = ::gluNewQuadric(); //::gluQuadricNormals( m_quadric, GLU_FLAT ); @@ -1689,31 +1690,31 @@ opengl_renderer::Create_Bank() { } // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL -gfx::geometry_handle -opengl_renderer::Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) { +gfx::geometry_handle opengl_renderer::Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) +{ - return m_geometry.create_chunk( Indices, Vertices, Geometry, Type ); + return m_geometry.create_chunk( Indices, Vertices, Userdata, Geometry, Type ); } // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL -gfx::geometry_handle -opengl_renderer::Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) { +gfx::geometry_handle opengl_renderer::Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) +{ - return m_geometry.create_chunk( Vertices, Geometry, Type ); + return m_geometry.create_chunk( Vertices, Userdata, Geometry, Type ); } // replaces data of specified chunk with the supplied vertex data, starting from specified offset -bool -opengl_renderer::Replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset ) { +bool opengl_renderer::Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset) +{ - return m_geometry.replace( Vertices, Geometry, Offset ); + return m_geometry.replace( Vertices, Userdata, Geometry, Offset ); } // adds supplied vertex data at the end of specified chunk -bool -opengl_renderer::Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) { +bool opengl_renderer::Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) +{ - return m_geometry.append( Vertices, Geometry ); + return m_geometry.append( Vertices, Userdata, Geometry ); } // provides direct access to vertex data of specfied chunk @@ -1730,6 +1731,11 @@ opengl_renderer::Vertices( gfx::geometry_handle const &Geometry ) const { return m_geometry.vertices( Geometry ); } +gfx::userdata_array const &opengl_renderer::UserData(const gfx::geometry_handle &Geometry) const +{ + return m_geometry.userdata( Geometry ); +} + // material methods material_handle opengl_renderer::Fetch_Material( std::string const &Filename, bool const Loadnow ) { @@ -4510,3 +4516,4 @@ std::unique_ptr opengl_renderer::create_func() } bool opengl_renderer::renderer_register = gfx_renderer_factory::get_instance()->register_backend("legacy", opengl_renderer::create_func); + diff --git a/openglrenderer.h b/openglrenderer.h index eacf92f3..3fc22a47 100644 --- a/openglrenderer.h +++ b/openglrenderer.h @@ -58,23 +58,22 @@ public: gfx::geometrybank_handle Create_Bank() override; // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL - gfx::geometry_handle - Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override; + gfx::geometry_handle Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override; // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL - gfx::geometry_handle - Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override; + gfx::geometry_handle Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override; // replaces data of specified chunk with the supplied vertex data, starting from specified offset - bool - Replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset = 0 ) override; + bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override; // adds supplied vertex data at the end of specified chunk - bool - Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) override; + bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override; // provides direct access to index data of specfied chunk gfx::index_array const & Indices( gfx::geometry_handle const &Geometry ) const override; // provides direct access to vertex data of specfied chunk gfx::vertex_array const & Vertices( gfx::geometry_handle const &Geometry ) const override; + // provides direct access to vertex data of specfied chunk + gfx::userdata_array const & + UserData( gfx::geometry_handle const &Geometry ) const override; // material methods material_handle Fetch_Material( std::string const &Filename, bool const Loadnow = true ) override; diff --git a/renderer.h b/renderer.h index 05741923..50b0c2e6 100644 --- a/renderer.h +++ b/renderer.h @@ -35,17 +35,19 @@ public: // creates a new geometry bank. returns: handle to the bank or NULL virtual auto Create_Bank() -> gfx::geometrybank_handle = 0; // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL - virtual auto Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle = 0; + virtual auto Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) -> gfx::geometry_handle = 0; // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL - virtual auto Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle = 0; + virtual auto Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) -> gfx::geometry_handle = 0; // replaces data of specified chunk with the supplied vertex data, starting from specified offset - virtual auto Replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset = 0 ) -> bool = 0; + virtual auto Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) -> bool = 0; // adds supplied vertex data at the end of specified chunk - virtual auto Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) -> bool = 0; + virtual auto Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) -> bool = 0; // provides direct access to index data of specfied chunk virtual auto Indices( gfx::geometry_handle const &Geometry ) const->gfx::index_array const & = 0; - // provides direct access to vertex data of specfied chunk - virtual auto Vertices( gfx::geometry_handle const &Geometry ) const ->gfx::vertex_array const & = 0; + // provides direct access to vertex data of specfied chunk + virtual auto Vertices( gfx::geometry_handle const &Geometry ) const ->gfx::vertex_array const & = 0; + // provides direct access to vertex user data of specfied chunk + virtual auto UserData( gfx::geometry_handle const &Geometry ) const ->gfx::userdata_array const & = 0; // material methods virtual auto Fetch_Material( std::string const &Filename, bool const Loadnow = true ) -> material_handle = 0; virtual void Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) = 0; diff --git a/scene.cpp b/scene.cpp index 9eac1af6..6933ed86 100644 --- a/scene.cpp +++ b/scene.cpp @@ -962,10 +962,11 @@ basic_section::create_geometry() { void basic_section::create_map_geometry(const gfx::geometrybank_handle handle) { std::vector lines; + gfx::userdata_array userdata{}; for (auto &cell : m_cells) cell.create_map_geometry(lines, handle); - m_map_geometryhandle = GfxRenderer->Insert(lines, handle, GL_LINES); + m_map_geometryhandle = GfxRenderer->Insert(lines, userdata, handle, GL_LINES); } void basic_section::get_map_active_paths(map_colored_paths &handles) @@ -1726,15 +1727,16 @@ void basic_region::create_map_geometry() void basic_region::update_poi_geometry() { std::vector vertices; + gfx::userdata_array userdata; for (const auto sem : map::Objects.entries) vertices.push_back(std::move(sem->vertex())); if (!m_map_poipoints) { gfx::geometrybank_handle poibank = GfxRenderer->Create_Bank(); - m_map_poipoints = GfxRenderer->Insert(vertices, poibank, GL_POINTS); + m_map_poipoints = GfxRenderer->Insert(vertices, userdata, poibank, GL_POINTS); } else { - GfxRenderer->Replace(vertices, m_map_poipoints, GL_POINTS); + GfxRenderer->Replace(vertices, userdata, m_map_poipoints, GL_POINTS); } } diff --git a/scenenode.cpp b/scenenode.cpp index 9d809d7a..5feeb616 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -62,12 +62,14 @@ void shape_node::shapenode_data::serialize( std::ostream &Output ) const { // bounding area area.serialize( Output ); + bool has_userdata = !userdata.empty(); // visibility sn_utils::ls_float64( Output, rangesquared_min ); sn_utils::ls_float64( Output, rangesquared_max ); sn_utils::s_bool( Output, visible ); // material sn_utils::s_bool( Output, translucent ); + sn_utils::s_bool( Output, has_userdata ); // NOTE: material handle is created dynamically on load sn_utils::s_str( Output, @@ -80,9 +82,12 @@ shape_node::shapenode_data::serialize( std::ostream &Output ) const { // NOTE: geometry handle is created dynamically on load // vertex count, followed by vertex data sn_utils::ls_uint32( Output, vertices.size() ); - for( auto const &vertex : vertices ) { - gfx::basic_vertex::convert(vertex, origin) - .serialize( Output, false, true ); + for( int i = 0; i < vertices.size(); ++i ) { + gfx::basic_vertex::convert(vertices[i], origin) + .serialize( Output, false ); + if(has_userdata){ + userdata[i].serialize(Output); + } } } @@ -97,6 +102,7 @@ shape_node::shapenode_data::deserialize( std::istream &Input ) { visible = sn_utils::d_bool( Input ); // material translucent = sn_utils::d_bool( Input ); + bool has_userdata = sn_utils::d_bool( Input ); auto const materialname { sn_utils::d_str( Input ) }; if( false == materialname.empty() ) { material = GfxRenderer->Fetch_Material( materialname ); @@ -107,10 +113,14 @@ shape_node::shapenode_data::deserialize( std::istream &Input ) { // NOTE: geometry handle is acquired during geometry creation // vertex data vertices.resize( sn_utils::ld_uint32( Input ) ); + if(has_userdata) + userdata.resize(vertices.size()); gfx::basic_vertex localvertex; - for( auto &vertex : vertices ) { - localvertex.deserialize( Input, false, true ); - vertex = localvertex.to_world(origin); + for( int i = 0; i < vertices.size(); ++i ) { + localvertex.deserialize( Input, false ); + vertices[i] = localvertex.to_world(origin); + if(has_userdata) + userdata[i].deserialize( Input ); } } @@ -347,24 +357,40 @@ shape_node::convert( TSubModel const *Submodel ) { int vertexcount { 0 }; std::vector importedvertices; - world_vertex vertex, vertex1, vertex2; + gfx::userdata_array importeduserdata; if(!GfxRenderer->Indices(Submodel->m_geometry.handle).empty()){ const auto& vertices = GfxRenderer->Vertices(Submodel->m_geometry.handle); + const auto& userdatas = GfxRenderer->UserData(Submodel->m_geometry.handle); + bool has_userdata = !userdatas.empty(); + world_vertex vertex; for(const auto index : GfxRenderer->Indices(Submodel->m_geometry.handle)){ vertex = vertices[index].to_world(); importedvertices.emplace_back(vertex); + if (has_userdata) + importeduserdata.emplace_back(userdatas[index]); } } else{ - for( auto const &sourcevertex : GfxRenderer->Vertices( Submodel->m_geometry.handle ) ) { - vertex = sourcevertex.to_world(); - if( vertexcount == 0 ) { vertex1 = vertex; } - else if( vertexcount == 1 ) { vertex2 = vertex; } + world_vertex vertex, vertex1, vertex2; + gfx::vertex_userdata userdata, userdata1, userdata2; + const auto& vertices = GfxRenderer->Vertices(Submodel->m_geometry.handle); + const auto& userdatas = GfxRenderer->UserData(Submodel->m_geometry.handle); + bool has_userdata = !userdatas.empty(); + for( int i = 0; i < vertices.size(); ++i ) { + vertex = vertices[i].to_world(); + if( has_userdata ) userdata = userdatas[i]; + if( vertexcount == 0 ) { vertex1 = vertex; userdata1 = userdata; } + else if( vertexcount == 1 ) { vertex2 = vertex; userdata2 = userdata; } else if( vertexcount >= 2 ) { - if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) { + if( !degenerate( vertex1.position, vertex2.position, vertex.position ) ) { importedvertices.emplace_back( vertex1 ); importedvertices.emplace_back( vertex2 ); importedvertices.emplace_back( vertex ); + if( has_userdata ) { + importeduserdata.emplace_back( userdata1 ); + importeduserdata.emplace_back( userdata2 ); + importeduserdata.emplace_back( userdata ); + } } // start a new triangle vertexcount = -1; @@ -377,6 +403,7 @@ shape_node::convert( TSubModel const *Submodel ) { // assign imported geometry to the node... m_data.vertices.swap( importedvertices ); + m_data.userdata.swap( importeduserdata ); // ...and calculate center... for( auto const &vertex : m_data.vertices ) { m_data.area.center += vertex.position; @@ -427,7 +454,7 @@ shape_node::create_geometry( gfx::geometrybank_handle const &Bank ) { for( auto const &vertex : m_data.vertices ) { vertices.emplace_back(gfx::basic_vertex::convert(vertex, m_data.origin)); } - m_data.geometry = GfxRenderer->Insert( vertices, Bank, GL_TRIANGLES ); + m_data.geometry = GfxRenderer->Insert(vertices, m_data.userdata, Bank, GL_TRIANGLES); std::vector().swap( m_data.vertices ); // hipster shrink_to_fit } @@ -656,7 +683,7 @@ lines_node::create_geometry( gfx::geometrybank_handle const &Bank ) { vertex.normal, vertex.texture ); } - m_data.geometry = GfxRenderer->Insert( vertices, Bank, GL_LINES ); + m_data.geometry = GfxRenderer->Insert( vertices, m_data.userdata, Bank, GL_LINES ); std::vector().swap( m_data.vertices ); // hipster shrink_to_fit } diff --git a/scenenode.h b/scenenode.h index 56a51620..760eab32 100644 --- a/scenenode.h +++ b/scenenode.h @@ -97,6 +97,7 @@ public: glm::dvec3 origin; // world position of the relative coordinate system origin gfx::geometry_handle geometry { 0, 0 }; // relative origin-centered chunk of geometry held by gfx renderer std::vector vertices; // world space source data of the geometry + gfx::userdata_array userdata; // methods: // sends content of the struct to provided stream void @@ -192,6 +193,7 @@ public: glm::dvec3 origin; // world position of the relative coordinate system origin gfx::geometry_handle geometry { 0, 0 }; // relative origin-centered chunk of geometry held by gfx renderer std::vector vertices; // world space source data of the geometry + gfx::userdata_array userdata; // methods: // sends content of the struct to provided stream void diff --git a/widgets/map.cpp b/widgets/map.cpp index 611a1227..b841bcb0 100644 --- a/widgets/map.cpp +++ b/widgets/map.cpp @@ -621,7 +621,8 @@ void ui::obstacle_insert_window::render_content() std::vector vertices; vertices.emplace_back(std::move(obstacle->vertex())); - GfxRenderer->Append(vertices, simulation::Region->get_map_poi_geometry(), GL_POINTS); + gfx::userdata_array userdata{}; + GfxRenderer->Append(vertices, userdata, simulation::Region->get_map_poi_geometry(), GL_POINTS); map::Objects.entries.push_back(std::move(obstacle)); From 18b18eed082398bc1faf61b18e60a061cee1addd Mon Sep 17 00:00:00 2001 From: Wls50 <167411000+wls50@users.noreply.github.com> Date: Sat, 6 Jul 2024 19:59:36 +0200 Subject: [PATCH 11/88] fix e3d serialization issues: failure to check for user data in submodel hierarchy after creating gfx bank; failure to increase chunk size when user data is present --- Model3d.cpp | 49 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/Model3d.cpp b/Model3d.cpp index 081577a0..13ad449d 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1735,24 +1735,39 @@ void TModel3d::SaveToBinFile(std::string const &FileName) if (!(Global.iConvertModels & 8)) { int modeltype = 1; - if(has_any_userdata) modeltype |= 4; - sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' + modeltype ) ); - sn_utils::ls_uint32( s, 8 + m_vertexcount * 20 ); + int vertexsize = 20; + if(has_any_userdata) + { + modeltype |= 4; + vertexsize += 8; + } + sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' + modeltype ) ); + sn_utils::ls_uint32( s, 8 + m_vertexcount * vertexsize ); Root->serialize_geometry( s, true, true, has_any_userdata ); } else { int modeltype = 2; - if(has_any_userdata) modeltype |= 4; - sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' + modeltype ) ); - sn_utils::ls_uint32( s, 8 + m_vertexcount * 48 ); + int vertexsize = 48; + if(has_any_userdata) + { + modeltype |= 4; + vertexsize += 16; + } + sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' + modeltype ) ); + sn_utils::ls_uint32( s, 8 + m_vertexcount * vertexsize ); Root->serialize_geometry( s, false, true, has_any_userdata ); } } else { int modeltype = 0; - if(has_any_userdata) modeltype |= 4; - sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' + modeltype ) ); - sn_utils::ls_uint32( s, 8 + m_vertexcount * 32 ); + int vertexsize = 32; + if(has_any_userdata) + { + modeltype |= 4; + vertexsize += 16; + } + sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' + modeltype ) ); + sn_utils::ls_uint32( s, 8 + m_vertexcount * vertexsize ); Root->serialize_geometry( s, false, false, has_any_userdata ); } @@ -2202,8 +2217,20 @@ void TSubModel::BinInit(TSubModel *s, float4x4 *m, std::vector *t, bool TSubModel::HasAnyVertexUserData() const { - for(const TSubModel* sm = this; sm; sm = sm->Next){ - if(!sm->Userdata.empty() || (sm->Child && sm->Child->HasAnyVertexUserData())) return true; + for (const TSubModel *sm = this; sm; sm = sm->Next) + { + if (m_geometry.handle) + { + if (!GfxRenderer->UserData(m_geometry.handle).empty()) + return true; + } + else + { + if (!sm->Userdata.empty()) + return true; + } + if (sm->Child && sm->Child->HasAnyVertexUserData()) + return true; } return false; }; From 04eff45b5db067f9c4edaed4eaf5094f3e2f25fc Mon Sep 17 00:00:00 2001 From: Wls50 <167411000+wls50@users.noreply.github.com> Date: Sat, 6 Jul 2024 20:25:53 +0200 Subject: [PATCH 12/88] remove duplicate user_data vector from world_vertex update max. number of vertex attributes to avoid user data getting "reused" across mesh draws --- gl/vao.cpp | 2 +- vertex.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/gl/vao.cpp b/gl/vao.cpp index 85735b5b..6d1e055d 100644 --- a/gl/vao.cpp +++ b/gl/vao.cpp @@ -66,7 +66,7 @@ void gl::vao::bind() glEnableVertexAttribArray(param.attrib); } - for (size_t i = params.size(); i < 4; i++) + for (size_t i = params.size(); i < 5; i++) glDisableVertexAttribArray(i); if (ebo) diff --git a/vertex.h b/vertex.h index 849b3f1b..91bf1ca0 100644 --- a/vertex.h +++ b/vertex.h @@ -18,7 +18,6 @@ struct world_vertex { glm::dvec3 position; glm::vec3 normal; glm::vec2 texture; - glm::vec4 user_data; // overloads // operator+ From a63273d47033d17ee12727b47671e105921a0e97 Mon Sep 17 00:00:00 2001 From: Ryba04 <71170857+Ryba04@users.noreply.github.com> Date: Wed, 10 Jul 2024 22:59:03 +0200 Subject: [PATCH 13/88] fix color.frag Fixed an error found by Michausto and JAN21 --- shaders/color.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shaders/color.frag b/shaders/color.frag index f969af27..45c0829e 100644 --- a/shaders/color.frag +++ b/shaders/color.frag @@ -12,7 +12,7 @@ layout(location = 1) out vec4 out_motion; void main() { - vec3 col = pow(f_color.rgb, vec3(2.2)); + vec3 col = clamp(pow(f_color.rgb, vec3(2.2)),0, 1); #if POSTFX_ENABLED out_color = vec4(apply_fog(col), 1.0f); #else From 001ca7699038d08f9a3a042279f058073a17eba7 Mon Sep 17 00:00:00 2001 From: Marcin Nowak Date: Sat, 3 Aug 2024 00:17:26 +0200 Subject: [PATCH 14/88] (commands) add epbrakecontrolenable and epbrakecontroldisable --- Train.cpp | 30 ++++++++++++++++++++++++++++++ Train.h | 2 ++ command.cpp | 2 ++ command.h | 2 ++ driverkeyboardinput.cpp | 2 ++ drivermouseinput.cpp | 6 ++++++ 6 files changed, 44 insertions(+) diff --git a/Train.cpp b/Train.cpp index 187b2545..9f34496b 100644 --- a/Train.cpp +++ b/Train.cpp @@ -254,6 +254,8 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::autosandboxactivate, &TTrain::OnCommand_autosandboxactivate }, { user_command::autosandboxdeactivate, &TTrain::OnCommand_autosandboxdeactivate }, { user_command::epbrakecontroltoggle, &TTrain::OnCommand_epbrakecontroltoggle }, + { user_command::epbrakecontrolenable, &TTrain::OnCommand_epbrakecontrolenable }, + { user_command::epbrakecontroldisable, &TTrain::OnCommand_epbrakecontroldisable }, { user_command::trainbrakeoperationmodeincrease, &TTrain::OnCommand_trainbrakeoperationmodeincrease }, { user_command::trainbrakeoperationmodedecrease, &TTrain::OnCommand_trainbrakeoperationmodedecrease }, { user_command::brakeactingspeedincrease, &TTrain::OnCommand_brakeactingspeedincrease }, @@ -1969,6 +1971,34 @@ void TTrain::OnCommand_autosandboxdeactivate(TTrain *Train, command_data const & } }; +void TTrain::OnCommand_epbrakecontrolenable( TTrain *Train, command_data const &Command ) { + auto const istoggle{ ( static_cast( Train->ggEPFuseButton.type() ) & static_cast( TGaugeType::toggle ) ) != 0 }; + if( Command.action == GLFW_PRESS ) { + // command only works for bistable switch + if(istoggle) { + if( Train->mvOccupied->EpFuseSwitch( true ) ) { + // audio feedback + if( Train->dsbPneumaticSwitch ) { + Train->dsbPneumaticSwitch->play(); + } + Train->ggEPFuseButton.UpdateValue(1.0f, Train->dsbSwitch); + }; + } + } +} + +void TTrain::OnCommand_epbrakecontroldisable( TTrain *Train, command_data const &Command ) { + auto const istoggle{ ( static_cast( Train->ggEPFuseButton.type() ) & static_cast( TGaugeType::toggle ) ) != 0 }; + if( Command.action == GLFW_PRESS ) { + // command only works for bistable switch + if(istoggle) { + if( Train->mvOccupied->EpFuseSwitch( false ) ) { + Train->ggEPFuseButton.UpdateValue(0.0f, Train->dsbSwitch); + }; + } + } +} + void TTrain::OnCommand_epbrakecontroltoggle( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_REPEAT ) { return; } diff --git a/Train.h b/Train.h index 4e2afda1..a1d6f245 100644 --- a/Train.h +++ b/Train.h @@ -277,6 +277,8 @@ class TTrain { static void OnCommand_autosandboxactivate(TTrain *Train, command_data const &Command); static void OnCommand_autosandboxdeactivate(TTrain *Train, command_data const &Command); static void OnCommand_epbrakecontroltoggle( TTrain *Train, command_data const &Command ); + static void OnCommand_epbrakecontrolenable( TTrain *Train, command_data const &Command ); + static void OnCommand_epbrakecontroldisable( TTrain *Train, command_data const &Command ); static void OnCommand_trainbrakeoperationmodeincrease(TTrain *Train, command_data const &Command); static void OnCommand_trainbrakeoperationmodedecrease(TTrain *Train, command_data const &Command); static void OnCommand_brakeactingspeedincrease( TTrain *Train, command_data const &Command ); diff --git a/command.cpp b/command.cpp index cef9f524..8e2ea97b 100644 --- a/command.cpp +++ b/command.cpp @@ -120,6 +120,8 @@ commanddescription_sequence Commands_descriptions = { { "universalrelayreset3", command_target::vehicle, command_mode::oneoff }, { "notchingrelaytoggle", command_target::vehicle, command_mode::oneoff }, { "epbrakecontroltoggle", command_target::vehicle, command_mode::oneoff }, + { "epbrakecontrolenable", command_target::vehicle, command_mode::oneoff }, + { "epbrakecontroldisable", command_target::vehicle, command_mode::oneoff }, { "trainbrakeoperationmodeincrease", command_target::vehicle, command_mode::oneoff }, { "trainbrakeoperationmodedecrease", command_target::vehicle, command_mode::oneoff }, { "brakeactingspeedincrease", command_target::vehicle, command_mode::oneoff }, diff --git a/command.h b/command.h index 47ae1b31..8fb5a87d 100644 --- a/command.h +++ b/command.h @@ -113,6 +113,8 @@ enum class user_command { universalrelayreset3, notchingrelaytoggle, epbrakecontroltoggle, + epbrakecontrolenable, + epbrakecontroldisable, trainbrakeoperationmodeincrease, trainbrakeoperationmodedecrease, brakeactingspeedincrease, diff --git a/driverkeyboardinput.cpp b/driverkeyboardinput.cpp index a093f023..ded09670 100644 --- a/driverkeyboardinput.cpp +++ b/driverkeyboardinput.cpp @@ -122,6 +122,8 @@ driverkeyboard_input::default_bindings() { // universalrelayreset3, { user_command::notchingrelaytoggle, {GLFW_KEY_G, ""} }, { user_command::epbrakecontroltoggle, {GLFW_KEY_Z | keymodifier::control, ""} }, + // epbrakecontrolenable + // epbrakecontroldisable { user_command::trainbrakeoperationmodeincrease, {GLFW_KEY_KP_2 | keymodifier::control, ""} }, { user_command::trainbrakeoperationmodedecrease, {GLFW_KEY_KP_8 | keymodifier::control, ""} }, { user_command::brakeactingspeedincrease, {GLFW_KEY_B | keymodifier::shift, ""} }, diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index 78529a23..65fde07e 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -638,6 +638,12 @@ drivermouse_input::default_bindings() { { "epbrake_bt:",{ user_command::epbrakecontroltoggle, user_command::none } }, + { "epbrakeon_bt:",{ + user_command::epbrakecontrolenable, + user_command::none } }, + { "epbrakeoff_bt:",{ + user_command::epbrakecontroldisable, + user_command::none } }, { "sand_bt:", { user_command::sandboxactivate, user_command::none } }, From f9d04f7397381d0582bb3a47f0f66df1bc39af9d Mon Sep 17 00:00:00 2001 From: Ryba04 Date: Tue, 10 Sep 2024 23:04:17 +0200 Subject: [PATCH 15/88] Revert "Improve pantograph selector" This reverts commit 13d9a91632b562902230ae0ba61902265031036d. --- McZapkie/MOVER.h | 1 - McZapkie/Mover.cpp | 1 - Train.cpp | 6 ------ Train.h | 4 +--- 4 files changed, 1 insertion(+), 11 deletions(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index f93b2e59..2702aee1 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1205,7 +1205,6 @@ public: bool UniCtrlIntegratedLocalBrakeCtrl = false; /*zintegrowany nastawnik JH obsluguje hamowanie hamulcem pomocniczym*/ int UniCtrlNoPowerPos{ 0 }; // cached highesr position not generating traction force std::pair> PantsPreset { "0132", { 0, 0 } }; // pantograph preset switches; .first holds possible setups as chars, .second holds currently selected preset in each cab - int PantsPresetDefault = 0; // default pantograph preset, this is not updated during simulation /*-sekcja parametrow dla lokomotywy elektrycznej*/ TSchemeTable RList; /*lista rezystorow rozruchowych i polaczen silnikow, dla dizla: napelnienia*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index a395051d..ae3180f5 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -11100,7 +11100,6 @@ void TMoverParameters::LoadFIZ_Switches( std::string const &Input ) { std::remove( std::begin( presets ), std::end( presets ), '|' ), std::end( presets ) ); } - extract_value( PantsPresetDefault, "PantographPresetDefault", Input, "" ); } void TMoverParameters::LoadFIZ_MotorParamTable( std::string const &Input ) { diff --git a/Train.cpp b/Train.cpp index 187b2545..5e1b28f8 100644 --- a/Train.cpp +++ b/Train.cpp @@ -637,12 +637,6 @@ bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d) DynamicObject->Mechanik->sync_consist_reversers(); } - // Set the default pantograph preset and update pantographs' valves accordingly. - change_pantograph_selection(mvOccupied->PantsPresetDefault); - // Avoid double update if the default is other than 0. - if (mvOccupied->PantsPresetDefault == 0) - update_pantograph_valves(); - return true; } diff --git a/Train.h b/Train.h index 4e2afda1..15bd2a03 100644 --- a/Train.h +++ b/Train.h @@ -211,14 +211,12 @@ class TTrain { void update_sounds( double const Deltatime ); void update_sounds_runningnoise( sound_source &Sound ); void update_sounds_radio(); - // Translates the given cab to an end ID: cab `1` and engine compartment (`0`) translate to `end::front`, cab `2` translates to `end::rear` inline end cab_to_end( int const End ) const { return ( End == 2 ? end::rear : end::front ); } - // Translates the currently occupied cab to an end ID: cab `1` and engine compartment (`0`) translate to `end::front`, cab `2` translates to `end::rear` inline end cab_to_end() const { return cab_to_end( iCabn ); } @@ -817,7 +815,7 @@ public: // reszta może by?publiczna */ // McZapkie: opis kabiny - obszar poruszania sie mechanika oraz zajetosc std::array Cabine; // przedzial maszynowy, kabina 1 (A), kabina 2 (B) - int iCabn { 0 }; // the cab number the player is currently inside of; 0: mid, 1: front, 2: rear + int iCabn { 0 }; // 0: mid, 1: front, 2: rear bool is_cab_initialized { false }; // McZapkie: do poruszania sie po kabinie Math3D::vector3 pMechSittingPosition; // ABu 180404 From fcfd681b444a243067f375d7c587a61a27b1a3fc Mon Sep 17 00:00:00 2001 From: Ryba04 <71170857+Ryba04@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:35:24 +0200 Subject: [PATCH 16/88] Revert "Make right mouse down + scroll wheel adjust FOV instead of master controller" This reverts commit 71ff6a60d033cfa0230386ab8d112d072158b8a4. --- drivermouseinput.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index 78529a23..de75d607 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -281,8 +281,8 @@ drivermouse_input::move( double Mousex, double Mousey ) { void drivermouse_input::scroll( double const Xoffset, double const Yoffset ) { - if( Global.ctrlState || m_buttons[1] == GLFW_PRESS ) { - // ctrl + scroll wheel or holding right mouse button + scroll wheel adjusts fov + if( Global.ctrlState ) { + // ctrl + scroll wheel adjusts fov Global.FieldOfView = clamp( static_cast( Global.FieldOfView - Yoffset * 20.0 / Timer::subsystem.mainloop_total.average() ), 15.0f, 75.0f ); } else { From 2342e103cd1f2646e500a833402ea6c80da91acb Mon Sep 17 00:00:00 2001 From: Hirek Date: Thu, 26 Dec 2024 18:24:25 +0100 Subject: [PATCH 17/88] Add missing asio ref --- .gitmodules | 3 +++ ref/asio | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 ref/asio diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..1d7e30cc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ref/asio"] + path = ref/asio + url = https://github.com/chriskohlhoff/asio.git diff --git a/ref/asio b/ref/asio new file mode 160000 index 00000000..62481a25 --- /dev/null +++ b/ref/asio @@ -0,0 +1 @@ +Subproject commit 62481a25be6cf78cbe714419a4462fd89bd84ab9 From a0a3e504fc345fce61c7dd4846daa3576640e178 Mon Sep 17 00:00:00 2001 From: Hirek Date: Thu, 26 Dec 2024 18:48:40 +0100 Subject: [PATCH 18/88] Add drawn and returned power values for pyrthon Added power_drawn and power_returned to dict (both double) --- Train.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Train.cpp b/Train.cpp index dc40337b..ac87ae1f 100644 --- a/Train.cpp +++ b/Train.cpp @@ -743,6 +743,9 @@ dictionary_source *TTrain::GetTrainState( dictionary_source const &Extraparamete dict->insert( "im", std::abs( mvControlled->Im ) ); dict->insert( "fuse", mvControlled->FuseFlag ); dict->insert( "epfuse", mvOccupied->EpFuse ); + dict->insert( "power_drawn", mvOccupied->EnergyMeter.first); + dict->insert( "power_returned", mvOccupied->EnergyMeter.second); + // induction motor state data char const *TXTT[ 10 ] = { "fd", "fdt", "fdb", "pd", "pdt", "pdb", "itothv", "1", "2", "3" }; char const *TXTC[ 10 ] = { "fr", "frt", "frb", "pr", "prt", "prb", "im", "vm", "ihv", "uhv" }; From dd49381c3c5c49501e851a009f79b07af79e018f Mon Sep 17 00:00:00 2001 From: Hirek Date: Thu, 26 Dec 2024 19:31:06 +0100 Subject: [PATCH 19/88] Set re/fasio desired tag --- ref/asio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ref/asio b/ref/asio index 62481a25..b3d2ab72 160000 --- a/ref/asio +++ b/ref/asio @@ -1 +1 @@ -Subproject commit 62481a25be6cf78cbe714419a4462fd89bd84ab9 +Subproject commit b3d2ab7255fabe46a49b24a584c9fd797c8248e5 From 0522d12cd38d26dedd358b16421756a8e1641128 Mon Sep 17 00:00:00 2001 From: Hirek Date: Sat, 28 Dec 2024 00:15:36 +0100 Subject: [PATCH 20/88] Implemented HideDirStatusWhenMoving entry --- McZapkie/MOVER.h | 1 + McZapkie/Mover.cpp | 1 + Train.cpp | 12 ++++++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 2702aee1..c550b76f 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1196,6 +1196,7 @@ public: bool ReleaseParkingBySpringBrakeWhenDoorIsOpen{ false }; bool SpringBrakeCutsOffDrive { true }; double SpringBrakeDriveEmergencyVel { -1 }; + bool HideDirStatusWhenMoving { false }; TSecuritySystem SecuritySystem; int EmergencyBrakeWarningSignal{ 0 }; // combined with basic WarningSignal when manual emergency brake is active TUniversalCtrlTable UniCtrlList; /*lista pozycji uniwersalnego nastawnika*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index ae3180f5..66a78eff 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -10576,6 +10576,7 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { extract_value( SpringBrakeCutsOffDrive, "SpringBrakeCutsOffDrive", line, ""); extract_value( SpringBrakeDriveEmergencyVel, "SpringBrakeDriveEmergencyVel", line, ""); + extract_value(HideDirStatusWhenMoving "HideDirStatusWhenMoving", line, ""); std::map starts { { "Disabled", start_t::disabled }, { "Manual", start_t::manual }, diff --git a/Train.cpp b/Train.cpp index ac87ae1f..4abd7f7b 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7537,8 +7537,16 @@ bool TTrain::Update( double const Deltatime ) btLampkaDoorLockOff.Turn( false == mvOccupied->Doors.lock_enabled ); btLampkaDepartureSignal.Turn( mvControlled->DepartureSignal ); btLampkaNapNastHam.Turn((mvControlled->DirActive != 0) && (mvOccupied->EpFuse)); // napiecie na nastawniku hamulcowym - btLampkaForward.Turn(mvControlled->DirActive > 0); // jazda do przodu - btLampkaBackward.Turn(mvControlled->DirActive < 0); // jazda do tyłu + // Wylaczanie lampek kierunku gdy jedziemy + // Feature uruchamiany z fiz z sekcji Ctrl. wpisem HideDirStatusWhenMoving=Yes (domyslnie No) + if (mvOccupied->HideDirStatusWhenMoving && mvOccupied->Vel > 1) { + btLampkaForward.Turn(false); + btLampkaBackward.Turn(false); + } + else { + btLampkaForward.Turn(mvControlled->DirActive > 0); // jazda do przodu + btLampkaBackward.Turn(mvControlled->DirActive < 0); // jazda do tyłu + } btLampkaED.Turn(mvControlled->DynamicBrakeFlag); // hamulec ED btLampkaBrakeProfileG.Turn( TestFlag( mvOccupied->BrakeDelayFlag, bdelay_G ) ); btLampkaBrakeProfileP.Turn( TestFlag( mvOccupied->BrakeDelayFlag, bdelay_P ) ); From efeaaf6235c43731fb35f2f1328bdd4f54cdd5cd Mon Sep 17 00:00:00 2001 From: Hirek Date: Sat, 28 Dec 2024 00:22:15 +0100 Subject: [PATCH 21/88] Add HideDirStatusSpeed parameter --- McZapkie/MOVER.h | 3 ++- McZapkie/Mover.cpp | 4 +++- Train.cpp | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index c550b76f..73a9df50 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1196,7 +1196,8 @@ public: bool ReleaseParkingBySpringBrakeWhenDoorIsOpen{ false }; bool SpringBrakeCutsOffDrive { true }; double SpringBrakeDriveEmergencyVel { -1 }; - bool HideDirStatusWhenMoving { false }; + bool HideDirStatusWhenMoving { false }; // Czy gasic lampki kierunku powyzej predkosci zdefiniowanej przez HideDirStatusSpeed + int HideDirStatusSpeed{ 1 }; // Predkosc od ktorej lampki kierunku sa wylaczane TSecuritySystem SecuritySystem; int EmergencyBrakeWarningSignal{ 0 }; // combined with basic WarningSignal when manual emergency brake is active TUniversalCtrlTable UniCtrlList; /*lista pozycji uniwersalnego nastawnika*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 66a78eff..1b9bfbfe 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -10576,7 +10576,9 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { extract_value( SpringBrakeCutsOffDrive, "SpringBrakeCutsOffDrive", line, ""); extract_value( SpringBrakeDriveEmergencyVel, "SpringBrakeDriveEmergencyVel", line, ""); - extract_value(HideDirStatusWhenMoving "HideDirStatusWhenMoving", line, ""); + extract_value(HideDirStatusWhenMoving, "HideDirStatusWhenMoving", line, ""); + extract_value(HideDirStatusSpeed, "HideDirStatusSpeed", line, ""); + std::map starts { { "Disabled", start_t::disabled }, { "Manual", start_t::manual }, diff --git a/Train.cpp b/Train.cpp index 4abd7f7b..f2d80fbe 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7539,7 +7539,9 @@ bool TTrain::Update( double const Deltatime ) btLampkaNapNastHam.Turn((mvControlled->DirActive != 0) && (mvOccupied->EpFuse)); // napiecie na nastawniku hamulcowym // Wylaczanie lampek kierunku gdy jedziemy // Feature uruchamiany z fiz z sekcji Ctrl. wpisem HideDirStatusWhenMoving=Yes (domyslnie No) - if (mvOccupied->HideDirStatusWhenMoving && mvOccupied->Vel > 1) { + if (mvOccupied->HideDirStatusWhenMoving && // Czy ta funkcja jest w ogole wlaczona + mvOccupied->Vel > mvOccupied->HideDirStatusSpeed) // Uzaleznienie od predkosci + { btLampkaForward.Turn(false); btLampkaBackward.Turn(false); } From 995c9eb47e59068172c1c7b3b735bca79d698154 Mon Sep 17 00:00:00 2001 From: Hirek Date: Sat, 28 Dec 2024 00:29:32 +0100 Subject: [PATCH 22/88] Cleanup for pull request --- Train.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Train.cpp b/Train.cpp index f2d80fbe..450b934c 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7537,6 +7537,7 @@ bool TTrain::Update( double const Deltatime ) btLampkaDoorLockOff.Turn( false == mvOccupied->Doors.lock_enabled ); btLampkaDepartureSignal.Turn( mvControlled->DepartureSignal ); btLampkaNapNastHam.Turn((mvControlled->DirActive != 0) && (mvOccupied->EpFuse)); // napiecie na nastawniku hamulcowym + // Wylaczanie lampek kierunku gdy jedziemy // Feature uruchamiany z fiz z sekcji Ctrl. wpisem HideDirStatusWhenMoving=Yes (domyslnie No) if (mvOccupied->HideDirStatusWhenMoving && // Czy ta funkcja jest w ogole wlaczona @@ -7549,6 +7550,7 @@ bool TTrain::Update( double const Deltatime ) btLampkaForward.Turn(mvControlled->DirActive > 0); // jazda do przodu btLampkaBackward.Turn(mvControlled->DirActive < 0); // jazda do tyłu } + btLampkaED.Turn(mvControlled->DynamicBrakeFlag); // hamulec ED btLampkaBrakeProfileG.Turn( TestFlag( mvOccupied->BrakeDelayFlag, bdelay_G ) ); btLampkaBrakeProfileP.Turn( TestFlag( mvOccupied->BrakeDelayFlag, bdelay_P ) ); From a73a8eeb6147a9506a38a176c931457689af1524 Mon Sep 17 00:00:00 2001 From: Hirek Date: Sat, 28 Dec 2024 00:53:16 +0100 Subject: [PATCH 23/88] Add i-neutral indicator --- Train.cpp | 5 +++++ Train.h | 1 + 2 files changed, 6 insertions(+) diff --git a/Train.cpp b/Train.cpp index 450b934c..07bea32a 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7545,10 +7545,12 @@ bool TTrain::Update( double const Deltatime ) { btLampkaForward.Turn(false); btLampkaBackward.Turn(false); + btLampkaNeutral.Turn(false); } else { btLampkaForward.Turn(mvControlled->DirActive > 0); // jazda do przodu btLampkaBackward.Turn(mvControlled->DirActive < 0); // jazda do tyłu + btLampkaNeutral.Turn(mvControlled->DirActive == 0); // kierunek neutral } btLampkaED.Turn(mvControlled->DynamicBrakeFlag); // hamulec ED @@ -7625,6 +7627,7 @@ bool TTrain::Update( double const Deltatime ) btLampkaNapNastHam.Turn( false ); btLampkaForward.Turn( false ); btLampkaBackward.Turn( false ); + btLampkaNeutral.Turn(false); btLampkaED.Turn( false ); // light indicators btLampkaUpperLight.Turn( false ); @@ -9495,6 +9498,7 @@ void TTrain::clear_cab_controls() btLampkaHVoltageB.Clear(); btLampkaForward.Clear(); btLampkaBackward.Clear(); + btLampkaNeutral.Clear(); // light indicators btLampkaUpperLight.Clear(); btLampkaLeftLight.Clear(); @@ -9965,6 +9969,7 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-malfunctionb:", btLampkaMalfunctionB }, { "i-forward:", btLampkaForward }, { "i-backward:", btLampkaBackward }, + { "i-neutral:", btLampkaNeutral }, { "i-upperlight:", btLampkaUpperLight }, { "i-leftlight:", btLampkaLeftLight }, { "i-rightlight:", btLampkaRightLight }, diff --git a/Train.h b/Train.h index 816570d1..49c7bfa1 100644 --- a/Train.h +++ b/Train.h @@ -757,6 +757,7 @@ public: // reszta może by?publiczna TButton btLampkaDoorLockOff; TButton btLampkaHamulecReczny; TButton btLampkaForward; // Ra: lampki w przód i w ty?dla komputerowych kabin + TButton btLampkaNeutral; // Kierunek neutralny TButton btLampkaBackward; // light indicators TButton btLampkaUpperLight; From e0d29531909f9d605635380ad8f513b76533968d Mon Sep 17 00:00:00 2001 From: Hirek Date: Sat, 28 Dec 2024 02:53:30 +0100 Subject: [PATCH 24/88] Add pipelock state dict parameter --- Train.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Train.cpp b/Train.cpp index ac87ae1f..add5ac0a 100644 --- a/Train.cpp +++ b/Train.cpp @@ -713,6 +713,7 @@ dictionary_source *TTrain::GetTrainState( dictionary_source const &Extraparamete dict->insert( "emergency_brake", mvOccupied->AlarmChainFlag ); dict->insert( "brake_delay_flag", mvOccupied->BrakeDelayFlag ); dict->insert( "brake_op_mode_flag", mvOccupied->BrakeOpModeFlag ); + dict->insert( "pipelock", mvOccupied->LockPipe); // other controls dict->insert( "ca", mvOccupied->SecuritySystem.is_vigilance_blinking()); dict->insert( "shp", mvOccupied->SecuritySystem.is_cabsignal_blinking()); From 1c8d0142a30a03b9fb5b7bd1d650851d9487c70a Mon Sep 17 00:00:00 2001 From: Hirek Date: Sat, 28 Dec 2024 03:56:53 +0100 Subject: [PATCH 25/88] Add possibility to assign separate SHP buzzer --- Train.cpp | 28 ++++++++++++++++++++++++++-- Train.h | 2 +- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Train.cpp b/Train.cpp index d0cf9786..c14423ea 100644 --- a/Train.cpp +++ b/Train.cpp @@ -8380,6 +8380,21 @@ TTrain::update_sounds( double const Deltatime ) { // power-reliant sounds if( mvOccupied->Power24vIsAvailable || mvOccupied->Power110vIsAvailable ) { + + // buzzer shp + if (mvOccupied->SecuritySystem.is_cabsignal_beeping()) { + if (dsbBuzzerShp && false == dsbBuzzerShp->is_playing()) { + dsbBuzzerShp->pitch(dsbBuzzerShp->m_frequencyoffset + dsbBuzzerShp->m_frequencyfactor); + dsbBuzzerShp->gain(dsbBuzzerShp->m_amplitudeoffset + dsbBuzzerShp->m_amplitudefactor); + dsbBuzzerShp->play(sound_flags::looping); + } + } + else { + if (dsbBuzzerShp && true == dsbBuzzerShp->is_playing()) { + dsbBuzzerShp->stop(); + } + } + // McZapkie-141102: SHP i czuwak, TODO: sygnalizacja kabinowa // hunter-091012: rozdzielenie alarmow if( mvOccupied->SecuritySystem.is_beeping() ) { @@ -8426,6 +8441,13 @@ TTrain::update_sounds( double const Deltatime ) { #endif } } + + if (dsbBuzzerShp && dsbBuzzerShp->is_playing()) { + dsbBuzzerShp->stop(); + } + { + } + if( m_distancecounterclear ) { m_distancecounterclear->stop(); } @@ -8596,6 +8618,7 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName) {"ctrlscnd:", {dsbNastawnikBocz, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}}, {"reverserkey:", {dsbReverserKey, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}}, {"buzzer:", {dsbBuzzer, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}}, + {"buzzershp:", {dsbBuzzerShp, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}}, {"radiostop:", {m_radiostop, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}}, {"slipalarm:", {dsbSlipAlarm, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}}, {"distancecounter:", {m_distancecounterclear, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}}, @@ -8688,7 +8711,7 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName) dsbSwitch, dsbPneumaticSwitch, rsHiss, rsHissU, rsHissE, rsHissX, rsHissT, rsSBHiss, rsSBHissU, rsFadeSound, rsRunningNoise, rsHuntingNoise, - dsbHasler, dsbBuzzer, dsbSlipAlarm, m_distancecounterclear, m_rainsound, m_radiostop + dsbHasler, dsbBuzzer,dsbBuzzerShp, dsbSlipAlarm, m_distancecounterclear, m_rainsound, m_radiostop }; for (auto &sound : sounds) { if (sound.get()) { @@ -8711,7 +8734,7 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) dsbSwitch, dsbPneumaticSwitch, rsHiss, rsHissU, rsHissE, rsHissX, rsHissT, rsSBHiss, rsSBHissU, rsFadeSound, rsRunningNoise, rsHuntingNoise, - dsbHasler, dsbBuzzer, dsbSlipAlarm, m_distancecounterclear, m_rainsound, m_radiostop + dsbHasler, dsbBuzzer, dsbBuzzerShp, dsbSlipAlarm, m_distancecounterclear, m_rainsound, m_radiostop }; for( auto &sound : sounds ) { if( sound.get() ) { @@ -9005,6 +9028,7 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) {rsHuntingNoise, caboffset}, {dsbHasler, caboffset}, {dsbBuzzer, btLampkaCzuwaka.model_offset()}, + {dsbBuzzerShp, btLampkaCzuwaka.model_offset()}, {dsbSlipAlarm, caboffset}, {m_distancecounterclear, btLampkaCzuwaka.model_offset()}, {m_rainsound, caboffset}, diff --git a/Train.h b/Train.h index 49c7bfa1..a6058835 100644 --- a/Train.h +++ b/Train.h @@ -787,7 +787,7 @@ public: // reszta może by?publiczna dsbNastawnikJazdy, dsbNastawnikBocz, dsbReverserKey, - dsbBuzzer, + dsbBuzzer, dsbBuzzerShp, m_radiostop, dsbSlipAlarm, m_distancecounterclear, From a01a63c8bfa991bcd3cda45eaba34ecf5872fbd5 Mon Sep 17 00:00:00 2001 From: Hirek Date: Fri, 3 Jan 2025 17:39:54 +0100 Subject: [PATCH 26/88] Add braking resistor ventilator sound and simple simulation --- DynObj.cpp | 24 +++++++++++++++++++++++- DynObj.h | 1 + McZapkie/MOVER.h | 2 ++ McZapkie/Mover.cpp | 15 ++++++++++++++- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 9ebf2bfa..d3571f05 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -4184,6 +4184,21 @@ void TDynamicObject::RenderSounds() { sConverter.stop(); } + // Odtworzenie dzwieku wentylatora rezystora hamowania + + if (MoverParameters->BRVentilators) + { + sBRVent.play(sound_flags::exclusive | sound_flags::looping); + } + else + { + sBRVent.stop(); + } + + + + + if( MoverParameters->CompressorSpeed > 0.0 ) { // McZapkie! - dzwiek compressor.wav tylko gdy dziala sprezarka if( MoverParameters->CompressorFlag ) { @@ -5976,6 +5991,13 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co sConverter.deserialize( parser, sound_type::multipart, sound_parameters::range ); sConverter.owner( this ); } + + // Dzwiek wentylatora rezystora hamowania + else if (token == "brakingresistorventilator:") + { + sBRVent.deserialize(parser, sound_type::multipart, sound_parameters::range); + sBRVent.owner(this); + } else if( token == "heater:" ) { // train heating device @@ -6740,7 +6762,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co // other engine compartment sounds auto const nullvector { glm::vec3() }; std::vector enginesounds = { - &sConverter, &sCompressor, &sCompressorIdle, &sSmallCompressor, &sHeater, &m_batterysound + &sConverter, &sBRVent, &sCompressor, &sCompressorIdle, &sSmallCompressor, &sHeater, &m_batterysound }; for( auto sound : enginesounds ) { if( sound->offset() == nullvector ) { diff --git a/DynObj.h b/DynObj.h index 840238d6..32207ab9 100644 --- a/DynObj.h +++ b/DynObj.h @@ -486,6 +486,7 @@ private: // engine sounds powertrain_sounds m_powertrainsounds; sound_source sConverter { sound_placement::engine }; + sound_source sBRVent {sound_placement::engine}; sound_source sCompressor { sound_placement::engine }; // NBMX wrzesien 2003 sound_source sCompressorIdle { sound_placement::engine }; sound_source sSmallCompressor { sound_placement::engine }; diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 73a9df50..bbbfbd8f 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1341,6 +1341,7 @@ public: double InvertersRatio = 0.0; std::vector Inverters; //all inverters int InverterControlCouplerFlag = 4; //which coupling flag is necessary to controll inverters + int Imaxrpc = 0; // Maksymalny prad rezystora hamowania chlodzonego pasywnie std::map EIM_Pmax_Table; /*tablica mocy maksymalnej od predkosci*/ /* -dla pojazdów z blendingiem EP/ED (MED) */ double MED_Vmax = 0; // predkosc maksymalna dla obliczen chwilowej sily hamowania EP w MED @@ -1471,6 +1472,7 @@ public: bool ConverterAllow = false; /*zezwolenie na prace przetwornicy NBMX*/ bool ConverterAllowLocal{ true }; // local device state override (most units don't have this fitted so it's set to true not to intefere) bool ConverterFlag = false; /*! czy wlaczona przetwornica NBMX*/ + bool BRVentilators = false; /* Czy rezystor hamowania pracuje */ start_t ConverterOverloadRelayStart { start_t::manual }; // whether overload relay reset responds to dedicated button bool ConverterOverloadRelayOffWhenMainIsOff { false }; fuel_pump FuelPump; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 1b9bfbfe..22a9ec79 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -6408,6 +6408,19 @@ double TMoverParameters::TractionForce( double dt ) { 0.007 * (std::abs(EngineVoltage) - (EnginePowerSource.CollectorParameters.MaxV - 100))); Itot = eimv[eimv_Ipoj] * (0.01 + std::min(0.99, 0.99 - Vadd)); + // Uproszczona symulacja rezystora hamowania + if (eimv[eimv_Ipoj] < 0) + { + // Prad oddawany na rezystor + double Irh = abs(eimv[eimv_Pe]) - abs(eimv[eimv_Ipoj]); + + // Wlacz wentylator jesli prad przekroczy maksymalny dla pasywnego chlodzenia + BRVentilators = Irh > Imaxrpc; + } + else + BRVentilators = false; + + EnginePower = abs(eimv[eimv_Ic] * eimv[eimv_U] * NPoweredAxles) / 1000; // power inverters auto const tmpV { std::abs( eimv[ eimv_fp ] ) }; @@ -11019,7 +11032,7 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) { extract_value( EIMCLogForce, "eimclf", Input, "" ); extract_value( InvertersNo, "InvNo", Input, "" ); extract_value( InverterControlCouplerFlag, "InvCtrCplFlag", Input, "" ); - + extract_value(Imaxrpc, "Imaxrpc", Input, ""); extract_value( Flat, "Flat", Input, ""); if (eimc[eimc_p_Pmax] > 0 && Power > 0 && InvertersNo == 0) { From a3099e6de7b0631634223fc5397f84614e43060c Mon Sep 17 00:00:00 2001 From: Hirek Date: Fri, 3 Jan 2025 19:22:37 +0100 Subject: [PATCH 27/88] Add sustain parameter (BRVto in Engine section) --- McZapkie/MOVER.h | 2 ++ McZapkie/Mover.cpp | 35 ++++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index bbbfbd8f..cb601888 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1342,6 +1342,8 @@ public: std::vector Inverters; //all inverters int InverterControlCouplerFlag = 4; //which coupling flag is necessary to controll inverters int Imaxrpc = 0; // Maksymalny prad rezystora hamowania chlodzonego pasywnie + int BRVto = 0; // Czas jaki wentylatory jeszcze dodatkowo schladzaja rezystor + double BRVtimer = 0; // Timer dla podtrzymania wentylatora std::map EIM_Pmax_Table; /*tablica mocy maksymalnej od predkosci*/ /* -dla pojazdów z blendingiem EP/ED (MED) */ double MED_Vmax = 0; // predkosc maksymalna dla obliczen chwilowej sily hamowania EP w MED diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 22a9ec79..886209a9 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1597,6 +1597,26 @@ void TMoverParameters::compute_movement_( double const Deltatime ) { } } + // Uproszczona symulacja wentylatorow rezystora hamowania + + // Prad oddawany na rezystor + double Irh = abs(eimv[eimv_Pe]) - abs(eimv[eimv_Ipoj]); + + // Wlacz wentylator jesli prad rekuperacji przekroczy maksymalny dla pasywnego chlodzenia rezystora + if (Irh > Imaxrpc && eimv[eimv_Ipoj] < 0) + { + BRVtimer = 0; + BRVentilators = true; + } + else { + BRVtimer += Deltatime; + if (BRVtimer > BRVto) + BRVentilators = false; + } + + + + // automatyczny rozruch if( EngineType == TEngineType::ElectricSeriesMotor ) { if( AutoRelayCheck() ) { @@ -6408,18 +6428,6 @@ double TMoverParameters::TractionForce( double dt ) { 0.007 * (std::abs(EngineVoltage) - (EnginePowerSource.CollectorParameters.MaxV - 100))); Itot = eimv[eimv_Ipoj] * (0.01 + std::min(0.99, 0.99 - Vadd)); - // Uproszczona symulacja rezystora hamowania - if (eimv[eimv_Ipoj] < 0) - { - // Prad oddawany na rezystor - double Irh = abs(eimv[eimv_Pe]) - abs(eimv[eimv_Ipoj]); - - // Wlacz wentylator jesli prad przekroczy maksymalny dla pasywnego chlodzenia - BRVentilators = Irh > Imaxrpc; - } - else - BRVentilators = false; - EnginePower = abs(eimv[eimv_Ic] * eimv[eimv_U] * NPoweredAxles) / 1000; // power inverters @@ -11032,7 +11040,8 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) { extract_value( EIMCLogForce, "eimclf", Input, "" ); extract_value( InvertersNo, "InvNo", Input, "" ); extract_value( InverterControlCouplerFlag, "InvCtrCplFlag", Input, "" ); - extract_value(Imaxrpc, "Imaxrpc", Input, ""); + extract_value(Imaxrpc, "Imaxrpc", Input, ""); + extract_value(BRVto, "BRVto", Input, ""); extract_value( Flat, "Flat", Input, ""); if (eimc[eimc_p_Pmax] > 0 && Power > 0 && InvertersNo == 0) { From a1cdaf9db0198a4f59427717b03bce242987a433 Mon Sep 17 00:00:00 2001 From: Hirek Date: Sat, 4 Jan 2025 21:15:59 +0100 Subject: [PATCH 28/88] Add resonancesound (slightly modified runningnoise calculation) --- Train.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++-- Train.h | 2 ++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Train.cpp b/Train.cpp index c14423ea..cf292a76 100644 --- a/Train.cpp +++ b/Train.cpp @@ -8311,6 +8311,20 @@ TTrain::update_sounds( double const Deltatime ) { dsbSlipAlarm->stop(); } } + + // dzwiek rezonansu (taki drugi runningnoise w sumie) + if (rsResonanceNoise) + { + if (!FreeFlyModeFlag && !Global.CabWindowOpen && DynamicObject->GetVelocity() > 0.5) + { + + update_sounds_resonancenoise(*rsResonanceNoise); + } + else + rsResonanceNoise->stop(FreeFlyModeFlag); + } + + // szum w czasie jazdy if( rsRunningNoise ) { if( ( false == FreeFlyModeFlag ) @@ -8456,6 +8470,25 @@ TTrain::update_sounds( double const Deltatime ) { update_sounds_radio(); } +void TTrain::update_sounds_resonancenoise(sound_source &Sound) +{ + // frequency calculation + auto const normalizer{mvOccupied->Vmax * 0.01f}; + auto const frequency{Sound.m_frequencyoffset + Sound.m_frequencyfactor * mvOccupied->Vel * normalizer}; + + // volume calculation + auto volume = Sound.m_amplitudeoffset + Sound.m_amplitudefactor * interpolate(mvOccupied->Vel / (1 + mvOccupied->Vmax), 1.0, 0.5); // scale base volume between 0.5-1.0 + + if (volume > 0.05) + { + Sound.pitch(frequency).gain(volume).play(sound_flags::exclusive | sound_flags::looping); + } + else + { + Sound.stop(); + } +} + void TTrain::update_sounds_runningnoise( sound_source &Sound ) { // frequency calculation auto const normalizer { ( @@ -8635,6 +8668,8 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName) {"brakesound:", {rsBrake, sound_placement::internal, -1, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency, 100.0}}, {"fadesound:", {rsFadeSound, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}}, {"runningnoise:", {rsRunningNoise, sound_placement::internal, EU07_SOUND_GLOBALRANGE, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax }}, + {"resonancenoise:", {rsResonanceNoise, sound_placement::internal, EU07_SOUND_GLOBALRANGE, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax }}, + {"windsound:", {rsWindSound, sound_placement::internal, EU07_SOUND_GLOBALRANGE, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax }}, {"huntingnoise:", {rsHuntingNoise, sound_placement::internal, EU07_SOUND_GLOBALRANGE, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax }}, {"rainsound:", {m_rainsound, sound_placement::internal, -1, sound_type::single, 0, 100.0}}, }; @@ -8698,6 +8733,12 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName) if (rsBrake) { rsBrake->m_frequencyfactor /= (1 + mvOccupied->Vmax); } + if (rsResonanceNoise) { + rsResonanceNoise->m_frequencyfactor /= (1 + mvOccupied->Vmax); + } + if (rsWindSound) { + rsWindSound->m_frequencyfactor /= (1 + mvOccupied->Vmax); + } if (rsRunningNoise) { rsRunningNoise->m_frequencyfactor /= (1 + mvOccupied->Vmax); } @@ -8710,7 +8751,7 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName) dsbReverserKey, dsbNastawnikJazdy, dsbNastawnikBocz, dsbSwitch, dsbPneumaticSwitch, rsHiss, rsHissU, rsHissE, rsHissX, rsHissT, rsSBHiss, rsSBHissU, - rsFadeSound, rsRunningNoise, rsHuntingNoise, + rsFadeSound, rsRunningNoise, rsResonanceNoise,rsWindSound, rsHuntingNoise, dsbHasler, dsbBuzzer,dsbBuzzerShp, dsbSlipAlarm, m_distancecounterclear, m_rainsound, m_radiostop }; for (auto &sound : sounds) { @@ -8733,7 +8774,7 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) dsbReverserKey, dsbNastawnikJazdy, dsbNastawnikBocz, dsbSwitch, dsbPneumaticSwitch, rsHiss, rsHissU, rsHissE, rsHissX, rsHissT, rsSBHiss, rsSBHissU, - rsFadeSound, rsRunningNoise, rsHuntingNoise, + rsFadeSound, rsRunningNoise, rsResonanceNoise, rsWindSound, rsHuntingNoise, dsbHasler, dsbBuzzer, dsbBuzzerShp, dsbSlipAlarm, m_distancecounterclear, m_rainsound, m_radiostop }; for( auto &sound : sounds ) { @@ -9025,6 +9066,8 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) {rsSBHissU, ggBrakeCtrl.model_offset()}, // NOTE: fallback if the local brake model can't be located {rsFadeSound, caboffset}, {rsRunningNoise, caboffset}, + {rsResonanceNoise, caboffset}, + {rsWindSound, caboffset}, {rsHuntingNoise, caboffset}, {dsbHasler, caboffset}, {dsbBuzzer, btLampkaCzuwaka.model_offset()}, diff --git a/Train.h b/Train.h index a6058835..39ec0af1 100644 --- a/Train.h +++ b/Train.h @@ -210,6 +210,7 @@ class TTrain { // update function subroutines void update_sounds( double const Deltatime ); void update_sounds_runningnoise( sound_source &Sound ); + void update_sounds_resonancenoise( sound_source &Sound ); void update_sounds_radio(); inline end cab_to_end( int const End ) const { @@ -804,6 +805,7 @@ public: // reszta może by?publiczna rsBrake, rsFadeSound, rsRunningNoise, + rsResonanceNoise, rsHuntingNoise, m_rainsound; sound_source m_radiosound { sound_placement::internal, 2 * EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // cached template for radio messages From f59c0f2d2cad13a03f0808aa9740d8b4b80885be Mon Sep 17 00:00:00 2001 From: Hirek Date: Sat, 4 Jan 2025 21:16:09 +0100 Subject: [PATCH 29/88] Add missing mover nrot comment --- McZapkie/MOVER.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 73a9df50..d8690fb3 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1418,7 +1418,7 @@ public: double AccSVBased {}; // tangential acceleration calculated from velocity change double AccN = 0.0; // przyspieszenie normalne w [m/s^2] double AccVert = 0.0; // vertical acceleration - double nrot = 0.0; + double nrot = 0.0; // predkosc obrotowa kol (obrotow na sekunde) double nrot_eps = 0.0; //przyspieszenie kątowe kół (bez kierunku) double WheelFlat = 0.0; bool TruckHunting { true }; // enable/disable truck hunting calculation From a8f47baca7864d80938877e53f8fc7e249a245f8 Mon Sep 17 00:00:00 2001 From: Hirek Date: Sat, 4 Jan 2025 21:25:00 +0100 Subject: [PATCH 30/88] Add windsound --- Train.cpp | 10 ++++++++++ Train.h | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Train.cpp b/Train.cpp index cf292a76..bd7d87cc 100644 --- a/Train.cpp +++ b/Train.cpp @@ -8312,6 +8312,16 @@ TTrain::update_sounds( double const Deltatime ) { } } + // dzwiek wiatru rozbijajacego sie o szyby w kabinie + if (rsWindSound) + { + if (!FreeFlyModeFlag && !Global.CabWindowOpen && DynamicObject->GetVelocity() > 0.5) + update_sounds_resonancenoise(*rsWindSound); + else + rsWindSound->stop(FreeFlyModeFlag); + } + + // dzwiek rezonansu (taki drugi runningnoise w sumie) if (rsResonanceNoise) { diff --git a/Train.h b/Train.h index 39ec0af1..11235468 100644 --- a/Train.h +++ b/Train.h @@ -805,7 +805,8 @@ public: // reszta może by?publiczna rsBrake, rsFadeSound, rsRunningNoise, - rsResonanceNoise, + rsResonanceNoise, + rsWindSound, rsHuntingNoise, m_rainsound; sound_source m_radiosound { sound_placement::internal, 2 * EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // cached template for radio messages From e228366c42de441b4e99ab7a6f6a972c6c320c6d Mon Sep 17 00:00:00 2001 From: Jano211 <107213310+Jano211@users.noreply.github.com> Date: Fri, 11 Aug 2023 21:19:52 +0200 Subject: [PATCH 31/88] Fog, skydome and other environment tweaks --- Globals.cpp | 5 +---- Globals.h | 1 + driveruipanels.cpp | 20 +++++++++++++++++--- simulation.cpp | 2 ++ simulationenvironment.cpp | 32 ++++++++++++++++++++++++-------- skydome.cpp | 20 ++++++++++---------- 6 files changed, 55 insertions(+), 25 deletions(-) diff --git a/Globals.cpp b/Globals.cpp index e2c360c7..79256ea0 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -388,10 +388,7 @@ global_settings::ConfigParse(cParser &Parser) { // selected device for audio renderer Parser.getTokens(); Parser >> AirTemperature; - if (false == DebugModeFlag) - { - AirTemperature = clamp(AirTemperature, -15.f, 45.f); - } + AirTemperature = clamp(AirTemperature, -15.f, 45.f); } else if (token == "scalespeculars") { diff --git a/Globals.h b/Globals.h index e0b9f36e..718aa8f5 100644 --- a/Globals.h +++ b/Globals.h @@ -86,6 +86,7 @@ struct global_settings { float Overcast{ 0.1f }; // NOTE: all this weather stuff should be moved elsewhere glm::vec3 FogColor = { 0.6f, 0.7f, 0.8f }; float fFogEnd{ 7500 }; + float fTurbidity{ 128 }; std::string Season{}; // season of the year, based on simulation date std::string Weather{ "cloudy:" }; // current weather std::string Period{}; // time of the day, based on sun position diff --git a/driveruipanels.cpp b/driveruipanels.cpp index cf1d893a..b4e8c70c 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -659,15 +659,29 @@ debug_panel::render_section_scenario() { { auto fogrange = std::log( Global.fFogEnd ); if( ImGui::SliderFloat( - ( to_string( std::exp( fogrange ), 0, 5 ) + " m###fogend" ).c_str(), &fogrange, std::log( 10.0f ), std::log( 25000.0f ), "Fog distance" ) ) { + ( to_string( std::exp( fogrange ), 0, 5 ) + " m###fogend" ).c_str(), &fogrange, std::log( 10.0f ), std::log( 50000.0f ), "Fog distance" ) ) { command_relay relay; relay.post( user_command::setweather, - clamp( std::exp( fogrange ), 10.0f, 25000.0f ), + clamp( std::exp( fogrange ), 10.0f, 50000.0f ), Global.Overcast, GLFW_PRESS, 0 ); } } + { + auto Airtemperature = Global.AirTemperature; + if (ImGui::SliderFloat( + (to_string(Airtemperature, 1) + " deg C###Airtemperature").c_str(), + &Airtemperature, -35.0f, 40.0f, "Air Temperature")) + { + command_relay relay; + relay.post( + user_command::settemperature, + clamp(Airtemperature, -35.0f, 40.0f), + Global.Overcast, + GLFW_PRESS, 0 ); + } + } // cloud cover slider { if( ImGui::SliderFloat( @@ -1288,7 +1302,7 @@ debug_panel::update_section_scenario( std::vector &Output ) { Output.emplace_back( textline, Global.UITextColor ); // current luminance level - textline = "Light level: " + to_string( Global.fLuminance, 3 ) + ( Global.FakeLight ? "(*)" : "" ); + textline = "Light level: " + to_string( Global.fLuminance, 3 ) + ( Global.FakeLight ? "(*)" : "" )+ to_string(Global.SunAngle,2); textline += "\nWind: azimuth " + to_string( simulation::Environment.wind_azimuth(), 0 ) // ma być azymut, czyli 0 na północy i rośnie na wschód diff --git a/simulation.cpp b/simulation.cpp index 1fe8a63e..b40782c1 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -339,6 +339,8 @@ void state_manager::process_commands() { if (commanddata.command == user_command::settemperature) { Global.AirTemperature = commanddata.param1; + Global.Overcast = commanddata.param2; + simulation::Environment.compute_weather(); } if (commanddata.command == user_command::insertmodel) { diff --git a/simulationenvironment.cpp b/simulationenvironment.cpp index 7707ddfb..0006899f 100644 --- a/simulationenvironment.cpp +++ b/simulationenvironment.cpp @@ -66,9 +66,23 @@ world_environment::compute_weather() { Global.Overcast <= 0.50 ? "scattered:" : Global.Overcast <= 0.90 ? "broken:" : Global.Overcast <= 1.00 ? "overcast:" : - ( Global.Season != "winter:" ? - "rain:" : + + (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 : + Global.Overcast > 0.90 ? 9 : + 9 + ); } void @@ -120,7 +134,9 @@ world_environment::update() { float keylightintensity; glm::vec3 keylightcolor; - if( moonlightlevel > sunlightlevel ) { + 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(); @@ -139,10 +155,10 @@ world_environment::update() { keylightintensity = sunlightlevel; m_lightintensity = 1.0f; // include 'golden hour' effect in twilight lighting - float const duskfactor = 1.0f - clamp( Global.SunAngle, 0.0f, 18.0f ) / 18.0f; + float const duskfactor = 1.25f - clamp( Global.SunAngle, 0.0f, 18.0f ) / 18.0f; keylightcolor = interpolate( glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ), - glm::vec3( 235.0f / 255.0f, 140.0f / 255.0f, 36.0f / 255.0f ), + glm::vec3( 235.0f / 255.0f, 120.0f / 255.0f, 36.0f / 255.0f ), duskfactor ); } // ...retrieve current sky colour and brightness... @@ -174,9 +190,9 @@ world_environment::update() { // 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 = - interpolate( m_skydome.GetAverageColor(), m_skydome.GetAverageHorizonColor(), 0.25f ) - * clamp( Global.fLuminance, 0.25f, 1.f ); + Global.FogColor = ((m_skydome.GetAverageHorizonColor()) * keylightcolor) * + clamp(Global.fLuminance, 0.0f, 1.f); + // weather-related simulation factors Global.FrictionWeatherFactor = ( diff --git a/skydome.cpp b/skydome.cpp index f0a8250d..cc7ad696 100644 --- a/skydome.cpp +++ b/skydome.cpp @@ -10,8 +10,8 @@ // by A. J. Preetham Peter Shirley Brian Smits (University of Utah) float CSkyDome::m_distributionluminance[ 5 ][ 2 ] = { // Perez distributions - { 0.17872f , -1.46303f }, // a = darkening or brightening of the horizon - { -0.35540f , 0.42749f }, // b = luminance gradient near the horizon, + { 0.17872f , -1.66303f }, // a = darkening or brightening of the horizon + { -0.35540f , 0.42750f }, // b = luminance gradient near the horizon, { -0.02266f , 5.32505f }, // c = relative intensity of the circumsolar region { 0.12064f , -2.57705f }, // d = width of the circumsolar region { -0.06696f , 0.37027f } // e = relative backscattered light @@ -48,8 +48,8 @@ CSkyDome::CSkyDome (int const Tesselation) : m_tesselation( Tesselation ) { // SetSunPosition( Math3D::vector3(75.0f, 0.0f, 0.0f) ); - SetTurbidity( 3.0f ); - SetExposure( true, 20.0f ); + SetTurbidity( Global.fTurbidity ); + SetExposure( true, 10.0f ); SetOvercastFactor( 0.05f ); Generate(); } @@ -109,7 +109,7 @@ void CSkyDome::Generate() { } void CSkyDome::Update( glm::vec3 const &Sun ) { - + SetTurbidity(Global.fTurbidity); if( true == SetSunPosition( Sun ) ) { // build colors if there's a change in sun position RebuildColors(); @@ -134,7 +134,7 @@ bool CSkyDome::SetSunPosition( glm::vec3 const &Direction ) { void CSkyDome::SetTurbidity( float const Turbidity ) { - m_turbidity = clamp( Turbidity, 1.f, 4.f ); + m_turbidity = clamp( Turbidity, 1.f, 128.f ); } void CSkyDome::SetExposure( bool const Linearexposure, float const Expfactor ) { @@ -296,20 +296,20 @@ void CSkyDome::RebuildColors() { color.y = 0.65f * color.z; } // simple gradient, darkening towards the top - color *= clamp( ( 1.25f - vertex.y ), 0.f, 1.f ); -// color *= ( 1.25f - vertex.y ); + color *= clamp( ( 1.0f - vertex.y ), 0.f, 1.f ); + //color *= ( 0.25f - vertex.y ); // gamma correction color = glm::pow( color, gammacorrection ); m_colours[ i ] = color; averagecolor += color; - if( ( m_vertices.size() - i ) <= ( m_tesselation * 3 + 3 ) ) { + if( ( m_vertices.size() - i ) <= ( m_tesselation * 10 + 10 ) ) { // calculate horizon colour from the bottom band of tris averagehorizoncolor += color; } } m_averagecolour = glm::max( glm::vec3(), averagecolor / static_cast( m_vertices.size() ) ); - m_averagehorizoncolour = glm::max( glm::vec3(), averagehorizoncolor / static_cast( m_tesselation * 3 + 3 ) ); + m_averagehorizoncolour = glm::max( glm::vec3(), averagehorizoncolor / static_cast( m_tesselation * 10 + 10 ) ); m_dirty = true; } From 8a204edaed3a37788ff2596299d07042d9062e89 Mon Sep 17 00:00:00 2001 From: Jano211 <107213310+Jano211@users.noreply.github.com> Date: Fri, 11 Aug 2023 21:22:46 +0200 Subject: [PATCH 32/88] 3-axis rotation, and some corrections in editor mode --- AnimModel.cpp | 10 ++++- editormode.cpp | 100 +++++++++++++++++++++++++++++++++++++-------- editormode.h | 6 ++- editoruipanels.cpp | 58 +++++++++++++++++++++++--- editoruipanels.h | 6 +-- sceneeditor.cpp | 2 +- 6 files changed, 152 insertions(+), 30 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index 13ca6589..557d85e0 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -659,8 +659,9 @@ TAnimModel::export_as_text_( std::ostream &Output ) const { Output << location().x << ' ' << location().y << ' ' - << location().z << ' ' - << vAngle.y << ' '; + << location().z << ' '; + Output + << "0 " ; // 3d shape auto modelfile { ( pModel ? @@ -698,6 +699,11 @@ TAnimModel::export_as_text_( std::ostream &Output ) const { Output << "notransition" << ' '; } // footer + Output << "angles " + << vAngle.x << ' ' + << vAngle.y << ' ' + << vAngle.z << ' '; + // footer Output << "endmodel" << "\n"; diff --git a/editormode.cpp b/editormode.cpp index bf069516..11a61890 100644 --- a/editormode.cpp +++ b/editormode.cpp @@ -246,16 +246,34 @@ editor_mode::on_cursor_pos( double const Horizontal, double const Vertical ) { m_editor.translate( m_node, mouseworldposition, mode_snap() ); } } - else { - // rotate selected node - auto const rotation { glm::vec3 { mousemove.y, mousemove.x, 0 } * 0.25f }; - auto const quantization { ( - mode_snap() ? - 15.f : // TODO: put quantization value in a variable - 0.f ) }; - m_editor.rotate( m_node, rotation, quantization ); - } + else if (mode_rotationY()) + { + // rotate selected node + // auto const rotation{glm::vec3{mousemove.y, mousemove.x, 0} * 0.25f}; + auto const rotation{glm::vec3{0, mousemove.x, 0} * 0.25f}; + auto const quantization{(mode_snap() ? 5.f : // TODO: put quantization value in a variable + 0.f)}; + m_editor.rotate(m_node, rotation, quantization); + } + else if (mode_rotationZ()) + { + // rotate selected node + // auto const rotation{glm::vec3{mousemove.y, mousemove.x, 0} * 0.25f}; + auto const rotation{glm::vec3{0, 0, mousemove.x} * 0.25f}; + auto const quantization{(mode_snap() ? 5.f : // TODO: put quantization value in a variable + 0.f)}; + m_editor.rotate(m_node, rotation, quantization); + } + else if (mode_rotationX()) + { + // rotate selected node + // auto const rotation{glm::vec3{mousemove.y, mousemove.x, 0} * 0.25f}; + auto const rotation{glm::vec3{mousemove.y, 0, 0} * 0.25f}; + auto const quantization{(mode_snap() ? 5.f : // TODO: put quantization value in a variable + 0.f)}; + m_editor.rotate(m_node, rotation, quantization); + } } /* @@ -303,9 +321,8 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods m_node = nullptr; - GfxRenderer->Pick_Node_Callback([this, mode](scene::basic_node *node) - { - editor_ui *ui = static_cast( m_userinterface.get() ); + GfxRenderer->Pick_Node_Callback([this, mode,Action,Button](scene::basic_node *node) { + editor_ui *ui = static_cast(m_userinterface.get()); if (mode == nodebank_panel::MODIFY) { if (!m_dragging) @@ -316,7 +333,7 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods Application.set_cursor( GLFW_CURSOR_DISABLED ); else m_dragging = false; - ui->set_node( m_node ); + ui->set_node(m_node); } else if (mode == nodebank_panel::COPY) { if (node && typeid(*node) == typeid(TAnimModel)) { @@ -360,6 +377,45 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods m_takesnapshot = true; } } + if (Button == GLFW_MOUSE_BUTTON_RIGHT) + { + + /*if (Action == GLFW_PRESS) + { + // left button press + auto const mode = static_cast(m_userinterface.get())->mode(); + + m_node = nullptr; + + GfxRenderer->Pick_Node_Callback([this, mode](scene::basic_node *node) { + editor_ui *ui = static_cast(m_userinterface.get()); + + if (mode == nodebank_panel::MODIFY) + { + if (!m_dragging) + return; + + m_node = node; + if (m_node) + Application.set_cursor(GLFW_CURSOR_DISABLED); + else + m_dragging = false; + ui->set_node(m_node); + } + }); + + m_dragging = true; + } + else + { + // left button release + if (m_node) + Application.set_cursor(GLFW_CURSOR_NORMAL); + m_dragging = false; + // prime history stack for another snapshot + m_takesnapshot = true; + }*/ + } m_input.mouse.button( Button, Action ); } @@ -388,14 +444,24 @@ editor_mode::mode_translation_vertical() const { return ( true == Global.shiftState ); } -bool -editor_mode::mode_rotation() const { +bool editor_mode::mode_rotationY() const +{ - return ( true == Global.altState ); + return ((true == Global.altState) && (false == Global.ctrlState) && (false == Global.shiftState)); +} +bool editor_mode::mode_rotationX() const +{ + + return ((true == Global.altState) && (true == Global.ctrlState) && (false == Global.shiftState)); +} +bool editor_mode::mode_rotationZ() const +{ + + return ((true == Global.altState) && (true == Global.ctrlState) && (true == Global.shiftState)); } bool editor_mode::mode_snap() const { - return ( true == Global.ctrlState ); + return ((false == Global.altState) && (true == Global.ctrlState) && (false == Global.shiftState)); } diff --git a/editormode.h b/editormode.h index cc6ea23c..2b757867 100644 --- a/editormode.h +++ b/editormode.h @@ -73,7 +73,11 @@ private: bool mode_translation_vertical() const; bool - mode_rotation() const; + mode_rotationY() const; + bool + mode_rotationX() const; + bool + mode_rotationZ() const; bool mode_snap() const; // members diff --git a/editoruipanels.cpp b/editoruipanels.cpp index 7b6afde1..8b15bef2 100644 --- a/editoruipanels.cpp +++ b/editoruipanels.cpp @@ -68,8 +68,10 @@ itemproperties_panel::update( scene::basic_node const *Node ) { auto const *subnode = static_cast( node ); - textline = "angle: " + to_string( clamp_circular( subnode->vAngle.y, 360.f ), 2 ) + " deg"; - textline += "; lights: "; + textline = "angle_x: " + to_string(clamp_circular(subnode->vAngle.x, 360.f), 2) + " deg, " + + "angle_y: " + to_string(clamp_circular(subnode->vAngle.y, 360.f), 2) + " deg, " + + "angle_z: " + to_string(clamp_circular(subnode->vAngle.z, 360.f), 2) + " deg"; + textline += ";\nlights: "; if( subnode->iNumLights > 0 ) { textline += '['; for( int lightidx = 0; lightidx < subnode->iNumLights; ++lightidx ) { @@ -150,7 +152,7 @@ itemproperties_panel::update( scene::basic_node const *Node ) { + to_string( path.points[ segment_data::point::start ].x, 3 ) + ", " + to_string( path.points[ segment_data::point::start ].y, 3 ) + ", " + to_string( path.points[ segment_data::point::start ].z, 3 ) + "]->" - + "[" + + " [" + to_string( path.points[ segment_data::point::end ].x, 3 ) + ", " + to_string( path.points[ segment_data::point::end ].y, 3 ) + ", " + to_string( path.points[ segment_data::point::end ].z, 3 ) + "] "; @@ -353,6 +355,42 @@ nodebank_panel::nodebank_panel( std::string const &Name, bool const Isopen ) : u return ( Left.first < Right.first ); } ); } } +void +nodebank_panel::nodebank_reload(){ + m_nodebank.clear(); + std::ifstream file; + file.open("nodebank.txt", std::ios_base::in | std::ios_base::binary); + std::string line; + while (std::getline(file, line)) + { + if (line.size() < 4) + { + continue; + } + auto const labelend{line.find("node")}; + auto const nodedata{ + (labelend == std::string::npos ? "" : labelend == 0 ? line : line.substr(labelend))}; + auto const label{ + (labelend == std::string::npos ? + line : + labelend == 0 ? generate_node_label(nodedata) : line.substr(0, labelend))}; + + m_nodebank.push_back({label, std::make_shared(nodedata)}); + } + // sort alphabetically content of each group + auto groupbegin{m_nodebank.begin()}; + auto groupend{groupbegin}; + while (groupbegin != m_nodebank.end()) + { + groupbegin = std::find_if(groupend, m_nodebank.end(), [](auto const &Entry) { + return (false == Entry.second->empty()); + }); + groupend = std::find_if(groupbegin, m_nodebank.end(), + [](auto const &Entry) { return (Entry.second->empty()); }); + std::sort(groupbegin, groupend, + [](auto const &Left, auto const &Right) { return (Left.first < Right.first); }); + } +} void nodebank_panel::render() { @@ -378,11 +416,19 @@ nodebank_panel::render() { if( true == ImGui::Begin( panelname.c_str(), nullptr, flags ) ) { - ImGui::RadioButton("modify node", (int*)&mode, MODIFY); + ImGui::RadioButton("Modify node", (int*)&mode, MODIFY); ImGui::SameLine(); - ImGui::RadioButton("insert from bank", (int*)&mode, ADD); + ImGui::RadioButton("Insert from bank", (int*)&mode, ADD); + ImGui::SameLine(); + ImGui::RadioButton("Brush", (int*)&mode, BRUSH); ImGui::SameLine(); - ImGui::RadioButton( "copy to bank", (int*)&mode, COPY ); + ImGui::RadioButton( "Copy to bank", (int*)&mode, COPY ); + ImGui::SameLine(); + if (ImGui::Button("Reload Nodebank")) + { + nodebank_reload(); + } + ImGui::PushItemWidth(-1); ImGui::InputTextWithHint( "Search", "Search node bank", m_nodesearch, IM_ARRAYSIZE( m_nodesearch ) ); diff --git a/editoruipanels.h b/editoruipanels.h index fc92f356..22fc554e 100644 --- a/editoruipanels.h +++ b/editoruipanels.h @@ -54,13 +54,13 @@ public: enum edit_mode { MODIFY, COPY, - ADD + ADD, + BRUSH }; - edit_mode mode = MODIFY; nodebank_panel( std::string const &Name, bool const Isopen ); - + void nodebank_reload(); void render() override; void add_template(const std::string &desc); const std::string* get_active_template(); diff --git a/sceneeditor.cpp b/sceneeditor.cpp index 2f88b63f..6c6c0420 100644 --- a/sceneeditor.cpp +++ b/sceneeditor.cpp @@ -137,7 +137,7 @@ basic_editor::translate_memorycell( TMemCell *Memorycell, float const Offset ) { void basic_editor::rotate( scene::basic_node *Node, glm::vec3 const &Angle, float const Quantization ) { - glm::vec3 rotation { 0, Angle.y, 0 }; + glm::vec3 rotation{Angle.x, Angle.y, Angle.z}; // quantize resulting angle if requested and type of the node allows it // TBD, TODO: angle quantization for types other than instanced models From 58b1bc38ae1a2fdbebd312455c9fb92cf2b86ac2 Mon Sep 17 00:00:00 2001 From: Jano211 <107213310+Jano211@users.noreply.github.com> Date: Fri, 11 Aug 2023 21:26:15 +0200 Subject: [PATCH 33/88] Improved scenery loading from SBT When found SBT file, ignores "*_ter.scm" files --- Globals.h | 3 +- parser.cpp | 86 ++++++++++++++++++++++++++++------- scene.cpp | 1 + simulationstateserializer.cpp | 10 ++++ 4 files changed, 82 insertions(+), 18 deletions(-) diff --git a/Globals.h b/Globals.h index 718aa8f5..82a1b3f9 100644 --- a/Globals.h +++ b/Globals.h @@ -72,11 +72,12 @@ struct global_settings { int iConvertModels{ 0 }; // tworzenie plików binarnych int iConvertIndexRange{ 1000 }; // range of duplicate vertex scan bool file_binary_terrain{ true }; // enable binary terrain (de)serialization + bool file_binary_terrain_state{true}; // logs int iWriteLogEnabled{ 3 }; // maska bitowa: 1-zapis do pliku, 2-okienko, 4-nazwy torów bool MultipleLogs{ false }; unsigned int DisabledLogTypes{ 0 }; - bool ParserLogIncludes{ false }; + bool ParserLogIncludes{ true }; // simulation bool RealisticControlMode{ false }; // controls ability to steer the vehicle from outside views bool bEnableTraction{ true }; diff --git a/parser.cpp b/parser.cpp index f854b8e2..6b1bb67a 100644 --- a/parser.cpp +++ b/parser.cpp @@ -247,17 +247,41 @@ std::string cParser::readToken( bool ToLower, const char *Break ) { if( expandIncludes && token == "include" ) { std::string includefile = allowRandomIncludes ? deserialize_random_set(*this) : readToken(ToLower); // nazwa pliku replace_slashes(includefile); - if( ( true == LoadTraction ) - || ( ( false == contains( includefile, "tr/" ) ) - && ( false == contains( includefile, "tra/" ) ) ) ) { - if (Global.ParserLogIncludes) - WriteLog("including: " + includefile); + if ((true == LoadTraction) || + ((false == contains(includefile, "tr/")) && (false == contains(includefile, "tra/")))) + { + if (false == contains(includefile, "_ter.scm")) + { + if (Global.ParserLogIncludes) + WriteLog("including: " + includefile); mIncludeParser = std::make_shared( includefile, buffer_FILE, mPath, LoadTraction, readParameters( *this ) ); mIncludeParser->allowRandomIncludes = allowRandomIncludes; mIncludeParser->autoclear( m_autoclear ); if( mIncludeParser->mSize <= 0 ) { ErrorLog( "Bad include: can't open file \"" + includefile + "\"" ); } + } + else + { + if(true == Global.file_binary_terrain_state) + { + WriteLog("SBT found, ignoring: " + includefile); + readParameters(*this); + } + else + { + if (Global.ParserLogIncludes) + WriteLog("including terrain: " + includefile); + mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, + LoadTraction, readParameters(*this)); + mIncludeParser->allowRandomIncludes = allowRandomIncludes; + mIncludeParser->autoclear(m_autoclear); + if (mIncludeParser->mSize <= 0) + { + ErrorLog("Bad include: can't open file \"" + includefile + "\""); + } + } + } } else { while( token != "end" ) { @@ -272,18 +296,46 @@ std::string cParser::readToken( bool ToLower, const char *Break ) { includeparser.allowRandomIncludes = allowRandomIncludes; std::string includefile = allowRandomIncludes ? deserialize_random_set( includeparser ) : includeparser.readToken( ToLower ); // nazwa pliku replace_slashes(includefile); - if( ( true == LoadTraction ) - || ( ( false == contains( includefile, "tr/" ) ) - && ( false == contains( includefile, "tra/" ) ) ) ) { - if (Global.ParserLogIncludes) - WriteLog("including: " + includefile); - mIncludeParser = std::make_shared( includefile, buffer_FILE, mPath, LoadTraction, readParameters( includeparser ) ); - mIncludeParser->allowRandomIncludes = allowRandomIncludes; - mIncludeParser->autoclear( m_autoclear ); - if( mIncludeParser->mSize <= 0 ) { - ErrorLog( "Bad include: can't open file \"" + includefile + "\"" ); - } - } + if ((true == LoadTraction) || + ((false == contains(includefile, "tr/")) && (false == contains(includefile, "tra/")))) + { + if (false == contains(includefile, "_ter.scm")) + { + if (Global.ParserLogIncludes) + WriteLog("including: " + includefile); + mIncludeParser = std::make_shared( + includefile, buffer_FILE, mPath, LoadTraction, readParameters(includeparser)); + mIncludeParser->allowRandomIncludes = allowRandomIncludes; + mIncludeParser->autoclear(m_autoclear); + if (mIncludeParser->mSize <= 0) + { + ErrorLog("Bad include: can't open file \"" + includefile + "\""); + } + } + else + { + if (true == Global.file_binary_terrain_state) + { + WriteLog("SBT found, ignoring: " + includefile); + readParameters(includeparser); + } + else + { + if (Global.ParserLogIncludes) + WriteLog("including terrain: " + includefile); + mIncludeParser = + std::make_shared(includefile, buffer_FILE, mPath, LoadTraction, + readParameters(includeparser)); + mIncludeParser->allowRandomIncludes = allowRandomIncludes; + mIncludeParser->autoclear(m_autoclear); + if (mIncludeParser->mSize <= 0) + { + ErrorLog("Bad include: can't open file \"" + includefile + "\""); + } + + } + } + } token = readToken( ToLower, Break ); } // all done diff --git a/scene.cpp b/scene.cpp index 6933ed86..8daa6117 100644 --- a/scene.cpp +++ b/scene.cpp @@ -1159,6 +1159,7 @@ basic_region::deserialize( std::string const &Scenariofile ) { filename += EU07_FILEEXTENSION_REGION; if( false == FileExists( filename ) ) { + Global.file_binary_terrain_state = false; return false; } // region file version 1 diff --git a/simulationstateserializer.cpp b/simulationstateserializer.cpp index 2327bc71..7b9dde5e 100644 --- a/simulationstateserializer.cpp +++ b/simulationstateserializer.cpp @@ -55,6 +55,16 @@ state_serializer::deserialize_begin( std::string const &Scenariofile ) { state->scratchpad.binary.terrain = Region->is_scene( Scenariofile ) ; } + if (false != state->scratchpad.binary.terrain) + { + Global.file_binary_terrain_state = true; + WriteLog("SBT present"); + } + else + { + Global.file_binary_terrain_state = false; + WriteLog("SBT absent"); + } scene::Groups.create(); if( false == state->input.ok() ) From 910ba6c2b235d54de6d441768c0b2749d36ad9e9 Mon Sep 17 00:00:00 2001 From: Jano211 <107213310+Jano211@users.noreply.github.com> Date: Sun, 5 Jan 2025 18:08:15 +0100 Subject: [PATCH 34/88] Tracks width smooth change and no "include file" info in log --- Track.cpp | 12 +++++++----- parser.cpp | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Track.cpp b/Track.cpp index ec859e91..80d33310 100644 --- a/Track.cpp +++ b/Track.cpp @@ -2604,7 +2604,9 @@ void TTrack::create_track_rail_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ) { auto const fHTW { 0.5f * std::abs( fTrackWidth ) }; - + float const fHTW2{((iTrapezoid & 2) != 0 ? // ten bit oznacza, że istnieje odpowiednie pNext + 0.5f * std::fabs(trNext->fTrackWidth) : // połowa rozstawu/nawierzchni + fHTW)}; float roll1{ 0.f }, roll2{ 0.f }; @@ -2660,8 +2662,8 @@ TTrack::create_track_rail_profile( gfx::vertex_array &Right, gfx::vertex_array & // trapez albo przechyłki, to oddzielne punkty na końcu Right[ pointcount + i ] = { // position - {( fHTW + szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, - -( fHTW + szyna[ i ].position.x ) * sin2 + szyna[ i ].position.y * cos2, + {( fHTW2 + szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, + -( fHTW2 + szyna[ i ].position.x ) * sin2 + szyna[ i ].position.y * cos2, szyna[ i ].position.z}, // normal { szyna[ i ].normal.x * cos2 + szyna[ i ].normal.y * sin2, @@ -2673,8 +2675,8 @@ TTrack::create_track_rail_profile( gfx::vertex_array &Right, gfx::vertex_array & Left[ pointcount * 2 - 1 - i ] = { // position - {(-fHTW - szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, - -(-fHTW - szyna[ i ].position.x ) * sin2 + szyna[ i ].position.y * cos2, + {(-fHTW2 - szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, + -(-fHTW2 - szyna[ i ].position.x ) * sin2 + szyna[ i ].position.y * cos2, szyna[ i ].position.z}, // normal {-szyna[ i ].normal.x * cos2 + szyna[ i ].normal.y * sin2, diff --git a/parser.cpp b/parser.cpp index 6b1bb67a..e46509d4 100644 --- a/parser.cpp +++ b/parser.cpp @@ -253,7 +253,7 @@ std::string cParser::readToken( bool ToLower, const char *Break ) { if (false == contains(includefile, "_ter.scm")) { if (Global.ParserLogIncludes) - WriteLog("including: " + includefile); + //WriteLog("including: " + includefile); mIncludeParser = std::make_shared( includefile, buffer_FILE, mPath, LoadTraction, readParameters( *this ) ); mIncludeParser->allowRandomIncludes = allowRandomIncludes; mIncludeParser->autoclear( m_autoclear ); @@ -302,7 +302,7 @@ std::string cParser::readToken( bool ToLower, const char *Break ) { if (false == contains(includefile, "_ter.scm")) { if (Global.ParserLogIncludes) - WriteLog("including: " + includefile); + //WriteLog("including: " + includefile); mIncludeParser = std::make_shared( includefile, buffer_FILE, mPath, LoadTraction, readParameters(includeparser)); mIncludeParser->allowRandomIncludes = allowRandomIncludes; From 7cac46df0294198c07579d1b9ac91f31cfedd7b6 Mon Sep 17 00:00:00 2001 From: Jano211 <107213310+Jano211@users.noreply.github.com> Date: Fri, 11 Aug 2023 21:34:09 +0200 Subject: [PATCH 35/88] Changes in camera shaking, and others: clatter sounds, csm stage boundaries, particles physics and hiding log panel when in launcher mode. --- DynObj.cpp | 58 ++++++++++++++++++------- DynObj.h | 2 +- Globals.cpp | 2 +- Track.cpp | 4 ++ application.cpp | 1 + launcher/scenery_list.cpp | 1 + opengl33renderer.cpp | 14 +++--- particles.cpp | 90 ++++++++++++++++++++++++++++++--------- 8 files changed, 129 insertions(+), 43 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index d3571f05..11ccd413 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -3491,17 +3491,17 @@ bool TDynamicObject::Update(double dt, double dt1) interpolate( 0.8, 1.2, clamp( - MyTrack->iQualityFlag / 20.0, - 0.0, 1.0 ) ); + MyTrack->iQualityFlag / 10.0, + 0.0, 1.5 ) ); switch( MyTrack->eEnvironment ) { case e_tunnel: { - volume *= 1.1; + volume *= 1.3; break; } case e_bridge: { - volume *= 1.2; - break; - } + volume *= 1.5; + break; + } default: { break; } @@ -3530,15 +3530,28 @@ bool TDynamicObject::Update(double dt, double dt1) .gain( volume ) .play(); // crude bump simulation, drop down on even axles, move back up on the odd ones - MoverParameters->AccVert += - interpolate( - 0.01, 0.05, - clamp( - GetVelocity() / ( 1 + MoverParameters->Vmax ), - 0.0, 1.0 ) ) - * ( ( axleindex % 2 ) != 0 ? - 1 : - -1 ); + //MoverParameters->AccVert += (MoverParameters->Vel*0.1f) * + MoverParameters->AccVert += + clamp(-1.0, 1.0, (MoverParameters->Vel / ( 1 + MoverParameters->Vmax )) * MyTrack->iDamageFlag * ((axleindex % 2) != 0 ? 1 : -1)); + + if (MyTrack->eType == tt_Switch){ + MoverParameters->AccSVBased += clamp( + -1.0, 1.0, + (MoverParameters->Vel / (1 + MoverParameters->Vmax)) * + MyTrack->iDamageFlag * ((axleindex % 2) != 0 ? 1 : -1)); + MoverParameters->AccS += clamp( + -1.0, 1.0, + (MoverParameters->Vel / (1 + MoverParameters->Vmax)) * + MyTrack->iDamageFlag * ((axleindex % 2) != 0 ? 1 : -1)); + MoverParameters->AccN += clamp( + -1.0, 1.0, + (MoverParameters->Vel / (1 + MoverParameters->Vmax)) * + MyTrack->iDamageFlag * ((axleindex % 2) != 0 ? 1 : -1)); + MoverParameters->AccVert += clamp( + -1.0, 1.0, + (MoverParameters->Vel / (1 + MoverParameters->Vmax)) * + MyTrack->iDamageFlag * ((axleindex % 2) != 0 ? 1 : -1)); + } } } ++axleindex; @@ -4694,6 +4707,19 @@ void TDynamicObject::RenderSounds() { MoverParameters->Vel / 40.0, 0.0, 1.0 ) ); } + switch( MyTrack->eEnvironment ) { + case e_tunnel: { + volume *= 1.3; + break; + } + case e_bridge: { + volume *= 1.5; + break; + } + default: { + break; + } + } if( volume > 0.05 ) { // apply calculated parameters to all motor instances @@ -7622,7 +7648,7 @@ TDynamicObject::update_shake( double const Timedelta ) { shakevector += Math3D::vector3( -MoverParameters->AccN * Timedelta * 5.0, // highlight side sway -MoverParameters->AccVert * Timedelta, - -MoverParameters->AccSVBased * Timedelta * 1.25 ); // accent acceleration/deceleration + -MoverParameters->AccSVBased * Timedelta * 1.5); // accent acceleration/deceleration } auto shake { 1.25 * ShakeSpring.ComputateForces( shakevector, ShakeState.offset ) }; diff --git a/DynObj.h b/DynObj.h index 32207ab9..5462d5ff 100644 --- a/DynObj.h +++ b/DynObj.h @@ -790,7 +790,7 @@ public: struct baseshake_config { Math3D::vector3 angle_scale { 0.05, 0.0, 0.1 }; // roll, yaw, pitch Math3D::vector3 jolt_scale { 0.2, 0.2, 0.1 }; - double jolt_limit { 0.15 }; + double jolt_limit { 2.0f }; } BaseShake; struct engineshake_config { float scale { 2.f }; diff --git a/Globals.cpp b/Globals.cpp index 79256ea0..186aaac1 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -320,7 +320,7 @@ global_settings::ConfigParse(cParser &Parser) { Parser.getTokens(1, false); int size; Parser >> size; - iMaxTextureSize = clamp_power_of_two(size, 512, 8192); + iMaxTextureSize = clamp_power_of_two(size, 64, 8192); } else if (token == "maxcabtexturesize") { diff --git a/Track.cpp b/Track.cpp index 80d33310..bf80b949 100644 --- a/Track.cpp +++ b/Track.cpp @@ -447,6 +447,10 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin) >> fTrackWidth >> fFriction >> fSoundDistance; + if (eType == tt_Switch) + { + fSoundDistance = 10.0f; + } fTrackWidth2 = fTrackWidth; // rozstaw/szerokość w punkcie 2, na razie taka sama parser->getTokens(2); *parser diff --git a/application.cpp b/application.cpp index ccd49af3..d2a486e0 100644 --- a/application.cpp +++ b/application.cpp @@ -519,6 +519,7 @@ eu07_application::push_mode( eu07_application::mode const Mode ) { if (!m_modes[Mode]) { if (Mode == mode::launcher) + Global.loading_log = false; m_modes[Mode] = std::make_shared(); if (Mode == mode::scenarioloader) m_modes[Mode] = std::make_shared(); diff --git a/launcher/scenery_list.cpp b/launcher/scenery_list.cpp index 3db32ac0..848afc30 100644 --- a/launcher/scenery_list.cpp +++ b/launcher/scenery_list.cpp @@ -100,6 +100,7 @@ void ui::scenerylist_panel::draw_launch_box() ImGui::TextWrapped(selected_trainset->description.c_str()); if (ImGui::Button(STR_C("Launch"), ImVec2(-1, 0))) { + Global.loading_log = !Global.loading_log; if (!launch_simulation()) ImGui::OpenPopup("missing_driver"); } diff --git a/opengl33renderer.cpp b/opengl33renderer.cpp index 281e362f..aeada261 100644 --- a/opengl33renderer.cpp +++ b/opengl33renderer.cpp @@ -984,14 +984,18 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) setup_drawing( false ); glViewport(0, 0, m_shadowbuffersize, m_shadowbuffersize); - - float csmstageboundaries[] = { 0.0f, 5.0f, 25.0f, 1.0f }; + float csmstageboundaries[] = {0.0f, + Global.shadowtune.range / 32, + Global.shadowtune.range / 8, + Global.shadowtune.range}; if( Global.shadowtune.map_size > 2048 ) { // increase coverage if our shadow map is large enough to produce decent results - csmstageboundaries[ 1 ] *= Global.shadowtune.map_size / 2048; - csmstageboundaries[ 2 ] *= Global.shadowtune.map_size / 2048; + csmstageboundaries[1] *= Global.shadowtune.map_size / 2048; + csmstageboundaries[2] *= Global.shadowtune.map_size / 2048; + csmstageboundaries[3] *= Global.shadowtune.map_size / 2048; } + for( auto idx = 0; idx < m_shadowpass.size(); ++idx ) { m_shadow_fb->attach( *m_shadow_tex, GL_DEPTH_ATTACHMENT, idx ); @@ -1003,7 +1007,7 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) setup_matrices(); scene_ubs.projection = OpenGLMatrices.data( GL_PROJECTION ); auto const csmstagezfar { - ( csmstageboundaries[ idx + 1 ] > 1.f ? csmstageboundaries[ idx + 1 ] : Global.shadowtune.range ) + (csmstageboundaries[ idx + 1 ]) + ( m_shadowpass.size() - idx ) }; // we can fairly safely add some extra padding in the early stages scene_ubs.cascade_end[ idx ] = csmstagezfar * csmstagezfar; // store squared to allow semi-optimized length2 comparisons in the shader scene_ubo->update( scene_ubs ); diff --git a/particles.cpp b/particles.cpp index 4b839031..361f2b8c 100644 --- a/particles.cpp +++ b/particles.cpp @@ -170,15 +170,46 @@ smoke_source::update( double const Timedelta, bool const Onlydespawn ) { m_max_particles ) ); // consider special spawn rate cases if( m_ownertype == owner_type::vehicle ) { - // HACK: don't spawn particles in tunnels, to prevent smoke clipping through 'terrain' outside - if( m_owner.vehicle->RaTrackGet()->eEnvironment == e_tunnel ) { - m_spawncount = 0.f; - } - if( false == m_owner.vehicle->bEnabled ) { - // don't spawn particles for vehicles which left the scenario - m_spawncount = 0.f; - } - } + + if (Global.AirTemperature <= 5.f || m_owner.vehicle->MoverParameters->dizel_heat.Ts < 45.f) + { + m_emitter.color == glm::vec3{128, 128, 128}; + } + + if (m_owner.vehicle->MoverParameters->dizel_spinup == true) + { + m_spawncount = + ((false == Global.Smoke) || (true == Onlydespawn)) ? + 0.f : + std::min( + // m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity ), + m_spawncount +(m_spawnrate * Timedelta * Global.SmokeFidelity * (((m_owner.vehicle->MoverParameters->enrot) / 4 ) * 0.01)), m_max_particles); + } + else + { + if (m_owner.vehicle->MoverParameters->DirAbsolute == 0 || + m_owner.vehicle->MoverParameters->Im == 0) + { + m_spawncount = + ((false == Global.Smoke) || (true == Onlydespawn)) ? + 0.f : + std::min( + // m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity ), + m_spawncount + (m_spawnrate * Timedelta * Global.SmokeFidelity * ((((m_owner.vehicle->MoverParameters->DElist[m_owner.vehicle->MoverParameters->MainCtrlPosNo].RPM -m_owner.vehicle->MoverParameters->enrot) /60) *0.02) *(m_owner.vehicle->MoverParameters->EnginePower * 0.005))), m_max_particles); + } + else + { + + m_spawncount = + ((false == Global.Smoke) || (true == Onlydespawn)) ? + 0.f : + std::min( + // m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity ), + m_spawncount + (m_spawnrate * Timedelta * Global.SmokeFidelity * ((((m_owner.vehicle->MoverParameters->DElist[m_owner.vehicle->MoverParameters->MainCtrlPosNo].RPM -m_owner.vehicle->MoverParameters->enrot) /60) * (sqrt(m_owner.vehicle->MoverParameters->Im) * 0.01) * 0.02) *(m_owner.vehicle->MoverParameters->EnginePower * 0.005))), m_max_particles); + } + } + } + // update spawned particles for( auto particleiterator { std::begin( m_particles ) }; particleiterator != std::end( m_particles ); ++particleiterator ) { @@ -301,14 +332,21 @@ smoke_source::initialize( smoke_particle &Particle ) { if( m_ownertype == owner_type::vehicle ) { Particle.opacity *= m_owner.vehicle->MoverParameters->dizel_fill; - auto const enginerevolutionsfactor { 0.5f }; // high engine revolutions increase initial particle velocity + auto const enginerevolutionsfactor { 1.5f }; // high engine revolutions increase initial particle velocity switch( m_owner.vehicle->MoverParameters->EngineType ) { - case TEngineType::DieselElectric: { - Particle.velocity *= 1.0 + enginerevolutionsfactor * m_owner.vehicle->MoverParameters->enrot / ( m_owner.vehicle->MoverParameters->DElist[ m_owner.vehicle->MoverParameters->MainCtrlPosNo ].RPM / 60.0 ); - break; - } - case TEngineType::DieselEngine: { - Particle.velocity *= 1.0 + enginerevolutionsfactor * m_owner.vehicle->MoverParameters->enrot / m_owner.vehicle->MoverParameters->nmax; + case TEngineType::DieselElectric: { + if (m_owner.vehicle->MoverParameters->dizel_spinup == true) + { + Particle.velocity *= 0.38*(((m_owner.vehicle->MoverParameters->enrot)/2)*0.5); // / m_owner.vehicle->MoverParameters->dizel_fill *0.01)) ; + + } + else + { + + Particle.velocity *= ((((m_owner.vehicle->MoverParameters->enrot)/60)*0.6) * (m_owner.vehicle->MoverParameters->EnginePower *0.03)); // / m_owner.vehicle->MoverParameters->dizel_fill *0.01)) ; + //Particle.velocity *= m_owner.vehicle->GetVelocity(); // / m_owner.vehicle->MoverParameters->dizel_fill *0.01)) ; + } + break; } default: { @@ -330,10 +368,23 @@ smoke_source::update( smoke_particle &Particle, bounding_box &Boundingbox, doubl // crude smoke dispersion simulation // http://www.auburn.edu/academic/forestry_wildlife/fire/smoke_guide/smoke_dispersion.htm - Particle.velocity.y += ( 0.005 * Particle.velocity.y ) * std::min( 0.f, Global.AirTemperature - 10 ) * Timedelta; // decelerate faster in cold weather + switch (m_ownertype) + { + case owner_type::vehicle: + { + Particle.velocity.y += ( 0.025 * Particle.velocity.y ) * std::min( 0.f, Global.AirTemperature - 90 ) * Timedelta; // decelerate faster in cold weather + Particle.velocity.y -= ( (0.05 * (pow(m_owner.vehicle->GetVelocity()*1,0.4))) * Particle.velocity.y ) * Global.Overcast * Timedelta; // decelerate faster with high air humidity and/or precipitation + break; + } + default: + { + Particle.velocity.y += ( 0.005 * Particle.velocity.y ) * std::min( 0.f, Global.AirTemperature - 10 ) * Timedelta; // decelerate faster in cold weather Particle.velocity.y -= ( 0.050 * Particle.velocity.y ) * Global.Overcast * Timedelta; // decelerate faster with high air humidity and/or precipitation Particle.velocity.y = std::max( 0.25 * ( 2.f - Global.Overcast ), Particle.velocity.y ); // put a cap on deceleration - + break; + } + } + Particle.position += Particle.velocity * static_cast( Timedelta ); Particle.position += 0.1f * Particle.age * simulation::Environment.wind() * static_cast( Timedelta ); // m_velocitymodifier.update( Particle.velocity, Timedelta ); @@ -423,8 +474,7 @@ particle_manager::find( std::string const &Template ) { } // ... and if it fails try to add the template to the database from a data file smoke_source source; - cParser sound_parser( templatepath + templatename + ".txt", cParser::buffer_FILE ); - if( source.deserialize( sound_parser ) ) { + if( source.deserialize( cParser( templatepath + templatename + ".txt", cParser::buffer_FILE ) ) ) { // if deserialization didn't fail finish source setup... source.m_opacitymodifier.bind( &Global.SmokeFidelity ); // ...then cache the source as template for future instances From 96f43e11ff87cf8b57164781073a90536ab65e52 Mon Sep 17 00:00:00 2001 From: Jano211 <107213310+Jano211@users.noreply.github.com> Date: Sun, 5 Jan 2025 20:13:47 +0100 Subject: [PATCH 36/88] Better bumps on switches --- DynObj.cpp | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 11ccd413..b9022a0f 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -3535,23 +3535,30 @@ bool TDynamicObject::Update(double dt, double dt1) clamp(-1.0, 1.0, (MoverParameters->Vel / ( 1 + MoverParameters->Vmax )) * MyTrack->iDamageFlag * ((axleindex % 2) != 0 ? 1 : -1)); if (MyTrack->eType == tt_Switch){ - MoverParameters->AccSVBased += clamp( - -1.0, 1.0, - (MoverParameters->Vel / (1 + MoverParameters->Vmax)) * - MyTrack->iDamageFlag * ((axleindex % 2) != 0 ? 1 : -1)); - MoverParameters->AccS += clamp( - -1.0, 1.0, - (MoverParameters->Vel / (1 + MoverParameters->Vmax)) * - MyTrack->iDamageFlag * ((axleindex % 2) != 0 ? 1 : -1)); - MoverParameters->AccN += clamp( - -1.0, 1.0, - (MoverParameters->Vel / (1 + MoverParameters->Vmax)) * - MyTrack->iDamageFlag * ((axleindex % 2) != 0 ? 1 : -1)); - MoverParameters->AccVert += clamp( - -1.0, 1.0, - (MoverParameters->Vel / (1 + MoverParameters->Vmax)) * - MyTrack->iDamageFlag * ((axleindex % 2) != 0 ? 1 : -1)); - } + MoverParameters->AccS += + clamp(0.0, 1.0, + (clamp(0.0, MoverParameters->Vmax, + MoverParameters->Vmax - + (MoverParameters->Vel + + MoverParameters->Vmax * 0.32f))) * + .05f * (MyTrack->iDamageFlag * 0.25f)) * + ((axleindex % 2) != 0 ? 1 : -1); + MoverParameters->AccN += + clamp(0.0, 1.0, + (clamp(0.0, MoverParameters->Vmax, + MoverParameters->Vmax - + (MoverParameters->Vel + + MoverParameters->Vmax * 0.32f))) * + .05f * (MyTrack->iDamageFlag * 0.25f)) * + ((axleindex % 2) != 0 ? 1 : -1); + MoverParameters->AccVert += + clamp(0.0, 2.0, + (clamp(0.0, MoverParameters->Vmax, + MoverParameters->Vmax - + (MoverParameters->Vel + + MoverParameters->Vmax * 0.32f))) * + .05f * (MyTrack->iDamageFlag * 0.25f)); + } } } ++axleindex; From 4d72d3522b64706f4a2f3de4a1b5fc14a022f00b Mon Sep 17 00:00:00 2001 From: Jano211 <107213310+Jano211@users.noreply.github.com> Date: Sun, 5 Jan 2025 20:43:15 +0100 Subject: [PATCH 37/88] Better bumps on normal tracks --- DynObj.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index b9022a0f..46d099b5 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -3529,11 +3529,19 @@ bool TDynamicObject::Update(double dt, double dt1) .pitch( frequency ) .gain( volume ) .play(); - // crude bump simulation, drop down on even axles, move back up on the odd ones - //MoverParameters->AccVert += (MoverParameters->Vel*0.1f) * - MoverParameters->AccVert += - clamp(-1.0, 1.0, (MoverParameters->Vel / ( 1 + MoverParameters->Vmax )) * MyTrack->iDamageFlag * ((axleindex % 2) != 0 ? 1 : -1)); - + // crude bump simulation, drop down on even axles, move back up on + // the odd ones + // MoverParameters->AccVert += (MoverParameters->Vel*0.1f) * + if(MyTrack->eType == tt_Normal) + { + MoverParameters->AccVert += + clamp(0.0, 4.0, + (clamp(0.0, MoverParameters->Vmax, + MoverParameters->Vmax - + (MoverParameters->Vel + + MoverParameters->Vmax * 0.32f))) * + .05f * (MyTrack->iDamageFlag * 0.25f)); + } if (MyTrack->eType == tt_Switch){ MoverParameters->AccS += clamp(0.0, 1.0, From 26f0b478f2cc6d54f8f0bdd4340fbc70969b3f2b Mon Sep 17 00:00:00 2001 From: Jano211 <107213310+Jano211@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:58:34 +0100 Subject: [PATCH 38/88] Fix for writing useless log informations --- application.cpp | 1 - launcher/scenery_list.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/application.cpp b/application.cpp index d2a486e0..ccd49af3 100644 --- a/application.cpp +++ b/application.cpp @@ -519,7 +519,6 @@ eu07_application::push_mode( eu07_application::mode const Mode ) { if (!m_modes[Mode]) { if (Mode == mode::launcher) - Global.loading_log = false; m_modes[Mode] = std::make_shared(); if (Mode == mode::scenarioloader) m_modes[Mode] = std::make_shared(); diff --git a/launcher/scenery_list.cpp b/launcher/scenery_list.cpp index 848afc30..3db32ac0 100644 --- a/launcher/scenery_list.cpp +++ b/launcher/scenery_list.cpp @@ -100,7 +100,6 @@ void ui::scenerylist_panel::draw_launch_box() ImGui::TextWrapped(selected_trainset->description.c_str()); if (ImGui::Button(STR_C("Launch"), ImVec2(-1, 0))) { - Global.loading_log = !Global.loading_log; if (!launch_simulation()) ImGui::OpenPopup("missing_driver"); } From 26c640eeafad9ae4d65763123ef25a7248d7abc4 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 6 Jan 2025 19:45:21 +0100 Subject: [PATCH 39/88] Add cab activated light i-cabactived in mmd --- Train.cpp | 11 ++++++++++- Train.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Train.cpp b/Train.cpp index bd7d87cc..5a974580 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7367,6 +7367,14 @@ bool TTrain::Update( double const Deltatime ) btLampkaPoslizg.Turn( false ); } + // Lampka aktywowanej kabiny + if (mvControlled->CabActive != 0) { + btCabActived.Turn(true); + } + else { + btCabActived.Turn(false); + } + if( true == lowvoltagepower ) { // McZapkie-141102: SHP i czuwak, TODO: sygnalizacja kabinowa if( mvOccupied->SecuritySystem.is_vigilance_blinking() ) { @@ -10069,7 +10077,8 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-universal6:", btUniversals[ 6 ] }, { "i-universal7:", btUniversals[ 7 ] }, { "i-universal8:", btUniversals[ 8 ] }, - { "i-universal9:", btUniversals[ 9 ] } + { "i-universal9:", btUniversals[ 9 ] }, + { "i-cabactived", btCabActived } }; { auto lookup = lights.find( Label ); diff --git a/Train.h b/Train.h index 11235468..f3d3f36f 100644 --- a/Train.h +++ b/Train.h @@ -771,6 +771,7 @@ public: // reszta może by?publiczna TButton btLampkaRearRightLight; TButton btLampkaRearLeftEndLight; TButton btLampkaRearRightEndLight; + TButton btCabActived; // other TButton btLampkaMalfunction; TButton btLampkaMalfunctionB; From ba34a4cd87c071989e606c36f28b4071c32e36a8 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 6 Jan 2025 19:46:11 +0100 Subject: [PATCH 40/88] Add missing ":" at end of i-cabactivated --- Train.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Train.cpp b/Train.cpp index 5a974580..e9656ede 100644 --- a/Train.cpp +++ b/Train.cpp @@ -10078,7 +10078,8 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-universal7:", btUniversals[ 7 ] }, { "i-universal8:", btUniversals[ 8 ] }, { "i-universal9:", btUniversals[ 9 ] }, - { "i-cabactived", btCabActived } + { "i-cabactived:", btCabActived }, + }; { auto lookup = lights.find( Label ); From eeb3387b333d8751bd209146e2b6ec133fcfaf69 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 6 Jan 2025 19:49:29 +0100 Subject: [PATCH 41/88] Add working light to indicate any active compressor i-compressorany: in mmd --- Train.cpp | 8 +++++++- Train.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Train.cpp b/Train.cpp index e9656ede..c7b2a93f 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7367,6 +7367,12 @@ bool TTrain::Update( double const Deltatime ) btLampkaPoslizg.Turn( false ); } + // Lampka pracujacej sprezacki + if (mvControlled->CompressorFlag || mvOccupied->CompressorFlag) + btCompressors.Turn(true); + else + btCompressors.Turn(false); + // Lampka aktywowanej kabiny if (mvControlled->CabActive != 0) { btCabActived.Turn(true); @@ -10079,7 +10085,7 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-universal8:", btUniversals[ 8 ] }, { "i-universal9:", btUniversals[ 9 ] }, { "i-cabactived:", btCabActived }, - + {"i-compressor:", btCompressors } }; { auto lookup = lights.find( Label ); diff --git a/Train.h b/Train.h index f3d3f36f..4e045600 100644 --- a/Train.h +++ b/Train.h @@ -772,6 +772,7 @@ public: // reszta może by?publiczna TButton btLampkaRearLeftEndLight; TButton btLampkaRearRightEndLight; TButton btCabActived; + TButton btCompressors; // lampka pracy jakiejkolwiek sprezarki // other TButton btLampkaMalfunction; TButton btLampkaMalfunctionB; From d72c709fff4310197ddbecbc1d4e7e4602392c06 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 6 Jan 2025 20:05:11 +0100 Subject: [PATCH 42/88] Add random 0 speed from range 0-4 on tachometerb --- Train.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Train.cpp b/Train.cpp index c7b2a93f..64cccf3e 100644 --- a/Train.cpp +++ b/Train.cpp @@ -6949,10 +6949,11 @@ bool TTrain::Update( double const Deltatime ) // pomiar, pol sekundy ustawienie if (ff != fTachoTimer) // jesli w tej sekundzie nie zmienial { - if (fTachoVelocity > 1) // jedzie - fTachoVelocityJump = fTachoVelocity + (2.0 - LocalRandom(3) + LocalRandom(3)) * 0.5; - else - fTachoVelocityJump = 0; // stoi + if (fTachoVelocity >= 5) // jedzie + fTachoVelocityJump = fTachoVelocity + (2.0 - LocalRandom(3) + LocalRandom(3)) * 0.5; + else if (2 < fTachoVelocity < 5) + fTachoVelocityJump = Random(0, 4); // tu ma sie bujac jak wariat i zatrzymac na jakiejs predkosci + // fTachoVelocityJump = 0; // stoi fTachoTimer = ff; // juz zmienil } } @@ -10085,7 +10086,7 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-universal8:", btUniversals[ 8 ] }, { "i-universal9:", btUniversals[ 9 ] }, { "i-cabactived:", btCabActived }, - {"i-compressor:", btCompressors } + {"i-compressorany:", btCompressors } }; { auto lookup = lights.find( Label ); From 10e7f21520c9b4ae82e0cc09c234ddd128f7e0e2 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 6 Jan 2025 20:05:38 +0100 Subject: [PATCH 43/88] Fix logic for low tachometerb speeds --- Train.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Train.cpp b/Train.cpp index 64cccf3e..936c98e9 100644 --- a/Train.cpp +++ b/Train.cpp @@ -6951,7 +6951,7 @@ bool TTrain::Update( double const Deltatime ) { if (fTachoVelocity >= 5) // jedzie fTachoVelocityJump = fTachoVelocity + (2.0 - LocalRandom(3) + LocalRandom(3)) * 0.5; - else if (2 < fTachoVelocity < 5) + else if (1 <= fTachoVelocity < 5) fTachoVelocityJump = Random(0, 4); // tu ma sie bujac jak wariat i zatrzymac na jakiejs predkosci // fTachoVelocityJump = 0; // stoi fTachoTimer = ff; // juz zmienil From 105e6283821ab8a30a981974ae5c56fda1781d8a Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 6 Jan 2025 20:14:30 +0100 Subject: [PATCH 44/88] Another random stop variable randomization logic fix --- Train.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Train.cpp b/Train.cpp index 936c98e9..446a3fbe 100644 --- a/Train.cpp +++ b/Train.cpp @@ -6951,7 +6951,7 @@ bool TTrain::Update( double const Deltatime ) { if (fTachoVelocity >= 5) // jedzie fTachoVelocityJump = fTachoVelocity + (2.0 - LocalRandom(3) + LocalRandom(3)) * 0.5; - else if (1 <= fTachoVelocity < 5) + else if (fTachoVelocity < 5 && fTachoVelocity > 1) fTachoVelocityJump = Random(0, 4); // tu ma sie bujac jak wariat i zatrzymac na jakiejs predkosci // fTachoVelocityJump = 0; // stoi fTachoTimer = ff; // juz zmienil From d66d4751c22c7de454fd8180ec1f583c0c80cc88 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 6 Jan 2025 20:27:25 +0100 Subject: [PATCH 45/88] Add logic for defining max analog tacho display speed MaxTachoSpeed in Cntrl. section in fiz --- McZapkie/MOVER.h | 1 + McZapkie/Mover.cpp | 4 ++++ Train.cpp | 8 +++++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 29e3f0b4..8d469041 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1191,6 +1191,7 @@ public: int Lights[2][17]; // pozycje świateł, przód - tył, 1 .. 16 int ScndInMain{ 0 }; /*zaleznosc bocznika od nastawnika*/ bool MBrake = false; /*Czy jest hamulec reczny*/ + double maxTachoSpeed = 0.0; // maksymalna predkosc na tarczce predkosciomierza analogowego double StopBrakeDecc = 0.0; bool ReleaseParkingBySpringBrake { false }; bool ReleaseParkingBySpringBrakeWhenDoorIsOpen{ false }; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 886209a9..7d030eed 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -10540,6 +10540,10 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { } // mbrake extract_value( MBrake, "ManualBrake", line, "" ); + + // maksymalna predkosc dostepna na tarczce predkosciomierza + extract_value(maxTachoSpeed, "MaxTachoSpeed", line, ""); + // dynamicbrake { std::map dynamicbrakes{ diff --git a/Train.cpp b/Train.cpp index 446a3fbe..f460a309 100644 --- a/Train.cpp +++ b/Train.cpp @@ -6943,7 +6943,13 @@ bool TTrain::Update( double const Deltatime ) // McZapkie: predkosc wyswietlana na tachometrze brana jest z obrotow kol auto const maxtacho { 3.0 }; - fTachoVelocity = static_cast( std::min( std::abs(11.31 * mvControlled->WheelDiameter * mvControlled->nrot), mvControlled->Vmax * 1.05) ); + + double maxSpeed = mvControlled->Vmax * 1.05; // zachowanie starej logiki jak nie ma definicji max tarczki + if (mvOccupied->maxTachoSpeed != 0) + { + maxSpeed = mvOccupied->maxTachoSpeed; + } + fTachoVelocity = static_cast(std::min(std::abs(11.31 * mvControlled->WheelDiameter * mvControlled->nrot), maxSpeed)); { // skacze osobna zmienna float ff = simulation::Time.data().wSecond; // skacze co sekunde - pol sekundy // pomiar, pol sekundy ustawienie From b8d7d4b09651c348b8ed4f50a93dff4e6ddb9915 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 6 Jan 2025 20:46:27 +0100 Subject: [PATCH 46/88] Add variable door lock speed DoorLockSpeed in Doors: section in fiz --- McZapkie/MOVER.h | 1 + McZapkie/Mover.cpp | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 8d469041..7d048d5f 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -959,6 +959,7 @@ private: std::array instances; // door on the right and left side of the vehicle // ld outputs bool is_locked { false }; + double doorLockSpeed = 10.0; // predkosc przy ktorej wyzwalana jest blokada drzwi }; struct water_heater { diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 7d030eed..87545bc2 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -8617,8 +8617,7 @@ TMoverParameters::update_doors( double const Deltatime ) { Doors.is_locked = ( true == Doors.has_lock ) - && ( true == Doors.lock_enabled ) - && ( Vel >= 10.0 ); + && ( true == Doors.lock_enabled ) && (Vel >= doorLockSpeed); for( auto &door : Doors.instances ) { // revoke permit if... @@ -10292,7 +10291,7 @@ void TMoverParameters::LoadFIZ_Doors( std::string const &line ) { extract_value( Doors.has_warning, "DoorClosureWarning", line, "" ); extract_value( Doors.has_autowarning, "DoorClosureWarningAuto", line, "" ); extract_value( Doors.has_lock, "DoorBlocked", line, "" ); - + extract_value(Doors.doorLockSpeed, "DoorLockSpeed", line, ""); { auto const remotedoorcontrol { ( Doors.open_control == control_t::driver ) From b58a137f66073bd34bf0aa6350e2390b0f682361 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 6 Jan 2025 20:50:49 +0100 Subject: [PATCH 47/88] Fix compilation error (badly assigned variable) --- McZapkie/Mover.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 87545bc2..53c535cb 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -8617,7 +8617,7 @@ TMoverParameters::update_doors( double const Deltatime ) { Doors.is_locked = ( true == Doors.has_lock ) - && ( true == Doors.lock_enabled ) && (Vel >= doorLockSpeed); + && ( true == Doors.lock_enabled ) && (Vel >= Doors.doorLockSpeed); for( auto &door : Doors.instances ) { // revoke permit if... From 69e29bf83c199250ccef4cb1039f8a5135a7d13c Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 7 Jan 2025 03:42:40 +0100 Subject: [PATCH 48/88] General lights overhaul Add modern light switcher Available fiz variables with defualt parameters Headlights: LampRed=255 LampGreen=255 LampBlue=255 DimmedMultiplier=0.6 NormalMultiplier=1.0 HighbeamDimmedMultiplier=2.5 HighBeamMultiplier=2.8 Switches: ModernDimmer=No ModernDimmerOffPosition=Yes --- DynObj.cpp | 77 ++++++++++++++++++++++++++++++++++++++++- DynObj.h | 5 +++ McZapkie/MOVER.h | 23 +++++++++++++ McZapkie/Mover.cpp | 26 ++++++++++++++ Train.cpp | 81 ++++++++++++++++++++++++++++++++++++++------ Train.h | 3 ++ command.cpp | 2 ++ command.h | 3 ++ drivermouseinput.cpp | 5 ++- lightarray.cpp | 39 +++++++++++++++++---- translation.cpp | 3 +- 11 files changed, 246 insertions(+), 21 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 46d099b5..6f36df0a 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -1168,6 +1168,28 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) } btnOn = true; } + + // logika dlugich + if (TestFlag(MoverParameters->iLights[end::front], light::highbeamlight_left)) + m_highbeam13.TurnxOnWithOnAsFallback(); + else + m_highbeam13.TurnOff(); + + if (TestFlag(MoverParameters->iLights[end::front], light::highbeamlight_right)) + m_highbeam12.TurnxOnWithOnAsFallback(); + else + m_highbeam12.TurnOff(); + + // i to samo od dupy strony + if (TestFlag(MoverParameters->iLights[end::rear], light::highbeamlight_left)) + m_highbeam23.TurnxOnWithOnAsFallback(); + else + m_highbeam23.TurnOff(); + + if (TestFlag(MoverParameters->iLights[end::rear], light::highbeamlight_right)) + m_highbeam22.TurnxOnWithOnAsFallback(); + else + m_highbeam22.TurnOff(); } // interior light levels auto sectionlightcolor { glm::vec4( 1.f ) }; @@ -2197,9 +2219,13 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" m_headlamp11.Init( "headlamp11", mdModel ); // górne m_headlamp12.Init( "headlamp12", mdModel ); // prawe m_headlamp13.Init( "headlamp13", mdModel ); // lewe + m_highbeam12.Init("highbeam12", mdModel); // prawe dlugie + m_highbeam13.Init("highbeam13", mdModel); // lewe dlugie m_headlamp21.Init( "headlamp21", mdModel ); m_headlamp22.Init( "headlamp22", mdModel ); m_headlamp23.Init( "headlamp23", mdModel ); + m_highbeam22.Init("highbeam22", mdModel); + m_highbeam23.Init("highbeam23", mdModel); m_headsignal12.Init( "headsignal12", mdModel ); m_headsignal13.Init( "headsignal13", mdModel ); m_headsignal22.Init( "headsignal22", mdModel ); @@ -2216,6 +2242,10 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" iInventory[ end::front ] |= m_headlamp11.Active() ? light::headlight_upper : 0; iInventory[ end::front ] |= m_headlamp12.Active() ? light::headlight_right : 0; iInventory[ end::front ] |= m_headlamp13.Active() ? light::headlight_left : 0; + + iInventory[end::front] |= m_highbeam12.Active() ? light::highbeamlight_right : 0; + iInventory[end::front] |= m_highbeam13.Active() ? light::highbeamlight_left : 0; + iInventory[ end::rear ] |= m_headlamp21.Active() ? light::headlight_upper : 0; iInventory[ end::rear ] |= m_headlamp22.Active() ? light::headlight_right : 0; iInventory[ end::rear ] |= m_headlamp23.Active() ? light::headlight_left : 0; @@ -2223,6 +2253,11 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" iInventory[ end::front ] |= m_headsignal13.Active() ? light::auxiliary_left : 0; iInventory[ end::rear ] |= m_headsignal22.Active() ? light::auxiliary_right : 0; iInventory[ end::rear ] |= m_headsignal23.Active() ? light::auxiliary_left : 0; + + iInventory[end::rear] |= m_highbeam22.Active() ? light::highbeamlight_right : 0; + iInventory[end::rear] |= m_highbeam23.Active() ? light::highbeamlight_left : 0; + + btMechanik1.Init( "mechanik1", mdLowPolyInt, false); btMechanik2.Init( "mechanik2", mdLowPolyInt, false); if( MoverParameters->dizel_heat.water.config.shutters ) { @@ -4162,6 +4197,10 @@ void TDynamicObject::TurnOff() m_headlamp21.TurnOff(); m_headlamp22.TurnOff(); m_headlamp23.TurnOff(); + m_highbeam12.TurnOff(); + m_highbeam13.TurnOff(); + m_highbeam22.TurnOff(); + m_highbeam23.TurnOff(); m_headsignal12.TurnOff(); m_headsignal13.TurnOff(); m_headsignal22.TurnOff(); @@ -7001,7 +7040,6 @@ void TDynamicObject::Damage(char flag) }; void TDynamicObject::SetLights() { - auto const isfrontcaboccupied { MoverParameters->CabOccupied * DirectionGet() >= 0 }; int const automaticmarkers { MoverParameters->CabActive == 0 && ( MoverParameters->InactiveCabFlag & activation::redmarkers ) ? light::redmarker_left + light::redmarker_right : 0 }; @@ -7055,6 +7093,43 @@ void TDynamicObject::RaLightsSet(int head, int rear) if( head >= 0 ) { auto const vehicleend { iDirection > 0 ? end::front : end::rear }; MoverParameters->iLights[ vehicleend ] = ( head & iInventory[ vehicleend ] ); + bool tLeft = MoverParameters->iLights[vehicleend] & (light::auxiliary_left | light::headlight_left); // roboczo czy jakiekolwiek swiatlo z lewej jest zapalone + bool tRight = MoverParameters->iLights[vehicleend] & (light::auxiliary_right | light::headlight_right); // a tu z prawej + switch (MoverParameters->modernDimmerState) + { + case 0: + // wylaczone + MoverParameters->iLights[vehicleend] &= 0 | light::rearendsignals; // zostawiamy tylko tabliczki jesli sa + HighBeamLights = false; + DimHeadlights = false; + break; + case 1: + // przyciemnione normalne + DimHeadlights = true; // odpalamy przyciemnienie normalnych reflektorow + HighBeamLights = false; + break; + case 3: + // dlugie przyciemnione + DimHeadlights = true; + HighBeamLights = true; + MoverParameters->iLights[vehicleend] &= light::headlight_upper | light::rearendsignals | light::redmarker_left | light::redmarker_right | light::rearendsignals; // nie ruszamy gornych i koncowek + MoverParameters->iLights[vehicleend] |= tLeft ? light::highbeamlight_left : 0; // jesli swiatlo z lewej zapalone to odpal dlugie + MoverParameters->iLights[vehicleend] |= tRight ? light::highbeamlight_right : 0; // a tu z prawej + break; + case 4: + // zwykle dlugie + DimHeadlights = false; + HighBeamLights = true; + MoverParameters->iLights[vehicleend] &= light::headlight_upper | light::rearendsignals | light::redmarker_left | light::redmarker_right | light::rearendsignals; // nie ruszamy gornych i koncowek + MoverParameters->iLights[vehicleend] |= tLeft ? light::highbeamlight_left : 0; // jesli swiatlo z lewej zapalone to odpal dlugie + MoverParameters->iLights[vehicleend] |= tRight ? light::highbeamlight_right : 0; // a tu z prawej + break; + default: // to case 2 - zwykle + DimHeadlights = false; + HighBeamLights = false; + break; + } + } if( rear >= 0 ) { auto const vehicleend{ iDirection > 0 ? end::rear : end::front }; diff --git a/DynObj.h b/DynObj.h index 5462d5ff..d97826c3 100644 --- a/DynObj.h +++ b/DynObj.h @@ -469,9 +469,13 @@ private: AirCoupler m_headlamp11; // oswietlenie czolowe - przod AirCoupler m_headlamp12; AirCoupler m_headlamp13; + AirCoupler m_highbeam12; // dlugie + AirCoupler m_highbeam13; AirCoupler m_headlamp21; // oswietlenie czolowe - tyl AirCoupler m_headlamp22; AirCoupler m_headlamp23; + AirCoupler m_highbeam22; + AirCoupler m_highbeam23; AirCoupler m_headsignal12; AirCoupler m_headsignal13; AirCoupler m_headsignal22; @@ -571,6 +575,7 @@ private: public: bool DimHeadlights{ false }; // status of the headlight dimming toggle. NOTE: single toggle for all lights is a simplification. TODO: separate per-light switches + bool HighBeamLights { false }; // status of the highbeam toggle // checks whether there's unbroken connection of specified type to specified vehicle bool is_connected( TDynamicObject const *Vehicle, coupling const Coupling = coupling::coupler ) const; TDynamicObject * PrevAny() const; diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 29e3f0b4..9d335364 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -205,6 +205,8 @@ enum light { rearendsignals = ( 1 << 6 ), auxiliary_left = ( 1 << 7 ), auxiliary_right = ( 1 << 8 ), + highbeamlight_left = ( 1 << 9 ), + highbeamlight_right = ( 1 << 10 ) }; // door operation methods; exclusive @@ -1703,6 +1705,26 @@ public: int iProblem = 0; // flagi problemów z taborem, aby AI nie musiało porównywać; 0=może jechać int iLights[2]; // bity zapalonych świateł tutaj, żeby dało się liczyć pobór prądu + // Status nowszego hebelka od przyciemniania swiatel/swiatel dlugich + // 0 - swiatla wylaczone (opcja dziala tylko gdy w fiz zdefiniowano OffState w sekcji Switches; w przeciwnym wypadku pstryk startuje z wartoscia == 1 + // 1 - swiatla normalne przyciemnione + // 2 - swiatla normalne + // 3 - swiatla dlugie przyciemnione + // 4 - swiatla dlugie normalne + int modernDimmerState{0}; + bool modernContainOffPos = true; + bool enableModernDimmer = false; + + // Barwa reflektora + int refR{255}; // Czerwony + int refG{255}; // Zielony + int refB{255}; // Niebieski + + double dimMultiplier{0.6f}; // mnoznik swiatel przyciemnionych + double normMultiplier{1.0f}; // mnoznik swiatel zwyklych + double highDimMultiplier{2.5f}; // mnoznik dlugich przyciemnionych + double highMultiplier{2.8f}; // mnoznik dlugich + plc::basic_controller m_plc; int AIHintPantstate{ 0 }; // suggested pantograph setup @@ -1967,6 +1989,7 @@ private: void LoadFIZ_DCEMUED(std::string const &line); void LoadFIZ_SpringBrake(std::string const &line); void LoadFIZ_Light( std::string const &line ); + void LoadFIZ_Headlights(std::string const &Line); void LoadFIZ_Clima( std::string const &line ); void LoadFIZ_Power( std::string const &Line ); void LoadFIZ_SpeedControl( std::string const &Line ); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 886209a9..fe94a58b 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -9667,6 +9667,14 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) continue; } + if (issection("Headlights:", inputline)) + { + startBPT = false; + fizlines.emplace("Headlights", inputline); + LoadFIZ_Headlights(inputline); + continue; + } + if (issection("Blending:", inputline)) { startBPT = false; LISTLINE = 0; @@ -10027,6 +10035,18 @@ void TMoverParameters::LoadFIZ_Load( std::string const &line ) { extract_value( UnLoadSpeed, "UnLoadSpeed", line, "" ); } +void TMoverParameters::LoadFIZ_Headlights(std::string const &line) +{ + extract_value(refR, "LampRed", line, ""); + extract_value(refG, "LampGreen", line, ""); + extract_value(refB, "LampBlue", line, ""); + + extract_value(dimMultiplier, "DimmedMultiplier", line, ""); + extract_value(normMultiplier, "NormalMultiplier", line, ""); + extract_value(highDimMultiplier, "HighbeamDimmedMultiplier", line, ""); + extract_value(highMultiplier, "HighBeamMultiplier", line, ""); +} + void TMoverParameters::LoadFIZ_Dimensions( std::string const &line ) { extract_value( Dim.L, "L", line, "" ); @@ -11117,6 +11137,12 @@ void TMoverParameters::LoadFIZ_Switches( std::string const &Input ) { extract_value( UniversalResetButtonFlag[ 0 ], "RelayResetButton1", Input, "" ); extract_value( UniversalResetButtonFlag[ 1 ], "RelayResetButton2", Input, "" ); extract_value( UniversalResetButtonFlag[ 2 ], "RelayResetButton3", Input, "" ); + extract_value(enableModernDimmer, "ModernDimmer", Input, ""); + extract_value(modernContainOffPos, "ModernDimmerOffPosition", Input, ""); + if (!modernContainOffPos) + modernDimmerState = 1; + if (!enableModernDimmer) + modernDimmerState = 2; // pantograph presets { auto &presets { PantsPreset.first }; diff --git a/Train.cpp b/Train.cpp index bd7d87cc..4ebcac59 100644 --- a/Train.cpp +++ b/Train.cpp @@ -377,6 +377,8 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::headlighttogglerearupper, &TTrain::OnCommand_headlighttogglerearupper }, { user_command::headlightenablerearupper, &TTrain::OnCommand_headlightenablerearupper }, { user_command::headlightdisablerearupper, &TTrain::OnCommand_headlightdisablerearupper }, + {user_command::modernlightdimmerdecrease, &TTrain::OnCommand_modernlightdimmerdecrease}, + {user_command::modernlightdimmerincrease, &TTrain::OnCommand_modernlightdimmerincrease}, { user_command::redmarkertogglerearleft, &TTrain::OnCommand_redmarkertogglerearleft }, { user_command::redmarkerenablerearleft, &TTrain::OnCommand_redmarkerenablerearleft }, { user_command::redmarkerdisablerearleft, &TTrain::OnCommand_redmarkerdisablerearleft }, @@ -4699,6 +4701,44 @@ void TTrain::OnCommand_headlightdisablerearupper( TTrain *Train, command_data co } } +void TTrain::OnCommand_modernlightdimmerincrease(TTrain* Train, command_data const& Command) +{ + if (!Train->mvOccupied->enableModernDimmer) + return; // if modern dimmer is disabled, skip entire command + if (Command.action == GLFW_PRESS) + { + // update modern dimmer state + if (Train->mvOccupied->modernDimmerState < 4) + Train->mvOccupied->modernDimmerState++; + Train->Dynamic()->SetLights(); + // visual feedback + if (Train->ggModernLightDimSw.SubModel != nullptr) + if (Train->mvOccupied->modernContainOffPos) + Train->ggModernLightDimSw.UpdateValue(Train->mvOccupied->modernDimmerState, Train->dsbSwitch); + else + Train->ggModernLightDimSw.UpdateValue(Train->mvOccupied->modernDimmerState - 1, Train->dsbSwitch); + } +} +void TTrain::OnCommand_modernlightdimmerdecrease(TTrain *Train, command_data const &Command) +{ + if (!Train->mvOccupied->enableModernDimmer) + return; // if modern dimmer is disabled, skip entire command + if (Command.action == GLFW_PRESS) + { + byte minPos = (Train->mvOccupied->modernContainOffPos) ? 0 : 1; // prevent switching to 0 if its not enabled + // update modern dimmer state + if (Train->mvOccupied->modernDimmerState > minPos) + Train->mvOccupied->modernDimmerState--; + Train->Dynamic()->SetLights(); + // visual feedback + if (Train->ggModernLightDimSw.SubModel != nullptr) + if (Train->mvOccupied->modernContainOffPos) + Train->ggModernLightDimSw.UpdateValue(Train->mvOccupied->modernDimmerState, Train->dsbSwitch); + else + Train->ggModernLightDimSw.UpdateValue(Train->mvOccupied->modernDimmerState - 1, Train->dsbSwitch); + } +} + void TTrain::OnCommand_redmarkertogglerearleft( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { // NOTE: we toggle the light on opposite side, as 'rear right' is 'front left' on the rear end etc @@ -4896,17 +4936,22 @@ void TTrain::OnCommand_headlightsdimenable( TTrain *Train, command_data const &C if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( Train->ggDimHeadlightsButton.SubModel == nullptr ) { + if( Train->ggDimHeadlightsButton.SubModel != nullptr ) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels - WriteLog( "Dim Headlights switch is missing, or wasn't defined" ); - return; + // visual feedback + Train->ggDimHeadlightsButton.UpdateValue(1.0, Train->dsbSwitch); } - // visual feedback - Train->ggDimHeadlightsButton.UpdateValue( 1.0, Train->dsbSwitch ); - if( true == Train->DynamicObject->DimHeadlights ) { return; } // already enabled + /* // to jest stara logika + if (true == Train->DynamicObject->DimHeadlights) + { + return; + } // already enabled Train->DynamicObject->DimHeadlights = true; + */ + + Train->mvOccupied->modernDimmerState = 1; // ustawiamy modern dimmer na flage przyciemnienia } } @@ -4914,17 +4959,20 @@ void TTrain::OnCommand_headlightsdimdisable( TTrain *Train, command_data const & if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( Train->ggDimHeadlightsButton.SubModel == nullptr ) { + if( Train->ggDimHeadlightsButton.SubModel != nullptr ) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels - WriteLog( "Dim Headlights switch is missing, or wasn't defined" ); - return; + // visual feedback + Train->ggDimHeadlightsButton.UpdateValue(0.0, Train->dsbSwitch); } - // visual feedback - Train->ggDimHeadlightsButton.UpdateValue( 0.0, Train->dsbSwitch ); + + /* // stara logika przyciemniania if( false == Train->DynamicObject->DimHeadlights ) { return; } // already enabled Train->DynamicObject->DimHeadlights = false; + + */ + Train->mvOccupied->modernDimmerState = 2; // ustawiamy modern dimmer na flage rozjasnienia } } @@ -8004,6 +8052,7 @@ bool TTrain::Update( double const Deltatime ) ggRightLightButton.Update(); ggLeftEndLightButton.Update(); ggRightEndLightButton.Update(); + ggModernLightDimSw.Update(); // hunter-230112 ggRearUpperLightButton.Update(); ggRearLeftLightButton.Update(); @@ -8011,6 +8060,7 @@ bool TTrain::Update( double const Deltatime ) ggRearLeftEndLightButton.Update(); ggRearRightEndLightButton.Update(); ggDimHeadlightsButton.Update(); + ggDimHeadlightsButton.Update(); //------------ ggConverterButton.Update(); ggConverterLocalButton.Update(); @@ -9600,6 +9650,7 @@ void TTrain::clear_cab_controls() ggRightLightButton.Clear(); ggUpperLightButton.Clear(); ggDimHeadlightsButton.Clear(); + ggModernLightDimSw.Clear(); ggLeftEndLightButton.Clear(); ggRightEndLightButton.Clear(); ggLightsButton.Clear(); @@ -9631,6 +9682,13 @@ void TTrain::set_cab_controls( int const Cab ) { m_linebreakerstate > 0 ? 1.f : 0.f ) ); } + + if (ggModernLightDimSw.SubModel != nullptr) { + ggModernLightDimSw.PutValue( + mvOccupied->modernDimmerState + ); + } + // motor connectors ggStLinOffButton.PutValue( ( mvControlled->StLinSwitchOff ? @@ -10190,6 +10248,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { "leftend_sw:", ggLeftEndLightButton }, { "rightend_sw:", ggRightEndLightButton }, { "lights_sw:", ggLightsButton }, + { "moderndimmer_sw:", ggModernLightDimSw }, { "rearupperlight_sw:", ggRearUpperLightButton }, { "rearleftlight_sw:", ggRearLeftLightButton }, { "rearrightlight_sw:", ggRearRightLightButton }, diff --git a/Train.h b/Train.h index 11235468..9549902f 100644 --- a/Train.h +++ b/Train.h @@ -401,6 +401,8 @@ class TTrain { static void OnCommand_headlighttogglerearupper( TTrain *Train, command_data const &Command ); static void OnCommand_headlightenablerearupper( TTrain *Train, command_data const &Command ); static void OnCommand_headlightdisablerearupper( TTrain *Train, command_data const &Command ); + static void OnCommand_modernlightdimmerincrease(TTrain *Train, command_data const &Command); + static void OnCommand_modernlightdimmerdecrease(TTrain *Train, command_data const &Command); static void OnCommand_redmarkertogglerearleft( TTrain *Train, command_data const &Command ); static void OnCommand_redmarkerenablerearleft( TTrain *Train, command_data const &Command ); static void OnCommand_redmarkerdisablerearleft( TTrain *Train, command_data const &Command ); @@ -580,6 +582,7 @@ public: // reszta może by?publiczna TGauge ggRightEndLightButton; TGauge ggLightsButton; // przelacznik reflektorow (wszystkich) TGauge ggDimHeadlightsButton; // headlights dimming switch + TGauge ggModernLightDimSw; // modern lights dimmer // hunter-230112: przelacznik swiatel tylnich TGauge ggRearUpperLightButton; diff --git a/command.cpp b/command.cpp index 8e2ea97b..636874c4 100644 --- a/command.cpp +++ b/command.cpp @@ -153,6 +153,8 @@ commanddescription_sequence Commands_descriptions = { { "radiovolumeset", command_target::vehicle, command_mode::oneoff }, { "cabchangeforward", command_target::vehicle, command_mode::oneoff }, { "cabchangebackward", command_target::vehicle, command_mode::oneoff }, + {"modernlightdimmerdecrease", command_target::vehicle, command_mode::oneoff}, + {"modernlightdimmerincrease", command_target::vehicle, command_mode::oneoff}, { "viewturn", command_target::entity, command_mode::oneoff }, { "movehorizontal", command_target::entity, command_mode::oneoff }, { "movehorizontalfast", command_target::entity, command_mode::oneoff }, diff --git a/command.h b/command.h index 8fb5a87d..4e73314c 100644 --- a/command.h +++ b/command.h @@ -147,6 +147,9 @@ enum class user_command { cabchangeforward, cabchangebackward, + modernlightdimmerdecrease, + modernlightdimmerincrease, + viewturn, movehorizontal, movehorizontalfast, diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index 1ff7f3b1..12a13ffa 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -506,7 +506,7 @@ drivermouse_input::bindings( std::string const &Control ) const { void drivermouse_input::default_bindings() { - + // pierwsza komenda jest od zwiekszania a druga od zmniejszania - ewentualnie kolejno lewy i prawy przycisk m_buttonbindings = { { "jointctrl:", { user_command::jointcontrollerset, @@ -737,6 +737,9 @@ drivermouse_input::default_bindings() { { "dimheadlights_sw:", { user_command::headlightsdimtoggle, user_command::none } }, + {"moderndimmer_sw:", { + user_command::modernlightdimmerincrease, + user_command::modernlightdimmerdecrease } }, { "leftend_sw:", { user_command::redmarkertoggleleft, user_command::none } }, diff --git a/lightarray.cpp b/lightarray.cpp index e262eb34..77dd3ccd 100644 --- a/lightarray.cpp +++ b/lightarray.cpp @@ -65,17 +65,42 @@ light_array::update() { light.count = 0 + ( ( lights & light::headlight_left ) ? 1 : 0 ) + ( ( lights & light::headlight_right ) ? 1 : 0 ) - + ( ( lights & light::headlight_upper ) ? 1 : 0 ); + + ( ( lights & light::headlight_upper ) ? 1 : 0 ) + + ( ( lights & light::highbeamlight_left ) ? 1: 0) + + ( ( lights & light::highbeamlight_right ) ? 1 : 0); if( light.count > 0 ) { - light.intensity = std::max( 0.0f, std::log( (float)light.count + 1.0f ) ); - light.intensity *= ( light.owner->DimHeadlights ? 0.6f : 1.0f ); + light.intensity = std::max(0.0f, std::log((float)light.count + 1.0f)); + if (light.owner->DimHeadlights && !light.owner->HighBeamLights) // tylko przyciemnione + light.intensity *= light.owner->MoverParameters->dimMultiplier; + else if (!light.owner->DimHeadlights && !light.owner->HighBeamLights) // normalne + light.intensity *= light.owner->MoverParameters->normMultiplier; + else if (light.owner->DimHeadlights && light.owner->HighBeamLights) // przyciemnione dlugie + light.intensity *= light.owner->MoverParameters->highDimMultiplier; + else if (!light.owner->DimHeadlights && light.owner->HighBeamLights) // dlugie zwykle + light.intensity *= light.owner->MoverParameters->highMultiplier; + // TBD, TODO: intensity can be affected further by other factors light.state = { - ( ( lights & light::headlight_left ) ? 1.f : 0.f ), - ( ( lights & light::headlight_upper ) ? 1.f : 0.f ), - ( ( lights & light::headlight_right ) ? 1.f : 0.f ) }; - light.state *= ( light.owner->DimHeadlights ? 0.6f : 1.0f ); + ( ( lights & light::headlight_left | light::highbeamlight_left ) ? 1.f : 0.f ), + ( ( lights & light::headlight_upper ) ? 1.f : 0.f ), + ( ( lights & light::headlight_right | light::highbeamlight_right) ? 1.f : 0.f ) }; + + light.color = { + light.owner->MoverParameters->refR / 255, + light.owner->MoverParameters->refG / 255, + light.owner->MoverParameters->refB / 255 + }; + + if (light.owner->DimHeadlights && !light.owner->HighBeamLights) // tylko przyciemnione + light.state *= light.owner->MoverParameters->dimMultiplier; + else if (!light.owner->DimHeadlights && !light.owner->HighBeamLights) + light.state *= light.owner->MoverParameters->normMultiplier; + else if (light.owner->DimHeadlights && light.owner->HighBeamLights) // przyciemnione dlugie + light.state *= light.owner->MoverParameters->highDimMultiplier; + else if (!light.owner->DimHeadlights && light.owner->HighBeamLights) // dlugie zwykle + light.state *= light.owner->MoverParameters->highMultiplier; + } else { light.intensity = 0.0f; diff --git a/translation.cpp b/translation.cpp index ac31091c..74a4e35a 100644 --- a/translation.cpp +++ b/translation.cpp @@ -249,6 +249,7 @@ std::string locale::label_cab_control(std::string const &Label) { "leftlight_sw:", STRN("left headlight") }, { "rightlight_sw:", STRN("right headlight") }, { "dimheadlights_sw:", STRN("headlights dimmer") }, + { "moderndimmer_sw:", STRN("headlights dimmer") }, { "leftend_sw:", STRN("left marker light") }, { "rightend_sw:", STRN("right marker light") }, { "lights_sw:", STRN("light pattern") }, @@ -319,7 +320,7 @@ std::string locale::label_cab_control(std::string const &Label) { "universal6:", STRN("interactive part") }, { "universal7:", STRN("interactive part") }, { "universal8:", STRN("interactive part") }, - { "universal9:", STRN("interactive part") } + { "universal9:", STRN("interactive part") }, }; auto const it = cabcontrols_labels.find( Label ); From e2b5a6d9a1ede7a37539a154ab2a11df1f872356 Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 7 Jan 2025 04:46:09 +0100 Subject: [PATCH 49/88] General logic fixes --- McZapkie/MOVER.h | 1219 +++++++++++++++++++++++--------------------- McZapkie/Mover.cpp | 11 +- Train.cpp | 26 +- 3 files changed, 651 insertions(+), 605 deletions(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 9d335364..b45e49fc 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -814,297 +814,310 @@ struct inverter { class TMoverParameters { // Ra: wrapper na kod pascalowy, przejmujący jego funkcje Q: 20160824 - juz nie wrapper a klasa bazowa :) -private: -// types -/* TODO: implement - // communication cable, exchanging control signals with adjacent vehicle - struct jumper_cable { - // types - using flag_pair = std::pair; - // members - // booleans - // std::array flags {}; - // boolean pairs, exchanged data is swapped when connected to a matching end (front to front or back to back) - // TBD, TODO: convert to regular bool array for efficiency once it's working? - std::array flag_pairs {}; - // integers - // std::array values {}; - }; -*/ - // basic approximation of a generic device - // TBD: inheritance or composition? - struct basic_device { - // config - start_t start_type { start_t::manual }; - // ld inputs - bool is_enabled { false }; // device is allowed/requested to operate - bool is_disabled { false }; // device is requested to stop - // TODO: add remaining inputs; start conditions and potential breakers - // ld outputs - bool is_active { false }; // device is working - }; - - struct basic_light : public basic_device { - // config - float dimming { 1.0f }; // light strength multiplier - // ld outputs - float intensity { 0.0f }; // current light strength - }; - - struct cooling_fan : public basic_device { - // config - float speed { 0.f }; // cooling fan rpm; either fraction of parent rpm, or absolute value if negative - float sustain_time { 0.f }; // time of sustaining work of cooling fans after stop - float min_start_velocity { -1.f }; // minimal velocity of vehicle, when cooling fans activate - // ld outputs - float revolutions { 0.f }; // current fan rpm - float stop_timer { 0.f }; // current time, when shut off condition is active - }; - - // basic approximation of a fuel pump - struct fuel_pump : public basic_device { - // TODO: fuel consumption, optional automatic engine start after activation - }; - - // basic approximation of an oil pump - struct oil_pump : public basic_device { - // config - float pressure_minimum { 0.f }; // lowest acceptable working pressure - float pressure_maximum { 0.65f }; // oil pressure at maximum engine revolutions - // ld inputs - float resource_amount { 1.f }; // amount of affected resource, compared to nominal value - // internal data - float pressure_target { 0.f }; - // ld outputs - float pressure { 0.f }; // current pressure - }; - - // basic approximation of a water pump - struct water_pump : public basic_device { - // ld inputs - // TODO: move to breaker list in the basic device once implemented - bool breaker { false }; // device is allowed to operate - }; - - // basic approximation of a solenoid valve - struct basic_valve : basic_device { - // config - bool solenoid { true }; // requires electric power to operate - bool spring { true }; // spring return or double acting actuator - }; - - // basic approximation of a pantograph - struct basic_pantograph { - // ld inputs - basic_valve valve; // associated pneumatic valve - // ld outputs - bool is_active { false }; // device is working - bool sound_event { false }; // indicates last state which generated sound event - double voltage { 0.0 }; - }; - - // basic approximation of doors - struct basic_door { - // config - // ld inputs - bool open_permit { false }; // door can be opened - bool local_open { false }; // local attempt to open the door - bool local_close { false }; // local attempt to close the door - bool remote_open { false }; // received remote signal to open the door - bool remote_close { false }; // received remote signal to close the door - // internal data - float auto_timer { -1.f }; // delay between activation of open state and closing state for automatic doors - float close_delay { 0.f }; // delay between activation of closing state and actual closing - float open_delay { 0.f }; // delay between activation of opening state and actual opening - float position { 0.f }; // current shift of the door from the closed position - float step_position { 0.f }; // current shift of the movable step from the retracted position - // ld outputs - bool is_closed { true }; // the door is fully closed - bool is_door_closed { true }; // the door is fully closed, step doesn't matter - bool is_closing { false }; // the door is currently closing - bool is_opening { false }; // the door is currently opening - bool is_open { false }; // the door is fully open - bool step_folding { false }; // the doorstep is currently closing - bool step_unfolding { false }; // the doorstep is currently opening - }; - - struct door_data { - // config - control_t open_control { control_t::passenger }; - float open_rate { 1.f }; - float open_delay { 0.f }; - control_t close_control { control_t::passenger }; - float close_rate { 1.f }; - float close_delay { 0.f }; - int type { 2 }; - float range { 0.f }; // extent of primary move/rotation - float range_out { 0.f }; // extent of shift outward, applicable for plug doors - int step_type { 2 }; - float step_rate { 0.5f }; - float step_range { 0.f }; - bool has_lock { false }; - bool has_warning { false }; - bool has_autowarning { false }; - float auto_duration { -1.f }; // automatic door closure delay period - float auto_velocity { -1.f }; // automatic door closure velocity threshold - bool auto_include_remote { false }; // automatic door closure applies also to remote control - bool permit_needed { false }; - std::vector permit_presets; // permit presets selectable with preset switch - float voltage { 0.f }; // power type required for door movement - // ld inputs - bool lock_enabled { true }; - bool step_enabled { true }; - bool remote_only { false }; // door ignores local control signals - // internal data - int permit_preset { -1 }; // curent position of preset selection switch - // vehicle parts - std::array instances; // door on the right and left side of the vehicle - // ld outputs - bool is_locked { false }; - }; - - struct water_heater { - // config - struct heater_config_t { - float temp_min { -1 }; // lowest accepted temperature - float temp_max { -1 }; // highest accepted temperature - } config; - // ld inputs - bool breaker { false }; // device is allowed to operate - bool is_enabled { false }; // device is requested to operate - // ld outputs - bool is_active { false }; // device is working - bool is_damaged { false }; // device is damaged - }; - - struct heat_data { - // input, state of relevant devices - bool cooling { false }; // TODO: user controlled device, implement - // bool okienko { true }; // window in the engine compartment - // system configuration - bool auxiliary_water_circuit { false }; // cooling system has an extra water circuit - double fan_speed { 0.075 }; // cooling fan rpm; either fraction of engine rpm, or absolute value if negative - // heat exchange factors - double kw { 0.35 }; - double kv { 0.6 }; - double kfe { 1.0 }; - double kfs { 80.0 }; - double kfo { 25.0 }; - double kfo2 { 25.0 }; - // system parts - struct fluid_circuit_t { - - struct circuit_config_t { - float temp_min { -1 }; // lowest accepted temperature - float temp_max { -1 }; // highest accepted temperature - float temp_cooling { -1 }; // active cooling activation point - float temp_flow { -1 }; // fluid flow activation point - bool shutters { false }; // the radiator has shutters to assist the cooling - } config; - bool is_cold { false }; // fluid is too cold - bool is_warm { false }; // fluid is too hot - bool is_hot { false }; // fluid temperature crossed cooling threshold - bool is_flowing { false }; // fluid is being pushed through the circuit - } water, - water_aux, - oil; - // output, state of affected devices - bool PA { false }; // malfunction flag - float rpmw { 0.0 }; // current main circuit fan revolutions - float rpmwz { 0.0 }; // desired main circuit fan revolutions - bool zaluzje1 { false }; - float rpmw2 { 0.0 }; // current auxiliary circuit fan revolutions - float rpmwz2 { 0.0 }; // desired auxiliary circuit fan revolutions - bool zaluzje2 { false }; - // output, temperatures - float Te { 15.0 }; // ambient temperature TODO: get it from environment data - // NOTE: by default the engine is initialized in warm, startup-ready state - float Ts { 50.0 }; // engine temperature - float To { 45.0 }; // oil temperature - float Tsr { 50.0 }; // main circuit radiator temperature (?) - float Twy { 50.0 }; // main circuit water temperature - float Tsr2 { 40.0 }; // secondary circuit radiator temperature (?) - float Twy2 { 40.0 }; // secondary circuit water temperature - float temperatura1 { 50.0 }; - float temperatura2 { 40.0 }; - float powerfactor { 1.0 }; // coefficient of heat generation for engines other than su45 - }; - - struct spring_brake { - std::shared_ptr Cylinder; - bool Activate { false }; //Input: switching brake on/off in exploitation - main valve/switch - bool ShuttOff { true }; //Input: shutting brake off during failure - valve in pneumatic container - bool Release { false }; //Input: emergency releasing rod - - bool IsReady{ false }; //Output: readyness to braking - cylinder is armed, spring is tentioned - bool IsActive{ false }; //Output: brake is working - double SBP{ 0.0 }; //Output: pressure in spring brake cylinder - - bool PNBrakeConnection{ false }; //Conf: connection to pneumatic brake cylinders - double MaxSetPressure { 0.0 }; //Conf: Maximal pressure for switched off brake - double ResetPressure{ 0.0 }; //Conf: Pressure for arming brake cylinder - double MinForcePressure{ 0.1 }; //Conf: Minimal pressure for zero force - double MaxBrakeForce{ 0.0 }; //Conf: Maximal tension for brake pads/shoes - double PressureOn{ -2.0 }; //Conf: Pressure changing ActiveFlag to "On" - double PressureOff{ -1.0 }; //Conf: Pressure changing ActiveFlag to "Off" - double ValveOffArea{ 0.0 }; //Conf: Area of filling valve - double ValveOnArea{ 0.0 }; //Conf: Area of dumping valve - double ValvePNBrakeArea{ 0.0 }; //Conf: Area of bypass to brake cylinders - - int MultiTractionCoupler{ 127 }; //Conf: Coupling flag necessary for transmitting the command - + private: + // types + /* TODO: implement + // communication cable, exchanging control signals with adjacent vehicle + struct jumper_cable { + // types + using flag_pair = std::pair; + // members + // booleans + // std::array flags {}; + // boolean pairs, exchanged data is swapped when connected to a matching end (front to front or back to back) + // TBD, TODO: convert to regular bool array for efficiency once it's working? + std::array flag_pairs {}; + // integers + // std::array values {}; + }; + */ + // basic approximation of a generic device + // TBD: inheritance or composition? + struct basic_device + { + // config + start_t start_type{start_t::manual}; + // ld inputs + bool is_enabled{false}; // device is allowed/requested to operate + bool is_disabled{false}; // device is requested to stop + // TODO: add remaining inputs; start conditions and potential breakers + // ld outputs + bool is_active{false}; // device is working }; -public: + struct basic_light : public basic_device + { + // config + float dimming{1.0f}; // light strength multiplier + // ld outputs + float intensity{0.0f}; // current light strength + }; + struct cooling_fan : public basic_device + { + // config + float speed{0.f}; // cooling fan rpm; either fraction of parent rpm, or absolute value if negative + float sustain_time{0.f}; // time of sustaining work of cooling fans after stop + float min_start_velocity{-1.f}; // minimal velocity of vehicle, when cooling fans activate + // ld outputs + float revolutions{0.f}; // current fan rpm + float stop_timer{0.f}; // current time, when shut off condition is active + }; + + // basic approximation of a fuel pump + struct fuel_pump : public basic_device + { + // TODO: fuel consumption, optional automatic engine start after activation + }; + + // basic approximation of an oil pump + struct oil_pump : public basic_device + { + // config + float pressure_minimum{0.f}; // lowest acceptable working pressure + float pressure_maximum{0.65f}; // oil pressure at maximum engine revolutions + // ld inputs + float resource_amount{1.f}; // amount of affected resource, compared to nominal value + // internal data + float pressure_target{0.f}; + // ld outputs + float pressure{0.f}; // current pressure + }; + + // basic approximation of a water pump + struct water_pump : public basic_device + { + // ld inputs + // TODO: move to breaker list in the basic device once implemented + bool breaker{false}; // device is allowed to operate + }; + + // basic approximation of a solenoid valve + struct basic_valve : basic_device + { + // config + bool solenoid{true}; // requires electric power to operate + bool spring{true}; // spring return or double acting actuator + }; + + // basic approximation of a pantograph + struct basic_pantograph + { + // ld inputs + basic_valve valve; // associated pneumatic valve + // ld outputs + bool is_active{false}; // device is working + bool sound_event{false}; // indicates last state which generated sound event + double voltage{0.0}; + }; + + // basic approximation of doors + struct basic_door + { + // config + // ld inputs + bool open_permit{false}; // door can be opened + bool local_open{false}; // local attempt to open the door + bool local_close{false}; // local attempt to close the door + bool remote_open{false}; // received remote signal to open the door + bool remote_close{false}; // received remote signal to close the door + // internal data + float auto_timer{-1.f}; // delay between activation of open state and closing state for automatic doors + float close_delay{0.f}; // delay between activation of closing state and actual closing + float open_delay{0.f}; // delay between activation of opening state and actual opening + float position{0.f}; // current shift of the door from the closed position + float step_position{0.f}; // current shift of the movable step from the retracted position + // ld outputs + bool is_closed{true}; // the door is fully closed + bool is_door_closed{true}; // the door is fully closed, step doesn't matter + bool is_closing{false}; // the door is currently closing + bool is_opening{false}; // the door is currently opening + bool is_open{false}; // the door is fully open + bool step_folding{false}; // the doorstep is currently closing + bool step_unfolding{false}; // the doorstep is currently opening + }; + + struct door_data + { + // config + control_t open_control{control_t::passenger}; + float open_rate{1.f}; + float open_delay{0.f}; + control_t close_control{control_t::passenger}; + float close_rate{1.f}; + float close_delay{0.f}; + int type{2}; + float range{0.f}; // extent of primary move/rotation + float range_out{0.f}; // extent of shift outward, applicable for plug doors + int step_type{2}; + float step_rate{0.5f}; + float step_range{0.f}; + bool has_lock{false}; + bool has_warning{false}; + bool has_autowarning{false}; + float auto_duration{-1.f}; // automatic door closure delay period + float auto_velocity{-1.f}; // automatic door closure velocity threshold + bool auto_include_remote{false}; // automatic door closure applies also to remote control + bool permit_needed{false}; + std::vector permit_presets; // permit presets selectable with preset switch + float voltage{0.f}; // power type required for door movement + // ld inputs + bool lock_enabled{true}; + bool step_enabled{true}; + bool remote_only{false}; // door ignores local control signals + // internal data + int permit_preset{-1}; // curent position of preset selection switch + // vehicle parts + std::array instances; // door on the right and left side of the vehicle + // ld outputs + bool is_locked{false}; + }; + + struct water_heater + { + // config + struct heater_config_t + { + float temp_min{-1}; // lowest accepted temperature + float temp_max{-1}; // highest accepted temperature + } config; + // ld inputs + bool breaker{false}; // device is allowed to operate + bool is_enabled{false}; // device is requested to operate + // ld outputs + bool is_active{false}; // device is working + bool is_damaged{false}; // device is damaged + }; + + struct heat_data + { + // input, state of relevant devices + bool cooling{false}; // TODO: user controlled device, implement + // bool okienko { true }; // window in the engine compartment + // system configuration + bool auxiliary_water_circuit{false}; // cooling system has an extra water circuit + double fan_speed{0.075}; // cooling fan rpm; either fraction of engine rpm, or absolute value if negative + // heat exchange factors + double kw{0.35}; + double kv{0.6}; + double kfe{1.0}; + double kfs{80.0}; + double kfo{25.0}; + double kfo2{25.0}; + // system parts + struct fluid_circuit_t + { + + struct circuit_config_t + { + float temp_min{-1}; // lowest accepted temperature + float temp_max{-1}; // highest accepted temperature + float temp_cooling{-1}; // active cooling activation point + float temp_flow{-1}; // fluid flow activation point + bool shutters{false}; // the radiator has shutters to assist the cooling + } config; + bool is_cold{false}; // fluid is too cold + bool is_warm{false}; // fluid is too hot + bool is_hot{false}; // fluid temperature crossed cooling threshold + bool is_flowing{false}; // fluid is being pushed through the circuit + } water, water_aux, oil; + // output, state of affected devices + bool PA{false}; // malfunction flag + float rpmw{0.0}; // current main circuit fan revolutions + float rpmwz{0.0}; // desired main circuit fan revolutions + bool zaluzje1{false}; + float rpmw2{0.0}; // current auxiliary circuit fan revolutions + float rpmwz2{0.0}; // desired auxiliary circuit fan revolutions + bool zaluzje2{false}; + // output, temperatures + float Te{15.0}; // ambient temperature TODO: get it from environment data + // NOTE: by default the engine is initialized in warm, startup-ready state + float Ts{50.0}; // engine temperature + float To{45.0}; // oil temperature + float Tsr{50.0}; // main circuit radiator temperature (?) + float Twy{50.0}; // main circuit water temperature + float Tsr2{40.0}; // secondary circuit radiator temperature (?) + float Twy2{40.0}; // secondary circuit water temperature + float temperatura1{50.0}; + float temperatura2{40.0}; + float powerfactor{1.0}; // coefficient of heat generation for engines other than su45 + }; + + struct spring_brake + { + std::shared_ptr Cylinder; + bool Activate{false}; // Input: switching brake on/off in exploitation - main valve/switch + bool ShuttOff{true}; // Input: shutting brake off during failure - valve in pneumatic container + bool Release{false}; // Input: emergency releasing rod + + bool IsReady{false}; // Output: readyness to braking - cylinder is armed, spring is tentioned + bool IsActive{false}; // Output: brake is working + double SBP{0.0}; // Output: pressure in spring brake cylinder + + bool PNBrakeConnection{false}; // Conf: connection to pneumatic brake cylinders + double MaxSetPressure{0.0}; // Conf: Maximal pressure for switched off brake + double ResetPressure{0.0}; // Conf: Pressure for arming brake cylinder + double MinForcePressure{0.1}; // Conf: Minimal pressure for zero force + double MaxBrakeForce{0.0}; // Conf: Maximal tension for brake pads/shoes + double PressureOn{-2.0}; // Conf: Pressure changing ActiveFlag to "On" + double PressureOff{-1.0}; // Conf: Pressure changing ActiveFlag to "Off" + double ValveOffArea{0.0}; // Conf: Area of filling valve + double ValveOnArea{0.0}; // Conf: Area of dumping valve + double ValvePNBrakeArea{0.0}; // Conf: Area of bypass to brake cylinders + + int MultiTractionCoupler{127}; // Conf: Coupling flag necessary for transmitting the command + }; + + public: double dMoveLen = 0.0; /*---opis lokomotywy, wagonu itp*/ /*--opis serii--*/ - int CategoryFlag = 1; /*1 - pociag, 2 - samochod, 4 - statek, 8 - samolot*/ - /*--sekcja stalych typowych parametrow*/ - std::string TypeName; /*nazwa serii/typu*/ + int CategoryFlag = 1; /*1 - pociag, 2 - samochod, 4 - statek, 8 - samolot*/ + /*--sekcja stalych typowych parametrow*/ + std::string TypeName; /*nazwa serii/typu*/ int TrainType = 0; /*typ: EZT/elektrowoz - Winger 040304 Ra: powinno być szybciej niż string*/ - TEngineType EngineType = TEngineType::None; /*typ napedu*/ - TPowerParameters EnginePowerSource; /*zrodlo mocy dla silnikow*/ - TPowerParameters SystemPowerSource; /*zrodlo mocy dla systemow sterowania/przetwornic/sprezarek*/ - TPowerParameters HeatingPowerSource; /*zrodlo mocy dla ogrzewania*/ + TEngineType EngineType = TEngineType::None; /*typ napedu*/ + TPowerParameters EnginePowerSource; /*zrodlo mocy dla silnikow*/ + TPowerParameters SystemPowerSource; /*zrodlo mocy dla systemow sterowania/przetwornic/sprezarek*/ + TPowerParameters HeatingPowerSource; /*zrodlo mocy dla ogrzewania*/ TPowerParameters AlterHeatPowerSource; /*alternatywne zrodlo mocy dla ogrzewania*/ - TPowerParameters LightPowerSource; /*zrodlo mocy dla oswietlenia*/ - TPowerParameters AlterLightPowerSource;/*alternatywne mocy dla oswietlenia*/ - double Vmax = -1.0; - double Mass = 0.0; - double Power = 0.0; /*max. predkosc kontrukcyjna, masa wlasna, moc*/ - double Mred = 0.0; /*Ra: zredukowane masy wirujące; potrzebne do obliczeń hamowania*/ + TPowerParameters LightPowerSource; /*zrodlo mocy dla oswietlenia*/ + TPowerParameters AlterLightPowerSource; /*alternatywne mocy dla oswietlenia*/ + double Vmax = -1.0; + double Mass = 0.0; + double Power = 0.0; /*max. predkosc kontrukcyjna, masa wlasna, moc*/ + double Mred = 0.0; /*Ra: zredukowane masy wirujące; potrzebne do obliczeń hamowania*/ double TotalMass = 0.0; /*wyliczane przez ComputeMass*/ double HeatingPower = 0.0; - double EngineHeatingRPM { 0.0 }; // guaranteed engine revolutions with heating enabled - double LightPower = 0.0; /*moc pobierana na ogrzewanie/oswietlenie*/ - double BatteryVoltage = 0.0; /*Winger - baterie w elektrykach*/ + double EngineHeatingRPM{0.0}; // guaranteed engine revolutions with heating enabled + double LightPower = 0.0; /*moc pobierana na ogrzewanie/oswietlenie*/ + double BatteryVoltage = 0.0; /*Winger - baterie w elektrykach*/ bool Battery = false; /*Czy sa zalaczone baterie*/ - start_t BatteryStart = start_t::manual; + start_t BatteryStart = start_t::manual; bool EpFuse = true; /*Czy sa zalavzone baterie*/ double EpForce = 0.0; /*Poziom zadanej sily EP*/ - bool Signalling = false; /*Czy jest zalaczona sygnalizacja hamowania ostatniego wagonu*/ - bool Radio = false; /*Czy jest zalaczony radiotelefon*/ - float NominalBatteryVoltage = 0.f; /*Winger - baterie w elektrykach*/ - TDimension Dim; /*wymiary*/ - double Cx = 0.0; /*wsp. op. aerodyn.*/ - float Floor = 0.96f; //poziom podłogi dla ładunków - float WheelDiameter = 1.f; /*srednica kol napednych*/ - float WheelDiameterL = 0.9f; //Ra: srednica kol tocznych przednich - float WheelDiameterT = 0.9f; //Ra: srednica kol tocznych tylnych - float TrackW = 1.435f; /*nominalna szerokosc toru [m]*/ + bool Signalling = false; /*Czy jest zalaczona sygnalizacja hamowania ostatniego wagonu*/ + bool Radio = false; /*Czy jest zalaczony radiotelefon*/ + float NominalBatteryVoltage = 0.f; /*Winger - baterie w elektrykach*/ + TDimension Dim; /*wymiary*/ + double Cx = 0.0; /*wsp. op. aerodyn.*/ + float Floor = 0.96f; // poziom podłogi dla ładunków + float WheelDiameter = 1.f; /*srednica kol napednych*/ + float WheelDiameterL = 0.9f; // Ra: srednica kol tocznych przednich + float WheelDiameterT = 0.9f; // Ra: srednica kol tocznych tylnych + float TrackW = 1.435f; /*nominalna szerokosc toru [m]*/ double AxleInertialMoment = 0.0; /*moment bezwladnosci zestawu kolowego*/ - std::string AxleArangement; /*uklad osi np. Bo'Bo' albo 1'C*/ - int NPoweredAxles = 0; /*ilosc osi napednych liczona z powyzszego*/ - int NAxles = 0; /*ilosc wszystkich osi j.w.*/ - int BearingType = 1; /*lozyska: 0 - slizgowe, 1 - toczne*/ - double ADist = 0.0; double BDist = 0.0; /*odlegosc osi oraz czopow skretu*/ - /*hamulce:*/ - int NBpA = 0; /*ilosc el. ciernych na os: 0 1 2 lub 4*/ - int SandCapacity = 0; /*zasobnik piasku [kg]*/ - TBrakeSystem BrakeSystem = TBrakeSystem::Individual;/*rodzaj hamulca zespolonego*/ - TBrakeSubSystem BrakeSubsystem = TBrakeSubSystem::ss_None ; + std::string AxleArangement; /*uklad osi np. Bo'Bo' albo 1'C*/ + int NPoweredAxles = 0; /*ilosc osi napednych liczona z powyzszego*/ + int NAxles = 0; /*ilosc wszystkich osi j.w.*/ + int BearingType = 1; /*lozyska: 0 - slizgowe, 1 - toczne*/ + double ADist = 0.0; + double BDist = 0.0; /*odlegosc osi oraz czopow skretu*/ + /*hamulce:*/ + int NBpA = 0; /*ilosc el. ciernych na os: 0 1 2 lub 4*/ + int SandCapacity = 0; /*zasobnik piasku [kg]*/ + TBrakeSystem BrakeSystem = TBrakeSystem::Individual; /*rodzaj hamulca zespolonego*/ + TBrakeSubSystem BrakeSubsystem = TBrakeSubSystem::ss_None; TBrakeValve BrakeValve = TBrakeValve::NoValve; TBrakeHandle BrakeHandle = TBrakeHandle::NoHandle; TBrakeHandle BrakeLocHandle = TBrakeHandle::NoHandle; @@ -1118,37 +1131,37 @@ public: std::shared_ptr Pipe2; spring_brake SpringBrake; - TLocalBrake LocalBrake = TLocalBrake::NoBrake; /*rodzaj hamulca indywidualnego*/ + TLocalBrake LocalBrake = TLocalBrake::NoBrake; /*rodzaj hamulca indywidualnego*/ TBrakePressureTable BrakePressureTable; /*wyszczegolnienie cisnien w rurze*/ - TBrakePressure BrakePressureActual; //wartości ważone dla aktualnej pozycji kranu - int ASBType = 0; /*0: brak hamulca przeciwposlizgowego, 1: reczny, 2: automat*/ - int UniversalBrakeButtonFlag[3] = { 0, 0, 0 }; /* mozliwe działania przycisków hamulcowych */ - int UniversalResetButtonFlag[3] = { 0, 0, 0 }; // customizable reset buttons assignments + TBrakePressure BrakePressureActual; // wartości ważone dla aktualnej pozycji kranu + int ASBType = 0; /*0: brak hamulca przeciwposlizgowego, 1: reczny, 2: automat*/ + int UniversalBrakeButtonFlag[3] = {0, 0, 0}; /* mozliwe działania przycisków hamulcowych */ + int UniversalResetButtonFlag[3] = {0, 0, 0}; // customizable reset buttons assignments int TurboTest = 0; - double MaxBrakeForce = 0.0; /*maksymalna sila nacisku hamulca*/ - double MaxBrakePress[5]; //pomocniczy, proz, sred, lad, pp + double MaxBrakeForce = 0.0; /*maksymalna sila nacisku hamulca*/ + double MaxBrakePress[5]; // pomocniczy, proz, sred, lad, pp double P2FTrans = 0.0; - double TrackBrakeForce = 0.0; /*sila nacisku hamulca szynowego*/ - int BrakeMethod = 0; /*flaga rodzaju hamulca*/ - bool Handle_AutomaticOverload = false; //automatyczna asymilacja na pozycji napelniania - bool Handle_ManualOverload = false; //reczna asymilacja na guzik + double TrackBrakeForce = 0.0; /*sila nacisku hamulca szynowego*/ + int BrakeMethod = 0; /*flaga rodzaju hamulca*/ + bool Handle_AutomaticOverload = false; // automatyczna asymilacja na pozycji napelniania + bool Handle_ManualOverload = false; // reczna asymilacja na guzik double Handle_GenericDoubleParameter1 = 0.0; double Handle_GenericDoubleParameter2 = 0.0; - double Handle_OverloadMaxPressure = 1.0; //maksymalne zwiekszenie cisnienia przy asymilacji - double Handle_OverloadPressureDecrease = 0.002; //predkosc spadku cisnienia przy asymilacji - /*max. cisnienie w cyl. ham., stala proporcjonalnosci p-K*/ + double Handle_OverloadMaxPressure = 1.0; // maksymalne zwiekszenie cisnienia przy asymilacji + double Handle_OverloadPressureDecrease = 0.002; // predkosc spadku cisnienia przy asymilacji + /*max. cisnienie w cyl. ham., stala proporcjonalnosci p-K*/ double HighPipePress = 0.0; - double LowPipePress = 0.0; - double DeltaPipePress = 0.0; + double LowPipePress = 0.0; + double DeltaPipePress = 0.0; /*max. i min. robocze cisnienie w przewodzie glownym oraz roznica miedzy nimi*/ - double CntrlPipePress = 0.0; //ciśnienie z zbiorniku sterującym + double CntrlPipePress = 0.0; // ciśnienie z zbiorniku sterującym double BrakeVolume = 0.0; - double BrakeVVolume = 0.0; - double VeselVolume = 0.0; + double BrakeVVolume = 0.0; + double VeselVolume = 0.0; /*pojemnosc powietrza w ukladzie hamulcowym, w ukladzie glownej sprezarki [m^3] */ int BrakeCylNo = 0; /*ilosc cylindrow ham.*/ double BrakeCylRadius = 0.0; - double BrakeCylDist = 0.0; + double BrakeCylDist = 0.0; double BrakeCylMult[3]; int LoadFlag = 0; /*promien cylindra, skok cylindra, przekladnia hamulcowa*/ @@ -1161,13 +1174,13 @@ public: std::string BrakeValveParams; double Spg = 0.0; double MinCompressor = 0.0; - double MaxCompressor = 0.0; + double MaxCompressor = 0.0; double MinCompressor_cabA = 0.0; double MaxCompressor_cabA = 0.0; double MinCompressor_cabB = 0.0; double MaxCompressor_cabB = 0.0; bool CabDependentCompressor = false; - double CompressorSpeed = 0.0; + double CompressorSpeed = 0.0; int CompressorList[4][9]; // pozycje świateł, przód - tył, 1 .. 16 double EmergencyValveOn = 0.0; double EmergencyValveOff = 0.0; @@ -1182,36 +1195,36 @@ public: bool CompressorListWrap = false; /*cisnienie wlaczania, zalaczania sprezarki, wydajnosc sprezarki*/ TBrakeDelayTable BrakeDelay; /*opoznienie hamowania/odhamowania t/o*/ - double AirLeakRate{ 0.01 }; // base rate of air leak from brake system components ( 0.001 = 1 l/sec ) - int BrakeCtrlPosNo = 0; /*ilosc pozycji hamulca*/ - /*nastawniki:*/ - int MainCtrlPosNo = 0; /*ilosc pozycji nastawnika*/ + double AirLeakRate{0.01}; // base rate of air leak from brake system components ( 0.001 = 1 l/sec ) + int BrakeCtrlPosNo = 0; /*ilosc pozycji hamulca*/ + /*nastawniki:*/ + int MainCtrlPosNo = 0; /*ilosc pozycji nastawnika*/ int ScndCtrlPosNo = 0; int LightsPosNo = 0; - int LightsDefPos = 1; + int LightsDefPos = 1; bool LightsWrap = false; int Lights[2][17]; // pozycje świateł, przód - tył, 1 .. 16 - int ScndInMain{ 0 }; /*zaleznosc bocznika od nastawnika*/ - bool MBrake = false; /*Czy jest hamulec reczny*/ + int ScndInMain{0}; /*zaleznosc bocznika od nastawnika*/ + bool MBrake = false; /*Czy jest hamulec reczny*/ double StopBrakeDecc = 0.0; - bool ReleaseParkingBySpringBrake { false }; - bool ReleaseParkingBySpringBrakeWhenDoorIsOpen{ false }; - bool SpringBrakeCutsOffDrive { true }; - double SpringBrakeDriveEmergencyVel { -1 }; - bool HideDirStatusWhenMoving { false }; // Czy gasic lampki kierunku powyzej predkosci zdefiniowanej przez HideDirStatusSpeed - int HideDirStatusSpeed{ 1 }; // Predkosc od ktorej lampki kierunku sa wylaczane + bool ReleaseParkingBySpringBrake{false}; + bool ReleaseParkingBySpringBrakeWhenDoorIsOpen{false}; + bool SpringBrakeCutsOffDrive{true}; + double SpringBrakeDriveEmergencyVel{-1}; + bool HideDirStatusWhenMoving{false}; // Czy gasic lampki kierunku powyzej predkosci zdefiniowanej przez HideDirStatusSpeed + int HideDirStatusSpeed{1}; // Predkosc od ktorej lampki kierunku sa wylaczane TSecuritySystem SecuritySystem; - int EmergencyBrakeWarningSignal{ 0 }; // combined with basic WarningSignal when manual emergency brake is active - TUniversalCtrlTable UniCtrlList; /*lista pozycji uniwersalnego nastawnika*/ - int UniCtrlListSize = 0; /*wielkosc listy pozycji uniwersalnego nastawnika*/ + int EmergencyBrakeWarningSignal{0}; // combined with basic WarningSignal when manual emergency brake is active + TUniversalCtrlTable UniCtrlList; /*lista pozycji uniwersalnego nastawnika*/ + int UniCtrlListSize = 0; /*wielkosc listy pozycji uniwersalnego nastawnika*/ bool UniCtrlIntegratedBrakePNCtrl = false; /*zintegrowany nastawnik JH obsluguje hamulec PN*/ bool UniCtrlIntegratedBrakeCtrl = false; /*zintegrowany nastawnik JH obsluguje hamowanie*/ - bool UniCtrlIntegratedLocalBrakeCtrl = false; /*zintegrowany nastawnik JH obsluguje hamowanie hamulcem pomocniczym*/ - int UniCtrlNoPowerPos{ 0 }; // cached highesr position not generating traction force - std::pair> PantsPreset { "0132", { 0, 0 } }; // pantograph preset switches; .first holds possible setups as chars, .second holds currently selected preset in each cab + bool UniCtrlIntegratedLocalBrakeCtrl = false; /*zintegrowany nastawnik JH obsluguje hamowanie hamulcem pomocniczym*/ + int UniCtrlNoPowerPos{0}; // cached highesr position not generating traction force + std::pair> PantsPreset{"0132", {0, 0}}; // pantograph preset switches; .first holds possible setups as chars, .second holds currently selected preset in each cab /*-sekcja parametrow dla lokomotywy elektrycznej*/ - TSchemeTable RList; /*lista rezystorow rozruchowych i polaczen silnikow, dla dizla: napelnienia*/ + TSchemeTable RList; /*lista rezystorow rozruchowych i polaczen silnikow, dla dizla: napelnienia*/ int RlistSize = 0; TMotorParameters MotorParam[MotorParametersArraySize + 1]; /*rozne parametry silnika przy bocznikowaniach*/ @@ -1221,22 +1234,24 @@ public: // NToothM, NToothW : byte; // Ratio: real; {NToothW/NToothM} // end; - double NominalVoltage = 0.0; /*nominalne napiecie silnika*/ + double NominalVoltage = 0.0; /*nominalne napiecie silnika*/ double WindingRes = 0.0; - double u = 0.0; //wspolczynnik tarcia yB wywalic!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - double CircuitRes = 0.0; /*rezystancje silnika i obwodu*/ - int IminLo = 0; int IminHi = 0; /*prady przelacznika automatycznego rozruchu, uzywane tez przez ai_driver*/ + double u = 0.0; // wspolczynnik tarcia yB wywalic!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + double CircuitRes = 0.0; /*rezystancje silnika i obwodu*/ + int IminLo = 0; + int IminHi = 0; /*prady przelacznika automatycznego rozruchu, uzywane tez przez ai_driver*/ int ImaxLo = 0; // maksymalny prad niskiego rozruchu - int ImaxHi = 0; // maksymalny prad wysokiego rozruchu - bool MotorOverloadRelayHighThreshold { false }; - double nmax = 0.0; /*maksymalna dop. ilosc obrotow /s*/ - double InitialCtrlDelay = 0.0; double CtrlDelay = 0.0; /* -//- -//- miedzy kolejnymi poz.*/ - double CtrlDownDelay = 0.0; /* -//- -//- przy schodzeniu z poz.*/ /*hunter-101012*/ - int FastSerialCircuit = 0;/*0 - po kolei zamyka styczniki az do osiagniecia szeregowej, 1 - natychmiastowe wejscie na szeregowa*/ /*hunter-111012*/ + int ImaxHi = 0; // maksymalny prad wysokiego rozruchu + bool MotorOverloadRelayHighThreshold{false}; + double nmax = 0.0; /*maksymalna dop. ilosc obrotow /s*/ + double InitialCtrlDelay = 0.0; + double CtrlDelay = 0.0; /* -//- -//- miedzy kolejnymi poz.*/ + double CtrlDownDelay = 0.0; /* -//- -//- przy schodzeniu z poz.*/ /*hunter-101012*/ + int FastSerialCircuit = 0; /*0 - po kolei zamyka styczniki az do osiagniecia szeregowej, 1 - natychmiastowe wejscie na szeregowa*/ /*hunter-111012*/ int BackwardsBranchesAllowed = 1; - int AutoRelayType = 0; /*0 -brak, 1 - jest, 2 - opcja*/ - bool CoupledCtrl = false; /*czy mainctrl i scndctrl sa sprzezone*/ - bool HasCamshaft { false }; + int AutoRelayType = 0; /*0 -brak, 1 - jest, 2 - opcja*/ + bool CoupledCtrl = false; /*czy mainctrl i scndctrl sa sprzezone*/ + bool HasCamshaft{false}; int DynamicBrakeType = 0; /*patrz dbrake_**/ int DynamicBrakeAmpmeters = 2; /*liczba amperomierzy przy hamowaniu ED*/ double DynamicBrakeRes = 5.8; /*rezystancja oporników przy hamowaniu ED*/ @@ -1250,28 +1265,28 @@ public: double TUHEX_Sum2 = 750; /*nastawa2 sterownika hamowania ED*/ double TUHEX_Sum3 = 750; /*nastawa3 sterownika hamowania ED*/ int TUHEX_Stages = 0; /*liczba stopni hamowania ED*/ - // TODO: wrap resistor fans variables and state into a device - int RVentType = 0; /*0 - brak, 1 - jest, 2 - automatycznie wlaczany*/ - double RVentnmax = 1.0; /*maks. obroty wentylatorow oporow rozruchowych*/ - double RVentCutOff = 0.0; /*rezystancja wylaczania wentylatorow dla RVentType=2*/ - double RVentSpeed { 0.5 }; //rozpedzanie sie wentylatora obr/s^2} - double RVentMinI { 50.0 }; //przy jakim pradzie sie wylaczaja} - bool RVentForceOn { false }; // forced activation switch + // TODO: wrap resistor fans variables and state into a device + int RVentType = 0; /*0 - brak, 1 - jest, 2 - automatycznie wlaczany*/ + double RVentnmax = 1.0; /*maks. obroty wentylatorow oporow rozruchowych*/ + double RVentCutOff = 0.0; /*rezystancja wylaczania wentylatorow dla RVentType=2*/ + double RVentSpeed{0.5}; // rozpedzanie sie wentylatora obr/s^2} + double RVentMinI{50.0}; // przy jakim pradzie sie wylaczaja} + bool RVentForceOn{false}; // forced activation switch int CompressorPower = 1; // 0: main circuit, 1: z przetwornicy, reczne, 2: w przetwornicy, stale, 3: diesel engine, 4: converter of unit in front, 5: converter of unit behind int SmallCompressorPower = 0; /*Winger ZROBIC*/ - bool Trafo = false; /*pojazd wyposażony w transformator*/ + bool Trafo = false; /*pojazd wyposażony w transformator*/ - /*-sekcja parametrow dla lokomotywy spalinowej z przekladnia mechaniczna*/ + /*-sekcja parametrow dla lokomotywy spalinowej z przekladnia mechaniczna*/ double dizel_Mmax = 1.0; - double dizel_nMmax = 1.0; - double dizel_Mnmax = 2.0; - double dizel_nmax = 2.0; - double dizel_nominalfill = 0.0; + double dizel_nMmax = 1.0; + double dizel_Mnmax = 2.0; + double dizel_nmax = 2.0; + double dizel_nominalfill = 0.0; std::map dizel_Momentum_Table; std::map dizel_vel2nmax_Table; /*parametry aproksymacji silnika spalinowego*/ double dizel_Mstand = 0.0; /*moment oporow ruchu silnika dla enrot=0*/ - /* dizel_auto_min, dizel_auto_max: real; {predkosc obrotowa przelaczania automatycznej skrzyni biegow*/ + /* dizel_auto_min, dizel_auto_max: real; {predkosc obrotowa przelaczania automatycznej skrzyni biegow*/ double dizel_nmax_cutoff = 0.0; /*predkosc obrotowa zadzialania ogranicznika predkosci*/ double dizel_nmin = 0.0; /*najmniejsza dopuszczalna predkosc obrotowa*/ double dizel_nmin_hdrive = 0.0; /*najmniejsza dopuszczalna predkosc obrotowa w czasie jazdy na hydro */ @@ -1281,12 +1296,14 @@ public: double dizel_minVelfullengage = 0.0; /*najmniejsza predkosc przy jezdzie ze sprzeglem bez poslizgu*/ double dizel_maxVelANS = 3.0; /*predkosc progowa rozlaczenia przetwornika momentu*/ double dizel_AIM = 1.0; /*moment bezwladnosci walu itp*/ - double dizel_RevolutionsDecreaseRate{ 2.0 }; + double dizel_RevolutionsDecreaseRate{2.0}; double dizel_NominalFuelConsumptionRate = 250.0; /*jednostkowe zużycie paliwa przy mocy nominalnej, wczytywane z fiz, g/kWh*/ double dizel_FuelConsumption = 0.0; /*współczynnik zużycia paliwa przeliczony do jednostek maszynowych, l/obrót*/ double dizel_FuelConsumptionActual = 0.0; /*chwilowe spalanie paliwa w l/h*/ double dizel_FuelConsumptedTotal = 0.0; /*ilość paliwa zużyta od początku symulacji, l*/ - double dizel_engageDia = 0.5; double dizel_engageMaxForce = 6000.0; double dizel_engagefriction = 0.5; /*parametry sprzegla*/ + double dizel_engageDia = 0.5; + double dizel_engageMaxForce = 6000.0; + double dizel_engagefriction = 0.5; /*parametry sprzegla*/ double engagedownspeed = 0.9; double engageupspeed = 0.5; /*parametry przetwornika momentu*/ @@ -1317,12 +1334,12 @@ public: bool hydro_R_Clutch = false; /*czy retarder ma rozłączalne sprzęgło*/ double hydro_R_ClutchSpeed = 10.0; /*szybkość narastania obrotów po włączeniu sprzęgła retardera*/ bool hydro_R_WithIndividual = false; /*czy dla autobusów jest to łączone*/ - /*- dla lokomotyw spalinowo-elektrycznych -*/ + /*- dla lokomotyw spalinowo-elektrycznych -*/ double AnPos = 0.0; // pozycja sterowania dokladnego (analogowego) bool AnalogCtrl = false; // bool AnMainCtrl = false; // bool ShuntModeAllow = false; - bool ShuntMode = false; + bool ShuntMode = false; bool Flat = false; double Vhyp = 1.0; @@ -1331,290 +1348,302 @@ public: TMPTRelayTable MPTRelay; int RelayType = 0; TShuntSchemeTable SST; - double PowerCorRatio = 1.0; //Wspolczynnik korekcyjny - /*- dla uproszczonego modelu silnika (dumb) oraz dla drezyny*/ + double PowerCorRatio = 1.0; // Wspolczynnik korekcyjny + /*- dla uproszczonego modelu silnika (dumb) oraz dla drezyny*/ double Ftmax = 0.0; /*- dla lokomotyw z silnikami indukcyjnymi -*/ double eimc[26]; - bool EIMCLogForce = false; // - static std::vector const eimc_labels; - double InverterFrequency { 0.0 }; // current frequency of power inverters + bool EIMCLogForce = false; // + static std::vector const eimc_labels; + double InverterFrequency{0.0}; // current frequency of power inverters int InvertersNo = 0; // number of inverters double InvertersRatio = 0.0; - std::vector Inverters; //all inverters - int InverterControlCouplerFlag = 4; //which coupling flag is necessary to controll inverters - int Imaxrpc = 0; // Maksymalny prad rezystora hamowania chlodzonego pasywnie - int BRVto = 0; // Czas jaki wentylatory jeszcze dodatkowo schladzaja rezystor - double BRVtimer = 0; // Timer dla podtrzymania wentylatora + std::vector Inverters; // all inverters + int InverterControlCouplerFlag = 4; // which coupling flag is necessary to controll inverters + int Imaxrpc = 0; // Maksymalny prad rezystora hamowania chlodzonego pasywnie + int BRVto = 0; // Czas jaki wentylatory jeszcze dodatkowo schladzaja rezystor + double BRVtimer = 0; // Timer dla podtrzymania wentylatora std::map EIM_Pmax_Table; /*tablica mocy maksymalnej od predkosci*/ /* -dla pojazdów z blendingiem EP/ED (MED) */ double MED_Vmax = 0; // predkosc maksymalna dla obliczen chwilowej sily hamowania EP w MED double MED_Vmin = 0; // predkosc minimalna dla obliczen chwilowej sily hamowania EP w MED double MED_Vref = 0; // predkosc referencyjna dla obliczen dostepnej sily hamowania EP w MED - double MED_amax { 9.81 }; // maksymalne opoznienie hamowania sluzbowego MED + double MED_amax{9.81}; // maksymalne opoznienie hamowania sluzbowego MED bool MED_EPVC = 0; // czy korekcja sily hamowania EP, gdy nie ma dostepnego ED double MED_EPVC_Time = 7; // czas korekcji sily hamowania EP, gdy nie ma dostepnego ED bool MED_Ncor = 0; // czy korekcja sily hamowania z uwzglednieniem nacisku - double MED_MinBrakeReqED = 0; // minimalne zadanie sily hamowania uruchamiajace ED - ponizej tylko EP + double MED_MinBrakeReqED = 0; // minimalne zadanie sily hamowania uruchamiajace ED - ponizej tylko EP double MED_FrED_factor = 1; // mnoznik sily hamowania ED do korekty blendingu double MED_ED_Delay1 = 0; // opoznienie wdrazania hamowania ED (pierwszy raz) double MED_ED_Delay2 = 0; // opoznienie zwiekszania sily hamowania ED (kolejne razy) - int DCEMUED_CC { 0 }; //na którym sprzęgu sprawdzać działanie ED - double DCEMUED_EP_max_Vel{ 0.0 }; //maksymalna prędkość, przy której działa EP przy włączonym ED w jednostce (dla tocznych) - double DCEMUED_EP_min_Im{ 0.0 }; //minimalny prąd, przy którym EP nie działa przy włączonym ED w członie (dla silnikowych) - double DCEMUED_EP_delay{ 0.0 }; //opóźnienie włączenia hamulca EP przy hamowaniu ED - zwłoka wstępna + int DCEMUED_CC{0}; // na którym sprzęgu sprawdzać działanie ED + double DCEMUED_EP_max_Vel{0.0}; // maksymalna prędkość, przy której działa EP przy włączonym ED w jednostce (dla tocznych) + double DCEMUED_EP_min_Im{0.0}; // minimalny prąd, przy którym EP nie działa przy włączonym ED w członie (dla silnikowych) + double DCEMUED_EP_delay{0.0}; // opóźnienie włączenia hamulca EP przy hamowaniu ED - zwłoka wstępna /*-dla wagonow*/ - struct load_attributes { - std::string name; // name of the cargo - float offset_min { 0.f }; // offset applied to cargo model when load amount is 0 + struct load_attributes + { + std::string name; // name of the cargo + float offset_min{0.f}; // offset applied to cargo model when load amount is 0 - load_attributes() = default; - load_attributes( std::string const &Name, float const Offsetmin ) : - name( Name ), offset_min( Offsetmin ) - {} - }; - std::vector LoadAttributes; - float MaxLoad = 0.f; /*masa w T lub ilosc w sztukach - ladownosc*/ - double OverLoadFactor = 0.0; /*ile razy moze byc przekroczona ladownosc*/ - float LoadSpeed = 0.f; float UnLoadSpeed = 0.f;/*szybkosc na- i rozladunku jednostki/s*/ + load_attributes() = default; + load_attributes(std::string const &Name, float const Offsetmin) : name(Name), offset_min(Offsetmin) {} + }; + std::vector LoadAttributes; + float MaxLoad = 0.f; /*masa w T lub ilosc w sztukach - ladownosc*/ + double OverLoadFactor = 0.0; /*ile razy moze byc przekroczona ladownosc*/ + float LoadSpeed = 0.f; + float UnLoadSpeed = 0.f; /*szybkosc na- i rozladunku jednostki/s*/ #ifdef EU07_USEOLDDOORCODE - int DoorOpenCtrl = 0; int DoorCloseCtrl = 0; /*0: przez pasazera, 1: przez maszyniste, 2: samoczynne (zamykanie)*/ - double DoorStayOpen = 0.0; /*jak dlugo otwarte w przypadku DoorCloseCtrl=2*/ - bool DoorClosureWarning = false; /*czy jest ostrzeganie przed zamknieciem*/ - bool DoorClosureWarningAuto = false; // departure signal plays automatically while door closing button is held down - double DoorOpenSpeed = 1.0; double DoorCloseSpeed = 1.0; /*predkosc otwierania i zamykania w j.u. */ - double DoorMaxShiftL = 0.5; double DoorMaxShiftR = 0.5; double DoorMaxPlugShift = 0.1;/*szerokosc otwarcia lub kat*/ - int DoorOpenMethod = 2; /*sposob otwarcia - 1: przesuwne, 2: obrotowe, 3: trójelementowe*/ - float DoorCloseDelay { 0.f }; // delay (in seconds) before the door begin closing, once conditions to close are met - double PlatformSpeed = 0.5; /*szybkosc stopnia*/ - double PlatformMaxShift { 0.0 }; /*wysuniecie stopnia*/ - int PlatformOpenMethod { 2 }; /*sposob animacji stopnia*/ + int DoorOpenCtrl = 0; + int DoorCloseCtrl = 0; /*0: przez pasazera, 1: przez maszyniste, 2: samoczynne (zamykanie)*/ + double DoorStayOpen = 0.0; /*jak dlugo otwarte w przypadku DoorCloseCtrl=2*/ + bool DoorClosureWarning = false; /*czy jest ostrzeganie przed zamknieciem*/ + bool DoorClosureWarningAuto = false; // departure signal plays automatically while door closing button is held down + double DoorOpenSpeed = 1.0; + double DoorCloseSpeed = 1.0; /*predkosc otwierania i zamykania w j.u. */ + double DoorMaxShiftL = 0.5; + double DoorMaxShiftR = 0.5; + double DoorMaxPlugShift = 0.1; /*szerokosc otwarcia lub kat*/ + int DoorOpenMethod = 2; /*sposob otwarcia - 1: przesuwne, 2: obrotowe, 3: trójelementowe*/ + float DoorCloseDelay{0.f}; // delay (in seconds) before the door begin closing, once conditions to close are met + double PlatformSpeed = 0.5; /*szybkosc stopnia*/ + double PlatformMaxShift{0.0}; /*wysuniecie stopnia*/ + int PlatformOpenMethod{2}; /*sposob animacji stopnia*/ #endif - double MirrorMaxShift { 90.0 }; - double MirrorVelClose { 5.0 }; - bool MirrorForbidden{ false }; /*czy jest pozwolenie na otworzenie lusterek (przycisk)*/ + double MirrorMaxShift{90.0}; + double MirrorVelClose{5.0}; + bool MirrorForbidden{false}; /*czy jest pozwolenie na otworzenie lusterek (przycisk)*/ bool ScndS = false; /*Czy jest bocznikowanie na szeregowej*/ bool SpeedCtrl = false; /*czy jest tempomat*/ speed_control SpeedCtrlUnit; /*parametry tempomatu*/ - double SpeedCtrlButtons[10] { 30, 40, 50, 60, 70, 80, 90, 100, 110, 120 }; /*przyciski prędkości*/ + double SpeedCtrlButtons[10]{30, 40, 50, 60, 70, 80, 90, 100, 110, 120}; /*przyciski prędkości*/ double SpeedCtrlDelay = 2; /*opoznienie dzialania tempomatu z wybieralna predkoscia*/ double SpeedCtrlValue = 0; /*wybrana predkosc jazdy na tempomacie*/ - /*--sekcja zmiennych*/ - /*--opis konkretnego egzemplarza taboru*/ - TLocation Loc { 0.0, 0.0, 0.0 }; //pozycja pojazdów do wyznaczenia odległości pomiędzy sprzęgami - TRotation Rot { 0.0, 0.0, 0.0 }; - glm::vec3 Front{}; - std::string Name; /*nazwa wlasna*/ - TCoupling Couplers[2]; //urzadzenia zderzno-sprzegowe, polaczenia miedzy wagonami - std::array Neighbours; // potential collision sources - bool Power110vIsAvailable = false; // cached availability of 110v power - bool Power24vIsAvailable = false; // cached availability of 110v power - double Power24vVoltage { 0.0 }; // cached battery voltage - bool EventFlag = false; /*!o true jesli cos nietypowego sie wydarzy*/ - int SoundFlag = 0; /*!o patrz stale sound_ */ - int AIFlag{ 0 }; // HACK: events of interest for consist owner - double DistCounter = 0.0; /*! licznik kilometrow */ - std::pair EnergyMeter; // energy from grid [kWh] - double V = 0.0; //predkosc w [m/s] względem sprzęgów (dodania gdy jedzie w stronę 0) - double Vel = 0.0; //moduł prędkości w [km/h], używany przez AI - double AccS = 0.0; //efektywne przyspieszenie styczne w [m/s^2] (wszystkie siły) - double AccSVBased {}; // tangential acceleration calculated from velocity change + /*--sekcja zmiennych*/ + /*--opis konkretnego egzemplarza taboru*/ + TLocation Loc{0.0, 0.0, 0.0}; // pozycja pojazdów do wyznaczenia odległości pomiędzy sprzęgami + TRotation Rot{0.0, 0.0, 0.0}; + glm::vec3 Front{}; + std::string Name; /*nazwa wlasna*/ + TCoupling Couplers[2]; // urzadzenia zderzno-sprzegowe, polaczenia miedzy wagonami + std::array Neighbours; // potential collision sources + bool Power110vIsAvailable = false; // cached availability of 110v power + bool Power24vIsAvailable = false; // cached availability of 110v power + double Power24vVoltage{0.0}; // cached battery voltage + bool EventFlag = false; /*!o true jesli cos nietypowego sie wydarzy*/ + int SoundFlag = 0; /*!o patrz stale sound_ */ + int AIFlag{0}; // HACK: events of interest for consist owner + double DistCounter = 0.0; /*! licznik kilometrow */ + std::pair EnergyMeter; // energy from grid [kWh] + double V = 0.0; // predkosc w [m/s] względem sprzęgów (dodania gdy jedzie w stronę 0) + double Vel = 0.0; // moduł prędkości w [km/h], używany przez AI + double AccS = 0.0; // efektywne przyspieszenie styczne w [m/s^2] (wszystkie siły) + double AccSVBased{}; // tangential acceleration calculated from velocity change double AccN = 0.0; // przyspieszenie normalne w [m/s^2] double AccVert = 0.0; // vertical acceleration - double nrot = 0.0; // predkosc obrotowa kol (obrotow na sekunde) - double nrot_eps = 0.0; //przyspieszenie kątowe kół (bez kierunku) + double nrot = 0.0; // predkosc obrotowa kol (obrotow na sekunde) + double nrot_eps = 0.0; // przyspieszenie kątowe kół (bez kierunku) double WheelFlat = 0.0; - bool TruckHunting { true }; // enable/disable truck hunting calculation + bool TruckHunting{true}; // enable/disable truck hunting calculation /*! rotacja kol [obr/s]*/ - double EnginePower = 0.0; /*! chwilowa moc silnikow*/ - double dL = 0.0; double Fb = 0.0; double Ff = 0.0; /*przesuniecie, sila hamowania i tarcia*/ - double FTrain = 0.0; double FStand = 0.0; /*! sila pociagowa i oporow ruchu*/ - double FTotal = 0.0; /*! calkowita sila dzialajaca na pojazd*/ - double UnitBrakeForce = 0.0; /*!s siła hamowania przypadająca na jeden element*/ - double Ntotal = 0.0; /*!s siła nacisku klockow*/ - bool SlippingWheels = false; bool SandDose = false; /*! poslizg kol, sypanie piasku*/ + double EnginePower = 0.0; /*! chwilowa moc silnikow*/ + double dL = 0.0; + double Fb = 0.0; + double Ff = 0.0; /*przesuniecie, sila hamowania i tarcia*/ + double FTrain = 0.0; + double FStand = 0.0; /*! sila pociagowa i oporow ruchu*/ + double FTotal = 0.0; /*! calkowita sila dzialajaca na pojazd*/ + double UnitBrakeForce = 0.0; /*!s siła hamowania przypadająca na jeden element*/ + double Ntotal = 0.0; /*!s siła nacisku klockow*/ + bool SlippingWheels = false; + bool SandDose = false; /*! poslizg kol, sypanie piasku*/ bool SandDoseManual = false; /*piaskowanie reczne*/ bool SandDoseAuto = false; /*piaskowanie automatyczne*/ bool SandDoseAutoAllow = true; /*zezwolenie na automatyczne piaskowanie*/ - double Sand = 0.0; /*ilosc piasku*/ - double BrakeSlippingTimer = 0.0; /*pomocnicza zmienna do wylaczania przeciwposlizgu*/ + double Sand = 0.0; /*ilosc piasku*/ + double BrakeSlippingTimer = 0.0; /*pomocnicza zmienna do wylaczania przeciwposlizgu*/ double dpBrake = 0.0; - double dpPipe = 0.0; - double dpMainValve = 0.0; - double dpLocalValve = 0.0; - double EmergencyValveFlow = 0.0; // air flow through alerter valve during last simulation step + double dpPipe = 0.0; + double dpMainValve = 0.0; + double dpLocalValve = 0.0; + double EmergencyValveFlow = 0.0; // air flow through alerter valve during last simulation step /*! przyrosty cisnienia w kroku czasowym*/ - double ScndPipePress = 0.0; /*cisnienie w przewodzie zasilajacym*/ - double BrakePress = 0.0; /*!o cisnienie w cylindrach hamulcowych*/ - double LocBrakePress = 0.0; /*!o cisnienie w cylindrach hamulcowych z pomocniczego*/ - double PipeBrakePress = 0.0; /*!o cisnienie w cylindrach hamulcowych z przewodu*/ - double PipePress = 0.0; /*!o cisnienie w przewodzie glownym*/ - double EqvtPipePress = 0.0; /*!o cisnienie w przewodzie glownym skladu*/ - double Volume = 0.0; /*objetosc spr. powietrza w zbiorniku hamulca*/ - double CompressedVolume = 0.0; /*objetosc spr. powietrza w ukl. zasilania*/ - double PantVolume = 0.48; /*objetosc spr. powietrza w ukl. pantografu*/ // aby podniesione pantografy opadły w krótkim czasie przy wyłączonej sprężarce - double Compressor = 0.0; /*! cisnienie w ukladzie zasilajacym*/ - bool CompressorFlag = false; /*!o czy wlaczona sprezarka*/ - bool PantCompFlag = false; /*!o czy wlaczona sprezarka pantografow*/ - bool CompressorAllow = false; /*! zezwolenie na uruchomienie sprezarki NBMX*/ - bool CompressorAllowLocal{ true }; // local device state override (most units don't have this fitted so it's set to true not to intefere) - bool CompressorGovernorLock{ false }; // indicates whether compressor pressure switch was activated due to reaching cut-out pressure - bool CompressorTankValve{ false }; // indicates excessive pressure is vented from compressor tank directly and instantly - start_t CompressorStart{ start_t::manual }; // whether the compressor is started manually, or another way - start_t PantographCompressorStart{ start_t::manual }; - basic_valve PantsValve; - std::array Pantographs; - bool PantAllDown { false }; - double PantFrontVolt = 0.0; //pantograf pod napieciem? 'Winger 160404 + double ScndPipePress = 0.0; /*cisnienie w przewodzie zasilajacym*/ + double BrakePress = 0.0; /*!o cisnienie w cylindrach hamulcowych*/ + double LocBrakePress = 0.0; /*!o cisnienie w cylindrach hamulcowych z pomocniczego*/ + double PipeBrakePress = 0.0; /*!o cisnienie w cylindrach hamulcowych z przewodu*/ + double PipePress = 0.0; /*!o cisnienie w przewodzie glownym*/ + double EqvtPipePress = 0.0; /*!o cisnienie w przewodzie glownym skladu*/ + double Volume = 0.0; /*objetosc spr. powietrza w zbiorniku hamulca*/ + double CompressedVolume = 0.0; /*objetosc spr. powietrza w ukl. zasilania*/ + double PantVolume = 0.48; /*objetosc spr. powietrza w ukl. pantografu*/ // aby podniesione pantografy opadły w krótkim czasie przy wyłączonej sprężarce + double Compressor = 0.0; /*! cisnienie w ukladzie zasilajacym*/ + bool CompressorFlag = false; /*!o czy wlaczona sprezarka*/ + bool PantCompFlag = false; /*!o czy wlaczona sprezarka pantografow*/ + bool CompressorAllow = false; /*! zezwolenie na uruchomienie sprezarki NBMX*/ + bool CompressorAllowLocal{true}; // local device state override (most units don't have this fitted so it's set to true not to intefere) + bool CompressorGovernorLock{false}; // indicates whether compressor pressure switch was activated due to reaching cut-out pressure + bool CompressorTankValve{false}; // indicates excessive pressure is vented from compressor tank directly and instantly + start_t CompressorStart{start_t::manual}; // whether the compressor is started manually, or another way + start_t PantographCompressorStart{start_t::manual}; + basic_valve PantsValve; + std::array Pantographs; + bool PantAllDown{false}; + double PantFrontVolt = 0.0; // pantograf pod napieciem? 'Winger 160404 double PantRearVolt = 0.0; - // TODO converter parameters, for when we start cleaning up mover parameters - start_t ConverterStart{ start_t::manual }; // whether converter is started manually, or by other means - float ConverterStartDelay{ 0.0f }; // delay (in seconds) before the converter is started, once its activation conditions are met - double ConverterStartDelayTimer{ 0.0 }; // helper, for tracking whether converter start delay passed - bool ConverterAllow = false; /*zezwolenie na prace przetwornicy NBMX*/ - bool ConverterAllowLocal{ true }; // local device state override (most units don't have this fitted so it's set to true not to intefere) - bool ConverterFlag = false; /*! czy wlaczona przetwornica NBMX*/ + // TODO converter parameters, for when we start cleaning up mover parameters + start_t ConverterStart{start_t::manual}; // whether converter is started manually, or by other means + float ConverterStartDelay{0.0f}; // delay (in seconds) before the converter is started, once its activation conditions are met + double ConverterStartDelayTimer{0.0}; // helper, for tracking whether converter start delay passed + bool ConverterAllow = false; /*zezwolenie na prace przetwornicy NBMX*/ + bool ConverterAllowLocal{true}; // local device state override (most units don't have this fitted so it's set to true not to intefere) + bool ConverterFlag = false; /*! czy wlaczona przetwornica NBMX*/ bool BRVentilators = false; /* Czy rezystor hamowania pracuje */ - start_t ConverterOverloadRelayStart { start_t::manual }; // whether overload relay reset responds to dedicated button - bool ConverterOverloadRelayOffWhenMainIsOff { false }; - fuel_pump FuelPump; - oil_pump OilPump; - water_pump WaterPump; - water_heater WaterHeater; - bool WaterCircuitsLink { false }; // optional connection between water circuits - heat_data dizel_heat; - std::array MotorBlowers; - door_data Doors; - float DoorsOpenWithPermitAfter { -1.f }; // remote open if permit button is held for specified time. NOTE: separate from door data as its cab control thing - int DoorsPermitLightBlinking { 0 }; //when the doors permit signal light is blinking + start_t ConverterOverloadRelayStart{start_t::manual}; // whether overload relay reset responds to dedicated button + bool ConverterOverloadRelayOffWhenMainIsOff{false}; + fuel_pump FuelPump; + oil_pump OilPump; + water_pump WaterPump; + water_heater WaterHeater; + bool WaterCircuitsLink{false}; // optional connection between water circuits + heat_data dizel_heat; + std::array MotorBlowers; + door_data Doors; + float DoorsOpenWithPermitAfter{-1.f}; // remote open if permit button is held for specified time. NOTE: separate from door data as its cab control thing + int DoorsPermitLightBlinking{0}; // when the doors permit signal light is blinking - int BrakeCtrlPos = -2; /*nastawa hamulca zespolonego*/ - double BrakeCtrlPosR = 0.0; /*nastawa hamulca zespolonego - plynna dla FV4a*/ - double BrakeCtrlPos2 = 0.0; /*nastawa hamulca zespolonego - kapturek dla FV4a*/ - int ManualBrakePos = 0; /*nastawa hamulca recznego*/ - double LocalBrakePosA = 0.0; /*nastawa hamulca pomocniczego*/ - double LocalBrakePosAEIM = 0.0; /*pozycja hamulca pomocniczego ep dla asynchronicznych ezt*/ - 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 -*/ - bool AlarmChainFlag = false; // manual emergency brake - bool RadioStopFlag = false; /*hamowanie nagle*/ - bool LockPipe = false; /*locking brake pipe in emergency state*/ - bool UnlockPipe = false; /*unlockig brake pipe button pressed*/ - int BrakeDelayFlag = 0; /*nastawa opoznienia ham. osob/towar/posp/exp 0/1/2/4*/ - int BrakeDelays = 0; /*nastawy mozliwe do uzyskania*/ - int BrakeOpModeFlag = 0; /*nastawa trybu pracy PS/PN/EP/MED 1/2/4/8*/ - int BrakeOpModes = 0; /*nastawy mozliwe do uzyskania*/ - bool DynamicBrakeFlag = false; /*czy wlaczony hamulec elektrodymiczny*/ - bool DynamicBrakeEMUStatus = true; /*czy hamulec ED dziala w ezt*/ - int TUHEX_StageActual = 0; /*aktualny stopien tuhexa*/ - bool TUHEX_ResChange = false; /*czy zmiana rezystancji hamowania ED - odwzbudzanie*/ - bool TUHEX_Active = false; /*czy hamowanie ED wywołane z zewnątrz*/ - // NapUdWsp: integer; - double LimPipePress = 0.0; /*stabilizator cisnienia*/ - double ActFlowSpeed = 0.0; /*szybkosc stabilizatora*/ + int BrakeCtrlPos = -2; /*nastawa hamulca zespolonego*/ + double BrakeCtrlPosR = 0.0; /*nastawa hamulca zespolonego - plynna dla FV4a*/ + double BrakeCtrlPos2 = 0.0; /*nastawa hamulca zespolonego - kapturek dla FV4a*/ + int ManualBrakePos = 0; /*nastawa hamulca recznego*/ + double LocalBrakePosA = 0.0; /*nastawa hamulca pomocniczego*/ + double LocalBrakePosAEIM = 0.0; /*pozycja hamulca pomocniczego ep dla asynchronicznych ezt*/ + 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 + */ + bool AlarmChainFlag = false; // manual emergency brake + bool RadioStopFlag = false; /*hamowanie nagle*/ + bool LockPipe = false; /*locking brake pipe in emergency state*/ + bool UnlockPipe = false; /*unlockig brake pipe button pressed*/ + int BrakeDelayFlag = 0; /*nastawa opoznienia ham. osob/towar/posp/exp 0/1/2/4*/ + int BrakeDelays = 0; /*nastawy mozliwe do uzyskania*/ + int BrakeOpModeFlag = 0; /*nastawa trybu pracy PS/PN/EP/MED 1/2/4/8*/ + int BrakeOpModes = 0; /*nastawy mozliwe do uzyskania*/ + bool DynamicBrakeFlag = false; /*czy wlaczony hamulec elektrodymiczny*/ + bool DynamicBrakeEMUStatus = true; /*czy hamulec ED dziala w ezt*/ + int TUHEX_StageActual = 0; /*aktualny stopien tuhexa*/ + bool TUHEX_ResChange = false; /*czy zmiana rezystancji hamowania ED - odwzbudzanie*/ + bool TUHEX_Active = false; /*czy hamowanie ED wywołane z zewnątrz*/ + // NapUdWsp: integer; + double LimPipePress = 0.0; /*stabilizator cisnienia*/ + double ActFlowSpeed = 0.0; /*szybkosc stabilizatora*/ + int DamageFlag = 0; // kombinacja bitowa stalych dtrain_* } + int EngDmgFlag = 0; // kombinacja bitowa stalych usterek} + int DerailReason = 0; // przyczyna wykolejenia - int DamageFlag = 0; //kombinacja bitowa stalych dtrain_* } - int EngDmgFlag = 0; //kombinacja bitowa stalych usterek} - int DerailReason = 0; //przyczyna wykolejenia - - //EndSignalsFlag: byte; {ABu 060205: zmiany - koncowki: 1/16 - swiatla prz/tyl, 2/31 - blachy prz/tyl} - //HeadSignalsFlag: byte; {ABu 060205: zmiany - swiatla: 1/2/4 - przod, 16/32/63 - tyl} + // EndSignalsFlag: byte; {ABu 060205: zmiany - koncowki: 1/16 - swiatla prz/tyl, 2/31 - blachy prz/tyl} + // HeadSignalsFlag: byte; {ABu 060205: zmiany - swiatla: 1/2/4 - przod, 16/32/63 - tyl} TCommand CommandIn; /*komenda przekazywana przez PutCommand*/ /*i wykonywana przez RunInternalCommand*/ - std::string CommandOut; /*komenda przekazywana przez ExternalCommand*/ - std::string CommandLast; //Ra: ostatnio wykonana komenda do podglądu - double ValueOut = 0.0; /*argument komendy która ma byc przekazana na zewnatrz*/ + std::string CommandOut; /*komenda przekazywana przez ExternalCommand*/ + std::string CommandLast; // Ra: ostatnio wykonana komenda do podglądu + double ValueOut = 0.0; /*argument komendy która ma byc przekazana na zewnatrz*/ - TTrackShape RunningShape;/*geometria toru po ktorym jedzie pojazd*/ - TTrackParam RunningTrack;/*parametry toru po ktorym jedzie pojazd*/ - double OffsetTrackH = 0.0; double OffsetTrackV = 0.0; /*przesuniecie poz. i pion. w/m osi toru*/ + TTrackShape RunningShape; /*geometria toru po ktorym jedzie pojazd*/ + TTrackParam RunningTrack; /*parametry toru po ktorym jedzie pojazd*/ + double OffsetTrackH = 0.0; + double OffsetTrackV = 0.0; /*przesuniecie poz. i pion. w/m osi toru*/ - /*-zmienne dla lokomotyw*/ - bool Mains = false; /*polozenie glownego wylacznika*/ - double MainsInitTime{ 0.0 }; // config, initialization time (in seconds) of the main circuit after it receives power, before it can be closed - double MainsInitTimeCountdown{ 0.0 }; // current state of main circuit initialization, remaining time (in seconds) until it's ready - start_t MainsStart { start_t::manual }; - bool LineBreakerClosesOnlyAtNoPowerPos{ false }; - bool ControlPressureSwitch{ false }; // activates if the main pipe and/or brake cylinder pressure aren't within operational levels - bool HasControlPressureSwitch{ true }; - bool ReleaserEnabledOnlyAtNoPowerPos{ false }; + /*-zmienne dla lokomotyw*/ + bool Mains = false; /*polozenie glownego wylacznika*/ + double MainsInitTime{0.0}; // config, initialization time (in seconds) of the main circuit after it receives power, before it can be closed + double MainsInitTimeCountdown{0.0}; // current state of main circuit initialization, remaining time (in seconds) until it's ready + start_t MainsStart{start_t::manual}; + bool LineBreakerClosesOnlyAtNoPowerPos{false}; + bool ControlPressureSwitch{false}; // activates if the main pipe and/or brake cylinder pressure aren't within operational levels + bool HasControlPressureSwitch{true}; + bool ReleaserEnabledOnlyAtNoPowerPos{false}; int MainCtrlPos = 0; /*polozenie glownego nastawnika*/ int ScndCtrlPos = 0; /*polozenie dodatkowego nastawnika*/ int LightsPos = 0; /*polozenie przelacznika wielopozycyjnego swiatel*/ int CompressorListPos = 0; /*polozenie przelacznika wielopozycyjnego sprezarek*/ - int DirActive = 0; //czy lok. jest wlaczona i w ktorym kierunku: względem wybranej kabiny: -1 - do tylu, +1 - do przodu, 0 - wylaczona - int DirAbsolute = 0; //zadany kierunek jazdy względem sprzęgów (1=w strone 0,-1=w stronę 1) - int MainCtrlMaxDirChangePos { 0 }; // can't change reverser state with master controller set above this position - int CabActive = 0; //numer kabiny, z której jest sterowanie: 1 lub -1; w przeciwnym razie brak sterowania - rozrzad - int CabOccupied = 0; //numer kabiny, w ktorej jest obsada (zwykle jedna na skład) // TODO: move to TController - bool CabMaster = false; //czy pojazd jest nadrzędny w składzie - inline bool IsCabMaster() { return ((CabActive == CabOccupied) && CabMaster); } //czy aktualna kabina jest na pewno tą, z której można sterować - bool AutomaticCabActivation = true; //czy zmostkowany rozrzad przelacza sie sam przy zmianie kabiny - int InactiveCabFlag = 0; //co sie dzieje przy dezaktywacji kabiny - bool InactiveCabPantsCheck = false; //niech DynamicObject sprawdzi pantografy + int DirActive = 0; // czy lok. jest wlaczona i w ktorym kierunku: względem wybranej kabiny: -1 - do tylu, +1 - do przodu, 0 - wylaczona + int DirAbsolute = 0; // zadany kierunek jazdy względem sprzęgów (1=w strone 0,-1=w stronę 1) + int MainCtrlMaxDirChangePos{0}; // can't change reverser state with master controller set above this position + int CabActive = 0; // numer kabiny, z której jest sterowanie: 1 lub -1; w przeciwnym razie brak sterowania - rozrzad + int CabOccupied = 0; // numer kabiny, w ktorej jest obsada (zwykle jedna na skład) // TODO: move to TController + bool CabMaster = false; // czy pojazd jest nadrzędny w składzie + inline bool IsCabMaster() + { + return ((CabActive == CabOccupied) && CabMaster); + } // czy aktualna kabina jest na pewno tą, z której można sterować + bool AutomaticCabActivation = true; // czy zmostkowany rozrzad przelacza sie sam przy zmianie kabiny + int InactiveCabFlag = 0; // co sie dzieje przy dezaktywacji kabiny + bool InactiveCabPantsCheck = false; // niech DynamicObject sprawdzi pantografy double LastSwitchingTime = 0.0; /*czas ostatniego przelaczania czegos*/ - int WarningSignal = 0; // 0: nie trabi, 1,2,4: trabi + int WarningSignal = 0; // 0: nie trabi, 1,2,4: trabi bool DepartureSignal = false; /*sygnal odjazdu*/ bool InsideConsist = false; /*-zmienne dla lokomotywy elektrycznej*/ - TTractionParam RunningTraction;/*parametry sieci trakcyjnej najblizej lokomotywy*/ + TTractionParam RunningTraction; /*parametry sieci trakcyjnej najblizej lokomotywy*/ double enrot = 0.0; // ilosc obrotow silnika - double Im = 0.0; // prad silnika -/* - // currently not used - double IHeating = 0.0; - double ITraction = 0.0; -*/ - double Itot = 0.0; // prad calkowity - double TotalCurrent = 0.0; - // momenty - double Mm = 0.0; - double Mw = 0.0; - // sily napedne - double Fw = 0.0; - double Ft = 0.0; - //Ra: Im jest ujemny, jeśli lok jedzie w stronę sprzęgu 1 - //a ujemne powinien być przy odwróconej polaryzacji sieci... - //w wielu miejscach jest używane abs(Im) - int Imin = 0; int Imax = 0; /*prad przelaczania automatycznego rozruchu, prad bezpiecznika*/ + double Im = 0.0; // prad silnika + /* + // currently not used + double IHeating = 0.0; + double ITraction = 0.0; + */ + double Itot = 0.0; // prad calkowity + double TotalCurrent = 0.0; + // momenty + double Mm = 0.0; + double Mw = 0.0; + // sily napedne + double Fw = 0.0; + double Ft = 0.0; + // Ra: Im jest ujemny, jeśli lok jedzie w stronę sprzęgu 1 + // a ujemne powinien być przy odwróconej polaryzacji sieci... + // w wielu miejscach jest używane abs(Im) + int Imin = 0; + int Imax = 0; /*prad przelaczania automatycznego rozruchu, prad bezpiecznika*/ double EngineVoltage = 0.0; // voltage supplied to engine int MainCtrlActualPos = 0; /*wskaznik RList*/ int ScndCtrlActualPos = 0; /*wskaznik MotorParam*/ - bool DelayCtrlFlag = false; //czy czekanie na 1. pozycji na załączenie? - double LastRelayTime = 0.0; /*czas ostatniego przelaczania stycznikow*/ - bool AutoRelayFlag = false; /*mozna zmieniac jesli AutoRelayType=2*/ - bool FuseFlag = false; /*!o bezpiecznik nadmiarowy*/ - bool ConvOvldFlag = false; /*! nadmiarowy przetwornicy i ogrzewania*/ - bool GroundRelay { true }; // switches off to protect against damage from earths - start_t GroundRelayStart { start_t::manual }; // relay activation method - bool StLinFlag = false; /*!o styczniki liniowe*/ - bool StLinSwitchOff{ false }; // state of the button forcing motor connectors open - bool ResistorsFlag = false; /*!o jazda rezystorowa*/ - double RventRot = 0.0; /*!s obroty wentylatorow rozruchowych*/ - double PantographVoltage{ 0.0 }; // voltage supplied to pantographs + bool DelayCtrlFlag = false; // czy czekanie na 1. pozycji na załączenie? + double LastRelayTime = 0.0; /*czas ostatniego przelaczania stycznikow*/ + bool AutoRelayFlag = false; /*mozna zmieniac jesli AutoRelayType=2*/ + bool FuseFlag = false; /*!o bezpiecznik nadmiarowy*/ + bool ConvOvldFlag = false; /*! nadmiarowy przetwornicy i ogrzewania*/ + bool GroundRelay{true}; // switches off to protect against damage from earths + start_t GroundRelayStart{start_t::manual}; // relay activation method + bool StLinFlag = false; /*!o styczniki liniowe*/ + bool StLinSwitchOff{false}; // state of the button forcing motor connectors open + bool ResistorsFlag = false; /*!o jazda rezystorowa*/ + double RventRot = 0.0; /*!s obroty wentylatorow rozruchowych*/ + double PantographVoltage{0.0}; // voltage supplied to pantographs double PantPress = 0.0; /*Cisnienie w zbiornikach pantografow*/ - bool PantPressSwitchActive{ false }; // state of the pantograph pressure switch. gets primed at defined pressure level in pantograph air system - bool PantPressLockActive{ false }; // pwr system state flag. fires when pressure switch activates by pantograph pressure dropping below defined level - bool NoVoltRelay{ true }; // switches off if the power level drops below threshold - bool OvervoltageRelay{ true }; // switches off if the power level goes above threshold - bool s_CAtestebrake = false; //hunter-091012: zmienna dla testu ca - std::array, 4> PowerCircuits; //24v, 110v, 3x400v and 3000v power circuits, voltage from local sources and current draw pairs + bool PantPressSwitchActive{false}; // state of the pantograph pressure switch. gets primed at defined pressure level in pantograph air system + bool PantPressLockActive{false}; // pwr system state flag. fires when pressure switch activates by pantograph pressure dropping below defined level + bool NoVoltRelay{true}; // switches off if the power level drops below threshold + bool OvervoltageRelay{true}; // switches off if the power level goes above threshold + bool s_CAtestebrake = false; // hunter-091012: zmienna dla testu ca + std::array, 4> PowerCircuits; // 24v, 110v, 3x400v and 3000v power circuits, voltage from local sources and current draw pairs - /*-zmienne dla lokomotywy spalinowej z przekladnia mechaniczna*/ + /*-zmienne dla lokomotywy spalinowej z przekladnia mechaniczna*/ double dizel_fill = 0.0; /*napelnienie*/ double dizel_engagestate = 0.0; /*sprzeglo skrzyni biegow: 0 - luz, 1 - wlaczone, 0.5 - wlaczone 50% (z poslizgiem)*/ double dizel_engage = 0.0; /*sprzeglo skrzyni biegow: aktualny docisk*/ double dizel_automaticgearstatus = 0.0; /*0 - bez zmiany, -1 zmiana na nizszy +1 zmiana na wyzszy*/ - bool dizel_startup { false }; // engine startup procedure request indicator - bool dizel_ignition { false }; // engine ignition request indicator - bool dizel_spinup { false }; // engine spin up to idle speed flag - double dizel_engagedeltaomega = 0.0; /*roznica predkosci katowych tarcz sprzegla*/ + bool dizel_startup{false}; // engine startup procedure request indicator + bool dizel_ignition{false}; // engine ignition request indicator + bool dizel_spinup{false}; // engine spin up to idle speed flag + double dizel_engagedeltaomega = 0.0; /*roznica predkosci katowych tarcz sprzegla*/ double dizel_n_old = 0.0; /*poredkosc na potrzeby obliczen sprzegiel*/ double dizel_Torque = 0.0; /*aktualny moment obrotowy silnika spalinowego*/ double dizel_Power = 0.0; /*aktualna moc silnika spalinowego*/ @@ -1638,7 +1667,7 @@ public: double hydro_R_n = 0.0; /*predkosc obrotowa retardera*/ bool hydro_R_ClutchActive = false; /*czy retarder jest napędzany*/ - /*- zmienne dla lokomotyw z silnikami indukcyjnymi -*/ + /*- zmienne dla lokomotyw z silnikami indukcyjnymi -*/ double eimic = 0; /*aktualna pozycja zintegrowanego sterowania jazda i hamowaniem*/ double eimic_analog = 0; /*pozycja zadajnika analogowa*/ double eimic_real = 0; /*faktycznie uzywana pozycja zintegrowanego sterowania jazda i hamowaniem*/ @@ -1650,7 +1679,7 @@ public: bool EIMCtrlEmergency = false; /*czy ma dodatkowe zero jazdy i zero hamowania */ double eimv_pr = 0; /*realizowany procent dostepnej sily rozruchu/hamowania*/ double eimv[21]; - static std::vector const eimv_labels; + static std::vector const eimv_labels; double SpeedCtrlTimer = 0; /*zegar dzialania tempomatu z wybieralna predkoscia*/ double eimicSpeedCtrl = 1; /*pozycja sugerowana przez tempomat*/ double eimicSpeedCtrlIntegral = 0; /*calkowany blad ustawienia predkosci*/ @@ -1659,7 +1688,7 @@ public: double MED_ED_DelayTimer = 0; /*aktualny czas licznika opoznienia hamowania ED*/ /*-zmienne dla drezyny*/ - double PulseForce = 0.0; /*przylozona sila*/ + double PulseForce = 0.0; /*przylozona sila*/ double PulseForceTimer = 0.0; int PulseForceCount = 0; @@ -1667,41 +1696,41 @@ public: double eAngle = M_PI * 0.5; /*-dla wagonow*/ - float LoadAmount = 0.f; /*masa w T lub ilosc w sztukach - zaladowane*/ - load_attributes LoadType; - std::string LoadQuantity; // jednostki miary - int LoadStatus = 0; //+1=trwa rozladunek,+2=trwa zaladunek,+4=zakończono,0=zaktualizowany model - bool LoadTypeChange{ false }; // indicates load type was changed - double LastLoadChangeTime = 0.0; //raz (roz)ładowania + float LoadAmount = 0.f; /*masa w T lub ilosc w sztukach - zaladowane*/ + load_attributes LoadType; + std::string LoadQuantity; // jednostki miary + int LoadStatus = 0; //+1=trwa rozladunek,+2=trwa zaladunek,+4=zakończono,0=zaktualizowany model + bool LoadTypeChange{false}; // indicates load type was changed + double LastLoadChangeTime = 0.0; // raz (roz)ładowania #ifdef EU07_USEOLDDOORCODE - bool DoorBlocked = false; //Czy jest blokada drzwi - bool DoorLockEnabled { true }; - bool DoorLeftOpened = false; //stan drzwi - double DoorLeftOpenTimer { -1.0 }; // left door closing timer for automatic door type + bool DoorBlocked = false; // Czy jest blokada drzwi + bool DoorLockEnabled{true}; + bool DoorLeftOpened = false; // stan drzwi + double DoorLeftOpenTimer{-1.0}; // left door closing timer for automatic door type bool DoorRightOpened = false; - double DoorRightOpenTimer{ -1.0 }; // right door closing timer for automatic door type + double DoorRightOpenTimer{-1.0}; // right door closing timer for automatic door type #endif - // TODO: move these switch types where they belong, cabin definition + // TODO: move these switch types where they belong, cabin definition std::string PantSwitchType; std::string ConvSwitchType; - std::string StLinSwitchType; + std::string StLinSwitchType; - bool Heating = false; //ogrzewanie 'Winger 020304 - bool HeatingAllow { false }; // heating switch // TODO: wrap heating in a basic device - int DoubleTr = 1; //trakcja ukrotniona - przedni pojazd 'Winger 160304 - basic_light CompartmentLights; + bool Heating = false; // ogrzewanie 'Winger 020304 + bool HeatingAllow{false}; // heating switch // TODO: wrap heating in a basic device + int DoubleTr = 1; // trakcja ukrotniona - przedni pojazd 'Winger 160304 + basic_light CompartmentLights; bool PhysicActivation = true; /*ABu: stale dla wyznaczania sil (i nie tylko) po optymalizacji*/ double FrictConst1 = 0.0; double FrictConst2s = 0.0; - double FrictConst2d= 0.0; + double FrictConst2d = 0.0; double TotalMassxg = 0.0; /*TotalMass*g*/ - double fBrakeCtrlPos = -2.0; // płynna nastawa hamulca zespolonego + double fBrakeCtrlPos = -2.0; // płynna nastawa hamulca zespolonego bool bPantKurek3 = true; // kurek trójdrogowy (pantografu): true=połączenie z ZG, false=połączenie z małą sprężarką // domyślnie zbiornik pantografu połączony jest ze zbiornikiem głównym - bool PantAutoValve { false }; // type of installed pantograph compressor valve + bool PantAutoValve{false}; // type of installed pantograph compressor valve int iProblem = 0; // flagi problemów z taborem, aby AI nie musiało porównywać; 0=może jechać int iLights[2]; // bity zapalonych świateł tutaj, żeby dało się liczyć pobór prądu @@ -1712,8 +1741,8 @@ public: // 3 - swiatla dlugie przyciemnione // 4 - swiatla dlugie normalne int modernDimmerState{0}; - bool modernContainOffPos = true; - bool enableModernDimmer = false; + bool modernContainOffPos{true}; + bool enableModernDimmer {false}; // Barwa reflektora int refR{255}; // Czerwony diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index fe94a58b..57fe8cf3 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -9944,6 +9944,13 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) else result = false; + if (!modernContainOffPos) + modernDimmerState = 1; + if (!enableModernDimmer) + { + modernDimmerState = 2; + } + WriteLog("CERROR: " + to_string(ConversionError) + ", SUCCES: " + to_string(result)); return result; } @@ -11139,10 +11146,6 @@ void TMoverParameters::LoadFIZ_Switches( std::string const &Input ) { extract_value( UniversalResetButtonFlag[ 2 ], "RelayResetButton3", Input, "" ); extract_value(enableModernDimmer, "ModernDimmer", Input, ""); extract_value(modernContainOffPos, "ModernDimmerOffPosition", Input, ""); - if (!modernContainOffPos) - modernDimmerState = 1; - if (!enableModernDimmer) - modernDimmerState = 2; // pantograph presets { auto &presets { PantsPreset.first }; diff --git a/Train.cpp b/Train.cpp index 4ebcac59..f70168e1 100644 --- a/Train.cpp +++ b/Train.cpp @@ -4918,10 +4918,12 @@ void TTrain::OnCommand_endsignalstoggle( TTrain *Train, command_data const &Comm } void TTrain::OnCommand_headlightsdimtoggle( TTrain *Train, command_data const &Command ) { - + if (Train->DynamicObject->MoverParameters->enableModernDimmer) + return; if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( false == Train->DynamicObject->DimHeadlights ) { + if (Train->DynamicObject->MoverParameters->modernDimmerState == 2) + { // turn on OnCommand_headlightsdimenable( Train, Command ); } @@ -4934,6 +4936,8 @@ void TTrain::OnCommand_headlightsdimtoggle( TTrain *Train, command_data const &C void TTrain::OnCommand_headlightsdimenable( TTrain *Train, command_data const &Command ) { + if (Train->DynamicObject->MoverParameters->enableModernDimmer) + return; if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( Train->ggDimHeadlightsButton.SubModel != nullptr ) { @@ -4950,13 +4954,18 @@ void TTrain::OnCommand_headlightsdimenable( TTrain *Train, command_data const &C Train->DynamicObject->DimHeadlights = true; */ - - Train->mvOccupied->modernDimmerState = 1; // ustawiamy modern dimmer na flage przyciemnienia + WriteLog("Switch do 1"); + Train->DynamicObject->MoverParameters->modernDimmerState = 1; // ustawiamy modern dimmer na flage przyciemnienia + Train->DynamicObject->RaLightsSet(Train->DynamicObject->MoverParameters->iLights[0], + Train->DynamicObject->MoverParameters->iLights[1] + ); // aktualizacja swiatelek } } void TTrain::OnCommand_headlightsdimdisable( TTrain *Train, command_data const &Command ) { + if (Train->DynamicObject->MoverParameters->enableModernDimmer) // nie wiem dlaczego to tak dziala ze jest odwrocona logika + return; if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( Train->ggDimHeadlightsButton.SubModel != nullptr ) { @@ -4972,7 +4981,12 @@ void TTrain::OnCommand_headlightsdimdisable( TTrain *Train, command_data const & Train->DynamicObject->DimHeadlights = false; */ - Train->mvOccupied->modernDimmerState = 2; // ustawiamy modern dimmer na flage rozjasnienia + WriteLog("Switch do 2"); + Train->DynamicObject->MoverParameters->modernDimmerState = 2; // ustawiamy modern dimmer na flage rozjasnienia + Train->DynamicObject->RaLightsSet( + Train->DynamicObject->MoverParameters->iLights[0], + Train->DynamicObject->MoverParameters->iLights[1] + ); // aktualizacja swiatelek } } @@ -9816,7 +9830,7 @@ void TTrain::set_cab_controls( int const Cab ) { ggRightLightButton.PutValue( -1.f ); } } - if( true == DynamicObject->DimHeadlights ) { + if( 1 == DynamicObject->MoverParameters->modernDimmerState ) { ggDimHeadlightsButton.PutValue( 1.f ); } // cab lights From a7bf952ed6608dcc52fdfaf57c6eb60165ab68e2 Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 7 Jan 2025 04:50:48 +0100 Subject: [PATCH 50/88] Light color calculation fix --- lightarray.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lightarray.cpp b/lightarray.cpp index 77dd3ccd..c12ea319 100644 --- a/lightarray.cpp +++ b/lightarray.cpp @@ -87,9 +87,9 @@ light_array::update() { ( ( lights & light::headlight_right | light::highbeamlight_right) ? 1.f : 0.f ) }; light.color = { - light.owner->MoverParameters->refR / 255, - light.owner->MoverParameters->refG / 255, - light.owner->MoverParameters->refB / 255 + static_cast(light.owner->MoverParameters->refR) / 255.0f, + static_cast(light.owner->MoverParameters->refG) / 255.0f, + static_cast(light.owner->MoverParameters->refB) / 255.0f }; if (light.owner->DimHeadlights && !light.owner->HighBeamLights) // tylko przyciemnione From c7e67c68165901439a291eddaa47f3f16455d612 Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 7 Jan 2025 05:06:24 +0100 Subject: [PATCH 51/88] Revert "Merge branch 'master-no-pull' into high-beam-lights" This reverts commit 1222f0fbb8890a1ef5078d9d3b76846d159a6830, reversing changes made to a7bf952ed6608dcc52fdfaf57c6eb60165ab68e2. --- McZapkie/MOVER.h | 274 ++++++--------------------------------------- McZapkie/Mover.cpp | 9 +- Train.cpp | 35 +----- Train.h | 2 - 4 files changed, 41 insertions(+), 279 deletions(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 8ad5e36f..b45e49fc 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -814,245 +814,36 @@ struct inverter { class TMoverParameters { // Ra: wrapper na kod pascalowy, przejmujący jego funkcje Q: 20160824 - juz nie wrapper a klasa bazowa :) -private: -// types -/* TODO: implement - // communication cable, exchanging control signals with adjacent vehicle - struct jumper_cable { - // types - using flag_pair = std::pair; - // members - // booleans - // std::array flags {}; - // boolean pairs, exchanged data is swapped when connected to a matching end (front to front or back to back) - // TBD, TODO: convert to regular bool array for efficiency once it's working? - std::array flag_pairs {}; - // integers - // std::array values {}; - }; -*/ - // basic approximation of a generic device - // TBD: inheritance or composition? - struct basic_device { - // config - start_t start_type { start_t::manual }; - // ld inputs - bool is_enabled { false }; // device is allowed/requested to operate - bool is_disabled { false }; // device is requested to stop - // TODO: add remaining inputs; start conditions and potential breakers - // ld outputs - bool is_active { false }; // device is working - }; - - struct basic_light : public basic_device { - // config - float dimming { 1.0f }; // light strength multiplier - // ld outputs - float intensity { 0.0f }; // current light strength - }; - - struct cooling_fan : public basic_device { - // config - float speed { 0.f }; // cooling fan rpm; either fraction of parent rpm, or absolute value if negative - float sustain_time { 0.f }; // time of sustaining work of cooling fans after stop - float min_start_velocity { -1.f }; // minimal velocity of vehicle, when cooling fans activate - // ld outputs - float revolutions { 0.f }; // current fan rpm - float stop_timer { 0.f }; // current time, when shut off condition is active - }; - - // basic approximation of a fuel pump - struct fuel_pump : public basic_device { - // TODO: fuel consumption, optional automatic engine start after activation - }; - - // basic approximation of an oil pump - struct oil_pump : public basic_device { - // config - float pressure_minimum { 0.f }; // lowest acceptable working pressure - float pressure_maximum { 0.65f }; // oil pressure at maximum engine revolutions - // ld inputs - float resource_amount { 1.f }; // amount of affected resource, compared to nominal value - // internal data - float pressure_target { 0.f }; - // ld outputs - float pressure { 0.f }; // current pressure - }; - - // basic approximation of a water pump - struct water_pump : public basic_device { - // ld inputs - // TODO: move to breaker list in the basic device once implemented - bool breaker { false }; // device is allowed to operate - }; - - // basic approximation of a solenoid valve - struct basic_valve : basic_device { - // config - bool solenoid { true }; // requires electric power to operate - bool spring { true }; // spring return or double acting actuator - }; - - // basic approximation of a pantograph - struct basic_pantograph { - // ld inputs - basic_valve valve; // associated pneumatic valve - // ld outputs - bool is_active { false }; // device is working - bool sound_event { false }; // indicates last state which generated sound event - double voltage { 0.0 }; - }; - - // basic approximation of doors - struct basic_door { - // config - // ld inputs - bool open_permit { false }; // door can be opened - bool local_open { false }; // local attempt to open the door - bool local_close { false }; // local attempt to close the door - bool remote_open { false }; // received remote signal to open the door - bool remote_close { false }; // received remote signal to close the door - // internal data - float auto_timer { -1.f }; // delay between activation of open state and closing state for automatic doors - float close_delay { 0.f }; // delay between activation of closing state and actual closing - float open_delay { 0.f }; // delay between activation of opening state and actual opening - float position { 0.f }; // current shift of the door from the closed position - float step_position { 0.f }; // current shift of the movable step from the retracted position - // ld outputs - bool is_closed { true }; // the door is fully closed - bool is_door_closed { true }; // the door is fully closed, step doesn't matter - bool is_closing { false }; // the door is currently closing - bool is_opening { false }; // the door is currently opening - bool is_open { false }; // the door is fully open - bool step_folding { false }; // the doorstep is currently closing - bool step_unfolding { false }; // the doorstep is currently opening - }; - - struct door_data { - // config - control_t open_control { control_t::passenger }; - float open_rate { 1.f }; - float open_delay { 0.f }; - control_t close_control { control_t::passenger }; - float close_rate { 1.f }; - float close_delay { 0.f }; - int type { 2 }; - float range { 0.f }; // extent of primary move/rotation - float range_out { 0.f }; // extent of shift outward, applicable for plug doors - int step_type { 2 }; - float step_rate { 0.5f }; - float step_range { 0.f }; - bool has_lock { false }; - bool has_warning { false }; - bool has_autowarning { false }; - float auto_duration { -1.f }; // automatic door closure delay period - float auto_velocity { -1.f }; // automatic door closure velocity threshold - bool auto_include_remote { false }; // automatic door closure applies also to remote control - bool permit_needed { false }; - std::vector permit_presets; // permit presets selectable with preset switch - float voltage { 0.f }; // power type required for door movement - // ld inputs - bool lock_enabled { true }; - bool step_enabled { true }; - bool remote_only { false }; // door ignores local control signals - // internal data - int permit_preset { -1 }; // curent position of preset selection switch - // vehicle parts - std::array instances; // door on the right and left side of the vehicle - // ld outputs - bool is_locked { false }; - double doorLockSpeed = 10.0; // predkosc przy ktorej wyzwalana jest blokada drzwi - }; - - struct water_heater { - // config - struct heater_config_t { - float temp_min { -1 }; // lowest accepted temperature - float temp_max { -1 }; // highest accepted temperature - } config; - // ld inputs - bool breaker { false }; // device is allowed to operate - bool is_enabled { false }; // device is requested to operate - // ld outputs - bool is_active { false }; // device is working - bool is_damaged { false }; // device is damaged - }; - - struct heat_data { - // input, state of relevant devices - bool cooling { false }; // TODO: user controlled device, implement - // bool okienko { true }; // window in the engine compartment - // system configuration - bool auxiliary_water_circuit { false }; // cooling system has an extra water circuit - double fan_speed { 0.075 }; // cooling fan rpm; either fraction of engine rpm, or absolute value if negative - // heat exchange factors - double kw { 0.35 }; - double kv { 0.6 }; - double kfe { 1.0 }; - double kfs { 80.0 }; - double kfo { 25.0 }; - double kfo2 { 25.0 }; - // system parts - struct fluid_circuit_t { - - struct circuit_config_t { - float temp_min { -1 }; // lowest accepted temperature - float temp_max { -1 }; // highest accepted temperature - float temp_cooling { -1 }; // active cooling activation point - float temp_flow { -1 }; // fluid flow activation point - bool shutters { false }; // the radiator has shutters to assist the cooling - } config; - bool is_cold { false }; // fluid is too cold - bool is_warm { false }; // fluid is too hot - bool is_hot { false }; // fluid temperature crossed cooling threshold - bool is_flowing { false }; // fluid is being pushed through the circuit - } water, - water_aux, - oil; - // output, state of affected devices - bool PA { false }; // malfunction flag - float rpmw { 0.0 }; // current main circuit fan revolutions - float rpmwz { 0.0 }; // desired main circuit fan revolutions - bool zaluzje1 { false }; - float rpmw2 { 0.0 }; // current auxiliary circuit fan revolutions - float rpmwz2 { 0.0 }; // desired auxiliary circuit fan revolutions - bool zaluzje2 { false }; - // output, temperatures - float Te { 15.0 }; // ambient temperature TODO: get it from environment data - // NOTE: by default the engine is initialized in warm, startup-ready state - float Ts { 50.0 }; // engine temperature - float To { 45.0 }; // oil temperature - float Tsr { 50.0 }; // main circuit radiator temperature (?) - float Twy { 50.0 }; // main circuit water temperature - float Tsr2 { 40.0 }; // secondary circuit radiator temperature (?) - float Twy2 { 40.0 }; // secondary circuit water temperature - float temperatura1 { 50.0 }; - float temperatura2 { 40.0 }; - float powerfactor { 1.0 }; // coefficient of heat generation for engines other than su45 - }; - - struct spring_brake { - std::shared_ptr Cylinder; - bool Activate { false }; //Input: switching brake on/off in exploitation - main valve/switch - bool ShuttOff { true }; //Input: shutting brake off during failure - valve in pneumatic container - bool Release { false }; //Input: emergency releasing rod - - bool IsReady{ false }; //Output: readyness to braking - cylinder is armed, spring is tentioned - bool IsActive{ false }; //Output: brake is working - double SBP{ 0.0 }; //Output: pressure in spring brake cylinder - - bool PNBrakeConnection{ false }; //Conf: connection to pneumatic brake cylinders - double MaxSetPressure { 0.0 }; //Conf: Maximal pressure for switched off brake - double ResetPressure{ 0.0 }; //Conf: Pressure for arming brake cylinder - double MinForcePressure{ 0.1 }; //Conf: Minimal pressure for zero force - double MaxBrakeForce{ 0.0 }; //Conf: Maximal tension for brake pads/shoes - double PressureOn{ -2.0 }; //Conf: Pressure changing ActiveFlag to "On" - double PressureOff{ -1.0 }; //Conf: Pressure changing ActiveFlag to "Off" - double ValveOffArea{ 0.0 }; //Conf: Area of filling valve - double ValveOnArea{ 0.0 }; //Conf: Area of dumping valve - double ValvePNBrakeArea{ 0.0 }; //Conf: Area of bypass to brake cylinders - - int MultiTractionCoupler{ 127 }; //Conf: Coupling flag necessary for transmitting the command + private: + // types + /* TODO: implement + // communication cable, exchanging control signals with adjacent vehicle + struct jumper_cable { + // types + using flag_pair = std::pair; + // members + // booleans + // std::array flags {}; + // boolean pairs, exchanged data is swapped when connected to a matching end (front to front or back to back) + // TBD, TODO: convert to regular bool array for efficiency once it's working? + std::array flag_pairs {}; + // integers + // std::array values {}; + }; + */ + // basic approximation of a generic device + // TBD: inheritance or composition? + struct basic_device + { + // config + start_t start_type{start_t::manual}; + // ld inputs + bool is_enabled{false}; // device is allowed/requested to operate + bool is_disabled{false}; // device is requested to stop + // TODO: add remaining inputs; start conditions and potential breakers + // ld outputs + bool is_active{false}; // device is working + }; struct basic_light : public basic_device { @@ -1413,9 +1204,8 @@ private: int LightsDefPos = 1; bool LightsWrap = false; int Lights[2][17]; // pozycje świateł, przód - tył, 1 .. 16 - int ScndInMain{ 0 }; /*zaleznosc bocznika od nastawnika*/ - bool MBrake = false; /*Czy jest hamulec reczny*/ - double maxTachoSpeed = 0.0; // maksymalna predkosc na tarczce predkosciomierza analogowego + int ScndInMain{0}; /*zaleznosc bocznika od nastawnika*/ + bool MBrake = false; /*Czy jest hamulec reczny*/ double StopBrakeDecc = 0.0; bool ReleaseParkingBySpringBrake{false}; bool ReleaseParkingBySpringBrakeWhenDoorIsOpen{false}; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 35211345..57fe8cf3 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -8617,7 +8617,8 @@ TMoverParameters::update_doors( double const Deltatime ) { Doors.is_locked = ( true == Doors.has_lock ) - && ( true == Doors.lock_enabled ) && (Vel >= Doors.doorLockSpeed); + && ( true == Doors.lock_enabled ) + && ( Vel >= 10.0 ); for( auto &door : Doors.instances ) { // revoke permit if... @@ -10318,7 +10319,7 @@ void TMoverParameters::LoadFIZ_Doors( std::string const &line ) { extract_value( Doors.has_warning, "DoorClosureWarning", line, "" ); extract_value( Doors.has_autowarning, "DoorClosureWarningAuto", line, "" ); extract_value( Doors.has_lock, "DoorBlocked", line, "" ); - extract_value(Doors.doorLockSpeed, "DoorLockSpeed", line, ""); + { auto const remotedoorcontrol { ( Doors.open_control == control_t::driver ) @@ -10566,10 +10567,6 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { } // mbrake extract_value( MBrake, "ManualBrake", line, "" ); - - // maksymalna predkosc dostepna na tarczce predkosciomierza - extract_value(maxTachoSpeed, "MaxTachoSpeed", line, ""); - // dynamicbrake { std::map dynamicbrakes{ diff --git a/Train.cpp b/Train.cpp index 23a0de62..f70168e1 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7005,23 +7005,16 @@ bool TTrain::Update( double const Deltatime ) // McZapkie: predkosc wyswietlana na tachometrze brana jest z obrotow kol auto const maxtacho { 3.0 }; - - double maxSpeed = mvControlled->Vmax * 1.05; // zachowanie starej logiki jak nie ma definicji max tarczki - if (mvOccupied->maxTachoSpeed != 0) - { - maxSpeed = mvOccupied->maxTachoSpeed; - } - fTachoVelocity = static_cast(std::min(std::abs(11.31 * mvControlled->WheelDiameter * mvControlled->nrot), maxSpeed)); + fTachoVelocity = static_cast( std::min( std::abs(11.31 * mvControlled->WheelDiameter * mvControlled->nrot), mvControlled->Vmax * 1.05) ); { // skacze osobna zmienna float ff = simulation::Time.data().wSecond; // skacze co sekunde - pol sekundy // pomiar, pol sekundy ustawienie if (ff != fTachoTimer) // jesli w tej sekundzie nie zmienial { - if (fTachoVelocity >= 5) // jedzie - fTachoVelocityJump = fTachoVelocity + (2.0 - LocalRandom(3) + LocalRandom(3)) * 0.5; - else if (fTachoVelocity < 5 && fTachoVelocity > 1) - fTachoVelocityJump = Random(0, 4); // tu ma sie bujac jak wariat i zatrzymac na jakiejs predkosci - // fTachoVelocityJump = 0; // stoi + if (fTachoVelocity > 1) // jedzie + fTachoVelocityJump = fTachoVelocity + (2.0 - LocalRandom(3) + LocalRandom(3)) * 0.5; + else + fTachoVelocityJump = 0; // stoi fTachoTimer = ff; // juz zmienil } } @@ -7436,20 +7429,6 @@ bool TTrain::Update( double const Deltatime ) btLampkaPoslizg.Turn( false ); } - // Lampka pracujacej sprezacki - if (mvControlled->CompressorFlag || mvOccupied->CompressorFlag) - btCompressors.Turn(true); - else - btCompressors.Turn(false); - - // Lampka aktywowanej kabiny - if (mvControlled->CabActive != 0) { - btCabActived.Turn(true); - } - else { - btCabActived.Turn(false); - } - if( true == lowvoltagepower ) { // McZapkie-141102: SHP i czuwak, TODO: sygnalizacja kabinowa if( mvOccupied->SecuritySystem.is_vigilance_blinking() ) { @@ -10162,9 +10141,7 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-universal6:", btUniversals[ 6 ] }, { "i-universal7:", btUniversals[ 7 ] }, { "i-universal8:", btUniversals[ 8 ] }, - { "i-universal9:", btUniversals[ 9 ] }, - { "i-cabactived:", btCabActived }, - {"i-compressorany:", btCompressors } + { "i-universal9:", btUniversals[ 9 ] } }; { auto lookup = lights.find( Label ); diff --git a/Train.h b/Train.h index 5bb58d07..9549902f 100644 --- a/Train.h +++ b/Train.h @@ -774,8 +774,6 @@ public: // reszta może by?publiczna TButton btLampkaRearRightLight; TButton btLampkaRearLeftEndLight; TButton btLampkaRearRightEndLight; - TButton btCabActived; - TButton btCompressors; // lampka pracy jakiejkolwiek sprezarki // other TButton btLampkaMalfunction; TButton btLampkaMalfunctionB; From e8c4a019a0700e1ed42b6264ae23f1713aa0eee8 Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 7 Jan 2025 12:21:21 +0100 Subject: [PATCH 52/88] Add aklvents button --- Train.cpp | 6 ++++++ Train.h | 1 + 2 files changed, 7 insertions(+) diff --git a/Train.cpp b/Train.cpp index f460a309..2dfdf5d8 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7388,6 +7388,11 @@ bool TTrain::Update( double const Deltatime ) btCabActived.Turn(false); } + if (mvControlled->Battery && mvControlled->CabActive == 0) + btAKLVents.Turn(true); + else + btAKLVents.Turn(true); + if( true == lowvoltagepower ) { // McZapkie-141102: SHP i czuwak, TODO: sygnalizacja kabinowa if( mvOccupied->SecuritySystem.is_vigilance_blinking() ) { @@ -10092,6 +10097,7 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-universal8:", btUniversals[ 8 ] }, { "i-universal9:", btUniversals[ 9 ] }, { "i-cabactived:", btCabActived }, + {"i-aklvents:", btAKLVents}, {"i-compressorany:", btCompressors } }; { diff --git a/Train.h b/Train.h index 4e045600..f5c7f10e 100644 --- a/Train.h +++ b/Train.h @@ -772,6 +772,7 @@ public: // reszta może by?publiczna TButton btLampkaRearLeftEndLight; TButton btLampkaRearRightEndLight; TButton btCabActived; + TButton btAKLVents; TButton btCompressors; // lampka pracy jakiejkolwiek sprezarki // other TButton btLampkaMalfunction; From ce57929209776fb1718ecc79005c0ccc38fe58bf Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 7 Jan 2025 14:59:12 +0100 Subject: [PATCH 53/88] Fix 4-step switch animation --- McZapkie/Mover.cpp | 2 +- Train.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 57fe8cf3..c3bd3883 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -9945,7 +9945,7 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) result = false; if (!modernContainOffPos) - modernDimmerState = 1; + modernDimmerState = 2; // jak nie ma opcji wylaczonej to niech sie odpali normalnie if (!enableModernDimmer) { modernDimmerState = 2; diff --git a/Train.cpp b/Train.cpp index d99b11b1..6dbc871e 100644 --- a/Train.cpp +++ b/Train.cpp @@ -9717,9 +9717,10 @@ void TTrain::set_cab_controls( int const Cab ) { } if (ggModernLightDimSw.SubModel != nullptr) { - ggModernLightDimSw.PutValue( - mvOccupied->modernDimmerState - ); + if (mvOccupied->modernContainOffPos) + ggModernLightDimSw.PutValue(mvOccupied->modernDimmerState); + else + ggModernLightDimSw.PutValue(mvOccupied->modernDimmerState - 1); } // motor connectors From 831e5d9f37ac61257fc8bc0165b5608cf0263cb3 Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 7 Jan 2025 22:06:54 +0100 Subject: [PATCH 54/88] Fix turbo sound --- DynObj.cpp | 2 +- McZapkie/Mover.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 46d099b5..2c547616 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -7924,7 +7924,7 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub // youBy - przenioslem, bo diesel tez moze miec turbo if( Vehicle.TurboTest > 0 ) { // udawanie turbo: - auto const pitch_diesel { Vehicle.EngineType == TEngineType::DieselEngine ? Vehicle.enrot / Vehicle.dizel_nmax * Vehicle.dizel_fill : 1 }; + auto const pitch_diesel{(Vehicle.EngineType == TEngineType::DieselEngine || Vehicle.EngineType == TEngineType::DieselElectric) ? Vehicle.enrot / Vehicle.dizel_nmax * Vehicle.dizel_fill : 1}; auto const goalpitch { std::max( 0.025, ( /*engine_volume **/ pitch_diesel + engine_turbo.m_frequencyoffset ) * engine_turbo.m_frequencyfactor ) }; auto const goalvolume { ( ( ( Vehicle.MainCtrlPos >= Vehicle.TurboTest ) && ( Vehicle.enrot > 0.1 ) ) ? diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 886209a9..7e163d1f 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -9309,7 +9309,6 @@ bool TMoverParameters::readWWList( std::string const &line ) { SST[ idx ].Pmin = std::sqrt( std::pow( SST[ idx ].Umin, 2 ) / 47.6 ); SST[ idx ].Pmax = std::min( SST[ idx ].Pmax, std::pow( SST[ idx ].Umax, 2 ) / 47.6 ); } - return true; } From 988b3dfe1fd74aad751269500c4bbb9d4988c126 Mon Sep 17 00:00:00 2001 From: Hirek Date: Wed, 8 Jan 2025 02:19:22 +0100 Subject: [PATCH 55/88] Add returnable python commands requires modified abstractscreenrenderer --- PyInt.cpp | 63 ++++++++++ PyInt.h | 10 +- command.cpp | 354 ++++++++++++++++++++++++++++++++++++++++++++++++++++ command.h | 1 + 4 files changed, 427 insertions(+), 1 deletion(-) diff --git a/PyInt.cpp b/PyInt.cpp index 12b79429..9ffaa3bb 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -108,6 +108,32 @@ void render_task::run() { if( outputwidth != nullptr ) { Py_DECREF( outputwidth ); } Py_DECREF( output ); } + + // get commands from renderer + auto *commandsPO = PyObject_CallMethod(m_renderer, "getCommands", nullptr); + std::vector commands = python_external_utils::PyObjectToStringArray(commandsPO); + Py_DECREF(commandsPO); + + // we perform any actions ONLY when there are any commands in buffer + if (!commands.empty()) + { + for (const auto &command : commands) + { + auto it = simulation::commandMap.find(command); + if (it != simulation::commandMap.end()) + { + // command found + command_data cd; + cd.command = it->second; + cd.action = GLFW_PRESS; + simulation::Commands.push(cd, static_cast(command_target::vehicle) | 1); // player train is always 1 + } + else + ErrorLog("Python: Command [" + command + "] not found!"); + } + } + + } void render_task::upload() @@ -474,6 +500,43 @@ python_taskqueue::error() { } } + +std::vector python_external_utils::PyObjectToStringArray(PyObject *pyList) +{ + std::vector result; + + if (!PySequence_Check(pyList)) + { + ErrorLog("Provided PyObject is not a sequence."); + return result; + } + + Py_ssize_t size = PySequence_Size(pyList); + for (Py_ssize_t i = 0; i < size; ++i) + { + PyObject *item = PySequence_GetItem(pyList, i); // Increments reference count + if (item == nullptr) + { + ErrorLog("Failed to get item from sequence."); + return result; + } + + const char *str = PyString_AsString(item); + if (str == nullptr) + { + Py_DECREF(item); + ErrorLog("Failed to convert item to string."); + return result; + } + + result.push_back(std::string(str)); + Py_DECREF(item); // Decrease reference count for the item + } + + return result; +} + + #ifdef __GNUC__ #pragma GCC diagnostic pop #endif diff --git a/PyInt.h b/PyInt.h index 88c8e646..f70b0685 100644 --- a/PyInt.h +++ b/PyInt.h @@ -134,7 +134,8 @@ private: auto fetch_renderer( std::string const Renderer ) -> PyObject *; void run(GLFWwindow *Context, rendertask_sequence &Tasks, uploadtask_sequence &Upload_Tasks, threading::condition_variable &Condition, std::atomic &Exit ); void error(); -// members + + // members PyObject *m_main { nullptr }; PyObject *m_stderr { nullptr }; PyThreadState *m_mainthread{ nullptr }; @@ -147,4 +148,11 @@ private: bool m_initialized { false }; }; +class python_external_utils +{ +public: + static std::vector PyObjectToStringArray(PyObject *pyList); + +}; + #endif diff --git a/command.cpp b/command.cpp index 8e2ea97b..535b0488 100644 --- a/command.cpp +++ b/command.cpp @@ -375,6 +375,360 @@ commanddescription_sequence Commands_descriptions = { { "quitsimulation", command_target::simulation, command_mode::oneoff }, }; +// Maps of command and coresponding strings +std::unordered_map commandMap = { + {"aidriverdisable", user_command::aidriverdisable}, + {"jointcontrollerset", user_command::jointcontrollerset}, + {"mastercontrollerincrease", user_command::mastercontrollerincrease}, + {"mastercontrollerincreasefast", user_command::mastercontrollerincreasefast}, + {"mastercontrollerdecrease", user_command::mastercontrollerdecrease}, + {"mastercontrollerdecreasefast", user_command::mastercontrollerdecreasefast}, + {"mastercontrollerset", user_command::mastercontrollerset}, + {"secondcontrollerincrease", user_command::secondcontrollerincrease}, + {"secondcontrollerincreasefast", user_command::secondcontrollerincreasefast}, + {"secondcontrollerdecrease", user_command::secondcontrollerdecrease}, + {"secondcontrollerdecreasefast", user_command::secondcontrollerdecreasefast}, + {"secondcontrollerset", user_command::secondcontrollerset}, + {"mucurrentindicatorothersourceactivate", user_command::mucurrentindicatorothersourceactivate}, + {"independentbrakeincrease", user_command::independentbrakeincrease}, + {"independentbrakeincreasefast", user_command::independentbrakeincreasefast}, + {"independentbrakedecrease", user_command::independentbrakedecrease}, + {"independentbrakedecreasefast", user_command::independentbrakedecreasefast}, + {"independentbrakeset", user_command::independentbrakeset}, + {"independentbrakebailoff", user_command::independentbrakebailoff}, + {"universalbrakebutton1", user_command::universalbrakebutton1}, + {"universalbrakebutton2", user_command::universalbrakebutton2}, + {"universalbrakebutton3", user_command::universalbrakebutton3}, + {"trainbrakeincrease", user_command::trainbrakeincrease}, + {"trainbrakedecrease", user_command::trainbrakedecrease}, + {"trainbrakeset", user_command::trainbrakeset}, + {"trainbrakecharging", user_command::trainbrakecharging}, + {"trainbrakerelease", user_command::trainbrakerelease}, + {"trainbrakefirstservice", user_command::trainbrakefirstservice}, + {"trainbrakeservice", user_command::trainbrakeservice}, + {"trainbrakefullservice", user_command::trainbrakefullservice}, + {"trainbrakehandleoff", user_command::trainbrakehandleoff}, + {"trainbrakeemergency", user_command::trainbrakeemergency}, + {"trainbrakebasepressureincrease", user_command::trainbrakebasepressureincrease}, + {"trainbrakebasepressuredecrease", user_command::trainbrakebasepressuredecrease}, + {"trainbrakebasepressurereset", user_command::trainbrakebasepressurereset}, + {"trainbrakeoperationtoggle", user_command::trainbrakeoperationtoggle}, + {"manualbrakeincrease", user_command::manualbrakeincrease}, + {"manualbrakedecrease", user_command::manualbrakedecrease}, + {"alarmchaintoggle", user_command::alarmchaintoggle}, + {"alarmchainenable", user_command::alarmchainenable}, + {"alarmchaindisable", user_command::alarmchaindisable}, + {"wheelspinbrakeactivate", user_command::wheelspinbrakeactivate}, + {"sandboxactivate", user_command::sandboxactivate}, + {"autosandboxtoggle", user_command::autosandboxtoggle}, + {"autosandboxactivate", user_command::autosandboxactivate}, + {"autosandboxdeactivate", user_command::autosandboxdeactivate}, + {"reverserincrease", user_command::reverserincrease}, + {"reverserdecrease", user_command::reverserdecrease}, + {"reverserforwardhigh", user_command::reverserforwardhigh}, + {"reverserforward", user_command::reverserforward}, + {"reverserneutral", user_command::reverserneutral}, + {"reverserbackward", user_command::reverserbackward}, + {"waterpumpbreakertoggle", user_command::waterpumpbreakertoggle}, + {"waterpumpbreakerclose", user_command::waterpumpbreakerclose}, + {"waterpumpbreakeropen", user_command::waterpumpbreakeropen}, + {"waterpumptoggle", user_command::waterpumptoggle}, + {"waterpumpenable", user_command::waterpumpenable}, + {"waterpumpdisable", user_command::waterpumpdisable}, + {"waterheaterbreakertoggle", user_command::waterheaterbreakertoggle}, + {"waterheaterbreakerclose", user_command::waterheaterbreakerclose}, + {"waterheaterbreakeropen", user_command::waterheaterbreakeropen}, + {"waterheatertoggle", user_command::waterheatertoggle}, + {"waterheaterenable", user_command::waterheaterenable}, + {"waterheaterdisable", user_command::waterheaterdisable}, + {"watercircuitslinktoggle", user_command::watercircuitslinktoggle}, + {"watercircuitslinkenable", user_command::watercircuitslinkenable}, + {"watercircuitslinkdisable", user_command::watercircuitslinkdisable}, + {"fuelpumptoggle", user_command::fuelpumptoggle}, + {"fuelpumpenable", user_command::fuelpumpenable}, + {"fuelpumpdisable", user_command::fuelpumpdisable}, + {"oilpumptoggle", user_command::oilpumptoggle}, + {"oilpumpenable", user_command::oilpumpenable}, + {"oilpumpdisable", user_command::oilpumpdisable}, + {"linebreakertoggle", user_command::linebreakertoggle}, + {"linebreakeropen", user_command::linebreakeropen}, + {"linebreakerclose", user_command::linebreakerclose}, + {"convertertoggle", user_command::convertertoggle}, + {"converterenable", user_command::converterenable}, + {"converterdisable", user_command::converterdisable}, + {"convertertogglelocal", user_command::convertertogglelocal}, + {"converteroverloadrelayreset", user_command::converteroverloadrelayreset}, + {"compressortoggle", user_command::compressortoggle}, + {"compressorenable", user_command::compressorenable}, + {"compressordisable", user_command::compressordisable}, + {"compressortogglelocal", user_command::compressortogglelocal}, + {"compressorpresetactivatenext", user_command::compressorpresetactivatenext}, + {"compressorpresetactivateprevious", user_command::compressorpresetactivateprevious}, + {"compressorpresetactivatedefault", user_command::compressorpresetactivatedefault}, + {"motoroverloadrelaythresholdtoggle", user_command::motoroverloadrelaythresholdtoggle}, + {"motoroverloadrelaythresholdsetlow", user_command::motoroverloadrelaythresholdsetlow}, + {"motoroverloadrelaythresholdsethigh", user_command::motoroverloadrelaythresholdsethigh}, + {"motoroverloadrelayreset", user_command::motoroverloadrelayreset}, + {"universalrelayreset1", user_command::universalrelayreset1}, + {"universalrelayreset2", user_command::universalrelayreset2}, + {"universalrelayreset3", user_command::universalrelayreset3}, + {"notchingrelaytoggle", user_command::notchingrelaytoggle}, + {"epbrakecontroltoggle", user_command::epbrakecontroltoggle}, + {"epbrakecontrolenable", user_command::epbrakecontrolenable}, + {"epbrakecontroldisable", user_command::epbrakecontroldisable}, + {"trainbrakeoperationmodeincrease", user_command::trainbrakeoperationmodeincrease}, + {"trainbrakeoperationmodedecrease", user_command::trainbrakeoperationmodedecrease}, + {"brakeactingspeedincrease", user_command::brakeactingspeedincrease}, + {"brakeactingspeeddecrease", user_command::brakeactingspeeddecrease}, + {"brakeactingspeedsetcargo", user_command::brakeactingspeedsetcargo}, + {"brakeactingspeedsetpassenger", user_command::brakeactingspeedsetpassenger}, + {"brakeactingspeedsetrapid", user_command::brakeactingspeedsetrapid}, + {"brakeloadcompensationincrease", user_command::brakeloadcompensationincrease}, + {"brakeloadcompensationdecrease", user_command::brakeloadcompensationdecrease}, + {"mubrakingindicatortoggle", user_command::mubrakingindicatortoggle}, + {"alerteracknowledge", user_command::alerteracknowledge}, + {"cabsignalacknowledge", user_command::cabsignalacknowledge}, + {"hornlowactivate", user_command::hornlowactivate}, + {"hornhighactivate", user_command::hornhighactivate}, + {"whistleactivate", user_command::whistleactivate}, + {"radiotoggle", user_command::radiotoggle}, + {"radioenable", user_command::radioenable}, + {"radiodisable", user_command::radiodisable}, + {"radiochannelincrease", user_command::radiochannelincrease}, + {"radiochanneldecrease", user_command::radiochanneldecrease}, + {"radiochannelset", user_command::radiochannelset}, + {"radiostopsend", user_command::radiostopsend}, + {"radiostopenable", user_command::radiostopenable}, + {"radiostopdisable", user_command::radiostopdisable}, + {"radiostoptest", user_command::radiostoptest}, + {"radiocall3send", user_command::radiocall3send}, + {"radiovolumeincrease", user_command::radiovolumeincrease}, + {"radiovolumedecrease", user_command::radiovolumedecrease}, + {"radiovolumeset", user_command::radiovolumeset}, + {"cabchangeforward", user_command::cabchangeforward}, + {"cabchangebackward", user_command::cabchangebackward}, + {"viewturn", user_command::viewturn}, + {"movehorizontal", user_command::movehorizontal}, + {"movehorizontalfast", user_command::movehorizontalfast}, + {"movevertical", user_command::movevertical}, + {"moveverticalfast", user_command::moveverticalfast}, + {"moveleft", user_command::moveleft}, + {"moveright", user_command::moveright}, + {"moveforward", user_command::moveforward}, + {"moveback", user_command::moveback}, + {"moveup", user_command::moveup}, + {"movedown", user_command::movedown}, + {"nearestcarcouplingincrease", user_command::nearestcarcouplingincrease}, + {"nearestcarcouplingdisconnect", user_command::nearestcarcouplingdisconnect}, + {"nearestcarcoupleradapterattach", user_command::nearestcarcoupleradapterattach}, + {"nearestcarcoupleradapterremove", user_command::nearestcarcoupleradapterremove}, + {"occupiedcarcouplingdisconnect", user_command::occupiedcarcouplingdisconnect}, + {"occupiedcarcouplingdisconnectback", user_command::occupiedcarcouplingdisconnectback}, + {"doortoggleleft", user_command::doortoggleleft}, + {"doortoggleright", user_command::doortoggleright}, + {"doorpermitleft", user_command::doorpermitleft}, + {"doorpermitright", user_command::doorpermitright}, + {"doorpermitpresetactivatenext", user_command::doorpermitpresetactivatenext}, + {"doorpermitpresetactivateprevious", user_command::doorpermitpresetactivateprevious}, + {"dooropenleft", user_command::dooropenleft}, + {"dooropenright", user_command::dooropenright}, + {"dooropenall", user_command::dooropenall}, + {"doorcloseleft", user_command::doorcloseleft}, + {"doorcloseright", user_command::doorcloseright}, + {"doorcloseall", user_command::doorcloseall}, + {"doorsteptoggle", user_command::doorsteptoggle}, + {"doormodetoggle", user_command::doormodetoggle}, + {"mirrorstoggle", user_command::mirrorstoggle}, + {"departureannounce", user_command::departureannounce}, + {"doorlocktoggle", user_command::doorlocktoggle}, + {"pantographcompressorvalvetoggle", user_command::pantographcompressorvalvetoggle}, + {"pantographcompressorvalveenable", user_command::pantographcompressorvalveenable}, + {"pantographcompressorvalvedisable", user_command::pantographcompressorvalvedisable}, + {"pantographcompressoractivate", user_command::pantographcompressoractivate}, + {"pantographtogglefront", user_command::pantographtogglefront}, + {"pantographtogglerear", user_command::pantographtogglerear}, + {"pantographraisefront", user_command::pantographraisefront}, + {"pantographraiserear", user_command::pantographraiserear}, + {"pantographlowerfront", user_command::pantographlowerfront}, + {"pantographlowerrear", user_command::pantographlowerrear}, + {"pantographlowerall", user_command::pantographlowerall}, + {"pantographselectnext", user_command::pantographselectnext}, + {"pantographselectprevious", user_command::pantographselectprevious}, + {"pantographtoggleselected", user_command::pantographtoggleselected}, + {"pantographraiseselected", user_command::pantographraiseselected}, + {"pantographlowerselected", user_command::pantographlowerselected}, + {"pantographvalvesupdate", user_command::pantographvalvesupdate}, + {"pantographvalvesoff", user_command::pantographvalvesoff}, + {"heatingtoggle", user_command::heatingtoggle}, + {"heatingenable", user_command::heatingenable}, + {"heatingdisable", user_command::heatingdisable}, + {"lightspresetactivatenext", user_command::lightspresetactivatenext}, + {"lightspresetactivateprevious", user_command::lightspresetactivateprevious}, + {"headlighttoggleleft", user_command::headlighttoggleleft}, + {"headlightenableleft", user_command::headlightenableleft}, + {"headlightdisableleft", user_command::headlightdisableleft}, + {"headlighttoggleright", user_command::headlighttoggleright}, + {"headlightenableright", user_command::headlightenableright}, + {"headlightdisableright", user_command::headlightdisableright}, + {"headlighttoggleupper", user_command::headlighttoggleupper}, + {"headlightenableupper", user_command::headlightenableupper}, + {"headlightdisableupper", user_command::headlightdisableupper}, + {"redmarkertoggleleft", user_command::redmarkertoggleleft}, + {"redmarkerenableleft", user_command::redmarkerenableleft}, + {"redmarkerdisableleft", user_command::redmarkerdisableleft}, + {"redmarkertoggleright", user_command::redmarkertoggleright}, + {"redmarkerenableright", user_command::redmarkerenableright}, + {"redmarkerdisableright", user_command::redmarkerdisableright}, + {"headlighttogglerearleft", user_command::headlighttogglerearleft}, + {"headlightenablerearleft", user_command::headlightenablerearleft}, + {"headlightdisablerearleft", user_command::headlightdisablerearleft}, + {"headlighttogglerearright", user_command::headlighttogglerearright}, + {"headlightenablerearright", user_command::headlightenablerearright}, + {"headlightdisablerearright", user_command::headlightdisablerearright}, + {"headlighttogglerearupper", user_command::headlighttogglerearupper}, + {"headlightenablerearupper", user_command::headlightenablerearupper}, + {"headlightdisablerearupper", user_command::headlightdisablerearupper}, + {"redmarkertogglerearleft", user_command::redmarkertogglerearleft}, + {"redmarkerenablerearleft", user_command::redmarkerenablerearleft}, + {"redmarkerdisablerearleft", user_command::redmarkerdisablerearleft}, + {"redmarkertogglerearright", user_command::redmarkertogglerearright}, + {"redmarkerenablerearright", user_command::redmarkerenablerearright}, + {"redmarkerdisablerearright", user_command::redmarkerdisablerearright}, + {"redmarkerstoggle", user_command::redmarkerstoggle}, + {"endsignalstoggle", user_command::endsignalstoggle}, + {"headlightsdimtoggle", user_command::headlightsdimtoggle}, + {"headlightsdimenable", user_command::headlightsdimenable}, + {"headlightsdimdisable", user_command::headlightsdimdisable}, + {"motorconnectorsopen", user_command::motorconnectorsopen}, + {"motorconnectorsclose", user_command::motorconnectorsclose}, + {"motordisconnect", user_command::motordisconnect}, + {"interiorlighttoggle", user_command::interiorlighttoggle}, + {"interiorlightenable", user_command::interiorlightenable}, + {"interiorlightdisable", user_command::interiorlightdisable}, + {"interiorlightdimtoggle", user_command::interiorlightdimtoggle}, + {"interiorlightdimenable", user_command::interiorlightdimenable}, + {"interiorlightdimdisable", user_command::interiorlightdimdisable}, + {"compartmentlightstoggle", user_command::compartmentlightstoggle}, + {"compartmentlightsenable", user_command::compartmentlightsenable}, + {"compartmentlightsdisable", user_command::compartmentlightsdisable}, + {"instrumentlighttoggle", user_command::instrumentlighttoggle}, + {"instrumentlightenable", user_command::instrumentlightenable}, + {"instrumentlightdisable", user_command::instrumentlightdisable}, + {"dashboardlighttoggle", user_command::dashboardlighttoggle}, + {"dashboardlightenable", user_command::dashboardlightenable}, + {"dashboardlightdisable", user_command::dashboardlightdisable}, + {"timetablelighttoggle", user_command::timetablelighttoggle}, + {"timetablelightenable", user_command::timetablelightenable}, + {"timetablelightdisable", user_command::timetablelightdisable}, + {"generictoggle0", user_command::generictoggle0}, + {"generictoggle1", user_command::generictoggle1}, + {"generictoggle2", user_command::generictoggle2}, + {"generictoggle3", user_command::generictoggle3}, + {"generictoggle4", user_command::generictoggle4}, + {"generictoggle5", user_command::generictoggle5}, + {"generictoggle6", user_command::generictoggle6}, + {"generictoggle7", user_command::generictoggle7}, + {"generictoggle8", user_command::generictoggle8}, + {"generictoggle9", user_command::generictoggle9}, + {"batterytoggle", user_command::batterytoggle}, + {"batteryenable", user_command::batteryenable}, + {"batterydisable", user_command::batterydisable}, + {"cabactivationtoggle", user_command::cabactivationtoggle}, + {"cabactivationenable", user_command::cabactivationenable}, + {"cabactivationdisable", user_command::cabactivationdisable}, + {"motorblowerstogglefront", user_command::motorblowerstogglefront}, + {"motorblowerstogglerear", user_command::motorblowerstogglerear}, + {"motorblowersdisableall", user_command::motorblowersdisableall}, + {"coolingfanstoggle", user_command::coolingfanstoggle}, + {"tempomattoggle", user_command::tempomattoggle}, + {"springbraketoggle", user_command::springbraketoggle}, + {"springbrakeenable", user_command::springbrakeenable}, + {"springbrakedisable", user_command::springbrakedisable}, + {"springbrakeshutofftoggle", user_command::springbrakeshutofftoggle}, + {"springbrakeshutoffenable", user_command::springbrakeshutoffenable}, + {"springbrakeshutoffdisable", user_command::springbrakeshutoffdisable}, + {"springbrakerelease", user_command::springbrakerelease}, + {"distancecounteractivate", user_command::distancecounteractivate}, + {"speedcontrolincrease", user_command::speedcontrolincrease}, + {"speedcontroldecrease", user_command::speedcontroldecrease}, + {"speedcontrolpowerincrease", user_command::speedcontrolpowerincrease}, + {"speedcontrolpowerdecrease", user_command::speedcontrolpowerdecrease}, + {"speedcontrolbutton0", user_command::speedcontrolbutton0}, + {"speedcontrolbutton1", user_command::speedcontrolbutton1}, + {"speedcontrolbutton2", user_command::speedcontrolbutton2}, + {"speedcontrolbutton3", user_command::speedcontrolbutton3}, + {"speedcontrolbutton4", user_command::speedcontrolbutton4}, + {"speedcontrolbutton5", user_command::speedcontrolbutton5}, + {"speedcontrolbutton6", user_command::speedcontrolbutton6}, + {"speedcontrolbutton7", user_command::speedcontrolbutton7}, + {"speedcontrolbutton8", user_command::speedcontrolbutton8}, + {"speedcontrolbutton9", user_command::speedcontrolbutton9}, + {"inverterenable1", user_command::inverterenable1}, + {"inverterenable2", user_command::inverterenable2}, + {"inverterenable3", user_command::inverterenable3}, + {"inverterenable4", user_command::inverterenable4}, + {"inverterenable5", user_command::inverterenable5}, + {"inverterenable6", user_command::inverterenable6}, + {"inverterenable7", user_command::inverterenable7}, + {"inverterenable8", user_command::inverterenable8}, + {"inverterenable9", user_command::inverterenable9}, + {"inverterenable10", user_command::inverterenable10}, + {"inverterenable11", user_command::inverterenable11}, + {"inverterenable12", user_command::inverterenable12}, + {"inverterdisable1", user_command::inverterdisable1}, + {"inverterdisable2", user_command::inverterdisable2}, + {"inverterdisable3", user_command::inverterdisable3}, + {"inverterdisable4", user_command::inverterdisable4}, + {"inverterdisable5", user_command::inverterdisable5}, + {"inverterdisable6", user_command::inverterdisable6}, + {"inverterdisable7", user_command::inverterdisable7}, + {"inverterdisable8", user_command::inverterdisable8}, + {"inverterdisable9", user_command::inverterdisable9}, + {"inverterdisable10", user_command::inverterdisable10}, + {"inverterdisable11", user_command::inverterdisable11}, + {"inverterdisable12", user_command::inverterdisable12}, + {"invertertoggle1", user_command::invertertoggle1}, + {"invertertoggle2", user_command::invertertoggle2}, + {"invertertoggle3", user_command::invertertoggle3}, + {"invertertoggle4", user_command::invertertoggle4}, + {"invertertoggle5", user_command::invertertoggle5}, + {"invertertoggle6", user_command::invertertoggle6}, + {"invertertoggle7", user_command::invertertoggle7}, + {"invertertoggle8", user_command::invertertoggle8}, + {"invertertoggle9", user_command::invertertoggle9}, + {"invertertoggle10", user_command::invertertoggle10}, + {"invertertoggle11", user_command::invertertoggle11}, + {"invertertoggle12", user_command::invertertoggle12}, + {"globalradiostop", user_command::globalradiostop}, + {"timejump", user_command::timejump}, + {"timejumplarge", user_command::timejumplarge}, + {"timejumpsmall", user_command::timejumpsmall}, + {"setdatetime", user_command::setdatetime}, + {"setweather", user_command::setweather}, + {"settemperature", user_command::settemperature}, + {"vehiclemoveforwards", user_command::vehiclemoveforwards}, + {"vehiclemovebackwards", user_command::vehiclemovebackwards}, + {"vehicleboost", user_command::vehicleboost}, + {"debugtoggle", user_command::debugtoggle}, + {"focuspauseset", user_command::focuspauseset}, + {"pausetoggle", user_command::pausetoggle}, + {"entervehicle", user_command::entervehicle}, + {"resetconsist", user_command::resetconsist}, + {"fillcompressor", user_command::fillcompressor}, + {"consistreleaser", user_command::consistreleaser}, + {"queueevent", user_command::queueevent}, + {"setlight", user_command::setlight}, + {"insertmodel", user_command::insertmodel}, + {"deletemodel", user_command::deletemodel}, + {"dynamicmove", user_command::dynamicmove}, + {"consistteleport", user_command::consistteleport}, + {"pullalarmchain", user_command::pullalarmchain}, + {"sendaicommand", user_command::sendaicommand}, + {"spawntrainset", user_command::spawntrainset}, + {"destroytrainset", user_command::destroytrainset}, + {"quitsimulation", user_command::quitsimulation}, + {"none", user_command::none}}; + } // simulation void command_queue::update() diff --git a/command.h b/command.h index 8fb5a87d..cf7da550 100644 --- a/command.h +++ b/command.h @@ -483,6 +483,7 @@ extern command_queue Commands; // TODO: add name to command map, and wrap these two into helper object extern commanddescription_sequence Commands_descriptions; +extern std::unordered_map commandMap; } // command_relay: composite class component, passes specified command to appropriate command stack From a46faf6ff9f15ba83b9a4189e73ea9a44bddba7e Mon Sep 17 00:00:00 2001 From: Hirek Date: Wed, 8 Jan 2025 16:28:50 +0100 Subject: [PATCH 56/88] Tweak python command return logic to prevent crashing simulator --- PyInt.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/PyInt.cpp b/PyInt.cpp index 9ffaa3bb..8798649d 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -504,11 +504,11 @@ python_taskqueue::error() { std::vector python_external_utils::PyObjectToStringArray(PyObject *pyList) { std::vector result; - + std::vector emptyIfError = {}; if (!PySequence_Check(pyList)) { - ErrorLog("Provided PyObject is not a sequence."); - return result; + ErrorLog("Python: Failed to convert PyObject -> vector"); + return emptyIfError; } Py_ssize_t size = PySequence_Size(pyList); @@ -517,16 +517,16 @@ std::vector python_external_utils::PyObjectToStringArray(PyObject * PyObject *item = PySequence_GetItem(pyList, i); // Increments reference count if (item == nullptr) { - ErrorLog("Failed to get item from sequence."); - return result; + ErrorLog("Python: Failed to get item from sequence."); + return emptyIfError; } const char *str = PyString_AsString(item); if (str == nullptr) { Py_DECREF(item); - ErrorLog("Failed to convert item to string."); - return result; + ErrorLog("Python: Failed to convert item to string."); + return emptyIfError; } result.push_back(std::string(str)); From 092b0ff168a4716a6f932cae0eb00880d47ce5b2 Mon Sep 17 00:00:00 2001 From: Hirek Date: Thu, 9 Jan 2025 15:10:16 +0100 Subject: [PATCH 57/88] Fix AI drivers going with dark lights --- Driver.cpp | 3 +++ DynObj.cpp | 70 +++++++++++++++++++++++++++++------------------------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 1269bcf3..e350979f 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -5704,6 +5704,7 @@ void TController::TakeControl( bool const Aidriver, bool const Forcevehiclecheck { // teraz AI prowadzi AIControllFlag = AIdriver; pVehicle->Controller = AIdriver; + control_lights(); // reinicjalizacja swiatel mvOccupied->CabActivisation(true); iDirection = 0; // kierunek jazdy trzeba dopiero zgadnąć TableClear(); // ponowne utworzenie tabelki, bo człowiek mógł pojechać niezgodnie z sygnałami @@ -5740,6 +5741,8 @@ void TController::TakeControl( bool const Aidriver, bool const Forcevehiclecheck } else { // a teraz użytkownik + if (!is_train() || !is_car()) // gasimy swiatla jesli przejmujemy odstawione + pVehicle->RaLightsSet(0, 0); AIControllFlag = Humandriver; pVehicle->Controller = Humandriver; if( eAction == TAction::actSleep ) { diff --git a/DynObj.cpp b/DynObj.cpp index 6f36df0a..763cae42 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -7095,39 +7095,43 @@ void TDynamicObject::RaLightsSet(int head, int rear) MoverParameters->iLights[ vehicleend ] = ( head & iInventory[ vehicleend ] ); bool tLeft = MoverParameters->iLights[vehicleend] & (light::auxiliary_left | light::headlight_left); // roboczo czy jakiekolwiek swiatlo z lewej jest zapalone bool tRight = MoverParameters->iLights[vehicleend] & (light::auxiliary_right | light::headlight_right); // a tu z prawej - switch (MoverParameters->modernDimmerState) - { - case 0: - // wylaczone - MoverParameters->iLights[vehicleend] &= 0 | light::rearendsignals; // zostawiamy tylko tabliczki jesli sa - HighBeamLights = false; - DimHeadlights = false; - break; - case 1: - // przyciemnione normalne - DimHeadlights = true; // odpalamy przyciemnienie normalnych reflektorow - HighBeamLights = false; - break; - case 3: - // dlugie przyciemnione - DimHeadlights = true; - HighBeamLights = true; - MoverParameters->iLights[vehicleend] &= light::headlight_upper | light::rearendsignals | light::redmarker_left | light::redmarker_right | light::rearendsignals; // nie ruszamy gornych i koncowek - MoverParameters->iLights[vehicleend] |= tLeft ? light::highbeamlight_left : 0; // jesli swiatlo z lewej zapalone to odpal dlugie - MoverParameters->iLights[vehicleend] |= tRight ? light::highbeamlight_right : 0; // a tu z prawej - break; - case 4: - // zwykle dlugie - DimHeadlights = false; - HighBeamLights = true; - MoverParameters->iLights[vehicleend] &= light::headlight_upper | light::rearendsignals | light::redmarker_left | light::redmarker_right | light::rearendsignals; // nie ruszamy gornych i koncowek - MoverParameters->iLights[vehicleend] |= tLeft ? light::highbeamlight_left : 0; // jesli swiatlo z lewej zapalone to odpal dlugie - MoverParameters->iLights[vehicleend] |= tRight ? light::highbeamlight_right : 0; // a tu z prawej - break; - default: // to case 2 - zwykle - DimHeadlights = false; - HighBeamLights = false; - break; + if (Controller == Humandriver) { + switch (MoverParameters->modernDimmerState) + { + case 0: + // wylaczone + MoverParameters->iLights[vehicleend] &= 0 | light::rearendsignals; // zostawiamy tylko tabliczki jesli sa + HighBeamLights = false; + DimHeadlights = false; + break; + case 1: + // przyciemnione normalne + DimHeadlights = true; // odpalamy przyciemnienie normalnych reflektorow + HighBeamLights = false; + break; + case 3: + // dlugie przyciemnione + DimHeadlights = true; + HighBeamLights = true; + MoverParameters->iLights[vehicleend] &= + light::headlight_upper | light::rearendsignals | light::redmarker_left | light::redmarker_right | light::rearendsignals; // nie ruszamy gornych i koncowek + MoverParameters->iLights[vehicleend] |= tLeft ? light::highbeamlight_left : 0; // jesli swiatlo z lewej zapalone to odpal dlugie + MoverParameters->iLights[vehicleend] |= tRight ? light::highbeamlight_right : 0; // a tu z prawej + break; + case 4: + // zwykle dlugie + DimHeadlights = false; + HighBeamLights = true; + MoverParameters->iLights[vehicleend] &= + light::headlight_upper | light::rearendsignals | light::redmarker_left | light::redmarker_right | light::rearendsignals; // nie ruszamy gornych i koncowek + MoverParameters->iLights[vehicleend] |= tLeft ? light::highbeamlight_left : 0; // jesli swiatlo z lewej zapalone to odpal dlugie + MoverParameters->iLights[vehicleend] |= tRight ? light::highbeamlight_right : 0; // a tu z prawej + break; + default: // to case 2 - zwykle + DimHeadlights = false; + HighBeamLights = false; + break; + } } } From 76b080deebe4dcc138a005d195c1b52baeb7f88f Mon Sep 17 00:00:00 2001 From: Hirek Date: Thu, 9 Jan 2025 16:07:57 +0100 Subject: [PATCH 58/88] Final fix for missing getCommands function inside abstractscreenrenderer --- PyInt.cpp | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/PyInt.cpp b/PyInt.cpp index 8798649d..613ac4f2 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -111,27 +111,33 @@ void render_task::run() { // get commands from renderer auto *commandsPO = PyObject_CallMethod(m_renderer, "getCommands", nullptr); - std::vector commands = python_external_utils::PyObjectToStringArray(commandsPO); - Py_DECREF(commandsPO); + if (commandsPO != nullptr) + { + std::vector commands = python_external_utils::PyObjectToStringArray(commandsPO); - // we perform any actions ONLY when there are any commands in buffer - if (!commands.empty()) - { - for (const auto &command : commands) + Py_DECREF(commandsPO); + // we perform any actions ONLY when there are any commands in buffer + if (!commands.empty()) { - auto it = simulation::commandMap.find(command); - if (it != simulation::commandMap.end()) + for (const auto &command : commands) { - // command found - command_data cd; - cd.command = it->second; - cd.action = GLFW_PRESS; - simulation::Commands.push(cd, static_cast(command_target::vehicle) | 1); // player train is always 1 + auto it = simulation::commandMap.find(command); + if (it != simulation::commandMap.end()) + { + // command found + command_data cd; + cd.command = it->second; + cd.action = GLFW_PRESS; + simulation::Commands.push(cd, static_cast(command_target::vehicle) | 1); // player train is always 1 + } + else + ErrorLog("Python: Command [" + command + "] not found!"); } - else - ErrorLog("Python: Command [" + command + "] not found!"); - } - } + } + + } + + } From 918cc35489e441ad729937419696f067e29047b0 Mon Sep 17 00:00:00 2001 From: Hirek Date: Fri, 10 Jan 2025 16:50:10 +0100 Subject: [PATCH 59/88] Fix aklvents logic --- Train.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Train.cpp b/Train.cpp index 2dfdf5d8..c7264d56 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7388,10 +7388,10 @@ bool TTrain::Update( double const Deltatime ) btCabActived.Turn(false); } - if (mvControlled->Battery && mvControlled->CabActive == 0) + if (mvControlled->Battery && mvControlled->CabActive != 0) btAKLVents.Turn(true); else - btAKLVents.Turn(true); + btAKLVents.Turn(false); if( true == lowvoltagepower ) { // McZapkie-141102: SHP i czuwak, TODO: sygnalizacja kabinowa From d335b3a3cbae458dea767e5d35dad6b5c825d882 Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 14 Jan 2025 20:56:21 +0100 Subject: [PATCH 60/88] Add models seasonal variants --- AnimModel.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ AnimModel.h | 19 ++++++++++++------ 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index 557d85e0..39d1dbce 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -318,6 +318,10 @@ bool TAnimModel::Load(cParser *parser, bool ter) LightsOff[5] = pModel->GetFromName("Light_Off05"); LightsOff[6] = pModel->GetFromName("Light_Off06"); LightsOff[7] = pModel->GetFromName("Light_Off07"); + sm_winter_variant = pModel->GetFromName("winter_variant"); + sm_spring_variant = pModel->GetFromName("spring_variant"); + sm_summer_variant = pModel->GetFromName("summer_variant"); + sm_autumn_variant = pModel->GetFromName("autumn_variant"); } for (int i = 0; i < iMaxNumLights; ++i) if (LightsOn[i] || LightsOff[i]) // Ra: zlikwidowałem wymóg istnienia obu @@ -494,9 +498,59 @@ void TAnimModel::RaAnimate( unsigned int const Framestamp ) { m_framestamp = Framestamp; } +// aktualizujemy submodele w zaleznosci od aktualnej porty roku +void TAnimModel::on_season_update() { + if (Global.Season == "winter:") // pokazujemy wariant zimowy + { + if (this->sm_winter_variant != nullptr) + this->sm_winter_variant->SetVisibilityLevel(1.f, true, false); + if (this->sm_spring_variant != nullptr) + this->sm_spring_variant->SetVisibilityLevel(0.f, true, false); + if (this->sm_summer_variant != nullptr) + this->sm_summer_variant->SetVisibilityLevel(0.f, true, false); + if (this->sm_autumn_variant != nullptr) + this->sm_autumn_variant->SetVisibilityLevel(0.f, true, false); + } + else if (Global.Season == "spring:") // pokazujemy wariant wiosenny + { + if (this->sm_winter_variant != nullptr) + this->sm_winter_variant->SetVisibilityLevel(0.f, true, false); + if (this->sm_spring_variant != nullptr) + this->sm_spring_variant->SetVisibilityLevel(1.f, true, false); + if (this->sm_summer_variant != nullptr) + this->sm_summer_variant->SetVisibilityLevel(0.f, true, false); + if (this->sm_autumn_variant != nullptr) + this->sm_autumn_variant->SetVisibilityLevel(0.f, true, false); + } + else if (Global.Season == "summer:") // pokazujemy wariant letni + { + if (this->sm_winter_variant != nullptr) + this->sm_winter_variant->SetVisibilityLevel(0.f, true, false); + if (this->sm_spring_variant != nullptr) + this->sm_spring_variant->SetVisibilityLevel(0.f, true, false); + if (this->sm_summer_variant != nullptr) + this->sm_summer_variant->SetVisibilityLevel(1.f, true, false); + if (this->sm_autumn_variant != nullptr) + this->sm_autumn_variant->SetVisibilityLevel(0.f, true, false); + } + else if (Global.Season == "autumn:") // pokazujemy wariant jesienny + { + if (this->sm_winter_variant != nullptr) + this->sm_winter_variant->SetVisibilityLevel(0.f, true, false); + if (this->sm_spring_variant != nullptr) + this->sm_spring_variant->SetVisibilityLevel(0.f, true, false); + if (this->sm_summer_variant != nullptr) + this->sm_summer_variant->SetVisibilityLevel(0.f, true, false); + if (this->sm_autumn_variant != nullptr) + this->sm_autumn_variant->SetVisibilityLevel(1.f, true, false); + } +} + void TAnimModel::RaPrepare() { // ustawia światła i animacje we wzorcu modelu przed renderowaniem egzemplarza bool state; // stan światła + if (Global.UpdateMaterials) + on_season_update(); for (int i = 0; i < iNumLights; ++i) { auto const lightmode { static_cast( std::abs( lsLights[ i ] ) ) }; diff --git a/AnimModel.h b/AnimModel.h index d19e24e2..b9f92d09 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -25,12 +25,14 @@ const int iMaxNumLights = 8; float const DefaultDarkThresholdLevel { 0.325f }; // typy stanu świateł -enum TLightState { - ls_Off = 0, // zgaszone - ls_On = 1, // zapalone - ls_Blink = 2, // migające - ls_Dark = 3, // Ra: zapalajce się automatycznie, gdy zrobi się ciemno - ls_Home = 4 // like ls_dark but off late at night +enum TLightState +{ + ls_Off = 0, // zgaszone + ls_On = 1, // zapalone + ls_Blink = 2, // migające + ls_Dark = 3, // Ra: zapalajce się automatycznie, gdy zrobi się ciemno + ls_Home = 4, // like ls_dark but off late at night + ls_winter = 5 // turned on when its winter }; class TAnimVocaloidFrame @@ -122,6 +124,7 @@ public: int TerrainCount(); TSubModel * TerrainSquare(int n); int Flags(); + void on_season_update(); inline material_data const * Material() const { @@ -171,6 +174,10 @@ private: int iNumLights { 0 }; std::array LightsOn {}; // Ra: te wskaźniki powinny być w ramach TModel3d std::array LightsOff {}; + TSubModel *sm_winter_variant {}; // submodel zimowego wariantu + TSubModel *sm_spring_variant {}; // submodel wiosennego wariantu + TSubModel *sm_summer_variant {}; // submodel letniego wariantu + TSubModel *sm_autumn_variant {}; // submodel jesiennego wariantu std::array lsLights {}; // ls_Off std::array m_lightcolors; // -1 in constructor std::array m_lighttimers {}; From 63c8a79023b7aab7cb7e241bd51b49f04442d4dc Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 28 Jan 2025 00:43:57 +0100 Subject: [PATCH 61/88] Floating point export fix for models --- AnimModel.cpp | 6 +++--- scenenode.h | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index 557d85e0..787dfe56 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -656,9 +656,9 @@ TAnimModel::export_as_text_( std::ostream &Output ) const { // header Output << "model "; // location and rotation - Output - << location().x << ' ' - << location().y << ' ' + Output << std::fixed << std::setprecision(3) // ustawienie dokładnie 3 cyfr po przecinku + << location().x << ' ' + << location().y << ' ' << location().z << ' '; Output << "0 " ; diff --git a/scenenode.h b/scenenode.h index 760eab32..a63bd2a0 100644 --- a/scenenode.h +++ b/scenenode.h @@ -47,8 +47,7 @@ operator!=( lighting_data const &Left, lighting_data const &Right ) { namespace scene { struct bounding_area { - - glm::dvec3 center; // mid point of the rectangle + glm::highp_dvec3 center; // mid point of the rectangle float radius { -1.0f }; // radius of the bounding sphere bounding_area() = default; @@ -94,7 +93,7 @@ public: material_handle material { null_handle }; lighting_data lighting; // geometry data - glm::dvec3 origin; // world position of the relative coordinate system origin + glm::highp_dvec3 origin; // world position of the relative coordinate system origin gfx::geometry_handle geometry { 0, 0 }; // relative origin-centered chunk of geometry held by gfx renderer std::vector vertices; // world space source data of the geometry gfx::userdata_array userdata; From ecebc70814b633ee291a385c952a8bb55f72b89a Mon Sep 17 00:00:00 2001 From: Jano211 <107213310+Jano211@users.noreply.github.com> Date: Mon, 10 Feb 2025 23:10:51 +0100 Subject: [PATCH 62/88] New functions in scene editor --- editormode.cpp | 40 +++++++++++++++++++++-------- editoruilayer.cpp | 16 ++++++++++++ editoruilayer.h | 7 ++++++ editoruipanels.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++-- editoruipanels.h | 32 +++++++++++++++++++++++ 5 files changed, 146 insertions(+), 12 deletions(-) diff --git a/editormode.cpp b/editormode.cpp index 11a61890..f2cd69bc 100644 --- a/editormode.cpp +++ b/editormode.cpp @@ -206,7 +206,7 @@ editor_mode::on_key( int const Key, int const Scancode, int const Action, int co m_node = nullptr; m_dragging = false; - Application.set_cursor( GLFW_CURSOR_NORMAL ); + //Application.set_cursor( GLFW_CURSOR_NORMAL ); static_cast( m_userinterface.get() )->set_node(nullptr); simulation::State.delete_model(model); @@ -318,10 +318,14 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods if( Action == GLFW_PRESS ) { // left button press auto const mode = static_cast( m_userinterface.get() )->mode(); + auto const rotation_mode = static_cast( m_userinterface.get() )->rot_mode(); + auto const fixed_rotation_value = + static_cast(m_userinterface.get())->rot_val(); m_node = nullptr; - GfxRenderer->Pick_Node_Callback([this, mode,Action,Button](scene::basic_node *node) { + GfxRenderer->Pick_Node_Callback([this, mode, rotation_mode, fixed_rotation_value, + Action, Button](scene::basic_node *node) { editor_ui *ui = static_cast(m_userinterface.get()); if (mode == nodebank_panel::MODIFY) { @@ -329,10 +333,10 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods return; m_node = node; - if( m_node ) - Application.set_cursor( GLFW_CURSOR_DISABLED ); - else - m_dragging = false; + //if( m_node ) + //Application.set_cursor( GLFW_CURSOR_DISABLED ); + //else + //m_dragging = false; ui->set_node(m_node); } else if (mode == nodebank_panel::COPY) { @@ -360,8 +364,24 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods if (!m_dragging) return; - m_node = cloned; - Application.set_cursor( GLFW_CURSOR_DISABLED ); + + m_node = cloned; + + if (rotation_mode == functions_panel::RANDOM) + { + auto const rotation{glm::vec3{0, LocalRandom(0.0, 360.0), 0}}; + + m_editor.rotate(m_node, rotation, 1); + } + else if (rotation_mode == functions_panel::FIXED) + { + + auto const rotation{glm::vec3{0, fixed_rotation_value, 0}}; + + m_editor.rotate(m_node, rotation, 0); + + } + //Application.set_cursor( GLFW_CURSOR_DISABLED ); ui->set_node( m_node ); } }); @@ -370,8 +390,8 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods } else { // left button release - if( m_node ) - Application.set_cursor( GLFW_CURSOR_NORMAL ); + //if( m_node ) + //Application.set_cursor( GLFW_CURSOR_NORMAL ); m_dragging = false; // prime history stack for another snapshot m_takesnapshot = true; diff --git a/editoruilayer.cpp b/editoruilayer.cpp index 9ea162bf..0af6d525 100644 --- a/editoruilayer.cpp +++ b/editoruilayer.cpp @@ -21,6 +21,7 @@ editor_ui::editor_ui() { add_external_panel( &m_itempropertiespanel ); add_external_panel( &m_nodebankpanel ); + add_external_panel( &m_functionspanel ); } // updates state of UI elements @@ -41,6 +42,7 @@ editor_ui::update() { ui_layer::update(); m_itempropertiespanel.update( m_node ); + m_functionspanel.update( m_node ); } void @@ -63,3 +65,17 @@ nodebank_panel::edit_mode editor_ui::mode() { return m_nodebankpanel.mode; } + +functions_panel::rotation_mode +editor_ui::rot_mode() { + return m_functionspanel.rot_mode; +} +float +editor_ui::rot_val() { + return m_functionspanel.rot_value; +} +bool +editor_ui::rot_from_last() +{ + return m_functionspanel.rot_from_last; +} \ No newline at end of file diff --git a/editoruilayer.h b/editoruilayer.h index 893a03e9..2330d533 100644 --- a/editoruilayer.h +++ b/editoruilayer.h @@ -31,6 +31,12 @@ public: set_node( scene::basic_node * Node ); void add_node_template(const std::string &desc); + float + rot_val(); + bool + rot_from_last(); + functions_panel::rotation_mode + rot_mode(); const std::string * get_active_node_template(); nodebank_panel::edit_mode @@ -39,6 +45,7 @@ public: private: // members itemproperties_panel m_itempropertiespanel { "Node Properties", true }; + functions_panel m_functionspanel { "Functions", true }; nodebank_panel m_nodebankpanel{ "Node Bank", true }; scene::basic_node * m_node { nullptr }; // currently bound scene node, if any }; diff --git a/editoruipanels.cpp b/editoruipanels.cpp index 8b15bef2..6b4d4759 100644 --- a/editoruipanels.cpp +++ b/editoruipanels.cpp @@ -420,8 +420,6 @@ nodebank_panel::render() { ImGui::SameLine(); ImGui::RadioButton("Insert from bank", (int*)&mode, ADD); ImGui::SameLine(); - ImGui::RadioButton("Brush", (int*)&mode, BRUSH); - ImGui::SameLine(); ImGui::RadioButton( "Copy to bank", (int*)&mode, COPY ); ImGui::SameLine(); if (ImGui::Button("Reload Nodebank")) @@ -493,3 +491,64 @@ nodebank_panel::generate_node_label( std::string Input ) const { model : model + " (" + texture + ")" ); } + +void functions_panel::update(scene::basic_node const *Node) +{ + m_node = Node; + + if (false == is_open) + { + return; + } + + text_lines.clear(); + m_grouplines.clear(); + + std::string textline; + + // scenario inspector + auto const *node{Node}; + auto const &camera{Global.pCamera}; + + + + +} + +void +functions_panel::render() { + + if( false == is_open ) { return; } + + auto flags = + ImGuiWindowFlags_NoFocusOnAppearing + | ImGuiWindowFlags_NoCollapse + | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 ); + + if( size.x > 0 ) { + ImGui::SetNextWindowSize( ImVec2S( size.x, size.y ) ); + } + if( size_min.x > 0 ) { + ImGui::SetNextWindowSizeConstraints( ImVec2S( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) ); + } + auto const panelname { ( + title.empty() ? + m_name : + title ) + + "###" + m_name }; + if( true == ImGui::Begin( panelname.c_str(), nullptr, flags ) ) { + // header section + + ImGui::RadioButton("Random rotation", (int *)&rot_mode, RANDOM); + ImGui::RadioButton("Fixed rotation", (int *)&rot_mode, FIXED); + if(rot_mode == FIXED){ + //ImGui::Checkbox("Get rotation from last object", &rot_from_last); + ImGui::SliderFloat("Rotation Value", &rot_value, 0.0f, 360.0f, "%.1f"); + }; + ImGui::RadioButton("Default rotation", (int *)&rot_mode, DEFAULT); + for( auto const &line : text_lines ) { + ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); + } + } + ImGui::End(); +} \ No newline at end of file diff --git a/editoruipanels.h b/editoruipanels.h index 22fc554e..5aed376f 100644 --- a/editoruipanels.h +++ b/editoruipanels.h @@ -73,3 +73,35 @@ private: char m_nodesearch[ 128 ]; std::shared_ptr m_selectedtemplate; }; + +class functions_panel : public ui_panel +{ + + public: + enum rotation_mode + { + RANDOM, + FIXED, + DEFAULT + }; + rotation_mode rot_mode = DEFAULT; + + float rot_value = 0.0f; + bool rot_from_last = false; + + functions_panel(std::string const &Name, bool const Isopen) : ui_panel(Name, Isopen) {} + + void update(scene::basic_node const *Node); + void render() override; + + + private: + // methods + + + // members + scene::basic_node const *m_node{nullptr}; // scene node bound to the panel + scene::group_handle m_grouphandle{null_handle}; // scene group bound to the panel + std::string m_groupprefix; + std::vector m_grouplines; +}; \ No newline at end of file From a96d3d352f764fc290bdfcba1269f83728bcbf5c Mon Sep 17 00:00:00 2001 From: Hirek Date: Fri, 14 Feb 2025 20:58:53 +0100 Subject: [PATCH 63/88] i-edenabled light --- Train.cpp | 12 +++++++++++- Train.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Train.cpp b/Train.cpp index 035ad6da..39bed695 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7435,6 +7435,12 @@ bool TTrain::Update( double const Deltatime ) else btCompressors.Turn(false); + // Lampka zezwolenia na hamowanie ED + if (mvControlled->EpFuse) + btEDenabled.Turn(true); + else + btEDenabled.Turn(false); + // Lampka aktywowanej kabiny if (mvControlled->CabActive != 0) { btCabActived.Turn(true); @@ -7448,6 +7454,9 @@ bool TTrain::Update( double const Deltatime ) else btAKLVents.Turn(false); + if () + + if( true == lowvoltagepower ) { // McZapkie-141102: SHP i czuwak, TODO: sygnalizacja kabinowa if( mvOccupied->SecuritySystem.is_vigilance_blinking() ) { @@ -10164,7 +10173,8 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-universal9:", btUniversals[ 9 ] }, { "i-cabactived:", btCabActived }, {"i-aklvents:", btAKLVents}, - {"i-compressorany:", btCompressors } + {"i-compressorany:", btCompressors }, + {"i-edenabled", btEDenabled } }; { auto lookup = lights.find( Label ); diff --git a/Train.h b/Train.h index ccfa21a5..10c54754 100644 --- a/Train.h +++ b/Train.h @@ -777,6 +777,7 @@ public: // reszta może by?publiczna TButton btCabActived; TButton btAKLVents; TButton btCompressors; // lampka pracy jakiejkolwiek sprezarki + TButton btEDenabled; // czy wlaczony jest hamulec ED (czy dostepny) // other TButton btLampkaMalfunction; TButton btLampkaMalfunctionB; From a06befc22c8cbdffc48e52e4e9c2ba060131a596 Mon Sep 17 00:00:00 2001 From: Hirek Date: Fri, 14 Feb 2025 21:10:18 +0100 Subject: [PATCH 64/88] Compilation fix --- Train.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Train.cpp b/Train.cpp index 39bed695..cdc5eab1 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7454,8 +7454,6 @@ bool TTrain::Update( double const Deltatime ) else btAKLVents.Turn(false); - if () - if( true == lowvoltagepower ) { // McZapkie-141102: SHP i czuwak, TODO: sygnalizacja kabinowa From b343d0032e0f1433bcf1edfe2e741135dd11dedc Mon Sep 17 00:00:00 2001 From: Hirek Date: Sat, 15 Feb 2025 03:03:31 +0100 Subject: [PATCH 65/88] Separate buttons for openning and closing pant valve --- Train.cpp | 62 ++++++++++++++++++++++++++++++++++++++------ Train.h | 2 ++ drivermouseinput.cpp | 6 +++++ translation.cpp | 2 ++ 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/Train.cpp b/Train.cpp index cdc5eab1..0f475edb 100644 --- a/Train.cpp +++ b/Train.cpp @@ -2848,23 +2848,43 @@ void TTrain::change_pantograph_selection( int const Change ) { void TTrain::OnCommand_pantographvalvesupdate( TTrain *Train, command_data const &Command ) { + bool hasSeparateSwitches = Train->m_controlmapper.contains("pantvalvesupdate_bt:") && + Train->m_controlmapper.contains("pantvalvesoff_bt:"); + if( Command.action == GLFW_REPEAT ) { return; } if( Command.action == GLFW_PRESS ) { - // implement action - Train->update_pantograph_valves(); - // visual feedback - Train->ggPantValvesButton.UpdateValue( 1.0, Train->dsbSwitch ); + if (hasSeparateSwitches) + { + // implement action + Train->update_pantograph_valves(); + // visual feedback + Train->ggPantValvesUpdate.UpdateValue(1.0, Train->dsbSwitch); + } + + // Old logic to maintain compatibility + else + { + Train->update_pantograph_valves(); + Train->ggPantValvesButton.UpdateValue(1.0, Train->dsbSwitch); + } } else if( Command.action == GLFW_RELEASE ) { // visual feedback // NOTE: pantvalves_sw: is a specialized button, with no toggle behavior support - Train->ggPantValvesButton.UpdateValue( 0.5, Train->dsbSwitch ); + if (hasSeparateSwitches) + Train->ggPantValvesUpdate.UpdateValue(0.5, Train->dsbSwitch); + + // Old logic to maintain compatibility + else + Train->ggPantValvesButton.UpdateValue(0.5, Train->dsbSwitch); } } void TTrain::OnCommand_pantographvalvesoff( TTrain *Train, command_data const &Command ) { + bool hasSeparateSwitches = Train->m_controlmapper.contains("pantvalvesupdate_bt:") && Train->m_controlmapper.contains("pantvalvesoff_bt:"); + if( Command.action == GLFW_REPEAT ) { return; } if( Command.action == GLFW_PRESS ) { @@ -2872,12 +2892,18 @@ void TTrain::OnCommand_pantographvalvesoff( TTrain *Train, command_data const &C Train->mvOccupied->OperatePantographValve( end::front, operation_t::disable ); Train->mvOccupied->OperatePantographValve( end::rear, operation_t::disable ); // visual feedback - Train->ggPantValvesButton.UpdateValue( 0.0, Train->dsbSwitch ); + if (hasSeparateSwitches) + Train->ggPantValvesOff.UpdateValue(1.0, Train->dsbSwitch); + else + Train->ggPantValvesButton.UpdateValue( 0.0, Train->dsbSwitch ); } else if( Command.action == GLFW_RELEASE ) { // visual feedback // NOTE: pantvalves_sw: is a specialized button, with no toggle behavior support - Train->ggPantValvesButton.UpdateValue( 0.5, Train->dsbSwitch ); + if (hasSeparateSwitches) + Train->ggPantValvesOff.UpdateValue(0.f, Train->dsbSwitch); + else + Train->ggPantValvesButton.UpdateValue( 0.5, Train->dsbSwitch ); } } @@ -8086,6 +8112,9 @@ bool TTrain::Update( double const Deltatime ) ggPantCompressorButton.Update(); ggPantCompressorValve.Update(); + ggPantValvesOff.Update(); + ggPantValvesUpdate.Update(); + ggLightsButton.Update(); ggUpperLightButton.Update(); ggLeftLightButton.Update(); @@ -9579,6 +9608,10 @@ void TTrain::clear_cab_controls() ggPantValvesButton.Clear(); ggPantCompressorButton.Clear(); ggPantCompressorValve.Clear(); + + ggPantValvesOff.Clear(); + ggPantValvesUpdate.Clear(); + ggI1B.Clear(); ggI2B.Clear(); ggI3B.Clear(); @@ -9730,6 +9763,16 @@ void TTrain::set_cab_controls( int const Cab ) { ggModernLightDimSw.PutValue(mvOccupied->modernDimmerState - 1); } + // Init separate buttons + if (ggPantValvesUpdate.SubModel != nullptr) + { + ggPantValvesUpdate.PutValue(0.f); + } + if (ggPantValvesOff.SubModel != nullptr) + { + ggPantValvesOff.PutValue(0.f); + } + // motor connectors ggStLinOffButton.PutValue( ( mvControlled->StLinSwitchOff ? @@ -9788,6 +9831,7 @@ void TTrain::set_cab_controls( int const Cab ) { 0.f ) ); } ggPantValvesButton.PutValue( 0.5f ); + // auxiliary compressor ggPantCompressorValve.PutValue( mvControlled->bPantKurek3 ? @@ -10172,7 +10216,7 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-cabactived:", btCabActived }, {"i-aklvents:", btAKLVents}, {"i-compressorany:", btCompressors }, - {"i-edenabled", btEDenabled } + {"i-edenabled", btEDenabled }, }; { auto lookup = lights.find( Label ); @@ -10404,6 +10448,8 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { "invertertoggle10_bt:", ggInverterToggleButtons[9] }, { "invertertoggle11_bt:", ggInverterToggleButtons[10] }, { "invertertoggle12_bt:", ggInverterToggleButtons[11] }, + {"pantvalvesupdate_bt:", ggPantValvesUpdate}, + {"pantvalvesoff_bt:", ggPantValvesOff} }; { auto const lookup { gauges.find( Label ) }; diff --git a/Train.h b/Train.h index 10c54754..360acdb1 100644 --- a/Train.h +++ b/Train.h @@ -663,6 +663,8 @@ public: // reszta może by?publiczna TGauge ggPantValvesButton; TGauge ggPantCompressorButton; TGauge ggPantCompressorValve; + TGauge ggPantValvesUpdate; + TGauge ggPantValvesOff; // Winger 020304 - wlacznik ogrzewania TGauge ggTrainHeatingButton; TGauge ggSignallingButton; diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index 12a13ffa..31da0943 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -854,6 +854,12 @@ drivermouse_input::default_bindings() { { "pantvalves_sw:", { user_command::pantographvalvesupdate, user_command::pantographvalvesoff } }, + { "pantvalvesupdate_bt:", { + user_command::pantographvalvesupdate, + user_command::none}}, + { "pantvalvesoff_bt:", { + user_command::pantographvalvesoff, + user_command::none}}, { "pantcompressor_sw:", { user_command::pantographcompressoractivate, user_command::none } }, diff --git a/translation.cpp b/translation.cpp index 74a4e35a..65638988 100644 --- a/translation.cpp +++ b/translation.cpp @@ -283,6 +283,8 @@ std::string locale::label_cab_control(std::string const &Label) { "pantselectedoff_sw:", STRN("selected pantograph") }, { "pantselect_sw:", STRN("selected pantograph") }, { "pantvalves_sw:", STRN("selected pantograph") }, + { "pantvalvesoff_bt:", STRN("all pantographs down") }, + { "pantvalvesupdate_bt:", STRN("selected pantographs up") }, { "pantcompressor_sw:", STRN("pantograph compressor") }, { "pantcompressorvalve_sw:", STRN("pantograph 3 way valve") }, { "trainheating_sw:", STRN("heating") }, From c5438c29f9c4b74cb361ead2918a172d3efa957c Mon Sep 17 00:00:00 2001 From: Hirek Date: Sun, 16 Feb 2025 21:10:32 +0100 Subject: [PATCH 66/88] Add distance counter double click to activate DCMB=[No] - Czy potrzebne jest podwojne nacisniecie DCDPP=[1.0] - W jakim czasie drugie nacisnienie ma nastapic od pierwszego, aby aktywowac pomiar --- McZapkie/MOVER.h | 2 ++ McZapkie/Mover.cpp | 3 +++ Train.cpp | 24 ++++++++++++++++++++++-- Train.h | 1 + 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index b45e49fc..dd8fc264 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1213,6 +1213,8 @@ class TMoverParameters double SpringBrakeDriveEmergencyVel{-1}; bool HideDirStatusWhenMoving{false}; // Czy gasic lampki kierunku powyzej predkosci zdefiniowanej przez HideDirStatusSpeed int HideDirStatusSpeed{1}; // Predkosc od ktorej lampki kierunku sa wylaczane + bool isDoubleClickForMeasureNeeded = {false}; // czy rozpoczecie pomiaru odleglosci odbywa sie po podwojnym wcisnienciu przycisku? + float DistanceCounterDoublePressPeriod = {1.f}; // czas w jakim nalezy podwojnie wcisnac przycisk, aby rozpoczac pomiar odleglosci TSecuritySystem SecuritySystem; int EmergencyBrakeWarningSignal{0}; // combined with basic WarningSignal when manual emergency brake is active TUniversalCtrlTable UniCtrlList; /*lista pozycji uniwersalnego nastawnika*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index f26544ed..8fbe41c4 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -10625,6 +10625,9 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { extract_value(HideDirStatusWhenMoving, "HideDirStatusWhenMoving", line, ""); extract_value(HideDirStatusSpeed, "HideDirStatusSpeed", line, ""); + extract_value(isDoubleClickForMeasureNeeded, "DCMB", line, ""); + extract_value(DistanceCounterDoublePressPeriod, "DCDPP", line, ""); + std::map starts { { "Disabled", start_t::disabled }, diff --git a/Train.cpp b/Train.cpp index 0f475edb..5e113ba9 100644 --- a/Train.cpp +++ b/Train.cpp @@ -1317,7 +1317,17 @@ void TTrain::OnCommand_distancecounteractivate( TTrain *Train, command_data cons // visual feedback Train->ggDistanceCounterButton.UpdateValue( 1.0, Train->dsbSwitch ); // activate or start anew - Train->m_distancecounter = 0.f; + if (Train->mvOccupied->isDoubleClickForMeasureNeeded) { + // handler tempomatu dla podwojnego kliku + if (Train->trainLenghtMeasureTimer >= 0.f) // jesli zdazylismy w czasie sekundy + Train->m_distancecounter = 0.f; // rozpoczynamy pomiar + else + Train->trainLenghtMeasureTimer = Train->mvOccupied->DistanceCounterDoublePressPeriod; // odpalamy zegarek od nowa + } + else { + // dla pojedynczego kliku + Train->m_distancecounter = 0.f; + } } else if( Command.action == GLFW_RELEASE ) { // visual feedback @@ -2899,7 +2909,7 @@ void TTrain::OnCommand_pantographvalvesoff( TTrain *Train, command_data const &C } else if( Command.action == GLFW_RELEASE ) { // visual feedback - // NOTE: pantvalves_sw: is a specialized button, with no toggle behavior support + // NOTE: pantvalves_sw: is a speciali zed button, with no toggle behavior support if (hasSeparateSwitches) Train->ggPantValvesOff.UpdateValue(0.f, Train->dsbSwitch); else @@ -6952,6 +6962,16 @@ bool TTrain::Update( double const Deltatime ) mvOccupied->OperateDoors( static_cast( idx ), true ); } } + + // train measurement timer + if (trainLenghtMeasureTimer >= 0.f) { + trainLenghtMeasureTimer -= Deltatime; + if (trainLenghtMeasureTimer < 0.f) + { + trainLenghtMeasureTimer = -1.f; + } + } + // helper variables if( DynamicObject->Mechanik != nullptr ) { m_doors = ( diff --git a/Train.h b/Train.h index 360acdb1..ef5f5da3 100644 --- a/Train.h +++ b/Train.h @@ -885,6 +885,7 @@ private: bool m_dirbackward{ false }; // helper, true if direction set to backward bool m_doorpermits{ false }; // helper, true if any door permit is active float m_doorpermittimers[2] = { -1.f, -1.f }; + float trainLenghtMeasureTimer = { -1.f }; // ld substitute bool m_couplingdisconnect { false }; bool m_couplingdisconnectback { false }; From 4aa0978290b33170bf803599f4b1d93ff75cee74 Mon Sep 17 00:00:00 2001 From: Hirek Date: Sun, 16 Feb 2025 23:07:36 +0100 Subject: [PATCH 67/88] Battery button impulse behavior --- McZapkie/MOVER.h | 3 + McZapkie/Mover.cpp | 3 + Train.cpp | 188 +++++++++++++++++++++++++++++++++++++-------- Train.h | 2 + 4 files changed, 163 insertions(+), 33 deletions(-) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index dd8fc264..c2f00b3b 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1138,6 +1138,9 @@ class TMoverParameters int UniversalBrakeButtonFlag[3] = {0, 0, 0}; /* mozliwe działania przycisków hamulcowych */ int UniversalResetButtonFlag[3] = {0, 0, 0}; // customizable reset buttons assignments int TurboTest = 0; + bool isBatteryButtonImpulse = false; // czy przelacznik baterii traktowac jako pojedynczy przycisk + bool shouldHoldBatteryButton = false; // czy nalezy przytrzymac przycisk baterii aby wlaczyc/wylaczyc baterie + float BatteryButtonHoldTime = 1.f; // minimalny czas przytrzymania przycisku baterii double MaxBrakeForce = 0.0; /*maksymalna sila nacisku hamulca*/ double MaxBrakePress[5]; // pomocniczy, proz, sred, lad, pp double P2FTrans = 0.0; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 8fbe41c4..96185aee 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -10628,6 +10628,9 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { extract_value(isDoubleClickForMeasureNeeded, "DCMB", line, ""); extract_value(DistanceCounterDoublePressPeriod, "DCDPP", line, ""); + extract_value(isBatteryButtonImpulse, "IBTB", line, ""); + extract_value(shouldHoldBatteryButton, "SBBBH", line, ""); + extract_value(BatteryButtonHoldTime, "BBHT", line, ""); std::map starts { { "Disabled", start_t::disabled }, diff --git a/Train.cpp b/Train.cpp index 5e113ba9..b2a76c05 100644 --- a/Train.cpp +++ b/Train.cpp @@ -2398,7 +2398,8 @@ void TTrain::OnCommand_cabsignalacknowledge( TTrain *Train, command_data const & void TTrain::OnCommand_batterytoggle( TTrain *Train, command_data const &Command ) { - if( Command.action != GLFW_REPEAT ) { + if (Train->allowBatteryToggle || Command.action != GLFW_REPEAT) + { // keep the switch from flipping back and forth if key is held down if( false == Train->mvOccupied->Power24vIsAvailable ) { // turn on @@ -2412,44 +2413,160 @@ void TTrain::OnCommand_batterytoggle( TTrain *Train, command_data const &Command } void TTrain::OnCommand_batteryenable( TTrain *Train, command_data const &Command ) { + if (!Train->mvOccupied->isBatteryButtonImpulse) + { // regular button behavior + if (Command.action == GLFW_PRESS) + { + // visual feedback + Train->ggBatteryButton.UpdateValue(1.0f, Train->dsbSwitch); + Train->ggBatteryOnButton.UpdateValue(1.0f, Train->dsbSwitch); - if( Command.action == GLFW_PRESS ) { - // visual feedback - Train->ggBatteryButton.UpdateValue( 1.0f, Train->dsbSwitch ); - Train->ggBatteryOnButton.UpdateValue( 1.0f, Train->dsbSwitch ); - - Train->mvOccupied->BatterySwitch( true ); - - // side-effects - if( Train->mvOccupied->LightsPosNo > 0 ) { - Train->Dynamic()->SetLights(); - } + Train->mvOccupied->BatterySwitch(true); + Train->allowBatteryToggle = false; + // side-effects + if (Train->mvOccupied->LightsPosNo > 0) + { + Train->Dynamic()->SetLights(); + } + } + else if (Command.action == GLFW_RELEASE) + { + if (Train->ggBatteryButton.type() == TGaugeType::push) + { + // return the switch to neutral position + Train->ggBatteryButton.UpdateValue(0.5f); + } + Train->ggBatteryOnButton.UpdateValue(0.0f, Train->dsbSwitch); + Train->allowBatteryToggle = true; + } } - else if( Command.action == GLFW_RELEASE ) { - if( Train->ggBatteryButton.type() == TGaugeType::push ) { - // return the switch to neutral position - Train->ggBatteryButton.UpdateValue( 0.5f ); + else // impulse button behavior + { + if (Command.action == GLFW_PRESS) + { + if (Train->mvOccupied->shouldHoldBatteryButton) + { + // jesli przycisk trzeba przytrzymac + Train->ggBatteryButton.UpdateValue(1.0f, Train->dsbSwitch); + Train->ggBatteryOnButton.UpdateValue(1.0f, Train->dsbSwitch); + Train->fBatteryTimer = Train->mvOccupied->BatteryButtonHoldTime; // start timer + } + else + { + // jesli przycisk dziala od razu + Train->mvOccupied->BatterySwitch(true); + Train->allowBatteryToggle = false; + // side-effects + if (Train->mvOccupied->LightsPosNo > 0) + { + Train->Dynamic()->SetLights(); + } + + // visual feedback + Train->ggBatteryButton.UpdateValue(1.0f, Train->dsbSwitch); + Train->ggBatteryOnButton.UpdateValue(1.0f, Train->dsbSwitch); + } } - Train->ggBatteryOnButton.UpdateValue( 0.0f, Train->dsbSwitch ); + else if (Command.action == GLFW_RELEASE) + { + // visual feedback + Train->ggBatteryButton.UpdateValue(0.0f, Train->dsbSwitch); + Train->ggBatteryOnButton.UpdateValue(0.0f, Train->dsbSwitch); + Train->fBatteryTimer = -1.f; // + Train->allowBatteryToggle = true; + } + else if (Command.action == GLFW_REPEAT && Train->mvOccupied->shouldHoldBatteryButton) + { + // trzymamy przycisk + if (Train->fBatteryTimer <= 0.0 && Train->mvOccupied->Battery == false) { + Train->mvOccupied->BatterySwitch(true); + // side-effects + if (Train->mvOccupied->LightsPosNo > 0) + { + Train->Dynamic()->SetLights(); + } + Train->allowBatteryToggle = false; + } + + } } } void TTrain::OnCommand_batterydisable( TTrain *Train, command_data const &Command ) { - // TBD, TODO: ewentualnie zablokować z FIZ, np. w samochodach się nie odłącza akumulatora - if( Command.action == GLFW_PRESS ) { - // visual feedback - Train->ggBatteryButton.UpdateValue( 0.0f, Train->dsbSwitch ); - Train->ggBatteryOffButton.UpdateValue( 1.0f, Train->dsbSwitch ); + if (!Train->mvOccupied->isBatteryButtonImpulse) + { // regular button behavior + if (Command.action == GLFW_PRESS) + { + // visual feedback + Train->ggBatteryButton.UpdateValue(0.0f, Train->dsbSwitch); + Train->ggBatteryOffButton.UpdateValue(1.0f, Train->dsbSwitch); - Train->mvOccupied->BatterySwitch( false ); - } - else if( Command.action == GLFW_RELEASE ) { - if( Train->ggBatteryButton.type() == TGaugeType::push ) { - // return the switch to neutral position - Train->ggBatteryButton.UpdateValue( 0.5f ); - } - Train->ggBatteryOffButton.UpdateValue( 0.0f, Train->dsbSwitch ); - } + Train->mvOccupied->BatterySwitch(false); + + // side-effects + if (Train->mvOccupied->LightsPosNo > 0) + { + Train->Dynamic()->SetLights(); + } + } + else if (Command.action == GLFW_RELEASE) + { + if (Train->ggBatteryButton.type() == TGaugeType::push) + { + // return the switch to neutral position + Train->ggBatteryButton.UpdateValue(0.5f); + } + Train->ggBatteryOffButton.UpdateValue(0.0f, Train->dsbSwitch); + } + } + else // impulse button behavior + { + if (Command.action == GLFW_PRESS) + { + if (Train->mvOccupied->shouldHoldBatteryButton) + { + // jesli przycisk trzeba przytrzymac + Train->ggBatteryButton.UpdateValue(1.0f, Train->dsbSwitch); + Train->ggBatteryOffButton.UpdateValue(1.0f, Train->dsbSwitch); + Train->fBatteryTimer = Train->mvOccupied->BatteryButtonHoldTime; // start timer + } + else + { + // jesli przycisk dziala od razu + Train->mvOccupied->BatterySwitch(false); + Train->allowBatteryToggle = false; + + // side-effects + if (Train->mvOccupied->LightsPosNo > 0) + { + Train->Dynamic()->SetLights(); + } + // visual feedback + Train->ggBatteryButton.UpdateValue(1.0f, Train->dsbSwitch); + Train->ggBatteryOffButton.UpdateValue(1.0f, Train->dsbSwitch); + } + } + else if (Command.action == GLFW_RELEASE) + { + // visual feedback + Train->ggBatteryButton.UpdateValue(0.0f, Train->dsbSwitch); + Train->ggBatteryOffButton.UpdateValue(0.0f, Train->dsbSwitch); + Train->allowBatteryToggle = true; + } + else if (Command.action == GLFW_REPEAT && Train->mvOccupied->shouldHoldBatteryButton) + { + // trzymamy przycisk + if (Train->fBatteryTimer <= 0.0 && Train->mvOccupied->Battery == true) { + Train->mvOccupied->BatterySwitch(false); + Train->allowBatteryToggle = false; + // side-effects + if (Train->mvOccupied->LightsPosNo > 0) + { + Train->Dynamic()->SetLights(); + } + } + } + } } void TTrain::OnCommand_cabactivationtoggle(TTrain *Train, command_data const &Command) { @@ -6967,9 +7084,14 @@ bool TTrain::Update( double const Deltatime ) if (trainLenghtMeasureTimer >= 0.f) { trainLenghtMeasureTimer -= Deltatime; if (trainLenghtMeasureTimer < 0.f) - { trainLenghtMeasureTimer = -1.f; - } + } + + // battery timer + if (fBatteryTimer >= 0.f) { + fBatteryTimer -= Deltatime; + if (fBatteryTimer < 0.f) + fBatteryTimer = -1.f; } // helper variables diff --git a/Train.h b/Train.h index ef5f5da3..a6267bb2 100644 --- a/Train.h +++ b/Train.h @@ -843,6 +843,8 @@ private: float fHaslerTimer; float fConverterTimer; // hunter-261211: dla przekaznika float fMainRelayTimer; // hunter-141211: zalaczanie WSa z opoznieniem + float fBatteryTimer = {-1.f}; // Hirek: zalaczanie baterii z opoznieniem (tylko gdy zdefiniowano takie zachowanie w fiz) + bool allowBatteryToggle = true; // Hirek: zabezpieczenie przed przelaczaniem bateri on/off int ScreenUpdateRate { 0 }; // vehicle specific python screen update rate override // McZapkie-240302 - przyda sie do tachometru From eb387c371524d72bac197d6b629873fe08b724eb Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 17 Feb 2025 01:23:43 +0100 Subject: [PATCH 68/88] Developer tools: Add vehicle fiz reload button --- DynObj.cpp | 3 ++- DynObj.h | 9 ++++++++- McZapkie/MOVER.h | 2 ++ McZapkie/Mover.cpp | 19 +++++++++++++++++++ driveruipanels.cpp | 14 ++++++++++++++ driveruipanels.h | 3 ++- 6 files changed, 47 insertions(+), 3 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 24d31fc6..15fa0db7 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -5041,7 +5041,8 @@ TDynamicObject::radius() const { // McZapkie-250202 // wczytywanie pliku z danymi multimedialnymi (dzwieki) void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string const &ReplacableSkin ) { - + rTypeName = TypeName; + rReplacableSkin = ReplacableSkin; Global.asCurrentDynamicPath = asBaseDir; std::string asFileName = asBaseDir + TypeName + ".mmd"; std::string asAnimName; diff --git a/DynObj.h b/DynObj.h index d97826c3..5de745e5 100644 --- a/DynObj.h +++ b/DynObj.h @@ -209,7 +209,11 @@ public: inline TDynamicObject *PrevConnected() const { return MoverParameters->Neighbours[ end::front ].vehicle; }; // pojazd podłączony od strony sprzęgu 0 (kabina 1) inline int NextConnectedNo() const { return MoverParameters->Neighbours[ end::rear ].vehicle_end; } inline int PrevConnectedNo() const { return MoverParameters->Neighbours[ end::front ].vehicle_end; } -// double fTrackBlock; // odległość do przeszkody do dalszego ruchu (wykrywanie kolizji z innym pojazdem) + + // Dev tools + void Reload(); + + // double fTrackBlock; // odległość do przeszkody do dalszego ruchu (wykrywanie kolizji z innym pojazdem) // modele składowe pojazdu TModel3d *mdModel; // model pudła @@ -573,6 +577,9 @@ private: TDynamicObject *ABuFindObject( int &Foundcoupler, double &Distance, TTrack const *Track, int const Direction, int const Mycoupler ) const; void ABuCheckMyTrack(); + std::string rTypeName; // nazwa typu pojazdu + std::string rReplacableSkin; // nazwa tekstury pojazdu + public: bool DimHeadlights{ false }; // status of the headlight dimming toggle. NOTE: single toggle for all lights is a simplification. TODO: separate per-light switches bool HighBeamLights { false }; // status of the highbeam toggle diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index c2f00b3b..7d38bbb9 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1069,6 +1069,8 @@ class TMoverParameters }; public: + std::string chkPath; + bool reload_FIZ(); double dMoveLen = 0.0; /*---opis lokomotywy, wagonu itp*/ /*--opis serii--*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 96185aee..81f0750a 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -9444,6 +9444,7 @@ void TMoverParameters::BrakeSubsystemDecode() // ************************************************************************************************* bool TMoverParameters::LoadFIZ(std::string chkpath) { + chkPath = chkpath; // assign class path for reloading const int param_ok = 1; const int wheels_ok = 2; const int dimensions_ok = 4; @@ -12473,6 +12474,24 @@ double TMoverParameters::ShowCurrentP(int AmpN) const } } +bool TMoverParameters::reload_FIZ() { + WriteLog("[DEV] Reloading FIZ for " + Name); + // pause simulation + Global.iPause |= 0b1000; + bool result = LoadFIZ(chkPath); + if (result == true) + { + // jesli sie udalo przeladowac FIZ + Global.iPause &= 0b0111; + WriteLog("[DEV] FIZ reloaded for " + Name); + } + else { + // failed to reload - exit simulator + ErrorLog("[DEV] Failed to reload fiz for vehicle " + Name); + } + +} + namespace simulation { weights_table Weights; diff --git a/driveruipanels.cpp b/driveruipanels.cpp index b4e8c70c..872cf244 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -622,6 +622,7 @@ debug_panel::render() { render_section( "Camera", m_cameralines ); render_section( "Gfx Renderer", m_rendererlines ); render_section_settings(); + render_section_developer(); // Developer tools #ifdef WITH_UART if(true == render_section( "UART", m_uartlines)) { int ports_num = UartStatus.available_ports.size(); @@ -1492,6 +1493,19 @@ debug_panel::render_section( std::vector const &Lines ) { return true; } +bool debug_panel::render_section_developer() +{ + if (false == ImGui::CollapsingHeader("Developer tools")) + return false; + ImGui::PushStyleColor(ImGuiCol_Text, {Global.UITextColor.r, Global.UITextColor.g, Global.UITextColor.b, Global.UITextColor.a}); + ImGui::TextUnformatted("Warning! These tools are only for developers.\nDo not use them if you are NOT sure what they do!"); + ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "These settings may crash your simulator!"); + if (ImGui::Button("Reload current vehicle .fiz") == true) + { + m_input.vehicle->MoverParameters->reload_FIZ(); // reload fiz + } +} + bool debug_panel::render_section_settings() { diff --git a/driveruipanels.h b/driveruipanels.h index e1a9acbe..f34a601e 100644 --- a/driveruipanels.h +++ b/driveruipanels.h @@ -104,7 +104,8 @@ private: bool render_section_uart(); #endif bool render_section_settings(); -// members + bool render_section_developer(); + // members std::array m_buffer; std::array m_eventsearch; input_data m_input; From 2a5d8cc6de70a46284cc2a006bc2133cfc45aea9 Mon Sep 17 00:00:00 2001 From: Hirek Date: Wed, 15 Jan 2025 04:44:56 +0100 Subject: [PATCH 69/88] Add WBL85, EC160 pantograph types --- DynObj.cpp | 146 ++++++++++++++++++++++++++++++++++++++++----- DynObj.h | 3 + McZapkie/MOVER.h | 7 +++ McZapkie/Mover.cpp | 11 ++++ 4 files changed, 152 insertions(+), 15 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 15fa0db7..76175af2 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -97,6 +97,108 @@ void TAnimPant::AKP_4E() fHeightExtra[3] = -0.07f; //+0.3048 fHeightExtra[4] = -0.15f; //+0.3810 }; +void TAnimPant::WBL85() +{ // ustawienie wymiarów dla pantografu WBL88 + vPos = Math3D::vector3(0, 0, 0); // przypisanie domyśnych współczynników do pantografów + + // mnozniki animacji ramion dla pantografu WBL88 + rd1rf = 1.f; + rd2rf = 1.2; + rg1rf = 0.875; + rg2rf = 1.0; + slizgrf = 1.f; + + fLenL1 = 1.98374; + fLenU1 = 2.14199; + fHoriz = 0.142; // 0.54555075 przesunięcie ślizgu w długości pojazdu względem + // osi obrotu dolnego ramienia + fHeight = 0.09353; // wysokość ślizgu ponad oś obrotu + fWidth = 0.4969; // połowa szerokości ślizgu + fAngleL0 = DegToRad(2.8547285515689267247882521833308); + fAngleL = fAngleL0; // początkowy kąt dolnego ramienia + // fAngleU0=acos((1.22*cos(fAngleL)+0.535)/1.755); //górne ramię + fAngleU0 = acos((fLenL1 * cos(fAngleL) + fHoriz) / fLenU1); // górne ramię + fAngleU = fAngleU0; // początkowy kąt + // PantWys=1.22*sin(fAngleL)+1.755*sin(fAngleU); //wysokość początkowa + PantWys = fLenL1 * sin(fAngleL) + fLenU1 * sin(fAngleU) + fHeight; // wysokość początkowa + PantTraction = PantWys; + hvPowerWire = NULL; + fWidthExtra = 0.381f; //(2.032m-1.027)/2 + // poza obszarem roboczym jest aproksymacja łamaną o 5 odcinkach + fHeightExtra[0] = 0.0f; //+0.0762 + fHeightExtra[1] = -0.01f; //+0.1524 + fHeightExtra[2] = -0.03f; //+0.2286 + fHeightExtra[3] = -0.07f; //+0.3048 + fHeightExtra[4] = -0.15f; //+0.3810 +}; +void TAnimPant::EC160_200() +{ // ustawienie wymiarów dla pantografow EC160 lub EC200 + vPos = Math3D::vector3(0, 0, 0); // przypisanie domyśnych współczynników do pantografów + + // mnozniki animacji ramion dla pantografow EC160 lub EC200 + rd1rf = 1.f; + rd2rf = 0.85; + rg1rf = 1.f; + rg2rf = 1.f; // 0.833 + slizgrf = 1.f; + + fLenL1 = 1.98374; + fLenU1 = 2.14199; + fHoriz = 0.142; // 0.54555075 przesunięcie ślizgu w długości pojazdu względem + // osi obrotu dolnego ramienia + fHeight = 0.09353; // wysokość ślizgu ponad oś obrotu + fWidth = 0.4969; // połowa szerokości ślizgu + fAngleL0 = DegToRad(2.8547285515689267247882521833308); + fAngleL = fAngleL0; // początkowy kąt dolnego ramienia + // fAngleU0=acos((1.22*cos(fAngleL)+0.535)/1.755); //górne ramię + fAngleU0 = acos((fLenL1 * cos(fAngleL) + fHoriz) / fLenU1); // górne ramię + fAngleU = fAngleU0; // początkowy kąt + // PantWys=1.22*sin(fAngleL)+1.755*sin(fAngleU); //wysokość początkowa + PantWys = fLenL1 * sin(fAngleL) + fLenU1 * sin(fAngleU) + fHeight; // wysokość początkowa + PantTraction = PantWys; + hvPowerWire = NULL; + fWidthExtra = 0.381f; //(2.032m-1.027)/2 + // poza obszarem roboczym jest aproksymacja łamaną o 5 odcinkach + fHeightExtra[0] = 0.0f; //+0.0762 + fHeightExtra[1] = -0.01f; //+0.1524 + fHeightExtra[2] = -0.03f; //+0.2286 + fHeightExtra[3] = -0.07f; //+0.3048 + fHeightExtra[4] = -0.15f; //+0.3810 +}; +void TAnimPant::DSAx() +{ // ustawienie wymiarów dla pantografow z rodziny DSA + vPos = Math3D::vector3(0, 0, 0); // przypisanie domyśnych współczynników do pantografów + + // mnozniki animacji ramion dla pantografow z rodziny DSA + rd1rf = 1.f; + rd2rf = 1.025; + rg1rf = 0.875; + rg2rf = 1.f; + slizgrf = 1.f; + + fLenL1 = 1.98374; + fLenU1 = 2.14199; + fHoriz = 0.142; // 0.54555075 przesunięcie ślizgu w długości pojazdu względem + // osi obrotu dolnego ramienia + fHeight = 0.09353; // wysokość ślizgu ponad oś obrotu + fWidth = 0.4969; // połowa szerokości ślizgu + fAngleL0 = DegToRad(2.8547285515689267247882521833308); + fAngleL = fAngleL0; // początkowy kąt dolnego ramienia + // fAngleU0=acos((1.22*cos(fAngleL)+0.535)/1.755); //górne ramię + fAngleU0 = acos((fLenL1 * cos(fAngleL) + fHoriz) / fLenU1); // górne ramię + fAngleU = fAngleU0; // początkowy kąt + // PantWys=1.22*sin(fAngleL)+1.755*sin(fAngleU); //wysokość początkowa + PantWys = fLenL1 * sin(fAngleL) + fLenU1 * sin(fAngleU) + fHeight; // wysokość początkowa + PantTraction = PantWys; + hvPowerWire = NULL; + fWidthExtra = 0.381f; //(2.032m-1.027)/2 + // poza obszarem roboczym jest aproksymacja łamaną o 5 odcinkach + fHeightExtra[0] = 0.0f; //+0.0762 + fHeightExtra[1] = -0.01f; //+0.1524 + fHeightExtra[2] = -0.03f; //+0.2286 + fHeightExtra[3] = -0.07f; //+0.3048 + fHeightExtra[4] = -0.15f; //+0.3810 +}; //--------------------------------------------------------------------------- int TAnim::TypeSet(int i, int fl) { // ustawienie typu animacji i zależnej od niego ilości animowanych submodeli @@ -127,7 +229,21 @@ int TAnim::TypeSet(int i, int fl) case 5: // 5-pantograf - 5 submodeli iFlags = 0x055; fParamPants = new TAnimPant(); - fParamPants->AKP_4E(); + switch (currentMover.EnginePowerSource.CollectorParameters.PantographType) { + case (TPantType::AKP_4E): + fParamPants->AKP_4E(); + break; + case(TPantType::DSAx): + fParamPants->DSAx(); + break; + case(TPantType::EC160_200): + fParamPants->EC160_200(); + break; + case(TPantType::WBL88): + fParamPants->WBL88(); + break; + } + break; case 6: iFlags = 0x068; @@ -576,20 +692,20 @@ void TDynamicObject::UpdateDoorPlug(TAnim *pAnim) { void TDynamicObject::UpdatePant(TAnim *pAnim) { // animacja pantografu - 4 obracane ramiona, ślizg piąty - float a, b, c; - a = RadToDeg(pAnim->fParamPants->fAngleL - pAnim->fParamPants->fAngleL0); - b = RadToDeg(pAnim->fParamPants->fAngleU - pAnim->fParamPants->fAngleU0); - c = a + b; - if (pAnim->smElement[0]) - pAnim->smElement[0]->SetRotate(float3(-1, 0, 0), a); // dolne ramię - if (pAnim->smElement[1]) - pAnim->smElement[1]->SetRotate(float3(1, 0, 0), a); - if (pAnim->smElement[2]) - pAnim->smElement[2]->SetRotate(float3(1, 0, 0), c); // górne ramię - if (pAnim->smElement[3]) - pAnim->smElement[3]->SetRotate(float3(-1, 0, 0), c); - if (pAnim->smElement[4]) - pAnim->smElement[4]->SetRotate(float3(-1, 0, 0), b); //ślizg + float a, b, c; + a = RadToDeg(pAnim->fParamPants->fAngleL - pAnim->fParamPants->fAngleL0); + b = RadToDeg(pAnim->fParamPants->fAngleU - pAnim->fParamPants->fAngleU0); + c = a + b; + if (pAnim->smElement[0]) + pAnim->smElement[0]->SetRotate(float3(-1, 0, 0), a); // dolne ramie 1 + if (pAnim->smElement[1]) + pAnim->smElement[1]->SetRotate(float3(1, 0, 0), a * pAnim->fParamPants->rd2rf); // dolne ramie 2 + if (pAnim->smElement[2]) + pAnim->smElement[2]->SetRotate(float3(1, 0, 0), c); // górne ramie 1 + if (pAnim->smElement[3]) + pAnim->smElement[3]->SetRotate(float3(-1, 0, 0), c * pAnim->fParamPants->rg2rf); // gorne ramie 2 + if (pAnim->smElement[4]) + pAnim->smElement[4]->SetRotate(float3(-1, 0, 0), b * pAnim->fParamPants->slizgrf); // ślizg } // doorstep animation, shift diff --git a/DynObj.h b/DynObj.h index 5de745e5..d5f7d6d0 100644 --- a/DynObj.h +++ b/DynObj.h @@ -104,6 +104,9 @@ class TAnimPant float fHeightExtra[5]; //łamana symulująca kształt nabieżnika // double fHorizontal; //Ra 2015-01: położenie drutu względem osi pantografu void AKP_4E(); + void WBL85(); + void DSAx(); + void EC160_200(); }; class TAnim diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 7d38bbb9..821cbcc3 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -469,6 +469,13 @@ struct TBoilerType { //} }; /*rodzaj odbieraka pradu*/ +enum TPantType +{ + AKP_4E, + DSAx, + EC160_200, + WBL85 +}; struct TCurrentCollector { long CollectorsNo; //musi być tu, bo inaczej się kopie double MinH; double MaxH; //zakres ruchu pantografu, nigdzie nie używany diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 81f0750a..4319af90 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -11338,6 +11338,17 @@ void TMoverParameters::LoadFIZ_PowerParamsDecode( TPowerParameters &Powerparamet auto &collectorparameters = Powerparameters.CollectorParameters; collectorparameters = TCurrentCollector { 0, 0, 0, 0, 0, 0, false, 0, 0, 0, false, 0 }; + + std::string PantType = ""; + extract_value(PantType, "PantType", Line, ""); + if (PantType == "AKP_4E") + collectorparameters.PantographType = TPantType::AKP_4E; + if (PantType._Starts_with("DSA")) // zakladam ze wszystkie pantografy DSA sa takie same + collectorparameters.PantographType = TPantType::DSAx; + if (PantType == "EC160" || PantType == "EC200") + collectorparameters.PantographType = TPantType::EC160_200; + if (PantType == "WBL85") + collectorparameters.PantographType = TPantType::WBL85; extract_value( collectorparameters.CollectorsNo, "CollectorsNo", Line, "" ); extract_value( collectorparameters.MinH, "MinH", Line, "" ); From 9c1e7cd36eab95249d544aa96b03852c2ee2c029 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 17 Feb 2025 18:26:59 +0100 Subject: [PATCH 70/88] After cherrypick fixes to 2a5d8cc6de70a46284cc2a006bc2133cfc45aea9 --- DynObj.cpp | 10 +++++----- DynObj.h | 11 +++++++++-- McZapkie/MOVER.h | 1 + 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 76175af2..5015b0cf 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -200,7 +200,7 @@ void TAnimPant::DSAx() fHeightExtra[4] = -0.15f; //+0.3810 }; //--------------------------------------------------------------------------- -int TAnim::TypeSet(int i, int fl) +int TAnim::TypeSet(int i, TMoverParameters currentMover, int fl) { // ustawienie typu animacji i zależnej od niego ilości animowanych submodeli fMaxDist = -1.0; // normalnie nie pokazywać switch (i) @@ -239,8 +239,8 @@ int TAnim::TypeSet(int i, int fl) case(TPantType::EC160_200): fParamPants->EC160_200(); break; - case(TPantType::WBL88): - fParamPants->WBL88(); + case(TPantType::WBL85): + fParamPants->WBL85(); break; } @@ -5259,8 +5259,8 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co if (iAnimType[ANIM_PANTS]) // o ile jakieś pantografy są (a domyślnie są) pants = &pAnimations[k]; // zapamiętanie na potrzeby wyszukania submodeli pAnimations[k].iShift = sm; // przesunięcie do przydzielenia wskaźnika - sm += pAnimations[k++].TypeSet(j); // ustawienie typu animacji i zliczanie tablicowanych submodeli - } + sm += pAnimations[k++].TypeSet(j, *MoverParameters); // ustawienie typu animacji i zliczanie tablicowanych submodeli + } if (sm) // o ile są bardziej złożone animacje { pAnimated = new TSubModel *[sm]; // tabela na animowane submodele diff --git a/DynObj.h b/DynObj.h index d5f7d6d0..97ca1a90 100644 --- a/DynObj.h +++ b/DynObj.h @@ -103,6 +103,13 @@ class TAnimPant float fWidthExtra; // dodatkowy rozmiar poziomy poza część roboczą (fWidth) float fHeightExtra[5]; //łamana symulująca kształt nabieżnika // double fHorizontal; //Ra 2015-01: położenie drutu względem osi pantografu + + // factory ktore mozna nadpisac z fiza + float rd1rf{1.f}; // mnoznik obrotu ramienia dolnego 1 + float rd2rf{1.f}; // mnoznik obrotu ramienia dolnego 2 + float rg1rf{1.f}; // mnoznik obrotu ramienia gornego 1 + float rg2rf{1.f}; // mnoznik obrotu ramienia gornego 2 + float slizgrf{1.f}; // mnoznik obrotu slizgacza void AKP_4E(); void WBL85(); void DSAx(); @@ -118,8 +125,8 @@ public: // destructor ~TAnim(); // methods - int TypeSet( int i, int fl = 0 ); // ustawienie typu -// members + int TypeSet(int i, TMoverParameters currentMover, int fl = 0); // ustawienie typu + // members union { TSubModel *smAnimated; // animowany submodel (jeśli tylko jeden, np. oś) diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 821cbcc3..10eae432 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -487,6 +487,7 @@ struct TCurrentCollector { double MaxPress; //maksymalne ciśnienie za reduktorem bool FakePower; int PhysicalLayout; + TPantType PantographType; //inline TCurrentCollector() { // CollectorsNo = 0; // MinH, MaxH, CSW, MinV, MaxV = 0.0; From bf852b7d45b5790532669616df8e07489e9aab95 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 17 Feb 2025 22:28:12 +0100 Subject: [PATCH 71/88] Add ini shakefactor shakefactor BF RL UD BF - tyl/przod RL - prawo/lewo UD - gora/dol --- DynObj.cpp | 6 +++--- Globals.cpp | 9 +++++++++ Globals.h | 4 +++- Spring.cpp | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 5015b0cf..ecd017cc 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -7857,9 +7857,9 @@ TDynamicObject::update_shake( double const Timedelta ) { if( iVel > 0.5 ) { // acceleration-driven base shake shakevector += Math3D::vector3( - -MoverParameters->AccN * Timedelta * 5.0, // highlight side sway - -MoverParameters->AccVert * Timedelta, - -MoverParameters->AccSVBased * Timedelta * 1.5); // accent acceleration/deceleration + -MoverParameters->AccN * Timedelta * 5.0 * Global.ShakingMultiplierRL, // highlight side sway + -MoverParameters->AccVert * Timedelta * Global.ShakingMultiplierUD, + -MoverParameters->AccSVBased * Timedelta * 1.5 * Global.ShakingMultiplierBF); // accent acceleration/deceleration } auto shake { 1.25 * ShakeSpring.ComputateForces( shakevector, ShakeState.offset ) }; diff --git a/Globals.cpp b/Globals.cpp index 186aaac1..fa75cfe3 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -213,6 +213,15 @@ global_settings::ConfigParse(cParser &Parser) { { Parser.getTokens(); Parser >> MultipleLogs; + } + else if (token == "shakefactor") + { + Parser.getTokens(); + Parser >> ShakingMultiplierBF; + Parser.getTokens(); + Parser >> ShakingMultiplierRL; + Parser.getTokens(); + Parser >> ShakingMultiplierUD; } else if (token == "logs.filter") { diff --git a/Globals.h b/Globals.h index 82a1b3f9..9225f555 100644 --- a/Globals.h +++ b/Globals.h @@ -118,7 +118,9 @@ struct global_settings { glm::ivec2 window_size; // main window size in platform-specific virtual pixels glm::ivec2 cursor_pos; // cursor position in platform-specific virtual pixels glm::ivec2 fb_size; // main window framebuffer size - + float ShakingMultiplierBF {1.f}; // mnożnik bujania kamera przod/tyl + float ShakingMultiplierRL {1.f}; // mnożnik bujania kamera lewo/prawo + float ShakingMultiplierUD {1.f}; // mnożnik bujania kamera gora/dol float fDistanceFactor{ 1.f }; // baza do przeliczania odległości dla LoD float targetfps{ 0.0f }; bool bFullScreen{ false }; diff --git a/Spring.cpp b/Spring.cpp index da955280..59e5c55d 100644 --- a/Spring.cpp +++ b/Spring.cpp @@ -40,7 +40,7 @@ Math3D::vector3 TSpring::ComputateForces( Math3D::vector3 const &pPosition1, Mat // ScaleVector(&deltaP,1.0f / dist, &springForce); // Normalize Distance Vector // ScaleVector(&springForce,-(Hterm + Dterm),&springForce); // Calc Force - springForce = deltaP / dist * ( -( Hterm + Dterm ) ); + springForce = deltaP / dist * ( -( Hterm + Dterm )); // VectorSum(&p1->f,&springForce,&p1->f); // Apply to Particle 1 // VectorDifference(&p2->f,&springForce,&p2->f); // - Force on Particle 2 } From 9bdc064ef309d7cd99d24ae0fad07c5120d5270f Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 17 Feb 2025 23:41:11 +0100 Subject: [PATCH 72/88] Tweaked turbo sound calculation logic --- DynObj.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index ecd017cc..dbc88d9a 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -8120,12 +8120,11 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub // youBy - przenioslem, bo diesel tez moze miec turbo if( Vehicle.TurboTest > 0 ) { // udawanie turbo: - auto const pitch_diesel{(Vehicle.EngineType == TEngineType::DieselEngine || Vehicle.EngineType == TEngineType::DieselElectric) ? Vehicle.enrot / Vehicle.dizel_nmax * Vehicle.dizel_fill : 1}; + auto const pitch_diesel{(Vehicle.EngineType == TEngineType::DieselEngine || Vehicle.EngineType == TEngineType::DieselElectric) ? std::pow(Vehicle.enrot / Vehicle.dizel_nmax, 2.0) * Vehicle.dizel_fill : 1}; auto const goalpitch { std::max( 0.025, ( /*engine_volume **/ pitch_diesel + engine_turbo.m_frequencyoffset ) * engine_turbo.m_frequencyfactor ) }; - auto const goalvolume { ( - ( ( Vehicle.MainCtrlPos >= Vehicle.TurboTest ) && ( Vehicle.enrot > 0.1 ) ) ? - std::max( 0.0, ( engine_turbo_pitch + engine_turbo.m_amplitudeoffset ) * engine_turbo.m_amplitudefactor ) : - 0.0 ) }; + auto const goalvolume{ + ((Vehicle.MainCtrlPos >= Vehicle.TurboTest) && (Vehicle.enrot > 0.1)) ? std::max(0.0, (engine_turbo_pitch + engine_turbo.m_amplitudeoffset) * engine_turbo.m_amplitudefactor) : 0.0}; + auto const currentvolume { engine_turbo.gain() }; auto const changerate { 0.4 * Deltatime }; From c8b184ff62213ecc606cf4dddd4a2aa66e2762a4 Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 18 Feb 2025 06:55:12 +0100 Subject: [PATCH 73/88] Revert "Tweaked turbo sound calculation logic" This reverts commit 9bdc064ef309d7cd99d24ae0fad07c5120d5270f. --- DynObj.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index dbc88d9a..ecd017cc 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -8120,11 +8120,12 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub // youBy - przenioslem, bo diesel tez moze miec turbo if( Vehicle.TurboTest > 0 ) { // udawanie turbo: - auto const pitch_diesel{(Vehicle.EngineType == TEngineType::DieselEngine || Vehicle.EngineType == TEngineType::DieselElectric) ? std::pow(Vehicle.enrot / Vehicle.dizel_nmax, 2.0) * Vehicle.dizel_fill : 1}; + auto const pitch_diesel{(Vehicle.EngineType == TEngineType::DieselEngine || Vehicle.EngineType == TEngineType::DieselElectric) ? Vehicle.enrot / Vehicle.dizel_nmax * Vehicle.dizel_fill : 1}; auto const goalpitch { std::max( 0.025, ( /*engine_volume **/ pitch_diesel + engine_turbo.m_frequencyoffset ) * engine_turbo.m_frequencyfactor ) }; - auto const goalvolume{ - ((Vehicle.MainCtrlPos >= Vehicle.TurboTest) && (Vehicle.enrot > 0.1)) ? std::max(0.0, (engine_turbo_pitch + engine_turbo.m_amplitudeoffset) * engine_turbo.m_amplitudefactor) : 0.0}; - + auto const goalvolume { ( + ( ( Vehicle.MainCtrlPos >= Vehicle.TurboTest ) && ( Vehicle.enrot > 0.1 ) ) ? + std::max( 0.0, ( engine_turbo_pitch + engine_turbo.m_amplitudeoffset ) * engine_turbo.m_amplitudefactor ) : + 0.0 ) }; auto const currentvolume { engine_turbo.gain() }; auto const changerate { 0.4 * Deltatime }; From 33b85baf778895a5dd3a244f1fa9fcaa21710ddb Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 24 Feb 2025 22:02:01 +0100 Subject: [PATCH 74/88] Add discord-rpc library --- .gitmodules | 4 ++++ CMakeLists.txt | 6 ++++++ ref/discord-rpc | 1 + 3 files changed, 11 insertions(+) create mode 160000 ref/discord-rpc diff --git a/.gitmodules b/.gitmodules index 1d7e30cc..beb00b27 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ [submodule "ref/asio"] path = ref/asio url = https://github.com/chriskohlhoff/asio.git +[submodule "ref/discord-rpc"] + path = ref/discord-rpc + url = https://github.com/discord/discord-rpc.git + branch = v3.4.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 115423b9..e87ed77f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -429,6 +429,12 @@ target_link_libraries(${PROJECT_NAME} Threads::Threads) find_package(GLM REQUIRED) include_directories(${GLM_INCLUDE_DIR}) +# add ref/discord-rpc to the project in the same way other dependencies are added +add_subdirectory(ref/discord-rpc) +target_link_libraries(${PROJECT_NAME} discord-rpc) + + + find_package(OpenAL REQUIRED) if (TARGET OpenAL::OpenAL) target_link_libraries(${PROJECT_NAME} OpenAL::OpenAL) diff --git a/ref/discord-rpc b/ref/discord-rpc new file mode 160000 index 00000000..963aa9f3 --- /dev/null +++ b/ref/discord-rpc @@ -0,0 +1 @@ +Subproject commit 963aa9f3e5ce81a4682c6ca3d136cddda614db33 From c986b60895cf37a8c6a539a0a5797cf31c74fed5 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 24 Feb 2025 22:30:23 +0100 Subject: [PATCH 75/88] Mover reload function fix --- McZapkie/Mover.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 4319af90..deb8492e 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -12500,7 +12500,7 @@ bool TMoverParameters::reload_FIZ() { // failed to reload - exit simulator ErrorLog("[DEV] Failed to reload fiz for vehicle " + Name); } - + return true; } namespace simulation { From c7b40ca7ae25e820d04c3587110696cad481b1be Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 24 Feb 2025 22:30:35 +0100 Subject: [PATCH 76/88] Add Discord RPC integration --- Globals.h | 8 ++++++++ application.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/Globals.h b/Globals.h index 9225f555..16c9b3b7 100644 --- a/Globals.h +++ b/Globals.h @@ -16,6 +16,7 @@ http://mozilla.org/MPL/2.0/. #include "light.h" #include "utilities.h" #include "motiontelemetry.h" +#include "ref/discord-rpc/include/discord_rpc.h" #ifdef WITH_UART #include "uart.h" #endif @@ -23,10 +24,17 @@ http://mozilla.org/MPL/2.0/. #include "zmq_input.h" #endif +struct DiscordData +{ + DiscordRichPresence dcRcp; + char *scnName = ""; +}; + struct global_settings { // members // data items // TODO: take these out of the settings + DiscordData dcData; bool shiftState{ false }; //m7todo: brzydko bool ctrlState{ false }; bool altState{ false }; diff --git a/application.cpp b/application.cpp index ccd49af3..3bd1b63e 100644 --- a/application.cpp +++ b/application.cpp @@ -29,6 +29,8 @@ http://mozilla.org/MPL/2.0/. #include "Timer.h" #include "dictionary.h" #include "version_info.h" +#include "ref/discord-rpc/include/discord_rpc.h" +#include #ifdef _WIN32 #pragma comment (lib, "dsound.lib") @@ -239,6 +241,43 @@ eu07_application::init( int Argc, char *Argv[] ) { if (!init_network()) return -1; + // initialize discord-rpc + WriteLog("Initializing Discord Rich Presence..."); + static const char *discord_app_id = "1343662664504840222"; + DiscordEventHandlers handlers; + memset(&handlers, 0, sizeof(handlers)); + Discord_Initialize(discord_app_id, &handlers, 1, nullptr); + + std::string rpcScnName = Global.SceneryFile; + if (rpcScnName[0] == '$') + rpcScnName.erase(0, 1); + rpcScnName.erase(rpcScnName.size() - 4, 4); + if (rpcScnName.find('_') != std::string::npos) + { + std::replace(rpcScnName.begin(), rpcScnName.end(), '_', ' '); + } + + // calculate startup timestamp + auto now = std::chrono::system_clock::now(); + auto now_c = std::chrono::system_clock::to_time_t(now); + + // Init RPC object + static DiscordRichPresence discord_rpc; + memset(&discord_rpc, 0, sizeof(discord_rpc)); + // realworld timestamp from datetime + discord_rpc.startTimestamp = static_cast(now_c); + static std::string state = "Sceneria: " + rpcScnName; + discord_rpc.state = state.c_str(); + discord_rpc.details = "Ładowanie scenerii"; + discord_rpc.largeImageKey = "logo"; + discord_rpc.largeImageText = "MaSzyna"; + Global.dcData.dcRcp = discord_rpc; + + // First RPC upload + Discord_UpdatePresence(&Global.dcData.dcRcp); + + + return result; } @@ -282,6 +321,19 @@ eu07_application::run() { // main application loop while (!glfwWindowShouldClose( m_windows.front() ) && !m_modestack.empty()) { + // Discord RPC updater + if (simulation::is_ready) + { + std::string PlayerVehicle = simulation::Train->name(); + // make to upper + for (auto &c : PlayerVehicle) c = toupper(c); + + PlayerVehicle = "Prowadzi: " + PlayerVehicle; + Global.dcData.dcRcp.details = PlayerVehicle.c_str(); + Discord_UpdatePresence(&Global.dcData.dcRcp); + } + + Timer::subsystem.mainloop_total.start(); glfwPollEvents(); From 3688d667b043dc06faf2804044609ba561f62985 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 24 Feb 2025 23:08:47 +0100 Subject: [PATCH 77/88] Appveyor fix for dynamic git submodules --- appveyor.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index d42cd5f6..10365519 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,12 +1,12 @@ version: '{build}' image: Visual Studio 2017 clone_depth: 1 +init: + - git submodule update --init --recursive build_script: - ps: >- - cd ref - - git clone "https://github.com/chriskohlhoff/asio" --depth 1 --branch asio-1-16-1 -q - + cd ref + curl -o crashpad86.zip "http://get.backtrace.io/crashpad/builds/release/x86/crashpad-2020-07-01-release-x86-558c9614e3819179f30b92541450f5ac643afce5.zip" 7z x crashpad86.zip From b5f5190012726223e899a50b205eede5057ada66 Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 24 Feb 2025 23:10:46 +0100 Subject: [PATCH 78/88] I've missed one identation --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 10365519..120990b7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,8 +5,8 @@ init: - git submodule update --init --recursive build_script: - ps: >- - cd ref - + cd ref + curl -o crashpad86.zip "http://get.backtrace.io/crashpad/builds/release/x86/crashpad-2020-07-01-release-x86-558c9614e3819179f30b92541450f5ac643afce5.zip" 7z x crashpad86.zip From 8049a3d986cfbb405065c7ce2d2135497c5b278d Mon Sep 17 00:00:00 2001 From: Hirek Date: Mon, 24 Feb 2025 23:15:52 +0100 Subject: [PATCH 79/88] Maybe now it will work correcty (hope the last one) --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 120990b7..6644ef1a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,12 +1,12 @@ version: '{build}' image: Visual Studio 2017 clone_depth: 1 -init: - - git submodule update --init --recursive build_script: - ps: >- - cd ref + git submodule update --init --recursive + cd ref + curl -o crashpad86.zip "http://get.backtrace.io/crashpad/builds/release/x86/crashpad-2020-07-01-release-x86-558c9614e3819179f30b92541450f5ac643afce5.zip" 7z x crashpad86.zip From 078470cc686c1e9da2bb2d6cd32f27192e51356b Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 25 Feb 2025 07:45:27 +0100 Subject: [PATCH 80/88] Move DiscordRPC to separate threaad --- Globals.h | 14 +++---- application.cpp | 108 ++++++++++++++++++++++++++---------------------- application.h | 2 + 3 files changed, 68 insertions(+), 56 deletions(-) diff --git a/Globals.h b/Globals.h index 16c9b3b7..d54b9f4a 100644 --- a/Globals.h +++ b/Globals.h @@ -17,6 +17,8 @@ http://mozilla.org/MPL/2.0/. #include "utilities.h" #include "motiontelemetry.h" #include "ref/discord-rpc/include/discord_rpc.h" +#include + #ifdef WITH_UART #include "uart.h" #endif @@ -24,17 +26,15 @@ http://mozilla.org/MPL/2.0/. #include "zmq_input.h" #endif -struct DiscordData -{ - DiscordRichPresence dcRcp; - char *scnName = ""; -}; - struct global_settings { // members // data items // TODO: take these out of the settings - DiscordData dcData; + + /// + /// Mapa z watkami w formacie + /// + std::map threads = {}; bool shiftState{ false }; //m7todo: brzydko bool ctrlState{ false }; bool altState{ false }; diff --git a/application.cpp b/application.cpp index 3bd1b63e..6e145311 100644 --- a/application.cpp +++ b/application.cpp @@ -176,6 +176,60 @@ int eu07_application::run_crashgui() } return -1; } +void eu07_application::DiscordRPCService() +{ + // initialize discord-rpc + WriteLog("Initializing Discord Rich Presence..."); + static const char *discord_app_id = "1343662664504840222"; + DiscordEventHandlers handlers; + memset(&handlers, 0, sizeof(handlers)); + Discord_Initialize(discord_app_id, &handlers, 1, nullptr); + + std::string rpcScnName = Global.SceneryFile; + if (rpcScnName[0] == '$') + rpcScnName.erase(0, 1); + rpcScnName.erase(rpcScnName.size() - 4, 4); + if (rpcScnName.find('_') != std::string::npos) + { + std::replace(rpcScnName.begin(), rpcScnName.end(), '_', ' '); + } + + // calculate startup timestamp + auto now = std::chrono::system_clock::now(); + auto now_c = std::chrono::system_clock::to_time_t(now); + + // Init RPC object + static DiscordRichPresence discord_rpc; + memset(&discord_rpc, 0, sizeof(discord_rpc)); + // realworld timestamp from datetime + discord_rpc.startTimestamp = static_cast(now_c); + static std::string state = "Sceneria: " + rpcScnName; + discord_rpc.state = state.c_str(); + discord_rpc.details = "Ładowanie scenerii"; + discord_rpc.largeImageKey = "logo"; + discord_rpc.largeImageText = "MaSzyna"; + + // First RPC upload + Discord_UpdatePresence(&discord_rpc); + + // run loop + while (!glfwWindowShouldClose(m_windows.front()) && !m_modestack.empty()) + { + // Discord RPC updater + if (simulation::is_ready) + { + std::string PlayerVehicle = simulation::Train->name(); + // make to upper + for (auto &c : PlayerVehicle) + c = toupper(c); + + PlayerVehicle = "Prowadzi: " + PlayerVehicle; + discord_rpc.details = PlayerVehicle.c_str(); + Discord_UpdatePresence(&discord_rpc); + } + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // update RPC every 5 secs + } +} int eu07_application::init( int Argc, char *Argv[] ) { @@ -241,46 +295,15 @@ eu07_application::init( int Argc, char *Argv[] ) { if (!init_network()) return -1; - // initialize discord-rpc - WriteLog("Initializing Discord Rich Presence..."); - static const char *discord_app_id = "1343662664504840222"; - DiscordEventHandlers handlers; - memset(&handlers, 0, sizeof(handlers)); - Discord_Initialize(discord_app_id, &handlers, 1, nullptr); - - std::string rpcScnName = Global.SceneryFile; - if (rpcScnName[0] == '$') - rpcScnName.erase(0, 1); - rpcScnName.erase(rpcScnName.size() - 4, 4); - if (rpcScnName.find('_') != std::string::npos) - { - std::replace(rpcScnName.begin(), rpcScnName.end(), '_', ' '); - } - - // calculate startup timestamp - auto now = std::chrono::system_clock::now(); - auto now_c = std::chrono::system_clock::to_time_t(now); - - // Init RPC object - static DiscordRichPresence discord_rpc; - memset(&discord_rpc, 0, sizeof(discord_rpc)); - // realworld timestamp from datetime - discord_rpc.startTimestamp = static_cast(now_c); - static std::string state = "Sceneria: " + rpcScnName; - discord_rpc.state = state.c_str(); - discord_rpc.details = "Ładowanie scenerii"; - discord_rpc.largeImageKey = "logo"; - discord_rpc.largeImageText = "MaSzyna"; - Global.dcData.dcRcp = discord_rpc; - - // First RPC upload - Discord_UpdatePresence(&Global.dcData.dcRcp); - - + // Run DiscordRPC service + std::thread sDiscordRPC(&eu07_application::DiscordRPCService, this); + Global.threads.emplace("DiscordRPC", std::move(sDiscordRPC)); return result; } + + double eu07_application::generate_sync() { if (Timer::GetDeltaTime() == 0.0) return 0.0; @@ -321,19 +344,6 @@ eu07_application::run() { // main application loop while (!glfwWindowShouldClose( m_windows.front() ) && !m_modestack.empty()) { - // Discord RPC updater - if (simulation::is_ready) - { - std::string PlayerVehicle = simulation::Train->name(); - // make to upper - for (auto &c : PlayerVehicle) c = toupper(c); - - PlayerVehicle = "Prowadzi: " + PlayerVehicle; - Global.dcData.dcRcp.details = PlayerVehicle.c_str(); - Discord_UpdatePresence(&Global.dcData.dcRcp); - } - - Timer::subsystem.mainloop_total.start(); glfwPollEvents(); diff --git a/application.h b/application.h index 4d6b2955..3cc73ff0 100644 --- a/application.h +++ b/application.h @@ -37,6 +37,8 @@ public: int run(); // issues request for a worker thread to perform specified task. returns: true if task was scheduled + + void eu07_application::DiscordRPCService(); // discord rich presence service function (runs as separate thread) bool request( python_taskqueue::task_request const &Task ); // ensures the main thread holds the python gil and can safely execute python calls From 016e4fed102b41a8fcd5e8f592667253292eee10 Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 25 Feb 2025 08:37:07 +0100 Subject: [PATCH 81/88] Make DiscordRPC strings localized --- application.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/application.cpp b/application.cpp index 6e145311..84a0bad8 100644 --- a/application.cpp +++ b/application.cpp @@ -31,6 +31,7 @@ http://mozilla.org/MPL/2.0/. #include "version_info.h" #include "ref/discord-rpc/include/discord_rpc.h" #include +#include "translation.h" #ifdef _WIN32 #pragma comment (lib, "dsound.lib") @@ -203,9 +204,9 @@ void eu07_application::DiscordRPCService() memset(&discord_rpc, 0, sizeof(discord_rpc)); // realworld timestamp from datetime discord_rpc.startTimestamp = static_cast(now_c); - static std::string state = "Sceneria: " + rpcScnName; + static std::string state = Translations.lookup_s("Scenery: ") + rpcScnName; discord_rpc.state = state.c_str(); - discord_rpc.details = "Ładowanie scenerii"; + discord_rpc.details = Translations.lookup_c("Loading scenery..."); discord_rpc.largeImageKey = "logo"; discord_rpc.largeImageText = "MaSzyna"; @@ -223,8 +224,24 @@ void eu07_application::DiscordRPCService() for (auto &c : PlayerVehicle) c = toupper(c); - PlayerVehicle = "Prowadzi: " + PlayerVehicle; + PlayerVehicle = Translations.lookup_s("Driving: ") + PlayerVehicle; discord_rpc.details = PlayerVehicle.c_str(); + + uint16_t playerTrainVelocity = simulation::Train->Dynamic()->GetVelocity(); + if (playerTrainVelocity > 1) + { + // ikonka ze jedziemy i nie spimy + discord_rpc.smallImageKey = "driving"; + std::string smallText = Translations.lookup_s("Speed: ") + std::to_string(playerTrainVelocity) + " km/h"; + discord_rpc.smallImageText = smallText.c_str(); + } + else + { + // krecimy postoj + discord_rpc.smallImageKey = "halt"; + discord_rpc.smallImageText = Translations.lookup_c("Stopped"); + } + Discord_UpdatePresence(&discord_rpc); } std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // update RPC every 5 secs From ca6c0f72e910bf11ddb55dc4500d218515d859d2 Mon Sep 17 00:00:00 2001 From: Hirek Date: Wed, 26 Feb 2025 17:49:21 +0100 Subject: [PATCH 82/88] Logging moved to separate thread --- Logs.cpp | 118 ++++++++++++++++++++++++++++++------------------ Logs.h | 2 +- application.cpp | 7 ++- thread_list.txt | 2 + 4 files changed, 82 insertions(+), 47 deletions(-) create mode 100644 thread_list.txt diff --git a/Logs.cpp b/Logs.cpp index 61d1c5ad..c24610c6 100644 --- a/Logs.cpp +++ b/Logs.cpp @@ -14,6 +14,7 @@ http://mozilla.org/MPL/2.0/. #include "winheaders.h" #include "utilities.h" #include "uilayer.h" +#include std::ofstream output; // standardowy "log.txt", można go wyłączyć std::ofstream errors; // lista błędów "errors.txt", zawsze działa @@ -68,61 +69,88 @@ std::string filename_scenery() { } } +// log service stacks +std::deque InfoStack; +std::deque ErrorStack; + + +void LogService() +{ + while (true) + { + // loop for logging + + // write logs and log.txt + while (!InfoStack.empty()) + { + char *msg = InfoStack.front(); // get first element of stack + InfoStack.pop_front(); + if (Global.iWriteLogEnabled & 1) + { + if (!output.is_open()) + { + + std::string const filename = (Global.MultipleLogs ? "logs/log (" + filename_scenery() + ") " + filename_date() + ".txt" : "log.txt"); + output.open(filename, std::ios::trunc); + } + output << msg << "\n"; + output.flush(); + } + + log_scrollback.emplace_back(std::string(msg)); + if (log_scrollback.size() > 200) + log_scrollback.pop_front(); + + if (Global.iWriteLogEnabled & 2) + { +#ifdef _WIN32 + // hunter-271211: pisanie do konsoli tylko, gdy nie jest ukrywana + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY); + DWORD wr = 0; + WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), msg, (DWORD)strlen(msg), &wr, NULL); + WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), endstring, (DWORD)strlen(endstring), &wr, NULL); +#else + printf("%s\n", msg); +#endif + } + } + + // write to errors.txt + while (!ErrorStack.empty()) + { + char *msg = ErrorStack.front(); + ErrorStack.pop_front(); + + if (!(Global.iWriteLogEnabled & 1)) + return; + + if (!errors.is_open()) + { + + std::string const filename = (Global.MultipleLogs ? "logs/errors (" + filename_scenery() + ") " + filename_date() + ".txt" : "errors.txt"); + errors.open(filename, std::ios::trunc); + errors << "EU07.EXE " + Global.asVersion << "\n"; + } + + errors << msg << "\n"; + errors.flush(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(5)); // dont burn cpu so much + } +} + void WriteLog( const char *str, logtype const Type ) { if( str == nullptr ) { return; } if( true == TestFlag( Global.DisabledLogTypes, static_cast( Type ) ) ) { return; } - - if (Global.iWriteLogEnabled & 1) { - if( !output.is_open() ) { - - std::string const filename = - ( Global.MultipleLogs ? - "logs/log (" + filename_scenery() + ") " + filename_date() + ".txt" : - "log.txt" ); - output.open( filename, std::ios::trunc ); - } - output << str << "\n"; - output.flush(); - } - - log_scrollback.emplace_back(std::string(str)); - if (log_scrollback.size() > 200) - log_scrollback.pop_front(); - - if( Global.iWriteLogEnabled & 2 ) { -#ifdef _WIN32 - // hunter-271211: pisanie do konsoli tylko, gdy nie jest ukrywana - SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), FOREGROUND_GREEN | FOREGROUND_INTENSITY ); - DWORD wr = 0; - WriteConsole( GetStdHandle( STD_OUTPUT_HANDLE ), str, (DWORD)strlen( str ), &wr, NULL ); - WriteConsole( GetStdHandle( STD_OUTPUT_HANDLE ), endstring, (DWORD)strlen( endstring ), &wr, NULL ); -#else - printf("%s\n", str); -#endif - } + InfoStack.emplace_back(strdup(str)); } void ErrorLog( const char *str, logtype const Type ) { if( str == nullptr ) { return; } if( true == TestFlag( Global.DisabledLogTypes, static_cast( Type ) ) ) { return; } - - if (!(Global.iWriteLogEnabled & 1)) - return; - - if (!errors.is_open()) { - - std::string const filename = - ( Global.MultipleLogs ? - "logs/errors (" + filename_scenery() + ") " + filename_date() + ".txt" : - "errors.txt" ); - errors.open( filename, std::ios::trunc ); - errors << "EU07.EXE " + Global.asVersion << "\n"; - } - - errors << str << "\n"; - errors.flush(); + ErrorStack.emplace_back(strdup(str)); }; void Error(const std::string &asMessage, bool box) diff --git a/Logs.h b/Logs.h index ebdb5719..f7bf5795 100644 --- a/Logs.h +++ b/Logs.h @@ -23,7 +23,7 @@ enum class logtype : unsigned int { traction = ( 1 << 9 ), powergrid = ( 1 << 10 ), }; - +void LogService(); void WriteLog( const char *str, logtype const Type = logtype::generic ); void Error( const std::string &asMessage, bool box = false ); void Error( const char* &asMessage, bool box = false ); diff --git a/application.cpp b/application.cpp index 84a0bad8..6dfb5811 100644 --- a/application.cpp +++ b/application.cpp @@ -259,6 +259,10 @@ eu07_application::init( int Argc, char *Argv[] ) { return result; } + // start logging service + std::thread sLoggingService(LogService); + Global.threads.emplace("LogService", std::move(sLoggingService)); + 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, " @@ -512,7 +516,8 @@ eu07_application::run() { std::this_thread::sleep_for( Global.minframetime - frametime ); } } - + Global.threads["LogService"].~thread(); // kill log service + Global.threads["DiscordRPC"].~thread(); // kill DiscordRPC service return 0; } diff --git a/thread_list.txt b/thread_list.txt new file mode 100644 index 00000000..71388293 --- /dev/null +++ b/thread_list.txt @@ -0,0 +1,2 @@ +- DiscordRPC - Thread for refreshing discord rich presence +- LogService - Service that logs data to files and console \ No newline at end of file From af50b7e77e132750dd44fe6239ea18427a8563b5 Mon Sep 17 00:00:00 2001 From: Hirek Date: Thu, 27 Feb 2025 01:08:52 +0100 Subject: [PATCH 83/88] Working threads counter in F12 panel --- driveruipanels.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/driveruipanels.cpp b/driveruipanels.cpp index 872cf244..b48e57ab 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -620,7 +620,7 @@ debug_panel::render() { ImGui::Checkbox( "Debug Traction", &DebugTractionFlag ); } render_section( "Camera", m_cameralines ); - render_section( "Gfx Renderer", m_rendererlines ); + render_section( "Gfx Renderer / Statistics", m_rendererlines ); render_section_settings(); render_section_developer(); // Developer tools #ifdef WITH_UART @@ -1470,6 +1470,14 @@ debug_panel::update_section_renderer( std::vector &Output ) { // renderer stats Output.emplace_back( GfxRenderer->info_times(), Global.UITextColor ); Output.emplace_back( GfxRenderer->info_stats(), Global.UITextColor ); + + // CPU related + Output.emplace_back("CPU:", Global.UITextColor); + + // thread counter + textline = "Running threads: " + std::to_string(Global.threads.size() + 1); + Output.emplace_back(textline, Global.UITextColor); + } bool From 1dc7d5085116c22269dc21ce8d6541419b26b34c Mon Sep 17 00:00:00 2001 From: Hirek Date: Thu, 27 Feb 2025 01:40:35 +0100 Subject: [PATCH 84/88] Move lowpoly and exterior loading to separate async threads --- DynObj.cpp | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index ecd017cc..5d43a1bb 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -32,6 +32,8 @@ http://mozilla.org/MPL/2.0/. #include "uitranscripts.h" #include "messaging.h" #include "Driver.h" +#include +#include // Ra: taki zapis funkcjonuje lepiej, ale może nie jest optymalny #define vWorldFront Math3D::vector3(0, 0, 1) @@ -5214,7 +5216,20 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co */ asModel = asBaseDir + asModel; // McZapkie 2002-07-20: dynamics maja swoje modele w dynamics/basedir Global.asCurrentTexturePath = asBaseDir; // biezaca sciezka do tekstur to dynamic/... - mdModel = TModelsManager::GetModel(asModel, true); + + // Load model asynchronously + std::future ModelLoadingThread = std::async(std::launch::async, + TModelsManager::GetModel, // Function + asModel, // std::string const& + true, // bool dynamic + true, // bool Logerrors + 0 // int uid + ); + //mdModel = TModelsManager::GetModel(asModel, true); + + std::future LowpolyLoaderThread; + bool promiseLowpolyModel = false; + if (ReplacableSkin != "none") { m_materialdata.assign( ReplacableSkin ); } @@ -5279,7 +5294,16 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co erase_leading_slashes( asModel ); asModel = asBaseDir + asModel; // McZapkie-200702 - dynamics maja swoje modele w dynamic/basedir Global.asCurrentTexturePath = asBaseDir; // biezaca sciezka do tekstur to dynamic/... - mdLowPolyInt = TModelsManager::GetModel(asModel, true); + + LowpolyLoaderThread = std::async(std::launch::async, + TModelsManager::GetModel, // Function + asModel, // std::string const& + true, // bool dynamic + true, // bool Logerrors + 0 // int uid + ); + promiseLowpolyModel = true; + //mdLowPolyInt = TModelsManager::GetModel(asModel, true); } else if(token == "coupleradapter:") { @@ -5832,6 +5856,11 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co } while( ( token != "" ) && ( token != "endmodels" ) ); + + // Wait for models + if (promiseLowpolyModel) + mdLowPolyInt = LowpolyLoaderThread.get(); + mdModel = ModelLoadingThread.get(); if( false == MoverParameters->LoadAttributes.empty() ) { // Ra: tu wczytywanie modelu ładunku jest w porządku From 8b3baa84fe03c9908924be88960174da0fe483fc Mon Sep 17 00:00:00 2001 From: Hirek Date: Fri, 28 Feb 2025 10:54:59 +0100 Subject: [PATCH 85/88] Add security locks for material manager thanks to @Milek7 for advice about material manager --- Globals.h | 2 ++ Model3d.cpp | 96 ++++++++++++++++++++++++++++------------------------- 2 files changed, 53 insertions(+), 45 deletions(-) diff --git a/Globals.h b/Globals.h index d54b9f4a..1056fd37 100644 --- a/Globals.h +++ b/Globals.h @@ -18,6 +18,7 @@ http://mozilla.org/MPL/2.0/. #include "motiontelemetry.h" #include "ref/discord-rpc/include/discord_rpc.h" #include +#include #ifdef WITH_UART #include "uart.h" @@ -35,6 +36,7 @@ struct global_settings { /// Mapa z watkami w formacie /// std::map threads = {}; + std::thread::id mainThreadId = std::this_thread::get_id(); bool shiftState{ false }; //m7todo: brzydko bool ctrlState{ false }; bool altState{ false }; diff --git a/Model3d.cpp b/Model3d.cpp index 13ad449d..1537ea60 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -29,6 +29,8 @@ Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others using namespace Mtable; +std::mutex materialLoadLock; + float TSubModel::fSquareDist = 0.f; std::uintptr_t TSubModel::iInstance; // numer renderowanego egzemplarza obiektu texture_handle const *TSubModel::ReplacableSkinId = NULL; @@ -377,52 +379,56 @@ std::pair TSubModel::Load( cParser &parser, bool dynamic ) if (!parser.expectToken("map:")) Error("Model map parse failure!"); - std::string material = parser.getToken(); - std::replace(material.begin(), material.end(), '\\', '/'); - if (material == "none") - { // rysowanie podanym kolorem - Name_Material( "colored" ); - m_material = GfxRenderer->Fetch_Material( m_materialname ); - iFlags |= 0x10; // rysowane w cyklu nieprzezroczystych - } - else if (material.find("replacableskin") != material.npos) - { // McZapkie-060702: zmienialne skory modelu - m_material = -1; - iFlags |= (Opacity < 0.999) ? 1 : 0x10; // zmienna tekstura 1 - } - else if (material == "-1") - { - m_material = -1; - iFlags |= (Opacity < 0.999) ? 1 : 0x10; // zmienna tekstura 1 - } - else if (material == "-2") - { - m_material = -2; - iFlags |= (Opacity < 0.999) ? 2 : 0x10; // zmienna tekstura 2 - } - else if (material == "-3") - { - m_material = -3; - iFlags |= (Opacity < 0.999) ? 4 : 0x10; // zmienna tekstura 3 - } - else if (material == "-4") - { - m_material = -4; - iFlags |= (Opacity < 0.999) ? 8 : 0x10; // zmienna tekstura 4 - } - else { - Name_Material(material); -/* - if( material.find_first_of( "/" ) == material.npos ) { - // jeśli tylko nazwa pliku, to dawać bieżącą ścieżkę do tekstur - material.insert( 0, Global.asCurrentTexturePath ); + materialLoadLock.lock(); + + std::string material = parser.getToken(); + std::replace(material.begin(), material.end(), '\\', '/'); + if (material == "none") + { // rysowanie podanym kolorem + Name_Material( "colored" ); + m_material = GfxRenderer->Fetch_Material( m_materialname ); + iFlags |= 0x10; // rysowane w cyklu nieprzezroczystych } -*/ - m_material = GfxRenderer->Fetch_Material( material ); - // renderowanie w cyklu przezroczystych tylko jeśli: - // 1. Opacity=0 (przejściowo <1, czy tam <100) - iFlags |= Opacity < 0.999f ? 0x20 : 0x10 ; // 0x20-przezroczysta, 0x10-nieprzezroczysta - }; + else if (material.find("replacableskin") != material.npos) + { // McZapkie-060702: zmienialne skory modelu + m_material = -1; + iFlags |= (Opacity < 0.999) ? 1 : 0x10; // zmienna tekstura 1 + } + else if (material == "-1") + { + m_material = -1; + iFlags |= (Opacity < 0.999) ? 1 : 0x10; // zmienna tekstura 1 + } + else if (material == "-2") + { + m_material = -2; + iFlags |= (Opacity < 0.999) ? 2 : 0x10; // zmienna tekstura 2 + } + else if (material == "-3") + { + m_material = -3; + iFlags |= (Opacity < 0.999) ? 4 : 0x10; // zmienna tekstura 3 + } + else if (material == "-4") + { + m_material = -4; + iFlags |= (Opacity < 0.999) ? 8 : 0x10; // zmienna tekstura 4 + } + else { + Name_Material(material); + /* + if( material.find_first_of( "/" ) == material.npos ) { + // jeśli tylko nazwa pliku, to dawać bieżącą ścieżkę do tekstur + material.insert( 0, Global.asCurrentTexturePath ); + } + */ + m_material = GfxRenderer->Fetch_Material( material ); + // renderowanie w cyklu przezroczystych tylko jeśli: + // 1. Opacity=0 (przejściowo <1, czy tam <100) + iFlags |= Opacity < 0.999f ? 0x20 : 0x10 ; // 0x20-przezroczysta, 0x10-nieprzezroczysta + }; + + materialLoadLock.unlock(); } else if (eType == TP_STARS) { From e010c67eabe6e633def53730079320966b10f311 Mon Sep 17 00:00:00 2001 From: Hirek Date: Fri, 28 Feb 2025 12:15:46 +0100 Subject: [PATCH 86/88] DiscordRPC fix when driving ghostview --- application.cpp | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/application.cpp b/application.cpp index 6dfb5811..08c9bfb6 100644 --- a/application.cpp +++ b/application.cpp @@ -219,28 +219,35 @@ void eu07_application::DiscordRPCService() // Discord RPC updater if (simulation::is_ready) { - std::string PlayerVehicle = simulation::Train->name(); - // make to upper - for (auto &c : PlayerVehicle) - c = toupper(c); + std::string PlayerVehicle; + if (simulation::Train != nullptr) + { + PlayerVehicle = simulation::Train->name(); + // make to upper + for (auto &c : PlayerVehicle) + c = toupper(c); + + PlayerVehicle = Translations.lookup_s("Driving: ") + PlayerVehicle; + discord_rpc.details = PlayerVehicle.c_str(); + + uint16_t playerTrainVelocity = simulation::Train->Dynamic()->GetVelocity(); + if (playerTrainVelocity > 1) + { + // ikonka ze jedziemy i nie spimy + discord_rpc.smallImageKey = "driving"; + std::string smallText = Translations.lookup_s("Speed: ") + std::to_string(playerTrainVelocity) + " km/h"; + discord_rpc.smallImageText = smallText.c_str(); + } + else + { + // krecimy postoj + discord_rpc.smallImageKey = "halt"; + discord_rpc.smallImageText = Translations.lookup_c("Stopped"); + } + } + - PlayerVehicle = Translations.lookup_s("Driving: ") + PlayerVehicle; - discord_rpc.details = PlayerVehicle.c_str(); - uint16_t playerTrainVelocity = simulation::Train->Dynamic()->GetVelocity(); - if (playerTrainVelocity > 1) - { - // ikonka ze jedziemy i nie spimy - discord_rpc.smallImageKey = "driving"; - std::string smallText = Translations.lookup_s("Speed: ") + std::to_string(playerTrainVelocity) + " km/h"; - discord_rpc.smallImageText = smallText.c_str(); - } - else - { - // krecimy postoj - discord_rpc.smallImageKey = "halt"; - discord_rpc.smallImageText = Translations.lookup_c("Stopped"); - } Discord_UpdatePresence(&discord_rpc); } From ce4c3b46f373a1c054d9fa8216ba33a864a66374 Mon Sep 17 00:00:00 2001 From: Hirek Date: Fri, 28 Feb 2025 13:54:10 +0100 Subject: [PATCH 87/88] Revert "Add security locks for material manager" This reverts commit 8b3baa84fe03c9908924be88960174da0fe483fc. --- Globals.h | 2 -- Model3d.cpp | 96 +++++++++++++++++++++++++---------------------------- 2 files changed, 45 insertions(+), 53 deletions(-) diff --git a/Globals.h b/Globals.h index 1056fd37..d54b9f4a 100644 --- a/Globals.h +++ b/Globals.h @@ -18,7 +18,6 @@ http://mozilla.org/MPL/2.0/. #include "motiontelemetry.h" #include "ref/discord-rpc/include/discord_rpc.h" #include -#include #ifdef WITH_UART #include "uart.h" @@ -36,7 +35,6 @@ struct global_settings { /// Mapa z watkami w formacie /// std::map threads = {}; - std::thread::id mainThreadId = std::this_thread::get_id(); bool shiftState{ false }; //m7todo: brzydko bool ctrlState{ false }; bool altState{ false }; diff --git a/Model3d.cpp b/Model3d.cpp index 1537ea60..13ad449d 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -29,8 +29,6 @@ Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others using namespace Mtable; -std::mutex materialLoadLock; - float TSubModel::fSquareDist = 0.f; std::uintptr_t TSubModel::iInstance; // numer renderowanego egzemplarza obiektu texture_handle const *TSubModel::ReplacableSkinId = NULL; @@ -379,56 +377,52 @@ std::pair TSubModel::Load( cParser &parser, bool dynamic ) if (!parser.expectToken("map:")) Error("Model map parse failure!"); - materialLoadLock.lock(); - - std::string material = parser.getToken(); - std::replace(material.begin(), material.end(), '\\', '/'); - if (material == "none") - { // rysowanie podanym kolorem - Name_Material( "colored" ); - m_material = GfxRenderer->Fetch_Material( m_materialname ); - iFlags |= 0x10; // rysowane w cyklu nieprzezroczystych + std::string material = parser.getToken(); + std::replace(material.begin(), material.end(), '\\', '/'); + if (material == "none") + { // rysowanie podanym kolorem + Name_Material( "colored" ); + m_material = GfxRenderer->Fetch_Material( m_materialname ); + iFlags |= 0x10; // rysowane w cyklu nieprzezroczystych + } + else if (material.find("replacableskin") != material.npos) + { // McZapkie-060702: zmienialne skory modelu + m_material = -1; + iFlags |= (Opacity < 0.999) ? 1 : 0x10; // zmienna tekstura 1 + } + else if (material == "-1") + { + m_material = -1; + iFlags |= (Opacity < 0.999) ? 1 : 0x10; // zmienna tekstura 1 + } + else if (material == "-2") + { + m_material = -2; + iFlags |= (Opacity < 0.999) ? 2 : 0x10; // zmienna tekstura 2 + } + else if (material == "-3") + { + m_material = -3; + iFlags |= (Opacity < 0.999) ? 4 : 0x10; // zmienna tekstura 3 + } + else if (material == "-4") + { + m_material = -4; + iFlags |= (Opacity < 0.999) ? 8 : 0x10; // zmienna tekstura 4 + } + else { + Name_Material(material); +/* + if( material.find_first_of( "/" ) == material.npos ) { + // jeśli tylko nazwa pliku, to dawać bieżącą ścieżkę do tekstur + material.insert( 0, Global.asCurrentTexturePath ); } - else if (material.find("replacableskin") != material.npos) - { // McZapkie-060702: zmienialne skory modelu - m_material = -1; - iFlags |= (Opacity < 0.999) ? 1 : 0x10; // zmienna tekstura 1 - } - else if (material == "-1") - { - m_material = -1; - iFlags |= (Opacity < 0.999) ? 1 : 0x10; // zmienna tekstura 1 - } - else if (material == "-2") - { - m_material = -2; - iFlags |= (Opacity < 0.999) ? 2 : 0x10; // zmienna tekstura 2 - } - else if (material == "-3") - { - m_material = -3; - iFlags |= (Opacity < 0.999) ? 4 : 0x10; // zmienna tekstura 3 - } - else if (material == "-4") - { - m_material = -4; - iFlags |= (Opacity < 0.999) ? 8 : 0x10; // zmienna tekstura 4 - } - else { - Name_Material(material); - /* - if( material.find_first_of( "/" ) == material.npos ) { - // jeśli tylko nazwa pliku, to dawać bieżącą ścieżkę do tekstur - material.insert( 0, Global.asCurrentTexturePath ); - } - */ - m_material = GfxRenderer->Fetch_Material( material ); - // renderowanie w cyklu przezroczystych tylko jeśli: - // 1. Opacity=0 (przejściowo <1, czy tam <100) - iFlags |= Opacity < 0.999f ? 0x20 : 0x10 ; // 0x20-przezroczysta, 0x10-nieprzezroczysta - }; - - materialLoadLock.unlock(); +*/ + m_material = GfxRenderer->Fetch_Material( material ); + // renderowanie w cyklu przezroczystych tylko jeśli: + // 1. Opacity=0 (przejściowo <1, czy tam <100) + iFlags |= Opacity < 0.999f ? 0x20 : 0x10 ; // 0x20-przezroczysta, 0x10-nieprzezroczysta + }; } else if (eType == TP_STARS) { From cbfe01d049061a8f202ad93ac39cd8ad256b0881 Mon Sep 17 00:00:00 2001 From: Hirek Date: Fri, 28 Feb 2025 13:54:26 +0100 Subject: [PATCH 88/88] Revert "Move lowpoly and exterior loading to separate async threads" This reverts commit 1dc7d5085116c22269dc21ce8d6541419b26b34c. --- DynObj.cpp | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 5d43a1bb..ecd017cc 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -32,8 +32,6 @@ http://mozilla.org/MPL/2.0/. #include "uitranscripts.h" #include "messaging.h" #include "Driver.h" -#include -#include // Ra: taki zapis funkcjonuje lepiej, ale może nie jest optymalny #define vWorldFront Math3D::vector3(0, 0, 1) @@ -5216,20 +5214,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co */ asModel = asBaseDir + asModel; // McZapkie 2002-07-20: dynamics maja swoje modele w dynamics/basedir Global.asCurrentTexturePath = asBaseDir; // biezaca sciezka do tekstur to dynamic/... - - // Load model asynchronously - std::future ModelLoadingThread = std::async(std::launch::async, - TModelsManager::GetModel, // Function - asModel, // std::string const& - true, // bool dynamic - true, // bool Logerrors - 0 // int uid - ); - //mdModel = TModelsManager::GetModel(asModel, true); - - std::future LowpolyLoaderThread; - bool promiseLowpolyModel = false; - + mdModel = TModelsManager::GetModel(asModel, true); if (ReplacableSkin != "none") { m_materialdata.assign( ReplacableSkin ); } @@ -5294,16 +5279,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co erase_leading_slashes( asModel ); asModel = asBaseDir + asModel; // McZapkie-200702 - dynamics maja swoje modele w dynamic/basedir Global.asCurrentTexturePath = asBaseDir; // biezaca sciezka do tekstur to dynamic/... - - LowpolyLoaderThread = std::async(std::launch::async, - TModelsManager::GetModel, // Function - asModel, // std::string const& - true, // bool dynamic - true, // bool Logerrors - 0 // int uid - ); - promiseLowpolyModel = true; - //mdLowPolyInt = TModelsManager::GetModel(asModel, true); + mdLowPolyInt = TModelsManager::GetModel(asModel, true); } else if(token == "coupleradapter:") { @@ -5856,11 +5832,6 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co } while( ( token != "" ) && ( token != "endmodels" ) ); - - // Wait for models - if (promiseLowpolyModel) - mdLowPolyInt = LowpolyLoaderThread.get(); - mdModel = ModelLoadingThread.get(); if( false == MoverParameters->LoadAttributes.empty() ) { // Ra: tu wczytywanie modelu ładunku jest w porządku