vehicle visualization config file parameters, vehicle attachments support, axle clatter fix, disabled vehicles smoke fix

This commit is contained in:
tmj-fstate
2019-09-25 18:18:33 +02:00
parent 4e0975f387
commit 41834c4d4c
10 changed files with 167 additions and 92 deletions

View File

@@ -1784,8 +1784,7 @@ void TController::OrdersClear()
void TController::Activation()
{ // umieszczenie obsady w odpowiednim członie, wykonywane wyłącznie gdy steruje AI
iDirection = iDirectionOrder; // kierunek (względem sprzęgów pojazdu z AI) właśnie został
// ustalony (zmieniony)
iDirection = iDirectionOrder; // kierunek (względem sprzęgów pojazdu z AI) właśnie został ustalony (zmieniony)
if (iDirection)
{ // jeśli jest ustalony kierunek
TDynamicObject *old = pVehicle, *d = pVehicle; // w tym siedzi AI
@@ -1794,7 +1793,7 @@ void TController::Activation()
ZeroSpeed();
ZeroDirection();
mvOccupied->SpringBrakeActivate(true);
if (TestFlag(d->MoverParameters->Couplers[iDirectionOrder < 0 ? 1 : 0].CouplingFlag, ctrain_controll)) {
if (TestFlag(d->MoverParameters->Couplers[iDirectionOrder < 0 ? end::rear : end::front].CouplingFlag, ctrain_controll)) {
mvControlling->MainSwitch( false); // dezaktywacja czuwaka, jeśli przejście do innego członu
mvOccupied->DecLocalBrakeLevel(LocalBrakePosNo); // zwolnienie hamulca w opuszczanym pojeździe
// mvOccupied->BrakeLevelSet((mvOccupied->BrakeHandle==FVel6)?4:-2); //odcięcie na
@@ -1806,20 +1805,17 @@ void TController::Activation()
mvOccupied->ActiveCab = mvOccupied->CabNo; // użytkownik moze zmienić ActiveCab wychodząc
mvOccupied->CabDeactivisation(); // tak jest w Train.cpp
// przejście AI na drugą stronę EN57, ET41 itp.
while (TestFlag(d->MoverParameters->Couplers[iDirection < 0 ? 1 : 0].CouplingFlag, ctrain_controll))
while (TestFlag(d->MoverParameters->Couplers[iDirection < 0 ? end::rear : end::front].CouplingFlag, ctrain_controll))
{ // jeśli pojazd z przodu jest ukrotniony, to przechodzimy do niego
d = iDirection * d->DirectionGet() < 0 ? d->Next() :
d->Prev(); // przechodzimy do następnego członu
if (d)
{
drugi = d->Mechanik; // zapamiętanie tego, co ewentualnie tam siedzi, żeby w razie
// dwóch zamienić miejscami
drugi = d->Mechanik; // zapamiętanie tego, co ewentualnie tam siedzi, żeby w razie dwóch zamienić miejscami
d->Mechanik = this; // na razie bilokacja
d->MoverParameters->SetInternalCommand(
"", 0, 0); // usunięcie ewentualnie zalegającej komendy (Change_direction?)
d->MoverParameters->SetInternalCommand("", 0, 0); // usunięcie ewentualnie zalegającej komendy (Change_direction?)
if (d->DirectionGet() != pVehicle->DirectionGet()) // jeśli są przeciwne do siebie
iDirection = -iDirection; // to będziemy jechać w drugą stronę względem
// zasiedzianego pojazdu
iDirection = -iDirection; // to będziemy jechać w drugą stronę względem zasiedzianego pojazdu
pVehicle->Mechanik = drugi; // wsadzamy tego, co ewentualnie był (podwójna trakcja)
pVehicle->MoverParameters->CabNo = 0; // wyłączanie kabin po drodze
pVehicle->MoverParameters->ActiveCab = 0; // i zaznaczenie, że nie ma tam nikogo

View File

@@ -2009,12 +2009,18 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
smBuforPrawy[ i ]->WillBeAnimated();
}
}
for( auto &axle : m_axlesounds ) {
// wyszukiwanie osi (0 jest na końcu, dlatego dodajemy długość?)
axle.distance = (
Reversed ?
-axle.offset :
( axle.offset + MoverParameters->Dim.L ) ) + fDist;
if( Track->fSoundDistance > 0.f ) {
for( auto &axle : m_axlesounds ) {
// wyszukiwanie osi (0 jest na końcu, dlatego dodajemy długość?)
axle.distance =
clamp_circular<double>(
( Reversed ?
-axle.offset :
axle.offset )
- 0.5 * MoverParameters->Dim.L
+ fDist,
Track->fSoundDistance );
}
}
// McZapkie-250202 end.
Track->AddDynamicObject(this); // wstawiamy do toru na pozycję 0, a potem przesuniemy
@@ -3155,12 +3161,19 @@ bool TDynamicObject::Update(double dt, double dt1)
if( MyTrack->fSoundDistance != -1 ) {
if( MyTrack->fSoundDistance != dRailLength ) {
dRailLength = MyTrack->fSoundDistance;
for( auto &axle : m_axlesounds ) {
axle.distance = axle.offset + MoverParameters->Dim.L;
if( dRailLength > 0.0 ) {
for( auto &axle : m_axlesounds ) {
axle.distance =
clamp_circular<double>(
axle.distance - dRailLength
+ axle.offset
/* - 0.5 * MoverParameters->Dim.L */,
MyTrack->fSoundDistance );
}
}
dRailLength = MyTrack->fSoundDistance;
}
if( dRailLength != -1 ) {
if( dRailLength > 0.0 ) {
if( MoverParameters->Vel > 0 ) {
// TODO: track quality and/or environment factors as separate subroutine
auto volume =
@@ -3182,35 +3195,37 @@ bool TDynamicObject::Update(double dt, double dt1)
break;
}
}
auto axleindex { 0 };
for( auto &axle : m_axlesounds ) {
axle.distance -= dDOMoveLen * Sign( dDOMoveLen );
if( axle.distance < 0 ) {
axle.distance += dRailLength;
if( MoverParameters->Vel > 2.5 ) {
// NOTE: for combined clatter sound we supply 1/100th of actual value, as the sound module converts does the opposite, converting received (typically) 0-1 values to 0-100 range
auto const frequency = (
true == axle.clatter.is_combined() ?
MoverParameters->Vel * 0.01 :
1.0 );
axle.clatter
.pitch( frequency )
.gain( volume )
.play();
// crude bump simulation, drop down on even axles, move back up on the odd ones
MoverParameters->AccVert +=
interpolate(
0.01, 0.05,
clamp(
GetVelocity() / ( 1 + MoverParameters->Vmax ),
0.0, 1.0 ) )
* ( ( axleindex % 2 ) != 0 ?
1 :
-1 );
if( dRailLength > 0.0 ) {
auto axleindex { 0 };
for( auto &axle : m_axlesounds ) {
axle.distance += dDOMoveLen * DirectionGet();
if( ( axle.distance < 0 )
|| ( axle.distance > dRailLength ) ) {
axle.distance = clamp_circular( axle.distance, dRailLength );
if( MoverParameters->Vel > 0.1 ) {
// NOTE: for combined clatter sound we supply 1/100th of actual value, as the sound module converts does the opposite, converting received (typically) 0-1 values to 0-100 range
auto const frequency = (
true == axle.clatter.is_combined() ?
MoverParameters->Vel * 0.01 :
1.0 );
axle.clatter
.pitch( frequency )
.gain( volume )
.play();
// crude bump simulation, drop down on even axles, move back up on the odd ones
MoverParameters->AccVert +=
interpolate(
0.01, 0.05,
clamp(
GetVelocity() / ( 1 + MoverParameters->Vmax ),
0.0, 1.0 ) )
* ( ( axleindex % 2 ) != 0 ?
1 :
-1 );
}
}
++axleindex;
}
++axleindex;
}
}
}
@@ -4268,11 +4283,24 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
std::string asAnimName;
bool Stop_InternalData = false;
pants = NULL; // wskaźnik pierwszego obiektu animującego dla pantografów
cParser parser( TypeName + ".mmd", cParser::buffer_FILE, asBaseDir );
if( false == parser.ok() ) {
ErrorLog( "Failed to load appearance data for vehicle " + MoverParameters->Name );
return;
{
// preliminary check whether the file exists
cParser parser( TypeName + ".mmd", cParser::buffer_FILE, asBaseDir );
if( false == parser.ok() ) {
ErrorLog( "Failed to load appearance data for vehicle " + MoverParameters->Name );
return;
}
}
// use #include wrapper to access the appearance data file
// this allows us to provide the file content with user-defined parameters
cParser parser(
"include " + TypeName + ".mmd"
+ " " + asName // (p1)
+ " " + TypeName // (p2)
+ " " + ReplacableSkin // (p3)
+ " end",
cParser::buffer_TEXT,
asBaseDir );
std::string token;
do {
token = "";
@@ -4430,15 +4458,30 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
parser.getTokens();
parser >> asModel;
replace_slashes( asModel );
if( asModel[ 0 ] == '/' ) {
// filename can potentially begin with a slash, and we don't need it
asModel.erase( 0, 1 );
}
erase_leading_slashes( asModel );
asModel = asBaseDir + asModel; // McZapkie-200702 - dynamics maja swoje modele w dynamic/basedir
Global.asCurrentTexturePath = asBaseDir; // biezaca sciezka do tekstur to dynamic/...
mdLowPolyInt = TModelsManager::GetModel(asModel, true);
}
else if(token == "attachments:") {
// additional 3d models attached to main body
// content provided as a series of values together enclosed in "{}"
// each value is a name of additional 3d model
// value can be optionally set of values enclosed in "[]" in which case one value will be picked randomly
// TBD: reconsider something more yaml-compliant and/or ability to define offset and rotation
while( ( ( token = parser.getToken<std::string>() ) != "" )
&& ( token != "}" ) ) {
auto attachmentmodelname { deserialize_random_set( parser ) };
replace_slashes( attachmentmodelname );
Global.asCurrentTexturePath = asBaseDir; // biezaca sciezka do tekstur to dynamic/...
auto *attachmentmodel { TModelsManager::GetModel( asBaseDir + attachmentmodelname, true ) };
if( attachmentmodel != nullptr ) {
mdAttachments.emplace_back( attachmentmodel );
}
}
}
else if(token == "loads:") {
// default load visualization models overrides
// content provided as "key: value" pairs together enclosed in "{}"
@@ -4982,18 +5025,18 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
// add another axle entry to the list
axle_sounds axle {
0,
std::atof( token.c_str() ),
std::atof( token.c_str() ) * -1.0, // for axle locations negative value means ahead of centre but vehicle faces +Z in 'its' space
{ sound_placement::external, static_cast<float>( dSDist ) } };
axle.clatter.deserialize( parser, sound_type::single );
axle.clatter.owner( this );
axle.clatter.offset( { 0, 0, -axle.offset } ); // vehicle faces +Z in 'its' space, for axle locations negative value means ahead of centre
axle.clatter.offset( { 0, 0, axle.offset } );
m_axlesounds.emplace_back( axle );
}
// arrange the axles in case they're listed out of order
std::sort(
std::begin( m_axlesounds ), std::end( m_axlesounds ),
[]( axle_sounds const &Left, axle_sounds const &Right ) {
return ( Left.offset < Right.offset ); } );
return ( Left.offset > Right.offset ); } );
}
else if( ( token == "engine:" )

View File

@@ -200,6 +200,7 @@ public:
TModel3d *mdLowPolyInt; // ABu 010305: wnetrze lowpoly
std::array<TSubModel *, 3> LowPolyIntCabs {}; // pointers to low fidelity version of individual driver cabs
bool JointCabs{ false }; // flag for vehicles with multiple virtual 'cabs' sharing location and 3d model(s)
std::vector<TModel3d *> mdAttachments; // additional models attached to main body
struct destination_data {
TSubModel *sign { nullptr }; // submodel mapped with replacable texture -4
bool has_light { false }; // the submodel was originally configured with self-illumination attribute

View File

@@ -78,25 +78,6 @@ bool TEventLauncher::Load(cParser *parser)
}
parser->getTokens();
*parser >> DeltaTime;
if (DeltaTime < 0)
DeltaTime = -DeltaTime; // dla ujemnego zmieniamy na dodatni
else if (DeltaTime > 0)
{ // wartość dodatnia oznacza wyzwalanie o określonej godzinie
iMinute = int(DeltaTime) % 100; // minuty są najmłodszymi cyframi dziesietnymi
iHour = int(DeltaTime - iMinute) / 100; // godzina to setki
DeltaTime = 0; // bez powtórzeń
// potentially shift the provided time by requested offset
auto const timeoffset { static_cast<int>( Global.ScenarioTimeOffset * 60 ) };
if( timeoffset != 0 ) {
auto const adjustedtime { clamp_circular( iHour * 60 + iMinute + timeoffset, 24 * 60 ) };
iHour = ( adjustedtime / 60 ) % 24;
iMinute = adjustedtime % 60;
}
WriteLog(
"EventLauncher at "
+ std::to_string( iHour ) + ":"
+ ( iMinute < 10 ? "0" : "" ) + to_string( iMinute ) ); // wyświetlenie czasu
}
parser->getTokens();
*parser >> token;
asEvent1Name = token; // pierwszy event
@@ -145,6 +126,29 @@ bool TEventLauncher::Load(cParser *parser)
parser->getTokens(); // słowo zamykające
*parser >> token;
}
if( DeltaTime < 0 )
DeltaTime = -DeltaTime; // dla ujemnego zmieniamy na dodatni
else if( DeltaTime > 0 ) { // wartość dodatnia oznacza wyzwalanie o określonej godzinie
iMinute = int( DeltaTime ) % 100; // minuty są najmłodszymi cyframi dziesietnymi
iHour = int( DeltaTime - iMinute ) / 100; // godzina to setki
DeltaTime = 0; // bez powtórzeń
// potentially shift the provided time by requested offset
auto const timeoffset{ static_cast<int>( Global.ScenarioTimeOffset * 60 ) };
if( timeoffset != 0 ) {
auto const adjustedtime{ clamp_circular( iHour * 60 + iMinute + timeoffset, 24 * 60 ) };
iHour = ( adjustedtime / 60 ) % 24;
iMinute = adjustedtime % 60;
}
WriteLog(
"EventLauncher at "
+ std::to_string( iHour ) + ":"
+ ( iMinute < 10 ? "0" : "" ) + to_string( iMinute )
+ " (" + asEvent1Name
+ ( asEvent2Name != "none" ? " / " + asEvent2Name : "" )
+ ")" ); // wyświetlenie czasu
}
return true;
}

View File

@@ -244,7 +244,7 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
{ // gdy zostaje na tym samym torze (przesuwanie już nie zmienia toru)
if (bPrimary)
{ // tylko gdy początkowe ustawienie, dodajemy eventy stania do kolejki
if (Owner->MoverParameters->CabNo != 0) {
if (Owner->MoverParameters->ActiveCab != 0) {
pCurrentTrack->QueueEvents( pCurrentTrack->m_events1, Owner, -1.0 );
pCurrentTrack->QueueEvents( pCurrentTrack->m_events2, Owner, -1.0 );

View File

@@ -44,6 +44,7 @@ drivingaid_panel::update() {
auto const *mover = controlled->MoverParameters;
auto const *driver = controlled->Mechanik;
auto const *owner = ( controlled->ctOwner != nullptr ? controlled->ctOwner : controlled->Mechanik );
{ // throttle, velocity, speed limits and grade
std::string expandedtext;
@@ -60,8 +61,8 @@ drivingaid_panel::update() {
gradetext = m_buffer.data();
}
// next speed limit
auto const speedlimit { static_cast<int>( std::floor( driver->VelDesired ) ) };
auto const nextspeedlimit { static_cast<int>( std::floor( driver->VelNext ) ) };
auto const speedlimit { static_cast<int>( std::floor( owner->VelDesired ) ) };
auto const nextspeedlimit { static_cast<int>( std::floor( owner->VelNext ) ) };
std::string nextspeedlimittext;
if( nextspeedlimit != speedlimit ) {
std::snprintf(
@@ -117,7 +118,7 @@ drivingaid_panel::update() {
{ // alerter, hints
std::string expandedtext;
if( is_expanded ) {
auto const stoptime { static_cast<int>( std::ceil( -1.0 * controlled->Mechanik->fStopTime ) ) };
auto const stoptime { static_cast<int>( std::ceil( -1.0 * owner->fStopTime ) ) };
if( stoptime > 0 ) {
std::snprintf(
m_buffer.data(), m_buffer.size(),
@@ -126,7 +127,7 @@ drivingaid_panel::update() {
expandedtext = m_buffer.data();
}
else {
auto const trackblockdistance{ std::abs( controlled->Mechanik->TrackBlock() ) };
auto const trackblockdistance{ std::abs( owner->TrackBlock() ) };
if( trackblockdistance <= 75.0 ) {
std::snprintf(
m_buffer.data(), m_buffer.size(),

View File

@@ -50,20 +50,24 @@ class cParser //: public std::stringstream
bool
expectToken( std::string const &Value ) {
return readToken() == Value; };
inline
bool
eof() {
return mStream->eof(); };
inline
bool
ok() {
return !mStream->fail(); };
return ( !mStream->fail() ); };
cParser &
autoclear( bool const Autoclear );
inline
bool
autoclear() const {
return m_autoclear; }
bool
getTokens( unsigned int Count = 1, bool ToLower = true, char const *Break = "\n\r\t ;" );
// returns next incoming token, if any, without removing it from the set
inline
std::string
peek() const {
return (

View File

@@ -167,10 +167,16 @@ smoke_source::update( double const Timedelta, bool const Onlydespawn ) {
std::min<float>(
m_spawncount + ( m_spawnrate * Timedelta * Global.SmokeFidelity ),
m_max_particles ) );
// HACK: don't spawn particles in tunnels, to prevent smoke clipping through 'terrain' outside
if( ( m_ownertype == owner_type::vehicle )
&& ( m_owner.vehicle->RaTrackGet()->eEnvironment == e_tunnel ) ) {
// consider special spawn rate cases
if( m_ownertype == owner_type::vehicle ) {
// HACK: don't spawn particles in tunnels, to prevent smoke clipping through 'terrain' outside
if( m_owner.vehicle->RaTrackGet()->eEnvironment == e_tunnel ) {
m_spawncount = 0.f;
}
if( false == m_owner.vehicle->bEnabled ) {
// don't spawn particles for vehicles which left the scenario
m_spawncount = 0.f;
}
}
// update spawned particles
for( auto particleiterator { std::begin( m_particles ) }; particleiterator != std::end( m_particles ); ++particleiterator ) {

View File

@@ -2393,8 +2393,13 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) {
}
}
if( Dynamic->mdModel ) {
// main model
Render( Dynamic->mdModel, Dynamic->Material(), squaredistance );
}
// optional attached models
for( auto *attachment : Dynamic->mdAttachments ) {
Render( attachment, Dynamic->Material(), squaredistance );
}
// post-render cleanup
m_renderspecular = false;
if( Dynamic->fShade > 0.0f ) {
@@ -2411,10 +2416,18 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) {
Render( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance );
// }
}
if( Dynamic->mdModel )
if( Dynamic->mdModel ) {
// main model
Render( Dynamic->mdModel, Dynamic->Material(), squaredistance );
if( Dynamic->mdLoad ) // renderowanie nieprzezroczystego ładunku
}
// optional attached models
for( auto *attachment : Dynamic->mdAttachments ) {
Render( attachment, Dynamic->Material(), squaredistance );
}
if( Dynamic->mdLoad ) {
// renderowanie nieprzezroczystego ładunku
Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} );
}
// post-render cleanup
break;
}
@@ -3523,11 +3536,18 @@ opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) {
// }
}
if( Dynamic->mdModel )
if( Dynamic->mdModel ) {
// main model
Render_Alpha( Dynamic->mdModel, Dynamic->Material(), squaredistance );
if( Dynamic->mdLoad ) // renderowanie nieprzezroczystego ładunku
}
// optional attached models
for( auto *attachment : Dynamic->mdAttachments ) {
Render_Alpha( attachment, Dynamic->Material(), squaredistance );
}
if( Dynamic->mdLoad ) {
// renderowanie nieprzezroczystego ładunku
Render_Alpha( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} );
}
// post-render cleanup
m_renderspecular = false;

View File

@@ -1,5 +1,5 @@
#pragma once
#define VERSION_MAJOR 19
#define VERSION_MINOR 916
#define VERSION_MINOR 925
#define VERSION_REVISION 0