From 044e3c921a9e7dfaa3275524329712632e103059 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Tue, 2 Oct 2018 22:38:01 +0200 Subject: [PATCH 1/6] ai order list size increase, random overcast value, minor bug fixes --- Driver.h | 2 +- PyInt.cpp | 12 ++++++------ simulationstateserializer.cpp | 10 +++++++++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Driver.h b/Driver.h index b86f7fd4..dce4b815 100644 --- a/Driver.h +++ b/Driver.h @@ -167,7 +167,7 @@ static const bool Aggressive = true; static const bool Easyman = false; static const bool AIdriver = true; static const bool Humandriver = false; -static const int maxorders = 32; // ilość rozkazów w tabelce +static const int maxorders = 64; // ilość rozkazów w tabelce static const int maxdriverfails = 4; // ile błędów może zrobić AI zanim zmieni nastawienie extern bool WriteLogFlag; // logowanie parametrów fizycznych static const int BrakeAccTableSize = 20; diff --git a/PyInt.cpp b/PyInt.cpp index ff4ddeb3..95d44486 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -187,14 +187,14 @@ auto python_taskqueue::fetch_renderer( std::string const Renderer ) ->PyObject * auto const path { substr_path( Renderer ) }; auto const file { Renderer.substr( path.size() ) }; PyObject *renderer { nullptr }; - if( m_main == nullptr ) { - ErrorLog( "Python Renderer: __main__ module is missing" ); - goto cache_and_return; - } - + PyObject *rendererarguments { nullptr }; PyEval_AcquireLock(); { - PyObject *rendererarguments{ nullptr }; + if( m_main == nullptr ) { + ErrorLog( "Python Renderer: __main__ module is missing" ); + goto cache_and_return; + } + if( false == run_file( file, path ) ) { goto cache_and_return; } diff --git a/simulationstateserializer.cpp b/simulationstateserializer.cpp index ba327225..0671ba44 100644 --- a/simulationstateserializer.cpp +++ b/simulationstateserializer.cpp @@ -162,7 +162,15 @@ state_serializer::deserialize_atmo( cParser &Input, scene::scratch_data &Scratch std::string token { Input.getToken() }; if( token != "endatmo" ) { // optional overcast parameter - Global.Overcast = clamp( std::stof( token ), 0.f, 2.f ); + Global.Overcast = std::stof( token ); + if( Global.Overcast < 0.f ) { + // negative overcast means random value in range 0-abs(specified range) + Global.Overcast = + Random( + clamp( + std::abs( Global.Overcast ), + 0.f, 2.f ) ); + } // overcast drives weather so do a calculation here // NOTE: ugly, clean it up when we're done with world refactoring simulation::Environment.compute_weather(); From 682514fe7bf66b08832b44f9ec099440511ca489 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 4 Oct 2018 02:39:55 +0200 Subject: [PATCH 2/6] basic motor blowers implementation --- Driver.cpp | 5 + DynObj.cpp | 53 ++++++++++ DynObj.h | 1 + McZapkie/MOVER.h | 220 +++++++++++++++++++++------------------ McZapkie/Mover.cpp | 194 +++++++++++++++++++++++++++------- Train.cpp | 223 +++++++++++++++++++++++++++++++++++++++- Train.h | 13 ++- command.cpp | 8 +- command.h | 3 + driverkeyboardinput.cpp | 4 + drivermouseinput.cpp | 9 ++ driveruipanels.cpp | 4 +- translation.cpp | 13 ++- translation.h | 3 + 14 files changed, 609 insertions(+), 144 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index a4666bea..ddba2e45 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2440,6 +2440,11 @@ bool TController::PrepareEngine() mvOccupied->ConverterSwitch( true ); // w EN57 sprężarka w ra jest zasilana z silnikowego mvOccupied->CompressorSwitch( true ); + // enable motor blowers + mvOccupied->MotorBlowersSwitchOff( false, side::front ); + mvOccupied->MotorBlowersSwitch( true, side::front ); + mvOccupied->MotorBlowersSwitchOff( false, side::rear ); + mvOccupied->MotorBlowersSwitch( true, side::rear ); } } else diff --git a/DynObj.cpp b/DynObj.cpp index 360ff418..6f92ca1e 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -5257,6 +5257,35 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co } } + else if( token == "motorblower:" ) { + + sound_source blowertemplate { sound_placement::engine }; + blowertemplate.deserialize( parser, sound_type::single, sound_parameters::range | sound_parameters::amplitude | sound_parameters::frequency ); + blowertemplate.owner( this ); + + auto const amplitudedivisor = static_cast( + MoverParameters->MotorBlowers[ side::front ].speed > 0.f ? + MoverParameters->MotorBlowers[ side::front ].speed * MoverParameters->nmax * 60 + MoverParameters->Power * 3 : + MoverParameters->MotorBlowers[ side::front ].speed * -1 ); + blowertemplate.m_amplitudefactor /= amplitudedivisor; + blowertemplate.m_frequencyfactor /= amplitudedivisor; + + if( true == m_powertrainsounds.motors.empty() ) { + // fallback for cases without specified motor locations, convert sound template to a single sound source + m_powertrainsounds.motorblowers.emplace_back( blowertemplate ); + } + else { + // apply configuration to all defined motor blowers + for( auto &blower : m_powertrainsounds.motorblowers ) { + // combine potential x- and y-axis offsets of the sound template with z-axis offsets of individual blowers + auto bloweroffset { blowertemplate.offset() }; + bloweroffset.z = blower.offset().z; + blower = blowertemplate; + blower.offset( bloweroffset ); + } + } + } + else if( token == "inverter:" ) { // plik z dzwiekiem wentylatora, mnozniki i ofsety amp. i czest. m_powertrainsounds.inverter.deserialize( parser, sound_type::single, sound_parameters::range | sound_parameters::amplitude | sound_parameters::frequency ); @@ -5604,10 +5633,13 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co auto const offset { std::atof( token.c_str() ) * -1.f }; // NOTE: we skip setting owner of the sounds, it'll be done during individual sound deserialization sound_source motor { sound_placement::external }; // generally traction motor + sound_source motorblower { sound_placement::engine }; // associated motor blowers // add entry to the list auto const location { glm::vec3 { 0.f, 0.f, offset } }; motor.offset( location ); m_powertrainsounds.motors.emplace_back( motor ); + motorblower.offset( location ); + m_powertrainsounds.motorblowers.emplace_back( motorblower ); } } @@ -6710,6 +6742,27 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub motor.stop(); } } + // motor blowers + if( false == motorblowers.empty() ) { + for( auto &blowersound : motorblowers ) { + // match the motor blower and the sound source based on whether they're located in the front or the back of the vehicle + auto const &blower { Vehicle.MotorBlowers[ ( blowersound.offset().z > 0 ? side::front : side::rear ) ] }; + if( blower.revolutions > 1 ) { + + blowersound + .pitch( + true == blowersound.is_combined() ? + blower.revolutions * 0.01f : + blowersound.m_frequencyoffset + blowersound.m_frequencyfactor * blower.revolutions ) + .gain( blowersound.m_amplitudeoffset + blowersound.m_amplitudefactor * blower.revolutions ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + blowersound.stop(); + } + } + } + // inverter sounds if( Vehicle.EngineType == TEngineType::ElectricInductionMotor ) { if( Vehicle.InverterFrequency > 0.1 ) { diff --git a/DynObj.h b/DynObj.h index 0a19dee1..f0731d3a 100644 --- a/DynObj.h +++ b/DynObj.h @@ -327,6 +327,7 @@ private: struct powertrain_sounds { sound_source inverter { sound_placement::engine }; + std::vector motorblowers; std::vector motors; // generally traction motor(s) double motor_volume { 0.0 }; // MC: pomocnicze zeby gladziej silnik buczal float motor_momentum { 0.f }; // recent change in motor revolutions diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index fed359ee..57d948a1 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -166,6 +166,7 @@ enum class start_t { manual, automatic, manualwithautofallback, + converter, battery }; // recognized vehicle light locations and types; can be combined @@ -619,107 +620,122 @@ struct TCoupling { int sounds { 0 }; // sounds emitted by the coupling devices }; -// basic approximation of a fuel pump -// TODO: fuel consumption, optional automatic engine start after activation -struct fuel_pump { - - bool is_enabled { false }; // device is allowed/requested to operate - bool is_disabled { false }; // device is requested to stop - bool is_active { false }; // device is working - start_t start_type { start_t::manual }; -}; - -// basic approximation of a fuel pump -// TODO: fuel consumption, optional automatic engine start after activation -struct oil_pump { - - bool is_enabled { false }; // device is allowed/requested to operate - bool is_disabled { false }; // device is requested to stop - bool is_active { false }; // device is working - start_t start_type { start_t::manual }; - float resource_amount { 1.f }; - float pressure_minimum { 0.f }; // lowest acceptable working pressure - float pressure_maximum { 0.65f }; // oil pressure at maximum engine revolutions - float pressure_target { 0.f }; - float pressure_present { 0.f }; -}; - -struct water_pump { - - bool breaker { true }; // device is allowed to operate - bool is_enabled { false }; // device is requested to operate - bool is_disabled { false }; // device is requested to stop - bool is_active { false }; // device is working - start_t start_type { start_t::manual }; -}; - -struct water_heater { - - bool breaker { true }; // device is allowed to operate - bool is_enabled { false }; // device is requested to operate - bool is_active { false }; // device is working - bool is_damaged { false }; // device is damaged - - struct heater_config_t { - float temp_min { -1 }; // lowest accepted temperature - float temp_max { -1 }; // highest accepted temperature - } config; -}; - -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 }; -}; - class TMoverParameters { // Ra: wrapper na kod pascalowy, przejmujący jego funkcje Q: 20160824 - juz nie wrapper a klasa bazowa :) +private: +// types + + // 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 cooling_fan : public basic_device { + // config + float speed { 0.f }; // cooling fan rpm; either fraction of parent rpm, or absolute value if negative + // ld outputs + float revolutions { 0.f }; // current fan rpm + }; + + // 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 { true }; // device is allowed to operate + }; + + 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 { true }; // 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 }; + }; + public: double dMoveLen = 0.0; @@ -1040,6 +1056,7 @@ public: water_heater WaterHeater; bool WaterCircuitsLink { false }; // optional connection between water circuits heat_data dizel_heat; + std::array MotorBlowers; int BrakeCtrlPos = -2; /*nastawa hamulca zespolonego*/ double BrakeCtrlPosR = 0.0; /*nastawa hamulca zespolonego - plynna dla FV4a*/ @@ -1126,7 +1143,7 @@ public: 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*/ - bool UnBrake = false; /*w EZT - nacisniete odhamowywanie*/ + bool UnBrake = false; /*w EZT - nacisniete odhamowywanie*/ 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 @@ -1343,6 +1360,8 @@ public: bool FuelPumpSwitchOff( bool State, range_t const Notify = range_t::consist ); // fuel pump state toggle bool OilPumpSwitch( bool State, range_t const Notify = range_t::consist ); // oil pump state toggle bool OilPumpSwitchOff( bool State, range_t const Notify = range_t::consist ); // oil pump state toggle + bool MotorBlowersSwitch( bool State, side const Side, range_t const Notify = range_t::consist ); // traction motor fan state toggle + bool MotorBlowersSwitchOff( bool State, side const Side, range_t const Notify = range_t::consist ); // traction motor fan state toggle bool MainSwitch( bool const State, range_t const Notify = range_t::consist );/*! wylacznik glowny*/ bool ConverterSwitch( bool State, range_t const Notify = range_t::consist );/*! wl/wyl przetwornicy*/ bool CompressorSwitch( bool State, range_t const Notify = range_t::consist );/*! wl/wyl sprezarki*/ @@ -1353,6 +1372,7 @@ public: void WaterHeaterCheck( double const Timestep ); void FuelPumpCheck( double const Timestep ); void OilPumpCheck( double const Timestep ); + void MotorBlowersCheck( double const Timestep ); bool FuseOn(void); //bezpiecznik nadamiary bool FuseFlagCheck(void) const; // sprawdzanie flagi nadmiarowego void FuseOff(void); // wylaczenie nadmiarowego diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 34fd7fd3..02a2a78b 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1438,6 +1438,8 @@ void TMoverParameters::compute_movement_( double const Deltatime ) { SetFlag( SoundFlag, sound::relay ); } } + // traction motors + MotorBlowersCheck( Deltatime ); // uklady hamulcowe: ConverterCheck( Deltatime ); if (VeselVolume > 0) @@ -1534,6 +1536,11 @@ void TMoverParameters::WaterPumpCheck( double const Timestep ) { // water heater status check void TMoverParameters::WaterHeaterCheck( double const Timestep ) { + WaterHeater.is_damaged = ( + ( true == WaterHeater.is_damaged ) + || ( ( true == WaterHeater.is_active ) + && ( false == WaterPump.is_active ) ) ); + WaterHeater.is_active = ( ( false == WaterHeater.is_damaged ) && ( true == Battery ) @@ -1545,11 +1552,6 @@ void TMoverParameters::WaterHeaterCheck( double const Timestep ) { && ( dizel_heat.temperatura1 > WaterHeater.config.temp_max ) ) { WaterHeater.is_active = false; } - - WaterHeater.is_damaged = ( - ( true == WaterHeater.is_damaged ) - || ( ( true == WaterHeater.is_active ) - && ( false == WaterPump.is_active ) ) ); } // fuel pump status update @@ -1592,20 +1594,57 @@ void TMoverParameters::OilPumpCheck( double const Timestep ) { true == OilPump.is_active ? std::min( minpressure + 0.1f, OilPump.pressure_maximum ) : // slight pressure margin to give time to switch off the pump and start the engine 0.f ); - if( OilPump.pressure_present < OilPump.pressure_target ) { + if( OilPump.pressure < OilPump.pressure_target ) { // TODO: scale change rate from 0.01-0.05 with oil/engine temperature/idle time - OilPump.pressure_present = + OilPump.pressure = std::min( OilPump.pressure_target, - OilPump.pressure_present + ( enrot > 5.0 ? 0.05 : 0.035 ) * Timestep ); + OilPump.pressure + ( enrot > 5.0 ? 0.05 : 0.035 ) * Timestep ); } - if( OilPump.pressure_present > OilPump.pressure_target ) { - OilPump.pressure_present = + if( OilPump.pressure > OilPump.pressure_target ) { + OilPump.pressure = std::max( OilPump.pressure_target, - OilPump.pressure_present - 0.01 * Timestep ); + OilPump.pressure - 0.01 * Timestep ); + } + OilPump.pressure = clamp( OilPump.pressure, 0.f, 1.5f ); +} + +void TMoverParameters::MotorBlowersCheck( double const Timestep ) { + // activation check + for( auto &blower : MotorBlowers ) { + + blower.is_active = ( + // TODO: bind properly power source when ld is in place + ( blower.start_type == start_t::battery ? Battery : + blower.start_type == start_t::converter ? ConverterFlag : + Mains ) // power source + // breaker condition disabled until it's implemented in the class data +// && ( true == blower.breaker ) + && ( false == blower.is_disabled ) + && ( ( true == blower.is_active ) + || ( blower.start_type == start_t::manual ? blower.is_enabled : true ) ) ); + } + // update + for( auto &fan : MotorBlowers ) { + + auto const revolutionstarget { ( + fan.is_active ? + ( fan.speed > 0.f ? fan.speed * static_cast( enrot ) * 60 : fan.speed * -1 ) : + 0.f ) }; + + if( std::abs( fan.revolutions - revolutionstarget ) < 0.01f ) { + fan.revolutions = revolutionstarget; + continue; + } + if( revolutionstarget > 0.f ) { + auto const speedincreasecap { std::max( 50.f, fan.speed * 0.05f * -1 ) }; // 5% of fixed revolution speed, or 50 + fan.revolutions += clamp( revolutionstarget - fan.revolutions, speedincreasecap * -2, speedincreasecap ) * Timestep; + } + else { + fan.revolutions *= std::max( 0.0, 1.0 - Timestep ); + } } - OilPump.pressure_present = clamp( OilPump.pressure_present, 0.f, 1.5f ); } @@ -2608,6 +2647,60 @@ bool TMoverParameters::OilPumpSwitchOff( bool State, range_t const Notify ) { return ( OilPump.is_disabled != initialstate ); } +bool TMoverParameters::MotorBlowersSwitch( bool State, side const Side, range_t const Notify ) { + + auto &fan { MotorBlowers[ Side ] }; + + if( ( fan.start_type != start_t::manual ) + && ( fan.start_type != start_t::manualwithautofallback ) ) { + // automatic device ignores 'manual' state commands + return false; + } + + bool const initialstate { fan.is_enabled }; + + fan.is_enabled = State; + + if( Notify != range_t::local ) { + SendCtrlToNext( + ( Side == side::front ? "MotorBlowersFrontSwitch" : "MotorBlowersRearSwitch" ), + ( fan.is_enabled ? 1 : 0 ), + CabNo, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return ( fan.is_enabled != initialstate ); +} + +bool TMoverParameters::MotorBlowersSwitchOff( bool State, side const Side, range_t const Notify ) { + + auto &fan { MotorBlowers[ Side ] }; + + if( ( fan.start_type != start_t::manual ) + && ( fan.start_type != start_t::manualwithautofallback ) ) { + // automatic device ignores 'manual' state commands + return false; + } + + bool const initialstate { fan.is_disabled }; + + fan.is_disabled = State; + + if( Notify != range_t::local ) { + SendCtrlToNext( + ( Side == side::front ? "MotorBlowersFrontSwitchOff" : "MotorBlowersRearSwitchOff" ), + ( fan.is_disabled ? 1 : 0 ), + CabNo, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return ( fan.is_disabled != initialstate ); +} + // ************************************************************************************************* // Q: 20160713 // włączenie / wyłączenie obwodu głownego @@ -6022,7 +6115,7 @@ bool TMoverParameters::dizel_StartupCheck() { } // test the oil pump if( ( false == OilPump.is_active ) - || ( OilPump.pressure_present < OilPump.pressure_minimum ) ) { + || ( OilPump.pressure < OilPump.pressure_minimum ) ) { engineisready = false; if( OilPump.start_type == start_t::manual ) { // with manual pump control startup procedure is done only once per starter switch press @@ -8208,59 +8301,54 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { } extract_value( ConverterStartDelay, "ConverterStartDelay", line, "" ); + // devices + std::map starts { + { "Manual", start_t::manual }, + { "Automatic", start_t::automatic }, + { "Mixed", start_t::manualwithautofallback }, + { "Battery", start_t::battery }, + { "Converter", start_t::converter } }; // compressor { - std::map starts { - { "Manual", start_t::manual }, - { "Automatic", start_t::automatic } - }; auto lookup = starts.find( extract_value( "CompressorStart", line ) ); CompressorStart = lookup != starts.end() ? lookup->second : start_t::manual; } - // fuel pump { - std::map starts { - { "Manual", start_t::manual }, - { "Automatic", start_t::automatic }, - { "Mixed", start_t::manualwithautofallback } - }; auto lookup = starts.find( extract_value( "FuelStart", line ) ); FuelPump.start_type = lookup != starts.end() ? lookup->second : start_t::manual; } - // oil pump { - std::map starts { - { "Manual", start_t::manual }, - { "Automatic", start_t::automatic }, - { "Mixed", start_t::manualwithautofallback } - }; auto lookup = starts.find( extract_value( "OilStart", line ) ); OilPump.start_type = lookup != starts.end() ? lookup->second : start_t::manual; } - // water pump { - std::map starts { - { "Manual", start_t::manual }, - { "Battery", start_t::battery } - }; auto lookup = starts.find( extract_value( "WaterStart", line ) ); WaterPump.start_type = lookup != starts.end() ? lookup->second : start_t::manual; } + // traction motor fans + { + auto lookup = starts.find( extract_value( "MotorBlowersStart", line ) ); + MotorBlowers[side::front].start_type = + MotorBlowers[side::rear].start_type = + lookup != starts.end() ? + lookup->second : + start_t::manual; + } } void TMoverParameters::LoadFIZ_Blending(std::string const &line) { @@ -8513,6 +8601,10 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) { extract_value( WaterHeater.config.temp_min, "HeaterMinTemperature", Input, "" ); extract_value( WaterHeater.config.temp_max, "HeaterMaxTemperature", Input, "" ); } + + // traction motors + extract_value( MotorBlowers[ side::front ].speed, "MotorBlowersSpeed", Input, "" ); + MotorBlowers[ side::rear ] = MotorBlowers[ side::front ]; } void TMoverParameters::LoadFIZ_Switches( std::string const &Input ) { @@ -9289,7 +9381,39 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C } OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); } - else if (Command == "MainSwitch") + else if( Command == "MotorBlowersFrontSwitch" ) { + if( ( MotorBlowers[ side::front ].start_type != start_t::manual ) + && ( MotorBlowers[ side::front ].start_type != start_t::manualwithautofallback ) ) { + // automatic device ignores 'manual' state commands + MotorBlowers[side::front].is_enabled = ( CValue1 == 1 ); + } + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } + else if( Command == "MotorBlowersFrontSwitchOff" ) { + if( ( MotorBlowers[ side::front ].start_type != start_t::manual ) + && ( MotorBlowers[ side::front ].start_type != start_t::manualwithautofallback ) ) { + // automatic device ignores 'manual' state commands + MotorBlowers[side::front].is_disabled = ( CValue1 == 1 ); + } + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } + else if( Command == "MotorBlowersRearSwitch" ) { + if( ( MotorBlowers[ side::rear ].start_type != start_t::manual ) + && ( MotorBlowers[ side::rear ].start_type != start_t::manualwithautofallback ) ) { + // automatic device ignores 'manual' state commands + MotorBlowers[side::rear].is_enabled = ( CValue1 == 1 ); + } + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } + else if( Command == "MotorBlowersRearSwitchOff" ) { + if( ( MotorBlowers[ side::rear ].start_type != start_t::manual ) + && ( MotorBlowers[ side::rear ].start_type != start_t::manualwithautofallback ) ) { + // automatic device ignores 'manual' state commands + MotorBlowers[side::rear].is_disabled = ( CValue1 == 1 ); + } + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } + else if (Command == "MainSwitch") { if (CValue1 == 1) { diff --git a/Train.cpp b/Train.cpp index 22ff5ab3..e9b0312a 100644 --- a/Train.cpp +++ b/Train.cpp @@ -266,6 +266,9 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::compressorenable, &TTrain::OnCommand_compressorenable }, { user_command::compressordisable, &TTrain::OnCommand_compressordisable }, { user_command::compressortogglelocal, &TTrain::OnCommand_compressortogglelocal }, + { user_command::motorblowerstogglefront, &TTrain::OnCommand_motorblowerstogglefront }, + { user_command::motorblowerstogglerear, &TTrain::OnCommand_motorblowerstogglerear }, + { user_command::motorblowersdisableall, &TTrain::OnCommand_motorblowersdisableall }, { user_command::motorconnectorsopen, &TTrain::OnCommand_motorconnectorsopen }, { user_command::motorconnectorsclose, &TTrain::OnCommand_motorconnectorsclose }, { user_command::motordisconnect, &TTrain::OnCommand_motordisconnect }, @@ -2770,6 +2773,191 @@ void TTrain::OnCommand_compressortogglelocal( TTrain *Train, command_data const } } +void TTrain::OnCommand_motorblowerstogglefront( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersFrontButton.type() == TGaugeType::push ) { + // impulse switch + // currently there's no off button so we always try to turn it on + OnCommand_motorblowersenablefront( Train, Command ); + } + else { + // two-state switch + if( Command.action == GLFW_RELEASE ) { return; } + + if( false == Train->mvControlled->MotorBlowers[side::front].is_enabled ) { + // turn on + OnCommand_motorblowersenablefront( Train, Command ); + } + else { + //turn off + OnCommand_motorblowersdisablefront( Train, Command ); + } + } +} + +void TTrain::OnCommand_motorblowersenablefront( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersFrontButton.type() == TGaugeType::push ) { + // impulse switch + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersFrontButton.UpdateValue( 1.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( true, side::front ); + } + else if( Command.action == GLFW_RELEASE ) { + // visual feedback + Train->ggMotorBlowersFrontButton.UpdateValue( 0.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( false, side::front ); + } + } + else { + // two-state switch, only cares about press events + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersFrontButton.UpdateValue( 1.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( true, side::front ); + Train->mvControlled->MotorBlowersSwitchOff( false, side::front ); + } + } +} + +void TTrain::OnCommand_motorblowersdisablefront( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersFrontButton.type() == TGaugeType::push ) { + // impulse switch + // currently there's no disable return type switch + return; + } + else { + // two-state switch, only cares about press events + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersFrontButton.UpdateValue( 0.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( false, side::front ); + Train->mvControlled->MotorBlowersSwitchOff( true, side::front ); + } + } +} + +void TTrain::OnCommand_motorblowerstogglerear( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersRearButton.type() == TGaugeType::push ) { + // impulse switch + // currently there's no off button so we always try to turn it on + OnCommand_motorblowersenablerear( Train, Command ); + } + else { + // two-state switch + if( Command.action == GLFW_RELEASE ) { return; } + + if( false == Train->mvControlled->MotorBlowers[ side::rear ].is_enabled ) { + // turn on + OnCommand_motorblowersenablerear( Train, Command ); + } + else { + //turn off + OnCommand_motorblowersdisablerear( Train, Command ); + } + } +} + +void TTrain::OnCommand_motorblowersenablerear( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersRearButton.type() == TGaugeType::push ) { + // impulse switch + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersRearButton.UpdateValue( 1.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( true, side::rear ); + } + else if( Command.action == GLFW_RELEASE ) { + // visual feedback + Train->ggMotorBlowersRearButton.UpdateValue( 0.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( false, side::rear ); + } + } + else { + // two-state switch, only cares about press events + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersRearButton.UpdateValue( 1.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( true, side::rear ); + Train->mvControlled->MotorBlowersSwitchOff( false, side::rear ); + } + } +} + +void TTrain::OnCommand_motorblowersdisablerear( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersRearButton.type() == TGaugeType::push ) { + // impulse switch + // currently there's no disable return type switch + return; + } + else { + // two-state switch, only cares about press events + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersRearButton.UpdateValue( 0.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( false, side::rear ); + Train->mvControlled->MotorBlowersSwitchOff( true, side::rear ); + } + } +} + +void TTrain::OnCommand_motorblowersdisableall( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersAllOffButton.type() == TGaugeType::push ) { + // impulse switch + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersAllOffButton.UpdateValue( 1.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitchOff( true, side::front ); + Train->mvControlled->MotorBlowersSwitchOff( true, side::rear ); + } + else if( Command.action == GLFW_RELEASE ) { + // visual feedback + Train->ggMotorBlowersAllOffButton.UpdateValue( 0.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitchOff( false, side::front ); + Train->mvControlled->MotorBlowersSwitchOff( false, side::rear ); + } + } + else { + // two-state switch, only cares about press events + // NOTE: generally this switch doesn't come in two-state form + if( Command.action == GLFW_PRESS ) { + if( Train->ggMotorBlowersAllOffButton.GetDesiredValue() < 0.5f ) { + // switch is off, activate + Train->mvControlled->MotorBlowersSwitchOff( true, side::front ); + Train->mvControlled->MotorBlowersSwitchOff( true, side::rear ); + // visual feedback + Train->ggMotorBlowersRearButton.UpdateValue( 1.f, Train->dsbSwitch ); + } + else { + // deactivate + Train->mvControlled->MotorBlowersSwitchOff( false, side::front ); + Train->mvControlled->MotorBlowersSwitchOff( false, side::rear ); + // visual feedback + Train->ggMotorBlowersRearButton.UpdateValue( 0.f, Train->dsbSwitch ); + } + } + } +} + void TTrain::OnCommand_motorconnectorsopen( TTrain *Train, command_data const &Command ) { // TODO: don't rely on presense of 3d model to determine presence of the switch @@ -5043,7 +5231,7 @@ bool TTrain::Update( double const Deltatime ) ggWater1TempB.Update(); } if( ggOilPressB.SubModel ) { - ggOilPressB.UpdateValue( tmp->MoverParameters->OilPump.pressure_present ); + ggOilPressB.UpdateValue( tmp->MoverParameters->OilPump.pressure ); ggOilPressB.Update(); } } @@ -5344,7 +5532,7 @@ bool TTrain::Update( double const Deltatime ) btLampkaRearRightEndLight.Turn( ( mvOccupied->iLights[ side::rear ] & light::redmarker_right ) != 0 ); // others btLampkaMalfunction.Turn( mvControlled->dizel_heat.PA ); - btLampkaMotorBlowers.Turn( mvControlled->RventRot > 0.1 ); + btLampkaMotorBlowers.Turn( ( mvControlled->MotorBlowers[ side::front ].is_active ) && ( mvControlled->MotorBlowers[ side::rear ].is_active ) ); } else { // wylaczone @@ -5704,6 +5892,9 @@ bool TTrain::Update( double const Deltatime ) ggWaterCircuitsLinkButton.Update(); ggFuelPumpButton.Update(); ggOilPumpButton.Update(); + ggMotorBlowersFrontButton.Update(); + ggMotorBlowersRearButton.Update(); + ggMotorBlowersAllOffButton.Update(); //------ } // wyprowadzenie sygnałów dla haslera na PoKeys (zaznaczanie na taśmie) @@ -6927,6 +7118,9 @@ void TTrain::clear_cab_controls() ggWaterCircuitsLinkButton.Clear(); ggFuelPumpButton.Clear(); ggOilPumpButton.Clear(); + ggMotorBlowersFrontButton.Clear(); + ggMotorBlowersRearButton.Clear(); + ggMotorBlowersAllOffButton.Clear(); btLampkaPrzetw.Clear(); btLampkaPrzetwB.Clear(); @@ -7271,6 +7465,26 @@ void TTrain::set_cab_controls() { 1.0 : 0.0 ); } + // traction motor fans + if( ggMotorBlowersFrontButton.type() != TGaugeType::push ) { + ggMotorBlowersFrontButton.PutValue( + mvControlled->MotorBlowers[side::front].is_enabled ? + 1.0 : + 0.0 ); + } + if( ggMotorBlowersRearButton.type() != TGaugeType::push ) { + ggMotorBlowersRearButton.PutValue( + mvControlled->MotorBlowers[side::rear].is_enabled ? + 1.0 : + 0.0 ); + } + if( ggMotorBlowersAllOffButton.type() != TGaugeType::push ) { + ggMotorBlowersAllOffButton.PutValue( + ( mvControlled->MotorBlowers[side::front].is_disabled + || mvControlled->MotorBlowers[ side::front ].is_disabled ) ? + 1.0 : + 0.0 ); + } // we reset all indicators, as they're set during the update pass // TODO: when cleaning up break setting indicator state into a separate function, so we can reuse it @@ -7465,6 +7679,9 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { "fuelpump_sw:", ggFuelPumpButton }, { "oilpump_sw:", ggOilPumpButton }, { "oilpressb:", ggOilPressB }, + { "motorblowersfront_sw:", ggMotorBlowersFrontButton }, + { "motorblowersrear_sw:", ggMotorBlowersRearButton }, + { "motorblowersalloff_sw:", ggMotorBlowersAllOffButton }, { "radio_sw:", ggRadioButton }, { "radiochannel_sw:", ggRadioChannelSelector }, { "radiochannelprev_sw:", ggRadioChannelPrevious }, @@ -7630,7 +7847,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con // oil pressure auto &gauge = Cabine[ Cabindex ].Gauge( -1 ); // pierwsza wolna gałka gauge.Load( Parser, DynamicObject, DynamicObject->mdKabina, nullptr ); - gauge.AssignFloat( &mvControlled->OilPump.pressure_present ); + gauge.AssignFloat( &mvControlled->OilPump.pressure ); } else if( Label == "oiltemp:" ) { // oil temperature diff --git a/Train.h b/Train.h index 2a84462d..dd6296dd 100644 --- a/Train.h +++ b/Train.h @@ -251,6 +251,13 @@ class TTrain static void OnCommand_compressorenable( TTrain *Train, command_data const &Command ); static void OnCommand_compressordisable( TTrain *Train, command_data const &Command ); static void OnCommand_compressortogglelocal( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowerstogglefront( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowersenablefront( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowersdisablefront( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowerstogglerear( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowersenablerear( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowersdisablerear( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowersdisableall( TTrain *Train, command_data const &Command ); static void OnCommand_motorconnectorsopen( TTrain *Train, command_data const &Command ); static void OnCommand_motorconnectorsclose( TTrain *Train, command_data const &Command ); static void OnCommand_motordisconnect( TTrain *Train, command_data const &Command ); @@ -322,6 +329,7 @@ class TTrain static void OnCommand_cabchangebackward( TTrain *Train, command_data const &Command ); static void OnCommand_generictoggle( TTrain *Train, command_data const &Command ); + // members TDynamicObject *DynamicObject { nullptr }; // przestawia zmiana pojazdu [F5] TMoverParameters *mvControlled { nullptr }; // człon, w którym sterujemy silnikiem @@ -350,7 +358,7 @@ public: // reszta może by?publiczna TGauge ggI3B; TGauge ggItotalB; - TGauge ggOilPressB; + TGauge ggOilPressB; // other unit oil pressure indicator TGauge ggWater1TempB; // McZapkie: definicje regulatorow @@ -458,6 +466,9 @@ public: // reszta może by?publiczna TGauge ggWaterCircuitsLinkButton; TGauge ggFuelPumpButton; // fuel pump switch TGauge ggOilPumpButton; // fuel pump switch + TGauge ggMotorBlowersFrontButton; // front traction motor fan switch + TGauge ggMotorBlowersRearButton; // rear traction motor fan switch + TGauge ggMotorBlowersAllOffButton; // motor fans shutdown switch TButton btLampkaPoslizg; TButton btLampkaStyczn; diff --git a/command.cpp b/command.cpp index d1e7e768..6edade69 100644 --- a/command.cpp +++ b/command.cpp @@ -214,10 +214,14 @@ commanddescription_sequence Commands_descriptions = { { "generictoggle9", command_target::vehicle }, { "batterytoggle", command_target::vehicle }, { "batteryenable", command_target::vehicle }, - { "batterydisable", command_target::vehicle } + { "batterydisable", command_target::vehicle }, + { "motorblowerstogglefront", command_target::vehicle }, + { "motorblowerstogglerear", command_target::vehicle }, + { "motorblowersdisableall", command_target::vehicle } + }; -} +} // simulation // posts specified command for specified recipient void diff --git a/command.h b/command.h index e6048bee..5f34b6fd 100644 --- a/command.h +++ b/command.h @@ -208,6 +208,9 @@ enum class user_command { batterytoggle, batteryenable, batterydisable, + motorblowerstogglefront, + motorblowerstogglerear, + motorblowersdisableall, none = -1 }; diff --git a/driverkeyboardinput.cpp b/driverkeyboardinput.cpp index efe49008..ae8aab93 100644 --- a/driverkeyboardinput.cpp +++ b/driverkeyboardinput.cpp @@ -217,6 +217,10 @@ driverkeyboard_input::default_bindings() { { user_command::batterytoggle, GLFW_KEY_J }, // batteryenable, // batterydisable, + { user_command::motorblowerstogglefront, GLFW_KEY_N | keymodifier::shift }, + { user_command::motorblowerstogglerear, GLFW_KEY_M | keymodifier::shift }, + { user_command::motorblowersdisableall, GLFW_KEY_M | keymodifier::control } + }; } diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index 235bc589..0a34c2f2 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -499,6 +499,15 @@ drivermouse_input::default_bindings() { { "oilpump_sw:", { user_command::oilpumptoggle, user_command::none } }, + { "motorblowersfront_sw:", { + user_command::motorblowerstogglefront, + user_command::none } }, + { "motorblowersrear_sw:", { + user_command::motorblowerstogglerear, + user_command::none } }, + { "motorblowersalloff_sw:", { + user_command::motorblowersdisableall, + user_command::none } }, { "main_off_bt:", { user_command::linebreakeropen, user_command::none } }, diff --git a/driveruipanels.cpp b/driveruipanels.cpp index 84b930f6..ceb9253f 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -414,7 +414,7 @@ debug_panel::update_section_vehicle( std::vector &Output ) { ( mover.CompressorFlag ? 'C' : ( false == mover.CompressorAllowLocal ? '-' : ( ( mover.CompressorAllow || mover.CompressorStart == start_t::automatic ) ? 'c' : '.' ) ) ), ( mover.CompressorGovernorLock ? '!' : '.' ), std::string( isplayervehicle ? locale::strings[ locale::string::debug_vehicle_radio ] + ( mover.Radio ? std::to_string( m_input.train->RadioChannel() ) : "-" ) : "" ).c_str(), - std::string( isdieselenginepowered ? locale::strings[ locale::string::debug_vehicle_oilpressure ] + to_string( mover.OilPump.pressure_present, 2 ) : "" ).c_str(), + std::string( isdieselenginepowered ? locale::strings[ locale::string::debug_vehicle_oilpressure ] + to_string( mover.OilPump.pressure, 2 ) : "" ).c_str(), // power transfers mover.Couplers[ side::front ].power_high.voltage, mover.Couplers[ side::front ].power_high.current, @@ -440,6 +440,8 @@ debug_panel::update_section_vehicle( std::vector &Output ) { std::abs( mover.enrot ) * 60, std::abs( mover.nrot ) * mover.Transmision.Ratio * 60, mover.RventRot * 60, + mover.MotorBlowers[side::front].revolutions, + mover.MotorBlowers[side::rear].revolutions, mover.dizel_heat.rpmw, mover.dizel_heat.rpmw2 ); diff --git a/translation.cpp b/translation.cpp index 52f255da..c9bc66f9 100644 --- a/translation.cpp +++ b/translation.cpp @@ -54,7 +54,7 @@ init() { "Devices: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nPower transfers: %.0f@%.0f%s%s%s%.0f@%.0f", " radio: ", " oil pressure: ", - "Controllers:\n master: %d(%d), secondary: %s\nEngine output: %.1f, current: %.0f\nRevolutions:\n engine: %.0f, motors: %.0f, ventilators: %.0f, fans: %.0f+%.0f", + "Controllers:\n master: %d(%d), secondary: %s\nEngine output: %.1f, current: %.0f\nRevolutions:\n engine: %.0f, motors: %.0f\n engine fans: %.0f, motor fans: %.0f+%.0f, cooling fans: %.0f+%.0f", " (shunt mode)", "\nTemperatures:\n engine: %.2f, oil: %.2f, water: %.2f%c%.2f", "Brakes:\n train: %.2f, independent: %.2f, delay: %s, load flag: %d\nBrake cylinder pressures:\n train: %.2f, independent: %.2f, status: 0x%.2x\nPipe pressures:\n brake: %.2f (hat: %.2f), main: %.2f, control: %.2f\nTank pressures:\n auxiliary: %.2f, main: %.2f, control: %.2f", @@ -80,6 +80,9 @@ init() { "water circuits link", "fuel pump", "oil pump", + "motor blowers A", + "motor blowers B", + "all motor blowers", "line breaker", "line breaker", "alerter", @@ -188,7 +191,7 @@ init() { "Urzadzenia: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nTransfer pradow: %.0f@%.0f%s%s%s%.0f@%.0f", " radio: ", " cisn.oleju: ", - "Nastawniki:\n glowny: %d(%d), dodatkowy: %s\nMoc silnika: %.1f, prad silnika: %.0f\nObroty:\n silnik: %.0f, motory: %.0f, went.silnika: %.0f, went.chlodnicy: %.0f+%.0f", + "Nastawniki:\n glowny: %d(%d), dodatkowy: %s\nMoc silnika: %.1f, prad silnika: %.0f\nObroty:\n silnik: %.0f, motory: %.0f\n went.silnika: %.0f, went.motorow: %.0f+%.0f, went.chlodnicy: %.0f+%.0f", " (tryb manewrowy)", "\nTemperatury:\n silnik: %.2f, olej: %.2f, woda: %.2f%c%.2f", "Hamulce:\n zespolony: %.2f, pomocniczy: %.2f, nastawa: %s, ladunek: %d\nCisnienie w cylindrach:\n zespolony: %.2f, pomocniczy: %.2f, status: 0x%.2x\nCisnienia w przewodach:\n glowny: %.2f (kapturek: %.2f), zasilajacy: %.2f, kontrolny: %.2f\nCisnienia w zbiornikach:\n pomocniczy: %.2f, glowny: %.2f, sterujacy: %.2f", @@ -214,6 +217,9 @@ init() { "zawor polaczenia obiegow wody", "pompa paliwa", "pompa oleju", + "wentylatory silnikow trakcyjnych A", + "wentylatory silnikow trakcyjnych B", + "wszystkie wentylatory silnikow trakcyjnych", "wylacznik szybki", "wylacznik szybki", "czuwak", @@ -320,6 +326,9 @@ init() { "watercircuitslink_sw:", "fuelpump_sw:", "oilpump_sw:", + "motorblowersfront_sw:", + "motorblowersrear_sw:", + "motorblowersalloff_sw:", "main_off_bt:", "main_on_bt:", "security_reset_bt:", diff --git a/translation.h b/translation.h index 2c39471e..4d40b762 100644 --- a/translation.h +++ b/translation.h @@ -69,6 +69,9 @@ enum string { cab_watercircuitslink_sw, cab_fuelpump_sw, cab_oilpump_sw, + cab_motorblowersfront_sw, + cab_motorblowersrear_sw, + cab_motorblowersalloff_sw, cab_main_off_bt, cab_main_on_bt, cab_security_reset_bt, From 8b1a26632d71cdd3619a84cd2316d20524fcdd46 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Fri, 5 Oct 2018 21:57:49 +0200 Subject: [PATCH 3/6] build 181005. automatic switch trackbed generation --- DynObj.cpp | 2 +- Globals.cpp | 5 + Globals.h | 1 + McZapkie/Mover.cpp | 7 +- Segment.cpp | 18 +- Segment.h | 2 +- Track.cpp | 1517 ++++++++++++++++----------------- Track.h | 14 +- renderer.cpp | 64 +- scene.cpp | 19 + simulationstateserializer.cpp | 12 +- version.h | 2 +- 12 files changed, 879 insertions(+), 784 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 6f92ca1e..dc833bd3 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2689,7 +2689,7 @@ void TDynamicObject::LoadUpdate() { } else if( MoverParameters->LoadAmount == 0 ) { // nie ma ładunku - MoverParameters->AssignLoad( "" ); +// MoverParameters->AssignLoad( "" ); mdLoad = nullptr; // erase bindings between lowpoly sections and potential load chunks placed inside them update_load_sections(); diff --git a/Globals.cpp b/Globals.cpp index 9b66abbf..a6a01fd6 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -369,6 +369,11 @@ global_settings::ConfigParse(cParser &Parser) { Parser >> splinefidelity; SplineFidelity = clamp( splinefidelity, 1.f, 4.f ); } + else if( token == "createswitchtrackbeds" ) { + // podwójna jasność ambient + Parser.getTokens(); + Parser >> CreateSwitchTrackbeds; + } else if( token == "gfx.resource.sweep" ) { Parser.getTokens(); diff --git a/Globals.h b/Globals.h index 8caf793a..2dc2c7c1 100644 --- a/Globals.h +++ b/Globals.h @@ -51,6 +51,7 @@ struct global_settings { // settings // filesystem bool bLoadTraction{ true }; + bool CreateSwitchTrackbeds { true }; std::string szTexturesTGA{ ".tga" }; // lista tekstur od TGA std::string szTexturesDDS{ ".dds" }; // lista tekstur od DDS std::string szDefaultExt{ szTexturesDDS }; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 02a2a78b..da6b7884 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -6640,15 +6640,14 @@ TMoverParameters::AssignLoad( std::string const &Name, float const Amount ) { } } - // can't mix load types, at least for the time being - if( ( LoadAmount > 0 ) && ( LoadType.name != Name ) ) { return false; } - if( Name.empty() ) { - // empty the vehicle + // empty the vehicle if requested LoadType = load_attributes(); LoadAmount = 0.f; return true; } + // can't mix load types, at least for the time being + if( ( LoadAmount > 0 ) && ( LoadType.name != Name ) ) { return false; } for( auto const &loadattributes : LoadAttributes ) { if( Name == loadattributes.name ) { diff --git a/Segment.cpp b/Segment.cpp index 10bf62cc..0bc1c272 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -114,9 +114,6 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M ErrorLog( "Bad track: zero length spline \"" + pOwner->name() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" ); fLength = 0.01; // crude workaround TODO: fix this properly -/* - return false; // zerowe nie mogą być -*/ } fStoop = std::atan2((Point2.y - Point1.y), fLength); // pochylenie toru prostego, żeby nie liczyć wielokrotnie @@ -128,13 +125,12 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M // NOTE: a workaround for too short switches (less than 3 segments) messing up animation/generation of blades fStep = fLength / ( 3.0 * Global.SplineFidelity ); } - iSegCount = static_cast( std::ceil( fLength / fStep ) ); // potrzebne do VBO -/* +// iSegCount = static_cast( std::ceil( fLength / fStep ) ); // potrzebne do VBO iSegCount = ( pOwner->eType == tt_Switch ? 6 * Global.SplineFidelity : static_cast( std::ceil( fLength / fStep ) ) ); // potrzebne do VBO -*/ + fStep = fLength / iSegCount; // update step to equalize size of individual pieces fTsBuffer.resize( iSegCount + 1 ); @@ -378,7 +374,7 @@ Math3D::vector3 TSegment::FastGetPoint(double const t) const interpolate( Point1, Point2, t ) ); } -bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, const gfx::basic_vertex *ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale, int iSkip, int iEnd, float fOffsetX, glm::vec3 **p, bool bRender) +bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, const gfx::vertex_array &ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale, int iSkip, int iEnd, float fOffsetX, glm::vec3 **p, bool bRender) { // generowanie trójkątów dla odcinka trajektorii ruchu // standardowo tworzy triangle_strip dla prostego albo ich zestaw dla łuku // po modyfikacji - dla ujemnego (iNumShapePoints) w dodatkowych polach tabeli @@ -459,7 +455,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Ori Output.emplace_back( pt, glm::normalize( norm ), - glm::vec2 { ( jmm1 * ShapePoints[ j ].texture.x + m1 * ShapePoints[ j + iNumShapePoints ].texture.x ) / texturescale, tv1 } ); + glm::vec2 { 1.f - ( jmm1 * ShapePoints[ j ].texture.x + m1 * ShapePoints[ j + iNumShapePoints ].texture.x ) / texturescale, tv1 } ); } if( p ) // jeśli jest wskaźnik do tablicy if( *p ) @@ -479,7 +475,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Ori Output.emplace_back( pt, glm::normalize( norm ), - glm::vec2 { ( jmm2 * ShapePoints[ j ].texture.x + m2 * ShapePoints[ j + iNumShapePoints ].texture.x ) / texturescale, tv2 } ); + glm::vec2 { 1.f - ( jmm2 * ShapePoints[ j ].texture.x + m2 * ShapePoints[ j + iNumShapePoints ].texture.x ) / texturescale, tv2 } ); } if( p ) // jeśli jest wskaźnik do tablicy if( *p ) @@ -503,7 +499,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Ori Output.emplace_back( pt, glm::normalize( norm ), - glm::vec2 { ShapePoints[ j ].texture.x / texturescale, tv1 } ); + glm::vec2 { 1.f - ShapePoints[ j ].texture.x / texturescale, tv1 } ); pt = parallel2 * ShapePoints[ j ].position.x + pos2; pt.y += ShapePoints[ j ].position.y; @@ -514,7 +510,7 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Ori Output.emplace_back( pt, glm::normalize( norm ), - glm::vec2 { ShapePoints[ j ].texture.x / texturescale, tv2 } ); + glm::vec2 { 1.f - ShapePoints[ j ].texture.x / texturescale, tv2 } ); } } } diff --git a/Segment.h b/Segment.h index 2304a62d..2f5cd2f9 100644 --- a/Segment.h +++ b/Segment.h @@ -116,7 +116,7 @@ public: r2 = fRoll2; } bool - RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, gfx::basic_vertex const *ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale = 1.0, int iSkip = 0, int iEnd = 0, float fOffsetX = 0.f, glm::vec3 **p = nullptr, bool bRender = true); + RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, gfx::vertex_array const &ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale = 1.0, int iSkip = 0, int iEnd = 0, float fOffsetX = 0.f, glm::vec3 **p = nullptr, bool bRender = true); /* void Render(); diff --git a/Track.cpp b/Track.cpp index 598e7f52..73254f87 100644 --- a/Track.cpp +++ b/Track.cpp @@ -1131,259 +1131,23 @@ void TTrack::RaAssign( TAnimModel *am, basic_event *done, basic_event *joined ) // wypełnianie tablic VBO void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { - // Ra: trzeba rozdzielić szyny od podsypki, aby móc grupować wg tekstur - auto const fHTW = 0.5f * std::abs(fTrackWidth); - auto const side = std::abs(fTexWidth); // szerokść podsypki na zewnątrz szyny albo pobocza - auto const slop = std::abs(fTexSlope); // brzeg zewnętrzny - auto const rozp = fHTW + side + slop; // brzeg zewnętrzny - auto hypot1 = std::hypot(slop, fTexHeight1); // rozmiar pochylenia do liczenia normalnych - if( hypot1 == 0.f ) - hypot1 = 1.f; - glm::vec3 const normalup{ 0.f, 1.f, 0.f }; - glm::vec3 normal1 { fTexHeight1 / hypot1, fTexSlope / hypot1, 0.f }; // wektor normalny - if( glm::length( normal1 ) == 0.f ) { - // fix normal for vertical surfaces - normal1 = glm::vec3 { 1.f, 0.f, 0.f }; - } - glm::vec3 normal2; - float fHTW2, side2, slop2, rozp2, fTexHeight2, hypot2; - if( iTrapezoid & 2 ) { - // ten bit oznacza, że istnieje odpowiednie pNext - // Ra: jest OK - fHTW2 = 0.5f * std::fabs(trNext->fTrackWidth); // połowa rozstawu/nawierzchni - side2 = std::fabs(trNext->fTexWidth); - slop2 = std::fabs(trNext->fTexSlope); // nie jest używane później - rozp2 = fHTW2 + side2 + slop2; - fTexHeight2 = trNext->fTexHeight1; - hypot2 = std::hypot(slop2, fTexHeight2); - if( hypot2 == 0.f ) - hypot2 = 1.f; - normal2 = { fTexHeight2 / hypot2, trNext->fTexSlope / hypot2, 0.f }; - if( glm::length( normal2 ) == 0.f ) { - // fix normal for vertical surfaces - normal2 = glm::vec3 { 1.f, 0.f, 0.f }; - } - } - else { - // gdy nie ma następnego albo jest nieodpowiednim końcem podpięty - fHTW2 = fHTW; - side2 = side; - slop2 = slop; - rozp2 = rozp; - fTexHeight2 = fTexHeight1; - hypot2 = hypot1; - normal2 = normal1; - } - float roll1, roll2; switch (iCategoryFlag & 15) { case 1: // tor { - if (Segment) - Segment->GetRolls(roll1, roll2); - else - roll1 = roll2 = 0.0; // dla zwrotnic - float const - sin1 = std::sin(roll1), - cos1 = std::cos(roll1), - sin2 = std::sin(roll2), - cos2 = std::cos(roll2); // zwykla szyna: //Ra: czemu główki są asymetryczne na wysokości 0.140? - gfx::basic_vertex rpts1[24], rpts2[24], rpts3[24], rpts4[24]; - for( int i = 0; i < 12; ++i ) { - - rpts1[ i ] = { - // position - {( fHTW + szyna[ i ].position.x ) * cos1 + szyna[ i ].position.y * sin1, - -( fHTW + szyna[ i ].position.x ) * sin1 + szyna[ i ].position.y * cos1, - szyna[ i ].position.z}, - // normal - { szyna[ i ].normal.x * cos1 + szyna[ i ].normal.y * sin1, - -szyna[ i ].normal.x * sin1 + szyna[ i ].normal.y * cos1, - szyna[ i ].normal.z }, - // texture - { szyna[ i ].texture.x, - szyna[ i ].texture.y } }; - - rpts2[ 11 - i ] = { - // position - {(-fHTW - szyna[ i ].position.x ) * cos1 + szyna[ i ].position.y * sin1, - -(-fHTW - szyna[ i ].position.x ) * sin1 + szyna[ i ].position.y * cos1, - szyna[ i ].position.z}, - // normal - {-szyna[ i ].normal.x * cos1 + szyna[ i ].normal.y * sin1, - szyna[ i ].normal.x * sin1 + szyna[ i ].normal.y * cos1, - szyna[ i ].normal.z }, - // texture - { szyna[ i ].texture.x, - szyna[ i ].texture.y } }; - - if( iTrapezoid == 0 ) { continue; } - // trapez albo przechyłki, to oddzielne punkty na końcu - - rpts1[ 12 + i ] = { - // position - {( fHTW + szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, - -( fHTW + 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, - -szyna[ i ].normal.x * sin2 + szyna[ i ].normal.y * cos2, - szyna[ i ].normal.z }, - // texture - { szyna[ i ].texture.x, - szyna[ i ].texture.y } }; - - rpts2[ 23 - i ] = { - // position - {(-fHTW - szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, - -(-fHTW - 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, - szyna[ i ].normal.x * sin2 + szyna[ i ].normal.y * cos2, - szyna[ i ].normal.z }, - // texture - { szyna[ i ].texture.x, - szyna[ i ].texture.y } }; - } + gfx::vertex_array rpts1, rpts2; + create_rail_profile( rpts1, rpts2 ); switch (eType) // dalej zależnie od typu { case tt_Table: // obrotnica jak zwykły tor, tylko animacja dochodzi case tt_Normal: if (m_material2) { // podsypka z podkładami jest tylko dla zwykłego toru - // potentially retrieve texture length override from the assigned material + gfx::vertex_array bpts1; + create_trackbed_profile( bpts1, trPrev, trNext ); auto const texturelength { texture_length( m_material2 ) }; - gfx::basic_vertex bpts1[ 8 ]; // punkty głównej płaszczyzny nie przydają się do robienia boków - if( texturelength == 4.f ) { - // stare mapowanie z różną gęstością pikseli i oddzielnymi teksturami na każdy profil - auto const normalx = std::cos( glm::radians( 75.f ) ); - auto const normaly = std::sin( glm::radians( 75.f ) ); - if( iTrapezoid ) { - // trapez albo przechyłki - // ewentualnie poprawić mapowanie, żeby środek mapował się na 1.435/4.671 ((0.3464,0.6536) - // bo się tekstury podsypki rozjeżdżają po zmianie proporcji profilu - bpts1[ 0 ] = { - {rozp, -fTexHeight1 - 0.18f, 0.f}, - {normalx, normaly, 0.f}, - {0.00f, 0.f} }; // lewy brzeg - bpts1[ 1 ] = { - {( fHTW + side ) * cos1, -( fHTW + side ) * sin1 - 0.18f, 0.f}, - {normalx, normaly, 0.f}, - {0.33f, 0.f} }; // krawędź załamania - bpts1[ 2 ] = { - {-bpts1[ 1 ].position.x, +( fHTW + side ) * sin1 - 0.18f, 0.f}, - {-normalx, normaly, 0.f}, - {0.67f, 0.f} }; // prawy brzeg początku symetrycznie - bpts1[ 3 ] = { - {-rozp, -fTexHeight1 - 0.18f, 0.f}, - {-normalx, normaly, 0.f}, - {1.f, 0.f} }; // prawy skos - // końcowy przekrój - bpts1[ 4 ] = { - {rozp2, -fTexHeight2 - 0.18f, 0.f}, - {normalx, normaly, 0.f}, - {0.00f, 0.f} }; // lewy brzeg - bpts1[ 5 ] = { - {( fHTW2 + side2 ) * cos2, -( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, - {normalx, normaly, 0.f}, - {0.33f, 0.f} }; // krawędź załamania - bpts1[ 6 ] = { - {-bpts1[ 5 ].position.x, +( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, - {-normalx, normaly, 0.f}, - {0.67f, 0.f} }; // prawy brzeg początku symetrycznie - bpts1[ 7 ] = { - {-rozp2, -fTexHeight2 - 0.18f, 0.f}, - {-normalx, normaly, 0.f}, - {1.00f, 0.f} }; // prawy skos - } - else { - bpts1[ 0 ] = { - {rozp, -fTexHeight1 - 0.18f, 0.f}, - {normalx, normaly, 0.f}, - {0.00f, 0.f} }; // lewy brzeg - bpts1[ 1 ] = { - {fHTW + side, -0.18f, 0.f}, - {normalx, normaly, 0.f}, - {0.33f, 0.f} }; // krawędź załamania - bpts1[ 2 ] = { - {-fHTW - side, -0.18f, 0.f}, - {-normalx, normaly, 0.f}, - {0.67f, 0.f} }; // druga - bpts1[ 3 ] = { - {-rozp, -fTexHeight1 - 0.18f, 0.f}, - {-normalx, normaly, 0.f}, - {1.00f, 0.f} }; // prawy skos - } - } - else { - // mapowanie proporcjonalne do powierzchni, rozmiar w poprzek określa fTexLength - auto const max = fTexRatio2 * texturelength; // szerokość proporcjonalna do długości - auto const map11 = max > 0.f ? (fHTW + side) / max : 0.25f; // załamanie od strony 1 - auto const map12 = max > 0.f ? (fHTW + side + hypot1) / max : 0.5f; // brzeg od strony 1 - if (iTrapezoid) { - // trapez albo przechyłki - auto const map21 = max > 0.f ? (fHTW2 + side2) / max : 0.25f; // załamanie od strony 2 - auto const map22 = max > 0.f ? (fHTW2 + side2 + hypot2) / max : 0.5f; // brzeg od strony 2 - // ewentualnie poprawić mapowanie, żeby środek mapował się na 1.435/4.671 - // ((0.3464,0.6536) - // bo się tekstury podsypki rozjeżdżają po zmianie proporcji profilu - bpts1[ 0 ] = { - {rozp, -fTexHeight1 - 0.18f, 0.f}, - {normal1.x, normal1.y, 0.f}, - {0.5f - map12, 0.f} }; // lewy brzeg - bpts1[ 1 ] = { - {( fHTW + side ) * cos1, -( fHTW + side ) * sin1 - 0.18f, 0.f}, - {normal1.x, normal1.y, 0.f}, - {0.5f - map11 , 0.f} }; // krawędź załamania - bpts1[ 2 ] = { - {-bpts1[ 1 ].position.x, +( fHTW + side ) * sin1 - 0.18f, 0.f}, - {-normal1.x, normal1.y, 0.f}, - {0.5 + map11, 0.f} }; // prawy brzeg początku symetrycznie - bpts1[ 3 ] = { - {-rozp, -fTexHeight1 - 0.18f, 0.f}, - {-normal1.x, normal1.y, 0.f}, - {0.5f + map12, 0.f} }; // prawy skos - // przekrój końcowy - bpts1[ 4 ] = { - {rozp2, -fTexHeight2 - 0.18f, 0.f}, - {normal2.x, normal2.y, 0.f}, - {0.5f - map22, 0.f} }; // lewy brzeg - bpts1[ 5 ] = { - {( fHTW2 + side2 ) * cos2, -( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, - {normal2.x, normal2.y, 0.f}, - {0.5f - map21 , 0.f} }; // krawędź załamania - bpts1[ 6 ] = { - {-bpts1[ 5 ].position.x, +( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, - {-normal2.x, normal2.y, 0.f}, - {0.5f + map21, 0.f} }; // prawy brzeg początku symetrycznie - bpts1[ 7 ] = { - {-rozp2, -fTexHeight2 - 0.18f, 0.f}, - {-normal2.x, normal2.y, 0.f}, - {0.5f + map22, 0.f} }; // prawy skos - } - else - { - bpts1[ 0 ] = { - {rozp, -fTexHeight1 - 0.18f, 0.f}, - {+normal1.x, normal1.y, 0.f}, - {0.5f - map12, 0.f} }; // lewy brzeg - bpts1[ 1 ] = { - {fHTW + side, - 0.18f, 0.f}, - {+normal1.x, normal1.y, 0.f}, - {0.5f - map11, 0.f} }; // krawędź załamania - bpts1[ 2 ] = { - {-fHTW - side, - 0.18f, 0.f}, - {-normal1.x, normal1.y, 0.f}, - {0.5f + map11, 0.f} }; // druga - bpts1[ 3 ] = { - {-rozp, -fTexHeight1 - 0.18f, 0.f}, - {-normal1.x, normal1.y, 0.f}, - {0.5f + map12, 0.f} }; // prawy skos - } - } gfx::vertex_array vertices; Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid ? -4 : 4, texturelength); if( ( Bank != 0 ) && ( true == Geometry2.empty() ) ) { @@ -1418,37 +1182,8 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { case tt_Switch: // dla zwrotnicy dwa razy szyny if( m_material1 || m_material2 ) { // iglice liczone tylko dla zwrotnic - gfx::basic_vertex - rpts3[24], - rpts4[24]; - glm::vec3 const flipxvalue { -1, 1, 1 }; - for( int i = 0; i < 12; ++i ) { - - rpts3[ i ] = { - {+( fHTW + iglica[ i ].position.x ) * cos1 + iglica[ i ].position.y * sin1, - -( fHTW + iglica[ i ].position.x ) * sin1 + iglica[ i ].position.y * cos1, - 0.f}, - {iglica[ i ].normal}, - {iglica[ i ].texture.x, 0.f} }; - rpts3[ i + 12 ] = { - {+( fHTW2 + szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, - -( fHTW2 + szyna[ i ].position.x ) * sin2 + iglica[ i ].position.y * cos2, - 0.f}, - {szyna[ i ].normal}, - {szyna[ i ].texture.x, 0.f} }; - rpts4[ 11 - i ] = { - { ( -fHTW - iglica[ i ].position.x ) * cos1 + iglica[ i ].position.y * sin1, - -( -fHTW - iglica[ i ].position.x ) * sin1 + iglica[ i ].position.y * cos1, - 0.f}, - {iglica[ i ].normal * flipxvalue}, - {iglica[ i ].texture.x, 0.f} }; - rpts4[ 23 - i ] = { - { ( -fHTW2 - szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, - -( -fHTW2 - szyna[ i ].position.x ) * sin2 + iglica[ i ].position.y * cos2, - 0.f}, - {szyna[ i ].normal * flipxvalue}, - {szyna[ i ].texture.x, 0.f} }; - } + gfx::vertex_array rpts3, rpts4; + create_blade_profile( rpts3, rpts4 ); // TODO, TBD: change all track geometry to triangles, to allow packing data in less, larger buffers auto const bladelength { static_cast( std::ceil( SwitchExtension->Segments[ 0 ]->RaSegCount() * 0.65 ) ) }; if (SwitchExtension->RightSwitch) @@ -1516,6 +1251,14 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { } } } + // auto-generated switch trackbed + if( true == Global.CreateSwitchTrackbeds ) { + gfx::vertex_array vertices; + create_switch_trackbed( vertices ); + SwitchExtension->m_geometry3 = GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ); + vertices.clear(); + } + break; } } // koniec obsługi torów @@ -1525,44 +1268,10 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { { case tt_Normal: // drogi proste, bo skrzyżowania osobno { - gfx::basic_vertex bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków + gfx::vertex_array bpts1; // punkty głównej płaszczyzny przydają się do robienia boków if (m_material1 || m_material2) { // punkty się przydadzą, nawet jeśli nawierzchni nie ma - auto const texturelength { texture_length( m_material1 ) }; - auto const max = fTexRatio1 * texturelength; // test: szerokość proporcjonalna do długości - auto const map1 = max > 0.f ? fHTW / max : 0.5f; // obcięcie tekstury od strony 1 - auto const map2 = max > 0.f ? fHTW2 / max : 0.5f; // obcięcie tekstury od strony 2 - if (iTrapezoid) { - // trapez albo przechyłki - Segment->GetRolls(roll1, roll2); - bpts1[ 0 ] = { - {fHTW * std::cos( roll1 ), -fHTW * std::sin( roll1 ), 0.f}, - normalup, - {0.5f - map1, 0.f} }; // lewy brzeg początku - bpts1[ 1 ] = { - {-bpts1[ 0 ].position.x, -bpts1[ 0 ].position.y, 0.f}, - normalup, - {0.5f + map1, 0.f} }; // prawy brzeg początku symetrycznie - bpts1[ 2 ] = { - {fHTW2 * std::cos( roll2 ), -fHTW2 * std::sin( roll2 ), 0.f}, - normalup, - {0.5f - map2, 0.f} }; // lewy brzeg końca - bpts1[ 3 ] = { - {-bpts1[ 2 ].position.x, -bpts1[ 2 ].position.y, 0.f}, - normalup, - {0.5f + map2, 0.f} }; // prawy brzeg początku symetrycznie - } - else - { - bpts1[ 0 ] = { - {fHTW, 0.f, 0.f}, - normalup, - {0.5f - map1, 0.f} }; - bpts1[ 1 ] = { - {-fHTW, 0.f, 0.f}, - normalup, - {0.5f + map1, 0.f} }; - } + create_road_profile( bpts1 ); } if (m_material1) // jeśli podana była tekstura, generujemy trójkąty { // tworzenie trójkątów nawierzchni szosy @@ -1573,161 +1282,11 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { } if (m_material2) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) + auto const side{ std::abs( fTexWidth ) }; // szerokść podsypki na zewnątrz szyny albo pobocza + auto const slop{ std::abs( fTexSlope ) }; // brzeg zewnętrzny auto const texturelength { texture_length( m_material2 ) }; - gfx::basic_vertex - rpts1[6], - rpts2[6]; // współrzędne przekroju i mapowania dla prawej i lewej strony - if (fTexHeight1 >= 0.f) - { // standardowo: od zewnątrz pochylenie, a od wewnątrz poziomo - rpts1[ 0 ] = { - {rozp, -fTexHeight1, 0.f}, - { 1.f, 0.f, 0.f }, - {0.f, 0.f} }; // lewy brzeg podstawy - rpts1[ 1 ] = { - {bpts1[ 0 ].position.x + side, bpts1[ 0 ].position.y, 0.f}, - normalup, - {0.5, 0.f} }; // lewa krawędź załamania - rpts1[ 2 ] = { - {bpts1[ 0 ].position.x, bpts1[ 0 ].position.y, 0.f}, - normalup, - {1.f, 0.f} }; // lewy brzeg pobocza (mapowanie może być inne - rpts2[ 0 ] = { - {bpts1[ 1 ].position.x, bpts1[ 1 ].position.y, 0.f}, - normalup, - {1.f, 0.f} }; // prawy brzeg pobocza - rpts2[ 1 ] = { - {bpts1[ 1 ].position.x - side, bpts1[ 1 ].position.y, 0.f}, - normalup, - {0.5f, 0.f} }; // prawa krawędź załamania - rpts2[ 2 ] = { - {-rozp, -fTexHeight1, 0.f}, - { -1.f, 0.f, 0.f }, - {0.f, 0.f} }; // prawy brzeg podstawy - if (iTrapezoid) { - // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka - rpts1[ 3 ] = { - {rozp2, -fTexHeight2, 0.f}, - { 1.f, 0.f, 0.f }, - {0.f, 0.f} }; // lewy brzeg lewego pobocza - rpts1[ 4 ] = { - {bpts1[ 2 ].position.x + side2, bpts1[ 2 ].position.y, 0.f}, - normalup, - {0.5f, 0.f} }; // krawędź załamania - rpts1[ 5 ] = { - {bpts1[ 2 ].position.x, bpts1[ 2 ].position.y, 0.f}, - normalup, - {1.f, 0.f} }; // brzeg pobocza - rpts2[ 3 ] = { - {bpts1[ 3 ].position.x, bpts1[ 3 ].position.y, 0.f}, - normalup, - {1.f, 0.f} }; - rpts2[ 4 ] = { - {bpts1[ 3 ].position.x - side2, bpts1[ 3 ].position.y, 0.f}, - normalup, - {0.5f, 0.f} }; - rpts2[ 5 ] = { - {-rozp2, -fTexHeight2, 0.f}, - { -1.f, 0.f, 0.f }, - {0.f, 0.f} }; // prawy brzeg prawego pobocza - } - } - else - { // wersja dla chodnika: skos 1:3.75, każdy chodnik innej szerokości - // mapowanie propocjonalne do szerokości chodnika - // krawężnik jest mapowany od 31/64 do 32/64 lewy i od 32/64 do 33/64 prawy - auto const d = -fTexHeight1 / 3.75f; // krawężnik o wysokości 150mm jest pochylony 40mm - auto const max = fTexRatio2 * texturelength; // test: szerokość proporcjonalna do długości - auto const map1l = ( - max > 0.f ? - side / max : - 0.484375f ); // obcięcie tekstury od lewej strony punktu 1 - auto const map1r = ( - max > 0.f ? - slop / max : - 0.484375f ); // obcięcie tekstury od prawej strony punktu 1 - auto const h1r = ( - slop > d ? - -fTexHeight1 : - 0.f ); - auto const h1l = ( - side > d ? - -fTexHeight1 : - 0.f ); - - rpts1[ 0 ] = { - {bpts1[ 0 ].position.x + slop, bpts1[ 0 ].position.y + h1r, 0.f}, - normalup, - {0.515625f + map1r, 0.f} }; // prawy brzeg prawego chodnika - rpts1[ 1 ] = { - {bpts1[ 0 ].position.x + d, bpts1[ 0 ].position.y + h1r, 0.f}, - normalup, - {0.515625f, 0.f} }; // prawy krawężnik u góry - rpts1[ 2 ] = { - {bpts1[ 0 ].position.x, bpts1[ 0 ].position.y, 0.f}, - { -1.f, 0.f, 0.f }, - {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu - rpts2[ 0 ] = { - {bpts1[ 1 ].position.x, bpts1[ 1 ].position.y, 0.f}, - { 1.f, 0.f, 0.f }, - {0.484375f + d / 2.56f, 0.f} }; // lewy krawężnik u dołu - rpts2[ 1 ] = { - {bpts1[ 1 ].position.x - d, bpts1[ 1 ].position.y + h1l, 0.f}, - normalup, - {0.484375f, 0.f} }; // lewy krawężnik u góry - rpts2[ 2 ] = { - {bpts1[ 1 ].position.x - side, bpts1[ 1 ].position.y + h1l, 0.f}, - normalup, - {0.484375f - map1l, 0.f} }; // lewy brzeg lewego chodnika - - if (iTrapezoid) { - // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka - slop2 = ( - std::fabs((iTrapezoid & 2) ? - slop2 : - slop) ); // szerokość chodnika po prawej - auto const map2l = ( - max > 0.f ? - side2 / max : - 0.484375f ); // obcięcie tekstury od lewej strony punktu 2 - auto const map2r = ( - max > 0.f ? - slop2 / max : - 0.484375f ); // obcięcie tekstury od prawej strony punktu 2 - auto const h2r = ( - slop2 > d ? - -fTexHeight2 : - 0.f ); - auto const h2l = ( - side2 > d ? - -fTexHeight2 : - 0.f ); - - rpts1[ 3 ] = { - {bpts1[ 2 ].position.x + slop2, bpts1[ 2 ].position.y + h2r, 0.f}, - normalup, - {0.515625f + map2r, 0.f} }; // prawy brzeg prawego chodnika - rpts1[ 4 ] = { - {bpts1[ 2 ].position.x + d, bpts1[ 2 ].position.y + h2r, 0.f}, - normalup, - {0.515625f, 0.f} }; // prawy krawężnik u góry - rpts1[ 5 ] = { - {bpts1[ 2 ].position.x, bpts1[ 2 ].position.y, 0.f}, - { -1.f, 0.f, 0.f }, - {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu - rpts2[ 3 ] = { - {bpts1[ 3 ].position.x, bpts1[ 3 ].position.y, 0.f}, - { 1.f, 0.f, 0.f }, - {0.484375f + d / 2.56f, 0.f} }; // lewy krawężnik u dołu - rpts2[ 4 ] = { - {bpts1[ 3 ].position.x - d, bpts1[ 3 ].position.y + h2l, 0.f}, - normalup, - {0.484375f, 0.f} }; // lewy krawężnik u góry - rpts2[ 5 ] = { - {bpts1[ 3 ].position.x - side2, bpts1[ 3 ].position.y + h2l, 0.f}, - normalup, - {0.484375f - map2l, 0.f} }; // lewy brzeg lewego chodnika - } - } + gfx::vertex_array rpts1, rpts2; // współrzędne przekroju i mapowania dla prawej i lewej strony + create_sidewalk_profile( rpts1, rpts2, bpts1 ); gfx::vertex_array vertices; if( iTrapezoid ) // trapez albo przechyłki { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony @@ -1807,33 +1366,9 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { SwitchExtension->bPoints ? nullptr : SwitchExtension->vPoints; // zmienna robocza, NULL gdy tablica punktów już jest wypełniona - gfx::basic_vertex bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków - if (m_material1 || m_material2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma - { // double max=2.0*(fHTW>fHTW2?fHTW:fHTW2); //z szerszej strony jest 100% - auto const texturelength { texture_length( m_material1 ) }; - auto const max = fTexRatio1 * texturelength; // test: szerokość proporcjonalna do długości - auto const map1 = max > 0.f ? fHTW / max : 0.5f; // obcięcie tekstury od strony 1 - auto const map2 = max > 0.f ? fHTW2 / max : 0.5f; // obcięcie tekstury od strony 2 - // if (iTrapezoid) //trapez albo przechyłki - { // nawierzchnia trapezowata - Segment->GetRolls(roll1, roll2); - bpts1[ 0 ] = { - {fHTW * std::cos( roll1 ), -fHTW * std::sin( roll1 ), 0.f}, - {std::sin( roll1 ), std::cos( roll1 ), 0.f}, - {0.5f - map1, 0.f} }; // lewy brzeg początku - bpts1[ 1 ] = { - {-bpts1[ 0 ].position.x, -bpts1[ 0 ].position.y, 0.f}, - {-std::sin( roll1 ), std::cos( roll1 ), 0.f}, - {0.5f + map1, 0.f} }; // prawy brzeg początku symetrycznie - bpts1[ 2 ] = { - {fHTW2 * std::cos( roll2 ), -fHTW2 * std::sin( roll2 ), 0.f}, - {std::sin( roll2 ), std::cos( roll2 ), 0.f}, - {0.5f - map2, 0.f} }; // lewy brzeg końca - bpts1[ 3 ] = { - {-bpts1[ 2 ].position.x, -bpts1[ 2 ].position.y, 0.f}, - {-std::sin( roll2 ), std::cos( roll2 ), 0.f}, - {0.5 + map2, 0.f} }; // prawy brzeg początku symetrycznie - } + gfx::vertex_array bpts1; // punkty głównej płaszczyzny przydają się do robienia boków + if (m_material1 || m_material2) { // punkty się przydadzą, nawet jeśli nawierzchni nie ma + create_road_profile( bpts1, true ); } // najpierw renderowanie poboczy i zapamiętywanie punktów // problem ze skrzyżowaniami jest taki, że teren chce się pogrupować wg tekstur, ale zaczyna od nawierzchni @@ -1841,146 +1376,12 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { // ale pobocza renderują się później, więc nawierzchnia nie załapuje się na renderowanie w swoim czasie if( m_material2 ) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) - auto const texturelength { texture_length( m_material2 ) }; - gfx::basic_vertex - rpts1[6], - rpts2[6]; // współrzędne przekroju i mapowania dla prawej i lewej strony + gfx::vertex_array rpts1, rpts2; // współrzędne przekroju i mapowania dla prawej i lewej strony + create_sidewalk_profile( rpts1, rpts2, bpts1, true ); // Ra 2014-07: trzeba to przerobić na pętlę i pobierać profile (przynajmniej 2..4) z sąsiednich dróg - if (fTexHeight1 >= 0.0) - { // standardowo: od zewnątrz pochylenie, a od wewnątrz poziomo - rpts1[ 0 ] = { - {rozp, -fTexHeight1, 0.f}, - { 1.f, 0.f, 0.f }, - {0.f, 0.f} }; // lewy brzeg podstawy - rpts1[ 1 ] = { - {bpts1[ 0 ].position.x + side, bpts1[ 0 ].position.y, 0.f}, - normalup, - {0.5f, 0.f} };// lewa krawędź załamania - rpts1[ 2 ] = { - {bpts1[ 0 ].position.x, bpts1[ 0 ].position.y, 0.f}, - normalup, - {1.f, 0.f} }; // lewy brzeg pobocza (mapowanie może być inne - rpts2[ 0 ] = { - {bpts1[ 1 ].position.x, bpts1[ 1 ].position.y, 0.f}, - normalup, - {1.f, 0.f} }; // prawy brzeg pobocza - rpts2[ 1 ] = { - {bpts1[ 1 ].position.x - side, bpts1[ 1 ].position.y, 0.f}, - normalup, - {0.5f, 0.f} }; // prawa krawędź załamania - rpts2[ 2 ] = { - {-rozp, -fTexHeight1, 0.f}, - { -1.f, 0.f, 0.f }, - {0.f, 0.f} }; // prawy brzeg podstawy - // if (iTrapezoid) //trapez albo przechyłki - { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka - rpts1[ 3 ] = { - {rozp2, -fTexHeight2, 0.f}, - { 1.f, 0.f, 0.f }, - {0.f, 0.f} }; // lewy brzeg lewego pobocza - rpts1[ 4 ] = { - {bpts1[ 2 ].position.x + side2, bpts1[ 2 ].position.y, 0.f}, - normalup, - {0.5f, 0.f} }; // krawędź załamania - rpts1[ 5 ] = { - {bpts1[ 2 ].position.x, bpts1[ 2 ].position.y, 0.f}, - normalup, - {1.f, 0.f} }; // brzeg pobocza - rpts2[ 3 ] = { - {bpts1[ 3 ].position.x, bpts1[ 3 ].position.y, 0.f}, - normalup, - {1.f, 0.f} }; - rpts2[ 4 ] = { - {bpts1[ 3 ].position.x - side2, bpts1[ 3 ].position.y, 0.f}, - normalup, - {0.5f, 0.f} }; - rpts2[ 5 ] = { - {-rozp2, -fTexHeight2, 0.f}, - { -1.f, 0.f, 0.f }, - {0.f, 0.f} }; // prawy brzeg prawego pobocza - } - } - else - { // wersja dla chodnika: skos 1:3.75, każdy chodnik innej szerokości - // mapowanie propocjonalne do szerokości chodnika - // krawężnik jest mapowany od 31/64 do 32/64 lewy i od 32/64 do 33/64 prawy - auto const d = -fTexHeight1 / 3.75f; // krawężnik o wysokości 150mm jest pochylony 40mm - auto const max = fTexRatio2 * texturelength; // test: szerokość proporcjonalna do długości - auto const map1l = ( - max > 0.f ? - side / max : - 0.484375f ); // obcięcie tekstury od lewej strony punktu 1 - auto const map1r = ( - max > 0.f ? - slop / max : - 0.484375f ); // obcięcie tekstury od prawej strony punktu 1 - - rpts1[ 0 ] = { - {bpts1[ 0 ].position.x + slop, bpts1[ 0 ].position.y - fTexHeight1, 0.f}, - normalup, - { 0.515625f + map1r, 0.f} }; // prawy brzeg prawego chodnika - rpts1[ 1 ] = { - {bpts1[ 0 ].position.x + d, bpts1[ 0 ].position.y - fTexHeight1, 0.f}, - normalup, - {0.515625f, 0.f} }; // prawy krawężnik u góry - rpts1[ 2 ] = { - {bpts1[ 0 ].position.x, bpts1[ 0 ].position.y, 0.f}, - { -1.f, 0.f, 0.f }, - {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu - rpts2[ 0 ] = { - {bpts1[ 1 ].position.x, bpts1[ 1 ].position.y, 0.f}, - { 1.f, 0.f, 0.f }, - {0.484375f + d / 2.56f, 0.f} }; // lewy krawężnik u dołu - rpts2[ 1 ] = { - {bpts1[ 1 ].position.x - d, bpts1[ 1 ].position.y - fTexHeight1, 0.f}, - normalup, - {0.484375f, 0.f} }; // lewy krawężnik u góry - rpts2[ 2 ] = { - {bpts1[ 1 ].position.x - side, bpts1[ 1 ].position.y - fTexHeight1, 0.f}, - normalup, - {0.484375f - map1l, 0.f} }; // lewy brzeg lewego chodnika - // if (iTrapezoid) //trapez albo przechyłki - { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka - slop2 = std::abs( - ( (iTrapezoid & 2) ? - slop2 : - slop ) ); // szerokość chodnika po prawej - auto const map2l = ( - max > 0.f ? - side2 / max : - 0.484375f ); // obcięcie tekstury od lewej strony punktu 2 - auto const map2r = ( - max > 0.f ? - slop2 / max : - 0.484375f ); // obcięcie tekstury od prawej strony punktu 2 - - rpts1[ 3 ] = { - {bpts1[ 2 ].position.x + slop2, bpts1[ 2 ].position.y - fTexHeight2, 0.f}, - normalup, - { 0.515625f + map2r, 0.f} }; // prawy brzeg prawego chodnika - rpts1[ 4 ] = { - {bpts1[ 2 ].position.x + d, bpts1[ 2 ].position.y - fTexHeight2, 0.f}, - normalup, - {0.515625f, 0.f} }; // prawy krawężnik u góry - rpts1[ 5 ] = { - {bpts1[ 2 ].position.x, bpts1[ 2 ].position.y, 0.f}, - { -1.f, 0.f, 0.f }, - {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu - rpts2[ 3 ] = { - {bpts1[ 3 ].position.x, bpts1[ 3 ].position.y, 0.f}, - { 1.f, 0.f, 0.f }, - {0.484375f + d / 2.56, 0.f} }; // lewy krawężnik u dołu - rpts2[ 4 ] = { - {bpts1[ 3 ].position.x - d, bpts1[ 3 ].position.y - fTexHeight2, 0.f}, - normalup, - {0.484375f, 0.f} }; // lewy krawężnik u góry - rpts2[ 5 ] = { - {bpts1[ 3 ].position.x - side2, bpts1[ 3 ].position.y - fTexHeight2, 0.f}, - normalup, - {0.484375f - map2l, 0.f} }; // lewy brzeg lewego chodnika - } - } bool render = ( m_material2 != 0 ); // renderować nie trzeba, ale trzeba wyznaczyć punkty brzegowe nawierzchni + auto const side{ std::abs( fTexWidth ) }; // szerokść podsypki na zewnątrz szyny albo pobocza + auto const texturelength{ texture_length( m_material2 ) }; gfx::vertex_array vertices; if (SwitchExtension->iRoads == 4) { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka @@ -2088,53 +1489,9 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { { case tt_Normal: // drogi proste, bo skrzyżowania osobno { - gfx::basic_vertex bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków - if (m_material1 || m_material2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma - { // double max=2.0*(fHTW>fHTW2?fHTW:fHTW2); //z szerszej strony jest 100% - auto const max = ( - ( iCategoryFlag & 4 ) ? - 0.f : - fTexLength ); // test: szerokość dróg proporcjonalna do długości - auto const map1 = ( - max > 0.f ? - fHTW / max : - 0.5f ); // obcięcie tekstury od strony 1 - auto const map2 = ( - max > 0.f ? - fHTW2 / max : - 0.5f ); // obcięcie tekstury od strony 2 - - if (iTrapezoid) { - // nawierzchnia trapezowata - Segment->GetRolls(roll1, roll2); - bpts1[ 0 ] = { - {fHTW * std::cos( roll1 ), -fHTW * std::sin( roll1 ), 0.f}, - normalup, - {0.5f - map1, 0.f} }; // lewy brzeg początku - bpts1[ 1 ] = { - {-bpts1[ 0 ].position.x, -bpts1[ 0 ].position.y, 0.f}, - normalup, - {0.5f + map1, 0.f} }; // prawy brzeg początku symetrycznie - bpts1[ 2 ] = { - {fHTW2 * std::cos( roll2 ), -fHTW2 * std::sin( roll2 ), 0.f}, - normalup, - {0.5f - map2, 0.f} }; // lewy brzeg końca - bpts1[ 3 ] = { - {-bpts1[ 2 ].position.x, -bpts1[ 2 ].position.y, 0.f}, - normalup, - {0.5f + map2, 0.f} }; // prawy brzeg początku symetrycznie - } - else - { - bpts1[ 0 ] = { - {fHTW, 0.f, 0.f}, - normalup, - {0.5 - map1, 0.f} }; // zawsze standardowe mapowanie - bpts1[ 1 ] = { - {-fHTW, 0.f, 0.f}, - normalup, - {0.5f + map1, 0.f} }; - } + gfx::vertex_array bpts1; // punkty głównej płaszczyzny przydają się do robienia boków + if (m_material1 || m_material2) { // punkty się przydadzą, nawet jeśli nawierzchni nie ma + create_road_profile( bpts1 ); } if (m_material1) // jeśli podana była tekstura, generujemy trójkąty { // tworzenie trójkątów nawierzchni szosy @@ -2144,61 +1501,11 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { } if (m_material2) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) + gfx::vertex_array rpts1, rpts2; // współrzędne przekroju i mapowania dla prawej i lewej strony + create_sidewalk_profile( rpts1, rpts2, bpts1 ); gfx::vertex_array vertices; - gfx::basic_vertex - rpts1[6], - rpts2[6]; // współrzędne przekroju i mapowania dla prawej i lewej strony - - rpts1[ 0 ] = { - {rozp, -fTexHeight1, 0.f}, - normalup, - {0.0f, 0.f} }; // lewy brzeg podstawy - rpts1[ 1 ] = { - {bpts1[ 0 ].position.x + side, bpts1[ 0 ].position.y, 0.f}, - normalup, - {0.5f, 0.f} }; // lewa krawędź załamania - rpts1[ 2 ] = { - {bpts1[ 0 ].position.x, bpts1[ 0 ].position.y, 0.f}, - normalup, - {1.0f, 0.f} }; // lewy brzeg pobocza (mapowanie może być inne - rpts2[ 0 ] = { - {bpts1[ 1 ].position.x, bpts1[ 1 ].position.y, 0.f}, - normalup, - {1.0f, 0.f} }; // prawy brzeg pobocza - rpts2[ 1 ] = { - {bpts1[ 1 ].position.x - side, bpts1[ 1 ].position.y, 0.f}, - normalup, - {0.5f, 0.f} }; // prawa krawędź załamania - rpts2[ 2 ] = { - {-rozp, -fTexHeight1, 0.f}, - normalup, - {0.0f, 0.f} }; // prawy brzeg podstawy if (iTrapezoid) // trapez albo przechyłki { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka - rpts1[ 3 ] = { - {rozp2, -fTexHeight2, 0.f}, - normalup, - {0.0f, 0.f} }; // lewy brzeg lewego pobocza - rpts1[ 4 ] = { - {bpts1[ 2 ].position.x + side2, bpts1[ 2 ].position.y, 0.f}, - normalup, - {0.5f, 0.f} }; // krawędź załamania - rpts1[ 5 ] = { - {bpts1[ 2 ].position.x, bpts1[ 2 ].position.y, 0.f}, - normalup, - {1.0f, 0.f} }; // brzeg pobocza - rpts2[ 3 ] = { - {bpts1[ 3 ].position.x, bpts1[ 3 ].position.y, 0.f}, - normalup, - {1.0f, 0.f} }; - rpts2[ 4 ] = { - {bpts1[ 3 ].position.x - side2, bpts1[ 3 ].position.y, 0.f}, - normalup, - {0.5f, 0.f} }; - rpts2[ 5 ] = { - {-rozp2, -fTexHeight2, 0.f}, - normalup, - {0.0f, 0.f} }; // prawy brzeg prawego pobocza Segment->RenderLoft(vertices, m_origin, rpts1, -3, fTexLength); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); @@ -2476,42 +1783,8 @@ TTrack * TTrack::RaAnimate() && ( ( false == Geometry1.empty() ) || ( false == Geometry2.empty() ) ) ) { // iglice liczone tylko dla zwrotnic - auto const fHTW = 0.5f * std::abs( fTrackWidth ); - auto const fHTW2 = fHTW; // Ra: na razie niech tak będzie - auto const cos1 = 1.0f, sin1 = 0.0f, cos2 = 1.0f, sin2 = 0.0f; // Ra: ... - - gfx::basic_vertex - rpts3[ 24 ], - rpts4[ 24 ]; - glm::vec3 const flipxvalue { -1, 1, 1 }; - for (int i = 0; i < 12; ++i) { - - rpts3[ i ] = { - {+( fHTW + iglica[ i ].position.x ) * cos1 + iglica[ i ].position.y * sin1, - -( fHTW + iglica[ i ].position.x ) * sin1 + iglica[ i ].position.y * cos1, - 0.f}, - {iglica[ i ].normal}, - {iglica[ i ].texture.x, 0.f} }; - rpts3[ i + 12 ] = { - {+( fHTW2 + szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, - -( fHTW2 + szyna[ i ].position.x ) * sin2 + iglica[ i ].position.y * cos2, - 0.f}, - {szyna[ i ].normal}, - {szyna[ i ].texture.x, 0.f} }; - rpts4[ 11 - i ] = { - {+( -fHTW - iglica[ i ].position.x ) * cos1 + iglica[ i ].position.y * sin1, - -( -fHTW - iglica[ i ].position.x ) * sin1 + iglica[ i ].position.y * cos1, - 0.f}, - {iglica[ i ].normal * flipxvalue}, - {iglica[ i ].texture.x, 0.f} }; - rpts4[ 23 - i ] = { - { ( -fHTW2 - szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, - -( -fHTW2 - szyna[ i ].position.x ) * sin2 + iglica[ i ].position.y * cos2, - 0.f}, - {szyna[ i ].normal * flipxvalue}, - {szyna[ i ].texture.x, 0.f} }; - } - + gfx::vertex_array rpts3, rpts4; + create_blade_profile( rpts3, rpts4 ); gfx::vertex_array vertices; auto const bladelength { static_cast( std::ceil( SwitchExtension->Segments[ 0 ]->RaSegCount() * 0.65 ) ) }; if (SwitchExtension->RightSwitch) @@ -2944,6 +2217,21 @@ void TTrack::ConnectionsLog() } }; +bool +TTrack::DoubleSlip() const { + + // crude way to discern part of double slip switch: + // a switch with name ending in _a or _b or _c or _d + return ( + ( iCategoryFlag == 1 ) + && ( eType == tt_Switch ) + && ( m_name.size() > 2 ) + && ( m_name[ m_name.size() - 2 ] == '_' ) + && ( m_name.back() >= 'a' ) + && ( m_name.back() <= 'd' ) ); +} + + TTrack * TTrack::Connected(int s, double &d) const { // zwraca wskaźnik na sąsiedni tor, w kierunku określonym znakiem (s), odwraca (d) w razie // niezgodności kierunku torów @@ -2994,6 +2282,705 @@ TTrack * TTrack::Connected(int s, double &d) const return NULL; }; +// creates rail profile data for current track +void +TTrack::create_rail_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ) { + + auto const fHTW { 0.5f * std::abs( fTrackWidth ) }; + + float + roll1{ 0.f }, + roll2{ 0.f }; + + if( Segment ) { + Segment->GetRolls( roll1, roll2 ); + } + + float const + sin1 { std::sin( roll1 ) }, + cos1 { std::cos( roll1 ) }, + sin2 { std::sin( roll2 ) }, + cos2 { std::cos( roll2 ) }; + + auto const pointcount { iTrapezoid == 0 ? 12 : 24 }; + Right.resize( pointcount ); + Left.resize( pointcount ); + + for( int i = 0; i < 12; ++i ) { + + Right[ i ] = { + // position + {( fHTW + szyna[ i ].position.x ) * cos1 + szyna[ i ].position.y * sin1, + -( fHTW + szyna[ i ].position.x ) * sin1 + szyna[ i ].position.y * cos1, + szyna[ i ].position.z}, + // normal + { szyna[ i ].normal.x * cos1 + szyna[ i ].normal.y * sin1, + -szyna[ i ].normal.x * sin1 + szyna[ i ].normal.y * cos1, + szyna[ i ].normal.z }, + // texture + { szyna[ i ].texture.x, + szyna[ i ].texture.y } }; + + Left[ 11 - i ] = { + // position + {(-fHTW - szyna[ i ].position.x ) * cos1 + szyna[ i ].position.y * sin1, + -(-fHTW - szyna[ i ].position.x ) * sin1 + szyna[ i ].position.y * cos1, + szyna[ i ].position.z}, + // normal + {-szyna[ i ].normal.x * cos1 + szyna[ i ].normal.y * sin1, + szyna[ i ].normal.x * sin1 + szyna[ i ].normal.y * cos1, + szyna[ i ].normal.z }, + // texture + { szyna[ i ].texture.x, + szyna[ i ].texture.y } }; + + if( iTrapezoid == 0 ) { continue; } + // trapez albo przechyłki, to oddzielne punkty na końcu + + Right[ 12 + i ] = { + // position + {( fHTW + szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, + -( fHTW + 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, + -szyna[ i ].normal.x * sin2 + szyna[ i ].normal.y * cos2, + szyna[ i ].normal.z }, + // texture + { szyna[ i ].texture.x, + szyna[ i ].texture.y } }; + + Left[ 23 - i ] = { + // position + {(-fHTW - szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, + -(-fHTW - 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, + szyna[ i ].normal.x * sin2 + szyna[ i ].normal.y * cos2, + szyna[ i ].normal.z }, + // texture + { szyna[ i ].texture.x, + szyna[ i ].texture.y } }; + } +} + +// creates switch blades profile data for current track +void +TTrack::create_blade_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 }; + + if( Segment ) { + Segment->GetRolls( roll1, roll2 ); + } + + float const + sin1 { std::sin( roll1 ) }, + cos1 { std::cos( roll1 ) }, + sin2 { std::sin( roll2 ) }, + cos2 { std::cos( roll2 ) }; + + auto const pointcount { 24 }; + Right.resize( pointcount ); + Left.resize( pointcount ); + + glm::vec3 const flipxvalue { -1, 1, 1 }; + for( int i = 0; i < 12; ++i ) { + + Right[ i ] = { + {+( fHTW + iglica[ i ].position.x ) * cos1 + iglica[ i ].position.y * sin1, + -( fHTW + iglica[ i ].position.x ) * sin1 + iglica[ i ].position.y * cos1, + 0.f}, + {iglica[ i ].normal}, + {iglica[ i ].texture.x, 0.f} }; + Right[ i + 12 ] = { + {+( fHTW2 + szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, + -( fHTW2 + szyna[ i ].position.x ) * sin2 + iglica[ i ].position.y * cos2, + 0.f}, + {szyna[ i ].normal}, + {szyna[ i ].texture.x, 0.f} }; + Left[ 11 - i ] = { + { ( -fHTW - iglica[ i ].position.x ) * cos1 + iglica[ i ].position.y * sin1, + -( -fHTW - iglica[ i ].position.x ) * sin1 + iglica[ i ].position.y * cos1, + 0.f}, + {iglica[ i ].normal * flipxvalue}, + {iglica[ i ].texture.x, 0.f} }; + Left[ 23 - i ] = { + { ( -fHTW2 - szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, + -( -fHTW2 - szyna[ i ].position.x ) * sin2 + iglica[ i ].position.y * cos2, + 0.f}, + {szyna[ i ].normal * flipxvalue}, + {szyna[ i ].texture.x, 0.f} }; + } +} + +// creates trackbed profile data for current track +void +TTrack::create_trackbed_profile( gfx::vertex_array &Output, TTrack const *Previous, TTrack const *Next ) { + // geometry parameters + auto * profilesource = ( + eType != tt_Switch ? this : + Previous && Previous->eType != tt_Switch ? Previous : + Next && Next->eType != tt_Switch ? Next : + this ); + + auto const texheight1 { profilesource->fTexHeight1 }; + auto const texwidth { profilesource->fTexWidth }; + auto const texslope { profilesource->fTexSlope }; + + auto const fHTW { 0.5f * std::abs( fTrackWidth ) }; + auto const side { std::abs( texwidth ) }; // szerokść podsypki na zewnątrz szyny albo pobocza + auto const slop { std::abs( texslope ) }; // brzeg zewnętrzny + auto const rozp { fHTW + side + slop }; // brzeg zewnętrzny + + auto hypot1 { std::hypot( slop, texheight1 ) }; // rozmiar pochylenia do liczenia normalnych + if( hypot1 == 0.f ) + hypot1 = 1.f; + + glm::vec3 normal1 { texheight1 / hypot1, texslope / hypot1, 0.f }; // wektor normalny + if( glm::length( normal1 ) == 0.f ) { + // fix normal for vertical surfaces + normal1 = glm::vec3 { 1.f, 0.f, 0.f }; + } + + glm::vec3 normal2; + float fHTW2, side2, slop2, rozp2, fTexHeight2, hypot2; + if( ( Next != nullptr ) + && ( Next->eType != tt_Switch ) + && ( ( iTrapezoid & 2 ) // ten bit oznacza, że istnieje odpowiednie pNext + || ( eType == tt_Switch ) ) ) { + fHTW2 = 0.5f * std::abs(Next->fTrackWidth); // połowa rozstawu/nawierzchni + side2 = std::abs(Next->fTexWidth); + slop2 = std::abs(Next->fTexSlope); // nie jest używane później + rozp2 = fHTW2 + side2 + slop2; + fTexHeight2 = Next->fTexHeight1; + hypot2 = std::hypot(slop2, fTexHeight2); + if( hypot2 == 0.f ) + hypot2 = 1.f; + normal2 = { fTexHeight2 / hypot2, Next->fTexSlope / hypot2, 0.f }; + if( glm::length( normal2 ) == 0.f ) { + // fix normal for vertical surfaces + normal2 = glm::vec3 { 1.f, 0.f, 0.f }; + } + } + else { + // gdy nie ma następnego albo jest nieodpowiednim końcem podpięty + fHTW2 = fHTW; + side2 = side; + slop2 = slop; + rozp2 = rozp; + fTexHeight2 = texheight1; + hypot2 = hypot1; + normal2 = normal1; + } + + float + roll1{ 0.f }, + roll2{ 0.f }; + + if( Segment ) { + Segment->GetRolls( roll1, roll2 ); + } + + float const + sin1 { std::sin( roll1 ) }, + cos1 { std::cos( roll1 ) }, + sin2 { std::sin( roll2 ) }, + cos2 { std::cos( roll2 ) }; + + // profile + auto const transition { ( iTrapezoid != 0 ) || ( eType == tt_Switch ) }; + auto const pointcount { transition ? 8 : 4 }; + Output.resize( pointcount ); + // potentially retrieve texture length override from the assigned material + auto const texturelength { texture_length( profilesource->m_material2 ) }; + if( texturelength == 4.f ) { + // stare mapowanie z różną gęstością pikseli i oddzielnymi teksturami na każdy profil + auto const normalx = std::cos( glm::radians( 75.f ) ); + auto const normaly = std::sin( glm::radians( 75.f ) ); + if( transition ) { + // trapez albo przechyłki + // ewentualnie poprawić mapowanie, żeby środek mapował się na 1.435/4.671 ((0.3464,0.6536) + // bo się tekstury podsypki rozjeżdżają po zmianie proporcji profilu + Output[ 0 ] = { + {rozp, -texheight1 - 0.18f, 0.f}, + {normalx, normaly, 0.f}, + {0.00f, 0.f} }; // lewy brzeg + Output[ 1 ] = { + {( fHTW + side ) * cos1, -( fHTW + side ) * sin1 - 0.18f, 0.f}, + {normalx, normaly, 0.f}, + {0.33f, 0.f} }; // krawędź załamania + Output[ 2 ] = { + {-Output[ 1 ].position.x, +( fHTW + side ) * sin1 - 0.18f, 0.f}, + {-normalx, normaly, 0.f}, + {0.67f, 0.f} }; // prawy brzeg początku symetrycznie + Output[ 3 ] = { + {-rozp, -texheight1 - 0.18f, 0.f}, + {-normalx, normaly, 0.f}, + {1.f, 0.f} }; // prawy skos + // końcowy przekrój + Output[ 4 ] = { + {rozp2, -fTexHeight2 - 0.18f, 0.f}, + {normalx, normaly, 0.f}, + {0.00f, 0.f} }; // lewy brzeg + Output[ 5 ] = { + {( fHTW2 + side2 ) * cos2, -( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, + {normalx, normaly, 0.f}, + {0.33f, 0.f} }; // krawędź załamania + Output[ 6 ] = { + {-Output[ 5 ].position.x, +( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, + {-normalx, normaly, 0.f}, + {0.67f, 0.f} }; // prawy brzeg początku symetrycznie + Output[ 7 ] = { + {-rozp2, -fTexHeight2 - 0.18f, 0.f}, + {-normalx, normaly, 0.f}, + {1.00f, 0.f} }; // prawy skos + } + else { + Output[ 0 ] = { + {rozp, -texheight1 - 0.18f, 0.f}, + {normalx, normaly, 0.f}, + {0.00f, 0.f} }; // lewy brzeg + Output[ 1 ] = { + {fHTW + side, -0.18f, 0.f}, + {normalx, normaly, 0.f}, + {0.33f, 0.f} }; // krawędź załamania + Output[ 2 ] = { + {-fHTW - side, -0.18f, 0.f}, + {-normalx, normaly, 0.f}, + {0.67f, 0.f} }; // druga + Output[ 3 ] = { + {-rozp, -texheight1 - 0.18f, 0.f}, + {-normalx, normaly, 0.f}, + {1.00f, 0.f} }; // prawy skos + } + } + else { + // mapowanie proporcjonalne do powierzchni, rozmiar w poprzek określa fTexLength + auto const max = fTexRatio2 * texturelength; // szerokość proporcjonalna do długości + auto const map11 = max > 0.f ? (fHTW + side) / max : 0.25f; // załamanie od strony 1 + auto const map12 = max > 0.f ? (fHTW + side + hypot1) / max : 0.5f; // brzeg od strony 1 + if (transition) { + // trapez albo przechyłki + auto const map21 = max > 0.f ? (fHTW2 + side2) / max : 0.25f; // załamanie od strony 2 + auto const map22 = max > 0.f ? (fHTW2 + side2 + hypot2) / max : 0.5f; // brzeg od strony 2 + // ewentualnie poprawić mapowanie, żeby środek mapował się na 1.435/4.671 + // ((0.3464,0.6536) + // bo się tekstury podsypki rozjeżdżają po zmianie proporcji profilu + Output[ 0 ] = { + {rozp, -texheight1 - 0.18f, 0.f}, + {normal1.x, normal1.y, 0.f}, + {0.5f - map12, 0.f} }; // lewy brzeg + Output[ 1 ] = { + {( fHTW + side ) * cos1, -( fHTW + side ) * sin1 - 0.18f, 0.f}, + {normal1.x, normal1.y, 0.f}, + {0.5f - map11 , 0.f} }; // krawędź załamania + Output[ 2 ] = { + {-Output[ 1 ].position.x, +( fHTW + side ) * sin1 - 0.18f, 0.f}, + {-normal1.x, normal1.y, 0.f}, + {0.5 + map11, 0.f} }; // prawy brzeg początku symetrycznie + Output[ 3 ] = { + {-rozp, -texheight1 - 0.18f, 0.f}, + {-normal1.x, normal1.y, 0.f}, + {0.5f + map12, 0.f} }; // prawy skos + // przekrój końcowy + Output[ 4 ] = { + {rozp2, -fTexHeight2 - 0.18f, 0.f}, + {normal2.x, normal2.y, 0.f}, + {0.5f - map22, 0.f} }; // lewy brzeg + Output[ 5 ] = { + {( fHTW2 + side2 ) * cos2, -( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, + {normal2.x, normal2.y, 0.f}, + {0.5f - map21 , 0.f} }; // krawędź załamania + Output[ 6 ] = { + {-Output[ 5 ].position.x, +( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, + {-normal2.x, normal2.y, 0.f}, + {0.5f + map21, 0.f} }; // prawy brzeg początku symetrycznie + Output[ 7 ] = { + {-rozp2, -fTexHeight2 - 0.18f, 0.f}, + {-normal2.x, normal2.y, 0.f}, + {0.5f + map22, 0.f} }; // prawy skos + } + else + { + Output[ 0 ] = { + {rozp, -texheight1 - 0.18f, 0.f}, + {+normal1.x, normal1.y, 0.f}, + {0.5f - map12, 0.f} }; // lewy brzeg + Output[ 1 ] = { + {fHTW + side, - 0.18f, 0.f}, + {+normal1.x, normal1.y, 0.f}, + {0.5f - map11, 0.f} }; // krawędź załamania + Output[ 2 ] = { + {-fHTW - side, - 0.18f, 0.f}, + {-normal1.x, normal1.y, 0.f}, + {0.5f + map11, 0.f} }; // druga + Output[ 3 ] = { + {-rozp, -texheight1 - 0.18f, 0.f}, + {-normal1.x, normal1.y, 0.f}, + {0.5f + map12, 0.f} }; // prawy skos + } + } +} + +// creates road profile data for current path +void +TTrack::create_road_profile( gfx::vertex_array &Output, bool const Forcetransition ) { + + 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 ) }; + + glm::vec3 const normalup { 0.f, 1.f, 0.f }; + + auto const texturelength { texture_length( m_material1 ) }; + auto const max = fTexRatio1 * texturelength; // test: szerokość proporcjonalna do długości + auto const map1 = max > 0.f ? fHTW / max : 0.5f; // obcięcie tekstury od strony 1 + auto const map2 = max > 0.f ? fHTW2 / max : 0.5f; // obcięcie tekstury od strony 2 + + auto const transition { ( true == Forcetransition ) || ( iTrapezoid != 0 ) }; + + auto const pointcount{ transition ? 4 : 2 }; + Output.resize( pointcount ); + + if( transition ) { + // trapez albo przechyłki + float + roll1 { 0.f }, + roll2 { 0.f }; + + if( Segment ) { + Segment->GetRolls( roll1, roll2 ); + } + + Output[ 0 ] = { + {fHTW * std::cos( roll1 ), -fHTW * std::sin( roll1 ), 0.f}, + normalup, + {0.5f - map1, 0.f} }; // lewy brzeg początku + Output[ 1 ] = { + {-Output[ 0 ].position.x, -Output[ 0 ].position.y, 0.f}, + normalup, + {0.5f + map1, 0.f} }; // prawy brzeg początku symetrycznie + Output[ 2 ] = { + {fHTW2 * std::cos( roll2 ), -fHTW2 * std::sin( roll2 ), 0.f}, + normalup, + {0.5f - map2, 0.f} }; // lewy brzeg końca + Output[ 3 ] = { + {-Output[ 2 ].position.x, -Output[ 2 ].position.y, 0.f}, + normalup, + {0.5f + map2, 0.f} }; // prawy brzeg początku symetrycznie + } + else { + Output[ 0 ] = { + {fHTW, 0.f, 0.f}, + normalup, + {0.5f - map1, 0.f} }; + Output[ 1 ] = { + {-fHTW, 0.f, 0.f}, + normalup, + {0.5f + map1, 0.f} }; + } +} + +void +TTrack::create_sidewalk_profile( gfx::vertex_array &Right, gfx::vertex_array &Left, gfx::vertex_array const &Road, bool const Forcetransition ) { + + auto const fHTW{ 0.5f * std::abs( fTrackWidth ) }; + auto const side{ std::abs( fTexWidth ) }; // szerokść podsypki na zewnątrz szyny albo pobocza + auto const slop{ std::abs( fTexSlope ) }; // brzeg zewnętrzny + auto const rozp{ fHTW + side + slop }; // brzeg zewnętrzny + + float fHTW2, side2, slop2, rozp2, fTexHeight2; + if( iTrapezoid & 2 ) { + // ten bit oznacza, że istnieje odpowiednie pNext + // Ra: jest OK + fHTW2 = 0.5f * std::fabs( trNext->fTrackWidth ); // połowa rozstawu/nawierzchni + side2 = std::fabs( trNext->fTexWidth ); + slop2 = std::fabs( trNext->fTexSlope ); // nie jest używane później + rozp2 = fHTW2 + side2 + slop2; + fTexHeight2 = trNext->fTexHeight1; + } + else { + // gdy nie ma następnego albo jest nieodpowiednim końcem podpięty + fHTW2 = fHTW; + side2 = side; + slop2 = slop; + rozp2 = rozp; + fTexHeight2 = fTexHeight1; + } + + glm::vec3 const normalup{ 0.f, 1.f, 0.f }; + + auto const texturelength{ texture_length( m_material2 ) }; + + auto const transition { ( true == Forcetransition ) || ( iTrapezoid != 0 ) }; + + auto const pointcount{ transition ? 6 : 3 }; + Right.resize( pointcount ); + Left.resize( pointcount ); + + + if( fTexHeight1 >= 0.f ) { // standardowo: od zewnątrz pochylenie, a od wewnątrz poziomo + Right[ 0 ] = { + {rozp, -fTexHeight1, 0.f}, + { 1.f, 0.f, 0.f }, + {0.f, 0.f} }; // lewy brzeg podstawy + Right[ 1 ] = { + {Road[ 0 ].position.x + side, Road[ 0 ].position.y, 0.f}, + normalup, + {0.5, 0.f} }; // lewa krawędź załamania + Right[ 2 ] = { + {Road[ 0 ].position.x, Road[ 0 ].position.y, 0.f}, + normalup, + {1.f, 0.f} }; // lewy brzeg pobocza (mapowanie może być inne + Left[ 0 ] = { + {Road[ 1 ].position.x, Road[ 1 ].position.y, 0.f}, + normalup, + {1.f, 0.f} }; // prawy brzeg pobocza + Left[ 1 ] = { + {Road[ 1 ].position.x - side, Road[ 1 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; // prawa krawędź załamania + Left[ 2 ] = { + {-rozp, -fTexHeight1, 0.f}, + { -1.f, 0.f, 0.f }, + {0.f, 0.f} }; // prawy brzeg podstawy + if( transition ) { + // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka + Right[ 3 ] = { + {rozp2, -fTexHeight2, 0.f}, + { 1.f, 0.f, 0.f }, + {0.f, 0.f} }; // lewy brzeg lewego pobocza + Right[ 4 ] = { + {Road[ 2 ].position.x + side2, Road[ 2 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; // krawędź załamania + Right[ 5 ] = { + {Road[ 2 ].position.x, Road[ 2 ].position.y, 0.f}, + normalup, + {1.f, 0.f} }; // brzeg pobocza + Left[ 3 ] = { + {Road[ 3 ].position.x, Road[ 3 ].position.y, 0.f}, + normalup, + {1.f, 0.f} }; + Left[ 4 ] = { + {Road[ 3 ].position.x - side2, Road[ 3 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; + Left[ 5 ] = { + {-rozp2, -fTexHeight2, 0.f}, + { -1.f, 0.f, 0.f }, + {0.f, 0.f} }; // prawy brzeg prawego pobocza + } + } + else { // wersja dla chodnika: skos 1:3.75, każdy chodnik innej szerokości + // mapowanie propocjonalne do szerokości chodnika + // krawężnik jest mapowany od 31/64 do 32/64 lewy i od 32/64 do 33/64 prawy + auto const d = -fTexHeight1 / 3.75f; // krawężnik o wysokości 150mm jest pochylony 40mm + auto const max = fTexRatio2 * texturelength; // test: szerokość proporcjonalna do długości + auto const map1l = ( + max > 0.f ? + side / max : + 0.484375f ); // obcięcie tekstury od lewej strony punktu 1 + auto const map1r = ( + max > 0.f ? + slop / max : + 0.484375f ); // obcięcie tekstury od prawej strony punktu 1 + auto const h1r = ( + slop > d ? + -fTexHeight1 : + 0.f ); + auto const h1l = ( + side > d ? + -fTexHeight1 : + 0.f ); + + Right[ 0 ] = { + {Road[ 0 ].position.x + slop, Road[ 0 ].position.y + h1r, 0.f}, + normalup, + {0.515625f + map1r, 0.f} }; // prawy brzeg prawego chodnika + Right[ 1 ] = { + {Road[ 0 ].position.x + d, Road[ 0 ].position.y + h1r, 0.f}, + normalup, + {0.515625f, 0.f} }; // prawy krawężnik u góry + Right[ 2 ] = { + {Road[ 0 ].position.x, Road[ 0 ].position.y, 0.f}, + { -1.f, 0.f, 0.f }, + {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu + Left[ 0 ] = { + {Road[ 1 ].position.x, Road[ 1 ].position.y, 0.f}, + { 1.f, 0.f, 0.f }, + {0.484375f + d / 2.56f, 0.f} }; // lewy krawężnik u dołu + Left[ 1 ] = { + {Road[ 1 ].position.x - d, Road[ 1 ].position.y + h1l, 0.f}, + normalup, + {0.484375f, 0.f} }; // lewy krawężnik u góry + Left[ 2 ] = { + {Road[ 1 ].position.x - side, Road[ 1 ].position.y + h1l, 0.f}, + normalup, + {0.484375f - map1l, 0.f} }; // lewy brzeg lewego chodnika + + if( transition ) { + // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka + slop2 = ( + std::fabs( ( iTrapezoid & 2 ) ? + slop2 : + slop ) ); // szerokość chodnika po prawej + auto const map2l = ( + max > 0.f ? + side2 / max : + 0.484375f ); // obcięcie tekstury od lewej strony punktu 2 + auto const map2r = ( + max > 0.f ? + slop2 / max : + 0.484375f ); // obcięcie tekstury od prawej strony punktu 2 + auto const h2r = ( + slop2 > d ? + -fTexHeight2 : + 0.f ); + auto const h2l = ( + side2 > d ? + -fTexHeight2 : + 0.f ); + + Right[ 3 ] = { + {Road[ 2 ].position.x + slop2, Road[ 2 ].position.y + h2r, 0.f}, + normalup, + {0.515625f + map2r, 0.f} }; // prawy brzeg prawego chodnika + Right[ 4 ] = { + {Road[ 2 ].position.x + d, Road[ 2 ].position.y + h2r, 0.f}, + normalup, + {0.515625f, 0.f} }; // prawy krawężnik u góry + Right[ 5 ] = { + {Road[ 2 ].position.x, Road[ 2 ].position.y, 0.f}, + { -1.f, 0.f, 0.f }, + {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu + Left[ 3 ] = { + {Road[ 3 ].position.x, Road[ 3 ].position.y, 0.f}, + { 1.f, 0.f, 0.f }, + {0.484375f + d / 2.56f, 0.f} }; // lewy krawężnik u dołu + Left[ 4 ] = { + {Road[ 3 ].position.x - d, Road[ 3 ].position.y + h2l, 0.f}, + normalup, + {0.484375f, 0.f} }; // lewy krawężnik u góry + Left[ 5 ] = { + {Road[ 3 ].position.x - side2, Road[ 3 ].position.y + h2l, 0.f}, + normalup, + {0.484375f - map2l, 0.f} }; // lewy brzeg lewego chodnika + } + } +} + +void +TTrack::create_switch_trackbed( gfx::vertex_array &Output ) { + // try to get trackbed material from a regular track connected to the primary path + if( ( trPrev != nullptr ) + && ( trPrev->eType == tt_Normal ) ) { + SwitchExtension->m_material3 = trPrev->m_material2; + } + else if( ( trNext != nullptr ) + && ( trNext->eType == tt_Normal ) ) { + SwitchExtension->m_material3 = trNext->m_material2; + } + // without material don't bother + if( SwitchExtension->m_material3 == null_handle ) { return; } + // generate trackbed for each path of the switch... + // TODO: create proper profile for each path + auto const texturelength { texture_length( SwitchExtension->m_material3 ) }; + gfx::vertex_array trackbedprofile; + gfx::vertex_array trackbedvertices1, trackbedvertices2; + create_trackbed_profile( trackbedprofile, SwitchExtension->pPrevs[ 0 ], SwitchExtension->pNexts[ 0 ] ); + SwitchExtension->Segments[ 0 ]->RenderLoft( trackbedvertices1, m_origin, trackbedprofile, -4, texturelength ); + create_trackbed_profile( trackbedprofile, SwitchExtension->pPrevs[ 1 ], SwitchExtension->pNexts[ 1 ] ); + SwitchExtension->Segments[ 1 ]->RenderLoft( trackbedvertices2, m_origin, trackbedprofile, -4, texturelength ); +// Output = trackbedvertices1; +// return; + // ...and take outer edge from each to create combined trackbed + auto const segmentsize { 8 }; + auto const segmentcount { trackbedvertices1.size() / segmentsize }; + auto const *sampler1 { trackbedvertices1.data() }; + auto const *sampler2 { trackbedvertices2.data() }; + auto const isright { SwitchExtension->RightSwitch }; + auto const isleft { false == isright }; + auto const samplersoffset { isright ? 2 : 0 }; + for( int segment = 0; segment < segmentcount; ++segment ) { + Output.emplace_back( *( sampler1 + samplersoffset + 0 ) ); if( isright ) { Output.back().position.y -= 0.02f; } + Output.emplace_back( *( sampler1 + samplersoffset + 1 ) ); if( isright ) { Output.back().position.y -= 0.02f; } + + Output.emplace_back( *( sampler1 + samplersoffset + 2 ) ); + Output.emplace_back( *( sampler1 + samplersoffset + 3 ) ); + + Output.emplace_back( *( sampler1 + samplersoffset + 4 ) ); if( isleft ) { Output.back().position.y -= 0.02f; } + Output.emplace_back( *( sampler1 + samplersoffset + 5 ) ); if( isleft ) { Output.back().position.y -= 0.02f; } + + Output.emplace_back( *( sampler2 - samplersoffset + 2 ) ); if( isleft ) { Output.back().position.y -= 0.02f; } + Output.emplace_back( *( sampler2 - samplersoffset + 3 ) ); if( isleft ) { Output.back().position.y -= 0.02f; } + + Output.emplace_back( *( sampler2 - samplersoffset + 4 ) ); + Output.emplace_back( *( sampler2 - samplersoffset + 5 ) ); + + Output.emplace_back( *( sampler2 - samplersoffset + 6 ) ); if( isright ) { Output.back().position.y -= 0.02f; } + Output.emplace_back( *( sampler2 - samplersoffset + 7 ) ); if( isright ) { Output.back().position.y -= 0.02f; } + + sampler1 += segmentsize; + sampler2 += segmentsize; + } +} + +material_handle +TTrack::copy_adjacent_trackbed_material( TTrack const *Exclude ) { + + if( iCategoryFlag != 1 ) { return null_handle; } // tracks only + + auto &material { eType == tt_Switch ? SwitchExtension->m_material3 : m_material2 }; + + if( material != null_handle ) { return material; } // already has material + + std::vector adjacents; + switch( eType ) { + case tt_Normal: { + // for regular tracks don't set the trackbed texture if we aren't sitting next to a part of a double slip + auto const hasadjacentdoubleslip { + ( trPrev ? trPrev->DoubleSlip() : false ) + || ( trNext ? trNext->DoubleSlip() : false ) }; + + if( true == hasadjacentdoubleslip ) { + adjacents.emplace_back( trPrev ); + adjacents.emplace_back( trNext ); + } + break; + } + case tt_Switch: { + // only check the neighbour on the joint side + adjacents.emplace_back( SwitchExtension->pPrevs[ 0 ] ); + break; + } + default: { + break; + } + } + + for( auto *adjacent : adjacents ) { + if( ( adjacent != nullptr ) && ( adjacent != Exclude ) ) { + material = adjacent->copy_adjacent_trackbed_material( this ); + if( material != null_handle ) { break; } // got what we wanted + } + } + + return material; +} + path_table::~path_table() { @@ -3116,6 +3103,10 @@ path_table::InitTracks() { break; } } + if( Global.CreateSwitchTrackbeds ) { + // when autogenerating trackbeds, try to restore trackbeds for tracks neighbouring double slips + track->copy_adjacent_trackbed_material(); + } break; } case tt_Switch: { @@ -3123,6 +3114,10 @@ path_table::InitTracks() { track->AssignForcedEvents( simulation::Events.FindEvent( trackname + ":forced+" ), simulation::Events.FindEvent( trackname + ":forced-" ) ); + if( Global.CreateSwitchTrackbeds ) { + // when autogenerating trackbeds, try to restore trackbeds for tracks neighbouring double slips + track->copy_adjacent_trackbed_material(); + } break; } default: { diff --git a/Track.h b/Track.h index e3ca3233..821dae90 100644 --- a/Track.h +++ b/Track.h @@ -93,6 +93,9 @@ class TSwitchExtension *evMinus = nullptr; // zdarzenia sygnalizacji rozprucia float fVelocity = -1.0; // maksymalne ograniczenie prędkości (ustawianej eventem) Math3D::vector3 vTrans; // docelowa translacja przesuwnicy + material_handle m_material3 = 0; // texture of auto generated switch trackbed + gfx::geometry_handle m_geometry3; // geometry of auto generated switch trackbed + }; class TIsolated @@ -134,7 +137,7 @@ private: // members int iAxles { 0 }; // ilość osi na odcinkach obsługiwanych przez obiekt TIsolated *pNext { nullptr }; // odcinki izolowane są trzymane w postaci listy jednikierunkowej - TIsolated *pParent { nullptr }; // optional parent piece, collecting data from its children + TIsolated *pParent { nullptr }; // optional parent piece, receiving data from its children static TIsolated *pRoot; // początek listy }; @@ -222,6 +225,7 @@ public: void ConnectPrevNext(TTrack *pNewPrev, int typ); void ConnectNextPrev(TTrack *pNewNext, int typ); void ConnectNextNext(TTrack *pNewNext, int typ); + material_handle copy_adjacent_trackbed_material( TTrack const *Exclude = nullptr ); inline double Length() const { return Segment->GetLength(); }; inline std::shared_ptr CurrentSegment() const { @@ -288,6 +292,7 @@ public: void VelocitySet(float v); double VelocityGet(); void ConnectionsLog(); + bool DoubleSlip() const; private: // radius() subclass details, calculates node's bounding radius @@ -300,6 +305,13 @@ private: void export_as_text_( std::ostream &Output ) const; // returns texture length for specified material float texture_length( material_handle const Material ); + // creates profile for a part of current path + void create_rail_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ); + void create_blade_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ); + void create_trackbed_profile( gfx::vertex_array &Output, TTrack const *Previous, TTrack const *Next ); + void create_road_profile( gfx::vertex_array &Output, bool const Forcetransition = false ); + void create_sidewalk_profile( gfx::vertex_array &Right, gfx::vertex_array &Left, gfx::vertex_array const &Road, bool const Forcetransition = false ); + void create_switch_trackbed( gfx::vertex_array &Output ); }; diff --git a/renderer.cpp b/renderer.cpp index bdcd6d15..883ffb47 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -2624,7 +2624,9 @@ void opengl_renderer::Render( TTrack *Track ) { if( ( Track->m_material1 == 0 ) - && ( Track->m_material2 == 0 ) ) { + && ( Track->m_material2 == 0 ) + && ( ( Track->eType != tt_Switch ) + || ( Track->SwitchExtension->m_material3 == 0 ) ) ) { return; } if( false == Track->m_visible ) { @@ -2646,6 +2648,11 @@ opengl_renderer::Render( TTrack *Track ) { Bind_Material( Track->m_material2 ); m_geometry.draw( std::begin( Track->Geometry2 ), std::end( Track->Geometry2 ) ); } + if( ( Track->eType == tt_Switch ) + && ( Track->SwitchExtension->m_material3 != 0 ) ) { + Bind_Material( Track->SwitchExtension->m_material3 ); + m_geometry.draw( Track->SwitchExtension->m_geometry3 ); + } setup_environment_light(); break; } @@ -2666,6 +2673,11 @@ opengl_renderer::Render( TTrack *Track ) { Bind_Material( Track->m_material2 ); m_geometry.draw( std::begin( Track->Geometry2 ), std::end( Track->Geometry2 ) ); } + if( ( Track->eType == tt_Switch ) + && ( Track->SwitchExtension->m_material3 != 0 ) ) { + Bind_Material( Track->SwitchExtension->m_material3 ); + m_geometry.draw( Track->SwitchExtension->m_geometry3 ); + } break; } case rendermode::pickcontrols: @@ -2692,6 +2704,7 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, } } + // TODO: render auto generated trackbeds together with regular trackbeds in pass 1, and all rails in pass 2 // first pass, material 1 for( auto first { First }; first != Last; ++first ) { @@ -2782,6 +2795,53 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, } } } + // third pass, material 3 + for( auto first { First }; first != Last; ++first ) { + + auto const track { *first }; + + if( track->eType != tt_Switch ) { + continue; + } + if( track->SwitchExtension->m_material3 == 0 ) { + continue; + } + if( false == track->m_visible ) { + continue; + } + + switch( m_renderpass.draw_mode ) { + case rendermode::color: + case rendermode::reflections: { + if( track->eEnvironment != e_flat ) { + setup_environment_light( track->eEnvironment ); + } + Bind_Material( track->SwitchExtension->m_material3 ); + m_geometry.draw( track->SwitchExtension->m_geometry3 ); + if( track->eEnvironment != e_flat ) { + // restore default lighting + setup_environment_light(); + } + break; + } + case rendermode::shadows: { + if( ( std::abs( track->fTexHeight1 ) < 0.35f ) + || ( ( track->iCategoryFlag == 1 ) + && ( track->eType != tt_Normal ) ) ) { + // shadows are only calculated for high enough trackbeds + continue; + } + Bind_Material( track->SwitchExtension->m_material3 ); + m_geometry.draw( track->SwitchExtension->m_geometry3 ); + break; + } + case rendermode::pickscenery: // pick scenery mode uses piece-by-piece approach + case rendermode::pickcontrols: + default: { + break; + } + } + } // post-render reset switch( m_renderpass.draw_mode ) { case rendermode::shadows: { @@ -3058,7 +3118,7 @@ opengl_renderer::Render_Alpha( TTraction *Traction ) { ::glLineWidth( clamp( 0.5f * linealpha + Traction->WireThickness * Traction->radius() / 1000.f, - 1.f, 1.5f ) ); + 1.f, 1.75f ) ); // McZapkie-261102: kolor zalezy od materialu i zasniedzenia ::glColor4fv( glm::value_ptr( diff --git a/scene.cpp b/scene.cpp index 29da186f..7c334196 100644 --- a/scene.cpp +++ b/scene.cpp @@ -1156,9 +1156,28 @@ basic_region::RadioStop( glm::dvec3 const &Location ) { } } +std::vector switchtrackbedtextures { + "rozkrz8r150-1pods-new", + "rozkrz8r150-2pods-new", + "rozkrz34r150-tpbps-new2", + "rozkrz34r150-tpd1", + "rkpd34r190-tpd1", + "rkpd34r190-tpd2", + "rkpd34r190-tpd-oil2" }; + void basic_region::insert( shape_node Shape, scratch_data &Scratchpad, bool const Transform ) { + if( Global.CreateSwitchTrackbeds ) { + + auto const materialname{ GfxRenderer.Material( Shape.data().material ).name }; + for( auto const &switchtrackbedtexture : switchtrackbedtextures ) { + if( materialname.find( switchtrackbedtexture ) != std::string::npos ) { + // geometry with blacklisted texture, part of old switch trackbed; ignore it + return; + } + } + } // shape might need to be split into smaller pieces, so we create list of nodes instead of just single one // using deque so we can do single pass iterating and addding generated pieces without invalidating anything std::deque shapes { Shape }; diff --git a/simulationstateserializer.cpp b/simulationstateserializer.cpp index 0671ba44..d22a007b 100644 --- a/simulationstateserializer.cpp +++ b/simulationstateserializer.cpp @@ -433,7 +433,16 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch || ( nodedata.type == "triangle_strip" ) || ( nodedata.type == "triangle_fan" ) ) { - if( false == Scratchpad.binary.terrain ) { + auto const skip { + // all shapes will be loaded from the binary version of the file + ( true == Scratchpad.binary.terrain ) + // crude way to detect fixed switch trackbed geometry + || ( ( true == Global.CreateSwitchTrackbeds ) + && ( Input.Name().size() >= 15 ) + && ( Input.Name().substr( 0, 11 ) == "scenery/zwr" ) + && ( Input.Name().substr( Input.Name().size() - 4 ) == ".inc" ) ) }; + + if( false == skip ) { simulation::Region->insert( scene::shape_node().import( @@ -442,7 +451,6 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch true ); } else { - // all shapes were already loaded from the binary version of the file skip_until( Input, "endtri" ); } } diff --git a/version.h b/version.h index abf7d5cf..2ab9ae6d 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 18 -#define VERSION_MINOR 1001 +#define VERSION_MINOR 1005 #define VERSION_REVISION 0 From 06abe187c68f931fe150808cd5950ad7b8877303 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sun, 7 Oct 2018 15:43:30 +0200 Subject: [PATCH 4/6] build 181007. automatic switch trackbed enhancements, portal crash fix, minor bug fixes --- DynObj.cpp | 2 +- Track.cpp | 227 +++++++++++++++++++++++++--------------- Track.h | 13 ++- Train.cpp | 2 + drivermode.cpp | 5 + maszyna.vcxproj.filters | 3 + renderer.cpp | 15 ++- renderer.h | 1 + scene.cpp | 5 +- uilayer.cpp | 20 ++++ version.h | 2 +- 11 files changed, 195 insertions(+), 100 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index dc833bd3..fc522fe6 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -7069,7 +7069,7 @@ vehicle_table::erase_disabled() { if( ( simulation::Train != nullptr ) && ( simulation::Train->Dynamic() == vehicle ) ) { // clear potential train binding - // TBD, TODO: manually eject the driver first ? + // TBD, TODO: kill vehicle sounds SafeDelete( simulation::Train ); } // remove potential entries in the light array diff --git a/Track.cpp b/Track.cpp index 73254f87..ca006e48 100644 --- a/Track.cpp +++ b/Track.cpp @@ -870,6 +870,13 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin) parser->getTokens(); *parser >> fVerticalRadius; } + else if( str == "trackbed" ) { + // switch trackbed texture + auto const trackbedtexture { parser->getToken() }; + if( eType == tt_Switch ) { + SwitchExtension->m_material3 = GfxRenderer.Fetch_Material( trackbedtexture ); + } + } else ErrorLog("Unknown property: \"" + str + "\" in track \"" + m_name + "\""); parser->getTokens(); @@ -1138,7 +1145,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { { // zwykla szyna: //Ra: czemu główki są asymetryczne na wysokości 0.140? gfx::vertex_array rpts1, rpts2; - create_rail_profile( rpts1, rpts2 ); + create_track_rail_profile( rpts1, rpts2 ); switch (eType) // dalej zależnie od typu { case tt_Table: // obrotnica jak zwykły tor, tylko animacja dochodzi @@ -1146,10 +1153,10 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { if (m_material2) { // podsypka z podkładami jest tylko dla zwykłego toru gfx::vertex_array bpts1; - create_trackbed_profile( bpts1, trPrev, trNext ); + create_track_bed_profile( bpts1, trPrev, trNext ); auto const texturelength { texture_length( m_material2 ) }; gfx::vertex_array vertices; - Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid ? -4 : 4, texturelength); + Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid ? -5 : 5, texturelength); if( ( Bank != 0 ) && ( true == Geometry2.empty() ) ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } @@ -1183,7 +1190,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { if( m_material1 || m_material2 ) { // iglice liczone tylko dla zwrotnic gfx::vertex_array rpts3, rpts4; - create_blade_profile( rpts3, rpts4 ); + create_track_blade_profile( rpts3, rpts4 ); // TODO, TBD: change all track geometry to triangles, to allow packing data in less, larger buffers auto const bladelength { static_cast( std::ceil( SwitchExtension->Segments[ 0 ]->RaSegCount() * 0.65 ) ) }; if (SwitchExtension->RightSwitch) @@ -1255,7 +1262,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { if( true == Global.CreateSwitchTrackbeds ) { gfx::vertex_array vertices; create_switch_trackbed( vertices ); - SwitchExtension->m_geometry3 = GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ); + SwitchExtension->Geometry3 = GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ); vertices.clear(); } @@ -1286,7 +1293,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { auto const slop{ std::abs( fTexSlope ) }; // brzeg zewnętrzny auto const texturelength { texture_length( m_material2 ) }; gfx::vertex_array rpts1, rpts2; // współrzędne przekroju i mapowania dla prawej i lewej strony - create_sidewalk_profile( rpts1, rpts2, bpts1 ); + create_road_side_profile( rpts1, rpts2, bpts1 ); gfx::vertex_array vertices; if( iTrapezoid ) // trapez albo przechyłki { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony @@ -1377,7 +1384,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { if( m_material2 ) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) gfx::vertex_array rpts1, rpts2; // współrzędne przekroju i mapowania dla prawej i lewej strony - create_sidewalk_profile( rpts1, rpts2, bpts1, true ); + create_road_side_profile( rpts1, rpts2, bpts1, true ); // Ra 2014-07: trzeba to przerobić na pętlę i pobierać profile (przynajmniej 2..4) z sąsiednich dróg bool render = ( m_material2 != 0 ); // renderować nie trzeba, ale trzeba wyznaczyć punkty brzegowe nawierzchni auto const side{ std::abs( fTexWidth ) }; // szerokść podsypki na zewnątrz szyny albo pobocza @@ -1502,7 +1509,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) { if (m_material2) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) gfx::vertex_array rpts1, rpts2; // współrzędne przekroju i mapowania dla prawej i lewej strony - create_sidewalk_profile( rpts1, rpts2, bpts1 ); + create_road_side_profile( rpts1, rpts2, bpts1 ); gfx::vertex_array vertices; if (iTrapezoid) // trapez albo przechyłki { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka @@ -1784,7 +1791,7 @@ TTrack * TTrack::RaAnimate() || ( false == Geometry2.empty() ) ) ) { // iglice liczone tylko dla zwrotnic gfx::vertex_array rpts3, rpts4; - create_blade_profile( rpts3, rpts4 ); + create_track_blade_profile( rpts3, rpts4 ); gfx::vertex_array vertices; auto const bladelength { static_cast( std::ceil( SwitchExtension->Segments[ 0 ]->RaSegCount() * 0.65 ) ) }; if (SwitchExtension->RightSwitch) @@ -2226,9 +2233,10 @@ TTrack::DoubleSlip() const { ( iCategoryFlag == 1 ) && ( eType == tt_Switch ) && ( m_name.size() > 2 ) - && ( m_name[ m_name.size() - 2 ] == '_' ) && ( m_name.back() >= 'a' ) - && ( m_name.back() <= 'd' ) ); + && ( m_name.back() <= 'd' ) + && ( ( m_name[ m_name.size() - 2 ] == '_' ) + || ( m_name.rfind( '_' ) != std::string::npos ) ) ); } @@ -2284,7 +2292,7 @@ TTrack * TTrack::Connected(int s, double &d) const // creates rail profile data for current track void -TTrack::create_rail_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ) { +TTrack::create_track_rail_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ) { auto const fHTW { 0.5f * std::abs( fTrackWidth ) }; @@ -2367,7 +2375,7 @@ TTrack::create_rail_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ) // creates switch blades profile data for current track void -TTrack::create_blade_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ) { +TTrack::create_track_blade_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ) { auto const fHTW { 0.5f * std::abs( fTrackWidth ) }; float const fHTW2 { ( @@ -2425,7 +2433,7 @@ TTrack::create_blade_profile( gfx::vertex_array &Right, gfx::vertex_array &Left // creates trackbed profile data for current track void -TTrack::create_trackbed_profile( gfx::vertex_array &Output, TTrack const *Previous, TTrack const *Next ) { +TTrack::create_track_bed_profile( gfx::vertex_array &Output, TTrack const *Previous, TTrack const *Next ) { // geometry parameters auto * profilesource = ( eType != tt_Switch ? this : @@ -2499,10 +2507,11 @@ TTrack::create_trackbed_profile( gfx::vertex_array &Output, TTrack const *Previo // profile auto const transition { ( iTrapezoid != 0 ) || ( eType == tt_Switch ) }; - auto const pointcount { transition ? 8 : 4 }; + auto const pointcount { transition ? 10 : 5 }; Output.resize( pointcount ); // potentially retrieve texture length override from the assigned material - auto const texturelength { texture_length( profilesource->m_material2 ) }; + auto const texturelength { texture_length( copy_adjacent_trackbed_material() ) }; + auto const railheight { 0.18f }; if( texturelength == 4.f ) { // stare mapowanie z różną gęstością pikseli i oddzielnymi teksturami na każdy profil auto const normalx = std::cos( glm::radians( 75.f ) ); @@ -2512,54 +2521,66 @@ TTrack::create_trackbed_profile( gfx::vertex_array &Output, TTrack const *Previo // ewentualnie poprawić mapowanie, żeby środek mapował się na 1.435/4.671 ((0.3464,0.6536) // bo się tekstury podsypki rozjeżdżają po zmianie proporcji profilu Output[ 0 ] = { - {rozp, -texheight1 - 0.18f, 0.f}, + {rozp, -texheight1 - railheight, 0.f}, {normalx, normaly, 0.f}, {0.00f, 0.f} }; // lewy brzeg Output[ 1 ] = { - {( fHTW + side ) * cos1, -( fHTW + side ) * sin1 - 0.18f, 0.f}, + {( fHTW + side ) * cos1, -( fHTW + side ) * sin1 - railheight, 0.f}, {normalx, normaly, 0.f}, {0.33f, 0.f} }; // krawędź załamania Output[ 2 ] = { - {-Output[ 1 ].position.x, +( fHTW + side ) * sin1 - 0.18f, 0.f}, + {0.f, -railheight + 0.01f, 0.f}, + {0.f, 1.f, 0.f}, + {0.5f, 0.f} }; // middle + Output[ 3 ] = { + {-Output[ 1 ].position.x, +( fHTW + side ) * sin1 - railheight, 0.f}, {-normalx, normaly, 0.f}, {0.67f, 0.f} }; // prawy brzeg początku symetrycznie - Output[ 3 ] = { - {-rozp, -texheight1 - 0.18f, 0.f}, + Output[ 4 ] = { + {-rozp, -texheight1 - railheight, 0.f}, {-normalx, normaly, 0.f}, {1.f, 0.f} }; // prawy skos // końcowy przekrój - Output[ 4 ] = { - {rozp2, -fTexHeight2 - 0.18f, 0.f}, + Output[ 5 ] = { + {rozp2, -fTexHeight2 - railheight, 0.f}, {normalx, normaly, 0.f}, {0.00f, 0.f} }; // lewy brzeg - Output[ 5 ] = { - {( fHTW2 + side2 ) * cos2, -( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, + Output[ 6 ] = { + {( fHTW2 + side2 ) * cos2, -( fHTW2 + side2 ) * sin2 - railheight, 0.f}, {normalx, normaly, 0.f}, {0.33f, 0.f} }; // krawędź załamania - Output[ 6 ] = { - {-Output[ 5 ].position.x, +( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, + Output[ 7 ] = { + {0.f, -railheight + 0.01f, 0.f}, + {0.f, 1.f, 0.f}, + {0.5f, 0.f} }; // middle + Output[ 8 ] = { + {-Output[ 6 ].position.x, +( fHTW2 + side2 ) * sin2 - railheight, 0.f}, {-normalx, normaly, 0.f}, {0.67f, 0.f} }; // prawy brzeg początku symetrycznie - Output[ 7 ] = { - {-rozp2, -fTexHeight2 - 0.18f, 0.f}, + Output[ 9 ] = { + {-rozp2, -fTexHeight2 - railheight, 0.f}, {-normalx, normaly, 0.f}, {1.00f, 0.f} }; // prawy skos } else { Output[ 0 ] = { - {rozp, -texheight1 - 0.18f, 0.f}, + {rozp, -texheight1 - railheight, 0.f}, {normalx, normaly, 0.f}, {0.00f, 0.f} }; // lewy brzeg Output[ 1 ] = { - {fHTW + side, -0.18f, 0.f}, + {fHTW + side, -railheight, 0.f}, {normalx, normaly, 0.f}, {0.33f, 0.f} }; // krawędź załamania Output[ 2 ] = { - {-fHTW - side, -0.18f, 0.f}, + {0.f, -railheight + 0.01f, 0.f}, + {0.f, 1.f, 0.f}, + {0.5f, 0.f} }; // middle + Output[ 3 ] = { + {-fHTW - side, -railheight, 0.f}, {-normalx, normaly, 0.f}, {0.67f, 0.f} }; // druga - Output[ 3 ] = { - {-rozp, -texheight1 - 0.18f, 0.f}, + Output[ 4 ] = { + {-rozp, -texheight1 - railheight, 0.f}, {-normalx, normaly, 0.f}, {1.00f, 0.f} }; // prawy skos } @@ -2577,55 +2598,67 @@ TTrack::create_trackbed_profile( gfx::vertex_array &Output, TTrack const *Previo // ((0.3464,0.6536) // bo się tekstury podsypki rozjeżdżają po zmianie proporcji profilu Output[ 0 ] = { - {rozp, -texheight1 - 0.18f, 0.f}, + {rozp, -texheight1 - railheight, 0.f}, {normal1.x, normal1.y, 0.f}, {0.5f - map12, 0.f} }; // lewy brzeg Output[ 1 ] = { - {( fHTW + side ) * cos1, -( fHTW + side ) * sin1 - 0.18f, 0.f}, + {( fHTW + side ) * cos1, -( fHTW + side ) * sin1 - railheight, 0.f}, {normal1.x, normal1.y, 0.f}, {0.5f - map11 , 0.f} }; // krawędź załamania Output[ 2 ] = { - {-Output[ 1 ].position.x, +( fHTW + side ) * sin1 - 0.18f, 0.f}, + {0.f, -railheight + 0.01f, 0.f}, + {0.f, 1.f, 0.f}, + {0.5f, 0.f} }; // middle + Output[ 3 ] = { + {-Output[ 1 ].position.x, +( fHTW + side ) * sin1 - railheight, 0.f}, {-normal1.x, normal1.y, 0.f}, {0.5 + map11, 0.f} }; // prawy brzeg początku symetrycznie - Output[ 3 ] = { - {-rozp, -texheight1 - 0.18f, 0.f}, + Output[ 4 ] = { + {-rozp, -texheight1 - railheight, 0.f}, {-normal1.x, normal1.y, 0.f}, {0.5f + map12, 0.f} }; // prawy skos // przekrój końcowy - Output[ 4 ] = { - {rozp2, -fTexHeight2 - 0.18f, 0.f}, + Output[ 5 ] = { + {rozp2, -fTexHeight2 - railheight, 0.f}, {normal2.x, normal2.y, 0.f}, {0.5f - map22, 0.f} }; // lewy brzeg - Output[ 5 ] = { - {( fHTW2 + side2 ) * cos2, -( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, + Output[ 6 ] = { + {( fHTW2 + side2 ) * cos2, -( fHTW2 + side2 ) * sin2 - railheight, 0.f}, {normal2.x, normal2.y, 0.f}, {0.5f - map21 , 0.f} }; // krawędź załamania - Output[ 6 ] = { - {-Output[ 5 ].position.x, +( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, + Output[ 7 ] = { + {0.f, -railheight + 0.01f, 0.f}, + {0.f, 1.f, 0.f}, + {0.5f, 0.f} }; // middle + Output[ 8 ] = { + {-Output[ 6 ].position.x, +( fHTW2 + side2 ) * sin2 - railheight, 0.f}, {-normal2.x, normal2.y, 0.f}, {0.5f + map21, 0.f} }; // prawy brzeg początku symetrycznie - Output[ 7 ] = { - {-rozp2, -fTexHeight2 - 0.18f, 0.f}, + Output[ 9 ] = { + {-rozp2, -fTexHeight2 - railheight, 0.f}, {-normal2.x, normal2.y, 0.f}, {0.5f + map22, 0.f} }; // prawy skos } else { Output[ 0 ] = { - {rozp, -texheight1 - 0.18f, 0.f}, + {rozp, -texheight1 - railheight, 0.f}, {+normal1.x, normal1.y, 0.f}, {0.5f - map12, 0.f} }; // lewy brzeg Output[ 1 ] = { - {fHTW + side, - 0.18f, 0.f}, + {fHTW + side, - railheight, 0.f}, {+normal1.x, normal1.y, 0.f}, {0.5f - map11, 0.f} }; // krawędź załamania Output[ 2 ] = { - {-fHTW - side, - 0.18f, 0.f}, + {0.f, -railheight + 0.01f, 0.f}, + {0.f, 1.f, 0.f}, + {0.5f, 0.f} }; // middle + Output[ 3 ] = { + {-fHTW - side, - railheight, 0.f}, {-normal1.x, normal1.y, 0.f}, {0.5f + map11, 0.f} }; // druga - Output[ 3 ] = { - {-rozp, -texheight1 - 0.18f, 0.f}, + Output[ 4 ] = { + {-rozp, -texheight1 - railheight, 0.f}, {-normal1.x, normal1.y, 0.f}, {0.5f + map12, 0.f} }; // prawy skos } @@ -2694,7 +2727,7 @@ TTrack::create_road_profile( gfx::vertex_array &Output, bool const Forcetransiti } void -TTrack::create_sidewalk_profile( gfx::vertex_array &Right, gfx::vertex_array &Left, gfx::vertex_array const &Road, bool const Forcetransition ) { +TTrack::create_road_side_profile( gfx::vertex_array &Right, gfx::vertex_array &Left, gfx::vertex_array const &Road, bool const Forcetransition ) { auto const fHTW{ 0.5f * std::abs( fTrackWidth ) }; auto const side{ std::abs( fTexWidth ) }; // szerokść podsypki na zewnątrz szyny albo pobocza @@ -2885,54 +2918,67 @@ TTrack::create_sidewalk_profile( gfx::vertex_array &Right, gfx::vertex_array &Le void TTrack::create_switch_trackbed( gfx::vertex_array &Output ) { // try to get trackbed material from a regular track connected to the primary path - if( ( trPrev != nullptr ) + if( ( SwitchExtension->m_material3 == null_handle ) + && ( trPrev != nullptr ) && ( trPrev->eType == tt_Normal ) ) { SwitchExtension->m_material3 = trPrev->m_material2; } - else if( ( trNext != nullptr ) - && ( trNext->eType == tt_Normal ) ) { + if( ( SwitchExtension->m_material3 == null_handle ) + && ( trNext != nullptr ) + && ( trNext->eType == tt_Normal ) ) { SwitchExtension->m_material3 = trNext->m_material2; } // without material don't bother if( SwitchExtension->m_material3 == null_handle ) { return; } // generate trackbed for each path of the switch... - // TODO: create proper profile for each path auto const texturelength { texture_length( SwitchExtension->m_material3 ) }; gfx::vertex_array trackbedprofile; gfx::vertex_array trackbedvertices1, trackbedvertices2; - create_trackbed_profile( trackbedprofile, SwitchExtension->pPrevs[ 0 ], SwitchExtension->pNexts[ 0 ] ); - SwitchExtension->Segments[ 0 ]->RenderLoft( trackbedvertices1, m_origin, trackbedprofile, -4, texturelength ); - create_trackbed_profile( trackbedprofile, SwitchExtension->pPrevs[ 1 ], SwitchExtension->pNexts[ 1 ] ); - SwitchExtension->Segments[ 1 ]->RenderLoft( trackbedvertices2, m_origin, trackbedprofile, -4, texturelength ); -// Output = trackbedvertices1; -// return; - // ...and take outer edge from each to create combined trackbed - auto const segmentsize { 8 }; + // main trackbed + create_track_bed_profile( trackbedprofile, SwitchExtension->pPrevs[ 0 ], SwitchExtension->pNexts[ 0 ] ); + SwitchExtension->Segments[ 0 ]->RenderLoft( trackbedvertices1, m_origin, trackbedprofile, -5, texturelength ); + // side trackbed + create_track_bed_profile( trackbedprofile, SwitchExtension->pPrevs[ 1 ], SwitchExtension->pNexts[ 1 ] ); + SwitchExtension->Segments[ 1 ]->RenderLoft( trackbedvertices2, m_origin, trackbedprofile, -5, texturelength ); + // ...then combine them into a single geometry sequence + auto const segmentsize { 10 }; auto const segmentcount { trackbedvertices1.size() / segmentsize }; - auto const *sampler1 { trackbedvertices1.data() }; - auto const *sampler2 { trackbedvertices2.data() }; + auto *sampler1 { trackbedvertices1.data() }; + auto *sampler2 { trackbedvertices2.data() }; auto const isright { SwitchExtension->RightSwitch }; auto const isleft { false == isright }; auto const samplersoffset { isright ? 2 : 0 }; + auto const geometryoffset { 0.025f }; for( int segment = 0; segment < segmentcount; ++segment ) { - Output.emplace_back( *( sampler1 + samplersoffset + 0 ) ); if( isright ) { Output.back().position.y -= 0.02f; } - Output.emplace_back( *( sampler1 + samplersoffset + 1 ) ); if( isright ) { Output.back().position.y -= 0.02f; } - - Output.emplace_back( *( sampler1 + samplersoffset + 2 ) ); - Output.emplace_back( *( sampler1 + samplersoffset + 3 ) ); - - Output.emplace_back( *( sampler1 + samplersoffset + 4 ) ); if( isleft ) { Output.back().position.y -= 0.02f; } - Output.emplace_back( *( sampler1 + samplersoffset + 5 ) ); if( isleft ) { Output.back().position.y -= 0.02f; } - - Output.emplace_back( *( sampler2 - samplersoffset + 2 ) ); if( isleft ) { Output.back().position.y -= 0.02f; } - Output.emplace_back( *( sampler2 - samplersoffset + 3 ) ); if( isleft ) { Output.back().position.y -= 0.02f; } - - Output.emplace_back( *( sampler2 - samplersoffset + 4 ) ); - Output.emplace_back( *( sampler2 - samplersoffset + 5 ) ); - - Output.emplace_back( *( sampler2 - samplersoffset + 6 ) ); if( isright ) { Output.back().position.y -= 0.02f; } - Output.emplace_back( *( sampler2 - samplersoffset + 7 ) ); if( isright ) { Output.back().position.y -= 0.02f; } - + // main trackbed + // lower outer edge to avoid z-fighting + if( isright ) { + ( sampler1 + samplersoffset + 0 )->position.y -= geometryoffset; + ( sampler1 + samplersoffset + 1 )->position.y -= geometryoffset; + } + if( isleft ) { + ( sampler1 + samplersoffset + 6 )->position.y -= geometryoffset; + ( sampler1 + samplersoffset + 7 )->position.y -= geometryoffset; + } + // copy the data + for( auto pointidx = 0; pointidx < segmentsize; ++pointidx ) { + Output.emplace_back( *( sampler1 + pointidx ) ); + } + // side trackbed + // lower outer edge to avoid z-fighting + if( isleft ) { + ( sampler2 - samplersoffset + 2 )->position.y -= geometryoffset; + ( sampler2 - samplersoffset + 3 )->position.y -= geometryoffset; + } + if( isright ) { + ( sampler2 - samplersoffset + 8 )->position.y -= geometryoffset; + ( sampler2 - samplersoffset + 9 )->position.y -= geometryoffset; + } + // copy the data + for( auto pointidx = 0; pointidx < segmentsize; ++pointidx ) { + Output.emplace_back( *( sampler2 + pointidx ) ); + } + // switch to next segment data sampler1 += segmentsize; sampler2 += segmentsize; } @@ -2951,6 +2997,7 @@ TTrack::copy_adjacent_trackbed_material( TTrack const *Exclude ) { switch( eType ) { case tt_Normal: { // for regular tracks don't set the trackbed texture if we aren't sitting next to a part of a double slip +/* auto const hasadjacentdoubleslip { ( trPrev ? trPrev->DoubleSlip() : false ) || ( trNext ? trNext->DoubleSlip() : false ) }; @@ -2959,6 +3006,16 @@ TTrack::copy_adjacent_trackbed_material( TTrack const *Exclude ) { adjacents.emplace_back( trPrev ); adjacents.emplace_back( trNext ); } +*/ + auto const hasadjacentswitch { + ( trPrev && trPrev->eType == tt_Switch ) + || ( trNext && trNext->eType == tt_Switch ) }; + +// if( true == hasadjacentdoubleslip ) { + if( true == hasadjacentswitch ) { + adjacents.emplace_back( trPrev ); + adjacents.emplace_back( trNext ); + } break; } case tt_Switch: { diff --git a/Track.h b/Track.h index 821dae90..c2384802 100644 --- a/Track.h +++ b/Track.h @@ -94,8 +94,7 @@ class TSwitchExtension float fVelocity = -1.0; // maksymalne ograniczenie prędkości (ustawianej eventem) Math3D::vector3 vTrans; // docelowa translacja przesuwnicy material_handle m_material3 = 0; // texture of auto generated switch trackbed - gfx::geometry_handle m_geometry3; // geometry of auto generated switch trackbed - + gfx::geometry_handle Geometry3; // geometry of auto generated switch trackbed }; class TIsolated @@ -306,12 +305,12 @@ private: // returns texture length for specified material float texture_length( material_handle const Material ); // creates profile for a part of current path - void create_rail_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ); - void create_blade_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ); - void create_trackbed_profile( gfx::vertex_array &Output, TTrack const *Previous, TTrack const *Next ); - void create_road_profile( gfx::vertex_array &Output, bool const Forcetransition = false ); - void create_sidewalk_profile( gfx::vertex_array &Right, gfx::vertex_array &Left, gfx::vertex_array const &Road, bool const Forcetransition = false ); void create_switch_trackbed( gfx::vertex_array &Output ); + void create_track_rail_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ); + void create_track_blade_profile( gfx::vertex_array &Right, gfx::vertex_array &Left ); + void create_track_bed_profile( gfx::vertex_array &Output, TTrack const *Previous, TTrack const *Next ); + void create_road_profile( gfx::vertex_array &Output, bool const Forcetransition = false ); + void create_road_side_profile( gfx::vertex_array &Right, gfx::vertex_array &Left, gfx::vertex_array const &Road, bool const Forcetransition = false ); }; diff --git a/Train.cpp b/Train.cpp index e9b0312a..e224af50 100644 --- a/Train.cpp +++ b/Train.cpp @@ -447,7 +447,9 @@ bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d) PyObject *TTrain::GetTrainState() { auto const *mover = DynamicObject->MoverParameters; + PyEval_AcquireLock(); auto *dict = PyDict_New(); + PyEval_ReleaseLock(); if( ( dict == nullptr ) || ( mover == nullptr ) ) { return nullptr; diff --git a/drivermode.cpp b/drivermode.cpp index 77dd143c..cc5d8ce1 100644 --- a/drivermode.cpp +++ b/drivermode.cpp @@ -178,6 +178,11 @@ driver_mode::update() { // variable step simulation time routines + if( ( simulation::Train == nullptr ) && ( false == FreeFlyModeFlag ) ) { + // intercept cases when the driven train got removed after entering portal + InOutKey(); + } + if( Global.changeDynObj ) { // ABu zmiana pojazdu - przejście do innego ChangeDynamic(); diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index bdbf15fc..7bf6e177 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -336,6 +336,9 @@ Source Files + + Source Files\imgui + diff --git a/renderer.cpp b/renderer.cpp index 883ffb47..a18be646 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -2651,7 +2651,7 @@ opengl_renderer::Render( TTrack *Track ) { if( ( Track->eType == tt_Switch ) && ( Track->SwitchExtension->m_material3 != 0 ) ) { Bind_Material( Track->SwitchExtension->m_material3 ); - m_geometry.draw( Track->SwitchExtension->m_geometry3 ); + m_geometry.draw( Track->SwitchExtension->Geometry3 ); } setup_environment_light(); break; @@ -2676,7 +2676,7 @@ opengl_renderer::Render( TTrack *Track ) { if( ( Track->eType == tt_Switch ) && ( Track->SwitchExtension->m_material3 != 0 ) ) { Bind_Material( Track->SwitchExtension->m_material3 ); - m_geometry.draw( Track->SwitchExtension->m_geometry3 ); + m_geometry.draw( Track->SwitchExtension->Geometry3 ); } break; } @@ -2817,7 +2817,7 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, setup_environment_light( track->eEnvironment ); } Bind_Material( track->SwitchExtension->m_material3 ); - m_geometry.draw( track->SwitchExtension->m_geometry3 ); + m_geometry.draw( track->SwitchExtension->Geometry3 ); if( track->eEnvironment != e_flat ) { // restore default lighting setup_environment_light(); @@ -2832,7 +2832,7 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, continue; } Bind_Material( track->SwitchExtension->m_material3 ); - m_geometry.draw( track->SwitchExtension->m_geometry3 ); + m_geometry.draw( track->SwitchExtension->Geometry3 ); break; } case rendermode::pickscenery: // pick scenery mode uses piece-by-piece approach @@ -3858,8 +3858,13 @@ opengl_renderer::Init_caps() { + " Vendor: " + std::string( (char *)glGetString( GL_VENDOR ) ) + " OpenGL Version: " + oglversion ); +#ifdef EU07_USEIMGUIIMPLOPENGL2 + if( !GLEW_VERSION_1_5 ) { + ErrorLog( "Requires openGL >= 1.5" ); +#else if( !GLEW_VERSION_3_0 ) { - ErrorLog( "Requires openGL >= 3.0" ); // technically 1.5 for now, but imgui wants more + ErrorLog( "Requires openGL >= 3.0" ); +#endif return false; } diff --git a/renderer.h b/renderer.h index 34c73e10..2ce563d8 100644 --- a/renderer.h +++ b/renderer.h @@ -25,6 +25,7 @@ http://mozilla.org/MPL/2.0/. //#define EU07_USE_DEBUG_CABSHADOWMAP //#define EU07_USE_DEBUG_CAMERA //#define EU07_USE_DEBUG_SOUNDEMITTERS +#define EU07_USEIMGUIIMPLOPENGL2 struct opengl_light : public basic_light { diff --git a/scene.cpp b/scene.cpp index 7c334196..81933f3f 100644 --- a/scene.cpp +++ b/scene.cpp @@ -1163,7 +1163,10 @@ std::vector switchtrackbedtextures { "rozkrz34r150-tpd1", "rkpd34r190-tpd1", "rkpd34r190-tpd2", - "rkpd34r190-tpd-oil2" }; + "rkpd34r190-tpd-oil2", + "zwr41r500", + "zwrot-tpd-oil1", + "zwrot34r300pods-new" }; void basic_region::insert( shape_node Shape, scratch_data &Scratchpad, bool const Transform ) { diff --git a/uilayer.cpp b/uilayer.cpp index 94ccfaea..f0d998f9 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -14,7 +14,11 @@ http://mozilla.org/MPL/2.0/. #include "renderer.h" #include "imgui_impl_glfw.h" +#ifdef EU07_USEIMGUIIMPLOPENGL2 +#include "imgui_impl_opengl2.h" +#else #include "imgui_impl_opengl3.h" +#endif extern "C" { @@ -74,8 +78,12 @@ ui_layer::init( GLFWwindow *Window ) { // m_imguiio->Fonts->AddFontFromFileTTF( "c:/windows/fonts/lucon.ttf", 13.0f ); ImGui_ImplGlfw_InitForOpenGL( m_window, false ); +#ifdef EU07_USEIMGUIIMPLOPENGL2 + ImGui_ImplOpenGL2_Init(); +#else // ImGui_ImplOpenGL3_Init( "#version 140" ); ImGui_ImplOpenGL3_Init(); +#endif init_colors(); @@ -119,7 +127,11 @@ ui_layer::init_colors() { void ui_layer::shutdown() { +#ifdef EU07_USEIMGUIIMPLOPENGL2 + ImGui_ImplOpenGL2_Shutdown(); +#else ImGui_ImplOpenGL3_Shutdown(); +#endif ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); } @@ -189,7 +201,11 @@ ui_layer::render() { ::glClientActiveTexture( m_textureunit ); ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); +#ifdef EU07_USEIMGUIIMPLOPENGL2 + ImGui_ImplOpenGL2_NewFrame(); +#else ImGui_ImplOpenGL3_NewFrame(); +#endif ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); @@ -199,7 +215,11 @@ ui_layer::render() { render_(); ImGui::Render(); +#ifdef EU07_USEIMGUIIMPLOPENGL2 + ImGui_ImplOpenGL2_RenderDrawData( ImGui::GetDrawData() ); +#else ImGui_ImplOpenGL3_RenderDrawData( ImGui::GetDrawData() ); +#endif ::glPopClientAttrib(); } diff --git a/version.h b/version.h index 2ab9ae6d..452cdd08 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 18 -#define VERSION_MINOR 1005 +#define VERSION_MINOR 1007 #define VERSION_REVISION 0 From 9cacf191c60046853da1947eead273e9a9db326d Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Tue, 9 Oct 2018 18:48:24 +0200 Subject: [PATCH 5/6] submodel opacity parameter, additional light blinking modes, scenario event queue filtering --- AnimModel.cpp | 110 +++++++++++++++++++++++++++++++++++++-------- AnimModel.h | 11 +++-- Driver.cpp | 4 +- Model3d.cpp | 23 +++++++++- Model3d.h | 6 ++- Traction.cpp | 2 +- driveruipanels.cpp | 68 +++++++++++++++------------- driveruipanels.h | 3 +- renderer.cpp | 36 +++++++++++---- 9 files changed, 195 insertions(+), 68 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index be263b5b..c004a647 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -444,8 +444,9 @@ bool TAnimModel::Init(std::string const &asName, std::string const &asReplacable // tekstura nieprzezroczysta - nie renderować w cyklu przezroczystych m_materialdata.textures_alpha = 0x30300030; } - - fBlinkTimer = double( Random( 1000 * fOffTime ) ) / ( 1000 * fOffTime ); + +// TODO: redo the random timer initialization +// fBlinkTimer = Random() * ( fOnTime + fOffTime ); pModel = TModelsManager::GetModel( asName ); return ( pModel != nullptr ); @@ -547,9 +548,73 @@ void TAnimModel::RaAnimate( unsigned int const Framestamp ) { if( Framestamp == m_framestamp ) { return; } - fBlinkTimer -= Timer::GetDeltaTime(); - if( fBlinkTimer <= 0 ) - fBlinkTimer += fOffTime; + auto const timedelta { Timer::GetDeltaTime() }; + + // interpretacja ułamka zależnie od typu + // case ls_Off: ustalenie czasu migotania, t<1s (f>1Hz), np. 0.1 => t=0.1 (f=10Hz) + // case ls_On: ustalenie wypełnienia ułamkiem, np. 1.25 => zapalony przez 1/4 okresu + // case ls_Blink: ustalenie częstotliwości migotania, f<1Hz (t>1s), np. 2.2 => f=0.2Hz (t=5s) + float modeintegral, modefractional; + for( int idx = 0; idx < iNumLights; ++idx ) { + + modefractional = std::modf( std::abs( lsLights[ idx ] ), &modeintegral ); + + if( modeintegral >= ls_Dark ) { + // light threshold modes don't use timers + continue; + } + auto const mode { static_cast( modeintegral ) }; + + auto &opacity { m_lightopacities[ idx ] }; + auto &timer { m_lighttimers[ idx ] }; + if( ( modeintegral < ls_Blink ) && ( modefractional < 0.01f ) ) { + // simple flip modes + switch( mode ) { + case ls_Off: { + // reduce to zero + timer = std::max( 0.f, timer - timedelta ); + break; + } + case ls_On: { + // increase to max value + timer = std::min( fTransitionTime, timer + timedelta ); + break; + } + default: { + break; + } + } + opacity = timer / fTransitionTime; + } + else { + // blink modes + auto const ontime { ( + ( mode == ls_Blink ) ? ( ( modefractional < 0.01f ) ? fOnTime : ( 1.f / modefractional ) * 0.5f ) : + ( mode == ls_Off ) ? modefractional * 0.5f : + ( mode == ls_On ) ? modefractional * ( fOnTime + fOffTime ) : + fOnTime ) }; // fallback + auto const offtime { ( + ( mode == ls_Blink ) ? ( ( modefractional < 0.01f ) ? fOffTime : ontime ) : + ( mode == ls_Off ) ? ontime : + ( mode == ls_On ) ? ( fOnTime + fOffTime ) - ontime : + fOffTime ) }; // fallback + auto const transitiontime { + std::min( + 1.f, + std::min( ontime, offtime ) * 0.9f ) }; + + timer = clamp_circular( timer + timedelta * ( lsLights[ idx ] > 0.f ? 1.f : -1.f ), ontime + offtime ); + // set opacity depending on blink stage + if( timer < ontime ) { + // blink on + opacity = clamp( timer / transitiontime, 0.f, 1.f ); + } + else { + // blink off + opacity = 1.f - clamp( ( timer - ontime ) / transitiontime, 0.f, 1.f ); + } + } + } // Ra 2F1I: to by można pomijać dla modeli bez animacji, których jest większość TAnimContainer *pCurrent; @@ -568,17 +633,19 @@ void TAnimModel::RaPrepare() bool state; // stan światła for (int i = 0; i < iNumLights; ++i) { - auto const lightmode { static_cast( lsLights[ i ] ) }; + auto const lightmode { static_cast( std::abs( lsLights[ i ] ) ) }; switch( lightmode ) { case ls_On: - case ls_Off: { - // zapalony albo zgaszony - state = ( lightmode == ls_On ); - break; - } + case ls_Off: case ls_Blink: { - // migotanie - state = ( fBlinkTimer < fOnTime ); + if (LightsOn[i]) { + LightsOn[i]->iVisible = ( m_lightopacities[i] > 0.f ); + LightsOn[i]->SetVisibilityLevel( m_lightopacities[i], true, false ); + } + if (LightsOff[i]) { + LightsOff[i]->iVisible = ( m_lightopacities[i] < 1.f ); + LightsOff[i]->SetVisibilityLevel( 1.f, true, false ); + } break; } case ls_Dark: { @@ -606,10 +673,16 @@ void TAnimModel::RaPrepare() break; } } - if (LightsOn[i]) - LightsOn[i]->iVisible = state; - if (LightsOff[i]) - LightsOff[i]->iVisible = !state; + if( lightmode >= ls_Dark ) { + // crude as hell but for test will do :x + if (LightsOn[i]) { + LightsOn[i]->iVisible = state; + // TODO: set visibility for the entire submodel's children as well + LightsOn[i]->fVisible = m_lightopacities[i]; + } + if (LightsOff[i]) + LightsOff[i]->iVisible = !state; + } } TSubModel::iInstance = reinterpret_cast( this ); //żeby nie robić cudzych animacji TSubModel::pasText = &asText; // przekazanie tekstu do wyświetlacza (!!!! do przemyślenia) @@ -774,8 +847,9 @@ void TAnimModel::AnimationVND(void *pData, double a, double b, double c, double //--------------------------------------------------------------------------- void TAnimModel::LightSet(int const n, float const v) { // ustawienie światła (n) na wartość (v) - if (n >= iMaxNumLights) + if( n >= iMaxNumLights ) { return; // przekroczony zakres + } lsLights[ n ] = v; }; diff --git a/AnimModel.h b/AnimModel.h index 6384f164..63f06a59 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -179,7 +179,7 @@ private: // members TAnimContainer *pRoot { nullptr }; // pojemniki sterujące, tylko dla aniomowanych submodeli TModel3d *pModel { nullptr }; - double fBlinkTimer { 0.0 }; +// double fBlinkTimer { 0.0 }; int iNumLights { 0 }; TSubModel *LightsOn[ iMaxNumLights ]; // Ra: te wskaźniki powinny być w ramach TModel3d TSubModel *LightsOff[ iMaxNumLights ]; @@ -188,10 +188,13 @@ private: std::string asText; // tekst dla wyświetlacza znakowego TAnimAdvanced *pAdvanced { nullptr }; + // TODO: wrap into a light state struct float lsLights[ iMaxNumLights ]; -// float fDark { DefaultDarkThresholdLevel }; // poziom zapalanie światła (powinno być chyba powiązane z danym światłem?) - float fOnTime { 0.66f }; - float fOffTime { 0.66f + 0.66f }; // były stałymi, teraz mogą być zmienne dla każdego egzemplarza + std::array m_lighttimers { 0.f }; + std::array m_lightopacities { 1.f }; + float fOnTime { 1.f / 2 };// { 60.f / 45.f / 2 }; + float fOffTime { 1.f / 2 };// { 60.f / 45.f / 2 }; // były stałymi, teraz mogą być zmienne dla każdego egzemplarza + float fTransitionTime { fOnTime * 0.9f }; // time unsigned int m_framestamp { 0 }; // id of last rendered gfx frame }; diff --git a/Driver.cpp b/Driver.cpp index ddba2e45..1a7daf0a 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -342,8 +342,8 @@ std::string TSpeedPos::TableText() const { // pozycja tabelki pr?dko?ci if (iFlags & spEnabled) { // o ile pozycja istotna - return "Flags:" + to_hex_str(iFlags, 8) + ", Dist:" + to_string(fDist, 1, 6) + - ", Vel:" + (fVelNext == -1.0 ? " - " : to_string(static_cast(fVelNext), 0, 3)) + ", Name:" + GetName(); + return to_hex_str(iFlags, 8) + " " + to_string(fDist, 1, 6) + + " " + (fVelNext == -1.0 ? " -" : to_string(static_cast(fVelNext), 0, 3)) + " " + GetName(); } return "Empty"; } diff --git a/Model3d.cpp b/Model3d.cpp index 272cb7cb..ecf7ed53 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -71,6 +71,23 @@ void TSubModel::Name(std::string const &Name) pName = Name; }; +// sets visibility level (alpha component) to specified value +void +TSubModel::SetVisibilityLevel( float const Level, bool const Includechildren, bool const Includesiblings ) { + + fVisible = Level; + if( true == Includesiblings ) { + auto sibling { this }; + while( ( sibling = sibling->Next ) != nullptr ) { + sibling->SetVisibilityLevel( Level, Includechildren, false ); // no need for all siblings to duplicate the work + } + } + if( ( true == Includechildren ) + && ( Child != nullptr ) ) { + Child->SetVisibilityLevel( Level, Includechildren, true ); // node's children include child's siblings and children + } +} + // sets light level (alpha component of illumination color) to specified value void TSubModel::SetLightLevel( float const Level, bool const Includechildren, bool const Includesiblings ) { @@ -1313,7 +1330,8 @@ void TSubModel::serialize(std::ostream &s, else sn_utils::ls_int32(s, (int32_t)get_container_pos(textures, m_materialname)); - sn_utils::ls_float32(s, fVisible); +// sn_utils::ls_float32(s, fVisible); + sn_utils::ls_float32(s, 1.f); sn_utils::ls_float32(s, fLight); sn_utils::s_vec4(s, f4Ambient); @@ -1433,7 +1451,8 @@ void TSubModel::deserialize(std::istream &s) tVboPtr = sn_utils::ld_int32(s); iTexture = sn_utils::ld_int32(s); - fVisible = sn_utils::ld_float32(s); +// fVisible = sn_utils::ld_float32(s); + auto discard = sn_utils::ld_float32(s); fLight = sn_utils::ld_float32(s); f4Ambient = sn_utils::d_vec4(s); diff --git a/Model3d.h b/Model3d.h index 7c8317a1..68a82983 100644 --- a/Model3d.h +++ b/Model3d.h @@ -98,7 +98,6 @@ private: int iNumVerts { -1 }; // ilość wierzchołków (1 dla FreeSpotLight) int tVboPtr; // początek na liście wierzchołków albo indeksów int iTexture { 0 }; // numer nazwy tekstury, -1 wymienna, 0 brak - float fVisible { 0.0f }; // próg jasności światła do załączenia submodelu float fLight { -1.0f }; // próg jasności światła do zadziałania selfillum glm::vec4 f4Ambient { 1.0f,1.0f,1.0f,1.0f }, @@ -140,7 +139,8 @@ public: // chwilowo float4x4 *mAnimMatrix{ nullptr }; // macierz do animacji kwaternionowych (należy do AnimContainer) TSubModel **smLetter{ nullptr }; // wskaźnik na tablicę submdeli do generoania tekstu (docelowo zapisać do E3D) TSubModel *Parent{ nullptr }; // nadrzędny, np. do wymnażania macierzy - int iVisible{ 1 }; // roboczy stan widoczności + int iVisible { 1 }; // roboczy stan widoczności + float fVisible { 1.f }; // visibility level std::string m_materialname; // robocza nazwa tekstury do zapisania w pliku binarnym std::string pName; // robocza nazwa private: @@ -192,6 +192,8 @@ public: int Flags() const { return iFlags; }; void UnFlagNext() { iFlags &= 0x00FFFFFF; }; void ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ); + // sets visibility level (alpha component) to specified value + void SetVisibilityLevel( float const Level, bool const Includechildren = false, bool const Includesiblings = false ); // sets light level (alpha component of illumination color) to specified value void SetLightLevel( float const Level, bool const Includechildren = false, bool const Includesiblings = false ); inline float3 Translation1Get() { diff --git a/Traction.cpp b/Traction.cpp index 28ff95a7..9839ea64 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -565,7 +565,7 @@ TTraction::wire_color() const { color.r *= Global.DayLight.ambient[ 0 ]; color.g *= Global.DayLight.ambient[ 1 ]; color.b *= Global.DayLight.ambient[ 2 ]; - color *= 0.5f; + color *= 0.35f; } else { // tymczasowo pokazanie zasilanych odcinków diff --git a/driveruipanels.cpp b/driveruipanels.cpp index ceb9253f..71572dc1 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -356,7 +356,10 @@ debug_panel::render() { render_section( "Vehicle AI", m_ailines ); render_section( "Vehicle Scan Table", m_scantablelines ); render_section( "Scenario", m_scenariolines ); - render_section( "Scenario Event Queue", m_eventqueuelines ); + if( true == render_section( "Scenario Event Queue", m_eventqueuelines ) ) { + // event queue filter + ImGui::Checkbox( "By This Vehicle Only", &m_eventqueueactivevehicleonly ); + } render_section( "Camera", m_cameralines ); render_section( "Gfx Renderer", m_rendererlines ); // toggles @@ -764,6 +767,8 @@ debug_panel::update_section_scantable( std::vector &Output ) { if( m_input.mechanik == nullptr ) { return; } + Output.emplace_back( "Flags: Dist: Vel: Name:", Global.UITextColor ); + auto const &mechanik{ *m_input.mechanik }; std::size_t i = 0; std::size_t const speedtablesize = clamp( static_cast( mechanik.TableSize() ) - 1, 0, 30 ); @@ -773,8 +778,8 @@ debug_panel::update_section_scantable( std::vector &Output ) { Output.emplace_back( Bezogonkow( scanline ), Global.UITextColor ); ++i; } while( i < speedtablesize ); - if( Output.empty() ) { - Output.emplace_back( "(no points of interest)", Global.UITextColor ); + if( Output.size() == 1 ) { + Output.front().data = "(no points of interest)"; } } @@ -787,7 +792,8 @@ 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 ); + textline = "Cloud cover: " + to_string( Global.Overcast, 3 ); + textline += "\nLight level: " + to_string( Global.fLuminance, 3 ); if( Global.FakeLight ) { textline += "(*)"; } textline += "\nAir temperature: " + to_string( Global.AirTemperature, 1 ) + " deg C"; @@ -799,32 +805,33 @@ debug_panel::update_section_eventqueue( std::vector &Output ) { std::string textline; - // current event queue - auto const time { Timer::GetTime() }; - auto const *event { simulation::Events.begin() }; - auto eventtableindex{ 0 }; - while( ( event != nullptr ) - && ( eventtableindex < 30 ) ) { + // current event queue + auto const time { Timer::GetTime() }; + auto const *event { simulation::Events.begin() }; - if( ( false == event->m_ignored ) - && ( false == event->m_passive ) ) { + Output.emplace_back( "Delay: Event:", Global.UITextColor ); - auto const delay { " " + to_string( std::max( 0.0, event->m_launchtime - time ), 1 ) }; - textline = - "Delay: " + delay.substr( delay.length() - 6 ) - + ", Event: " + event->m_name - + ( event->m_activator ? " (by: " + event->m_activator->asName + ")" : "" ) - + ( event->m_sibling ? " (joint event)" : "" ); + while( ( event != nullptr ) + && ( Output.size() < 30 ) ) { - Output.emplace_back( textline, Global.UITextColor ); - ++eventtableindex; - } - event = event->m_next; - } - if( Output.empty() ) { - textline = "(no queued events)"; - Output.emplace_back( textline, Global.UITextColor ); - } + if( ( false == event->m_ignored ) + && ( false == event->m_passive ) + && ( ( false == m_eventqueueactivevehicleonly ) + || ( event->m_activator == m_input.vehicle ) ) ) { + + auto const delay { " " + to_string( std::max( 0.0, event->m_launchtime - time ), 1 ) }; + textline = delay.substr( delay.length() - 6 ) + + " " + event->m_name + + ( event->m_activator ? " (by: " + event->m_activator->asName + ")" : "" ) + + ( event->m_sibling ? " (joint event)" : "" ); + + Output.emplace_back( textline, Global.UITextColor ); + } + event = event->m_next; + } + if( Output.size() == 1 ) { + Output.front().data = "(no queued events)"; + } } void @@ -888,15 +895,16 @@ debug_panel::update_section_renderer( std::vector &Output ) { Output.emplace_back( GfxRenderer.info_stats(), Global.UITextColor ); } -void +bool debug_panel::render_section( std::string const &Header, std::vector const &Lines ) { - if( Lines.empty() ) { return; } - if( false == ImGui::CollapsingHeader( Header.c_str() ) ) { return; } + if( true == Lines.empty() ) { return false; } + if( false == ImGui::CollapsingHeader( Header.c_str() ) ) { return false; } for( auto const &line : Lines ) { ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); } + return true; } void diff --git a/driveruipanels.h b/driveruipanels.h index abd09a3f..505565be 100644 --- a/driveruipanels.h +++ b/driveruipanels.h @@ -76,7 +76,7 @@ private: std::string update_vehicle_coupler( int const Side ); std::string update_vehicle_brake() const; // renders provided lines, under specified collapsing header - void render_section( std::string const &Header, std::vector const &Lines ); + bool render_section( std::string const &Header, std::vector const &Lines ); // members std::array m_buffer; input_data m_input; @@ -92,6 +92,7 @@ private: int tprev { 0 }; // poprzedni czas double VelPrev { 0.0 }; // poprzednia prędkość double Acc { 0.0 }; // przyspieszenie styczne + bool m_eventqueueactivevehicleonly { false }; }; class transcripts_panel : public ui_panel { diff --git a/renderer.cpp b/renderer.cpp index a18be646..f172bf0b 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -2409,7 +2409,20 @@ opengl_renderer::Render( TSubModel *Submodel ) { Bind_Material( Submodel->m_material ); } // ...colors... - ::glColor3fv( glm::value_ptr( Submodel->f4Diffuse ) ); // McZapkie-240702: zamiast ub + if( Submodel->fVisible < 1.f ) { + // setup + ::glAlphaFunc( GL_GREATER, 0.f ); + ::glEnable( GL_BLEND ); + ::glColor4f( + Submodel->f4Diffuse.r, + Submodel->f4Diffuse.g, + Submodel->f4Diffuse.b, + Submodel->fVisible ); + } + else { + ::glColor3fv( glm::value_ptr( Submodel->f4Diffuse ) ); // McZapkie-240702: zamiast ub + } + // ...specular... if( ( true == m_renderspecular ) && ( m_sunlight.specular.a > 0.01f ) ) { // specular strength in legacy models is set uniformly to 150, 150, 150 so we scale it down for opaque elements ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( Submodel->f4Specular * m_sunlight.specular.a * m_specularopaquescalefactor ) ); @@ -2441,6 +2454,10 @@ opengl_renderer::Render( TSubModel *Submodel ) { } */ // post-draw reset + if( Submodel->fVisible < 1.f ) { + ::glAlphaFunc( GL_GREATER, 0.5f ); + ::glDisable( GL_BLEND ); + } if( ( true == m_renderspecular ) && ( m_sunlight.specular.a > 0.01f ) ) { ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( colors::none ) ); } @@ -2535,7 +2552,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { // distance attenuation. NOTE: since it's fixed pipeline with built-in gamma correction we're using linear attenuation // we're capping how much effect the distance attenuation can have, otherwise the lights get too tiny at regular distances float const distancefactor { std::max( 0.5f, ( Submodel->fSquareMaxDist - TSubModel::fSquareDist ) / Submodel->fSquareMaxDist ) }; - float const precipitationfactor { std::max( 1.f, Global.Overcast - 1.f ) }; + float const precipitationfactor { std::max( 1.f, 0.5f * ( Global.Overcast - 1.f ) ) }; if( lightlevel > 0.f ) { // material configuration: @@ -2543,9 +2560,11 @@ opengl_renderer::Render( TSubModel *Submodel ) { Bind_Material( null_handle ); ::glPointSize( std::max( 3.f, 5.f * distancefactor * anglefactor ) ); - ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], std::min( 1.f, lightlevel * anglefactor * precipitationfactor ) ); + ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], Submodel->fVisible * std::min( 1.f, lightlevel * anglefactor * precipitationfactor ) ); ::glDisable( GL_LIGHTING ); + ::glDisable( GL_FOG ); ::glEnable( GL_BLEND ); + ::glAlphaFunc( GL_GREATER, 0.f ); ::glPushMatrix(); ::glLoadIdentity(); @@ -3439,10 +3458,10 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { float glarelevel = 0.6f; // luminosity at night is at level of ~0.1, so the overall resulting transparency in clear conditions is ~0.5 at full 'brightness' if( Submodel->fCosViewAngle > Submodel->fCosFalloffAngle ) { // only bother if the viewer is inside the visibility cone - auto glarelevel { clamp( - 0.6f - - Global.fLuminance // reduce the glare in bright daylight - + std::max( 0.f, Global.Overcast - 1.f ), // increase the glare in rainy/foggy conditions + auto glarelevel { clamp( + std::max( + 0.6f - Global.fLuminance, // reduce the glare in bright daylight + Global.Overcast - 1.f ), // ensure some glare in rainy/foggy conditions 0.f, 1.f ) }; // scale it down based on view angle glarelevel *= ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( 1.0f - Submodel->fCosFalloffAngle ); @@ -3452,8 +3471,9 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { ::glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT ); Bind_Texture( m_glaretexture ); - ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], glarelevel ); + ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], Submodel->fVisible * glarelevel ); ::glDisable( GL_LIGHTING ); + ::glDisable( GL_FOG ); ::glDepthMask( GL_FALSE ); ::glBlendFunc( GL_SRC_ALPHA, GL_ONE ); From 8a904f4da3192164b4f512291f6e11cc56e512a3 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Wed, 10 Oct 2018 18:44:39 +0200 Subject: [PATCH 6/6] build 181010. ai disconnect logic tweak, fog-driven freespot blur --- Driver.cpp | 251 ++++++++++++++++++++++++++------------------------- renderer.cpp | 66 +++++++++----- renderer.h | 1 + version.h | 2 +- 4 files changed, 174 insertions(+), 146 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 1a7daf0a..1ad4807e 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -4262,123 +4262,6 @@ TController::UpdateSituation(double dt) { fMaxProximityDist = 10.0; //[m] fVelPlus = 1.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania fVelMinus = 0.5; // margines prędkości powodujący załączenie napędu - if (AIControllFlag) { - if (iVehicleCount >= 0) { - // jeśli była podana ilość wagonów - if (iDrivigFlags & movePress) { - // jeśli dociskanie w celu odczepienia - // 3. faza odczepiania. - SetVelocity(2, 0); // jazda w ustawionym kierunku z prędkością 2 - if ((mvControlling->MainCtrlPos > 0) || - (mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic)) // jeśli jazda - { - WriteLog(mvOccupied->Name + " odczepianie w kierunku " + std::to_string(mvOccupied->DirAbsolute)); - TDynamicObject *p = - pVehicle; // pojazd do odczepienia, w (pVehicle) siedzi AI - int d; // numer sprzęgu, który sprawdzamy albo odczepiamy - int n = iVehicleCount; // ile wagonów ma zostać - do - { // szukanie pojazdu do odczepienia - d = p->DirectionGet() > 0 ? - 0 : - 1; // numer sprzęgu od strony czoła składu - // if (p->MoverParameters->Couplers[d].CouplerType==Articulated) - // //jeśli sprzęg typu wózek (za mało) - if (p->MoverParameters->Couplers[d].CouplingFlag & - ctrain_depot) // jeżeli sprzęg zablokowany - // if (p->GetTrack()->) //a nie stoi na torze warsztatowym - // (ustalić po czym poznać taki tor) - ++n; // to liczymy człony jako jeden - p->MoverParameters->BrakeReleaser(1); // wyluzuj pojazd, aby dało się dopychać - p->MoverParameters->BrakeLevelSet(0); // hamulec na zero, aby nie hamował - if (n) - { // jeśli jeszcze nie koniec - p = p->Prev(); // kolejny w stronę czoła składu (licząc od - // tyłu), bo dociskamy - if (!p) - iVehicleCount = -2, - n = 0; // nie ma co dalej sprawdzać, doczepianie zakończone - } - } while (n--); - if( p ? p->MoverParameters->Couplers[ d ].CouplingFlag == coupling::faux : true ) { - // no target, or already just virtual coupling - WriteLog( mvOccupied->Name + " didn't find anything to disconnect." ); - iVehicleCount = -2; // odczepiono, co było do odczepienia - } else if ( p->Dettach(d) == coupling::faux ) { - // tylko jeśli odepnie - WriteLog( mvOccupied->Name + " odczepiony." ); - iVehicleCount = -2; - } // a jak nie, to dociskać dalej - } - if (iVehicleCount >= 0) // zmieni się po odczepieniu - if (!mvOccupied->DecLocalBrakeLevel(1)) - { // dociśnij sklad - WriteLog( mvOccupied->Name + " dociskanie..." ); - // mvOccupied->BrakeReleaser(); //wyluzuj lokomotywę - // Ready=true; //zamiast sprawdzenia odhamowania całego składu - IncSpeed(); // dla (Ready)==false nie ruszy - } - } - if ((mvOccupied->Vel < 0.01) && !(iDrivigFlags & movePress)) - { // 2. faza odczepiania: zmień kierunek na przeciwny i dociśnij - // za radą yB ustawiamy pozycję 3 kranu (ruszanie kranem w innych miejscach - // powino zostać wyłączone) - // WriteLog("Zahamowanie składu"); - mvOccupied->BrakeLevelSet( - mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ? - 1 : - 3 ); - double p = mvOccupied->BrakePressureActual.PipePressureVal; - if( p < 3.9 ) { - // tu może być 0 albo -1 nawet - // TODO: zabezpieczenie przed dziwnymi CHK do czasu wyjaśnienia sensu 0 oraz -1 w tym miejscu - p = 3.9; - } - if (mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ? - mvOccupied->BrakePress > 2 : - mvOccupied->PipePress < p + 0.1) - { // jeśli w miarę został zahamowany (ciśnienie mniejsze niż podane na - // pozycji 3, zwyle 0.37) - if (mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic) - mvOccupied->BrakeLevelSet(0); // wyłączenie EP, gdy wystarczy (może - // nie być potrzebne, bo na początku - // jest) - WriteLog("Luzowanie lokomotywy i zmiana kierunku"); - mvOccupied->BrakeReleaser(1); // wyluzuj lokomotywę; a ST45? - mvOccupied->DecLocalBrakeLevel(LocalBrakePosNo); // zwolnienie hamulca - iDrivigFlags |= movePress; // następnie będzie dociskanie - DirectionForward(mvOccupied->ActiveDir < 0); // zmiana kierunku jazdy na przeciwny (dociskanie) - CheckVehicles(); // od razu zmienić światła (zgasić) - bez tego się nie odczepi -/* - // NOTE: disabled to prevent closing the door before passengers can disembark - fStopTime = 0.0; // nie ma na co czekać z odczepianiem -*/ - } - } - else { - if( mvOccupied->Vel > 0.01 ) { - // 1st phase(?) - // bring it to stop if it's not already stopped - SetVelocity( 0, 0, stopJoin ); // wyłączyć przyspieszanie - } - } - } // odczepiania - else // to poniżej jeśli ilość wagonów ujemna - if (iDrivigFlags & movePress) - { // 4. faza odczepiania: zwolnij i zmień kierunek - SetVelocity(0, 0, stopJoin); // wyłączyć przyspieszanie - if (!DecSpeed()) // jeśli już bardziej wyłączyć się nie da - { // ponowna zmiana kierunku - WriteLog( mvOccupied->Name + " ponowna zmiana kierunku" ); - DirectionForward(mvOccupied->ActiveDir < 0); // zmiana kierunku jazdy na właściwy - iDrivigFlags &= ~movePress; // koniec dociskania - JumpToNextOrder(); // zmieni światła - TableClear(); // skanowanie od nowa - iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed ruszeniem - SetVelocity(fShuntVelocity, fShuntVelocity); // ustawienie prędkości jazdy - } - } - } break; } case Shunt: { @@ -4593,10 +4476,13 @@ TController::UpdateSituation(double dt) { } // ustalanie zadanej predkosci - if (iDrivigFlags & moveActive) // jeśli może skanować sygnały i reagować na komendy - { // jeśli jest wybrany kierunek jazdy, można ustalić prędkość jazdy - // Ra: tu by jeszcze trzeba było wstawić uzależnienie (VelDesired) od odległości od - // przeszkody + if (iDrivigFlags & moveActive) { + + SetDriverPsyche(); // ustawia AccPreferred (potrzebne tu?) + + // jeśli może skanować sygnały i reagować na komendy + // jeśli jest wybrany kierunek jazdy, można ustalić prędkość jazdy + // Ra: tu by jeszcze trzeba było wstawić uzależnienie (VelDesired) od odległości od przeszkody // no chyba żeby to uwzgldnić już w (ActualProximityDist) VelDesired = fVelMax; // wstępnie prędkość maksymalna dla pojazdu(-ów), będzie // następnie ograniczana @@ -4605,9 +4491,6 @@ TController::UpdateSituation(double dt) { // jeśli ma rozkład i ograniczenie w rozkładzie to nie przekraczać rozkladowej VelDesired = min_speed( VelDesired, TrainParams->TTVmax ); } - - SetDriverPsyche(); // ustawia AccPreferred (potrzebne tu?) - // szukanie optymalnych wartości AccDesired = AccPreferred; // AccPreferred wynika z osobowości mechanika VelNext = VelDesired; // maksymalna prędkość wynikająca z innych czynników niż trajektoria ruchu @@ -4655,6 +4538,126 @@ TController::UpdateSituation(double dt) { default: break; } + // disconnect mode potentially overrides scan results + // TBD: when in this mode skip scanning altogether? + if( ( OrderCurrentGet() & Disconnect ) != 0 ) { + + if (AIControllFlag) { + if (iVehicleCount >= 0) { + // jeśli była podana ilość wagonów + if (iDrivigFlags & movePress) { + // jeśli dociskanie w celu odczepienia + // 3. faza odczepiania. + SetVelocity(2, 0); // jazda w ustawionym kierunku z prędkością 2 + if ((mvControlling->MainCtrlPos > 0) || + (mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic)) // jeśli jazda + { + WriteLog(mvOccupied->Name + " odczepianie w kierunku " + std::to_string(mvOccupied->DirAbsolute)); + TDynamicObject *p = + pVehicle; // pojazd do odczepienia, w (pVehicle) siedzi AI + int d; // numer sprzęgu, który sprawdzamy albo odczepiamy + int n = iVehicleCount; // ile wagonów ma zostać + do + { // szukanie pojazdu do odczepienia + d = p->DirectionGet() > 0 ? + 0 : + 1; // numer sprzęgu od strony czoła składu + // if (p->MoverParameters->Couplers[d].CouplerType==Articulated) + // //jeśli sprzęg typu wózek (za mało) + if (p->MoverParameters->Couplers[d].CouplingFlag & ctrain_depot) // jeżeli sprzęg zablokowany + // if (p->GetTrack()->) //a nie stoi na torze warsztatowym + // (ustalić po czym poznać taki tor) + ++n; // to liczymy człony jako jeden + p->MoverParameters->BrakeReleaser(1); // wyluzuj pojazd, aby dało się dopychać + p->MoverParameters->BrakeLevelSet(0); // hamulec na zero, aby nie hamował + if (n) + { // jeśli jeszcze nie koniec + p = p->Prev(); // kolejny w stronę czoła składu (licząc od + // tyłu), bo dociskamy + if (!p) + iVehicleCount = -2, + n = 0; // nie ma co dalej sprawdzać, doczepianie zakończone + } + } while (n--); + if( p ? p->MoverParameters->Couplers[ d ].CouplingFlag == coupling::faux : true ) { + // no target, or already just virtual coupling + WriteLog( mvOccupied->Name + " didn't find anything to disconnect." ); + iVehicleCount = -2; // odczepiono, co było do odczepienia + } else if ( p->Dettach(d) == coupling::faux ) { + // tylko jeśli odepnie + WriteLog( mvOccupied->Name + " odczepiony." ); + iVehicleCount = -2; + } // a jak nie, to dociskać dalej + } + if (iVehicleCount >= 0) // zmieni się po odczepieniu + if (!mvOccupied->DecLocalBrakeLevel(1)) + { // dociśnij sklad + WriteLog( mvOccupied->Name + " dociskanie..." ); + // mvOccupied->BrakeReleaser(); //wyluzuj lokomotywę + // Ready=true; //zamiast sprawdzenia odhamowania całego składu + IncSpeed(); // dla (Ready)==false nie ruszy + } + } + if ((mvOccupied->Vel < 0.01) && !(iDrivigFlags & movePress)) + { // 2. faza odczepiania: zmień kierunek na przeciwny i dociśnij + // za radą yB ustawiamy pozycję 3 kranu (ruszanie kranem w innych miejscach + // powino zostać wyłączone) + // WriteLog("Zahamowanie składu"); + mvOccupied->BrakeLevelSet( + mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ? + 1 : + 3 ); + double p = mvOccupied->BrakePressureActual.PipePressureVal; + if( p < 3.9 ) { + // tu może być 0 albo -1 nawet + // TODO: zabezpieczenie przed dziwnymi CHK do czasu wyjaśnienia sensu 0 oraz -1 w tym miejscu + p = 3.9; + } + if (mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ? + mvOccupied->BrakePress > 2 : + mvOccupied->PipePress < p + 0.1) + { // jeśli w miarę został zahamowany (ciśnienie mniejsze niż podane na + // pozycji 3, zwyle 0.37) + if (mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic) + mvOccupied->BrakeLevelSet(0); // wyłączenie EP, gdy wystarczy (może + // nie być potrzebne, bo na początku jest) + WriteLog("Luzowanie lokomotywy i zmiana kierunku"); + mvOccupied->BrakeReleaser(1); // wyluzuj lokomotywę; a ST45? + mvOccupied->DecLocalBrakeLevel(LocalBrakePosNo); // zwolnienie hamulca + iDrivigFlags |= movePress; // następnie będzie dociskanie + DirectionForward(mvOccupied->ActiveDir < 0); // zmiana kierunku jazdy na przeciwny (dociskanie) + CheckVehicles(); // od razu zmienić światła (zgasić) - bez tego się nie odczepi + /* + // NOTE: disabled to prevent closing the door before passengers can disembark + fStopTime = 0.0; // nie ma na co czekać z odczepianiem + */ + } + } + else { + if( mvOccupied->Vel > 0.01 ) { + // 1st phase(?) + // bring it to stop if it's not already stopped + SetVelocity( 0, 0, stopJoin ); // wyłączyć przyspieszanie + } + } + } // odczepiania + else // to poniżej jeśli ilość wagonów ujemna + if (iDrivigFlags & movePress) + { // 4. faza odczepiania: zwolnij i zmień kierunek + SetVelocity(0, 0, stopJoin); // wyłączyć przyspieszanie + if (!DecSpeed()) // jeśli już bardziej wyłączyć się nie da + { // ponowna zmiana kierunku + WriteLog( mvOccupied->Name + " ponowna zmiana kierunku" ); + DirectionForward(mvOccupied->ActiveDir < 0); // zmiana kierunku jazdy na właściwy + iDrivigFlags &= ~movePress; // koniec dociskania + JumpToNextOrder(); // zmieni światła + TableClear(); // skanowanie od nowa + iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed ruszeniem + SetVelocity(fShuntVelocity, fShuntVelocity); // ustawienie prędkości jazdy + } + } + } + } if( true == TestFlag( OrderList[ OrderPos ], Change_direction ) ) { // if ordered to change direction, try to stop diff --git a/renderer.cpp b/renderer.cpp index f172bf0b..521c2cab 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -1058,9 +1058,9 @@ opengl_renderer::setup_drawing( bool const Alpha ) { // setup fog if( Global.fFogEnd > 0 ) { // fog setup - auto const adjustedfogrange { Global.fFogEnd / std::max( 1.f, Global.Overcast * 2.f ) }; + m_fogrange = Global.fFogEnd / std::max( 1.f, Global.Overcast * 2.f ); ::glFogfv( GL_FOG_COLOR, glm::value_ptr( Global.FogColor ) ); - ::glFogf( GL_FOG_DENSITY, static_cast( 1.0 / adjustedfogrange ) ); + ::glFogf( GL_FOG_DENSITY, static_cast( 1.0 / m_fogrange ) ); ::glEnable( GL_FOG ); } else { ::glDisable( GL_FOG ); } @@ -2549,44 +2549,65 @@ opengl_renderer::Render( TSubModel *Submodel ) { float const anglefactor = clamp( ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( Submodel->fCosHotspotAngle - Submodel->fCosFalloffAngle ), 0.f, 1.f ); - // distance attenuation. NOTE: since it's fixed pipeline with built-in gamma correction we're using linear attenuation - // we're capping how much effect the distance attenuation can have, otherwise the lights get too tiny at regular distances - float const distancefactor { std::max( 0.5f, ( Submodel->fSquareMaxDist - TSubModel::fSquareDist ) / Submodel->fSquareMaxDist ) }; - float const precipitationfactor { std::max( 1.f, 0.5f * ( Global.Overcast - 1.f ) ) }; + lightlevel *= anglefactor; + float const precipitationfactor { interpolate( 1.f, 0.25f, clamp( Global.Overcast * 0.75f - 0.5f, 0.f, 1.f ) ) }; + lightlevel *= precipitationfactor; if( lightlevel > 0.f ) { - // material configuration: - ::glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT ); + // distance attenuation. NOTE: since it's fixed pipeline with built-in gamma correction we're using linear attenuation + // we're capping how much effect the distance attenuation can have, otherwise the lights get too tiny at regular distances + float const distancefactor { std::max( 0.5f, ( Submodel->fSquareMaxDist - TSubModel::fSquareDist ) / Submodel->fSquareMaxDist ) }; + auto const pointsize { std::max( 3.f, 5.f * distancefactor * anglefactor ) }; + // material configuration: Bind_Material( null_handle ); - ::glPointSize( std::max( 3.f, 5.f * distancefactor * anglefactor ) ); - ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], Submodel->fVisible * std::min( 1.f, lightlevel * anglefactor * precipitationfactor ) ); + // limit impact of dense fog on the lights + ::glFogf( GL_FOG_DENSITY, static_cast( 1.0 / std::min( Global.fFogEnd, m_fogrange * 2 ) ) ); + + ::glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT ); ::glDisable( GL_LIGHTING ); - ::glDisable( GL_FOG ); ::glEnable( GL_BLEND ); ::glAlphaFunc( GL_GREATER, 0.f ); ::glPushMatrix(); ::glLoadIdentity(); ::glTranslatef( lightcenter.x, lightcenter.y, lightcenter.z ); // początek układu zostaje bez zmian -/* - setup_shadow_color( colors::white ); -*/ + auto const unitstate = m_unitstate; switch_units( m_unitstate.diffuse, false, false ); // main draw call + if( Global.Overcast > 1.f ) { + // fake fog halo + float const fogfactor { + interpolate( + 2.f, 1.f, + clamp( Global.fFogEnd / 2000, 0.f, 1.f ) ) + * std::max( 1.f, Global.Overcast ) }; + ::glPointSize( pointsize * fogfactor ); + ::glColor4f( + Submodel->f4Diffuse[ 0 ], + Submodel->f4Diffuse[ 1 ], + Submodel->f4Diffuse[ 2 ], + Submodel->fVisible * std::min( 1.f, lightlevel ) * 0.5f ); + ::glDepthMask( GL_FALSE ); + m_geometry.draw( Submodel->m_geometry ); + ::glDepthMask( GL_TRUE ); + } + ::glPointSize( pointsize ); + ::glColor4f( + Submodel->f4Diffuse[ 0 ], + Submodel->f4Diffuse[ 1 ], + Submodel->f4Diffuse[ 2 ], + Submodel->fVisible * std::min( 1.f, lightlevel ) ); m_geometry.draw( Submodel->m_geometry ); // post-draw reset - // re-enable shadows -/* - setup_shadow_color( m_shadowcolor ); -*/ switch_units( unitstate.diffuse, unitstate.shadows, unitstate.reflections ); ::glPopMatrix(); ::glPopAttrib(); + ::glFogf( GL_FOG_DENSITY, static_cast( 1.0 / m_fogrange ) ); } } break; @@ -3455,16 +3476,19 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { static_cast( TSubModel::fSquareDist / Submodel->fSquareMaxDist ) ); // pozycja punktu świecącego względem kamery Submodel->fCosViewAngle = glm::dot( glm::normalize( modelview * glm::vec4( 0.f, 0.f, -1.f, 1.f ) - lightcenter ), glm::normalize( -lightcenter ) ); - float glarelevel = 0.6f; // luminosity at night is at level of ~0.1, so the overall resulting transparency in clear conditions is ~0.5 at full 'brightness' if( Submodel->fCosViewAngle > Submodel->fCosFalloffAngle ) { // only bother if the viewer is inside the visibility cone + // luminosity at night is at level of ~0.1, so the overall resulting transparency in clear conditions is ~0.5 at full 'brightness' auto glarelevel { clamp( std::max( 0.6f - Global.fLuminance, // reduce the glare in bright daylight Global.Overcast - 1.f ), // ensure some glare in rainy/foggy conditions 0.f, 1.f ) }; - // scale it down based on view angle - glarelevel *= ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( 1.0f - Submodel->fCosFalloffAngle ); + // view angle attenuation + float const anglefactor { clamp( + ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( Submodel->fCosHotspotAngle - Submodel->fCosFalloffAngle ), + 0.f, 1.f ) }; + glarelevel *= anglefactor; if( glarelevel > 0.0f ) { // setup diff --git a/renderer.h b/renderer.h index 2ce563d8..bf6c6d5f 100644 --- a/renderer.h +++ b/renderer.h @@ -386,6 +386,7 @@ private: glm::vec4 m_baseambient { 0.0f, 0.0f, 0.0f, 1.0f }; glm::vec4 m_shadowcolor { colors::shadow }; + float m_fogrange { 2000.f }; // TEnvironmentType m_environment { e_flat }; float m_specularopaquescalefactor { 1.f }; float m_speculartranslucentscalefactor { 1.f }; diff --git a/version.h b/version.h index 452cdd08..195f3b38 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 18 -#define VERSION_MINOR 1007 +#define VERSION_MINOR 1010 #define VERSION_REVISION 0