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

tmj merge

This commit is contained in:
milek7
2017-10-30 16:30:04 +01:00
73 changed files with 9521 additions and 7735 deletions

View File

@@ -15,15 +15,12 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "AnimModel.h"
#include "Globals.h"
#include "Logs.h"
#include "usefull.h"
#include "McZapkie/mctools.h"
#include "Timer.h"
#include "MdlMngr.h"
// McZapkie:
#include "renderer.h"
//---------------------------------------------------------------------------
#include "simulation.h"
#include "Globals.h"
#include "Timer.h"
#include "Logs.h"
TAnimContainer *TAnimModel::acAnimList = NULL;
TAnimAdvanced::TAnimAdvanced(){};
@@ -232,9 +229,10 @@ void TAnimContainer::UpdateModel() {
fTranslateSpeed = 0.0; // wyłączenie przeliczania wektora
if (LengthSquared3(vTranslation) <= 0.0001) // jeśli jest w punkcie początkowym
iAnim &= ~2; // wyłączyć zmianę pozycji submodelu
if (evDone)
Global::AddToQuery(evDone, NULL); // wykonanie eventu informującego o
// zakończeniu
if( evDone ) {
// wykonanie eventu informującego o zakończeniu
simulation::Events.AddToQuery( evDone, nullptr );
}
}
}
if (fRotateSpeed != 0.0)
@@ -299,9 +297,10 @@ void TAnimContainer::UpdateModel() {
if (!anim)
{ // nie potrzeba przeliczać już
fRotateSpeed = 0.0;
if (evDone)
Global::AddToQuery(evDone, NULL); // wykonanie eventu informującego o
// zakończeniu
if( evDone ) {
// wykonanie eventu informującego o zakończeniu
simulation::Events.AddToQuery( evDone, nullptr );
}
}
}
if( fAngleSpeed != 0.f ) {
@@ -330,7 +329,7 @@ void TAnimContainer::PrepareModel()
fAngleSpeed = 0.0; // wyłączenie przeliczania wektora
if( evDone ) {
// wykonanie eventu informującego o zakończeniu
Global::AddToQuery( evDone, NULL );
simulation::Events.AddToQuery( evDone, nullptr );
}
}
else
@@ -406,23 +405,20 @@ void TAnimContainer::EventAssign(TEvent *ev)
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
TAnimModel::TAnimModel()
{
pRoot = NULL;
pModel = NULL;
iNumLights = 0;
fBlinkTimer = 0;
for (int i = 0; i < iMaxNumLights; ++i)
{
LightsOn[i] = LightsOff[i] = nullptr; // normalnie nie ma
lsLights[i] = ls_Off; // a jeśli są, to wyłączone
TAnimModel::TAnimModel( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {
// TODO: wrap these in a tuple and move to underlying model
for( int index = 0; index < iMaxNumLights; ++index ) {
LightsOn[ index ] = LightsOff[ index ] = nullptr; // normalnie nie ma
lsLights[ index ] = ls_Off; // a jeśli są, to wyłączone
}
}
TAnimModel::TAnimModel() {
// TODO: wrap these in a tuple and move to underlying model
for( int index = 0; index < iMaxNumLights; ++index ) {
LightsOn[index] = LightsOff[index] = nullptr; // normalnie nie ma
lsLights[index] = ls_Off; // a jeśli są, to wyłączone
}
vAngle.x = vAngle.y = vAngle.z = 0.0; // zerowanie obrotów egzemplarza
pAdvanced = NULL; // nie ma zaawansowanej animacji
fDark = 0.25f; // standardowy próg zaplania
fOnTime = 0.66f;
fOffTime = fOnTime + 0.66f;
}
TAnimModel::~TAnimModel()
@@ -572,6 +568,15 @@ void TAnimModel::RaAnimate( unsigned int const Framestamp ) {
m_framestamp = Framestamp;
};
// calculates piece's bounding radius
void
TAnimModel::radius_() {
if( pModel != nullptr ) {
m_area.radius = pModel->bounding_radius();
}
}
void TAnimModel::RaPrepare()
{ // ustawia światła i animacje we wzorcu modelu przed renderowaniem egzemplarza
bool state; // stan światła
@@ -762,21 +767,27 @@ void TAnimModel::LightSet(int n, float v)
{ // ustawienie światła (n) na wartość (v)
if (n >= iMaxNumLights)
return; // przekroczony zakres
lsLights[n] = TLightState(int(v));
switch (lsLights[n])
{ // interpretacja ułamka zależnie od typu
case 0: // ustalenie czasu migotania, t<1s (f>1Hz), np. 0.1 => t=0.1 (f=10Hz)
break;
case 1: // ustalenie wypełnienia ułamkiem, np. 1.25 => zapalony przez 1/4 okresu
break;
case 2: // ustalenie częstotliwości migotania, f<1Hz (t>1s), np. 2.2 => f=0.2Hz (t=5s)
break;
case 3: // zapalenie świateł zależne od oświetlenia scenerii
if (v > 3.0)
fDark = v - 3.0; // ustawienie indywidualnego progu zapalania
else
fDark = 0.25; // standardowy próg zaplania
break;
lsLights[ n ] = TLightState( static_cast<int>( v ) );
switch( lsLights[ n ] ) {
// 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)
break;
}
case ls_On: {
// ustalenie wypełnienia ułamkiem, np. 1.25 => zapalony przez 1/4 okresu
break;
}
case ls_Blink: {
// ustalenie częstotliwości migotania, f<1Hz (t>1s), np. 2.2 => f=0.2Hz (t=5s)
break;
}
case ls_Dark: {
// zapalenie świateł zależne od oświetlenia scenerii
if( v > 3.0 ) { fDark = v - 3.0; } // ustawienie indywidualnego progu zapalania
else { fDark = DefaultDarkThresholdLevel; } // standardowy próg zaplania
break;
}
}
};
//---------------------------------------------------------------------------

View File

@@ -19,15 +19,16 @@ http://mozilla.org/MPL/2.0/.
#include "DynObj.h"
const int iMaxNumLights = 8;
float const DefaultDarkThresholdLevel { 0.325f };
// typy stanu świateł
typedef enum
enum TLightState
{
ls_Off = 0, // zgaszone
ls_On = 1, // zapalone
ls_Blink = 2, // migające
ls_Dark = 3 // Ra: zapalajce się automatycznie, gdy zrobi się ciemno
} TLightState;
};
class TAnimVocaloidFrame
{ // ramka animacji typu Vocaloid Motion Data z programu MikuMikuDance
@@ -77,16 +78,9 @@ class TAnimContainer
TAnimContainer();
~TAnimContainer();
bool Init(TSubModel *pNewSubModel);
// std::string inline GetName() { return
// std::string(pSubModel?pSubModel->asName.c_str():""); };
// std::string inline GetName() { return std::string(pSubModel?pSubModel->pName:"");
// };
std::string NameGet()
{
return (pSubModel ? pSubModel->pName : "");
};
// void SetRotateAnim(vector3 vNewRotateAxis, double fNewDesiredAngle, double
// fNewRotateSpeed, bool bResetAngle=false);
inline
std::string NameGet() {
return (pSubModel ? pSubModel->pName : ""); };
void SetRotateAnim(vector3 vNewRotateAngles, double fNewRotateSpeed);
void SetTranslateAnim(vector3 vNewTranslate, double fNewSpeed);
void AnimSetVMD(double fNewSpeed);
@@ -94,24 +88,20 @@ class TAnimContainer
void UpdateModel();
void UpdateModelIK();
bool InMovement(); // czy w trakcie animacji?
double AngleGet()
{
return vRotateAngles.z;
}; // jednak ostatnia, T3D ma inny układ
vector3 TransGet()
{
return vector3(-vTranslation.x, vTranslation.z, vTranslation.y);
}; // zmiana, bo T3D ma inny układ
void WillBeAnimated()
{
inline
double AngleGet() {
return vRotateAngles.z; }; // jednak ostatnia, T3D ma inny układ
inline
vector3 TransGet() {
return vector3(-vTranslation.x, vTranslation.z, vTranslation.y); }; // zmiana, bo T3D ma inny układ
inline
void WillBeAnimated() {
if (pSubModel)
pSubModel->WillBeAnimated();
};
pSubModel->WillBeAnimated(); };
void EventAssign(TEvent *ev);
TEvent * Event()
{
return evDone;
};
inline
TEvent * Event() {
return evDone; };
};
class TAnimAdvanced
@@ -129,55 +119,77 @@ class TAnimAdvanced
};
// opakowanie modelu, określające stan egzemplarza
class TAnimModel {
class TAnimModel : public editor::basic_node {
friend class opengl_renderer;
private:
TAnimContainer *pRoot; // pojemniki sterujące, tylko dla aniomowanych submodeli
TModel3d *pModel;
double fBlinkTimer;
int iNumLights;
TSubModel *LightsOn[iMaxNumLights]; // Ra: te wskaźniki powinny być w ramach TModel3d
TSubModel *LightsOff[iMaxNumLights];
vector3 vAngle; // bazowe obroty egzemplarza względem osi
material_data m_materialdata;
std::string asText; // tekst dla wyświetlacza znakowego
TAnimAdvanced *pAdvanced { nullptr };
void Advanced();
TLightState lsLights[iMaxNumLights];
float fDark; // poziom zapalanie światła (powinno być chyba powiązane z danym światłem?)
float fOnTime, fOffTime; // były stałymi, teraz mogą być zmienne dla każdego egzemplarza
unsigned int m_framestamp { 0 }; // id of last rendered gfx frame
private:
void RaAnimate( unsigned int const Framestamp ); // przeliczenie animacji egzemplarza
void RaPrepare(); // ustawienie animacji egzemplarza na wzorcu
public:
static TAnimContainer *acAnimList; // lista animacji z eventem, które muszą być przeliczane również bez wyświetlania
inline
material_data const *Material() const { return &m_materialdata; }
public:
// constructors
TAnimModel( scene::node_data const &Nodedata );
TAnimModel();
// destructor
~TAnimModel();
// methods
static void AnimUpdate( double dt );
bool Init(TModel3d *pNewModel);
bool Init(std::string const &asName, std::string const &asReplacableTexture);
bool Load(cParser *parser, bool ter = false);
TAnimContainer * AddContainer(std::string const &Name);
TAnimContainer * GetContainer(std::string const &Name = "");
int Flags();
void RaAnglesSet(double a, double b, double c)
{
vAngle.x = a;
vAngle.y = b;
vAngle.z = c;
};
void RaAnglesSet( glm::vec3 Angles ) {
vAngle.x = Angles.x;
vAngle.y = Angles.y;
vAngle.z = Angles.z; };
void LightSet( int n, float v );
void AnimationVND( void *pData, double a, double b, double c, double d );
bool TerrainLoaded();
int TerrainCount();
TSubModel * TerrainSquare(int n);
void AnimationVND(void *pData, double a, double b, double c, double d);
void LightSet(int n, float v);
static void AnimUpdate(double dt);
int Flags();
inline
material_data const *
Material() const {
return &m_materialdata; }
inline
TModel3d *
Model() {
return pModel; }
// members
static TAnimContainer *acAnimList; // lista animacji z eventem, które muszą być przeliczane również bez wyświetlania
protected:
// calculates piece's bounding radius
void
radius_();
private:
// methods
void RaPrepare(); // ustawienie animacji egzemplarza na wzorcu
void RaAnimate( unsigned int const Framestamp ); // przeliczenie animacji egzemplarza
void Advanced();
// members
TAnimContainer *pRoot { nullptr }; // pojemniki sterujące, tylko dla aniomowanych submodeli
TModel3d *pModel { nullptr };
double fBlinkTimer { 0.0 };
int iNumLights { 0 };
TSubModel *LightsOn[ iMaxNumLights ]; // Ra: te wskaźniki powinny być w ramach TModel3d
TSubModel *LightsOff[ iMaxNumLights ];
vector3 vAngle; // bazowe obroty egzemplarza względem osi
material_data m_materialdata;
std::string asText; // tekst dla wyświetlacza znakowego
TAnimAdvanced *pAdvanced { nullptr };
TLightState 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
unsigned int m_framestamp { 0 }; // id of last rendered gfx frame
};
class instance_table : public basic_table<TAnimModel> {
};
//---------------------------------------------------------------------------

View File

@@ -30,7 +30,6 @@ set(SOURCES
"Float3d.cpp"
"Gauge.cpp"
"Globals.cpp"
"Ground.cpp"
"Logs.cpp"
"McZapkie/friction.cpp"
"McZapkie/hamulce.cpp"
@@ -68,6 +67,11 @@ set(SOURCES
"material.cpp"
"lua.cpp"
"uart.cpp"
"messaging.cpp"
"scene.cpp"
"scenenode.cpp"
"simulation.cpp"
"vertex.cpp"
)
if (WIN32)

View File

@@ -18,9 +18,6 @@ http://mozilla.org/MPL/2.0/.
//---------------------------------------------------------------------------
// TViewPyramid TCamera::OrgViewPyramid;
//={vector3(-1,1,1),vector3(1,1,1),vector3(-1,-1,1),vector3(1,-1,1),vector3(0,0,0)};
void TCamera::Init(vector3 NPos, vector3 NAngle)
{
@@ -37,8 +34,8 @@ void TCamera::Init(vector3 NPos, vector3 NAngle)
void TCamera::OnCursorMove(double x, double y)
{
// McZapkie-170402: zeby mysz dzialala zawsze if (Type==tp_Follow)
Pitch += y;
Yaw += -x;
Yaw -= x;
Pitch -= y;
if (Yaw > M_PI)
Yaw -= 2 * M_PI;
else if (Yaw < -M_PI)
@@ -62,7 +59,7 @@ TCamera::OnCommand( command_data const &Command ) {
OnCursorMove(
reinterpret_cast<double const &>( Command.param1 ) * 0.005 * Global::fMouseXScale / Global::ZoomFactor,
reinterpret_cast<double const &>( Command.param2 ) * -0.01 * Global::fMouseYScale / Global::ZoomFactor );
reinterpret_cast<double const &>( Command.param2 ) * 0.01 * Global::fMouseYScale / Global::ZoomFactor );
break;
}

View File

@@ -37,7 +37,7 @@ class TMtableTime; // czas dla danego posterunku
class TController; // obiekt sterujący pociągiem (AI)
typedef enum
enum TCommandType
{ // binarne odpowiedniki komend w komórce pamięci
cm_Unknown, // ciąg nierozpoznany (nie jest komendą)
cm_Ready, // W4 zezwala na odjazd, ale semafor może zatrzymać
@@ -51,6 +51,6 @@ typedef enum
cm_OutsideStation,
cm_Shunt,
cm_Command // komenda pobierana z komórki
} TCommandType;
};
#endif

View File

@@ -20,7 +20,6 @@ http://mozilla.org/MPL/2.0/.
#include "mtable.h"
#include "DynObj.h"
#include "Event.h"
#include "Ground.h"
#include "MemCell.h"
#include "World.h"
#include "McZapkie/mctools.h"
@@ -73,7 +72,17 @@ double GetDistanceToEvent(TTrack const *track, TEvent const *event, double scan_
{ // przejście na inny tor
track = track->Connected(int(sd), sd);
start_dist += (1 == krok) ? 0 : back ? -segment->GetLength() : segment->GetLength();
return GetDistanceToEvent(track, event, sd, start_dist, ++iter, 1 == krok ? true : false);
if( ( track != nullptr )
&& ( track->eType == tt_Cross ) ) {
// NOTE: tracing through crossroads currently poses risk of tracing through wrong segment
// as it's possible to be performerd before setting a route through the crossroads
// as a stop-gap measure we don't trace through crossroads which should be reasonable in most cases
// TODO: establish route before the scan, or a way for the function to know which route to pick
return start_dist;
}
else {
return GetDistanceToEvent( track, event, sd, start_dist, ++iter, 1 == krok ? true : false );
}
}
else
{ // obliczenie mojego toru
@@ -303,7 +312,7 @@ bool TSpeedPos::Update()
std::string TSpeedPos::GetName()
{
if (iFlags & spTrack) // jeśli tor
return trTrack->NameGet();
return trTrack->name();
else if( iFlags & spEvent ) // jeśli event
return evEvent->asName;
else
@@ -517,7 +526,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
if (pTrack != tLast) // ostatni zapisany w tabelce nie był jeszcze sprawdzony
{ // jeśli tor nie był jeszcze sprawdzany
if( Global::iWriteLogEnabled & 8 ) {
WriteLog( "Speed table for " + OwnerName() + " tracing through track " + pTrack->NameGet() );
WriteLog( "Speed table for " + OwnerName() + " tracing through track " + pTrack->name() );
}
if( ( pEvent = CheckTrackEvent( pTrack, fLastDir ) ) != nullptr ) // jeśli jest semafor na tym torze
@@ -580,11 +589,15 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
// na skrzyżowaniach trzeba wybrać segment, po którym pojedzie pojazd
// dopiero tutaj jest ustalany kierunek segmentu na skrzyżowaniu
sSpeedTable[iLast].iFlags |=
((pTrack->CrossSegment(
(fLastDir < 0 ?
tLast->iPrevDirection :
tLast->iNextDirection),
iRouteWanted) & 0xf) << 28); // ostatnie 4 bity pola flag
( ( pTrack->CrossSegment(
(fLastDir < 0 ?
tLast->iPrevDirection :
tLast->iNextDirection),
/*
iRouteWanted )
*/
1 + std::floor( Random( static_cast<double>(pTrack->RouteCount()) - 0.001 ) ) )
& 0xf ) << 28 ); // ostatnie 4 bity pola flag
sSpeedTable[iLast].iFlags &= ~spReverse; // usunięcie flagi kierunku, bo może być błędna
if (sSpeedTable[iLast].iFlags < 0) {
sSpeedTable[iLast].iFlags |= spReverse; // ustawienie flagi kierunku na podstawie wybranego segmentu
@@ -592,10 +605,12 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
if (int(fLastDir) * sSpeedTable[iLast].iFlags < 0) {
fLastDir = -fLastDir;
}
/*
if (AIControllFlag) {
// dla AI na razie losujemy kierunek na kolejnym skrzyżowaniu
iRouteWanted = 1 + Random(3);
}
*/
}
}
else if ( ( pTrack->fRadius != 0.0 ) // odległość na łuku lepiej aproksymować cięciwami
@@ -708,7 +723,7 @@ void TController::TableCheck(double fDistance)
if (sSpeedTable[i].Update())
{
if( Global::iWriteLogEnabled & 8 ) {
WriteLog( "Speed table for " + OwnerName() + " detected switch change at " + sSpeedTable[ i ].trTrack->NameGet() + " (generating fresh trace)" );
WriteLog( "Speed table for " + OwnerName() + " detected switch change at " + sSpeedTable[ i ].trTrack->name() + " (generating fresh trace)" );
}
// usuwamy wszystko za tym torem
while (sSpeedTable.back().trTrack != sSpeedTable[i].trTrack)
@@ -812,7 +827,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
WriteLog(
pVehicle->asName + " as " + TrainParams->TrainName
+ ": at " + std::to_string(simulation::Time.data().wHour) + ":" + std::to_string(simulation::Time.data().wMinute)
+ " skipped " + asNextStop); // informacja
+ " passed " + asNextStop); // informacja
#endif
// przy jakim dystansie (stanie licznika) ma przesunąć na następny postój
fLastStopExpDist = mvOccupied->DistCounter + 0.250 + 0.001 * fLength;
@@ -1252,7 +1267,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
// jeśli ma stać, dostaje komendę od razu
go = cm_Command; // komenda z komórki, do wykonania po zatrzymaniu
}
else if( sSpeedTable[ i ].fDist <= 20.0 ) {
else if( sSpeedTable[ i ].fDist <= fMaxProximityDist ) {
// jeśli ma dociągnąć, to niech dociąga
// (moveStopCloser dotyczy dociągania do W4, nie semafora)
go = cm_Command; // komenda z komórki, do wykonania po zatrzymaniu
@@ -1782,7 +1797,7 @@ void TController::AutoRewident()
}
if (mvOccupied->TrainType == dt_EZT)
{
fAccThreshold = -fBrake_a0[BrakeAccTableSize] - 8 * fBrake_a1[BrakeAccTableSize];
fAccThreshold = std::max(-fBrake_a0[BrakeAccTableSize] - 8 * fBrake_a1[BrakeAccTableSize], -0.75);
fBrakeReaction = 0.25;
}
else if (ustaw > 16)
@@ -2400,7 +2415,7 @@ bool TController::IncBrake()
}
case Pneumatic: {
// NOTE: can't perform just test whether connected vehicle == nullptr, due to virtual couplers formed with nearby vehicles
bool standalone{ true };
bool standalone { true };
if( ( mvOccupied->TrainType == dt_ET41 )
|| ( mvOccupied->TrainType == dt_ET42 ) ) {
// NOTE: we're doing simplified checks full of presuptions here.
@@ -2415,9 +2430,24 @@ bool TController::IncBrake()
}
}
else {
/*
standalone =
( ( mvOccupied->Couplers[ 0 ].CouplingFlag == 0 )
&& ( mvOccupied->Couplers[ 1 ].CouplingFlag == 0 ) );
*/
if( pVehicles[ 0 ] != pVehicles[ 1 ] ) {
// more detailed version, will use manual braking also for coupled sets of controlled vehicles
auto *vehicle = pVehicles[ 0 ]; // start from first
while( ( true == standalone )
&& ( vehicle != nullptr ) ) {
// NOTE: we could simplify this by doing only check of the rear coupler, but this can be quite tricky in itself
// TODO: add easier ways to access front/rear coupler taking into account vehicle's direction
standalone =
( ( ( vehicle->MoverParameters->Couplers[ 0 ].CouplingFlag == 0 ) || ( vehicle->MoverParameters->Couplers[ 0 ].CouplingFlag & coupling::control ) )
&& ( ( vehicle->MoverParameters->Couplers[ 1 ].CouplingFlag == 0 ) || ( vehicle->MoverParameters->Couplers[ 1 ].CouplingFlag & coupling::control ) ) );
vehicle = vehicle->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła)
}
}
}
if( true == standalone ) {
OK = mvOccupied->IncLocalBrakeLevel(
@@ -2973,20 +3003,15 @@ void TController::RecognizeCommand()
c->Command = ""; // usunięcie obsłużonej komendy
}
void TController::PutCommand(std::string NewCommand, double NewValue1, double NewValue2,
const TLocation &NewLocation, TStopReason reason)
{ // wysłanie komendy przez event PutValues, jak pojazd ma obsadę, to wysyła tutaj, a nie do pojazdu
// bezpośrednio
vector3 sl;
sl.x = -NewLocation.X; // zamiana na współrzędne scenerii
sl.z = NewLocation.Y;
sl.y = NewLocation.Z;
void TController::PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const TLocation &NewLocation, TStopReason reason)
{ // wysłanie komendy przez event PutValues, jak pojazd ma obsadę, to wysyła tutaj, a nie do pojazdu bezpośrednio
// zamiana na współrzędne scenerii
glm::dvec3 sl { -NewLocation.X, NewLocation.Z, NewLocation.Y };
if (!PutCommand(NewCommand, NewValue1, NewValue2, &sl, reason))
mvOccupied->PutCommand(NewCommand, NewValue1, NewValue2, NewLocation);
}
bool TController::PutCommand(std::string NewCommand, double NewValue1, double NewValue2,
const vector3 *NewLocation, TStopReason reason)
bool TController::PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *NewLocation, TStopReason reason )
{ // analiza komendy
if (NewCommand == "CabSignal")
{ // SHP wyzwalane jest przez człon z obsadą, ale obsługiwane przez silnikowy
@@ -3639,6 +3664,14 @@ TController::UpdateSituation(double dt) {
// dla nastawienia G koniecznie należy wydłużyć drogę na czas reakcji
fBrakeDist += 2 * mvOccupied->Vel;
}
/*
// take into account effect of gravity (but to stay on safe side of calculations, only downhill)
if( fAccGravity > 0.025 ) {
fBrakeDist *= ( 1.0 + fAccGravity );
// TBD: use version which shortens route going uphill, too
//fBrakeDist = std::max( fBrakeDist, fBrakeDist * ( 1.0 + fAccGravity ) );
}
*/
// route scan
double routescanrange = (
mvOccupied->Vel > 5.0 ?
@@ -3812,8 +3845,11 @@ TController::UpdateSituation(double dt) {
CheckVehicles(); // sprawdzić światła nowego składu
JumpToNextOrder(); // wykonanie następnej komendy
}
/*
// NOTE: disabled as speed limit is decided in another place based on distance to potential target
else
SetVelocity(2.0, 0.0); // jazda w ustawionym kierunku z prędkością 2 (18s)
*/
} // if (AIControllFlag) //koniec zblokowania, bo była zmienna lokalna
}
else {
@@ -4208,13 +4244,12 @@ TController::UpdateSituation(double dt) {
~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders
if (fabs(VelSignal) >=
1.0) // 0.1 nie wysyła się do samochodow, bo potem nie ruszą
PutCommand("SetVelocity", VelSignal, VelNext,
NULL); // komenda robi dodatkowe operacje
PutCommand("SetVelocity", VelSignal, VelNext, nullptr); // komenda robi dodatkowe operacje
break;
case cm_ShuntVelocity: // od wersji 357 Tm nie budzi wyłączonej lokomotywy
if (!(OrderList[OrderPos] &
~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders
PutCommand("ShuntVelocity", VelSignal, VelNext, NULL);
PutCommand("ShuntVelocity", VelSignal, VelNext, nullptr);
else if (iCoupler) // jeśli jedzie w celu połączenia
SetVelocity(VelSignal, VelNext);
break;
@@ -4302,7 +4337,12 @@ TController::UpdateSituation(double dt) {
if( OrderCurrentGet() & Connect ) {
// jeśli spinanie, to jechać dalej
AccPreferred = std::min( 0.25, AccPreferred ); // nie hamuj
VelDesired = Global::Min0RSpeed( 20.0, VelDesired );
VelDesired =
Global::Min0RSpeed(
VelDesired,
( vehicle->fTrackBlock > 150.0 ?
20.0:
4.0 ) );
VelNext = 2.0; // i pakuj się na tamtego
}
else {
@@ -4345,7 +4385,12 @@ TController::UpdateSituation(double dt) {
else {
if( OrderCurrentGet() & Connect ) {
// if there's something nearby in the connect mode don't speed up too much
VelDesired = Global::Min0RSpeed( 20.0, VelDesired );
VelDesired =
Global::Min0RSpeed(
VelDesired,
( vehicle->fTrackBlock > 150.0 ?
20.0 :
4.0 ) );
}
}
}
@@ -4452,6 +4497,7 @@ TController::UpdateSituation(double dt) {
AbsAccS /= fMass;
}
AbsAccS_pub = AbsAccS;
AbsAccS_avg = interpolate( AbsAccS_avg, mvOccupied->AccS * iDirection, 0.25 );
#if LOGVELOCITY
// WriteLog("VelDesired="+AnsiString(VelDesired)+",
@@ -4580,26 +4626,53 @@ TController::UpdateSituation(double dt) {
// decisions based on current speed
if( mvOccupied->CategoryFlag == 1 ) {
// try to estimate increase of current velocity before engaged brakes start working
auto const speedestimate = vel + vel * ( 1.0 - fBrake_a0[ 0 ] ) * AbsAccS;
if( speedestimate > VelDesired ) {
// jesli jedzie za szybko do AKTUALNEGO
if( VelDesired == 0.0 ) {
// jesli stoj, to hamuj, ale i tak juz za pozno :)
AccDesired = std::min( AccDesired, -0.85 ); // hamuj solidnie
}
else {
if( speedestimate > ( VelDesired + fVelPlus ) ) {
// if it looks like we'll exceed maximum allowed speed start thinking about slight slowing down
AccDesired = std::min( AccDesired, -0.25 );
if( fAccGravity < 0.025 ) {
// on flats on uphill we can be less careful
if( vel > VelDesired ) {
// jesli jedzie za szybko do AKTUALNEGO
if( VelDesired == 0.0 ) {
// jesli stoj, to hamuj, ale i tak juz za pozno :)
AccDesired = std::min( AccDesired, -0.85 ); // hamuj solidnie
}
else {
// close enough to target to stop accelerating
AccDesired = std::min(
AccDesired, // but don't override decceleration for VelNext
interpolate( // ease off as you close to the target velocity
-0.06, AccPreferred,
clamp( speedestimate - vel, 0.0, fVelPlus ) / fVelPlus ) );
// slow down, not full stop
if( vel > ( VelDesired + fVelPlus ) ) {
// hamuj tak średnio
AccDesired = std::min( AccDesired, -0.25 );
}
else {
// o 5 km/h to olej (zacznij luzować)
AccDesired = std::min(
AccDesired, // but don't override decceleration for VelNext
std::max( 0.0, AccPreferred ) );
}
}
}
}
else {
// going sharply downhill we may need to start braking sooner than usual
// try to estimate increase of current velocity before engaged brakes start working
auto const speedestimate = vel + ( 1.0 - fBrake_a0[ 0 ] ) * 30.0 * AbsAccS;
if( speedestimate > VelDesired ) {
// jesli jedzie za szybko do AKTUALNEGO
if( VelDesired == 0.0 ) {
// jesli stoj, to hamuj, ale i tak juz za pozno :)
AccDesired = std::min( AccDesired, -0.85 ); // hamuj solidnie
}
else {
if( speedestimate > ( VelDesired + fVelPlus ) ) {
// if it looks like we'll exceed maximum allowed speed start thinking about slight slowing down
AccDesired = std::min( AccDesired, -0.25 );
}
else {
// close enough to target to stop accelerating
AccDesired = std::min(
AccDesired, // but don't override decceleration for VelNext
interpolate( // ease off as you close to the target velocity
-0.06, AccPreferred,
clamp( speedestimate - vel, 0.0, fVelPlus ) / fVelPlus ) );
}
}
}
}
@@ -4631,8 +4704,10 @@ TController::UpdateSituation(double dt) {
// last step sanity check, until the whole calculation is straightened out
AccDesired = std::min( AccDesired, AccPreferred );
if( mvOccupied->CategoryFlag == 1 ) {
// also take into account impact of gravity
if( ( mvOccupied->CategoryFlag == 1 )
&& ( fAccGravity > 0.025 ) ) {
// going downhill also take into account impact of gravity
AccDesired = clamp( AccDesired - fAccGravity, -0.9, 0.9 );
}
@@ -4884,41 +4959,19 @@ TController::UpdateSituation(double dt) {
}
}
}
// Mietek-end1
SpeedSet(); // ciągla regulacja prędkości
#if LOGVELOCITY
WriteLog("BrakePos=" + AnsiString(mvOccupied->BrakeCtrlPos) + ", MainCtrl=" +
AnsiString(mvControlling->MainCtrlPos));
#endif
/* //Ra: mamy teraz wskażnik na człon silnikowy, gorzej jak są dwa w
ukrotnieniu...
//zapobieganie poslizgowi w czlonie silnikowym; Ra: Couplers[1] powinno
być
if (Controlling->Couplers[0].Connected!=NULL)
if (TestFlag(Controlling->Couplers[0].CouplingFlag,ctrain_controll))
if (Controlling->Couplers[0].Connected->SlippingWheels)
if (Controlling->ScndCtrlPos>0?!Controlling->DecScndCtrl(1):true)
{
if (!Controlling->DecMainCtrl(1))
if (mvOccupied->BrakeCtrlPos==mvOccupied->BrakeCtrlPosNo)
mvOccupied->DecBrakeLevel();
++iDriverFailCount;
}
*/
// zapobieganie poslizgowi u nas
if (mvControlling->SlippingWheels)
{
if (!mvControlling->DecScndCtrl(2)) // bocznik na zero
mvControlling->DecMainCtrl(1);
if (mvOccupied->BrakeCtrlPos ==
mvOccupied->BrakeCtrlPosNo) // jeśli ostatnia pozycja hamowania
//yB: ten warunek wyżej nie ma sensu
mvOccupied->DecBrakeLevel(); // to cofnij hamulec
else
mvControlling->AntiSlippingButton();
++iDriverFailCount;
//mvControlling->SlippingWheels = false; // flaga już wykorzystana
if ((AccDesired < fAccGravity - 0.05) && (AbsAccS < AccDesired - fBrake_a1[0]*0.51)) {
// jak hamuje, to nie tykaj kranu za często
// yB: luzuje hamulec dopiero przy różnicy opóźnień rzędu 0.2
if( OrderList[ OrderPos ] != Disconnect ) {
// przy odłączaniu nie zwalniamy tu hamulca
DecBrake(); // tutaj zmniejszało o 1 przy odczepianiu
}
fBrakeTime = (
mvOccupied->BrakeDelayFlag > bdelay_G ?
mvOccupied->BrakeDelay[ 0 ] :
mvOccupied->BrakeDelay[ 2 ] )
/ 3.0;
fBrakeTime *= 0.5; // Ra: tymczasowo, bo przeżyna S1
}
// stop-gap measure to ensure cars actually brake to stop even when above calculactions go awry
// instead of releasing the brakes and creeping into obstacle at 1-2 km/h
@@ -5177,7 +5230,7 @@ std::string TController::StopReasonText()
if (eStopReason != 7) // zawalidroga będzie inaczej
return StopReasonTable[eStopReason];
else
return "Blocked by " + (pVehicles[0]->PrevAny()->GetName());
return "Blocked by " + (pVehicles[0]->PrevAny()->name());
};
//----------------------------------------------------------------------------------------------------------------------
@@ -5280,7 +5333,7 @@ TTrack * TController::BackwardTraceRoute(double &fDistance, double &fDirection,
}
// sprawdzanie zdarzeń semaforów i ograniczeń szlakowych
void TController::SetProximityVelocity(double dist, double vel, const vector3 *pos)
void TController::SetProximityVelocity( double dist, double vel, glm::dvec3 const *pos )
{ // Ra:przeslanie do AI prędkości
/*
//!!!! zastąpić prawidłową reakcją AI na SetProximityVelocity !!!!
@@ -5292,7 +5345,7 @@ void TController::SetProximityVelocity(double dist, double vel, const vector3 *p
if ((vel<0)?true:dist>0.1*(MoverParameters->Vel*MoverParameters->Vel-vel*vel)+50)
{//jeśli jest dalej od umownej drogi hamowania
*/
PutCommand("SetProximityVelocity", dist, vel, pos);
PutCommand( "SetProximityVelocity", dist, vel, pos );
/*
}
else
@@ -5304,43 +5357,47 @@ void TController::SetProximityVelocity(double dist, double vel, const vector3 *p
TCommandType TController::BackwardScan()
{ // sprawdzanie zdarzeń semaforów z tyłu pojazdu, zwraca komendę
// dzięki temu będzie można stawać za wskazanym sygnalizatorem, a zwłaszcza jeśli będzie jazda
// na kozioł
// ograniczenia prędkości nie są wtedy istotne, również koniec toru jest do niczego nie
// przydatny
// dzięki temu będzie można stawać za wskazanym sygnalizatorem, a zwłaszcza jeśli będzie jazda na kozioł
// ograniczenia prędkości nie są wtedy istotne, również koniec toru jest do niczego nie przydatny
// zwraca true, jeśli należy odwrócić kierunek jazdy pojazdu
if ((OrderList[OrderPos] & ~(Shunt | Connect)))
return cm_Unknown; // skanowanie sygnałów tylko gdy jedzie w trybie manewrowym albo czeka na
// rozkazy
if( ( OrderList[ OrderPos ] & ~( Shunt | Connect ) ) ) {
// skanowanie sygnałów tylko gdy jedzie w trybie manewrowym albo czeka na rozkazy
return cm_Unknown;
}
vector3 sl;
int startdir =
-pVehicles[0]->DirectionGet(); // kierunek jazdy względem sprzęgów pojazdu na czele
if (startdir == 0) // jeśli kabina i kierunek nie jest określony
return cm_Unknown; // nie robimy nic
double scandir =
startdir * pVehicles[0]->RaDirectionGet(); // szukamy od pierwszej osi w wybranym kierunku
if (scandir !=
0.0) // skanowanie toru w poszukiwaniu eventów GetValues (PutValues nie są przydatne)
{ // Ra: przy wstecznym skanowaniu prędkość nie ma znaczenia
// scanback=pVehicles[1]->NextDistance(fLength+1000.0); //odległość do następnego pojazdu,
// 1000 gdy nic nie ma
// kierunek jazdy względem sprzęgów pojazdu na czele
int const startdir = -pVehicles[0]->DirectionGet();
if( startdir == 0 ) {
// jeśli kabina i kierunek nie jest określony nie robimy nic
return cm_Unknown;
}
// szukamy od pierwszej osi w wybranym kierunku
double scandir = startdir * pVehicles[0]->RaDirectionGet();
if (scandir != 0.0) {
// skanowanie toru w poszukiwaniu eventów GetValues (PutValues nie są przydatne)
// Ra: przy wstecznym skanowaniu prędkość nie ma znaczenia
double scanmax = 1000; // 1000m do tyłu, żeby widział przeciwny koniec stacji
double scandist = scanmax; // zmodyfikuje na rzeczywiście przeskanowane
TEvent *e = NULL; // event potencjalnie od semafora
// opcjonalnie może być skanowanie od "wskaźnika" z przodu, np. W5, Tm=Ms1, koniec toru
TTrack *scantrack = BackwardTraceRoute(scandist, scandir, pVehicles[0]->RaTrackGet(),
e); // wg drugiej osi w kierunku ruchu
vector3 dir = startdir * pVehicles[0]->VectorFront(); // wektor w kierunku jazdy/szukania
if (!scantrack) // jeśli wstecz wykryto koniec toru
return cm_Unknown; // to raczej nic się nie da w takiej sytuacji zrobić
else
{ // a jeśli są dalej tory
// opcjonalnie może być skanowanie od "wskaźnika" z przodu, np. W5, Tm=Ms1, koniec toru wg drugiej osi w kierunku ruchu
TTrack *scantrack = BackwardTraceRoute(scandist, scandir, pVehicles[0]->RaTrackGet(), e);
vector3 const dir = startdir * pVehicles[0]->VectorFront(); // wektor w kierunku jazdy/szukania
if( !scantrack ) {
// jeśli wstecz wykryto koniec toru to raczej nic się nie da w takiej sytuacji zrobić
return cm_Unknown;
}
else {
// a jeśli są dalej tory
double vmechmax; // prędkość ustawiona semaforem
if (e)
{ // jeśli jest jakiś sygnał na widoku
if( e != nullptr ) {
// jeśli jest jakiś sygnał na widoku
#if LOGBACKSCAN
AnsiString edir =
pVehicle->asName + " - " + AnsiString((scandir > 0) ? "Event2 " : "Event1 ");
std::string edir {
"Backward scan by "
+ pVehicle->asName + " - "
+ ( ( scandir > 0 ) ?
"Event2 " :
"Event1 " ) };
#endif
// najpierw sprawdzamy, czy semafor czy inny znak został przejechany
vector3 pos = pVehicles[1]->RearPosition(); // pozycja tyłu
@@ -5348,66 +5405,67 @@ TCommandType TController::BackwardScan()
if (e->Type == tp_GetValues)
{ // przesłać info o zbliżającym się semaforze
#if LOGBACKSCAN
edir += "(" + (e->Params[8].asGroundNode->asName) + "): ";
edir += "(" + ( e->asNodeName ) + ")";
#endif
sl = e->PositionGet(); // położenie komórki pamięci
sem = sl - pos; // wektor do komórki pamięci od końca składu
// sem=e->Params[8].asGroundNode->pCenter-pos; //wektor do komórki pamięci
if (dir.x * sem.x + dir.z * sem.z < 0) // jeśli został minięty
// if ((mvOccupied->CategoryFlag&1)?(VelNext!=0.0):true) //dla pociągu wymagany
// sygnał zezwalający
{ // iloczyn skalarny jest ujemny, gdy sygnał stoi z tyłu
if (dir.x * sem.x + dir.z * sem.z < 0) {
// jeśli został minięty
// iloczyn skalarny jest ujemny, gdy sygnał stoi z tyłu
#if LOGBACKSCAN
WriteLog(edir + "- ignored as not passed yet");
WriteLog(edir + " - ignored as not passed yet");
#endif
return cm_Unknown; // nic
}
vmechmax = e->ValueGet(1); // prędkość przy tym semaforze
// przeliczamy odległość od semafora - potrzebne by były współrzędne początku
// składu
// scandist=(pos-e->Params[8].asGroundNode->pCenter).Length()-0.5*mvOccupied->Dim.L-10;
// //10m luzu
// przeliczamy odległość od semafora - potrzebne by były współrzędne początku składu
scandist = sem.Length() - 2; // 2m luzu przy manewrach wystarczy
if (scandist < 0)
scandist = 0; // ujemnych nie ma po co wysyłać
if( scandist < 0 ) {
// ujemnych nie ma po co wysyłać
scandist = 0;
}
bool move = false; // czy AI w trybie manewerowym ma dociągnąć pod S1
if (e->Command() == cm_SetVelocity)
if ((vmechmax == 0.0) ? (OrderCurrentGet() & (Shunt | Connect)) :
(OrderCurrentGet() &
Connect)) // przy podczepianiu ignorować wyjazd?
if( e->Command() == cm_SetVelocity ) {
if( ( vmechmax == 0.0 ) ?
( OrderCurrentGet() & ( Shunt | Connect ) ) :
( OrderCurrentGet() & Connect ) ) { // przy podczepianiu ignorować wyjazd?
move = true; // AI w trybie manewerowym ma dociągnąć pod S1
else
{ //
if ((scandist > fMinProximityDist) ?
(mvOccupied->Vel > 0.0) && (OrderCurrentGet() != Shunt) :
false)
{ // jeśli semafor jest daleko, a pojazd jedzie, to informujemy o
// zmianie prędkości
// jeśli jedzie manewrowo, musi dostać SetVelocity, żeby sie na pociągowy przełączył
// Mechanik->PutCommand("SetProximityVelocity",scandist,vmechmax,sl);
}
else {
if( ( scandist > fMinProximityDist )
&& ( ( mvOccupied->Vel > 0.0 )
&& ( OrderCurrentGet() != Shunt ) ) ) {
// jeśli semafor jest daleko, a pojazd jedzie, to informujemy o zmianie prędkości
// jeśli jedzie manewrowo, musi dostać SetVelocity, żeby sie na pociągowy przełączył
#if LOGBACKSCAN
// WriteLog(edir+"SetProximityVelocity "+AnsiString(scandist)+"
// "+AnsiString(vmechmax));
// WriteLog(edir+"SetProximityVelocity "+AnsiString(scandist) + AnsiString(vmechmax));
WriteLog(edir);
#endif
// SetProximityVelocity(scandist,vmechmax,&sl);
return (vmechmax > 0) ? cm_SetVelocity : cm_Unknown;
return (
vmechmax > 0 ?
cm_SetVelocity :
cm_Unknown );
}
else // ustawiamy prędkość tylko wtedy, gdy ma ruszyć, stanąć albo ma
// stać
// if ((MoverParameters->Vel==0.0)||(vmechmax==0.0)) //jeśli stoi lub ma
// stanąć/st
{ // semafor na tym torze albo lokomtywa stoi, a ma ruszyć, albo ma
// stanąć, albo nie ruszać
// stop trzeba powtarzać, bo inaczej zatrąbi i pojedzie sam
// PutCommand("SetVelocity",vmechmax,e->Params[9].asMemCell->Value2(),&sl,stopSem);
else {
// ustawiamy prędkość tylko wtedy, gdy ma ruszyć, stanąć albo ma stać
// if ((MoverParameters->Vel==0.0)||(vmechmax==0.0)) //jeśli stoi lub ma stanąć/stać
// semafor na tym torze albo lokomtywa stoi, a ma ruszyć, albo ma stanąć, albo nie rusz
// stop trzeba powtarzać, bo inaczej zatrąbi i pojedzie sam
// PutCommand("SetVelocity",vmechmax,e->Params[9].asMemCell->Value2(),&sl,stopSem);
#if LOGBACKSCAN
WriteLog(edir + "SetVelocity " + AnsiString(vmechmax) + " " +
AnsiString(e->Params[9].asMemCell->Value2()));
WriteLog(
edir + " - [SetVelocity] ["
+ to_string( vmechmax, 2 ) + "] ["
+ to_string( e->Params[ 9 ].asMemCell->Value2(), 2 ) + "]" );
#endif
return (vmechmax > 0) ? cm_SetVelocity : cm_Unknown;
return (
vmechmax > 0 ?
cm_SetVelocity :
cm_Unknown );
}
}
}
if (OrderCurrentGet() ? OrderCurrentGet() & (Shunt | Connect) :
true) // w Wait_for_orders też widzi tarcze
{ // reakcja AI w trybie manewrowym dodatkowo na sygnały manewrowe
@@ -5434,28 +5492,34 @@ TCommandType TController::BackwardScan()
// to można zmienić kierunek
}
}
else // ustawiamy prędkość tylko wtedy, gdy ma ruszyć, albo stanąć albo
// ma stać pod tarczą
{ // stop trzeba powtarzać, bo inaczej zatrąbi i pojedzie sam
// if ((MoverParameters->Vel==0.0)||(vmechmax==0.0)) //jeśli jedzie
// lub ma stanąć/stać
else {
// ustawiamy prędkość tylko wtedy, gdy ma ruszyć, albo stanąć albo ma stać pod tarczą
// stop trzeba powtarzać, bo inaczej zatrąbi i pojedzie sam
// if ((MoverParameters->Vel==0.0)||(vmechmax==0.0)) //jeśli jedzie lub ma stanąć/stać
{ // nie dostanie komendy jeśli jedzie i ma jechać
// PutCommand("ShuntVelocity",vmechmax,e->Params[9].asMemCell->Value2(),&sl,stopSem);
// PutCommand("ShuntVelocity",vmechmax,e->Params[9].asMemCell->Value2(),&sl,stopSem);
#if LOGBACKSCAN
WriteLog(edir + "ShuntVelocity " + AnsiString(vmechmax) + " " +
AnsiString(e->ValueGet(2)));
WriteLog(
edir + " - [ShuntVelocity] ["
+ to_string( vmechmax, 2 ) + "] ["
+ to_string( e->ValueGet( 2 ), 2 ) + "]" );
#endif
return (vmechmax > 0) ? cm_ShuntVelocity : cm_Unknown;
return (
vmechmax > 0 ?
cm_ShuntVelocity :
cm_Unknown );
}
}
if ((vmechmax != 0.0) && (scandist < 100.0))
{ // jeśli Tm w odległości do 100m podaje zezwolenie na jazdę, to od
// razu ją ignorujemy, aby móc szukać kolejnej
// eSignSkip=e; //wtedy uznajemy ignorowaną przy poszukiwaniu nowej
if ((vmechmax != 0.0) && (scandist < 100.0)) {
// jeśli Tm w odległości do 100m podaje zezwolenie na jazdę, to od razu ją ignorujemy, aby móc szukać kolejnej
// eSignSkip=e; //wtedy uznajemy ignorowaną przy poszukiwaniu nowej
#if LOGBACKSCAN
WriteLog(edir + "- will be ignored due to Ms2");
WriteLog(edir + " - will be ignored due to Ms2");
#endif
return (vmechmax > 0) ? cm_ShuntVelocity : cm_Unknown;
return (
vmechmax > 0 ?
cm_ShuntVelocity :
cm_Unknown );
}
} // if (move?...
} // if (OrderCurrentGet()==Shunt)
@@ -5644,31 +5708,30 @@ int TController::CrossRoute(TTrack *tr)
}
return 0; // nic nie znaleziono?
};
/*
void TController::RouteSwitch(int d)
{ // ustawienie kierunku jazdy z kabiny
d &= 3;
if( d ) {
if( iRouteWanted != d ) { // nowy kierunek
iRouteWanted = d; // zapamiętanie
if( mvOccupied->CategoryFlag & 2 ) {
// jeśli samochód
for( std::size_t i = 0; i < sSpeedTable.size(); ++i ) {
// szukanie pierwszego skrzyżowania i resetowanie kierunku na nim
if( true == TestFlag( sSpeedTable[ i ].iFlags, spEnabled | spTrack ) ) {
// jeśli pozycja istotna (1) oraz odcinek (2)
if( false == TestFlag( sSpeedTable[ i ].iFlags, spElapsed ) ) {
// odcinek nie może być miniętym
if( sSpeedTable[ i ].trTrack->eType == tt_Cross ) // jeśli skrzyżowanie
{
while( sSpeedTable.size() >= i ) {
// NOTE: we're ignoring semaphor flags and not resetting them like we do for train route trimming
// but what if there's street lights?
// TODO: investigate
sSpeedTable.pop_back();
}
iLast = sSpeedTable.size();
if( ( d != 0 )
&& ( iRouteWanted != d ) ) { // nowy kierunek
iRouteWanted = d; // zapamiętanie
if( mvOccupied->CategoryFlag & 2 ) {
// jeśli samochód
for( std::size_t i = 0; i < sSpeedTable.size(); ++i ) {
// szukanie pierwszego skrzyżowania i resetowanie kierunku na nim
if( true == TestFlag( sSpeedTable[ i ].iFlags, spEnabled | spTrack ) ) {
// jeśli pozycja istotna (1) oraz odcinek (2)
if( false == TestFlag( sSpeedTable[ i ].iFlags, spElapsed ) ) {
// odcinek nie może być miniętym
if( sSpeedTable[ i ].trTrack->eType == tt_Cross ) // jeśli skrzyżowanie
{
while( sSpeedTable.size() >= i ) {
// NOTE: we're ignoring semaphor flags and not resetting them like we do for train route trimming
// but what if there's street lights?
// TODO: investigate
sSpeedTable.pop_back();
}
iLast = sSpeedTable.size();
}
}
}
@@ -5676,6 +5739,7 @@ void TController::RouteSwitch(int d)
}
}
};
*/
std::string TController::OwnerName() const
{
return ( pVehicle ? pVehicle->MoverParameters->Name : "none" );

View File

@@ -209,6 +209,7 @@ public:
double BrakeAccFactor();
double fBrakeReaction = 1.0; //opóźnienie zadziałania hamulca - czas w s / (km/h)
double fAccThreshold = 0.0; // próg opóźnienia dla zadziałania hamulca
double AbsAccS_avg = 0.0; // averaged out directional acceleration
double AbsAccS_pub = 0.0; // próg opóźnienia dla zadziałania hamulca
double fBrake_a0[BrakeAccTableSize+1] = { 0.0 }; // próg opóźnienia dla zadziałania hamulca
double fBrake_a1[BrakeAccTableSize+1] = { 0.0 }; // próg opóźnienia dla zadziałania hamulca
@@ -230,8 +231,9 @@ private:
TAction GetAction() {
return eAction; }
bool AIControllFlag = false; // rzeczywisty/wirtualny maszynista
int iRouteWanted = 3; // oczekiwany kierunek jazdy (0-stop,1-lewo,2-prawo,3-prosto) np. odpala
// migacz lub czeka na stan zwrotnicy
/*
int iRouteWanted = 3; // oczekiwany kierunek jazdy (0-stop,1-lewo,2-prawo,3-prosto) np. odpala migacz lub czeka na stan zwrotnicy
*/
private:
TDynamicObject *pVehicle = nullptr; // pojazd w którym siedzi sterujący
TDynamicObject *pVehicles[2]; // skrajne pojazdy w składzie (niekoniecznie bezpośrednio sterowane)
@@ -311,10 +313,8 @@ private:
public:
Mtable::TTrainParameters *Timetable() {
return TrainParams; };
void PutCommand(std::string NewCommand, double NewValue1, double NewValue2,
const TLocation &NewLocation, TStopReason reason = stopComm);
bool PutCommand(std::string NewCommand, double NewValue1, double NewValue2,
const vector3 *NewLocation, TStopReason reason = stopComm);
void PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const TLocation &NewLocation, TStopReason reason = stopComm);
bool PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *NewLocation, TStopReason reason = stopComm );
void UpdateSituation(double dt); // uruchamiac przynajmniej raz na sekundę
// procedury dotyczace rozkazow dla maszynisty
// uaktualnia informacje o prędkości
@@ -374,7 +374,7 @@ private:
bool BackwardTrackBusy(TTrack *Track);
TEvent *CheckTrackEventBackward(double fDirection, TTrack *Track);
TTrack *BackwardTraceRoute(double &fDistance, double &fDirection, TTrack *Track, TEvent *&Event);
void SetProximityVelocity(double dist, double vel, const vector3 *pos);
void SetProximityVelocity( double dist, double vel, glm::dvec3 const *pos );
TCommandType BackwardScan();
public:
@@ -401,7 +401,9 @@ private:
void DirectionInitial();
std::string TableText(std::size_t const Index);
int CrossRoute(TTrack *tr);
/*
void RouteSwitch(int d);
*/
std::string OwnerName() const;
TMoverParameters const *Controlling() const {
return mvControlling; }

View File

@@ -15,34 +15,20 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "DynObj.h"
#include "Logs.h"
#include "MdlMngr.h"
#include "Timer.h"
#include "usefull.h"
// McZapkie-260202
#include "simulation.h"
#include "Globals.h"
#include "renderer.h"
#include "AirCoupler.h"
#include "TractionPower.h"
#include "Ground.h" //bo Global::pGround->bDynamicRemove
#include "Event.h"
#include "Driver.h"
#include "Camera.h" //bo likwidujemy trzęsienie
#include "Timer.h"
#include "Logs.h"
#include "Console.h"
#include "Traction.h"
#include "sound.h"
#include "MdlMngr.h"
// Ra: taki zapis funkcjonuje lepiej, ale może nie jest optymalny
#define vWorldFront Math3D::vector3(0, 0, 1)
#define vWorldUp Math3D::vector3(0, 1, 0)
#define vWorldLeft CrossProduct(vWorldUp, vWorldFront)
// Ra: bo te poniżej to się powielały w każdym module odobno
// vector3 vWorldFront=vector3(0,0,1);
// vector3 vWorldUp=vector3(0,1,0);
// vector3 vWorldLeft=CrossProduct(vWorldUp,vWorldFront);
#define M_2PI 6.283185307179586476925286766559;
const float maxrot = (float)(M_PI / 3.0); // 60°
@@ -50,6 +36,7 @@ std::string const TDynamicObject::MED_labels[] = {
"masa: ", "amax: ", "Fzad: ", "FmPN: ", "FmED: ", "FrED: ", "FzPN: ", "nPrF: "
};
bool TDynamicObject::bDynamicRemove { false };
//---------------------------------------------------------------------------
void TAnimPant::AKP_4E()
@@ -416,14 +403,22 @@ void TDynamicObject::UpdateDoorTranslate(TAnim *pAnim)
// Ra: te współczynniki są bez sensu, bo modyfikują wektor przesunięcia
// w efekcie drzwi otwierane na zewnątrz będą odlatywac dowolnie daleko :)
// ograniczyłem zakres ruchu funkcją max
if (pAnim->smAnimated)
{
if (pAnim->iNumber & 1)
if (pAnim->smAnimated) {
if( pAnim->iNumber & 1 ) {
pAnim->smAnimated->SetTranslate(
vector3(0, 0, Min0R(dDoorMoveR * pAnim->fSpeed, dDoorMoveR)));
else
vector3{
0.0,
0.0,
dDoorMoveR } );
}
else {
pAnim->smAnimated->SetTranslate(
vector3(0, 0, Min0R(dDoorMoveL * pAnim->fSpeed, dDoorMoveL)));
vector3{
0.0,
0.0,
dDoorMoveL } );
}
}
};
@@ -494,18 +489,30 @@ void TDynamicObject::UpdatePant(TAnim *pAnim)
void TDynamicObject::UpdateDoorPlug(TAnim *pAnim)
{ // animacja drzwi - odskokprzesuw
if (pAnim->smAnimated)
{
if (pAnim->iNumber & 1)
if (pAnim->smAnimated) {
if( pAnim->iNumber & 1 ) {
pAnim->smAnimated->SetTranslate(
vector3(Min0R(dDoorMoveR * 2, MoverParameters->DoorMaxPlugShift), 0,
Max0R(0, Min0R(dDoorMoveR * pAnim->fSpeed, dDoorMoveR) -
MoverParameters->DoorMaxPlugShift * 0.5f)));
else
vector3 {
std::min(
dDoorMoveR * 2,
MoverParameters->DoorMaxPlugShift ),
0.0,
std::max(
0.0,
dDoorMoveR - MoverParameters->DoorMaxPlugShift * 0.5 ) } );
}
else {
pAnim->smAnimated->SetTranslate(
vector3(Min0R(dDoorMoveL * 2, MoverParameters->DoorMaxPlugShift), 0,
Max0R(0, Min0R(dDoorMoveL * pAnim->fSpeed, dDoorMoveL) -
MoverParameters->DoorMaxPlugShift * 0.5f)));
vector3 {
std::min(
dDoorMoveL * 2,
MoverParameters->DoorMaxPlugShift ),
0.0,
std::max(
0.0,
dDoorMoveL - MoverParameters->DoorMaxPlugShift * 0.5f ) } );
}
}
};
@@ -1757,8 +1764,8 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
}
if (ActPar.find('0') != std::string::npos) // wylaczanie na sztywno
{
MoverParameters->Hamulec->SetBrakeStatus( MoverParameters->Hamulec->GetBrakeStatus() | b_dmg ); // wylacz
MoverParameters->Hamulec->ForceEmptiness();
MoverParameters->Hamulec->SetBrakeStatus( MoverParameters->Hamulec->GetBrakeStatus() | b_dmg ); // wylacz
}
if (ActPar.find('E') != std::string::npos) // oprozniony
{
@@ -1782,16 +1789,16 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
{
if (Random(10) < 1) // losowanie 1/10
{
MoverParameters->Hamulec->SetBrakeStatus( MoverParameters->Hamulec->GetBrakeStatus() | b_dmg ); // wylacz
MoverParameters->Hamulec->ForceEmptiness();
MoverParameters->Hamulec->SetBrakeStatus( MoverParameters->Hamulec->GetBrakeStatus() | b_dmg ); // wylacz
}
}
if (ActPar.find('X') != std::string::npos) // agonalny wylaczanie 20%, usrednienie przekladni
{
if (Random(100) < 20) // losowanie 20/100
{
MoverParameters->Hamulec->SetBrakeStatus( MoverParameters->Hamulec->GetBrakeStatus() | b_dmg ); // wylacz
MoverParameters->Hamulec->ForceEmptiness();
MoverParameters->Hamulec->SetBrakeStatus( MoverParameters->Hamulec->GetBrakeStatus() | b_dmg ); // wylacz
}
if (MoverParameters->BrakeCylMult[2] * MoverParameters->BrakeCylMult[1] >
0.01) // jesli jest nastawiacz mechaniczny PL
@@ -2625,77 +2632,36 @@ bool TDynamicObject::Update(double dt, double dt1)
NoVoltTime = 0;
tmpTraction.TractionVoltage = v;
}
else
{
/*
if (MoverParameters->Vel>0.1f) //jeśli jedzie
if (NoVoltTime==0.0) //tylko przy pierwszym zaniku napięcia
if (MoverParameters->PantFrontUp||MoverParameters->PantRearUp)
//if
((pants[0].fParamPants->PantTraction>1.0)||(pants[1].fParamPants->PantTraction>1.0))
{//wspomagacz usuwania problemów z siecią
if (!Global::iPause)
{//Ra: tymczasowa teleportacja do miejsca, gdzie brakuje prądu
Global::SetCameraPosition(vPosition+vector3(0,0,5)); //nowa
pozycja dla
generowania obiektów
Global::pCamera->Init(vPosition+vector3(0,0,5),Global::pFreeCameraInitAngle[0]);
//przestawienie
}
Global:l::pGround->Silence(Global::pCamera->Pos); //wyciszenie
wszystkiego
z poprzedniej pozycji
Globa:iPause|=1; //tymczasowe zapauzowanie, gdy problem z
siecią
}
*/
NoVoltTime = NoVoltTime + dt;
if (NoVoltTime > 0.2) // jeśli brak zasilania dłużej niż 0.2 sekundy (25km/h pod
// izolatorem daje 0.15s)
{ // Ra 2F1H: prowizorka, trzeba przechować napięcie, żeby nie wywalało
// WS pod
// izolatorem
if (MoverParameters->Vel > 0.5) // jeśli jedzie
if (MoverParameters->PantFrontUp ||
MoverParameters->PantRearUp) // Ra 2014-07: doraźna blokada logowania
// zimnych lokomotyw - zrobić to trzeba
// inaczej
else {
NoVoltTime += dt;
if( NoVoltTime > 0.2 ) {
// jeśli brak zasilania dłużej niż 0.2 sekundy (25km/h pod izolatorem daje 0.15s)
// Ra 2F1H: prowizorka, trzeba przechować napięcie, żeby nie wywalało WS pod izolatorem
if( MoverParameters->Vel > 0.5 ) {
// jeśli jedzie
// Ra 2014-07: doraźna blokada logowania zimnych lokomotyw - zrobić to trzeba inaczej
if( MoverParameters->PantFrontUp || MoverParameters->PantRearUp )
// if (NoVoltTime>0.02) //tu można ograniczyć czas rozłączenia
// if (DebugModeFlag) //logowanie nie zawsze
if ((MoverParameters->Mains) &&
((MoverParameters->EngineType != ElectricInductionMotor)
|| (MoverParameters->GetTrainsetVoltage() < 0.1f)))
{ // Ra 15-01: logować tylko, jeśli WS załączony
// yB 16-03: i nie jest to asynchron zasilany z daleka
// if (MoverParameters->PantFrontUp&&pants)
if( ( MoverParameters->Mains )
&& ( ( MoverParameters->EngineType != ElectricInductionMotor )
|| ( MoverParameters->GetTrainsetVoltage() < 0.1f ) ) ) {
// Ra 15-01: logować tylko, jeśli WS załączony
// yB 16-03: i nie jest to asynchron zasilany z daleka
// Ra 15-01: bezwzględne współrzędne pantografu nie są dostępne,
// więc lepiej się tego nie zaloguje
ErrorLog("Voltage loss: by " + MoverParameters->Name + " at " +
to_string(vPosition.x, 2, 7) + " " +
to_string(vPosition.y, 2, 7) + " " +
to_string(vPosition.z, 2, 7) + ", time " +
to_string(NoVoltTime, 2, 7));
// if (MoverParameters->PantRearUp)
// if (iAnimType[ANIM_PANTS]>1)
// if (pants[1])
// ErrorLog("Voltage loss: by "+MoverParameters->Name+" at
// "+FloatToStrF(vPosition.x,ffFixed,7,2)+"
// "+FloatToStrF(vPosition.y,ffFixed,7,2)+"
// "+FloatToStrF(vPosition.z,ffFixed,7,2)+", time
// "+FloatToStrF(NoVoltTime,ffFixed,7,2));
ErrorLog(
"Bad traction: " + MoverParameters->Name
+ " lost power for " + to_string( NoVoltTime, 2 ) + " sec. at "
+ to_string( glm::dvec3{ vPosition } ) );
}
// Ra 2F1H: nie było sensu wpisywać tu zera po upływie czasu, bo
// zmienna była
}
// Ra 2F1H: nie było sensu wpisywać tu zera po upływie czasu, bo zmienna była
// tymczasowa, a napięcie zerowane od razu
tmpTraction.TractionVoltage = 0; // Ra 2013-12: po co tak?
// pControlled->MainSwitch(false); //może tak?
}
}
}
// else //Ra: nie no, trzeba podnieść pantografy, jak nie będzie drutu, to
// będą miały prąd
// po osiągnięciu 1.4m
// tmpTraction.TractionVoltage=0.95*MoverParameters->EnginePowerSource.MaxVoltage;
}
else
tmpTraction.TractionVoltage = 0.95 * MoverParameters->EnginePowerSource.MaxVoltage;
@@ -2760,9 +2726,9 @@ bool TDynamicObject::Update(double dt, double dt1)
1000; // chwilowy max ED -> do rozdzialu sil
FfulED = std::min(p->MoverParameters->eimv[eimv_Fful], 0.0) *
1000; // chwilowy max ED -> do rozdzialu sil
FrED -= std::min(p->MoverParameters->eimv[eimv_Fr], 0.0) *
FrED -= std::min(p->MoverParameters->eimv[eimv_Fmax], 0.0) *
1000; // chwilowo realizowane ED -> do pneumatyki
Frj += std::max(p->MoverParameters->eimv[eimv_Fr], 0.0) *
Frj += std::max(p->MoverParameters->eimv[eimv_Fmax], 0.0) *
1000;// chwilowo realizowany napęd -> do utrzymującego
masa += p->MoverParameters->TotalMass;
osie += p->MoverParameters->NAxles;
@@ -2861,10 +2827,10 @@ bool TDynamicObject::Update(double dt, double dt1)
if ((FzEP[i] > 0.01) &&
(FzEP[i] >
p->MoverParameters->TotalMass * p->MoverParameters->eimc[eimc_p_eped] +
Min0R(p->MoverParameters->eimv[eimv_Fr], 0) * 1000) &&
Min0R(p->MoverParameters->eimv[eimv_Fmax], 0) * 1000) &&
(!PrzekrF[i]))
{
float przek1 = -Min0R(p->MoverParameters->eimv[eimv_Fr], 0) * 1000 +
float przek1 = -Min0R(p->MoverParameters->eimv[eimv_Fmax], 0) * 1000 +
FzEP[i] -
p->MoverParameters->TotalMass *
p->MoverParameters->eimc[eimc_p_eped] * 0.999;
@@ -2969,19 +2935,13 @@ bool TDynamicObject::Update(double dt, double dt1)
// McZapkie-260202 - dMoveLen przyda sie przy stukocie kol
dDOMoveLen =
GetdMoveLen() + MoverParameters->ComputeMovement(dt, dt1, ts, tp, tmpTraction, l, r);
// yB: zeby zawsze wrzucalo w jedna strone zakretu
/*
// this seemed to have opposite effect, if anything -- the sway direction would be affected
// by the 'direction' of the track, making the sway go sometimes inward, sometimes outward
MoverParameters->AccN *= -ABuGetDirection();
*/
// if (dDOMoveLen!=0.0) //Ra: nie może być, bo blokuje Event0
if( Mechanik )
Mechanik->MoveDistanceAdd( dDOMoveLen ); // dodanie aktualnego przemieszczenia
Move(dDOMoveLen);
if (!bEnabled) // usuwane pojazdy nie mają toru
{ // pojazd do usunięcia
Global::pGround->bDynamicRemove = true; // sprawdzić
bDynamicRemove = true; // sprawdzić
return false;
}
Global::ABuDebug = dDOMoveLen / dt1;
@@ -3218,19 +3178,18 @@ bool TDynamicObject::Update(double dt, double dt1)
if ((MoverParameters->PantRearVolt == 0.0) &&
(MoverParameters->PantFrontVolt == 0.0) && sPantUp)
sPantUp->gain(vol).position(vPosition).play();
if (p->hvPowerWire) // TODO: wyliczyć trzeba prąd przypadający na
// pantograf i
// wstawić do GetVoltage()
{
MoverParameters->PantRearVolt =
p->hvPowerWire->VoltageGet(MoverParameters->Voltage, fPantCurrent);
if (p->hvPowerWire) {
// TODO: wyliczyć trzeba prąd przypadający na pantograf i wstawić do GetVoltage()
MoverParameters->PantRearVolt = p->hvPowerWire->VoltageGet( MoverParameters->Voltage, fPantCurrent );
fCurrent -= fPantCurrent; // taki prąd płynie przez powyższy pantograf
}
else
MoverParameters->PantRearVolt = 0.0;
}
else
else {
// Global::iPause ^= 2;
MoverParameters->PantRearVolt = 0.0;
}
break;
} // pozostałe na razie nie obsługiwane
if( MoverParameters->PantPress > (
@@ -3297,8 +3256,8 @@ bool TDynamicObject::Update(double dt, double dt1)
p->fAngleU = acos((p->fLenL1 * cos(k) + p->fHoriz) / p->fLenU1); // górne ramię
// wyliczyć aktualną wysokość z wzoru sinusowego
// h=a*sin()+b*sin()
p->PantWys = p->fLenL1 * sin(k) + p->fLenU1 * sin(p->fAngleU) +
p->fHeight; // wysokość całości
// wysokość całości
p->PantWys = p->fLenL1 * sin(k) + p->fLenU1 * sin(p->fAngleU) + p->fHeight;
}
}
} // koniec pętli po pantografach
@@ -4183,7 +4142,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName,
// Ra 15-01: gałka nastawy hamulca
parser.getTokens();
parser >> asAnimName;
smBrakeMode = mdModel->GetFromName(asAnimName.c_str());
smBrakeMode = mdModel->GetFromName(asAnimName);
// jeszcze wczytać kąty obrotu dla poszczególnych ustawień
}
@@ -4191,7 +4150,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName,
// Ra 15-01: gałka nastawy hamulca
parser.getTokens();
parser >> asAnimName;
smLoadMode = mdModel->GetFromName(asAnimName.c_str());
smLoadMode = mdModel->GetFromName(asAnimName);
// jeszcze wczytać kąty obrotu dla poszczególnych ustawień
}
@@ -4203,7 +4162,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName,
for (i = 0; i < iAnimType[ANIM_WHEELS]; ++i) // liczba osi
{ // McZapkie-050402: wyszukiwanie kol o nazwie str*
asAnimName = token + std::to_string(i + 1);
pAnimations[i].smAnimated = mdModel->GetFromName(asAnimName.c_str()); // ustalenie submodelu
pAnimations[i].smAnimated = mdModel->GetFromName(asAnimName); // ustalenie submodelu
if (pAnimations[i].smAnimated)
{ //++iAnimatedAxles;
pAnimations[i].smAnimated->WillBeAnimated(); // wyłączenie optymalizacji transformu
@@ -4344,8 +4303,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName,
}
}
else
ErrorLog("Bad model: " + asFileName + " - missed submodel " +
asAnimName); // brak ramienia
ErrorLog("Bad model: " + asFileName + " - missed submodel " + asAnimName); // brak ramienia
}
}
@@ -4379,10 +4337,9 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName,
}
}
else
ErrorLog( "Bad model: " + asFileName + " - missed submodel " +
asAnimName ); // brak ramienia
ErrorLog( "Bad model: " + asFileName + " - missed submodel " + asAnimName ); // brak ramienia
}
}
}
}
else if( token == "animpantrg1prefix:" ) {
@@ -4473,10 +4430,8 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName,
// pants[i].fParamPants->vPos.z=0; //niezerowe dla pantografów
// asymetrycznych
pants[ i ].fParamPants->PantTraction = pants[ i ].fParamPants->PantWys;
pants[ i ].fParamPants->fWidth =
0.5 *
MoverParameters->EnginePowerSource.CollectorParameters
.CSW; // połowa szerokości ślizgu; jest w "Power: CSW="
// połowa szerokości ślizgu; jest w "Power: CSW="
pants[ i ].fParamPants->fWidth = 0.5 * MoverParameters->EnginePowerSource.CollectorParameters.CSW;
}
}
}
@@ -4955,7 +4910,9 @@ void TDynamicObject::RadioStop()
if( ( MoverParameters->SecuritySystem.RadioStop )
&& ( MoverParameters->Radio ) ) {
// jeśli pojazd ma RadioStop i jest on aktywny
Mechanik->PutCommand( "Emergency_brake", 1.0, 1.0, &vPosition, stopRadio );
// HAX cast until math types unification
glm::dvec3 pos = static_cast<glm::dvec3>(vPosition);
Mechanik->PutCommand( "Emergency_brake", 1.0, 1.0, &pos, stopRadio );
// add onscreen notification for human driver
// TODO: do it selectively for the 'local' driver once the multiplayer is in
if( false == Mechanik->AIControllFlag ) {
@@ -5414,3 +5371,166 @@ TDynamicObject::ConnectedEnginePowerSource( TDynamicObject const *Caller ) const
// ...if we're still here, report lack of power source
return MoverParameters->EnginePowerSource.SourceType;
}
// legacy method, calculates changes in simulation state over specified time
void
vehicle_table::update( double Deltatime, int Iterationcount ) {
// Ra: w zasadzie to trzeba by utworzyć oddzielną listę taboru do liczenia fizyki
// na którą by się zapisywały wszystkie pojazdy będące w ruchu
// pojazdy stojące nie potrzebują aktualizacji, chyba że np. ktoś im zmieni nastawę hamulca
// oddzielną listę można by zrobić na pojazdy z napędem, najlepiej posortowaną wg typu napędu
for( auto *vehicle : m_items ) {
if( false == vehicle->bEnabled ) { continue; }
// Ra: zmienić warunek na sprawdzanie pantografów w jednej zmiennej: czy pantografy i czy podniesione
if( vehicle->MoverParameters->EnginePowerSource.SourceType == CurrentCollector ) {
update_traction( vehicle );
}
vehicle->MoverParameters->ComputeConstans();
vehicle->CoupleDist();
}
if( Iterationcount > 1 ) {
// ABu: ponizsze wykonujemy tylko jesli wiecej niz jedna iteracja
for( int iteration = 0; iteration < ( Iterationcount - 1 ); ++iteration ) {
for( auto *vehicle : m_items ) {
vehicle->UpdateForce( Deltatime, Deltatime, false );
}
for( auto *vehicle : m_items ) {
vehicle->FastUpdate( Deltatime );
}
}
}
auto const totaltime { Deltatime * Iterationcount }; // całkowity czas
for( auto *vehicle : m_items ) {
vehicle->UpdateForce( Deltatime, totaltime, true );
}
for( auto *vehicle : m_items ) {
// Ra 2015-01: tylko tu przelicza sieć trakcyjną
vehicle->Update( Deltatime, totaltime );
}
/*
// TODO: re-implement
if (TDynamicObject::bDynamicRemove)
{ // jeśli jest coś do usunięcia z listy, to trzeba na końcu
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
if ( false == Current->DynamicObject->bEnabled)
{
DynamicRemove(Current->DynamicObject); // usunięcie tego i podłączonych
Current = nRootDynamic; // sprawdzanie listy od początku
}
TDynamicObject::bDynamicRemove = false; // na razie koniec
}
*/
}
// legacy method, checks for presence and height of traction wire for specified vehicle
void
vehicle_table::update_traction( TDynamicObject *Vehicle ) {
auto const vFront = glm::make_vec3( Vehicle->VectorFront().getArray() ); // wektor normalny dla płaszczyzny ruchu pantografu
auto const vUp = glm::make_vec3( Vehicle->VectorUp().getArray() ); // wektor pionu pudła (pochylony od pionu na przechyłce)
auto const vLeft = glm::make_vec3( Vehicle->VectorLeft().getArray() ); // wektor odległości w bok (odchylony od poziomu na przechyłce)
auto const position = glm::dvec3 { Vehicle->GetPosition() }; // współrzędne środka pojazdu
for( int pantographindex = 0; pantographindex < Vehicle->iAnimType[ ANIM_PANTS ]; ++pantographindex ) {
// pętla po pantografach
auto pantograph { Vehicle->pants[ pantographindex ].fParamPants };
if( true == (
pantographindex == TMoverParameters::side::front ?
Vehicle->MoverParameters->PantFrontUp :
Vehicle->MoverParameters->PantRearUp ) ) {
// jeśli pantograf podniesiony
auto const pant0 { position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x ) };
if( pantograph->hvPowerWire != nullptr ) {
// jeżeli znamy drut z poprzedniego przebiegu
for( int attempts = 0; attempts < 30; ++attempts ) {
// powtarzane aż do znalezienia odpowiedniego odcinka na liście dwukierunkowej
if( pantograph->hvPowerWire->iLast & 0x3 ) {
// dla ostatniego i przedostatniego przęsła wymuszamy szukanie innego
// nie to, że nie ma, ale trzeba sprawdzić inne
pantograph->hvPowerWire = nullptr;
break;
}
if( pantograph->hvPowerWire->hvParallel ) {
// jeśli przęsło tworzy bieżnię wspólną, to trzeba sprawdzić pozostałe
// nie to, że nie ma, ale trzeba sprawdzić inne
pantograph->hvPowerWire = nullptr;
break;
}
// obliczamy wyraz wolny równania płaszczyzny (to miejsce nie jest odpowienie)
// podstawiamy równanie parametryczne drutu do równania płaszczyzny pantografu
auto const fRaParam =
-( glm::dot( pantograph->hvPowerWire->pPoint1, vFront ) - glm::dot( pant0, vFront ) )
/ glm::dot( pantograph->hvPowerWire->vParametric, vFront );
if( fRaParam < -0.001 ) {
// histereza rzędu 7cm na 70m typowego przęsła daje 1 promil
pantograph->hvPowerWire = pantograph->hvPowerWire->hvNext[ 0 ];
continue;
}
if( fRaParam > 1.001 ) {
pantograph->hvPowerWire = pantograph->hvPowerWire->hvNext[ 1 ];
continue;
}
// jeśli t jest w przedziale, wyznaczyć odległość wzdłuż wektorów vUp i vLeft
// punkt styku płaszczyzny z drutem (dla generatora łuku el.)
auto const vStyk { pantograph->hvPowerWire->pPoint1 + fRaParam * pantograph->hvPowerWire->vParametric };
auto const vGdzie { vStyk - pant0 }; // wektor
// odległość w pionie musi być w zasięgu ruchu "pionowego" pantografu
// musi się mieścić w przedziale ruchu pantografu
auto const fVertical { glm::dot( vGdzie, vUp ) };
// odległość w bok powinna być mniejsza niż pół szerokości pantografu
// to się musi mieścić w przedziale zależnym od szerokości pantografu
auto const fHorizontal { std::abs( glm::dot( vGdzie, vLeft ) ) - pantograph->fWidth };
// jeśli w pionie albo w bok jest za daleko, to dany drut jest nieużyteczny
if( fHorizontal <= 0.0 ) {
// koniec pętli, aktualny drut pasuje
pantograph->PantTraction = fVertical;
break;
}
else {
// the wire is outside contact area and as of now we don't have good detection of parallel sections
// as such there's no guaratee there isn't parallel section present.
// therefore we don't bother checking if the wire is still within range of guide horns
// but simply force area search for potential better option
pantograph->hvPowerWire = nullptr;
break;
}
}
}
if( pantograph->hvPowerWire == nullptr ) {
// look in the region for a suitable traction piece if we don't already have any
simulation::Region->update_traction( Vehicle, pantographindex );
}
if( ( pantograph->hvPowerWire == nullptr )
&& ( false == Global::bLiveTraction ) ) {
// jeśli drut nie znaleziony ale można oszukiwać to dajemy coś tam dla picu
Vehicle->pants[ pantographindex ].fParamPants->PantTraction = 1.4;
}
}
else {
// pantograph is down
pantograph->hvPowerWire = nullptr;
}
}
}
// legacy method, sends list of vehicles over network
void
vehicle_table::DynamicList( bool const Onlycontrolled ) const {
// odesłanie nazw pojazdów dostępnych na scenerii (nazwy, szczególnie wagonów, mogą się powtarzać!)
for( auto const *vehicle : m_items ) {
if( ( false == Onlycontrolled )
|| ( vehicle->Mechanik != nullptr ) ) {
// same nazwy pojazdów
multiplayer::WyslijString( vehicle->asName, 6 );
}
}
// informacja o końcu listy
multiplayer::WyslijString( "none", 6 );
}

196
DynObj.h
View File

@@ -149,6 +149,9 @@ class TDynamicObject { // klasa pojazdu
friend class opengl_renderer;
public:
static bool bDynamicRemove; // moved from ground
private: // położenie pojazdu w świecie oraz parametry ruchu
Math3D::vector3 vPosition; // Ra: pozycja pojazdu liczona zaraz po przesunięciu
Math3D::vector3 vCoulpler[ 2 ]; // współrzędne sprzęgów do liczenia zderzeń czołowych
@@ -158,19 +161,17 @@ private: // położenie pojazdu w świecie oraz parametry ruchu
TTrackParam tp; // parametry toru przekazywane do fizyki
TTrackFollower Axle0; // oś z przodu (od sprzęgu 0)
TTrackFollower Axle1; // oś z tyłu (od sprzęgu 1)
int iAxleFirst; // numer pierwszej osi w kierunku ruchu (oś wiążąca pojazd z torem i wyzwalająca
// eventy)
int iAxleFirst; // numer pierwszej osi w kierunku ruchu (oś wiążąca pojazd z torem i wyzwalająca eventy)
float fAxleDist; // rozstaw wózków albo osi do liczenia proporcji zacienienia
Math3D::vector3 modelRot; // obrot pudła względem świata - do przeanalizowania, czy potrzebne!!!
// bool bCameraNear; //blisko kamer są potrzebne dodatkowe obliczenia szczegółów
TDynamicObject * ABuFindNearestObject( TTrack *Track, TDynamicObject *MyPointer, int &CouplNr );
public: // parametry położenia pojazdu dostępne publicznie
public:
// parametry położenia pojazdu dostępne publicznie
std::string asTrack; // nazwa toru początkowego; wywalić?
std::string asDestination; // dokąd pojazd ma być kierowany "(stacja):(tor)"
Math3D::matrix4x4 mMatrix; // macierz przekształcenia do renderowania modeli
TMoverParameters *MoverParameters; // parametry fizyki ruchu oraz przeliczanie
// TMoverParameters *pControlled; //wskaźnik do sterowanego członu silnikowego
TDynamicObject *NextConnected; // pojazd podłączony od strony sprzęgu 1 (kabina -1)
TDynamicObject *PrevConnected; // pojazd podłączony od strony sprzęgu 0 (kabina 1)
int NextConnectedNo; // numer sprzęgu podłączonego z tyłu
@@ -180,7 +181,7 @@ public: // parametry położenia pojazdu dostępne publicznie
TPowerSource ConnectedEnginePowerSource( TDynamicObject const *Caller ) const;
public: // modele składowe pojazdu
// modele składowe pojazdu
TModel3d *mdModel; // model pudła
TModel3d *mdLoad; // model zmiennego ładunku
TModel3d *mdKabina; // model kabiny dla użytkownika; McZapkie-030303: to z train.h
@@ -199,28 +200,26 @@ public: // modele składowe pojazdu
bool SectionLightsActive { false }; // flag indicating whether section lights were set.
float fShade; // zacienienie: 0:normalnie, -1:w ciemności, +1:dodatkowe światło (brak koloru?)
private: // zmienne i metody do animacji submodeli; Ra: sprzatam animacje w pojeździe
private:
// zmienne i metody do animacji submodeli; Ra: sprzatam animacje w pojeździe
material_data m_materialdata;
public:
public:
inline
material_data const
*Material() const {
return &m_materialdata; }
// tymczasowo udostępnione do wyszukiwania drutu
int iAnimType[ ANIM_TYPES ]; // 0-osie,1-drzwi,2-obracane,3-zderzaki,4-wózki,5-pantografy,6-tłoki
private:
private:
int iAnimations; // liczba obiektów animujących
/*
TAnim *pAnimations; // obiekty animujące (zawierają wskaźnik do funkcji wykonującej animację)
*/
std::vector<TAnim> pAnimations;
TSubModel **
pAnimated; // lista animowanych submodeli (może być ich więcej niż obiektów animujących)
double dWheelAngle[3]; // kąty obrotu kół: 0=przednie toczne, 1=napędzające i wiązary, 2=tylne
// toczne
void
UpdateNone(TAnim *pAnim){}; // animacja pusta (funkcje ustawiania submodeli, gdy blisko kamery)
TSubModel ** pAnimated; // lista animowanych submodeli (może być ich więcej niż obiektów animujących)
double dWheelAngle[3]; // kąty obrotu kół: 0=przednie toczne, 1=napędzające i wiązary, 2=tylne toczne
void UpdateNone(TAnim *pAnim){}; // animacja pusta (funkcje ustawiania submodeli, gdy blisko kamery)
void UpdateAxle(TAnim *pAnim); // animacja osi
void UpdateBoogie(TAnim *pAnim); // animacja wózka
void UpdateDoorTranslate(TAnim *pAnim); // animacja drzwi - przesuw
@@ -266,8 +265,7 @@ public: // modele składowe pojazdu
TButton btCoupler1; // sprzegi
TButton btCoupler2;
TAirCoupler
btCPneumatic1; // sprzegi powietrzne //yB - zmienione z Button na AirCoupler - krzyzyki
TAirCoupler btCPneumatic1; // sprzegi powietrzne //yB - zmienione z Button na AirCoupler - krzyzyki
TAirCoupler btCPneumatic2;
TAirCoupler btCPneumatic1r; // ABu: to zeby nie bylo problemow przy laczeniu wagonow,
TAirCoupler btCPneumatic2r; // jesli beda polaczone sprzegami 1<->1 lub 0<->0
@@ -298,8 +296,6 @@ public: // modele składowe pojazdu
TButton btHeadSignals23;
TButton btMechanik1;
TButton btMechanik2;
//TSubModel *smMechanik0; // Ra: mechanik wbudowany w model jako submodel?
//TSubModel *smMechanik1; // mechanik od strony sprzęgu 1
double enginevolume; // MC: pomocnicze zeby gladziej silnik buczal
int iAxles; // McZapkie: to potem mozna skasowac i zastapic iNumAxles
@@ -323,7 +319,6 @@ public: // modele składowe pojazdu
sound* sReleaser = nullptr;
// Winger 010304
// sound* rsPanTup; //PSound sPantUp;
sound* sPantUp = nullptr;
sound* sPantDown = nullptr;
sound* rsDoorOpen = nullptr; // Ra: przeniesione z kabiny
@@ -350,8 +345,6 @@ public: // modele składowe pojazdu
int iHornWarning; // numer syreny do użycia po otrzymaniu sygnału do jazdy
bool bEnabled; // Ra: wyjechał na portal i ma być usunięty
protected:
// TTrackFollower Axle2; //dwie osie z czterech (te są protected)
// TTrackFollower Axle3; //Ra: wyłączyłem, bo kąty są liczone w Segment.cpp
int iNumAxles; // ilość osi
std::string asModel;
@@ -371,26 +364,18 @@ public: // modele składowe pojazdu
TDynamicObject * PrevC(int C);
TDynamicObject * NextC(int C);
double NextDistance(double d = -1.0);
void SetdMoveLen(double dMoveLen)
{
MoverParameters->dMoveLen = dMoveLen;
}
void ResetdMoveLen()
{
MoverParameters->dMoveLen = 0;
}
double GetdMoveLen()
{
return MoverParameters->dMoveLen;
}
void SetdMoveLen(double dMoveLen) {
MoverParameters->dMoveLen = dMoveLen; }
void ResetdMoveLen() {
MoverParameters->dMoveLen = 0; }
double GetdMoveLen() {
return MoverParameters->dMoveLen; }
int GetPneumatic(bool front, bool red);
void SetPneumatic(bool front, bool red);
std::string asName;
std::string GetName()
{
return this ? asName : std::string("");
};
std::string name() const {
return this ? asName : std::string(); };
sound* rsDiesielInc = nullptr; // youBy
sound* rscurve = nullptr; // youBy
@@ -403,7 +388,6 @@ public: // modele składowe pojazdu
TDynamicObject * ABuScanNearestObject(TTrack *Track, double ScanDir, double ScanDist,
int &CouplNr);
TDynamicObject * GetFirstDynamic(int cpl_type, int cf = 1);
// TDynamicObject* GetFirstCabDynamic(int cpl_type);
void ABuSetModelShake( Math3D::vector3 mShake);
// McZapkie-010302
@@ -435,99 +419,65 @@ public: // modele składowe pojazdu
void Move(double fDistance);
void FastMove(double fDistance);
void RenderSounds();
inline Math3D::vector3 GetPosition() const
{
return vPosition;
};
inline Math3D::vector3 HeadPosition()
{
return vCoulpler[iDirection ^ 1];
}; // pobranie współrzędnych czoła
inline Math3D::vector3 RearPosition()
{
return vCoulpler[iDirection];
}; // pobranie współrzędnych tyłu
inline Math3D::vector3 AxlePositionGet()
{
return iAxleFirst ? Axle1.pPosition : Axle0.pPosition;
};
inline Math3D::vector3 VectorFront() const
{
return vFront;
};
inline Math3D::vector3 VectorUp()
{
return vUp;
};
inline Math3D::vector3 VectorLeft() const
{
return vLeft;
};
inline double * Matrix()
{
return mMatrix.getArray();
};
inline double GetVelocity()
{
return MoverParameters->Vel;
};
inline double GetLength() const
{
return MoverParameters->Dim.L;
};
inline double GetWidth() const
{
return MoverParameters->Dim.W;
};
inline TTrack * GetTrack()
{
return (iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack());
};
// void UpdatePos();
inline Math3D::vector3 GetPosition() const {
return vPosition; };
// pobranie współrzędnych czoła
inline Math3D::vector3 HeadPosition() {
return vCoulpler[iDirection ^ 1]; };
// pobranie współrzędnych tyłu
inline Math3D::vector3 RearPosition() {
return vCoulpler[iDirection]; };
inline Math3D::vector3 AxlePositionGet() {
return iAxleFirst ? Axle1.pPosition : Axle0.pPosition; };
inline Math3D::vector3 VectorFront() const {
return vFront; };
inline Math3D::vector3 VectorUp() {
return vUp; };
inline Math3D::vector3 VectorLeft() const {
return vLeft; };
inline double * Matrix() {
return mMatrix.getArray(); };
inline double GetVelocity() {
return MoverParameters->Vel; };
inline double GetLength() const {
return MoverParameters->Dim.L; };
inline double GetWidth() const {
return MoverParameters->Dim.W; };
inline TTrack * GetTrack() {
return (iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack()); };
// McZapkie-260202
void LoadMMediaFile(std::string BaseDir, std::string TypeName, std::string ReplacableSkin);
inline double ABuGetDirection() const // ABu.
{
return (Axle1.GetTrack() == MyTrack ? Axle1.GetDirection() : Axle0.GetDirection());
};
// inline double ABuGetTranslation() //ABu.
// {//zwraca przesunięcie wózka względem Point1 toru
// return (Axle1.GetTrack()==MyTrack?Axle1.GetTranslation():Axle0.GetTranslation());
// };
inline double RaDirectionGet()
{ // zwraca kierunek pojazdu na torze z aktywną osą
return iAxleFirst ? Axle1.GetDirection() : Axle0.GetDirection();
};
inline double RaTranslationGet()
{ // zwraca przesunięcie wózka względem Point1 toru z aktywną osią
return iAxleFirst ? Axle1.GetTranslation() : Axle0.GetTranslation();
};
inline TTrack * RaTrackGet()
{ // zwraca tor z aktywną osią
return iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack();
};
inline double ABuGetDirection() const { // ABu.
return (Axle1.GetTrack() == MyTrack ? Axle1.GetDirection() : Axle0.GetDirection()); };
// zwraca kierunek pojazdu na torze z aktywną osą
inline double RaDirectionGet() {
return iAxleFirst ? Axle1.GetDirection() : Axle0.GetDirection(); };
// zwraca przesunięcie wózka względem Point1 toru z aktywną osią
inline double RaTranslationGet() {
return iAxleFirst ? Axle1.GetTranslation() : Axle0.GetTranslation(); };
// zwraca tor z aktywną osią
inline TTrack * RaTrackGet() {
return iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack(); };
void CouplersDettach(double MinDist, int MyScanDir);
void RadioStop();
void Damage(char flag);
void RaLightsSet(int head, int rear);
// void RaAxleEvent(TEvent *e);
TDynamicObject * FirstFind(int &coupler_nr, int cf = 1);
float GetEPP(); // wyliczanie sredniego cisnienia w PG
int DirectionSet(int d); // ustawienie kierunku w składzie
int DirectionGet()
{
return iDirection + iDirection - 1;
}; // odczyt kierunku w składzie
// odczyt kierunku w składzie
int DirectionGet() {
return iDirection + iDirection - 1; };
int DettachStatus(int dir);
int Dettach(int dir);
TDynamicObject * Neightbour(int &dir);
void CoupleDist();
TDynamicObject * ControlledFind();
void ParamSet(int what, int into);
int RouteWish(TTrack *tr); // zapytanie do AI, po którym segmencie skrzyżowania
// jechać
// zapytanie do AI, po którym segmencie skrzyżowania jechać
int RouteWish(TTrack *tr);
void DestinationSet(std::string to, std::string numer);
std::string TextureTest(std::string const &name);
void OverheadTrack(float o);
@@ -535,4 +485,20 @@ public: // modele składowe pojazdu
static std::string const MED_labels[ 8 ];
};
class vehicle_table : public basic_table<TDynamicObject> {
public:
// legacy method, calculates changes in simulation state over specified time
void
update( double dt, int iter );
// legacy method, checks for presence and height of traction wire for specified vehicle
void
update_traction( TDynamicObject *Vehicle );
// legacy method, sends list of vehicles over network
void
DynamicList( bool const Onlycontrolled = false ) const;
};
//---------------------------------------------------------------------------

View File

@@ -20,7 +20,10 @@ Stele, firleju, szociu, hunter, ZiomalCl, OLI_EU and others
#include <png.h>
#include <thread>
#include "World.h"
#include "simulation.h"
#include "Globals.h"
#include "Timer.h"
#include "Logs.h"
#include "keyboardinput.h"
#include "mouseinput.h"
@@ -332,16 +335,6 @@ int main(int argc, char *argv[])
BaseWindowProc = (WNDPROC)::SetWindowLongPtr( Hwnd, GWLP_WNDPROC, (LONG_PTR)WndProc );
// switch off the topmost flag
::SetWindowPos( Hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
const HANDLE icon = ::LoadImage(
::GetModuleHandle( 0 ),
MAKEINTRESOURCE( IDI_ICON1 ),
IMAGE_ICON,
::GetSystemMetrics( SM_CXSMICON ),
::GetSystemMetrics( SM_CYSMICON ),
0 );
if( icon )
::SendMessage( Hwnd, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>( icon ) );
#endif
try {
@@ -419,9 +412,11 @@ int main(int argc, char *argv[])
#ifdef _WIN32
Console::Off(); // wyłączenie konsoli (komunikacji zwrotnej)
delete pConsole;
SafeDelete( pConsole );
#endif
SafeDelete( simulation::Region );
glfwDestroyWindow(window);
glfwTerminate();

View File

@@ -17,36 +17,14 @@ http://mozilla.org/MPL/2.0/.
#include "EvLaunch.h"
#include "Globals.h"
#include "Logs.h"
#include "usefull.h"
#include "McZapkie/mctools.h"
#include "Event.h"
#include "MemCell.h"
#include "mtable.h"
#include "Timer.h"
#include "parser.h"
#include "Console.h"
using namespace Mtable;
//---------------------------------------------------------------------------
TEventLauncher::TEventLauncher()
{ // ustawienie początkowych wartości dla wszystkich zmiennych
iKey = 0;
DeltaTime = -1;
UpdatedTime = 0;
fVal1 = fVal2 = 0;
iHour = iMinute = -1; // takiego czasu nigdy nie będzie
dRadius = 0;
Event1 = Event2 = NULL;
MemCell = NULL;
iCheckMask = 0;
}
void TEventLauncher::Init()
{
}
// encodes expected key in a short, where low byte represents the actual key,
// and the high byte holds modifiers: 0x1 = shift, 0x2 = ctrl, 0x4 = alt
int vk_to_glfw_key( int const Keycode ) {
@@ -145,9 +123,9 @@ bool TEventLauncher::Load(cParser *parser)
*parser >> token;
}
return true;
};
}
bool TEventLauncher::Render()
bool TEventLauncher::check_conditions()
{ //"renderowanie" wyzwalacza
bool bCond = false;
if (iKey != 0)
@@ -199,13 +177,20 @@ bool TEventLauncher::Render()
return bCond; // sprawdzanie dRadius w Ground.cpp
}
bool TEventLauncher::IsGlobal()
{ // sprawdzenie, czy jest globalnym wyzwalaczem czasu
if (DeltaTime == 0)
if (iHour >= 0)
if (iMinute >= 0)
if (dRadius < 0.0) // bez ograniczenia zasięgu
return true;
return false;
};
// sprawdzenie, czy jest globalnym wyzwalaczem czasu
bool TEventLauncher::IsGlobal() const {
return ( ( DeltaTime == 0 )
&& ( iHour >= 0 )
&& ( iMinute >= 0 )
&& ( dRadius < 0.0 ) ); // bez ograniczenia zasięgu
}
// calculates node's bounding radius
void
TEventLauncher::radius_() {
m_area.radius = std::sqrt( dRadius );
}
//---------------------------------------------------------------------------

View File

@@ -10,32 +10,48 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include <string>
#include "Classes.h"
class TEventLauncher
{
private:
int iKey;
double DeltaTime;
double UpdatedTime;
double fVal1;
double fVal2;
std::string szText;
int iHour, iMinute; // minuta uruchomienia
public:
double dRadius;
#include "Classes.h"
#include "scenenode.h"
class TEventLauncher : public editor::basic_node {
public:
// constructor
TEventLauncher( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {}
// legacy constructor
TEventLauncher() = default;
// methods
bool Load( cParser *parser );
// checks conditions associated with the event. returns: true if the conditions are met
bool check_conditions();
bool IsGlobal() const;
// members
std::string asEvent1Name;
std::string asEvent2Name;
std::string asMemCellName;
TEvent *Event1;
TEvent *Event2;
TMemCell *MemCell;
int iCheckMask;
TEventLauncher();
void Init();
bool Load(cParser *parser);
bool Render();
bool IsGlobal();
TEvent *Event1 { nullptr };
TEvent *Event2 { nullptr };
TMemCell *MemCell { nullptr };
int iCheckMask { 0 };
double dRadius { 0.0 };
protected:
// calculates node's bounding radius
void
radius_();
private:
// members
int iKey { 0 };
double DeltaTime { -1.0 };
double UpdatedTime { 0.0 };
double fVal1 { 0.0 };
double fVal2 { 0.0 };
std::string szText;
int iHour { -1 };
int iMinute { -1 }; // minuta uruchomienia
};
//---------------------------------------------------------------------------

992
Event.cpp

File diff suppressed because it is too large Load Diff

81
Event.h
View File

@@ -12,8 +12,9 @@ http://mozilla.org/MPL/2.0/.
#include <string>
#include "dumb3d.h"
#include "Classes.h"
using namespace Math3D;
#include "Names.h"
#include "scenenode.h"
#include "EvLaunch.h"
enum TEventType {
tp_Unknown,
@@ -32,7 +33,7 @@ enum TEventType {
tp_TrackVel,
tp_Multiple,
tp_AddValues,
tp_Ignored,
// tp_Ignored, // NOTE: refactored to separate flag
tp_CopyValues,
tp_WhoIs,
tp_LogValues,
@@ -63,7 +64,8 @@ union TParam
{
void *asPointer;
TMemCell *asMemCell;
TGroundNode *nGroundNode;
editor::basic_node *asEditorNode;
glm::dvec3 const *asLocation;
TTrack *asTrack;
TAnimModel *asModel;
TAnimContainer *asAnimContainer;
@@ -86,11 +88,10 @@ class TEvent // zmienne: ev*
public:
std::string asName;
bool m_ignored { false }; // replacement for tp_ignored
bool bEnabled = false; // false gdy ma nie być dodawany do kolejki (skanowanie sygnałów)
int iQueued = 0; // ile razy dodany do kolejki
// bool bIsHistory;
TEvent *evNext = nullptr; // następny w kolejce
TEvent *evNext2 = nullptr;
TEventType Type = tp_Unknown;
double fStartTime = 0.0;
double fDelay = 0.0;
@@ -100,19 +101,79 @@ class TEvent // zmienne: ev*
std::string asNodeName; // McZapkie-100302 - dodalem zeby zapamietac nazwe toru
TEvent *evJoined = nullptr; // kolejny event z tą samą nazwą - od wersji 378
double fRandomDelay = 0.0; // zakres dodatkowego opóźnienia // standardowo nie będzie dodatkowego losowego opóźnienia
public: // metody
public:
// metody
TEvent(std::string const &m = "");
~TEvent();
void Init();
void Load(cParser *parser, vector3 *org);
void Load(cParser *parser, Math3D::vector3 const &org);
static void AddToQuery( TEvent *Event, TEvent *&Start );
std::string CommandGet();
TCommandType Command();
double ValueGet(int n);
vector3 PositionGet() const;
glm::dvec3 PositionGet() const;
bool StopCommand();
void StopCommandSent();
void Append(TEvent *e);
};
class event_manager {
public:
// destructor
~event_manager();
// methods
// adds specified event launcher to the list of global launchers
void
queue( TEventLauncher *Launcher );
// legacy method, updates event queues
void
update();
// adds provided event to the collection. returns: true on success
// TBD, TODO: return handle to the event
bool
insert( TEvent *Event );
bool
insert( TEventLauncher *Launcher ) {
return m_launchers.insert( Launcher ); }
// legacy method, returns pointer to specified event, or null
TEvent *
FindEvent( std::string const &Name );
// legacy method, inserts specified event in the event query
bool
AddToQuery( TEvent *Event, TDynamicObject *Owner );
// legacy method, executes queued events
bool
CheckQuery();
// legacy method, initializes events after deserialization from scenario file
void
InitEvents();
// legacy method, initializes event launchers after deserialization from scenario file
void
InitLaunchers();
private:
// types
using event_sequence = std::deque<TEvent *>;
using event_map = std::unordered_map<std::string, std::size_t>;
using eventlauncher_sequence = std::vector<TEventLauncher *>;
// methods
// legacy method, verifies condition for specified event
bool
EventConditon( TEvent *Event );
// members
event_sequence m_events;
/*
// NOTE: disabled until event class refactoring
event_sequence m_eventqueue;
*/
// legacy version of the above
TEvent *QueryRootEvent { nullptr };
TEvent *m_workevent { nullptr };
event_map m_eventmap;
basic_table<TEventLauncher> m_launchers;
eventlauncher_sequence m_launcherqueue;
};
//---------------------------------------------------------------------------

View File

@@ -12,15 +12,14 @@ http://mozilla.org/MPL/2.0/.
#include "Classes.h"
#include "sound.h"
typedef enum
{ // typ ruchu
enum TGaugeType {
// typ ruchu
gt_Unknown, // na razie nie znany
gt_Rotate, // obrót
gt_Move, // przesunięcie równoległe
gt_Wiper, // obrót trzech kolejnych submodeli o ten sam kąt (np. wycieraczka, drzwi
// harmonijkowe)
gt_Wiper, // obrót trzech kolejnych submodeli o ten sam kąt (np. wycieraczka, drzwi harmonijkowe)
gt_Digital // licznik cyfrowy, np. kilometrów
} TGaugeType;
};
// animowany wskaźnik, mogący przyjmować wiele stanów pośrednich
class TGauge {

View File

@@ -12,22 +12,17 @@ http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "Globals.h"
#include "usefull.h"
//#include "Mover.h"
#include "Console.h"
#include "Driver.h"
#include "Logs.h"
#include "PyInt.h"
#include "World.h"
#include "parser.h"
#include "simulation.h"
#include "Logs.h"
#include "Console.h"
#include "PyInt.h"
// namespace Global {
// parametry do użytku wewnętrznego
// double Global::tSinceStart=0;
TGround *Global::pGround = NULL;
std::string Global::AppName{ "EU07" };
std::string Global::asCurrentSceneryPath = "scenery/";
std::string Global::asCurrentTexturePath = std::string(szTexturePath);
@@ -74,12 +69,15 @@ std::vector<vector3> Global::FreeCameraInitAngle;
GLfloat Global::FogColor[] = {0.6f, 0.7f, 0.8f};
double Global::fFogStart = 1700;
double Global::fFogEnd = 2000;
float Global::Overcast{ 0.1f }; // NOTE: all this weather stuff should be moved elsewhere
int Global::DynamicLightCount = 7;
bool Global::ScaleSpecularValues = false;
float Global::Overcast { 0.1f }; // NOTE: all this weather stuff should be moved elsewhere
std::string Global::Season; // season of the year, based on simulation date
float Global::BaseDrawRange { 2500.f };
bool Global::RenderShadows { false };
opengl_light Global::DayLight;
int Global::DynamicLightCount { 3 };
bool Global::ScaleSpecularValues { true };
bool Global::BasicRenderer { false };
bool Global::RenderShadows { true };
Global::shadowtune_t Global::shadowtune = { 2048, 250.f, 250.f, 500.f };
bool Global::bRollFix = true; // czy wykonać przeliczanie przechyłki
bool Global::bJoinEvents = false; // czy grupować eventy o tych samych nazwach
@@ -118,7 +116,7 @@ bool Global::bLiveTraction = true;
float Global::AnisotropicFiltering = 8.0f; // requested level of anisotropic filtering. TODO: move it to renderer object
std::string Global::LastGLError;
GLint Global::iMaxTextureSize = 4096; // maksymalny rozmiar tekstury
bool Global::bSmoothTraction = false; // wygładzanie drutów starym sposobem
bool Global::bSmoothTraction { true }; // wygładzanie drutów starym sposobem
float Global::SplineFidelity { 1.f }; // determines segment size during conversion of splines to geometry
std::string Global::szDefaultExt = Global::szTexturesDDS; // domyślnie od DDS
int Global::iMultisampling = 2; // tryb antyaliasingu: 0=brak,1=2px,2=4px,3=8px,4=16px
@@ -172,7 +170,6 @@ Global::uart_conf_t Global::uart_conf;
//randomizacja
std::mt19937 Global::random_engine = std::mt19937(std::time(NULL));
opengl_light Global::DayLight;
Global::soundmode_t Global::soundpitchmode = Global::linear;
Global::soundmode_t Global::soundgainmode = Global::linear;
Global::soundstopmode_t Global::soundstopmode = Global::queue;
@@ -181,19 +178,6 @@ Global::soundstopmode_t Global::soundstopmode = Global::queue;
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
std::string Global::GetNextSymbol()
{ // pobranie tokenu z aktualnego parsera
std::string token;
if (pParser != nullptr)
{
pParser->getTokens();
*pParser >> token;
};
return token;
};
void Global::LoadIniFile(std::string asFileName)
{
FreeCameraInit.resize( 10 );
@@ -449,6 +433,7 @@ void Global::ConfigParse(cParser &Parser)
std::tm *localtime = std::localtime(&timenow);
Global::fMoveLight = localtime->tm_yday + 1; // numer bieżącego dnia w roku
}
Global::pWorld->compute_season( Global::fMoveLight );
}
else if( token == "dynamiclights" ) {
// number of dynamic lights in the scene
@@ -949,20 +934,6 @@ void Global::TrainDelete(TDynamicObject *d)
pWorld->TrainDelete(d);
};
TDynamicObject *Global::DynamicNearest()
{ // ustalenie pojazdu najbliższego kamerze
return pGround->DynamicNearest(pCamera->Pos);
};
TDynamicObject *Global::CouplerNearest()
{ // ustalenie pojazdu najbliższego kamerze
return pGround->CouplerNearest(pCamera->Pos);
};
bool Global::AddToQuery(TEvent *event, TDynamicObject *who)
{
return pGround->AddToQuery(event, who);
};
//---------------------------------------------------------------------------
TTranscripts::TTranscripts()

View File

@@ -150,16 +150,27 @@ private:
void Update();
};
class Global
{
private:
public:
class Global {
public:
// methods
static void LoadIniFile(std::string asFileName);
static void ConfigParse( cParser &parser );
static void InitKeys();
inline static Math3D::vector3 GetCameraPosition() { return pCameraPosition; };
static void SetCameraPosition(Math3D::vector3 pNewCameraPosition);
static void SetCameraRotation(double Yaw);
static void TrainDelete(TDynamicObject *d);
static bool DoEvents();
static std::string Bezogonkow(std::string str, bool _ = false);
static double Min0RSpeed(double vel1, double vel2);
// members
static int Keys[MaxKeys];
static bool RealisticControlMode; // controls ability to steer the vehicle from outside views
static Math3D::vector3 pCameraPosition; // pozycja kamery w świecie
static Math3D::vector3 DebugCameraPosition; // pozycja kamery w świecie
static double
pCameraRotation; // kierunek bezwzględny kamery w świecie: 0=północ, 90°=zachód (-azymut)
static double pCameraRotation; // kierunek bezwzględny kamery w świecie: 0=północ, 90°=zachód (-azymut)
static double pCameraRotationDeg; // w stopniach, dla animacji billboard
static std::vector<Math3D::vector3> FreeCameraInit; // pozycje kamery
static std::vector<Math3D::vector3> FreeCameraInitAngle;
@@ -179,27 +190,21 @@ class Global
static bool bLiveTraction;
static float fMouseXScale;
static float fMouseYScale;
static double fFogStart;
static double fFogEnd;
static TGround *pGround;
static std::string szDefaultExt;
static std::string SceneryFile;
static std::string AppName;
static std::string asCurrentSceneryPath;
static std::string asCurrentTexturePath;
static std::string asCurrentDynamicPath;
// McZapkie-170602: zewnetrzna definicja pojazdu uzytkownika
static std::string asHumanCtrlVehicle;
static void LoadIniFile(std::string asFileName);
static void InitKeys();
inline static Math3D::vector3 GetCameraPosition() { return pCameraPosition; };
static void SetCameraPosition(Math3D::vector3 pNewCameraPosition);
static void SetCameraRotation(double Yaw);
static int iWriteLogEnabled; // maska bitowa: 1-zapis do pliku, 2-okienko
static bool MultipleLogs;
// McZapkie-221002: definicja swiatla dziennego
static GLfloat FogColor[];
// McZapkie-170602: zewnetrzna definicja pojazdu uzytkownika
static std::string asHumanCtrlVehicle;
// world environment
static float Overcast;
static double fFogStart;
static double fFogEnd;
static std::string Season; // season of the year, based on simulation date
// TODO: put these things in the renderer
static float BaseDrawRange;
@@ -221,6 +226,9 @@ class Global
static float FieldOfView; // vertical field of view for the camera. TODO: move it to the renderer
static GLint iMaxTextureSize; // maksymalny rozmiar tekstury
static int iMultisampling; // tryb antyaliasingu: 0=brak,1=2px,2=4px,3=8px,4=16px
static bool bSmoothTraction; // wygładzanie drutów
static float SplineFidelity; // determines segment size during conversion of splines to geometry
static GLfloat FogColor[];
static bool FullPhysics; // full calculations performed for each simulation step
static int iSlowMotion;
@@ -247,8 +255,6 @@ class Global
static int iScreenMode[12]; // numer ekranu wyświetlacza tekstowego
static double fMoveLight; // numer dnia w roku albo -1
static bool FakeLight; // toggle between fixed and dynamic daylight
static bool bSmoothTraction; // wygładzanie drutów
static float SplineFidelity; // determines segment size during conversion of splines to geometry
static double fTimeSpeed; // przyspieszenie czasu, zmienna do testów
static double fTimeAngleDeg; // godzina w postaci kąta
static float fClockAngleDeg[6]; // kąty obrotu cylindrów dla zegara cyfrowego
@@ -324,16 +330,6 @@ class Global
};
static uart_conf_t uart_conf;
// metody
static void TrainDelete(TDynamicObject *d);
static void ConfigParse(cParser &parser);
static std::string GetNextSymbol();
static TDynamicObject * DynamicNearest();
static TDynamicObject * CouplerNearest();
static bool AddToQuery(TEvent *event, TDynamicObject *who);
static std::string Bezogonkow(std::string str, bool _ = false);
static double Min0RSpeed(double vel1, double vel2);
static opengl_light DayLight;
enum soundmode_t

4360
Ground.cpp

File diff suppressed because it is too large Load Diff

352
Ground.h
View File

@@ -1,352 +0,0 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <string>
#include "GL/glew.h"
#include "openglgeometrybank.h"
#include "VBO.h"
#include "Classes.h"
#include "ResourceManager.h"
#include "material.h"
#include "dumb3d.h"
#include "Float3d.h"
#include "Names.h"
#include "lightarray.h"
#include "lua.h"
typedef int TGroundNodeType;
// Ra: zmniejszone liczby, aby zrobić tabelkę i zoptymalizować wyszukiwanie
const int TP_MODEL = 10;
/*
const int TP_MESH = 11; // Ra: specjalny obiekt grupujący siatki dla tekstury
const int TP_DUMMYTRACK = 12; // Ra: zdublowanie obiektu toru dla rozdzielenia tekstur
*/
const int TP_TERRAIN = 13; // Ra: specjalny model dla terenu
const int TP_DYNAMIC = 14;
const int TP_SOUND = 15;
const int TP_TRACK = 16;
/*
const int TP_GEOMETRY=17;
*/
const int TP_MEMCELL = 18;
const int TP_EVLAUNCH = 19; // MC
const int TP_TRACTION = 20;
const int TP_TRACTIONPOWERSOURCE = 21; // MC
/*
const int TP_ISOLATED=22; //Ra
*/
const int TP_SUBMODEL = 22; // Ra: submodele terenu
const int TP_LAST = 25; // rozmiar tablicy
struct DaneRozkaz
{ // struktura komunikacji z EU07.EXE
int iSygn; // sygnatura 'EU07'
int iComm; // rozkaz/status (kod ramki)
union
{
float fPar[62];
int iPar[62];
char cString[248]; // upakowane stringi
};
};
struct DaneRozkaz2
{ // struktura komunikacji z EU07.EXE
int iSygn; // sygnatura 'EU07'
int iComm; // rozkaz/status (kod ramki)
union
{
float fPar[496];
int iPar[496];
char cString[1984]; // upakowane stringi
};
};
struct TGroundVertex
{
glm::dvec3 position;
glm::vec3 normal;
glm::vec2 texture;
void HalfSet(const TGroundVertex &v1, const TGroundVertex &v2)
{ // wyliczenie współrzędnych i mapowania punktu na środku odcinka v1<->v2
interpolate_( v1, v2, 0.5 );
}
void SetByX(const TGroundVertex &v1, const TGroundVertex &v2, double x)
{ // wyliczenie współrzędnych i mapowania punktu na odcinku v1<->v2
interpolate_( v1, v2, ( x - v1.position.x ) / ( v2.position.x - v1.position.x ) );
}
void SetByZ(const TGroundVertex &v1, const TGroundVertex &v2, double z)
{ // wyliczenie współrzędnych i mapowania punktu na odcinku v1<->v2
interpolate_( v1, v2, ( z - v1.position.z ) / ( v2.position.z - v1.position.z ) );
}
void interpolate_( const TGroundVertex &v1, const TGroundVertex &v2, double factor ) {
position = interpolate( v1.position, v2.position, factor );
normal = interpolate( v1.normal, v2.normal, static_cast<float>( factor ) );
texture = interpolate( v1.texture, v2.texture, static_cast<float>( factor ) );
}
};
// ground node holding single, unique piece of 3d geometry. TBD, TODO: unify this with basic 3d model node
struct piece_node {
std::vector<TGroundVertex> vertices;
geometry_handle geometry { 0,0 }; // geometry prepared for drawing
};
// obiekt scenerii
class TGroundNode {
friend class opengl_renderer;
public:
TGroundNode *nNext; // lista wszystkich w scenerii, ostatni na początku
TGroundNode *nNext2; // lista obiektów w sektorze
TGroundNode *nNext3; // lista obiektów renderowanych we wspólnym cyklu
std::string asName; // nazwa (nie zawsze ma znaczenie)
TGroundNodeType iType; // typ obiektu
union
{ // Ra: wskażniki zależne od typu - zrobić klasy dziedziczone zamiast
void *Pointer; // do przypisywania NULL
TSubModel *smTerrain; // modele terenu (kwadratow kilometrowych)
TAnimModel *Model; // model z animacjami
TDynamicObject *DynamicObject; // pojazd
piece_node *Piece; // non-instanced piece of geometry
TTrack *pTrack; // trajektoria ruchu
TMemCell *MemCell; // komórka pamięci
TEventLauncher *EvLaunch; // wyzwalacz zdarzeń
TTraction *hvTraction; // drut zasilający
TTractionPowerSource *psTractionPowerSource; // zasilanie drutu (zaniedbane w sceneriach)
sound *tsStaticSound; // dźwięk przestrzenny
TGroundNode *nNode; // obiekt renderujący grupowo ma tu wskaźnik na listę obiektów
};
Math3D::vector3 pCenter; // współrzędne środka do przydzielenia sektora
float m_radius { 0.0f }; // bounding radius of geometry stored in the node. TODO: reuse bounding_area struct for radius and center
glm::dvec3 m_rootposition; // position of the ground (sub)rectangle holding the node, in the 3d world
// visualization-related data
// TODO: wrap these in a struct, when cleaning objects up
double fSquareMinRadius; // kwadrat widoczności od
double fSquareRadius; // kwadrat widoczności do
union
{
int iNumVerts; // dla trójkątów
int iNumPts; // dla linii
int iCount; // dla terenu
};
int iFlags; // tryb przezroczystości: 0x10-nieprz.,0x20-przezroczysty,0x30-mieszany
material_handle m_material; // główna (jedna) tekstura obiektu
glm::vec3
Ambient{ 1.0f, 1.0f, 1.0f },
Diffuse{ 1.0f, 1.0f, 1.0f },
Specular{ 1.0f, 1.0f, 1.0f }; // oświetlenie
double fLineThickness; // McZapkie-120702: grubosc linii
bool bVisible;
TGroundNode();
TGroundNode(TGroundNodeType t);
~TGroundNode();
void InitNormals();
void RenderHidden(); // obsługa dźwięków i wyzwalaczy zdarzeń
};
struct bounding_area {
glm::vec3 center; // mid point of the rectangle
float radius { 0.0f }; // radius of the bounding sphere
};
class TSubRect : /*public Resource,*/ public CMesh
{ // sektor składowy kwadratu kilometrowego
public:
bounding_area m_area;
unsigned int m_framestamp { 0 }; // id of last rendered gfx frame
int iTracks = 0; // ilość torów w (tTracks)
TTrack **tTracks = nullptr; // tory do renderowania pojazdów
protected:
TTrack *tTrackAnim = nullptr; // obiekty do przeliczenia animacji
public:
TGroundNode *nRootNode = nullptr; // wszystkie obiekty w sektorze, z wyjątkiem renderujących i pojazdów (nNext2)
TGroundNode *nRenderHidden = nullptr; // lista obiektów niewidocznych, "renderowanych" również z tyłu (nNext3)
TGroundNode *nRenderRect = nullptr; // z poziomu sektora - nieprzezroczyste (nNext3)
TGroundNode *nRenderRectAlpha = nullptr; // z poziomu sektora - przezroczyste (nNext3)
TGroundNode *nRenderWires = nullptr; // z poziomu sektora - druty i inne linie (nNext3)
TGroundNode *nRender = nullptr; // indywidualnie - nieprzezroczyste (nNext3)
TGroundNode *nRenderMixed = nullptr; // indywidualnie - nieprzezroczyste i przezroczyste (nNext3)
TGroundNode *nRenderAlpha = nullptr; // indywidualnie - przezroczyste (nNext3)
#ifdef EU07_SCENERY_EDITOR
std::deque< TGroundNode* > m_memcells; // collection of memcells present in the sector
#endif
int iNodeCount = 0; // licznik obiektów, do pomijania pustych sektorów
public:
void LoadNodes(); // utworzenie VBO sektora
public:
virtual ~TSubRect();
virtual void NodeAdd(TGroundNode *Node); // dodanie obiektu do sektora na etapie rozdzielania na sektory
void Sort(); // optymalizacja obiektów w sektorze (sortowanie wg tekstur)
TTrack * FindTrack(vector3 *Point, int &iConnection, TTrack *Exclude);
TTraction * FindTraction(glm::dvec3 const &Point, int &iConnection, TTraction *Exclude);
bool RaTrackAnimAdd(TTrack *t); // zgłoszenie toru do animacji
void RaAnimate( unsigned int const Framestamp ); // przeliczenie animacji torów
void RenderSounds(); // dźwięki pojazdów z niewidocznych sektorów
};
// Ra: trzeba sprawdzić wydajność siatki
const int iNumSubRects = 5; // na ile dzielimy kilometr
const int iNumRects = 500;
const int iTotalNumSubRects = iNumRects * iNumSubRects;
const double fHalfTotalNumSubRects = iTotalNumSubRects / 2.0;
const double fSubRectSize = 1000.0 / iNumSubRects;
const double fRectSize = fSubRectSize * iNumSubRects;
class TGroundRect : public TSubRect
{ // kwadrat kilometrowy
// obiekty o niewielkiej ilości wierzchołków będą renderowane stąd
// Ra: 2012-02 doszły submodele terenu
friend class opengl_renderer;
private:
TSubRect *pSubRects { nullptr };
void Init();
public:
virtual ~TGroundRect();
// pobranie wskaźnika do małego kwadratu, utworzenie jeśli trzeba
TSubRect * SafeGetSubRect(int iCol, int iRow) {
if( !pSubRects ) {
// utworzenie małych kwadratów
Init();
}
return pSubRects + iRow * iNumSubRects + iCol; // zwrócenie właściwego
};
// pobranie wskaźnika do małego kwadratu, bez tworzenia jeśli nie ma
TSubRect *FastGetSubRect(int iCol, int iRow) const {
return (
pSubRects ?
pSubRects + iRow * iNumSubRects + iCol :
nullptr); };
void NodeAdd( TGroundNode *Node ); // dodanie obiektu do sektora na etapie rozdzielania na sektory
// compares two provided nodes, returns true if their content can be merged
bool mergeable( TGroundNode const &Left, TGroundNode const &Right );
// optymalizacja obiektów w sektorach
void Optimize() {
if( pSubRects ) {
for( int i = iNumSubRects * iNumSubRects - 1; i >= 0; --i ) {
// optymalizacja obiektów w sektorach
pSubRects[ i ].Sort(); } } };
TGroundNode *nTerrain { nullptr }; // model terenu z E3D - użyć nRootMesh?
};
class TGround
{
friend class opengl_renderer;
TGroundRect Rects[ iNumRects ][ iNumRects ]; // mapa kwadratów kilometrowych
TSubRect srGlobal; // zawiera obiekty globalne (na razie wyzwalacze czasowe)
TGroundNode *nRootDynamic = nullptr; // lista pojazdów
TGroundNode *nRootOfType[ TP_LAST ]; // tablica grupująca obiekty, przyspiesza szukanie
TEvent *RootEvent = nullptr; // lista zdarzeń
TEvent *QueryRootEvent = nullptr,
*tmpEvent = nullptr;
typedef std::unordered_map<std::string, TEvent *> event_map;
event_map m_eventmap;
TNames<TGroundNode *> m_trackmap;
light_array m_lights; // collection of dynamic light sources present in the scene
lua m_lua;
vector3 pOrigin;
vector3 aRotate;
bool bInitDone = false;
private: // metody prywatne
bool EventConditon(TEvent *e);
public:
bool bDynamicRemove = false; // czy uruchomić procedurę usuwania pojazdów
TGround();
~TGround();
void Free();
bool Init( std::string File );
void FirstInit();
void InitTracks();
void InitTraction();
bool InitEvents();
bool InitLaunchers();
TTrack * FindTrack(vector3 Point, int &iConnection, TGroundNode *Exclude);
TTraction * FindTraction(glm::dvec3 const &Point, int &iConnection, TGroundNode *Exclude);
TTraction * TractionNearestFind(glm::dvec3 &p, int dir, TGroundNode *n);
TGroundNode * AddGroundNode(cParser *parser);
void UpdatePhys(double dt, int iter); // aktualizacja fizyki stałym krokiem
bool Update(double dt, int iter); // aktualizacja przesunięć zgodna z FPS
void Update_Lights(); // updates scene lights array
void Update_Hidden(); // updates invisible elements of the scene
bool AddToQuery(TEvent *Event, TDynamicObject *Node);
bool GetTraction(TDynamicObject *model);
bool CheckQuery();
TGroundNode * DynamicFindAny(std::string const &Name);
TGroundNode * DynamicFind(std::string const &Name);
void DynamicList(bool all = false);
TGroundNode * FindGroundNode(std::string asNameToFind, TGroundNodeType iNodeType);
TGroundRect * GetRect( double x, double z );
TSubRect * GetSubRect( int iCol, int iRow );
inline
TSubRect * GetSubRect(double x, double z) {
return GetSubRect(GetColFromX(x), GetRowFromZ(z)); };
TSubRect * FastGetSubRect( int iCol, int iRow );
inline
TSubRect * FastGetSubRect( double x, double z ) {
return FastGetSubRect( GetColFromX( x ), GetRowFromZ( z ) ); };
inline
int GetRowFromZ(double z) {
return (int)(z / fSubRectSize + fHalfTotalNumSubRects); };
inline
int GetColFromX(double x) {
return (int)(x / fSubRectSize + fHalfTotalNumSubRects); };
TEvent * FindEvent(const std::string &asEventName);
TEvent * FindEventScan(const std::string &asEventName);
void TrackJoin(TGroundNode *Current);
private:
// convert tp_terrain model to a series of triangle nodes
void convert_terrain( TGroundNode const *Terrain );
void convert_terrain( TSubModel const *Submodel );
void RaTriangleDivider(TGroundNode *node);
#ifdef _WIN32
void Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam);
#endif
public:
void WyslijEvent(const std::string &e, const std::string &d);
void WyslijString(const std::string &t, int n);
void WyslijWolny(const std::string &t);
void WyslijNamiary(TGroundNode *t);
void WyslijParam(int nr, int fl);
void WyslijUszkodzenia(const std::string &t, char fl);
void WyslijObsadzone(); // -> skladanie wielu pojazdow
void RadioStop(vector3 pPosition);
TDynamicObject * DynamicNearest(vector3 pPosition, double distance = 20.0,
bool mech = false);
TDynamicObject * CouplerNearest(vector3 pPosition, double distance = 20.0,
bool mech = false);
void DynamicRemove(TDynamicObject *dyn);
void TerrainRead(std::string const &f);
void TerrainWrite();
void TrackBusyList();
void IsolatedBusyList();
void IsolatedBusy(const std::string t);
void Silence(vector3 gdzie);
void add_event(TEvent *event);
};
//---------------------------------------------------------------------------

View File

@@ -706,6 +706,7 @@ public:
headlight_upper = 0x04,
headlight_right = 0x10,
redmarker_right = 0x20,
rearendsignals = 0x40
};
int ScndInMain{ 0 }; /*zaleznosc bocznika od nastawnika*/
bool MBrake = false; /*Czy jest hamulec reczny*/
@@ -778,6 +779,7 @@ public:
double Ftmax = 0.0;
/*- dla lokomotyw z silnikami indukcyjnymi -*/
double eimc[26];
bool EIMCLogForce; //
static std::vector<std::string> const eimc_labels;
/*-dla wagonow*/
double MaxLoad = 0.0; /*masa w T lub ilosc w sztukach - ladownosc*/
@@ -820,6 +822,7 @@ public:
double AccN = 0.0; //przyspieszenie normalne w [m/s^2]
double AccV = 0.0;
double nrot = 0.0;
double WheelFlat = 0.0;
/*! rotacja kol [obr/s]*/
double EnginePower = 0.0; /*! chwilowa moc silnikow*/
double dL = 0.0; double Fb = 0.0; double Ff = 0.0; /*przesuniecie, sila hamowania i tarcia*/

View File

@@ -3455,7 +3455,7 @@ void TMoverParameters::UpdatePipePressure(double dt)
temp = 0.0; // odetnij
else
temp = 1.0; // połącz
Pipe->Flow(temp * Hamulec->GetPF(temp * PipePress, dt, Vel) + GetDVc(dt));
Pipe->Flow( temp * Hamulec->GetPF( temp * PipePress, dt, Vel ) + GetDVc( dt ) );
if (ASBType == 128)
Hamulec->ASB(int(SlippingWheels));
@@ -3751,14 +3751,23 @@ void TMoverParameters::ComputeTotalForce(double dt, double dt1, bool FullVer)
Sign(nrot * M_PI * WheelDiameter - V) *
Adhesive(RunningTrack.friction) * TotalMassxg,
dt, nrot);
Fwheels = Sign(nrot * M_PI * WheelDiameter - V) * TotalMassxg * Adhesive(RunningTrack.friction);
Fwheels = Sign(temp_nrot * M_PI * WheelDiameter - V) * TotalMassxg * Adhesive(RunningTrack.friction);
if (Fwheels*Sign(V)>0)
{
FTrain = Fwheels - Fb;
FTrain = Fwheels + Fb*Sign(V);
}
else if (FTrain*Sign(V)>0)
{
Fb = FTrain*Sign(V) - Fwheels*Sign(V);
}
else
{
Fb = FTrain - Fwheels;
Fb = -Fwheels*Sign(V);
FTrain = 0;
}
if (nrot < 0.1)
{
WheelFlat = sqrt(sqr(WheelFlat) + abs(Fwheels) / NAxles*Vel*0.000002);
}
if (Sign(nrot * M_PI * WheelDiameter - V)*Sign(temp_nrot * M_PI * WheelDiameter - V) < 0)
{
@@ -4648,7 +4657,7 @@ double TMoverParameters::TractionForce(double dt)
{
PosRatio = -Sign(V) * DirAbsolute * eimv[eimv_Fr] /
(eimc[eimc_p_Fh] *
Max0R(dtrans / MaxBrakePress[0], AnPos) /*dizel_fill*/);
Max0R(Max0R(dtrans,0.01) / MaxBrakePress[0], AnPos) /*dizel_fill*/);
}
else
PosRatio = 0;
@@ -4699,11 +4708,11 @@ double TMoverParameters::TractionForce(double dt)
if( ( SlippingWheels ) ) {
PosRatio = 0;
tmp = 9;
Sandbox( true, range::local );
Sandbox( true, range::unit );
} // przeciwposlizg
else {
// switch sandbox off
Sandbox( false, range::local );
Sandbox( false, range::unit );
}
dizel_fill += Max0R(Min0R(PosRatio - dizel_fill, 0.1), -0.1) * 2 *
@@ -4740,6 +4749,13 @@ double TMoverParameters::TractionForce(double dt)
-Sign(V) * (DirAbsolute)*std::min(
eimc[eimc_p_Ph] * 3.6 / (Vel != 0.0 ? Vel : 0.001),
std::min(-eimc[eimc_p_Fh] * dizel_fill, eimv[eimv_FMAXMAX]));
double pr = dizel_fill;
if (EIMCLogForce)
pr = -log(1 - 4 * pr) / log(5);
eimv[eimv_Fr] =
-Sign(V) * (DirAbsolute)*std::min(
eimc[eimc_p_Ph] * 3.6 / (Vel != 0.0 ? Vel : 0.001),
std::min(-eimc[eimc_p_Fh] * pr, eimv[eimv_FMAXMAX]));
//*Min0R(1,(Vel-eimc[eimc_p_Vh0])/(eimc[eimc_p_Vh1]-eimc[eimc_p_Vh0]))
}
else
@@ -4751,9 +4767,13 @@ double TMoverParameters::TractionForce(double dt)
eimv[eimv_Fmax] = eimv[eimv_Fful] * dizel_fill;
// else
// eimv[eimv_Fmax]:=Min0R(eimc[eimc_p_F0]*dizel_fill,eimv[eimv_Fful]);
double pr = dizel_fill;
if (EIMCLogForce)
pr = log(1 + 4 * pr) / log(5);
eimv[eimv_Fr] = eimv[eimv_Fful] * pr;
}
eimv[eimv_ks] = eimv[eimv_Fmax] / eimv[eimv_FMAXMAX];
eimv[eimv_ks] = eimv[eimv_Fr] / eimv[eimv_FMAXMAX];
eimv[eimv_df] = eimv[eimv_ks] * eimc[eimc_s_dfmax];
eimv[eimv_fp] = DirAbsolute * enrot * eimc[eimc_s_p] +
eimv[eimv_df]; // do przemyslenia dzialanie pp z tmpV
@@ -4924,7 +4944,7 @@ double TMoverParameters::v2n(void)
n = V / (M_PI * WheelDiameter); // predkosc obrotowa wynikajaca z liniowej [obr/s]
deltan = n - nrot; //"pochodna" prędkości obrotowej
if (SlippingWheels)
if (std::abs(deltan) < 0.01)
if (std::abs(deltan) < 0.001)
SlippingWheels = false; // wygaszenie poslizgu
if (SlippingWheels) // nie ma zwiazku z predkoscia liniowa V
{ // McZapkie-221103: uszkodzenia kol podczas poslizgu
@@ -6923,7 +6943,7 @@ void TMoverParameters::LoadFIZ_Brake( std::string const &line ) {
}
if( true == extract_value( AirLeakRate, "AirLeakRate", line, "" ) ) {
// the parameter is provided in form of a multiplier, where 1.0 means the default rate of 0.001
// the parameter is provided in form of a multiplier, where 1.0 means the default rate of 0.01
AirLeakRate *= 0.01;
}
}
@@ -7149,6 +7169,7 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) {
if( asb == "Manual" ) { ASBType = 1; }
else if( asb == "Automatic" ) { ASBType = 2; }
else if (asb == "Yes") { ASBType = 128; }
}
else {
@@ -7397,6 +7418,7 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) {
extract_value( eimc[ eimc_p_Imax ], "Imax", Input, "" );
extract_value( eimc[ eimc_p_abed ], "abed", Input, "" );
extract_value( eimc[ eimc_p_eped ], "edep", Input, "" );
EIMCLogForce = ( extract_value( "eimclf", Input ) == "Yes" );
Flat = ( extract_value( "Flat", Input ) == "1" );
@@ -7826,10 +7848,13 @@ bool TMoverParameters::CheckLocomotiveParameters(bool ReadyFlag, int Dir)
BrakeCtrlPos = static_cast<int>( Handle->GetPos( bh_NP ) );
else
BrakeCtrlPos = static_cast<int>( Handle->GetPos( bh_RP ) );
/*
// NOTE: disabled and left up to the driver, if there's any
MainSwitch( false );
PantFront( true );
PantRear( true );
MainSwitch( true );
*/
ActiveDir = 0; // Dir; //nastawnik kierunkowy - musi być ustawiane osobno!
DirAbsolute = ActiveDir * CabNo; // kierunek jazdy względem sprzęgów
LimPipePress = CntrlPipePress;

View File

@@ -435,8 +435,6 @@ void TNESt3::ForceEmptiness()
Miedzypoj->CreatePress(0);
CntrlRes->CreatePress(0);
BrakeStatus = 0;
ValveRes->Act();
BrakeRes->Act();
Miedzypoj->Act();

View File

@@ -413,6 +413,7 @@ void TBrake::ForceEmptiness()
{
ValveRes->CreatePress(0);
BrakeRes->CreatePress(0);
ValveRes->Act();
BrakeRes->Act();
}
@@ -757,6 +758,17 @@ double TESt::GetCRP()
return CntrlRes->P();
}
void TESt::ForceEmptiness() {
ValveRes->CreatePress( 0 );
BrakeRes->CreatePress( 0 );
CntrlRes->CreatePress( 0 );
ValveRes->Act();
BrakeRes->Act();
CntrlRes->Act();
}
//---EP2---
void TEStEP2::Init( double const PP, double const HPP, double const LPP, double const BP, int const BDF )
@@ -1462,9 +1474,15 @@ double TEStED::GetPF( double const PP, double const dt, double const Vel )
// powtarzacz — podwojny zawor zwrotny
temp = Max0R(LoadC * BCP / temp * Min0R(Max0R(1 - EDFlag, 0), 1), LBP);
double speed = 1;
if ((ASBP < 0.1) && ((BrakeStatus & b_asb) == b_asb))
{
temp = 0;
speed = 3;
}
if ((BrakeCyl->P() > temp))
dv = -PFVd(BrakeCyl->P(), 0, 0.02 * SizeBC, temp) * dt;
dv = -PFVd(BrakeCyl->P(), 0, 0.02 * SizeBC * speed, temp) * dt;
else if ((BrakeCyl->P() < temp))
dv = PFVa(BVP, BrakeCyl->P(), 0.02 * SizeBC, temp) * dt;
else
@@ -1721,6 +1739,17 @@ double TCV1::GetCRP()
return CntrlRes->P();
}
void TCV1::ForceEmptiness() {
ValveRes->CreatePress( 0 );
BrakeRes->CreatePress( 0 );
CntrlRes->CreatePress( 0 );
ValveRes->Act();
BrakeRes->Act();
CntrlRes->Act();
}
//---CV1-L-TR---
void TCV1L_TR::SetLBP( double const P )
@@ -1995,7 +2024,8 @@ double TKE::GetPF( double const PP, double const dt, double const Vel )
// luzowanie CH
// temp:=Max0R(BCP,LBP);
IMP = Max0R(IMP / temp, Max0R(LBP, ASBP * int((BrakeStatus & b_asb) == b_asb)));
if ((ASBP < 0.1) && ((BrakeStatus & b_asb) == b_asb))
IMP = 0;
// luzowanie CH
if ((BCP > IMP + 0.005) || (Max0R(ImplsRes->P(), 8 * LBP) < 0.25))
dv = PFVd(BCP, 0, 0.05, IMP) * dt;
@@ -2090,6 +2120,21 @@ void TKE::SetLBP( double const P )
LBP = P;
}
void TKE::ForceEmptiness() {
ValveRes->CreatePress( 0 );
BrakeRes->CreatePress( 0 );
CntrlRes->CreatePress( 0 );
ImplsRes->CreatePress( 0 );
Brak2Res->CreatePress( 0 );
ValveRes->Act();
BrakeRes->Act();
CntrlRes->Act();
ImplsRes->Act();
Brak2Res->Act();
}
//---KRANY---
double TDriverHandle::GetPF(double const i_bcp, double PP, double HP, double dt, double ep)

View File

@@ -260,6 +260,7 @@ class TESt : public TBrake {
void CheckReleaser(double dt); //odluzniacz
double CVs(double BP); //napelniacz sterujacego
double BVs(double BCP); //napelniacz pomocniczego
void ForceEmptiness() /*override*/; // wymuszenie bycia pustym
inline TESt(double i_mbp, double i_bcr, double i_bcd, double i_brc, int i_bcn, int i_BD, int i_mat, int i_ba, int i_nbpa) :
TBrake( i_mbp, i_bcr, i_bcd, i_brc, i_bcn, i_BD, i_mat, i_ba, i_nbpa)
@@ -410,6 +411,7 @@ public:
void CheckState( double const BCP, double &dV1 );
double CVs( double const BP );
double BVs( double const BCP );
void ForceEmptiness() /*override*/; // wymuszenie bycia pustym
inline TCV1(double i_mbp, double i_bcr, double i_bcd, double i_brc, int i_bcn, int i_BD, int i_mat, int i_ba, int i_nbpa) :
TBrake( i_mbp, i_bcr, i_bcd, i_brc, i_bcn, i_BD, i_mat, i_ba, i_nbpa)
@@ -482,6 +484,7 @@ class TKE : public TBrake { //Knorr Einheitsbauart — jeden do wszystkiego
void PLC( double const mass ); //wspolczynnik cisnienia przystawki wazacej
void SetLP( double const TM, double const LM, double const TBP ); //parametry przystawki wazacej
void SetLBP( double const P ); //cisnienie z hamulca pomocniczego
void ForceEmptiness() /*override*/; // wymuszenie bycia pustym
inline TKE(double i_mbp, double i_bcr, double i_bcd, double i_brc, int i_bcn, int i_BD, int i_mat, int i_ba, int i_nbpa) :
TBrake( i_mbp, i_bcr, i_bcd, i_brc, i_bcn, i_BD, i_mat, i_ba, i_nbpa)

View File

@@ -16,26 +16,23 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "MemCell.h"
#include "Globals.h"
#include "simulation.h"
#include "Logs.h"
#include "usefull.h"
#include "Driver.h"
#include "Event.h"
#include "parser.h"
//---------------------------------------------------------------------------
// legacy constructor
TMemCell::TMemCell(vector3 *p)
{
fValue1 = fValue2 = 0;
vPosition =
p ? *p : vector3(0, 0, 0); // ustawienie współrzędnych, bo do TGroundNode nie ma dostępu
location(
p ? *p : vector3() ); // ustawienie współrzędnych, bo do TGroundNode nie ma dostępu
bCommand = false; // komenda wysłana
OnSent = NULL;
}
void TMemCell::Init()
{
}
TMemCell::TMemCell( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {}
void TMemCell::UpdateValues( std::string const &szNewText, double const fNewValue1, double const fNewValue2, int const CheckMask )
{
if (CheckMask & update_memadd)
@@ -103,10 +100,12 @@ TCommandType TMemCell::CommandCheck()
bool TMemCell::Load(cParser *parser)
{
std::string token;
parser->getTokens(1, false); // case sensitive
*parser >> szText;
parser->getTokens( 2, false );
parser->getTokens( 6, false );
*parser
>> m_area.center.x
>> m_area.center.y
>> m_area.center.z
>> szText
>> fValue1
>> fValue2;
parser->getTokens();
@@ -121,7 +120,7 @@ bool TMemCell::Load(cParser *parser)
return true;
}
void TMemCell::PutCommand(TController *Mech, vector3 *Loc)
void TMemCell::PutCommand( TController *Mech, glm::dvec3 const *Loc )
{ // wysłanie zawartości komórki do AI
if (Mech)
Mech->PutCommand(szText, fValue1, fValue2, Loc);
@@ -150,11 +149,6 @@ bool TMemCell::Compare( std::string const &szTestText, double const fTestValue1,
&& ( !TestFlag( CheckMask, conditional_memval2 ) || ( fValue2 == fTestValue2 ) ) );
};
bool TMemCell::Render()
{
return true;
}
bool TMemCell::IsVelocity()
{ // sprawdzenie, czy event odczytu tej komórki ma być do skanowania, czy do kolejkowania
if (eCommand == cm_SetVelocity)
@@ -169,11 +163,38 @@ void TMemCell::StopCommandSent()
if (!bCommand)
return;
bCommand = false;
if (OnSent) // jeśli jest event
Global::AddToQuery(OnSent, NULL);
if( OnSent ) {
// jeśli jest event
simulation::Events.AddToQuery( OnSent, nullptr );
}
};
void TMemCell::AssignEvents(TEvent *e)
{ // powiązanie eventu
OnSent = e;
};
// legacy method, initializes traction after deserialization from scenario file
void
memory_table::InitCells() {
for( auto *cell : m_items ) {
// Ra: eventy komórek pamięci, wykonywane po wysłaniu komendy do zatrzymanego pojazdu
cell->AssignEvents( simulation::Events.FindEvent( cell->name() + ":sent" ) );
}
}
// legacy method, sends content of all cells to the log
void
memory_table::log_all() {
for( auto *cell : m_items ) {
WriteLog( "Memcell \"" + cell->name() + "\": ["
+ cell->Text() + "] ["
+ to_string( cell->Value1(), 2 ) + "] ["
+ to_string( cell->Value2(), 2 ) + "]" );
}
}

View File

@@ -10,52 +10,69 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include "Classes.h"
#include "scenenode.h"
#include "dumb3d.h"
#include "Names.h"
class TMemCell
{
private:
Math3D::vector3 vPosition;
std::string szText;
double fValue1;
double fValue2;
TCommandType eCommand;
bool bCommand; // czy zawiera komendę dla zatrzymanego AI
TEvent *OnSent; // event dodawany do kolejki po wysłaniu komendy zatrzymującej skład
public:
class TMemCell : public editor::basic_node {
public:
std::string asTrackName; // McZapkie-100302 - zeby nazwe toru na ktory jest Putcommand wysylane pamietac
TMemCell( scene::node_data const &Nodedata );
// legacy constructor
TMemCell( Math3D::vector3 *p );
void Init();
void UpdateValues( std::string const &szNewText, double const fNewValue1, double const fNewValue2, int const CheckMask );
bool Load(cParser *parser);
void PutCommand( TController *Mech, Math3D::vector3 *Loc );
bool Compare( std::string const &szTestText, double const fTestValue1, double const fTestValue2, int const CheckMask );
bool Render();
inline std::string const &Text() { return szText; }
inline double Value1()
{
return fValue1;
};
inline double Value2()
{
return fValue2;
};
inline Math3D::vector3 Position()
{
return vPosition;
};
inline TCommandType Command()
{
return eCommand;
};
inline bool StopCommand()
{
return bCommand;
};
void
UpdateValues( std::string const &szNewText, double const fNewValue1, double const fNewValue2, int const CheckMask );
bool
Load(cParser *parser);
void
PutCommand( TController *Mech, glm::dvec3 const *Loc );
bool
Compare( std::string const &szTestText, double const fTestValue1, double const fTestValue2, int const CheckMask );
std::string const &
Text() const {
return szText; }
double
Value1() const {
return fValue1; };
double
Value2() const {
return fValue2; };
TCommandType
Command() const {
return eCommand; };
bool
StopCommand() const {
return bCommand; };
void StopCommandSent();
TCommandType CommandCheck();
bool IsVelocity();
void AssignEvents(TEvent *e);
private:
// content
std::string szText;
double fValue1 { 0.0 };
double fValue2 { 0.0 };
// other
TCommandType eCommand { cm_Unknown };
bool bCommand { false }; // czy zawiera komendę dla zatrzymanego AI
TEvent *OnSent { nullptr }; // event dodawany do kolejki po wysłaniu komendy zatrzymującej skład
};
class memory_table : public basic_table<TMemCell> {
public:
// legacy method, initializes traction after deserialization from scenario file
void
InitCells();
// legacy method, sends content of all cells to the log
void
log_all();
};
//---------------------------------------------------------------------------

View File

@@ -988,11 +988,11 @@ TSubModel::create_geometry( std::size_t &Dataoffset, geometrybank_handle const &
// data offset is used to determine data offset of each submodel into single shared geometry bank
// (the offsets are part of legacy system which we now need to work around for backward compatibility)
if( Child )
Child->create_geometry( Dataoffset, Bank );
if( false == Vertices.empty() ) {
tVboPtr = static_cast<int>( Dataoffset );
Dataoffset += Vertices.size();
// conveniently all relevant custom node types use GL_POINTS, or we'd have to determine the type on individual basis
@@ -1003,67 +1003,33 @@ TSubModel::create_geometry( std::size_t &Dataoffset, geometrybank_handle const &
m_geometry = GfxRenderer.Insert( Vertices, Bank, type );
}
if( Next )
Next->create_geometry( Dataoffset, Bank );
}
// places contained geometry in provided ground node
void
TSubModel::convert( TGroundNode &Groundnode ) const {
Groundnode.asName = pName;
Groundnode.Ambient = f4Ambient;
Groundnode.Diffuse = f4Diffuse;
Groundnode.Specular = f4Specular;
Groundnode.m_material = m_material;
Groundnode.iFlags = (
( true == GfxRenderer.Material( m_material ).has_alpha ) ?
0x20 :
0x10 );
if( m_geometry == null_handle ) { return; }
std::size_t vertexcount { 0 };
std::vector<TGroundVertex> importedvertices;
TGroundVertex vertex, vertex1, vertex2;
for( auto const &sourcevertex : GfxRenderer.Vertices( m_geometry ) ) {
vertex.position = sourcevertex.position;
vertex.normal = sourcevertex.normal;
vertex.texture = sourcevertex.texture;
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
importedvertices.emplace_back( vertex1 );
importedvertices.emplace_back( vertex2 );
importedvertices.emplace_back( vertex );
if( m_geometry != 0 ) {
// calculate bounding radius while we're at it
// NOTE: doesn't take into account transformation hierarchy TODO: implement it
float squaredradius { 0.f };
// if this happens to be root node it may already have non-squared radius of the largest child
// since we're comparing squared radii, we need to square it back for correct results
m_boundingradius *= m_boundingradius;
for( auto const &vertex : GfxRenderer.Vertices( m_geometry ) ) {
squaredradius = static_cast<float>( glm::length2( vertex.position ) );
if( squaredradius > m_boundingradius ) {
m_boundingradius = squaredradius;
}
}
++vertexcount;
if( vertexcount > 2 ) { vertexcount = 0; } // start new triangle if needed
}
if( Groundnode.Piece == nullptr ) {
Groundnode.Piece = new piece_node();
}
Groundnode.iNumVerts = importedvertices.size();
if( Groundnode.iNumVerts > 0 ) {
Groundnode.Piece->vertices.swap( importedvertices );
for( auto const &vertex : Groundnode.Piece->vertices ) {
Groundnode.pCenter += vertex.position;
if( m_boundingradius > 0.f ) { m_boundingradius = std::sqrt( m_boundingradius ); }
// adjust overall radius if needed
// NOTE: the method to access root submodel is... less than ideal
auto *root { this };
while( root->Parent != nullptr ) {
root = root->Parent;
}
Groundnode.pCenter /= Groundnode.iNumVerts;
double r { 0.0 };
double tf;
for( auto const &vertex : Groundnode.Piece->vertices ) {
tf = glm::length2( vertex.position - glm::dvec3{ Groundnode.pCenter } );
if( tf > r )
r = tf;
}
Groundnode.fSquareRadius += r;
root->m_boundingradius = std::max(
root->m_boundingradius,
m_boundingradius );
}
if( Next )
Next->create_geometry( Dataoffset, Bank );
}
void TSubModel::ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular )
@@ -1151,7 +1117,8 @@ TModel3d::~TModel3d() {
if (iFlags & 0x0200) {
// wczytany z pliku tekstowego, submodele sprzątają same
Root = nullptr;
SafeDelete( Root );
// Root = nullptr;
}
else {
// wczytano z pliku binarnego (jest właścicielem tablic)
@@ -1639,7 +1606,7 @@ void TSubModel::BinInit(TSubModel *s, float4x4 *m, std::vector<std::string> *t,
}
}
else {
ErrorLog( "Bad model: reference to non-existent texture index in sub-model" + ( pName.empty() ? "" : " \"" + pName + "\"" ) );
ErrorLog( "Bad model: reference to nonexistent texture index in sub-model" + ( pName.empty() ? "" : " \"" + pName + "\"" ) );
m_material = null_handle;
}
}
@@ -1750,18 +1717,11 @@ void TModel3d::Init()
return; // operacje zostały już wykonane
if (Root)
{
if (iFlags & 0x0200) // jeśli wczytano z pliku tekstowego
{ // jest jakiś dziwny błąd, że obkręcany ma być tylko ostatni submodel
// głównego łańcucha
// TSubModel *p=Root;
// do
//{p->InitialRotate(true); //ostatniemu należy się konwersja układu
// współrzędnych
// p=p->NextGet();
//}
// while (p->NextGet())
// Root->InitialRotate(false); //a poprzednim tylko optymalizacja
Root->InitialRotate(true); // argumet określa, czy wykonać pierwotny obrót
if (iFlags & 0x0200) {
// jeśli wczytano z pliku tekstowego jest jakiś dziwny błąd,
// że obkręcany ma być tylko ostatni submodel głównego łańcucha
// argumet określa, czy wykonać pierwotny obrót
Root->InitialRotate(true);
}
iFlags |= Root->FlagsCheck() | 0x8000; // flagi całego modelu
if (iNumVerts) {
@@ -1779,11 +1739,6 @@ void TModel3d::Init()
}
};
void TModel3d::BreakHierarhy()
{
Error("Not implemented yet :(");
};
//-----------------------------------------------------------------------------
// 2012-02 funkcje do tworzenia terenu z E3D
//-----------------------------------------------------------------------------

View File

@@ -50,14 +50,18 @@ enum TAnimType // rodzaj animacji
at_Undefined = 0x800000FF // animacja chwilowo nieokreślona
};
namespace scene {
class shape_node;
}
class TModel3d;
class TGroundNode;
class TSubModel
{ // klasa submodelu - pojedyncza siatka, punkt świetlny albo grupa punktów
friend class opengl_renderer;
friend class TModel3d; // temporary workaround. TODO: clean up class content/hierarchy
friend class TDynamicObject; // temporary etc
friend class scene::shape_node; // temporary etc
public:
enum normalization {
@@ -130,10 +134,8 @@ private:
public: // chwilowo
float3 v_TransVector { 0.0f, 0.0f, 0.0f };
/*
basic_vertex *Vertices; // roboczy wskaźnik - wczytanie T3D do VBO
*/
vertex_array Vertices;
float m_boundingradius { 0 };
size_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację
TAnimType b_aAnim{ at_None }; // kody animacji oddzielnie, bo zerowane
public:
@@ -163,9 +165,6 @@ public:
TSubModel * NextGet() { return Next; };
TSubModel * ChildGet() { return Child; };
int TriangleAdd(TModel3d *m, material_handle tex, int tri);
/*
basic_vertex * TrianglePtr(int tex, int pos, glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular );
*/
void SetRotate(float3 vNewRotateAxis, float fNewAngle);
void SetRotateXYZ(vector3 vNewAngles);
void SetRotateXYZ(float3 vNewAngles);
@@ -215,8 +214,6 @@ public:
std::vector<float4x4>&);
void serialize_geometry( std::ostream &Output ) const;
// places contained geometry in provided ground node
void convert( TGroundNode &Groundnode ) const;
};
class TModel3d : public CMesh
@@ -236,6 +233,11 @@ private:
std::string asBinary; // nazwa pod którą zapisać model binarny
std::string m_filename;
public:
float bounding_radius() const {
return (
Root ?
Root->m_boundingradius :
0.f ); }
inline TSubModel * GetSMRoot() { return (Root); };
TModel3d();
~TModel3d();
@@ -246,7 +248,6 @@ public:
void LoadFromBinFile(std::string const &FileName, bool dynamic);
bool LoadFromFile(std::string const &FileName, bool dynamic);
void SaveToBinFile(std::string const &FileName);
void BreakHierarhy();
int Flags() const { return iFlags; };
void Init();
std::string NameGet() { return m_filename; };

83
Names.h
View File

@@ -13,55 +13,52 @@ http://mozilla.org/MPL/2.0/.
#include <string>
template <typename Type_>
class TNames {
class basic_table {
public:
// types:
// constructors:
TNames() = default;
// destructor:
// methods:
// dodanie obiektu z wskaźnikiem. updates data field if the object already exists. returns true for insertion, false for update
// destructor
~basic_table() {
for( auto *item : m_items ) {
delete item; } }
// methods
// adds provided item to the collection. returns: true if there's no duplicate with the same name, false otherwise
bool
Add( int const Type, std::string const &Name, Type_ Data ) {
auto lookup = find_map( Type ).emplace( Name, Data );
if( lookup.second == false ) {
// record already exists, update it
lookup.first->second = Data;
return false;
}
else {
// new record inserted, bail out
insert( Type_ *Item ) {
m_items.emplace_back( Item );
auto const itemname = Item->name();
if( ( true == itemname.empty() ) || ( itemname == "none" ) ) {
return true;
}
}
// returns pointer associated with provided label, or nullptr if there's no match
Type_
Find( int const Type, std::string const &Name ) {
auto const itemhandle { m_items.size() - 1 };
// add item name to the map
auto mapping = m_itemmap.emplace( itemname, itemhandle );
if( true == mapping.second ) {
return true;
}
// cell with this name already exists; update mapping to point to the new one, for backward compatibility
mapping.first->second = itemhandle;
return false; }
// locates item with specified name. returns pointer to the item, or nullptr
Type_ *
find( std::string const &Name ) {
auto lookup = m_itemmap.find( Name );
return (
lookup != m_itemmap.end() ?
m_items[ lookup->second ] :
nullptr ); }
auto const &map = find_map( Type );
auto const lookup = map.find( Name );
if( lookup != map.end() ) { return lookup->second; }
else { return nullptr; }
}
protected:
// types
using type_sequence = std::deque<Type_ *>;
using type_map = std::unordered_map<std::string, std::size_t>;
// members
type_sequence m_items;
type_map m_itemmap;
private:
// types:
typedef std::unordered_map<std::string, Type_> type_map;
typedef std::unordered_map<int, type_map> typemap_map;
public:
// data access
type_sequence &
sequence() {
return m_items; }
// methods:
// returns database stored with specified type key; creates new database if needed.
type_map &
find_map( int const Type ) {
return m_maps.emplace( Type, type_map() ).first->second;
}
// members:
typemap_map m_maps; // list of object maps of types specified so far
};

View File

@@ -59,8 +59,8 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M
CPointIn = NewCPointIn;
Point2 = NewPoint2;
// poprawienie przechyłki
fRoll1 = DegToRad(fNewRoll1); // Ra: przeliczone jest bardziej przydatne do obliczeń
fRoll2 = DegToRad(fNewRoll2);
fRoll1 = glm::radians(fNewRoll1); // Ra: przeliczone jest bardziej przydatne do obliczeń
fRoll2 = glm::radians(fNewRoll2);
if (Global::bRollFix)
{ // Ra: poprawianie przechyłki
// Przechyłka powinna być na środku wewnętrznej szyny, a standardowo jest w osi
@@ -78,7 +78,7 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M
CPointOut.y += w1; // prosty ma wektory jednostkowe
pOwner->MovedUp1(w1); // zwrócić trzeba informację o podwyższeniu podsypki
}
if (fRoll2 != 0.0)
if (fRoll2 != 0.f)
{
double w2 = std::abs(std::sin(fRoll2) * 0.75); // 0.5*w2+0.0325; //0.75m dla 1.435
Point2.y += w2; // modyfikacja musi być przed policzeniem dalszych parametrów
@@ -87,9 +87,9 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M
// zwrócić trzeba informację o podwyższeniu podsypki
}
}
// kąt w planie, żeby nie liczyć wielokrotnie
// Ra: ten kąt jeszcze do przemyślenia jest
fDirection = -atan2(Point2.x - Point1.x,
Point2.z - Point1.z); // kąt w planie, żeby nie liczyć wielokrotnie
fDirection = -std::atan2(Point2.x - Point1.x, Point2.z - Point1.z);
bCurve = bIsCurve;
if (bCurve)
{ // przeliczenie współczynników wielomianu, będzie mniej mnożeń i można policzyć pochodne
@@ -101,10 +101,9 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M
else
fLength = (Point1 - Point2).Length();
fStep = fNewStep;
if (fLength <= 0)
{
ErrorLog( "Bad geometry: zero length spline \"" + pOwner->NameGet() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" );
// MessageBox(0,"Length<=0","TSegment::Init",MB_OK);
if (fLength <= 0) {
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ć
@@ -206,7 +205,7 @@ double TSegment::GetTFromS(double const s) const
// Newton's method failed. If this happens, increase iterations or
// tolerance or integration accuracy.
// return -1; //Ra: tu nigdy nie dojdzie
ErrorLog( "Bad geometry: shape estimation failed for spline \"" + pOwner->NameGet() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" );
ErrorLog( "Bad track: shape estimation failed for spline \"" + pOwner->name() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" );
// MessageBox(0,"Too many iterations","GetTFromS",MB_OK);
return fTime;
};
@@ -324,23 +323,28 @@ Math3D::vector3 TSegment::GetPoint(double const fDistance) const
}
};
void TSegment::RaPositionGet(double const fDistance, Math3D::vector3 &p, Math3D::vector3 &a) const
{ // ustalenie pozycji osi na torze, przechyłki, pochylenia i kierunku jazdy
if (bCurve)
{ // można by wprowadzić uproszczony wzór dla okręgów płaskich
double t = GetTFromS(fDistance); // aproksymacja dystansu na krzywej Beziera na parametr (t)
p = RaInterpolate(t);
a.x = (1.0 - t) * fRoll1 + (t)*fRoll2; // przechyłka w danym miejscu (zmienia się liniowo)
// ustalenie pozycji osi na torze, przechyłki, pochylenia i kierunku jazdy
void TSegment::RaPositionGet(double const fDistance, Math3D::vector3 &p, Math3D::vector3 &a) const {
if (bCurve) {
// można by wprowadzić uproszczony wzór dla okręgów płaskich
auto const t = GetTFromS(fDistance); // aproksymacja dystansu na krzywej Beziera na parametr (t)
p = FastGetPoint( t );
// przechyłka w danym miejscu (zmienia się liniowo)
a.x = interpolate<double>( fRoll1, fRoll2, t );
// pochodna jest 3*A*t^2+2*B*t+C
a.y = atan(t * (t * 3.0 * vA.y + vB.y + vB.y) + vC.y); // pochylenie krzywej (w pionie)
a.z = -atan2(t * (t * 3.0 * vA.x + vB.x + vB.x) + vC.x,
t * (t * 3.0 * vA.z + vB.z + vB.z) + vC.z); // kierunek krzywej w planie
auto const tangent = t * ( t * 3.0 * vA + vB + vB ) + vC;
// pochylenie krzywej (w pionie)
a.y = std::atan( tangent.y );
// kierunek krzywej w planie
a.z = -std::atan2( tangent.x, tangent.z );
}
else
{ // wyliczenie dla odcinka prostego jest prostsze
double t = fDistance / fLength; // zerowych torów nie ma
p = ((1.0 - t) * Point1 + (t)*Point2);
a.x = (1.0 - t) * fRoll1 + (t)*fRoll2; // przechyłka w danym miejscu (zmienia się liniowo)
else {
// wyliczenie dla odcinka prostego jest prostsze
auto const t = fDistance / fLength; // zerowych torów nie ma
p = FastGetPoint( t );
// przechyłka w danym miejscu (zmienia się liniowo)
a.x = interpolate<double>( fRoll1, fRoll2, t );
a.y = fStoop; // pochylenie toru prostego
a.z = fDirection; // kierunek toru w planie
}
@@ -355,7 +359,7 @@ Math3D::vector3 TSegment::FastGetPoint(double const t) const
interpolate( Point1, Point2, t ) );
}
bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, const vector6 *ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale, int iSkip, int iEnd, double fOffsetX, Math3D::vector3 **p, bool bRender)
bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, const basic_vertex *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
@@ -365,13 +369,14 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin,
if( !fTsBuffer )
return false; // prowizoryczne zabezpieczenie przed wysypem - ustalić faktyczną przyczynę
Math3D::vector3 pos1, pos2, dir, parallel1, parallel2, pt, norm;
double s, step, fOffset, tv1, tv2, t, fEnd;
glm::vec3 pos1, pos2, dir, parallel1, parallel2, pt, norm;
float s, step, fOffset, tv1, tv2, t, fEnd;
bool const trapez = iNumShapePoints < 0; // sygnalizacja trapezowatości
iNumShapePoints = std::abs( iNumShapePoints );
fTextureLength *= Texturescale;
float const texturelength = fTextureLength * Texturescale;
float const texturescale = Texturescale;
double m1, jmm1, m2, jmm2; // pozycje względne na odcinku 0...1 (ale nie parametr Beziera)
float m1, jmm1, m2, jmm2; // pozycje względne na odcinku 0...1 (ale nie parametr Beziera)
step = fStep;
tv1 = 1.0; // Ra: to by można było wyliczać dla odcinka, wyglądało by lepiej
s = fStep * iSkip; // iSkip - ile odcinków z początku pominąć
@@ -379,9 +384,14 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin,
t = fTsBuffer[ i ]; // tabela wattości t dla segmentów
// BUG: length of spline can be 0, we should skip geometry generation for such cases
fOffset = 0.1 / fLength; // pierwsze 10cm
pos1 = FastGetPoint( t ); // wektor początku segmentu
dir = FastGetDirection( t, fOffset ); // wektor kierunku
parallel1 = Normalize( Math3D::vector3( -dir.z, 0.0, dir.x ) ); // wektor poprzeczny
pos1 = glm::dvec3{ FastGetPoint( t ) - Origin }; // wektor początku segmentu
dir = glm::dvec3{ FastGetDirection( t, fOffset ) }; // wektor kierunku
parallel1 = glm::vec3{ -dir.z, 0.f, dir.x }; // wektor poprzeczny
if( glm::length2( parallel1 ) == 0.f ) {
// temporary workaround for malformed situations with control points placed above endpoints
parallel1 = glm::vec3{ glm::dvec3{ FastGetPoint_1() - FastGetPoint_0() } };
}
parallel1 = glm::normalize( parallel1 );
if( iEnd == 0 )
iEnd = iSegCount;
fEnd = fLength * double( iEnd ) / double( iSegCount );
@@ -406,26 +416,31 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin,
while( tv1 < 0.0 ) {
tv1 += 1.0;
}
tv2 = tv1 - step / fTextureLength; // mapowanie na końcu segmentu
tv2 = tv1 - step / texturelength; // mapowanie na końcu segmentu
t = fTsBuffer[ i ]; // szybsze od GetTFromS(s);
pos2 = FastGetPoint( t );
dir = FastGetDirection( t, fOffset ); // nowy wektor kierunku
parallel2 = Normalize( Math3D::vector3( -dir.z, 0.0, dir.x ) ); // wektor poprzeczny
pos2 = glm::dvec3{ FastGetPoint( t ) - Origin };
dir = glm::dvec3{ FastGetDirection( t, fOffset ) }; // nowy wektor kierunku
parallel2 = glm::vec3{ -dir.z, 0.f, dir.x }; // wektor poprzeczny
if( glm::length2( parallel2 ) == 0.f ) {
// temporary workaround for malformed situations with control points placed above endpoints
parallel2 = glm::vec3{ glm::dvec3{ FastGetPoint_1() - FastGetPoint_0() } };
}
parallel2 = glm::normalize( parallel2 );
if( trapez ) {
for( int j = 0; j < iNumShapePoints; ++j ) {
pt = parallel1 * ( jmm1 * ( ShapePoints[ j ].x - fOffsetX ) + m1 * ShapePoints[ j + iNumShapePoints ].x ) + pos1;
pt.y += jmm1 * ShapePoints[ j ].y + m1 * ShapePoints[ j + iNumShapePoints ].y;
pt -= Origin;
norm = ( jmm1 * ShapePoints[ j ].n.x + m1 * ShapePoints[ j + iNumShapePoints ].n.x ) * parallel1;
norm.y += jmm1 * ShapePoints[ j ].n.y + m1 * ShapePoints[ j + iNumShapePoints ].n.y;
pt = parallel1 * ( jmm1 * ( ShapePoints[ j ].position.x - fOffsetX ) + m1 * ShapePoints[ j + iNumShapePoints ].position.x ) + pos1;
pt.y += jmm1 * ShapePoints[ j ].position.y + m1 * ShapePoints[ j + iNumShapePoints ].position.y;
// pt -= Origin;
norm = ( jmm1 * ShapePoints[ j ].normal.x + m1 * ShapePoints[ j + iNumShapePoints ].normal.x ) * parallel1;
norm.y += jmm1 * ShapePoints[ j ].normal.y + m1 * ShapePoints[ j + iNumShapePoints ].normal.y;
if( bRender ) {
// skrzyżowania podczas łączenia siatek mogą nie renderować poboczy, ale potrzebować punktów
Output.emplace_back(
glm::vec3 { pt.x, pt.y, pt.z },
glm::vec3 { norm.x, norm.y, norm.z },
glm::vec2 { ( jmm1 * ShapePoints[ j ].z + m1 * ShapePoints[ j + iNumShapePoints ].z ) / Texturescale, tv1 } );
pt,
glm::normalize( norm ),
glm::vec2 { ( jmm1 * ShapePoints[ j ].texture.x + m1 * ShapePoints[ j + iNumShapePoints ].texture.x ) / texturescale, tv1 } );
}
if( p ) // jeśli jest wskaźnik do tablicy
if( *p )
@@ -435,17 +450,17 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin,
( *p )++;
} // zapamiętanie brzegu jezdni
// dla trapezu drugi koniec ma inne współrzędne
pt = parallel2 * ( jmm2 * ( ShapePoints[ j ].x - fOffsetX ) + m2 * ShapePoints[ j + iNumShapePoints ].x ) + pos2;
pt.y += jmm2 * ShapePoints[ j ].y + m2 * ShapePoints[ j + iNumShapePoints ].y;
pt -= Origin;
norm = ( jmm1 * ShapePoints[ j ].n.x + m1 * ShapePoints[ j + iNumShapePoints ].n.x ) * parallel2;
norm.y += jmm1 * ShapePoints[ j ].n.y + m1 * ShapePoints[ j + iNumShapePoints ].n.y;
pt = parallel2 * ( jmm2 * ( ShapePoints[ j ].position.x - fOffsetX ) + m2 * ShapePoints[ j + iNumShapePoints ].position.x ) + pos2;
pt.y += jmm2 * ShapePoints[ j ].position.y + m2 * ShapePoints[ j + iNumShapePoints ].position.y;
// pt -= Origin;
norm = ( jmm1 * ShapePoints[ j ].normal.x + m1 * ShapePoints[ j + iNumShapePoints ].normal.x ) * parallel2;
norm.y += jmm1 * ShapePoints[ j ].normal.y + m1 * ShapePoints[ j + iNumShapePoints ].normal.y;
if( bRender ) {
// skrzyżowania podczas łączenia siatek mogą nie renderować poboczy, ale potrzebować punktów
Output.emplace_back(
glm::vec3 { pt.x, pt.y, pt.z },
glm::vec3 { norm.x, norm.y, norm.z },
glm::vec2 { ( jmm2 * ShapePoints[ j ].z + m2 * ShapePoints[ j + iNumShapePoints ].z ) / Texturescale, tv2 } );
pt,
glm::normalize( norm ),
glm::vec2 { ( jmm2 * ShapePoints[ j ].texture.x + m2 * ShapePoints[ j + iNumShapePoints ].texture.x ) / texturescale, tv2 } );
}
if( p ) // jeśli jest wskaźnik do tablicy
if( *p )
@@ -460,27 +475,27 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin,
if( bRender ) {
for( int j = 0; j < iNumShapePoints; ++j ) {
//łuk z jednym profilem
pt = parallel1 * ( ShapePoints[ j ].x - fOffsetX ) + pos1;
pt.y += ShapePoints[ j ].y;
pt -= Origin;
norm = ShapePoints[ j ].n.x * parallel1;
norm.y += ShapePoints[ j ].n.y;
pt = parallel1 * ( ShapePoints[ j ].position.x - fOffsetX ) + pos1;
pt.y += ShapePoints[ j ].position.y;
// pt -= Origin;
norm = ShapePoints[ j ].normal.x * parallel1;
norm.y += ShapePoints[ j ].normal.y;
Output.emplace_back(
glm::vec3 { pt.x, pt.y, pt.z },
glm::vec3 { norm.x, norm.y, norm.z },
glm::vec2 { ShapePoints[ j ].z / Texturescale, tv1 } );
pt,
glm::normalize( norm ),
glm::vec2 { ShapePoints[ j ].texture.x / texturescale, tv1 } );
pt = parallel2 * ShapePoints[ j ].x + pos2;
pt.y += ShapePoints[ j ].y;
pt -= Origin;
norm = ShapePoints[ j ].n.x * parallel2;
norm.y += ShapePoints[ j ].n.y;
pt = parallel2 * ShapePoints[ j ].position.x + pos2;
pt.y += ShapePoints[ j ].position.y;
// pt -= Origin;
norm = ShapePoints[ j ].normal.x * parallel2;
norm.y += ShapePoints[ j ].normal.y;
Output.emplace_back(
glm::vec3 { pt.x, pt.y, pt.z },
glm::vec3 { norm.x, norm.y, norm.z },
glm::vec2 { ShapePoints[ j ].z / Texturescale, tv2 } );
pt,
glm::normalize( norm ),
glm::vec2 { ShapePoints[ j ].texture.x / texturescale, tv2 } );
}
}
}

View File

@@ -14,44 +14,13 @@ http://mozilla.org/MPL/2.0/.
#include "Classes.h"
#include "usefull.h"
// 110405 Ra: klasa punktów przekroju z normalnymi
class vector6 : public Math3D::vector3
{ // punkt przekroju wraz z wektorem normalnym
public:
Math3D::vector3 n;
vector6()
{
x = y = z = n.x = n.z = 0.0;
n.y = 1.0;
};
vector6(double a, double b, double c, double d, double e, double f)
//{x=a; y=b; z=c; n.x=d; n.y=e; n.z=f;};
{
x = a;
y = b;
z = c;
n.x = 0.0;
n.y = 1.0;
n.z = 0.0;
}; // Ra: bo na razie są z tym problemy
vector6(double a, double b, double c)
{
x = a;
y = b;
z = c;
n.x = 0.0;
n.y = 1.0;
n.z = 0.0;
};
};
class TSegment
{ // aproksymacja toru (zwrotnica ma dwa takie, jeden z nich jest aktywny)
private:
Math3D::vector3 Point1, CPointOut, CPointIn, Point2;
double fRoll1 = 0.0,
fRoll2 = 0.0; // przechyłka na końcach
float
fRoll1{ 0.f },
fRoll2{ 0.f }; // przechyłka na końcach
double fLength = 0.0; // długość policzona
double *fTsBuffer = nullptr; // wartości parametru krzywej dla równych odcinków
double fStep = 0.0;
@@ -60,7 +29,6 @@ class TSegment
double fStoop = 0.0; // Ra: kąt wzniesienia; dla łuku od Point1
Math3D::vector3 vA, vB, vC; // współczynniki wielomianów trzeciego stopnia vD==Point1
TTrack *pOwner = nullptr; // wskaźnik na właściciela
double fAngle[ 2 ] = { 0.0, 0.0 }; // kąty zakończenia drogi na przejazdach
Math3D::vector3
GetFirstDerivative(double const fTime) const;
@@ -118,18 +86,18 @@ public:
FastGetPoint_1() const {
return Point2; };
inline
double
float
GetRoll(double const Distance) const {
return interpolate( fRoll1, fRoll2, Distance / fLength ); }
return interpolate( fRoll1, fRoll2, static_cast<float>(Distance / fLength) ); }
inline
void
GetRolls(double &r1, double &r2) const {
GetRolls(float &r1, float &r2) const {
// pobranie przechyłek (do generowania trójkątów)
r1 = fRoll1;
r2 = fRoll2; }
bool
RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, vector6 const *ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale = 1.0, int iSkip = 0, int iEnd = 0, double fOffsetX = 0.0, Math3D::vector3 **p = nullptr, bool bRender = true);
RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, 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);
void
Render();
inline
@@ -140,10 +108,6 @@ public:
int
RaSegCount() const {
return fTsBuffer ? iSegCount : 1; };
inline
void
AngleSet(int const i, double const a) {
fAngle[i] = a; };
};
//---------------------------------------------------------------------------

View File

@@ -765,8 +765,11 @@ texture_manager::create( std::string Filename, bool const Loadnow ) {
Filename.erase( traitpos );
}
if( Filename.rfind( '.' ) != std::string::npos )
Filename.erase( Filename.rfind( '.' ) ); // trim extension if there's one
if( ( Filename.rfind( '.' ) != std::string::npos )
&& ( Filename.rfind( '.' ) != Filename.rfind( ".." ) + 1 ) ) {
// trim extension if there's one, but don't mistake folder traverse for extension
Filename.erase( Filename.rfind( '.' ) );
}
std::replace(Filename.begin(), Filename.end(), '\\', '/'); // fix slashes

View File

@@ -11,8 +11,9 @@ http://mozilla.org/MPL/2.0/.
#include "Timer.h"
#include "Globals.h"
namespace Timer
{
namespace Timer {
subsystem_stopwatches subsystem;
double DeltaTime, DeltaRenderTime;
double fFPS{ 0.0f };

36
Timer.h
View File

@@ -9,8 +9,7 @@ http://mozilla.org/MPL/2.0/.
#pragma once
namespace Timer
{
namespace Timer {
double GetTime();
@@ -28,6 +27,39 @@ double GetFPS();
void ResetTimers();
void UpdateTimers(bool pause);
class stopwatch {
public:
void
start() {
m_start = std::chrono::steady_clock::now(); }
void
stop() {
m_accumulator = 0.95f * m_accumulator + std::chrono::duration_cast<std::chrono::microseconds>( ( std::chrono::steady_clock::now() - m_start ) ).count() / 1000.f; }
float
average() const {
return m_accumulator / 20.f;}
private:
std::chrono::time_point<std::chrono::steady_clock> m_start { std::chrono::steady_clock::now() };
float m_accumulator { 1000.f / 30.f * 20.f }; // 20 last samples, initial 'neutral' rate of 30 fps
};
struct subsystem_stopwatches {
stopwatch gfx_total;
stopwatch gfx_color;
stopwatch gfx_shadows;
stopwatch gfx_reflections;
stopwatch gfx_swap;
stopwatch sim_total;
stopwatch sim_dynamics;
stopwatch sim_events;
stopwatch sim_ai;
};
extern subsystem_stopwatches subsystem;
};
//---------------------------------------------------------------------------

1713
Track.cpp

File diff suppressed because it is too large Load Diff

138
Track.h
View File

@@ -10,23 +10,29 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include <string>
#include "GL/glew.h"
#include "ResourceManager.h"
#include <vector>
#include <deque>
#include "Segment.h"
#include "material.h"
#include "scenenode.h"
#include "Names.h"
typedef enum
{
namespace scene {
class basic_cell;
}
enum TTrackType {
tt_Unknown,
tt_Normal,
tt_Switch,
tt_Table,
tt_Cross,
tt_Tributary
} TTrackType;
};
// McZapkie-100502
typedef enum
{
enum TEnvironmentType {
e_unknown = -1,
e_flat = 0,
e_mountains,
@@ -34,7 +40,7 @@ typedef enum
e_tunnel,
e_bridge,
e_bank
} TEnvironmentType;
};
// Ra: opracować alternatywny system cieni/świateł z definiowaniem koloru oświetlenia w halach
class TEvent;
@@ -49,49 +55,43 @@ class TSwitchExtension
TSwitchExtension(TTrack *owner, int const what);
~TSwitchExtension();
std::shared_ptr<TSegment> Segments[6]; // dwa tory od punktu 1, pozosta³e dwa od 2? Ra 140101: 6 po³¹czeñ dla skrzy¿owañ
// TTrack *trNear[4]; //tory do³¹czone do punktów 1, 2, 3 i 4
// dotychczasowe [2]+[2] wskaŸniki zamieniæ na nowe [4]
TTrack *pNexts[2]; // tory do³¹czone do punktów 2 i 4
TTrack *pPrevs[2]; // tory do³¹czone do punktów 1 i 3
int iNextDirection[2]; // to te¿ z [2]+[2] przerobiæ na [4]
int iPrevDirection[2];
int CurrentIndex = 0; // dla zwrotnicy
double fOffset = 0.0,
fDesiredOffset = 0.0; // aktualne i docelowe położenie napędu iglic
double fOffsetSpeed = 0.1; // prędkość liniowa ruchu iglic
double fOffsetDelay = 0.05; // opóźnienie ruchu drugiej iglicy względem pierwszej // dodatkowy ruch drugiej iglicy po zablokowaniu pierwszej na opornicy
float
fOffset{ 0.f },
fDesiredOffset{ 0.f }; // aktualne i docelowe położenie napędu iglic
float fOffsetSpeed = 0.1f; // prędkość liniowa ruchu iglic
float fOffsetDelay = 0.05f; // opóźnienie ruchu drugiej iglicy względem pierwszej // dodatkowy ruch drugiej iglicy po zablokowaniu pierwszej na opornicy
union
{
struct
{ // zmienne potrzebne tylko dla zwrotnicy
double fOffset1,
fOffset2; // przesunięcia iglic - 0=na wprost
float fOffset1,
fOffset2; // przesunięcia iglic - 0=na wprost
bool RightSwitch; // czy zwrotnica w prawo
};
struct
{ // zmienne potrzebne tylko dla obrotnicy/przesuwnicy
TGroundNode *pMyNode; // dla obrotnicy do wtórnego podłączania torów
// TAnimContainer *pAnim; //animator modelu dla obrotnicy
// TAnimContainer *pAnim; //animator modelu dla obrotnicy
TAnimModel *pModel; // na razie model
};
struct
{ // zmienne dla skrzyżowania
int iRoads; // ile dróg się spotyka?
Math3D::vector3 *vPoints; // tablica wierzchołków nawierzchni, generowana przez pobocze
// int iPoints; // liczba faktycznie użytych wierzchołków nawierzchni
glm::vec3 *vPoints; // tablica wierzchołków nawierzchni, generowana przez pobocze
bool bPoints; // czy utworzone?
};
};
bool bMovement = false; // czy w trakcie animacji
int iLeftVBO = 0,
iRightVBO = 0; // indeksy iglic w VBO
TSubRect *pOwner = nullptr; // sektor, któremu trzeba zgłosić animację
scene::basic_cell *pOwner = nullptr; // TODO: convert this to observer pattern
TTrack *pNextAnim = nullptr; // następny tor do animowania
TEvent *evPlus = nullptr,
*evMinus = nullptr; // zdarzenia sygnalizacji rozprucia
float fVelocity = -1.0; // maksymalne ograniczenie prędkości (ustawianej eventem)
Math3D::vector3 vTrans; // docelowa translacja przesuwnicy
private:
};
class TIsolated
@@ -106,7 +106,7 @@ class TIsolated
TMemCell *pMemCell = nullptr; // automatyczna komórka pamięci, która współpracuje z odcinkiem izolowanym
TIsolated();
TIsolated(const std::string &n, TIsolated *i);
~TIsolated();
static void DeleteAll();
static TIsolated * Find(const std::string &n); // znalezienie obiektu albo utworzenie nowego
void Modify(int i, TDynamicObject *o); // dodanie lub odjęcie osi
bool Busy() {
@@ -118,12 +118,11 @@ class TIsolated
};
// trajektoria ruchu - opakowanie
class TTrack /*: public Resource*/ {
class TTrack : public editor::basic_node {
friend class opengl_renderer;
private:
TGroundNode * pMyNode = nullptr; // Ra: proteza, żeby tor znał swoją nazwę TODO: odziedziczyć TTrack z TGroundNode
TIsolated * pIsolated = nullptr; // obwód izolowany obsługujący zajęcia/zwolnienia grupy torów
std::shared_ptr<TSwitchExtension> SwitchExtension; // dodatkowe dane do toru, który jest zwrotnicą
std::shared_ptr<TSegment> Segment;
@@ -132,7 +131,7 @@ private:
// McZapkie-070402: dodalem zmienne opisujace rozmiary tekstur
int iTrapezoid = 0; // 0-standard, 1-przechyłka, 2-trapez, 3-oba
double fRadiusTable[ 2 ]; // dwa promienie, drugi dla zwrotnicy
double fRadiusTable[ 2 ] = { 0.0, 0.0 }; // dwa promienie, drugi dla zwrotnicy
float fTexLength = 4.0f; // długość powtarzania tekstury w metrach
float fTexRatio1 = 1.0f; // proporcja boków tekstury nawierzchni (żeby zaoszczędzić na rozmiarach tekstur...)
float fTexRatio2 = 1.0f; // proporcja boków tekstury chodnika (żeby zaoszczędzić na rozmiarach tekstur...)
@@ -140,6 +139,7 @@ private:
float fTexWidth = 0.9f; // szerokość boku
float fTexSlope = 0.9f;
glm::dvec3 m_origin;
material_handle m_material1 = 0; // tekstura szyn albo nawierzchni
material_handle m_material2 = 0; // tekstura automatycznej podsypki albo pobocza
typedef std::vector<geometry_handle> geometryhandle_sequence;
@@ -173,22 +173,24 @@ public:
int iQualityFlag = 20;
int iDamageFlag = 0;
TEnvironmentType eEnvironment = e_flat; // dźwięk i oświetlenie
bool bVisible = true; // czy rysowany
int iAction = 0; // czy modyfikowany eventami (specjalna obsługa przy skanowaniu)
float fOverhead = -1.0; // można normalnie pobierać prąd (0 dla jazdy bezprądowej po danym odcinku, >0-z opuszczonym i ograniczeniem prędkości)
private:
private:
double fVelocity = -1.0; // ograniczenie prędkości // prędkość dla AI (powyżej rośnie prawdopowobieństwo wykolejenia)
public:
public:
// McZapkie-100502:
double fTrackLength = 100.0; // długość z wpisu, nigdzie nie używana
double fRadius = 0.0; // promień, dla zwrotnicy kopiowany z tabeli
bool ScannedFlag = false; // McZapkie: do zaznaczania kolorem torów skanowanych przez AI
TTraction *hvOverhead = nullptr; // drut zasilający do szybkiego znalezienia (nie używany)
TGroundNode *nFouling[ 2 ]; // współrzędne ukresu albo oporu kozła
TGroundNode *nFouling[ 2 ] = { nullptr, nullptr }; // współrzędne ukresu albo oporu kozła
TTrack( scene::node_data const &Nodedata );
// legacy constructor
TTrack( std::string Name );
virtual ~TTrack();
TTrack(TGroundNode *g);
~TTrack();
void Init();
static bool sort_by_material( TTrack const *Left, TTrack const *Right );
static TTrack * Create400m(int what, double dx);
TTrack * NullCreate(int dir);
inline bool IsEmpty() {
@@ -207,7 +209,7 @@ public:
return trPrev; };
TTrack *Connected(int s, double &d) const;
bool SetConnections(int i);
bool Switch(int i, double t = -1.0, double d = -1.0);
bool Switch(int i, float const t = -1.f, float const d = -1.f);
bool SwitchForced(int i, TDynamicObject *o);
int CrossSegment(int from, int into);
inline int GetSwitchState() {
@@ -215,7 +217,16 @@ public:
SwitchExtension ?
SwitchExtension->CurrentIndex :
-1); };
void Load(cParser *parser, Math3D::vector3 pOrigin, std::string name);
// returns number of different routes possible to take from given point
// TODO: specify entry point, number of routes for switches can vary
inline
int
RouteCount() const {
return (
SwitchExtension != nullptr ?
SwitchExtension->iRoads - 1 :
1 ); }
void Load(cParser *parser, Math3D::vector3 pOrigin);
bool AssignEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent2);
bool AssignallEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent2);
bool AssignForcedEvents(TEvent *NewEventPlus, TEvent *NewEventMinus);
@@ -223,14 +234,21 @@ public:
bool AddDynamicObject(TDynamicObject *Dynamic);
bool RemoveDynamicObject(TDynamicObject *Dynamic);
void create_geometry(geometrybank_handle const &Bank); // wypełnianie VBO
// set origin point
void
origin( glm::dvec3 Origin ) {
m_origin = Origin; }
// retrieves list of the track's end points
std::vector<glm::dvec3>
endpoints() const;
void create_geometry( geometrybank_handle const &Bank ); // wypełnianie VBO
void RenderDynSounds(); // odtwarzanie dźwięków pojazdów jest niezależne od ich wyświetlania
void RaOwnerSet(TSubRect *o) {
if (SwitchExtension)
SwitchExtension->pOwner = o; };
void RaOwnerSet( scene::basic_cell *o ) {
if( SwitchExtension ) { SwitchExtension->pOwner = o; } };
bool InMovement(); // czy w trakcie animacji?
void RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joined);
void RaAssign( TAnimModel *am, TEvent *done, TEvent *joined );
void RaAnimListAdd(TTrack *t);
TTrack * RaAnimate();
@@ -241,22 +259,42 @@ public:
std::string IsolatedName();
bool IsolatedEventsAssign(TEvent *busy, TEvent *free);
double WidthTotal();
GLuint TextureGet(int i) {
return (
i ?
m_material1 :
m_material2 ); };
bool IsGroupable();
int TestPoint( Math3D::vector3 *Point);
void MovedUp1(float const dh);
std::string NameGet();
void VelocitySet(float v);
float VelocityGet();
double VelocityGet();
void ConnectionsLog();
private:
protected:
// calculates path's bounding radius
void
radius_();
private:
void EnvironmentSet();
void EnvironmentReset();
};
// collection of virtual tracks and roads present in the scene
class path_table : public basic_table<TTrack> {
public:
~path_table();
// legacy method, initializes tracks after deserialization from scenario file
void
InitTracks();
// legacy method, sends list of occupied paths over network
void
TrackBusyList() const;
// legacy method, sends list of occupied path sections over network
void
IsolatedBusyList() const;
// legacy method, sends state of specified path section over network
void
IsolatedBusy( std::string const &Name ) const;
};
//---------------------------------------------------------------------------

View File

@@ -14,10 +14,10 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "Traction.h"
#include "simulation.h"
#include "Globals.h"
#include "Logs.h"
#include "mctools.h"
#include "TractionPower.h"
//---------------------------------------------------------------------------
/*
@@ -90,9 +90,97 @@ jawnie nazwę sekcji, ewentualnie nazwę zasilacza (zostanie zastąpiona wskazan
sekcji z sąsiedniego przęsła).
*/
std::size_t
TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ) {
TTraction::TTraction( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {}
// legacy constructor
TTraction::TTraction( std::string Name ) {
m_name = Name;
}
glm::dvec3 LoadPoint( cParser &Input ) {
// pobranie współrzędnych punktu
glm::dvec3 point;
Input.getTokens( 3 );
Input
>> point.x
>> point.y
>> point.z;
return point;
}
void
TTraction::Load( cParser *parser, glm::dvec3 const &pOrigin ) {
parser->getTokens( 4 );
*parser
>> asPowerSupplyName
>> NominalVoltage
>> MaxCurrent
>> fResistivity;
if( fResistivity == 0.01f ) {
// tyle jest w sceneriach [om/km]
// taka sensowniejsza wartość za http://www.ikolej.pl/fileadmin/user_upload/Seminaria_IK/13_05_07_Prezentacja_Kruczek.pdf
fResistivity = 0.075f;
}
fResistivity *= 0.001f; // teraz [om/m]
// Ra 2014-02: a tutaj damy symbol sieci i jej budowę, np.:
// SKB70-C, CuCd70-2C, KB95-2C, C95-C, C95-2C, YC95-2C, YpC95-2C, YC120-2C
// YpC120-2C, YzC120-2C, YwsC120-2C, YC150-C150, YC150-2C150, C150-C150
// C120-2C, 2C120-2C, 2C120-2C-1, 2C120-2C-2, 2C120-2C-3, 2C120-2C-4
auto const material = parser->getToken<std::string>();
// 1=miedziana, rysuje się na zielono albo czerwono
// 2=aluminiowa, rysuje się na czarno
if( material == "none" ) { Material = 0; }
else if( material == "al" ) { Material = 2; }
else { Material = 1; }
parser->getTokens( 2 );
*parser
>> WireThickness
>> DamageFlag;
pPoint1 = LoadPoint( *parser ) + pOrigin;
pPoint2 = LoadPoint( *parser ) + pOrigin;
pPoint3 = LoadPoint( *parser ) + pOrigin;
pPoint4 = LoadPoint( *parser ) + pOrigin;
auto const minheight { parser->getToken<double>() };
fHeightDifference = ( pPoint3.y - pPoint1.y + pPoint4.y - pPoint2.y ) * 0.5 - minheight;
auto const segmentlength { parser->getToken<double>() };
iNumSections = (
segmentlength ?
glm::length( ( pPoint1 - pPoint2 ) ) / segmentlength :
0 );
parser->getTokens( 2 );
*parser
>> Wires
>> WireOffset;
m_visible = ( parser->getToken<std::string>() == "vis" );
std::string token { parser->getToken<std::string>() };
if( token == "parallel" ) {
// jawne wskazanie innego przęsła, na które może przestawić się pantograf
parser->getTokens();
*parser >> asParallel;
}
while( ( false == token.empty() )
&& ( token != "endtraction" ) ) {
token = parser->getToken<std::string>();
}
Init(); // przeliczenie parametrów
// calculate traction location
location( interpolate( pPoint2, pPoint1, 0.5 ) );
}
// retrieves list of the track's end points
std::vector<glm::dvec3>
TTraction::endpoints() const {
return { pPoint1, pPoint2 };
}
std::size_t
TTraction::create_geometry( geometrybank_handle const &Bank ) {
if( m_geometry != null_handle ) {
return GfxRenderer.Vertices( m_geometry ).size() / 2;
}
@@ -106,14 +194,14 @@ TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &O
basic_vertex startvertex, endvertex;
startvertex.position =
glm::vec3(
pPoint1.x - ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - Origin.x,
pPoint1.y - Origin.y,
pPoint1.z - ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - Origin.z );
pPoint1.x - ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - m_origin.x,
pPoint1.y - m_origin.y,
pPoint1.z - ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - m_origin.z );
endvertex.position =
glm::vec3(
pPoint2.x - ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - Origin.x,
pPoint2.y - Origin.y,
pPoint2.z - ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - Origin.z );
pPoint2.x - ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - m_origin.x,
pPoint2.y - m_origin.y,
pPoint2.z - ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - m_origin.z );
vertices.emplace_back( startvertex );
vertices.emplace_back( endvertex );
// Nie wiem co 'Marcin
@@ -130,9 +218,9 @@ TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &O
if( Wires > 1 ) { // lina nośna w kawałkach
startvertex.position =
glm::vec3(
pPoint3.x - Origin.x,
pPoint3.y - Origin.y,
pPoint3.z - Origin.z );
pPoint3.x - m_origin.x,
pPoint3.y - m_origin.y,
pPoint3.z - m_origin.z );
for( int i = 0; i < iNumSections - 1; ++i ) {
pt3 = pPoint3 + v1 * f;
t = ( 1 - std::fabs( f - mid ) * 2 );
@@ -141,9 +229,9 @@ TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &O
&& ( i != iNumSections - 2 ) ) ) {
endvertex.position =
glm::vec3(
pt3.x - Origin.x,
pt3.y - std::sqrt( t ) * fHeightDifference - Origin.y,
pt3.z - Origin.z );
pt3.x - m_origin.x,
pt3.y - std::sqrt( t ) * fHeightDifference - m_origin.y,
pt3.z - m_origin.z );
vertices.emplace_back( startvertex );
vertices.emplace_back( endvertex );
startvertex = endvertex;
@@ -152,9 +240,9 @@ TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &O
}
endvertex.position =
glm::vec3(
pPoint4.x - Origin.x,
pPoint4.y - Origin.y,
pPoint4.z - Origin.z );
pPoint4.x - m_origin.x,
pPoint4.y - m_origin.y,
pPoint4.z - m_origin.z );
vertices.emplace_back( startvertex );
vertices.emplace_back( endvertex );
}
@@ -162,14 +250,14 @@ TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &O
if( Wires > 2 ) {
startvertex.position =
glm::vec3(
pPoint1.x + ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - Origin.x,
pPoint1.y - Origin.y,
pPoint1.z + ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - Origin.z );
pPoint1.x + ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - m_origin.x,
pPoint1.y - m_origin.y,
pPoint1.z + ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - m_origin.z );
endvertex.position =
glm::vec3(
pPoint2.x + ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - Origin.x,
pPoint2.y - Origin.y,
pPoint2.z + ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - Origin.z );
pPoint2.x + ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - m_origin.x,
pPoint2.y - m_origin.y,
pPoint2.z + ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - m_origin.z );
vertices.emplace_back( startvertex );
vertices.emplace_back( endvertex );
}
@@ -179,22 +267,22 @@ TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &O
if( Wires == 4 ) {
startvertex.position =
glm::vec3(
pPoint3.x - Origin.x,
pPoint3.y - 0.65f * fHeightDifference - Origin.y,
pPoint3.z - Origin.z );
pPoint3.x - m_origin.x,
pPoint3.y - 0.65f * fHeightDifference - m_origin.y,
pPoint3.z - m_origin.z );
for( int i = 0; i < iNumSections - 1; ++i ) {
pt3 = pPoint3 + v1 * f;
t = ( 1 - std::fabs( f - mid ) * 2 );
endvertex.position =
glm::vec3(
pt3.x - Origin.x,
pt3.x - m_origin.x,
pt3.y - std::sqrt( t ) * fHeightDifference - (
( ( i == 0 )
|| ( i == iNumSections - 2 ) ) ?
0.25f * fHeightDifference :
0.05 )
- Origin.y,
pt3.z - Origin.z );
- m_origin.y,
pt3.z - m_origin.z );
vertices.emplace_back( startvertex );
vertices.emplace_back( endvertex );
startvertex = endvertex;
@@ -202,9 +290,9 @@ TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &O
}
endvertex.position =
glm::vec3(
pPoint4.x - Origin.x,
pPoint4.y - 0.65f * fHeightDifference - Origin.y,
pPoint4.z - Origin.z );
pPoint4.x - m_origin.x,
pPoint4.y - 0.65f * fHeightDifference - m_origin.y,
pPoint4.z - m_origin.z );
vertices.emplace_back( startvertex );
vertices.emplace_back( endvertex );
}
@@ -223,28 +311,28 @@ TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &O
if( ( i % 2 ) == 0 ) {
startvertex.position =
glm::vec3(
pt3.x - Origin.x,
pt3.y - std::sqrt( t ) * fHeightDifference - ( ( i == 0 ) || ( i == iNumSections - 2 ) ? flo : flo1 ) - Origin.y,
pt3.z - Origin.z );
pt3.x - m_origin.x,
pt3.y - std::sqrt( t ) * fHeightDifference - ( ( i == 0 ) || ( i == iNumSections - 2 ) ? flo : flo1 ) - m_origin.y,
pt3.z - m_origin.z );
endvertex.position =
glm::vec3(
pt4.x - ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - Origin.x,
pt4.y - Origin.y,
pt4.z - ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - Origin.z );
pt4.x - ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - m_origin.x,
pt4.y - m_origin.y,
pt4.z - ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - m_origin.z );
vertices.emplace_back( startvertex );
vertices.emplace_back( endvertex );
}
else {
startvertex.position =
glm::vec3(
pt3.x - Origin.x,
pt3.y - std::sqrt( t ) * fHeightDifference - ( ( i == 0 ) || ( i == iNumSections - 2 ) ? flo : flo1 ) - Origin.y,
pt3.z - Origin.z );
pt3.x - m_origin.x,
pt3.y - std::sqrt( t ) * fHeightDifference - ( ( i == 0 ) || ( i == iNumSections - 2 ) ? flo : flo1 ) - m_origin.y,
pt3.z - m_origin.z );
endvertex.position =
glm::vec3(
pt4.x + ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - Origin.x,
pt4.y - Origin.y,
pt4.z - ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - Origin.z );
pt4.x + ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - m_origin.x,
pt4.y - m_origin.y,
pt4.z - ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - m_origin.z );
vertices.emplace_back( startvertex );
vertices.emplace_back( endvertex );
}
@@ -253,14 +341,14 @@ TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &O
|| ( i == iNumSections - 3 ) ) ) ) {
startvertex.position =
glm::vec3(
pt3.x - Origin.x,
pt3.y - std::sqrt( t ) * fHeightDifference - 0.05 - Origin.y,
pt3.z - Origin.z );
pt3.x - m_origin.x,
pt3.y - std::sqrt( t ) * fHeightDifference - 0.05 - m_origin.y,
pt3.z - m_origin.z );
endvertex.position =
glm::vec3(
pt3.x - Origin.x,
pt3.y - std::sqrt( t ) * fHeightDifference - Origin.y,
pt3.z - Origin.z );
pt3.x - m_origin.x,
pt3.y - std::sqrt( t ) * fHeightDifference - m_origin.y,
pt3.z - m_origin.z );
vertices.emplace_back( startvertex );
vertices.emplace_back( endvertex );
}
@@ -276,55 +364,59 @@ TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &O
int TTraction::TestPoint(glm::dvec3 const &Point)
{ // sprawdzanie, czy przęsła można połączyć
if (!hvNext[0])
if( glm::all( glm::epsilonEqual( Point, pPoint1, 0.025 ) ) )
return 0;
if (!hvNext[1])
if( glm::all( glm::epsilonEqual( Point, pPoint2, 0.025 ) ) )
return 1;
if( ( hvNext[ 0 ] == nullptr )
&& ( glm::all( glm::epsilonEqual( Point, pPoint1, 0.025 ) ) ) ) {
return 0;
}
if( ( hvNext[ 1 ] == nullptr )
&& ( glm::all( glm::epsilonEqual( Point, pPoint2, 0.025 ) ) ) ) {
return 1;
}
return -1;
};
void TTraction::Connect(int my, TTraction *with, int to)
{ //łączenie segmentu (with) od strony (my) do jego (to)
if (my)
{ // do mojego Point2
hvNext[1] = with;
iNext[1] = to;
}
else
{ // do mojego Point1
hvNext[0] = with;
iNext[0] = to;
}
if (to)
{ // do jego Point2
with->hvNext[1] = this;
with->iNext[1] = my;
}
else
{ // do jego Point1
with->hvNext[0] = this;
with->iNext[0] = my;
}
if (hvNext[0]) // jeśli z obu stron podłączony
if (hvNext[1])
iLast = 0; // to nie jest ostatnim
if (with->hvNext[0]) // temu też, bo drugi raz łączenie się nie nie wykona
if (with->hvNext[1])
with->iLast = 0; // to nie jest ostatnim
};
//łączenie segmentu (with) od strony (my) do jego (to)
void
TTraction::Connect(int my, TTraction *with, int to) {
bool TTraction::WhereIs()
{ // ustalenie przedostatnich przęseł
if (iLast)
return (iLast == 1); // ma już ustaloną informację o położeniu
if (hvNext[0] ? hvNext[0]->iLast == 1 : false) // jeśli poprzedni jest ostatnim
iLast = 2; // jest przedostatnim
else if (hvNext[1] ? hvNext[1]->iLast == 1 : false) // jeśli następny jest ostatnim
iLast = 2; // jest przedostatnim
return (iLast == 1); // ostatnie będą dostawać zasilanie
};
hvNext[ my ] = with;
iNext[ my ] = to;
if( ( hvNext[ 0 ] != nullptr )
&& ( hvNext[ 1 ] != nullptr ) ) {
// jeśli z obu stron podłączony to nie jest ostatnim
iLast = 0;
}
with->hvNext[ to ] = this;
with->iNext[ to ] = my;
if( ( with->hvNext[ 0 ] != nullptr )
&& ( with->hvNext[ 1 ] != nullptr ) ) {
// temu też, bo drugi raz łączenie się nie nie wykona
with->iLast = 0;
}
}
// ustalenie przedostatnich przęseł
bool TTraction::WhereIs() {
if( iLast ) {
// ma już ustaloną informację o położeniu
return ( iLast & 1 );
}
for( int endindex = 0; endindex < 2; ++endindex ) {
if( hvNext[ endindex ] == nullptr ) {
// no neighbour piece means this one is last
iLast |= 1;
}
else if( hvNext[ endindex ]->hvNext[ 1 - iNext[ endindex ] ] == nullptr ) {
// otherwise if that neighbour has no further connection on the opposite end then this piece is second-last
iLast |= 2;
}
}
return (iLast & 1); // ostatnie będą dostawać zasilanie
}
void TTraction::Init()
{ // przeliczenie parametrów
@@ -441,6 +533,18 @@ double TTraction::VoltageGet(double u, double i)
return 0.0; // gdy nie podłączony wcale?
};
// calculates path's bounding radius
void
TTraction::radius_() {
auto const points = endpoints();
for( auto &point : points ) {
m_area.radius = std::max(
m_area.radius,
static_cast<float>( glm::length( m_area.center - point ) ) );
}
}
glm::vec3
TTraction::wire_color() const {
@@ -488,30 +592,41 @@ TTraction::wire_color() const {
case 1: {
// czerwone z podłączonym zasilaniem 1
color.r = 1.0f;
color.g = 0.0f;
color.b = 0.0f;
// color.r = 1.0f;
// color.g = 0.0f;
// color.b = 0.0f;
// cyan
color = glm::vec3 { 0.f, 174.f / 255.f, 239.f / 255.f };
break;
}
case 2: {
// zielone z podłączonym zasilaniem 2
color.r = 0.0f;
color.g = 1.0f;
color.b = 0.0f;
// color.r = 0.0f;
// color.g = 1.0f;
// color.b = 0.0f;
// yellow
color = glm::vec3 { 240.f / 255.f, 228.f / 255.f, 0.f };
break;
}
case 3: {
//żółte z podłączonym zasilaniem z obu stron
color.r = 1.0f;
color.g = 1.0f;
color.b = 0.0f;
// color.r = 1.0f;
// color.g = 1.0f;
// color.b = 0.0f;
// green
color = glm::vec3 { 0.f, 239.f / 255.f, 118.f / 255.f };
break;
}
case 4: {
// niebieskie z podłączonym zasilaniem
color.r = 0.5f;
color.g = 0.5f;
color.b = 1.0f;
// color.r = 0.5f;
// color.g = 0.5f;
// color.b = 1.0f;
// white for powered, red for ends
color = (
psPowered != nullptr ?
glm::vec3{ 239.f / 255.f, 239.f / 255.f, 239.f / 255.f } :
glm::vec3{ 239.f / 255.f, 128.f / 255.f, 128.f / 255.f } );
break;
}
default: { break; }
@@ -521,6 +636,262 @@ TTraction::wire_color() const {
color.g *= 0.6f;
color.b *= 0.6f;
}
/*
switch( iTries ) {
case 0: {
color = glm::vec3{ 239.f / 255.f, 128.f / 255.f, 128.f / 255.f }; // red
break;
}
case 1: {
color = glm::vec3{ 240.f / 255.f, 228.f / 255.f, 0.f }; // yellow
break;
}
case 5: {
color = glm::vec3{ 0.f / 255.f, 239.f / 255.f, 118.f / 255.f }; // green
break;
}
default: {
color = glm::vec3{ 239.f / 255.f, 239.f / 255.f, 239.f / 255.f };
break;
}
}
*/
/*
switch( iLast ) {
case 0: {
color = glm::vec3{ 240.f / 255.f, 228.f / 255.f, 0.f }; // yellow
break;
}
case 1: {
color = glm::vec3{ 239.f / 255.f, 128.f / 255.f, 128.f / 255.f }; // red
break;
}
case 2: {
color = glm::vec3{ 0.f / 255.f, 239.f / 255.f, 118.f / 255.f }; // green
break;
}
default: {
color = glm::vec3{ 239.f / 255.f, 239.f / 255.f, 239.f / 255.f };
break;
}
}
*/
}
return color;
}
// legacy method, initializes traction after deserialization from scenario file
void
traction_table::InitTraction() {
//łączenie drutów ze sobą oraz z torami i eventami
// TGroundNode *nCurrent, *nTemp;
// TTraction *tmp; // znalezione przęsło
int connection { -1 };
TTraction *matchingtraction { nullptr };
for( auto *traction : m_items ) {
// podłączenie do zasilacza, żeby można było sumować prąd kilku pojazdów
// a jednocześnie z jednego miejsca zmieniać napięcie eventem
// wykonywane najpierw, żeby można było logować podłączenie 2 zasilaczy do jednego drutu
// izolator zawieszony na przęśle jest ma być osobnym odcinkiem drutu o długości ok. 1m,
// podłączonym do zasilacza o nazwie "*" (gwiazka); "none" nie będzie odpowiednie
auto *powersource = simulation::Powergrid.find( traction->asPowerSupplyName );
if( powersource ) {
// jak zasilacz znaleziony to podłączyć do przęsła
traction->PowerSet( powersource );
}
else {
if( ( traction->asPowerSupplyName != "*" ) // gwiazdka dla przęsła z izolatorem
&& ( traction->asPowerSupplyName != "none" ) ) { // dopuszczamy na razie brak podłączenia?
// logowanie błędu i utworzenie zasilacza o domyślnej zawartości
ErrorLog( "Bad scenario: traction piece connected to nonexistent power source \"" + traction->asPowerSupplyName + "\"" );
scene::node_data nodedata;
nodedata.name = traction->asPowerSupplyName;
powersource = new TTractionPowerSource( nodedata );
powersource->Init( traction->NominalVoltage, traction->MaxCurrent );
simulation::Powergrid.insert( powersource );
}
}
}
#ifdef EU07_IGNORE_LEGACYPROCESSINGORDER
for( auto *traction : m_items ) {
#else
// NOTE: legacy code peformed item operations last-to-first due to way the items were added to the list
// this had impact in situations like two possible connection candidates, where only the first one would be used
for( auto first = std::rbegin(m_items); first != std::rend(m_items); ++first ) {
auto *traction = *first;
#endif
if( traction->hvNext[ 0 ] == nullptr ) {
// tylko jeśli jeszcze nie podłączony
std::tie( matchingtraction, connection ) = simulation::Region->find_traction( traction->pPoint1, traction );
switch (connection) {
case 0:
case 1: {
traction->Connect( 0, matchingtraction, connection );
break;
}
default: {
break;
}
}
if( traction->hvNext[ 0 ] ) {
// jeśli został podłączony
if( ( traction->psSection != nullptr )
&& ( matchingtraction->psSection != nullptr ) ) {
// tylko przęsło z izolatorem może nie mieć zasilania, bo ma 2, trzeba sprawdzać sąsiednie
if( traction->psSection != matchingtraction->psSection ) {
// połączone odcinki mają różne zasilacze
// to może być albo podłączenie podstacji lub kabiny sekcyjnej do sekcji, albo błąd
if( ( true == traction->psSection->bSection )
&& ( false == matchingtraction->psSection->bSection ) ) {
//(tmp->psSection) jest podstacją, a (Traction->psSection) nazwą sekcji
matchingtraction->PowerSet( traction->psSection ); // zastąpienie wskazaniem sekcji
}
else if( ( false == traction->psSection->bSection )
&& ( true == matchingtraction->psSection->bSection ) ) {
//(Traction->psSection) jest podstacją, a (tmp->psSection) nazwą sekcji
traction->PowerSet( matchingtraction->psSection ); // zastąpienie wskazaniem sekcji
}
else {
// jeśli obie to sekcje albo obie podstacje, to będzie błąd
ErrorLog( "Bad scenario: faulty traction power connection at location " + to_string( traction->pPoint1 ) );
}
}
}
}
}
if( traction->hvNext[ 1 ] == nullptr ) {
// tylko jeśli jeszcze nie podłączony
std::tie( matchingtraction, connection ) = simulation::Region->find_traction( traction->pPoint2, traction );
switch (connection) {
case 0:
case 1: {
traction->Connect( 1, matchingtraction, connection );
break;
}
default: {
break;
}
}
if( traction->hvNext[ 1 ] ) {
// jeśli został podłączony
if( ( traction->psSection != nullptr )
&& ( matchingtraction->psSection != nullptr ) ) {
// tylko przęsło z izolatorem może nie mieć zasilania, bo ma 2, trzeba sprawdzać sąsiednie
if( traction->psSection != matchingtraction->psSection ) {
// to może być albo podłączenie podstacji lub kabiny sekcyjnej do sekcji, albo błąd
if( ( true == traction->psSection->bSection )
&& ( false == matchingtraction->psSection->bSection ) ) {
//(tmp->psSection) jest podstacją, a (Traction->psSection) nazwą sekcji
matchingtraction->PowerSet( traction->psSection ); // zastąpienie wskazaniem sekcji
}
else if( ( false == traction->psSection->bSection )
&& ( true == matchingtraction->psSection->bSection ) ) {
//(Traction->psSection) jest podstacją, a (tmp->psSection) nazwą sekcji
traction->PowerSet( matchingtraction->psSection ); // zastąpienie wskazaniem sekcji
}
else {
// jeśli obie to sekcje albo obie podstacje, to będzie błąd
ErrorLog( "Bad scenario: faulty traction power connection at location " + to_string( traction->pPoint2 ) );
}
}
}
}
}
}
auto endcount { 0 };
for( auto *traction : m_items ) {
// operacje mające na celu wykrywanie bieżni wspólnych i łączenie przęseł naprążania
if( traction->WhereIs() ) {
// true for outer pieces of the traction section
traction->iTries = 5; // oznaczanie końcowych
++endcount;
}
if (traction->fResistance[0] == 0.0) {
// obliczanie przęseł w segmencie z bezpośrednim zasilaniem
traction->ResistanceCalc();
traction->iTries = 0; // nie potrzeba mu szukać zasilania
}
}
std::vector<TTraction *> ends; ends.reserve( endcount );
for( auto *traction : m_items ) {
//łączenie bieżni wspólnych, w tym oznaczanie niepodanych jawnie
if( false == traction->asParallel.empty() ) {
// będzie wskaźnik na inne przęsło
if( ( traction->asParallel == "none" )
|| ( traction->asParallel == "*" ) ) {
// jeśli nieokreślone
traction->iLast |= 2; // jakby przedostatni - niech po prostu szuka (iLast już przeliczone)
}
else if( traction->hvParallel == nullptr ) {
// jeśli jeszcze nie został włączony w kółko
auto *nTemp = find( traction->asParallel );
if( nTemp != nullptr ) {
// o ile zostanie znalezione przęsło o takiej nazwie
if( nTemp->hvParallel == nullptr ) {
// jeśli tamten jeszcze nie ma wskaźnika bieżni wspólnej
traction->hvParallel = nTemp; // wpisać siebie i dalej dać mu wskaźnik zwrotny
}
else {
// a jak ma, to albo dołączyć się do kółeczka
traction->hvParallel = nTemp->hvParallel; // przjąć dotychczasowy wskaźnik od niego
}
nTemp->hvParallel = traction; // i na koniec ustawienie wskaźnika zwrotnego
}
if( traction->hvParallel == nullptr ) {
ErrorLog( "Missed overhead: " + traction->asParallel ); // logowanie braku
}
}
}
if( traction->iTries == 5 ) {
// jeśli zaznaczony do podłączenia
// wypełnianie tabeli końców w celu szukania im połączeń
ends.emplace_back( traction );
}
}
bool connected; // nieefektywny przebieg kończy łączenie
do {
// ustalenie zastępczej rezystancji dla każdego przęsła
// flaga podłączonych przęseł końcowych: -1=puste wskaźniki, 0=coś zostało, 1=wykonano łączenie
connected = false;
for( auto &end : ends ) {
// załatwione będziemy zerować
if( end == nullptr ) { continue; }
// każdy przebieg to próba podłączenia końca segmentu naprężania do innego zasilanego przęsła
if( end->hvNext[ 0 ] != nullptr ) {
// jeśli końcowy ma ciąg dalszy od strony 0 (Point1), szukamy odcinka najbliższego do Point2
std::tie( matchingtraction, connection ) = simulation::Region->find_traction( end->pPoint2, end, 0 );
if( matchingtraction != nullptr ) {
// jak znalezione przęsło z zasilaniem, to podłączenie "równoległe"
end->ResistanceCalc( 0, matchingtraction->fResistance[ connection ], matchingtraction->psPower[ connection ] );
// jak coś zostało podłączone, to może zasilanie gdzieś dodatkowo dotrze
connected = true;
end = nullptr;
}
}
else if( end->hvNext[ 1 ] != nullptr ) {
// jeśli końcowy ma ciąg dalszy od strony 1 (Point2), szukamy odcinka najbliższego do Point1
std::tie( matchingtraction, connection ) = simulation::Region->find_traction( end->pPoint1, end, 1 );
if( matchingtraction != nullptr ) {
// jak znalezione przęsło z zasilaniem, to podłączenie "równoległe"
end->ResistanceCalc( 1, matchingtraction->fResistance[ connection ], matchingtraction->psPower[ connection ] );
// jak coś zostało podłączone, to może zasilanie gdzieś dodatkowo dotrze
connected = true;
end = nullptr;
}
}
else {
// gdy koniec jest samotny, to na razie nie zostanie podłączony (nie powinno takich być)
end = nullptr;
}
}
} while( true == connected );
}

View File

@@ -10,14 +10,17 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include <string>
#include "GL/glew.h"
#include "dumb3d.h"
#include "openglgeometrybank.h"
#include "scenenode.h"
#include "Segment.h"
#include "material.h"
#include "Names.h"
#include "Model3d.h"
class TTractionPowerSource;
class TTraction
{ // drut zasilający, dla wskaźników używać przedrostka "hv"
class TTraction : public editor::basic_node {
friend class opengl_renderer;
public: // na razie
@@ -25,11 +28,11 @@ class TTraction
TTractionPowerSource *psPowered { nullptr }; // ustawione tylko dla bezpośrednio zasilanego przęsła
TTraction *hvNext[ 2 ] { nullptr, nullptr }; //łączenie drutów w sieć
int iNext[ 2 ] { 0, 0 }; // do którego końca się łączy
int iLast { 1 }; //że niby ostatni drut // ustawiony bit 0, jeśli jest ostatnim drutem w sekcji; bit1 - przedostatni
int iLast { 0 }; //że niby ostatni drut // ustawiony bit 0, jeśli jest ostatnim drutem w sekcji; bit1 - przedostatni
public:
glm::dvec3 pPoint1, pPoint2, pPoint3, pPoint4;
glm::dvec3 vParametric; // współczynniki równania parametrycznego odcinka
double fHeightDifference { 0.0 }; //,fMiddleHeight;
double fHeightDifference { 0.0 };
int iNumSections { 0 };
float NominalVoltage { 0.0f };
float MaxCurrent { 0.0f };
@@ -44,14 +47,27 @@ class TTraction
std::string asParallel; // nazwa przęsła, z którym może być bieżnia wspólna
TTraction *hvParallel { nullptr }; // jednokierunkowa i zapętlona lista przęseł ewentualnej bieżni wspólnej
float fResistance[ 2 ] { -1.0f, -1.0f }; // rezystancja zastępcza do punktu zasilania (0: przęsło zasilane, <0: do policzenia)
int iTries { 0 };
int iTries { 1 }; // 0 is used later down the road to mark directly powered pieces
int PowerState { 0 }; // type of incoming power, if any
// visualization data
glm::dvec3 m_origin;
geometry_handle m_geometry;
TTraction( scene::node_data const &Nodedata );
// legacy constructor
TTraction( std::string Name );
void Load( cParser *parser, glm::dvec3 const &pOrigin );
// set origin point
void
origin( glm::dvec3 Origin ) {
m_origin = Origin; }
// retrieves list of the track's end points
std::vector<glm::dvec3>
endpoints() const;
// creates geometry data in specified geometry bank. returns: number of created elements, or NULL
// NOTE: deleting nodes doesn't currently release geometry data owned by the node. TODO: implement erasing individual geometry chunks and banks
std::size_t create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin );
std::size_t create_geometry( geometrybank_handle const &Bank );
int TestPoint(glm::dvec3 const &Point);
void Connect(int my, TTraction *with, int to);
void Init();
@@ -59,8 +75,25 @@ class TTraction
void ResistanceCalc(int d = -1, double r = 0, TTractionPowerSource *ps = nullptr);
void PowerSet(TTractionPowerSource *ps);
double VoltageGet(double u, double i);
protected:
// calculates piece's bounding radius
void
radius_();
private:
glm::vec3 wire_color() const;
};
// collection of virtual tracks and roads present in the scene
class traction_table : public basic_table<TTraction> {
public:
// legacy method, initializes traction after deserialization from scenario file
void
InitTraction();
};
//---------------------------------------------------------------------------

View File

@@ -15,19 +15,17 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "TractionPower.h"
#include "Logs.h"
#include "Ground.h"
//---------------------------------------------------------------------------
TTractionPowerSource::TTractionPowerSource(TGroundNode const *node) :
gMyNode( node )
{
psNode[0] = nullptr; // sekcje zostaną podłączone do zasilaczy
psNode[1] = nullptr;
};
TTractionPowerSource::TTractionPowerSource( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {}
// legacy constructor
TTractionPowerSource::TTractionPowerSource( std::string Name ) {
TTractionPowerSource::~TTractionPowerSource(){};
m_name = Name;
}
void TTractionPowerSource::Init(double const u, double const i)
{ // ustawianie zasilacza przy braku w scenerii
@@ -36,61 +34,62 @@ void TTractionPowerSource::Init(double const u, double const i)
MaxOutputCurrent = i;
};
bool TTractionPowerSource::Load(cParser *parser)
{
std::string token;
// AnsiString str;
// str= Parser->GetNextSymbol()LowerCase();
// asName= str;
parser->getTokens(5);
*parser >> NominalVoltage >> VoltageFrequency >> InternalRes >> MaxOutputCurrent >>
FastFuseTimeOut;
parser->getTokens();
*parser >> FastFuseRepetition;
parser->getTokens();
*parser >> SlowFuseTimeOut;
parser->getTokens();
*parser >> token;
if (token.compare("recuperation") == 0)
Recuperation = true;
else if (token.compare("section") == 0) // odłącznik sekcyjny
bSection = true; // nie jest źródłem zasilania, a jedynie informuje o prądzie odłączenia
// sekcji z obwodu
parser->getTokens();
*parser >> token;
if (token.compare("end") != 0)
Error("tractionpowersource end statement missing");
// if (!bSection) //odłącznik sekcji zasadniczo nie ma impedancji (0.01 jest OK)
if (InternalRes < 0.1) // coś mała ta rezystancja była...
InternalRes = 0.2; // tak około 0.2, wg
// http://www.ikolej.pl/fileadmin/user_upload/Seminaria_IK/13_05_07_Prezentacja_Kruczek.pdf
return true;
};
bool TTractionPowerSource::Load(cParser *parser) {
bool TTractionPowerSource::Render()
{
parser->getTokens( 10, false );
*parser
>> m_area.center.x
>> m_area.center.y
>> m_area.center.z
>> NominalVoltage
>> VoltageFrequency
>> InternalRes
>> MaxOutputCurrent
>> FastFuseTimeOut
>> FastFuseRepetition
>> SlowFuseTimeOut;
std::string token { parser->getToken<std::string>() };
if( token == "recuperation" ) {
Recuperation = true;
}
else if( token == "section" ) {
// odłącznik sekcyjny
// nie jest źródłem zasilania, a jedynie informuje o prądzie odłączenia sekcji z obwodu
bSection = true;
}
// skip rest of the section
while( ( false == token.empty() )
&& ( token != "end" ) ) {
token = parser->getToken<std::string>();
}
if( InternalRes < 0.1 ) {
// coś mała ta rezystancja była...
// tak około 0.2, wg
// http://www.ikolej.pl/fileadmin/user_upload/Seminaria_IK/13_05_07_Prezentacja_Kruczek.pdf
InternalRes = 0.2;
}
return true;
};
bool TTractionPowerSource::Update(double dt)
{ // powinno być wykonane raz na krok fizyki
// if (NominalVoltage * TotalPreviousAdmitance >
// MaxOutputCurrent * 0.00000005) // iloczyn napięcia i admitancji daje prąd
// ErrorLog("Power overload: \"" + gMyNode->asName + "\" with current " + AnsiString(NominalVoltage * TotalPreviousAdmitance) + "A");
if (NominalVoltage * TotalPreviousAdmitance >
MaxOutputCurrent) // iloczyn napięcia i admitancji daje prąd
{
// iloczyn napięcia i admitancji daje prąd
if (NominalVoltage * TotalPreviousAdmitance > MaxOutputCurrent) {
FastFuse = true;
FuseCounter += 1;
if (FuseCounter > FastFuseRepetition)
{
if (FuseCounter > FastFuseRepetition) {
SlowFuse = true;
ErrorLog("Power overload: \"" + gMyNode->asName + "\" disabled for " +
std::to_string(SlowFuseTimeOut) + "s");
ErrorLog( "Power overload: \"" + m_name + "\" disabled for " + std::to_string( SlowFuseTimeOut ) + "s" );
}
else
ErrorLog("Power overload: \"" + gMyNode->asName + "\" disabled for " +
std::to_string(FastFuseTimeOut) + "s");
else {
ErrorLog( "Power overload: \"" + m_name + "\" disabled for " + std::to_string( FastFuseTimeOut ) + "s" );
}
FuseTimer = 0;
}
if (FastFuse || SlowFuse)
@@ -145,4 +144,15 @@ void TTractionPowerSource::PowerSet(TTractionPowerSource *ps)
// else ErrorLog("nie może być więcej punktów zasilania niż dwa");
};
// legacy method, calculates changes in simulation state over specified time
void
powergridsource_table::update( double const Deltatime ) {
for( auto *powersource : m_items ) {
powersource->Update( Deltatime );
}
}
//---------------------------------------------------------------------------

View File

@@ -7,14 +7,14 @@ obtain one at
http://mozilla.org/MPL/2.0/.
*/
#ifndef TractionPowerH
#define TractionPowerH
#pragma once
#include "scenenode.h"
#include "parser.h" //Tolaris-010603
#include "Names.h"
class TGroundNode;
class TTractionPowerSource : public editor::basic_node {
class TTractionPowerSource
{
private:
double NominalVoltage = 0.0;
double VoltageFrequency = 0.0;
@@ -33,27 +33,34 @@ class TTractionPowerSource
bool SlowFuse = false;
double FuseTimer = 0.0;
int FuseCounter = 0;
TGroundNode const *gMyNode = nullptr; // wskaźnik na węzeł rodzica
protected:
public: // zmienne publiczne
TTractionPowerSource *psNode[2]; // zasilanie na końcach dla sekcji
public:
// zmienne publiczne
TTractionPowerSource *psNode[ 2 ] = { nullptr, nullptr }; // zasilanie na końcach dla sekcji
bool bSection = false; // czy jest sekcją
public:
// AnsiString asName;
TTractionPowerSource(TGroundNode const *node);
~TTractionPowerSource();
TTractionPowerSource( scene::node_data const &Nodedata );
// legacy constructor
TTractionPowerSource( std::string Name );
void Init(double const u, double const i);
bool Load(cParser *parser);
bool Render();
bool Update(double dt);
double CurrentGet(double res);
void VoltageSet(double const v)
{
NominalVoltage = v;
};
void VoltageSet(double const v) {
NominalVoltage = v; };
void PowerSet(TTractionPowerSource *ps);
};
// collection of generators for power grid present in the scene
class powergridsource_table : public basic_table<TTractionPowerSource> {
public:
// legacy method, calculates changes in simulation state over specified time
void
update( double const Deltatime );
};
//---------------------------------------------------------------------------
#endif

View File

@@ -360,11 +360,6 @@ TTrain::TTrain() {
TTrain::~TTrain()
{
if (DynamicObject)
if (DynamicObject->Mechanik)
DynamicObject->Mechanik->TakeControl(
true); // likwidacja kabiny wymaga przejęcia przez AI
sound_man->destroy_sound(&dsbNastawnikJazdy);
sound_man->destroy_sound(&dsbNastawnikBocz);
sound_man->destroy_sound(&dsbRelay);
@@ -515,11 +510,13 @@ PyObject *TTrain::GetTrainState() {
PyDict_SetItemString( dict, "velocity", PyGetFloat( mover->Vel ) );
PyDict_SetItemString( dict, "tractionforce", PyGetFloat( mover->Ft ) );
PyDict_SetItemString( dict, "slipping_wheels", PyGetBool( mover->SlippingWheels ) );
PyDict_SetItemString( dict, "sanding", PyGetBool( mover->SandDose ));
// electric current data
PyDict_SetItemString( dict, "traction_voltage", PyGetFloat( mover->RunningTraction.TractionVoltage ) );
PyDict_SetItemString( dict, "voltage", PyGetFloat( mover->Voltage ) );
PyDict_SetItemString( dict, "im", PyGetFloat( mover->Im ) );
PyDict_SetItemString( dict, "fuse", PyGetBool( mover->FuseFlag ) );
PyDict_SetItemString( dict, "epfuse", PyGetBool( mover->EpFuse ));
// induction motor state data
const char* TXTT[ 10 ] = { "fd", "fdt", "fdb", "pd", "pdt", "pdb", "itothv", "1", "2", "3" };
const char* TXTC[ 10 ] = { "fr", "frt", "frb", "pr", "prt", "prb", "im", "vm", "ihv", "uhv" };
@@ -559,6 +556,7 @@ PyObject *TTrain::GetTrainState() {
PyDict_SetItemString( dict, ( std::string( "code_" ) + std::to_string( i + 1 ) ).c_str(), PyGetString( std::string( std::to_string( iUnits[ i ] ) +
cCode[ i ] ).c_str() ) );
PyDict_SetItemString( dict, ( std::string( "car_name" ) + std::to_string( i + 1 ) ).c_str(), PyGetString( asCarName[ i ].c_str() ) );
PyDict_SetItemString( dict, ( std::string( "slip_" ) + std::to_string( i + 1 )).c_str(), PyGetBool( bSlip[i]) );
}
// ai state data
auto const &driver = DynamicObject->Mechanik;
@@ -1630,7 +1628,7 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com
// sound feedback, engine start for diesel vehicle
Train->play_sound( Train->dsbDieselIgnition );
// side-effects
Train->mvControlled->ConverterSwitch( Train->ggConverterButton.GetValue() > 0.5 );
Train->mvControlled->ConverterSwitch( ( Train->ggConverterButton.GetValue() > 0.5 ) || ( Train->mvControlled->ConverterStart == start::automatic ) );
Train->mvControlled->CompressorSwitch( Train->ggCompressorButton.GetValue() > 0.5 );
}
}
@@ -3400,19 +3398,33 @@ if
d = d->Prev(); // w drugą stronę też
}
}
else if (cKey == GLFW_KEY_RIGHT_BRACKET)
{
while (d)
{
d->Move(-100.0 * d->DirectionGet());
d = d->Next(); // pozostałe też
}
d = DynamicObject->Prev();
while (d)
{
d->Move(-100.0 * d->DirectionGet());
d = d->Prev(); // w drugą stronę też
}
else if (cKey == GLFW_KEY_RIGHT_BRACKET)
{
while (d)
{
d->Move(-100.0 * d->DirectionGet());
d = d->Next(); // pozostałe też
}
d = DynamicObject->Prev();
while (d)
{
d->Move(-100.0 * d->DirectionGet());
d = d->Prev(); // w drugą stronę też
}
}
else if (cKey == GLFW_KEY_TAB)
{
while (d)
{
d->MoverParameters->V+= d->DirectionGet()*2.78;
d = d->Next(); // pozostałe też
}
d = DynamicObject->Prev();
while (d)
{
d->MoverParameters->V += d->DirectionGet()*2.78;
d = d->Prev(); // w drugą stronę też
}
}
}
if (cKey == GLFW_KEY_MINUS)
@@ -3697,21 +3709,22 @@ bool TTrain::Update( double const Deltatime )
bDoors[i][2] = (p->dDoorMoveL > 0.001);
iDoorNo[i] = p->iAnimType[ANIM_DOORS];
iUnits[i] = iUnitNo;
cCode[i] = p->MoverParameters->TypeName[p->MoverParameters->TypeName.length()];
asCarName[i] = p->GetName();
cCode[i] = p->MoverParameters->TypeName[p->MoverParameters->TypeName.length() - 1];
asCarName[i] = p->name();
bPants[iUnitNo - 1][0] = (bPants[iUnitNo - 1][0] || p->MoverParameters->PantFrontUp);
bPants[iUnitNo - 1][1] = (bPants[iUnitNo - 1][1] || p->MoverParameters->PantRearUp);
bComp[iUnitNo - 1][0] = (bComp[iUnitNo - 1][0] || p->MoverParameters->CompressorAllow);
bSlip[i] = p->MoverParameters->SlippingWheels;
if (p->MoverParameters->CompressorSpeed > 0.00001)
{
bComp[iUnitNo - 1][1] = (bComp[iUnitNo - 1][1] || p->MoverParameters->CompressorFlag);
}
if ((in < 8) && (p->MoverParameters->eimc[eimc_p_Pmax] > 1))
{
fEIMParams[1 + in][0] = p->MoverParameters->eimv[eimv_Fr];
fEIMParams[1 + in][0] = p->MoverParameters->eimv[eimv_Fmax];
fEIMParams[1 + in][1] = Max0R(fEIMParams[1 + in][0], 0);
fEIMParams[1 + in][2] = -Min0R(fEIMParams[1 + in][0], 0);
fEIMParams[1 + in][3] = p->MoverParameters->eimv[eimv_Fr] /
fEIMParams[1 + in][3] = p->MoverParameters->eimv[eimv_Fmax] /
Max0R(p->MoverParameters->eimv[eimv_Fful], 1);
fEIMParams[1 + in][4] = Max0R(fEIMParams[1 + in][3], 0);
fEIMParams[1 + in][5] = -Min0R(fEIMParams[1 + in][3], 0);
@@ -3743,6 +3756,7 @@ bool TTrain::Update( double const Deltatime )
bDoors[i][0] = false;
bDoors[i][1] = false;
bDoors[i][2] = false;
bSlip[i] = false;
iUnits[i] = 0;
cCode[i] = 0; //'0';
asCarName[i] = "";
@@ -6385,7 +6399,7 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
}
else if (token == "pyscreen:")
{
pyScreens.init(*parser, DynamicObject->mdKabina, DynamicObject->GetName(),
pyScreens.init(*parser, DynamicObject->mdKabina, DynamicObject->name(),
NewCabNo);
}
// btLampkaUnknown.Init("unknown",mdKabina,false);
@@ -6868,9 +6882,11 @@ void TTrain::set_cab_controls() {
if( true == bCabLightDim ) {
ggCabLightDimButton.PutValue( 1.0 );
}
if( true == InstrumentLightActive ) {
ggInstrumentLightButton.PutValue( 1.0 );
}
ggInstrumentLightButton.PutValue( (
InstrumentLightActive ?
1.0 :
0.0 ) );
// doors
// NOTE: we're relying on the cab models to have switches reversed for the rear cab(?)
ggDoorLeftButton.PutValue( mvOccupied->DoorLeftOpened ? 1.0 : 0.0 );
@@ -7168,7 +7184,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con
/*
TGauge *gg { nullptr }; // roboczy wskaźnik na obiekt animujący gałkę
*/
std::unordered_map<std::string, TGauge &> gauges = {
std::unordered_map<std::string, TGauge &> const gauges = {
{ "mainctrl:", ggMainCtrl },
{ "scndctrl:", ggScndCtrl },
{ "dirkey:" , ggDirKey },

View File

@@ -72,12 +72,14 @@ public:
class TTrain
{
friend class TWorld; // temporary due to use of play_sound TODO: refactor this
public:
bool CabChange(int iDirection);
bool ShowNextCurrent; // pokaz przd w podlaczonej lokomotywie (ET41)
bool InitializeCab(int NewCabNo, std::string const &asFileName);
TTrain();
~TTrain();
~TTrain();
// McZapkie-010302
bool Init(TDynamicObject *NewDynamicObject, bool e3d = false);
void OnKeyDown(int cKey);
@@ -468,6 +470,7 @@ public: // reszta może by?publiczna
int iUnits[20]; // numer jednostki
int iDoorNo[20]; // liczba drzwi
char cCode[20]; // kod pojazdu
bool bSlip[20]; // poślizg kół pojazdu
std::string asCarName[20]; // nazwa czlonu
bool bMains[8]; // WSy
float fCntVol[8]; // napiecie NN

View File

@@ -15,11 +15,10 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "TrkFoll.h"
#include "simulation.h"
#include "Globals.h"
#include "Logs.h"
#include "Driver.h"
#include "DynObj.h"
#include "Event.h"
TTrackFollower::~TTrackFollower()
{
@@ -116,7 +115,7 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
&& ( pCurrentTrack->evEvent1 )
&& ( !pCurrentTrack->evEvent1->iQueued ) ) {
// dodanie do kolejki
Global::AddToQuery( pCurrentTrack->evEvent1, Owner );
simulation::Events.AddToQuery( pCurrentTrack->evEvent1, Owner );
}
}
}
@@ -127,7 +126,7 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
-1)) // McZapkie-280503: wyzwalanie eventall dla wszystkich pojazdow
if (bPrimary && pCurrentTrack->evEventall1 &&
(!pCurrentTrack->evEventall1->iQueued))
Global::AddToQuery(pCurrentTrack->evEventall1, Owner); // dodanie do kolejki
simulation::Events.AddToQuery(pCurrentTrack->evEventall1, Owner); // dodanie do kolejki
// Owner->RaAxleEvent(pCurrentTrack->Eventall1); //Ra: dynamic zdecyduje, czy dodać
// do kolejki
}
@@ -142,7 +141,7 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
if( ( bPrimary )
&& ( pCurrentTrack->evEvent2 )
&& ( !pCurrentTrack->evEvent2->iQueued ) ) {
Global::AddToQuery( pCurrentTrack->evEvent2, Owner );
simulation::Events.AddToQuery( pCurrentTrack->evEvent2, Owner );
}
}
}
@@ -154,7 +153,7 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
if( ( bPrimary )
&& ( pCurrentTrack->evEventall2 )
&& ( !pCurrentTrack->evEventall2->iQueued ) ) {
Global::AddToQuery( pCurrentTrack->evEventall2, Owner );
simulation::Events.AddToQuery( pCurrentTrack->evEventall2, Owner );
}
}
// Owner->RaAxleEvent(pCurrentTrack->Eventall2); //Ra: dynamic zdecyduje, czy dodać
@@ -167,13 +166,13 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
// tylko dla jednego członu
if( pCurrentTrack->evEvent0 )
if( !pCurrentTrack->evEvent0->iQueued )
Global::AddToQuery( pCurrentTrack->evEvent0, Owner );
simulation::Events.AddToQuery( pCurrentTrack->evEvent0, Owner );
}
// Owner->RaAxleEvent(pCurrentTrack->Event0); //Ra: dynamic zdecyduje, czy dodać do
// kolejki
if (pCurrentTrack->evEventall0)
if (!pCurrentTrack->evEventall0->iQueued)
Global::AddToQuery(pCurrentTrack->evEventall0, Owner);
simulation::Events.AddToQuery(pCurrentTrack->evEventall0, Owner);
// Owner->RaAxleEvent(pCurrentTrack->Eventall0); //Ra: dynamic zdecyduje, czy dodać
// do kolejki
}
@@ -275,14 +274,14 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
// if (Owner->MoverParameters->CabNo!=0)
{
if (pCurrentTrack->evEvent1 && pCurrentTrack->evEvent1->fDelay <= -1.0f)
Global::AddToQuery(pCurrentTrack->evEvent1, Owner);
simulation::Events.AddToQuery(pCurrentTrack->evEvent1, Owner);
if (pCurrentTrack->evEvent2 && pCurrentTrack->evEvent2->fDelay <= -1.0f)
Global::AddToQuery(pCurrentTrack->evEvent2, Owner);
simulation::Events.AddToQuery(pCurrentTrack->evEvent2, Owner);
}
if (pCurrentTrack->evEventall1 && pCurrentTrack->evEventall1->fDelay <= -1.0f)
Global::AddToQuery(pCurrentTrack->evEventall1, Owner);
simulation::Events.AddToQuery(pCurrentTrack->evEventall1, Owner);
if (pCurrentTrack->evEventall2 && pCurrentTrack->evEventall2->fDelay <= -1.0f)
Global::AddToQuery(pCurrentTrack->evEventall2, Owner);
simulation::Events.AddToQuery(pCurrentTrack->evEventall2, Owner);
}
fCurrentDistance = s;
// fDistance=0;

751
World.cpp

File diff suppressed because it is too large Load Diff

11
World.h
View File

@@ -11,14 +11,16 @@ http://mozilla.org/MPL/2.0/.
#include <GLFW/glfw3.h>
#include <string>
#include "Camera.h"
#include "Ground.h"
#include "scene.h"
#include "sky.h"
#include "sun.h"
#include "moon.h"
#include "stars.h"
#include "skydome.h"
#include "McZapkie/MOVER.h"
#include "messaging.h"
// wrapper for simulation time
class simulation_time {
@@ -105,12 +107,15 @@ TWorld();
void OnKeyDown(int cKey);
// void UpdateWindow();
void OnMouseMove(double x, double y);
void OnCommandGet(DaneRozkaz *pRozkaz);
void OnCommandGet(multiplayer::DaneRozkaz *pRozkaz);
bool Update();
void TrainDelete(TDynamicObject *d = NULL);
TTrain* train() { return Train; }
// switches between static and dynamic daylight calculation
void ToggleDaylight();
// calculates current season of the year based on set simulation date
void compute_season( int const Yearday ) const;
private:
void Update_Environment();
@@ -144,8 +149,6 @@ private:
void CabChange(TDynamicObject *old, TDynamicObject *now);
// handles vehicle change flag
void ChangeDynamic();
TGround Ground; //m7todo: tmp
};
//---------------------------------------------------------------------------

View File

@@ -45,6 +45,9 @@ public:
point_inside( float const X, float const Y, float const Z ) const;
// tests if the sphere is in frustum, returns the distance between origin and sphere centre
inline
float
sphere_inside( glm::dvec3 const &Center, float const Radius ) const { return sphere_inside( static_cast<float>( Center.x ), static_cast<float>( Center.y ), static_cast<float>( Center.z ), Radius ); }
inline
float
sphere_inside( glm::vec3 const &Center, float const Radius ) const { return sphere_inside( Center.x, Center.y, Center.z, Radius ); }
inline

31
lua.cpp
View File

@@ -6,6 +6,7 @@
#include "World.h"
#include "Driver.h"
#include "lua_ffi.h"
#include "simulation.h"
extern TWorld World;
@@ -72,29 +73,31 @@ extern "C"
event->asName = std::string(name);
event->fDelay = delay;
event->Params[0].asPointer = (void*)handler;
World.Ground.add_event(event);
return event;
if (simulation::Events.insert(event))
return event;
else
return nullptr;
}
EXPORT TEvent* scriptapi_event_find(const char* name)
{
std::string str(name);
TEvent *e = World.Ground.FindEvent(str);
TEvent *e = simulation::Events.FindEvent(str);
if (e)
return e;
else
WriteLog("missing event: " + str);
WriteLog("lua: missing event: " + str);
return nullptr;
}
EXPORT TTrack* scriptapi_track_find(const char* name)
{
std::string str(name);
TGroundNode *n = World.Ground.FindGroundNode(str, TP_TRACK);
if (n)
return n->pTrack;
TTrack *track = simulation::Paths.find(str);
if (track)
return track;
else
WriteLog("missing track: " + str);
WriteLog("lua: missing track: " + str);
return nullptr;
}
@@ -122,7 +125,7 @@ extern "C"
EXPORT void scriptapi_event_dispatch(TEvent *e, TDynamicObject *activator)
{
if (e)
World.Ground.AddToQuery(e, activator);
simulation::Events.AddToQuery(e, activator);
}
EXPORT double scriptapi_random(double a, double b)
@@ -132,7 +135,7 @@ extern "C"
EXPORT void scriptapi_writelog(const char* txt)
{
WriteLog("lua log: " + std::string(txt));
WriteLog("lua: log: " + std::string(txt));
}
struct memcell_values { const char *str; double num1; double num2; };
@@ -140,11 +143,11 @@ extern "C"
EXPORT TMemCell* scriptapi_memcell_find(const char *name)
{
std::string str(name);
TGroundNode *n = World.Ground.FindGroundNode(str, TP_MEMCELL);
if (n)
return n->MemCell;
TMemCell *mc = simulation::Memory.find(str);
if (mc)
return mc;
else
WriteLog("missing memcell: " + str);
WriteLog("lua: missing memcell: " + str);
return nullptr;
}

View File

@@ -18,7 +18,7 @@ bool
opengl_material::deserialize( cParser &Input, bool const Loadnow ) {
bool result { false };
while( true == deserialize_mapping( Input, Loadnow ) ) {
while( true == deserialize_mapping( Input, 0, Loadnow ) ) {
result = true; // once would suffice but, eh
}
@@ -32,7 +32,7 @@ opengl_material::deserialize( cParser &Input, bool const Loadnow ) {
// imports member data pair from the config file
bool
opengl_material::deserialize_mapping( cParser &Input, bool const Loadnow ) {
opengl_material::deserialize_mapping( cParser &Input, int const Priority, bool const Loadnow ) {
if( false == Input.getTokens( 2, true, "\n\r\t;, " ) ) {
return false;
@@ -48,8 +48,30 @@ opengl_material::deserialize_mapping( cParser &Input, bool const Loadnow ) {
>> key
>> value;
if( key == "texture1:" ) { texture1 = GfxRenderer.Fetch_Texture( path + value, Loadnow ); }
else if( key == "texture2:" ) { texture2 = GfxRenderer.Fetch_Texture( path + value, Loadnow ); }
if( value == "{" ) {
// detect and optionally process config blocks
cParser blockparser( Input.getToken<std::string>( false, "}" ) );
if( key == Global::Season ) {
// seasonal textures override generic textures
while( true == deserialize_mapping( blockparser, 1, Loadnow ) ) {
; // all work is done in the header
}
}
}
else if( key == "texture1:" ) {
// TODO: full-fledged priority system
if( ( texture1 == null_handle )
|| ( Priority > 0 ) ) {
texture1 = GfxRenderer.Fetch_Texture( path + value, Loadnow );
}
}
else if( key == "texture2:" ) {
// TODO: full-fledged priority system
if( ( texture2 == null_handle )
|| ( Priority > 0 ) ) {
texture2 = GfxRenderer.Fetch_Texture( path + value, Loadnow );
}
}
return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized
}
@@ -65,7 +87,8 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) {
if( filename.find( '|' ) != std::string::npos )
filename.erase( filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury
if( filename.rfind( '.' ) != std::string::npos ) {
if( ( filename.rfind( '.' ) != std::string::npos )
&& ( filename.rfind( '.' ) != filename.rfind( ".." ) + 1 ) ) {
// we can get extension for .mat or, in legacy files, some image format. just trim it and set it to material file extension
filename.erase( filename.rfind( '.' ) );
}

View File

@@ -28,9 +28,9 @@ struct opengl_material {
bool
deserialize( cParser &Input, bool const Loadnow );
private:
// imports member data pair from the config file
// imports member data pair from the config file, overriding existing parameter values of lower priority
bool
deserialize_mapping( cParser &Input, bool const Loadnow );
deserialize_mapping( cParser &Input, int const Priority, bool const Loadnow );
};
class material_manager {

258
messaging.cpp Normal file
View File

@@ -0,0 +1,258 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "messaging.h"
#include "Globals.h"
#include "simulation.h"
#include "mtable.h"
#include "Logs.h"
#ifdef _WIN32
extern "C"
{
GLFWAPI HWND glfwGetWin32Window( GLFWwindow* window ); //m7todo: potrzebne do directsound
}
#endif
namespace multiplayer {
void
Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam) {
#ifdef _WIN32
// wysłanie komunikatu do sterującego
HWND h = FindWindow(ClassName.c_str(), 0); // można by to zapamiętać
if (h == 0)
h = FindWindow(0, ClassName.c_str()); // można by to zapamiętać
SendMessage(h, Msg, wParam, lParam);
#endif
}
void
WyslijEvent(const std::string &e, const std::string &d)
{ // Ra: jeszcze do wyczyszczenia
#ifdef _WIN32
DaneRozkaz r;
r.iSygn = MAKE_ID4( 'E', 'U', '0', '7' );
r.iComm = 2; // 2 - event
size_t i = e.length(), j = d.length();
r.cString[0] = char(i);
strcpy(r.cString + 1, e.c_str()); // zakończony zerem
r.cString[i + 2] = char(j); // licznik po zerze kończącym
strcpy(r.cString + 3 + i, d.c_str()); // zakończony zerem
COPYDATASTRUCT cData;
cData.dwData = MAKE_ID4( 'E', 'U', '0', '7' ); // sygnatura
cData.cbData = (DWORD)(12 + i + j); // 8+dwa liczniki i dwa zera kończące
cData.lpData = &r;
Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global::window ), (LPARAM)&cData );
CommLog( Now() + " " + std::to_string(r.iComm) + " " + e + " sent" );
#endif
}
void
WyslijUszkodzenia(const std::string &t, char fl)
{ // wysłanie informacji w postaci pojedynczego tekstu
#ifdef _WIN32
DaneRozkaz r;
r.iSygn = MAKE_ID4( 'E', 'U', '0', '7' );
r.iComm = 13; // numer komunikatu
size_t i = t.length();
r.cString[0] = char(fl);
r.cString[1] = char(i);
strcpy(r.cString + 2, t.c_str()); // z zerem kończącym
COPYDATASTRUCT cData;
cData.dwData = MAKE_ID4( 'E', 'U', '0', '7' ); // sygnatura
cData.cbData = (DWORD)(11 + i); // 8+licznik i zero kończące
cData.lpData = &r;
Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global::window ), (LPARAM)&cData );
CommLog( Now() + " " + std::to_string(r.iComm) + " " + t + " sent");
#endif
}
void
WyslijString(const std::string &t, int n)
{ // wysłanie informacji w postaci pojedynczego tekstu
#ifdef _WIN32
DaneRozkaz r;
r.iSygn = MAKE_ID4( 'E', 'U', '0', '7' );
r.iComm = n; // numer komunikatu
size_t i = t.length();
r.cString[0] = char(i);
strcpy(r.cString + 1, t.c_str()); // z zerem kończącym
COPYDATASTRUCT cData;
cData.dwData = MAKE_ID4( 'E', 'U', '0', '7' ); // sygnatura
cData.cbData = (DWORD)(10 + i); // 8+licznik i zero kończące
cData.lpData = &r;
Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global::window ), (LPARAM)&cData );
CommLog( Now() + " " + std::to_string(r.iComm) + " " + t + " sent");
#endif
}
void
WyslijWolny(const std::string &t)
{ // Ra: jeszcze do wyczyszczenia
WyslijString(t, 4); // tor wolny
}
void
WyslijNamiary(TDynamicObject const *Vehicle)
{ // wysłanie informacji o pojeździe - (float), długość ramki będzie zwiększana w miarę potrzeby
#ifdef _WIN32
// WriteLog("Wysylam pojazd");
DaneRozkaz r;
r.iSygn = MAKE_ID4( 'E', 'U', '0', '7' );
r.iComm = 7; // 7 - dane pojazdu
int i = 32;
size_t j = Vehicle->asName.length();
r.iPar[0] = i; // ilość danych liczbowych
r.fPar[1] = Global::fTimeAngleDeg / 360.0; // aktualny czas (1.0=doba)
r.fPar[2] = Vehicle->MoverParameters->Loc.X; // pozycja X
r.fPar[3] = Vehicle->MoverParameters->Loc.Y; // pozycja Y
r.fPar[4] = Vehicle->MoverParameters->Loc.Z; // pozycja Z
r.fPar[5] = Vehicle->MoverParameters->V; // prędkość ruchu X
r.fPar[6] = Vehicle->MoverParameters->nrot * M_PI *
Vehicle->MoverParameters->WheelDiameter; // prędkość obrotowa kóŁ
r.fPar[7] = 0; // prędkość ruchu Z
r.fPar[8] = Vehicle->MoverParameters->AccS; // przyspieszenie X
r.fPar[9] = Vehicle->MoverParameters->AccN; // przyspieszenie Y //na razie nie
r.fPar[10] = Vehicle->MoverParameters->AccV; // przyspieszenie Z
r.fPar[11] = Vehicle->MoverParameters->DistCounter; // przejechana odległość w km
r.fPar[12] = Vehicle->MoverParameters->PipePress; // ciśnienie w PG
r.fPar[13] = Vehicle->MoverParameters->ScndPipePress; // ciśnienie w PZ
r.fPar[14] = Vehicle->MoverParameters->BrakePress; // ciśnienie w CH
r.fPar[15] = Vehicle->MoverParameters->Compressor; // ciśnienie w ZG
r.fPar[16] = Vehicle->MoverParameters->Itot; // Prąd całkowity
r.iPar[17] = Vehicle->MoverParameters->MainCtrlPos; // Pozycja NJ
r.iPar[18] = Vehicle->MoverParameters->ScndCtrlPos; // Pozycja NB
r.iPar[19] = Vehicle->MoverParameters->MainCtrlActualPos; // Pozycja jezdna
r.iPar[20] = Vehicle->MoverParameters->ScndCtrlActualPos; // Pozycja bocznikowania
r.iPar[21] = Vehicle->MoverParameters->ScndCtrlActualPos; // Pozycja bocznikowania
r.iPar[22] = Vehicle->MoverParameters->ResistorsFlag * 1 +
Vehicle->MoverParameters->ConverterFlag * 2 +
+Vehicle->MoverParameters->CompressorFlag * 4 +
Vehicle->MoverParameters->Mains * 8 +
+Vehicle->MoverParameters->DoorLeftOpened * 16 +
Vehicle->MoverParameters->DoorRightOpened * 32 +
+Vehicle->MoverParameters->FuseFlag * 64 +
Vehicle->MoverParameters->DepartureSignal * 128;
// WriteLog("Zapisalem stare");
// WriteLog("Mam patykow "+IntToStr(t->DynamicObject->iAnimType[ANIM_PANTS]));
for (int p = 0; p < 4; p++)
{
// WriteLog("Probuje pant "+IntToStr(p));
if (p < Vehicle->iAnimType[ANIM_PANTS])
{
r.fPar[23 + p] = Vehicle->pants[p].fParamPants->PantWys; // stan pantografów 4
// WriteLog("Zapisalem pant "+IntToStr(p));
}
else
{
r.fPar[23 + p] = -2;
// WriteLog("Nie mam pant "+IntToStr(p));
}
}
// WriteLog("Zapisalem pantografy");
for (int p = 0; p < 3; p++)
r.fPar[27 + p] =
Vehicle->MoverParameters->ShowCurrent(p + 1); // amperomierze kolejnych grup
// WriteLog("zapisalem prady");
r.iPar[30] = Vehicle->MoverParameters->WarningSignal; // trabienie
r.fPar[31] = Vehicle->MoverParameters->RunningTraction.TractionVoltage; // napiecie WN
// WriteLog("Parametry gotowe");
i <<= 2; // ilość bajtów
r.cString[i] = char(j); // na końcu nazwa, żeby jakoś zidentyfikować
strcpy(r.cString + i + 1, Vehicle->asName.c_str()); // zakończony zerem
COPYDATASTRUCT cData;
cData.dwData = MAKE_ID4( 'E', 'U', '0', '7' ); // sygnatura
cData.cbData = (DWORD)(10 + i + j); // 8+licznik i zero kończące
cData.lpData = &r;
// WriteLog("Ramka gotowa");
Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global::window ), (LPARAM)&cData );
// WriteLog("Ramka poszla!");
CommLog( Now() + " " + std::to_string(r.iComm) + " " + Vehicle->asName + " sent");
#endif
}
void
WyslijObsadzone()
{ // wysłanie informacji o pojeździe
#ifdef _WIN32
DaneRozkaz2 r;
r.iSygn = MAKE_ID4( 'E', 'U', '0', '7' );
r.iComm = 12; // kod 12
for (int i=0; i<1984; ++i) r.cString[i] = 0;
// TODO: clean this up, we shouldn't be relying on direct list access
auto &vehiclelist = simulation::Vehicles.sequence();
int i = 0;
for( auto *vehicle : vehiclelist ) {
if( vehicle->Mechanik ) {
strcpy( r.cString + 64 * i, vehicle->asName.c_str() );
r.fPar[ 16 * i + 4 ] = vehicle->GetPosition().x;
r.fPar[ 16 * i + 5 ] = vehicle->GetPosition().y;
r.fPar[ 16 * i + 6 ] = vehicle->GetPosition().z;
r.iPar[ 16 * i + 7 ] = vehicle->Mechanik->GetAction();
strcpy( r.cString + 64 * i + 32, vehicle->GetTrack()->IsolatedName().c_str() );
strcpy( r.cString + 64 * i + 48, vehicle->Mechanik->Timetable()->TrainName.c_str() );
i++;
if( i > 30 ) break;
}
}
while (i <= 30)
{
strcpy(r.cString + 64 * i, "none");
r.fPar[16 * i + 4] = 1;
r.fPar[16 * i + 5] = 2;
r.fPar[16 * i + 6] = 3;
r.iPar[16 * i + 7] = 0;
strcpy(r.cString + 64 * i + 32, "none");
strcpy(r.cString + 64 * i + 48, "none");
i++;
}
COPYDATASTRUCT cData;
cData.dwData = MAKE_ID4( 'E', 'U', '0', '7' ); // sygnatura
cData.cbData = 8 + 1984; // 8+licznik i zero kończące
cData.lpData = &r;
// WriteLog("Ramka gotowa");
Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global::window ), (LPARAM)&cData );
CommLog( Now() + " " + std::to_string(r.iComm) + " obsadzone" + " sent");
#endif
}
void
WyslijParam(int nr, int fl)
{ // wysłanie parametrów symulacji w ramce (nr) z flagami (fl)
#ifdef _WIN32
DaneRozkaz r;
r.iSygn = MAKE_ID4( 'E', 'U', '0', '7' );
r.iComm = nr; // zwykle 5
r.iPar[0] = fl; // flagi istotności kolejnych parametrów
int i = 0; // domyślnie brak danych
switch (nr)
{ // można tym przesyłać różne zestawy parametrów
case 5: // czas i pauza
r.fPar[1] = Global::fTimeAngleDeg / 360.0; // aktualny czas (1.0=doba)
r.iPar[2] = Global::iPause; // stan zapauzowania
i = 8; // dwa parametry po 4 bajty każdy
break;
}
COPYDATASTRUCT cData;
cData.dwData = MAKE_ID4( 'E', 'U', '0', '7' ); // sygnatura
cData.cbData = 12 + i; // 12+rozmiar danych
cData.lpData = &r;
Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global::window ), (LPARAM)&cData );
#endif
}
} // multiplayer
//---------------------------------------------------------------------------

51
messaging.h Normal file
View File

@@ -0,0 +1,51 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <string>
#include "winheaders.h"
class TDynamicObject;
namespace multiplayer {
struct DaneRozkaz { // struktura komunikacji z EU07.EXE
int iSygn; // sygnatura 'EU07'
int iComm; // rozkaz/status (kod ramki)
union {
float fPar[ 62 ];
int iPar[ 62 ];
char cString[ 248 ]; // upakowane stringi
};
};
struct DaneRozkaz2 { // struktura komunikacji z EU07.EXE
int iSygn; // sygnatura 'EU07'
int iComm; // rozkaz/status (kod ramki)
union {
float fPar[ 496 ];
int iPar[ 496 ];
char cString[ 1984 ]; // upakowane stringi
};
};
void Navigate( std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam );
void WyslijEvent( const std::string &e, const std::string &d );
void WyslijString( const std::string &t, int n );
void WyslijWolny( const std::string &t );
void WyslijNamiary( TDynamicObject const *Vehicle );
void WyslijParam( int nr, int fl );
void WyslijUszkodzenia( const std::string &t, char fl );
void WyslijObsadzone(); // -> skladanie wielu pojazdow
} // multiplayer
//---------------------------------------------------------------------------

View File

@@ -363,6 +363,12 @@ mouse_input::default_bindings() {
{ "pantselectedoff_sw:", {
user_command::none,
user_command::none } }, // TODO: lower selected pantograp(s) command
{ "pantcompressor_sw:", {
user_command::pantographcompressoractivate,
user_command::none } },
{ "pantcompressorvalve_sw:", {
user_command::pantographcompressorvalvetoggle,
user_command::none } },
{ "trainheating_sw:", {
user_command::heatingtoggle,
user_command::none } },

View File

@@ -25,8 +25,8 @@ struct basic_vertex {
glm::vec2 texture; // uv space
basic_vertex() = default;
basic_vertex( glm::vec3 const &Position, glm::vec3 const &Normal, glm::vec2 const &Texture ) :
position( Position ), normal( Normal ), texture( Texture )
basic_vertex( glm::vec3 Position, glm::vec3 Normal, glm::vec2 Texture ) :
position( Position ), normal( Normal ), texture( Texture )
{}
void serialize( std::ostream& ) const;
void deserialize( std::istream& );
@@ -58,8 +58,8 @@ struct geometry_handle {
geometry_handle() :
bank( 0 ), chunk( 0 )
{}
geometry_handle( std::uint32_t const Bank, std::uint32_t const Chunk ) :
bank( Bank ), chunk( Chunk )
geometry_handle( std::uint32_t Bank, std::uint32_t Chunk ) :
bank( Bank ), chunk( Chunk )
{}
// methods
inline
@@ -121,8 +121,8 @@ protected:
unsigned int type; // kind of geometry used by the chunk
vertex_array vertices; // geometry data
// NOTE: constructor doesn't copy provided vertex data, but moves it
geometry_chunk( vertex_array &Vertices, unsigned int const Type ) :
type( Type )
geometry_chunk( vertex_array &Vertices, unsigned int Type ) :
type( Type )
{
vertices.swap( Vertices );
}

View File

@@ -21,13 +21,14 @@ http://mozilla.org/MPL/2.0/.
// cParser -- generic class for parsing text data.
// constructors
cParser::cParser( std::string const &Stream, buffertype const Type, std::string Path, bool const Loadtraction ) :
cParser::cParser( std::string const &Stream, buffertype const Type, std::string Path, bool const Loadtraction, std::vector<std::string> Parameters ) :
mPath(Path),
LoadTraction( Loadtraction ) {
// build comments map
mComments.insert(commentmap::value_type("/*", "*/"));
mComments.insert(commentmap::value_type("//", "\n"));
// mComments.insert(commentmap::value_type("--","\n")); //Ra: to chyba nie używane
// store to calculate sub-sequent includes from relative path
if( Type == buffertype::buffer_FILE ) {
mFile = Stream;
@@ -60,6 +61,10 @@ cParser::cParser( std::string const &Stream, buffertype const Type, std::string
mLine = 1;
}
}
// set parameter set if one was provided
if( false == Parameters.empty() ) {
parameters.swap( Parameters );
}
}
// destructor
@@ -146,90 +151,83 @@ bool cParser::getTokens(unsigned int Count, bool ToLower, const char *Break)
return true;
}
std::string cParser::readToken(bool ToLower, const char *Break)
{
std::string token = "";
size_t pos; // początek podmienianego ciągu
// see if there's include parsing going on. clean up when it's done.
if (mIncludeParser)
{
token = mIncludeParser->readToken(ToLower, Break);
if (!token.empty())
{
// check if the token is a parameter which should be replaced with stored true value
while((pos = token.find("(p")) != std::string::npos) //!=npos to znalezione
{
std::string parameter =
token.substr(pos + 2, token.find(")", pos) - pos + 2); // numer parametru
token.erase(pos, token.find(")", pos) - pos + 1); // najpierw usunięcie "(pN)"
size_t nr = atoi(parameter.c_str()) - 1;
if (nr < parameters.size())
{
token.insert(pos, parameters.at(nr)); // wklejenie wartości parametru
if (ToLower)
for (; pos < token.length(); ++pos)
token[pos] = tolower(token[pos]);
}
else
token.insert(pos, "none"); // zabezpieczenie przed brakiem parametru
}
return token;
}
else
{
mIncludeParser = NULL;
parameters.clear();
std::string cParser::readToken( bool ToLower, const char *Break ) {
std::string token;
if( mIncludeParser ) {
// see if there's include parsing going on. clean up when it's done.
token = mIncludeParser->readToken( ToLower, Break );
if( true == token.empty() ) {
mIncludeParser = nullptr;
}
}
// get the token yourself if there's no child to delegate it to.
char c { 0 };
do
{
while (mStream->peek() != EOF && strchr(Break, c = mStream->get()) == NULL)
{
if (ToLower)
c = tolower(c);
token += c;
if (findQuotes(token)) // do glue together words enclosed in quotes
break;
if (trimComments(token)) // don't glue together words separated with comment
break;
}
if( c == '\n' ) {
// update line counter
++mLine;
}
} while (token == "" && mStream->peek() != EOF); // double check to deal with trailing spaces
// launch child parser if include directive found.
// NOTE: parameter collecting uses default set of token separators.
if (token.compare("include") == 0)
{ // obsługa include
std::string includefile = readToken(ToLower); // nazwa pliku
if (LoadTraction ? true : ((includefile.find("tr/") == std::string::npos) &&
(includefile.find("tra/") == std::string::npos)))
{
// std::string trtest2="niemaproblema"; //nazwa odporna na znalezienie "tr/"
// if (trtest=="x") //jeśli nie wczytywać drutów
// trtest2=includefile; //kopiowanie ścieżki do pliku
std::string parameter = readToken(false); // w parametrach nie zmniejszamy
while( (parameter.empty() == false)
&& (parameter.compare("end") != 0) )
{
parameters.push_back(parameter);
parameter = readToken(false);
if( true == token.empty() ) {
// get the token yourself if the delegation attempt failed
char c { 0 };
do {
while( mStream->peek() != EOF && strchr( Break, c = mStream->get() ) == NULL ) {
if( ToLower )
c = tolower( c );
token += c;
if( findQuotes( token ) ) // do glue together words enclosed in quotes
break;
if( trimComments( token ) ) // don't glue together words separated with comment
break;
}
if( c == '\n' ) {
// update line counter
++mLine;
}
} while( token == "" && mStream->peek() != EOF ); // double check to deal with trailing spaces
}
if( false == parameters.empty() ) {
// if there's parameter list, check the token for potential parameters to replace
size_t pos; // początek podmienianego ciągu
while( ( pos = token.find( "(p" ) ) != std::string::npos ) {
// check if the token is a parameter which should be replaced with stored true value
auto const parameter{ token.substr( pos + 2, token.find( ")", pos ) - ( pos + 2 ) ) }; // numer parametru
token.erase( pos, token.find( ")", pos ) - pos + 1 ); // najpierw usunięcie "(pN)"
size_t nr = atoi( parameter.c_str() ) - 1;
if( nr < parameters.size() ) {
token.insert( pos, parameters.at( nr ) ); // wklejenie wartości parametru
if( ToLower )
for( ; pos < parameters.at( nr ).size(); ++pos )
token[ pos ] = tolower( token[ pos ] );
}
else
token.insert( pos, "none" ); // zabezpieczenie przed brakiem parametru
}
}
if( token == "include" ) {
// launch child parser if include directive found.
// NOTE: parameter collecting uses default set of token separators.
std::string includefile = readToken(ToLower); // nazwa pliku
if( ( true == LoadTraction )
|| ( ( includefile.find( "tr/" ) == std::string::npos )
&& ( includefile.find( "tra/" ) == std::string::npos ) ) ) {
// get parameter list for the child parser
std::vector<std::string> includeparameters;
std::string parameter = readToken( false ); // w parametrach nie zmniejszamy
while( ( parameter.empty() == false )
&& ( parameter != "end" ) ) {
includeparameters.emplace_back( parameter );
parameter = readToken( false );
}
mIncludeParser = std::make_shared<cParser>( includefile, buffer_FILE, mPath, LoadTraction, includeparameters );
if( mIncludeParser->mSize <= 0 ) {
ErrorLog( "Bad include: can't open file \"" + includefile + "\"" );
}
// if (trtest2.find("tr/")!=0)
mIncludeParser = std::make_shared<cParser>(includefile, buffer_FILE, mPath, LoadTraction);
if (mIncludeParser->mSize <= 0)
ErrorLog("Missed include: " + includefile);
}
else {
while( token.compare( "end" ) != 0 ) {
while( token != "end" ) {
token = readToken( true ); // minimize risk of case mismatch on comparison
}
}
token = readToken(ToLower, Break);
}
// all done
return token;
}

View File

@@ -28,7 +28,7 @@ class cParser //: public std::stringstream
buffer_TEXT
};
// constructors:
cParser(std::string const &Stream, buffertype const Type = buffer_TEXT, std::string Path = "", bool const Loadtraction = true );
cParser(std::string const &Stream, buffertype const Type = buffer_TEXT, std::string Path = "", bool const Loadtraction = true, std::vector<std::string> Parameters = std::vector<std::string>() );
// destructor:
virtual ~cParser();
// methods:

File diff suppressed because it is too large Load Diff

View File

@@ -17,10 +17,12 @@ http://mozilla.org/MPL/2.0/.
#include "frustum.h"
#include "World.h"
#include "MemCell.h"
#include "scene.h"
#define EU07_USE_PICKING_FRAMEBUFFER
//#define EU07_USE_DEBUG_SHADOWMAP
//#define EU07_USE_DEBUG_CAMERA
//#define EU07_USE_DEBUG_CULLING
struct opengl_light {
@@ -84,7 +86,7 @@ public:
void
update_frustum( glm::mat4 const &Projection, glm::mat4 const &Modelview );
bool
visible( bounding_area const &Area ) const;
visible( scene::bounding_area const &Area ) const;
bool
visible( TDynamicObject const *Dynamic ) const;
inline
@@ -190,18 +192,20 @@ public:
// utility methods
TSubModel const *
Pick_Control() const { return m_pickcontrolitem; }
TGroundNode const *
editor::basic_node const *
Pick_Node() const { return m_picksceneryitem; }
// maintenance jobs
void
Update( double const Deltatime );
TSubModel const *
Update_Pick_Control();
TGroundNode const *
editor::basic_node const *
Update_Pick_Node();
// debug performance string
std::string const &
Info() const;
info_times() const;
std::string const &
info_stats() const;
// members
GLenum static const sunlight{ GL_LIGHT0 };
@@ -225,7 +229,20 @@ private:
diffuse
};
typedef std::pair< double, TSubRect * > distancesubcell_pair;
struct debug_stats {
int dynamics { 0 };
int models { 0 };
int submodels { 0 };
int paths { 0 };
int traction { 0 };
int shapes { 0 };
int lines { 0 };
int drawcalls { 0 };
};
using section_sequence = std::vector<scene::basic_section *>;
using distancecell_pair = std::pair<double, scene::basic_cell *>;
using cell_sequence = std::vector<distancecell_pair>;
struct renderpass_config {
@@ -266,14 +283,16 @@ private:
Render_reflections();
bool
Render( world_environment *Environment );
bool
Render( TGround *Ground );
bool
Render( TGroundRect *Groundcell );
bool
Render( TSubRect *Groundsubcell );
bool
Render( TGroundNode *Node );
void
Render( scene::basic_region *Region );
void
Render( section_sequence::iterator First, section_sequence::iterator Last );
void
Render( cell_sequence::iterator First, cell_sequence::iterator Last );
void
Render( scene::shape_node const &Shape, bool const Ignorerange );
void
Render( TAnimModel *Instance );
bool
Render( TDynamicObject *Dynamic );
bool
@@ -284,16 +303,22 @@ private:
Render( TSubModel *Submodel );
void
Render( TTrack *Track );
void
Render( scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last );
bool
Render_cab( TDynamicObject *Dynamic, bool const Alpha = false );
void
Render( TMemCell *Memcell );
bool
Render_Alpha( TGround *Ground );
bool
Render_Alpha( TSubRect *Groundsubcell );
bool
Render_Alpha( TGroundNode *Node );
void
Render_Alpha( scene::basic_region *Region );
void
Render_Alpha( cell_sequence::reverse_iterator First, cell_sequence::reverse_iterator Last );
void
Render_Alpha( TAnimModel *Instance );
void
Render_Alpha( TTraction *Traction );
void
Render_Alpha( scene::lines_node const &Lines );
bool
Render_Alpha( TDynamicObject *Dynamic );
bool
@@ -351,28 +376,26 @@ private:
units_state m_unitstate;
unsigned int m_framestamp; // id of currently rendered gfx frame
float m_drawtime { 1000.f / 30.f * 20.f }; // start with presumed 'neutral' average of 30 fps
std::chrono::steady_clock::time_point m_drawstart; // cached start time of previous frame
float m_framerate;
float m_drawtimecolorpass { 1000.f / 30.f * 20.f };
float m_drawtimeshadowpass { 0.f };
double m_updateaccumulator { 0.0 };
std::string m_debuginfo;
std::string m_debugtimestext;
std::string m_pickdebuginfo;
debug_stats m_debugstats;
std::string m_debugstatstext;
glm::vec4 m_baseambient { 0.0f, 0.0f, 0.0f, 1.0f };
glm::vec4 m_shadowcolor { 0.5f, 0.5f, 0.5f, 1.f };
glm::vec4 m_shadowcolor { 0.65f, 0.65f, 0.65f, 1.f };
float m_specularopaquescalefactor { 1.f };
float m_speculartranslucentscalefactor { 1.f };
bool m_renderspecular{ false }; // controls whether to include specular component in the calculations
renderpass_config m_renderpass;
std::vector<distancesubcell_pair> m_drawqueue; // list of subcells to be drawn in current render pass
std::vector<TGroundNode const *> m_picksceneryitems;
section_sequence m_sectionqueue; // list of sections in current render pass
cell_sequence m_cellqueue;
std::vector<TSubModel const *> m_pickcontrolsitems;
TSubModel const *m_pickcontrolitem { nullptr };
TGroundNode const *m_picksceneryitem { nullptr };
std::vector<editor::basic_node const *> m_picksceneryitems;
editor::basic_node const *m_picksceneryitem { nullptr };
#ifdef EU07_USE_DEBUG_CAMERA
renderpass_config m_worldcamera; // debug item
#endif

View File

@@ -2,7 +2,7 @@
// Microsoft Visual C++ generated include file.
// Used by maszyna.rc
//
#define IDI_ICON1 101
//#define GLFW_ICON 101
// Next default values for new objects
//

1339
scene.cpp Normal file

File diff suppressed because it is too large Load Diff

347
scene.h Normal file
View File

@@ -0,0 +1,347 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <vector>
#include <deque>
#include <array>
#include <stack>
#include <unordered_set>
#include "parser.h"
#include "openglgeometrybank.h"
#include "scenenode.h"
#include "Track.h"
#include "Traction.h"
class opengl_renderer;
namespace scene {
int const EU07_CELLSIZE = 250;
int const EU07_SECTIONSIZE = 1000;
int const EU07_REGIONSIDESECTIONCOUNT = 500; // number of sections along a side of square region
struct scratch_data {
struct binary_data {
bool terrain{ false };
} binary;
struct location_data {
std::stack<glm::dvec3> offset;
glm::vec3 rotation;
} location;
struct trainset_data {
std::string name;
std::string track;
float offset { 0.f };
float velocity { 0.f };
std::vector<TDynamicObject *> vehicles;
std::vector<std::int8_t> couplings;
TDynamicObject * driver { nullptr };
bool is_open { false };
} trainset;
bool initialized { false };
};
// basic element of rudimentary partitioning scheme for the section. fixed size, no further subdivision
// TBD, TODO: replace with quadtree scheme?
class basic_cell {
friend class ::opengl_renderer;
public:
// methods
// legacy method, updates sounds and polls event launchers within radius around specified point
void
update();
// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle
void
update_traction( TDynamicObject *Vehicle, int const Pantographindex );
// legacy method, triggers radio-stop procedure for all vehicles located on paths in the cell
void
radio_stop();
// legacy method, adds specified path to the list of pieces undergoing state change
bool
RaTrackAnimAdd( TTrack *Track );
// legacy method, updates geometry for pieces in the animation list
void
RaAnimate( unsigned int const Framestamp );
// adds provided shape to the cell
void
insert( shape_node Shape );
// adds provided lines to the cell
void
insert( lines_node Lines );
// adds provided path to the cell
void
insert( TTrack *Path );
// adds provided path to the cell
void
insert( TTraction *Traction );
// adds provided model instance to the cell
void
insert( TAnimModel *Instance );
// adds provided sound instance to the cell
void
insert( sound *Sound );
// adds provided event launcher to the cell
void
insert( TEventLauncher *Launcher );
// registers provided path in the lookup directory of the cell
void
register_end( TTrack *Path );
// registers provided traction piece in the lookup directory of the cell
void
register_end( TTraction *Traction );
// find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler );
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
std::tuple<TTrack *, int>
find( glm::dvec3 const &Point, TTrack const *Exclude );
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
std::tuple<TTraction *, int>
find( glm::dvec3 const &Point, TTraction const *Exclude );
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece
std::tuple<TTraction *, int, float>
find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection );
// sets center point of the cell
void
center( glm::dvec3 Center );
// generates renderable version of held non-instanced geometry in specified geometry bank
void
create_geometry( geometrybank_handle const &Bank );
// provides access to bounding area data
bounding_area const &
area() const {
return m_area; }
private:
// types
using path_sequence = std::vector<TTrack *>;
using shapenode_sequence = std::vector<shape_node>;
using linesnode_sequence = std::vector<lines_node>;
using traction_sequence = std::vector<TTraction *>;
using instance_sequence = std::vector<TAnimModel *>;
using sound_sequence = std::vector<sound *>;
using eventlauncher_sequence = std::vector<TEventLauncher *>;
// methods
void
enclose_area( editor::basic_node *Node );
// members
scene::bounding_area m_area { glm::dvec3(), static_cast<float>( 0.5 * M_SQRT2 * EU07_CELLSIZE ) };
bool m_active { false }; // whether the cell holds any actual data
// content
shapenode_sequence m_shapesopaque; // opaque pieces of geometry
shapenode_sequence m_shapestranslucent; // translucent pieces of geometry
linesnode_sequence m_lines;
path_sequence m_paths; // path pieces
instance_sequence m_instancesopaque;
instance_sequence m_instancetranslucent;
traction_sequence m_traction;
sound_sequence m_sounds;
eventlauncher_sequence m_eventlaunchers;
// search helpers
struct lookup_data {
path_sequence paths;
traction_sequence traction;
} m_directories;
// animation of owned items (legacy code, clean up along with track refactoring)
bool m_geometrycreated { false };
unsigned int m_framestamp { 0 }; // id of last rendered gfx frame
TTrack *tTrackAnim = nullptr; // obiekty do przeliczenia animacji
};
// basic scene partitioning structure, holds terrain geometry and collection of cells
class basic_section {
friend class ::opengl_renderer;
public:
// methods
// legacy method, updates sounds and polls event launchers within radius around specified point
void
update( glm::dvec3 const &Location, float const Radius );
// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle
void
update_traction( TDynamicObject *Vehicle, int const Pantographindex );
// legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location
void
radio_stop( glm::dvec3 const &Location, float const Radius );
// adds provided shape to the section
void
insert( shape_node Shape );
// adds provided lines to the section
void
insert( lines_node Lines );
// adds provided node to the section
template <class Type_>
void
insert( Type_ *Node ) {
auto &targetcell { cell( Node->location() ) };
targetcell.insert( Node );
// some node types can extend bounding area of the target cell
m_area.radius = std::max(
m_area.radius,
static_cast<float>( glm::length( m_area.center - targetcell.area().center ) + targetcell.area().radius ) ); }
// registers provided node in the lookup directory of the section enclosing specified point
template <class Type_>
void
register_node( Type_ *Node, glm::dvec3 const &Point ) {
cell( Point ).register_end( Node ); }
// find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler );
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
std::tuple<TTrack *, int>
find( glm::dvec3 const &Point, TTrack const *Exclude );
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
std::tuple<TTraction *, int>
find( glm::dvec3 const &Point, TTraction const *Exclude );
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece
std::tuple<TTraction *, int, float>
find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection );
// sets center point of the section
void
center( glm::dvec3 Center );
// generates renderable version of held non-instanced geometry
void
create_geometry();
// provides access to bounding area data
bounding_area const &
area() const {
return m_area; }
private:
// types
using cell_array = std::array<basic_cell, (EU07_SECTIONSIZE / EU07_CELLSIZE) * (EU07_SECTIONSIZE / EU07_CELLSIZE)>;
using shapenode_sequence = std::vector<shape_node>;
// methods
// provides access to section enclosing specified point
basic_cell &
cell( glm::dvec3 const &Location );
// members
// placement and visibility
scene::bounding_area m_area { glm::dvec3(), static_cast<float>( 0.5 * M_SQRT2 * EU07_SECTIONSIZE ) };
// content
cell_array m_cells; // partitioning scheme
shapenode_sequence m_shapes; // large pieces of opaque geometry and (legacy) terrain
// TODO: implement dedicated, higher fidelity, fixed resolution terrain mesh item
// gfx renderer data
geometrybank_handle m_geometrybank;
bool m_geometrycreated { false };
};
// top-level of scene spatial structure, holds collection of sections
class basic_region {
friend class ::opengl_renderer;
public:
// constructors
basic_region();
// destructor
~basic_region();
// methods
// legacy method, updates sounds and polls event launchers around camera
void
update();
// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle
void
update_traction( TDynamicObject *Vehicle, int const Pantographindex );
// stores content of the class in file with specified name
void
serialize( std::string const &Scenariofile );
// restores content of the class from file with specified name. returns: true on success, false otherwise
bool
deserialize( std::string const &Scenariofile );
// legacy method, links specified path piece with potential neighbours
void
TrackJoin( TTrack *Track );
// legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location
void
RadioStop( glm::dvec3 const &Location );
// inserts provided shape in the region
void
insert_shape( shape_node Shape, scratch_data &Scratchpad, bool const Transform );
// inserts provided lines in the region
void
insert_lines( lines_node Lines, scratch_data &Scratchpad );
// inserts provided track in the region
void
insert_path( TTrack *Path, const scratch_data &Scratchpad );
// inserts provided track in the region
void
insert_traction( TTraction *Traction, scratch_data &Scratchpad );
// inserts provided instance of 3d model in the region
void
insert_instance( TAnimModel *Instance, scratch_data &Scratchpad );
// inserts provided sound in the region
void
insert_sound( sound *Sound, scratch_data &Scratchpad );
// inserts provided event launcher in the region
void
insert_launcher( TEventLauncher *Launcher, scratch_data &Scratchpad );
// find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler );
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
std::tuple<TTrack *, int>
find_path( glm::dvec3 const &Point, TTrack const *Exclude );
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
std::tuple<TTraction *, int>
find_traction( glm::dvec3 const &Point, TTraction const *Exclude );
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece
std::tuple<TTraction *, int>
find_traction( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection );
// finds sections inside specified sphere. returns: list of sections
std::vector<basic_section *> const &
sections( glm::dvec3 const &Point, float const Radius );
private:
// types
using section_array = std::array<basic_section *, EU07_REGIONSIDESECTIONCOUNT * EU07_REGIONSIDESECTIONCOUNT>;
struct region_scratchpad {
std::vector<basic_section *> sections;
};
// methods
// registers specified end point of the provided path in the lookup directory of the region
void
register_path( TTrack *Path, glm::dvec3 const &Point );
// registers specified end point of the provided traction piece in the lookup directory of the region
void
register_traction( TTraction *Traction, glm::dvec3 const &Point );
// checks whether specified point is within boundaries of the region
bool
point_inside( glm::dvec3 const &Location );
// legacy method, trims provided shape to fit into a section. adds trimmed part at the end of provided list, returns true if changes were made
bool
RaTriangleDivider( shape_node &Shape, std::deque<shape_node> &Shapes );
// provides access to section enclosing specified point
basic_section &
section( glm::dvec3 const &Location );
// members
section_array m_sections;
region_scratchpad m_scratchpad;
};
} // scene
//---------------------------------------------------------------------------

519
scenenode.cpp Normal file
View File

@@ -0,0 +1,519 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "scenenode.h"
#include "renderer.h"
#include "Logs.h"
namespace scene {
// restores content of the node from provded input stream
shape_node &
shape_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) {
// import common data
m_name = Nodedata.name;
m_data.rangesquared_min = Nodedata.range_min * Nodedata.range_min;
m_data.rangesquared_max = (
Nodedata.range_max >= 0.0 ?
Nodedata.range_max * Nodedata.range_max :
std::numeric_limits<double>::max() );
std::string token = Input.getToken<std::string>();
if( token == "material" ) {
// lighting settings
token = Input.getToken<std::string>();
while( token != "endmaterial" ) {
if( token == "ambient:" ) {
Input.getTokens( 3 );
Input
>> m_data.lighting.ambient.r
>> m_data.lighting.ambient.g
>> m_data.lighting.ambient.b;
m_data.lighting.ambient /= 255.f;
m_data.lighting.ambient.a = 1.f;
}
else if( token == "diffuse:" ) {
Input.getTokens( 3 );
Input
>> m_data.lighting.diffuse.r
>> m_data.lighting.diffuse.g
>> m_data.lighting.diffuse.b;
m_data.lighting.diffuse /= 255.f;
m_data.lighting.diffuse.a = 1.f;
}
else if( token == "specular:" ) {
Input.getTokens( 3 );
Input
>> m_data.lighting.specular.r
>> m_data.lighting.specular.g
>> m_data.lighting.specular.b;
m_data.lighting.specular /= 255.f;
m_data.lighting.specular.a = 1.f;
}
token = Input.getToken<std::string>();
}
token = Input.getToken<std::string>();
}
// assigned material
m_data.material = GfxRenderer.Fetch_Material( token );
// determine way to proceed from the assigned diffuse texture
// TBT, TODO: add methods to material manager to access these simpler
auto const texturehandle = (
m_data.material != null_handle ?
GfxRenderer.Material( m_data.material ).texture1 :
null_handle );
auto const &texture = (
texturehandle ?
GfxRenderer.Texture( texturehandle ) :
opengl_texture() ); // dirty workaround for lack of better api
bool const clamps = (
texturehandle ?
texture.traits.find( 's' ) != std::string::npos :
false );
bool const clampt = (
texturehandle ?
texture.traits.find( 't' ) != std::string::npos :
false );
// remainder of legacy 'problend' system -- geometry assigned a texture with '@' in its name is treated as translucent, opaque otherwise
if( texturehandle != null_handle ) {
m_data.translucent = (
( ( texture.name.find( '@' ) != std::string::npos )
&& ( true == texture.has_alpha ) ) ?
true :
false );
}
else {
m_data.translucent = false;
}
// geometry
enum subtype {
triangles,
triangle_strip,
triangle_fan
} const nodetype = (
Nodedata.type == "triangles" ? triangles :
Nodedata.type == "triangle_strip" ? triangle_strip :
triangle_fan );
std::size_t vertexcount{ 0 };
world_vertex vertex, vertex1, vertex2;
do {
Input.getTokens( 8, false );
Input
>> vertex.position.x
>> vertex.position.y
>> vertex.position.z
>> vertex.normal.x
>> vertex.normal.y
>> vertex.normal.z
>> vertex.texture.s
>> vertex.texture.t;
// clamp texture coordinates if texture wrapping is off
if( true == clamps ) { vertex.texture.s = clamp( vertex.texture.s, 0.001f, 0.999f ); }
if( true == clampt ) { vertex.texture.t = clamp( vertex.texture.t, 0.001f, 0.999f ); }
// convert all data to gl_triangles to allow data merge for matching nodes
switch( nodetype ) {
case triangles: {
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex2 );
m_data.vertices.emplace_back( vertex );
}
else {
ErrorLog(
"Bad geometry: degenerate triangle encountered"
+ ( m_name != "" ? " in node \"" + m_name + "\"" : "" )
+ " (vertices: " + to_string( vertex1.position ) + " + " + to_string( vertex2.position ) + " + " + to_string( vertex.position ) + ")" );
}
}
++vertexcount;
if( vertexcount > 2 ) { vertexcount = 0; } // start new triangle if needed
break;
}
case triangle_fan: {
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex2 );
m_data.vertices.emplace_back( vertex );
vertex2 = vertex;
}
else {
ErrorLog(
"Bad geometry: degenerate triangle encountered"
+ ( m_name != "" ? " in node \"" + m_name + "\"" : "" )
+ " (vertices: " + to_string( vertex1.position ) + " + " + to_string( vertex2.position ) + " + " + to_string( vertex.position ) + ")" );
}
}
++vertexcount;
break;
}
case triangle_strip: {
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
// swap order every other triangle, to maintain consistent winding
if( vertexcount % 2 == 0 ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex2 );
}
else {
m_data.vertices.emplace_back( vertex2 );
m_data.vertices.emplace_back( vertex1 );
}
m_data.vertices.emplace_back( vertex );
vertex1 = vertex2;
vertex2 = vertex;
}
else {
ErrorLog(
"Bad geometry: degenerate triangle encountered"
+ ( m_name != "" ? " in node \"" + m_name + "\"" : "" )
+ " (vertices: " + to_string( vertex1.position ) + " + " + to_string( vertex2.position ) + " + " + to_string( vertex.position ) + ")" );
}
}
++vertexcount;
break;
}
default: { break; }
}
token = Input.getToken<std::string>();
} while( token != "endtri" );
return *this;
}
// imports data from provided submodel
shape_node &
shape_node::convert( TSubModel const *Submodel ) {
m_name = Submodel->pName;
m_data.lighting.ambient = Submodel->f4Ambient;
m_data.lighting.diffuse = Submodel->f4Diffuse;
m_data.lighting.specular = Submodel->f4Specular;
m_data.material = Submodel->m_material;
m_data.translucent = ( true == GfxRenderer.Material( m_data.material ).has_alpha );
// NOTE: we set unlimited view range typical for terrain, because we don't expect to convert any other 3d models
m_data.rangesquared_max = std::numeric_limits<double>::max();
if( Submodel->m_geometry == null_handle ) { return *this; }
int vertexcount { 0 };
std::vector<world_vertex> importedvertices;
world_vertex vertex, vertex1, vertex2;
for( auto const &sourcevertex : GfxRenderer.Vertices( Submodel->m_geometry ) ) {
vertex.position = sourcevertex.position;
vertex.normal = sourcevertex.normal;
vertex.texture = sourcevertex.texture;
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
importedvertices.emplace_back( vertex1 );
importedvertices.emplace_back( vertex2 );
importedvertices.emplace_back( vertex );
}
// start a new triangle
vertexcount = -1;
}
++vertexcount;
}
if( true == importedvertices.empty() ) { return *this; }
// assign imported geometry to the node...
m_data.vertices.swap( importedvertices );
// ...and calculate center...
for( auto const &vertex : m_data.vertices ) {
m_data.area.center += vertex.position;
}
m_data.area.center /= m_data.vertices.size();
// ...and bounding area
double squareradius { 0.0 };
for( auto const &vertex : m_data.vertices ) {
squareradius = std::max(
squareradius,
glm::length2( vertex.position - m_data.area.center ) );
}
m_data.area.radius = std::max(
m_data.area.radius,
static_cast<float>( std::sqrt( squareradius ) ) );
return *this;
}
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
shape_node::merge( shape_node &Shape ) {
if( ( m_data.material != Shape.m_data.material )
|| ( m_data.lighting != Shape.m_data.lighting ) ) {
// can't merge nodes with different appearance
return false;
}
// add geometry from provided node
m_data.area.center =
interpolate(
m_data.area.center, Shape.m_data.area.center,
static_cast<float>( Shape.m_data.vertices.size() ) / ( Shape.m_data.vertices.size() + m_data.vertices.size() ) );
m_data.vertices.insert(
std::end( m_data.vertices ),
std::begin( Shape.m_data.vertices ), std::end( Shape.m_data.vertices ) );
// NOTE: we could recalculate radius with something other than brute force, but it'll do
compute_radius();
return true;
}
// generates renderable version of held non-instanced geometry in specified geometry bank
void
shape_node::create_geometry( geometrybank_handle const &Bank ) {
vertex_array vertices; vertices.reserve( m_data.vertices.size() );
for( auto const &vertex : m_data.vertices ) {
vertices.emplace_back(
vertex.position - m_data.origin,
vertex.normal,
vertex.texture );
}
m_data.geometry = GfxRenderer.Insert( vertices, Bank, GL_TRIANGLES );
std::vector<world_vertex>().swap( m_data.vertices ); // hipster shrink_to_fit
}
// calculates shape's bounding radius
void
shape_node::compute_radius() {
auto squaredradius { 0.0 };
for( auto const &vertex : m_data.vertices ) {
squaredradius = std::max(
squaredradius,
glm::length2( vertex.position - m_data.area.center ) );
}
m_data.area.radius = static_cast<float>( std::sqrt( squaredradius ) );
}
// restores content of the node from provded input stream
lines_node &
lines_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) {
// import common data
m_name = Nodedata.name;
m_data.rangesquared_min = Nodedata.range_min * Nodedata.range_min;
m_data.rangesquared_max = (
Nodedata.range_max >= 0.0 ?
Nodedata.range_max * Nodedata.range_max :
std::numeric_limits<double>::max() );
// material
Input.getTokens( 3, false );
Input
>> m_data.lighting.diffuse.r
>> m_data.lighting.diffuse.g
>> m_data.lighting.diffuse.b;
m_data.lighting.diffuse /= 255.f;
m_data.lighting.diffuse.a = 1.f;
Input.getTokens( 1, false );
Input
>> m_data.line_width;
m_data.line_width = std::min( 30.f, m_data.line_width ); // 30 pix equals rougly width of a signal pole viewed from ~1m away
// geometry
enum subtype {
lines,
line_strip,
line_loop
} const nodetype = (
Nodedata.type == "lines" ? lines :
Nodedata.type == "line_strip" ? line_strip :
line_loop );
std::size_t vertexcount { 0 };
world_vertex vertex, vertex0, vertex1;
std::string token = Input.getToken<std::string>();
do {
vertex.position.x = std::atof( token.c_str() );
Input.getTokens( 2, false );
Input
>> vertex.position.y
>> vertex.position.z;
// convert all data to gl_lines to allow data merge for matching nodes
switch( nodetype ) {
case lines: {
m_data.vertices.emplace_back( vertex );
break;
}
case line_strip: {
if( vertexcount > 0 ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex );
}
vertex1 = vertex;
++vertexcount;
break;
}
case line_loop: {
if( vertexcount == 0 ) {
vertex0 = vertex;
vertex1 = vertex;
}
else {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex );
}
vertex1 = vertex;
++vertexcount;
break;
}
default: { break; }
}
token = Input.getToken<std::string>();
} while( token != "endline" );
// add closing line for the loop
if( ( nodetype == line_loop )
&& ( vertexcount > 2 ) ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex0 );
}
if( m_data.vertices.size() % 2 != 0 ) {
ErrorLog( "Lines node specified odd number of vertices, encountered in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
m_data.vertices.pop_back();
}
return *this;
}
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
lines_node::merge( lines_node &Lines ) {
if( ( m_data.line_width != Lines.m_data.line_width )
|| ( m_data.lighting != Lines.m_data.lighting ) ) {
// can't merge nodes with different appearance
return false;
}
// add geometry from provided node
m_data.area.center =
interpolate(
m_data.area.center, Lines.m_data.area.center,
static_cast<float>( Lines.m_data.vertices.size() ) / ( Lines.m_data.vertices.size() + m_data.vertices.size() ) );
m_data.vertices.insert(
std::end( m_data.vertices ),
std::begin( Lines.m_data.vertices ), std::end( Lines.m_data.vertices ) );
// NOTE: we could recalculate radius with something other than brute force, but it'll do
compute_radius();
return true;
}
// generates renderable version of held non-instanced geometry in specified geometry bank
void
lines_node::create_geometry( geometrybank_handle const &Bank ) {
vertex_array vertices; vertices.reserve( m_data.vertices.size() );
for( auto const &vertex : m_data.vertices ) {
vertices.emplace_back(
vertex.position - m_data.origin,
vertex.normal,
vertex.texture );
}
m_data.geometry = GfxRenderer.Insert( vertices, Bank, GL_LINES );
std::vector<world_vertex>().swap( m_data.vertices ); // hipster shrink_to_fit
}
// calculates node's bounding radius
void
lines_node::compute_radius() {
auto squaredradius { 0.0 };
for( auto const &vertex : m_data.vertices ) {
squaredradius = std::max(
squaredradius,
glm::length2( vertex.position - m_data.area.center ) );
}
m_data.area.radius = static_cast<float>( std::sqrt( squaredradius ) );
}
/*
memory_node &
memory_node::deserialize( cParser &Input, node_data const &Nodedata ) {
// import common data
m_name = Nodedata.name;
Input.getTokens( 3 );
Input
>> m_data.area.center.x
>> m_data.area.center.y
>> m_data.area.center.z;
TMemCell memorycell( Nodedata.name );
memorycell.Load( &Input );
}
*/
} // scene
namespace editor {
basic_node::basic_node( scene::node_data const &Nodedata ) :
m_name( Nodedata.name )
{
m_rangesquaredmin = Nodedata.range_min * Nodedata.range_min;
m_rangesquaredmax = (
Nodedata.range_max >= 0.0 ?
Nodedata.range_max * Nodedata.range_max :
std::numeric_limits<double>::max() );
}
float const &
basic_node::radius() {
if( m_area.radius == -1.0 ) {
// calculate if needed
radius_();
}
return m_area.radius;
}
// radius() subclass details, calculates node's bounding radius
// by default nodes are 'virtual don't extend from their center point
void
basic_node::radius_() {
m_area.radius = 0.f;
}
} // editor
//---------------------------------------------------------------------------

318
scenenode.h Normal file
View File

@@ -0,0 +1,318 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <vector>
#include "material.h"
#include "vertex.h"
#include "openglgeometrybank.h"
#include "parser.h"
#include "Model3d.h"
struct lighting_data {
glm::vec4 diffuse { 0.8f, 0.8f, 0.8f, 1.0f };
glm::vec4 ambient { 0.2f, 0.2f, 0.2f, 1.0f };
glm::vec4 specular { 0.0f, 0.0f, 0.0f, 1.0f };
};
inline
bool
operator==( lighting_data const &Left, lighting_data const &Right ) {
return ( ( Left.diffuse == Right.diffuse )
&& ( Left.ambient == Right.ambient )
&& ( Left.specular == Right.specular ) ); }
inline
bool
operator!=( lighting_data const &Left, lighting_data const &Right ) {
return !( Left == Right ); }
namespace scene {
struct bounding_area {
glm::dvec3 center; // mid point of the rectangle
float radius { -1.0f }; // radius of the bounding sphere
bounding_area() = default;
bounding_area( glm::dvec3 Center, float Radius ) :
center( Center ),
radius( Radius )
{}
};
/*
enum nodetype {
unknown,
model,
triangles,
lines,
dynamic,
track,
traction,
powersource,
sound,
memorycell,
eventlauncher
};
class node_manager {
};
*/
struct node_data {
double range_min { 0.0 };
double range_max { std::numeric_limits<double>::max() };
std::string name;
std::string type;
};
// holds unique piece of geometry, covered with single material
class shape_node {
friend class basic_region; // region might want to modify node content when it's being inserted
public:
// types
struct shapenode_data {
// placement and visibility
scene::bounding_area area; // bounding area, in world coordinates
bool visible { true }; // visibility flag
double rangesquared_min { 0.0 }; // visibility range, min
double rangesquared_max { 0.0 }; // visibility range, max
// material data
material_handle material { 0 };
lighting_data lighting;
bool translucent { false }; // whether opaque or translucent
// geometry data
std::vector<world_vertex> vertices; // world space source data of the geometry
glm::dvec3 origin; // world position of the relative coordinate system origin
geometry_handle geometry { 0, 0 }; // relative origin-centered chunk of geometry held by gfx renderer
};
// methods
// restores content of the node from provded input stream
shape_node &
deserialize( cParser &Input, scene::node_data const &Nodedata );
// imports data from provided submodel
shape_node &
convert( TSubModel const *Submodel );
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
merge( shape_node &Shape );
// generates renderable version of held non-instanced geometry in specified geometry bank
void
create_geometry( geometrybank_handle const &Bank );
// calculates shape's bounding radius
void
compute_radius();
// set visibility
void
visible( bool State ) {
m_data.visible = State; }
// set origin point
void
origin( glm::dvec3 Origin ) {
m_data.origin = Origin; }
// data access
shapenode_data const &
data() const {
return m_data; }
private:
// members
std::string m_name;
shapenode_data m_data;
};
// holds a group of untextured lines
class lines_node {
friend class basic_region; // region might want to modify node content when it's being inserted
public:
// types
struct linesnode_data {
// placement and visibility
scene::bounding_area area; // bounding area, in world coordinates
bool visible { true }; // visibility flag
double rangesquared_min { 0.0 }; // visibility range, min
double rangesquared_max { 0.0 }; // visibility range, max
// material data
lighting_data lighting;
float line_width; // thickness of stored lines
// geometry data
std::vector<world_vertex> vertices; // world space source data of the geometry
glm::dvec3 origin; // world position of the relative coordinate system origin
geometry_handle geometry { 0, 0 }; // relative origin-centered chunk of geometry held by gfx renderer
};
// methods
// restores content of the node from provded input stream
lines_node &
deserialize( cParser &Input, scene::node_data const &Nodedata );
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
merge( lines_node &Lines );
// generates renderable version of held non-instanced geometry in specified geometry bank
void
create_geometry( geometrybank_handle const &Bank );
// calculates shape's bounding radius
void
compute_radius();
// set visibility
void
visible( bool State ) {
m_data.visible = State; }
// set origin point
void
origin( glm::dvec3 Origin ) {
m_data.origin = Origin; }
// data access
linesnode_data const &
data() const {
return m_data; }
private:
// members
std::string m_name;
linesnode_data m_data;
};
/*
// holds geometry for specific piece of track/road/waterway
class path_node {
friend class basic_region; // region might want to modify node content when it's being inserted
public:
// types
// TODO: enable after track class refactoring
struct pathnode_data {
// placement and visibility
bounding_area area; // bounding area, in world coordinates
bool visible { true }; // visibility flag
// material data
material_handle material_1 { 0 };
material_handle material_2 { 0 };
lighting_data lighting;
TEnvironmentType environment { e_flat };
// geometry data
std::vector<world_vertex> vertices; // world space source data of the geometry
glm::dvec3 origin; // world position of the relative coordinate system origin
using geometryhandle_sequence = std::vector<geometry_handle>;
geometryhandle_sequence geometry_1; // geometry chunks textured with texture 1
geometryhandle_sequence geometry_2; // geometry chunks textured with texture 2
};
// methods
// restores content of the node from provded input stream
// TODO: implement
path_node &
deserialize( cParser &Input, node_data const &Nodedata );
// binds specified track to the node
// TODO: remove after track class refactoring
void
path( TTrack *Path ) {
m_path = Path; }
TTrack *
path() {
return m_path; }
private:
// members
// // TODO: enable after track class refactoring
// pathnode_data m_data;
TTrack * m_path;
};
*/
/*
// holds reference to memory cell
class memorycell_node {
friend class basic_region; // region might want to modify node content when it's being inserted
public:
// types
struct memorynode_data {
// placement and visibility
bounding_area area; // bounding area, in world coordinates
bool visible { false }; // visibility flag
};
// methods
// restores content of the node from provded input stream
// TODO: implement
memory_node &
deserialize( cParser &Input, node_data const &Nodedata );
void
cell( TMemCell *Cell ) {
m_memorycell = Cell; }
TMemCell *
cell() {
return m_memorycell; }
private:
// members
memorynode_data m_data;
TMemCell * m_memorycell;
};
*/
} // scene
namespace editor {
// base interface for nodes which can be actvated in scenario editor
struct basic_node {
public:
// constructor
basic_node() = default; // TODO: remove after refactor
basic_node( scene::node_data const &Nodedata );
// destructor
virtual ~basic_node() = default;
// methods
std::string const &
name() const {
return m_name; }
void
location( glm::dvec3 const Location ) {
m_area.center = Location; }
glm::dvec3 const &
location() const {
return m_area.center; };
float const &
radius();
void
visible( bool const Visible ) {
m_visible = Visible; }
bool
visible() const {
return m_visible; }
protected:
// methods
// radius() subclass details, calculates node's bounding radius
virtual void radius_();
// members
scene::bounding_area m_area;
bool m_visible { true };
double m_rangesquaredmin { 0.0 }; // visibility range, min
double m_rangesquaredmax { 0.0 }; // visibility range, max
std::string m_name;
};
} // editor
//---------------------------------------------------------------------------

876
simulation.cpp Normal file
View File

@@ -0,0 +1,876 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "simulation.h"
#include "Globals.h"
#include "Logs.h"
#include "uilayer.h"
namespace simulation {
state_manager State;
event_manager Events;
memory_table Memory;
path_table Paths;
traction_table Traction;
powergridsource_table Powergrid;
instance_table Instances;
vehicle_table Vehicles;
light_array Lights;
lua Lua;
scene::basic_region *Region { nullptr };
bool
state_manager::deserialize( std::string const &Scenariofile ) {
// TODO: move initialization to separate routine so we can reuse it
SafeDelete( Region );
Region = new scene::basic_region();
// TODO: check first for presence of serialized binary files
// if this fails, fall back on the legacy text format
scene::scratch_data importscratchpad;
importscratchpad.binary.terrain = Region->deserialize( Scenariofile );
// NOTE: for the time being import from text format is a given, since we don't have full binary serialization
cParser scenarioparser( Scenariofile, cParser::buffer_FILE, Global::asCurrentSceneryPath, Global::bLoadTraction );
if( false == scenarioparser.ok() ) { return false; }
deserialize( scenarioparser, importscratchpad );
// if we didn't find usable binary version of the scenario files, create them now for future use
if( false == importscratchpad.binary.terrain ) { Region->serialize( Scenariofile ); }
Global::iPause &= ~0x10; // koniec pauzy wczytywania
return true;
}
// legacy method, calculates changes in simulation state over specified time
void
state_manager::update( double const Deltatime, int Iterationcount ) {
// aktualizacja animacji krokiem FPS: dt=krok czasu [s], dt*iter=czas od ostatnich przeliczeń
if (Deltatime == 0.0) {
// jeśli załączona jest pauza, to tylko obsłużyć ruch w kabinie trzeba
return;
}
auto const totaltime { Deltatime * Iterationcount };
// NOTE: we perform animations first, as they can determine factors like contact with powergrid
TAnimModel::AnimUpdate( totaltime ); // wykonanie zakolejkowanych animacji
simulation::Powergrid.update( totaltime );
simulation::Vehicles.update( Deltatime, Iterationcount );
}
// restores class data from provided stream
void
state_manager::deserialize( cParser &Input, scene::scratch_data &Scratchpad ) {
// prepare deserialization function table
// since all methods use the same objects, we can have simple, hard-coded binds or lambdas for the task
using deserializefunction = void(state_manager::*)(cParser &, scene::scratch_data &);
std::vector<
std::pair<
std::string,
deserializefunction> > functionlist = {
{ "atmo", &state_manager::deserialize_atmo },
{ "camera", &state_manager::deserialize_camera },
{ "config", &state_manager::deserialize_config },
{ "description", &state_manager::deserialize_description },
{ "event", &state_manager::deserialize_event },
{ "lua", &state_manager::deserialize_lua },
{ "firstinit", &state_manager::deserialize_firstinit },
{ "light", &state_manager::deserialize_light },
{ "node", &state_manager::deserialize_node },
{ "origin", &state_manager::deserialize_origin },
{ "endorigin", &state_manager::deserialize_endorigin },
{ "rotate", &state_manager::deserialize_rotate },
{ "sky", &state_manager::deserialize_sky },
{ "test", &state_manager::deserialize_test },
{ "time", &state_manager::deserialize_time },
{ "trainset", &state_manager::deserialize_trainset },
{ "endtrainset", &state_manager::deserialize_endtrainset } };
using deserializefunctionbind = std::function<void()>;
std::unordered_map<
std::string,
deserializefunctionbind> functionmap;
for( auto &function : functionlist ) {
functionmap.emplace( function.first, std::bind( function.second, this, std::ref( Input ), std::ref( Scratchpad ) ) );
}
// deserialize content from the provided input
auto
timelast { std::chrono::steady_clock::now() },
timenow { timelast };
std::string token { Input.getToken<std::string>() };
while( false == token.empty() ) {
auto lookup = functionmap.find( token );
if( lookup != functionmap.end() ) {
lookup->second();
}
else {
ErrorLog( "Bad scenario: unexpected token \"" + token + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
}
timenow = std::chrono::steady_clock::now();
if( std::chrono::duration_cast<std::chrono::milliseconds>( timenow - timelast ).count() >= 200 ) {
timelast = timenow;
glfwPollEvents();
UILayer.set_progress( Input.getProgress(), Input.getFullProgress() );
GfxRenderer.Render();
}
token = Input.getToken<std::string>();
}
if( false == Scratchpad.initialized ) {
// manually perform scenario initialization
deserialize_firstinit( Input, Scratchpad );
}
}
void
state_manager::deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad ) {
// NOTE: parameter system needs some decent replacement, but not worth the effort if we're moving to built-in editor
// atmosphere color; legacy parameter, no longer used
Input.getTokens( 3 );
// fog range
Input.getTokens( 2 );
Input
>> Global::fFogStart
>> Global::fFogEnd;
if( Global::fFogEnd > 0.0 ) {
// fog colour; optional legacy parameter, no longer used
Input.getTokens( 3 );
}
std::string token { Input.getToken<std::string>() };
if( token != "endatmo" ) {
// optional overcast parameter
Global::Overcast = clamp( std::stof( token ), 0.f, 1.f );
}
while( ( false == token.empty() )
&& ( token != "endatmo" ) ) {
// anything else left in the section has no defined meaning
token = Input.getToken<std::string>();
}
}
void
state_manager::deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad ) {
glm::dvec3 xyz, abc;
int i = -1, into = -1; // do której definicji kamery wstawić
std::string token;
do { // opcjonalna siódma liczba określa numer kamery, a kiedyś były tylko 3
Input.getTokens();
Input >> token;
switch( ++i ) { // kiedyś camera miało tylko 3 współrzędne
case 0: { xyz.x = atof( token.c_str() ); break; }
case 1: { xyz.y = atof( token.c_str() ); break; }
case 2: { xyz.z = atof( token.c_str() ); break; }
case 3: { abc.x = atof( token.c_str() ); break; }
case 4: { abc.y = atof( token.c_str() ); break; }
case 5: { abc.z = atof( token.c_str() ); break; }
case 6: { into = atoi( token.c_str() ); break; } // takie sobie, bo można wpisać -1
default: { break; }
}
} while( token.compare( "endcamera" ) != 0 );
if( into < 0 )
into = ++Global::iCameraLast;
if( into < 10 ) { // przepisanie do odpowiedniego miejsca w tabelce
Global::FreeCameraInit[ into ] = xyz;
Global::FreeCameraInitAngle[ into ] =
Math3D::vector3(
glm::radians( abc.x ),
glm::radians( abc.y ),
glm::radians( abc.z ) );
Global::iCameraLast = into; // numer ostatniej
}
/*
// cleaned up version of the above.
// NOTE: no longer supports legacy mode where some parameters were optional
Input.getTokens( 7 );
glm::vec3
position,
rotation;
int index;
Input
>> position.x
>> position.y
>> position.z
>> rotation.x
>> rotation.y
>> rotation.z
>> index;
skip_until( Input, "endcamera" );
// TODO: finish this
*/
}
void
state_manager::deserialize_config( cParser &Input, scene::scratch_data &Scratchpad ) {
// config parameters (re)definition
Global::ConfigParse( Input );
}
void
state_manager::deserialize_description( cParser &Input, scene::scratch_data &Scratchpad ) {
// legacy section, never really used;
skip_until( Input, "enddescription" );
}
void
state_manager::deserialize_event( cParser &Input, scene::scratch_data &Scratchpad ) {
// TODO: refactor event class and its de/serialization. do offset and rotation after deserialization is done
auto *event = new TEvent();
Math3D::vector3 offset = (
Scratchpad.location.offset.empty() ?
Math3D::vector3() :
Math3D::vector3(
Scratchpad.location.offset.top().x,
Scratchpad.location.offset.top().y,
Scratchpad.location.offset.top().z ) );
event->Load( &Input, offset );
if( false == simulation::Events.insert( event ) ) {
delete event;
}
}
void state_manager::deserialize_lua( cParser &Input, scene::scratch_data &Scratchpad )
{
Input.getTokens(1, false);
std::string file;
Input >> file;
simulation::Lua.interpret(Global::asCurrentSceneryPath + file);
}
void
state_manager::deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad ) {
if( true == Scratchpad.initialized ) { return; }
simulation::Paths.InitTracks();
simulation::Traction.InitTraction();
simulation::Events.InitEvents();
simulation::Events.InitLaunchers();
simulation::Memory.InitCells();
Scratchpad.initialized = true;
}
void
state_manager::deserialize_light( cParser &Input, scene::scratch_data &Scratchpad ) {
// legacy section, no longer used nor supported;
skip_until( Input, "endlight" );
}
void
state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad ) {
auto const inputline = Input.Line(); // cache in case we need to report error
scene::node_data nodedata;
// common data and node type indicator
Input.getTokens( 4 );
Input
>> nodedata.range_max
>> nodedata.range_min
>> nodedata.name
>> nodedata.type;
// type-based deserialization. not elegant but it'll do
if( nodedata.type == "dynamic" ) {
auto *vehicle { deserialize_dynamic( Input, Scratchpad, nodedata ) };
// vehicle import can potentially fail
if( vehicle == nullptr ) { return; }
if( false == simulation::Vehicles.insert( vehicle ) ) {
ErrorLog( "Bad scenario: vehicle with duplicate name \"" + vehicle->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
if( ( vehicle->MoverParameters->CategoryFlag == 1 ) // trains only
&& ( ( vehicle->MoverParameters->SecuritySystem.SystemType != 0 )
|| ( vehicle->MoverParameters->SandCapacity > 0.0 ) ) ) {
// we check for presence of security system or sand load, as a way to determine whether the vehicle is a controllable engine
// NOTE: this isn't 100% precise, e.g. middle EZT module comes with security system, while it has no lights, and some engines
// don't have security systems fitted
simulation::Lights.insert( vehicle );
}
}
else if( nodedata.type == "track" ) {
auto *path { deserialize_path( Input, Scratchpad, nodedata ) };
// duplicates of named tracks are currently experimentally allowed
if( false == simulation::Paths.insert( path ) ) {
ErrorLog( "Bad scenario: track with duplicate name \"" + path->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
/*
delete path;
delete pathnode;
*/
}
simulation::Region->insert_path( path, Scratchpad );
}
else if( nodedata.type == "traction" ) {
auto *traction { deserialize_traction( Input, Scratchpad, nodedata ) };
// traction loading is optional
if( traction == nullptr ) { return; }
if( false == simulation::Traction.insert( traction ) ) {
ErrorLog( "Bad scenario: traction piece with duplicate name \"" + traction->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
simulation::Region->insert_traction( traction, Scratchpad );
}
else if( nodedata.type == "tractionpowersource" ) {
auto *powersource { deserialize_tractionpowersource( Input, Scratchpad, nodedata ) };
// traction loading is optional
if( powersource == nullptr ) { return; }
if( false == simulation::Powergrid.insert( powersource ) ) {
ErrorLog( "Bad scenario: power grid source with duplicate name \"" + powersource->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
/*
// TODO: implement this
simulation::Region.insert_powersource( powersource, Scratchpad );
*/
}
else if( nodedata.type == "model" ) {
if( nodedata.range_min < 0.0 ) {
// convert and import 3d terrain
auto *instance { deserialize_model( Input, Scratchpad, nodedata ) };
// model import can potentially fail
if( instance == nullptr ) { return; }
// go through submodels, and import them as shapes
auto const cellcount = instance->TerrainCount() + 1; // zliczenie submodeli
for( auto i = 1; i < cellcount; ++i ) {
auto *submodel = instance->TerrainSquare( i - 1 );
simulation::Region->insert_shape(
scene::shape_node().convert( submodel ),
Scratchpad,
false );
// if there's more than one group of triangles in the cell they're held as children of the primary submodel
submodel = submodel->ChildGet();
while( submodel != nullptr ) {
simulation::Region->insert_shape(
scene::shape_node().convert( submodel ),
Scratchpad,
false );
submodel = submodel->NextGet();
}
}
// with the import done we can get rid of the source model
delete instance;
}
else {
// regular instance of 3d mesh
auto *instance { deserialize_model( Input, Scratchpad, nodedata ) };
// model import can potentially fail
if( instance == nullptr ) { return; }
if( false == simulation::Instances.insert( instance ) ) {
ErrorLog( "Bad scenario: 3d model instance with duplicate name \"" + instance->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
simulation::Region->insert_instance( instance, Scratchpad );
}
}
else if( ( nodedata.type == "triangles" )
|| ( nodedata.type == "triangle_strip" )
|| ( nodedata.type == "triangle_fan" ) ) {
if( false == Scratchpad.binary.terrain ) {
simulation::Region->insert_shape(
scene::shape_node().deserialize(
Input, nodedata ),
Scratchpad,
true );
}
else {
// all shapes were already loaded from the binary version of the file
skip_until( Input, "endtri" );
}
}
else if( ( nodedata.type == "lines" )
|| ( nodedata.type == "line_strip" )
|| ( nodedata.type == "line_loop" ) ) {
if( false == Scratchpad.binary.terrain ) {
simulation::Region->insert_lines(
scene::lines_node().deserialize(
Input, nodedata ),
Scratchpad );
}
else {
// all lines were already loaded from the binary version of the file
skip_until( Input, "endline" );
}
}
else if( nodedata.type == "memcell" ) {
auto *memorycell { deserialize_memorycell( Input, Scratchpad, nodedata ) };
if( false == simulation::Memory.insert( memorycell ) ) {
ErrorLog( "Bad scenario: memory cell with duplicate name \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
/*
// TODO: implement this
simulation::Region.insert_memorycell( memorycell, Scratchpad );
*/
}
else if( nodedata.type == "eventlauncher" ) {
auto *eventlauncher { deserialize_eventlauncher( Input, Scratchpad, nodedata ) };
if( false == simulation::Events.insert( eventlauncher ) ) {
ErrorLog( "Bad scenario: event launcher with duplicate name \"" + eventlauncher->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
}
// event launchers can be either global, or local with limited range of activation
// each gets assigned different caretaker
if( true == eventlauncher->IsGlobal() ) {
simulation::Events.queue( eventlauncher );
}
else {
simulation::Region->insert_launcher( eventlauncher, Scratchpad );
}
}
else if( nodedata.type == "sound" ) {
auto *sound { deserialize_sound( Input, Scratchpad, nodedata ) };
simulation::Region->insert_sound( sound, Scratchpad );
}
}
void
state_manager::deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad ) {
glm::dvec3 offset;
Input.getTokens( 3 );
Input
>> offset.x
>> offset.y
>> offset.z;
// sumowanie całkowitego przesunięcia
Scratchpad.location.offset.emplace(
offset + (
Scratchpad.location.offset.empty() ?
glm::dvec3() :
Scratchpad.location.offset.top() ) );
}
void
state_manager::deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad ) {
if( false == Scratchpad.location.offset.empty() ) {
Scratchpad.location.offset.pop();
}
else {
ErrorLog( "Bad origin: endorigin instruction with empty origin stack in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
}
}
void
state_manager::deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ) {
Input.getTokens( 3 );
Input
>> Scratchpad.location.rotation.x
>> Scratchpad.location.rotation.y
>> Scratchpad.location.rotation.z;
}
void
state_manager::deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad ) {
// sky model
Input.getTokens( 1 );
Input
>> Global::asSky;
// anything else left in the section has no defined meaning
skip_until( Input, "endsky" );
}
void
state_manager::deserialize_test( cParser &Input, scene::scratch_data &Scratchpad ) {
// legacy section, no longer supported;
skip_until( Input, "endtest" );
}
void
state_manager::deserialize_time( cParser &Input, scene::scratch_data &Scratchpad ) {
// current scenario time
cParser timeparser( Input.getToken<std::string>() );
timeparser.getTokens( 2, false, ":" );
auto &time = simulation::Time.data();
timeparser
>> time.wHour
>> time.wMinute;
// remaining sunrise and sunset parameters are no longer used, as they're now calculated dynamically
// anything else left in the section has no defined meaning
skip_until( Input, "endtime" );
}
void
state_manager::deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad ) {
if( true == Scratchpad.trainset.is_open ) {
// shouldn't happen but if it does wrap up currently open trainset and report an error
deserialize_endtrainset( Input, Scratchpad );
ErrorLog( "Bad scenario: encountered nested trainset definitions in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() ) + ")" );
}
Scratchpad.trainset = scene::scratch_data::trainset_data();
Scratchpad.trainset.is_open = true;
Input.getTokens( 4 );
Input
>> Scratchpad.trainset.name
>> Scratchpad.trainset.track
>> Scratchpad.trainset.offset
>> Scratchpad.trainset.velocity;
}
void
state_manager::deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ) {
if( ( false == Scratchpad.trainset.is_open )
|| ( true == Scratchpad.trainset.vehicles.empty() ) ) {
// not bloody likely but we better check for it just the same
ErrorLog( "Bad trainset: empty trainset defined in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
Scratchpad.trainset.is_open = false;
return;
}
std::size_t vehicleindex { 0 };
for( auto *vehicle : Scratchpad.trainset.vehicles ) {
// go through list of vehicles in the trainset, coupling them together and checking for potential driver
if( ( vehicle->Mechanik != nullptr )
&& ( vehicle->Mechanik->Primary() ) ) {
// primary driver will receive the timetable for this trainset
Scratchpad.trainset.driver = vehicle;
}
if( vehicleindex > 0 ) {
// from second vehicle on couple it with the previous one
Scratchpad.trainset.vehicles[ vehicleindex - 1 ]->AttachPrev(
vehicle,
Scratchpad.trainset.couplings[ vehicleindex - 1 ] );
}
++vehicleindex;
}
if( Scratchpad.trainset.driver != nullptr ) {
// if present, send timetable to the driver
// wysłanie komendy "Timetable" ustawia odpowiedni tryb jazdy
auto *controller = Scratchpad.trainset.driver->Mechanik;
controller->DirectionInitial();
controller->PutCommand(
"Timetable:" + Scratchpad.trainset.name,
Scratchpad.trainset.velocity,
0,
nullptr );
}
if( Scratchpad.trainset.couplings.back() == coupling::faux ) {
// jeśli ostatni pojazd ma sprzęg 0 to założymy mu końcówki blaszane (jak AI się odpali, to sobie poprawi)
Scratchpad.trainset.vehicles.back()->RaLightsSet( -1, TMoverParameters::light::rearendsignals );
}
// all done
Scratchpad.trainset.is_open = false;
}
// creates path and its wrapper, restoring class data from provided stream
TTrack *
state_manager::deserialize_path( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
// TODO: refactor track and wrapper classes and their de/serialization. do offset and rotation after deserialization is done
auto *track = new TTrack( Nodedata );
Math3D::vector3 offset = (
Scratchpad.location.offset.empty() ?
Math3D::vector3() :
Math3D::vector3(
Scratchpad.location.offset.top().x,
Scratchpad.location.offset.top().y,
Scratchpad.location.offset.top().z ) );
track->Load( &Input, offset );
return track;
}
TTraction *
state_manager::deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
if( false == Global::bLoadTraction ) {
skip_until( Input, "endtraction" );
return nullptr;
}
// TODO: refactor track and wrapper classes and their de/serialization. do offset and rotation after deserialization is done
auto *traction = new TTraction( Nodedata );
auto offset = (
Scratchpad.location.offset.empty() ?
glm::dvec3() :
Scratchpad.location.offset.top() );
traction->Load( &Input, offset );
return traction;
}
TTractionPowerSource *
state_manager::deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
if( false == Global::bLoadTraction ) {
skip_until( Input, "end" );
return nullptr;
}
auto *powersource = new TTractionPowerSource( Nodedata );
powersource->Load( &Input );
// adjust location
powersource->location( transform( powersource->location(), Scratchpad ) );
return powersource;
}
TMemCell *
state_manager::deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
auto *memorycell = new TMemCell( Nodedata );
memorycell->Load( &Input );
// adjust location
memorycell->location( transform( memorycell->location(), Scratchpad ) );
return memorycell;
}
TEventLauncher *
state_manager::deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
glm::dvec3 location;
Input.getTokens( 3 );
Input
>> location.x
>> location.y
>> location.z;
auto *eventlauncher = new TEventLauncher( Nodedata );
eventlauncher->Load( &Input );
eventlauncher->location( transform( location, Scratchpad ) );
return eventlauncher;
}
TAnimModel *
state_manager::deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
glm::dvec3 location;
glm::vec3 rotation;
Input.getTokens( 4 );
Input
>> location.x
>> location.y
>> location.z
>> rotation.y;
auto *instance = new TAnimModel( Nodedata );
instance->RaAnglesSet( Scratchpad.location.rotation + rotation ); // dostosowanie do pochylania linii
if( false == instance->Load( &Input, false ) ) {
// model nie wczytał się - ignorowanie node
SafeDelete( instance );
}
instance->location( transform( location, Scratchpad ) );
return instance;
}
TDynamicObject *
state_manager::deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
if( false == Scratchpad.trainset.is_open ) {
// part of trainset data is used when loading standalone vehicles, so clear it just in case
Scratchpad.trainset = scene::scratch_data::trainset_data();
}
auto const inputline { Input.Line() }; // cache in case of errors
// basic attributes
auto const datafolder { Input.getToken<std::string>() };
auto const skinfile { Input.getToken<std::string>() };
auto const mmdfile { Input.getToken<std::string>() };
auto const pathname = (
Scratchpad.trainset.is_open ?
Scratchpad.trainset.track :
Input.getToken<std::string>() );
auto const offset { Input.getToken<double>( false ) };
auto const drivertype { Input.getToken<std::string>() };
auto const couplingdata = (
Scratchpad.trainset.is_open ?
Input.getToken<std::string>() :
"3" );
auto const velocity = (
Scratchpad.trainset.is_open ?
Scratchpad.trainset.velocity :
Input.getToken<float>( false ) );
// extract coupling type and optional parameters
auto const couplingdatawithparams = couplingdata.find( '.' );
auto coupling = (
couplingdatawithparams != std::string::npos ?
std::atoi( couplingdata.substr( 0, couplingdatawithparams ).c_str() ) :
std::atoi( couplingdata.c_str() ) );
if( coupling < 0 ) {
// sprzęg zablokowany (pojazdy nierozłączalne przy manewrach)
coupling = ( -coupling ) | coupling::permanent;
}
if( ( offset != -1.0 )
&& ( std::abs( offset ) > 0.5 ) ) { // maksymalna odległość między sprzęgami - do przemyślenia
// likwidacja sprzęgu, jeśli odległość zbyt duża - to powinno być uwzględniane w fizyce sprzęgów...
coupling = coupling::faux;
}
auto const params = (
couplingdatawithparams != std::string::npos ?
couplingdata.substr( couplingdatawithparams + 1 ) :
"" );
// load amount and type
auto loadcount { Input.getToken<int>( false ) };
auto loadtype = (
loadcount ?
Input.getToken<std::string>() :
"" );
if( loadtype == "enddynamic" ) {
// idiotoodporność: ładunek bez podanego typu nie liczy się jako ładunek
loadcount = 0;
loadtype = "";
}
auto *path = simulation::Paths.find( pathname );
if( path == nullptr ) {
ErrorLog( "Bad scenario: vehicle \"" + Nodedata.name + "\" placed on nonexistent path \"" + pathname + "\" in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
skip_until( Input, "enddynamic" );
return nullptr;
}
if( ( true == Scratchpad.trainset.vehicles.empty() ) // jeśli pierwszy pojazd,
&& ( false == path->asEvent0Name.empty() ) // tor ma Event0
&& ( std::abs( velocity ) <= 1.f ) // a skład stoi
&& ( Scratchpad.trainset.offset >= 0.0 ) // ale może nie sięgać na owy tor
&& ( Scratchpad.trainset.offset < 8.0 ) ) { // i raczej nie sięga
// przesuwamy około pół EU07 dla wstecznej zgodności
Scratchpad.trainset.offset = 8.0;
}
auto *vehicle = new TDynamicObject();
auto const length =
vehicle->Init(
Nodedata.name,
datafolder, skinfile, mmdfile,
path,
( offset == -1.0 ?
Scratchpad.trainset.offset :
Scratchpad.trainset.offset - offset ),
drivertype,
velocity,
Scratchpad.trainset.name,
loadcount, loadtype,
( offset == -1.0 ),
params );
if( length != 0.0 ) { // zero oznacza błąd
// przesunięcie dla kolejnego, minus bo idziemy w stronę punktu 1
Scratchpad.trainset.offset -= length;
// automatically establish permanent connections for couplers which specify them in their definitions
if( ( coupling != 0 )
&& ( vehicle->MoverParameters->Couplers[ ( offset == -1.0 ? 0 : 1 ) ].AllowedFlag & coupling::permanent ) ) {
coupling |= coupling::permanent;
}
if( true == Scratchpad.trainset.is_open ) {
Scratchpad.trainset.vehicles.emplace_back( vehicle );
Scratchpad.trainset.couplings.emplace_back( coupling );
}
}
else {
delete vehicle;
skip_until( Input, "enddynamic" );
return nullptr;
}
auto const destination { Input.getToken<std::string>() };
if( destination != "enddynamic" ) {
// optional vehicle destination parameter
vehicle->asDestination = Input.getToken<std::string>();
skip_until( Input, "enddynamic" );
}
return vehicle;
}
sound *
state_manager::deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ) {
glm::dvec3 location;
Input.getTokens( 3 );
Input
>> location.x
>> location.y
>> location.z;
// adjust location
location = transform( location, Scratchpad );
auto const soundname { Input.getToken<std::string>() };
auto *sound = sound_man->create_text_sound(soundname);
sound->position((glm::vec3)location).dist(Nodedata.range_max);
skip_until( Input, "endsound" );
return sound;
}
// skips content of stream until specified token
void
state_manager::skip_until( cParser &Input, std::string const &Token ) {
std::string token { Input.getToken<std::string>() };
while( ( false == token.empty() )
&& ( token != Token ) ) {
token = Input.getToken<std::string>();
}
}
// transforms provided location by specifed rotation and offset
glm::dvec3
state_manager::transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad ) {
if( Scratchpad.location.rotation != glm::vec3( 0, 0, 0 ) ) {
auto const rotation = glm::radians( Scratchpad.location.rotation );
Location = glm::rotateY<double>( Location, rotation.y ); // Ra 2014-11: uwzględnienie rotacji
}
if( false == Scratchpad.location.offset.empty() ) {
Location += Scratchpad.location.offset.top();
}
return Location;
}
} // simulation
//---------------------------------------------------------------------------

92
simulation.h Normal file
View File

@@ -0,0 +1,92 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "parser.h"
#include "scene.h"
#include "event.h"
#include "MemCell.h"
#include "EvLaunch.h"
#include "Track.h"
#include "Traction.h"
#include "TractionPower.h"
#include "sound.h"
#include "AnimModel.h"
#include "DynObj.h"
#include "Driver.h"
#include "lightarray.h"
#include "Event.h"
#include "lua.h"
namespace simulation {
class state_manager {
public:
// types
// methods
// legacy method, calculates changes in simulation state over specified time
void
update( double dt, int iter );
bool
deserialize( std::string const &Scenariofile );
private:
// methods
// restores class data from provided stream
void deserialize( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_config( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_description( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_event( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_lua( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_light( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_node( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_sky( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_test( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_time( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad );
TTrack * deserialize_path( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TTraction * deserialize_traction( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TTractionPowerSource * deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TMemCell * deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TEventLauncher * deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TAnimModel * deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
TDynamicObject * deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
sound * deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
// skips content of stream until specified token
void skip_until( cParser &Input, std::string const &Token );
// transforms provided location by specifed rotation and offset
glm::dvec3 transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad );
};
extern state_manager State;
extern event_manager Events;
extern memory_table Memory;
extern path_table Paths;
extern traction_table Traction;
extern powergridsource_table Powergrid;
extern instance_table Instances;
extern vehicle_table Vehicles;
extern light_array Lights;
extern lua Lua;
extern scene::basic_region *Region;
} // simulation
//---------------------------------------------------------------------------

View File

@@ -348,6 +348,13 @@ sound& sound::position(glm::vec3 p)
return *this;
}
glm::vec3 sound::location()
{
if (mode == global)
throw std::runtime_error("sound: bad state, no position");
return pos;
}
sound& sound::position(Math3D::vector3 const &pos)
{
position((glm::vec3)glm::make_vec3(&pos.x));

View File

@@ -13,6 +13,7 @@
#include <glm/glm.hpp>
#include "dumb3d.h"
#include "parser.h"
#include "Names.h"
class load_error : public std::runtime_error
{
@@ -70,6 +71,7 @@ public:
virtual ~sound();
glm::vec3 location(); //get position
virtual bool is_playing() = 0;
virtual void play() = 0;

View File

@@ -49,6 +49,7 @@
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <tuple>
#include <cctype>
#include <locale>
#include <codecvt>

View File

@@ -70,6 +70,8 @@ static std::unordered_map<std::string, std::string> m_cabcontrols = {
{ "pantalloff_sw:", "all pantographs" },
{ "pantselected_sw:", "selected pantograph" },
{ "pantselectedoff_sw:", "selected pantograph" },
{ "pantcompressor_sw:", "pantograph compressor" },
{ "pantcompressorvalve_sw:", "pantograph 3 way valve" },
{ "trainheating_sw:", "heating" },
{ "signalling_sw:", "braking indicator" },
{ "door_signalling_sw:", "door locking" },

View File

@@ -1,5 +1,5 @@
#pragma once
#define VERSION_MAJOR 17
#define VERSION_MINOR 914
#define VERSION_MINOR 1027
#define VERSION_REVISION 0

33
vertex.cpp Normal file
View File

@@ -0,0 +1,33 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "vertex.h"
template <>
world_vertex &
world_vertex::operator+=( world_vertex const &Right ) {
position += Right.position;
normal += Right.normal;
texture += Right.texture;
return *this;
}
template <>
world_vertex &
world_vertex::operator*=( world_vertex const &Right ) {
position *= Right.position;
normal *= Right.normal;
texture *= Right.texture;
return *this;
}
//---------------------------------------------------------------------------

87
vertex.h Normal file
View File

@@ -0,0 +1,87 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <glm/glm.hpp>
#include "usefull.h"
// geometry vertex with double precision position
struct world_vertex {
// members
glm::dvec3 position;
glm::vec3 normal;
glm::vec2 texture;
// overloads
// operator+
template <typename Scalar_>
world_vertex &
operator+=( Scalar_ const &Right ) {
position += Right;
normal += Right;
texture += Right;
return *this; }
template <typename Scalar_>
friend
world_vertex
operator+( world_vertex Left, Scalar_ const &Right ) {
Left += Right;
return Left; }
// operator*
template <typename Scalar_>
world_vertex &
operator*=( Scalar_ const &Right ) {
position *= Right;
normal *= Right;
texture *= Right;
return *this; }
template <typename Type_>
friend
world_vertex
operator*( world_vertex Left, Type_ const &Right ) {
Left *= Right;
return Left; }
// methods
// wyliczenie współrzędnych i mapowania punktu na środku odcinka v1<->v2
void
set_half( world_vertex const &Vertex1, world_vertex const &Vertex2 ) {
*this =
interpolate(
Vertex1,
Vertex2,
0.5 ); }
// wyliczenie współrzędnych i mapowania punktu na odcinku v1<->v2
void
set_from_x( world_vertex const &Vertex1, world_vertex const &Vertex2, double const X ) {
*this =
interpolate(
Vertex1,
Vertex2,
( X - Vertex1.position.x ) / ( Vertex2.position.x - Vertex1.position.x ) ); }
// wyliczenie współrzędnych i mapowania punktu na odcinku v1<->v2
void
set_from_z( world_vertex const &Vertex1, world_vertex const &Vertex2, double const Z ) {
*this =
interpolate(
Vertex1,
Vertex2,
( Z - Vertex1.position.z ) / ( Vertex2.position.z - Vertex1.position.z ) ); }
};
template <>
world_vertex &
world_vertex::operator+=( world_vertex const &Right );
template <>
world_vertex &
world_vertex::operator*=( world_vertex const &Right );
//---------------------------------------------------------------------------

View File

@@ -63,7 +63,7 @@ LRESULT APIENTRY WndProc( HWND hWnd, // handle for this window
// obsługa danych przesłanych przez program sterujący
pDane = (PCOPYDATASTRUCT)lParam;
if( pDane->dwData == MAKE_ID4('E', 'U', '0', '7')) // sygnatura danych
World.OnCommandGet( (DaneRozkaz *)( pDane->lpData ) );
World.OnCommandGet( ( multiplayer::DaneRozkaz *)( pDane->lpData ) );
break;
}
case WM_KEYDOWN:

View File

@@ -6,6 +6,9 @@
#else
#include <cinttypes>
#define WORD uint16_t
#define UINT uint32_t
#define WPARAM uint64_t
#define LPARAM uint64_t
#define DWORD uint32_t
#define LONG int32_t
#define BYTE uint8_t