From f3a55a1443d2d506306a93e037c62c384912be98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3lik=20Uszasty?= Date: Fri, 8 Sep 2017 22:00:05 +0200 Subject: [PATCH 01/31] Fixed bug with negative adhesion --- McZapkie/Mover.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index ba9b03f1..b8d93467 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -3956,6 +3956,7 @@ double TMoverParameters::Adhesive(double staticfriction) double Vwheels = nrot * M_PI * WheelDiameter; // predkosc liniowa koła wynikająca z obrotowej double deltaV = V - Vwheels; //poślizg - różnica prędkości w punkcie styku koła i szyny deltaV = std::max(0.0, std::abs(deltaV) - 0.25); //mikropoślizgi do ok. 0,25 m/s nie zrywają przyczepności + Vwheels = abs(Vwheels); adhesion = staticfriction * (28 + Vwheels) / (14 + Vwheels) * ((SandDose? sandfactor : 1) - (1 - adh_factor)*(deltaV / (deltaV + slipfactor))); return adhesion; } From 5c08631c7f9275ad456f933f694e5034a94f776f Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 16 Sep 2017 16:59:20 +0200 Subject: [PATCH 02/31] fixed trackbed normal generation --- AnimModel.h | 41 +- Segment.cpp | 104 ++--- Segment.h | 18 +- Track.cpp | 1062 ++++++++++++++++++++++++++++++++++----------------- Track.h | 49 +-- 5 files changed, 802 insertions(+), 472 deletions(-) diff --git a/AnimModel.h b/AnimModel.h index e24ab335..cf7d9d98 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -77,16 +77,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 +87,20 @@ class TAnimContainer void UpdateModel(); void UpdateModelIK(); bool InMovement(); // czy w trakcie animacji? - double _fastcall AngleGet() - { - return vRotateAngles.z; - }; // jednak ostatnia, T3D ma inny układ - vector3 _fastcall 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 diff --git a/Segment.cpp b/Segment.cpp index f002cc3c..bfed769e 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -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) - { + 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); fLength = 0.01; // crude workaround TODO: fix this properly /* return false; // zerowe nie mogą być @@ -355,7 +354,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 +364,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 +379,9 @@ 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::normalize( glm::vec3{ -dir.z, 0.f, dir.x } ); // wektor poprzeczny if( iEnd == 0 ) iEnd = iSegCount; fEnd = fLength * double( iEnd ) / double( iSegCount ); @@ -406,26 +406,26 @@ 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::normalize( glm::vec3{ -dir.z, 0.f, dir.x } ); // wektor poprzeczny 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 +435,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 +460,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 } ); } } } diff --git a/Segment.h b/Segment.h index 618132da..93209804 100644 --- a/Segment.h +++ b/Segment.h @@ -15,7 +15,7 @@ http://mozilla.org/MPL/2.0/. #include "usefull.h" // 110405 Ra: klasa punktów przekroju z normalnymi - +/* class vector6 : public Math3D::vector3 { // punkt przekroju wraz z wektorem normalnym public: @@ -26,7 +26,6 @@ class vector6 : public Math3D::vector3 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; @@ -45,13 +44,14 @@ class vector6 : public Math3D::vector3 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; @@ -118,18 +118,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(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 diff --git a/Track.cpp b/Track.cpp index ed464b22..ed94059a 100644 --- a/Track.cpp +++ b/Track.cpp @@ -30,7 +30,7 @@ http://mozilla.org/MPL/2.0/. // 101206 Ra: trapezoidalne drogi i tory // 110720 Ra: rozprucie zwrotnicy i odcinki izolowane -static const double fMaxOffset = 0.1; // double(0.1f)==0.100000001490116 +static float const fMaxOffset = 0.1f; // double(0.1f)==0.100000001490116 // const int NextMask[4]={0,1,0,1}; //tor następny dla stanów 0, 1, 2, 3 // const int PrevMask[4]={0,0,1,1}; //tor poprzedni dla stanów 0, 1, 2, 3 const int iLewo4[4] = {5, 3, 4, 6}; // segmenty (1..6) do skręcania w lewo @@ -52,7 +52,7 @@ TSwitchExtension::TSwitchExtension(TTrack *owner, int const what) pPrevs[0] = nullptr; pPrevs[1] = nullptr; fOffset1 = fOffset = fDesiredOffset = -fOffsetDelay; // położenie zasadnicze - fOffset2 = 0.0; // w zasadniczym wewnętrzna iglica dolega + fOffset2 = 0.f; // w zasadniczym wewnętrzna iglica dolega Segments[0] = std::make_shared(owner); // z punktu 1 do 2 Segments[1] = std::make_shared(owner); // z punktu 3 do 4 (1=3 dla zwrotnic; odwrócony dla skrzyżowań, ewentualnie 1=4) Segments[2] = (what >= 3) ? @@ -83,8 +83,7 @@ TIsolated::TIsolated(std::string const &n, TIsolated *i) : // utworznie obwodu izolowanego. nothing to do here. }; -TIsolated::~TIsolated(){ - // usuwanie +// TODO: put this in the cleanup routine on exit /* TIsolated *p=pRoot; while (pRoot) @@ -94,7 +93,6 @@ TIsolated::~TIsolated(){ delete p; } */ -}; TIsolated * TIsolated::Find(std::string const &n) { // znalezienie obiektu albo utworzenie nowego @@ -155,16 +153,6 @@ TTrack::~TTrack() if( eType == tt_Cross ) { delete SwitchExtension->vPoints; // skrzyżowanie może mieć punkty } - -/* if (eType == tt_Normal) - delete Segment; // dla zwrotnic nie usuwać tego (kopiowany) - else - { // usuwanie dodatkowych danych dla niezwykłych odcinków - if (eType == tt_Cross) - delete SwitchExtension->vPoints; // skrzyżowanie może mieć punkty - SafeDelete(SwitchExtension); - } -*/ } void TTrack::Init() @@ -215,7 +203,7 @@ TTrack * TTrack::NullCreate(int dir) trk->bVisible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur // trk->iTrapezoid=1; //są przechyłki do uwzględniania w rysowaniu trk->iCategoryFlag = (iCategoryFlag & 15) | 0x80; // taki sam typ plus informacja, że dodatkowy - double r1, r2; + float r1, r2; Segment->GetRolls(r1, r2); // pobranie przechyłek na początku toru vector3 p1, cv1, cv2, p2; // będziem tworzyć trajektorię lotu if (iCategoryFlag & 1) @@ -965,37 +953,39 @@ bool TTrack::AddDynamicObject(TDynamicObject *Dynamic) const int numPts = 4; const int nnumPts = 12; -const vector6 szyna[nnumPts] = // szyna - vextor6(x,y,mapowanie tekstury,xn,yn,zn) - { // tę wersję opracował Tolein (bez pochylenia) - vector6(0.111, -0.180, 0.00, 1.000, 0.000, 0.000), - vector6(0.046, -0.150, 0.15, 0.707, 0.707, 0.000), - vector6(0.044, -0.050, 0.25, 0.707, -0.707, 0.000), - vector6(0.073, -0.038, 0.35, 0.707, -0.707, 0.000), - vector6(0.072, -0.010, 0.40, 0.707, 0.707, 0.000), - vector6(0.052, -0.000, 0.45, 0.000, 1.000, 0.000), - vector6(0.020, -0.000, 0.55, 0.000, 1.000, 0.000), - vector6(0.000, -0.010, 0.60, -0.707, 0.707, 0.000), - vector6(-0.001, -0.038, 0.65, -0.707, -0.707, 0.000), - vector6(0.028, -0.050, 0.75, -0.707, -0.707, 0.000), - vector6(0.026, -0.150, 0.85, -0.707, 0.707, 0.000), - vector6(-0.039, -0.180, 1.00, -1.000, 0.000, 0.000)}; +// szyna - vextor6(x,y,mapowanie tekstury,xn,yn,zn) +// tę wersję opracował Tolein (bez pochylenia) +// TODO: profile definitions in external files +basic_vertex const szyna[ nnumPts ] = { + {{ 0.111f, -0.180f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.00f, 0.f}}, + {{ 0.046f, -0.150f, 0.f}, { 0.707f, 0.707f, 0.f}, {0.15f, 0.f}}, + {{ 0.044f, -0.050f, 0.f}, { 0.707f, -0.707f, 0.f}, {0.25f, 0.f}}, + {{ 0.073f, -0.038f, 0.f}, { 0.707f, -0.707f, 0.f}, {0.35f, 0.f}}, + {{ 0.072f, -0.010f, 0.f}, { 0.707f, 0.707f, 0.f}, {0.40f, 0.f}}, + {{ 0.052f, -0.000f, 0.f}, { 0.000f, 1.000f, 0.f}, {0.45f, 0.f}}, + {{ 0.020f, -0.000f, 0.f}, { 0.000f, 1.000f, 0.f}, {0.55f, 0.f}}, + {{ 0.000f, -0.010f, 0.f}, {-0.707f, 0.707f, 0.f}, {0.60f, 0.f}}, + {{-0.001f, -0.038f, 0.f}, {-0.707f, -0.707f, 0.f}, {0.65f, 0.f}}, + {{ 0.028f, -0.050f, 0.f}, {-0.707f, -0.707f, 0.f}, {0.75f, 0.f}}, + {{ 0.026f, -0.150f, 0.f}, {-0.707f, 0.707f, 0.f}, {0.85f, 0.f}}, + {{-0.039f, -0.180f, 0.f}, {-1.000f, 0.000f, 0.f}, {1.00f, 0.f}} }; -const vector6 iglica[nnumPts] = // iglica - vextor3(x,y,mapowanie tekstury) - { - vector6(0.010, -0.180, 0.00, 1.000, 0.000, 0.000), - vector6(0.010, -0.155, 0.15, 1.000, 0.000, 0.000), - vector6(0.010, -0.070, 0.25, 1.000, 0.000, 0.000), - vector6(0.010, -0.040, 0.35, 1.000, 0.000, 0.000), - vector6(0.010, -0.010, 0.40, 1.000, 0.000, 0.000), - vector6(0.010, -0.000, 0.45, 0.707, 0.707, 0.000), - vector6(0.000, -0.000, 0.55, 0.707, 0.707, 0.000), - vector6(0.000, -0.010, 0.60, -1.000, 0.000, 0.000), - vector6(0.000, -0.040, 0.65, -1.000, 0.000, 0.000), - vector6(0.000, -0.070, 0.75, -1.000, 0.000, 0.000), - vector6(0.000, -0.155, 0.85, -0.707, 0.707, 0.000), - vector6(-0.040, -0.180, 1.00, -1.000, 0.000, - 0.000) // 1mm więcej, żeby nie nachodziły tekstury? -}; +// iglica - vextor3(x,y,mapowanie tekstury) +// 1 mm więcej, żeby nie nachodziły tekstury? +// TODO: automatic generation from base profile TBD: reuse base profile? +basic_vertex const iglica[ nnumPts ] = { + {{ 0.010f, -0.180f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.00f, 0.f}}, + {{ 0.010f, -0.155f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.15f, 0.f}}, + {{ 0.010f, -0.070f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.25f, 0.f}}, + {{ 0.010f, -0.040f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.35f, 0.f}}, + {{ 0.010f, -0.010f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.40f, 0.f}}, + {{ 0.010f, -0.000f, 0.f}, { 0.707f, 0.707f, 0.f}, {0.45f, 0.f}}, + {{ 0.000f, -0.000f, 0.f}, { 0.707f, 0.707f, 0.f}, {0.55f, 0.f}}, + {{ 0.000f, -0.010f, 0.f}, {-1.000f, 0.000f, 0.f}, {0.60f, 0.f}}, + {{ 0.000f, -0.040f, 0.f}, {-1.000f, 0.000f, 0.f}, {0.65f, 0.f}}, + {{ 0.000f, -0.070f, 0.f}, {-1.000f, 0.000f, 0.f}, {0.75f, 0.f}}, + {{ 0.000f, -0.155f, 0.f}, {-0.707f, 0.707f, 0.f}, {0.85f, 0.f}}, + {{-0.040f, -0.180f, 0.f}, {-1.000f, 0.000f, 0.f}, {1.00f, 0.f}} }; bool TTrack::CheckDynamicObject(TDynamicObject *Dynamic) { // sprawdzenie, czy pojazd jest przypisany do toru @@ -1091,30 +1081,32 @@ void TTrack::RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joi // wypełnianie tablic VBO void TTrack::create_geometry( geometrybank_handle const &Bank ) { // Ra: trzeba rozdzielić szyny od podsypki, aby móc grupować wg tekstur - double const fHTW = 0.5 * std::fabs(fTrackWidth); - double const side = std::fabs(fTexWidth); // szerokść podsypki na zewnątrz szyny albo pobocza - double const slop = std::fabs(fTexSlope); // brzeg zewnętrzny - double const rozp = fHTW + side + slop; // brzeg zewnętrzny - double hypot1 = std::hypot(slop, fTexHeight1); // rozmiar pochylenia do liczenia normalnych - if (hypot1 == 0.0) - hypot1 = 1.0; - vector3 normal1 { fTexSlope / hypot1, fTexHeight1 / hypot1, 0.0 }; // wektor normalny - double fHTW2, side2, slop2, rozp2, fTexHeight2, hypot2; - vector3 normal2; - if (iTrapezoid & 2) // ten bit oznacza, że istnieje odpowiednie pNext - { // Ra: jest OK - fHTW2 = 0.5 * std::fabs(trNext->fTrackWidth); // połowa rozstawu/nawierzchni + auto const fHTW = 0.5f * std::abs(fTrackWidth); + auto const side = std::abs(fTexWidth); // szerokść podsypki na zewnątrz szyny albo pobocza + auto const slop = std::abs(fTexSlope); // brzeg zewnętrzny + auto const rozp = fHTW + side + slop; // brzeg zewnętrzny + auto hypot1 = std::hypot(slop, fTexHeight1); // rozmiar pochylenia do liczenia normalnych + if( hypot1 == 0.f ) + hypot1 = 1.f; + glm::vec3 const normalup{ 0.f, 1.f, 0.f }; + glm::vec3 normal1 { fTexHeight1 / hypot1, fTexSlope / hypot1, 0.f }; // wektor normalny + glm::vec3 normal2; + float fHTW2, side2, slop2, rozp2, fTexHeight2, hypot2; + if( iTrapezoid & 2 ) { + // ten bit oznacza, że istnieje odpowiednie pNext + // Ra: jest OK + fHTW2 = 0.5f * std::fabs(trNext->fTrackWidth); // połowa rozstawu/nawierzchni side2 = std::fabs(trNext->fTexWidth); slop2 = std::fabs(trNext->fTexSlope); // nie jest używane później rozp2 = fHTW2 + side2 + slop2; fTexHeight2 = trNext->fTexHeight1; hypot2 = std::hypot(slop2, fTexHeight2); - if (hypot2 == 0.0) - hypot2 = 1.0; - normal2 = vector3(trNext->fTexSlope / hypot2, fTexHeight2 / hypot2, 0.0); + if( hypot2 == 0.f ) + hypot2 = 1.f; + normal2 = { fTexHeight2 / hypot2, trNext->fTexSlope / hypot2, 0.f }; } - else // gdy nie ma następnego albo jest nieodpowiednim końcem podpięty - { + else { + // gdy nie ma następnego albo jest nieodpowiednim końcem podpięty fHTW2 = fHTW; side2 = side; slop2 = slop; @@ -1126,7 +1118,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { auto const origin { pMyNode->m_rootposition }; - double roll1, roll2; + float roll1, roll2; switch (iCategoryFlag & 15) { case 1: // tor @@ -1135,107 +1127,202 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { Segment->GetRolls(roll1, roll2); else roll1 = roll2 = 0.0; // dla zwrotnic - double sin1 = std::sin(roll1), cos1 = std::cos(roll1), sin2 = std::sin(roll2), cos2 = std::cos(roll2); + float const + sin1 = std::sin(roll1), + cos1 = std::cos(roll1), + sin2 = std::sin(roll2), + cos2 = std::cos(roll2); // zwykla szyna: //Ra: czemu główki są asymetryczne na wysokości 0.140? - vector6 rpts1[24], rpts2[24], rpts3[24], rpts4[24]; - int i; - for (i = 0; i < 12; ++i) - { - rpts1[i] = vector6( - (fHTW + szyna[i].x) * cos1 + szyna[i].y * sin1, - -(fHTW + szyna[i].x) * sin1 + szyna[i].y * cos1, - szyna[i].z, - szyna[i].n.x * cos1 + szyna[i].n.y * sin1, - -szyna[i].n.x * sin1 + szyna[i].n.y * cos1, - 0.0); - rpts2[11 - i] = vector6( - (-fHTW - szyna[i].x) * cos1 + szyna[i].y * sin1, - -(-fHTW - szyna[i].x) * sin1 + szyna[i].y * cos1, - szyna[i].z, - -szyna[i].n.x * cos1 + szyna[i].n.y * sin1, - szyna[i].n.x * sin1 + szyna[i].n.y * cos1, - 0.0); + basic_vertex rpts1[24], rpts2[24], rpts3[24], rpts4[24]; + for( int i = 0; i < 12; ++i ) { + + rpts1[ i ] = { + // position + {( fHTW + szyna[ i ].position.x ) * cos1 + szyna[ i ].position.y * sin1, + -( fHTW + szyna[ i ].position.x ) * sin1 + szyna[ i ].position.y * cos1, + szyna[ i ].position.z}, + // normal + { szyna[ i ].normal.x * cos1 + szyna[ i ].normal.y * sin1, + -szyna[ i ].normal.x * sin1 + szyna[ i ].normal.y * cos1, + szyna[ i ].normal.z }, + // texture + { szyna[ i ].texture.x, + szyna[ i ].texture.y } }; + + rpts2[ 11 - i ] = { + // position + {(-fHTW - szyna[ i ].position.x ) * cos1 + szyna[ i ].position.y * sin1, + -(-fHTW - szyna[ i ].position.x ) * sin1 + szyna[ i ].position.y * cos1, + szyna[ i ].position.z}, + // normal + {-szyna[ i ].normal.x * cos1 + szyna[ i ].normal.y * sin1, + szyna[ i ].normal.x * sin1 + szyna[ i ].normal.y * cos1, + szyna[ i ].normal.z }, + // texture + { szyna[ i ].texture.x, + szyna[ i ].texture.y } }; + + if( false == iTrapezoid ) { continue; } + // trapez albo przechyłki, to oddzielne punkty na końcu + + rpts1[ 12 + i ] = { + // position + {( fHTW + szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, + -( fHTW + szyna[ i ].position.x ) * sin2 + szyna[ i ].position.y * cos2, + szyna[ i ].position.z}, + // normal + { szyna[ i ].normal.x * cos2 + szyna[ i ].normal.y * sin2, + -szyna[ i ].normal.x * sin2 + szyna[ i ].normal.y * cos2, + szyna[ i ].normal.z }, + // texture + { szyna[ i ].texture.x, + szyna[ i ].texture.y } }; + + rpts2[ 23 - i ] = { + // position + {(-fHTW - szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, + -(-fHTW - szyna[ i ].position.x ) * sin2 + szyna[ i ].position.y * cos2, + szyna[ i ].position.z}, + // normal + {-szyna[ i ].normal.x * cos2 + szyna[ i ].normal.y * sin2, + szyna[ i ].normal.x * sin2 + szyna[ i ].normal.y * cos2, + szyna[ i ].normal.z }, + // texture + { szyna[ i ].texture.x, + szyna[ i ].texture.y } }; } - if (iTrapezoid) // trapez albo przechyłki, to oddzielne punkty na końcu - for (i = 0; i < 12; ++i) - { - rpts1[12 + i] = vector6( - (fHTW2 + szyna[i].x) * cos2 + szyna[i].y * sin2, - -(fHTW2 + szyna[i].x) * sin2 + szyna[i].y * cos2, - szyna[i].z, - szyna[i].n.x * cos2 + szyna[i].n.y * sin2, - -szyna[i].n.x * sin2 + szyna[i].n.y * cos2, - 0.0); - rpts2[23 - i] = vector6( - (-fHTW2 - szyna[i].x) * cos2 + szyna[i].y * sin2, - -(-fHTW2 - szyna[i].x) * sin2 + szyna[i].y * cos2, - szyna[i].z, - -szyna[i].n.x * cos2 + szyna[i].n.y * sin2, - szyna[i].n.x * sin2 + szyna[i].n.y * cos2, - 0.0); - } switch (eType) // dalej zależnie od typu { case tt_Table: // obrotnica jak zwykły tor, tylko animacja dochodzi case tt_Normal: if (m_material2) { // podsypka z podkładami jest tylko dla zwykłego toru - vector6 bpts1[8]; // punkty głównej płaszczyzny nie przydają się do robienia boków - if (fTexLength == 4.0) // jeśli stare mapowanie - { // stare mapowanie z różną gęstością pikseli i oddzielnymi teksturami na każdy profil - if (iTrapezoid) // trapez albo przechyłki - { // podsypka z podkladami trapezowata + basic_vertex bpts1[ 8 ]; // punkty głównej płaszczyzny nie przydają się do robienia boków + if( fTexLength == 4.f ) { + // stare mapowanie z różną gęstością pikseli i oddzielnymi teksturami na każdy profil + auto const normalx = std::cos( glm::radians( 75.f ) ); + auto const normaly = std::sin( glm::radians( 75.f ) ); + if( iTrapezoid ) { + // trapez albo przechyłki // ewentualnie poprawić mapowanie, żeby środek mapował się na 1.435/4.671 ((0.3464,0.6536) // bo się tekstury podsypki rozjeżdżają po zmianie proporcji profilu - bpts1[0] = vector6(rozp, -fTexHeight1 - 0.18, 0.00, -0.707, 0.707, 0.0); // lewy brzeg - bpts1[1] = vector6((fHTW + side) * cos1, -(fHTW + side) * sin1 - 0.18, 0.33, -0.707, 0.707, 0.0); // krawędź załamania - bpts1[2] = vector6(-bpts1[1].x, +(fHTW + side) * sin1 - 0.18, 0.67, 0.707, 0.707, 0.0); // prawy brzeg początku symetrycznie - bpts1[3] = vector6(-rozp, -fTexHeight1 - 0.18, 1.00, 0.707, 0.707, 0.0); // prawy skos + bpts1[ 0 ] = { + {rozp, -fTexHeight1 - 0.18f, 0.f}, + {normalx, normaly, 0.f}, + {0.00f, 0.f} }; // lewy brzeg + bpts1[ 1 ] = { + {( fHTW + side ) * cos1, -( fHTW + side ) * sin1 - 0.18f, 0.f}, + {normalx, normaly, 0.f}, + {0.33f, 0.f} }; // krawędź załamania + bpts1[ 2 ] = { + {-bpts1[ 1 ].position.x, +( fHTW + side ) * sin1 - 0.18f, 0.f}, + {-normalx, normaly, 0.f}, + {0.67f, 0.f} }; // prawy brzeg początku symetrycznie + bpts1[ 3 ] = { + {-rozp, -fTexHeight1 - 0.18f, 0.f}, + {-normalx, normaly, 0.f}, + {1.f, 0.f} }; // prawy skos // końcowy przekrój - bpts1[4] = vector6(rozp2, -fTexHeight2 - 0.18, 0.00, -0.707, 0.707, 0.0); // lewy brzeg - bpts1[5] = vector6((fHTW2 + side2) * cos2, -(fHTW2 + side2) * sin2 - 0.18, 0.33, -0.707, 0.707, 0.0); // krawędź załamania - bpts1[6] = vector6(-bpts1[5].x, +(fHTW2 + side2) * sin2 - 0.18, 0.67, 0.707, 0.707, 0.0); // prawy brzeg początku symetrycznie - bpts1[7] = vector6(-rozp2, -fTexHeight2 - 0.18, 1.00, 0.707, 0.707, 0.0); // prawy skos + bpts1[ 4 ] = { + {rozp2, -fTexHeight2 - 0.18f, 0.f}, + {normalx, normaly, 0.f}, + {0.00f, 0.f} }; // lewy brzeg + bpts1[ 5 ] = { + {( fHTW2 + side2 ) * cos2, -( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, + {normalx, normaly, 0.f}, + {0.33f, 0.f} }; // krawędź załamania + bpts1[ 6 ] = { + {-bpts1[ 5 ].position.x, +( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, + {-normalx, normaly, 0.f}, + {0.67f, 0.f} }; // prawy brzeg początku symetrycznie + bpts1[ 7 ] = { + {-rozp2, -fTexHeight2 - 0.18f, 0.f}, + {-normalx, normaly, 0.f}, + {1.00f, 0.f} }; // prawy skos } - else - { - bpts1[0] = vector6(rozp, -fTexHeight1 - 0.18, 0.0, -0.707, 0.707, 0.0); // lewy brzeg - bpts1[1] = vector6(fHTW + side, -0.18, 0.33, -0.707, 0.707, 0.0); // krawędź załamania - bpts1[2] = vector6(-fHTW - side, -0.18, 0.67, 0.707, 0.707, 0.0); // druga - bpts1[3] = vector6(-rozp, -fTexHeight1 - 0.18, 1.0, 0.707, 0.707, 0.0); // prawy skos + else { + bpts1[ 0 ] = { + {rozp, -fTexHeight1 - 0.18f, 0.f}, + {normalx, normaly, 0.f}, + {0.00f, 0.f} }; // lewy brzeg + bpts1[ 1 ] = { + {fHTW + side, -0.18f, 0.f}, + {normalx, normaly, 0.f}, + {0.33f, 0.f} }; // krawędź załamania + bpts1[ 2 ] = { + {-fHTW - side, -0.18f, 0.f}, + {-normalx, normaly, 0.f}, + {0.67f, 0.f} }; // druga + bpts1[ 3 ] = { + {-rozp, -fTexHeight1 - 0.18f, 0.f}, + {-normalx, normaly, 0.f}, + {1.00f, 0.f} }; // prawy skos } } - else - { // mapowanie proporcjonalne do powierzchni, rozmiar w poprzek określa fTexLength - double max = fTexRatio2 * fTexLength; // szerokość proporcjonalna do długości - double map11 = max > 0.0 ? (fHTW + side) / max : 0.25; // załamanie od strony 1 - double map12 = - max > 0.0 ? (fHTW + side + hypot1) / max : 0.5; // brzeg od strony 1 - if (iTrapezoid) // trapez albo przechyłki - { // podsypka z podkladami trapezowata - double map21 = - max > 0.0 ? (fHTW2 + side2) / max : 0.25; // załamanie od strony 2 - double map22 = - max > 0.0 ? (fHTW2 + side2 + hypot2) / max : 0.5; // brzeg od strony 2 + else { + // mapowanie proporcjonalne do powierzchni, rozmiar w poprzek określa fTexLength + auto const max = fTexRatio2 * fTexLength; // szerokość proporcjonalna do długości + auto const map11 = max > 0.f ? (fHTW + side) / max : 0.25f; // załamanie od strony 1 + auto const map12 = max > 0.f ? (fHTW + side + hypot1) / max : 0.5f; // brzeg od strony 1 + if (iTrapezoid) { + // trapez albo przechyłki + auto const map21 = max > 0.f ? (fHTW2 + side2) / max : 0.25f; // załamanie od strony 2 + auto const map22 = max > 0.f ? (fHTW2 + side2 + hypot2) / max : 0.5f; // brzeg od strony 2 // ewentualnie poprawić mapowanie, żeby środek mapował się na 1.435/4.671 // ((0.3464,0.6536) // bo się tekstury podsypki rozjeżdżają po zmianie proporcji profilu - bpts1[0] = vector6(rozp, -fTexHeight1 - 0.18, 0.5 - map12, normal1.x, -normal1.y, 0.0); // lewy brzeg - bpts1[1] = vector6((fHTW + side) * cos1, -(fHTW + side) * sin1 - 0.18, 0.5 - map11, 0.0, 1.0, 0.0); // krawędź załamania - bpts1[2] = vector6(-bpts1[1].x, +(fHTW + side) * sin1 - 0.18, 0.5 + map11, 0.0, 1.0, 0.0); // prawy brzeg początku symetrycznie - bpts1[3] = vector6(-rozp, -fTexHeight1 - 0.18, 0.5 + map12, -normal1.x, -normal1.y, 0.0); // prawy skos + bpts1[ 0 ] = { + {rozp, -fTexHeight1 - 0.18f, 0.f}, + {normal1.x, normal1.y, 0.f}, + {0.5f - map12, 0.f} }; // lewy brzeg + bpts1[ 1 ] = { + {( fHTW + side ) * cos1, -( fHTW + side ) * sin1 - 0.18f, 0.f}, + {normal1.x, normal1.y, 0.f}, + {0.5f - map11 , 0.f} }; // krawędź załamania + bpts1[ 2 ] = { + {-bpts1[ 1 ].position.x, +( fHTW + side ) * sin1 - 0.18f, 0.f}, + {-normal1.x, normal1.y, 0.f}, + {0.5 + map11, 0.f} }; // prawy brzeg początku symetrycznie + bpts1[ 3 ] = { + {-rozp, -fTexHeight1 - 0.18f, 0.f}, + {-normal1.x, normal1.y, 0.f}, + {0.5f + map12, 0.f} }; // prawy skos // przekrój końcowy - bpts1[4] = vector6(rozp2, -fTexHeight2 - 0.18, 0.5 - map22, normal2.x, -normal2.y, 0.0); // lewy brzeg - bpts1[5] = vector6((fHTW2 + side2) * cos2, -(fHTW2 + side2) * sin2 - 0.18, 0.5 - map21, 0.0, 1.0, 0.0); // krawędź załamania - bpts1[6] = vector6(-bpts1[5].x, +(fHTW2 + side2) * sin2 - 0.18, 0.5 + map21, 0.0, 1.0, 0.0); // prawy brzeg początku symetrycznie - bpts1[7] = vector6(-rozp2, -fTexHeight2 - 0.18, 0.5 + map22, -normal2.x, -normal2.y, 0.0); // prawy skos + bpts1[ 4 ] = { + {rozp2, -fTexHeight2 - 0.18f, 0.f}, + {normal2.x, normal2.y, 0.f}, + {0.5f - map22, 0.f} }; // lewy brzeg + bpts1[ 5 ] = { + {( fHTW2 + side2 ) * cos2, -( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, + {normal2.x, normal2.y, 0.f}, + {0.5f - map21 , 0.f} }; // krawędź załamania + bpts1[ 6 ] = { + {-bpts1[ 5 ].position.x, +( fHTW2 + side2 ) * sin2 - 0.18f, 0.f}, + {-normal2.x, normal2.y, 0.f}, + {0.5f + map21, 0.f} }; // prawy brzeg początku symetrycznie + bpts1[ 7 ] = { + {-rozp2, -fTexHeight2 - 0.18f, 0.f}, + {-normal2.x, normal2.y, 0.f}, + {0.5f + map22, 0.f} }; // prawy skos } else { - bpts1[0] = vector6(rozp, -fTexHeight1 - 0.18, 0.5 - map12, +normal1.x, -normal1.y, 0.0); // lewy brzeg - bpts1[1] = vector6(fHTW + side, -0.18, 0.5 - map11, +normal1.x, -normal1.y, 0.0); // krawędź załamania - bpts1[2] = vector6(-fHTW - side, -0.18, 0.5 + map11, -normal1.x, -normal1.y, 0.0); // druga - bpts1[3] = vector6(-rozp, -fTexHeight1 - 0.18, 0.5 + map12, -normal1.x, -normal1.y, 0.0); // prawy skos + bpts1[ 0 ] = { + {rozp, -fTexHeight1 - 0.18f, 0.f}, + {+normal1.x, normal1.y, 0.f}, + {0.5f - map12, 0.f} }; // lewy brzeg + bpts1[ 1 ] = { + {fHTW + side, - 0.18f, 0.f}, + {+normal1.x, normal1.y, 0.f}, + {0.5f - map11, 0.f} }; // krawędź załamania + bpts1[ 2 ] = { + {-fHTW - side, - 0.18f, 0.f}, + {-normal1.x, normal1.y, 0.f}, + {0.5f + map11, 0.f} }; // druga + bpts1[ 3 ] = { + {-rozp, -fTexHeight1 - 0.18f, 0.f}, + {-normal1.x, normal1.y, 0.f}, + {0.5f + map12, 0.f} }; // prawy skos } } vertex_array vertices; @@ -1269,31 +1356,35 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { } break; case tt_Switch: // dla zwrotnicy dwa razy szyny - if( m_material1 || m_material2 ) - { // iglice liczone tylko dla zwrotnic - vector6 rpts3[24], rpts4[24]; - for (i = 0; i < 12; ++i) - { - rpts3[i] = - vector6( - +( fHTW + iglica[i].x) * cos1 + iglica[i].y * sin1, - -(+fHTW + iglica[i].x) * sin1 + iglica[i].y * cos1, - iglica[i].z); - rpts3[i + 12] = - vector6( - +( fHTW2 + szyna[i].x) * cos2 + szyna[i].y * sin2, - -(+fHTW2 + szyna[i].x) * sin2 + iglica[i].y * cos2, - szyna[i].z); - rpts4[11 - i] = - vector6( - (-fHTW - iglica[i].x) * cos1 + iglica[i].y * sin1, - -(-fHTW - iglica[i].x) * sin1 + iglica[i].y * cos1, - iglica[i].z); - rpts4[23 - i] = - vector6( - (-fHTW2 - szyna[i].x) * cos2 + szyna[i].y * sin2, - -(-fHTW2 - szyna[i].x) * sin2 + iglica[i].y * cos2, - szyna[i].z); + if( m_material1 || m_material2 ) { + // iglice liczone tylko dla zwrotnic + basic_vertex rpts3[24], rpts4[24]; + for( int i = 0; i < 12; ++i ) { + + rpts3[ i ] = { + {+( fHTW + iglica[ i ].position.x ) * cos1 + iglica[ i ].position.y * sin1, + -( fHTW + iglica[ i ].position.x ) * sin1 + iglica[ i ].position.y * cos1, + 0.f}, + {iglica[ i ].normal}, + {iglica[ i ].texture.x, 0.f} }; + rpts3[ i + 12 ] = { + {+( fHTW2 + szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, + -( fHTW2 + szyna[ i ].position.x ) * sin2 + iglica[ i ].position.y * cos2, + 0.f}, + {szyna[ i ].normal}, + {szyna[ i ].texture.x, 0.f} }; + rpts4[ 11 - i ] = { + { ( -fHTW - iglica[ i ].position.x ) * cos1 + iglica[ i ].position.y * sin1, + -( -fHTW - iglica[ i ].position.x ) * sin1 + iglica[ i ].position.y * cos1, + 0.f}, + {iglica[ i ].normal}, + {iglica[ i ].texture.x, 0.f} }; + rpts4[ 23 - i ] = { + { ( -fHTW2 - szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, + -( -fHTW2 - szyna[ i ].position.x ) * sin2 + iglica[ i ].position.y * cos2, + 0.f}, + {szyna[ i ].normal}, + {szyna[ i ].texture.x, 0.f} }; } // TODO, TBD: change all track geometry to triangles, to allow packing data in less, larger buffers if (SwitchExtension->RightSwitch) @@ -1366,24 +1457,45 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { { case tt_Normal: // drogi proste, bo skrzyżowania osobno { - vector6 bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków - if (m_material1 || m_material2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma - { // double max=2.0*(fHTW>fHTW2?fHTW:fHTW2); //z szerszej strony jest 100% - double max = fTexRatio1 * fTexLength; // test: szerokość proporcjonalna do długości - double map1 = max > 0.0 ? fHTW / max : 0.5; // obcięcie tekstury od strony 1 - double map2 = max > 0.0 ? fHTW2 / max : 0.5; // obcięcie tekstury od strony 2 - if (iTrapezoid) // trapez albo przechyłki - { // nawierzchnia trapezowata + basic_vertex bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków + if (m_material1 || m_material2) { + // punkty się przydadzą, nawet jeśli nawierzchni nie ma +/* + double max=2.0*(fHTW>fHTW2?fHTW:fHTW2); //z szerszej strony jest 100% +*/ + auto const max = fTexRatio1 * fTexLength; // test: szerokość proporcjonalna do długości + auto const map1 = max > 0.f ? fHTW / max : 0.5f; // obcięcie tekstury od strony 1 + auto const map2 = max > 0.f ? fHTW2 / max : 0.5f; // obcięcie tekstury od strony 2 + if (iTrapezoid) { + // trapez albo przechyłki Segment->GetRolls(roll1, roll2); - bpts1[0] = vector6(fHTW * cos(roll1), -fHTW * sin(roll1), 0.5 - map1); // lewy brzeg początku - bpts1[1] = vector6(-bpts1[0].x, -bpts1[0].y, 0.5 + map1); // prawy brzeg początku symetrycznie - bpts1[2] = vector6(fHTW2 * cos(roll2), -fHTW2 * sin(roll2), 0.5 - map2); // lewy brzeg końca - bpts1[3] = vector6(-bpts1[2].x, -bpts1[2].y, 0.5 + map2); // prawy brzeg początku symetrycznie + bpts1[ 0 ] = { + {fHTW * std::cos( roll1 ), -fHTW * std::sin( roll1 ), 0.f}, + normalup, + {0.5f - map1, 0.f} }; // lewy brzeg początku + bpts1[ 1 ] = { + {-bpts1[ 0 ].position.x, -bpts1[ 0 ].position.y, 0.f}, + normalup, + {0.5f + map1, 0.f} }; // prawy brzeg początku symetrycznie + bpts1[ 2 ] = { + {fHTW2 * std::cos( roll2 ), -fHTW2 * std::sin( roll2 ), 0.f}, + normalup, + {0.5f - map2, 0.f} }; // lewy brzeg końca + bpts1[ 3 ] = { + {-bpts1[ 2 ].position.x, -bpts1[ 2 ].position.y, 0.f}, + normalup, + {0.5f + map2, 0.f} }; // prawy brzeg początku symetrycznie } else { - bpts1[ 0 ] = vector6( fHTW, 0.0, 0.5 - map1, 0.0, 1.0, 0.0 ); - bpts1[ 1 ] = vector6( -fHTW, 0.0, 0.5 + map1, 0.0, 1.0, 0.0 ); + bpts1[ 0 ] = { + {fHTW, 0.f, 0.f}, + normalup, + {0.5f - map1, 0.f} }; + bpts1[ 1 ] = { + {-fHTW, 0.f, 0.f}, + normalup, + {0.5f + map1, 0.f} }; } } if (m_material1) // jeśli podana była tekstura, generujemy trójkąty @@ -1394,77 +1506,158 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { } if (m_material2) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) - vector6 + basic_vertex rpts1[6], rpts2[6]; // współrzędne przekroju i mapowania dla prawej i lewej strony - if (fTexHeight1 >= 0.0) + if (fTexHeight1 >= 0.f) { // standardowo: od zewnątrz pochylenie, a od wewnątrz poziomo - rpts1[0] = vector6(rozp, -fTexHeight1, 0.0); // lewy brzeg podstawy - rpts1[1] = vector6(bpts1[0].x + side, bpts1[0].y, 0.5), // lewa krawędź załamania - rpts1[2] = vector6(bpts1[0].x, bpts1[0].y, 1.0); // lewy brzeg pobocza (mapowanie może być inne - rpts2[0] = vector6(bpts1[1].x, bpts1[1].y, 1.0); // prawy brzeg pobocza - rpts2[1] = vector6(bpts1[1].x - side, bpts1[1].y, 0.5); // prawa krawędź załamania - rpts2[2] = vector6(-rozp, -fTexHeight1, 0.0); // prawy brzeg podstawy - if (iTrapezoid) // trapez albo przechyłki - { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka - rpts1[3] = vector6(rozp2, -fTexHeight2, 0.0); // lewy brzeg lewego pobocza - rpts1[4] = vector6(bpts1[2].x + side2, bpts1[2].y, 0.5); // krawędź załamania - rpts1[5] = vector6(bpts1[2].x, bpts1[2].y, 1.0); // brzeg pobocza - rpts2[3] = vector6(bpts1[3].x, bpts1[3].y, 1.0); - rpts2[4] = vector6(bpts1[3].x - side2, bpts1[3].y, 0.5); - rpts2[5] = vector6(-rozp2, -fTexHeight2, 0.0); // prawy brzeg prawego pobocza + rpts1[ 0 ] = { + {rozp, -fTexHeight1, 0.f}, + normalup, + {0.f, 0.f} }; // lewy brzeg podstawy + rpts1[ 1 ] = { + {bpts1[ 0 ].position.x + side, bpts1[ 0 ].position.y, 0.f}, + normalup, + {0.5, 0.f} }; // lewa krawędź załamania + rpts1[ 2 ] = { + {bpts1[ 0 ].position.x, bpts1[ 0 ].position.y, 0.f}, + normalup, + {1.f, 0.f} }; // lewy brzeg pobocza (mapowanie może być inne + rpts2[ 0 ] = { + {bpts1[ 1 ].position.x, bpts1[ 1 ].position.y, 0.f}, + normalup, + {1.f, 0.f} }; // prawy brzeg pobocza + rpts2[ 1 ] = { + {bpts1[ 1 ].position.x - side, bpts1[ 1 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; // prawa krawędź załamania + rpts2[ 2 ] = { + {-rozp, -fTexHeight1, 0.f}, + normalup, + {0.f, 0.f} }; // prawy brzeg podstawy + if (iTrapezoid) { + // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka + rpts1[ 3 ] = { + {rozp2, -fTexHeight2, 0.f}, + normalup, + {0.f, 0.f} }; // lewy brzeg lewego pobocza + rpts1[ 4 ] = { + {bpts1[ 2 ].position.x + side2, bpts1[ 2 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; // krawędź załamania + rpts1[ 5 ] = { + {bpts1[ 2 ].position.x, bpts1[ 2 ].position.y, 0.f}, + normalup, + {1.f, 0.f} }; // brzeg pobocza + rpts2[ 3 ] = { + {bpts1[ 3 ].position.x, bpts1[ 3 ].position.y, 0.f}, + normalup, + {1.f, 0.f} }; + rpts2[ 4 ] = { + {bpts1[ 3 ].position.x - side2, bpts1[ 3 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; + rpts2[ 5 ] = { + {-rozp2, -fTexHeight2, 0.f}, + normalup, + {0.f, 0.f} }; // prawy brzeg prawego pobocza } } else { // wersja dla chodnika: skos 1:3.75, każdy chodnik innej szerokości // mapowanie propocjonalne do szerokości chodnika // krawężnik jest mapowany od 31/64 do 32/64 lewy i od 32/64 do 33/64 prawy - double d = -fTexHeight1 / 3.75; // krawężnik o wysokości 150mm jest pochylony 40mm - double max = fTexRatio2 * fTexLength; // test: szerokość proporcjonalna do długości - double map1l = ( - max > 0.0 ? + auto const d = -fTexHeight1 / 3.75f; // krawężnik o wysokości 150mm jest pochylony 40mm + auto const max = fTexRatio2 * fTexLength; // test: szerokość proporcjonalna do długości + auto const map1l = ( + max > 0.f ? side / max : - 0.484375 ); // obcięcie tekstury od lewej strony punktu 1 - double map1r = ( - max > 0.0 ? + 0.484375f ); // obcięcie tekstury od lewej strony punktu 1 + auto const map1r = ( + max > 0.f ? slop / max : - 0.484375 ); // obcięcie tekstury od prawej strony punktu 1 - double h1r = ( + 0.484375f ); // obcięcie tekstury od prawej strony punktu 1 + auto const h1r = ( slop > d ? -fTexHeight1 : - 0 ); - double h1l = ( + 0.f ); + auto const h1l = ( side > d ? -fTexHeight1 : - 0 ); - rpts1[0] = vector6(bpts1[0].x + slop, bpts1[0].y + h1r, 0.515625 + map1r); // prawy brzeg prawego chodnika - rpts1[1] = vector6(bpts1[0].x + d, bpts1[0].y + h1r, 0.515625); // prawy krawężnik u góry - rpts1[2] = vector6(bpts1[0].x, bpts1[0].y, 0.515625 - d / 2.56); // prawy krawężnik u dołu - rpts2[0] = vector6(bpts1[1].x, bpts1[1].y, 0.484375 + d / 2.56); // lewy krawężnik u dołu - rpts2[1] = vector6(bpts1[1].x - d, bpts1[1].y + h1l, 0.484375); // lewy krawężnik u góry - rpts2[2] = vector6(bpts1[1].x - side, bpts1[1].y + h1l, 0.484375 - map1l); // lewy brzeg lewego chodnika - if (iTrapezoid) // trapez albo przechyłki - { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka + 0.f ); + + rpts1[ 0 ] = { + {bpts1[ 0 ].position.x + slop, bpts1[ 0 ].position.y + h1r, 0.f}, + normalup, + {0.515625f + map1r, 0.f} }; // prawy brzeg prawego chodnika + rpts1[ 1 ] = { + {bpts1[ 0 ].position.x + d, bpts1[ 0 ].position.y + h1r, 0.f}, + normalup, + {0.515625f, 0.f} }; // prawy krawężnik u góry + rpts1[ 2 ] = { + {bpts1[ 0 ].position.x, bpts1[ 0 ].position.y, 0.f}, + normalup, + {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu + rpts2[ 0 ] = { + {bpts1[ 1 ].position.x, bpts1[ 1 ].position.y, 0.f}, + normalup, + {0.484375f + d / 2.56f, 0.f} }; // lewy krawężnik u dołu + rpts2[ 1 ] = { + {bpts1[ 1 ].position.x - d, bpts1[ 1 ].position.y + h1l, 0.f}, + normalup, + {0.484375f, 0.f} }; // lewy krawężnik u góry + rpts2[ 2 ] = { + {bpts1[ 1 ].position.x - side, bpts1[ 1 ].position.y + h1l, 0.f}, + normalup, + {0.484375f - map1l, 0.f} }; // lewy brzeg lewego chodnika + + if (iTrapezoid) { + // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka slop2 = ( - fabs((iTrapezoid & 2) ? + std::fabs((iTrapezoid & 2) ? slop2 : slop) ); // szerokość chodnika po prawej - double map2l = ( - max > 0.0 ? + auto const map2l = ( + max > 0.f ? side2 / max : - 0.484375 ); // obcięcie tekstury od lewej strony punktu 2 - double map2r = ( - max > 0.0 ? + 0.484375f ); // obcięcie tekstury od lewej strony punktu 2 + auto const map2r = ( + max > 0.f ? slop2 / max : - 0.484375 ); // obcięcie tekstury od prawej strony punktu 2 - double h2r = (slop2 > d) ? -fTexHeight2 : 0; - double h2l = (side2 > d) ? -fTexHeight2 : 0; - rpts1[3] = vector6(bpts1[2].x + slop2, bpts1[2].y + h2r, 0.515625 + map2r); // prawy brzeg prawego chodnika - rpts1[4] = vector6(bpts1[2].x + d, bpts1[2].y + h2r, 0.515625); // prawy krawężnik u góry - rpts1[5] = vector6(bpts1[2].x, bpts1[2].y, 0.515625 - d / 2.56); // prawy krawężnik u dołu - rpts2[3] = vector6(bpts1[3].x, bpts1[3].y, 0.484375 + d / 2.56); // lewy krawężnik u dołu - rpts2[4] = vector6(bpts1[3].x - d, bpts1[3].y + h2l, 0.484375); // lewy krawężnik u góry - rpts2[5] = vector6(bpts1[3].x - side2, bpts1[3].y + h2l, 0.484375 - map2l); // lewy brzeg lewego chodnika + 0.484375f ); // obcięcie tekstury od prawej strony punktu 2 + auto const h2r = ( + slop2 > d ? + -fTexHeight2 : + 0.f ); + auto const h2l = ( + side2 > d ? + -fTexHeight2 : + 0.f ); + + rpts1[ 3 ] = { + {bpts1[ 2 ].position.x + slop2, bpts1[ 2 ].position.y + h2r, 0.f}, + normalup, + {0.515625f + map2r, 0.f} }; // prawy brzeg prawego chodnika + rpts1[ 4 ] = { + {bpts1[ 2 ].position.x + d, bpts1[ 2 ].position.y + h2r, 0.f}, + normalup, + {0.515625f, 0.f} }; // prawy krawężnik u góry + rpts1[ 5 ] = { + {bpts1[ 2 ].position.x, bpts1[ 2 ].position.y, 0.f}, + normalup, + {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu + rpts2[ 3 ] = { + {bpts1[ 3 ].position.x, bpts1[ 3 ].position.y, 0.f}, + normalup, + {0.484375f + d / 2.56f, 0.f} }; // lewy krawężnik u dołu + rpts2[ 4 ] = { + {bpts1[ 3 ].position.x - d, bpts1[ 3 ].position.y + h2l, 0.f}, + normalup, + {0.484375f, 0.f} }; // lewy krawężnik u góry + rpts2[ 5 ] = { + {bpts1[ 3 ].position.x - side2, bpts1[ 3 ].position.y + h2l, 0.f}, + normalup, + {0.484375f - map2l, 0.f} }; // lewy brzeg lewego chodnika } } vertex_array vertices; @@ -1540,25 +1733,37 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { if (!SwitchExtension->vPoints) { // jeśli tablica punktów nie jest jeszcze utworzona, zliczamy punkty i tworzymy ją // we'll need to add couple extra points for the complete fan we'll build - SwitchExtension->vPoints = new vector3[pointcount + SwitchExtension->iRoads]; + SwitchExtension->vPoints = new glm::vec3[pointcount + SwitchExtension->iRoads]; } - vector3 *b = + glm::vec3 *b = SwitchExtension->bPoints ? nullptr : SwitchExtension->vPoints; // zmienna robocza, NULL gdy tablica punktów już jest wypełniona - vector6 bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków + basic_vertex bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków if (m_material1 || m_material2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma { // double max=2.0*(fHTW>fHTW2?fHTW:fHTW2); //z szerszej strony jest 100% - double max = fTexRatio1 * fTexLength; // test: szerokość proporcjonalna do długości - double map1 = max > 0.0 ? fHTW / max : 0.5; // obcięcie tekstury od strony 1 - double map2 = max > 0.0 ? fHTW2 / max : 0.5; // obcięcie tekstury od strony 2 + auto const max = fTexRatio1 * fTexLength; // test: szerokość proporcjonalna do długości + auto const map1 = max > 0.f ? fHTW / max : 0.5f; // obcięcie tekstury od strony 1 + auto const map2 = max > 0.f ? fHTW2 / max : 0.5f; // obcięcie tekstury od strony 2 // if (iTrapezoid) //trapez albo przechyłki { // nawierzchnia trapezowata Segment->GetRolls(roll1, roll2); - bpts1[0] = vector6(fHTW * cos(roll1), -fHTW * sin(roll1), 0.5 - map1, sin(roll1), cos(roll1), 0.0); // lewy brzeg początku - bpts1[1] = vector6(-bpts1[0].x, -bpts1[0].y, 0.5 + map1, -sin(roll1), cos(roll1), 0.0); // prawy brzeg początku symetrycznie - bpts1[2] = vector6(fHTW2 * cos(roll2), -fHTW2 * sin(roll2), 0.5 - map2, sin(roll2), cos(roll2), 0.0); // lewy brzeg końca - bpts1[3] = vector6(-bpts1[2].x, -bpts1[2].y, 0.5 + map2, -sin(roll2), cos(roll2), 0.0); // prawy brzeg początku symetrycznie + bpts1[ 0 ] = { + {fHTW * std::cos( roll1 ), -fHTW * std::sin( roll1 ), 0.f}, + {std::sin( roll1 ), std::cos( roll1 ), 0.f}, + {0.5f - map1, 0.f} }; // lewy brzeg początku + bpts1[ 1 ] = { + {-bpts1[ 0 ].position.x, -bpts1[ 0 ].position.y, 0.f}, + {-std::sin( roll1 ), std::cos( roll1 ), 0.f}, + {0.5f + map1, 0.f} }; // prawy brzeg początku symetrycznie + bpts1[ 2 ] = { + {fHTW2 * std::cos( roll2 ), -fHTW2 * std::sin( roll2 ), 0.f}, + {std::sin( roll2 ), std::cos( roll2 ), 0.f}, + {0.5f - map2, 0.f} }; // lewy brzeg końca + bpts1[ 3 ] = { + {-bpts1[ 2 ].position.x, -bpts1[ 2 ].position.y, 0.f}, + {-std::sin( roll2 ), std::cos( roll2 ), 0.f}, + {0.5 + map2, 0.f} }; // prawy brzeg początku symetrycznie } } // najpierw renderowanie poboczy i zapamiętywanie punktów @@ -1567,61 +1772,142 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { // ale pobocza renderują się później, więc nawierzchnia nie załapuje się na renderowanie w swoim czasie if( m_material2 ) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) - vector6 + basic_vertex rpts1[6], rpts2[6]; // współrzędne przekroju i mapowania dla prawej i lewej strony // Ra 2014-07: trzeba to przerobić na pętlę i pobierać profile (przynajmniej 2..4) z sąsiednich dróg if (fTexHeight1 >= 0.0) { // standardowo: od zewnątrz pochylenie, a od wewnątrz poziomo - rpts1[0] = vector6(rozp, -fTexHeight1, 0.0); // lewy brzeg podstawy - rpts1[1] = vector6(bpts1[0].x + side, bpts1[0].y, 0.5); // lewa krawędź załamania - rpts1[2] = vector6(bpts1[0].x, bpts1[0].y, 1.0); // lewy brzeg pobocza (mapowanie może być inne - rpts2[0] = vector6(bpts1[1].x, bpts1[1].y, 1.0); // prawy brzeg pobocza - rpts2[1] = vector6(bpts1[1].x - side, bpts1[1].y, 0.5); // prawa krawędź załamania - rpts2[2] = vector6(-rozp, -fTexHeight1, 0.0); // prawy brzeg podstawy + rpts1[ 0 ] = { + {rozp, -fTexHeight1, 0.f}, + normalup, + {0.f, 0.f} }; // lewy brzeg podstawy + rpts1[ 1 ] = { + {bpts1[ 0 ].position.x + side, bpts1[ 0 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} };// lewa krawędź załamania + rpts1[ 2 ] = { + {bpts1[ 0 ].position.x, bpts1[ 0 ].position.y, 0.f}, + normalup, + {1.f, 0.f} }; // lewy brzeg pobocza (mapowanie może być inne + rpts2[ 0 ] = { + {bpts1[ 1 ].position.x, bpts1[ 1 ].position.y, 0.f}, + normalup, + {1.f, 0.f} }; // prawy brzeg pobocza + rpts2[ 1 ] = { + {bpts1[ 1 ].position.x - side, bpts1[ 1 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; // prawa krawędź załamania + rpts2[ 2 ] = { + {-rozp, -fTexHeight1, 0.f}, + normalup, + {0.f, 0.f} }; // prawy brzeg podstawy // if (iTrapezoid) //trapez albo przechyłki { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka - rpts1[3] = vector6(rozp2, -fTexHeight2, 0.0); // lewy brzeg lewego pobocza - rpts1[4] = vector6(bpts1[2].x + side2, bpts1[2].y, 0.5); // krawędź załamania - rpts1[5] = vector6(bpts1[2].x, bpts1[2].y, 1.0); // brzeg pobocza - rpts2[3] = vector6(bpts1[3].x, bpts1[3].y, 1.0); - rpts2[4] = vector6(bpts1[3].x - side2, bpts1[3].y, 0.5); - rpts2[5] = vector6(-rozp2, -fTexHeight2, 0.0); // prawy brzeg prawego pobocza + rpts1[ 3 ] = { + {rozp2, -fTexHeight2, 0.f}, + normalup, + {0.f, 0.f} }; // lewy brzeg lewego pobocza + rpts1[ 4 ] = { + {bpts1[ 2 ].position.x + side2, bpts1[ 2 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; // krawędź załamania + rpts1[ 5 ] = { + {bpts1[ 2 ].position.x, bpts1[ 2 ].position.y, 0.f}, + normalup, + {1.f, 0.f} }; // brzeg pobocza + rpts2[ 3 ] = { + {bpts1[ 3 ].position.x, bpts1[ 3 ].position.y, 0.f}, + normalup, + {1.f, 0.f} }; + rpts2[ 4 ] = { + {bpts1[ 3 ].position.x - side2, bpts1[ 3 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; + rpts2[ 5 ] = { + {-rozp2, -fTexHeight2, 0.f}, + normalup, + {0.f, 0.f} }; // prawy brzeg prawego pobocza } } else { // wersja dla chodnika: skos 1:3.75, każdy chodnik innej szerokości // mapowanie propocjonalne do szerokości chodnika // krawężnik jest mapowany od 31/64 do 32/64 lewy i od 32/64 do 33/64 prawy - double d = -fTexHeight1 / 3.75; // krawężnik o wysokości 150mm jest pochylony 40mm - double max = fTexRatio2 * fTexLength; // test: szerokość proporcjonalna do długości - double map1l = max > 0.0 ? - side / max : - 0.484375; // obcięcie tekstury od lewej strony punktu 1 - double map1r = max > 0.0 ? - slop / max : - 0.484375; // obcięcie tekstury od prawej strony punktu 1 - rpts1[0] = vector6(bpts1[0].x + slop, bpts1[0].y - fTexHeight1, 0.515625 + map1r); // prawy brzeg prawego chodnika - rpts1[1] = vector6(bpts1[0].x + d, bpts1[0].y - fTexHeight1, 0.515625); // prawy krawężnik u góry - rpts1[2] = vector6(bpts1[0].x, bpts1[0].y, 0.515625 - d / 2.56); // prawy krawężnik u dołu - rpts2[0] = vector6(bpts1[1].x, bpts1[1].y, 0.484375 + d / 2.56); // lewy krawężnik u dołu - rpts2[1] = vector6(bpts1[1].x - d, bpts1[1].y - fTexHeight1, 0.484375); // lewy krawężnik u góry - rpts2[2] = vector6(bpts1[1].x - side, bpts1[1].y - fTexHeight1, 0.484375 - map1l); // lewy brzeg lewego chodnika + auto const d = -fTexHeight1 / 3.75f; // krawężnik o wysokości 150mm jest pochylony 40mm + auto const max = fTexRatio2 * fTexLength; // test: szerokość proporcjonalna do długości + auto const map1l = ( + max > 0.f ? + side / max : + 0.484375f ); // obcięcie tekstury od lewej strony punktu 1 + auto const map1r = ( + max > 0.f ? + slop / max : + 0.484375f ); // obcięcie tekstury od prawej strony punktu 1 + + rpts1[ 0 ] = { + {bpts1[ 0 ].position.x + slop, bpts1[ 0 ].position.y - fTexHeight1, 0.f}, + normalup, + { 0.515625f + map1r, 0.f} }; // prawy brzeg prawego chodnika + rpts1[ 1 ] = { + {bpts1[ 0 ].position.x + d, bpts1[ 0 ].position.y - fTexHeight1, 0.f}, + normalup, + {0.515625f, 0.f} }; // prawy krawężnik u góry + rpts1[ 2 ] = { + {bpts1[ 0 ].position.x, bpts1[ 0 ].position.y, 0.f}, + normalup, + {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu + rpts2[ 0 ] = { + {bpts1[ 1 ].position.x, bpts1[ 1 ].position.y, 0.f}, + normalup, + {0.484375f + d / 2.56f, 0.f} }; // lewy krawężnik u dołu + rpts2[ 1 ] = { + {bpts1[ 1 ].position.x - d, bpts1[ 1 ].position.y - fTexHeight1, 0.f}, + normalup, + {0.484375f, 0.f} }; // lewy krawężnik u góry + rpts2[ 2 ] = { + {bpts1[ 1 ].position.x - side, bpts1[ 1 ].position.y - fTexHeight1, 0.f}, + normalup, + {0.484375f - map1l, 0.f} }; // lewy brzeg lewego chodnika // if (iTrapezoid) //trapez albo przechyłki { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka - slop2 = std::fabs((iTrapezoid & 2) ? slop2 : slop); // szerokość chodnika po prawej - double map2l = max > 0.0 ? - side2 / max : - 0.484375; // obcięcie tekstury od lewej strony punktu 2 - double map2r = max > 0.0 ? - slop2 / max : - 0.484375; // obcięcie tekstury od prawej strony punktu 2 - rpts1[3] = vector6(bpts1[2].x + slop2, bpts1[2].y - fTexHeight2, 0.515625 + map2r); // prawy brzeg prawego chodnika - rpts1[4] = vector6(bpts1[2].x + d, bpts1[2].y - fTexHeight2, 0.515625); // prawy krawężnik u góry - rpts1[5] = vector6(bpts1[2].x, bpts1[2].y, 0.515625 - d / 2.56); // prawy krawężnik u dołu - rpts2[3] = vector6(bpts1[3].x, bpts1[3].y, 0.484375 + d / 2.56); // lewy krawężnik u dołu - rpts2[4] = vector6(bpts1[3].x - d, bpts1[3].y - fTexHeight2, 0.484375); // lewy krawężnik u góry - rpts2[5] = vector6(bpts1[3].x - side2, bpts1[3].y - fTexHeight2, 0.484375 - map2l); // lewy brzeg lewego chodnika + slop2 = std::abs( + ( (iTrapezoid & 2) ? + slop2 : + slop ) ); // szerokość chodnika po prawej + auto const map2l = ( + max > 0.f ? + side2 / max : + 0.484375f ); // obcięcie tekstury od lewej strony punktu 2 + auto const map2r = ( + max > 0.f ? + slop2 / max : + 0.484375f ); // obcięcie tekstury od prawej strony punktu 2 + + rpts1[ 3 ] = { + {bpts1[ 2 ].position.x + slop2, bpts1[ 2 ].position.y - fTexHeight2, 0.f}, + normalup, + { 0.515625f + map2r, 0.f} }; // prawy brzeg prawego chodnika + rpts1[ 4 ] = { + {bpts1[ 2 ].position.x + d, bpts1[ 2 ].position.y - fTexHeight2, 0.f}, + normalup, + {0.515625f, 0.f} }; // prawy krawężnik u góry + rpts1[ 5 ] = { + {bpts1[ 2 ].position.x, bpts1[ 2 ].position.y, 0.f}, + normalup, + {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu + rpts2[ 3 ] = { + {bpts1[ 3 ].position.x, bpts1[ 3 ].position.y, 0.f}, + normalup, + {0.484375f + d / 2.56, 0.f} }; // lewy krawężnik u dołu + rpts2[ 4 ] = { + {bpts1[ 3 ].position.x - d, bpts1[ 3 ].position.y - fTexHeight2, 0.f}, + normalup, + {0.484375f, 0.f} }; // lewy krawężnik u góry + rpts2[ 5 ] = { + {bpts1[ 3 ].position.x - side2, bpts1[ 3 ].position.y - fTexHeight2, 0.f}, + normalup, + {0.484375f - map2l, 0.f} }; // lewy brzeg lewego chodnika } } bool render = ( m_material2 != 0 ); // renderować nie trzeba, ale trzeba wyznaczyć punkty brzegowe nawierzchni @@ -1731,30 +2017,52 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { { case tt_Normal: // drogi proste, bo skrzyżowania osobno { - vector6 bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków + basic_vertex bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków if (m_material1 || m_material2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma { // double max=2.0*(fHTW>fHTW2?fHTW:fHTW2); //z szerszej strony jest 100% - double max = (iCategoryFlag & 4) ? - 0.0 : - fTexLength; // test: szerokość dróg proporcjonalna do długości - double map1 = max > 0.0 ? fHTW / max : 0.5; // obcięcie tekstury od strony 1 - double map2 = max > 0.0 ? fHTW2 / max : 0.5; // obcięcie tekstury od strony 2 - if (iTrapezoid) // trapez albo przechyłki - { // nawierzchnia trapezowata + auto const max = ( + ( iCategoryFlag & 4 ) ? + 0.f : + fTexLength ); // test: szerokość dróg proporcjonalna do długości + auto const map1 = ( + max > 0.f ? + fHTW / max : + 0.5f ); // obcięcie tekstury od strony 1 + auto const map2 = ( + max > 0.f ? + fHTW2 / max : + 0.5f ); // obcięcie tekstury od strony 2 + + if (iTrapezoid) { + // nawierzchnia trapezowata Segment->GetRolls(roll1, roll2); - bpts1[0] = vector6(fHTW * cos(roll1), -fHTW * sin(roll1), - 0.5 - map1); // lewy brzeg początku - bpts1[1] = vector6(-bpts1[0].x, -bpts1[0].y, - 0.5 + map1); // prawy brzeg początku symetrycznie - bpts1[2] = vector6(fHTW2 * cos(roll2), -fHTW2 * sin(roll2), - 0.5 - map2); // lewy brzeg końca - bpts1[3] = vector6(-bpts1[2].x, -bpts1[2].y, - 0.5 + map2); // prawy brzeg początku symetrycznie + bpts1[ 0 ] = { + {fHTW * std::cos( roll1 ), -fHTW * std::sin( roll1 ), 0.f}, + normalup, + {0.5f - map1, 0.f} }; // lewy brzeg początku + bpts1[ 1 ] = { + {-bpts1[ 0 ].position.x, -bpts1[ 0 ].position.y, 0.f}, + normalup, + {0.5f + map1, 0.f} }; // prawy brzeg początku symetrycznie + bpts1[ 2 ] = { + {fHTW2 * std::cos( roll2 ), -fHTW2 * std::sin( roll2 ), 0.f}, + normalup, + {0.5f - map2, 0.f} }; // lewy brzeg końca + bpts1[ 3 ] = { + {-bpts1[ 2 ].position.x, -bpts1[ 2 ].position.y, 0.f}, + normalup, + {0.5f + map2, 0.f} }; // prawy brzeg początku symetrycznie } else { - bpts1[0] = vector6(fHTW, 0.0, 0.5 - map1); // zawsze standardowe mapowanie - bpts1[1] = vector6(-fHTW, 0.0, 0.5 + map1); + bpts1[ 0 ] = { + {fHTW, 0.f, 0.f}, + normalup, + {0.5 - map1, 0.f} }; // zawsze standardowe mapowanie + bpts1[ 1 ] = { + {-fHTW, 0.f, 0.f}, + normalup, + {0.5f + map1, 0.f} }; } } if (m_material1) // jeśli podana była tekstura, generujemy trójkąty @@ -1766,23 +2074,60 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { if (m_material2) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) vertex_array vertices; - vector6 rpts1[6], + basic_vertex + rpts1[6], rpts2[6]; // współrzędne przekroju i mapowania dla prawej i lewej strony - rpts1[0] = vector6(rozp, -fTexHeight1, 0.0); // lewy brzeg podstawy - rpts1[1] = vector6(bpts1[0].x + side, bpts1[0].y, 0.5), // lewa krawędź załamania - rpts1[2] = vector6(bpts1[0].x, bpts1[0].y, - 1.0); // lewy brzeg pobocza (mapowanie może być inne - rpts2[0] = vector6(bpts1[1].x, bpts1[1].y, 1.0); // prawy brzeg pobocza - rpts2[1] = vector6(bpts1[1].x - side, bpts1[1].y, 0.5); // prawa krawędź załamania - rpts2[2] = vector6(-rozp, -fTexHeight1, 0.0); // prawy brzeg podstawy + + rpts1[ 0 ] = { + {rozp, -fTexHeight1, 0.f}, + normalup, + {0.0f, 0.f} }; // lewy brzeg podstawy + rpts1[ 1 ] = { + {bpts1[ 0 ].position.x + side, bpts1[ 0 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; // lewa krawędź załamania + rpts1[ 2 ] = { + {bpts1[ 0 ].position.x, bpts1[ 0 ].position.y, 0.f}, + normalup, + {1.0f, 0.f} }; // lewy brzeg pobocza (mapowanie może być inne + rpts2[ 0 ] = { + {bpts1[ 1 ].position.x, bpts1[ 1 ].position.y, 0.f}, + normalup, + {1.0f, 0.f} }; // prawy brzeg pobocza + rpts2[ 1 ] = { + {bpts1[ 1 ].position.x - side, bpts1[ 1 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; // prawa krawędź załamania + rpts2[ 2 ] = { + {-rozp, -fTexHeight1, 0.f}, + normalup, + {0.0f, 0.f} }; // prawy brzeg podstawy if (iTrapezoid) // trapez albo przechyłki { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka - rpts1[3] = vector6(rozp2, -fTexHeight2, 0.0); // lewy brzeg lewego pobocza - rpts1[4] = vector6(bpts1[2].x + side2, bpts1[2].y, 0.5); // krawędź załamania - rpts1[5] = vector6(bpts1[2].x, bpts1[2].y, 1.0); // brzeg pobocza - rpts2[3] = vector6(bpts1[3].x, bpts1[3].y, 1.0); - rpts2[4] = vector6(bpts1[3].x - side2, bpts1[3].y, 0.5); - rpts2[5] = vector6(-rozp2, -fTexHeight2, 0.0); // prawy brzeg prawego pobocza + rpts1[ 3 ] = { + {rozp2, -fTexHeight2, 0.f}, + normalup, + {0.0f, 0.f} }; // lewy brzeg lewego pobocza + rpts1[ 4 ] = { + {bpts1[ 2 ].position.x + side2, bpts1[ 2 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; // krawędź załamania + rpts1[ 5 ] = { + {bpts1[ 2 ].position.x, bpts1[ 2 ].position.y, 0.f}, + normalup, + {1.0f, 0.f} }; // brzeg pobocza + rpts2[ 3 ] = { + {bpts1[ 3 ].position.x, bpts1[ 3 ].position.y, 0.f}, + normalup, + {1.0f, 0.f} }; + rpts2[ 4 ] = { + {bpts1[ 3 ].position.x - side2, bpts1[ 3 ].position.y, 0.f}, + normalup, + {0.5f, 0.f} }; + rpts2[ 5 ] = { + {-rozp2, -fTexHeight2, 0.f}, + normalup, + {0.0f, 0.f} }; // prawy brzeg prawego pobocza Segment->RenderLoft(vertices, origin, rpts1, -3, fTexLength); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); @@ -1871,14 +2216,14 @@ bool TTrack::SetConnections(int i) return false; } -bool TTrack::Switch(int i, double t, double d) +bool TTrack::Switch(int i, float const t, float const d) { // przełączenie torów z uruchomieniem animacji if (SwitchExtension) // tory przełączalne mają doklejkę if (eType == tt_Switch) { // przekładanie zwrotnicy jak zwykle - if (t > 0.0) // prędkość liniowa ruchu iglic + if (t > 0.f) // prędkość liniowa ruchu iglic SwitchExtension->fOffsetSpeed = t; // prędkość łatwiej zgrać z animacją modelu - if (d >= 0.0) // dodatkowy ruch drugiej iglicy (zamknięcie nastawnicze) + if (d >= 0.f) // dodatkowy ruch drugiej iglicy (zamknięcie nastawnicze) SwitchExtension->fOffsetDelay = d; i &= 1; // ograniczenie błędów !!!! SwitchExtension->fDesiredOffset = @@ -2052,16 +2397,16 @@ TTrack * TTrack::RaAnimate() bool m = true; // animacja trwa if (eType == tt_Switch) // dla zwrotnicy tylko szyny { - double v = SwitchExtension->fDesiredOffset - SwitchExtension->fOffset; // kierunek + auto const v = SwitchExtension->fDesiredOffset - SwitchExtension->fOffset; // kierunek SwitchExtension->fOffset += sign(v) * Timer::GetDeltaTime() * SwitchExtension->fOffsetSpeed; // Ra: trzeba dać to do klasy... SwitchExtension->fOffset1 = SwitchExtension->fOffset; SwitchExtension->fOffset2 = SwitchExtension->fOffset; - if (SwitchExtension->fOffset1 >= fMaxOffset) + if (SwitchExtension->fOffset1 > fMaxOffset) SwitchExtension->fOffset1 = fMaxOffset; // ograniczenie animacji zewnętrznej iglicy - if (SwitchExtension->fOffset2 <= 0.00) - SwitchExtension->fOffset2 = 0.0; // ograniczenie animacji wewnętrznej iglicy - if (v < 0) + if (SwitchExtension->fOffset2 < 0.f) + SwitchExtension->fOffset2 = 0.f; // ograniczenie animacji wewnętrznej iglicy + if (v < 0.f) { // jak na pierwszy z torów if (SwitchExtension->fOffset <= SwitchExtension->fDesiredOffset) { @@ -2083,32 +2428,39 @@ TTrack * TTrack::RaAnimate() && ( ( false == Geometry1.empty() ) || ( false == Geometry2.empty() ) ) ) { // iglice liczone tylko dla zwrotnic - double fHTW = 0.5 * fabs( fTrackWidth ); - double fHTW2 = fHTW; // Ra: na razie niech tak będzie - double cos1 = 1.0, sin1 = 0.0, cos2 = 1.0, sin2 = 0.0; // Ra: ... - vector6 rpts3[ 24 ], rpts4[ 24 ]; - for (int i = 0; i < 12; ++i) - { - rpts3[i] = - vector6( - +( fHTW + iglica[i].x) * cos1 + iglica[i].y * sin1, - -(+fHTW + iglica[i].x) * sin1 + iglica[i].y * cos1, - iglica[i].z); - rpts3[i + 12] = - vector6( - +( fHTW2 + szyna[i].x) * cos2 + szyna[i].y * sin2, - -(+fHTW2 + szyna[i].x) * sin2 + iglica[i].y * cos2, - szyna[i].z); - rpts4[11 - i] = - vector6( - (-fHTW - iglica[i].x) * cos1 + iglica[i].y * sin1, - -(-fHTW - iglica[i].x) * sin1 + iglica[i].y * cos1, - iglica[i].z); - rpts4[23 - i] = - vector6( - (-fHTW2 - szyna[i].x) * cos2 + szyna[i].y * sin2, - -(-fHTW2 - szyna[i].x) * sin2 + iglica[i].y * cos2, - szyna[i].z); + auto const fHTW = 0.5f * std::abs( fTrackWidth ); + auto const fHTW2 = fHTW; // Ra: na razie niech tak będzie + auto const cos1 = 1.0f, sin1 = 0.0f, cos2 = 1.0f, sin2 = 0.0f; // Ra: ... + + basic_vertex + rpts3[ 24 ], + rpts4[ 24 ]; + for (int i = 0; i < 12; ++i) { + + rpts3[ i ] = { + {+( fHTW + iglica[ i ].position.x ) * cos1 + iglica[ i ].position.y * sin1, + -( fHTW + iglica[ i ].position.x ) * sin1 + iglica[ i ].position.y * cos1, + 0.f}, + {iglica[ i ].normal}, + {iglica[ i ].texture.x, 0.f} }; + rpts3[ i + 12 ] = { + {+( fHTW2 + szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, + -( fHTW2 + szyna[ i ].position.x ) * sin2 + iglica[ i ].position.y * cos2, + 0.f}, + {szyna[ i ].normal}, + {szyna[ i ].texture.x, 0.f} }; + rpts4[ 11 - i ] = { + {+( -fHTW - iglica[ i ].position.x ) * cos1 + iglica[ i ].position.y * sin1, + -( -fHTW - iglica[ i ].position.x ) * sin1 + iglica[ i ].position.y * cos1, + 0.f}, + {iglica[ i ].normal}, + {iglica[ i ].texture.x, 0.f} }; + rpts4[ 23 - i ] = { + { ( -fHTW2 - szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2, + -( -fHTW2 - szyna[ i ].position.x ) * sin2 + iglica[ i ].position.y * cos2, + 0.f}, + {szyna[ i ].normal}, + {szyna[ i ].texture.x, 0.f} }; } auto const origin { pMyNode->m_rootposition }; @@ -2163,8 +2515,8 @@ TTrack * TTrack::RaAnimate() double hlen = 0.5 * SwitchExtension->Segments[0]->GetLength(); // połowa // długości SwitchExtension->fOffset = ac->AngleGet(); // pobranie kąta z submodelu - double sina = -hlen * sin(DegToRad(SwitchExtension->fOffset)), - cosa = -hlen * cos(DegToRad(SwitchExtension->fOffset)); + double sina = -hlen * std::sin(glm::radians(SwitchExtension->fOffset)), + cosa = -hlen * std::cos(glm::radians(SwitchExtension->fOffset)); SwitchExtension->vTrans = ac->TransGet(); vector3 middle = SwitchExtension->pMyNode->pCenter + @@ -2317,9 +2669,9 @@ void TTrack::VelocitySet(float v) fVelocity = v; // nie ma ograniczenia }; -float TTrack::VelocityGet() +double TTrack::VelocityGet() { // pobranie dozwolonej prędkości podczas skanowania - return ((iDamageFlag & 128) ? 0.0f : fVelocity); // tor uszkodzony = prędkość zerowa + return ((iDamageFlag & 128) ? 0.0 : fVelocity); // tor uszkodzony = prędkość zerowa }; void TTrack::ConnectionsLog() diff --git a/Track.h b/Track.h index 91ea6138..a26e3d83 100644 --- a/Track.h +++ b/Track.h @@ -10,23 +10,23 @@ http://mozilla.org/MPL/2.0/. #pragma once #include -#include "GL/glew.h" -#include "ResourceManager.h" +#include +#include + #include "Segment.h" #include "material.h" -typedef enum -{ +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 +34,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,23 +49,22 @@ class TSwitchExtension TSwitchExtension(TTrack *owner, int const what); ~TSwitchExtension(); std::shared_ptr 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 @@ -77,21 +76,17 @@ class TSwitchExtension 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ę 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 +101,6 @@ 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 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,7 +112,7 @@ class TIsolated }; // trajektoria ruchu - opakowanie -class TTrack /*: public Resource*/ { +class TTrack { friend class opengl_renderer; @@ -207,7 +201,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() { @@ -241,17 +235,12 @@ 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: From c419d9fc8677c75930eba42f435ff366d71a4781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3lik=20Uszasty?= Date: Sat, 16 Sep 2017 22:43:33 +0200 Subject: [PATCH 03/31] Fixed slip calculations, some new parameters for python scripts --- McZapkie/Mover.cpp | 13 +++++++++---- Train.cpp | 47 ++++++++++++++++++++++++++++++++-------------- Train.h | 1 + 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index b8d93467..d7799a8b 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -3720,14 +3720,19 @@ 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 (Sign(nrot * M_PI * WheelDiameter - V)*Sign(temp_nrot * M_PI * WheelDiameter - V) < 0) { @@ -4630,7 +4635,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; diff --git a/Train.cpp b/Train.cpp index 46dbb7b9..e6f4902b 100644 --- a/Train.cpp +++ b/Train.cpp @@ -478,11 +478,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->SlippingWheels )); // 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 char* TXTT[ 10 ] = { "fd", "fdt", "fdb", "pd", "pdt", "pdb", "itothv", "1", "2", "3" }; char* TXTC[ 10 ] = { "fr", "frt", "frb", "pr", "prt", "prb", "im", "vm", "ihv", "uhv" }; @@ -522,6 +524,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; @@ -3879,19 +3882,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) @@ -4176,11 +4193,12 @@ 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()]; + cCode[i] = p->MoverParameters->TypeName[p->MoverParameters->TypeName.length()-1]; asCarName[i] = p->GetName(); 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); @@ -4222,6 +4240,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] = ""; diff --git a/Train.h b/Train.h index c9c9e6d2..de3b00b4 100644 --- a/Train.h +++ b/Train.h @@ -464,6 +464,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 From 6e8fbf73626185b109ccecc7dd2bb7283425ad9c Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sun, 17 Sep 2017 03:18:34 +0200 Subject: [PATCH 04/31] build 170916. mouse support for pantograph valve and compressor, minor tweak in ai route selection for cars --- Driver.cpp | 69 ++++++++++++++++++++++++++++++-------------------- Driver.h | 7 +++-- Track.cpp | 8 +++--- Track.h | 9 +++++++ Train.cpp | 8 +++--- mouseinput.cpp | 6 +++++ translation.h | 2 ++ version.h | 2 +- 8 files changed, 74 insertions(+), 37 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index f6e6e728..2af71d90 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -73,7 +73,16 @@ 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->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 @@ -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(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 @@ -5616,31 +5631,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(); } } } @@ -5648,6 +5662,7 @@ void TController::RouteSwitch(int d) } } }; +*/ std::string TController::OwnerName() const { return ( pVehicle ? pVehicle->MoverParameters->Name : "none" ); diff --git a/Driver.h b/Driver.h index 17fc2f72..d1695179 100644 --- a/Driver.h +++ b/Driver.h @@ -230,8 +230,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) @@ -401,7 +402,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; } diff --git a/Track.cpp b/Track.cpp index ed94059a..e84652e5 100644 --- a/Track.cpp +++ b/Track.cpp @@ -2343,18 +2343,18 @@ int TTrack::CrossSegment(int from, int into) switch (into) { case 0: // stop - // WriteLog("Crossing from P"+AnsiString(from+1)+" into stop on "+pMyNode->asName); +// WriteLog( "Stopping in P" + to_string( from + 1 ) + " on " + pMyNode->asName ); break; case 1: // left - // WriteLog("Crossing from P"+AnsiString(from+1)+" to left on "+pMyNode->asName); +// WriteLog( "Turning left from P" + to_string( from + 1 ) + " on " + pMyNode->asName ); i = (SwitchExtension->iRoads == 4) ? iLewo4[from] : iLewo3[from]; break; case 2: // right - // WriteLog("Crossing from P"+AnsiString(from+1)+" to right on "+pMyNode->asName); +// WriteLog( "Turning right from P" + to_string( from + 1 ) + " on " + pMyNode->asName ); i = (SwitchExtension->iRoads == 4) ? iPrawo4[from] : iPrawo3[from]; break; case 3: // stright - // WriteLog("Crossing from P"+AnsiString(from+1)+" to straight on "+pMyNode->asName); +// WriteLog( "Going straight from P" + to_string( from + 1 ) + " on " + pMyNode->asName ); i = (SwitchExtension->iRoads == 4) ? iProsto4[from] : iProsto3[from]; break; } diff --git a/Track.h b/Track.h index a26e3d83..f396df26 100644 --- a/Track.h +++ b/Track.h @@ -209,6 +209,15 @@ public: SwitchExtension ? SwitchExtension->CurrentIndex : -1); }; + // 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, std::string name); bool AssignEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent2); bool AssignallEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent2); diff --git a/Train.cpp b/Train.cpp index 89fd9182..fde9fd70 100644 --- a/Train.cpp +++ b/Train.cpp @@ -6735,9 +6735,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 ); diff --git a/mouseinput.cpp b/mouseinput.cpp index be72ce1d..8fd53744 100644 --- a/mouseinput.cpp +++ b/mouseinput.cpp @@ -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 } }, diff --git a/translation.h b/translation.h index 297f35cf..a9831f81 100644 --- a/translation.h +++ b/translation.h @@ -70,6 +70,8 @@ static std::unordered_map 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" }, diff --git a/version.h b/version.h index c063cca4..bbb0332c 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 914 +#define VERSION_MINOR 916 #define VERSION_REVISION 0 From fd06e2306a1b6b855fcce272f3733ddb373cc9f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3lik=20Uszasty?= Date: Sun, 24 Sep 2017 20:20:18 +0200 Subject: [PATCH 05/31] Added wheel flats calculation and some small changes to induction motors --- Driver.cpp | 2 +- DynObj.cpp | 8 ++++---- McZapkie/MOVER.h | 2 ++ McZapkie/Mover.cpp | 35 ++++++++++++++++++++++++++--------- McZapkie/hamulce.cpp | 11 +++++++++-- Train.cpp | 6 +++--- World.cpp | 30 +++++++++++++++--------------- 7 files changed, 60 insertions(+), 34 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 12af005a..ee8d8e2d 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -1707,7 +1707,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) diff --git a/DynObj.cpp b/DynObj.cpp index 2a5c9254..5a7f852c 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2784,9 +2784,9 @@ bool TDynamicObject::Update(double dt, double dt1) 1000; // chwilowy max ED -> do rozdzialu sil FfulED = Min0R(p->MoverParameters->eimv[eimv_Fful], 0) * 1000; // chwilowy max ED -> do rozdzialu sil - FrED -= Min0R(p->MoverParameters->eimv[eimv_Fr], 0) * + FrED -= Min0R(p->MoverParameters->eimv[eimv_Fmax], 0) * 1000; // chwilowo realizowane ED -> do pneumatyki - Frj += Max0R(p->MoverParameters->eimv[eimv_Fr], 0) * + Frj += Max0R(p->MoverParameters->eimv[eimv_Fmax], 0) * 1000;// chwilowo realizowany napęd -> do utrzymującego masa += p->MoverParameters->TotalMass; osie += p->MoverParameters->NAxles; @@ -2880,10 +2880,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; diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 19e14c7d..073ac37b 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -780,6 +780,7 @@ public: double Ftmax = 0.0; /*- dla lokomotyw z silnikami indukcyjnymi -*/ double eimc[26]; + bool EIMCLogForce; // static std::vector const eimc_labels; /*-dla wagonow*/ double MaxLoad = 0.0; /*masa w T lub ilosc w sztukach - ladownosc*/ @@ -823,6 +824,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*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index d7799a8b..06bc5830 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -3734,6 +3734,10 @@ void TMoverParameters::ComputeTotalForce(double dt, double dt1, bool FullVer) 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) { SlippingWheels = false; @@ -4684,23 +4688,23 @@ double TMoverParameters::TractionForce(double dt) if ((abs((PosRatio + 9.66 * dizel_fill) * dmoment * 100) > 0.95 * Adhesive(RunningTrack.friction) * TotalMassxg)) { - PosRatio = 0; - tmp = 4; - Sandbox( true, range::local ); + //PosRatio = 0; + //tmp = 4; + //Sandbox( true, range::local ); } // przeciwposlizg if ((abs((PosRatio + 9.80 * dizel_fill) * dmoment * 100) > 0.95 * Adhesive(RunningTrack.friction) * TotalMassxg)) { - PosRatio = 0; - tmp = 9; - Sandbox( true, range::local ); + //PosRatio = 0; + //tmp = 9; + //Sandbox( true, range::local ); } // przeciwposlizg if ((SlippingWheels)) { // PosRatio = -PosRatio * 0; // serio -0 ??? PosRatio = 0; tmp = 9; - Sandbox( true, range::local ); + Sandbox( true, range::unit ); } // przeciwposlizg dizel_fill += Max0R(Min0R(PosRatio - dizel_fill, 0.1), -0.1) * 2 * @@ -4737,6 +4741,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 @@ -4748,9 +4759,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 @@ -4921,7 +4936,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 @@ -7136,6 +7151,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 { @@ -7384,6 +7400,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" ); diff --git a/McZapkie/hamulce.cpp b/McZapkie/hamulce.cpp index 06e45445..a1156baf 100644 --- a/McZapkie/hamulce.cpp +++ b/McZapkie/hamulce.cpp @@ -1462,9 +1462,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 @@ -1995,7 +2001,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; diff --git a/Train.cpp b/Train.cpp index e6f4902b..a9aba82f 100644 --- a/Train.cpp +++ b/Train.cpp @@ -478,7 +478,7 @@ 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->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 ) ); @@ -4205,10 +4205,10 @@ bool TTrain::Update( double const Deltatime ) } 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); diff --git a/World.cpp b/World.cpp index 127a4aa6..f077fa2f 100644 --- a/World.cpp +++ b/World.cpp @@ -1061,12 +1061,12 @@ bool TWorld::Update() /* if (DebugModeFlag) if (Global::bActive) // nie przyspieszać, gdy jedzie w tle :) - if( Console::Pressed( GLFW_KEY_ESCAPE ) ) { + if (glfwGetKey(window, GLFW_KEY_PAUSE) == GLFW_PRESS) { // yB dodał przyspieszacz fizyki - Ground.Update(dt, n); - Ground.Update(dt, n); - Ground.Update(dt, n); - Ground.Update(dt, n); // 5 razy + Ground.Update(dt / updatecount, updatecount); + Ground.Update(dt / updatecount, updatecount); + Ground.Update(dt / updatecount, updatecount); + Ground.Update(dt / updatecount, updatecount); // 5 razy } */ // secondary fixed step simulation time routines @@ -1481,7 +1481,7 @@ TWorld::Update_UI() { uitextline2 += "; Ft: " + to_string( tmp->MoverParameters->Ft * 0.001f * tmp->MoverParameters->ActiveCab, 1 ) + ", Fb: " + to_string( tmp->MoverParameters->Fb * 0.001f, 1 ) - + ", Fr: " + to_string( tmp->MoverParameters->RunningTrack.friction, 2 ) + + ", Fr: " + to_string( tmp->MoverParameters->Adhesive(tmp->MoverParameters->RunningTrack.friction), 2 ) + ( tmp->MoverParameters->SlippingWheels ? " (!)" : "" ); uitextline2 += @@ -1680,15 +1680,15 @@ TWorld::Update_UI() { if( tmp == nullptr ) { break; } - - uitextline1 = - "vel: " + to_string( tmp->GetVelocity(), 2 ) + " km/h" + ( tmp->MoverParameters->SlippingWheels ? " (!)" : "" ) - + "; dist: " + to_string( tmp->MoverParameters->DistCounter, 2 ) + " km" - + "; pos: (" - + to_string( tmp->GetPosition().x, 2 ) + ", " - + to_string( tmp->GetPosition().y, 2 ) + ", " - + to_string( tmp->GetPosition().z, 2 ) - + ")"; + uitextline1 = + "vel: " + to_string(tmp->GetVelocity(), 2) + "/" + to_string(tmp->MoverParameters->nrot* M_PI * tmp->MoverParameters->WheelDiameter * 3.6, 2) + + " km/h" + (tmp->MoverParameters->SlippingWheels ? " (!)" : " ") + + "; dist: " + to_string(tmp->MoverParameters->DistCounter, 2) + " km" + + "; pos: (" + + to_string(tmp->GetPosition().x, 2) + ", " + + to_string(tmp->GetPosition().y, 2) + ", " + + to_string(tmp->GetPosition().z, 2) + "), PM=" + + to_string(tmp->MoverParameters->WheelFlat, 1) + " mm"; uitextline2 = "HamZ=" + to_string( tmp->MoverParameters->fBrakeCtrlPos, 1 ) From f6272d37f1e6933778d17d46ed7dfca170eecf9a Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 7 Oct 2017 01:18:54 +0200 Subject: [PATCH 06/31] partial initial refactoring: events, memcells, tracks, models, traction; NOTE: debug mode broken, investigate --- AnimModel.cpp | 29 +- AnimModel.h | 87 ++-- DynObj.cpp | 6 - Event.cpp | 908 +++++++++++++++++++++++++++++++++++++++- Event.h | 58 ++- Globals.cpp | 6 +- Ground.cpp | 363 +++++++--------- Ground.h | 53 +-- MemCell.cpp | 29 +- MemCell.h | 77 ++-- Model3d.cpp | 20 +- Names.h | 44 ++ Track.cpp | 415 ++++++++++++------ Track.h | 45 +- Traction.cpp | 389 ++++++++++++++--- Traction.h | 38 +- Train.cpp | 2 +- Train.h | 2 + World.cpp | 65 ++- World.h | 1 + maszyna.vcxproj.filters | 24 ++ renderer.cpp | 644 ++++++++++++++++++++++++++-- renderer.h | 45 +- scene.cpp | 579 +++++++++++++++++++++++++ scene.h | 172 ++++++++ scenenode.cpp | 296 +++++++++++++ scenenode.h | 253 +++++++++++ simulation.cpp | 521 +++++++++++++++++++++++ simulation.h | 73 ++++ stdafx.h | 3 + vertex.cpp | 35 ++ vertex.h | 87 ++++ 32 files changed, 4767 insertions(+), 602 deletions(-) create mode 100644 scene.cpp create mode 100644 scene.h create mode 100644 scenenode.cpp create mode 100644 scenenode.h create mode 100644 simulation.cpp create mode 100644 simulation.h create mode 100644 vertex.cpp create mode 100644 vertex.h diff --git a/AnimModel.cpp b/AnimModel.cpp index b956331a..5646811d 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -406,23 +406,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() diff --git a/AnimModel.h b/AnimModel.h index cf7d9d98..69bdc8bb 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -21,13 +21,13 @@ http://mozilla.org/MPL/2.0/. const int iMaxNumLights = 8; // 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 @@ -118,55 +118,66 @@ 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; } +// members + static TAnimContainer *acAnimList; // lista animacji z eventem, które muszą być przeliczane również bez wyświetlania + +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 { 0.25f }; // 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_manager : public basic_table { + }; //--------------------------------------------------------------------------- diff --git a/DynObj.cpp b/DynObj.cpp index 13c65909..aaadd391 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2957,12 +2957,6 @@ 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 diff --git a/Event.cpp b/Event.cpp index 5461c74d..15d75f90 100644 --- a/Event.cpp +++ b/Event.cpp @@ -14,7 +14,8 @@ http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" -#include "Event.h" +#include "event.h" + #include "Globals.h" #include "Logs.h" #include "Usefull.h" @@ -23,6 +24,10 @@ http://mozilla.org/MPL/2.0/. #include "MemCell.h" #include "Ground.h" #include "McZapkie\mctools.h" +#include "animmodel.h" +#include "dynobj.h" +#include "driver.h" +#include "tractionpower.h" TEvent::TEvent( std::string const &m ) : asNodeName( m ) @@ -36,8 +41,8 @@ TEvent::TEvent( std::string const &m ) : } }; -TEvent::~TEvent() -{ +TEvent::~TEvent() { + switch (Type) { // sprzątanie case tp_Multiple: @@ -56,14 +61,17 @@ TEvent::~TEvent() // SafeDeleteArray(Params[9].asText); //nie usuwać - nazwa jest zamieniana na wskaźnik do // submodelu if (Params[0].asInt == 4) // jeśli z pliku VMD - delete[] Params[8].asPointer; // zwolnić obszar + SafeDeleteArray( Params[8].asPointer ); // zwolnić obszar case tp_GetValues: // nic break; case tp_PutValues: // params[0].astext stores the token SafeDeleteArray( Params[ 0 ].asText ); break; + default: + break; } - evJoined = NULL; // nie usuwać podczepionych tutaj + evJoined = nullptr; // nie usuwać podczepionych tutaj + }; void TEvent::Init(){ @@ -138,7 +146,7 @@ void TEvent::Conditions(cParser *parser, std::string s) } }; -void TEvent::Load(cParser *parser, vector3 *org) +void TEvent::Load(cParser *parser, Math3D::vector3 const &org) { std::string token; @@ -294,12 +302,12 @@ void TEvent::Load(cParser *parser, vector3 *org) parser->getTokens(3); *parser >> Params[3].asdouble >> Params[4].asdouble >> Params[5].asdouble; // położenie // X,Y,Z - if (org) + if ( !(org == Math3D::vector3()) ) { // przesunięcie // tmp->pCenter.RotateY(aRotate.y/180.0*M_PI); //Ra 2014-11: uwzględnienie rotacji - Params[3].asdouble += org->x; // współrzędne w scenerii - Params[4].asdouble += org->y; - Params[5].asdouble += org->z; + Params[3].asdouble += org.x; // współrzędne w scenerii + Params[4].asdouble += org.y; + Params[5].asdouble += org.z; } // Params[12].asInt=0; parser->getTokens(1, false); // komendy 'case sensitive' @@ -679,16 +687,16 @@ double TEvent::ValueGet(int n) return 0.0; // inne eventy się nie liczą }; -vector3 TEvent::PositionGet() const +glm::dvec3 TEvent::PositionGet() const { // pobranie współrzędnych eventu switch (Type) { // case tp_GetValues: - return Params[9].asMemCell->Position(); // współrzędne podłączonej komórki pamięci + return Params[9].asMemCell->location(); // współrzędne podłączonej komórki pamięci case tp_PutValues: - return vector3(Params[3].asdouble, Params[4].asdouble, Params[5].asdouble); + return glm::dvec3(Params[3].asdouble, Params[4].asdouble, Params[5].asdouble); } - return vector3(0, 0, 0); // inne eventy się nie liczą + return glm::dvec3(0, 0, 0); // inne eventy się nie liczą }; bool TEvent::StopCommand() @@ -714,3 +722,875 @@ void TEvent::Append(TEvent *e) e->bEnabled = true; // ten doczepiony może być tylko kolejkowany } }; + + + +event_manager::~event_manager() { + + for( auto *event : m_events ) { + delete event; + } +} + +// adds provided event to the collection. returns: true on success +// TODO: return handle instead of pointer +bool +event_manager::insert( TEvent *Event ) { + + if( Event->Type == tp_Unknown ) { return false; } + + // najpierw sprawdzamy, czy nie ma, a potem dopisujemy + auto lookup = m_eventmap.find( Event->asName ); + if( lookup != m_eventmap.end() ) { + // duplicate of already existing event + auto const size = Event->asName.size(); + // zawsze jeden znak co najmniej jest + if( Event->asName[ 0 ] == '#' ) { + // utylizacja duplikatu z krzyżykiem + return false; + } + // tymczasowo wyjątki: + else if( ( size > 8 ) + && ( Event->asName.substr( 0, 9 ) == "lineinfo:" ) ) { + // tymczasowa utylizacja duplikatów W5 + return false; + } + else if( ( size > 8 ) + && ( Event->asName.substr( size - 8 ) == "_warning" ) ) { + // tymczasowa utylizacja duplikatu z trąbieniem + return false; + } + else if( ( size > 4 ) + && ( Event->asName.substr( size - 4 ) == "_shp" ) ) { + // nie podlegają logowaniu + // tymczasowa utylizacja duplikatu SHP + return false; + } + + auto *duplicate = m_events[ lookup->second ]; + if( Global::bJoinEvents ) { + // doczepka (taki wirtualny multiple bez warunków) + duplicate->Append( Event ); + } + else { + // NOTE: somewhat convoluted way to deal with 'replacing' events without leaving dangling pointers + // can be cleaned up if pointers to events were replaced with handles + ErrorLog( "Bad event: encountered duplicated event, \"" + Event->asName + "\"" ); + duplicate->Append( Event ); // doczepka (taki wirtualny multiple bez warunków) + // BUG: source of memory leak. + // erasing original type of event prevents it from proper resource de-allocation on exit + // TODO: mark ignored event with separate flag or ideally refactor the whole thing + duplicate->Type = tp_Ignored; // dezaktywacja pierwotnego - taka proteza na wsteczną zgodność + } + } + + m_events.emplace_back( Event ); + if( lookup == m_eventmap.end() ) { + // if it's first event with such name, it's potential candidate for the execution queue + m_eventmap.emplace( Event->asName, m_events.size() - 1 ); + if( ( Event->Type != tp_Ignored ) + && ( Event->asName.find( "onstart" ) != std::string::npos ) ) { + // event uruchamiany automatycznie po starcie + AddToQuery( Event, nullptr ); + } + } + + return true; +} + +// legacy method, returns pointer to specified event, or null +TEvent * +event_manager::FindEvent( std::string const &Name ) { + + if( Name.empty() ) { return nullptr; } + + auto const lookup = m_eventmap.find( Name ); + return ( + lookup != m_eventmap.end() ? + m_events[ lookup->second ] : + nullptr ); +} + +// legacy method, inserts specified event in the event query +bool +event_manager::AddToQuery( TEvent *Event, TDynamicObject *Owner ) { + + if( Event->bEnabled ) { + // jeśli może być dodany do kolejki (nie używany w skanowaniu) + if( !Event->iQueued ) // jeśli nie dodany jeszcze do kolejki + { // kolejka eventów jest posortowana względem (fStartTime) + Event->Activator = Owner; + if( ( Event->Type == tp_AddValues ) + && ( Event->fDelay == 0.0 ) ) { + // eventy AddValues trzeba wykonywać natychmiastowo, inaczej kolejka może zgubić jakieś dodawanie + // Ra: kopiowanie wykonania tu jest bez sensu, lepiej by było wydzielić funkcję + // wykonującą eventy i ją wywołać + if( EventConditon( Event ) ) { // teraz mogą być warunki do tych eventów + Event->Params[ 5 ].asMemCell->UpdateValues( + Event->Params[ 0 ].asText, Event->Params[ 1 ].asdouble, + Event->Params[ 2 ].asdouble, Event->iFlags ); + if( Event->Params[ 6 ].asTrack ) { // McZapkie-100302 - updatevalues oprocz zmiany wartosci robi putcommand dla + // wszystkich 'dynamic' na danym torze + for( auto dynamic : Event->Params[ 6 ].asTrack->Dynamics ) { + Event->Params[ 5 ].asMemCell->PutCommand( + dynamic->Mechanik, + &Event->Params[ 4 ].nGroundNode->pCenter ); + } + //if (DebugModeFlag) + WriteLog( + "EVENT EXECUTED" + ( Owner ? ( " by " + Owner->asName ) : "" ) + ": AddValues & Track command ( " + + std::string( Event->Params[ 0 ].asText ) + " " + + std::to_string( Event->Params[ 1 ].asdouble ) + " " + + std::to_string( Event->Params[ 2 ].asdouble ) + " )" ); + } + //else if (DebugModeFlag) + WriteLog( + "EVENT EXECUTED" + ( Owner ? ( " by " + Owner->asName ) : "" ) + ": AddValues ( " + + std::string( Event->Params[ 0 ].asText ) + " " + + std::to_string( Event->Params[ 1 ].asdouble ) + " " + + std::to_string( Event->Params[ 2 ].asdouble ) + " )" ); + } + // jeśli jest kolejny o takiej samej nazwie, to idzie do kolejki (and if there's no joint event it'll be set to null and processing will end here) + do { + Event = Event->evJoined; + // NOTE: we could've received a new event from joint event above, so we need to check conditions just in case and discard the bad events + // TODO: refactor this arrangement, it's hardly optimal + } while( ( Event != nullptr ) + && ( ( false == Event->bEnabled ) + || ( Event->iQueued > 0 ) ) ); + } + if( Event != nullptr ) { + // standardowe dodanie do kolejki + ++Event->iQueued; // zabezpieczenie przed podwójnym dodaniem do kolejki + WriteLog( "EVENT ADDED TO QUEUE" + ( Owner ? ( " by " + Owner->asName ) : "" ) + ": " + Event->asName ); + Event->fStartTime = std::abs( Event->fDelay ) + Timer::GetTime(); // czas od uruchomienia scenerii + if( Event->fRandomDelay > 0.0 ) { + // doliczenie losowego czasu opóźnienia + Event->fStartTime += Event->fRandomDelay * Random( 10000 ) * 0.0001; + } + if( QueryRootEvent != nullptr ) { + TEvent::AddToQuery( Event, QueryRootEvent ); + } + else { + QueryRootEvent = Event; + QueryRootEvent->evNext = nullptr; + } + } + } + } + return true; +} + +// legacy method, executes queued events +bool +event_manager::CheckQuery() { + + TLocation loc; + int i; + while( ( QueryRootEvent != nullptr ) + && ( QueryRootEvent->fStartTime < Timer::GetTime() ) ) + { // eventy są posortowana wg czasu wykonania + m_workevent = QueryRootEvent; // wyjęcie eventu z kolejki + if (QueryRootEvent->evJoined) // jeśli jest kolejny o takiej samej nazwie + { // to teraz on będzie następny do wykonania + QueryRootEvent = QueryRootEvent->evJoined; // następny będzie ten doczepiony + QueryRootEvent->evNext = m_workevent->evNext; // pamiętając o następnym z kolejki + QueryRootEvent->fStartTime = m_workevent->fStartTime; // czas musi być ten sam, bo nie jest aktualizowany + QueryRootEvent->Activator = m_workevent->Activator; // pojazd aktywujący + QueryRootEvent->iQueued = 1; + // w sumie można by go dodać normalnie do kolejki, ale trzeba te połączone posortować wg czasu wykonania + } + else // a jak nazwa jest unikalna, to kolejka idzie dalej + QueryRootEvent = QueryRootEvent->evNext; // NULL w skrajnym przypadku + if (m_workevent->bEnabled) + { // w zasadzie te wyłączone są skanowane i nie powinny się nigdy w kolejce znaleźć + --m_workevent->iQueued; // teraz moze być ponownie dodany do kolejki + WriteLog( "EVENT LAUNCHED" + ( m_workevent->Activator ? ( " by " + m_workevent->Activator->asName ) : "" ) + ": " + m_workevent->asName ); + switch (m_workevent->Type) + { + case tp_CopyValues: // skopiowanie wartości z innej komórki + m_workevent->Params[5].asMemCell->UpdateValues( + m_workevent->Params[9].asMemCell->Text(), + m_workevent->Params[9].asMemCell->Value1(), + m_workevent->Params[9].asMemCell->Value2(), + m_workevent->iFlags // flagi określają, co ma być skopiowane + ); + // break; //żeby się wysłało do torów i nie było potrzeby na AddValues * 0 0 + case tp_AddValues: // różni się jedną flagą od UpdateValues + case tp_UpdateValues: + if (EventConditon(m_workevent)) + { // teraz mogą być warunki do tych eventów + if (m_workevent->Type != tp_CopyValues) // dla CopyValues zrobiło się wcześniej + m_workevent->Params[5].asMemCell->UpdateValues( + m_workevent->Params[0].asText, + m_workevent->Params[1].asdouble, + m_workevent->Params[2].asdouble, + m_workevent->iFlags); + if (m_workevent->Params[6].asTrack) + { // McZapkie-100302 - updatevalues oprocz zmiany wartosci robi putcommand dla + // wszystkich 'dynamic' na danym torze + for( auto dynamic : m_workevent->Params[ 6 ].asTrack->Dynamics ) { + m_workevent->Params[ 5 ].asMemCell->PutCommand( + dynamic->Mechanik, + &m_workevent->Params[ 4 ].nGroundNode->pCenter ); + } + //if (DebugModeFlag) + WriteLog("Type: UpdateValues & Track command - [" + + m_workevent->Params[5].asMemCell->Text() + "] [" + + to_string( m_workevent->Params[ 5 ].asMemCell->Value1(), 2 ) + "] [" + + to_string( m_workevent->Params[ 5 ].asMemCell->Value2(), 2 ) + "]" ); + } + else //if (DebugModeFlag) + WriteLog("Type: UpdateValues - [" + + m_workevent->Params[5].asMemCell->Text() + "] [" + + to_string( m_workevent->Params[ 5 ].asMemCell->Value1(), 2 ) + "] [" + + to_string( m_workevent->Params[ 5 ].asMemCell->Value2(), 2 ) + "]" ); + } + break; + case tp_GetValues: { + if( m_workevent->Activator ) { +/* + // TODO: re-enable when messaging module is in place + if( Global::iMultiplayer ) { + // potwierdzenie wykonania dla serwera (odczyt semafora już tak nie działa) + WyslijEvent( tmpEvent->asName, tmpEvent->Activator->GetName() ); + } +*/ + m_workevent->Params[ 9 ].asMemCell->PutCommand( + m_workevent->Activator->Mechanik, + &m_workevent->Params[ 8 ].nGroundNode->pCenter ); + } + WriteLog( "Type: GetValues" ); + break; + } + case tp_PutValues: { + if (m_workevent->Activator) { + // zamiana, bo fizyka ma inaczej niż sceneria + loc.X = -m_workevent->Params[3].asdouble; + loc.Y = m_workevent->Params[5].asdouble; + loc.Z = m_workevent->Params[4].asdouble; + if( m_workevent->Activator->Mechanik ) { + // przekazanie rozkazu do AI + m_workevent->Activator->Mechanik->PutCommand( + m_workevent->Params[0].asText, m_workevent->Params[1].asdouble, + m_workevent->Params[2].asdouble, loc); + } + else { + // przekazanie do pojazdu + m_workevent->Activator->MoverParameters->PutCommand( + m_workevent->Params[0].asText, m_workevent->Params[1].asdouble, + m_workevent->Params[2].asdouble, loc); + } + WriteLog("Type: PutValues - [" + + std::string(m_workevent->Params[0].asText) + "] [" + + to_string( m_workevent->Params[ 1 ].asdouble, 2 ) + "] [" + + to_string( m_workevent->Params[ 2 ].asdouble, 2 ) + "]" ); + } + break; + } + case tp_Lights: + if (m_workevent->Params[9].asModel) + for (i = 0; i < iMaxNumLights; i++) + if (m_workevent->Params[i].asdouble >= 0) //-1 zostawia bez zmiany + m_workevent->Params[9].asModel->LightSet( + i, m_workevent->Params[i].asdouble); // teraz też ułamek + break; + case tp_Visible: + if (m_workevent->Params[9].nGroundNode) + m_workevent->Params[9].nGroundNode->bVisible = (m_workevent->Params[i].asInt > 0); + break; + case tp_Velocity: + Error("Not implemented yet :("); + break; + case tp_Exit: + MessageBox(0, m_workevent->asNodeName.c_str(), " THE END ", MB_OK); + Global::iTextMode = -1; // wyłączenie takie samo jak sekwencja F10 -> Y + return false; + case tp_Sound: + switch (m_workevent->Params[0].asInt) + { // trzy możliwe przypadki: + case 0: + m_workevent->Params[9].tsTextSound->Stop(); + break; + case 1: + m_workevent->Params[9].tsTextSound->Play( + 1, 0, true, m_workevent->Params[9].tsTextSound->vSoundPosition); + break; + case -1: + m_workevent->Params[9].tsTextSound->Play( + 1, DSBPLAY_LOOPING, true, m_workevent->Params[9].tsTextSound->vSoundPosition); + break; + } + break; + case tp_Disable: + Error("Not implemented yet :("); + break; + case tp_Animation: // Marcin: dorobic translacje - Ra: dorobiłem ;-) + if (m_workevent->Params[0].asInt == 1) + m_workevent->Params[9].asAnimContainer->SetRotateAnim( + Math3D::vector3(m_workevent->Params[1].asdouble, m_workevent->Params[2].asdouble, + m_workevent->Params[3].asdouble), + m_workevent->Params[4].asdouble); + else if (m_workevent->Params[0].asInt == 2) + m_workevent->Params[9].asAnimContainer->SetTranslateAnim( + Math3D::vector3(m_workevent->Params[1].asdouble, m_workevent->Params[2].asdouble, + m_workevent->Params[3].asdouble), + m_workevent->Params[4].asdouble); + else if (m_workevent->Params[0].asInt == 4) + m_workevent->Params[9].asModel->AnimationVND( + m_workevent->Params[8].asPointer, + m_workevent->Params[1].asdouble, // tu mogą być dodatkowe parametry, np. od-do + m_workevent->Params[2].asdouble, m_workevent->Params[3].asdouble, + m_workevent->Params[4].asdouble); + break; + case tp_Switch: { + if( m_workevent->Params[ 9 ].asTrack ) { + m_workevent->Params[ 9 ].asTrack->Switch( + m_workevent->Params[ 0 ].asInt, + m_workevent->Params[ 1 ].asdouble, + m_workevent->Params[ 2 ].asdouble ); + } +/* + // TODO: re-enable when messaging module is in place + if( Global::iMultiplayer ) { + // dajemy znać do serwera o przełożeniu + WyslijEvent( m_workevent->asName, "" ); // wysłanie nazwy eventu przełączajacego + } +*/ + // Ra: bardziej by się przydała nazwa toru, ale nie ma do niej stąd dostępu + break; + } + case tp_TrackVel: + if (m_workevent->Params[9].asTrack) + { // prędkość na zwrotnicy może być ograniczona z góry we wpisie, większej się nie + // ustawi eventem + WriteLog("Type: TrackVel"); + m_workevent->Params[9].asTrack->VelocitySet(m_workevent->Params[0].asdouble); + if (DebugModeFlag) // wyświetlana jest ta faktycznie ustawiona + WriteLog(" - velocity: ", m_workevent->Params[9].asTrack->VelocityGet()); + } + break; + case tp_DynVel: + Error("Event \"DynVel\" is obsolete"); + break; + case tp_Multiple: { + auto const bCondition = EventConditon(m_workevent); + if( ( bCondition ) + || ( m_workevent->iFlags & conditional_anyelse ) ) { + // warunek spelniony albo było użyte else + WriteLog("Type: Multi-event"); + for (i = 0; i < 8; ++i) { + // dodawane do kolejki w kolejności zapisania + if( m_workevent->Params[ i ].asEvent ) { + if( bCondition != ( ( ( m_workevent->iFlags & ( conditional_else << i ) ) != 0 ) ) ) { + if( m_workevent->Params[ i ].asEvent != m_workevent ) + AddToQuery( m_workevent->Params[ i ].asEvent, m_workevent->Activator ); // normalnie dodać + else { + // jeśli ma być rekurencja to musi mieć sensowny okres powtarzania + if( m_workevent->fDelay >= 5.0 ) { + AddToQuery( m_workevent, m_workevent->Activator ); + } + } + } + } + } +/* + // TODO: re-enable when messaging component is in place + if( Global::iMultiplayer ) { + // dajemy znać do serwera o wykonaniu + if( ( m_workevent->iFlags & conditional_anyelse ) == 0 ) { + // jednoznaczne tylko, gdy nie było else + if( m_workevent->Activator ) { + WyslijEvent( m_workevent->asName, m_workevent->Activator->GetName() ); + } + else { + WyslijEvent( m_workevent->asName, "" ); + } + } + } +*/ + } + break; + } + case tp_WhoIs: { + // pobranie nazwy pociągu do komórki pamięci + if (m_workevent->iFlags & update_load) { + // jeśli pytanie o ładunek + if( m_workevent->iFlags & update_memadd ) { + // jeśli typ pojazdu + // TODO: define and recognize individual request types + auto const owner = ( + ( ( m_workevent->Activator->Mechanik != nullptr ) && ( m_workevent->Activator->Mechanik->Primary() ) ) ? + m_workevent->Activator->Mechanik : + m_workevent->Activator->ctOwner ); + auto const consistbrakelevel = ( + owner != nullptr ? + owner->fReady : + -1.0 ); + auto const collisiondistance = ( + owner != nullptr ? + owner->TrackBlock() : + -1.0 ); + + m_workevent->Params[ 9 ].asMemCell->UpdateValues( + m_workevent->Activator->MoverParameters->TypeName, // typ pojazdu + consistbrakelevel, + collisiondistance, + m_workevent->iFlags & ( update_memstring | update_memval1 | update_memval2 ) ); + + WriteLog( + "Type: WhoIs (" + to_string( m_workevent->iFlags ) + ") - " + + "[name: " + m_workevent->Activator->MoverParameters->TypeName + "], " + + "[consist brake level: " + to_string( consistbrakelevel, 2 ) + "], " + + "[obstacle distance: " + to_string( collisiondistance, 2 ) + " m]" ); + } + else { + // jeśli parametry ładunku + m_workevent->Params[ 9 ].asMemCell->UpdateValues( + m_workevent->Activator->MoverParameters->LoadType, // nazwa ładunku + m_workevent->Activator->MoverParameters->Load, // aktualna ilość + m_workevent->Activator->MoverParameters->MaxLoad, // maksymalna ilość + m_workevent->iFlags & ( update_memstring | update_memval1 | update_memval2 ) ); + + WriteLog( + "Type: WhoIs (" + to_string( m_workevent->iFlags ) + ") - " + + "[load type: " + m_workevent->Activator->MoverParameters->LoadType + "], " + + "[current load: " + to_string( m_workevent->Activator->MoverParameters->Load, 2 ) + "], " + + "[max load: " + to_string( m_workevent->Activator->MoverParameters->MaxLoad, 2 ) + "]" ); + } + } + else if (m_workevent->iFlags & update_memadd) + { // jeśli miejsce docelowe pojazdu + m_workevent->Params[ 9 ].asMemCell->UpdateValues( + m_workevent->Activator->asDestination, // adres docelowy + m_workevent->Activator->DirectionGet(), // kierunek pojazdu względem czoła składu (1=zgodny,-1=przeciwny) + m_workevent->Activator->MoverParameters->Power, // moc pojazdu silnikowego: 0 dla wagonu + m_workevent->iFlags & (update_memstring | update_memval1 | update_memval2)); + + WriteLog( + "Type: WhoIs (" + to_string( m_workevent->iFlags ) + ") - " + + "[destination: " + m_workevent->Activator->asDestination + "], " + + "[direction: " + to_string( m_workevent->Activator->DirectionGet() ) + "], " + + "[engine power: " + to_string( m_workevent->Activator->MoverParameters->Power, 2 ) + "]" ); + } + else if (m_workevent->Activator->Mechanik) + if (m_workevent->Activator->Mechanik->Primary()) + { // tylko jeśli ktoś tam siedzi - nie powinno dotyczyć pasażera! + m_workevent->Params[ 9 ].asMemCell->UpdateValues( + m_workevent->Activator->Mechanik->TrainName(), + m_workevent->Activator->Mechanik->StationCount() - m_workevent->Activator->Mechanik->StationIndex(), // ile przystanków do końca + m_workevent->Activator->Mechanik->IsStop() ? 1 : + 0, // 1, gdy ma tu zatrzymanie + m_workevent->iFlags); + WriteLog("Train detected: " + m_workevent->Activator->Mechanik->TrainName()); + } + break; + } + case tp_LogValues: { + // zapisanie zawartości komórki pamięci do logu + if( m_workevent->Params[ 9 ].asMemCell ) { + // jeśli była podana nazwa komórki + WriteLog( "Memcell \"" + m_workevent->asNodeName + "\": " + + m_workevent->Params[ 9 ].asMemCell->Text() + " " + + std::to_string( m_workevent->Params[ 9 ].asMemCell->Value1() ) + " " + + std::to_string( m_workevent->Params[ 9 ].asMemCell->Value2() ) ); + } + else { + // TODO: re-enable when cell manager is in place +/* + // lista wszystkich + for( TGroundNode *Current = nRootOfType[ TP_MEMCELL ]; Current; Current = Current->nNext ) { + WriteLog( "Memcell \"" + Current->asName + "\": " + + Current->MemCell->Text() + " " + + std::to_string( Current->MemCell->Value1() ) + " " + + std::to_string( Current->MemCell->Value2() ) ); + } +*/ + } + break; + } + case tp_Voltage: // zmiana napięcia w zasilaczu (TractionPowerSource) + if (m_workevent->Params[9].psPower) + { // na razie takie chamskie ustawienie napięcia zasilania + WriteLog("Type: Voltage"); + m_workevent->Params[9].psPower->VoltageSet(m_workevent->Params[0].asdouble); + } + case tp_Friction: // zmiana tarcia na scenerii + { // na razie takie chamskie ustawienie napięcia zasilania + WriteLog("Type: Friction"); + Global::fFriction = (m_workevent->Params[0].asdouble); + } + break; + case tp_Message: // wyświetlenie komunikatu + break; + } // switch (tmpEvent->Type) + } // if (tmpEvent->bEnabled) + } // while + return true; +} + +// legacy method, initializes events after deserialization from scenario file +void +event_manager::InitEvents() { + + //łączenie eventów z pozostałymi obiektami + for( auto *Current : m_events ) { + + switch (Current->Type) { + + case tp_AddValues: // sumowanie wartości + case tp_UpdateValues: { // zmiana wartości + auto *cell = simulation::Memory.find( Current->asNodeName ); // nazwa komórki powiązanej z eventem + if( cell ) { // McZapkie-100302 + if( Current->iFlags & ( conditional_trackoccupied | conditional_trackfree ) ) { + // jeśli chodzi o zajetosc toru (tor może być inny, niż wpisany w komórce) + // nazwa toru ta sama, co nazwa komórki + Current->Params[ 9 ].asTrack = simulation::Paths.find( Current->asNodeName ); + if( Current->Params[ 9 ].asTrack == nullptr ) { + ErrorLog( "Bad event: track \"" + Current->asNodeName + "\" referenced in event \"" + Current->asName + "\" doesn't exist" ); + } + } + Current->Params[ 4 ].asLocation = &( cell->location() ); + Current->Params[ 5 ].asMemCell = cell; // komórka do aktualizacji + if( Current->iFlags & ( conditional_memcompare ) ) { + // komórka do badania warunku + Current->Params[ 9 ].asMemCell = cell; + } + if( false == cell->asTrackName.empty() ) { + // tor powiązany z komórką powiązaną z eventem + // tu potrzebujemy wskaźnik do komórki w (tmp) + Current->Params[ 6 ].asTrack = simulation::Paths.find( cell->asTrackName ); + if( Current->Params[ 6 ].asTrack == nullptr ) { + ErrorLog( "Bad memcell: track \"" + cell->asTrackName + "\" referenced in memcell \"" + cell->name() + "\" doesn't exist" ); + } + } + else { + Current->Params[ 6 ].asTrack = nullptr; + } + } + else { + // nie ma komórki, to nie będzie działał poprawnie + Current->Type = tp_Ignored; // deaktywacja + ErrorLog( "Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\"" ); + } + break; + } + case tp_LogValues: { + // skojarzenie z memcell + if( Current->asNodeName.empty() ) { // brak skojarzenia daje logowanie wszystkich + Current->Params[ 9 ].asMemCell = nullptr; + break; + } + } + case tp_GetValues: + case tp_WhoIs: { + auto *cell = simulation::Memory.find( Current->asNodeName ); + if( cell ) { + Current->Params[ 8 ].asLocation = &( cell->location() ); + Current->Params[ 9 ].asMemCell = cell; + if( ( Current->Type == tp_GetValues ) + && ( cell->IsVelocity() ) ) { + // jeśli odczyt komórki a komórka zawiera komendę SetVelocity albo ShuntVelocity + // to event nie będzie dodawany do kolejki + Current->bEnabled = false; + } + } + else { + // nie ma komórki, to nie będzie działał poprawnie + Current->Type = tp_Ignored; // deaktywacja + ErrorLog( "Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\"" ); + } + break; + } + case tp_CopyValues: { + // skopiowanie komórki do innej + auto *cell = simulation::Memory.find( Current->asNodeName ); // komórka docelowa + if( cell ) { + Current->Params[ 4 ].asLocation = &( cell->location() ); + Current->Params[ 5 ].asMemCell = cell; // komórka docelowa + if( false == cell->asTrackName.empty() ) { + // tor powiązany z komórką powiązaną z eventem + // tu potrzebujemy wskaźnik do komórki w (tmp) + Current->Params[ 6 ].asTrack = simulation::Paths.find( cell->asTrackName ); + if( Current->Params[ 6 ].asTrack == nullptr ) { + ErrorLog( "Bad memcell: track \"" + cell->asTrackName + "\" referenced in memcell \"" + cell->name() + "\" doesn't exists" ); + } + } + else { + Current->Params[ 6 ].asTrack = nullptr; + } + } + else { + ErrorLog( "Bad event: copyvalues event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\"" ); + } + std::string const cellastext { Current->Params[ 9 ].asText }; + cell = simulation::Memory.find( Current->Params[ 9 ].asText ); // komórka źódłowa + SafeDeleteArray( Current->Params[ 9 ].asText ); // usunięcie nazwy komórki + if( cell != nullptr ) { + Current->Params[ 8 ].asLocation = &( cell->location() ); + Current->Params[ 9 ].asMemCell = cell; // komórka źródłowa + } + else { + ErrorLog( "Bad event: copyvalues event \"" + Current->asName + "\" cannot find memcell \"" + cellastext + "\"" ); + } + break; + } + case tp_Animation: { + // animacja modelu + // retrieve target name parameter + std::string const cellastext = Current->Params[ 9 ].asText; + SafeDeleteArray( Current->Params[ 9 ].asText ); + // egzemplarz modelu do animowania + auto *instance = simulation::Instances.find( Current->asNodeName ); + if( instance ) { + if( Current->Params[ 0 ].asInt == 4 ) { + // model dla całomodelowych animacji + Current->Params[ 9 ].asModel = instance; + } + else { + // standardowo przypisanie submodelu + Current->Params[ 9 ].asAnimContainer = instance->GetContainer( cellastext ); // submodel + if( Current->Params[ 9 ].asAnimContainer ) { + Current->Params[ 9 ].asAnimContainer->WillBeAnimated(); // oflagowanie animacji + if( Current->Params[ 9 ].asAnimContainer->Event() == nullptr ) { + // nie szukać, gdy znaleziony + Current->Params[ 9 ].asAnimContainer->EventAssign( + FindEvent( Current->asNodeName + "." + cellastext + ":done" ) ); + } + } + } + } + else { + ErrorLog( "Bad event: animation event \"" + Current->asName + "\" cannot find model instance \"" + Current->asNodeName + "\"" ); + } + Current->asNodeName = ""; + break; + } + case tp_Lights: { + // zmiana świeteł modelu + auto *instance = simulation::Instances.find( Current->asNodeName ); + if( instance ) + Current->Params[ 9 ].asModel = instance; + else + ErrorLog( "Bad event: lights event \"" + Current->asName + "\" cannot find model instance \"" + Current->asNodeName + "\"" ); + Current->asNodeName = ""; + break; + } +/* + case tp_Visible: { + // ukrycie albo przywrócenie obiektu + tmp = FindGroundNode( Current->asNodeName, TP_MODEL ); // najpierw model + if( !tmp ) + tmp = FindGroundNode( Current->asNodeName, TP_TRACK ); // albo tory? + if( !tmp ) + tmp = FindGroundNode( Current->asNodeName, TP_TRACTION ); // może druty? + if( tmp ) + Current->Params[ 9 ].nGroundNode = tmp; + else + ErrorLog( "Bad event: visibility event \"" + Current->asName + "\" cannot find item \"" + Current->asNodeName + "\"" ); + Current->asNodeName = ""; + break; + } +*/ + case tp_Switch: { + // przełożenie zwrotnicy albo zmiana stanu obrotnicy + auto *track = simulation::Paths.find( Current->asNodeName ); + if( track ) { + // dowiązanie toru + if( track->iAction == NULL ) { + // jeśli nie jest zwrotnicą ani obrotnicą to będzie się zmieniał stan uszkodzenia + track->iAction |= 0x100; + } + Current->Params[ 9 ].asTrack = track; + if( ( Current->Params[ 0 ].asInt == 0 ) + && ( Current->Params[ 2 ].asdouble >= 0.0 ) ) { + // jeśli przełącza do stanu 0 & jeśli jest zdefiniowany dodatkowy ruch iglic + // przesłanie parametrów + Current->Params[ 9 ].asTrack->Switch( + Current->Params[ 0 ].asInt, + Current->Params[ 1 ].asdouble, + Current->Params[ 2 ].asdouble ); + } + } + else { + ErrorLog( "Bad event: switch event \"" + Current->asName + "\" cannot find track \"" + Current->asNodeName + "\"" ); + } + Current->asNodeName = ""; + break; + } +/* + case tp_Sound: { + // odtworzenie dźwięku + tmp = FindGroundNode( Current->asNodeName, TP_SOUND ); + if( tmp ) + Current->Params[ 9 ].tsTextSound = tmp->tsStaticSound; + else + ErrorLog( "Bad event: sound event \"" + Current->asName + "\" cannot find static sound \"" + Current->asNodeName + "\"" ); + Current->asNodeName = ""; + break; + } +*/ + case tp_TrackVel: { + // ustawienie prędkości na torze + if( false == Current->asNodeName.empty() ) { + auto *track = simulation::Paths.find( Current->asNodeName ); + if( track ) { + // flaga zmiany prędkości toru jest istotna dla skanowania + track->iAction |= 0x200; + Current->Params[ 9 ].asTrack = track; + } + else { + ErrorLog( "Bad event: track velocity event \"" + Current->asName + "\" cannot find track \"" + Current->asNodeName + "\"" ); + } + } + Current->asNodeName = ""; + break; + } +/* + case tp_DynVel: { + // komunikacja z pojazdem o konkretnej nazwie + if( Current->asNodeName == "activator" ) + Current->Params[ 9 ].asDynamic = nullptr; + else { + tmp = FindGroundNode( Current->asNodeName, TP_DYNAMIC ); + if( tmp ) + Current->Params[ 9 ].asDynamic = tmp->DynamicObject; + else + Error( "Bad event: vehicle velocity event \"" + Current->asName + "\" cannot find vehicle \"" + Current->asNodeName + "\"" ); + } + Current->asNodeName = ""; + break; + } +*/ + case tp_Multiple: { + std::string cellastext; + if( Current->Params[ 9 ].asText != nullptr ) { // przepisanie nazwy do bufora + cellastext = Current->Params[ 9 ].asText; + SafeDeleteArray( Current->Params[ 9 ].asText ); + Current->Params[ 9 ].asPointer = nullptr; // zerowanie wskaźnika, aby wykryć brak obeiktu + } + if( Current->iFlags & ( conditional_trackoccupied | conditional_trackfree ) ) { + // jeśli chodzi o zajetosc toru + Current->Params[ 9 ].asTrack = simulation::Paths.find( cellastext ); + if( Current->Params[ 9 ].asTrack == nullptr ) { + ErrorLog( "Bad event: multi-event \"" + Current->asName + "\" cannot find track \"" + cellastext + "\"" ); + Current->iFlags &= ~( conditional_trackoccupied | conditional_trackfree ); // zerowanie flag + } + } + else if( Current->iFlags & ( conditional_memstring | conditional_memval1 | conditional_memval2 ) ) { + // jeśli chodzi o komorke pamieciową + Current->Params[ 9 ].asMemCell = simulation::Memory.find( cellastext ); + if( Current->Params[ 9 ].asMemCell == nullptr ) { + ErrorLog( "Bad event: multi-event \"" + Current->asName + "\" cannot find memory cell \"" + cellastext + "\"" ); + Current->iFlags &= ~( conditional_memstring | conditional_memval1 | conditional_memval2 ); + } + } + for( int i = 0; i < 8; ++i ) { + if( Current->Params[ i ].asText != nullptr ) { + cellastext = Current->Params[ i ].asText; + SafeDeleteArray( Current->Params[ i ].asText ); + Current->Params[ i ].asEvent = FindEvent( cellastext ); + if( Current->Params[ i ].asEvent == nullptr ) { + // Ra: tylko w logu informacja o braku + ErrorLog( "Bad event: multi-event \"" + Current->asName + "\" cannot find event \"" + cellastext + "\"" ); + } + } + } + break; + } +/* + case tp_Voltage: { + // zmiana napięcia w zasilaczu (TractionPowerSource) + if( !Current->asNodeName.empty() ) { + tmp = FindGroundNode( Current->asNodeName, TP_TRACTIONPOWERSOURCE ); // podłączenie zasilacza + if( tmp ) + Current->Params[ 9 ].psPower = tmp->psTractionPowerSource; + else + ErrorLog( "Bad event: voltage event \"" + Current->asName + "\" cannot find power source \"" + Current->asNodeName + "\"" ); + } + Current->asNodeName = ""; + break; + } +*/ + case tp_Message: { + // wyświetlenie komunikatu + break; + } + + } // switch + if( Current->fDelay < 0 ) { + AddToQuery( Current, nullptr ); + } + } +/* + for (TGroundNode *Current = nRootOfType[TP_MEMCELL]; Current; Current = Current->nNext) + { // Ra: eventy komórek pamięci, wykonywane po wysłaniu komendy do zatrzymanego pojazdu + Current->MemCell->AssignEvents( FindEvent( Current->asName + ":sent" ) ); + } +*/ +} + +// legacy method, verifies condition for specified event +bool +event_manager::EventConditon( TEvent *Event ) { + + if (Event->iFlags <= update_only) + return true; // bezwarunkowo + + if (Event->iFlags & conditional_trackoccupied) + return (!Event->Params[9].asTrack->IsEmpty()); + else if (Event->iFlags & conditional_trackfree) + return (Event->Params[9].asTrack->IsEmpty()); + else if (Event->iFlags & conditional_propability) + { + double rprobability = Random(); + WriteLog( "Random integer: " + std::to_string( rprobability ) + " / " + std::to_string( Event->Params[ 10 ].asdouble ) ); + return (Event->Params[10].asdouble > rprobability); + } + else if( Event->iFlags & conditional_memcompare ) { + // porównanie wartości + if( nullptr == Event->Params[9].asMemCell ) { + + ErrorLog( "Event " + Event->asName + " trying conditional_memcompare with nonexistent memcell" ); + return true; // though this is technically error, we report success to maintain backward compatibility + } + auto const comparisonresult = + m_workevent->Params[ 9 ].asMemCell->Compare( + ( Event->Params[ 10 ].asText != nullptr ? + Event->Params[ 10 ].asText : + "" ), + Event->Params[ 11 ].asdouble, + Event->Params[ 12 ].asdouble, + Event->iFlags ); + + std::string comparisonlog = "Type: MemCompare - "; + + comparisonlog += + "[" + Event->Params[ 9 ].asMemCell->Text() + "]" + + " [" + to_string( Event->Params[ 9 ].asMemCell->Value1(), 2 ) + "]" + + " [" + to_string( m_workevent->Params[ 9 ].asMemCell->Value2(), 2 ) + "]"; + + comparisonlog += ( + true == comparisonresult ? + " == " : + " != " ); + + comparisonlog += ( + TestFlag( Event->iFlags, conditional_memstring ) ? + "[" + std::string( m_workevent->Params[ 10 ].asText ) + "]" : + "[*]" ); + comparisonlog += ( + TestFlag( m_workevent->iFlags, conditional_memval1 ) ? + " [" + to_string( m_workevent->Params[ 11 ].asdouble, 2 ) + "]" : + " [*]" ); + comparisonlog += ( + TestFlag( m_workevent->iFlags, conditional_memval2 ) ? + " [" + to_string( m_workevent->Params[ 12 ].asdouble, 2 ) + "]" : + " [*]" ); + + WriteLog( comparisonlog ); + return comparisonresult; + } + // unrecognized request + return false; +} diff --git a/Event.h b/Event.h index 663efef3..219441d2 100644 --- a/Event.h +++ b/Event.h @@ -11,9 +11,7 @@ http://mozilla.org/MPL/2.0/. #include #include "dumb3d.h" -#include "Classes.h" - -using namespace Math3D; +#include "classes.h" enum TEventType { tp_Unknown, @@ -63,6 +61,7 @@ union TParam void *asPointer; TMemCell *asMemCell; TGroundNode *nGroundNode; + glm::dvec3 const *asLocation; TTrack *asTrack; TAnimModel *asModel; TAnimContainer *asAnimContainer; @@ -87,9 +86,10 @@ class TEvent // zmienne: ev* std::string asName; 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 +#ifdef EU07_USE_OLD_GROUNDCODE TEvent *evNext2 = nullptr; +#endif TEventType Type = tp_Unknown; double fStartTime = 0.0; double fDelay = 0.0; @@ -103,15 +103,61 @@ class TEvent // zmienne: ev* 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 provided event to the collection. returns: true on success + // TODO: return handle instead of pointer + bool + insert( TEvent *Event ); + // 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(); + +private: +// types + using event_sequence = std::deque; + using event_map = std::unordered_map; + +// 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; +}; + //--------------------------------------------------------------------------- diff --git a/Globals.cpp b/Globals.cpp index 5702dc55..fea1cb39 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -120,7 +120,7 @@ float Global::AnisotropicFiltering = 8.0f; // requested level of anisotropic fil bool Global::bUseVBO = true; // czy jest VBO w karcie graficznej (czy użyć) 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 @@ -1008,7 +1008,11 @@ TDynamicObject *Global::CouplerNearest() bool Global::AddToQuery(TEvent *event, TDynamicObject *who) { +#ifdef EU07_USE_OLD_GROUNDCODE return pGround->AddToQuery(event, who); +#else + return simulation::Events.AddToQuery( event, who ); +#endif }; //--------------------------------------------------------------------------- diff --git a/Ground.cpp b/Ground.cpp index 2cbe7e31..f415dab9 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -20,7 +20,6 @@ http://mozilla.org/MPL/2.0/. #include "Logs.h" #include "usefull.h" #include "Timer.h" -#include "renderer.h" #include "Event.h" #include "EvLaunch.h" #include "TractionPower.h" @@ -34,9 +33,6 @@ http://mozilla.org/MPL/2.0/. #include "Data.h" #include "parser.h" //Tolaris-010603 #include "Driver.h" -#include "Console.h" -#include "Names.h" -#include "world.h" #include "uilayer.h" //--------------------------------------------------------------------------- @@ -47,7 +43,6 @@ extern "C" } bool bCondition; // McZapkie: do testowania warunku na event multiple -std::string LogComment; //--------------------------------------------------------------------------- // Obiekt renderujący siatkę jest sztucznie tworzonym obiektem pomocniczym, @@ -123,6 +118,8 @@ TGroundNode::~TGroundNode() case GL_TRIANGLES: SafeDelete( Piece ); break; + default: + break; } } /* @@ -144,7 +141,7 @@ TGroundNode::TGroundNode( TGroundNodeType t ) : break; } case TP_TRACK: { - pTrack = new TTrack( this ); + pTrack = new TTrack( asName ); break; } default: { @@ -314,9 +311,8 @@ void TSubRect::NodeAdd(TGroundNode *Node) // przygotowanie sektora do renderowania void TSubRect::Sort() { - if( tTracks != nullptr ) { - SafeDelete( tTracks ); - } + SafeDeleteArray( tTracks ); + if( iTracks > 0 ) { tTracks = new TTrack *[ iTracks ]; // tworzenie tabeli torów do renderowania pojazdów int i = 0; @@ -414,12 +410,20 @@ void TSubRect::LoadNodes() { break; } case TP_TRACK: - if( node->pTrack->bVisible ) { // bo tory zabezpieczające są niewidoczne + if( node->pTrack->visible() ) { // bo tory zabezpieczające są niewidoczne +#ifdef EU07_USE_OLD_GROUNDCODE + node->pTrack->create_geometry( m_geometrybank, node->m_rootposition ); +#else node->pTrack->create_geometry( m_geometrybank ); +#endif } break; case TP_TRACTION: +#ifdef EU07_USE_OLD_GROUNDCODE node->hvTraction->create_geometry( m_geometrybank, node->m_rootposition ); +#else + node->hvTraction->create_geometry( m_geometrybank ); +#endif break; default: { break; } } @@ -448,16 +452,16 @@ TGroundRect::Init() { } pSubRects = new TSubRect[ iNumSubRects * iNumSubRects ]; - float const subrectsize = 1000.0f / iNumSubRects; + auto const subrectsize = 1000.0 / iNumSubRects; for( int column = 0; column < iNumSubRects; ++column ) { for( int row = 0; row < iNumSubRects; ++row ) { auto subcell = FastGetSubRect( column, row ); auto &area = subcell->m_area; area.center = m_area.center - - glm::vec3( 500.0f, 0.0f, 500.0f ) // 'upper left' corner of rectangle - + glm::vec3( subrectsize * 0.5f, 0.0f, subrectsize * 0.5f ) // center of sub-rectangle - + glm::vec3( subrectsize * column, 0.0f, subrectsize * row ); + - glm::dvec3( 500.0, 0.0, 500.0 ) // 'upper left' corner of rectangle + + glm::dvec3( subrectsize * 0.5, 0.0, subrectsize * 0.5 ) // center of sub-rectangle + + glm::dvec3( subrectsize * column, 0.0, subrectsize * row ); area.radius = subrectsize * M_SQRT2; // all subcells share the same geometry bank with their parent, to reduce buffer switching during render subcell->m_geometrybank = m_geometrybank; @@ -507,7 +511,7 @@ TGroundRect::NodeAdd( TGroundNode *Node ) { // compares two provided nodes, returns true if their content can be merged bool -TGroundRect::mergeable( TGroundNode const &Left, TGroundNode const &Right ) { +TGroundRect::mergeable( TGroundNode const &Left, TGroundNode const &Right ) const { // since view ranges and transparency type for all nodes put through this method are guaranteed to be equal, // we can skip their tests and only do the material check. // TODO, TBD: material as dedicated type, and refactor this method into a simple equality test @@ -553,6 +557,7 @@ TGround::~TGround() void TGround::Free() { +#ifdef EU07_USE_OLD_GROUNDCODE TEvent *tmp; for (TEvent *Current = RootEvent; Current;) { @@ -560,6 +565,7 @@ void TGround::Free() Current = Current->evNext2; delete tmp; } +#endif TGroundNode *tmpn; for (int i = 0; i < TP_LAST; ++i) { @@ -598,7 +604,8 @@ TGroundNode * TGround::DynamicFind(std::string const &Name) return NULL; }; -void TGround::DynamicList(bool all) +void +TGround::DynamicList(bool all) { // odesłanie nazw pojazdów dostępnych na scenerii (nazwy, szczególnie wagonów, mogą się // powtarzać!) for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext) @@ -607,45 +614,23 @@ void TGround::DynamicList(bool all) WyslijString("none", 6); // informacja o końcu listy }; -TGroundNode * TGround::FindGroundNode(std::string asNameToFind, TGroundNodeType iNodeType) -{ // wyszukiwanie obiektu o podanej nazwie i konkretnym typie - if ((iNodeType == TP_TRACK) || (iNodeType == TP_MEMCELL) || (iNodeType == TP_MODEL)) - { // wyszukiwanie w drzewie binarnym -/* - switch( iNodeType ) { +// wyszukiwanie obiektu o podanej nazwie i konkretnym typie +TGroundNode * +TGround::FindGroundNode(std::string const &asNameToFind, TGroundNodeType const iNodeType) { - case TP_TRACK: { - auto const lookup = m_trackmap.find( asNameToFind ); - return - lookup != m_trackmap.end() ? - lookup->second : - nullptr; - } - case TP_MODEL: { - auto const lookup = m_modelmap.find( asNameToFind ); - return - lookup != m_modelmap.end() ? - lookup->second : - nullptr; - } - case TP_MEMCELL: { - auto const lookup = m_memcellmap.find( asNameToFind ); - return - lookup != m_memcellmap.end() ? - lookup->second : - nullptr; - } - } - return nullptr; -*/ - return m_trackmap.Find( iNodeType, asNameToFind ); + if( ( iNodeType == TP_TRACK ) + || ( iNodeType == TP_MEMCELL ) + || ( iNodeType == TP_MODEL ) ) { + // wyszukiwanie w drzewie binarnym + return m_nodemap.Find( iNodeType, asNameToFind ); } // standardowe wyszukiwanie liniowe - TGroundNode *Current; - for (Current = nRootOfType[iNodeType]; Current; Current = Current->nNext) - if (Current->asName == asNameToFind) + for( TGroundNode *Current = nRootOfType[ iNodeType ]; Current != nullptr; Current = Current->nNext ) { + if( Current->asName == asNameToFind ) { return Current; - return NULL; + } + } + return nullptr; } TGroundRect * @@ -905,7 +890,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) switch (tmp->iType) { case TP_TRACTION: - tmp->hvTraction = new TTraction(); + tmp->hvTraction = new TTraction( tmp->asName ); parser->getTokens(); *parser >> token; tmp->hvTraction->asPowerSupplyName = token; // nazwa zasilacza @@ -989,8 +974,6 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) if (token.compare("endtraction") != 0) Error("ENDTRACTION delimiter missing! " + str2 + " found instead."); tmp->hvTraction->Init(); // przeliczenie parametrów - // if (Global::bLoadTraction) - // tmp->hvTraction->Optimize(); //generowanie DL dla wszystkiego przy wczytywaniu? tmp->pCenter = interpolate( tmp->hvTraction->pPoint2, tmp->hvTraction->pPoint1, 0.5 ); break; case TP_TRACTIONPOWERSOURCE: @@ -1015,7 +998,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) tmp->MemCell->Load(parser); if (!tmp->asName.empty()) // jest pusta gdy "none" { // dodanie do wyszukiwarki - if( false == m_trackmap.Add( TP_MEMCELL, tmp->asName, tmp ) ) { + if( false == m_nodemap.Add( TP_MEMCELL, tmp->asName, tmp ) ) { // przy zdublowaniu wskaźnik zostanie podmieniony w drzewku na późniejszy (zgodność wsteczna) ErrorLog( "Duplicated memcell: " + tmp->asName ); // to zgłaszać duplikat } @@ -1033,14 +1016,14 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) tmp->EvLaunch->Load(parser); break; case TP_TRACK: - tmp->pTrack = new TTrack(tmp); + tmp->pTrack = new TTrack( tmp->asName ); if (Global::iWriteLogEnabled & 4) if (!tmp->asName.empty()) WriteLog(tmp->asName); - tmp->pTrack->Load(parser, pOrigin, tmp->asName); // w nazwie może być nazwa odcinka izolowanego + tmp->pTrack->Load(parser, pOrigin); // w nazwie może być nazwa odcinka izolowanego if (!tmp->asName.empty()) // jest pusta gdy "none" { // dodanie do wyszukiwarki - if( false == m_trackmap.Add( TP_TRACK, tmp->asName, tmp ) ) { + if( false == m_nodemap.Add( TP_TRACK, tmp->asName, tmp ) ) { // przy zdublowaniu wskaźnik zostanie podmieniony w drzewku na późniejszy (zgodność wsteczna) ErrorLog( "Duplicated track: " + tmp->asName ); // to zgłaszać duplikat } @@ -1148,22 +1131,18 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) if (fTrainSetDist < 8.0) // i raczej nie sięga fTrainSetDist = 8.0; // przesuwamy około pół EU07 dla wstecznej zgodności - // WriteLog("Dynamic shift: "+AnsiString(fTrainSetDist)); - /* //Ra: to jednak robi duże problemy - przesunięcie w dynamic jest przesunięciem do - tyłu, odwrotnie niż w trainset - if (!iTrainSetWehicleNumber) //dla pierwszego jest to przesunięcie (ujemne = do - tyłu) - if (tf1!=-1.0) //-1 wyjątkowo oznacza odwrócenie - tf1=-tf1; //a dla kolejnych odległość między sprzęgami (ujemne = wbite) - */ - tf3 = tmp->DynamicObject->Init(asNodeName, str1, Skin, str3, Track, - (tf1 == -1.0 ? fTrainSetDist : fTrainSetDist - tf1), - DriverType, tf3, asTrainName, int2, str2, (tf1 == -1.0), - str4); - if (tf3 != 0.0) // zero oznacza błąd + + auto const length = + tmp->DynamicObject->Init( + asNodeName, str1, Skin, str3, Track, + ( tf1 == -1.0 ? + fTrainSetDist : + fTrainSetDist - tf1 ), + DriverType, tf3, asTrainName, int2, str2, (tf1 == -1.0), str4); + if (length != 0.0) // zero oznacza błąd { // przesunięcie dla kolejnego, minus bo idziemy w stronę punktu 1 - fTrainSetDist -= tf3; + fTrainSetDist -= length; tmp->pCenter = tmp->DynamicObject->GetPosition(); // automatically establish permanent connections for couplers which specify them in their definitions if( TempConnectionType[ iTrainSetWehicleNumber ] ) { @@ -1208,7 +1187,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) // 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 - m_lights.insert( tmp->DynamicObject ); + simulation::Lights.insert( tmp->DynamicObject ); } break; @@ -1257,7 +1236,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } else if( !tmp->asName.empty() ) // jest pusta gdy "none" { // dodanie do wyszukiwarki - if( false == m_trackmap.Add( TP_MODEL, tmp->asName, tmp ) ) { + if( false == m_nodemap.Add( TP_MODEL, tmp->asName, tmp ) ) { // przy zdublowaniu wskaźnik zostanie podmieniony w drzewku na późniejszy (zgodność wsteczna) ErrorLog( "Duplicated model: " + tmp->asName ); // to zgłaszać duplikat } @@ -1310,7 +1289,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) tmp->iFlags = 0x200; // flaga usuwania tmp->Model = new TAnimModel(); - tmp->Model->RaAnglesSet( aRotate.x, tf1 + aRotate.y, aRotate.z ); // dostosowanie do pochylania linii + tmp->Model->RaAnglesSet( glm::vec3{ aRotate.x, tf1 + aRotate.y, aRotate.z } ); // dostosowanie do pochylania linii if( false == tmp->Model->Load( parser, false ) ) { // model nie wczytał się - ignorowanie node delete tmp; @@ -1320,7 +1299,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) tmp->iFlags |= tmp->Model->Flags(); // ustalenie, czy przezroczysty if( false == tmp->asName.empty() ) { // jest pusta gdy "none" // dodanie do wyszukiwarki - if( false == m_trackmap.Add( TP_MODEL, tmp->asName, tmp ) ) { + if( false == m_nodemap.Add( TP_MODEL, tmp->asName, tmp ) ) { // przy zdublowaniu wskaźnik zostanie podmieniony w drzewku na późniejszy (zgodność wsteczna) ErrorLog( "Duplicated model: " + tmp->asName ); // to zgłaszać duplikat } @@ -1652,6 +1631,7 @@ TSubRect * TGround::GetSubRect(int iCol, int iRow) return (Rects[bc][br].SafeGetSubRect(sc, sr)); // pobranie małego kwadratu } +#ifdef EU07_USE_OLD_GROUNDCODE TEvent * TGround::FindEvent(const std::string &asEventName) { if( asEventName.empty() ) { return nullptr; } @@ -1662,24 +1642,7 @@ TEvent * TGround::FindEvent(const std::string &asEventName) lookup->second : nullptr ); } - -TEvent * TGround::FindEventScan( std::string const &asEventName ) -{ // wyszukanie eventu z opcją utworzenia niejawnego dla komórek skanowanych - auto const lookup = m_eventmap.find( asEventName ); - auto e = ( - lookup != m_eventmap.end() ? - lookup->second : - nullptr ); - if (e) - return e; // jak istnieje, to w porządku - if (asEventName.rfind(":scan") != std::string::npos) // jeszcze może być event niejawny - { // no to szukamy komórki pamięci o nazwie zawartej w evencie - std::string n = asEventName.substr(0, asEventName.length() - 5); // do dwukropka - if( m_trackmap.Find( TP_MEMCELL, n ) != nullptr ) // jeśli jest takowa komórka pamięci - e = new TEvent(n); // utworzenie niejawnego eventu jej odczytu - } - return e; // utworzony albo się nie udało -} +#endif void TGround::FirstInit() { // ustalanie zależności na scenerii przed wczytaniem pojazdów @@ -1747,14 +1710,14 @@ void TGround::FirstInit() WriteLog("InitNormals OK"); +#ifdef EU07_USE_OLD_GROUNDCODE InitTracks(); //łączenie odcinków ze sobą i przyklejanie eventów WriteLog("InitTracks OK"); - InitTraction(); //łączenie drutów ze sobą WriteLog("InitTraction OK"); - InitEvents(); - WriteLog("InitEvents OK"); + WriteLog( "InitEvents OK" ); +#endif InitLaunchers(); WriteLog("InitLaunchers OK"); @@ -1919,8 +1882,9 @@ bool TGround::Init(std::string File) } else if (str == "event") { +#ifdef EU07_USE_OLD_GROUNDCODE TEvent *tmp = new TEvent(); - tmp->Load(&parser, &pOrigin); + tmp->Load(&parser, pOrigin); if (tmp->Type == tp_Unknown) delete tmp; else @@ -1983,6 +1947,7 @@ bool TGround::Init(std::string File) } } } +#endif } else if (str == "rotate") { @@ -2211,7 +2176,7 @@ bool TGround::Init(std::string File) Global::iPause &= ~0x10; // koniec pauzy wczytywania return true; } - +#ifdef EU07_USE_OLD_GROUNDCODE bool TGround::InitEvents() { //łączenie eventów z pozostałymi obiektami TGroundNode *tmp, *trk; @@ -2346,14 +2311,14 @@ bool TGround::InitEvents() break; case tp_Visible: // ukrycie albo przywrócenie obiektu tmp = FindGroundNode(Current->asNodeName, TP_MODEL); // najpierw model - if (!tmp) - tmp = FindGroundNode(Current->asNodeName, TP_TRACTION); // może druty? if (!tmp) tmp = FindGroundNode(Current->asNodeName, TP_TRACK); // albo tory? + if (!tmp) + tmp = FindGroundNode(Current->asNodeName, TP_TRACTION); // może druty? if (tmp) Current->Params[9].nGroundNode = tmp; else - ErrorLog("Bad event: visibility event \"" + Current->asName + "\" cannot find model \"" + Current->asNodeName + "\""); + ErrorLog("Bad event: visibility event \"" + Current->asName + "\" cannot find item \"" + Current->asNodeName + "\""); Current->asNodeName = ""; break; case tp_Switch: // przełożenie zwrotnicy albo zmiana stanu obrotnicy @@ -2490,7 +2455,6 @@ void TGround::InitTracks() int iConnection; for (Current = nRootOfType[TP_TRACK]; Current; Current = Current->nNext) { - Track = Current->pTrack; // assign track events Track->AssignEvents( @@ -2513,7 +2477,6 @@ void TGround::InitTracks() FindEvent( Current->asName + ":eventall1" ), FindEvent( Current->asName + ":eventall2" ) ); } - switch (Track->eType) { case tt_Table: // obrotnicę też łączymy na starcie z innymi torami @@ -2523,8 +2486,8 @@ void TGround::InitTracks() // Track->ModelAssign(tmp->Model->GetContainer(NULL)); //wiązanie toru z modelem // obrotnicy Track->RaAssign( - Current, Model ? Model->Model : NULL, FindEvent(Current->asName + ":done"), - FindEvent(Current->asName + ":joined")); // wiązanie toru z modelem obrotnicy + Current, Model ? Model->Model : NULL, simulation::Events.FindEvent(Current->asName + ":done"), + simulation::Events.FindEvent(Current->asName + ":joined")); // wiązanie toru z modelem obrotnicy // break; //jednak połączę z sąsiednim, jak ma się wysypywać null track } if (!Model) // jak nie ma modelu @@ -2607,15 +2570,18 @@ void TGround::InitTracks() } break; case tt_Switch: // dla rozjazdów szukamy eventów sygnalizacji rozprucia - Track->AssignForcedEvents(FindEvent(Current->asName + ":forced+"), - FindEvent(Current->asName + ":forced-")); + Track->AssignForcedEvents( + FindEvent( Current->asName + ":forced+" ), + FindEvent( Current->asName + ":forced-" ) ); break; } std::string const name = Track->IsolatedName(); // pobranie nazwy odcinka izolowanego - if (!name.empty()) // jeśli została zwrócona nazwa - Track->IsolatedEventsAssign(FindEvent(name + ":busy"), FindEvent(name + ":free")); - if (Current->asName.substr(0, 1) == - "*") // możliwy portal, jeśli nie podłączony od striny 1 + if( !name.empty() ) // jeśli została zwrócona nazwa + Track->IsolatedEventsAssign( + FindEvent( name + ":busy" ), + FindEvent( name + ":free" ) ); + // możliwy portal, jeśli nie podłączony od striny 1 + if (Current->asName.substr(0, 1) == "*") if (!Track->CurrentPrev() && Track->CurrentNext()) Track->iCategoryFlag |= 0x100; // ustawienie flagi portalu } @@ -2631,7 +2597,7 @@ void TGround::InitTracks() Current = new TGroundNode(); // to nie musi mieć nazwy, nazwa w wyszukiwarce wystarczy // Current->asName=p->asName; //mazwa identyczna, jak nazwa odcinka izolowanego Current->MemCell = new TMemCell(NULL); // nowa komórka - m_trackmap.Add( TP_MEMCELL, p->asName, Current ); + m_nodemap.Add( TP_MEMCELL, p->asName, Current ); Current->nNext = nRootOfType[TP_MEMCELL]; // to nie powinno tutaj być, bo robi się śmietnik nRootOfType[TP_MEMCELL] = Current; @@ -2639,9 +2605,6 @@ void TGround::InitTracks() } p = p->Next(); } - // for (Current=nRootOfType[TP_TRACK];Current;Current=Current->nNext) - // if (Current->pTrack->eType==tt_Cross) - // Current->pTrack->ConnectionsLog(); //zalogowanie informacji o połączeniach } void TGround::InitTraction() @@ -2849,7 +2812,7 @@ void TGround::InitTraction() } delete[] nEnds; // nie potrzebne już }; - +#endif void TGround::TrackJoin(TGroundNode *Current) { // wyszukiwanie sąsiednich torów do podłączenia (wydzielone na użytek obrotnicy) TTrack *Track = Current->pTrack; @@ -2903,13 +2866,13 @@ bool TGround::InitLaunchers() MessageBox(0, "Cannot find Memory Cell for Event Launcher", "Error", MB_OK); } else - EventLauncher->MemCell = NULL; - EventLauncher->Event1 = (EventLauncher->asEvent1Name != "none") ? - FindEvent(EventLauncher->asEvent1Name) : - NULL; - EventLauncher->Event2 = (EventLauncher->asEvent2Name != "none") ? - FindEvent(EventLauncher->asEvent2Name) : - NULL; + EventLauncher->MemCell = nullptr; + EventLauncher->Event1 = ( EventLauncher->asEvent1Name != "none") ? + simulation::Events.FindEvent(EventLauncher->asEvent1Name) : + nullptr; + EventLauncher->Event2 = ( EventLauncher->asEvent2Name != "none") ? + simulation::Events.FindEvent(EventLauncher->asEvent2Name) : + nullptr; } return true; } @@ -3039,6 +3002,7 @@ TTraction * TGround::TractionNearestFind(glm::dvec3 &p, int dir, TGroundNode *n) return (nBest ? nBest->hvTraction : nullptr); }; +#ifdef EU07_USE_OLD_GROUNDCODE bool TGround::AddToQuery(TEvent *Event, TDynamicObject *Node) { if( Event->bEnabled ) { @@ -3107,8 +3071,9 @@ bool TGround::AddToQuery(TEvent *Event, TDynamicObject *Node) return true; } -bool TGround::EventConditon(TEvent *e) -{ // sprawdzenie spelnienia warunków dla eventu +// sprawdzenie spelnienia warunków dla eventu +bool TGround::EventConditon(TEvent *e){ + if (e->iFlags <= update_only) return true; // bezwarunkowo @@ -3122,56 +3087,51 @@ bool TGround::EventConditon(TEvent *e) WriteLog("Random integer: " + std::to_string(rprobability) + " / " + std::to_string(e->Params[10].asdouble)); return (e->Params[10].asdouble > rprobability); } - else if (e->iFlags & conditional_memcompare) - { // porównanie wartości + else if( e->iFlags & conditional_memcompare ) { + // porównanie wartości if( nullptr == e->Params[9].asMemCell ) { ErrorLog( "Event " + e->asName + " trying conditional_memcompare with nonexistent memcell" ); return true; // though this is technically error, we report success to maintain backward compatibility } - if (tmpEvent->Params[9].asMemCell->Compare( ( e->Params[ 10 ].asText != nullptr ? e->Params[10].asText : "" ), e->Params[11].asdouble, e->Params[12].asdouble, e->iFlags) ) { - //logowanie spełnionych warunków - LogComment = e->Params[9].asMemCell->Text() + " " + - to_string(e->Params[9].asMemCell->Value1(), 2, 8) + " " + - to_string(tmpEvent->Params[9].asMemCell->Value2(), 2, 8) + - " = "; - if (TestFlag(e->iFlags, conditional_memstring)) - LogComment += std::string(tmpEvent->Params[10].asText); - else - LogComment += "*"; - if (TestFlag(tmpEvent->iFlags, conditional_memval1)) - LogComment += " " + to_string(tmpEvent->Params[11].asdouble, 2, 8); - else - LogComment += " *"; - if (TestFlag(tmpEvent->iFlags, conditional_memval2)) - LogComment += " " + to_string(tmpEvent->Params[12].asdouble, 2, 8); - else - LogComment += " *"; - WriteLog(LogComment); - return true; - } - //else if (Global::iWriteLogEnabled && DebugModeFlag) //zawsze bo to bardzo istotne w debugowaniu scenariuszy - else - { // nie zgadza się, więc sprawdzmy, co - LogComment = e->Params[9].asMemCell->Text() + " " + - to_string(e->Params[9].asMemCell->Value1(), 2, 8) + " " + - to_string(tmpEvent->Params[9].asMemCell->Value2(), 2, 8) + - " != "; - if (TestFlag(e->iFlags, conditional_memstring)) - LogComment += (tmpEvent->Params[10].asText); - else - LogComment += "*"; - if (TestFlag(tmpEvent->iFlags, conditional_memval1)) - LogComment += " " + to_string(tmpEvent->Params[11].asdouble, 2, 8); - else - LogComment += " *"; - if (TestFlag(tmpEvent->iFlags, conditional_memval2)) - LogComment += " " + to_string(tmpEvent->Params[12].asdouble, 2, 8); - else - LogComment += " *"; - WriteLog(LogComment); - } + auto const comparisonresult = + tmpEvent->Params[ 9 ].asMemCell->Compare( + ( e->Params[ 10 ].asText != nullptr ? + e->Params[ 10 ].asText : + "" ), + e->Params[ 11 ].asdouble, + e->Params[ 12 ].asdouble, + e->iFlags ); + + std::string comparisonlog = "Type: MemCompare - "; + + comparisonlog += + "[" + e->Params[ 9 ].asMemCell->Text() + "]" + + " [" + to_string( e->Params[ 9 ].asMemCell->Value1(), 2 ) + "]" + + " [" + to_string( tmpEvent->Params[ 9 ].asMemCell->Value2(), 2 ) + "]"; + + comparisonlog += ( + true == comparisonresult ? + " == " : + " != " ); + + comparisonlog += ( + TestFlag( e->iFlags, conditional_memstring ) ? + "[" + std::string( tmpEvent->Params[ 10 ].asText ) + "]" : + "[*]" ); + comparisonlog += ( + TestFlag( tmpEvent->iFlags, conditional_memval1 ) ? + " [" + to_string( tmpEvent->Params[ 11 ].asdouble, 2 ) + "]" : + " [*]" ); + comparisonlog += ( + TestFlag( tmpEvent->iFlags, conditional_memval2 ) ? + " [" + to_string( tmpEvent->Params[ 12 ].asdouble, 2 ) + "]" : + " [*]" ); + + WriteLog( comparisonlog ); + return comparisonresult; } + // unrecognized request return false; }; @@ -3227,16 +3187,16 @@ bool TGround::CheckQuery() &tmpEvent->Params[ 4 ].nGroundNode->pCenter ); } //if (DebugModeFlag) - WriteLog("Type: UpdateValues & Track command - " + - tmpEvent->Params[5].asMemCell->Text() + " " + - std::to_string( tmpEvent->Params[ 5 ].asMemCell->Value1() ) + " " + - std::to_string( tmpEvent->Params[ 5 ].asMemCell->Value2() ) ); + WriteLog("Type: UpdateValues & Track command - [" + + tmpEvent->Params[5].asMemCell->Text() + "] [" + + to_string( tmpEvent->Params[ 5 ].asMemCell->Value1(), 2 ) + "] [" + + to_string( tmpEvent->Params[ 5 ].asMemCell->Value2(), 2 ) + "]" ); } else //if (DebugModeFlag) - WriteLog("Type: UpdateValues - " + - tmpEvent->Params[5].asMemCell->Text() + " " + - std::to_string( tmpEvent->Params[ 5 ].asMemCell->Value1() ) + " " + - std::to_string( tmpEvent->Params[ 5 ].asMemCell->Value2() ) ); + WriteLog("Type: UpdateValues - [" + + tmpEvent->Params[5].asMemCell->Text() + "] [" + + to_string( tmpEvent->Params[ 5 ].asMemCell->Value1(), 2 ) + "] [" + + to_string( tmpEvent->Params[ 5 ].asMemCell->Value2(), 2 ) + "]" ); } break; case tp_GetValues: @@ -3255,24 +3215,26 @@ bool TGround::CheckQuery() WriteLog("Type: GetValues"); break; case tp_PutValues: - if (tmpEvent->Activator) - { - loc.X = - -tmpEvent->Params[3].asdouble; // zamiana, bo fizyka ma inaczej niż sceneria - loc.Y = tmpEvent->Params[5].asdouble; - loc.Z = tmpEvent->Params[4].asdouble; + if (tmpEvent->Activator) { + // zamiana, bo fizyka ma inaczej niż sceneria + loc.X = -tmpEvent->Params[3].asdouble; + loc.Y = tmpEvent->Params[5].asdouble; + loc.Z = tmpEvent->Params[4].asdouble; if (tmpEvent->Activator->Mechanik) // przekazanie rozkazu do AI tmpEvent->Activator->Mechanik->PutCommand( tmpEvent->Params[0].asText, tmpEvent->Params[1].asdouble, tmpEvent->Params[2].asdouble, loc); - else - { // przekazanie do pojazdu + else { + // przekazanie do pojazdu tmpEvent->Activator->MoverParameters->PutCommand( tmpEvent->Params[0].asText, tmpEvent->Params[1].asdouble, tmpEvent->Params[2].asdouble, loc); } + WriteLog("Type: PutValues - [" + + std::string(tmpEvent->Params[0].asText) + "] [" + + to_string( tmpEvent->Params[ 1 ].asdouble, 2 ) + "] [" + + to_string( tmpEvent->Params[ 2 ].asdouble, 2 ) + "]" ); } - WriteLog("Type: PutValues"); break; case tp_Lights: if (tmpEvent->Params[9].asModel) @@ -3342,11 +3304,10 @@ bool TGround::CheckQuery() if (tmpEvent->Params[9].asTrack) { // prędkość na zwrotnicy może być ograniczona z góry we wpisie, większej się nie // ustawi eventem - WriteLog("type: TrackVel"); - // WriteLog("Vel: ",tmpEvent->Params[0].asdouble); + WriteLog("Type: TrackVel"); tmpEvent->Params[9].asTrack->VelocitySet(tmpEvent->Params[0].asdouble); if (DebugModeFlag) // wyświetlana jest ta faktycznie ustawiona - WriteLog("vel: ", tmpEvent->Params[9].asTrack->VelocityGet()); + WriteLog(" - velocity: ", tmpEvent->Params[9].asTrack->VelocityGet()); } break; case tp_DynVel: @@ -3358,7 +3319,7 @@ bool TGround::CheckQuery() if (bCondition || (tmpEvent->iFlags & conditional_anyelse)) // warunek spelniony albo było użyte else { - WriteLog("Multiple passed"); + WriteLog("Type: Multi-event"); for (i = 0; i < 8; ++i) { // dodawane do kolejki w kolejności zapisania if( tmpEvent->Params[ i ].asEvent ) { @@ -3413,7 +3374,7 @@ bool TGround::CheckQuery() tmpEvent->iFlags & ( update_memstring | update_memval1 | update_memval2 ) ); WriteLog( - "whois request (" + to_string( tmpEvent->iFlags ) + ") " + "Type: WhoIs (" + to_string( tmpEvent->iFlags ) + ") - " + "[name: " + tmpEvent->Activator->MoverParameters->TypeName + "], " + "[consist brake level: " + to_string( consistbrakelevel, 2 ) + "], " + "[obstacle distance: " + to_string( collisiondistance, 2 ) + " m]" ); @@ -3427,7 +3388,7 @@ bool TGround::CheckQuery() tmpEvent->iFlags & ( update_memstring | update_memval1 | update_memval2 ) ); WriteLog( - "whois request (" + to_string( tmpEvent->iFlags ) + ") " + "Type: WhoIs (" + to_string( tmpEvent->iFlags ) + ") - " + "[load type: " + tmpEvent->Activator->MoverParameters->LoadType + "], " + "[current load: " + to_string( tmpEvent->Activator->MoverParameters->Load, 2 ) + "], " + "[max load: " + to_string( tmpEvent->Activator->MoverParameters->MaxLoad, 2 ) + "]" ); @@ -3442,7 +3403,7 @@ bool TGround::CheckQuery() tmpEvent->iFlags & (update_memstring | update_memval1 | update_memval2)); WriteLog( - "whois request (" + to_string( tmpEvent->iFlags ) + ") " + "Type: WhoIs (" + to_string( tmpEvent->iFlags ) + ") - " + "[destination: " + tmpEvent->Activator->asDestination + "], " + "[direction: " + to_string( tmpEvent->Activator->DirectionGet() ) + "], " + "[engine power: " + to_string( tmpEvent->Activator->MoverParameters->Power, 2 ) + "]" ); @@ -3476,12 +3437,12 @@ bool TGround::CheckQuery() case tp_Voltage: // zmiana napięcia w zasilaczu (TractionPowerSource) if (tmpEvent->Params[9].psPower) { // na razie takie chamskie ustawienie napięcia zasilania - WriteLog("type: Voltage"); + WriteLog("Type: Voltage"); tmpEvent->Params[9].psPower->VoltageSet(tmpEvent->Params[0].asdouble); } case tp_Friction: // zmiana tarcia na scenerii { // na razie takie chamskie ustawienie napięcia zasilania - WriteLog("type: Friction"); + WriteLog("Type: Friction"); Global::fFriction = (tmpEvent->Params[0].asdouble); } break; @@ -3492,6 +3453,7 @@ bool TGround::CheckQuery() } // while return true; } +#endif void TGround::UpdatePhys(double dt, int iter) { // aktualizacja fizyki stałym krokiem: dt=krok czasu [s], dt*iter=czas od ostatnich przeliczeń @@ -3574,13 +3536,6 @@ bool TGround::Update(double dt, int iter) return true; }; -// updates scene lights array -void -TGround::Update_Lights() { - - m_lights.update(); -} - void TGround::Update_Hidden() { @@ -4179,7 +4134,7 @@ void TGround::DynamicRemove(TDynamicObject *dyn) (*n) = node->nNext; // pominięcie na liście Global::TrainDelete(d); // remove potential entries in the light array - m_lights.remove( d ); + simulation::Lights.remove( d ); d = d->Next(); // przejście do kolejnego pojazdu, póki jeszcze jest delete node; // usuwanie fizyczne z pamięci } diff --git a/Ground.h b/Ground.h index b79ef633..f045c23b 100644 --- a/Ground.h +++ b/Ground.h @@ -10,16 +10,13 @@ http://mozilla.org/MPL/2.0/. #pragma once #include -#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 "simulation.h" typedef int TGroundNodeType; // Ra: zmniejszone liczby, aby zrobić tabelkę i zoptymalizować wyszukiwanie @@ -97,7 +94,7 @@ struct TGroundVertex // ground node holding single, unique piece of 3d geometry. TBD, TODO: unify this with basic 3d model node struct piece_node { std::vector vertices; - geometry_handle geometry { 0,0 }; // geometry prepared for drawing + geometry_handle geometry { 0, 0 }; // geometry prepared for drawing }; // obiekt scenerii @@ -142,9 +139,9 @@ public: 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 + Ambient{ 0.2f }, + Diffuse{ 1.0f }, + Specular{ 0.0f }; // oświetlenie double fLineThickness; // McZapkie-120702: grubosc linii bool bVisible; @@ -155,17 +152,17 @@ public: 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; + scene::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 @@ -219,6 +216,7 @@ private: public: virtual ~TGroundRect(); // pobranie wskaźnika do małego kwadratu, utworzenie jeśli trzeba + inline TSubRect * SafeGetSubRect(int iCol, int iRow) { if( !pSubRects ) { // utworzenie małych kwadratów @@ -227,6 +225,7 @@ public: return pSubRects + iRow * iNumSubRects + iCol; // zwrócenie właściwego }; // pobranie wskaźnika do małego kwadratu, bez tworzenia jeśli nie ma + inline TSubRect *FastGetSubRect(int iCol, int iRow) const { return ( pSubRects ? @@ -234,8 +233,9 @@ public: 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 ); + bool mergeable( TGroundNode const &Left, TGroundNode const &Right ) const; // optymalizacja obiektów w sektorach + inline void Optimize() { if( pSubRects ) { for( int i = iNumSubRects * iNumSubRects - 1; i >= 0; --i ) { @@ -253,20 +253,23 @@ class TGround 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 +#ifdef EU07_USE_OLD_GROUNDCODE TEvent *RootEvent = nullptr; // lista zdarzeń TEvent *QueryRootEvent = nullptr, *tmpEvent = nullptr; typedef std::unordered_map event_map; event_map m_eventmap; - TNames m_trackmap; - light_array m_lights; // collection of dynamic light sources present in the scene +#endif + TNames m_nodemap; vector3 pOrigin; vector3 aRotate; bool bInitDone = false; private: // metody prywatne - bool EventConditon(TEvent *e); +#ifdef EU07_USE_OLD_GROUNDCODE + bool EventConditon(TEvent *e); +#endif public: bool bDynamicRemove = false; // czy uruchomić procedurę usuwania pojazdów @@ -276,9 +279,11 @@ class TGround void Free(); bool Init( std::string File ); void FirstInit(); +#ifdef EU07_USE_OLD_GROUNDCODE void InitTracks(); void InitTraction(); bool InitEvents(); +#endif bool InitLaunchers(); TTrack * FindTrack(vector3 Point, int &iConnection, TGroundNode *Exclude); TTraction * FindTraction(glm::dvec3 const &Point, int &iConnection, TGroundNode *Exclude); @@ -286,15 +291,16 @@ class TGround 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); +#ifdef EU07_USE_OLD_GROUNDCODE + bool AddToQuery( TEvent *Event, TDynamicObject *Node ); bool CheckQuery(); +#endif TGroundNode * DynamicFindAny(std::string const &Name); TGroundNode * DynamicFind(std::string const &Name); void DynamicList(bool all = false); - TGroundNode * FindGroundNode(std::string asNameToFind, TGroundNodeType iNodeType); + TGroundNode * FindGroundNode(std::string const &asNameToFind, TGroundNodeType const iNodeType); TGroundRect * GetRect( double x, double z ); TSubRect * GetSubRect( int iCol, int iRow ); inline @@ -310,8 +316,9 @@ class TGround inline int GetColFromX(double x) { return (int)(x / fSubRectSize + fHalfTotalNumSubRects); }; +#ifdef EU07_USE_OLD_GROUNDCODE TEvent * FindEvent(const std::string &asEventName); - TEvent * FindEventScan(const std::string &asEventName); +#endif void TrackJoin(TGroundNode *Current); private: @@ -330,10 +337,8 @@ class TGround 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); + 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(); diff --git a/MemCell.cpp b/MemCell.cpp index 9a9ad4fe..0bf32bf3 100644 --- a/MemCell.cpp +++ b/MemCell.cpp @@ -25,17 +25,18 @@ http://mozilla.org/MPL/2.0/. //--------------------------------------------------------------------------- +// 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 + m_location = + p ? *p : glm::dvec3(); // 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 +104,17 @@ TCommandType TMemCell::CommandCheck() bool TMemCell::Load(cParser *parser) { std::string token; - parser->getTokens(1, false); // case sensitive - *parser >> szText; - parser->getTokens( 2, false ); +#ifdef EU07_USE_OLD_GROUNDCODE + parser->getTokens( 3, false ); *parser +#else + parser->getTokens( 6, false ); + *parser + >> m_location.x + >> m_location.y + >> m_location.z +#endif + >> szText >> fValue1 >> fValue2; parser->getTokens(); @@ -150,11 +158,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) diff --git a/MemCell.h b/MemCell.h index a4384b3f..35637431 100644 --- a/MemCell.h +++ b/MemCell.h @@ -10,52 +10,59 @@ http://mozilla.org/MPL/2.0/. #pragma once #include "Classes.h" +#include "scenenode.h" #include "dumb3d.h" +#include "names.h" + +class TMemCell : public editor::basic_node { -class TMemCell -{ private: - Math3D::vector3 vPosition; + // content 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 + 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 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, Math3D::vector3 *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); }; +class memory_manager : public basic_table { + +}; + //--------------------------------------------------------------------------- diff --git a/Model3d.cpp b/Model3d.cpp index 2b74dd34..081b0709 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1023,7 +1023,7 @@ TSubModel::convert( TGroundNode &Groundnode ) const { if( m_geometry == null_handle ) { return; } - std::size_t vertexcount { 0 }; + int vertexcount { 0 }; std::vector importedvertices; TGroundVertex vertex, vertex1, vertex2; for( auto const &sourcevertex : GfxRenderer.Vertices( m_geometry ) ) { @@ -1038,31 +1038,29 @@ TSubModel::convert( TGroundNode &Groundnode ) const { importedvertices.emplace_back( vertex2 ); importedvertices.emplace_back( vertex ); } + // start a new triangle + vertexcount = -1; } ++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 ) { - + // assign imported geometry to the node... Groundnode.Piece->vertices.swap( importedvertices ); - + // ...and calculate center... for( auto const &vertex : Groundnode.Piece->vertices ) { Groundnode.pCenter += vertex.position; } Groundnode.pCenter /= Groundnode.iNumVerts; - - double r { 0.0 }; - double tf; + // ...and bounding area + double squareradius { 0.0 }; for( auto const &vertex : Groundnode.Piece->vertices ) { - tf = glm::length2( vertex.position - glm::dvec3{ Groundnode.pCenter } ); - if( tf > r ) - r = tf; + squareradius = std::max( squareradius, glm::length2( vertex.position - glm::dvec3{ Groundnode.pCenter } ) ); } - Groundnode.fSquareRadius += r; + Groundnode.fSquareRadius += squareradius; } } diff --git a/Names.h b/Names.h index 2a3addb6..5f76306a 100644 --- a/Names.h +++ b/Names.h @@ -65,3 +65,47 @@ private: // members: typemap_map m_maps; // list of object maps of types specified so far }; + +template +class basic_table { + +public: +// 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 + insert( Type_ *Item ) { + m_items.emplace_back( Item ); + auto const itemname = Item->name(); + if( ( true == itemname.empty() ) || ( itemname == "none" ) ) { + return true; + } + 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 ); } + +protected: +// types + using type_sequence = std::deque; + using type_map = std::unordered_map; +// members + type_sequence m_items; + type_map m_itemmap; +}; diff --git a/Track.cpp b/Track.cpp index e84652e5..041842bf 100644 --- a/Track.cpp +++ b/Track.cpp @@ -139,13 +139,12 @@ void TIsolated::Modify(int i, TDynamicObject *o) }; // tworzenie nowego odcinka ruchu -TTrack::TTrack(TGroundNode *g) : - pMyNode( g ) // Ra: proteza, żeby tor znał swoją nazwę TODO: odziedziczyć TTrack z TGroundNode -{ - fRadiusTable[ 0 ] = 0.0; - fRadiusTable[ 1 ] = 0.0; - nFouling[ 0 ] = nullptr; - nFouling[ 1 ] = nullptr; +TTrack::TTrack( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {} + +// legacy constructor +TTrack::TTrack( std::string Name ) { + + m_name = Name; } TTrack::~TTrack() @@ -182,7 +181,7 @@ TTrack * TTrack::Create400m(int what, double dx) { // tworzenie toru do wstawiania taboru podczas konwersji na E3D TGroundNode *tmp = new TGroundNode(TP_TRACK); // node TTrack *trk = tmp->pTrack; - trk->bVisible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur + trk->m_visible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur trk->iCategoryFlag = what; // taki sam typ plus informacja, że dodatkowy trk->Init(); // utworzenie segmentu trk->Segment->Init(vector3(-dx, 0, 0), vector3(-dx, 0, 400), 10.0, 0, 0); // prosty @@ -195,12 +194,10 @@ TTrack * TTrack::Create400m(int what, double dx) TTrack * TTrack::NullCreate(int dir) { // tworzenie toru wykolejającego od strony (dir), albo pętli dla samochodów - TGroundNode *tmp = new TGroundNode(TP_TRACK), *tmp2 = NULL; // node + TGroundNode *tmp = new TGroundNode( TP_TRACK ); + TGroundNode *tmp2 = nullptr; // node TTrack *trk = tmp->pTrack; // tor; UWAGA! obrotnica może generować duże ilości tego - // tmp->iType=TP_TRACK; - // TTrack* trk=new TTrack(tmp); //tor; UWAGA! obrotnica może generować duże ilości tego - // tmp->pTrack=trk; - trk->bVisible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur + trk->m_visible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur // trk->iTrapezoid=1; //są przechyłki do uwzględniania w rysowaniu trk->iCategoryFlag = (iCategoryFlag & 15) | 0x80; // taki sam typ plus informacja, że dodatkowy float r1, r2; @@ -216,25 +213,21 @@ TTrack * TTrack::NullCreate(int dir) case 0: p1 = Segment->FastGetPoint_0(); p2 = p1 - 450.0 * Normalize(Segment->GetDirection1()); - trk->Segment->Init(p1, p2, 5, -RadToDeg(r1), - 70.0); // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + trk->Segment->Init(p1, p2, 5, -RadToDeg(r1), 70.0); ConnectPrevPrev(trk, 0); break; case 1: p1 = Segment->FastGetPoint_1(); p2 = p1 - 450.0 * Normalize(Segment->GetDirection2()); - trk->Segment->Init(p1, p2, 5, RadToDeg(r2), - 70.0); // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + trk->Segment->Init(p1, p2, 5, RadToDeg(r2), 70.0); ConnectNextPrev(trk, 0); break; case 3: // na razie nie możliwe p1 = SwitchExtension->Segments[1]->FastGetPoint_1(); // koniec toru drugiego zwrotnicy - p2 = p1 - - 450.0 * - Normalize( - SwitchExtension->Segments[1]->GetDirection2()); // przedłużenie na wprost - trk->Segment->Init(p1, p2, 5, RadToDeg(r2), - 70.0); // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + p2 = p1 - 450.0 * Normalize( SwitchExtension->Segments[1]->GetDirection2()); // przedłużenie na wprost + trk->Segment->Init(p1, p2, 5, RadToDeg(r2), 70.0); // bo prosty, kontrolne wyliczane przy zmiennej przechyłce ConnectNextPrev(trk, 0); // trk->ConnectPrevNext(trk,dir); SetConnections(1); // skopiowanie połączeń @@ -251,13 +244,13 @@ TTrack * TTrack::NullCreate(int dir) TTrack *trk2 = tmp2->pTrack; trk2->iCategoryFlag = (iCategoryFlag & 15) | 0x80; // taki sam typ plus informacja, że dodatkowy - trk2->bVisible = false; + trk2->m_visible = false; trk2->fVelocity = 20.0; // zawracanie powoli trk2->fRadius = 20.0; // promień, aby się dodawało do tabelki prędkości i liczyło // narastająco trk2->Init(); // utworzenie segmentu - trk->pMyNode->asName = pMyNode->asName + ":loopstart"; - trk2->pMyNode->asName = pMyNode->asName + ":loopfinish"; + trk->m_name = m_name + ":loopstart"; + trk2->m_name = m_name + ":loopfinish"; switch (dir) { //łączenie z nowym torem case 0: @@ -320,8 +313,8 @@ void TTrack::ConnectPrevNext(TTrack *pTrack, int typ) iPrevDirection = typ | 1; // 1:zwykły lub pierwszy zwrotnicy, 3:drugi zwrotnicy pTrack->trNext = this; pTrack->iNextDirection = 0; - if (bVisible) - if (pTrack->bVisible) + if (m_visible) + if (pTrack->m_visible) if (eType == tt_Normal) // jeśli łączone są dwa normalne if (pTrack->eType == tt_Normal) if ((fTrackWidth != @@ -340,8 +333,8 @@ void TTrack::ConnectNextPrev(TTrack *pTrack, int typ) iNextDirection = ((pTrack->eType == tt_Switch) ? 0 : (typ & 2)); pTrack->trPrev = this; pTrack->iPrevDirection = 1; - if (bVisible) - if (pTrack->bVisible) + if (m_visible) + if (pTrack->m_visible) if (eType == tt_Normal) // jeśli łączone są dwa normalne if (pTrack->eType == tt_Normal) if ((fTrackWidth != @@ -363,25 +356,15 @@ void TTrack::ConnectNextNext(TTrack *pTrack, int typ) } } -vector3 MakeCPoint(vector3 p, double d, double a1, double a2) -{ - vector3 cp = vector3(0, 0, 1); - cp.RotateX(DegToRad(a2)); - cp.RotateY(DegToRad(a1)); - cp = cp * d + p; - return cp; -} - vector3 LoadPoint(cParser *parser) { // pobranie współrzędnych punktu vector3 p; - std::string token; parser->getTokens(3); *parser >> p.x >> p.y >> p.z; return p; } -void TTrack::Load(cParser *parser, vector3 pOrigin, std::string name) +void TTrack::Load(cParser *parser, vector3 pOrigin) { // pobranie obiektu trajektorii ruchu vector3 pt, vec, p1, p2, cp1, cp2, p3, p4, cp3, cp4; // dodatkowe punkty potrzebne do skrzyżowań double a1, a2, r1, r2, r3, r4; @@ -464,8 +447,8 @@ void TTrack::Load(cParser *parser, vector3 pOrigin, std::string name) } parser->getTokens(); *parser >> token; - bVisible = (token.compare("vis") == 0); // visible - if (bVisible) + m_visible = (token.compare("vis") == 0); // visible + if (m_visible) { parser->getTokens(); *parser >> str; // railtex @@ -718,24 +701,28 @@ void TTrack::Load(cParser *parser, vector3 pOrigin, std::string name) } else if (str == "angle1") { // kąt ścięcia końca od strony 1 + // NOTE: not used/implemented parser->getTokens(); *parser >> a1; - Segment->AngleSet(0, a1); + //Segment->AngleSet(0, a1); } else if (str == "angle2") { // kąt ścięcia końca od strony 2 + // NOTE: not used/implemented parser->getTokens(); *parser >> a2; - Segment->AngleSet(1, a2); + //Segment->AngleSet(1, a2); } else if (str == "fouling1") { // wskazanie modelu ukresu w kierunku 1 + // NOTE: not used/implemented parser->getTokens(); *parser >> token; // nFouling[0]= } else if (str == "fouling2") { // wskazanie modelu ukresu w kierunku 2 + // NOTE: not used/implemented parser->getTokens(); *parser >> token; // nFouling[1]= @@ -749,19 +736,26 @@ void TTrack::Load(cParser *parser, vector3 pOrigin, std::string name) // ograniczenie dla pantografujących) } else - ErrorLog("Unknown property: \"" + str + "\" in track \"" + name + "\""); + ErrorLog("Unknown property: \"" + str + "\" in track \"" + m_name + "\""); parser->getTokens(); *parser >> token; str = token; } // alternatywny zapis nazwy odcinka izolowanego - po znaku "@" w nazwie toru if (!pIsolated) - if ((i = name.find("@")) != std::string::npos) - if (i < name.length()) // nie może być puste + if ((i = m_name.find("@")) != std::string::npos) + if (i < m_name.length()) // nie może być puste { - pIsolated = TIsolated::Find(name.substr(i + 1, name.length())); - name = name.substr(0, i - 1); // usunięcie z nazwy + pIsolated = TIsolated::Find(m_name.substr(i + 1, m_name.length())); + m_name = m_name.substr(0, i - 1); // usunięcie z nazwy } + + // calculate path location + m_location = glm::dvec3{ ( + CurrentSegment()->FastGetPoint_0() + + CurrentSegment()->FastGetPoint( 0.5 ) + + CurrentSegment()->FastGetPoint_1() ) + / 3.0 }; } // TODO: refactor this mess @@ -771,7 +765,7 @@ bool TTrack::AssignEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent if( NewEvent0 == nullptr ) { if( false == asEvent0Name.empty() ) { - ErrorLog( "Bad event: event \"" + asEvent0Name + "\" assigned to track \"" + pMyNode->asName + "\" does not exist" ); + ErrorLog( "Bad event: event \"" + asEvent0Name + "\" assigned to track \"" + m_name + "\" does not exist" ); bError = true; } } @@ -789,7 +783,7 @@ bool TTrack::AssignEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent if( NewEvent1 == nullptr ) { if( false == asEvent1Name.empty() ) { - ErrorLog( "Bad event: event \"" + asEvent1Name + "\" assigned to track \"" + pMyNode->asName + "\" does not exist" ); + ErrorLog( "Bad event: event \"" + asEvent1Name + "\" assigned to track \"" + m_name + "\" does not exist" ); bError = true; } } @@ -807,7 +801,7 @@ bool TTrack::AssignEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent if( NewEvent2 == nullptr ) { if( false == asEvent2Name.empty() ) { - ErrorLog( "Bad event: event \"" + asEvent2Name + "\" assigned to track \"" + pMyNode->asName + "\" does not exist" ); + ErrorLog( "Bad event: event \"" + asEvent2Name + "\" assigned to track \"" + m_name + "\" does not exist" ); bError = true; } } @@ -832,7 +826,7 @@ bool TTrack::AssignallEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEv if( NewEvent0 == nullptr ) { if( false == asEventall0Name.empty() ) { - ErrorLog( "Bad event: event \"" + asEventall0Name + "\" assigned to track \"" + pMyNode->asName + "\" does not exist" ); + ErrorLog( "Bad event: event \"" + asEventall0Name + "\" assigned to track \"" + m_name + "\" does not exist" ); bError = true; } } @@ -850,7 +844,7 @@ bool TTrack::AssignallEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEv if( NewEvent1 == nullptr ) { if( false == asEventall1Name.empty() ) { - ErrorLog( "Bad event: event \"" + asEventall1Name + "\" assigned to track \"" + pMyNode->asName + "\" does not exist" ); + ErrorLog( "Bad event: event \"" + asEventall1Name + "\" assigned to track \"" + m_name + "\" does not exist" ); bError = true; } } @@ -868,7 +862,7 @@ bool TTrack::AssignallEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEv if( NewEvent2 == nullptr ) { if( false == asEventall2Name.empty() ) { - ErrorLog( "Bad event: event \"" + asEventall2Name + "\" assigned to track \"" + pMyNode->asName + "\" does not exist" ); + ErrorLog( "Bad event: event \"" + asEventall2Name + "\" assigned to track \"" + m_name + "\" does not exist" ); bError = true; } } @@ -921,7 +915,7 @@ bool TTrack::IsolatedEventsAssign(TEvent *busy, TEvent *free) return false; }; -// ABu: przeniesione z Track.h i poprawione!!! +// ABu: przeniesione z Path.h i poprawione!!! bool TTrack::AddDynamicObject(TDynamicObject *Dynamic) { // dodanie pojazdu do trajektorii // Ra: tymczasowo wysyłanie informacji o zajętości konkretnego toru @@ -935,9 +929,9 @@ bool TTrack::AddDynamicObject(TDynamicObject *Dynamic) // jeśli multiplayer if( true == Dynamics.empty() ) { // pierwszy zajmujący - if( pMyNode->asName != "none" ) { + if( m_name != "none" ) { // przekazanie informacji o zajętości toru - Global::pGround->WyslijString( pMyNode->asName, 8 ); + Global::pGround->WyslijString( m_name, 8 ); } } } @@ -1027,9 +1021,9 @@ bool TTrack::RemoveDynamicObject(TDynamicObject *Dynamic) // jeśli multiplayer if( true == Dynamics.empty() ) { // jeśli już nie ma żadnego - if( pMyNode->asName != "none" ) { + if( m_name != "none" ) { // przekazanie informacji o zwolnieniu toru - Global::pGround->WyslijString( pMyNode->asName, 9 ); + Global::pGround->WyslijString( m_name, 9 ); } } } @@ -1079,7 +1073,12 @@ void TTrack::RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joi }; // wypełnianie tablic VBO + +#ifdef EU07_USE_OLD_GROUNDCODE +void TTrack::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ) { +#else void TTrack::create_geometry( geometrybank_handle const &Bank ) { +#endif // Ra: trzeba rozdzielić szyny od podsypki, aby móc grupować wg tekstur auto const fHTW = 0.5f * std::abs(fTrackWidth); auto const side = std::abs(fTexWidth); // szerokść podsypki na zewnątrz szyny albo pobocza @@ -1116,7 +1115,11 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { normal2 = normal1; } - auto const origin { pMyNode->m_rootposition }; +#ifdef EU07_USE_OLD_GROUNDCODE + if( Bank != 0 ) { + m_origin = Origin; + } +#endif float roll1, roll2; switch (iCategoryFlag & 15) @@ -1326,7 +1329,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { } } vertex_array vertices; - Segment->RenderLoft(vertices, origin, bpts1, iTrapezoid ? -4 : 4, fTexLength); + Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid ? -4 : 4, fTexLength); if( ( Bank != 0 ) && ( true == Geometry2.empty() ) ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } @@ -1339,18 +1342,18 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { { // szyny - generujemy dwie, najwyżej rysować się będzie jedną vertex_array vertices; if( ( Bank != 0 ) && ( true == Geometry1.empty() ) ) { - Segment->RenderLoft( vertices, origin, rpts1, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); + Segment->RenderLoft( vertices, m_origin, rpts1, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); // reuse the scratchpad - Segment->RenderLoft( vertices, origin, rpts2, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); + Segment->RenderLoft( vertices, m_origin, rpts2, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } if( ( Bank == 0 ) && ( false == Geometry1.empty() ) ) { // special variant, replace existing data for a turntable track - Segment->RenderLoft( vertices, origin, rpts1, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); + Segment->RenderLoft( vertices, m_origin, rpts1, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); GfxRenderer.Replace( vertices, Geometry1[ 0 ] ); vertices.clear(); // reuse the scratchpad - Segment->RenderLoft( vertices, origin, rpts2, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); + Segment->RenderLoft( vertices, m_origin, rpts2, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); GfxRenderer.Replace( vertices, Geometry1[ 1 ] ); } } @@ -1392,27 +1395,27 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { vertex_array vertices; if( m_material1 ) { // fixed parts - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts2, nnumPts, fTexLength ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, fTexLength ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts1, nnumPts, fTexLength, 1.0, 2 ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, fTexLength, 1.0, 2 ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); // left blade - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, SwitchExtension->fOffset2 ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, SwitchExtension->fOffset2 ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } if( m_material2 ) { // fixed parts - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts1, nnumPts, fTexLength ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, fTexLength ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts2, nnumPts, fTexLength, 1.0, 2 ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, fTexLength, 1.0, 2 ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); // right blade - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -fMaxOffset + SwitchExtension->fOffset1 ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -fMaxOffset + SwitchExtension->fOffset1 ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } @@ -1422,27 +1425,27 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { vertex_array vertices; if( m_material1 ) { // fixed parts - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts1, nnumPts, fTexLength ); // lewa szyna normalna cała + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, fTexLength ); // lewa szyna normalna cała Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts2, nnumPts, fTexLength, 1.0, 2 ); // prawa szyna za iglicą + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, fTexLength, 1.0, 2 ); // prawa szyna za iglicą Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); // right blade - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -SwitchExtension->fOffset2 ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -SwitchExtension->fOffset2 ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } if( m_material2 ) { // fixed parts - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts2, nnumPts, fTexLength ); // prawa szyna normalnie cała + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, fTexLength ); // prawa szyna normalnie cała Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts1, nnumPts, fTexLength, 1.0, 2 ); // lewa szyna za iglicą + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, fTexLength, 1.0, 2 ); // lewa szyna za iglicą Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); // left blade - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, fMaxOffset - SwitchExtension->fOffset1 ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, fMaxOffset - SwitchExtension->fOffset1 ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } @@ -1501,7 +1504,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { if (m_material1) // jeśli podana była tekstura, generujemy trójkąty { // tworzenie trójkątów nawierzchni szosy vertex_array vertices; - Segment->RenderLoft(vertices, origin, bpts1, iTrapezoid ? -2 : 2, fTexLength); + Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid ? -2 : 2, fTexLength); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } if (m_material2) @@ -1665,24 +1668,24 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony // odcinka if( ( fTexHeight1 >= 0.0 ) || ( slop != 0.0 ) ) { - Segment->RenderLoft( vertices, origin, rpts1, -3, fTexLength ); // tylko jeśli jest z prawej + Segment->RenderLoft( vertices, m_origin, rpts1, -3, fTexLength ); // tylko jeśli jest z prawej Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) { - Segment->RenderLoft( vertices, origin, rpts2, -3, fTexLength ); // tylko jeśli jest z lewej + Segment->RenderLoft( vertices, m_origin, rpts2, -3, fTexLength ); // tylko jeśli jest z lewej Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } } else { // pobocza zwykłe, brak przechyłki if( ( fTexHeight1 >= 0.0 ) || ( slop != 0.0 ) ) { - Segment->RenderLoft( vertices, origin, rpts1, 3, fTexLength ); + Segment->RenderLoft( vertices, m_origin, rpts1, 3, fTexLength ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) { - Segment->RenderLoft( vertices, origin, rpts2, 3, fTexLength ); + Segment->RenderLoft( vertices, m_origin, rpts2, 3, fTexLength ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } @@ -1915,22 +1918,22 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { if (SwitchExtension->iRoads == 4) { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) { - SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); + SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, m_origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - SwitchExtension->Segments[ 3 ]->RenderLoft( vertices, origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); + SwitchExtension->Segments[ 3 ]->RenderLoft( vertices, m_origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - SwitchExtension->Segments[ 4 ]->RenderLoft( vertices, origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); + SwitchExtension->Segments[ 4 ]->RenderLoft( vertices, m_origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - SwitchExtension->Segments[ 5 ]->RenderLoft( vertices, origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); + SwitchExtension->Segments[ 5 ]->RenderLoft( vertices, m_origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); @@ -1940,17 +1943,17 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { else { // punkt 3 pokrywa się z punktem 1, jak w zwrotnicy; połączenie 1->2 nie musi być prostoliniowe if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) { - SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); // z P2 do P4 + SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, m_origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); // z P2 do P4 if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); // z P4 do P3=P1 (odwrócony) + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); // z P4 do P3=P1 (odwrócony) if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); // z P1 do P2 + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, -3, fTexLength, 1.0, 0, 0, 0.0, &b, render ); // z P1 do P2 if( true == render ) { Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); @@ -1972,14 +1975,14 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { // we start with a vertex in the middle... vertices.emplace_back( glm::vec3{ - oxz.x - origin.x, - oxz.y - origin.y, - oxz.z - origin.z }, + oxz.x - m_origin.x, + oxz.y - m_origin.y, + oxz.z - m_origin.z }, glm::vec3{ 0.0f, 1.0f, 0.0f }, glm::vec2{ 0.5f, 0.5f } ); // ...and add one extra vertex to close the fan... - u = ( SwitchExtension->vPoints[ 0 ].x - oxz.x + origin.x ) / fTexLength; - v = ( SwitchExtension->vPoints[ 0 ].z - oxz.z + origin.z ) / ( fTexRatio1 * fTexLength ); + u = ( SwitchExtension->vPoints[ 0 ].x - oxz.x + m_origin.x ) / fTexLength; + v = ( SwitchExtension->vPoints[ 0 ].z - oxz.z + m_origin.z ) / ( fTexRatio1 * fTexLength ); vertices.emplace_back( glm::vec3 { SwitchExtension->vPoints[ 0 ].x, @@ -1993,8 +1996,8 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { // ...then draw the precalculated rest for (int i = pointcount + SwitchExtension->iRoads - 1; i >= 0; --i) { // mapowanie we współrzędnych scenerii - u = ( SwitchExtension->vPoints[ i ].x - oxz.x + origin.x ) / fTexLength; - v = ( SwitchExtension->vPoints[ i ].z - oxz.z + origin.z ) / ( fTexRatio1 * fTexLength ); + u = ( SwitchExtension->vPoints[ i ].x - oxz.x + m_origin.x ) / fTexLength; + v = ( SwitchExtension->vPoints[ i ].z - oxz.z + m_origin.z ) / ( fTexRatio1 * fTexLength ); vertices.emplace_back( glm::vec3 { SwitchExtension->vPoints[ i ].x, @@ -2068,7 +2071,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { if (m_material1) // jeśli podana była tekstura, generujemy trójkąty { // tworzenie trójkątów nawierzchni szosy vertex_array vertices; - Segment->RenderLoft(vertices, origin, bpts1, iTrapezoid ? -2 : 2, fTexLength); + Segment->RenderLoft(vertices, m_origin, bpts1, iTrapezoid ? -2 : 2, fTexLength); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } if (m_material2) @@ -2128,19 +2131,19 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { {-rozp2, -fTexHeight2, 0.f}, normalup, {0.0f, 0.f} }; // prawy brzeg prawego pobocza - Segment->RenderLoft(vertices, origin, rpts1, -3, fTexLength); + Segment->RenderLoft(vertices, m_origin, rpts1, -3, fTexLength); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); - Segment->RenderLoft(vertices, origin, rpts2, -3, fTexLength); + Segment->RenderLoft(vertices, m_origin, rpts2, -3, fTexLength); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } else { // pobocza zwykłe, brak przechyłki - Segment->RenderLoft(vertices, origin, rpts1, 3, fTexLength); + Segment->RenderLoft(vertices, m_origin, rpts1, 3, fTexLength); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); - Segment->RenderLoft(vertices, origin, rpts2, 3, fTexLength); + Segment->RenderLoft(vertices, m_origin, rpts2, 3, fTexLength); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } @@ -2463,20 +2466,19 @@ TTrack * TTrack::RaAnimate() {szyna[ i ].texture.x, 0.f} }; } - auto const origin { pMyNode->m_rootposition }; vertex_array vertices; if (SwitchExtension->RightSwitch) { // nowa wersja z SPKS, ale odwrotnie lewa/prawa if( m_material1 ) { // left blade - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, SwitchExtension->fOffset2 ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, SwitchExtension->fOffset2 ); GfxRenderer.Replace( vertices, Geometry1[ 2 ] ); vertices.clear(); } if( m_material2 ) { // right blade - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -fMaxOffset + SwitchExtension->fOffset1 ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -fMaxOffset + SwitchExtension->fOffset1 ); GfxRenderer.Replace( vertices, Geometry2[ 2 ] ); vertices.clear(); } @@ -2484,13 +2486,13 @@ TTrack * TTrack::RaAnimate() else { // lewa działa lepiej niż prawa if( m_material1 ) { // right blade - SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -SwitchExtension->fOffset2 ); + SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -SwitchExtension->fOffset2 ); GfxRenderer.Replace( vertices, Geometry1[ 2 ] ); vertices.clear(); } if( m_material2 ) { // left blade - SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, fMaxOffset - SwitchExtension->fOffset1 ); + SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, fMaxOffset - SwitchExtension->fOffset1 ); GfxRenderer.Replace( vertices, Geometry2[ 2 ] ); vertices.clear(); } @@ -2528,7 +2530,11 @@ TTrack * TTrack::RaAnimate() dynamic->Move( 0.000001 ); } // NOTE: passing empty handle is a bit of a hack here. could be refactored into something more elegant +#ifdef EU07_USE_OLD_GROUNDCODE + create_geometry( geometrybank_handle(), glm::dvec3() ); +#else create_geometry( geometrybank_handle() ); +#endif } // animacja trwa nadal } else @@ -2650,14 +2656,6 @@ void TTrack::MovedUp1(float const dh) fTexHeight1 += dh; }; -std::string TTrack::NameGet() -{ // ustalenie nazwy toru - if (this) - if (pMyNode) - return pMyNode->asName; - return "none"; -}; - void TTrack::VelocitySet(float v) { // ustawienie prędkości z ograniczeniem do pierwotnej wartości (zapisanej w scenerii) if (SwitchExtension ? SwitchExtension->fVelocity >= 0.0 : false) @@ -2677,17 +2675,17 @@ double TTrack::VelocityGet() void TTrack::ConnectionsLog() { // wypisanie informacji o połączeniach int i; - WriteLog("--> tt_Cross named " + pMyNode->asName); + WriteLog("--> tt_Cross named " + m_name); if (eType == tt_Cross) for (i = 0; i < 2; ++i) { if (SwitchExtension->pPrevs[i]) WriteLog("Point " + std::to_string(i + i + 1) + " -> track " + - SwitchExtension->pPrevs[i]->pMyNode->asName + ":" + + SwitchExtension->pPrevs[i]->m_name + ":" + std::to_string(int(SwitchExtension->iPrevDirection[i]))); if (SwitchExtension->pNexts[i]) WriteLog("Point " + std::to_string(i + i + 2) + " -> track " + - SwitchExtension->pNexts[i]->pMyNode->asName + ":" + + SwitchExtension->pNexts[i]->m_name + ":" + std::to_string(int(SwitchExtension->iNextDirection[i]))); } }; @@ -2741,3 +2739,182 @@ TTrack * TTrack::Connected(int s, double &d) const } return NULL; }; + + + +// legacy method, initializes tracks after deserialization from scenario file +void +path_table::InitTracks() { +/* + TGroundNode *Model; + int iConnection; + + for( auto *track : m_paths ) { + + track->AssignEvents( + simulation::Events.FindEvent( track->asEvent0Name ), + simulation::Events.FindEvent( track->asEvent1Name ), + simulation::Events.FindEvent( track->asEvent2Name ) ); + track->AssignallEvents( + simulation::Events.FindEvent( track->asEventall0Name ), + simulation::Events.FindEvent( track->asEventall1Name ), + simulation::Events.FindEvent( track->asEventall2Name ) ); + + auto const trackname { track->name() }; + + if( ( Global::iHiddenEvents & 1 ) + && ( false == trackname.empty() ) ) { + // jeśli podana jest nazwa torów, można szukać eventów skojarzonych przez nazwę + track->AssignEvents( + simulation::Events.FindEvent( trackname + ":event0" ), + simulation::Events.FindEvent( trackname + ":event1" ), + simulation::Events.FindEvent( trackname + ":event2" ) ); + track->AssignallEvents( + simulation::Events.FindEvent( trackname + ":eventall0" ), + simulation::Events.FindEvent( trackname + ":eventall1" ), + simulation::Events.FindEvent( trackname + ":eventall2" ) ); + } + + switch (track->eType) { + case tt_Table: { + // obrotnicę też łączymy na starcie z innymi torami + // szukamy modelu o tej samej nazwie + Model = FindGroundNode(Current->asName, TP_MODEL); + // wiązanie toru z modelem obrotnicy + track->RaAssign( + Current, + ( Model ? + Model->Model : + nullptr ), + simulation::Events.FindEvent( trackname + ":done" ), + simulation::Events.FindEvent( trackname + ":joined" ) ); + if( Model == nullptr ) { + // jak nie ma modelu to pewnie jest wykolejnica, a ta jest domyślnie zamknięta i wykoleja + break; + } + // jak coś pójdzie źle, to robimy z tego normalny tor + } + case tt_Normal: { + // tylko proste są podłączane do rozjazdów, stąd dwa rozjazdy się nie połączą ze sobą + if( track->CurrentPrev() == nullptr ) { + // tylko jeśli jeszcze nie podłączony + auto *matchingtrack = simulation::Region.FindTrack( track->CurrentSegment()->FastGetPoint_0(), iConnection, track ); + switch( iConnection ) { + case -1: // Ra: pierwsza koncepcja zawijania samochodów i statków + // if ((Track->iCategoryFlag&1)==0) //jeśli nie jest torem szynowym + // Track->ConnectPrevPrev(Track,0); //łączenie końca odcinka do samego siebie + break; + case 0: + track->ConnectPrevPrev( matchingtrack, 0 ); + break; + case 1: + track->ConnectPrevNext( matchingtrack, 1 ); + break; + case 2: + track->ConnectPrevPrev( matchingtrack, 0 ); // do Point1 pierwszego + matchingtrack->SetConnections( 0 ); // zapamiętanie ustawień w Segmencie + break; + case 3: + track->ConnectPrevNext( matchingtrack, 1 ); // do Point2 pierwszego + matchingtrack->SetConnections( 0 ); // zapamiętanie ustawień w Segmencie + break; + case 4: + matchingtrack->Switch( 1 ); + track->ConnectPrevPrev( matchingtrack, 2 ); // do Point1 drugiego + matchingtrack->SetConnections( 1 ); // robi też Switch(0) + matchingtrack->Switch( 0 ); + break; + case 5: + matchingtrack->Switch( 1 ); + track->ConnectPrevNext( matchingtrack, 3 ); // do Point2 drugiego + matchingtrack->SetConnections( 1 ); // robi też Switch(0) + matchingtrack->Switch( 0 ); + break; + } + } + if( track->CurrentNext() == nullptr ) { + // tylko jeśli jeszcze nie podłączony + auto *matchingtrack = simulation::Region.FindTrack( track->CurrentSegment()->FastGetPoint_1(), iConnection, track ); + switch( iConnection ) { + case -1: // Ra: pierwsza koncepcja zawijania samochodów i statków + // if ((Track->iCategoryFlag&1)==0) //jeśli nie jest torem szynowym + // Track->ConnectNextNext(Track,1); //łączenie końca odcinka do samego siebie + break; + case 0: + track->ConnectNextPrev( matchingtrack, 0 ); + break; + case 1: + track->ConnectNextNext( matchingtrack, 1 ); + break; + case 2: + track->ConnectNextPrev( matchingtrack, 0 ); + matchingtrack->SetConnections( 0 ); // zapamiętanie ustawień w Segmencie + break; + case 3: + track->ConnectNextNext( matchingtrack, 1 ); + matchingtrack->SetConnections( 0 ); // zapamiętanie ustawień w Segmencie + break; + case 4: + matchingtrack->Switch( 1 ); + track->ConnectNextPrev( matchingtrack, 2 ); + matchingtrack->SetConnections( 1 ); // robi też Switch(0) + // tmp->Switch(0); + break; + case 5: + matchingtrack->Switch( 1 ); + track->ConnectNextNext( matchingtrack, 3 ); + matchingtrack->SetConnections( 1 ); // robi też Switch(0) + // tmp->Switch(0); + break; + } + } + break; + } + case tt_Switch: { + // dla rozjazdów szukamy eventów sygnalizacji rozprucia + track->AssignForcedEvents( + simulation::Events.FindEvent( trackname + ":forced+" ), + simulation::Events.FindEvent( trackname + ":forced-" ) ); + break; + } + default: { + break; + } + } // switch + + // pobranie nazwy odcinka izolowanego + auto const isolatedname { track->IsolatedName() }; + if( false == isolatedname.empty() ) { + // jeśli została zwrócona nazwa + track->IsolatedEventsAssign( + simulation::Events.FindEvent( isolatedname + ":busy" ), + simulation::Events.FindEvent( isolatedname + ":free" ) ); + } + + if( ( trackname[ 0 ] == '*' ) + && ( !track->CurrentPrev() && track->CurrentNext() ) ) { + // możliwy portal, jeśli nie podłączony od strony 1 + // ustawienie flagi portalu + track->iCategoryFlag |= 0x100; + } + } + + TIsolated *isolated = TIsolated::Root(); + while( isolated ) { + // jeśli się znajdzie, to podać wskaźnik + auto *memorycell = simulation::Memory.find( isolated->asName ); // czy jest komóka o odpowiedniej nazwie + if( memorycell != nullptr ) { + // przypisanie powiązanej komórki + isolated->pMemCell = memorycell; + } + else { + // utworzenie automatycznej komórki + // TODO: determine suitable location for this one, create and add world reference node + auto *memorycell = new TMemCell( isolated->asName ); // to nie musi mieć nazwy, nazwa w wyszukiwarce wystarczy + simulation::Memory.insert( memorycell ); + isolated->pMemCell = memorycell; // wskaźnik komóki przekazany do odcinka izolowanego + } + isolated = isolated->Next(); + } +*/ +} diff --git a/Track.h b/Track.h index f396df26..fe9d4e1c 100644 --- a/Track.h +++ b/Track.h @@ -13,8 +13,10 @@ http://mozilla.org/MPL/2.0/. #include #include +#include "scenenode.h" #include "Segment.h" #include "material.h" +#include "names.h" enum TTrackType { tt_Unknown, @@ -112,12 +114,12 @@ class TIsolated }; // trajektoria ruchu - opakowanie -class TTrack { +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 +// 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 SwitchExtension; // dodatkowe dane do toru, który jest zwrotnicą std::shared_ptr Segment; @@ -126,7 +128,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...) @@ -134,6 +136,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 geometryhandle_sequence; @@ -167,7 +170,6 @@ 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: @@ -177,11 +179,13 @@ public: 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 TTrack * Create400m(int what, double dx); TTrack * NullCreate(int dir); @@ -218,7 +222,7 @@ public: SwitchExtension != nullptr ? SwitchExtension->iRoads - 1 : 1 ); } - void Load(cParser *parser, Math3D::vector3 pOrigin, std::string name); + 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); @@ -226,7 +230,16 @@ 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; } + +#ifdef EU07_USE_OLD_GROUNDCODE + void create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ); // wypełnianie VBO +#else + void create_geometry( geometrybank_handle const &Bank ); // wypełnianie VBO +#endif void RenderDynSounds(); // odtwarzanie dźwięków pojazdów jest niezależne od ich wyświetlania void RaOwnerSet(TSubRect *o) { @@ -247,7 +260,6 @@ public: bool IsGroupable(); int TestPoint( Math3D::vector3 *Point); void MovedUp1(float const dh); - std::string NameGet(); void VelocitySet(float v); double VelocityGet(); void ConnectionsLog(); @@ -257,4 +269,15 @@ public: void EnvironmentReset(); }; + + +// collection of virtual tracks and roads present in the scene +class path_table : public basic_table { + +public: + // legacy method, initializes tracks after deserialization from scenario file + void + InitTracks(); +}; + //--------------------------------------------------------------------------- diff --git a/Traction.cpp b/Traction.cpp index 4f1ab726..458dd7e7 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -90,8 +90,91 @@ jawnie nazwę sekcji, ewentualnie nazwę zasilacza (zostanie zastąpiona wskazan sekcji z sąsiedniego przęsła). */ +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(); + // 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() }; + fHeightDifference = ( pPoint3.y - pPoint1.y + pPoint4.y - pPoint2.y ) * 0.5 - minheight; + auto const segmentlength { parser->getToken() }; + iNumSections = ( + segmentlength ? + glm::length( ( pPoint1 - pPoint2 ) ) / segmentlength : + 0 ); + parser->getTokens( 2 ); + *parser + >> Wires + >> WireOffset; + m_visible = ( parser->getToken() == "vis" ); + + std::string token { parser->getToken() }; + 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(); + } + + Init(); // przeliczenie parametrów + + // calculate traction location + m_location = interpolate( pPoint2, pPoint1, 0.5 ); +} + std::size_t -TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ) { +TTraction::create_geometry( geometrybank_handle const &Bank ) { if( m_geometry != null_handle ) { return GfxRenderer.Vertices( m_geometry ).size() / 2; @@ -106,14 +189,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 +213,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 +224,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 +235,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 +245,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 +262,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 +285,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 +306,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 +336,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 ); } @@ -523,3 +606,211 @@ TTraction::wire_color() const { } 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 iConnection; + std::string name; + 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 + nTemp = FindGroundNode(traction->asPowerSupplyName, TP_TRACTIONPOWERSOURCE); + if (nTemp) // jak zasilacz znaleziony + traction->PowerSet(nTemp->psTractionPowerSource); // to podłączyć do przęsła + else if (traction->asPowerSupplyName != "*") // gwiazdka dla przęsła z izolatorem + if (traction->asPowerSupplyName != "none") // dopuszczamy na razie brak podłączenia? + { // logowanie błędu i utworzenie zasilacza o domyślnej zawartości + ErrorLog("Missed TractionPowerSource: " + traction->asPowerSupplyName); + nTemp = new TGroundNode(); + nTemp->iType = TP_TRACTIONPOWERSOURCE; + nTemp->asName = traction->asPowerSupplyName; + nTemp->psTractionPowerSource = new TTractionPowerSource(nTemp); + nTemp->psTractionPowerSource->Init(traction->NominalVoltage, traction->MaxCurrent); + nTemp->nNext = nRootOfType[nTemp->iType]; // ostatni dodany dołączamy na końcu + // nowego + nRootOfType[nTemp->iType] = nTemp; // ustawienie nowego na początku listy + } + } + + for( auto *traction : m_items ) { + + if (!traction->hvNext[0]) // tylko jeśli jeszcze nie podłączony + { + tmp = FindTraction(traction->pPoint1, iConnection, nCurrent); + switch (iConnection) + { + case 0: + traction->Connect(0, tmp, 0); + break; + case 1: + traction->Connect(0, tmp, 1); + break; + } + if (traction->hvNext[0]) // jeśli został podłączony + if (traction->psSection && tmp->psSection) // tylko przęsło z izolatorem może nie + // mieć zasilania, bo ma 2, trzeba + // sprawdzać sąsiednie + if (traction->psSection != + tmp->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 (traction->psSection->bSection && !tmp->psSection->bSection) + { //(tmp->psSection) jest podstacją, a (Traction->psSection) nazwą sekcji + tmp->PowerSet(traction->psSection); // zastąpienie wskazaniem sekcji + } + else if (!traction->psSection->bSection && tmp->psSection->bSection) + { //(Traction->psSection) jest podstacją, a (tmp->psSection) nazwą sekcji + traction->PowerSet(tmp->psSection); // zastąpienie wskazaniem sekcji + } + else // jeśli obie to sekcje albo obie podstacje, to będzie błąd + ErrorLog("Bad power: at " + + to_string(traction->pPoint1.x, 2, 6) + " " + + to_string(traction->pPoint1.y, 2, 6) + " " + + to_string(traction->pPoint1.z, 2, 6)); + } + } + if (!traction->hvNext[1]) // tylko jeśli jeszcze nie podłączony + { + tmp = FindTraction(traction->pPoint2, iConnection, nCurrent); + switch (iConnection) + { + case 0: + traction->Connect(1, tmp, 0); + break; + case 1: + traction->Connect(1, tmp, 1); + break; + } + if (traction->hvNext[1]) // jeśli został podłączony + if (traction->psSection && tmp->psSection) // tylko przęsło z izolatorem może nie + // mieć zasilania, bo ma 2, trzeba + // sprawdzać sąsiednie + if (traction->psSection != tmp->psSection) + { // to może być albo podłączenie podstacji lub kabiny sekcyjnej do sekcji, albo + // błąd + if (traction->psSection->bSection && !tmp->psSection->bSection) + { //(tmp->psSection) jest podstacją, a (Traction->psSection) nazwą sekcji + tmp->PowerSet(traction->psSection); // zastąpienie wskazaniem sekcji + } + else if (!traction->psSection->bSection && tmp->psSection->bSection) + { //(Traction->psSection) jest podstacją, a (tmp->psSection) nazwą sekcji + traction->PowerSet(tmp->psSection); // zastąpienie wskazaniem sekcji + } + else // jeśli obie to sekcje albo obie podstacje, to będzie błąd + ErrorLog("Bad power: at " + + to_string(traction->pPoint2.x, 2, 6) + " " + + to_string(traction->pPoint2.y, 2, 6) + " " + + to_string(traction->pPoint2.z, 2, 6)); + } + } + } + + iConnection = 0; // teraz będzie licznikiem końców + for( auto *traction : m_items ) { + // operacje mające na celu wykrywanie bieżni wspólnych i łączenie przęseł naprążania + if (traction->WhereIs()) // oznakowanie przedostatnich przęseł + { // poszukiwanie bieżni wspólnej dla przedostatnich przęseł, również w celu połączenia + // zasilania + // to się nie sprawdza, bo połączyć się mogą dwa niezasilane odcinki jako najbliższe + // sobie + // nCurrent->hvTraction->hvParallel=TractionNearestFind(nCurrent->pCenter,0,nCurrent); + // //szukanie najbliższego przęsła + // trzeba by zliczać końce, a potem wpisać je do tablicy, aby sukcesywnie podłączać do + // zasilaczy + traction->iTries = 5; // oznaczanie końcowych + ++iConnection; + } + if (traction->fResistance[0] == 0.0) + { + traction->ResistanceCalc(); // obliczanie przęseł w segmencie z bezpośrednim zasilaniem + // ErrorLog("Section "+nCurrent->hvTraction->asPowerSupplyName+" connected"); //jako + // niby błąd będzie bardziej widoczne + traction->iTries = 0; // nie potrzeba mu szukać zasilania + } + // if (!Traction->hvParallel) //jeszcze utworzyć pętle z bieżni wspólnych + } + int zg = 0; // zgodność kierunku przęseł, tymczasowo iterator do tabeli końców + // końców jest ok. 10 razy mniej niż wszystkich przęseł (Quark: 216) + TGroundNode **nEnds = new TGroundNode *[iConnection]; + for( auto *traction : m_items ) { + //łączenie bieżni wspólnych, w tym oznaczanie niepodanych jawnie + if (!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) // jeśli jeszcze nie został włączony w kółko + { + auto *nTemp = find( traction->asParallel ); + if (nTemp) + { // o ile zostanie znalezione przęsło o takiej nazwie + if (!nTemp->hvParallel) // 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) + ErrorLog("Missed overhead: " + traction->asParallel); // logowanie braku + } + if (traction->iTries > 0) // jeśli zaznaczony do podłączenia + // if (!nCurrent->hvTraction->psPower[0]||!nCurrent->hvTraction->psPower[1]) + if (zg < iConnection) // zabezpieczenie + nEnds[zg++] = nCurrent; // wypełnianie tabeli końców w celu szukania im połączeń + } + while( zg < iConnection ) { + // zapełnienie do końca tablicy, jeśli by jakieś końce wypadły + nEnds[ zg++ ] = nullptr; + } + zg = 1; // nieefektywny przebieg kończy łączenie + while (zg) + { // ustalenie zastępczej rezystancji dla każdego przęsła + zg = 0; // flaga podłączonych przęseł końcowych: -1=puste wskaźniki, 0=coś zostało, + // 1=wykonano łączenie + for (int i = 0; i < iConnection; ++i) + if (nEnds[i]) // załatwione będziemy zerować + { // każdy przebieg to próba podłączenia końca segmentu naprężania do innego zasilanego + // przęsła + if (nEnds[i]->hvTraction->hvNext[0]) + { // jeśli końcowy ma ciąg dalszy od strony 0 (Point1), szukamy odcinka najbliższego + // do Point2 + if (TractionNearestFind(nEnds[i]->hvTraction->pPoint2, 0, + nEnds[i])) // poszukiwanie przęsła + { + nEnds[i] = NULL; + zg = 1; // jak coś zostało podłączone, to może zasilanie gdzieś dodatkowo + // dotrze + } + } + else if (nEnds[i]->hvTraction->hvNext[1]) + { // jeśli końcowy ma ciąg dalszy od strony 1 (Point2), szukamy odcinka najbliższego + // do Point1 + if (TractionNearestFind(nEnds[i]->hvTraction->pPoint1, 1, + nEnds[i])) // poszukiwanie przęsła + { + nEnds[i] = NULL; + zg = 1; // jak coś zostało podłączone, to może zasilanie gdzieś dodatkowo + // dotrze + } + } + else + { // gdy koniec jest samotny, to na razie nie zostanie podłączony (nie powinno + // takich być) + nEnds[i] = NULL; + } + } + } + delete[] nEnds; // nie potrzebne już +*/ +} diff --git a/Traction.h b/Traction.h index 49304a30..b07fe017 100644 --- a/Traction.h +++ b/Traction.h @@ -10,14 +10,16 @@ http://mozilla.org/MPL/2.0/. #pragma once #include -#include "GL/glew.h" -#include "dumb3d.h" -#include "openglgeometrybank.h" + +#include "scenenode.h" +#include "Segment.h" +#include "material.h" +#include "names.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 @@ -29,7 +31,7 @@ class TTraction 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 }; @@ -47,11 +49,22 @@ class TTraction int iTries { 0 }; 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 ); + virtual ~TTraction() = default; + + void Load( cParser *parser, glm::dvec3 const &pOrigin ); + // set origin point + void + origin( glm::dvec3 Origin ) { + m_origin = Origin; } // 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(); @@ -63,4 +76,15 @@ private: glm::vec3 wire_color() const; }; + + +// collection of virtual tracks and roads present in the scene +class traction_table : public basic_table { + +public: + // legacy method, initializes traction after deserialization from scenario file + void + InitTraction(); +}; + //--------------------------------------------------------------------------- diff --git a/Train.cpp b/Train.cpp index fde9fd70..4a33dd9d 100644 --- a/Train.cpp +++ b/Train.cpp @@ -1568,7 +1568,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 ); } } diff --git a/Train.h b/Train.h index 80b67bc0..9fe73f93 100644 --- a/Train.h +++ b/Train.h @@ -73,6 +73,8 @@ 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) diff --git a/World.cpp b/World.cpp index 2559d914..53c0634a 100644 --- a/World.cpp +++ b/World.cpp @@ -16,6 +16,7 @@ http://mozilla.org/MPL/2.0/. #include "World.h" #include "Globals.h" +#include "simulation.h" #include "Logs.h" #include "MdlMngr.h" #include "renderer.h" @@ -242,6 +243,7 @@ bool TWorld::Init( GLFWwindow *Window ) { GfxRenderer.Render(); WriteLog( "World setup..." ); +#ifdef EU07_USE_OLD_GROUNDCODE if( true == Ground.Init( Global::SceneryFile ) ) { WriteLog( "...world setup done" ); } @@ -249,6 +251,9 @@ bool TWorld::Init( GLFWwindow *Window ) { ErrorLog( "...world setup failed" ); return false; } +#else + if( false == simulation::State.deserialize( Global::SceneryFile ) ) { return false; } +#endif simulation::Time.init(); @@ -299,6 +304,7 @@ bool TWorld::Init( GLFWwindow *Window ) { // if (!Global::bMultiplayer) //na razie włączone { // eventy aktywowane z klawiatury tylko dla jednego użytkownika +#ifdef EU07_USE_OLD_GROUNDCODE KeyEvents[0] = Ground.FindEvent("keyctrl00"); KeyEvents[1] = Ground.FindEvent("keyctrl01"); KeyEvents[2] = Ground.FindEvent("keyctrl02"); @@ -309,6 +315,18 @@ bool TWorld::Init( GLFWwindow *Window ) { KeyEvents[7] = Ground.FindEvent("keyctrl07"); KeyEvents[8] = Ground.FindEvent("keyctrl08"); KeyEvents[9] = Ground.FindEvent("keyctrl09"); +#else + KeyEvents[ 0 ] = simulation::Events.FindEvent( "keyctrl00" ); + KeyEvents[ 1 ] = simulation::Events.FindEvent( "keyctrl01" ); + KeyEvents[ 2 ] = simulation::Events.FindEvent( "keyctrl02" ); + KeyEvents[ 3 ] = simulation::Events.FindEvent( "keyctrl03" ); + KeyEvents[ 4 ] = simulation::Events.FindEvent( "keyctrl04" ); + KeyEvents[ 5 ] = simulation::Events.FindEvent( "keyctrl05" ); + KeyEvents[ 6 ] = simulation::Events.FindEvent( "keyctrl06" ); + KeyEvents[ 7 ] = simulation::Events.FindEvent( "keyctrl07" ); + KeyEvents[ 8 ] = simulation::Events.FindEvent( "keyctrl08" ); + KeyEvents[ 9 ] = simulation::Events.FindEvent( "keyctrl09" ); +#endif } WriteLog( "Load time: " + @@ -396,7 +414,11 @@ void TWorld::OnKeyDown(int cKey) { // z [Shift] uruchomienie eventu if (!Global::iPause) // podczas pauzy klawisze nie działają if (KeyEvents[i]) +#ifdef EU07_USE_OLD_GROUNDCODE Ground.AddToQuery(KeyEvents[i], NULL); +#else + simulation::Events.AddToQuery( KeyEvents[ i ], NULL ); +#endif } else // zapamiętywanie kamery może działać podczas pauzy if (FreeFlyModeFlag) // w trybie latania można przeskakiwać do ustawionych kamer @@ -685,8 +707,7 @@ void TWorld::OnKeyDown(int cKey) temp->MoverParameters->DecBrakeMult()) if (Train) { // dźwięk oczywiście jest w kabinie - Train->dsbSwitch->SetVolume(DSBVOLUME_MAX); - Train->dsbSwitch->Play(0, 0, 0); + Train->play_sound( Train->dsbSwitch ); } } } @@ -715,8 +736,7 @@ void TWorld::OnKeyDown(int cKey) tmp->iLights[CouplNr] = (tmp->iLights[CouplNr] & ~mask) | set; if (Train) { // Ra: ten dźwięk z kabiny to przegięcie, ale na razie zostawiam - Train->dsbSwitch->SetVolume(DSBVOLUME_MAX); - Train->dsbSwitch->Play(0, 0, 0); + Train->play_sound( Train->dsbSwitch ); } } } @@ -736,8 +756,7 @@ void TWorld::OnKeyDown(int cKey) if (temp->MoverParameters->IncLocalBrakeLevelFAST()) if (Train) { // dźwięk oczywiście jest w kabinie - Train->dsbPneumaticRelay->SetVolume(-80); - Train->dsbPneumaticRelay->Play(0, 0, 0); + Train->play_sound( Train->dsbPneumaticRelay ); } } } @@ -756,8 +775,7 @@ void TWorld::OnKeyDown(int cKey) if (temp->MoverParameters->DecLocalBrakeLevelFAST()) if (Train) { // dźwięk oczywiście jest w kabinie - Train->dsbPneumaticRelay->SetVolume(-80); - Train->dsbPneumaticRelay->Play(0, 0, 0); + Train->play_sound( Train->dsbPneumaticRelay ); } } } @@ -1045,10 +1063,14 @@ bool TWorld::Update() TSubModel::iInstance = 0; } +#ifdef EU07_USE_OLD_GROUNDCODE Ground.CheckQuery(); +#else + simulation::Events.CheckQuery(); +#endif Ground.Update_Hidden(); - Ground.Update_Lights(); + simulation::Lights.update(); // render time routines follow: @@ -1818,19 +1840,24 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) CommLog( Now() + " " + std::to_string(pRozkaz->iComm) + " scenery" + " rcvd"); Ground.WyslijString(Global::SceneryFile, 1); // nazwa scenerii break; - case 2: // event - CommLog( Now() + " " + std::to_string(pRozkaz->iComm) + " " + - std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd"); - if (Global::iMultiplayer) - { // WriteLog("Komunikat: "+AnsiString(pRozkaz->Name1)); + case 2: { + // event + CommLog( Now() + " " + std::to_string( pRozkaz->iComm ) + " " + + std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) + " rcvd" ); +/* + // TODO: re-enable when messaging module is in place + if( Global::iMultiplayer ) { + // WriteLog("Komunikat: "+AnsiString(pRozkaz->Name1)); TEvent *e = Ground.FindEvent( - std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0]))); - if (e) - if ((e->Type == tp_Multiple) || (e->Type == tp_Lights) || - (e->evJoined != 0)) // tylko jawne albo niejawne Multiple - Ground.AddToQuery(e, NULL); // drugi parametr to dynamic wywołujący - tu brak + std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); + if( e ) + if( ( e->Type == tp_Multiple ) || ( e->Type == tp_Lights ) || + ( e->evJoined != 0 ) ) // tylko jawne albo niejawne Multiple + Ground.AddToQuery( e, NULL ); // drugi parametr to dynamic wywołujący - tu brak } +*/ break; + } case 3: // rozkaz dla AI if (Global::iMultiplayer) { diff --git a/World.h b/World.h index ac97a1dd..471a6262 100644 --- a/World.h +++ b/World.h @@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/. #include #include "Camera.h" #include "Ground.h" +#include "scene.h" #include "sky.h" #include "sun.h" #include "moon.h" diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index 00ac0856..a6d77415 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -228,6 +228,18 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -446,6 +458,18 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/renderer.cpp b/renderer.cpp index bef13681..4943cafa 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/. #include "renderer.h" #include "globals.h" #include "timer.h" +#include "simulation.h" #include "world.h" #include "train.h" #include "data.h" @@ -53,9 +54,9 @@ opengl_camera::update_frustum( glm::mat4 const &Projection, glm::mat4 const &Mod // returns true if specified object is within camera frustum, false otherwise bool -opengl_camera::visible( bounding_area const &Area ) const { +opengl_camera::visible( scene::bounding_area const &Area ) const { - return ( m_frustum.sphere_inside( Area.center, Area.radius ) > 0.0f ); + return ( m_frustum.sphere_inside( Area.center, Area.radius ) > 0.f ); } bool @@ -325,10 +326,10 @@ opengl_renderer::Render() { ++m_framestamp; Render_pass( rendermode::color ); - m_drawcount = m_drawqueue.size(); + m_drawcount = m_cellqueue.size(); // accumulate last 20 frames worth of render time (cap at 1000 fps to prevent calculations going awry) m_drawtimecolorpass = std::max( 20.f, 0.95f * m_drawtimecolorpass + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - drawstartcolorpass ) ).count() / 1000.f ); - m_debuginfo += "frame total: " + to_string( m_drawtimecolorpass / 20.f, 2 ) + " msec (" + std::to_string( m_drawqueue.size() ) + " sectors) "; + m_debuginfo += "frame total: " + to_string( m_drawtimecolorpass / 20.f, 2 ) + " msec (" + std::to_string( m_cellqueue.size() ) + " sectors) "; glfwSwapBuffers( m_window ); @@ -360,12 +361,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { m_worldcamera, rendermode::color, 0.f, - std::min( - 1.f, - Global::shadowtune.depth / ( Global::BaseDrawRange * Global::fDistanceFactor ) - * std::max( - 1.f, - Global::ZoomFactor * 0.5f ) ), + 1.0, true ); #endif // setup shadowmap matrix @@ -433,10 +429,18 @@ opengl_renderer::Render_pass( rendermode const Mode ) { } #endif switch_units( true, true, true ); +#ifdef EU07_USE_OLD_GROUNDCODE Render( &World.Ground ); +#else + Render( simulation::Region ); +#endif // ...translucent parts setup_drawing( true ); +#ifdef EU07_USE_OLD_GROUNDCODE Render_Alpha( &World.Ground ); +#else + Render_Alpha( simulation::Region ); +#endif if( World.Train != nullptr ) { // cab render is performed without shadows, due to low resolution and number of models without windows :| switch_units( true, false, false ); @@ -486,7 +490,11 @@ opengl_renderer::Render_pass( rendermode const Mode ) { #else setup_units( false, false, false ); #endif +#ifdef EU07_USE_OLD_GROUNDCODE Render( &World.Ground ); +#else + Render( simulation::Region ); +#endif // post-render restore ::glDisable( GL_POLYGON_OFFSET_FILL ); ::glDisable( GL_SCISSOR_TEST ); @@ -494,7 +502,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target m_drawtimeshadowpass = 0.95f * m_drawtimeshadowpass + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - shadowdrawstart ) ).count() / 1000.f; - m_debuginfo += "shadows: " + to_string( m_drawtimeshadowpass / 20.f, 2 ) + " msec (" + std::to_string( m_drawqueue.size() ) + " sectors) "; + m_debuginfo += "shadows: " + to_string( m_drawtimeshadowpass / 20.f, 2 ) + " msec (" + std::to_string( m_cellqueue.size() ) + " sectors) "; } break; } @@ -516,7 +524,11 @@ opengl_renderer::Render_pass( rendermode const Mode ) { // opaque parts... setup_drawing( false ); setup_units( true, true, true ); +#ifdef EU07_USE_OLD_GROUNDCODE Render( &World.Ground ); +#else + Render( simulation::Region ); +#endif /* // reflections are limited to sky and ground only, the update rate is too low for anything else // ...translucent parts @@ -569,7 +581,11 @@ opengl_renderer::Render_pass( rendermode const Mode ) { // opaque parts... setup_drawing( false ); setup_units( false, false, false ); +#ifdef EU07_USE_OLD_GROUNDCODE Render( &World.Ground ); +#else + Render( simulation::Region ); +#endif // post-render cleanup } break; @@ -1337,12 +1353,11 @@ opengl_renderer::Texture( texture_handle const Texture ) const { return m_textures.texture( Texture ); } - - +#ifdef EU07_USE_OLD_GROUNDCODE bool opengl_renderer::Render( TGround *Ground ) { - m_drawqueue.clear(); + m_cellqueue.clear(); glm::vec3 const cameraposition { m_renderpass.camera.position() }; int const camerax = static_cast( std::floor( cameraposition.x / 1000.0f ) + iNumRects / 2 ); @@ -1354,25 +1369,29 @@ opengl_renderer::Render( TGround *Ground ) { switch( m_renderpass.draw_mode ) { case rendermode::color: { - Update_Lights( Ground->m_lights ); + Update_Lights( simulation::Lights ); for( int column = originx; column <= originx + segmentcount; ++column ) { for( int row = originz; row <= originz + segmentcount; ++row ) { auto *cell = &Ground->Rects[ column ][ row ]; +#ifdef EU07_USE_DEBUG_CULLING + if( m_worldcamera.camera.visible( cell->m_area ) ) { +#else if( m_renderpass.camera.visible( cell->m_area ) ) { +#endif Render( cell ); } } } // draw queue was filled while rendering content of ground cells. now sort the nodes based on their distance to viewer... std::sort( - std::begin( m_drawqueue ), - std::end( m_drawqueue ), + std::begin( m_cellqueue ), + std::end( m_cellqueue ), []( distancesubcell_pair const &Left, distancesubcell_pair const &Right ) { return ( Left.first ) < ( Right.first ); } ); // ...then render the opaque content of the visible subcells. - for( auto subcellpair : m_drawqueue ) { + for( auto subcellpair : m_cellqueue ) { Render( subcellpair.second ); } break; @@ -1404,7 +1423,7 @@ opengl_renderer::Render( TGround *Ground ) { } // they can also skip queue sorting, as they only deal with opaque geometry // NOTE: there's benefit from rendering front-to-back, but is it significant enough? TODO: investigate - for( auto subcellpair : m_drawqueue ) { + for( auto subcellpair : m_cellqueue ) { Render( subcellpair.second ); } break; @@ -1475,7 +1494,7 @@ opengl_renderer::Render( TGroundRect *Groundcell ) { auto subcell = Groundcell->pSubRects + subcellindex; if( subcell->iNodeCount ) { // o ile są jakieś obiekty, bo po co puste sektory przelatywać - m_drawqueue.emplace_back( + m_cellqueue.emplace_back( glm::length2( m_renderpass.camera.position() - glm::dvec3( subcell->m_area.center ) ), subcell ); } @@ -1577,6 +1596,407 @@ opengl_renderer::Render( TSubRect *Groundsubcell ) { return true; } +#else +void +opengl_renderer::Render( scene::basic_region &Region ) { + + m_sectionqueue.clear(); + m_cellqueue.clear(); + + // build a list of region sections to render + glm::vec3 const cameraposition { m_renderpass.camera.position() }; + auto const camerax = static_cast( std::floor( cameraposition.x / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2 ) ); + auto const cameraz = static_cast( std::floor( cameraposition.z / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2 ) ); + int const segmentcount = 2 * static_cast( std::ceil( m_renderpass.draw_range * Global::fDistanceFactor / scene::EU07_SECTIONSIZE ) ); + int const originx = camerax - segmentcount / 2; + int const originz = cameraz - segmentcount / 2; + + for( int row = originz; row <= originz + segmentcount; ++row ) { + if( row < 0 ) { continue; } + if( row >= scene::EU07_REGIONSIDESECTIONCOUNT ) { break; } + for( int column = originx; column <= originx + segmentcount; ++column ) { + if( column < 0 ) { continue; } + if( column >= scene::EU07_REGIONSIDESECTIONCOUNT ) { break; } + auto §ion { Region.m_sections[ row * scene::EU07_REGIONSIDESECTIONCOUNT + column ] }; + if( ( true == section.m_active ) +#ifdef EU07_USE_DEBUG_CULLING + && ( m_worldcamera.camera.visible( section.m_area ) ) ) { +#else + && ( m_renderpass.camera.visible( section.m_area ) ) ) { +#endif + m_sectionqueue.emplace_back( §ion ); + } + } + } + + switch( m_renderpass.draw_mode ) { + case rendermode::color: { + + Update_Lights( simulation::Lights ); + + Render( std::begin( m_sectionqueue ), std::end( m_sectionqueue ) ); +/* + // draw queue was filled while rendering content of ground cells. now sort the nodes based on their distance to viewer... + // TODO: move sorting for translucent phase, for opaque geometry render cells in initial order to reduce vbo switching + std::sort( + std::begin( m_cellqueue ), + std::end( m_cellqueue ), + []( distancecell_pair const &Left, distancecell_pair const &Right ) { + return ( Left.first < Right.first ); } ); +*/ + // ...then render the opaque content of the visible cells. + Render( std::begin( m_cellqueue ), std::end( m_cellqueue ) ); + break; + } + case rendermode::shadows: + case rendermode::pickscenery: { + // these render modes don't bother with lights + Render( std::begin( m_sectionqueue ), std::end( m_sectionqueue ) ); + // they can also skip queue sorting, as they only deal with opaque geometry + // NOTE: there's benefit from rendering front-to-back, but is it significant enough? TODO: investigate + Render( std::begin( m_cellqueue ), std::end( m_cellqueue ) ); + break; + } + case rendermode::reflections: { + // for the time being reflections render only terrain geometry + Render( std::begin( m_sectionqueue ), std::end( m_sectionqueue ) ); + break; + } + case rendermode::pickcontrols: + default: { + // no need to render anything ourside of the cab in control picking mode + break; + } + } +} + +void +opengl_renderer::Render( section_sequence::iterator First, section_sequence::iterator Last ) { + + switch( m_renderpass.draw_mode ) { + case rendermode::color: + case rendermode::reflections: { + + break; + } + case rendermode::shadows: { + // experimental, for shadows render both back and front faces, to supply back faces of the 'forest strips' + ::glDisable( GL_CULL_FACE ); + break; } + case rendermode::pickscenery: { + // non-interactive scenery elements get neutral colour + ::glColor3fv( glm::value_ptr( colors::none ) ); + break; + } + default: { + break; } + } + + while( First != Last ) { + + auto *section = *First; + section->create_geometry(); + + // render shapes held by the section + switch( m_renderpass.draw_mode ) { + case rendermode::color: + case rendermode::reflections: + case rendermode::shadows: + case rendermode::pickscenery: { + if( false == section->m_shapes.empty() ) { + // since all shapes of the section share center point we can optimize out a few calls here + ::glPushMatrix(); + auto const originoffset { section->m_area.center - m_renderpass.camera.position() }; + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + // render +#ifdef EU07_USE_DEBUG_CULLING + // debug + float const width = scene::EU07_SECTIONSIZE * 0.5f; + float const height = scene::EU07_SECTIONSIZE * 0.2f; + glDisable( GL_LIGHTING ); + glDisable( GL_TEXTURE_2D ); + glColor3ub( 255, 128, 128 ); + glBegin( GL_LINE_LOOP ); + glVertex3f( -width, height, width ); + glVertex3f( -width, height, -width ); + glVertex3f( width, height, -width ); + glVertex3f( width, height, width ); + glEnd(); + glBegin( GL_LINE_LOOP ); + glVertex3f( -width, 0, width ); + glVertex3f( -width, 0, -width ); + glVertex3f( width, 0, -width ); + glVertex3f( width, 0, width ); + glEnd(); + glBegin( GL_LINES ); + glVertex3f( -width, height, width ); glVertex3f( -width, 0, width ); + glVertex3f( -width, height, -width ); glVertex3f( -width, 0, -width ); + glVertex3f( width, height, -width ); glVertex3f( width, 0, -width ); + glVertex3f( width, height, width ); glVertex3f( width, 0, width ); + glEnd(); + glColor3ub( 255, 255, 255 ); + glEnable( GL_TEXTURE_2D ); + glEnable( GL_LIGHTING ); +#endif + // shapes + for( auto const &shape : section->m_shapes ) { Render( shape ); } + // post-render cleanup + ::glPopMatrix(); + } + break; + } + case rendermode::pickcontrols: + default: { + break; + } + } + + // add the section's cells to the cell queue + switch( m_renderpass.draw_mode ) { + case rendermode::color: + case rendermode::shadows: + case rendermode::pickscenery: { + for( auto &cell : section->m_cells ) { + if( ( true == cell.m_active ) +#ifdef EU07_USE_DEBUG_CULLING + && ( m_worldcamera.camera.visible( cell.m_area ) ) ) { +#else + && ( m_renderpass.camera.visible( cell.m_area ) ) ) { +#endif + // store visible cells with content as well as their current distance, for sorting later + m_cellqueue.emplace_back( + glm::length2( m_renderpass.camera.position() - cell.m_area.center ), + &cell ); + } + } + break; + } + case rendermode::reflections: + case rendermode::pickcontrols: + default: { + break; + } + } + // proceed to next section + ++First; + } + + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // restore standard face cull mode + ::glEnable( GL_CULL_FACE ); + break; } + default: { + break; } + } +} + +void +opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator Last ) { + + // cache initial iterator for the second sweep + auto first { First }; + // first pass draws elements which we know are located in section banks, to reduce vbo switching + while( First != Last ) { + + auto const *cell = First->second; +/* + // przeliczenia animacji torów w sektorze + Groundsubcell->RaAnimate( m_framestamp ); +*/ + switch( m_renderpass.draw_mode ) { + case rendermode::color: { + // since all shapes of the section share center point we can optimize out a few calls here + ::glPushMatrix(); + auto const originoffset { cell->m_area.center - m_renderpass.camera.position() }; + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + + // render +#ifdef EU07_USE_DEBUG_CULLING + // debug + float const width = scene::EU07_CELLSIZE * 0.5f; + float const height = scene::EU07_CELLSIZE * 0.15f; + glDisable( GL_LIGHTING ); + glDisable( GL_TEXTURE_2D ); + glColor3ub( 255, 255, 0 ); + glBegin( GL_LINE_LOOP ); + glVertex3f( -width, height, width ); + glVertex3f( -width, height, -width ); + glVertex3f( width, height, -width ); + glVertex3f( width, height, width ); + glEnd(); + glBegin( GL_LINE_LOOP ); + glVertex3f( -width, 0, width ); + glVertex3f( -width, 0, -width ); + glVertex3f( width, 0, -width ); + glVertex3f( width, 0, width ); + glEnd(); + glBegin( GL_LINES ); + glVertex3f( -width, height, width ); glVertex3f( -width, 0, width ); + glVertex3f( -width, height, -width ); glVertex3f( -width, 0, -width ); + glVertex3f( width, height, -width ); glVertex3f( width, 0, -width ); + glVertex3f( width, height, width ); glVertex3f( width, 0, width ); + glEnd(); + glColor3ub( 255, 255, 255 ); + glEnable( GL_TEXTURE_2D ); + glEnable( GL_LIGHTING ); +#endif + // opaque non-instanced shapes + for( auto const &shape : cell->m_shapesopaque ) { Render( shape ); } + // tracks + // TODO: update after path node refactoring + for( auto *path : cell->m_paths ) { Render( path ); } + // TODO: add other content types + + // post-render cleanup + ::glPopMatrix(); + + break; + } + case rendermode::shadows: { + // since all shapes of the section share center point we can optimize out a few calls here + ::glPushMatrix(); + auto const originoffset { cell->m_area.center - m_renderpass.camera.position() }; + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + + // render + // opaque non-instanced shapes + for( auto const &shape : cell->m_shapesopaque ) { Render( shape ); } + // TODO: add other content types + + // post-render cleanup + ::glPopMatrix(); + + break; + } + case rendermode::pickscenery: { + // same procedure like with regular render, but editor-enabled nodes receive custom colour used for picking + // since all shapes of the section share center point we can optimize out a few calls here + ::glPushMatrix(); + auto const originoffset { cell->m_area.center - m_renderpass.camera.position() }; + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + // render + // opaque non-instanced shapes + for( auto const &shape : cell->m_shapesopaque ) { Render( shape ); } + // tracks + // TODO: add path to the node picking list + for( auto *path : cell->m_paths ) { Render( path ); } + // TODO: add other content types + // post-render cleanup + ::glPopMatrix(); + + break; + } + case rendermode::reflections: + case rendermode::pickcontrols: + default: { + break; + } + } + + ++First; + } + // second pass draws elements with their own vbos + while( first != Last ) { + + auto const *cell = first->second; + + switch( m_renderpass.draw_mode ) { + case rendermode::color: + case rendermode::shadows: + case rendermode::pickscenery: { + // render + // opaque parts of instanced models + for( auto *instance : cell->m_instancesopaque ) { Render( instance ); } + // TODO: add remaining content types + break; + } + case rendermode::reflections: + case rendermode::pickcontrols: + default: { + break; + } + } + + ++first; + } +} + +void +opengl_renderer::Render( scene::shape_node const &Shape ) { + + auto const &data { Shape.data() }; + // setup + Bind_Material( data.material ); + switch( m_renderpass.draw_mode ) { + case rendermode::color: + case rendermode::reflections: { + ::glColor3fv( glm::value_ptr( data.lighting.diffuse ) ); +/* + // NOTE: ambient component is set by diffuse component + // NOTE: for the time being non-instanced shapes are rendered without specular component due to wrong/arbitrary values set in legacy scenarios + // TBD, TODO: find a way to resolve this with the least amount of tears? + ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( data.lighting.specular * Global::DayLight.specular.a * m_specularopaquescalefactor ) ); +*/ + break; + } + // pick modes are painted with custom colours, and shadow pass doesn't use any + case rendermode::shadows: + case rendermode::pickscenery: + case rendermode::pickcontrols: + default: { + break; + } + } + // render + m_geometry.draw( data.geometry ); +} + +void +opengl_renderer::Render( TAnimModel *Instance ) { + + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees + distancesquared = SquareMagnitude( ( Instance->m_location - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + default: { + distancesquared = SquareMagnitude( ( Instance->m_location - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + } + if( ( distancesquared < Instance->m_rangesquaredmin ) + || ( distancesquared >= Instance->m_rangesquaredmax ) ) { + return; + } +/* + // TODO: enable adding items to the picking list + switch( m_renderpass.draw_mode ) { + case rendermode::pickscenery: { + // add the node to the pick list + m_picksceneryitems.emplace_back( Node ); + break; + } + default: { + break; + } + } +*/ + Instance->RaAnimate( m_framestamp ); // jednorazowe przeliczenie animacji + Instance->RaPrepare(); + if( Instance->pModel ) { + // renderowanie rekurencyjne submodeli + Render( + Instance->pModel, + Instance->Material(), + distancesquared, + Instance->m_location - m_renderpass.camera.position(), + Instance->vAngle ); + } +} +#endif bool opengl_renderer::Render( TGroundNode *Node ) { @@ -2272,6 +2692,9 @@ opengl_renderer::Render( TTrack *Track ) { && ( Track->m_material2 == 0 ) ) { return; } + if( false == Track->m_visible ) { + return; + } switch( m_renderpass.draw_mode ) { case rendermode::color: @@ -2311,7 +2734,7 @@ void opengl_renderer::Render( TMemCell *Memcell ) { ::glPushMatrix(); - auto const position = Memcell->Position() - m_renderpass.camera.position(); + auto const position = Memcell->location() - m_renderpass.camera.position(); ::glTranslated( position.x, position.y + 0.5, position.z ); switch( m_renderpass.draw_mode ) { @@ -2342,27 +2765,28 @@ opengl_renderer::Render( TMemCell *Memcell ) { ::glPopMatrix(); } +#ifdef EU07_USE_OLD_GROUNDCODE bool opengl_renderer::Render_Alpha( TGround *Ground ) { TGroundNode *node; TSubRect *tmp; // Ra: renderowanie progresywne - zależne od FPS oraz kierunku patrzenia - for( auto subcellpair = std::rbegin( m_drawqueue ); subcellpair != std::rend( m_drawqueue ); ++subcellpair ) { + for( auto subcellpair = std::rbegin( m_cellqueue ); subcellpair != std::rend( m_cellqueue ); ++subcellpair ) { // przezroczyste trójkąty w oddzielnym cyklu przed modelami tmp = subcellpair->second; for( node = tmp->nRenderRectAlpha; node; node = node->nNext3 ) { Render_Alpha( node ); } } - for( auto subcellpair = std::rbegin( m_drawqueue ); subcellpair != std::rend( m_drawqueue ); ++subcellpair ) + for( auto subcellpair = std::rbegin( m_cellqueue ); subcellpair != std::rend( m_cellqueue ); ++subcellpair ) { // renderowanie przezroczystych modeli oraz pojazdów Render_Alpha( subcellpair->second ); } ::glDisable( GL_LIGHTING ); // linie nie powinny świecić - for( auto subcellpair = std::rbegin( m_drawqueue ); subcellpair != std::rend( m_drawqueue ); ++subcellpair ) { + for( auto subcellpair = std::rbegin( m_cellqueue ); subcellpair != std::rend( m_cellqueue ); ++subcellpair ) { // druty na końcu, żeby się nie robiły białe plamy na tle lasu tmp = subcellpair->second; for( node = tmp->nRenderWires; node; node = node->nNext3 ) { @@ -2391,7 +2815,177 @@ opengl_renderer::Render_Alpha( TSubRect *Groundsubcell ) { return true; } +#else +void +opengl_renderer::Render_Alpha( scene::basic_region &Region ) { + // sort the nodes based on their distance to viewer + std::sort( + std::begin( m_cellqueue ), + std::end( m_cellqueue ), + []( distancecell_pair const &Left, distancecell_pair const &Right ) { + return ( Left.first ) < ( Right.first ); } ); + + Render_Alpha( std::rbegin( m_cellqueue ), std::rend( m_cellqueue ) ); +/* + ::glDisable( GL_LIGHTING ); // linie nie powinny świecić + for( auto subcellpair = std::rbegin( m_cellqueue ); subcellpair != std::rend( m_cellqueue ); ++subcellpair ) { + // druty na końcu, żeby się nie robiły białe plamy na tle lasu + tmp = subcellpair->second; + for( node = tmp->nRenderWires; node; node = node->nNext3 ) { + Render_Alpha( node ); + } + } + ::glEnable( GL_LIGHTING ); +*/ +} + +void +opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_sequence::reverse_iterator Last ) { + + // first pass draws elements which we know are located in section banks, to reduce vbo switching + { + auto first { First }; + while( first != Last ) { + + auto const *cell = first->second; + + if( false == cell->m_shapestranslucent.empty() ) { + // since all shapes of the cell share center point we can optimize out a few calls here + ::glPushMatrix(); + auto const originoffset{ cell->m_area.center - m_renderpass.camera.position() }; + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + // render + // NOTE: we can reuse the method used to draw opaque geometry + for( auto const &shape : cell->m_shapestranslucent ) { Render( shape ); } + // post-render cleanup + ::glPopMatrix(); + } + + ++first; + } + } + // second pass draws elements with their own vbos + { + auto first { First }; + while( first != Last ) { + + auto const *cell = first->second; + + // translucent parts of instanced models + for( auto *instance : cell->m_instancetranslucent ) { Render_Alpha( instance ); } + // TODO: add remaining content types + + ++first; + } + } + // third pass draws the wires; + // wires use section vbos, but for the time being we want to draw them at the very end + { + auto first{ First }; + while( first != Last ) { + + auto const *cell = first->second; + // TODO: include lines here + if( false == cell->m_traction.empty() ) { + // since all shapes of the cell share center point we can optimize out a few calls here + ::glPushMatrix(); + auto const originoffset { cell->m_area.center - m_renderpass.camera.position() }; + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + if( !Global::bSmoothTraction ) { + // na liniach kiepsko wygląda - robi gradient + ::glDisable( GL_LINE_SMOOTH ); + } + Bind_Material( null_handle ); + // render + for( auto *traction : cell->m_traction ) { Render_Alpha( traction ); } + // post-render cleanup + ::glLineWidth( 1.0 ); + if( !Global::bSmoothTraction ) { + ::glEnable( GL_LINE_SMOOTH ); + } + ::glPopMatrix(); + } + + ++first; + } + } +} + +void +opengl_renderer::Render_Alpha( TAnimModel *Instance ) { + + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees + distancesquared = SquareMagnitude( ( Instance->m_location - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + default: { + distancesquared = SquareMagnitude( ( Instance->m_location - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + } + if( ( distancesquared < Instance->m_rangesquaredmin ) + || ( distancesquared >= Instance->m_rangesquaredmax ) ) { + return; + } + + Instance->RaPrepare(); + if( Instance->pModel ) { + // renderowanie rekurencyjne submodeli + Render_Alpha( + Instance->pModel, + Instance->Material(), + distancesquared, + Instance->m_location - m_renderpass.camera.position(), + Instance->vAngle ); + } +} + +void +opengl_renderer::Render_Alpha( TTraction *Traction ) { + + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees + distancesquared = SquareMagnitude( ( Traction->m_location - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + default: { + distancesquared = SquareMagnitude( ( Traction->m_location - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + } + if( ( distancesquared < Traction->m_rangesquaredmin ) + || ( distancesquared >= Traction->m_rangesquaredmax ) ) { + return; + } + + if( false == Traction->m_visible ) { + return; + } + // rysuj jesli sa druty i nie zerwana + if( ( Traction->Wires == 0 ) + || ( true == TestFlag( Traction->DamageFlag, 128 ) ) ) { + return; + } + // setup + float const linealpha = static_cast( + std::min( + 1.25, + 5000 * Traction->WireThickness / ( distancesquared + 1.0 ) ) ); // zbyt grube nie są dobre + ::glLineWidth( linealpha ); + // McZapkie-261102: kolor zalezy od materialu i zasniedzenia + auto const color { Traction->wire_color() }; + ::glColor4f( color.r, color.g, color.b, linealpha ); + + // render + m_geometry.draw( Traction->m_geometry ); +} +#endif bool opengl_renderer::Render_Alpha( TGroundNode *Node ) { @@ -2857,6 +3451,8 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { Render_Alpha( Submodel->Next ); }; + + // utility methods TSubModel const * opengl_renderer::Update_Pick_Control() { diff --git a/renderer.h b/renderer.h index 70141315..1009e715 100644 --- a/renderer.h +++ b/renderer.h @@ -15,12 +15,14 @@ http://mozilla.org/MPL/2.0/. #include "lightarray.h" #include "dumb3d.h" #include "frustum.h" +#include "scene.h" #include "world.h" #include "memcell.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 @@ -225,7 +227,13 @@ private: diffuse }; - typedef std::pair< double, TSubRect * > distancesubcell_pair; +#ifdef EU07_USE_OLD_GROUNDCODE + using distancesubcell_pair = std::pair< double, TSubRect * >; +#else + using section_sequence = std::vector; + using distancecell_pair = std::pair; + using cell_sequence = std::vector; +#endif struct renderpass_config { @@ -266,12 +274,25 @@ private: Render_reflections(); bool Render( world_environment *Environment ); +#ifdef EU07_USE_OLD_GROUNDCODE bool Render( TGround *Ground ); bool Render( TGroundRect *Groundcell ); bool Render( TSubRect *Groundsubcell ); +#else + 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 ); + void + Render( TAnimModel *Instance ); +#endif bool Render( TGroundNode *Node ); bool @@ -288,10 +309,21 @@ private: Render_cab( TDynamicObject *Dynamic, bool const Alpha = false ); void Render( TMemCell *Memcell ); +#ifdef EU07_USE_OLD_GROUNDCODE bool Render_Alpha( TGround *Ground ); bool Render_Alpha( TSubRect *Groundsubcell ); +#else + 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 ); +#endif bool Render_Alpha( TGroundNode *Node ); bool @@ -361,13 +393,18 @@ private: std::string m_pickdebuginfo; 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 m_drawqueue; // list of subcells to be drawn in current render pass +#ifdef EU07_USE_OLD_GROUNDCODE + std::vector m_cellqueue; // list of subcells to be drawn in current render pass +#else + section_sequence m_sectionqueue; // list of sections in current render pass + cell_sequence m_cellqueue; +#endif std::vector m_picksceneryitems; std::vector m_pickcontrolsitems; diff --git a/scene.cpp b/scene.cpp new file mode 100644 index 00000000..a6d04d96 --- /dev/null +++ b/scene.cpp @@ -0,0 +1,579 @@ +/* +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 "scene.h" + +#include "renderer.h" +#include "logs.h" + +namespace scene { + +// adds provided shape to the cell +void +basic_cell::insert( shape_node Shape ) { + + m_active = true; + + auto const &shapedata { Shape.data() }; + auto &shapes = ( + shapedata.translucent ? + m_shapestranslucent : + m_shapesopaque ); + for( auto &targetshape : shapes ) { + // try to merge shapes with matching view ranges... + auto const &targetshapedata { targetshape.data() }; + if( ( shapedata.rangesquared_min == targetshapedata.rangesquared_min ) + && ( shapedata.rangesquared_max == targetshapedata.rangesquared_max ) + // ...and located close to each other (within arbitrary limit of 25m) + && ( glm::length( shapedata.area.center - targetshapedata.area.center ) < 25.0 ) ) { + + if( true == targetshape.merge( Shape ) ) { + // if the shape was merged there's nothing left to do + return; + } + } + } + // otherwise add the shape to the relevant list + Shape.origin( m_area.center ); + shapes.emplace_back( Shape ); +} + +// adds provided path to the cell +void +basic_cell::insert( TTrack *Path ) { + + m_active = true; + + // TODO: add animation hook + Path->origin( m_area.center ); + m_paths.emplace_back( Path ); +} + +// adds provided traction piece to the cell +void +basic_cell::insert( TTraction *Traction ) { + + m_active = true; + + Traction->origin( m_area.center ); + m_traction.emplace_back( Traction ); +} + +// adds provided model instance to the cell +void +basic_cell::insert( TAnimModel *Instance ) { + + m_active = true; + + auto const flags = Instance->Flags(); + auto alpha = + ( Instance->Material() != nullptr ? + Instance->Material()->textures_alpha : + 0x30300030 ); + + // assign model to appropriate render phases + if( alpha & flags & 0x2F2F002F ) { + // translucent pieces + m_instancetranslucent.emplace_back( Instance ); + } + alpha ^= 0x0F0F000F; // odwrócenie flag tekstur, aby wyłapać nieprzezroczyste + if( alpha & flags & 0x1F1F001F ) { + // opaque pieces + m_instancesopaque.emplace_back( Instance ); + } +} + +// generates renderable version of held non-instanced geometry +void +basic_cell::create_geometry( geometrybank_handle const &Bank ) { + + if( false == m_active ) { return; } // nothing to do here + + for( auto &shape : m_shapesopaque ) { shape.create_geometry( Bank ); } + for( auto &shape : m_shapestranslucent ) { shape.create_geometry( Bank ); } +#ifndef EU07_USE_OLD_GROUNDCODE + for( auto *path : m_paths ) { path->create_geometry( Bank ); } + for( auto *traction : m_traction ) { traction->create_geometry( Bank ); } +#endif +} + +// sets center point of the section +void +basic_cell::center( glm::dvec3 Center ) { + + m_area.center = Center; + // NOTE: we should also update origin point for the contained nodes, but in practice we can skip this + // as all nodes will be added only after the proper center point was set, and won't change +} + + + +// adds provided shape to the section +void +basic_section::insert( shape_node Shape ) { + + m_active = true; + + auto const &shapedata = Shape.data(); + if( ( true == shapedata.translucent ) + || ( shapedata.rangesquared_max <= 90000.0 ) + || ( shapedata.rangesquared_min > 0.0 ) ) { + // small, translucent or not always visible shapes are placed in the sub-cells + cell( shapedata.area.center ).insert( Shape ); + } + else { + // large, opaque shapes are placed on section level + for( auto &shape : m_shapes ) { + // check first if the shape can't be merged with one of the shapes already present in the section + if( true == shape.merge( Shape ) ) { + // if the shape was merged there's nothing left to do + return; + } + } + // otherwise add the shape to the section's list + Shape.origin( m_area.center ); + m_shapes.emplace_back( Shape ); + } +} + +// adds provided path to the section +void +basic_section::insert( TTrack *Path ) { + + m_active = true; + // pass the node to the appropriate partitioning cell + // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done + cell( Path->location() ).insert( Path ); +} + +// adds provided path to the section +void +basic_section::insert( TTraction *Traction ) { + + m_active = true; + // pass the node to the appropriate partitioning cell + // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done + cell( Traction->location() ).insert( Traction ); +} + +// adds provided model instance to the section +void +basic_section::insert( TAnimModel *Instance ) { + + m_active = true; + // pass the node to the appropriate partitioning cell + // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done + cell( Instance->location() ).insert( Instance ); +} + +// sets center point of the section +void +basic_section::center( glm::dvec3 Center ) { + + m_area.center = Center; + // set accordingly center points of the section's partitioning cells + // NOTE: we should also update origin point for the contained nodes, but in practice we can skip this + // as all nodes will be added only after the proper center point was set, and won't change + auto const centeroffset = -( EU07_SECTIONSIZE / EU07_CELLSIZE / 2 * EU07_CELLSIZE ) + EU07_CELLSIZE / 2; + glm::dvec3 sectioncornercenter { m_area.center + glm::dvec3{ centeroffset, 0, centeroffset } }; + auto row { 0 }, column { 0 }; + for( auto &cell : m_cells ) { + cell.center( sectioncornercenter + glm::dvec3{ column * EU07_CELLSIZE, 0.0, row * EU07_CELLSIZE } ); + if( ++column >= EU07_SECTIONSIZE / EU07_CELLSIZE ) { + ++row; + column = 0; + } + } +} + +// generates renderable version of held non-instanced geometry +void +basic_section::create_geometry() { + + if( true == m_geometrycreated ) { return; } + else { + // mark it done for future checks + m_geometrycreated = true; + } + + if( false == m_active ) { return; } // nothing to do here + + // since sections can be empty, we're doing lazy initialization of the geometry bank, when something may actually use it + if( m_geometrybank == null_handle ) { + m_geometrybank = GfxRenderer.Create_Bank(); + } + + for( auto &shape : m_shapes ) { + shape.create_geometry( m_geometrybank ); + } + for( auto &cell : m_cells ) { + cell.create_geometry( m_geometrybank ); + } +} + +// provides access to section enclosing specified point +basic_cell & +basic_section::cell( glm::dvec3 const &Location ) { + + auto const column = static_cast( std::floor( ( Location.x - ( m_area.center.x - EU07_SECTIONSIZE / 2 ) ) / EU07_CELLSIZE ) ); + auto const row = static_cast( std::floor( ( Location.z - ( m_area.center.z - EU07_SECTIONSIZE / 2 ) ) / EU07_CELLSIZE ) ); + + return + m_cells[ + clamp( row, 0, ( EU07_SECTIONSIZE / EU07_CELLSIZE ) - 1 ) * ( EU07_SECTIONSIZE / EU07_CELLSIZE ) + + clamp( column, 0, ( EU07_SECTIONSIZE / EU07_CELLSIZE ) - 1 ) ] ; +} + + + +basic_region::basic_region() { + + // initialize centers of sections: + // calculate center of 'top left' region section... + auto const centeroffset = -( EU07_REGIONSIDESECTIONCOUNT / 2 * EU07_SECTIONSIZE ) + EU07_SECTIONSIZE / 2; + glm::dvec3 regioncornercenter { centeroffset, 0, centeroffset }; + auto row { 0 }, column { 0 }; + // ...move through section array assigning centers left to right, front/top to back/bottom + for( auto §ion : m_sections ) { + section.center( regioncornercenter + glm::dvec3{ column * EU07_SECTIONSIZE, 0.0, row * EU07_SECTIONSIZE } ); + if( ++column >= EU07_REGIONSIDESECTIONCOUNT ) { + ++row; + column = 0; + } + } +} + +void +basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad ) { + + // shape might need to be split into smaller pieces, so we create list of nodes instead of just single one + // using deque so we can do single pass iterating and addding generated pieces without invalidating anything + std::deque shapes { Shape }; + auto &shape = shapes.front(); + if( shape.m_data.vertices.empty() ) { return; } + + // adjust input if necessary: + if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) { + // rotate... + auto const rotation = glm::radians( Scratchpad.location_rotation ); + for( auto &vertex : shape.m_data.vertices ) { + vertex.position = glm::rotateZ( vertex.position, rotation.z ); + vertex.position = glm::rotateX( vertex.position, rotation.x ); + vertex.position = glm::rotateY( vertex.position, rotation.y ); + vertex.normal = glm::rotateZ( vertex.normal, rotation.z ); + vertex.normal = glm::rotateX( vertex.normal, rotation.x ); + vertex.normal = glm::rotateY( vertex.normal, rotation.y ); + } + } + if( ( false == Scratchpad.location_offset.empty() ) + && ( Scratchpad.location_offset.top() != glm::dvec3( 0, 0, 0 ) ) ) { + // ...and move + auto const offset = Scratchpad.location_offset.top(); + for( auto &vertex : shape.m_data.vertices ) { + vertex.position += offset; + } + } + // calculate bounding area + for( auto const &vertex : shape.m_data.vertices ) { + shape.m_data.area.center += vertex.position; + } + shape.m_data.area.center /= shape.m_data.vertices.size(); + // trim the shape if needed. trimmed parts will be added to list as separate nodes + for( std::size_t index = 0; index < shapes.size(); ++index ) { + while( true == RaTriangleDivider( shapes[ index ], shapes ) ) { + ; // all work is done during expression check + } + // with the trimming done we can calculate shape's bounding radius + shape.compute_radius(); + } + // move the data into appropriate section(s) + for( auto &shape : shapes ) { + + if( point_inside( shape.m_data.area.center ) ) { + // NOTE: nodes placed outside of region boundaries are discarded + section( shape.m_data.area.center ).insert( shape ); + } + else { + ErrorLog( + "Bad scenario: shape node" + ( + shape.m_name.empty() ? + "" : + " \"" + shape.m_name + "\"" ) + + " placed in location outside region bounds (" + to_string( shape.m_data.area.center ) + ")" ); + } + } +} + +// inserts provided track in the region +void +basic_region::insert_path( TTrack *Path, scratch_data &Scratchpad ) { + + // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done + auto center = Path->location(); + + if( point_inside( center ) ) { + // NOTE: nodes placed outside of region boundaries are discarded + section( center ).insert( Path ); + } + else { + // tracks are guaranteed to hava a name so we can skip the check + ErrorLog( "Bad scenario: track node \"" + Path->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); + } +} + +// inserts provided track in the region +void +basic_region::insert_traction( TTraction *Traction, scratch_data &Scratchpad ) { + + // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done + auto center = Traction->location(); + + if( point_inside( center ) ) { + // NOTE: nodes placed outside of region boundaries are discarded + section( center ).insert( Traction ); + } + else { + // tracks are guaranteed to hava a name so we can skip the check + ErrorLog( "Bad scenario: traction node \"" + Traction->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); + } +} + +// inserts provided instance of 3d model in the region +void +basic_region::insert_instance( TAnimModel *Instance, scratch_data &Scratchpad ) { + + // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done + auto center = Instance->location(); + + if( point_inside( center ) ) { + // NOTE: nodes placed outside of region boundaries are discarded + section( center ).insert( Instance ); + } + else { + // tracks are guaranteed to hava a name so we can skip the check + ErrorLog( "Bad scenario: model node \"" + Instance->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); + } +} + +// checks whether specified point is within boundaries of the region +bool +basic_region::point_inside( glm::dvec3 const &Location ) { + + double const regionboundary = EU07_REGIONSIDESECTIONCOUNT / 2 * EU07_SECTIONSIZE; + return ( ( Location.x > -regionboundary ) && ( Location.x < regionboundary ) + && ( Location.z > -regionboundary ) && ( Location.z < regionboundary ) ); +} + +// trims provided shape to fit into a section, adds trimmed part at the end of provided list +// NOTE: legacy function. TBD, TODO: clean it up? +bool +basic_region::RaTriangleDivider( shape_node &Shape, std::deque &Shapes ) { + + if( Shape.m_data.vertices.size() != 3 ) { + // tylko gdy jeden trójkąt + return false; + } + + auto const margin { 200.0 }; + auto x0 = EU07_SECTIONSIZE * std::floor( 0.001 * Shape.m_data.area.center.x ) - margin; + auto x1 = x0 + EU07_SECTIONSIZE + margin * 2; + auto z0 = EU07_SECTIONSIZE * std::floor( 0.001 * Shape.m_data.area.center.z ) - margin; + auto z1 = z0 + EU07_SECTIONSIZE + margin * 2; + + if( ( Shape.m_data.vertices[ 0 ].position.x >= x0 ) && ( Shape.m_data.vertices[ 0 ].position.x <= x1 ) + && ( Shape.m_data.vertices[ 0 ].position.z >= z0 ) && ( Shape.m_data.vertices[ 0 ].position.z <= z1 ) + && ( Shape.m_data.vertices[ 1 ].position.x >= x0 ) && ( Shape.m_data.vertices[ 1 ].position.x <= x1 ) + && ( Shape.m_data.vertices[ 1 ].position.z >= z0 ) && ( Shape.m_data.vertices[ 1 ].position.z <= z1 ) + && ( Shape.m_data.vertices[ 2 ].position.x >= x0 ) && ( Shape.m_data.vertices[ 2 ].position.x <= x1 ) + && ( Shape.m_data.vertices[ 2 ].position.z >= z0 ) && ( Shape.m_data.vertices[ 2 ].position.z <= z1 ) ) { + // trójkąt wystający mniej niż 200m z kw. kilometrowego jest do przyjęcia + return false; + } + // Ra: przerobić na dzielenie na 2 trójkąty, podział w przecięciu z siatką kilometrową + // Ra: i z rekurencją będzie dzielić trzy trójkąty, jeśli będzie taka potrzeba + int divide { -1 }; // bok do podzielenia: 0=AB, 1=BC, 2=CA; +4=podział po OZ; +8 na x1/z1 + double + min { 0.0 }, + mul; // jeśli przechodzi przez oś, iloczyn będzie ujemny + x0 += margin; + x1 -= margin; // przestawienie na siatkę + z0 += margin; + z1 -= margin; + // AB na wschodzie + mul = ( Shape.m_data.vertices[ 0 ].position.x - x0 ) * ( Shape.m_data.vertices[ 1 ].position.x - x0 ); + if( mul < min ) { + min = mul; + divide = 0; + } + // BC na wschodzie + mul = ( Shape.m_data.vertices[ 1 ].position.x - x0 ) * ( Shape.m_data.vertices[ 2 ].position.x - x0 ); + if( mul < min ) { + min = mul; + divide = 1; + } + // CA na wschodzie + mul = ( Shape.m_data.vertices[ 2 ].position.x - x0 ) * ( Shape.m_data.vertices[ 0 ].position.x - x0 ); + if( mul < min ) { + min = mul; + divide = 2; + } + // AB na zachodzie + mul = ( Shape.m_data.vertices[ 0 ].position.x - x1 ) * ( Shape.m_data.vertices[ 1 ].position.x - x1 ); + if( mul < min ) { + min = mul; + divide = 8; + } + // BC na zachodzie + mul = ( Shape.m_data.vertices[ 1 ].position.x - x1 ) * ( Shape.m_data.vertices[ 2 ].position.x - x1 ); + if( mul < min ) { + min = mul; + divide = 9; + } + // CA na zachodzie + mul = ( Shape.m_data.vertices[ 2 ].position.x - x1 ) * ( Shape.m_data.vertices[ 0 ].position.x - x1 ); + if( mul < min ) { + min = mul; + divide = 10; + } + // AB na południu + mul = ( Shape.m_data.vertices[ 0 ].position.z - z0 ) * ( Shape.m_data.vertices[ 1 ].position.z - z0 ); + if( mul < min ) { + min = mul; + divide = 4; + } + // BC na południu + mul = ( Shape.m_data.vertices[ 1 ].position.z - z0 ) * ( Shape.m_data.vertices[ 2 ].position.z - z0 ); + if( mul < min ) { + min = mul; + divide = 5; + } + // CA na południu + mul = ( Shape.m_data.vertices[ 2 ].position.z - z0 ) * ( Shape.m_data.vertices[ 0 ].position.z - z0 ); + if( mul < min ) { + min = mul; + divide = 6; + } + // AB na północy + mul = ( Shape.m_data.vertices[ 0 ].position.z - z1 ) * ( Shape.m_data.vertices[ 1 ].position.z - z1 ); + if( mul < min ) { + min = mul; + divide = 12; + } + // BC na północy + mul = ( Shape.m_data.vertices[ 1 ].position.z - z1 ) * ( Shape.m_data.vertices[ 2 ].position.z - z1 ); + if( mul < min ) { + min = mul; + divide = 13; + } + // CA na północy + mul = (Shape.m_data.vertices[2].position.z - z1) * (Shape.m_data.vertices[0].position.z - z1); + if( mul < min ) { + divide = 14; + } + + // tworzymy jeden dodatkowy trójkąt, dzieląc jeden bok na przecięciu siatki kilometrowej + Shapes.emplace_back( Shape ); // copy current shape + auto &newshape = Shapes.back(); + + switch (divide & 3) { + // podzielenie jednego z boków, powstaje wierzchołek D + case 0: { + // podział AB (0-1) -> ADC i DBC + newshape.m_data.vertices[ 2 ] = Shape.m_data.vertices[ 2 ]; // wierzchołek C jest wspólny + newshape.m_data.vertices[ 1 ] = Shape.m_data.vertices[ 1 ]; // wierzchołek B przechodzi do nowego + if( divide & 4 ) { + Shape.m_data.vertices[ 1 ].set_from_z( + Shape.m_data.vertices[ 0 ], + Shape.m_data.vertices[ 1 ], + ( ( divide & 8 ) ? + z1 : + z0 ) ); + } + else { + Shape.m_data.vertices[ 1 ].set_from_x( + Shape.m_data.vertices[ 0 ], + Shape.m_data.vertices[ 1 ], + ( ( divide & 8 ) ? + x1 : + x0 ) ); + } + newshape.m_data.vertices[ 0 ] = Shape.m_data.vertices[ 1 ]; // wierzchołek D jest wspólny + break; + } + case 1: { + // podział BC (1-2) -> ABD i ADC + newshape.m_data.vertices[ 0 ] = Shape.m_data.vertices[ 0 ]; // wierzchołek A jest wspólny + newshape.m_data.vertices[ 2 ] = Shape.m_data.vertices[ 2 ]; // wierzchołek C przechodzi do nowego + if( divide & 4 ) { + Shape.m_data.vertices[ 2 ].set_from_z( + Shape.m_data.vertices[ 1 ], + Shape.m_data.vertices[ 2 ], + ( ( divide & 8 ) ? + z1 : + z0 ) ); + } + else { + Shape.m_data.vertices[ 2 ].set_from_x( + Shape.m_data.vertices[ 1 ], + Shape.m_data.vertices[ 2 ], + ( ( divide & 8 ) ? + x1 : + x0 ) ); + } + newshape.m_data.vertices[ 1 ] = Shape.m_data.vertices[ 2 ]; // wierzchołek D jest wspólny + break; + } + case 2: { + // podział CA (2-0) -> ABD i DBC + newshape.m_data.vertices[ 1 ] = Shape.m_data.vertices[ 1 ]; // wierzchołek B jest wspólny + newshape.m_data.vertices[ 2 ] = Shape.m_data.vertices[ 2 ]; // wierzchołek C przechodzi do nowego + if( divide & 4 ) { + Shape.m_data.vertices[ 2 ].set_from_z( + Shape.m_data.vertices[ 2 ], + Shape.m_data.vertices[ 0 ], + ( ( divide & 8 ) ? + z1 : + z0 ) ); + } + else { + Shape.m_data.vertices[ 2 ].set_from_x( + Shape.m_data.vertices[ 2 ], + Shape.m_data.vertices[ 0 ], + ( ( divide & 8 ) ? + x1 : + x0 ) ); + } + newshape.m_data.vertices[ 0 ] = Shape.m_data.vertices[ 2 ]; // wierzchołek D jest wspólny + break; + } + } + // przeliczenie środków ciężkości obu + Shape.m_data.area.center = ( Shape.m_data.vertices[ 0 ].position + Shape.m_data.vertices[ 1 ].position + Shape.m_data.vertices[ 2 ].position ) / 3.0; + newshape.m_data.area.center = ( newshape.m_data.vertices[ 0 ].position + newshape.m_data.vertices[ 1 ].position + newshape.m_data.vertices[ 2 ].position ) / 3.0; + + return true; +} + +// provides access to section enclosing specified point +basic_section & +basic_region::section( glm::dvec3 const &Location ) { + + auto const column = static_cast( std::floor( Location.x / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ); + auto const row = static_cast( std::floor( Location.z / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ); + + return + m_sections[ + clamp( row, 0, EU07_REGIONSIDESECTIONCOUNT - 1 ) * EU07_REGIONSIDESECTIONCOUNT + + clamp( column, 0, EU07_REGIONSIDESECTIONCOUNT - 1 ) ] ; +} + +} // scene + +//--------------------------------------------------------------------------- diff --git a/scene.h b/scene.h new file mode 100644 index 00000000..a03ec979 --- /dev/null +++ b/scene.h @@ -0,0 +1,172 @@ +/* +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 +#include +#include +#include + +#include "parser.h" +#include "openglgeometrybank.h" +#include "scenenode.h" +#include "track.h" +#include "traction.h" + +namespace scene { + +int const EU07_CELLSIZE = 250; +int const EU07_SECTIONSIZE = 1000; +int const EU07_REGIONSIDESECTIONCOUNT = 500; // number of 1km sections along a side of square region + +struct scratch_data { + + std::stack location_offset; + glm::vec3 location_rotation; +}; + +// 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 + // adds provided shape to the cell + void + insert( shape_node Shape ); + // 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 ); + // generates renderable version of held non-instanced geometry in specified geometry bank + void + create_geometry( geometrybank_handle const &Bank ); + // sets center point of the cell + void + center( glm::dvec3 Center ); + +private: +// types + using shapenode_sequence = std::vector; + using path_sequence = std::vector; + using traction_sequence = std::vector; + using instance_sequence = std::vector; +// members + scene::bounding_area m_area { glm::dvec3(), static_cast( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * 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 + path_sequence m_paths; // path pieces + instance_sequence m_instancesopaque; + instance_sequence m_instancetranslucent; + traction_sequence m_traction; +}; + +// basic scene partitioning structure, holds terrain geometry and collection of cells +class basic_section { + + friend class opengl_renderer; + +public: +// methods + // adds provided shape to the section + void + insert( shape_node Shape ); + // adds provided path to the section + void + insert( TTrack *Path ); + // adds provided path to the section + void + insert( TTraction *Traction ); + // adds provided model instance to the section + void + insert( TAnimModel *Instance ); + // generates renderable version of held non-instanced geometry + void + create_geometry(); + // sets center point of the section + void + center( glm::dvec3 Center ); + +private: +// types + using cell_array = std::array; + using shapenode_sequence = std::vector; +// 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( 0.5 * M_SQRT2 * EU07_SECTIONSIZE + 0.25 * EU07_SECTIONSIZE ) }; + bool m_active { false }; // whether the section holds any actual data, itself or in the cells + // 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(); +// methods + // inserts provided shape in the region + void + insert_shape( shape_node Shape, scratch_data &Scratchpad ); + // inserts provided track in the region + void + insert_path( TTrack *Path, 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 ); + + +private: +// types + using section_array = std::array; + +// methods + // checks whether specified point is within boundaries of the region + bool + point_inside( glm::dvec3 const &Location ); + // trims provided shape to fit into a section, adds trimmed part at the end of provided list + bool + RaTriangleDivider( shape_node &Shape, std::deque &Shapes ); + // provides access to section enclosing specified point + basic_section & + section( glm::dvec3 const &Location ); + +// members + section_array m_sections; + +}; + +} // scene + +//--------------------------------------------------------------------------- diff --git a/scenenode.cpp b/scenenode.cpp new file mode 100644 index 00000000..015bdd92 --- /dev/null +++ b/scenenode.cpp @@ -0,0 +1,296 @@ +/* +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, 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::max() ); + + std::string token = Input.getToken(); + if( token == "material" ) { + // lighting settings + token = Input.getToken(); + 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(); + } + token = Input.getToken(); + } + + // 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(); + + } while( token != "endtri" ); + + 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( 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().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( 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::max() ); +} + + +} // editor +//--------------------------------------------------------------------------- diff --git a/scenenode.h b/scenenode.h new file mode 100644 index 00000000..fed77544 --- /dev/null +++ b/scenenode.h @@ -0,0 +1,253 @@ +/* +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 + +#include "material.h" +#include "vertex.h" +#include "openglgeometrybank.h" +#include "parser.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 { 0.0 }; + 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 + 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 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, node_data const &Nodedata ); + // 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 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 vertices; // world space source data of the geometry + glm::dvec3 origin; // world position of the relative coordinate system origin + using geometryhandle_sequence = std::vector; + 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; } + glm::dvec3 const & + location() { + return m_location; }; + void + location( glm::dvec3 const Location ) { + m_location = Location; } + bool + visible() const { + return m_visible; } + +protected: +// members + glm::dvec3 m_location; + 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 + +//--------------------------------------------------------------------------- diff --git a/simulation.cpp b/simulation.cpp new file mode 100644 index 00000000..a522a636 --- /dev/null +++ b/simulation.cpp @@ -0,0 +1,521 @@ +/* +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" + +namespace simulation { + +state_manager State; +event_manager Events; +memory_manager Memory; +path_table Paths; +traction_table Traction; +instance_manager Instances; +light_array Lights; +scene::basic_region Region; + +bool +state_manager::deserialize( std::string const &Scenariofile ) { + + // TODO: check first for presence of serialized binary files + // if this fails, fall back on the legacy text format + cParser scenarioparser( Scenariofile, cParser::buffer_FILE, Global::asCurrentSceneryPath, Global::bLoadTraction ); + + if( false == scenarioparser.ok() ) { return false; } + + deserialize( scenarioparser ); + // TODO: initialize links between loaded nodes + + return true; +} + +// restores class data from provided stream +void +state_manager::deserialize( cParser &Input ) { + + scene::scratch_data importscratchpad; + // 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 }, + { "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; + 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( importscratchpad ) ) ); + } + // deserialize content from the provided input + std::string token { Input.getToken() }; + 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 ) + ")" ); + } + + token = Input.getToken(); + } +} + +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() }; + 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(); + } +} + +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_firstinit( cParser &Input, scene::scratch_data &Scratchpad ) { + + // TODO: implement + simulation::Paths.InitTracks(); + simulation::Traction.InitTraction(); + simulation::Events.InitEvents(); +} + +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 ) { + + 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" ) { + // TODO: implement + skip_until( Input, "enddynamic" ); + } + else if( nodedata.type == "track" ) { + + auto const inputline = Input.Line(); // cache in case we need to report error + + auto *path { deserialize_path( Input, Scratchpad, nodedata ) }; + // duplicates of named tracks are currently experimentally allowed + if( simulation::Paths.insert( path ) ) { + simulation::Region.insert_path( path, Scratchpad ); + } + else { + ErrorLog( "Bad scenario: track with duplicate name, \"" + path->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); +/* + delete path; + delete pathnode; +*/ + } + } + else if( nodedata.type == "traction" ) { + + auto const inputline = Input.Line(); // cache in case we need to report error + + auto *traction { deserialize_traction( Input, Scratchpad, nodedata ) }; + // duplicates of named tracks are currently discarded + if( simulation::Traction.insert( traction ) ) { + simulation::Region.insert_traction( traction, Scratchpad ); + } + else { + ErrorLog( "Bad scenario: traction piece with duplicate name, \"" + traction->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } + } + else if( nodedata.type == "tractionpowersource" ) { + // TODO: implement + skip_until( Input, "end" ); + } + else if( nodedata.type == "model" ) { + + if( nodedata.range_min < 0.0 ) { + // convert and import 3d terrain + } + else { + // regular instance of 3d mesh + auto const inputline = Input.Line(); // cache in case we need to report error + + auto *instance { deserialize_model( Input, Scratchpad, nodedata ) }; + // model import can potentially fail + if( instance == nullptr ) { return; } + + if( simulation::Instances.insert( instance ) ) { + simulation::Region.insert_instance( instance, Scratchpad ); + } + else { + ErrorLog( "Bad scenario: 3d model instance with duplicate name, \"" + instance->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } + } + } + else if( ( nodedata.type == "triangles" ) + || ( nodedata.type == "triangle_strip" ) + || ( nodedata.type == "triangle_fan" ) ) { + + simulation::Region.insert_shape( scene::shape_node().deserialize( Input, nodedata ), Scratchpad ); + } + else if( ( nodedata.type == "lines" ) + || ( nodedata.type == "line_strip" ) + || ( nodedata.type == "line_loop" ) ) { + + // TODO: implement + skip_until( Input, "endline" ); + } + else if( nodedata.type == "memcell" ) { + + auto const inputline = Input.Line(); // cache in case we need to report error + + auto *memorycell { deserialize_memorycell( Input, Scratchpad, nodedata ) }; + // duplicates of named tracks are currently discarded + if( simulation::Memory.insert( memorycell ) ) { +/* + // TODO: implement this + simulation::Region.insert_memorycell( memorycell, Scratchpad ); +*/ + } + else { + ErrorLog( "Bad scenario: memory cell with duplicate name, \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); +/* + delete memorycell; + delete memorycellnode; +*/ + } + } + else if( nodedata.type == "eventlauncher" ) { + // TODO: implement + skip_until( Input, "end" ); + } + else if( nodedata.type == "sound" ) { + // TODO: implement + skip_until( Input, "endsound" ); + } + +} + +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 " + 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() ); + 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 ) { + + // TODO: implement + skip_until( Input, "endtrainset" ); +} + +void +state_manager::deserialize_endtrainset( cParser &Input, scene::scratch_data &Scratchpad ) { + + // TODO: implement +} + +// 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 ) { + + // 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; +} + +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; +} + +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; + // adjust location + + 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; +} + +// skips content of stream until specified token +void +state_manager::skip_until( cParser &Input, std::string const &Token ) { + + std::string token { Input.getToken() }; + while( ( false == token.empty() ) + && ( token != Token ) ) { + + token = Input.getToken(); + } +} + +// 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( Location, rotation.y ); // Ra 2014-11: uwzględnienie rotacji + } + if( false == Scratchpad.location_offset.empty() ) { + Location += Scratchpad.location_offset.top(); + } + return Location; +} + +} // simulation + +//--------------------------------------------------------------------------- diff --git a/simulation.h b/simulation.h new file mode 100644 index 00000000..be7da4dc --- /dev/null +++ b/simulation.h @@ -0,0 +1,73 @@ +/* +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 "event.h" +#include "memcell.h" +#include "track.h" +#include "traction.h" +#include "animmodel.h" +#include "scene.h" +#include "lightarray.h" +#include "parser.h" + +namespace simulation { + +class state_manager { + +public: +// types + +// methods + bool + deserialize( std::string const &Scenariofile ); + +private: +// methods + // restores class data from provided stream + void deserialize( cParser &Input ); + 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_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 ); + TMemCell * deserialize_memorycell( 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 ); + // 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_manager Memory; +extern path_table Paths; +extern traction_table Traction; +extern instance_manager Instances; +extern light_array Lights; +extern scene::basic_region Region; + +} // simulation + +//--------------------------------------------------------------------------- diff --git a/stdafx.h b/stdafx.h index c9a7f419..c08f2fed 100644 --- a/stdafx.h +++ b/stdafx.h @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -93,3 +94,5 @@ #include #include "openglmatrixstack.h" + +//#define EU07_USE_OLD_GROUNDCODE diff --git a/vertex.cpp b/vertex.cpp new file mode 100644 index 00000000..79093460 --- /dev/null +++ b/vertex.cpp @@ -0,0 +1,35 @@ +/* +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 "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; +} + +//--------------------------------------------------------------------------- diff --git a/vertex.h b/vertex.h new file mode 100644 index 00000000..ee983439 --- /dev/null +++ b/vertex.h @@ -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 +#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 + world_vertex & + operator+=( Scalar_ const &Right ) { + position += Right; + normal += Right; + texture += Right; + return *this; } + template + friend + world_vertex + operator+( world_vertex Left, Scalar_ const &Right ) { + Left += Right; + return Left; } + // operator* + template + world_vertex & + operator*=( Scalar_ const &Right ) { + position *= Right; + normal *= Right; + texture *= Right; + return *this; } + template + 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 ); + +//--------------------------------------------------------------------------- From 020c71533fc189c54a56cdc36097927ffc35ead6 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 7 Oct 2017 17:20:53 +0200 Subject: [PATCH 07/31] reduced memory use, re-enabled debug mode, plugged some of remaining memory leaks --- EU07.cpp | 14 ++------------ Event.cpp | 24 ++++++++++-------------- Event.h | 6 ++++-- Ground.cpp | 17 ++++++++++++++++- Model3d.cpp | 3 ++- Traction.cpp | 11 ++++++++++- Traction.h | 4 ++++ frustum.h | 3 +++ maszyna.rc | Bin 6376 -> 6376 bytes renderer.cpp | 29 +++++++++++------------------ renderer.h | 4 ++-- resource.h | 2 +- scene.cpp | 28 ++++++++++++++++++++-------- scene.h | 5 +++-- simulation.cpp | 15 ++++++++++----- simulation.h | 3 ++- 16 files changed, 100 insertions(+), 68 deletions(-) diff --git a/EU07.cpp b/EU07.cpp index 5879c7e3..590890fa 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -31,7 +31,6 @@ Stele, firleju, szociu, hunter, ZiomalCl, OLI_EU and others #include "Mover.h" #include "usefull.h" #include "timer.h" -#include "resource.h" #include "uilayer.h" #ifdef EU07_BUILD_STATIC @@ -400,16 +399,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( icon ) ); #endif if( ( false == GfxRenderer.Init( window ) ) @@ -466,7 +455,8 @@ int main(int argc, char *argv[]) Console::Off(); // wyłączenie konsoli (komunikacji zwrotnej) TPythonInterpreter::killInstance(); - delete pConsole; + SafeDelete( pConsole ); + SafeDelete( simulation::Region ); glfwDestroyWindow(window); glfwTerminate(); diff --git a/Event.cpp b/Event.cpp index 15d75f90..6d013b8a 100644 --- a/Event.cpp +++ b/Event.cpp @@ -210,13 +210,12 @@ void TEvent::Load(cParser *parser, Math3D::vector3 const &org) parser->getTokens(); *parser >> token; - // str = AnsiString(token.c_str()); if (token != "none") asNodeName = token; // nazwa obiektu powiązanego if (asName.substr(0, 5) == "none_") - Type = tp_Ignored; // Ra: takie są ignorowane + m_ignored = true; // Ra: takie są ignorowane switch (Type) { @@ -226,7 +225,7 @@ void TEvent::Load(cParser *parser, Math3D::vector3 const &org) // if (Type==tp_UpdateValues) iFlags=0; //co modyfikować parser->getTokens(1, false); // case sensitive *parser >> token; - Params[0].asText = new char[token.size() + 1]; // BUG: source of memory leak + Params[0].asText = new char[token.size() + 1]; strcpy(Params[0].asText, token.c_str()); if (token != "*") // czy ma zostać bez zmian? iFlags |= update_memstring; @@ -605,7 +604,7 @@ void TEvent::Load(cParser *parser, Math3D::vector3 const &org) // str = AnsiString(token.c_str()); } while (token != "endevent"); break; - case tp_Ignored: // ignorowany +// case tp_Ignored: // ignorowany case tp_Unknown: // nieznany do { @@ -777,10 +776,7 @@ event_manager::insert( TEvent *Event ) { // can be cleaned up if pointers to events were replaced with handles ErrorLog( "Bad event: encountered duplicated event, \"" + Event->asName + "\"" ); duplicate->Append( Event ); // doczepka (taki wirtualny multiple bez warunków) - // BUG: source of memory leak. - // erasing original type of event prevents it from proper resource de-allocation on exit - // TODO: mark ignored event with separate flag or ideally refactor the whole thing - duplicate->Type = tp_Ignored; // dezaktywacja pierwotnego - taka proteza na wsteczną zgodność + duplicate->m_ignored = true; // dezaktywacja pierwotnego - taka proteza na wsteczną zgodność } } @@ -788,7 +784,7 @@ event_manager::insert( TEvent *Event ) { if( lookup == m_eventmap.end() ) { // if it's first event with such name, it's potential candidate for the execution queue m_eventmap.emplace( Event->asName, m_events.size() - 1 ); - if( ( Event->Type != tp_Ignored ) + if( ( Event->m_ignored != true ) && ( Event->asName.find( "onstart" ) != std::string::npos ) ) { // event uruchamiany automatycznie po starcie AddToQuery( Event, nullptr ); @@ -815,7 +811,7 @@ event_manager::FindEvent( std::string const &Name ) { bool event_manager::AddToQuery( TEvent *Event, TDynamicObject *Owner ) { - if( Event->bEnabled ) { + if( ( false == Event->m_ignored ) && ( true == Event->bEnabled ) ) { // jeśli może być dodany do kolejki (nie używany w skanowaniu) if( !Event->iQueued ) // jeśli nie dodany jeszcze do kolejki { // kolejka eventów jest posortowana względem (fStartTime) @@ -902,8 +898,8 @@ event_manager::CheckQuery() { } else // a jak nazwa jest unikalna, to kolejka idzie dalej QueryRootEvent = QueryRootEvent->evNext; // NULL w skrajnym przypadku - if (m_workevent->bEnabled) - { // w zasadzie te wyłączone są skanowane i nie powinny się nigdy w kolejce znaleźć + if( ( false == m_workevent->m_ignored ) && ( true == m_workevent->bEnabled ) ) { + // w zasadzie te wyłączone są skanowane i nie powinny się nigdy w kolejce znaleźć --m_workevent->iQueued; // teraz moze być ponownie dodany do kolejki WriteLog( "EVENT LAUNCHED" + ( m_workevent->Activator ? ( " by " + m_workevent->Activator->asName ) : "" ) + ": " + m_workevent->asName ); switch (m_workevent->Type) @@ -1270,7 +1266,7 @@ event_manager::InitEvents() { } else { // nie ma komórki, to nie będzie działał poprawnie - Current->Type = tp_Ignored; // deaktywacja + Current->m_ignored = true; // deaktywacja ErrorLog( "Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\"" ); } break; @@ -1297,7 +1293,7 @@ event_manager::InitEvents() { } else { // nie ma komórki, to nie będzie działał poprawnie - Current->Type = tp_Ignored; // deaktywacja + Current->m_ignored = true; // deaktywacja ErrorLog( "Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\"" ); } break; diff --git a/Event.h b/Event.h index 219441d2..340aa245 100644 --- a/Event.h +++ b/Event.h @@ -30,7 +30,7 @@ enum TEventType { tp_TrackVel, tp_Multiple, tp_AddValues, - tp_Ignored, +// tp_Ignored, // NOTE: refactored to separate flag tp_CopyValues, tp_WhoIs, tp_LogValues, @@ -84,6 +84,7 @@ 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 TEvent *evNext = nullptr; // następny w kolejce @@ -99,7 +100,8 @@ 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(); diff --git a/Ground.cpp b/Ground.cpp index f415dab9..8ffe1c38 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -2485,9 +2485,15 @@ void TGround::InitTracks() { // jak coś pójdzie źle, to robimy z tego normalny tor // Track->ModelAssign(tmp->Model->GetContainer(NULL)); //wiązanie toru z modelem // obrotnicy +#ifdef EU07_USE_OLD_GROUNDCODE + Track->RaAssign( + Current, Model ? Model->Model : NULL, FindEvent( Current->asName + ":done" ), + FindEvent( Current->asName + ":joined" ) ); // wiązanie toru z modelem obrotnicy +#else Track->RaAssign( Current, Model ? Model->Model : NULL, simulation::Events.FindEvent(Current->asName + ":done"), simulation::Events.FindEvent(Current->asName + ":joined")); // wiązanie toru z modelem obrotnicy +#endif // break; //jednak połączę z sąsiednim, jak ma się wysypywać null track } if (!Model) // jak nie ma modelu @@ -2867,12 +2873,21 @@ bool TGround::InitLaunchers() } else EventLauncher->MemCell = nullptr; +#ifdef EU07_USE_OLD_GROUNDCODE + EventLauncher->Event1 = ( EventLauncher->asEvent1Name != "none") ? + FindEvent(EventLauncher->asEvent1Name) : + nullptr; + EventLauncher->Event2 = ( EventLauncher->asEvent2Name != "none") ? + FindEvent(EventLauncher->asEvent2Name) : + nullptr; +#else EventLauncher->Event1 = ( EventLauncher->asEvent1Name != "none") ? simulation::Events.FindEvent(EventLauncher->asEvent1Name) : nullptr; EventLauncher->Event2 = ( EventLauncher->asEvent2Name != "none") ? simulation::Events.FindEvent(EventLauncher->asEvent2Name) : nullptr; +#endif } return true; } @@ -3526,7 +3541,7 @@ bool TGround::Update(double dt, int iter) if (bDynamicRemove) { // jeśli jest coś do usunięcia z listy, to trzeba na końcu for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext) - if (!Current->DynamicObject->bEnabled) + if ( false == Current->DynamicObject->bEnabled) { DynamicRemove(Current->DynamicObject); // usunięcie tego i podłączonych Current = nRootDynamic; // sprawdzanie listy od początku diff --git a/Model3d.cpp b/Model3d.cpp index 081b0709..ea6dd7b9 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1147,7 +1147,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) diff --git a/Traction.cpp b/Traction.cpp index 458dd7e7..dccce473 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -174,12 +174,21 @@ TTraction::Load( cParser *parser, glm::dvec3 const &pOrigin ) { } std::size_t +#ifdef EU07_USE_OLD_GROUNDCODE +TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ) { +#else TTraction::create_geometry( geometrybank_handle const &Bank ) { - +#endif if( m_geometry != null_handle ) { return GfxRenderer.Vertices( m_geometry ).size() / 2; } +#ifdef EU07_USE_OLD_GROUNDCODE + if( Bank != 0 ) { + m_origin = Origin; + } +#endif + vertex_array vertices; double ddp = std::hypot( pPoint2.x - pPoint1.x, pPoint2.z - pPoint1.z ); diff --git a/Traction.h b/Traction.h index b07fe017..67f46d6a 100644 --- a/Traction.h +++ b/Traction.h @@ -64,7 +64,11 @@ class TTraction : public editor::basic_node { m_origin = Origin; } // 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 +#ifdef EU07_USE_OLD_GROUNDCODE + std::size_t create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ); // wypełnianie VBO +#else std::size_t create_geometry( geometrybank_handle const &Bank ); +#endif int TestPoint(glm::dvec3 const &Point); void Connect(int my, TTraction *with, int to); void Init(); diff --git a/frustum.h b/frustum.h index 2029a412..96b456ce 100644 --- a/frustum.h +++ b/frustum.h @@ -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( Center.x ), static_cast( Center.y ), static_cast( 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 diff --git a/maszyna.rc b/maszyna.rc index 4736fb51865821fb456bc3acf5b565882f5eb1ab..ec21daa0ef2ca66621b1d14a43e5a9d9165995c0 100644 GIT binary patch delta 34 qcmaE1_`+~Qnz*1lgAaonLpVb`gC~PCgFl1cC@#Nwns^W+0IjeI9RL6T diff --git a/renderer.cpp b/renderer.cpp index 4943cafa..7c1ac70d 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -1598,7 +1598,7 @@ opengl_renderer::Render( TSubRect *Groundsubcell ) { } #else void -opengl_renderer::Render( scene::basic_region &Region ) { +opengl_renderer::Render( scene::basic_region *Region ) { m_sectionqueue.clear(); m_cellqueue.clear(); @@ -1617,14 +1617,14 @@ opengl_renderer::Render( scene::basic_region &Region ) { for( int column = originx; column <= originx + segmentcount; ++column ) { if( column < 0 ) { continue; } if( column >= scene::EU07_REGIONSIDESECTIONCOUNT ) { break; } - auto §ion { Region.m_sections[ row * scene::EU07_REGIONSIDESECTIONCOUNT + column ] }; - if( ( true == section.m_active ) + auto *section { Region->m_sections[ row * scene::EU07_REGIONSIDESECTIONCOUNT + column ] }; + if( ( section != nullptr ) #ifdef EU07_USE_DEBUG_CULLING - && ( m_worldcamera.camera.visible( section.m_area ) ) ) { + && ( m_worldcamera.camera.visible( section->m_area ) ) ) { #else - && ( m_renderpass.camera.visible( section.m_area ) ) ) { + && ( m_renderpass.camera.visible( section->m_area ) ) ) { #endif - m_sectionqueue.emplace_back( §ion ); + m_sectionqueue.emplace_back( section ); } } } @@ -2817,7 +2817,7 @@ opengl_renderer::Render_Alpha( TSubRect *Groundsubcell ) { } #else void -opengl_renderer::Render_Alpha( scene::basic_region &Region ) { +opengl_renderer::Render_Alpha( scene::basic_region *Region ) { // sort the nodes based on their distance to viewer std::sort( @@ -2827,17 +2827,6 @@ opengl_renderer::Render_Alpha( scene::basic_region &Region ) { return ( Left.first ) < ( Right.first ); } ); Render_Alpha( std::rbegin( m_cellqueue ), std::rend( m_cellqueue ) ); -/* - ::glDisable( GL_LIGHTING ); // linie nie powinny świecić - for( auto subcellpair = std::rbegin( m_cellqueue ); subcellpair != std::rend( m_cellqueue ); ++subcellpair ) { - // druty na końcu, żeby się nie robiły białe plamy na tle lasu - tmp = subcellpair->second; - for( node = tmp->nRenderWires; node; node = node->nNext3 ) { - Render_Alpha( node ); - } - } - ::glEnable( GL_LIGHTING ); -*/ } void @@ -2882,6 +2871,8 @@ opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_seque // third pass draws the wires; // wires use section vbos, but for the time being we want to draw them at the very end { + ::glDisable( GL_LIGHTING ); // linie nie powinny świecić + auto first{ First }; while( first != Last ) { @@ -2909,6 +2900,8 @@ opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_seque ++first; } + + ::glEnable( GL_LIGHTING ); } } diff --git a/renderer.h b/renderer.h index 1009e715..f5e888ad 100644 --- a/renderer.h +++ b/renderer.h @@ -283,7 +283,7 @@ private: Render( TSubRect *Groundsubcell ); #else void - Render( scene::basic_region &Region ); + Render( scene::basic_region *Region ); void Render( section_sequence::iterator First, section_sequence::iterator Last ); void @@ -316,7 +316,7 @@ private: Render_Alpha( TSubRect *Groundsubcell ); #else void - Render_Alpha( scene::basic_region &Region ); + Render_Alpha( scene::basic_region *Region ); void Render_Alpha( cell_sequence::reverse_iterator First, cell_sequence::reverse_iterator Last ); void diff --git a/resource.h b/resource.h index 52fca2ce..ead65dc1 100644 --- a/resource.h +++ b/resource.h @@ -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 // diff --git a/scene.cpp b/scene.cpp index a6d04d96..43adb766 100644 --- a/scene.cpp +++ b/scene.cpp @@ -119,8 +119,6 @@ basic_cell::center( glm::dvec3 Center ) { void basic_section::insert( shape_node Shape ) { - m_active = true; - auto const &shapedata = Shape.data(); if( ( true == shapedata.translucent ) || ( shapedata.rangesquared_max <= 90000.0 ) @@ -147,7 +145,6 @@ basic_section::insert( shape_node Shape ) { void basic_section::insert( TTrack *Path ) { - m_active = true; // pass the node to the appropriate partitioning cell // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done cell( Path->location() ).insert( Path ); @@ -157,7 +154,6 @@ basic_section::insert( TTrack *Path ) { void basic_section::insert( TTraction *Traction ) { - m_active = true; // pass the node to the appropriate partitioning cell // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done cell( Traction->location() ).insert( Traction ); @@ -167,7 +163,6 @@ basic_section::insert( TTraction *Traction ) { void basic_section::insert( TAnimModel *Instance ) { - m_active = true; // pass the node to the appropriate partitioning cell // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done cell( Instance->location() ).insert( Instance ); @@ -203,8 +198,6 @@ basic_section::create_geometry() { m_geometrycreated = true; } - if( false == m_active ) { return; } // nothing to do here - // since sections can be empty, we're doing lazy initialization of the geometry bank, when something may actually use it if( m_geometrybank == null_handle ) { m_geometrybank = GfxRenderer.Create_Bank(); @@ -235,6 +228,8 @@ basic_section::cell( glm::dvec3 const &Location ) { basic_region::basic_region() { + m_sections.fill( nullptr ); +/* // initialize centers of sections: // calculate center of 'top left' region section... auto const centeroffset = -( EU07_REGIONSIDESECTIONCOUNT / 2 * EU07_SECTIONSIZE ) + EU07_SECTIONSIZE / 2; @@ -248,6 +243,12 @@ basic_region::basic_region() { column = 0; } } +*/ +} + +basic_region::~basic_region() { + + for( auto section : m_sections ) { if( section != nullptr ) { delete section; } } } void @@ -568,10 +569,21 @@ basic_region::section( glm::dvec3 const &Location ) { auto const column = static_cast( std::floor( Location.x / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ); auto const row = static_cast( std::floor( Location.z / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ); - return + auto §ion = m_sections[ clamp( row, 0, EU07_REGIONSIDESECTIONCOUNT - 1 ) * EU07_REGIONSIDESECTIONCOUNT + clamp( column, 0, EU07_REGIONSIDESECTIONCOUNT - 1 ) ] ; + + if( section == nullptr ) { + // there's no guarantee the section exists at this point, so check and if needed, create it + section = new basic_section(); + // assign center of the section + auto const centeroffset = -( EU07_REGIONSIDESECTIONCOUNT / 2 * EU07_SECTIONSIZE ) + EU07_SECTIONSIZE / 2; + glm::dvec3 regioncornercenter { centeroffset, 0, centeroffset }; + section->center( regioncornercenter + glm::dvec3{ column * EU07_SECTIONSIZE, 0.0, row * EU07_SECTIONSIZE } ); + } + + return *section; } } // scene diff --git a/scene.h b/scene.h index a03ec979..66314e13 100644 --- a/scene.h +++ b/scene.h @@ -114,7 +114,6 @@ private: // members // placement and visibility scene::bounding_area m_area { glm::dvec3(), static_cast( 0.5 * M_SQRT2 * EU07_SECTIONSIZE + 0.25 * EU07_SECTIONSIZE ) }; - bool m_active { false }; // whether the section holds any actual data, itself or in the cells // content cell_array m_cells; // partitioning scheme shapenode_sequence m_shapes; // large pieces of opaque geometry and (legacy) terrain @@ -132,6 +131,8 @@ class basic_region { public: // constructors basic_region(); +// destructor + ~basic_region(); // methods // inserts provided shape in the region void @@ -149,7 +150,7 @@ public: private: // types - using section_array = std::array; + using section_array = std::array; // methods // checks whether specified point is within boundaries of the region diff --git a/simulation.cpp b/simulation.cpp index a522a636..9fdc5174 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -22,11 +22,16 @@ path_table Paths; traction_table Traction; instance_manager Instances; light_array Lights; -scene::basic_region Region; + +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 cParser scenarioparser( Scenariofile, cParser::buffer_FILE, Global::asCurrentSceneryPath, Global::bLoadTraction ); @@ -245,7 +250,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad auto *path { deserialize_path( Input, Scratchpad, nodedata ) }; // duplicates of named tracks are currently experimentally allowed if( simulation::Paths.insert( path ) ) { - simulation::Region.insert_path( path, Scratchpad ); + simulation::Region->insert_path( path, Scratchpad ); } else { ErrorLog( "Bad scenario: track with duplicate name, \"" + path->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); @@ -262,7 +267,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad auto *traction { deserialize_traction( Input, Scratchpad, nodedata ) }; // duplicates of named tracks are currently discarded if( simulation::Traction.insert( traction ) ) { - simulation::Region.insert_traction( traction, Scratchpad ); + simulation::Region->insert_traction( traction, Scratchpad ); } else { ErrorLog( "Bad scenario: traction piece with duplicate name, \"" + traction->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); @@ -286,7 +291,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad if( instance == nullptr ) { return; } if( simulation::Instances.insert( instance ) ) { - simulation::Region.insert_instance( instance, Scratchpad ); + simulation::Region->insert_instance( instance, Scratchpad ); } else { ErrorLog( "Bad scenario: 3d model instance with duplicate name, \"" + instance->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); @@ -297,7 +302,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad || ( nodedata.type == "triangle_strip" ) || ( nodedata.type == "triangle_fan" ) ) { - simulation::Region.insert_shape( scene::shape_node().deserialize( Input, nodedata ), Scratchpad ); + simulation::Region->insert_shape( scene::shape_node().deserialize( Input, nodedata ), Scratchpad ); } else if( ( nodedata.type == "lines" ) || ( nodedata.type == "line_strip" ) diff --git a/simulation.h b/simulation.h index be7da4dc..3aa6bbf0 100644 --- a/simulation.h +++ b/simulation.h @@ -66,7 +66,8 @@ extern path_table Paths; extern traction_table Traction; extern instance_manager Instances; extern light_array Lights; -extern scene::basic_region Region; + +extern scene::basic_region *Region; } // simulation From de4e10ab394ef20f5bd95d6c0a36727d773cde1c Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Wed, 11 Oct 2017 20:21:05 +0200 Subject: [PATCH 08/31] continued refacoring: vehicles, events, memcells, tracks, traction, traction power sources; minor readability enhancements and bug fixes --- AdvSound.cpp | 17 +- AdvSound.h | 19 +- AnimModel.cpp | 36 ++-- AnimModel.h | 4 +- Classes.h | 4 +- DynObj.cpp | 70 ++++++- DynObj.h | 187 ++++++++----------- Event.cpp | 248 ++++++++++++++----------- Event.h | 14 +- Gauge.h | 9 +- Globals.cpp | 2 + Globals.h | 2 + Ground.cpp | 189 +++++++------------ Ground.h | 8 +- MemCell.cpp | 12 ++ MemCell.h | 8 +- Model3d.cpp | 2 +- RealSound.cpp | 36 +--- RealSound.h | 75 ++++---- Segment.cpp | 39 ++-- Segment.h | 36 ---- Track.cpp | 63 +++++-- Track.h | 4 +- Traction.cpp | 322 ++++++++++++++++---------------- Traction.h | 5 +- TractionPower.cpp | 119 ++++++------ TractionPower.h | 45 +++-- Train.cpp | 12 +- Train.h | 1 - World.cpp | 201 +++++++++++++------- openglgeometrybank.h | 12 +- parser.cpp | 14 +- renderer.cpp | 36 +++- scene.cpp | 425 ++++++++++++++++++++++++++++++++++++++++++- scene.h | 115 +++++++++++- scenenode.cpp | 3 +- scenenode.h | 15 +- simulation.cpp | 326 ++++++++++++++++++++++++++++++--- simulation.h | 21 ++- 39 files changed, 1849 insertions(+), 907 deletions(-) diff --git a/AdvSound.cpp b/AdvSound.cpp index 7aefa52f..94870c95 100644 --- a/AdvSound.cpp +++ b/AdvSound.cpp @@ -19,12 +19,7 @@ TAdvancedSound::~TAdvancedSound() // SoundShut.Stop(); } -void TAdvancedSound::Free() -{ -} - -void TAdvancedSound::Init( std::string const &NameOn, std::string const &Name, std::string const &NameOff, double DistanceAttenuation, - vector3 const &pPosition) +void TAdvancedSound::Init( std::string const &NameOn, std::string const &Name, std::string const &NameOff, double DistanceAttenuation, Math3D::vector3 const &pPosition) { SoundStart.Init(NameOn, DistanceAttenuation, pPosition.x, pPosition.y, pPosition.z, true); SoundCommencing.Init(Name, DistanceAttenuation, pPosition.x, pPosition.y, pPosition.z, true); @@ -47,7 +42,7 @@ void TAdvancedSound::Init( std::string const &NameOn, std::string const &Name, s SoundShut.FA = 0.0; } -void TAdvancedSound::Load(cParser &Parser, vector3 const &pPosition) +void TAdvancedSound::Load(cParser &Parser, Math3D::vector3 const &pPosition) { std::string nameon, name, nameoff; double attenuation; @@ -62,7 +57,7 @@ void TAdvancedSound::Load(cParser &Parser, vector3 const &pPosition) Init( nameon, name, nameoff, attenuation, pPosition ); } -void TAdvancedSound::TurnOn(bool ListenerInside, vector3 NewPosition) +void TAdvancedSound::TurnOn(bool ListenerInside, Math3D::vector3 NewPosition) { // hunter-311211: nie trzeba czekac na ponowne odtworzenie dzwieku, az sie wylaczy if ((State == ss_Off || State == ss_ShuttingDown) && (SoundStart.AM > 0)) @@ -76,7 +71,7 @@ void TAdvancedSound::TurnOn(bool ListenerInside, vector3 NewPosition) } } -void TAdvancedSound::TurnOff(bool ListenerInside, vector3 NewPosition) +void TAdvancedSound::TurnOff(bool ListenerInside, Math3D::vector3 NewPosition) { if ((State == ss_Commencing || State == ss_Starting) && (SoundShut.AM > 0)) { @@ -90,7 +85,7 @@ void TAdvancedSound::TurnOff(bool ListenerInside, vector3 NewPosition) } } -void TAdvancedSound::Update(bool ListenerInside, vector3 NewPosition) +void TAdvancedSound::Update(bool ListenerInside, Math3D::vector3 NewPosition) { if ((State == ss_Commencing) && (SoundCommencing.AM > 0)) { @@ -126,7 +121,7 @@ void TAdvancedSound::Update(bool ListenerInside, vector3 NewPosition) } } -void TAdvancedSound::UpdateAF(double A, double F, bool ListenerInside, vector3 NewPosition) +void TAdvancedSound::UpdateAF(double A, double F, bool ListenerInside, Math3D::vector3 NewPosition) { // update, ale z amplituda i czestotliwoscia if( State == ss_Off ) { return; diff --git a/AdvSound.h b/AdvSound.h index 1049e750..f5c4de40 100644 --- a/AdvSound.h +++ b/AdvSound.h @@ -13,13 +13,13 @@ http://mozilla.org/MPL/2.0/. #include "RealSound.h" #include "parser.h" -typedef enum -{ +enum TSoundState { + ss_Off, ss_Starting, ss_Commencing, ss_ShuttingDown -} TSoundState; +}; class TAdvancedSound { // klasa dźwięków mających początek, dowolnie długi środek oraz zakończenie (np. Rp1) @@ -36,13 +36,12 @@ class TAdvancedSound public: TAdvancedSound() = default; ~TAdvancedSound(); - void Init( std::string const &NameOn, std::string const &Name, std::string const &NameOff, double DistanceAttenuation, vector3 const &pPosition); - void Load(cParser &Parser, vector3 const &pPosition); - void TurnOn(bool ListenerInside, vector3 NewPosition); - void TurnOff(bool ListenerInside, vector3 NewPosition); - void Free(); - void Update(bool ListenerInside, vector3 NewPosition); - void UpdateAF(double A, double F, bool ListenerInside, vector3 NewPosition); + void Init( std::string const &NameOn, std::string const &Name, std::string const &NameOff, double DistanceAttenuation, Math3D::vector3 const &pPosition); + void Load(cParser &Parser, Math3D::vector3 const &pPosition); + void TurnOn(bool ListenerInside, Math3D::vector3 NewPosition); + void TurnOff(bool ListenerInside, Math3D::vector3 NewPosition); + void Update(bool ListenerInside, Math3D::vector3 NewPosition); + void UpdateAF(double A, double F, bool ListenerInside, Math3D::vector3 NewPosition); void CopyIfEmpty(TAdvancedSound &s); }; diff --git a/AnimModel.cpp b/AnimModel.cpp index 5646811d..56eaa3b1 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -759,21 +759,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( 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 = 0.25; } // standardowy próg zaplania + break; + } } }; //--------------------------------------------------------------------------- diff --git a/AnimModel.h b/AnimModel.h index 69bdc8bb..47d1c25a 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -176,7 +176,9 @@ private: unsigned int m_framestamp { 0 }; // id of last rendered gfx frame }; -class instance_manager : public basic_table { + + +class instance_table : public basic_table { }; diff --git a/Classes.h b/Classes.h index b8967103..74111f52 100644 --- a/Classes.h +++ b/Classes.h @@ -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 diff --git a/DynObj.cpp b/DynObj.cpp index aaadd391..cbf34062 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -4150,7 +4150,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ń } @@ -4158,7 +4158,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ń } @@ -4170,7 +4170,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 @@ -4311,8 +4311,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 } } @@ -4346,10 +4345,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:" ) { @@ -5393,3 +5391,59 @@ 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 ) { + // Ra: zmienić warunek na sprawdzanie pantografów w jednej zmiennej: czy pantografy i czy podniesione + if( vehicle->MoverParameters->EnginePowerSource.SourceType == CurrentCollector ) { +/* + // TODO: re-implement + GetTraction( 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 (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 + } + bDynamicRemove = false; // na razie koniec + } +*/ +} diff --git a/DynObj.h b/DynObj.h index 623b6ac4..df0794e9 100644 --- a/DynObj.h +++ b/DynObj.h @@ -160,19 +160,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 @@ -182,7 +180,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 @@ -201,28 +199,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 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 @@ -268,8 +264,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 @@ -300,8 +295,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 @@ -325,7 +318,6 @@ public: // modele składowe pojazdu TAdvancedSound sReleaser; // Winger 010304 - // TRealSound rsPanTup; //PSound sPantUp; TRealSound sPantUp; TRealSound sPantDown; TRealSound rsDoorOpen; // Ra: przeniesione z kabiny @@ -352,8 +344,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; @@ -373,26 +363,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(); }; TRealSound rsDiesielInc; // youBy TRealSound rscurve; // youBy @@ -405,7 +387,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 @@ -437,99 +418,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); @@ -537,4 +484,14 @@ public: // modele składowe pojazdu static std::string const MED_labels[ 8 ]; }; + + +class vehicle_table : public basic_table { + +public: + // legacy method, calculates changes in simulation state over specified time + void + update( double dt, int iter ); +}; + //--------------------------------------------------------------------------- diff --git a/Event.cpp b/Event.cpp index 6d013b8a..389b92ca 100644 --- a/Event.cpp +++ b/Event.cpp @@ -74,10 +74,6 @@ TEvent::~TEvent() { }; -void TEvent::Init(){ - -}; - void TEvent::Conditions(cParser *parser, std::string s) { // przetwarzanie warunków, wspólne dla Multiple i UpdateValues if (s == "condition") @@ -949,7 +945,7 @@ event_manager::CheckQuery() { // TODO: re-enable when messaging module is in place if( Global::iMultiplayer ) { // potwierdzenie wykonania dla serwera (odczyt semafora już tak nie działa) - WyslijEvent( tmpEvent->asName, tmpEvent->Activator->GetName() ); + WyslijEvent( tmpEvent->asName, tmpEvent->Activator->name() ); } */ m_workevent->Params[ 9 ].asMemCell->PutCommand( @@ -984,61 +980,105 @@ event_manager::CheckQuery() { } break; } - case tp_Lights: - if (m_workevent->Params[9].asModel) - for (i = 0; i < iMaxNumLights; i++) - if (m_workevent->Params[i].asdouble >= 0) //-1 zostawia bez zmiany - m_workevent->Params[9].asModel->LightSet( - i, m_workevent->Params[i].asdouble); // teraz też ułamek - break; - case tp_Visible: - if (m_workevent->Params[9].nGroundNode) - m_workevent->Params[9].nGroundNode->bVisible = (m_workevent->Params[i].asInt > 0); - break; - case tp_Velocity: - Error("Not implemented yet :("); - break; - case tp_Exit: - MessageBox(0, m_workevent->asNodeName.c_str(), " THE END ", MB_OK); - Global::iTextMode = -1; // wyłączenie takie samo jak sekwencja F10 -> Y - return false; - case tp_Sound: - switch (m_workevent->Params[0].asInt) - { // trzy możliwe przypadki: - case 0: - m_workevent->Params[9].tsTextSound->Stop(); - break; - case 1: - m_workevent->Params[9].tsTextSound->Play( - 1, 0, true, m_workevent->Params[9].tsTextSound->vSoundPosition); - break; - case -1: - m_workevent->Params[9].tsTextSound->Play( - 1, DSBPLAY_LOOPING, true, m_workevent->Params[9].tsTextSound->vSoundPosition); - break; + case tp_Lights: { + if( m_workevent->Params[ 9 ].asModel ) { + for( i = 0; i < iMaxNumLights; ++i ) { + if( m_workevent->Params[ i ].asdouble >= 0 ) { + // -1 zostawia bez zmiany + m_workevent->Params[ 9 ].asModel->LightSet( + i, + m_workevent->Params[ i ].asdouble ); + } + } } break; + } + case tp_Visible: { +#ifdef EU07_USE_OLD_GROUNDCODE + if( m_workevent->Params[ 9 ].nGroundNode ) + m_workevent->Params[ 9 ].nGroundNode->bVisible = ( m_workevent->Params[ i ].asInt > 0 ); +#else + if( m_workevent->Params[ 9 ].asEditorNode ) + m_workevent->Params[ 9 ].asEditorNode->visible( m_workevent->Params[ i ].asInt > 0 ); +#endif + break; + } + case tp_Velocity: { + Error( "Not implemented yet :(" ); + break; + } + case tp_Exit: { + MessageBox( 0, m_workevent->asNodeName.c_str(), " THE END ", MB_OK ); + Global::iTextMode = -1; // wyłączenie takie samo jak sekwencja F10 -> Y + return false; + } + case tp_Sound: { + switch( m_workevent->Params[ 0 ].asInt ) { + // trzy możliwe przypadki: + case 0: { + m_workevent->Params[ 9 ].tsTextSound->Stop(); + break; + } + case 1: { + m_workevent->Params[ 9 ].tsTextSound->Play( + 1, + 0, + true, + m_workevent->Params[ 9 ].tsTextSound->vSoundPosition ); + break; + } + case -1: { + m_workevent->Params[ 9 ].tsTextSound->Play( + 1, + DSBPLAY_LOOPING, + true, + m_workevent->Params[ 9 ].tsTextSound->vSoundPosition ); + break; + } + default: { + break; + } + } + break; + } case tp_Disable: Error("Not implemented yet :("); break; - case tp_Animation: // Marcin: dorobic translacje - Ra: dorobiłem ;-) - if (m_workevent->Params[0].asInt == 1) - m_workevent->Params[9].asAnimContainer->SetRotateAnim( - Math3D::vector3(m_workevent->Params[1].asdouble, m_workevent->Params[2].asdouble, - m_workevent->Params[3].asdouble), - m_workevent->Params[4].asdouble); - else if (m_workevent->Params[0].asInt == 2) - m_workevent->Params[9].asAnimContainer->SetTranslateAnim( - Math3D::vector3(m_workevent->Params[1].asdouble, m_workevent->Params[2].asdouble, - m_workevent->Params[3].asdouble), - m_workevent->Params[4].asdouble); - else if (m_workevent->Params[0].asInt == 4) - m_workevent->Params[9].asModel->AnimationVND( - m_workevent->Params[8].asPointer, - m_workevent->Params[1].asdouble, // tu mogą być dodatkowe parametry, np. od-do - m_workevent->Params[2].asdouble, m_workevent->Params[3].asdouble, - m_workevent->Params[4].asdouble); + case tp_Animation: { + switch( m_workevent->Params[ 0 ].asInt ) { + case 1: { + m_workevent->Params[ 9 ].asAnimContainer->SetRotateAnim( + Math3D::vector3 { + m_workevent->Params[ 1 ].asdouble, + m_workevent->Params[ 2 ].asdouble, + m_workevent->Params[ 3 ].asdouble }, + m_workevent->Params[ 4 ].asdouble ); + break; + } + case 2: { + m_workevent->Params[ 9 ].asAnimContainer->SetTranslateAnim( + Math3D::vector3 { + m_workevent->Params[ 1 ].asdouble, + m_workevent->Params[ 2 ].asdouble, + m_workevent->Params[ 3 ].asdouble }, + m_workevent->Params[ 4 ].asdouble ); + break; + } + case 4: { + m_workevent->Params[ 9 ].asModel->AnimationVND( + m_workevent->Params[ 8 ].asPointer, + m_workevent->Params[ 1 ].asdouble, // tu mogą być dodatkowe parametry, np. od-do + m_workevent->Params[ 2 ].asdouble, + m_workevent->Params[ 3 ].asdouble, + m_workevent->Params[ 4 ].asdouble ); + break; + } + default: { + break; + } + } break; + } case tp_Switch: { if( m_workevent->Params[ 9 ].asTrack ) { m_workevent->Params[ 9 ].asTrack->Switch( @@ -1057,13 +1097,15 @@ event_manager::CheckQuery() { break; } case tp_TrackVel: - if (m_workevent->Params[9].asTrack) - { // prędkość na zwrotnicy może być ograniczona z góry we wpisie, większej się nie - // ustawi eventem - WriteLog("Type: TrackVel"); + if (m_workevent->Params[9].asTrack) { + // prędkość na zwrotnicy może być ograniczona z góry we wpisie, większej się nie ustawi eventem m_workevent->Params[9].asTrack->VelocitySet(m_workevent->Params[0].asdouble); - if (DebugModeFlag) // wyświetlana jest ta faktycznie ustawiona - WriteLog(" - velocity: ", m_workevent->Params[9].asTrack->VelocityGet()); + // wyświetlana jest ta faktycznie ustawiona + WriteLog( "Type: TrackVel - [" + + to_string( m_workevent->Params[ 0 ].asdouble, 2 ) + "]" + + ( DebugModeFlag ? + ", actual [ " + to_string( m_workevent->Params[ 9 ].asTrack->VelocityGet(), 2 ) + "]" : + "" ) ); } break; case tp_DynVel: @@ -1097,7 +1139,7 @@ event_manager::CheckQuery() { if( ( m_workevent->iFlags & conditional_anyelse ) == 0 ) { // jednoznaczne tylko, gdy nie było else if( m_workevent->Activator ) { - WyslijEvent( m_workevent->asName, m_workevent->Activator->GetName() ); + WyslijEvent( m_workevent->asName, m_workevent->Activator->name() ); } else { WyslijEvent( m_workevent->asName, "" ); @@ -1175,8 +1217,9 @@ event_manager::CheckQuery() { m_workevent->Params[ 9 ].asMemCell->UpdateValues( m_workevent->Activator->Mechanik->TrainName(), m_workevent->Activator->Mechanik->StationCount() - m_workevent->Activator->Mechanik->StationIndex(), // ile przystanków do końca - m_workevent->Activator->Mechanik->IsStop() ? 1 : - 0, // 1, gdy ma tu zatrzymanie + m_workevent->Activator->Mechanik->IsStop() ? + 1 : + 0, // 1, gdy ma tu zatrzymanie m_workevent->iFlags); WriteLog("Train detected: " + m_workevent->Activator->Mechanik->TrainName()); } @@ -1186,10 +1229,10 @@ event_manager::CheckQuery() { // zapisanie zawartości komórki pamięci do logu if( m_workevent->Params[ 9 ].asMemCell ) { // jeśli była podana nazwa komórki - WriteLog( "Memcell \"" + m_workevent->asNodeName + "\": " - + m_workevent->Params[ 9 ].asMemCell->Text() + " " - + std::to_string( m_workevent->Params[ 9 ].asMemCell->Value1() ) + " " - + std::to_string( m_workevent->Params[ 9 ].asMemCell->Value2() ) ); + WriteLog( "Memcell \"" + m_workevent->asNodeName + "\": [" + + m_workevent->Params[ 9 ].asMemCell->Text() + "] [" + + to_string( m_workevent->Params[ 9 ].asMemCell->Value1(), 2 ) + "] [" + + to_string( m_workevent->Params[ 9 ].asMemCell->Value2(), 2 ) + "]" ); } else { // TODO: re-enable when cell manager is in place @@ -1237,7 +1280,7 @@ event_manager::InitEvents() { case tp_AddValues: // sumowanie wartości case tp_UpdateValues: { // zmiana wartości auto *cell = simulation::Memory.find( Current->asNodeName ); // nazwa komórki powiązanej z eventem - if( cell ) { // McZapkie-100302 + if( cell != nullptr ) { // McZapkie-100302 if( Current->iFlags & ( conditional_trackoccupied | conditional_trackfree ) ) { // jeśli chodzi o zajetosc toru (tor może być inny, niż wpisany w komórce) // nazwa toru ta sama, co nazwa komórki @@ -1281,7 +1324,7 @@ event_manager::InitEvents() { case tp_GetValues: case tp_WhoIs: { auto *cell = simulation::Memory.find( Current->asNodeName ); - if( cell ) { + if( cell != nullptr ) { Current->Params[ 8 ].asLocation = &( cell->location() ); Current->Params[ 9 ].asMemCell = cell; if( ( Current->Type == tp_GetValues ) @@ -1301,7 +1344,7 @@ event_manager::InitEvents() { case tp_CopyValues: { // skopiowanie komórki do innej auto *cell = simulation::Memory.find( Current->asNodeName ); // komórka docelowa - if( cell ) { + if( cell != nullptr ) { Current->Params[ 4 ].asLocation = &( cell->location() ); Current->Params[ 5 ].asMemCell = cell; // komórka docelowa if( false == cell->asTrackName.empty() ) { @@ -1338,7 +1381,7 @@ event_manager::InitEvents() { SafeDeleteArray( Current->Params[ 9 ].asText ); // egzemplarz modelu do animowania auto *instance = simulation::Instances.find( Current->asNodeName ); - if( instance ) { + if( instance != nullptr ) { if( Current->Params[ 0 ].asInt == 4 ) { // model dla całomodelowych animacji Current->Params[ 9 ].asModel = instance; @@ -1365,33 +1408,35 @@ event_manager::InitEvents() { case tp_Lights: { // zmiana świeteł modelu auto *instance = simulation::Instances.find( Current->asNodeName ); - if( instance ) + if( instance != nullptr ) Current->Params[ 9 ].asModel = instance; else ErrorLog( "Bad event: lights event \"" + Current->asName + "\" cannot find model instance \"" + Current->asNodeName + "\"" ); Current->asNodeName = ""; break; } -/* case tp_Visible: { // ukrycie albo przywrócenie obiektu - tmp = FindGroundNode( Current->asNodeName, TP_MODEL ); // najpierw model - if( !tmp ) - tmp = FindGroundNode( Current->asNodeName, TP_TRACK ); // albo tory? - if( !tmp ) - tmp = FindGroundNode( Current->asNodeName, TP_TRACTION ); // może druty? - if( tmp ) - Current->Params[ 9 ].nGroundNode = tmp; + editor::basic_node *node = simulation::Instances.find( Current->asNodeName ); // najpierw model + if( node == nullptr ) { + // albo tory? + node = simulation::Paths.find( Current->asNodeName ); + } + if( node == nullptr ) { + // może druty? + node = simulation::Traction.find( Current->asNodeName ); + } + if( node != nullptr ) + Current->Params[ 9 ].asEditorNode = node; else ErrorLog( "Bad event: visibility event \"" + Current->asName + "\" cannot find item \"" + Current->asNodeName + "\"" ); Current->asNodeName = ""; break; } -*/ case tp_Switch: { // przełożenie zwrotnicy albo zmiana stanu obrotnicy auto *track = simulation::Paths.find( Current->asNodeName ); - if( track ) { + if( track != nullptr ) { // dowiązanie toru if( track->iAction == NULL ) { // jeśli nie jest zwrotnicą ani obrotnicą to będzie się zmieniał stan uszkodzenia @@ -1414,23 +1459,21 @@ event_manager::InitEvents() { Current->asNodeName = ""; break; } -/* case tp_Sound: { // odtworzenie dźwięku - tmp = FindGroundNode( Current->asNodeName, TP_SOUND ); - if( tmp ) - Current->Params[ 9 ].tsTextSound = tmp->tsStaticSound; + auto *sound = simulation::Sounds.find( Current->asNodeName ); + if( sound != nullptr ) + Current->Params[ 9 ].tsTextSound = sound; else ErrorLog( "Bad event: sound event \"" + Current->asName + "\" cannot find static sound \"" + Current->asNodeName + "\"" ); Current->asNodeName = ""; break; } -*/ case tp_TrackVel: { // ustawienie prędkości na torze if( false == Current->asNodeName.empty() ) { auto *track = simulation::Paths.find( Current->asNodeName ); - if( track ) { + if( track != nullptr ) { // flaga zmiany prędkości toru jest istotna dla skanowania track->iAction |= 0x200; Current->Params[ 9 ].asTrack = track; @@ -1442,22 +1485,20 @@ event_manager::InitEvents() { Current->asNodeName = ""; break; } -/* case tp_DynVel: { // komunikacja z pojazdem o konkretnej nazwie if( Current->asNodeName == "activator" ) Current->Params[ 9 ].asDynamic = nullptr; else { - tmp = FindGroundNode( Current->asNodeName, TP_DYNAMIC ); - if( tmp ) - Current->Params[ 9 ].asDynamic = tmp->DynamicObject; + auto *vehicle = simulation::Vehicles.find( Current->asNodeName ); + if( vehicle != nullptr ) + Current->Params[ 9 ].asDynamic = vehicle; else Error( "Bad event: vehicle velocity event \"" + Current->asName + "\" cannot find vehicle \"" + Current->asNodeName + "\"" ); } Current->asNodeName = ""; break; } -*/ case tp_Multiple: { std::string cellastext; if( Current->Params[ 9 ].asText != nullptr ) { // przepisanie nazwy do bufora @@ -1494,36 +1535,27 @@ event_manager::InitEvents() { } break; } -/* case tp_Voltage: { // zmiana napięcia w zasilaczu (TractionPowerSource) - if( !Current->asNodeName.empty() ) { - tmp = FindGroundNode( Current->asNodeName, TP_TRACTIONPOWERSOURCE ); // podłączenie zasilacza - if( tmp ) - Current->Params[ 9 ].psPower = tmp->psTractionPowerSource; + if( false == Current->asNodeName.empty() ) { + auto *powersource = simulation::Powergrid.find( Current->asNodeName ); // podłączenie zasilacza + if( powersource != nullptr ) + Current->Params[ 9 ].psPower = powersource; else ErrorLog( "Bad event: voltage event \"" + Current->asName + "\" cannot find power source \"" + Current->asNodeName + "\"" ); } Current->asNodeName = ""; break; } -*/ case tp_Message: { // wyświetlenie komunikatu break; } } // switch - if( Current->fDelay < 0 ) { - AddToQuery( Current, nullptr ); - } + + if( Current->fDelay < 0 ) { AddToQuery( Current, nullptr ); } } -/* - for (TGroundNode *Current = nRootOfType[TP_MEMCELL]; Current; Current = Current->nNext) - { // Ra: eventy komórek pamięci, wykonywane po wysłaniu komendy do zatrzymanego pojazdu - Current->MemCell->AssignEvents( FindEvent( Current->asName + ":sent" ) ); - } -*/ } // legacy method, verifies condition for specified event diff --git a/Event.h b/Event.h index 340aa245..cd0f89cd 100644 --- a/Event.h +++ b/Event.h @@ -12,6 +12,9 @@ http://mozilla.org/MPL/2.0/. #include #include "dumb3d.h" #include "classes.h" +#include "names.h" +#include "scenenode.h" +#include "evlaunch.h" enum TEventType { tp_Unknown, @@ -61,6 +64,7 @@ union TParam void *asPointer; TMemCell *asMemCell; TGroundNode *nGroundNode; + editor::basic_node *asEditorNode; glm::dvec3 const *asLocation; TTrack *asTrack; TAnimModel *asModel; @@ -104,7 +108,6 @@ public: // metody TEvent(std::string const &m = ""); ~TEvent(); - void Init(); void Load(cParser *parser, Math3D::vector3 const &org); static void AddToQuery( TEvent *Event, TEvent *&Start ); std::string CommandGet(); @@ -123,9 +126,12 @@ public: ~event_manager(); // methods // adds provided event to the collection. returns: true on success - // TODO: return handle instead of pointer + // 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 ); @@ -143,6 +149,7 @@ private: // types using event_sequence = std::deque; using event_map = std::unordered_map; + using eventlauncher_sequence = std::vector; // methods // legacy method, verifies condition for specified event @@ -158,8 +165,9 @@ private: // legacy version of the above TEvent *QueryRootEvent { nullptr }; TEvent *m_workevent { nullptr }; - event_map m_eventmap; + basic_table m_launchers; + eventlauncher_sequence m_launcherqueue; }; //--------------------------------------------------------------------------- diff --git a/Gauge.h b/Gauge.h index 516294a7..c437883c 100644 --- a/Gauge.h +++ b/Gauge.h @@ -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 { diff --git a/Globals.cpp b/Globals.cpp index fea1cb39..f0e85b79 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -996,10 +996,12 @@ void Global::TrainDelete(TDynamicObject *d) pWorld->TrainDelete(d); }; +#ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *Global::DynamicNearest() { // ustalenie pojazdu najbliższego kamerze return pGround->DynamicNearest(pCamera->Pos); }; +#endif TDynamicObject *Global::CouplerNearest() { // ustalenie pojazdu najbliższego kamerze diff --git a/Globals.h b/Globals.h index 77abf302..e3e15a8d 100644 --- a/Globals.h +++ b/Globals.h @@ -297,7 +297,9 @@ class Global static void TrainDelete(TDynamicObject *d); static void ConfigParse(cParser &parser); static std::string GetNextSymbol(); +#ifdef EU07_USE_OLD_GROUNDCODE static TDynamicObject * DynamicNearest(); +#endif static TDynamicObject * CouplerNearest(); static bool AddToQuery(TEvent *event, TDynamicObject *who); static bool DoEvents(); diff --git a/Ground.cpp b/Ground.cpp index 8ffe1c38..ac467df8 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -586,7 +586,7 @@ void TGround::Free() // RootNode=NULL; nRootDynamic = NULL; } - +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode * TGround::DynamicFindAny(std::string const &Name) { // wyszukanie pojazdu o podanej nazwie, szukanie po wszystkich (użyć drzewa!) for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext) @@ -603,7 +603,7 @@ TGroundNode * TGround::DynamicFind(std::string const &Name) return Current; return NULL; }; - +#endif void TGround::DynamicList(bool all) { // odesłanie nazw pojazdów dostępnych na scenerii (nazwy, szczególnie wagonów, mogą się @@ -694,6 +694,7 @@ int iTrainSetWehicleNumber = 0; TGroundNode *nTrainSetNode = NULL; // poprzedni pojazd do łączenia TGroundNode *nTrainSetDriver = NULL; // pojazd, któremu zostanie wysłany rozkład +#ifdef EU07_USE_OLD_GROUNDCODE void TGround::RaTriangleDivider(TGroundNode *node) { // tworzy dodatkowe trójkąty i zmiejsza podany // to jest wywoływane przy wczytywaniu trójkątów @@ -814,7 +815,7 @@ void TGround::RaTriangleDivider(TGroundNode *node) RaTriangleDivider(node); // rekurencja, bo nawet na TD raz nie wystarczy RaTriangleDivider(ntri); }; - +#endif TGroundNode * TGround::AddGroundNode(cParser *parser) { // wczytanie wpisu typu "node" std::string str, str1, str2, str3, str4, Skin, DriverType, asNodeName; @@ -889,7 +890,8 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) switch (tmp->iType) { - case TP_TRACTION: +#ifdef EU07_USE_OLD_GROUNDCODE + case TP_TRACTION: tmp->hvTraction = new TTraction( tmp->asName ); parser->getTokens(); *parser >> token; @@ -983,7 +985,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) >> tmp->pCenter.y >> tmp->pCenter.z; tmp->pCenter += pOrigin; - tmp->psTractionPowerSource = new TTractionPowerSource(tmp); + tmp->psTractionPowerSource = new TTractionPowerSource( tmp->asName ); tmp->psTractionPowerSource->Load(parser); break; case TP_MEMCELL: @@ -1004,6 +1006,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } } break; +#endif case TP_EVLAUNCH: parser->getTokens(3); *parser @@ -1015,6 +1018,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) tmp->EvLaunch = new TEventLauncher(); tmp->EvLaunch->Load(parser); break; +#ifdef EU07_USE_OLD_GROUNDCODE case TP_TRACK: tmp->pTrack = new TTrack( tmp->asName ); if (Global::iWriteLogEnabled & 4) @@ -1191,57 +1195,8 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } break; +#endif case TP_MODEL: { -#ifdef EU07_USE_OLD_TERRAINCODE - if( rmin < 0 ) { - tmp->iType = TP_TERRAIN; - tmp->fSquareMinRadius = 0; // to w ogóle potrzebne? - } - parser->getTokens( 3 ); - *parser >> tmp->pCenter.x >> tmp->pCenter.y >> tmp->pCenter.z; - parser->getTokens(); - *parser >> tf1; - // OlO_EU&KAKISH-030103: obracanie punktow zaczepien w modelu - tmp->pCenter.RotateY( aRotate.y / 180.0 * M_PI ); - // McZapkie-260402: model tez ma wspolrzedne wzgledne - tmp->pCenter += pOrigin; - - tmp->Model = new TAnimModel(); - tmp->Model->RaAnglesSet( aRotate.x, tf1 + aRotate.y, aRotate.z ); // dostosowanie do pochylania linii - if( tmp->Model->Load( parser, tmp->iType == TP_TERRAIN ) ) { - // wczytanie modelu, tekstury i stanu świateł... - tmp->iFlags = tmp->Model->Flags() | 0x200; // ustalenie, czy przezroczysty; flaga usuwania - } - else if( tmp->iType != TP_TERRAIN ) { // model nie wczytał się - ignorowanie node - delete tmp; - tmp = NULL; // nie może być tu return - break; // nie może być tu return? - } - if( tmp->iType == TP_TERRAIN ) { // jeśli model jest terenem, trzeba utworzyć dodatkowe obiekty - // po wczytaniu model ma już utworzone DL albo VBO - Global::pTerrainCompact = tmp->Model; // istnieje co najmniej jeden obiekt terenu - tmp->pCenter = Math3D::vector3( 0.0, 0.0, 0.0 ); // enforce placement in the world center - tmp->iCount = Global::pTerrainCompact->TerrainCount() + 1; // zliczenie submodeli - tmp->nNode = new TGroundNode[ tmp->iCount ]; // sztuczne node dla kwadratów - tmp->nNode[ 0 ].iType = TP_MODEL; // pierwszy zawiera model (dla delete) - tmp->nNode[ 0 ].Model = Global::pTerrainCompact; - tmp->nNode[ 0 ].iFlags = 0x200; // nie wyświetlany, ale usuwany - for( int i = 1; i < tmp->iCount; ++i ) { // a reszta to submodele - tmp->nNode[ i ].iType = TP_SUBMODEL; - tmp->nNode[ i ].smTerrain = Global::pTerrainCompact->TerrainSquare( i - 1 ); - tmp->nNode[ i ].iFlags = 0x10; // nieprzezroczyste; nie usuwany - tmp->nNode[ i ].bVisible = true; - tmp->nNode[ i ].pCenter = tmp->pCenter; // nie przesuwamy w inne miejsce - } - } - else if( !tmp->asName.empty() ) // jest pusta gdy "none" - { // dodanie do wyszukiwarki - if( false == m_nodemap.Add( TP_MODEL, tmp->asName, tmp ) ) { - // przy zdublowaniu wskaźnik zostanie podmieniony w drzewku na późniejszy (zgodność wsteczna) - ErrorLog( "Duplicated model: " + tmp->asName ); // to zgłaszać duplikat - } - } -#else if( rmin < 0 ) { // legacy leftover: special case, terrain provided as 3d model tmp->iType = TP_TERRAIN; @@ -1274,6 +1229,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } } else { +#ifdef EU07_USE_OLD_GROUNDCODE // regular 3d model parser->getTokens( 3 ); *parser @@ -1304,10 +1260,11 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) ErrorLog( "Duplicated model: " + tmp->asName ); // to zgłaszać duplikat } } - } #endif + } break; } +#ifdef EU07_USE_OLD_GROUNDCODE // case TP_GEOMETRY : case GL_TRIANGLES: case GL_TRIANGLE_STRIP: @@ -1510,6 +1467,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } // koniec wczytywania trójkątów break; } +#endif case GL_LINES: case GL_LINE_STRIP: case GL_LINE_LOOP: { @@ -1802,7 +1760,7 @@ bool TGround::Init(std::string File) else { // jeśli jest pojazdem if( ( LastNode->DynamicObject->Mechanik != nullptr ) && ( LastNode->DynamicObject->Mechanik->Primary() ) ) { - // jeśli jest głównym (pasażer nie jest) + // jeśli jest głównym (pasażer nie jest) nTrainSetDriver = LastNode; // pojazd, któremu zostanie wysłany rozkład } LastNode->nNext = nRootDynamic; @@ -1859,8 +1817,7 @@ bool TGround::Init(std::string File) if (nTrainSetDriver) // pojazd, któremu zostanie wysłany rozkład { // wysłanie komendy "Timetable" ustawia odpowiedni tryb jazdy nTrainSetDriver->DynamicObject->Mechanik->DirectionInitial(); - nTrainSetDriver->DynamicObject->Mechanik->PutCommand("Timetable:" + asTrainName, - fTrainSetVel, 0, NULL); + nTrainSetDriver->DynamicObject->Mechanik->PutCommand("Timetable:" + asTrainName, fTrainSetVel, 0, NULL); } } if( LastNode ) { @@ -1926,7 +1883,7 @@ bool TGround::Init(std::string File) { ErrorLog("Duplicated event: " + tmp->asName); found->Append(tmp); // doczepka (taki wirtualny multiple bez warunków) - found->Type = tp_Ignored; // dezaktywacja pierwotnego - taka proteza na + found->m_ignored = true; // dezaktywacja pierwotnego - taka proteza na // wsteczną zgodność // SafeDelete(tmp); //bezlitośnie usuwamy wszelkie duplikaty, żeby nie // zaśmiecać drzewka @@ -1938,7 +1895,7 @@ bool TGround::Init(std::string File) RootEvent = tmp; if (!found) { // jeśli nazwa wystąpiła, to do kolejki i wyszukiwarki dodawany jest tylko pierwszy - if( ( RootEvent->Type != tp_Ignored ) + if( ( RootEvent->m_ignored == false ) && ( RootEvent->asName.find( "onstart" ) != std::string::npos ) ) { // event uruchamiany automatycznie po starcie AddToQuery( RootEvent, NULL ); // dodanie do kolejki @@ -2217,7 +2174,7 @@ bool TGround::InitEvents() } else { // nie ma komórki, to nie będzie działał poprawnie - Current->Type = tp_Ignored; // deaktywacja + Current->m_ignored = true; // deaktywacja ErrorLog("Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\""); } break; @@ -2241,7 +2198,7 @@ bool TGround::InitEvents() } else { // nie ma komórki, to nie będzie działał poprawnie - Current->Type = tp_Ignored; // deaktywacja + Current->m_ignored = true; // deaktywacja ErrorLog("Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\""); } break; @@ -2637,7 +2594,7 @@ void TGround::InitTraction() nTemp = new TGroundNode(); nTemp->iType = TP_TRACTIONPOWERSOURCE; nTemp->asName = Traction->asPowerSupplyName; - nTemp->psTractionPowerSource = new TTractionPowerSource(nTemp); + nTemp->psTractionPowerSource = new TTractionPowerSource(nTemp->asName); nTemp->psTractionPowerSource->Init(Traction->NominalVoltage, Traction->MaxCurrent); nTemp->nNext = nRootOfType[nTemp->iType]; // ostatni dodany dołączamy na końcu // nowego @@ -2826,8 +2783,11 @@ void TGround::TrackJoin(TGroundNode *Current) int iConnection; if (!Track->CurrentPrev()) { - tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_0(), iConnection, - Current); // Current do pominięcia +#ifdef EU07_USE_OLD_GROUNDCODE + tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_0(), iConnection, Current); // Current do pominięcia +#else + std::tie( tmp, iConnection ) = simulation::Region->find_path( Track->CurrentSegment()->FastGetPoint_0(), Track ); +#endif switch (iConnection) { case 0: @@ -2840,7 +2800,11 @@ void TGround::TrackJoin(TGroundNode *Current) } if (!Track->CurrentNext()) { +#ifdef EU07_USE_OLD_GROUNDCODE tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_1(), iConnection, Current); +#else + std::tie( tmp, iConnection ) = simulation::Region->find_path( Track->CurrentSegment()->FastGetPoint_1(), Track ); +#endif switch (iConnection) { case 0: @@ -2892,6 +2856,7 @@ bool TGround::InitLaunchers() return true; } +#ifdef EU07_USE_OLD_GROUNDCODE TTrack * TGround::FindTrack(vector3 Point, int &iConnection, TGroundNode *Exclude) { // wyszukiwanie innego toru kończącego się w (Point) TTrack *tmp; @@ -2978,49 +2943,36 @@ TTraction * TGround::TractionNearestFind(glm::dvec3 &p, int dir, TGroundNode *n) if ((sr = FastGetSubRect(c + i, r + j)) != NULL) // o ile w ogóle sektor jest for (nCurrent = sr->nRenderWires; nCurrent; nCurrent = nCurrent->nNext3) if (nCurrent->iType == TP_TRACTION) - if (nCurrent->hvTraction->psSection == - n->hvTraction->psSection) // jeśli ta sama sekcja + if (nCurrent->hvTraction->psSection == n->hvTraction->psSection) // jeśli ta sama sekcja if (nCurrent != n) // ale nie jest tym samym - if (nCurrent->hvTraction != - n->hvTraction - ->hvNext[0]) // ale nie jest bezpośrednio podłączonym + if (nCurrent->hvTraction != n->hvTraction->hvNext[0]) // ale nie jest bezpośrednio podłączonym if (nCurrent->hvTraction != n->hvTraction->hvNext[1]) if (nCurrent->hvTraction->psPower [k = (glm::dot( n->hvTraction->vParametric, nCurrent->hvTraction->vParametric) >= 0 ? dir ^ 1 : - dir)]) // ma zasilanie z odpowiedniej - // strony - if (nCurrent->hvTraction->fResistance[k] >= - 0.0) //żeby się nie propagowały jakieś ujemne - { // znaleziony kandydat do połączenia + dir)]) // ma zasilanie z odpowiedniej strony + if (nCurrent->hvTraction->fResistance[k] >= 0.0) { // żeby się nie propagowały jakieś ujemne + // znaleziony kandydat do połączenia d = glm::length2( p - glm::dvec3{ nCurrent->pCenter } ); // kwadrat odległości środków - if (dist > d) - { // zapamiętanie nowego najbliższego + if (dist > d) { + // zapamiętanie nowego najbliższego dist = d; // nowy rekord odległości nBest = nCurrent; - zg = k; // z którego końca brać wskaźnik - // zasilacza + zg = k; // z którego końca brać wskaźnik zasilacza } } - if (nBest) // jak znalezione przęsło z zasilaniem, to podłączenie "równoległe" - { - n->hvTraction->ResistanceCalc(dir, nBest->hvTraction->fResistance[zg], - nBest->hvTraction->psPower[zg]); - // testowo skrzywienie przęsła tak, aby pokazać skąd ma zasilanie - // if (dir) //1 gdy ciąg dalszy jest od strony Point2 - // n->hvTraction->pPoint3=0.25*(nBest->pCenter+3*(zg?nBest->hvTraction->pPoint4:nBest->hvTraction->pPoint3)); - // else - // n->hvTraction->pPoint4=0.25*(nBest->pCenter+3*(zg?nBest->hvTraction->pPoint4:nBest->hvTraction->pPoint3)); + if (nBest) { + // jak znalezione przęsło z zasilaniem, to podłączenie "równoległe" + n->hvTraction->ResistanceCalc(dir, nBest->hvTraction->fResistance[zg], nBest->hvTraction->psPower[zg]); } return (nBest ? nBest->hvTraction : nullptr); }; -#ifdef EU07_USE_OLD_GROUNDCODE bool TGround::AddToQuery(TEvent *Event, TDynamicObject *Node) { - if( Event->bEnabled ) { + if( ( false == Event->m_ignored ) && ( true == Event->bEnabled ) ) { // jeśli może być dodany do kolejki (nie używany w skanowaniu) if( !Event->iQueued ) // jeśli nie dodany jeszcze do kolejki { // kolejka eventów jest posortowana względem (fStartTime) @@ -3169,8 +3121,8 @@ bool TGround::CheckQuery() } else // a jak nazwa jest unikalna, to kolejka idzie dalej QueryRootEvent = QueryRootEvent->evNext; // NULL w skrajnym przypadku - if (tmpEvent->bEnabled) - { // w zasadzie te wyłączone są skanowane i nie powinny się nigdy w kolejce znaleźć + if( ( false == tmpEvent->m_ignored ) && ( true == tmpEvent->bEnabled ) ) { + // w zasadzie te wyłączone są skanowane i nie powinny się nigdy w kolejce znaleźć --tmpEvent->iQueued; // teraz moze być ponownie dodany do kolejki WriteLog( "EVENT LAUNCHED" + ( tmpEvent->Activator ? ( " by " + tmpEvent->Activator->asName ) : "" ) + ": " + tmpEvent->asName ); switch (tmpEvent->Type) @@ -3222,7 +3174,7 @@ bool TGround::CheckQuery() // loc.Z= tmpEvent->Params[8].nGroundNode->pCenter.y; if (Global::iMultiplayer) // potwierdzenie wykonania dla serwera (odczyt // semafora już tak nie działa) - WyslijEvent(tmpEvent->asName, tmpEvent->Activator->GetName()); + WyslijEvent(tmpEvent->asName, tmpEvent->Activator->name()); // tmpEvent->Params[9].asMemCell->PutCommand(tmpEvent->Activator->Mechanik,loc); tmpEvent->Params[9].asMemCell->PutCommand( tmpEvent->Activator->Mechanik, &tmpEvent->Params[8].nGroundNode->pCenter); @@ -3355,7 +3307,7 @@ bool TGround::CheckQuery() 0) // jednoznaczne tylko, gdy nie było else { if (tmpEvent->Activator) - WyslijEvent(tmpEvent->asName, tmpEvent->Activator->GetName()); + WyslijEvent(tmpEvent->asName, tmpEvent->Activator->name()); else WyslijEvent(tmpEvent->asName, ""); } @@ -3468,7 +3420,6 @@ bool TGround::CheckQuery() } // while return true; } -#endif void TGround::UpdatePhys(double dt, int iter) { // aktualizacja fizyki stałym krokiem: dt=krok czasu [s], dt*iter=czas od ostatnich przeliczeń @@ -3587,9 +3538,8 @@ TGround::Update_Hidden() { } } } - } - +#endif // Winger 170204 - szukanie trakcji nad pantografami bool TGround::GetTraction(TDynamicObject *model) { // aktualizacja drutu zasilającego dla każdego pantografu, żeby odczytać napięcie @@ -3709,35 +3659,29 @@ bool TGround::GetTraction(TDynamicObject *model) { // dany sektor może nie mieć nic w środku for (node = tmp->nRenderWires; node; node = node->nNext3) // następny z grupy - if (node->iType == - TP_TRACTION) // w grupie tej są druty oraz inne linie + if (node->iType == TP_TRACTION) // w grupie tej są druty oraz inne linie { - vParam = - node->hvTraction - ->vParametric; // współczynniki równania parametrycznego + // współczynniki równania parametrycznego + vParam = node->hvTraction->vParametric; fRaParam = -glm::dot(pant0, vFront); auto const paramfrontdot = glm::dot( vParam, vFront ); fRaParam = -( glm::dot( node->hvTraction->pPoint1, vFront ) + fRaParam ) / ( paramfrontdot != 0.0 ? paramfrontdot : 0.001 ); // div0 trap if ((fRaParam >= -0.001) ? (fRaParam <= 1.001) : false) - { // jeśli tylko jest w przedziale, wyznaczyć odległość wzdłuż - // wektorów vUp i vLeft - vStyk = node->hvTraction->pPoint1 + - fRaParam * vParam; // punkt styku płaszczyzny z - // drutem (dla generatora łuku - // el.) - vGdzie = vStyk - pant0; // wektor - fVertical = glm::dot( - vGdzie, - vUp); // musi się mieścić w przedziale ruchu pantografu + { // jeśli tylko jest w przedziale, wyznaczyć odległość wzdłuż wektorów vUp i vLeft + // punkt styku płaszczyzny z drutem (dla generatora łuku el.) + vStyk = node->hvTraction->pPoint1 + fRaParam * vParam; + // wektor musi się mieścić w przedziale ruchu pantografu + vGdzie = vStyk - pant0; + fVertical = glm::dot( vGdzie, vUp); if (fVertical >= 0.0) // jeśli ponad pantografem (bo może // łapać druty spod wiaduktu) if (Global::bEnableTraction ? fVertical < p->PantWys - 0.15 : - false) // jeśli drut jest niżej niż 15cm pod - // ślizgiem - { // przełączamy w tryb połamania, o ile jedzie; + false) { + // jeśli drut jest niżej niż 15cm pod ślizgiem + // przełączamy w tryb połamania, o ile jedzie; // (bEnableTraction) aby dało się jeździć na // koślawych // sceneriach @@ -3827,18 +3771,7 @@ bool TGround::GetTraction(TDynamicObject *model) else p->hvPowerWire = NULL; // pantograf opuszczony } - // if (model->fWahaczeAmpMoverParameters->DistCounter) - //{//nieużywana normalnie zmienna ogranicza powtórzone logowania - // model->fWahaczeAmp=model->MoverParameters->DistCounter; - // ErrorLog(FloatToStrF(1000.0*model->MoverParameters->DistCounter,ffFixed,7,3)+","+FloatToStrF(p->PantTraction,ffFixed,7,3)+","+FloatToStrF(p->fHorizontal,ffFixed,7,3)+","+FloatToStrF(p->PantWys,ffFixed,7,3)+","+AnsiString(p->hvPowerWire?1:0)); - // // - // if (p->fHorizontal>1.0) - //{ - // //Global::iPause|=1; //zapauzowanie symulacji - // Global::fTimeSpeed=1; //spowolnienie czasu do obejrzenia pantografu - // return true; //łapacz - //} - //} + return true; }; diff --git a/Ground.h b/Ground.h index f045c23b..00bf072b 100644 --- a/Ground.h +++ b/Ground.h @@ -285,20 +285,24 @@ class TGround bool InitEvents(); #endif bool InitLaunchers(); +#ifdef EU07_USE_OLD_GROUNDCODE 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); +#endif TGroundNode * AddGroundNode(cParser *parser); +#ifdef EU07_USE_OLD_GROUNDCODE void UpdatePhys(double dt, int iter); // aktualizacja fizyki stałym krokiem bool Update(double dt, int iter); // aktualizacja przesunięć zgodna z FPS void Update_Hidden(); // updates invisible elements of the scene +#endif bool GetTraction(TDynamicObject *model); #ifdef EU07_USE_OLD_GROUNDCODE bool AddToQuery( TEvent *Event, TDynamicObject *Node ); bool CheckQuery(); -#endif TGroundNode * DynamicFindAny(std::string const &Name); TGroundNode * DynamicFind(std::string const &Name); +#endif void DynamicList(bool all = false); TGroundNode * FindGroundNode(std::string const &asNameToFind, TGroundNodeType const iNodeType); TGroundRect * GetRect( double x, double z ); @@ -325,7 +329,9 @@ class TGround // convert tp_terrain model to a series of triangle nodes void convert_terrain( TGroundNode const *Terrain ); void convert_terrain( TSubModel const *Submodel ); +#ifdef EU07_USE_OLD_GROUNDCODE void RaTriangleDivider(TGroundNode *node); +#endif void Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam); public: diff --git a/MemCell.cpp b/MemCell.cpp index 0bf32bf3..8143b2fa 100644 --- a/MemCell.cpp +++ b/MemCell.cpp @@ -180,3 +180,15 @@ 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" ) ); + } +} diff --git a/MemCell.h b/MemCell.h index 35637431..aa84d3b9 100644 --- a/MemCell.h +++ b/MemCell.h @@ -61,8 +61,14 @@ class TMemCell : public editor::basic_node { void AssignEvents(TEvent *e); }; -class memory_manager : public basic_table { + +class memory_table : public basic_table { + +public: + // legacy method, initializes traction after deserialization from scenario file + void + InitCells(); }; //--------------------------------------------------------------------------- diff --git a/Model3d.cpp b/Model3d.cpp index ea6dd7b9..f2ce05be 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1641,7 +1641,7 @@ void TSubModel::BinInit(TSubModel *s, float4x4 *m, std::vector *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; } } diff --git a/RealSound.cpp b/RealSound.cpp index ce9c6a3b..f16ffe05 100644 --- a/RealSound.cpp +++ b/RealSound.cpp @@ -21,8 +21,7 @@ http://mozilla.org/MPL/2.0/. #include "mczapkie/mctools.h" #include "usefull.h" -TRealSound::TRealSound(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, - bool freqmod, double rmin) +TRealSound::TRealSound(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod, double rmin) { Init(SoundName, SoundAttenuation, X, Y, Z, Dynamic, freqmod, rmin); } @@ -32,12 +31,7 @@ TRealSound::~TRealSound() // if (this) if (pSound) pSound->Stop(); } -void TRealSound::Free() -{ -} - -void TRealSound::Init(std::string const &SoundName, double DistanceAttenuation, double X, double Y, double Z, - bool Dynamic, bool freqmod, double rmin) +void TRealSound::Init(std::string const &SoundName, double DistanceAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod, double rmin) { // Nazwa=SoundName; //to tak raczej nie zadziała, (SoundName) jest tymczasowe pSound = TSoundsManager::GetFromName(SoundName, Dynamic, &fFrequency); @@ -71,7 +65,7 @@ void TRealSound::Init(std::string const &SoundName, double DistanceAttenuation, dSoundAtt = -1; }; -double TRealSound::ListenerDistance(vector3 ListenerPosition) +double TRealSound::ListenerDistance( Math3D::vector3 ListenerPosition) { if (dSoundAtt == -1) { @@ -83,7 +77,7 @@ double TRealSound::ListenerDistance(vector3 ListenerPosition) } } -void TRealSound::Play(double Volume, int Looping, bool ListenerInside, vector3 NewPosition) +void TRealSound::Play(double Volume, int Looping, bool ListenerInside, Math3D::vector3 NewPosition) { if (!pSound) return; @@ -155,11 +149,6 @@ void TRealSound::Play(double Volume, int Looping, bool ListenerInside, vector3 N } }; -void TRealSound::Start(){ - // włączenie dźwięku - -}; - void TRealSound::Stop() { DWORD stat; @@ -231,17 +220,14 @@ void TRealSound::ResetPosition() pSound->SetCurrentPosition(0); } -TTextSound::TTextSound(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, - bool Dynamic, bool freqmod, double rmin) - : TRealSound(SoundName, SoundAttenuation, X, Y, Z, Dynamic, freqmod, rmin) +TTextSound::TTextSound(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod, double rmin) : + TRealSound(SoundName, SoundAttenuation, X, Y, Z, Dynamic, freqmod, rmin) { Init(SoundName, SoundAttenuation, X, Y, Z, Dynamic, freqmod, rmin); } -void TTextSound::Init(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, - bool Dynamic, bool freqmod, double rmin) +void TTextSound::Init(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod, double rmin) { // dodatkowo doczytuje plik tekstowy - //TRealSound::Init(SoundName, SoundAttenuation, X, Y, Z, Dynamic, freqmod, rmin); fTime = GetWaveTime(); std::string txt(SoundName); txt.erase( txt.rfind( '.' ) ); // obcięcie rozszerzenia @@ -253,15 +239,11 @@ void TTextSound::Init(std::string const &SoundName, double SoundAttenuation, dou txt = "sounds\\" + txt; //ścieżka może nie być podana if (FileExists(txt)) { // wczytanie -/* TFileStream *ts = new TFileStream(txt, fmOpenRead); - asText = AnsiString::StringOfChar(' ', ts->Size); - ts->Read(asText.c_str(), ts->Size); - delete ts; -*/ std::ifstream inputfile( txt ); + std::ifstream inputfile( txt ); asText.assign( std::istreambuf_iterator( inputfile ), std::istreambuf_iterator() ); } }; -void TTextSound::Play(double Volume, int Looping, bool ListenerInside, vector3 NewPosition) +void TTextSound::Play(double Volume, int Looping, bool ListenerInside, Math3D::vector3 NewPosition) { if (false == asText.empty()) { // jeśli ma powiązany tekst diff --git a/RealSound.h b/RealSound.h index 1eec3761..c72d76a0 100644 --- a/RealSound.h +++ b/RealSound.h @@ -7,24 +7,25 @@ obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef RealSoundH -#define RealSoundH +#pragma once #include -#include "Sound.h" -#include "Geometry.h" -class TRealSound -{ - protected: +#include "Sound.h" +#include "dumb3d.h" +#include "names.h" + +class TRealSound { + +protected: PSound pSound = nullptr; -// char *Nazwa; // dla celow odwszawiania NOTE: currently not used anywhere - double fDistance = 0.0, - fPreviousDistance = 0.0; // dla liczenia Dopplera + double fDistance = 0.0; + double fPreviousDistance = 0.0; // dla liczenia Dopplera float fFrequency = 22050.0; // częstotliwość samplowania pliku int iDoppler = 0; // Ra 2014-07: możliwość wyłączenia efektu Dopplera np. dla śpiewu ptaków - public: - vector3 vSoundPosition; // polozenie zrodla dzwieku +public: + std::string m_name; + Math3D::vector3 vSoundPosition; // polozenie zrodla dzwieku double dSoundAtt = -1.0; // odleglosc polowicznego zaniku dzwieku double AM = 0.0; // mnoznik amplitudy double AA = 0.0; // offset amplitudy @@ -32,37 +33,43 @@ class TRealSound double FA = 0.0; // offset czestotliwosci bool bLoopPlay = false; // czy zapętlony dźwięk jest odtwarzany TRealSound() = default; - TRealSound( std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, - bool freqmod = false, double rmin = 0.0); + TRealSound( std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod = false, double rmin = 0.0 ); ~TRealSound(); - void Free(); - void Init(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, - bool freqmod = false, double rmin = 0.0); - double ListenerDistance(vector3 ListenerPosition); - void Play(double Volume, int Looping, bool ListenerInside, vector3 NewPosition); - void Start(); + void Init( std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod = false, double rmin = 0.0 ); + double ListenerDistance( Math3D::vector3 ListenerPosition); + void Play(double Volume, int Looping, bool ListenerInside, Math3D::vector3 NewPosition); void Stop(); void AdjFreq(double Freq, double dt); void SetPan(int Pan); double GetWaveTime(); // McZapkie TODO: dorobic dla roznych bps int GetStatus(); void ResetPosition(); - // void FreqReset(float f=22050.0) {fFrequency=f;}; bool Empty() { return ( pSound == nullptr ); } + void + name( std::string Name ) { + m_name = Name; } + std::string const & + name() const { + return m_name; } + glm::dvec3 + location() const { + return vSoundPosition; }; }; -class TTextSound : public TRealSound -{ // dźwięk ze stenogramem + + +class TTextSound : public TRealSound { + // dźwięk ze stenogramem std::string asText; float fTime; // czas trwania - public: - TTextSound(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, - bool Dynamic, bool freqmod = false, double rmin = 0.0); - void Init(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, - bool Dynamic, bool freqmod = false, double rmin = 0.0); - void Play(double Volume, int Looping, bool ListenerInside, vector3 NewPosition); +public: + TTextSound(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod = false, double rmin = 0.0); + void Init(std::string const &SoundName, double SoundAttenuation, double X, double Y, double Z, bool Dynamic, bool freqmod = false, double rmin = 0.0); + void Play(double Volume, int Looping, bool ListenerInside, Math3D::vector3 NewPosition); }; + + class TSynthSound { // klasa generująca sygnał odjazdu (Rp12, Rp13), potem rozbudować o pracę manewrowego... int iIndex[44]; // indeksy początkowe, gdy mamy kilka wariantów dźwięków składowych @@ -74,10 +81,16 @@ class TSynthSound // 41 - "tysiące" // 42 - indeksy początkowe dla "odjazd" // 43 - indeksy początkowe dla "gotów" - PSound *sSound; // posortowana tablica dźwięków, rozmiar zależny od liczby znalezionych plików + PSound sSound; // posortowana tablica dźwięków, rozmiar zależny od liczby znalezionych plików // a może zamiast wielu plików/dźwięków zrobić jeden połączony plik i posługiwać się czasem // od..do? }; + + +// collection of generators for power grid present in the scene +class sound_table : public basic_table { + +}; + //--------------------------------------------------------------------------- -#endif diff --git a/Segment.cpp b/Segment.cpp index bfed769e..293daf57 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -103,7 +103,7 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M fStep = fNewStep; if (fLength <= 0) { - ErrorLog( "Bad geometry: zero length spline \"" + pOwner->NameGet() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" ); + ErrorLog( "Bad geometry: 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ć @@ -205,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 geometry: shape estimation failed for spline \"" + pOwner->name() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" ); // MessageBox(0,"Too many iterations","GetTFromS",MB_OK); return fTime; }; @@ -323,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( 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( fRoll1, fRoll2, t ); a.y = fStoop; // pochylenie toru prostego a.z = fDirection; // kierunek toru w planie } diff --git a/Segment.h b/Segment.h index 93209804..dfcf09be 100644 --- a/Segment.h +++ b/Segment.h @@ -14,37 +14,6 @@ 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 = 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: @@ -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; @@ -140,10 +108,6 @@ public: int RaSegCount() const { return fTsBuffer ? iSegCount : 1; }; - inline - void - AngleSet(int const i, double const a) { - fAngle[i] = a; }; }; //--------------------------------------------------------------------------- diff --git a/Track.cpp b/Track.cpp index 041842bf..04884ce7 100644 --- a/Track.cpp +++ b/Track.cpp @@ -26,6 +26,7 @@ http://mozilla.org/MPL/2.0/. #include "AnimModel.h" #include "MemCell.h" #include "Event.h" +#include "simulation.h" // 101206 Ra: trapezoidalne drogi i tory // 110720 Ra: rozprucie zwrotnicy i odcinki izolowane @@ -2651,6 +2652,31 @@ int TTrack::TestPoint(vector3 *Point) return -1; }; +// retrieves list of the track's end points +std::vector +TTrack::endpoints() const { + + switch( eType ) { + case tt_Normal: + case tt_Table: { + return { + glm::dvec3{ Segment->FastGetPoint_0() }, + glm::dvec3{ Segment->FastGetPoint_1() } }; + } + case tt_Switch: + case tt_Cross: { + return { + glm::dvec3{ SwitchExtension->Segments[ 0 ]->FastGetPoint_0() }, + glm::dvec3{ SwitchExtension->Segments[ 0 ]->FastGetPoint_1() }, + glm::dvec3{ SwitchExtension->Segments[ 1 ]->FastGetPoint_0() }, + glm::dvec3{ SwitchExtension->Segments[ 1 ]->FastGetPoint_1() } }; + } + default: { + return{}; + } + } +} + void TTrack::MovedUp1(float const dh) { // poprawienie przechyłki wymaga wydłużenia podsypki fTexHeight1 += dh; @@ -2745,11 +2771,11 @@ TTrack * TTrack::Connected(int s, double &d) const // legacy method, initializes tracks after deserialization from scenario file void path_table::InitTracks() { -/* - TGroundNode *Model; - int iConnection; - for( auto *track : m_paths ) { + int connection { -1 }; + TTrack *matchingtrack { nullptr }; + + for( auto *track : m_items ) { track->AssignEvents( simulation::Events.FindEvent( track->asEvent0Name ), @@ -2776,30 +2802,32 @@ path_table::InitTracks() { } switch (track->eType) { +/* + // TODO: re-enable case tt_Table: { // obrotnicę też łączymy na starcie z innymi torami // szukamy modelu o tej samej nazwie - Model = FindGroundNode(Current->asName, TP_MODEL); + auto *instance = simulation::Instances.find( trackname ); // wiązanie toru z modelem obrotnicy track->RaAssign( Current, - ( Model ? - Model->Model : - nullptr ), + instance, simulation::Events.FindEvent( trackname + ":done" ), simulation::Events.FindEvent( trackname + ":joined" ) ); - if( Model == nullptr ) { + if( instance == nullptr ) { // jak nie ma modelu to pewnie jest wykolejnica, a ta jest domyślnie zamknięta i wykoleja break; } + // no break on purpose: // jak coś pójdzie źle, to robimy z tego normalny tor } +*/ case tt_Normal: { // tylko proste są podłączane do rozjazdów, stąd dwa rozjazdy się nie połączą ze sobą if( track->CurrentPrev() == nullptr ) { // tylko jeśli jeszcze nie podłączony - auto *matchingtrack = simulation::Region.FindTrack( track->CurrentSegment()->FastGetPoint_0(), iConnection, track ); - switch( iConnection ) { + std::tie( matchingtrack, connection ) = simulation::Region->find_path( track->CurrentSegment()->FastGetPoint_0(), track ); + switch( connection ) { case -1: // Ra: pierwsza koncepcja zawijania samochodów i statków // if ((Track->iCategoryFlag&1)==0) //jeśli nie jest torem szynowym // Track->ConnectPrevPrev(Track,0); //łączenie końca odcinka do samego siebie @@ -2830,12 +2858,14 @@ path_table::InitTracks() { matchingtrack->SetConnections( 1 ); // robi też Switch(0) matchingtrack->Switch( 0 ); break; + default: + break; } } if( track->CurrentNext() == nullptr ) { // tylko jeśli jeszcze nie podłączony - auto *matchingtrack = simulation::Region.FindTrack( track->CurrentSegment()->FastGetPoint_1(), iConnection, track ); - switch( iConnection ) { + std::tie( matchingtrack, connection ) = simulation::Region->find_path( track->CurrentSegment()->FastGetPoint_1(), track ); + switch( connection ) { case -1: // Ra: pierwsza koncepcja zawijania samochodów i statków // if ((Track->iCategoryFlag&1)==0) //jeśli nie jest torem szynowym // Track->ConnectNextNext(Track,1); //łączenie końca odcinka do samego siebie @@ -2866,6 +2896,8 @@ path_table::InitTracks() { matchingtrack->SetConnections( 1 ); // robi też Switch(0) // tmp->Switch(0); break; + default: + break; } } break; @@ -2910,11 +2942,12 @@ path_table::InitTracks() { else { // utworzenie automatycznej komórki // TODO: determine suitable location for this one, create and add world reference node - auto *memorycell = new TMemCell( isolated->asName ); // to nie musi mieć nazwy, nazwa w wyszukiwarce wystarczy + scene::node_data nodedata; + nodedata.name = isolated->asName; + auto *memorycell = new TMemCell( nodedata ); // to nie musi mieć nazwy, nazwa w wyszukiwarce wystarczy simulation::Memory.insert( memorycell ); isolated->pMemCell = memorycell; // wskaźnik komóki przekazany do odcinka izolowanego } isolated = isolated->Next(); } -*/ } diff --git a/Track.h b/Track.h index fe9d4e1c..0aaac064 100644 --- a/Track.h +++ b/Track.h @@ -119,7 +119,6 @@ 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 SwitchExtension; // dodatkowe dane do toru, który jest zwrotnicą std::shared_ptr Segment; @@ -234,6 +233,9 @@ public: void origin( glm::dvec3 Origin ) { m_origin = Origin; } + // retrieves list of the track's end points + std::vector + endpoints() const; #ifdef EU07_USE_OLD_GROUNDCODE void create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ); // wypełnianie VBO diff --git a/Traction.cpp b/Traction.cpp index dccce473..75686a97 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -91,7 +91,6 @@ sekcji z sąsiedniego przęsła). */ TTraction::TTraction( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {} - // legacy constructor TTraction::TTraction( std::string Name ) { @@ -173,6 +172,13 @@ TTraction::Load( cParser *parser, glm::dvec3 const &pOrigin ) { m_location = interpolate( pPoint2, pPoint1, 0.5 ); } +// retrieves list of the track's end points +std::vector +TTraction::endpoints() const { + + return { pPoint1, pPoint2 }; +} + std::size_t #ifdef EU07_USE_OLD_GROUNDCODE TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ) { @@ -621,205 +627,213 @@ TTraction::wire_color() const { // 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 iConnection; - std::string name; + + 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 - nTemp = FindGroundNode(traction->asPowerSupplyName, TP_TRACTIONPOWERSOURCE); - if (nTemp) // jak zasilacz znaleziony - traction->PowerSet(nTemp->psTractionPowerSource); // to podłączyć do przęsła - else if (traction->asPowerSupplyName != "*") // gwiazdka dla przęsła z izolatorem - if (traction->asPowerSupplyName != "none") // dopuszczamy na razie brak podłączenia? - { // logowanie błędu i utworzenie zasilacza o domyślnej zawartości - ErrorLog("Missed TractionPowerSource: " + traction->asPowerSupplyName); - nTemp = new TGroundNode(); - nTemp->iType = TP_TRACTIONPOWERSOURCE; - nTemp->asName = traction->asPowerSupplyName; - nTemp->psTractionPowerSource = new TTractionPowerSource(nTemp); - nTemp->psTractionPowerSource->Init(traction->NominalVoltage, traction->MaxCurrent); - nTemp->nNext = nRootOfType[nTemp->iType]; // ostatni dodany dołączamy na końcu - // nowego - nRootOfType[nTemp->iType] = nTemp; // ustawienie nowego na początku listy + 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 ); } + } } for( auto *traction : m_items ) { - if (!traction->hvNext[0]) // tylko jeśli jeszcze nie podłączony - { - tmp = FindTraction(traction->pPoint1, iConnection, nCurrent); - switch (iConnection) - { - case 0: - traction->Connect(0, tmp, 0); - break; - case 1: - traction->Connect(0, tmp, 1); - break; + if (!traction->hvNext[0]) { + // tylko jeśli jeszcze nie podłączony + std::tie( matchingtraction, connection ) = simulation::Region->find_traction( traction->pPoint1, traction ); + switch (connection) { + case 0: { + traction->Connect( 0, matchingtraction, 0 ); + break; + } + case 1: { + traction->Connect( 0, matchingtraction, 1 ); + break; + } + default: { + break; + } } - if (traction->hvNext[0]) // jeśli został podłączony - if (traction->psSection && tmp->psSection) // tylko przęsło z izolatorem może nie - // mieć zasilania, bo ma 2, trzeba - // sprawdzać sąsiednie - if (traction->psSection != - tmp->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 (traction->psSection->bSection && !tmp->psSection->bSection) - { //(tmp->psSection) jest podstacją, a (Traction->psSection) nazwą sekcji - tmp->PowerSet(traction->psSection); // zastąpienie wskazaniem sekcji + 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 (!traction->psSection->bSection && tmp->psSection->bSection) - { //(Traction->psSection) jest podstacją, a (tmp->psSection) nazwą sekcji - traction->PowerSet(tmp->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 ) ); } - else // jeśli obie to sekcje albo obie podstacje, to będzie błąd - ErrorLog("Bad power: at " + - to_string(traction->pPoint1.x, 2, 6) + " " + - to_string(traction->pPoint1.y, 2, 6) + " " + - to_string(traction->pPoint1.z, 2, 6)); } + } + } } - if (!traction->hvNext[1]) // tylko jeśli jeszcze nie podłączony - { - tmp = FindTraction(traction->pPoint2, iConnection, nCurrent); - switch (iConnection) - { - case 0: - traction->Connect(1, tmp, 0); - break; - case 1: - traction->Connect(1, tmp, 1); - break; + if (!traction->hvNext[1]) { + // tylko jeśli jeszcze nie podłączony + std::tie( matchingtraction, connection ) = simulation::Region->find_traction( traction->pPoint2, traction ); + switch (connection) { + case 0: { + traction->Connect( 1, matchingtraction, 0 ); + break; + } + case 1: { + traction->Connect( 1, matchingtraction, 1 ); + break; + } + default: { + break; + } } - if (traction->hvNext[1]) // jeśli został podłączony - if (traction->psSection && tmp->psSection) // tylko przęsło z izolatorem może nie - // mieć zasilania, bo ma 2, trzeba - // sprawdzać sąsiednie - if (traction->psSection != tmp->psSection) - { // to może być albo podłączenie podstacji lub kabiny sekcyjnej do sekcji, albo - // błąd - if (traction->psSection->bSection && !tmp->psSection->bSection) - { //(tmp->psSection) jest podstacją, a (Traction->psSection) nazwą sekcji - tmp->PowerSet(traction->psSection); // zastąpienie wskazaniem sekcji + 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 (!traction->psSection->bSection && tmp->psSection->bSection) - { //(Traction->psSection) jest podstacją, a (tmp->psSection) nazwą sekcji - traction->PowerSet(tmp->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 ) ); } - else // jeśli obie to sekcje albo obie podstacje, to będzie błąd - ErrorLog("Bad power: at " + - to_string(traction->pPoint2.x, 2, 6) + " " + - to_string(traction->pPoint2.y, 2, 6) + " " + - to_string(traction->pPoint2.z, 2, 6)); } + } + } } } - iConnection = 0; // teraz będzie licznikiem końców + 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()) // oznakowanie przedostatnich przęseł - { // poszukiwanie bieżni wspólnej dla przedostatnich przęseł, również w celu połączenia - // zasilania - // to się nie sprawdza, bo połączyć się mogą dwa niezasilane odcinki jako najbliższe - // sobie - // nCurrent->hvTraction->hvParallel=TractionNearestFind(nCurrent->pCenter,0,nCurrent); - // //szukanie najbliższego przęsła - // trzeba by zliczać końce, a potem wpisać je do tablicy, aby sukcesywnie podłączać do - // zasilaczy + if( traction->WhereIs() ) { + // true for outer pieces of the traction section traction->iTries = 5; // oznaczanie końcowych - ++iConnection; + ++endcount; } - if (traction->fResistance[0] == 0.0) - { - traction->ResistanceCalc(); // obliczanie przęseł w segmencie z bezpośrednim zasilaniem - // ErrorLog("Section "+nCurrent->hvTraction->asPowerSupplyName+" connected"); //jako - // niby błąd będzie bardziej widoczne + 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 } - // if (!Traction->hvParallel) //jeszcze utworzyć pętle z bieżni wspólnych } - int zg = 0; // zgodność kierunku przęseł, tymczasowo iterator do tabeli końców - // końców jest ok. 10 razy mniej niż wszystkich przęseł (Quark: 216) - TGroundNode **nEnds = new TGroundNode *[iConnection]; + + std::vector ends; ends.reserve( endcount ); for( auto *traction : m_items ) { //łączenie bieżni wspólnych, w tym oznaczanie niepodanych jawnie - if (!traction->asParallel.empty()) // będzie wskaźnik na inne przęsło + 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) // jeśli jeszcze nie został włączony w kółko - { + else if( traction->hvParallel == nullptr ) { + // jeśli jeszcze nie został włączony w kółko auto *nTemp = find( traction->asParallel ); - if (nTemp) - { // o ile zostanie znalezione przęsło o takiej nazwie - if (!nTemp->hvParallel) // jeśli tamten jeszcze nie ma wskaźnika bieżni wspólnej + 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 + } + 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) - ErrorLog("Missed overhead: " + traction->asParallel); // logowanie braku - } - if (traction->iTries > 0) // jeśli zaznaczony do podłączenia - // if (!nCurrent->hvTraction->psPower[0]||!nCurrent->hvTraction->psPower[1]) - if (zg < iConnection) // zabezpieczenie - nEnds[zg++] = nCurrent; // wypełnianie tabeli końców w celu szukania im połączeń - } - while( zg < iConnection ) { - // zapełnienie do końca tablicy, jeśli by jakieś końce wypadły - nEnds[ zg++ ] = nullptr; - } - zg = 1; // nieefektywny przebieg kończy łączenie - while (zg) - { // ustalenie zastępczej rezystancji dla każdego przęsła - zg = 0; // flaga podłączonych przęseł końcowych: -1=puste wskaźniki, 0=coś zostało, - // 1=wykonano łączenie - for (int i = 0; i < iConnection; ++i) - if (nEnds[i]) // załatwione będziemy zerować - { // każdy przebieg to próba podłączenia końca segmentu naprężania do innego zasilanego - // przęsła - if (nEnds[i]->hvTraction->hvNext[0]) - { // jeśli końcowy ma ciąg dalszy od strony 0 (Point1), szukamy odcinka najbliższego - // do Point2 - if (TractionNearestFind(nEnds[i]->hvTraction->pPoint2, 0, - nEnds[i])) // poszukiwanie przęsła - { - nEnds[i] = NULL; - zg = 1; // jak coś zostało podłączone, to może zasilanie gdzieś dodatkowo - // dotrze - } - } - else if (nEnds[i]->hvTraction->hvNext[1]) - { // jeśli końcowy ma ciąg dalszy od strony 1 (Point2), szukamy odcinka najbliższego - // do Point1 - if (TractionNearestFind(nEnds[i]->hvTraction->pPoint1, 1, - nEnds[i])) // poszukiwanie przęsła - { - nEnds[i] = NULL; - zg = 1; // jak coś zostało podłączone, to może zasilanie gdzieś dodatkowo - // dotrze - } - } - else - { // gdy koniec jest samotny, to na razie nie zostanie podłączony (nie powinno - // takich być) - nEnds[i] = NULL; + if( traction->hvParallel == nullptr ) { + ErrorLog( "Missed overhead: " + traction->asParallel ); // logowanie braku } } + } + if( traction->iTries > 0 ) { + // jeśli zaznaczony do podłączenia + // wypełnianie tabeli końców w celu szukania im połączeń + ends.emplace_back( traction ); + } } - delete[] nEnds; // nie potrzebne już -*/ + + 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 ); } diff --git a/Traction.h b/Traction.h index 67f46d6a..fab4909d 100644 --- a/Traction.h +++ b/Traction.h @@ -55,13 +55,16 @@ class TTraction : public editor::basic_node { TTraction( scene::node_data const &Nodedata ); // legacy constructor TTraction( std::string Name ); - virtual ~TTraction() = default; 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 + 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 #ifdef EU07_USE_OLD_GROUNDCODE diff --git a/TractionPower.cpp b/TractionPower.cpp index ade5cc5b..0b6ee2c9 100644 --- a/TractionPower.cpp +++ b/TractionPower.cpp @@ -20,14 +20,12 @@ http://mozilla.org/MPL/2.0/. //--------------------------------------------------------------------------- -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,67 @@ 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() -{ +#ifdef EU07_USE_OLD_GROUNDCODE + parser->getTokens( 7, false ); + *parser +#else + parser->getTokens( 10, false ); + *parser + >> m_location.x + >> m_location.y + >> m_location.z +#endif + >> NominalVoltage + >> VoltageFrequency + >> InternalRes + >> MaxOutputCurrent + >> FastFuseTimeOut + >> FastFuseRepetition + >> SlowFuseTimeOut; + + std::string token { parser->getToken() }; + 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(); + } + + 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 +149,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 ); + } +} + //--------------------------------------------------------------------------- diff --git a/TractionPower.h b/TractionPower.h index d85330f7..ae9bb18e 100644 --- a/TractionPower.h +++ b/TractionPower.h @@ -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 { + +public: + // legacy method, calculates changes in simulation state over specified time + void + update( double const Deltatime ); +}; + //--------------------------------------------------------------------------- -#endif diff --git a/Train.cpp b/Train.cpp index 4a33dd9d..3d1c34db 100644 --- a/Train.cpp +++ b/Train.cpp @@ -357,14 +357,6 @@ TTrain::TTrain() { fPress[ i ][ j ] = 0.0; } -TTrain::~TTrain() -{ - if (DynamicObject) - if (DynamicObject->Mechanik) - DynamicObject->Mechanik->TakeControl( - true); // likwidacja kabiny wymaga przejęcia przez AI -} - bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d) { // powiązanie ręcznego sterowania kabiną z pojazdem // Global::pUserDynamic=NewDynamicObject; //pojazd renderowany bez trzęsienia @@ -3587,7 +3579,7 @@ bool TTrain::Update( double const Deltatime ) iDoorNo[i] = p->iAnimType[ANIM_DOORS]; iUnits[i] = iUnitNo; cCode[i] = p->MoverParameters->TypeName[p->MoverParameters->TypeName.length()]; - asCarName[i] = p->GetName(); + 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); @@ -6259,7 +6251,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); diff --git a/Train.h b/Train.h index 9fe73f93..6b077597 100644 --- a/Train.h +++ b/Train.h @@ -80,7 +80,6 @@ class TTrain bool ShowNextCurrent; // pokaz przd w podlaczonej lokomotywie (ET41) bool InitializeCab(int NewCabNo, std::string const &asFileName); TTrain(); - ~TTrain(); // McZapkie-010302 bool Init(TDynamicObject *NewDynamicObject, bool e3d = false); void OnKeyDown(int cKey); diff --git a/World.cpp b/World.cpp index 53c0634a..be3f6b32 100644 --- a/World.cpp +++ b/World.cpp @@ -207,6 +207,13 @@ void TWorld::TrainDelete(TDynamicObject *d) if (Train) if (Train->Dynamic() != d) return; // nie tego usuwać +#ifdef EU07_SCENERY_EDITOR + if( ( Train->DynamicObject ) + && ( Train->DynamicObject->Mechanik ) ) { + // likwidacja kabiny wymaga przejęcia przez AI + Train->DynamicObject->Mechanik->TakeControl( true ); + } +#endif delete Train; // i nie ma czym sterować Train = NULL; Controlled = NULL; // tego też już nie ma @@ -263,13 +270,25 @@ bool TWorld::Init( GLFWwindow *Window ) { UILayer.set_progress( "Preparing train / Przygotowanie kabiny" ); WriteLog( "Player train init: " + Global::asHumanCtrlVehicle ); +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *nPlayerTrain = NULL; - if (Global::asHumanCtrlVehicle != "ghostview") - nPlayerTrain = Ground.DynamicFind(Global::asHumanCtrlVehicle); // szukanie w tych z obsadą +#else + TDynamicObject *nPlayerTrain; +#endif + if( Global::asHumanCtrlVehicle != "ghostview" ) +#ifdef EU07_USE_OLD_GROUNDCODE + nPlayerTrain = Ground.DynamicFind( Global::asHumanCtrlVehicle ); // szukanie w tych z obsadą +#else + nPlayerTrain = simulation::Vehicles.find( Global::asHumanCtrlVehicle ); +#endif if (nPlayerTrain) { Train = new TTrain(); +#ifdef EU07_USE_OLD_GROUNDCODE if (Train->Init(nPlayerTrain->DynamicObject)) +#else + if( Train->Init( nPlayerTrain ) ) +#endif { Controlled = Train->Dynamic(); mvControlled = Controlled->ControlledFind()->MoverParameters; @@ -522,7 +541,11 @@ void TWorld::OnKeyDown(int cKey) break; } +#ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *tmp = Ground.DynamicNearest( Camera.Pos, 50, true ); //łapiemy z obsadą +#else + TDynamicObject *tmp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 50, true ) ); +#endif if( ( tmp != nullptr ) && ( tmp != Controlled ) ) { @@ -700,7 +723,11 @@ void TWorld::OnKeyDown(int cKey) */ if (cKey == Global::Keys[k_Heating]) // Ra: klawisz nie jest najszczęśliwszy { // zmiana próżny/ładowny; Ra: zabrane z kabiny - TDynamicObject *temp = Global::DynamicNearest(); +#ifdef EU07_USE_OLD_GROUNDCODE + TDynamicObject *temp = Global::DynamicNearest(); +#else + TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false ) ); +#endif if (temp) { if (Global::shiftState ? temp->MoverParameters->IncBrakeMult() : @@ -743,7 +770,11 @@ void TWorld::OnKeyDown(int cKey) } else if (cKey == Global::Keys[k_IncLocalBrakeLevel]) { // zahamowanie dowolnego pojazdu +#ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *temp = Global::DynamicNearest(); +#else + TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false ) ); +#endif if (temp) { if (Global::ctrlState) @@ -762,7 +793,11 @@ void TWorld::OnKeyDown(int cKey) } else if (cKey == Global::Keys[k_DecLocalBrakeLevel]) { // odhamowanie dowolnego pojazdu +#ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *temp = Global::DynamicNearest(); +#else + TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false ) ); +#endif if (temp) { if (Global::ctrlState) @@ -915,20 +950,7 @@ void TWorld::FollowView(bool wycisz) { DistantView(); }; -bool TWorld::Update() -{ -#ifdef USE_SCENERY_MOVING - vector3 tmpvector = Global::GetCameraPosition(); - tmpvector = vector3(-int(tmpvector.x) + int(tmpvector.x) % 10000, - -int(tmpvector.y) + int(tmpvector.y) % 10000, - -int(tmpvector.z) + int(tmpvector.z) % 10000); - if (tmpvector.x || tmpvector.y || tmpvector.z) - { - WriteLog("Moving scenery"); - Ground.MoveGroundNode(tmpvector); - WriteLog("Scenery moved"); - }; -#endif +bool TWorld::Update() { Timer::UpdateTimers(Global::iPause != 0); @@ -955,7 +977,7 @@ bool TWorld::Update() /* fTimeBuffer += dt; //[s] dodanie czasu od poprzedniej ramki */ - m_primaryupdateaccumulator += dt; +// m_primaryupdateaccumulator += dt; // unused for the time being m_secondaryupdateaccumulator += dt; /* if (fTimeBuffer >= fMaxDt) // jest co najmniej jeden krok; normalnie 0.01s @@ -999,21 +1021,26 @@ bool TWorld::Update() dt = dt / iterations; // Ra: fizykę lepiej by było przeliczać ze stałym krokiem */ } + auto const stepdeltatime { dt / updatecount }; // NOTE: updates are limited to 20, but dt is distributed over potentially many more iterations // this means at count > 20 simulation and render are going to desync. is that right? // NOTE: experimentally changing this to prevent the desync. // TODO: test what happens if we hit more than 20 * 0.01 sec slices, i.e. less than 5 fps +#ifdef EU07_USE_OLD_GROUNDCODE if( true == Global::FullPhysics ) { - // default calculation mode, each step calculated separately - for( int updateidx = 0; updateidx < updatecount; ++updateidx ) { - Ground.Update( dt / updatecount, 1 ); + // mixed calculation mode, steps calculated in ~0.05s chunks + while( updatecount >= 5 ) { + Ground.Update( stepdeltatime, 5 ); + updatecount -= 5; + } + if( updatecount ) { + Ground.Update( stepdeltatime, updatecount ); } } else { - // slightly simplified calculation mode; can lead to errors - Ground.Update( dt / updatecount, updatecount ); + // simplified calculation mode; faster but can lead to errors + Ground.Update( stepdeltatime, updatecount ); } - // yB dodał przyspieszacz fizyki if( (true == DebugModeFlag) && (true == Global::bActive) // nie przyspieszać, gdy jedzie w tle :) @@ -1024,8 +1051,24 @@ bool TWorld::Update() Ground.Update( dt, 1 ); Ground.Update( dt, 1 ); // 5 razy } - // secondary fixed step simulation time routines +#else + if( true == Global::FullPhysics ) { + // mixed calculation mode, steps calculated in ~0.05s chunks + while( updatecount >= 5 ) { + simulation::State.update( stepdeltatime, 5 ); + updatecount -= 5; + } + if( updatecount ) { + simulation::State.update( stepdeltatime, updatecount ); + } + } + else { + // simplified calculation mode; faster but can lead to errors + simulation::State.update( stepdeltatime, updatecount ); + } +#endif + // secondary fixed step simulation time routines while( m_secondaryupdateaccumulator >= m_secondaryupdaterate ) { Global::tranTexts.Update(); // obiekt obsługujący stenogramy dźwięków na ekranie @@ -1065,11 +1108,12 @@ bool TWorld::Update() #ifdef EU07_USE_OLD_GROUNDCODE Ground.CheckQuery(); + Ground.Update_Hidden(); #else simulation::Events.CheckQuery(); + simulation::Region->update(); #endif - Ground.Update_Hidden(); simulation::Lights.update(); // render time routines follow: @@ -1119,10 +1163,15 @@ TWorld::Update_Camera( double const Deltatime ) { Camera.LookAt = Controlled->GetPosition(); } else { - TDynamicObject *d = - Ground.DynamicNearest( Camera.Pos, 300 ); // szukaj w promieniu 300m +#ifdef EU07_USE_OLD_GROUNDCODE + TDynamicObject *d = Ground.DynamicNearest( Camera.Pos, 300 ); // szukaj w promieniu 300m if( !d ) d = Ground.DynamicNearest( Camera.Pos, 1000 ); // dalej szukanie, jesli bliżej nie ma +#else + TDynamicObject *d = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 300, false ) ); + if( !d ) + d = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 1000, false ) ); // dalej szukanie, jesli bliżej nie ma +#endif if( d && pDynamicNearest ) { // jeśli jakiś jest znaleziony wcześniej if( 100.0 * LengthSquared3( d->GetPosition() - Camera.Pos ) > LengthSquared3( pDynamicNearest->GetPosition() - Camera.Pos ) ) { @@ -1305,7 +1354,11 @@ TWorld::Update_UI() { // timetable TDynamicObject *tmp = ( FreeFlyModeFlag ? +#ifdef EU07_USE_OLD_GROUNDCODE Ground.DynamicNearest( Camera.Pos ) : +#else + std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false ) ) : +#endif Controlled ); // w trybie latania lokalizujemy wg mapy if( tmp == nullptr ) { break; } @@ -1386,8 +1439,12 @@ TWorld::Update_UI() { TDynamicObject *tmp = ( FreeFlyModeFlag ? - Ground.DynamicNearest( Camera.Pos ) : - Controlled ); // w trybie latania lokalizujemy wg mapy +#ifdef EU07_USE_OLD_GROUNDCODE + Ground.DynamicNearest( Camera.Pos ) : +#else + std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false ) ) : +#endif + Controlled ); // w trybie latania lokalizujemy wg mapy if( tmp != nullptr ) { // @@ -1403,7 +1460,7 @@ TWorld::Update_UI() { uitextline1 += "; C0:" + ( tmp->PrevConnected ? - tmp->PrevConnected->GetName() + ":" + to_string( tmp->MoverParameters->Couplers[ 0 ].CouplingFlag ) + ( + tmp->PrevConnected->name() + ":" + to_string( tmp->MoverParameters->Couplers[ 0 ].CouplingFlag ) + ( tmp->MoverParameters->Couplers[ 0 ].CouplingFlag == 0 ? " (" + to_string( tmp->MoverParameters->Couplers[ 0 ].CoupleDist, 1 ) + " m)" : "" ) : @@ -1411,7 +1468,7 @@ TWorld::Update_UI() { uitextline1 += " C1:" + ( tmp->NextConnected ? - tmp->NextConnected->GetName() + ":" + to_string( tmp->MoverParameters->Couplers[ 1 ].CouplingFlag ) + ( + tmp->NextConnected->name() + ":" + to_string( tmp->MoverParameters->Couplers[ 1 ].CouplingFlag ) + ( tmp->MoverParameters->Couplers[ 1 ].CouplingFlag == 0 ? " (" + to_string( tmp->MoverParameters->Couplers[ 1 ].CoupleDist, 1 ) + " m)" : "" ) : @@ -1653,7 +1710,11 @@ TWorld::Update_UI() { TDynamicObject *tmp = ( FreeFlyModeFlag ? +#ifdef EU07_USE_OLD_GROUNDCODE Ground.DynamicNearest( Camera.Pos ) : +#else + std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false ) ) : +#endif Controlled ); // w trybie latania lokalizujemy wg mapy if( tmp == nullptr ) { break; @@ -1867,6 +1928,7 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) Now() + " " + to_string(pRozkaz->iComm) + " " + std::string(pRozkaz->cString + 11 + i, (unsigned)(pRozkaz->cString[10 + i])) + " rcvd"); +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *t = Ground.DynamicFind( std::string(pRozkaz->cString + 11 + i, (unsigned)pRozkaz->cString[10 + i])); // nazwa pojazdu jest druga @@ -1878,6 +1940,19 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) NULL, stopExt); // floaty są z przodu WriteLog("AI command: " + std::string(pRozkaz->cString + 9, i)); } +#else + // nazwa pojazdu jest druga + auto *vehicle = simulation::Vehicles.find( { pRozkaz->cString + 11 + i, (unsigned)pRozkaz->cString[ 10 + i ] } ); + if( ( vehicle != nullptr ) + && ( vehicle->Mechanik != nullptr ) ) { + vehicle->Mechanik->PutCommand( + { pRozkaz->cString + 9, static_cast(i) }, + pRozkaz->fPar[0], pRozkaz->fPar[1], + nullptr, + stopExt ); // floaty są z przodu + WriteLog("AI command: " + std::string(pRozkaz->cString + 9, i)); + } +#endif } break; case 4: // badanie zajętości toru @@ -1920,6 +1995,7 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) " rcvd"); if (pRozkaz->cString[0]) // jeśli długość nazwy jest niezerowa { // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #7 +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *t; if (pRozkaz->cString[1] == '*') t = Ground.DynamicFind( @@ -1929,6 +2005,9 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) pRozkaz->cString + 1, (unsigned)pRozkaz->cString[0])); // nazwa pojazdu if (t) Ground.WyslijNamiary(t); // wysłanie informacji o pojeździe +#else + // TODO: implement +#endif } else { // dla pustego wysyłamy ramki 6 z nazwami pojazdów AI (jeśli potrzebne wszystkie, @@ -1964,34 +2043,34 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd"); - if (pRozkaz->cString[1]) // jeśli długość nazwy jest niezerowa - { // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #13 - TGroundNode *t; - if (pRozkaz->cString[2] == '*') - t = Ground.DynamicFind( - Global::asHumanCtrlVehicle); // nazwa pojazdu użytkownika - else - t = Ground.DynamicFindAny( - std::string(pRozkaz->cString + 2, - (unsigned)pRozkaz->cString[1])); // nazwa pojazdu - if (t) - { - TDynamicObject *d = t->DynamicObject; - while (d) - { - d->Damage(pRozkaz->cString[0]); - d = d->Next(); // pozostałe też - } - d = t->DynamicObject->Prev(); - while (d) - { - d->Damage(pRozkaz->cString[0]); - d = d->Prev(); // w drugą stronę też - } - Ground.WyslijUszkodzenia(t->asName, t->DynamicObject->MoverParameters->EngDmgFlag); // zwrot informacji o pojeździe - } - } - // Ground.IsolatedBusy(AnsiString(pRozkaz->cString+1,(unsigned)(pRozkaz->cString[0]))); +#ifdef EU07_USE_OLD_GROUNDCODE + if( pRozkaz->cString[ 1 ] ) // jeśli długość nazwy jest niezerowa + { // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #13 + TGroundNode *t; + if( pRozkaz->cString[ 2 ] == '*' ) + t = Ground.DynamicFind( + Global::asHumanCtrlVehicle ); // nazwa pojazdu użytkownika + else + t = Ground.DynamicFindAny( + std::string( pRozkaz->cString + 2, + (unsigned)pRozkaz->cString[ 1 ] ) ); // nazwa pojazdu + if( t ) { + TDynamicObject *d = t->DynamicObject; + while( d ) { + d->Damage( pRozkaz->cString[ 0 ] ); + d = d->Next(); // pozostałe też + } + d = t->DynamicObject->Prev(); + while( d ) { + d->Damage( pRozkaz->cString[ 0 ] ); + d = d->Prev(); // w drugą stronę też + } + Ground.WyslijUszkodzenia( t->asName, t->DynamicObject->MoverParameters->EngDmgFlag ); // zwrot informacji o pojeździe + } + } +#else + // TODO: implement +#endif break; } }; @@ -2143,7 +2222,7 @@ void TWorld::ChangeDynamic() { Train->DynamicSet( temp ); Controlled = temp; mvControlled = Controlled->ControlledFind()->MoverParameters; - Global::asHumanCtrlVehicle = Train->Dynamic()->GetName(); + Global::asHumanCtrlVehicle = Train->Dynamic()->name(); if( Train->Dynamic()->Mechanik ) // AI może sobie samo pójść if( !Train->Dynamic()->Mechanik->AIControllFlag ) // tylko jeśli ręcznie prowadzony { diff --git a/openglgeometrybank.h b/openglgeometrybank.h index ee0aa234..05a813c1 100644 --- a/openglgeometrybank.h +++ b/openglgeometrybank.h @@ -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 ); } diff --git a/parser.cpp b/parser.cpp index 465b1d7b..c220b0c2 100644 --- a/parser.cpp +++ b/parser.cpp @@ -205,23 +205,21 @@ std::string cParser::readToken(bool ToLower, const char *Break) 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 + if( ( true == LoadTraction ) + || ( ( includefile.find( "tr/" ) == std::string::npos ) + && ( includefile.find( "tra/" ) == std::string::npos ) ) ) { + std::string parameter = readToken(false); // w parametrach nie zmniejszamy while( (parameter.empty() == false) && (parameter.compare("end") != 0) ) { - parameters.push_back(parameter); + parameters.emplace_back(parameter); parameter = readToken(false); } // if (trtest2.find("tr/")!=0) mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, LoadTraction); if (mIncludeParser->mSize <= 0) - ErrorLog("Missed include: " + includefile); + ErrorLog("Bad include: can't open file \"" + includefile + "\"" ); } else { while( token.compare( "end" ) != 0 ) { diff --git a/renderer.cpp b/renderer.cpp index 7c1ac70d..3107a28e 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -1602,7 +1602,17 @@ opengl_renderer::Render( scene::basic_region *Region ) { m_sectionqueue.clear(); m_cellqueue.clear(); - +/* + for( auto *section : Region->sections( m_renderpass.camera.position(), m_renderpass.draw_range * Global::fDistanceFactor ) ) { +#ifdef EU07_USE_DEBUG_CULLING + if( m_worldcamera.camera.visible( section->m_area ) ) { +#else + if( m_renderpass.camera.visible( section->m_area ) ) { +#endif + m_sectionqueue.emplace_back( section ); + } + } +*/ // build a list of region sections to render glm::vec3 const cameraposition { m_renderpass.camera.position() }; auto const camerax = static_cast( std::floor( cameraposition.x / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2 ) ); @@ -1903,11 +1913,22 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator switch( m_renderpass.draw_mode ) { case rendermode::color: - case rendermode::shadows: - case rendermode::pickscenery: { - // render + case rendermode::shadows: { // opaque parts of instanced models for( auto *instance : cell->m_instancesopaque ) { Render( instance ); } + // opaque parts of vehicles + for( auto *path : cell->m_paths ) { + for( auto *dynamic : path->Dynamics ) { + Render( dynamic ); + } + } + } + case rendermode::pickscenery: { + // opaque parts of instanced models + for( auto *instance : cell->m_instancesopaque ) { + ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); + Render( instance ); + } // TODO: add remaining content types break; } @@ -2863,7 +2884,12 @@ opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_seque // translucent parts of instanced models for( auto *instance : cell->m_instancetranslucent ) { Render_Alpha( instance ); } - // TODO: add remaining content types + // translucent parts of vehicles + for( auto *path : cell->m_paths ) { + for( auto *dynamic : path->Dynamics ) { + Render_Alpha( dynamic ); + } + } ++first; } diff --git a/scene.cpp b/scene.cpp index 43adb766..2dfb70da 100644 --- a/scene.cpp +++ b/scene.cpp @@ -10,11 +10,28 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "scene.h" +#include "globals.h" #include "renderer.h" #include "logs.h" namespace scene { +// legacy method, updates sounds and polls event launchers within radius around specified point +void +basic_cell::update() { +/* + // renderowanie obiektów aktywnych a niewidocznych + for( auto node = subcell->nRenderHidden; node; node = node->nNext3 ) { + node->RenderHidden(); + } +*/ + // TBD, TODO: move to sound renderer + for( auto *path : m_paths ) { + // dźwięki pojazdów, również niewidocznych + path->RenderDynSounds(); + } +} + // adds provided shape to the cell void basic_cell::insert( shape_node Shape ) { @@ -90,6 +107,131 @@ basic_cell::insert( TAnimModel *Instance ) { } } +// registers provided path in the lookup directory of the cell +void +basic_cell::register_end( TTrack *Path ) { + + m_directories.paths.emplace( Path ); +} + +// registers provided traction piece in the lookup directory of the cell +void +basic_cell::register_end( TTraction *Traction ) { + + m_directories.traction.emplace( Traction ); +} + +// find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance +std::tuple +basic_cell::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) { + + TDynamicObject *vehiclenearest { nullptr }; + float leastdistance { std::numeric_limits::max() }; + float distance; + float const distancecutoff { Radius * Radius }; // we'll ignore vehicles farther than this + + for( auto *path : m_paths ) { + for( auto *vehicle : path->Dynamics ) { + if( ( true == Onlycontrolled ) + && ( vehicle->Mechanik == nullptr ) ) { + continue; + } + distance = glm::length2( glm::dvec3{ vehicle->GetPosition() } - Point ); + if( ( distance > distancecutoff ) + || ( distance > leastdistance ) ){ + continue; + } + std::tie( vehiclenearest, leastdistance ) = std::tie( vehicle, distance ); + } + } + return std::tie( vehiclenearest, leastdistance ); +} + +// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint +std::tuple +basic_cell::find( glm::dvec3 const &Point, TTrack const *Exclude ) { + + Math3D::vector3 point { Point.x, Point.y, Point.z }; // sad workaround until math classes unification + int endpointid; + + for( auto *path : m_directories.paths ) { + + if( path == Exclude ) { continue; } + + endpointid = path->TestPoint( &point ); + if( endpointid >= 0 ) { + + return std::tie( path, endpointid ); + } + } + return { nullptr, -1 }; +} + +// 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 +basic_cell::find( glm::dvec3 const &Point, TTraction const *Exclude ) { + + int endpointid; + + for( auto *traction : m_directories.traction ) { + + if( traction == Exclude ) { continue; } + + endpointid = traction->TestPoint( Point ); + if( endpointid >= 0 ) { + + return std::tie( traction, endpointid ); + } + } + return { nullptr, -1 }; +} + +// 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 +basic_cell::find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ) { + + TTraction + *tractionnearest { nullptr }; + float + distance, + distancenearest { std::numeric_limits::max() }; + int endpoint, + endpointnearest { -1 }; + + for( auto *traction : m_directories.traction ) { + + if( ( traction == Other ) + || ( traction->psSection != Other->psSection ) + || ( traction == Other->hvNext[ 0 ] ) + || ( traction == Other->hvNext[ 1 ] ) ) { + // ignore pieces from different sections, and ones connected to the other piece + continue; + } + endpoint = ( + glm::dot( traction->vParametric, Other->vParametric ) >= 0.0 ? + Currentdirection ^ 1 : + Currentdirection ); + if( ( traction->psPower[ endpoint ] == nullptr ) + || ( traction->fResistance[ endpoint ] < 0.0 ) ) { + continue; + } + distance = glm::length2( traction->location() - Point ); + if( distance < distancenearest ) { + std::tie( tractionnearest, endpointnearest, distancenearest ) = std::tie( traction, endpoint, distance ); + } + } + return { tractionnearest, endpointnearest, distancenearest }; +} + +// sets center point of the section +void +basic_cell::center( glm::dvec3 Center ) { + + m_area.center = Center; + // NOTE: we should also update origin point for the contained nodes, but in practice we can skip this + // as all nodes will be added only after the proper center point was set, and won't change +} + // generates renderable version of held non-instanced geometry void basic_cell::create_geometry( geometrybank_handle const &Bank ) { @@ -104,17 +246,23 @@ basic_cell::create_geometry( geometrybank_handle const &Bank ) { #endif } -// sets center point of the section + + +// legacy method, updates sounds and polls event launchers within radius around specified point void -basic_cell::center( glm::dvec3 Center ) { +basic_section::update( glm::dvec3 const &Location, float const Radius ) { - m_area.center = Center; - // NOTE: we should also update origin point for the contained nodes, but in practice we can skip this - // as all nodes will be added only after the proper center point was set, and won't change + auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + Radius, 2 ) }; + + for( auto &cell : m_cells ) { + + if( glm::length2( cell.area().center - Location ) < squaredradii ) { + // we reject cells which aren't within our area of interest + cell.update(); + } + } } - - // adds provided shape to the section void basic_section::insert( shape_node Shape ) { @@ -168,6 +316,96 @@ basic_section::insert( TAnimModel *Instance ) { cell( Instance->location() ).insert( Instance ); } +// registers specified end point of the provided path in the lookup directory of the region +void +basic_section::register_end( TTrack *Path, glm::dvec3 const &Point ) { + + cell( Point ).register_end( Path ); +} + +// registers specified end point of the provided traction piece in the lookup directory of the region +void +basic_section::register_end( TTraction *Traction, glm::dvec3 const &Point ) { + + cell( Point ).register_end( Traction ); +} + +// find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance +std::tuple +basic_section::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) { + + // go through sections within radius of interest, and pick the nearest candidate + TDynamicObject + *vehiclefound, + *vehiclenearest { nullptr }; + float + distancefound, + distancenearest { std::numeric_limits::max() }; + + auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + Radius, 2 ) }; + + for( auto &cell : m_cells ) { + // we reject early cells which aren't within our area of interest + if( glm::length2( cell.area().center - Point ) > squaredradii ) { + continue; + } + std::tie( vehiclefound, distancefound ) = cell.find( Point, Radius, Onlycontrolled ); + if( ( vehiclefound != nullptr ) + && ( distancefound < distancenearest ) ) { + + std::tie( vehiclenearest, distancenearest ) = std::tie( vehiclefound, distancefound ); + } + } + return std::tie( vehiclenearest, distancenearest ); +} + +// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint +std::tuple +basic_section::find( glm::dvec3 const &Point, TTrack const *Exclude ) { + + return cell( Point ).find( Point, 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 +basic_section::find( glm::dvec3 const &Point, TTraction const *Exclude ) { + + return cell( Point ).find( Point, 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 +basic_section::find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ) { + + // go through sections within radius of interest, and pick the nearest candidate + TTraction + *tractionfound, + *tractionnearest { nullptr }; + float + distancefound, + distancenearest { std::numeric_limits::max() }; + int + endpointfound, + endpointnearest { -1 }; + + auto const radius { 0.0 }; // { EU07_CELLSIZE * 0.5 }; // experimentally limited, check if it has any negative effect + auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + radius, 2 ) }; + + for( auto &cell : m_cells ) { + // we reject early cells which aren't within our area of interest + if( glm::length2( cell.area().center - Point ) > squaredradii ) { + continue; + } + std::tie( tractionfound, endpointfound, distancefound ) = cell.find( Point, Other, Currentdirection ); + if( ( tractionfound != nullptr ) + && ( distancefound < distancenearest ) ) { + + std::tie( tractionnearest, endpointnearest, distancenearest ) = std::tie( tractionfound, endpointfound, distancefound ); + } + } + return { tractionnearest, endpointnearest, distancenearest }; +} + // sets center point of the section void basic_section::center( glm::dvec3 Center ) { @@ -251,6 +489,17 @@ basic_region::~basic_region() { for( auto section : m_sections ) { if( section != nullptr ) { delete section; } } } +// legacy method, updates sounds and polls event launchers around camera +void +basic_region::update() { + // render events and sounds from sectors near enough to the viewer + auto const range = 2750.f; // audible range of 100 db sound + auto const §ionlist = sections( Global::pCameraPosition, range ); + for( auto *section : sectionlist ) { + section->update( Global::pCameraPosition, range ); + } +} + void basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad ) { @@ -327,6 +576,11 @@ basic_region::insert_path( TTrack *Path, scratch_data &Scratchpad ) { // tracks are guaranteed to hava a name so we can skip the check ErrorLog( "Bad scenario: track node \"" + Path->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); } + // also register path ends in appropriate sections, for path merging lookups + // TODO: clean this up during track refactoring + for( auto &point : Path->endpoints() ) { + register_path( Path, point ); + } } // inserts provided track in the region @@ -344,6 +598,11 @@ basic_region::insert_traction( TTraction *Traction, scratch_data &Scratchpad ) { // tracks are guaranteed to hava a name so we can skip the check ErrorLog( "Bad scenario: traction node \"" + Traction->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); } + // also register traction ends in appropriate sections, for path merging lookups + // TODO: clean this up during track refactoring + for( auto &point : Traction->endpoints() ) { + register_traction( Traction, point ); + } } // inserts provided instance of 3d model in the region @@ -363,6 +622,154 @@ basic_region::insert_instance( TAnimModel *Instance, scratch_data &Scratchpad ) } } +// inserts provided sound in the region +void +basic_region::insert_sound( TTextSound *Sound, scratch_data &Scratchpad ) { + +/* + // TODO: implement + // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done + auto center = Sound->location(); + + if( point_inside( center ) ) { + // NOTE: nodes placed outside of region boundaries are discarded + section( center ).insert( Instance ); + } + else { + // tracks are guaranteed to hava a name so we can skip the check + ErrorLog( "Bad scenario: model node \"" + Instance->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); + } +*/ +} + +// find a vehicle located neares to specified location, within specified radius, optionally discarding vehicles without drivers +std::tuple +basic_region::find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) { + + auto const §ionlist = sections( Point, Radius ); + // go through sections within radius of interest, and pick the nearest candidate + TDynamicObject + *foundvehicle, + *nearestvehicle { nullptr }; + float + founddistance, + nearestdistance { std::numeric_limits::max() }; + + for( auto *section : sectionlist ) { + std::tie( foundvehicle, founddistance ) = section->find( Point, Radius, Onlycontrolled ); + if( ( foundvehicle != nullptr ) + && ( founddistance < nearestdistance ) ) { + + std::tie( nearestvehicle, nearestdistance ) = std::tie( foundvehicle, founddistance ); + } + } + return std::tie( nearestvehicle, nearestdistance ); +} + +// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint +std::tuple +basic_region::find_path( glm::dvec3 const &Point, TTrack const *Exclude ) { + + // TBD: throw out of bounds exception instead of checks all over the place..? + if( point_inside( Point ) ) { + + return section( Point ).find( Point, Exclude ); + } + + return std::make_tuple( nullptr, -1 ); +} + +// 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 +basic_region::find_traction( glm::dvec3 const &Point, TTraction const *Exclude ) { + + // TBD: throw out of bounds exception instead of checks all over the place..? + if( point_inside( Point ) ) { + + return section( Point ).find( Point, Exclude ); + } + + return std::make_tuple( nullptr, -1 ); +} + +// 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 +basic_region::find_traction( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ) { + + auto const §ionlist = sections( Point, 0.f ); + // go through sections within radius of interest, and pick the nearest candidate + TTraction + *tractionfound, + *tractionnearest { nullptr }; + float + distancefound, + distancenearest { std::numeric_limits::max() }; + int + endpointfound, + endpointnearest { -1 }; + + for( auto *section : sectionlist ) { + std::tie( tractionfound, endpointfound, distancefound ) = section->find( Point, Other, Currentdirection ); + if( ( tractionfound != nullptr ) + && ( distancefound < distancenearest ) ) { + + std::tie( tractionnearest, endpointnearest, distancenearest ) = std::tie( tractionfound, endpointfound, distancefound ); + } + } + return { tractionnearest, endpointnearest }; +} + +// finds sections inside specified sphere. returns: list of sections +std::vector const & +basic_region::sections( glm::dvec3 const &Point, float const Radius ) { + + m_scratchpad.sections.clear(); + + auto const centerx { static_cast( std::floor( Point.x / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ) }; + auto const centerz { static_cast( std::floor( Point.z / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ) }; + auto const sectioncount { 2 * static_cast( std::ceil( Radius / EU07_SECTIONSIZE ) ) }; + + int const originx = centerx - sectioncount / 2; + int const originz = centerz - sectioncount / 2; + + auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_SECTIONSIZE + 0.25 * EU07_SECTIONSIZE ) + Radius, 2 ) }; + + for( int row = originz; row <= originz + sectioncount; ++row ) { + if( row < 0 ) { continue; } + if( row >= EU07_REGIONSIDESECTIONCOUNT ) { break; } + for( int column = originx; column <= originx + sectioncount; ++column ) { + if( column < 0 ) { continue; } + if( column >= EU07_REGIONSIDESECTIONCOUNT ) { break; } + + auto *section { m_sections[ row * EU07_REGIONSIDESECTIONCOUNT + column ] }; + if( ( section != nullptr ) + && ( glm::length2( section->area().center - Point ) < squaredradii ) ) { + + m_scratchpad.sections.emplace_back( section ); + } + } + } + return m_scratchpad.sections; +} + +// registers specified path in the lookup directory of a cell enclosing specified point +void +basic_region::register_path( TTrack *Path, glm::dvec3 const &Point ) { + + if( point_inside( Point ) ) { + section( Point ).register_end( Path, Point ); + } +} + +// registers specified end point of the provided traction piece in the lookup directory of the region +void +basic_region::register_traction( TTraction *Traction, glm::dvec3 const &Point ) { + + if( point_inside( Point ) ) { + section( Point ).register_end( Traction, Point ); + } +} + // checks whether specified point is within boundaries of the region bool basic_region::point_inside( glm::dvec3 const &Location ) { @@ -566,8 +973,8 @@ basic_region::RaTriangleDivider( shape_node &Shape, std::deque &Shap basic_section & basic_region::section( glm::dvec3 const &Location ) { - auto const column = static_cast( std::floor( Location.x / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ); - auto const row = static_cast( std::floor( Location.z / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ); + auto const column { static_cast( std::floor( Location.x / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ) }; + auto const row { static_cast( std::floor( Location.z / EU07_SECTIONSIZE + EU07_REGIONSIDESECTIONCOUNT / 2 ) ) }; auto §ion = m_sections[ diff --git a/scene.h b/scene.h index 66314e13..1ccd42ba 100644 --- a/scene.h +++ b/scene.h @@ -30,6 +30,18 @@ struct scratch_data { std::stack location_offset; glm::vec3 location_rotation; + + struct trainset_data { + + std::string name; + std::string track; + float offset { 0.f }; + float velocity { 0.f }; + std::vector vehicles; + std::vector couplings; + TDynamicObject * driver { nullptr }; + bool is_open { false }; + } trainset; }; // basic element of rudimentary partitioning scheme for the section. fixed size, no further subdivision @@ -40,6 +52,9 @@ class basic_cell { public: // methods + // legacy method, updates sounds and polls event launchers within radius around specified point + void + update(); // adds provided shape to the cell void insert( shape_node Shape ); @@ -52,18 +67,42 @@ public: // adds provided model instance to the cell void insert( TAnimModel *Instance ); + // 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, optionally ignoring vehicles without drivers. reurns: located vehicle and distance + std::tuple + find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ); + // finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint + std::tuple + 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 + 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 + 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 ); - // sets center point of the cell - void - center( glm::dvec3 Center ); + // provides access to bounding area data + bounding_area const & + area() const { + return m_area; } private: // types using shapenode_sequence = std::vector; using path_sequence = std::vector; + using path_set = std::set; using traction_sequence = std::vector; + using traction_set = std::set; using instance_sequence = std::vector; // members scene::bounding_area m_area { glm::dvec3(), static_cast( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) }; @@ -75,6 +114,11 @@ private: instance_sequence m_instancesopaque; instance_sequence m_instancetranslucent; traction_sequence m_traction; + // search helpers + struct lookup_data { + path_set paths; + traction_set traction; + } m_directories; }; // basic scene partitioning structure, holds terrain geometry and collection of cells @@ -84,6 +128,9 @@ class basic_section { public: // methods + // legacy method, updates sounds and polls event launchers within radius around specified point + void + update( glm::dvec3 const &Location, float const Radius ); // adds provided shape to the section void insert( shape_node Shape ); @@ -96,12 +143,34 @@ public: // adds provided model instance to the section void insert( TAnimModel *Instance ); - // generates renderable version of held non-instanced geometry + // registers specified end point of the provided path in the lookup directory of the region void - create_geometry(); + register_end( TTrack *Path, glm::dvec3 const &Point ); + // registers specified end point of the provided traction piece in the lookup directory of the region + void + register_end( TTraction *Traction, glm::dvec3 const &Point ); + // find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance + std::tuple + find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ); + // finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint + std::tuple + 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 + 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 + 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 @@ -134,6 +203,9 @@ public: // destructor ~basic_region(); // methods + // legacy method, updates sounds and polls event launchers around camera + void + update(); // inserts provided shape in the region void insert_shape( shape_node Shape, scratch_data &Scratchpad ); @@ -146,17 +218,45 @@ public: // 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( TTextSound *Sound, scratch_data &Scratchpad ); + // find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance + std::tuple + find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ); + // finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint + std::tuple + 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 + 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 + find_traction( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ); + // finds sections inside specified sphere. returns: list of sections + std::vector const & + sections( glm::dvec3 const &Point, float const Radius ); private: // types using section_array = std::array; + struct region_scratchpad { + + std::vector 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 ); - // trims provided shape to fit into a section, adds trimmed part at the end of provided list + // 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 &Shapes ); // provides access to section enclosing specified point @@ -165,6 +265,7 @@ private: // members section_array m_sections; + region_scratchpad m_scratchpad; }; diff --git a/scenenode.cpp b/scenenode.cpp index 015bdd92..1be3d643 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -17,7 +17,7 @@ namespace scene { // restores content of the node from provded input stream shape_node & -shape_node::deserialize( cParser &Input, node_data const &Nodedata ) { +shape_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) { // import common data m_name = Nodedata.name; @@ -291,6 +291,5 @@ basic_node::basic_node( scene::node_data const &Nodedata ) : std::numeric_limits::max() ); } - } // editor //--------------------------------------------------------------------------- diff --git a/scenenode.h b/scenenode.h index fed77544..ea4e029f 100644 --- a/scenenode.h +++ b/scenenode.h @@ -72,7 +72,7 @@ class node_manager { struct node_data { double range_min { 0.0 }; - double range_max { 0.0 }; + double range_max { std::numeric_limits::max() }; std::string name; std::string type; }; @@ -86,7 +86,7 @@ public: // types struct shapenode_data { // placement and visibility - bounding_area area; // bounding area, in world coordinates + 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 @@ -103,7 +103,7 @@ public: // methods // restores content of the node from provded input stream shape_node & - deserialize( cParser &Input, node_data const &Nodedata ); + 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( shape_node &Shape ); @@ -229,12 +229,15 @@ public: std::string const & name() const { return m_name; } - glm::dvec3 const & - location() { - return m_location; }; void location( glm::dvec3 const Location ) { m_location = Location; } + glm::dvec3 const & + location() const { + return m_location; }; + void + visible( bool const Visible ) { + m_visible = Visible; } bool visible() const { return m_visible; } diff --git a/simulation.cpp b/simulation.cpp index 9fdc5174..843d23b1 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -12,15 +12,19 @@ http://mozilla.org/MPL/2.0/. #include "globals.h" #include "logs.h" +#include "uilayer.h" namespace simulation { state_manager State; event_manager Events; -memory_manager Memory; +memory_table Memory; path_table Paths; traction_table Traction; -instance_manager Instances; +powergridsource_table Powergrid; +sound_table Sounds; +instance_table Instances; +vehicle_table Vehicles; light_array Lights; scene::basic_region *Region { nullptr }; @@ -44,6 +48,23 @@ state_manager::deserialize( std::string const &Scenariofile ) { 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 ) { @@ -79,7 +100,11 @@ state_manager::deserialize( cParser &Input ) { for( auto &function : functionlist ) { functionmap.emplace( function.first, std::bind( function.second, this, std::ref( Input ), std::ref( importscratchpad ) ) ); } + // deserialize content from the provided input + auto + timelast { std::chrono::steady_clock::now() }, + timenow { timelast }; std::string token { Input.getToken() }; while( false == token.empty() ) { @@ -91,6 +116,14 @@ state_manager::deserialize( cParser &Input ) { 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( timenow - timelast ).count() >= 200 ) { + timelast = timenow; + glfwPollEvents(); + UILayer.set_progress( Input.getProgress(), Input.getFullProgress() ); + GfxRenderer.Render(); + } + token = Input.getToken(); } } @@ -218,6 +251,7 @@ state_manager::deserialize_firstinit( cParser &Input, scene::scratch_data &Scrat simulation::Paths.InitTracks(); simulation::Traction.InitTraction(); simulation::Events.InitEvents(); + simulation::Memory.InitCells(); } void @@ -230,6 +264,8 @@ state_manager::deserialize_light( cParser &Input, scene::scratch_data &Scratchpa 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 ); @@ -240,13 +276,27 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad >> nodedata.type; // type-based deserialization. not elegant but it'll do if( nodedata.type == "dynamic" ) { - // TODO: implement - skip_until( Input, "enddynamic" ); + + 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 const inputline = Input.Line(); // cache in case we need to report error - auto *path { deserialize_path( Input, Scratchpad, nodedata ) }; // duplicates of named tracks are currently experimentally allowed if( simulation::Paths.insert( path ) ) { @@ -262,10 +312,10 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad } else if( nodedata.type == "traction" ) { - auto const inputline = Input.Line(); // cache in case we need to report error - auto *traction { deserialize_traction( Input, Scratchpad, nodedata ) }; - // duplicates of named tracks are currently discarded + // traction loading is optional + if( traction == nullptr ) { return; } + if( simulation::Traction.insert( traction ) ) { simulation::Region->insert_traction( traction, Scratchpad ); } @@ -274,18 +324,29 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad } } else if( nodedata.type == "tractionpowersource" ) { - // TODO: implement - skip_until( Input, "end" ); + + auto *powersource { deserialize_tractionpowersource( Input, Scratchpad, nodedata ) }; + // traction loading is optional + if( powersource == nullptr ) { return; } + + if( simulation::Powergrid.insert( powersource ) ) { +/* + // TODO: implement this + simulation::Region.insert_powersource( powersource, Scratchpad ); +*/ + } + else { + ErrorLog( "Bad scenario: power grid source with duplicate name, \"" + powersource->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } } else if( nodedata.type == "model" ) { if( nodedata.range_min < 0.0 ) { // convert and import 3d terrain + // TODO: implement this } else { // regular instance of 3d mesh - auto const inputline = Input.Line(); // cache in case we need to report error - auto *instance { deserialize_model( Input, Scratchpad, nodedata ) }; // model import can potentially fail if( instance == nullptr ) { return; } @@ -313,10 +374,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad } else if( nodedata.type == "memcell" ) { - auto const inputline = Input.Line(); // cache in case we need to report error - auto *memorycell { deserialize_memorycell( Input, Scratchpad, nodedata ) }; - // duplicates of named tracks are currently discarded if( simulation::Memory.insert( memorycell ) ) { /* // TODO: implement this @@ -325,10 +383,6 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad } else { ErrorLog( "Bad scenario: memory cell with duplicate name, \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); -/* - delete memorycell; - delete memorycellnode; -*/ } } else if( nodedata.type == "eventlauncher" ) { @@ -336,8 +390,14 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad skip_until( Input, "end" ); } else if( nodedata.type == "sound" ) { - // TODO: implement - skip_until( Input, "endsound" ); + + auto *sound { deserialize_sound( Input, Scratchpad, nodedata ) }; + if( simulation::Sounds.insert( sound ) ) { + simulation::Region->insert_sound( sound, Scratchpad ); + } + else { + ErrorLog( "Bad scenario: sound node with duplicate name, \"" + sound->m_name + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } } } @@ -417,14 +477,68 @@ state_manager::deserialize_time( cParser &Input, scene::scratch_data &Scratchpad void state_manager::deserialize_trainset( cParser &Input, scene::scratch_data &Scratchpad ) { - // TODO: implement - skip_until( Input, "endtrainset" ); + 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 " + 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 ) { - // TODO: implement + 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 " + 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 @@ -448,6 +562,10 @@ state_manager::deserialize_path( cParser &Input, scene::scratch_data &Scratchpad 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 = ( @@ -459,6 +577,22 @@ state_manager::deserialize_traction( cParser &Input, scene::scratch_data &Scratc 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 ) { @@ -481,7 +615,6 @@ state_manager::deserialize_model( cParser &Input, scene::scratch_data &Scratchpa >> location.y >> location.z >> rotation.y; - // adjust location auto *instance = new TAnimModel( Nodedata ); instance->RaAnglesSet( Scratchpad.location_rotation + rotation ); // dostosowanie do pochylania linii @@ -495,6 +628,147 @@ state_manager::deserialize_model( cParser &Input, scene::scratch_data &Scratchpa 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() }; + auto const skinfile { Input.getToken() }; + auto const mmdfile { Input.getToken() }; + auto const pathname = ( + Scratchpad.trainset.is_open ? + Scratchpad.trainset.track : + Input.getToken() ); + auto const offset { Input.getToken( false ) }; + auto const drivertype { Input.getToken() }; + auto const couplingparams = ( + Scratchpad.trainset.is_open ? + Input.getToken() : + "3" ); + auto const velocity = ( + Scratchpad.trainset.is_open ? + Scratchpad.trainset.velocity : + Input.getToken( false ) ); + // extract coupling type and optional parameters + auto const couplingparamsplit = couplingparams.find( '.' ); + auto coupling = ( + couplingparamsplit != std::string::npos ? + std::atoi( couplingparams.substr( 0, couplingparamsplit ).c_str() ) : + std::atoi( couplingparams.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 = 0; + } + auto const params = ( + couplingparamsplit != std::string::npos ? + couplingparams.substr( couplingparamsplit + 1 ) : + "" ); + // load amount and type + auto loadcount { Input.getToken( false ) }; + auto loadtype = ( + loadcount ? + Input.getToken() : + "" ); + 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() }; + if( destination != "enddynamic" ) { + // optional vehicle destination parameter + vehicle->asDestination = Input.getToken(); + skip_until( Input, "enddynamic" ); + } + + return vehicle; +} + +TTextSound * +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() }; + auto *sound = new TTextSound( soundname, Nodedata.range_max, location.x, location.y, location.z, false, false, Nodedata.range_min ); + sound->name( Nodedata.name ); + + skip_until( Input, "endsound" ); + + return sound; +} + // skips content of stream until specified token void state_manager::skip_until( cParser &Input, std::string const &Token ) { diff --git a/simulation.h b/simulation.h index 3aa6bbf0..781785d4 100644 --- a/simulation.h +++ b/simulation.h @@ -9,14 +9,18 @@ http://mozilla.org/MPL/2.0/. #pragma once +#include "parser.h" +#include "scene.h" #include "event.h" #include "memcell.h" #include "track.h" #include "traction.h" +#include "tractionpower.h" +#include "realsound.h" #include "animmodel.h" -#include "scene.h" +#include "dynobj.h" +#include "driver.h" #include "lightarray.h" -#include "parser.h" namespace simulation { @@ -26,6 +30,9 @@ 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 ); @@ -51,8 +58,11 @@ private: 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 ); 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 ); + TTextSound * 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 @@ -61,10 +71,13 @@ private: extern state_manager State; extern event_manager Events; -extern memory_manager Memory; +extern memory_table Memory; extern path_table Paths; extern traction_table Traction; -extern instance_manager Instances; +extern powergridsource_table Powergrid; +extern sound_table Sounds; +extern instance_table Instances; +extern vehicle_table Vehicles; extern light_array Lights; extern scene::basic_region *Region; From a12b84f50fbea9c7b6bf6ce865dc322d5288ceec Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Wed, 11 Oct 2017 20:30:17 +0200 Subject: [PATCH 09/31] ai driving logic tweaks, minor bug fixes --- Driver.cpp | 91 +++++++++++++++++++++++++++++++++------------- Driver.h | 1 + McZapkie/MOVER.h | 1 + McZapkie/Mover.cpp | 5 ++- 4 files changed, 71 insertions(+), 27 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 2af71d90..a85b2729 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -73,7 +73,8 @@ 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(); - if( track->eType == tt_Cross ) { + 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 @@ -312,7 +313,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 @@ -526,7 +527,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 @@ -723,7 +724,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) @@ -827,7 +828,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; @@ -3651,6 +3652,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 ? @@ -4461,6 +4470,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)+", @@ -4589,26 +4599,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 ) ); + } } } } @@ -4640,8 +4677,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 ); } @@ -5164,7 +5203,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()); }; //---------------------------------------------------------------------------------------------------------------------- diff --git a/Driver.h b/Driver.h index d1695179..09d7326a 100644 --- a/Driver.h +++ b/Driver.h @@ -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 diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index c53b59f3..da94f8c5 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -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*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index b1c7251c..a1f76501 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -6923,7 +6923,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; } } @@ -7826,10 +7826,13 @@ bool TMoverParameters::CheckLocomotiveParameters(bool ReadyFlag, int Dir) BrakeCtrlPos = static_cast( Handle->GetPos( bh_NP ) ); else BrakeCtrlPos = static_cast( 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; From 09dbb3c639b472f643e00842e84f9830b5232071 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 14 Oct 2017 03:53:13 +0200 Subject: [PATCH 10/31] continued refactoring: traction, events, event launchers, sounds; additional diagnostics timers, minor performance enhancements and bug fixes --- Driver.cpp | 26 +++-- Driver.h | 14 ++- DynObj.cpp | 214 ++++++++++++++++++++++------------- DynObj.h | 3 + EvLaunch.cpp | 40 ++----- EvLaunch.h | 55 +++++---- Event.cpp | 292 +++++++++++++++++++++++++++++------------------- Event.h | 11 ++ Ground.cpp | 32 +----- Ground.h | 6 +- MemCell.cpp | 4 + MemCell.h | 4 + Names.h | 7 ++ Timer.cpp | 5 +- Timer.h | 36 +++++- World.cpp | 20 ++-- renderer.cpp | 190 +++++++++++++++++++++---------- renderer.h | 25 +++-- scene.cpp | 298 +++++++++++++++++++++++++++++++++++-------------- scene.h | 56 +++++++--- scenenode.h | 4 +- simulation.cpp | 61 ++++++++-- simulation.h | 2 + 23 files changed, 921 insertions(+), 484 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index a85b2729..8746c591 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2985,20 +2985,27 @@ 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) +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 +#ifdef EU07_USE_OLD_GROUNDCODE vector3 sl; sl.x = -NewLocation.X; // zamiana na współrzędne scenerii sl.z = NewLocation.Y; sl.y = NewLocation.Z; +#else + // zamiana na współrzędne scenerii + glm::dvec3 sl { -NewLocation.X, NewLocation.Y, NewLocation.Z }; +#endif 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) +#ifdef EU07_USE_OLD_GROUNDCODE +bool TController::PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const vector3 *NewLocation, TStopReason reason) +#else +bool TController::PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *NewLocation, TStopReason reason ) +#endif { // analiza komendy if (NewCommand == "CabSignal") { // SHP wyzwalane jest przez człon z obsadą, ale obsługiwane przez silnikowy @@ -4229,13 +4236,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; @@ -5306,7 +5312,11 @@ TTrack * TController::BackwardTraceRoute(double &fDistance, double &fDirection, } // sprawdzanie zdarzeń semaforów i ograniczeń szlakowych +#ifdef EU07_USE_OLD_GROUNDCODE void TController::SetProximityVelocity(double dist, double vel, const vector3 *pos) +#else +void TController::SetProximityVelocity( double dist, double vel, glm::dvec3 const *pos ) +#endif { // Ra:przeslanie do AI prędkości /* //!!!! zastąpić prawidłową reakcją AI na SetProximityVelocity !!!! @@ -5318,7 +5328,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 diff --git a/Driver.h b/Driver.h index 09d7326a..d669b498 100644 --- a/Driver.h +++ b/Driver.h @@ -313,10 +313,12 @@ 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); +#ifdef EU07_USE_OLD_GROUNDCODE + bool PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const vector3 *NewLocation, TStopReason reason = stopComm); +#else + bool PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *NewLocation, TStopReason reason = stopComm ); +#endif void UpdateSituation(double dt); // uruchamiac przynajmniej raz na sekundę // procedury dotyczace rozkazow dla maszynisty // uaktualnia informacje o prędkości @@ -376,7 +378,11 @@ private: bool BackwardTrackBusy(TTrack *Track); TEvent *CheckTrackEventBackward(double fDirection, TTrack *Track); TTrack *BackwardTraceRoute(double &fDistance, double &fDirection, TTrack *Track, TEvent *&Event); +#ifdef EU07_USE_OLD_GROUNDCODE void SetProximityVelocity(double dist, double vel, const vector3 *pos); +#else + void SetProximityVelocity( double dist, double vel, glm::dvec3 const *pos ); +#endif TCommandType BackwardScan(); public: diff --git a/DynObj.cpp b/DynObj.cpp index cbf34062..beb068b2 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2613,77 +2613,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; @@ -3199,22 +3158,22 @@ bool TDynamicObject::Update(double dt, double dt1) else if ( ( true == MoverParameters->PantRearUp ) && ( PantDiff < 0.01 ) ) { - if ((MoverParameters->PantRearVolt == 0.0) && - (MoverParameters->PantFrontVolt == 0.0)) - sPantUp.Play(vol, 0, MechInside, vPosition); - 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( ( MoverParameters->PantRearVolt == 0.0 ) + && ( MoverParameters->PantFrontVolt == 0.0 ) ) { + sPantUp.Play( vol, 0, MechInside, vPosition ); + } + 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 > ( @@ -3281,8 +3240,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 @@ -4438,10 +4397,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; } } } @@ -4932,7 +4889,12 @@ void TDynamicObject::RadioStop() if( ( MoverParameters->SecuritySystem.RadioStop ) && ( MoverParameters->Radio ) ) { // jeśli pojazd ma RadioStop i jest on aktywny +#ifdef EU07_USE_OLD_GROUNDCODE Mechanik->PutCommand( "Emergency_brake", 1.0, 1.0, &vPosition, stopRadio ); +#else + // HAX cast until math types unification + Mechanik->PutCommand( "Emergency_brake", 1.0, 1.0, &static_cast(vPosition), stopRadio ); +#endif // add onscreen notification for human driver // TODO: do it selectively for the 'local' driver once the multiplayer is in if( false == Mechanik->AIControllFlag ) { @@ -5404,10 +5366,8 @@ vehicle_table::update( double Deltatime, int Iterationcount ) { for( auto *vehicle : m_items ) { // Ra: zmienić warunek na sprawdzanie pantografów w jednej zmiennej: czy pantografy i czy podniesione if( vehicle->MoverParameters->EnginePowerSource.SourceType == CurrentCollector ) { -/* // TODO: re-implement - GetTraction( vehicle ); -*/ + update_traction( vehicle ); } vehicle->MoverParameters->ComputeConstans(); vehicle->CoupleDist(); @@ -5447,3 +5407,97 @@ vehicle_table::update( double Deltatime, int Iterationcount ) { } */ } + +// 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; + } + } +} diff --git a/DynObj.h b/DynObj.h index df0794e9..8670ea20 100644 --- a/DynObj.h +++ b/DynObj.h @@ -492,6 +492,9 @@ 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 ); }; //--------------------------------------------------------------------------- diff --git a/EvLaunch.cpp b/EvLaunch.cpp index 4fda3368..349a29db 100644 --- a/EvLaunch.cpp +++ b/EvLaunch.cpp @@ -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,7 +123,7 @@ bool TEventLauncher::Load(cParser *parser) return true; }; -bool TEventLauncher::Render() +bool TEventLauncher::check_conditions() { //"renderowanie" wyzwalacza bool bCond = false; if (iKey != 0) @@ -199,13 +177,13 @@ 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 }; + //--------------------------------------------------------------------------- diff --git a/EvLaunch.h b/EvLaunch.h index 42ce2c73..1dbeae97 100644 --- a/EvLaunch.h +++ b/EvLaunch.h @@ -10,32 +10,43 @@ http://mozilla.org/MPL/2.0/. #pragma once #include -#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 }; + +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 }; //--------------------------------------------------------------------------- diff --git a/Event.cpp b/Event.cpp index 389b92ca..398b7699 100644 --- a/Event.cpp +++ b/Event.cpp @@ -727,6 +727,32 @@ event_manager::~event_manager() { } } +// adds specified event launcher to the list of global launchers +void +event_manager::queue( TEventLauncher *Launcher ) { + + m_launcherqueue.emplace_back( Launcher ); +} + +// legacy method, updates event queues +void +event_manager::update() { + + // process currently queued events + CheckQuery(); + // test list of global events for possible new additions to the queue + for( auto *launcher : m_launcherqueue ) { + + if( true == launcher->check_conditions() ) { + // NOTE: we're presuming global events aren't going to use event2 + WriteLog( "Eventlauncher " + launcher->name() ); + if( launcher->Event1 ) { + Global::AddToQuery( launcher->Event1, nullptr ); + } + } + } +} + // adds provided event to the collection. returns: true on success // TODO: return handle instead of pointer bool @@ -826,7 +852,11 @@ event_manager::AddToQuery( TEvent *Event, TDynamicObject *Owner ) { for( auto dynamic : Event->Params[ 6 ].asTrack->Dynamics ) { Event->Params[ 5 ].asMemCell->PutCommand( dynamic->Mechanik, +#ifdef EU07_USE_OLD_GROUNDCODE &Event->Params[ 4 ].nGroundNode->pCenter ); +#else + Event->Params[ 4 ].asLocation ); +#endif } //if (DebugModeFlag) WriteLog( @@ -924,7 +954,11 @@ event_manager::CheckQuery() { for( auto dynamic : m_workevent->Params[ 6 ].asTrack->Dynamics ) { m_workevent->Params[ 5 ].asMemCell->PutCommand( dynamic->Mechanik, +#ifdef EU07_USE_OLD_GROUNDCODE &m_workevent->Params[ 4 ].nGroundNode->pCenter ); +#else + m_workevent->Params[ 4 ].asLocation ); +#endif } //if (DebugModeFlag) WriteLog("Type: UpdateValues & Track command - [" + @@ -950,7 +984,11 @@ event_manager::CheckQuery() { */ m_workevent->Params[ 9 ].asMemCell->PutCommand( m_workevent->Activator->Mechanik, +#ifdef EU07_USE_OLD_GROUNDCODE &m_workevent->Params[ 8 ].nGroundNode->pCenter ); +#else + m_workevent->Params[ 8 ].asLocation ); +#endif } WriteLog( "Type: GetValues" ); break; @@ -1273,263 +1311,263 @@ void event_manager::InitEvents() { //łączenie eventów z pozostałymi obiektami - for( auto *Current : m_events ) { + for( auto *event : m_events ) { - switch (Current->Type) { + switch( event->Type ) { case tp_AddValues: // sumowanie wartości case tp_UpdateValues: { // zmiana wartości - auto *cell = simulation::Memory.find( Current->asNodeName ); // nazwa komórki powiązanej z eventem + auto *cell = simulation::Memory.find( event->asNodeName ); // nazwa komórki powiązanej z eventem if( cell != nullptr ) { // McZapkie-100302 - if( Current->iFlags & ( conditional_trackoccupied | conditional_trackfree ) ) { + if( event->iFlags & ( conditional_trackoccupied | conditional_trackfree ) ) { // jeśli chodzi o zajetosc toru (tor może być inny, niż wpisany w komórce) // nazwa toru ta sama, co nazwa komórki - Current->Params[ 9 ].asTrack = simulation::Paths.find( Current->asNodeName ); - if( Current->Params[ 9 ].asTrack == nullptr ) { - ErrorLog( "Bad event: track \"" + Current->asNodeName + "\" referenced in event \"" + Current->asName + "\" doesn't exist" ); + event->Params[ 9 ].asTrack = simulation::Paths.find( event->asNodeName ); + if( event->Params[ 9 ].asTrack == nullptr ) { + ErrorLog( "Bad event: track \"" + event->asNodeName + "\" referenced in event \"" + event->asName + "\" doesn't exist" ); } } - Current->Params[ 4 ].asLocation = &( cell->location() ); - Current->Params[ 5 ].asMemCell = cell; // komórka do aktualizacji - if( Current->iFlags & ( conditional_memcompare ) ) { + event->Params[ 4 ].asLocation = &( cell->location() ); + event->Params[ 5 ].asMemCell = cell; // komórka do aktualizacji + if( event->iFlags & ( conditional_memcompare ) ) { // komórka do badania warunku - Current->Params[ 9 ].asMemCell = cell; + event->Params[ 9 ].asMemCell = cell; } if( false == cell->asTrackName.empty() ) { // tor powiązany z komórką powiązaną z eventem // tu potrzebujemy wskaźnik do komórki w (tmp) - Current->Params[ 6 ].asTrack = simulation::Paths.find( cell->asTrackName ); - if( Current->Params[ 6 ].asTrack == nullptr ) { + event->Params[ 6 ].asTrack = simulation::Paths.find( cell->asTrackName ); + if( event->Params[ 6 ].asTrack == nullptr ) { ErrorLog( "Bad memcell: track \"" + cell->asTrackName + "\" referenced in memcell \"" + cell->name() + "\" doesn't exist" ); } } else { - Current->Params[ 6 ].asTrack = nullptr; + event->Params[ 6 ].asTrack = nullptr; } } else { // nie ma komórki, to nie będzie działał poprawnie - Current->m_ignored = true; // deaktywacja - ErrorLog( "Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\"" ); + event->m_ignored = true; // deaktywacja + ErrorLog( "Bad event: event \"" + event->asName + "\" cannot find memcell \"" + event->asNodeName + "\"" ); } break; } case tp_LogValues: { // skojarzenie z memcell - if( Current->asNodeName.empty() ) { // brak skojarzenia daje logowanie wszystkich - Current->Params[ 9 ].asMemCell = nullptr; + if( event->asNodeName.empty() ) { // brak skojarzenia daje logowanie wszystkich + event->Params[ 9 ].asMemCell = nullptr; break; } } case tp_GetValues: case tp_WhoIs: { - auto *cell = simulation::Memory.find( Current->asNodeName ); + auto *cell = simulation::Memory.find( event->asNodeName ); if( cell != nullptr ) { - Current->Params[ 8 ].asLocation = &( cell->location() ); - Current->Params[ 9 ].asMemCell = cell; - if( ( Current->Type == tp_GetValues ) + event->Params[ 8 ].asLocation = &( cell->location() ); + event->Params[ 9 ].asMemCell = cell; + if( ( event->Type == tp_GetValues ) && ( cell->IsVelocity() ) ) { // jeśli odczyt komórki a komórka zawiera komendę SetVelocity albo ShuntVelocity // to event nie będzie dodawany do kolejki - Current->bEnabled = false; + event->bEnabled = false; } } else { // nie ma komórki, to nie będzie działał poprawnie - Current->m_ignored = true; // deaktywacja - ErrorLog( "Bad event: event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\"" ); + event->m_ignored = true; // deaktywacja + ErrorLog( "Bad event: event \"" + event->asName + "\" cannot find memcell \"" + event->asNodeName + "\"" ); } break; } case tp_CopyValues: { // skopiowanie komórki do innej - auto *cell = simulation::Memory.find( Current->asNodeName ); // komórka docelowa + auto *cell = simulation::Memory.find( event->asNodeName ); // komórka docelowa if( cell != nullptr ) { - Current->Params[ 4 ].asLocation = &( cell->location() ); - Current->Params[ 5 ].asMemCell = cell; // komórka docelowa + event->Params[ 4 ].asLocation = &( cell->location() ); + event->Params[ 5 ].asMemCell = cell; // komórka docelowa if( false == cell->asTrackName.empty() ) { // tor powiązany z komórką powiązaną z eventem // tu potrzebujemy wskaźnik do komórki w (tmp) - Current->Params[ 6 ].asTrack = simulation::Paths.find( cell->asTrackName ); - if( Current->Params[ 6 ].asTrack == nullptr ) { + event->Params[ 6 ].asTrack = simulation::Paths.find( cell->asTrackName ); + if( event->Params[ 6 ].asTrack == nullptr ) { ErrorLog( "Bad memcell: track \"" + cell->asTrackName + "\" referenced in memcell \"" + cell->name() + "\" doesn't exists" ); } } else { - Current->Params[ 6 ].asTrack = nullptr; + event->Params[ 6 ].asTrack = nullptr; } } else { - ErrorLog( "Bad event: copyvalues event \"" + Current->asName + "\" cannot find memcell \"" + Current->asNodeName + "\"" ); + ErrorLog( "Bad event: copyvalues event \"" + event->asName + "\" cannot find memcell \"" + event->asNodeName + "\"" ); } - std::string const cellastext { Current->Params[ 9 ].asText }; - cell = simulation::Memory.find( Current->Params[ 9 ].asText ); // komórka źódłowa - SafeDeleteArray( Current->Params[ 9 ].asText ); // usunięcie nazwy komórki + std::string const cellastext { event->Params[ 9 ].asText }; + cell = simulation::Memory.find( event->Params[ 9 ].asText ); // komórka źódłowa + SafeDeleteArray( event->Params[ 9 ].asText ); // usunięcie nazwy komórki if( cell != nullptr ) { - Current->Params[ 8 ].asLocation = &( cell->location() ); - Current->Params[ 9 ].asMemCell = cell; // komórka źródłowa + event->Params[ 8 ].asLocation = &( cell->location() ); + event->Params[ 9 ].asMemCell = cell; // komórka źródłowa } else { - ErrorLog( "Bad event: copyvalues event \"" + Current->asName + "\" cannot find memcell \"" + cellastext + "\"" ); + ErrorLog( "Bad event: copyvalues event \"" + event->asName + "\" cannot find memcell \"" + cellastext + "\"" ); } break; } case tp_Animation: { // animacja modelu // retrieve target name parameter - std::string const cellastext = Current->Params[ 9 ].asText; - SafeDeleteArray( Current->Params[ 9 ].asText ); + std::string const cellastext = event->Params[ 9 ].asText; + SafeDeleteArray( event->Params[ 9 ].asText ); // egzemplarz modelu do animowania - auto *instance = simulation::Instances.find( Current->asNodeName ); + auto *instance = simulation::Instances.find( event->asNodeName ); if( instance != nullptr ) { - if( Current->Params[ 0 ].asInt == 4 ) { + if( event->Params[ 0 ].asInt == 4 ) { // model dla całomodelowych animacji - Current->Params[ 9 ].asModel = instance; + event->Params[ 9 ].asModel = instance; } else { // standardowo przypisanie submodelu - Current->Params[ 9 ].asAnimContainer = instance->GetContainer( cellastext ); // submodel - if( Current->Params[ 9 ].asAnimContainer ) { - Current->Params[ 9 ].asAnimContainer->WillBeAnimated(); // oflagowanie animacji - if( Current->Params[ 9 ].asAnimContainer->Event() == nullptr ) { + event->Params[ 9 ].asAnimContainer = instance->GetContainer( cellastext ); // submodel + if( event->Params[ 9 ].asAnimContainer ) { + event->Params[ 9 ].asAnimContainer->WillBeAnimated(); // oflagowanie animacji + if( event->Params[ 9 ].asAnimContainer->Event() == nullptr ) { // nie szukać, gdy znaleziony - Current->Params[ 9 ].asAnimContainer->EventAssign( - FindEvent( Current->asNodeName + "." + cellastext + ":done" ) ); + event->Params[ 9 ].asAnimContainer->EventAssign( + FindEvent( event->asNodeName + "." + cellastext + ":done" ) ); } } } } else { - ErrorLog( "Bad event: animation event \"" + Current->asName + "\" cannot find model instance \"" + Current->asNodeName + "\"" ); + ErrorLog( "Bad event: animation event \"" + event->asName + "\" cannot find model instance \"" + event->asNodeName + "\"" ); } - Current->asNodeName = ""; + event->asNodeName = ""; break; } case tp_Lights: { // zmiana świeteł modelu - auto *instance = simulation::Instances.find( Current->asNodeName ); + auto *instance = simulation::Instances.find( event->asNodeName ); if( instance != nullptr ) - Current->Params[ 9 ].asModel = instance; + event->Params[ 9 ].asModel = instance; else - ErrorLog( "Bad event: lights event \"" + Current->asName + "\" cannot find model instance \"" + Current->asNodeName + "\"" ); - Current->asNodeName = ""; + ErrorLog( "Bad event: lights event \"" + event->asName + "\" cannot find model instance \"" + event->asNodeName + "\"" ); + event->asNodeName = ""; break; } case tp_Visible: { // ukrycie albo przywrócenie obiektu - editor::basic_node *node = simulation::Instances.find( Current->asNodeName ); // najpierw model + editor::basic_node *node = simulation::Instances.find( event->asNodeName ); // najpierw model if( node == nullptr ) { // albo tory? - node = simulation::Paths.find( Current->asNodeName ); + node = simulation::Paths.find( event->asNodeName ); } if( node == nullptr ) { // może druty? - node = simulation::Traction.find( Current->asNodeName ); + node = simulation::Traction.find( event->asNodeName ); } if( node != nullptr ) - Current->Params[ 9 ].asEditorNode = node; + event->Params[ 9 ].asEditorNode = node; else - ErrorLog( "Bad event: visibility event \"" + Current->asName + "\" cannot find item \"" + Current->asNodeName + "\"" ); - Current->asNodeName = ""; + ErrorLog( "Bad event: visibility event \"" + event->asName + "\" cannot find item \"" + event->asNodeName + "\"" ); + event->asNodeName = ""; break; } case tp_Switch: { // przełożenie zwrotnicy albo zmiana stanu obrotnicy - auto *track = simulation::Paths.find( Current->asNodeName ); + auto *track = simulation::Paths.find( event->asNodeName ); if( track != nullptr ) { // dowiązanie toru if( track->iAction == NULL ) { // jeśli nie jest zwrotnicą ani obrotnicą to będzie się zmieniał stan uszkodzenia track->iAction |= 0x100; } - Current->Params[ 9 ].asTrack = track; - if( ( Current->Params[ 0 ].asInt == 0 ) - && ( Current->Params[ 2 ].asdouble >= 0.0 ) ) { + event->Params[ 9 ].asTrack = track; + if( ( event->Params[ 0 ].asInt == 0 ) + && ( event->Params[ 2 ].asdouble >= 0.0 ) ) { // jeśli przełącza do stanu 0 & jeśli jest zdefiniowany dodatkowy ruch iglic // przesłanie parametrów - Current->Params[ 9 ].asTrack->Switch( - Current->Params[ 0 ].asInt, - Current->Params[ 1 ].asdouble, - Current->Params[ 2 ].asdouble ); + event->Params[ 9 ].asTrack->Switch( + event->Params[ 0 ].asInt, + event->Params[ 1 ].asdouble, + event->Params[ 2 ].asdouble ); } } else { - ErrorLog( "Bad event: switch event \"" + Current->asName + "\" cannot find track \"" + Current->asNodeName + "\"" ); + ErrorLog( "Bad event: switch event \"" + event->asName + "\" cannot find track \"" + event->asNodeName + "\"" ); } - Current->asNodeName = ""; + event->asNodeName = ""; break; } case tp_Sound: { // odtworzenie dźwięku - auto *sound = simulation::Sounds.find( Current->asNodeName ); + auto *sound = simulation::Sounds.find( event->asNodeName ); if( sound != nullptr ) - Current->Params[ 9 ].tsTextSound = sound; + event->Params[ 9 ].tsTextSound = sound; else - ErrorLog( "Bad event: sound event \"" + Current->asName + "\" cannot find static sound \"" + Current->asNodeName + "\"" ); - Current->asNodeName = ""; + ErrorLog( "Bad event: sound event \"" + event->asName + "\" cannot find static sound \"" + event->asNodeName + "\"" ); + event->asNodeName = ""; break; } case tp_TrackVel: { // ustawienie prędkości na torze - if( false == Current->asNodeName.empty() ) { - auto *track = simulation::Paths.find( Current->asNodeName ); + if( false == event->asNodeName.empty() ) { + auto *track = simulation::Paths.find( event->asNodeName ); if( track != nullptr ) { // flaga zmiany prędkości toru jest istotna dla skanowania track->iAction |= 0x200; - Current->Params[ 9 ].asTrack = track; + event->Params[ 9 ].asTrack = track; } else { - ErrorLog( "Bad event: track velocity event \"" + Current->asName + "\" cannot find track \"" + Current->asNodeName + "\"" ); + ErrorLog( "Bad event: track velocity event \"" + event->asName + "\" cannot find track \"" + event->asNodeName + "\"" ); } } - Current->asNodeName = ""; + event->asNodeName = ""; break; } case tp_DynVel: { // komunikacja z pojazdem o konkretnej nazwie - if( Current->asNodeName == "activator" ) - Current->Params[ 9 ].asDynamic = nullptr; + if( event->asNodeName == "activator" ) + event->Params[ 9 ].asDynamic = nullptr; else { - auto *vehicle = simulation::Vehicles.find( Current->asNodeName ); + auto *vehicle = simulation::Vehicles.find( event->asNodeName ); if( vehicle != nullptr ) - Current->Params[ 9 ].asDynamic = vehicle; + event->Params[ 9 ].asDynamic = vehicle; else - Error( "Bad event: vehicle velocity event \"" + Current->asName + "\" cannot find vehicle \"" + Current->asNodeName + "\"" ); + ErrorLog( "Bad event: vehicle velocity event \"" + event->asName + "\" cannot find vehicle \"" + event->asNodeName + "\"" ); } - Current->asNodeName = ""; + event->asNodeName = ""; break; } case tp_Multiple: { std::string cellastext; - if( Current->Params[ 9 ].asText != nullptr ) { // przepisanie nazwy do bufora - cellastext = Current->Params[ 9 ].asText; - SafeDeleteArray( Current->Params[ 9 ].asText ); - Current->Params[ 9 ].asPointer = nullptr; // zerowanie wskaźnika, aby wykryć brak obeiktu + if( event->Params[ 9 ].asText != nullptr ) { // przepisanie nazwy do bufora + cellastext = event->Params[ 9 ].asText; + SafeDeleteArray( event->Params[ 9 ].asText ); + event->Params[ 9 ].asPointer = nullptr; // zerowanie wskaźnika, aby wykryć brak obeiktu } - if( Current->iFlags & ( conditional_trackoccupied | conditional_trackfree ) ) { + if( event->iFlags & ( conditional_trackoccupied | conditional_trackfree ) ) { // jeśli chodzi o zajetosc toru - Current->Params[ 9 ].asTrack = simulation::Paths.find( cellastext ); - if( Current->Params[ 9 ].asTrack == nullptr ) { - ErrorLog( "Bad event: multi-event \"" + Current->asName + "\" cannot find track \"" + cellastext + "\"" ); - Current->iFlags &= ~( conditional_trackoccupied | conditional_trackfree ); // zerowanie flag + event->Params[ 9 ].asTrack = simulation::Paths.find( cellastext ); + if( event->Params[ 9 ].asTrack == nullptr ) { + ErrorLog( "Bad event: multi-event \"" + event->asName + "\" cannot find track \"" + cellastext + "\"" ); + event->iFlags &= ~( conditional_trackoccupied | conditional_trackfree ); // zerowanie flag } } - else if( Current->iFlags & ( conditional_memstring | conditional_memval1 | conditional_memval2 ) ) { + else if( event->iFlags & ( conditional_memstring | conditional_memval1 | conditional_memval2 ) ) { // jeśli chodzi o komorke pamieciową - Current->Params[ 9 ].asMemCell = simulation::Memory.find( cellastext ); - if( Current->Params[ 9 ].asMemCell == nullptr ) { - ErrorLog( "Bad event: multi-event \"" + Current->asName + "\" cannot find memory cell \"" + cellastext + "\"" ); - Current->iFlags &= ~( conditional_memstring | conditional_memval1 | conditional_memval2 ); + event->Params[ 9 ].asMemCell = simulation::Memory.find( cellastext ); + if( event->Params[ 9 ].asMemCell == nullptr ) { + ErrorLog( "Bad event: multi-event \"" + event->asName + "\" cannot find memory cell \"" + cellastext + "\"" ); + event->iFlags &= ~( conditional_memstring | conditional_memval1 | conditional_memval2 ); } } for( int i = 0; i < 8; ++i ) { - if( Current->Params[ i ].asText != nullptr ) { - cellastext = Current->Params[ i ].asText; - SafeDeleteArray( Current->Params[ i ].asText ); - Current->Params[ i ].asEvent = FindEvent( cellastext ); - if( Current->Params[ i ].asEvent == nullptr ) { + if( event->Params[ i ].asText != nullptr ) { + cellastext = event->Params[ i ].asText; + SafeDeleteArray( event->Params[ i ].asText ); + event->Params[ i ].asEvent = FindEvent( cellastext ); + if( event->Params[ i ].asEvent == nullptr ) { // Ra: tylko w logu informacja o braku - ErrorLog( "Bad event: multi-event \"" + Current->asName + "\" cannot find event \"" + cellastext + "\"" ); + ErrorLog( "Bad event: multi-event \"" + event->asName + "\" cannot find event \"" + cellastext + "\"" ); } } } @@ -1537,14 +1575,14 @@ event_manager::InitEvents() { } case tp_Voltage: { // zmiana napięcia w zasilaczu (TractionPowerSource) - if( false == Current->asNodeName.empty() ) { - auto *powersource = simulation::Powergrid.find( Current->asNodeName ); // podłączenie zasilacza + if( false == event->asNodeName.empty() ) { + auto *powersource = simulation::Powergrid.find( event->asNodeName ); // podłączenie zasilacza if( powersource != nullptr ) - Current->Params[ 9 ].psPower = powersource; + event->Params[ 9 ].psPower = powersource; else - ErrorLog( "Bad event: voltage event \"" + Current->asName + "\" cannot find power source \"" + Current->asNodeName + "\"" ); + ErrorLog( "Bad event: voltage event \"" + event->asName + "\" cannot find power source \"" + event->asNodeName + "\"" ); } - Current->asNodeName = ""; + event->asNodeName = ""; break; } case tp_Message: { @@ -1554,7 +1592,37 @@ event_manager::InitEvents() { } // switch - if( Current->fDelay < 0 ) { AddToQuery( Current, nullptr ); } + if( event->fDelay < 0 ) { AddToQuery( event, nullptr ); } + } +} + +// legacy method, initializes event launchers after deserialization from scenario file +void +event_manager::InitLaunchers() { + + for( auto *launcher : m_launchers.sequence() ) { + + if( launcher->iCheckMask != 0 ) { + if( launcher->asMemCellName != "none" ) { + // jeśli jest powiązana komórka pamięci + launcher->MemCell = simulation::Memory.find( launcher->asMemCellName ); + if( launcher->MemCell == nullptr ) { + ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" cannot find memcell \"" + launcher->asMemCellName + "\"" ); + } + } + else { + launcher->MemCell = nullptr; + } + } + + launcher->Event1 = ( + launcher->asEvent1Name != "none" ? + simulation::Events.FindEvent( launcher->asEvent1Name ) : + nullptr ); + launcher->Event2 = ( + launcher->asEvent2Name != "none" ? + simulation::Events.FindEvent( launcher->asEvent2Name ) : + nullptr ); } } diff --git a/Event.h b/Event.h index cd0f89cd..ad32a5b0 100644 --- a/Event.h +++ b/Event.h @@ -63,7 +63,9 @@ union TParam { void *asPointer; TMemCell *asMemCell; +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *nGroundNode; +#endif editor::basic_node *asEditorNode; glm::dvec3 const *asLocation; TTrack *asTrack; @@ -125,6 +127,12 @@ 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 @@ -144,6 +152,9 @@ public: // 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 diff --git a/Ground.cpp b/Ground.cpp index ac467df8..4ccef2ef 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -190,7 +190,7 @@ void TGroundNode::RenderHidden() } return; case TP_EVLAUNCH: - if (EvLaunch->Render()) + if (EvLaunch->check_conditions()) if ((EvLaunch->dRadius < 0) || (mgn < EvLaunch->dRadius)) { WriteLog("Eventlauncher " + asName); @@ -1006,7 +1006,6 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } } break; -#endif case TP_EVLAUNCH: parser->getTokens(3); *parser @@ -1018,7 +1017,6 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) tmp->EvLaunch = new TEventLauncher(); tmp->EvLaunch->Load(parser); break; -#ifdef EU07_USE_OLD_GROUNDCODE case TP_TRACK: tmp->pTrack = new TTrack( tmp->asName ); if (Global::iWriteLogEnabled & 4) @@ -1600,7 +1598,6 @@ TEvent * TGround::FindEvent(const std::string &asEventName) lookup->second : nullptr ); } -#endif void TGround::FirstInit() { // ustalanie zależności na scenerii przed wczytaniem pojazdów @@ -1668,18 +1665,14 @@ void TGround::FirstInit() WriteLog("InitNormals OK"); -#ifdef EU07_USE_OLD_GROUNDCODE InitTracks(); //łączenie odcinków ze sobą i przyklejanie eventów WriteLog("InitTracks OK"); InitTraction(); //łączenie drutów ze sobą WriteLog("InitTraction OK"); InitEvents(); WriteLog( "InitEvents OK" ); -#endif - InitLaunchers(); WriteLog("InitLaunchers OK"); - WriteLog("FirstInit is done"); }; @@ -1839,7 +1832,6 @@ bool TGround::Init(std::string File) } else if (str == "event") { -#ifdef EU07_USE_OLD_GROUNDCODE TEvent *tmp = new TEvent(); tmp->Load(&parser, pOrigin); if (tmp->Type == tp_Unknown) @@ -1904,7 +1896,6 @@ bool TGround::Init(std::string File) } } } -#endif } else if (str == "rotate") { @@ -2126,14 +2117,13 @@ bool TGround::Init(std::string File) if (!bInitDone) FirstInit(); // jeśli nie było w scenerii -#ifdef EU07_USE_OLD_TERRAINCODE + if (Global::pTerrainCompact) TerrainWrite(); // Ra: teraz można zapisać teren w jednym pliku -#endif Global::iPause &= ~0x10; // koniec pauzy wczytywania return true; } -#ifdef EU07_USE_OLD_GROUNDCODE + bool TGround::InitEvents() { //łączenie eventów z pozostałymi obiektami TGroundNode *tmp, *trk; @@ -2816,7 +2806,7 @@ void TGround::TrackJoin(TGroundNode *Current) } } } - +#ifdef EU07_USE_OLD_GROUNDCODE // McZapkie-070602: wyzwalacze zdarzen bool TGround::InitLaunchers() { @@ -2837,26 +2827,16 @@ bool TGround::InitLaunchers() } else EventLauncher->MemCell = nullptr; -#ifdef EU07_USE_OLD_GROUNDCODE EventLauncher->Event1 = ( EventLauncher->asEvent1Name != "none") ? FindEvent(EventLauncher->asEvent1Name) : nullptr; EventLauncher->Event2 = ( EventLauncher->asEvent2Name != "none") ? FindEvent(EventLauncher->asEvent2Name) : nullptr; -#else - EventLauncher->Event1 = ( EventLauncher->asEvent1Name != "none") ? - simulation::Events.FindEvent(EventLauncher->asEvent1Name) : - nullptr; - EventLauncher->Event2 = ( EventLauncher->asEvent2Name != "none") ? - simulation::Events.FindEvent(EventLauncher->asEvent2Name) : - nullptr; -#endif } return true; } -#ifdef EU07_USE_OLD_GROUNDCODE TTrack * TGround::FindTrack(vector3 Point, int &iConnection, TGroundNode *Exclude) { // wyszukiwanie innego toru kończącego się w (Point) TTrack *tmp; @@ -3539,7 +3519,7 @@ TGround::Update_Hidden() { } } } -#endif + // Winger 170204 - szukanie trakcji nad pantografami bool TGround::GetTraction(TDynamicObject *model) { // aktualizacja drutu zasilającego dla każdego pantografu, żeby odczytać napięcie @@ -3774,7 +3754,7 @@ bool TGround::GetTraction(TDynamicObject *model) return true; }; - +#endif #ifdef _WINDOWS //--------------------------------------------------------------------------- void TGround::Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam) diff --git a/Ground.h b/Ground.h index 00bf072b..a6ef54c7 100644 --- a/Ground.h +++ b/Ground.h @@ -277,15 +277,13 @@ class TGround TGround(); ~TGround(); void Free(); +#ifdef EU07_USE_OLD_GROUNDCODE bool Init( std::string File ); void FirstInit(); -#ifdef EU07_USE_OLD_GROUNDCODE void InitTracks(); void InitTraction(); bool InitEvents(); -#endif bool InitLaunchers(); -#ifdef EU07_USE_OLD_GROUNDCODE 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); @@ -295,9 +293,7 @@ class TGround void UpdatePhys(double dt, int iter); // aktualizacja fizyki stałym krokiem bool Update(double dt, int iter); // aktualizacja przesunięć zgodna z FPS void Update_Hidden(); // updates invisible elements of the scene -#endif bool GetTraction(TDynamicObject *model); -#ifdef EU07_USE_OLD_GROUNDCODE bool AddToQuery( TEvent *Event, TDynamicObject *Node ); bool CheckQuery(); TGroundNode * DynamicFindAny(std::string const &Name); diff --git a/MemCell.cpp b/MemCell.cpp index 8143b2fa..dcbba49c 100644 --- a/MemCell.cpp +++ b/MemCell.cpp @@ -129,7 +129,11 @@ bool TMemCell::Load(cParser *parser) return true; } +#ifdef EU07_USE_OLD_GROUNDCODE void TMemCell::PutCommand(TController *Mech, vector3 *Loc) +#else +void TMemCell::PutCommand( TController *Mech, glm::dvec3 const *Loc ) +#endif { // wysłanie zawartości komórki do AI if (Mech) Mech->PutCommand(szText, fValue1, fValue2, Loc); diff --git a/MemCell.h b/MemCell.h index aa84d3b9..0364eb89 100644 --- a/MemCell.h +++ b/MemCell.h @@ -37,7 +37,11 @@ class TMemCell : public editor::basic_node { bool Load(cParser *parser); void +#ifdef EU07_USE_OLD_GROUNDCODE PutCommand( TController *Mech, Math3D::vector3 *Loc ); +#else + PutCommand( TController *Mech, glm::dvec3 const *Loc ); +#endif bool Compare( std::string const &szTestText, double const fTestValue1, double const fTestValue2, int const CheckMask ); std::string const & diff --git a/Names.h b/Names.h index 5f76306a..1b338531 100644 --- a/Names.h +++ b/Names.h @@ -108,4 +108,11 @@ protected: // members type_sequence m_items; type_map m_itemmap; + +public: + // data access + typename type_sequence & + sequence() { + return m_items; } + }; diff --git a/Timer.cpp b/Timer.cpp index e78b50a7..dcfb6780 100644 --- a/Timer.cpp +++ b/Timer.cpp @@ -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 }; diff --git a/Timer.h b/Timer.h index b6b58371..d0d9e5c9 100644 --- a/Timer.h +++ b/Timer.h @@ -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::steady_clock::now() - m_start ) ).count() / 1000.f; } + float + average() const { + return m_accumulator / 20.f;} + +private: + std::chrono::time_point 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; + }; //--------------------------------------------------------------------------- diff --git a/World.cpp b/World.cpp index be3f6b32..c02db34d 100644 --- a/World.cpp +++ b/World.cpp @@ -953,6 +953,7 @@ void TWorld::FollowView(bool wycisz) { bool TWorld::Update() { Timer::UpdateTimers(Global::iPause != 0); + Timer::subsystem.sim_total.start(); if( (Global::iPause == false) || (m_init == false) ) { @@ -1026,6 +1027,7 @@ bool TWorld::Update() { // this means at count > 20 simulation and render are going to desync. is that right? // NOTE: experimentally changing this to prevent the desync. // TODO: test what happens if we hit more than 20 * 0.01 sec slices, i.e. less than 5 fps + Timer::subsystem.sim_dynamics.start(); #ifdef EU07_USE_OLD_GROUNDCODE if( true == Global::FullPhysics ) { // mixed calculation mode, steps calculated in ~0.05s chunks @@ -1067,6 +1069,7 @@ bool TWorld::Update() { simulation::State.update( stepdeltatime, updatecount ); } #endif + Timer::subsystem.sim_dynamics.stop(); // secondary fixed step simulation time routines while( m_secondaryupdateaccumulator >= m_secondaryupdaterate ) { @@ -1110,7 +1113,7 @@ bool TWorld::Update() { Ground.CheckQuery(); Ground.Update_Hidden(); #else - simulation::Events.CheckQuery(); + simulation::Events.update(); simulation::Region->update(); #endif @@ -1144,6 +1147,8 @@ bool TWorld::Update() { Update_Camera( dt ); + Timer::subsystem.sim_total.stop(); + GfxRenderer.Update( dt ); ResourceSweep(); @@ -1659,20 +1664,14 @@ TWorld::Update_UI() { "VBO" : "Display Lists" ) + " "; - // dump last opengl error, if any - GLenum glerror = ::glGetError(); - if( glerror != GL_NO_ERROR ) { - std::string glerrorstring( (char *)::gluErrorString( glerror ) ); - win1250_to_ascii( glerrorstring ); - Global::LastGLError = std::to_string( glerror ) + " (" + glerrorstring + ")"; - } if( false == Global::LastGLError.empty() ) { uitextline2 += "Last openGL error: " + Global::LastGLError; } // renderer stats - uitextline3 = GfxRenderer.Info(); + uitextline3 = GfxRenderer.info_times(); + uitextline4 = GfxRenderer.info_stats(); break; } @@ -1683,6 +1682,9 @@ TWorld::Update_UI() { if( Global::iMultiplayer ) { uitextline1 += " (multiplayer mode is active)"; } + uitextline3 = + "vehicles: " + to_string( Timer::subsystem.sim_dynamics.average(), 2 ) + " msec" + + " update total: " + to_string( Timer::subsystem.sim_total.average(), 2 ) + " msec"; break; } diff --git a/renderer.cpp b/renderer.cpp index 3107a28e..e875f081 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -315,23 +315,32 @@ opengl_renderer::Init( GLFWwindow *Window ) { bool opengl_renderer::Render() { - if( m_drawstart != std::chrono::steady_clock::time_point() ) { - m_drawtime = std::max( 20.f, 0.95f * m_drawtime + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - m_drawstart ) ).count() / 1000.f ); - } - m_drawstart = std::chrono::steady_clock::now(); - auto const drawstartcolorpass = m_drawstart; + Timer::subsystem.gfx_total.stop(); + Timer::subsystem.gfx_total.start(); // note: gfx_total is actually frame total, clean this up + Timer::subsystem.gfx_color.start(); m_renderpass.draw_mode = rendermode::none; // force setup anew - m_debuginfo.clear(); - ++m_framestamp; + m_debugtimestext.clear(); + m_debugstats = debug_stats(); Render_pass( rendermode::color ); + Timer::subsystem.gfx_color.stop(); + + Timer::subsystem.gfx_swap.start(); + glfwSwapBuffers( m_window ); + Timer::subsystem.gfx_swap.stop(); m_drawcount = m_cellqueue.size(); - // accumulate last 20 frames worth of render time (cap at 1000 fps to prevent calculations going awry) - m_drawtimecolorpass = std::max( 20.f, 0.95f * m_drawtimecolorpass + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - drawstartcolorpass ) ).count() / 1000.f ); - m_debuginfo += "frame total: " + to_string( m_drawtimecolorpass / 20.f, 2 ) + " msec (" + std::to_string( m_cellqueue.size() ) + " sectors) "; + m_debugtimestext + += "frame: " + to_string( Timer::subsystem.gfx_color.average(), 2 ) + " msec (" + std::to_string( m_cellqueue.size() ) + " sectors) " + += "gpu side: " + to_string( Timer::subsystem.gfx_swap.average(), 2 ) + " msec " + += "(" + to_string( Timer::subsystem.gfx_color.average() + Timer::subsystem.gfx_swap.average(), 2 ) + " msec total)"; + m_debugstatstext = + "drawcalls: " + to_string( m_debugstats.drawcalls ) + + "; dyn: " + to_string( m_debugstats.dynamics ) + " mod: " + to_string( m_debugstats.models ) + " sub: " + to_string( m_debugstats.submodels ) + + "; trk: " + to_string( m_debugstats.paths ) + " shp: " + to_string( m_debugstats.shapes ) + + " trc: " + to_string( m_debugstats.traction ) + " lin: " + to_string( m_debugstats.lines ); - glfwSwapBuffers( m_window ); + ++m_framestamp; return true; // for now always succeed } @@ -464,7 +473,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { if( World.InitPerformed() ) { // setup - auto const shadowdrawstart = std::chrono::steady_clock::now(); + Timer::subsystem.gfx_shadows.start(); ::glBindFramebufferEXT( GL_FRAMEBUFFER, m_shadowframebuffer ); @@ -500,9 +509,8 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glDisable( GL_SCISSOR_TEST ); ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target - - m_drawtimeshadowpass = 0.95f * m_drawtimeshadowpass + std::chrono::duration_cast( ( std::chrono::steady_clock::now() - shadowdrawstart ) ).count() / 1000.f; - m_debuginfo += "shadows: " + to_string( m_drawtimeshadowpass / 20.f, 2 ) + " msec (" + std::to_string( m_cellqueue.size() ) + " sectors) "; + Timer::subsystem.gfx_shadows.stop(); + m_debugtimestext += "shadows: " + to_string( Timer::subsystem.gfx_shadows.average(), 2 ) + " msec (" + std::to_string( m_cellqueue.size() ) + " sectors) "; } break; } @@ -602,7 +610,7 @@ bool opengl_renderer::Render_reflections() { auto const &time = simulation::Time.data(); - auto const timestamp = time.wDay * 60 * 24 + time.wHour * 60 + time.wMinute; + auto const timestamp = time.wDay * 24 * 60 + time.wHour * 60 + time.wMinute; if( ( timestamp - m_environmentupdatetime < 5 ) && ( glm::length( m_renderpass.camera.position() - m_environmentupdatelocation ) < 1000.0 ) ) { // run update every 5+ mins of simulation time, or at least 1km from the last location @@ -1477,12 +1485,6 @@ opengl_renderer::Render( TGroundRect *Groundcell ) { break; } } -#ifdef EU07_USE_OLD_TERRAINCODE - if( Groundcell->nTerrain ) { - - Render( Groundcell->nTerrain ); - } -#endif // add the subcells of the cell to the draw queue switch( m_renderpass.draw_mode ) { @@ -1645,16 +1647,7 @@ opengl_renderer::Render( scene::basic_region *Region ) { Update_Lights( simulation::Lights ); Render( std::begin( m_sectionqueue ), std::end( m_sectionqueue ) ); -/* - // draw queue was filled while rendering content of ground cells. now sort the nodes based on their distance to viewer... - // TODO: move sorting for translucent phase, for opaque geometry render cells in initial order to reduce vbo switching - std::sort( - std::begin( m_cellqueue ), - std::end( m_cellqueue ), - []( distancecell_pair const &Left, distancecell_pair const &Right ) { - return ( Left.first < Right.first ); } ); -*/ - // ...then render the opaque content of the visible cells. + // draw queue is filled while rendering sections Render( std::begin( m_cellqueue ), std::end( m_cellqueue ) ); break; } @@ -1749,7 +1742,7 @@ opengl_renderer::Render( section_sequence::iterator First, section_sequence::ite glEnable( GL_LIGHTING ); #endif // shapes - for( auto const &shape : section->m_shapes ) { Render( shape ); } + for( auto const &shape : section->m_shapes ) { Render( shape, true ); } // post-render cleanup ::glPopMatrix(); } @@ -1852,7 +1845,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator glEnable( GL_LIGHTING ); #endif // opaque non-instanced shapes - for( auto const &shape : cell->m_shapesopaque ) { Render( shape ); } + for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } // tracks // TODO: update after path node refactoring for( auto *path : cell->m_paths ) { Render( path ); } @@ -1871,7 +1864,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator // render // opaque non-instanced shapes - for( auto const &shape : cell->m_shapesopaque ) { Render( shape ); } + for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } // TODO: add other content types // post-render cleanup @@ -1887,7 +1880,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render // opaque non-instanced shapes - for( auto const &shape : cell->m_shapesopaque ) { Render( shape ); } + for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } // tracks // TODO: add path to the node picking list for( auto *path : cell->m_paths ) { Render( path ); } @@ -1922,6 +1915,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator Render( dynamic ); } } + break; } case rendermode::pickscenery: { // opaque parts of instanced models @@ -1944,9 +1938,46 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator } void -opengl_renderer::Render( scene::shape_node const &Shape ) { +opengl_renderer::Render( scene::shape_node const &Shape, bool const Ignorerange ) { +/* + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees + distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + default: { + distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + } + if( ( distancesquared < Node->fSquareMinRadius ) + || ( distancesquared >= Node->fSquareRadius ) ) { + return false; + } +*/ + auto const &data{ Shape.data() }; + + if( false == Ignorerange ) { + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees + distancesquared = SquareMagnitude( ( data.area.center - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + default: { + distancesquared = SquareMagnitude( ( data.area.center - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + } + if( ( distancesquared < data.rangesquared_min ) + || ( distancesquared >= data.rangesquared_max ) ) { + return; + } + } - auto const &data { Shape.data() }; // setup Bind_Material( data.material ); switch( m_renderpass.draw_mode ) { @@ -1971,6 +2002,9 @@ opengl_renderer::Render( scene::shape_node const &Shape ) { } // render m_geometry.draw( data.geometry ); + // debug data + ++m_debugstats.shapes; + ++m_debugstats.drawcalls; } void @@ -2021,19 +2055,7 @@ opengl_renderer::Render( TAnimModel *Instance ) { bool opengl_renderer::Render( TGroundNode *Node ) { -#ifdef EU07_USE_OLD_TERRAINCODE - switch (Node->iType) - { // obiekty renderowane niezależnie od odległości - case TP_SUBMODEL: - ::glPushMatrix(); - auto const originoffset = Node->pCenter - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - TSubModel::fSquareDist = 0; - Render( Node->smTerrain ); - ::glPopMatrix(); - return true; - } -#endif + double distancesquared; switch( m_renderpass.draw_mode ) { case rendermode::shadows: { @@ -2073,6 +2095,9 @@ opengl_renderer::Render( TGroundNode *Node ) { ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render Render( Node->pTrack ); + // debug + ++m_debugstats.paths; + ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); return true; @@ -2155,7 +2180,9 @@ opengl_renderer::Render( TGroundNode *Node ) { } // render m_geometry.draw( Node->Piece->geometry ); - + // debug +// ++m_debugstats.lines; +// ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); @@ -2201,6 +2228,9 @@ opengl_renderer::Render( TGroundNode *Node ) { } // render m_geometry.draw( Node->Piece->geometry ); + // debug + ++m_debugstats.shapes; + ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); @@ -2236,6 +2266,9 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { if( false == Dynamic->renderme ) { return false; } + // debug data + ++m_debugstats.dynamics; + // setup TSubModel::iInstance = ( size_t )this; //żeby nie robić cudzych animacji glm::dvec3 const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); @@ -2437,6 +2470,9 @@ opengl_renderer::Render( TModel3d *Model, material_data const *Material, float c // render Render( Model->Root ); + // debug data + ++m_debugstats.models; + // post-render cleanup return true; @@ -2468,6 +2504,10 @@ opengl_renderer::Render( TSubModel *Submodel ) { && ( TSubModel::fSquareDist >= Submodel->fSquareMinDist ) && ( TSubModel::fSquareDist < Submodel->fSquareMaxDist ) ) { + // debug data + ++m_debugstats.submodels; + ++m_debugstats.drawcalls; + if( Submodel->iFlags & 0xC000 ) { ::glPushMatrix(); if( Submodel->fMatrix ) @@ -2717,6 +2757,9 @@ opengl_renderer::Render( TTrack *Track ) { return; } + ++m_debugstats.paths; + ++m_debugstats.drawcalls; + switch( m_renderpass.draw_mode ) { case rendermode::color: case rendermode::reflections: { @@ -2867,7 +2910,7 @@ opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_seque ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render // NOTE: we can reuse the method used to draw opaque geometry - for( auto const &shape : cell->m_shapestranslucent ) { Render( shape ); } + for( auto const &shape : cell->m_shapestranslucent ) { Render( shape, false ); } // post-render cleanup ::glPopMatrix(); } @@ -3003,6 +3046,9 @@ opengl_renderer::Render_Alpha( TTraction *Traction ) { // render m_geometry.draw( Traction->m_geometry ); + // debug data + ++m_debugstats.traction; + ++m_debugstats.drawcalls; } #endif bool @@ -3056,6 +3102,9 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { // render m_geometry.draw( Node->hvTraction->m_geometry ); + // debug data + ++m_debugstats.traction; + ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); @@ -3116,6 +3165,8 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { // render m_geometry.draw( Node->Piece->geometry ); +// ++m_debugstats.lines; +// ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); @@ -3141,7 +3192,9 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { // render m_geometry.draw( Node->Piece->geometry ); - + // debug data + ++m_debugstats.shapes; + ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); @@ -3285,6 +3338,10 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { && ( TSubModel::fSquareDist >= Submodel->fSquareMinDist ) && ( TSubModel::fSquareDist < Submodel->fSquareMaxDist ) ) { + // debug data + ++m_debugstats.submodels; + ++m_debugstats.drawcalls; + if( Submodel->iFlags & 0xC000 ) { ::glPushMatrix(); if( Submodel->fMatrix ) @@ -3580,10 +3637,10 @@ opengl_renderer::Update( double const Deltatime ) { } m_updateaccumulator = 0.0; - m_framerate = 1000.f / ( m_drawtime / 20.f ); + m_framerate = 1000.f / ( Timer::subsystem.gfx_total.average() ); // adjust draw ranges etc, based on recent performance - auto const framerate = 1000.f / (m_drawtimecolorpass / 20.f); + auto const framerate = 1000.f / Timer::subsystem.gfx_color.average(); float targetfactor; if( framerate > 90.0 ) { targetfactor = 3.0f; } @@ -3620,7 +3677,7 @@ opengl_renderer::Update( double const Deltatime ) { } if( true == DebugModeFlag ) { - m_debuginfo += m_textures.info(); + m_debugtimestext += m_textures.info(); } if( ( true == Global::ControlPicking ) @@ -3639,13 +3696,26 @@ opengl_renderer::Update( double const Deltatime ) { else { m_picksceneryitem = nullptr; } -}; + // dump last opengl error, if any + auto const glerror = ::glGetError(); + if( glerror != GL_NO_ERROR ) { + std::string glerrorstring( ( char * )::gluErrorString( glerror ) ); + win1250_to_ascii( glerrorstring ); + Global::LastGLError = std::to_string( glerror ) + " (" + glerrorstring + ")"; + } +} // debug performance string std::string const & -opengl_renderer::Info() const { +opengl_renderer::info_times() const { - return m_debuginfo; + return m_debugtimestext; +} + +std::string const & +opengl_renderer::info_stats() const { + + return m_debugstatstext; } void diff --git a/renderer.h b/renderer.h index f5e888ad..9879fb93 100644 --- a/renderer.h +++ b/renderer.h @@ -203,7 +203,9 @@ public: 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 }; @@ -227,6 +229,17 @@ private: diffuse }; + 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 }; + }; + #ifdef EU07_USE_OLD_GROUNDCODE using distancesubcell_pair = std::pair< double, TSubRect * >; #else @@ -289,7 +302,7 @@ private: void Render( cell_sequence::iterator First, cell_sequence::iterator Last ); void - Render( scene::shape_node const &Shape ); + Render( scene::shape_node const &Shape, bool const Ignorerange ); void Render( TAnimModel *Instance ); #endif @@ -383,14 +396,12 @@ 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.65f, 0.65f, 0.65f, 1.f }; diff --git a/scene.cpp b/scene.cpp index 2dfb70da..2fa1783d 100644 --- a/scene.cpp +++ b/scene.cpp @@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "scene.h" #include "globals.h" +#include "timer.h" #include "renderer.h" #include "logs.h" @@ -19,12 +20,32 @@ namespace scene { // legacy method, updates sounds and polls event launchers within radius around specified point void basic_cell::update() { -/* - // renderowanie obiektów aktywnych a niewidocznych - for( auto node = subcell->nRenderHidden; node; node = node->nNext3 ) { - node->RenderHidden(); + + // sounds + auto const deltatime = Timer::GetDeltaTime(); + for( auto *sound : m_sounds ) { + + if( ( sound->GetStatus() & DSBSTATUS_PLAYING ) == DSBPLAY_LOOPING ) { + sound->Play( 1, DSBPLAY_LOOPING, true, sound->vSoundPosition ); + sound->AdjFreq( 1.0, deltatime ); + } } -*/ + // event launchers + for( auto *launcher : m_eventlaunchers ) { + if( ( true == launcher->check_conditions() ) + && ( SquareMagnitude( launcher->location() - Global::pCameraPosition ) < launcher->dRadius ) ) { + + WriteLog( "Eventlauncher " + launcher->name() ); + if( ( true == Global::shiftState ) + && ( launcher->Event2 != nullptr ) ) { + simulation::Events.AddToQuery( launcher->Event2, nullptr ); + } + else if( launcher->Event1 ) { + simulation::Events.AddToQuery( launcher->Event1, nullptr ); + } + } + } + // TBD, TODO: move to sound renderer for( auto *path : m_paths ) { // dźwięki pojazdów, również niewidocznych @@ -32,6 +53,83 @@ basic_cell::update() { } } +// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle +void +basic_cell::update_traction( TDynamicObject *Vehicle, int const Pantographindex ) { + // Winger 170204 - szukanie trakcji nad pantografami + 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 + + auto pantograph = Vehicle->pants[ Pantographindex ].fParamPants; + auto const pantographposition = position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x ); + + for( auto *traction : m_traction ) { + + // współczynniki równania parametrycznego + auto const paramfrontdot = glm::dot( traction->vParametric, vFront ); + auto const fRaParam = + -( glm::dot( traction->pPoint1, vFront ) - glm::dot( pantographposition, vFront ) ) + / ( paramfrontdot != 0.0 ? + paramfrontdot : + 0.001 ); // div0 trap + + if( ( fRaParam < -0.001 ) + || ( fRaParam > 1.001 ) ) { continue; } + // jeśli tylko 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 = traction->pPoint1 + fRaParam * traction->vParametric; + // wektor musi się mieścić w przedziale ruchu pantografu + auto const vGdzie = vStyk - pantographposition; + auto fVertical = glm::dot( vGdzie, vUp ); + if( fVertical >= 0.0 ) { + // jeśli ponad pantografem (bo może łapać druty spod wiaduktu) + auto const fHorizontal = std::abs( glm::dot( vGdzie, vLeft ) ) - pantograph->fWidth; + + if( ( Global::bEnableTraction ) + && ( fVertical < pantograph->PantWys - 0.15 ) ) { + // jeśli drut jest niżej niż 15cm pod ślizgiem przełączamy w tryb połamania, o ile jedzie; + // (bEnableTraction) aby dało się jeździć na koślawych sceneriach + // i do tego jeszcze wejdzie pod ślizg + if( fHorizontal <= 0.0 ) { + // 0.635 dla AKP-1 AKP-4E + pantograph->PantWys = -1.0; // ujemna liczba oznacza połamanie + pantograph->hvPowerWire = nullptr; // bo inaczej się zasila w nieskończoność z połamanego + if( Vehicle->MoverParameters->EnginePowerSource.CollectorParameters.CollectorsNo > 0 ) { + // liczba pantografów teraz będzie mniejsza + --Vehicle->MoverParameters->EnginePowerSource.CollectorParameters.CollectorsNo; + } + if( DebugModeFlag ) { + ErrorLog( "Bad traction: " + Vehicle->name() + " broke pantograph at " + to_string( pantographposition ) ); + } + } + } + else if( fVertical < pantograph->PantTraction ) { + // ale niżej, niż poprzednio znaleziony + if( fHorizontal <= 0.0 ) { + // 0.635 dla AKP-1 AKP-4E + // to się musi mieścić w przedziale zaleznym od szerokości pantografu + pantograph->hvPowerWire = traction; // jakiś znaleziony + pantograph->PantTraction = fVertical; // zapamiętanie nowej wysokości + } + else if( fHorizontal < pantograph->fWidthExtra ) { + // czy zmieścił się w zakresie nabieżnika? problem jest, gdy nowy drut jest wyżej, + // wtedy pantograf odłącza się od starego, a na podniesienie do nowego potrzebuje czasu + // korekta wysokości o nabieżnik - drut nad nabieżnikiem jest geometrycznie jakby nieco wyżej + fVertical += 0.15 * fHorizontal / pantograph->fWidthExtra; + if( fVertical < pantograph->PantTraction ) { + // gdy po korekcie jest niżej, niż poprzednio znaleziony + // gdyby to wystarczyło, to możemy go uznać + pantograph->hvPowerWire = traction; // może być + pantograph->PantTraction = fVertical; // na razie liniowo na nabieżniku, dokładność poprawi się później + } + } + } + } + } +} + // adds provided shape to the cell void basic_cell::insert( shape_node Shape ) { @@ -107,18 +205,48 @@ basic_cell::insert( TAnimModel *Instance ) { } } +// adds provided sound instance to the cell +void +basic_cell::insert( TTextSound *Sound ) { + + m_active = true; + + m_sounds.emplace_back( Sound ); +} + +// adds provided sound instance to the cell +void +basic_cell::insert( TEventLauncher *Launcher ) { + + m_active = true; + + m_eventlaunchers.emplace_back( Launcher ); +} + // registers provided path in the lookup directory of the cell void basic_cell::register_end( TTrack *Path ) { - m_directories.paths.emplace( Path ); + m_directories.paths.emplace_back( Path ); + // eliminate potential duplicates + m_directories.paths.erase( + std::unique( + std::begin( m_directories.paths ), + std::end( m_directories.paths ) ), + std::end( m_directories.paths ) ); } // registers provided traction piece in the lookup directory of the cell void basic_cell::register_end( TTraction *Traction ) { - m_directories.traction.emplace( Traction ); + m_directories.traction.emplace_back( Traction ); + // eliminate potential duplicates + m_directories.traction.erase( + std::unique( + std::begin( m_directories.traction ), + std::end( m_directories.traction ) ), + std::end( m_directories.traction ) ); } // find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance @@ -263,6 +391,29 @@ basic_section::update( glm::dvec3 const &Location, float const Radius ) { } } +// legacy method, finds and assigns traction piece(s) to pantographs of provided vehicle +void +basic_section::update_traction( TDynamicObject *Vehicle, int const Pantographindex ) { + + 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 + + auto pantograph = Vehicle->pants[ Pantographindex ].fParamPants; + auto const pantographposition = position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x ); + + auto const radius { 0.0 }; // { EU07_CELLSIZE * 0.5 }; // experimentally limited, check if it has any negative effect + auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + radius, 2 ) }; + + for( auto &cell : m_cells ) { + // we reject early cells which aren't within our area of interest + if( glm::length2( cell.area().center - pantographposition ) < squaredradii ) { + cell.update_traction( Vehicle, Pantographindex ); + } + } +} + // adds provided shape to the section void basic_section::insert( shape_node Shape ) { @@ -289,47 +440,6 @@ basic_section::insert( shape_node Shape ) { } } -// adds provided path to the section -void -basic_section::insert( TTrack *Path ) { - - // pass the node to the appropriate partitioning cell - // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - cell( Path->location() ).insert( Path ); -} - -// adds provided path to the section -void -basic_section::insert( TTraction *Traction ) { - - // pass the node to the appropriate partitioning cell - // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - cell( Traction->location() ).insert( Traction ); -} - -// adds provided model instance to the section -void -basic_section::insert( TAnimModel *Instance ) { - - // pass the node to the appropriate partitioning cell - // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - cell( Instance->location() ).insert( Instance ); -} - -// registers specified end point of the provided path in the lookup directory of the region -void -basic_section::register_end( TTrack *Path, glm::dvec3 const &Point ) { - - cell( Point ).register_end( Path ); -} - -// registers specified end point of the provided traction piece in the lookup directory of the region -void -basic_section::register_end( TTraction *Traction, glm::dvec3 const &Point ) { - - cell( Point ).register_end( Traction ); -} - // find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance std::tuple basic_section::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) { @@ -467,26 +577,11 @@ basic_section::cell( glm::dvec3 const &Location ) { basic_region::basic_region() { m_sections.fill( nullptr ); -/* - // initialize centers of sections: - // calculate center of 'top left' region section... - auto const centeroffset = -( EU07_REGIONSIDESECTIONCOUNT / 2 * EU07_SECTIONSIZE ) + EU07_SECTIONSIZE / 2; - glm::dvec3 regioncornercenter { centeroffset, 0, centeroffset }; - auto row { 0 }, column { 0 }; - // ...move through section array assigning centers left to right, front/top to back/bottom - for( auto §ion : m_sections ) { - section.center( regioncornercenter + glm::dvec3{ column * EU07_SECTIONSIZE, 0.0, row * EU07_SECTIONSIZE } ); - if( ++column >= EU07_REGIONSIDESECTIONCOUNT ) { - ++row; - column = 0; - } - } -*/ } basic_region::~basic_region() { - for( auto section : m_sections ) { if( section != nullptr ) { delete section; } } + for( auto *section : m_sections ) { if( section != nullptr ) { delete section; } } } // legacy method, updates sounds and polls event launchers around camera @@ -500,6 +595,25 @@ basic_region::update() { } } +// legacy method, finds and assigns traction piece(s) to pantographs of provided vehicle +void +basic_region::update_traction( TDynamicObject *Vehicle, int const Pantographindex ) { + // TODO: convert vectors to transformation matrix and pass them down the chain along with calculated position + 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 + + auto p = Vehicle->pants[ Pantographindex ].fParamPants; + auto const pant0 = position + ( vLeft * p->vPos.z ) + ( vUp * p->vPos.y ) + ( vFront * p->vPos.x ); + p->PantTraction = std::numeric_limits::max(); // taka za duża wartość + + auto const §ionlist = sections( pant0, 0.0 ); + for( auto *section : sectionlist ) { + section->update_traction( Vehicle, Pantographindex ); + } +} + void basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad ) { @@ -566,15 +680,15 @@ void basic_region::insert_path( TTrack *Path, scratch_data &Scratchpad ) { // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto center = Path->location(); + auto location = Path->location(); - if( point_inside( center ) ) { + if( point_inside( location ) ) { // NOTE: nodes placed outside of region boundaries are discarded - section( center ).insert( Path ); + section( location ).insert( Path ); } else { // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: track node \"" + Path->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); + ErrorLog( "Bad scenario: track node \"" + Path->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); } // also register path ends in appropriate sections, for path merging lookups // TODO: clean this up during track refactoring @@ -588,15 +702,15 @@ void basic_region::insert_traction( TTraction *Traction, scratch_data &Scratchpad ) { // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto center = Traction->location(); + auto location = Traction->location(); - if( point_inside( center ) ) { + if( point_inside( location ) ) { // NOTE: nodes placed outside of region boundaries are discarded - section( center ).insert( Traction ); + section( location ).insert( Traction ); } else { // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: traction node \"" + Traction->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); + ErrorLog( "Bad scenario: traction node \"" + Traction->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); } // also register traction ends in appropriate sections, for path merging lookups // TODO: clean this up during track refactoring @@ -610,15 +724,15 @@ void basic_region::insert_instance( TAnimModel *Instance, scratch_data &Scratchpad ) { // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto center = Instance->location(); + auto location = Instance->location(); - if( point_inside( center ) ) { + if( point_inside( location ) ) { // NOTE: nodes placed outside of region boundaries are discarded - section( center ).insert( Instance ); + section( location ).insert( Instance ); } else { // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: model node \"" + Instance->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); + ErrorLog( "Bad scenario: model node \"" + Instance->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); } } @@ -626,20 +740,34 @@ basic_region::insert_instance( TAnimModel *Instance, scratch_data &Scratchpad ) void basic_region::insert_sound( TTextSound *Sound, scratch_data &Scratchpad ) { -/* - // TODO: implement // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto center = Sound->location(); + auto location = Sound->location(); - if( point_inside( center ) ) { + if( point_inside( location ) ) { // NOTE: nodes placed outside of region boundaries are discarded - section( center ).insert( Instance ); + section( location ).insert( Sound ); } else { // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: model node \"" + Instance->name() + "\" placed in location outside region bounds (" + to_string( center ) + ")" ); + ErrorLog( "Bad scenario: sound node \"" + Sound->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); + } +} + +// inserts provided event launcher in the region +void +basic_region::insert_launcher( TEventLauncher *Launcher, scratch_data &Scratchpad ) { + + // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done + auto location = Launcher->location(); + + if( point_inside( location ) ) { + // NOTE: nodes placed outside of region boundaries are discarded + section( location ).insert( Launcher ); + } + else { + // tracks are guaranteed to hava a name so we can skip the check + ErrorLog( "Bad scenario: event launcher \"" + Launcher->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); } -*/ } // find a vehicle located neares to specified location, within specified radius, optionally discarding vehicles without drivers @@ -757,7 +885,7 @@ void basic_region::register_path( TTrack *Path, glm::dvec3 const &Point ) { if( point_inside( Point ) ) { - section( Point ).register_end( Path, Point ); + section( Point ).register_node( Path, Point ); } } @@ -766,7 +894,7 @@ void basic_region::register_traction( TTraction *Traction, glm::dvec3 const &Point ) { if( point_inside( Point ) ) { - section( Point ).register_end( Traction, Point ); + section( Point ).register_node( Traction, Point ); } } diff --git a/scene.h b/scene.h index 1ccd42ba..05cfa1d3 100644 --- a/scene.h +++ b/scene.h @@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/. #include #include #include +#include #include "parser.h" #include "openglgeometrybank.h" @@ -24,7 +25,7 @@ namespace scene { int const EU07_CELLSIZE = 250; int const EU07_SECTIONSIZE = 1000; -int const EU07_REGIONSIDESECTIONCOUNT = 500; // number of 1km sections along a side of square region +int const EU07_REGIONSIDESECTIONCOUNT = 500; // number of sections along a side of square region struct scratch_data { @@ -42,6 +43,8 @@ struct scratch_data { 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 @@ -55,6 +58,9 @@ public: // 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 ); // adds provided shape to the cell void insert( shape_node Shape ); @@ -67,6 +73,12 @@ public: // adds provided model instance to the cell void insert( TAnimModel *Instance ); + // adds provided sound instance to the cell + void + insert( TTextSound *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 ); @@ -100,10 +112,12 @@ private: // types using shapenode_sequence = std::vector; using path_sequence = std::vector; - using path_set = std::set; +// using path_set = std::unordered_set; using traction_sequence = std::vector; - using traction_set = std::set; +// using traction_set = std::unordered_set; using instance_sequence = std::vector; + using sound_sequence = std::vector; + using eventlauncher_sequence = std::vector; // members scene::bounding_area m_area { glm::dvec3(), static_cast( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) }; bool m_active { false }; // whether the cell holds any actual data @@ -114,10 +128,12 @@ private: 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_set paths; - traction_set traction; + path_sequence paths; + traction_sequence traction; } m_directories; }; @@ -131,24 +147,22 @@ public: // 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 ); // adds provided shape to the section void insert( shape_node Shape ); - // adds provided path to the section + // adds provided node to the section + template void - insert( TTrack *Path ); - // adds provided path to the section + insert( Type_ *Node ) { + cell( Node->location() ).insert( Node ); } + // registers provided node in the lookup directory of the section enclosing specified point + template void - insert( TTraction *Traction ); - // adds provided model instance to the section - void - insert( TAnimModel *Instance ); - // registers specified end point of the provided path in the lookup directory of the region - void - register_end( TTrack *Path, glm::dvec3 const &Point ); - // registers specified end point of the provided traction piece in the lookup directory of the region - void - register_end( TTraction *Traction, glm::dvec3 const &Point ); + register_node( Type_ *Node, glm::dvec3 const &Point ) { + cell( Point ).register_end( Node ); } // find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance std::tuple find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ); @@ -206,6 +220,9 @@ public: // 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 ); // inserts provided shape in the region void insert_shape( shape_node Shape, scratch_data &Scratchpad ); @@ -221,6 +238,9 @@ public: // inserts provided sound in the region void insert_sound( TTextSound *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, optionally ignoring vehicles without drivers. reurns: located vehicle and distance std::tuple find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ); diff --git a/scenenode.h b/scenenode.h index ea4e029f..99e44f23 100644 --- a/scenenode.h +++ b/scenenode.h @@ -26,8 +26,8 @@ struct lighting_data { inline bool operator==( lighting_data const &Left, lighting_data const &Right ) { - return ( ( Left.diffuse == Right.diffuse ) - && ( Left.ambient == Right.ambient ) + return ( ( Left.diffuse == Right.diffuse ) + && ( Left.ambient == Right.ambient ) && ( Left.specular == Right.specular ) ); } inline diff --git a/simulation.cpp b/simulation.cpp index 843d23b1..0d0415d6 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -43,8 +43,8 @@ state_manager::deserialize( std::string const &Scenariofile ) { if( false == scenarioparser.ok() ) { return false; } deserialize( scenarioparser ); - // TODO: initialize links between loaded nodes + Global::iPause &= ~0x10; // koniec pauzy wczytywania return true; } @@ -126,6 +126,11 @@ state_manager::deserialize( cParser &Input ) { token = Input.getToken(); } + + if( false == importscratchpad.initialized ) { + // manually perform scenario initialization + deserialize_firstinit( Input, importscratchpad ); + } } void @@ -247,11 +252,15 @@ state_manager::deserialize_event( cParser &Input, scene::scratch_data &Scratchpa void state_manager::deserialize_firstinit( cParser &Input, scene::scratch_data &Scratchpad ) { - // TODO: implement + if( true == Scratchpad.initialized ) { return; } + simulation::Paths.InitTracks(); simulation::Traction.InitTraction(); simulation::Events.InitEvents(); + simulation::Events.InitLaunchers(); simulation::Memory.InitCells(); + + Scratchpad.initialized = true; } void @@ -283,7 +292,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad 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 ) + ")" ); + 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 @@ -303,7 +312,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad simulation::Region->insert_path( path, Scratchpad ); } else { - ErrorLog( "Bad scenario: track with duplicate name, \"" + path->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + ErrorLog( "Bad scenario: track with duplicate name \"" + path->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); /* delete path; delete pathnode; @@ -320,7 +329,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad simulation::Region->insert_traction( traction, Scratchpad ); } else { - ErrorLog( "Bad scenario: traction piece with duplicate name, \"" + traction->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + ErrorLog( "Bad scenario: traction piece with duplicate name \"" + traction->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } } else if( nodedata.type == "tractionpowersource" ) { @@ -336,7 +345,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad */ } else { - ErrorLog( "Bad scenario: power grid source with duplicate name, \"" + powersource->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + ErrorLog( "Bad scenario: power grid source with duplicate name \"" + powersource->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } } else if( nodedata.type == "model" ) { @@ -355,7 +364,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad simulation::Region->insert_instance( instance, Scratchpad ); } else { - ErrorLog( "Bad scenario: 3d model instance with duplicate name, \"" + instance->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + ErrorLog( "Bad scenario: 3d model instance with duplicate name \"" + instance->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } } } @@ -382,12 +391,25 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad */ } else { - ErrorLog( "Bad scenario: memory cell with duplicate name, \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + ErrorLog( "Bad scenario: memory cell with duplicate name \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } } else if( nodedata.type == "eventlauncher" ) { - // TODO: implement - skip_until( Input, "end" ); + + auto *eventlauncher{ deserialize_eventlauncher( Input, Scratchpad, nodedata ) }; + if( simulation::Events.insert( eventlauncher ) ) { + // 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 { + ErrorLog( "Bad scenario: event launcher with duplicate name \"" + eventlauncher->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + } } else if( nodedata.type == "sound" ) { @@ -396,7 +418,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad simulation::Region->insert_sound( sound, Scratchpad ); } else { - ErrorLog( "Bad scenario: sound node with duplicate name, \"" + sound->m_name + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + ErrorLog( "Bad scenario: sound node with duplicate name \"" + sound->m_name + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } } @@ -604,6 +626,23 @@ state_manager::deserialize_memorycell( cParser &Input, scene::scratch_data &Scra 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 ) { diff --git a/simulation.h b/simulation.h index 781785d4..14740ecd 100644 --- a/simulation.h +++ b/simulation.h @@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/. #include "scene.h" #include "event.h" #include "memcell.h" +#include "evlaunch.h" #include "track.h" #include "traction.h" #include "tractionpower.h" @@ -60,6 +61,7 @@ private: 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 ); TTextSound * deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); From 630b9ecf1bac0efb25f86a79ac6346b35f3d7eac Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sun, 15 Oct 2017 01:06:40 +0200 Subject: [PATCH 11/31] track rendering optimization, application messaging routines moved to separate namespace --- Ground.cpp | 237 +----------- Ground.h | 48 +-- Names.h | 2 + Track.cpp | 16 +- Track.h | 1 + World.cpp | 83 +++-- World.h | 4 +- maszyna.vcxproj.filters | 6 + messaging.cpp | 245 ++++++++++++ messaging.h | 50 +++ renderer.cpp | 809 ++++++++++++++++++++++------------------ renderer.h | 10 +- scene.cpp | 4 + simulation.cpp | 16 +- windows.cpp | 2 +- 15 files changed, 860 insertions(+), 673 deletions(-) create mode 100644 messaging.cpp create mode 100644 messaging.h diff --git a/Ground.cpp b/Ground.cpp index 4ccef2ef..b5f14519 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -17,6 +17,7 @@ http://mozilla.org/MPL/2.0/. #include "Ground.h" #include "Globals.h" +#include "messaging.h" #include "Logs.h" #include "usefull.h" #include "Timer.h" @@ -610,10 +611,10 @@ TGround::DynamicList(bool all) // powtarzać!) for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext) if (all || Current->DynamicObject->Mechanik) - WyslijString(Current->asName, 6); // same nazwy pojazdów - WyslijString("none", 6); // informacja o końcu listy + multiplayer::WyslijString(Current->asName, 6); // same nazwy pojazdów + multiplayer::WyslijString("none", 6); // informacja o końcu listy }; - +#ifdef EU07_USE_OLD_GROUNDCODE // wyszukiwanie obiektu o podanej nazwie i konkretnym typie TGroundNode * TGround::FindGroundNode(std::string const &asNameToFind, TGroundNodeType const iNodeType) { @@ -632,7 +633,7 @@ TGround::FindGroundNode(std::string const &asNameToFind, TGroundNodeType const i } return nullptr; } - +#endif TGroundRect * TGround::GetRect( double x, double z ) { @@ -3154,7 +3155,7 @@ bool TGround::CheckQuery() // loc.Z= tmpEvent->Params[8].nGroundNode->pCenter.y; if (Global::iMultiplayer) // potwierdzenie wykonania dla serwera (odczyt // semafora już tak nie działa) - WyslijEvent(tmpEvent->asName, tmpEvent->Activator->name()); + multiplayer::WyslijEvent(tmpEvent->asName, tmpEvent->Activator->name()); // tmpEvent->Params[9].asMemCell->PutCommand(tmpEvent->Activator->Mechanik,loc); tmpEvent->Params[9].asMemCell->PutCommand( tmpEvent->Activator->Mechanik, &tmpEvent->Params[8].nGroundNode->pCenter); @@ -3244,7 +3245,7 @@ bool TGround::CheckQuery() tmpEvent->Params[1].asdouble, tmpEvent->Params[2].asdouble); if (Global::iMultiplayer) // dajemy znać do serwera o przełożeniu - WyslijEvent(tmpEvent->asName, ""); // wysłanie nazwy eventu przełączajacego + multiplayer::WyslijEvent(tmpEvent->asName, ""); // wysłanie nazwy eventu przełączajacego // Ra: bardziej by się przydała nazwa toru, ale nie ma do niej stąd dostępu break; case tp_TrackVel: @@ -3287,9 +3288,9 @@ bool TGround::CheckQuery() 0) // jednoznaczne tylko, gdy nie było else { if (tmpEvent->Activator) - WyslijEvent(tmpEvent->asName, tmpEvent->Activator->name()); + multiplayer::WyslijEvent(tmpEvent->asName, tmpEvent->Activator->name()); else - WyslijEvent(tmpEvent->asName, ""); + multiplayer::WyslijEvent(tmpEvent->asName, ""); } } } @@ -3755,214 +3756,6 @@ bool TGround::GetTraction(TDynamicObject *model) return true; }; #endif -#ifdef _WINDOWS -//--------------------------------------------------------------------------- -void TGround::Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam) -{ // 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); -}; -//-------------------------------- -void TGround::WyslijEvent(const std::string &e, const std::string &d) -{ // Ra: jeszcze do wyczyszczenia - 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" ); -}; -//--------------------------------------------------------------------------- -void TGround::WyslijUszkodzenia(const std::string &t, char fl) -{ // wysłanie informacji w postaci pojedynczego tekstu - 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"); -}; -//--------------------------------------------------------------------------- -void TGround::WyslijString(const std::string &t, int n) -{ // wysłanie informacji w postaci pojedynczego tekstu - 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"); -}; -//--------------------------------------------------------------------------- -void TGround::WyslijWolny(const std::string &t) -{ // Ra: jeszcze do wyczyszczenia - WyslijString(t, 4); // tor wolny -}; -//-------------------------------- -void TGround::WyslijNamiary(TGroundNode *t) -{ // wysłanie informacji o pojeździe - (float), długość ramki będzie zwiększana w miarę potrzeby - // 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 = t->asName.length(); - r.iPar[0] = i; // ilość danych liczbowych - r.fPar[1] = Global::fTimeAngleDeg / 360.0; // aktualny czas (1.0=doba) - r.fPar[2] = t->DynamicObject->MoverParameters->Loc.X; // pozycja X - r.fPar[3] = t->DynamicObject->MoverParameters->Loc.Y; // pozycja Y - r.fPar[4] = t->DynamicObject->MoverParameters->Loc.Z; // pozycja Z - r.fPar[5] = t->DynamicObject->MoverParameters->V; // prędkość ruchu X - r.fPar[6] = t->DynamicObject->MoverParameters->nrot * M_PI * - t->DynamicObject->MoverParameters->WheelDiameter; // prędkość obrotowa kóŁ - r.fPar[7] = 0; // prędkość ruchu Z - r.fPar[8] = t->DynamicObject->MoverParameters->AccS; // przyspieszenie X - r.fPar[9] = t->DynamicObject->MoverParameters->AccN; // przyspieszenie Y //na razie nie - r.fPar[10] = t->DynamicObject->MoverParameters->AccV; // przyspieszenie Z - r.fPar[11] = t->DynamicObject->MoverParameters->DistCounter; // przejechana odległość w km - r.fPar[12] = t->DynamicObject->MoverParameters->PipePress; // ciśnienie w PG - r.fPar[13] = t->DynamicObject->MoverParameters->ScndPipePress; // ciśnienie w PZ - r.fPar[14] = t->DynamicObject->MoverParameters->BrakePress; // ciśnienie w CH - r.fPar[15] = t->DynamicObject->MoverParameters->Compressor; // ciśnienie w ZG - r.fPar[16] = t->DynamicObject->MoverParameters->Itot; // Prąd całkowity - r.iPar[17] = t->DynamicObject->MoverParameters->MainCtrlPos; // Pozycja NJ - r.iPar[18] = t->DynamicObject->MoverParameters->ScndCtrlPos; // Pozycja NB - r.iPar[19] = t->DynamicObject->MoverParameters->MainCtrlActualPos; // Pozycja jezdna - r.iPar[20] = t->DynamicObject->MoverParameters->ScndCtrlActualPos; // Pozycja bocznikowania - r.iPar[21] = t->DynamicObject->MoverParameters->ScndCtrlActualPos; // Pozycja bocznikowania - r.iPar[22] = t->DynamicObject->MoverParameters->ResistorsFlag * 1 + - t->DynamicObject->MoverParameters->ConverterFlag * 2 + - +t->DynamicObject->MoverParameters->CompressorFlag * 4 + - t->DynamicObject->MoverParameters->Mains * 8 + - +t->DynamicObject->MoverParameters->DoorLeftOpened * 16 + - t->DynamicObject->MoverParameters->DoorRightOpened * 32 + - +t->DynamicObject->MoverParameters->FuseFlag * 64 + - t->DynamicObject->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 < t->DynamicObject->iAnimType[ANIM_PANTS]) - { - r.fPar[23 + p] = t->DynamicObject->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] = - t->DynamicObject->MoverParameters->ShowCurrent(p + 1); // amperomierze kolejnych grup - // WriteLog("zapisalem prady"); - r.iPar[30] = t->DynamicObject->MoverParameters->WarningSignal; // trabienie - r.fPar[31] = t->DynamicObject->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, t->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) + " " + t->asName + " sent"); -}; -// -void TGround::WyslijObsadzone() -{ // wysłanie informacji o pojeździe - 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; - - int i = 0; - for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext) - if (Current->DynamicObject->Mechanik) - { - strcpy(r.cString + 64 * i, Current->DynamicObject->asName.c_str()); - r.fPar[16 * i + 4] = Current->DynamicObject->GetPosition().x; - r.fPar[16 * i + 5] = Current->DynamicObject->GetPosition().y; - r.fPar[16 * i + 6] = Current->DynamicObject->GetPosition().z; - r.iPar[16 * i + 7] = Current->DynamicObject->Mechanik->GetAction(); - strcpy(r.cString + 64 * i + 32, Current->DynamicObject->GetTrack()->IsolatedName().c_str()); - strcpy(r.cString + 64 * i + 48, Current->DynamicObject->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"); -} - -//-------------------------------- -void TGround::WyslijParam(int nr, int fl) -{ // wysłanie parametrów symulacji w ramce (nr) z flagami (fl) - 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 - //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- @@ -4165,7 +3958,7 @@ void TGround::TrackBusyList() for (Current = nRootOfType[TP_TRACK]; Current; Current = Current->nNext) if (!Current->asName.empty()) // musi być nazwa if( false == Current->pTrack->Dynamics.empty() ) - WyslijString(Current->asName, 8); // zajęty + multiplayer::WyslijString(Current->asName, 8); // zajęty }; //--------------------------------------------------------------------------- @@ -4174,10 +3967,10 @@ void TGround::IsolatedBusyList() TIsolated *Current; for (Current = TIsolated::Root(); Current; Current = Current->Next()) if (Current->Busy()) // sprawdź zajętość - WyslijString(Current->asName, 11); // zajęty + multiplayer::WyslijString(Current->asName, 11); // zajęty else - WyslijString(Current->asName, 10); // wolny - WyslijString("none", 10); // informacja o końcu listy + multiplayer::WyslijString(Current->asName, 10); // wolny + multiplayer::WyslijString("none", 10); // informacja o końcu listy }; //--------------------------------------------------------------------------- @@ -4189,10 +3982,10 @@ void TGround::IsolatedBusy(const std::string t) if (Current->asName == t) // wyszukiwanie odcinka o nazwie (t) if (Current->Busy()) // sprawdź zajetość { - WyslijString(Current->asName, 11); // zajęty + multiplayer::WyslijString(Current->asName, 11); // zajęty return; // nie sprawdzaj dalszych } - WyslijString(t, 10); // wolny + multiplayer::WyslijString(t, 10); // wolny }; //--------------------------------------------------------------------------- diff --git a/Ground.h b/Ground.h index a6ef54c7..3452cdbd 100644 --- a/Ground.h +++ b/Ground.h @@ -42,30 +42,6 @@ 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; @@ -259,9 +235,8 @@ class TGround *tmpEvent = nullptr; typedef std::unordered_map event_map; event_map m_eventmap; -#endif TNames m_nodemap; - +#endif vector3 pOrigin; vector3 aRotate; bool bInitDone = false; @@ -300,7 +275,9 @@ class TGround TGroundNode * DynamicFind(std::string const &Name); #endif void DynamicList(bool all = false); +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode * FindGroundNode(std::string const &asNameToFind, TGroundNodeType const iNodeType); +#endif TGroundRect * GetRect( double x, double z ); TSubRect * GetSubRect( int iCol, int iRow ); inline @@ -321,32 +298,25 @@ class TGround #endif void TrackJoin(TGroundNode *Current); - private: +private: // convert tp_terrain model to a series of triangle nodes void convert_terrain( TGroundNode const *Terrain ); void convert_terrain( TSubModel const *Submodel ); #ifdef EU07_USE_OLD_GROUNDCODE void RaTriangleDivider(TGroundNode *node); -#endif void Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam); +#endif +public: + void TrackBusyList(); + void IsolatedBusyList(); + void IsolatedBusy( const std::string t ); - 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); }; diff --git a/Names.h b/Names.h index 1b338531..e28028fd 100644 --- a/Names.h +++ b/Names.h @@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/. #include #include +#ifdef EU07_USE_OLD_GROUNDCODE template class TNames { @@ -65,6 +66,7 @@ private: // members: typemap_map m_maps; // list of object maps of types specified so far }; +#endif template class basic_table { diff --git a/Track.cpp b/Track.cpp index 04884ce7..dccdb40b 100644 --- a/Track.cpp +++ b/Track.cpp @@ -118,7 +118,7 @@ void TIsolated::Modify(int i, TDynamicObject *o) if (evFree) Global::AddToQuery(evFree, o); // dodanie zwolnienia do kolejki if (Global::iMultiplayer) // jeśli multiplayer - Global::pGround->WyslijString(asName, 10); // wysłanie pakietu o zwolnieniu + multiplayer::WyslijString(asName, 10); // wysłanie pakietu o zwolnieniu if (pMemCell) // w powiązanej komórce pMemCell->UpdateValues( "", 0, int( pMemCell->Value2() ) & ~0xFF, update_memval2 ); //"zerujemy" ostatnią wartość @@ -132,7 +132,7 @@ void TIsolated::Modify(int i, TDynamicObject *o) if (evBusy) Global::AddToQuery(evBusy, o); // dodanie zajętości do kolejki if (Global::iMultiplayer) // jeśli multiplayer - Global::pGround->WyslijString(asName, 11); // wysłanie pakietu o zajęciu + multiplayer::WyslijString(asName, 11); // wysłanie pakietu o zajęciu if (pMemCell) // w powiązanej komórce pMemCell->UpdateValues( "", 0, int( pMemCell->Value2() ) | 1, update_memval2 ); // zmieniamy ostatnią wartość na nieparzystą } @@ -178,6 +178,13 @@ void TTrack::Init() } } +bool +TTrack::sort_by_material( TTrack const *Left, TTrack const *Right ) { + + return ( ( Left->m_material1 < Right->m_material1 ) + && ( Left->m_material2 < Right->m_material2 ) ); +} + TTrack * TTrack::Create400m(int what, double dx) { // tworzenie toru do wstawiania taboru podczas konwersji na E3D TGroundNode *tmp = new TGroundNode(TP_TRACK); // node @@ -932,7 +939,7 @@ bool TTrack::AddDynamicObject(TDynamicObject *Dynamic) // pierwszy zajmujący if( m_name != "none" ) { // przekazanie informacji o zajętości toru - Global::pGround->WyslijString( m_name, 8 ); + multiplayer::WyslijString( m_name, 8 ); } } } @@ -1024,7 +1031,7 @@ bool TTrack::RemoveDynamicObject(TDynamicObject *Dynamic) // jeśli już nie ma żadnego if( m_name != "none" ) { // przekazanie informacji o zwolnieniu toru - Global::pGround->WyslijString( m_name, 9 ); + multiplayer::WyslijString( m_name, 9 ); } } } @@ -2158,7 +2165,6 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { void TTrack::EnvironmentSet() { // ustawienie zmienionego światła - glColor3f(1.0f, 1.0f, 1.0f); // Ra: potrzebne to? switch( eEnvironment ) { case e_canyon: { Global::DayLight.apply_intensity( 0.4f ); diff --git a/Track.h b/Track.h index 0aaac064..7ff04d3f 100644 --- a/Track.h +++ b/Track.h @@ -186,6 +186,7 @@ public: virtual ~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() { diff --git a/World.cpp b/World.cpp index c02db34d..3f127841 100644 --- a/World.cpp +++ b/World.cpp @@ -23,7 +23,6 @@ http://mozilla.org/MPL/2.0/. #include "Timer.h" #include "mtable.h" #include "Sound.h" -#include "Camera.h" #include "ResourceManager.h" #include "Event.h" #include "Train.h" @@ -1079,7 +1078,7 @@ bool TWorld::Update() { // awaria PoKeys mogła włączyć pauzę - przekazać informację if( Global::iMultiplayer ) // dajemy znać do serwera o wykonaniu if( iPause != Global::iPause ) { // przesłanie informacji o pauzie do programu nadzorującego - Ground.WyslijParam( 5, 3 ); // ramka 5 z czasem i stanem zapauzowania + multiplayer::WyslijParam( 5, 3 ); // ramka 5 z czasem i stanem zapauzowania iPause = Global::iPause; } @@ -1890,25 +1889,24 @@ TWorld::Update_UI() { } //--------------------------------------------------------------------------- -void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) +void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) { // odebranie komunikatu z serwera if (pRozkaz->iSygn == MAKE_ID4('E','U','0','7') ) switch (pRozkaz->iComm) { case 0: // odesłanie identyfikatora wersji CommLog( Now() + " " + std::to_string(pRozkaz->iComm) + " version" + " rcvd"); - Ground.WyslijString(Global::asVersion, 0); // przedsatwienie się + multiplayer::WyslijString(Global::asVersion, 0); // przedsatwienie się break; case 1: // odesłanie identyfikatora wersji CommLog( Now() + " " + std::to_string(pRozkaz->iComm) + " scenery" + " rcvd"); - Ground.WyslijString(Global::SceneryFile, 1); // nazwa scenerii + multiplayer::WyslijString(Global::SceneryFile, 1); // nazwa scenerii break; case 2: { // event CommLog( Now() + " " + std::to_string( pRozkaz->iComm ) + " " + std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) + " rcvd" ); -/* - // TODO: re-enable when messaging module is in place +#ifdef EU07_USE_OLD_GROUNDCODE if( Global::iMultiplayer ) { // WriteLog("Komunikat: "+AnsiString(pRozkaz->Name1)); TEvent *e = Ground.FindEvent( @@ -1918,7 +1916,20 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) ( e->evJoined != 0 ) ) // tylko jawne albo niejawne Multiple Ground.AddToQuery( e, NULL ); // drugi parametr to dynamic wywołujący - tu brak } -*/ +#else + if( Global::iMultiplayer ) { + // WriteLog("Komunikat: "+AnsiString(pRozkaz->Name1)); + auto *event = simulation::Events.FindEvent( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); + if( event != nullptr ) { + if( ( event->Type == tp_Multiple ) + || ( event->Type == tp_Lights ) + || ( event->evJoined != 0 ) ) { + // tylko jawne albo niejawne Multiple + simulation::Events.AddToQuery( event, nullptr ); // drugi parametr to dynamic wywołujący - tu brak + } + } + } +#endif break; } case 3: // rozkaz dla AI @@ -1961,17 +1972,23 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) { CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd"); - TGroundNode *t = Ground.FindGroundNode( - std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])), TP_TRACK); +#ifdef EU07_USE_OLD_GROUNDCODE + TGroundNode *t = Ground.FindGroundNode( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ), TP_TRACK ); if (t) if (t->pTrack->IsEmpty()) - Ground.WyslijWolny(t->asName); + multiplayer::WyslijWolny(t->asName); +#else + auto *track = simulation::Paths.find( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); + if( ( track != nullptr ) + && ( track->IsEmpty() ) ) { + multiplayer::WyslijWolny( track->name() ); + } +#endif } break; case 5: // ustawienie parametrów { - CommLog(Now() + " " + to_string(pRozkaz->iComm) + " params " + - to_string(*pRozkaz->iPar) + " rcvd"); + CommLog(Now() + " " + to_string(pRozkaz->iComm) + " params " + to_string(*pRozkaz->iPar) + " rcvd"); if (*pRozkaz->iPar == 0) // sprawdzenie czasu if (*pRozkaz->iPar & 1) // ustawienie czasu { @@ -1990,13 +2007,15 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) } break; case 6: // pobranie parametrów ruchu pojazdu - if (Global::iMultiplayer) - { // Ra 2014-12: to ma działać również dla pojazdów bez obsady - CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + - std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + - " rcvd"); - if (pRozkaz->cString[0]) // jeśli długość nazwy jest niezerowa - { // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #7 + if (Global::iMultiplayer) { + // Ra 2014-12: to ma działać również dla pojazdów bez obsady + CommLog( + Now() + " " + + to_string( pRozkaz->iComm ) + " " + + std::string{ pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) } + + " rcvd" ); + if (pRozkaz->cString[0]) { + // jeśli długość nazwy jest niezerowa szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #7 #ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *t; if (pRozkaz->cString[1] == '*') @@ -2006,14 +2025,22 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) t = Ground.DynamicFindAny(std::string( pRozkaz->cString + 1, (unsigned)pRozkaz->cString[0])); // nazwa pojazdu if (t) - Ground.WyslijNamiary(t); // wysłanie informacji o pojeździe + multiplayer::WyslijNamiary(t); // wysłanie informacji o pojeździe #else - // TODO: implement +/* + // TODO: re-enable when messaging component is in place + auto *vehicle = ( + pRozkaz->cString[ 1 ] == '*' ? + simulation::Vehicles.find( Global::asHumanCtrlVehicle ) : + simulation::Vehicles.find( std::string{ pRozkaz->cString + 1, (unsigned)pRozkaz->cString[ 0 ] } ) ); + if( vehicle != nullptr ) { + multiplayer::WyslijNamiary( vehicle ); // wysłanie informacji o pojeździe + } +*/ #endif } - else - { // dla pustego wysyłamy ramki 6 z nazwami pojazdów AI (jeśli potrzebne wszystkie, - // to rozpoznać np. "*") + else { + // dla pustego wysyłamy ramki 6 z nazwami pojazdów AI (jeśli potrzebne wszystkie, to rozpoznać np. "*") Ground.DynamicList(); } } @@ -2036,12 +2063,10 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) break; case 12: // skrocona ramka parametrow pojazdow AI (wszystkich!!) CommLog(Now() + " " + to_string(pRozkaz->iComm) + " obsadzone" + " rcvd"); - Ground.WyslijObsadzone(); + multiplayer::WyslijObsadzone(); // Ground.IsolatedBusy(AnsiString(pRozkaz->cString+1,(unsigned)(pRozkaz->cString[0]))); break; case 13: // ramka uszkodzenia i innych stanow pojazdu, np. wylaczenie CA, wlaczenie recznego itd. - // WriteLog("Przyszlo 13!"); - // WriteLog(pRozkaz->cString); CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd"); @@ -2067,7 +2092,7 @@ void TWorld::OnCommandGet(DaneRozkaz *pRozkaz) d->Damage( pRozkaz->cString[ 0 ] ); d = d->Prev(); // w drugą stronę też } - Ground.WyslijUszkodzenia( t->asName, t->DynamicObject->MoverParameters->EngDmgFlag ); // zwrot informacji o pojeździe + multiplayer::WyslijUszkodzenia( t->asName, t->DynamicObject->MoverParameters->EngDmgFlag ); // zwrot informacji o pojeździe } } #else diff --git a/World.h b/World.h index 471a6262..68d75ce6 100644 --- a/World.h +++ b/World.h @@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/. #include #include + #include "Camera.h" #include "Ground.h" #include "scene.h" @@ -20,6 +21,7 @@ http://mozilla.org/MPL/2.0/. #include "stars.h" #include "skydome.h" #include "mczapkie/mover.h" +#include "messaging.h" // wrapper for simulation time class simulation_time { @@ -104,7 +106,7 @@ 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 const * diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index a6d77415..ab330c49 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -240,6 +240,9 @@ Source Files + + Source Files + @@ -470,6 +473,9 @@ Header Files + + Header Files + diff --git a/messaging.cpp b/messaging.cpp new file mode 100644 index 00000000..5024f6ee --- /dev/null +++ b/messaging.cpp @@ -0,0 +1,245 @@ +/* +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 "ground.h" +#include "mtable.h" +#include "logs.h" + +extern "C" +{ + GLFWAPI HWND glfwGetWin32Window( GLFWwindow* window ); //m7todo: potrzebne do directsound +} + +namespace multiplayer { + +#ifdef _WINDOWS +void +Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam) { + // 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); +} + +void +WyslijEvent(const std::string &e, const std::string &d) +{ // Ra: jeszcze do wyczyszczenia + 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" ); +} + +void +WyslijUszkodzenia(const std::string &t, char fl) +{ // wysłanie informacji w postaci pojedynczego tekstu + 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"); +} + +void +WyslijString(const std::string &t, int n) +{ // wysłanie informacji w postaci pojedynczego tekstu + 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"); +} + +void +WyslijWolny(const std::string &t) +{ // Ra: jeszcze do wyczyszczenia + WyslijString(t, 4); // tor wolny +} + +void +WyslijNamiary(TGroundNode *t) +{ // wysłanie informacji o pojeździe - (float), długość ramki będzie zwiększana w miarę potrzeby + // 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 = t->asName.length(); + r.iPar[0] = i; // ilość danych liczbowych + r.fPar[1] = Global::fTimeAngleDeg / 360.0; // aktualny czas (1.0=doba) + r.fPar[2] = t->DynamicObject->MoverParameters->Loc.X; // pozycja X + r.fPar[3] = t->DynamicObject->MoverParameters->Loc.Y; // pozycja Y + r.fPar[4] = t->DynamicObject->MoverParameters->Loc.Z; // pozycja Z + r.fPar[5] = t->DynamicObject->MoverParameters->V; // prędkość ruchu X + r.fPar[6] = t->DynamicObject->MoverParameters->nrot * M_PI * + t->DynamicObject->MoverParameters->WheelDiameter; // prędkość obrotowa kóŁ + r.fPar[7] = 0; // prędkość ruchu Z + r.fPar[8] = t->DynamicObject->MoverParameters->AccS; // przyspieszenie X + r.fPar[9] = t->DynamicObject->MoverParameters->AccN; // przyspieszenie Y //na razie nie + r.fPar[10] = t->DynamicObject->MoverParameters->AccV; // przyspieszenie Z + r.fPar[11] = t->DynamicObject->MoverParameters->DistCounter; // przejechana odległość w km + r.fPar[12] = t->DynamicObject->MoverParameters->PipePress; // ciśnienie w PG + r.fPar[13] = t->DynamicObject->MoverParameters->ScndPipePress; // ciśnienie w PZ + r.fPar[14] = t->DynamicObject->MoverParameters->BrakePress; // ciśnienie w CH + r.fPar[15] = t->DynamicObject->MoverParameters->Compressor; // ciśnienie w ZG + r.fPar[16] = t->DynamicObject->MoverParameters->Itot; // Prąd całkowity + r.iPar[17] = t->DynamicObject->MoverParameters->MainCtrlPos; // Pozycja NJ + r.iPar[18] = t->DynamicObject->MoverParameters->ScndCtrlPos; // Pozycja NB + r.iPar[19] = t->DynamicObject->MoverParameters->MainCtrlActualPos; // Pozycja jezdna + r.iPar[20] = t->DynamicObject->MoverParameters->ScndCtrlActualPos; // Pozycja bocznikowania + r.iPar[21] = t->DynamicObject->MoverParameters->ScndCtrlActualPos; // Pozycja bocznikowania + r.iPar[22] = t->DynamicObject->MoverParameters->ResistorsFlag * 1 + + t->DynamicObject->MoverParameters->ConverterFlag * 2 + + +t->DynamicObject->MoverParameters->CompressorFlag * 4 + + t->DynamicObject->MoverParameters->Mains * 8 + + +t->DynamicObject->MoverParameters->DoorLeftOpened * 16 + + t->DynamicObject->MoverParameters->DoorRightOpened * 32 + + +t->DynamicObject->MoverParameters->FuseFlag * 64 + + t->DynamicObject->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 < t->DynamicObject->iAnimType[ANIM_PANTS]) + { + r.fPar[23 + p] = t->DynamicObject->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] = + t->DynamicObject->MoverParameters->ShowCurrent(p + 1); // amperomierze kolejnych grup + // WriteLog("zapisalem prady"); + r.iPar[30] = t->DynamicObject->MoverParameters->WarningSignal; // trabienie + r.fPar[31] = t->DynamicObject->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, t->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) + " " + t->asName + " sent"); +} + +void +WyslijObsadzone() +{ // wysłanie informacji o pojeździe + 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"); +} + +void +WyslijParam(int nr, int fl) +{ // wysłanie parametrów symulacji w ramce (nr) z flagami (fl) + 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 + +//--------------------------------------------------------------------------- diff --git a/messaging.h b/messaging.h new file mode 100644 index 00000000..fd2826ac --- /dev/null +++ b/messaging.h @@ -0,0 +1,50 @@ +/* +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 + +class TGroundNode; + +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( TGroundNode *t ); +void WyslijParam( int nr, int fl ); +void WyslijUszkodzenia( const std::string &t, char fl ); +void WyslijObsadzone(); // -> skladanie wielu pojazdow + +} // multiplayer + +//--------------------------------------------------------------------------- diff --git a/renderer.cpp b/renderer.cpp index e875f081..d4a6b65e 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -1598,6 +1598,212 @@ opengl_renderer::Render( TSubRect *Groundsubcell ) { return true; } + +bool +opengl_renderer::Render( TGroundNode *Node ) { + + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees + distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + default: { + distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + } + if( ( distancesquared < Node->fSquareMinRadius ) + || ( distancesquared >= Node->fSquareRadius ) ) { + return false; + } + + switch (Node->iType) { + + case TP_TRACK: { + // setup + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + return false; + } + case rendermode::pickscenery: { + // add the node to the pick list + m_picksceneryitems.emplace_back( Node ); + break; + } + default: { + break; + } + } + ::glPushMatrix(); + auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + // render + Render( Node->pTrack ); + // debug + ++m_debugstats.paths; + ++m_debugstats.drawcalls; + // post-render cleanup + ::glPopMatrix(); + return true; + } + + case TP_MODEL: { + switch( m_renderpass.draw_mode ) { + case rendermode::pickscenery: { + // add the node to the pick list + m_picksceneryitems.emplace_back( Node ); + break; + } + default: { + break; + } + } + Node->Model->RaAnimate( m_framestamp ); // jednorazowe przeliczenie animacji + Node->Model->RaPrepare(); + if( Node->Model->pModel ) { + // renderowanie rekurencyjne submodeli + Render( + Node->Model->pModel, + Node->Model->Material(), + distancesquared, + Node->pCenter - m_renderpass.camera.position(), + Node->Model->vAngle ); + } + return true; + } + + case GL_LINES: { + if( ( Node->Piece->geometry == null_handle ) + || ( Node->fLineThickness > 0.0 ) ) { + return false; + } + // setup + auto const distance = std::sqrt( distancesquared ); + auto const linealpha = + 10.0 * Node->fLineThickness + / std::max( + 0.5 * Node->m_radius + 1.0, + distance - ( 0.5 * Node->m_radius ) ); + switch( m_renderpass.draw_mode ) { + // wire colouring is disabled for modes other than colour + case rendermode::color: { + ::glColor4fv( + glm::value_ptr( + glm::vec4( + Node->Diffuse * glm::vec3( Global::DayLight.ambient ), // w zaleznosci od koloru swiatla + 1.0 ) ) ); // if the thickness is defined negative, lines are always drawn opaque + break; + } + case rendermode::shadows: + case rendermode::pickcontrols: + case rendermode::pickscenery: + default: { + break; + } + } + auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 8.0 ); + if( linewidth > 1.0 ) { + ::glLineWidth( static_cast( linewidth ) ); + } + + GfxRenderer.Bind_Material( null_handle ); + + ::glPushMatrix(); + auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + + switch( m_renderpass.draw_mode ) { + case rendermode::pickscenery: { + // add the node to the pick list + m_picksceneryitems.emplace_back( Node ); + break; + } + default: { + break; + } + } + // render + m_geometry.draw( Node->Piece->geometry ); + // debug +// ++m_debugstats.lines; +// ++m_debugstats.drawcalls; + // post-render cleanup + ::glPopMatrix(); + + if( linewidth > 1.0 ) { ::glLineWidth( 1.0f ); } + + return true; + } + + case GL_TRIANGLES: { + if( ( Node->Piece->geometry == null_handle ) + || ( ( Node->iFlags & 0x10 ) == 0 ) ) { + return false; + } + // setup + Bind_Material( Node->m_material ); + switch( m_renderpass.draw_mode ) { + case rendermode::color: { + ::glColor3fv( glm::value_ptr( Node->Diffuse ) ); + break; + } + // pick modes get custom colours, and shadow pass doesn't use any + case rendermode::shadows: + case rendermode::pickcontrols: + case rendermode::pickscenery: + default: { + break; + } + } + + ::glPushMatrix(); + auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + + switch( m_renderpass.draw_mode ) { + case rendermode::pickscenery: { + // add the node to the pick list + m_picksceneryitems.emplace_back( Node ); + break; + } + default: { + break; + } + } + // render + m_geometry.draw( Node->Piece->geometry ); + // debug + ++m_debugstats.shapes; + ++m_debugstats.drawcalls; + + // post-render cleanup + ::glPopMatrix(); + + return true; + } + + case TP_MEMCELL: { + switch( m_renderpass.draw_mode ) { + case rendermode::pickscenery: { + // add the node to the pick list + m_picksceneryitems.emplace_back( Node ); + break; + } + default: { + break; + } + } + Render( Node->MemCell ); + return true; + } + + default: { break; } + } + // in theory we shouldn't ever get here but, eh + return false; +} #else void opengl_renderer::Render( scene::basic_region *Region ) { @@ -1848,7 +2054,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } // tracks // TODO: update after path node refactoring - for( auto *path : cell->m_paths ) { Render( path ); } + Render( std::begin( cell->m_paths ), std::end( cell->m_paths ) ); // TODO: add other content types // post-render cleanup @@ -1880,10 +2086,14 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render // opaque non-instanced shapes + ::glColor3fv( glm::value_ptr( colors::none ) ); for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } // tracks // TODO: add path to the node picking list - for( auto *path : cell->m_paths ) { Render( path ); } + for( auto *path : cell->m_paths ) { + ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); + Render( path ); + } // TODO: add other content types // post-render cleanup ::glPopMatrix(); @@ -2053,212 +2263,6 @@ opengl_renderer::Render( TAnimModel *Instance ) { } #endif -bool -opengl_renderer::Render( TGroundNode *Node ) { - - double distancesquared; - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; - break; - } - default: { - distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; - break; - } - } - if( ( distancesquared < Node->fSquareMinRadius ) - || ( distancesquared >= Node->fSquareRadius ) ) { - return false; - } - - switch (Node->iType) { - - case TP_TRACK: { - // setup - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - return false; - } - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Node ); - break; - } - default: { - break; - } - } - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - // render - Render( Node->pTrack ); - // debug - ++m_debugstats.paths; - ++m_debugstats.drawcalls; - // post-render cleanup - ::glPopMatrix(); - return true; - } - - case TP_MODEL: { - switch( m_renderpass.draw_mode ) { - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Node ); - break; - } - default: { - break; - } - } - Node->Model->RaAnimate( m_framestamp ); // jednorazowe przeliczenie animacji - Node->Model->RaPrepare(); - if( Node->Model->pModel ) { - // renderowanie rekurencyjne submodeli - Render( - Node->Model->pModel, - Node->Model->Material(), - distancesquared, - Node->pCenter - m_renderpass.camera.position(), - Node->Model->vAngle ); - } - return true; - } - - case GL_LINES: { - if( ( Node->Piece->geometry == null_handle ) - || ( Node->fLineThickness > 0.0 ) ) { - return false; - } - // setup - auto const distance = std::sqrt( distancesquared ); - auto const linealpha = - 10.0 * Node->fLineThickness - / std::max( - 0.5 * Node->m_radius + 1.0, - distance - ( 0.5 * Node->m_radius ) ); - switch( m_renderpass.draw_mode ) { - // wire colouring is disabled for modes other than colour - case rendermode::color: { - ::glColor4fv( - glm::value_ptr( - glm::vec4( - Node->Diffuse * glm::vec3( Global::DayLight.ambient ), // w zaleznosci od koloru swiatla - 1.0 ) ) ); // if the thickness is defined negative, lines are always drawn opaque - break; - } - case rendermode::shadows: - case rendermode::pickcontrols: - case rendermode::pickscenery: - default: { - break; - } - } - auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 8.0 ); - if( linewidth > 1.0 ) { - ::glLineWidth( static_cast( linewidth ) ); - } - - GfxRenderer.Bind_Material( null_handle ); - - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - switch( m_renderpass.draw_mode ) { - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Node ); - break; - } - default: { - break; - } - } - // render - m_geometry.draw( Node->Piece->geometry ); - // debug -// ++m_debugstats.lines; -// ++m_debugstats.drawcalls; - // post-render cleanup - ::glPopMatrix(); - - if( linewidth > 1.0 ) { ::glLineWidth( 1.0f ); } - - return true; - } - - case GL_TRIANGLES: { - if( ( Node->Piece->geometry == null_handle ) - || ( ( Node->iFlags & 0x10 ) == 0 ) ) { - return false; - } - // setup - Bind_Material( Node->m_material ); - switch( m_renderpass.draw_mode ) { - case rendermode::color: { - ::glColor3fv( glm::value_ptr( Node->Diffuse ) ); - break; - } - // pick modes get custom colours, and shadow pass doesn't use any - case rendermode::shadows: - case rendermode::pickcontrols: - case rendermode::pickscenery: - default: { - break; - } - } - - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - switch( m_renderpass.draw_mode ) { - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Node ); - break; - } - default: { - break; - } - } - // render - m_geometry.draw( Node->Piece->geometry ); - // debug - ++m_debugstats.shapes; - ++m_debugstats.drawcalls; - - // post-render cleanup - ::glPopMatrix(); - - return true; - } - - case TP_MEMCELL: { - switch( m_renderpass.draw_mode ) { - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Node ); - break; - } - default: { - break; - } - } - Render( Node->MemCell ); - return true; - } - - default: { break; } - } - // in theory we shouldn't ever get here but, eh - return false; -} - bool opengl_renderer::Render( TDynamicObject *Dynamic ) { @@ -2794,6 +2798,83 @@ opengl_renderer::Render( TTrack *Track ) { } } +// experimental, does track rendering in two passes, to take advantage of reduced texture switching +void +opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last ) { + + ::glColor3fv( glm::value_ptr( colors::white ) ); + + // first pass, material 1 + for( auto first { First }; first != Last; ++first ) { + + auto const track { *first }; + + if( track->m_material1 == 0 ) { + continue; + } + if( false == track->m_visible ) { + continue; + } + + ++m_debugstats.paths; + ++m_debugstats.drawcalls; + + switch( m_renderpass.draw_mode ) { + case rendermode::color: + case rendermode::reflections: { + track->EnvironmentSet(); + Bind_Material( track->m_material1 ); + m_geometry.draw( std::begin( track->Geometry1 ), std::end( track->Geometry1 ) ); + track->EnvironmentReset(); + break; + } + case rendermode::shadows: { + Bind_Material( track->m_material1 ); + m_geometry.draw( std::begin( track->Geometry1 ), std::end( track->Geometry1 ) ); + break; + } + case rendermode::pickscenery: // pick scenery should use track-by-track approach + case rendermode::pickcontrols: + default: { + break; + } + } + } + // second pass, material 2 + for( auto first { First }; first != Last; ++first ) { + + auto const track { *first }; + + if( track->m_material2 == 0 ) { + continue; + } + if( false == track->m_visible ) { + continue; + } + + switch( m_renderpass.draw_mode ) { + case rendermode::color: + case rendermode::reflections: { + track->EnvironmentSet(); + Bind_Material( track->m_material2 ); + m_geometry.draw( std::begin( track->Geometry2 ), std::end( track->Geometry2 ) ); + track->EnvironmentReset(); + break; + } + case rendermode::shadows: { + Bind_Material( track->m_material2 ); + m_geometry.draw( std::begin( track->Geometry2 ), std::end( track->Geometry2 ) ); + break; + } + case rendermode::pickscenery: // pick scenery should use track-by-track approach + case rendermode::pickcontrols: + default: { + break; + } + } + } +} + void opengl_renderer::Render( TMemCell *Memcell ) { @@ -2879,6 +2960,162 @@ opengl_renderer::Render_Alpha( TSubRect *Groundsubcell ) { return true; } + +bool +opengl_renderer::Render_Alpha( TGroundNode *Node ) { + + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees + distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + default: { + distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + } + if( ( distancesquared < Node->fSquareMinRadius ) + || ( distancesquared >= Node->fSquareRadius ) ) { + return false; + } + + switch (Node->iType) + { + case TP_TRACTION: { + if( Node->bVisible ) { + // rysuj jesli sa druty i nie zerwana + if( ( Node->hvTraction->Wires == 0 ) + || ( true == TestFlag( Node->hvTraction->DamageFlag, 128 ) ) ) { + return false; + } + // setup + if( !Global::bSmoothTraction ) { + // na liniach kiepsko wygląda - robi gradient + ::glDisable( GL_LINE_SMOOTH ); + } + float const linealpha = static_cast( + std::min( + 1.25, + 5000 * Node->hvTraction->WireThickness / ( distancesquared + 1.0 ) ) ); // zbyt grube nie są dobre + ::glLineWidth( linealpha ); + // McZapkie-261102: kolor zalezy od materialu i zasniedzenia + auto const color { Node->hvTraction->wire_color() }; + ::glColor4f( color.r, color.g, color.b, linealpha ); + + Bind_Material( null_handle ); + + ::glPushMatrix(); + auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + + // render + m_geometry.draw( Node->hvTraction->m_geometry ); + // debug data + ++m_debugstats.traction; + ++m_debugstats.drawcalls; + + // post-render cleanup + ::glPopMatrix(); + + ::glLineWidth( 1.0 ); + if( !Global::bSmoothTraction ) { + ::glEnable( GL_LINE_SMOOTH ); + } + + return true; + } + else { + return false; + } + } + case TP_MODEL: { + + Node->Model->RaPrepare(); + if( Node->Model->pModel ) { + // renderowanie rekurencyjne submodeli + Render_Alpha( + Node->Model->pModel, + Node->Model->Material(), + distancesquared, + Node->pCenter - m_renderpass.camera.position(), + Node->Model->vAngle ); + } + return true; + } + + case GL_LINES: { + if( ( Node->Piece->geometry == null_handle ) + || ( Node->fLineThickness < 0.0 ) ) { + return false; + } + // setup + auto const distance = std::sqrt( distancesquared ); + auto const linealpha = + 10.0 * Node->fLineThickness + / std::max( + 0.5 * Node->m_radius + 1.0, + distance - ( 0.5 * Node->m_radius ) ); + ::glColor4fv( + glm::value_ptr( + glm::vec4( + Node->Diffuse * glm::vec3( Global::DayLight.ambient ), // w zaleznosci od koloru swiatla + std::min( 1.0, linealpha ) ) ) ); + auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 8.0 ); + if( linewidth > 1.0 ) { + ::glLineWidth( static_cast(linewidth) ); + } + + GfxRenderer.Bind_Material( null_handle ); + + ::glPushMatrix(); + auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + + // render + m_geometry.draw( Node->Piece->geometry ); +// ++m_debugstats.lines; +// ++m_debugstats.drawcalls; + + // post-render cleanup + ::glPopMatrix(); + + if( linewidth > 1.0 ) { ::glLineWidth( 1.0f ); } + + return true; + } + + case GL_TRIANGLES: { + if( ( Node->Piece->geometry == null_handle ) + || ( ( Node->iFlags & 0x20 ) == 0 ) ) { + return false; + } + // setup + ::glColor3fv( glm::value_ptr( Node->Diffuse ) ); + + Bind_Material( Node->m_material ); + + ::glPushMatrix(); + auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); + + // render + m_geometry.draw( Node->Piece->geometry ); + // debug data + ++m_debugstats.shapes; + ++m_debugstats.drawcalls; + // post-render cleanup + ::glPopMatrix(); + + return true; + } + + default: { break; } + } + // in theory we shouldn't ever get here but, eh + return false; +} #else void opengl_renderer::Render_Alpha( scene::basic_region *Region ) { @@ -3051,162 +3288,6 @@ opengl_renderer::Render_Alpha( TTraction *Traction ) { ++m_debugstats.drawcalls; } #endif -bool -opengl_renderer::Render_Alpha( TGroundNode *Node ) { - - double distancesquared; - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; - break; - } - default: { - distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; - break; - } - } - if( ( distancesquared < Node->fSquareMinRadius ) - || ( distancesquared >= Node->fSquareRadius ) ) { - return false; - } - - switch (Node->iType) - { - case TP_TRACTION: { - if( Node->bVisible ) { - // rysuj jesli sa druty i nie zerwana - if( ( Node->hvTraction->Wires == 0 ) - || ( true == TestFlag( Node->hvTraction->DamageFlag, 128 ) ) ) { - return false; - } - // setup - if( !Global::bSmoothTraction ) { - // na liniach kiepsko wygląda - robi gradient - ::glDisable( GL_LINE_SMOOTH ); - } - float const linealpha = static_cast( - std::min( - 1.25, - 5000 * Node->hvTraction->WireThickness / ( distancesquared + 1.0 ) ) ); // zbyt grube nie są dobre - ::glLineWidth( linealpha ); - // McZapkie-261102: kolor zalezy od materialu i zasniedzenia - auto const color { Node->hvTraction->wire_color() }; - ::glColor4f( color.r, color.g, color.b, linealpha ); - - Bind_Material( null_handle ); - - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - // render - m_geometry.draw( Node->hvTraction->m_geometry ); - // debug data - ++m_debugstats.traction; - ++m_debugstats.drawcalls; - - // post-render cleanup - ::glPopMatrix(); - - ::glLineWidth( 1.0 ); - if( !Global::bSmoothTraction ) { - ::glEnable( GL_LINE_SMOOTH ); - } - - return true; - } - else { - return false; - } - } - case TP_MODEL: { - - Node->Model->RaPrepare(); - if( Node->Model->pModel ) { - // renderowanie rekurencyjne submodeli - Render_Alpha( - Node->Model->pModel, - Node->Model->Material(), - distancesquared, - Node->pCenter - m_renderpass.camera.position(), - Node->Model->vAngle ); - } - return true; - } - - case GL_LINES: { - if( ( Node->Piece->geometry == null_handle ) - || ( Node->fLineThickness < 0.0 ) ) { - return false; - } - // setup - auto const distance = std::sqrt( distancesquared ); - auto const linealpha = - 10.0 * Node->fLineThickness - / std::max( - 0.5 * Node->m_radius + 1.0, - distance - ( 0.5 * Node->m_radius ) ); - ::glColor4fv( - glm::value_ptr( - glm::vec4( - Node->Diffuse * glm::vec3( Global::DayLight.ambient ), // w zaleznosci od koloru swiatla - std::min( 1.0, linealpha ) ) ) ); - auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 8.0 ); - if( linewidth > 1.0 ) { - ::glLineWidth( static_cast(linewidth) ); - } - - GfxRenderer.Bind_Material( null_handle ); - - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - // render - m_geometry.draw( Node->Piece->geometry ); -// ++m_debugstats.lines; -// ++m_debugstats.drawcalls; - - // post-render cleanup - ::glPopMatrix(); - - if( linewidth > 1.0 ) { ::glLineWidth( 1.0f ); } - - return true; - } - - case GL_TRIANGLES: { - if( ( Node->Piece->geometry == null_handle ) - || ( ( Node->iFlags & 0x20 ) == 0 ) ) { - return false; - } - // setup - ::glColor3fv( glm::value_ptr( Node->Diffuse ) ); - - Bind_Material( Node->m_material ); - - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - // render - m_geometry.draw( Node->Piece->geometry ); - // debug data - ++m_debugstats.shapes; - ++m_debugstats.drawcalls; - // post-render cleanup - ::glPopMatrix(); - - return true; - } - - default: { break; } - } - // in theory we shouldn't ever get here but, eh - return false; -} - bool opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { diff --git a/renderer.h b/renderer.h index 9879fb93..75027568 100644 --- a/renderer.h +++ b/renderer.h @@ -294,6 +294,8 @@ private: Render( TGroundRect *Groundcell ); bool Render( TSubRect *Groundsubcell ); + bool + Render( TGroundNode *Node ); #else void Render( scene::basic_region *Region ); @@ -306,8 +308,6 @@ private: void Render( TAnimModel *Instance ); #endif - bool - Render( TGroundNode *Node ); bool Render( TDynamicObject *Dynamic ); bool @@ -318,6 +318,8 @@ 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 @@ -327,6 +329,8 @@ private: Render_Alpha( TGround *Ground ); bool Render_Alpha( TSubRect *Groundsubcell ); + bool + Render_Alpha( TGroundNode *Node ); #else void Render_Alpha( scene::basic_region *Region ); @@ -337,8 +341,6 @@ private: void Render_Alpha( TTraction *Traction ); #endif - bool - Render_Alpha( TGroundNode *Node ); bool Render_Alpha( TDynamicObject *Dynamic ); bool diff --git a/scene.cpp b/scene.cpp index 2fa1783d..e2587e7d 100644 --- a/scene.cpp +++ b/scene.cpp @@ -372,6 +372,10 @@ basic_cell::create_geometry( geometrybank_handle const &Bank ) { for( auto *path : m_paths ) { path->create_geometry( Bank ); } for( auto *traction : m_traction ) { traction->create_geometry( Bank ); } #endif + // arrange content by assigned materials to minimize state switching + std::sort( + std::begin( m_paths ), std::end( m_paths ), + TTrack::sort_by_material ); } diff --git a/simulation.cpp b/simulation.cpp index 0d0415d6..b45c5532 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -685,7 +685,7 @@ state_manager::deserialize_dynamic( cParser &Input, scene::scratch_data &Scratch Input.getToken() ); auto const offset { Input.getToken( false ) }; auto const drivertype { Input.getToken() }; - auto const couplingparams = ( + auto const couplingdata = ( Scratchpad.trainset.is_open ? Input.getToken() : "3" ); @@ -694,11 +694,11 @@ state_manager::deserialize_dynamic( cParser &Input, scene::scratch_data &Scratch Scratchpad.trainset.velocity : Input.getToken( false ) ); // extract coupling type and optional parameters - auto const couplingparamsplit = couplingparams.find( '.' ); + auto const couplingdatawithparams = couplingdata.find( '.' ); auto coupling = ( - couplingparamsplit != std::string::npos ? - std::atoi( couplingparams.substr( 0, couplingparamsplit ).c_str() ) : - std::atoi( couplingparams.c_str() ) ); + 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; @@ -706,11 +706,11 @@ state_manager::deserialize_dynamic( cParser &Input, scene::scratch_data &Scratch 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 = 0; + coupling = coupling::faux; } auto const params = ( - couplingparamsplit != std::string::npos ? - couplingparams.substr( couplingparamsplit + 1 ) : + couplingdatawithparams != std::string::npos ? + couplingdata.substr( couplingdatawithparams + 1 ) : "" ); // load amount and type auto loadcount { Input.getToken( false ) }; diff --git a/windows.cpp b/windows.cpp index dd23a74d..28b46abc 100644 --- a/windows.cpp +++ b/windows.cpp @@ -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; } } From 87348a2ab8cd8ab439ba45a445ec3046e8e3ce93 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Tue, 17 Oct 2017 19:49:14 +0200 Subject: [PATCH 12/31] continued refactoring: lines, terrain models; render culling optimizations and fixes --- AnimModel.cpp | 26 ++++- AnimModel.h | 4 + Camera.cpp | 9 +- DynObj.cpp | 15 +++ DynObj.h | 3 + Event.cpp | 40 +++---- Globals.cpp | 4 +- Globals.h | 2 +- Ground.cpp | 31 +++-- Ground.h | 21 +--- MemCell.cpp | 10 +- Model3d.cpp | 49 ++++---- Model3d.h | 22 ++-- Track.cpp | 105 +++++++++++++---- Track.h | 34 +++++- World.cpp | 78 ++++++++++--- messaging.cpp | 79 +++++++++++++ messaging.h | 8 ++ renderer.cpp | 109 +++++++++++------- renderer.h | 2 + scene.cpp | 298 +++++++++++++++++++++++++++++++++++++++++-------- scene.h | 58 ++++++++-- scenenode.cpp | 206 ++++++++++++++++++++++++++++++++++ scenenode.h | 57 ++++++++++ simulation.cpp | 36 +++++- 25 files changed, 1062 insertions(+), 244 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index 56eaa3b1..ae83516b 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -232,9 +232,14 @@ 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 +#ifdef EU07_USE_OLD_GROUNDCODE + Global::AddToQuery( evDone, NULL ); +#else + simulation::Events.AddToQuery( evDone, nullptr ); +#endif + } } } if (fRotateSpeed != 0.0) @@ -299,9 +304,14 @@ 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 +#ifdef EU07_USE_OLD_GROUNDCODE + Global::AddToQuery( evDone, NULL ); +#else + simulation::Events.AddToQuery( evDone, nullptr ); +#endif + } } } if( fAngleSpeed != 0.f ) { @@ -330,7 +340,11 @@ void TAnimContainer::PrepareModel() fAngleSpeed = 0.0; // wyłączenie przeliczania wektora if( evDone ) { // wykonanie eventu informującego o zakończeniu +#ifdef EU07_USE_OLD_GROUNDCODE Global::AddToQuery( evDone, NULL ); +#else + simulation::Events.AddToQuery( evDone, nullptr ); +#endif } } else diff --git a/AnimModel.h b/AnimModel.h index 47d1c25a..a1528cdd 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -149,6 +149,10 @@ public: 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 diff --git a/Camera.cpp b/Camera.cpp index ef589bc5..175a8773 100644 --- a/Camera.cpp +++ b/Camera.cpp @@ -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( Command.param1 ) * 0.005 * Global::fMouseXScale / Global::ZoomFactor, - reinterpret_cast( Command.param2 ) * -0.01 * Global::fMouseYScale / Global::ZoomFactor ); + reinterpret_cast( Command.param2 ) * 0.01 * Global::fMouseYScale / Global::ZoomFactor ); break; } diff --git a/DynObj.cpp b/DynObj.cpp index beb068b2..b4ec1427 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -5501,3 +5501,18 @@ vehicle_table::update_traction( TDynamicObject *Vehicle ) { } } } + +// 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 ); +} diff --git a/DynObj.h b/DynObj.h index 8670ea20..931ba255 100644 --- a/DynObj.h +++ b/DynObj.h @@ -495,6 +495,9 @@ public: // 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; }; //--------------------------------------------------------------------------- diff --git a/Event.cpp b/Event.cpp index 398b7699..daf24862 100644 --- a/Event.cpp +++ b/Event.cpp @@ -46,8 +46,7 @@ TEvent::~TEvent() { switch (Type) { // sprzątanie case tp_Multiple: - // SafeDeleteArray(Params[9].asText); //nie usuwać - nazwa obiektu powiązanego zamieniana na - // wskaźnik + // SafeDeleteArray(Params[9].asText); //nie usuwać - nazwa obiektu powiązanego zamieniana na wskaźnik if (iFlags & conditional_memstring) // o ile jest łańcuch do porównania w memcompare SafeDeleteArray(Params[10].asText); break; @@ -82,8 +81,7 @@ void TEvent::Conditions(cParser *parser, std::string s) if (!asNodeName.empty()) { // podczepienie łańcucha, jeśli nie jest pusty // BUG: source of a memory leak -- the array never gets deleted. fix the destructor - Params[9].asText = new char[asNodeName.size() + 1]; // usuwane i zamieniane na - // wskaźnik + Params[9].asText = new char[asNodeName.size() + 1]; // usuwane i zamieniane na wskaźnik strcpy(Params[9].asText, asNodeName.c_str()); } parser->getTokens(); @@ -860,17 +858,17 @@ event_manager::AddToQuery( TEvent *Event, TDynamicObject *Owner ) { } //if (DebugModeFlag) WriteLog( - "EVENT EXECUTED" + ( Owner ? ( " by " + Owner->asName ) : "" ) + ": AddValues & Track command ( " - + std::string( Event->Params[ 0 ].asText ) + " " - + std::to_string( Event->Params[ 1 ].asdouble ) + " " - + std::to_string( Event->Params[ 2 ].asdouble ) + " )" ); + "EVENT EXECUTED" + ( Owner ? ( " by " + Owner->asName ) : "" ) + ": AddValues & Track command - [" + + std::string{ Event->Params[ 0 ].asText } + "] [" + + to_string( Event->Params[ 1 ].asdouble, 2 ) + "] [" + + to_string( Event->Params[ 2 ].asdouble, 2 ) + " ]" ); } //else if (DebugModeFlag) WriteLog( - "EVENT EXECUTED" + ( Owner ? ( " by " + Owner->asName ) : "" ) + ": AddValues ( " - + std::string( Event->Params[ 0 ].asText ) + " " - + std::to_string( Event->Params[ 1 ].asdouble ) + " " - + std::to_string( Event->Params[ 2 ].asdouble ) + " )" ); + "EVENT EXECUTED" + ( Owner ? ( " by " + Owner->asName ) : "" ) + ": AddValues - [" + + std::string( Event->Params[ 0 ].asText ) + "] [" + + to_string( Event->Params[ 1 ].asdouble, 2 ) + "] [" + + to_string( Event->Params[ 2 ].asdouble, 2 ) + "]" ); } // jeśli jest kolejny o takiej samej nazwie, to idzie do kolejki (and if there's no joint event it'll be set to null and processing will end here) do { @@ -975,13 +973,11 @@ event_manager::CheckQuery() { break; case tp_GetValues: { if( m_workevent->Activator ) { -/* // TODO: re-enable when messaging module is in place if( Global::iMultiplayer ) { // potwierdzenie wykonania dla serwera (odczyt semafora już tak nie działa) - WyslijEvent( tmpEvent->asName, tmpEvent->Activator->name() ); + multiplayer::WyslijEvent( m_workevent->asName, m_workevent->Activator->name() ); } -*/ m_workevent->Params[ 9 ].asMemCell->PutCommand( m_workevent->Activator->Mechanik, #ifdef EU07_USE_OLD_GROUNDCODE @@ -1124,13 +1120,10 @@ event_manager::CheckQuery() { m_workevent->Params[ 1 ].asdouble, m_workevent->Params[ 2 ].asdouble ); } -/* - // TODO: re-enable when messaging module is in place if( Global::iMultiplayer ) { // dajemy znać do serwera o przełożeniu - WyslijEvent( m_workevent->asName, "" ); // wysłanie nazwy eventu przełączajacego + multiplayer::WyslijEvent( m_workevent->asName, "" ); // wysłanie nazwy eventu przełączajacego } -*/ // Ra: bardziej by się przydała nazwa toru, ale nie ma do niej stąd dostępu break; } @@ -1170,21 +1163,18 @@ event_manager::CheckQuery() { } } } -/* - // TODO: re-enable when messaging component is in place if( Global::iMultiplayer ) { // dajemy znać do serwera o wykonaniu if( ( m_workevent->iFlags & conditional_anyelse ) == 0 ) { // jednoznaczne tylko, gdy nie było else if( m_workevent->Activator ) { - WyslijEvent( m_workevent->asName, m_workevent->Activator->name() ); + multiplayer::WyslijEvent( m_workevent->asName, m_workevent->Activator->name() ); } else { - WyslijEvent( m_workevent->asName, "" ); + multiplayer::WyslijEvent( m_workevent->asName, "" ); } } } -*/ } break; } @@ -1273,8 +1263,8 @@ event_manager::CheckQuery() { + to_string( m_workevent->Params[ 9 ].asMemCell->Value2(), 2 ) + "]" ); } else { - // TODO: re-enable when cell manager is in place /* + // TODO: re-enable when cell manager is in place // lista wszystkich for( TGroundNode *Current = nRootOfType[ TP_MEMCELL ]; Current; Current = Current->nNext ) { WriteLog( "Memcell \"" + Current->asName + "\": " diff --git a/Globals.cpp b/Globals.cpp index f0e85b79..9544503e 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -1001,13 +1001,12 @@ TDynamicObject *Global::DynamicNearest() { // ustalenie pojazdu najbliższego kamerze return pGround->DynamicNearest(pCamera->Pos); }; -#endif TDynamicObject *Global::CouplerNearest() { // ustalenie pojazdu najbliższego kamerze return pGround->CouplerNearest(pCamera->Pos); }; - +#endif bool Global::AddToQuery(TEvent *event, TDynamicObject *who) { #ifdef EU07_USE_OLD_GROUNDCODE @@ -1016,6 +1015,7 @@ bool Global::AddToQuery(TEvent *event, TDynamicObject *who) return simulation::Events.AddToQuery( event, who ); #endif }; + //--------------------------------------------------------------------------- bool Global::DoEvents() diff --git a/Globals.h b/Globals.h index e3e15a8d..46ce880c 100644 --- a/Globals.h +++ b/Globals.h @@ -299,8 +299,8 @@ class Global static std::string GetNextSymbol(); #ifdef EU07_USE_OLD_GROUNDCODE static TDynamicObject * DynamicNearest(); -#endif static TDynamicObject * CouplerNearest(); +#endif static bool AddToQuery(TEvent *event, TDynamicObject *who); static bool DoEvents(); static std::string Bezogonkow(std::string str, bool _ = false); diff --git a/Ground.cpp b/Ground.cpp index b5f14519..c628faa3 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -243,7 +243,9 @@ void TSubRect::NodeAdd(TGroundNode *Node) break; case TP_TRACK: // TODO: tory z cieniem (tunel, canyon) też dać bez łączenia? ++iTracks; // jeden tor więcej +#ifdef EU07_USE_OLD_GROUNDCODE Node->pTrack->RaOwnerSet(this); // do którego sektora ma zgłaszać animację +#endif // NOTE: track merge/sort temporarily disabled to simplify unification of render code // TODO: refactor sorting as universal part of drawing process in the renderer Node->nNext3 = nRenderRect; @@ -340,6 +342,7 @@ TTrack * TSubRect::FindTrack(vector3 *Point, int &iConnection, TTrack *Exclude) return NULL; }; +#ifdef EU07_USE_OLD_GROUNDCODE bool TSubRect::RaTrackAnimAdd(TTrack *t) { // aktywacja animacji torów w VBO (zwrotnica, obrotnica) if( false == m_geometrycreated ) { @@ -365,6 +368,7 @@ void TSubRect::RaAnimate( unsigned int const Framestamp ) { m_framestamp = Framestamp; }; +#endif TTraction * TSubRect::FindTraction(glm::dvec3 const &Point, int &iConnection, TTraction *Exclude) { // szukanie przęsła w sektorze, którego koniec jest najbliższy (*Point) @@ -604,7 +608,7 @@ TGroundNode * TGround::DynamicFind(std::string const &Name) return Current; return NULL; }; -#endif + void TGround::DynamicList(bool all) { // odesłanie nazw pojazdów dostępnych na scenerii (nazwy, szczególnie wagonów, mogą się @@ -614,7 +618,7 @@ TGround::DynamicList(bool all) multiplayer::WyslijString(Current->asName, 6); // same nazwy pojazdów multiplayer::WyslijString("none", 6); // informacja o końcu listy }; -#ifdef EU07_USE_OLD_GROUNDCODE + // wyszukiwanie obiektu o podanej nazwie i konkretnym typie TGroundNode * TGround::FindGroundNode(std::string const &asNameToFind, TGroundNodeType const iNodeType) { @@ -648,7 +652,7 @@ TGround::GetRect( double x, double z ) { return nullptr; } }; - +#ifdef EU07_USE_OLD_GROUNDCODE // convert tp_terrain model to a series of triangle nodes void TGround::convert_terrain( TGroundNode const *Terrain ) { @@ -683,7 +687,7 @@ TGround::convert_terrain( TSubModel const *Submodel ) { delete groundnode; } } - +#endif double fTrainSetVel = 0; double fTrainSetDir = 0; double fTrainSetDist = 0; // odległość składu od punktu 1 w stronę punktu 2 @@ -2766,7 +2770,7 @@ void TGround::InitTraction() } delete[] nEnds; // nie potrzebne już }; -#endif + void TGround::TrackJoin(TGroundNode *Current) { // wyszukiwanie sąsiednich torów do podłączenia (wydzielone na użytek obrotnicy) TTrack *Track = Current->pTrack; @@ -2774,11 +2778,7 @@ void TGround::TrackJoin(TGroundNode *Current) int iConnection; if (!Track->CurrentPrev()) { -#ifdef EU07_USE_OLD_GROUNDCODE tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_0(), iConnection, Current); // Current do pominięcia -#else - std::tie( tmp, iConnection ) = simulation::Region->find_path( Track->CurrentSegment()->FastGetPoint_0(), Track ); -#endif switch (iConnection) { case 0: @@ -2791,11 +2791,7 @@ void TGround::TrackJoin(TGroundNode *Current) } if (!Track->CurrentNext()) { -#ifdef EU07_USE_OLD_GROUNDCODE tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_1(), iConnection, Current); -#else - std::tie( tmp, iConnection ) = simulation::Region->find_path( Track->CurrentSegment()->FastGetPoint_1(), Track ); -#endif switch (iConnection) { case 0: @@ -2807,7 +2803,7 @@ void TGround::TrackJoin(TGroundNode *Current) } } } -#ifdef EU07_USE_OLD_GROUNDCODE + // McZapkie-070602: wyzwalacze zdarzen bool TGround::InitLaunchers() { @@ -3755,7 +3751,7 @@ bool TGround::GetTraction(TDynamicObject *model) return true; }; -#endif + //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- @@ -3800,6 +3796,7 @@ TDynamicObject * TGround::DynamicNearest(vector3 pPosition, double distance, boo } return dyn; }; + TDynamicObject * TGround::CouplerNearest(vector3 pPosition, double distance, bool mech) { // wyszukanie pojazdu, którego sprzęg jest najbliżej względem (pPosition) TGroundNode *node; @@ -3829,6 +3826,7 @@ TDynamicObject * TGround::CouplerNearest(vector3 pPosition, double distance, boo } return dyn; }; +#endif //--------------------------------------------------------------------------- void TGround::DynamicRemove(TDynamicObject *dyn) { // Ra: usunięcie pojazdów ze scenerii (gdy dojadą na koniec i nie sa potrzebne) @@ -3951,7 +3949,7 @@ void TGround::TerrainWrite() }; //--------------------------------------------------------------------------- - +#ifdef EU07_USE_OLD_GROUNDCODE void TGround::TrackBusyList() { // wysłanie informacji o wszystkich zajętych odcinkach TGroundNode *Current; @@ -3987,6 +3985,7 @@ void TGround::IsolatedBusy(const std::string t) } multiplayer::WyslijString(t, 10); // wolny }; +#endif //--------------------------------------------------------------------------- void TGround::Silence(vector3 gdzie) diff --git a/Ground.h b/Ground.h index 3452cdbd..8844752d 100644 --- a/Ground.h +++ b/Ground.h @@ -128,14 +128,8 @@ public: 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 +class TSubRect : public CMesh { // sektor składowy kwadratu kilometrowego public: scene::bounding_area m_area; @@ -165,8 +159,10 @@ class TSubRect : /*public Resource,*/ public CMesh 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); +#ifdef EU07_USE_OLD_GROUNDCODE bool RaTrackAnimAdd(TTrack *t); // zgłoszenie toru do animacji void RaAnimate( unsigned int const Framestamp ); // przeliczenie animacji torów +#endif void RenderSounds(); // dźwięki pojazdów z niewidocznych sektorów }; @@ -273,9 +269,6 @@ class TGround bool CheckQuery(); TGroundNode * DynamicFindAny(std::string const &Name); TGroundNode * DynamicFind(std::string const &Name); -#endif - void DynamicList(bool all = false); -#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode * FindGroundNode(std::string const &asNameToFind, TGroundNodeType const iNodeType); #endif TGroundRect * GetRect( double x, double z ); @@ -295,25 +288,23 @@ class TGround return (int)(x / fSubRectSize + fHalfTotalNumSubRects); }; #ifdef EU07_USE_OLD_GROUNDCODE TEvent * FindEvent(const std::string &asEventName); -#endif 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 ); -#ifdef EU07_USE_OLD_GROUNDCODE void RaTriangleDivider(TGroundNode *node); - void Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam); #endif public: +#ifdef EU07_USE_OLD_GROUNDCODE + void DynamicList( bool all = false ); void TrackBusyList(); void IsolatedBusyList(); void IsolatedBusy( const std::string t ); - 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); +#endif void DynamicRemove(TDynamicObject *dyn); void TerrainRead(std::string const &f); void TerrainWrite(); diff --git a/MemCell.cpp b/MemCell.cpp index dcbba49c..63c27b40 100644 --- a/MemCell.cpp +++ b/MemCell.cpp @@ -176,8 +176,14 @@ void TMemCell::StopCommandSent() if (!bCommand) return; bCommand = false; - if (OnSent) // jeśli jest event - Global::AddToQuery(OnSent, NULL); + if( OnSent ) { + // jeśli jest event +#ifdef EU07_USE_OLD_GROUNDCODE + Global::AddToQuery( OnSent, NULL ); +#else + simulation::Events.AddToQuery( OnSent, nullptr ); +#endif + } }; void TMemCell::AssignEvents(TEvent *e) diff --git a/Model3d.cpp b/Model3d.cpp index f2ce05be..0cbdfb17 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -19,7 +19,9 @@ Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others #include "logs.h" #include "mczapkie/mctools.h" #include "Usefull.h" +#ifdef EU07_USE_OLD_GROUNDCODE #include "ground.h" +#endif #include "renderer.h" #include "Timer.h" #include "mtable.h" @@ -988,11 +990,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( 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,10 +1005,29 @@ TSubModel::create_geometry( std::size_t &Dataoffset, geometrybank_handle const & m_geometry = GfxRenderer.Insert( Vertices, Bank, type ); } + if( m_geometry != NULL ) { + // calculate bounding radius while we're at it + // NOTE: doesn't take into account transformation hierarchy TODO: implement it + float squaredradius{ 0.f }; + for( auto const &vertex : GfxRenderer.Vertices( m_geometry ) ) { + squaredradius = static_cast( glm::length2( vertex.position ) ); + if( squaredradius > m_boundingradius ) { + m_boundingradius = squaredradius; + } + } + if( m_boundingradius > 0.f ) { m_boundingradius = std::sqrt( m_boundingradius ); } + if( Parent ) { + // propagate radius up the chain + Parent->m_boundingradius = std::max( + Parent->m_boundingradius, + m_boundingradius ); + } + } + if( Next ) Next->create_geometry( Dataoffset, Bank ); } - +#ifdef EU07_USE_OLD_GROUNDCODE // places contained geometry in provided ground node void TSubModel::convert( TGroundNode &Groundnode ) const { @@ -1063,7 +1084,7 @@ TSubModel::convert( TGroundNode &Groundnode ) const { Groundnode.fSquareRadius += squareradius; } } - +#endif void TSubModel::ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ) { // ustawienie kolorów dla modelu terenu f4Ambient = glm::vec4( Ambient, 1.0f ); @@ -1759,18 +1780,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) { @@ -1788,11 +1802,6 @@ void TModel3d::Init() } }; -void TModel3d::BreakHierarhy() -{ - Error("Not implemented yet :("); -}; - //----------------------------------------------------------------------------- // 2012-02 funkcje do tworzenia terenu z E3D //----------------------------------------------------------------------------- diff --git a/Model3d.h b/Model3d.h index 181bd93a..863c962d 100644 --- a/Model3d.h +++ b/Model3d.h @@ -50,7 +50,12 @@ enum TAnimType // rodzaj animacji at_Undefined = 0x800000FF // animacja chwilowo nieokreślona }; +#ifdef EU07_USE_OLD_GROUNDCODE class TGroundNode; +#endif +namespace scene { +class shape_node; +} class TSubModel { // klasa submodelu - pojedyncza siatka, punkt świetlny albo grupa punktów @@ -59,6 +64,7 @@ class TSubModel 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 { @@ -131,10 +137,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 +167,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 +216,9 @@ public: std::vector&); void serialize_geometry( std::ostream &Output ) const; // places contained geometry in provided ground node +#ifdef EU07_USE_OLD_GROUNDCODE void convert( TGroundNode &Groundnode ) const; - +#endif }; class TModel3d : public CMesh @@ -236,6 +238,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 +253,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; }; diff --git a/Track.cpp b/Track.cpp index dccdb40b..3266e5c0 100644 --- a/Track.cpp +++ b/Track.cpp @@ -84,16 +84,14 @@ TIsolated::TIsolated(std::string const &n, TIsolated *i) : // utworznie obwodu izolowanego. nothing to do here. }; -// TODO: put this in the cleanup routine on exit - /* - TIsolated *p=pRoot; - while (pRoot) - { - p=pRoot; - p->pNext=NULL; - delete p; - } - */ +void TIsolated::DeleteAll() { + + while( pRoot ) { + auto *next = pRoot->Next(); + delete pRoot; + pRoot = next; + } +} TIsolated * TIsolated::Find(std::string const &n) { // znalezienie obiektu albo utworzenie nowego @@ -1065,7 +1063,11 @@ bool TTrack::InMovement() return false; }; +#ifdef EU07_USE_OLD_GROUNDCODE void TTrack::RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joined) +#else +void TTrack::RaAssign( scene::basic_cell *gn, TAnimModel *am, TEvent *done, TEvent *joined ) +#endif { // Ra: wiązanie toru z modelem obrotnicy if (eType == tt_Table) { @@ -2259,7 +2261,7 @@ bool TTrack::Switch(int i, float const t, float const d) } else if (eType == tt_Table) { // blokowanie (0, szukanie torów) lub odblokowanie (1, rozłączenie) obrotnicy - if (i) + if (i) // NOTE: this condition seems opposite to intention/comment? TODO: investigate this { // 0: rozłączenie sąsiednich torów od obrotnicy if (trPrev) // jeśli jest tor od Point1 obrotnicy if (iPrevDirection) // 0:dołączony Point1, 1:dołączony Point2 @@ -2276,25 +2278,34 @@ bool TTrack::Switch(int i, float const t, float const d) fVelocity = 0.0; // AI, nie ruszaj się! if (SwitchExtension->pOwner) SwitchExtension->pOwner->RaTrackAnimAdd(this); // dodanie do listy animacyjnej + // TODO: unregister path ends in the owner cell } else { // 1: ustalenie finalnego położenia (gdy nie było animacji) RaAnimate(); // ostatni etap animowania // zablokowanie pozycji i połączenie do sąsiednich torów + // TODO: register new position of the path endpoints with the region +#ifdef EU07_USE_OLD_GROUNDCODE Global::pGround->TrackJoin(SwitchExtension->pMyNode); +#else + simulation::Region->TrackJoin( this ); +#endif if (trNext || trPrev) { fVelocity = 6.0; // jazda dozwolona - if (trPrev) - if (trPrev->fVelocity == - 0.0) // ustawienie 0 da możliwość zatrzymania AI na obrotnicy - trPrev->VelocitySet(6.0); // odblokowanie dołączonego toru do jazdy - if (trNext) - if (trNext->fVelocity == 0.0) - trNext->VelocitySet(6.0); - if (SwitchExtension->evPlus) // w starych sceneriach może nie być - Global::AddToQuery(SwitchExtension->evPlus, - NULL); // potwierdzenie wykonania (np. odpala WZ) + if( ( trPrev ) + && ( trPrev->fVelocity == 0.0 ) ) { + // ustawienie 0 da możliwość zatrzymania AI na obrotnicy + trPrev->VelocitySet( 6.0 ); // odblokowanie dołączonego toru do jazdy + } + if( ( trNext ) + && ( trNext->fVelocity == 0.0 ) ) { + trNext->VelocitySet( 6.0 ); + } + if( SwitchExtension->evPlus ) { // w starych sceneriach może nie być + // potwierdzenie wykonania (np. odpala WZ) + Global::AddToQuery( SwitchExtension->evPlus, nullptr ); + } } } SwitchExtension->CurrentIndex = i; // zapamiętanie stanu zablokowania @@ -2528,7 +2539,11 @@ TTrack * TTrack::RaAnimate() cosa = -hlen * std::cos(glm::radians(SwitchExtension->fOffset)); SwitchExtension->vTrans = ac->TransGet(); vector3 middle = +#ifdef EU07_USE_OLD_GROUNDCODE SwitchExtension->pMyNode->pCenter + +#else + SwitchExtension->pMyNode->area().center + +#endif SwitchExtension->vTrans; // SwitchExtension->Segments[0]->FastGetPoint(0.5); Segment->Init(middle + vector3(sina, 0.0, cosa), middle - vector3(sina, 0.0, cosa), 5.0); // nowy odcinek @@ -2774,6 +2789,11 @@ TTrack * TTrack::Connected(int s, double &d) const +path_table::~path_table() { + + TIsolated::DeleteAll(); +} + // legacy method, initializes tracks after deserialization from scenario file void path_table::InitTracks() { @@ -2957,3 +2977,46 @@ path_table::InitTracks() { isolated = isolated->Next(); } } + +// legacy method, sends list of occupied paths over network +void +path_table::TrackBusyList() const { + // wysłanie informacji o wszystkich zajętych odcinkach + for( auto const *path : m_items ) { + if( ( false == path->name().empty() ) // musi być nazwa + && ( false == path->Dynamics.empty() ) ) { + // zajęty + multiplayer::WyslijString( path->name(), 8 ); + } + } +} + +// legacy method, sends list of occupied path sections over network +void +path_table::IsolatedBusyList() const { + // wysłanie informacji o wszystkich odcinkach izolowanych + TIsolated *Current; + for( Current = TIsolated::Root(); Current; Current = Current->Next() ) { + + if( Current->Busy() ) { multiplayer::WyslijString( Current->asName, 11 ); } + else { multiplayer::WyslijString( Current->asName, 10 ); } + } + multiplayer::WyslijString( "none", 10 ); // informacja o końcu listy +} + +// legacy method, sends state of specified path section over network +void +path_table::IsolatedBusy( std::string const &Name ) const { + // wysłanie informacji o odcinku izolowanym (t) + // Ra 2014-06: do wyszukania użyć drzewka nazw + TIsolated *Current; + for( Current = TIsolated::Root(); Current; Current = Current->Next() ) { + if( Current->asName == Name ) { + if( Current->Busy() ) { multiplayer::WyslijString( Current->asName, 11 ); } + else { multiplayer::WyslijString( Current->asName, 10 ); } + // nie sprawdzaj dalszych + return; + } + } + multiplayer::WyslijString( Name, 10 ); // wolny (technically not found but, eh) +} diff --git a/Track.h b/Track.h index 7ff04d3f..59102064 100644 --- a/Track.h +++ b/Track.h @@ -13,11 +13,15 @@ http://mozilla.org/MPL/2.0/. #include #include -#include "scenenode.h" #include "Segment.h" #include "material.h" +#include "scenenode.h" #include "names.h" +namespace scene { +class basic_cell; +} + enum TTrackType { tt_Unknown, tt_Normal, @@ -71,7 +75,11 @@ class TSwitchExtension }; struct { // zmienne potrzebne tylko dla obrotnicy/przesuwnicy +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *pMyNode; // dla obrotnicy do wtórnego podłączania torów +#else + scene::basic_cell *pMyNode; // TODO: convert this to observer pattern +#endif // TAnimContainer *pAnim; //animator modelu dla obrotnicy TAnimModel *pModel; // na razie model }; @@ -83,7 +91,11 @@ class TSwitchExtension }; }; bool bMovement = false; // czy w trakcie animacji +#ifdef EU07_USE_OLD_GROUNDCODE TSubRect *pOwner = nullptr; // sektor, któremu trzeba zgłosić animację +#else + scene::basic_cell *pOwner = nullptr; // TODO: convert this to observer pattern +#endif TTrack *pNextAnim = nullptr; // następny tor do animowania TEvent *evPlus = nullptr, *evMinus = nullptr; // zdarzenia sygnalizacji rozprucia @@ -103,6 +115,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); + 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() { @@ -245,11 +258,20 @@ public: #endif void RenderDynSounds(); // odtwarzanie dźwięków pojazdów jest niezależne od ich wyświetlania +#ifdef EU07_USE_OLD_GROUNDCODE void RaOwnerSet(TSubRect *o) { if (SwitchExtension) SwitchExtension->pOwner = o; }; +#else + void RaOwnerSet( scene::basic_cell *o ) { + if( SwitchExtension ) { SwitchExtension->pOwner = o; } }; +#endif bool InMovement(); // czy w trakcie animacji? +#ifdef EU07_USE_OLD_GROUNDCODE void RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joined); +#else + void RaAssign( scene::basic_cell *gn, TAnimModel *am, TEvent *done, TEvent *joined ); +#endif void RaAnimListAdd(TTrack *t); TTrack * RaAnimate(); @@ -278,9 +300,19 @@ public: class path_table : public basic_table { 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; }; //--------------------------------------------------------------------------- diff --git a/World.cpp b/World.cpp index 3f127841..ab2bafe9 100644 --- a/World.cpp +++ b/World.cpp @@ -318,6 +318,8 @@ bool TWorld::Init( GLFWwindow *Window ) { Controlled = NULL; mvControlled = NULL; Camera.Type = tp_Free; + DebugCamera = Camera; + Global::DebugCameraPosition = DebugCamera.Pos; } // if (!Global::bMultiplayer) //na razie włączone @@ -543,7 +545,7 @@ void TWorld::OnKeyDown(int cKey) #ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *tmp = Ground.DynamicNearest( Camera.Pos, 50, true ); //łapiemy z obsadą #else - TDynamicObject *tmp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 50, true ) ); + TDynamicObject *tmp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 50, true, false ) ); #endif if( ( tmp != nullptr ) && ( tmp != Controlled ) ) { @@ -688,8 +690,12 @@ void TWorld::OnKeyDown(int cKey) } else if( ( cKey == GLFW_KEY_PAUSE ) && ( Global::ctrlState ) && ( Global::shiftState ) ) { //[Ctrl]+[Break] hamowanie wszystkich pojazdów w okolicy // added shift to prevent odd issue with glfw producing pause presses on its own - if (Controlled->MoverParameters->Radio) - Ground.RadioStop(Camera.Pos); + if( Controlled->MoverParameters->Radio ) +#ifdef EU07_USE_OLD_GROUNDCODE + Ground.RadioStop( Camera.Pos ); +#else + simulation::Region->RadioStop( Camera.Pos ); +#endif } else if (!Global::iPause) //||(cKey==VK_F4)) //podczas pauzy sterownaie nie działa, F4 tak if (Train) @@ -725,7 +731,7 @@ void TWorld::OnKeyDown(int cKey) #ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *temp = Global::DynamicNearest(); #else - TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false ) ); + TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, false ) ); #endif if (temp) { @@ -739,7 +745,11 @@ void TWorld::OnKeyDown(int cKey) } else if (cKey == Global::Keys[k_EndSign]) { // Ra 2014-07: zabrane z kabiny +#ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *tmp = Global::CouplerNearest(); // domyślnie wyszukuje do 20m +#else + TDynamicObject *tmp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, true ) ); +#endif if (tmp) { int CouplNr = (LengthSquared3(tmp->HeadPosition() - Camera.Pos) > @@ -772,7 +782,7 @@ void TWorld::OnKeyDown(int cKey) #ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *temp = Global::DynamicNearest(); #else - TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false ) ); + TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, false ) ); #endif if (temp) { @@ -795,7 +805,7 @@ void TWorld::OnKeyDown(int cKey) #ifdef EU07_USE_OLD_GROUNDCODE TDynamicObject *temp = Global::DynamicNearest(); #else - TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false ) ); + TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, false ) ); #endif if (temp) { @@ -835,6 +845,8 @@ void TWorld::InOutKey( bool const Near ) Train->Dynamic()->bDisplayCab = false; DistantView( Near ); } + DebugCamera = Camera; + Global::DebugCameraPosition = DebugCamera.Pos; } else { // jazda w kabinie @@ -1172,9 +1184,9 @@ TWorld::Update_Camera( double const Deltatime ) { if( !d ) d = Ground.DynamicNearest( Camera.Pos, 1000 ); // dalej szukanie, jesli bliżej nie ma #else - TDynamicObject *d = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 300, false ) ); + TDynamicObject *d = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 300, false, false ) ); if( !d ) - d = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 1000, false ) ); // dalej szukanie, jesli bliżej nie ma + d = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 1000, false, false ) ); // dalej szukanie, jesli bliżej nie ma #endif if( d && pDynamicNearest ) { // jeśli jakiś jest znaleziony wcześniej @@ -1361,7 +1373,7 @@ TWorld::Update_UI() { #ifdef EU07_USE_OLD_GROUNDCODE Ground.DynamicNearest( Camera.Pos ) : #else - std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false ) ) : + std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false, false ) ) : #endif Controlled ); // w trybie latania lokalizujemy wg mapy @@ -1446,7 +1458,7 @@ TWorld::Update_UI() { #ifdef EU07_USE_OLD_GROUNDCODE Ground.DynamicNearest( Camera.Pos ) : #else - std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false ) ) : + std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false, false ) ) : #endif Controlled ); // w trybie latania lokalizujemy wg mapy @@ -1714,7 +1726,7 @@ TWorld::Update_UI() { #ifdef EU07_USE_OLD_GROUNDCODE Ground.DynamicNearest( Camera.Pos ) : #else - std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false ) ) : + std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false, false ) ) : #endif Controlled ); // w trybie latania lokalizujemy wg mapy if( tmp == nullptr ) { @@ -1908,7 +1920,6 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) + " rcvd" ); #ifdef EU07_USE_OLD_GROUNDCODE if( Global::iMultiplayer ) { - // WriteLog("Komunikat: "+AnsiString(pRozkaz->Name1)); TEvent *e = Ground.FindEvent( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); if( e ) @@ -1918,7 +1929,6 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) } #else if( Global::iMultiplayer ) { - // WriteLog("Komunikat: "+AnsiString(pRozkaz->Name1)); auto *event = simulation::Events.FindEvent( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); if( event != nullptr ) { if( ( event->Type == tp_Multiple ) @@ -2027,7 +2037,6 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) if (t) multiplayer::WyslijNamiary(t); // wysłanie informacji o pojeździe #else -/* // TODO: re-enable when messaging component is in place auto *vehicle = ( pRozkaz->cString[ 1 ] == '*' ? @@ -2036,27 +2045,42 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) if( vehicle != nullptr ) { multiplayer::WyslijNamiary( vehicle ); // wysłanie informacji o pojeździe } -*/ #endif } else { // dla pustego wysyłamy ramki 6 z nazwami pojazdów AI (jeśli potrzebne wszystkie, to rozpoznać np. "*") +#ifdef EU07_USE_OLD_GROUNDCODE Ground.DynamicList(); +#else + simulation::Vehicles.DynamicList(); +#endif } } break; case 8: // ponowne wysłanie informacji o zajętych odcinkach toru CommLog(Now() + " " + to_string(pRozkaz->iComm) + " all busy track" + " rcvd"); - Ground.TrackBusyList(); +#ifdef EU07_USE_OLD_GROUNDCODE + Ground.TrackBusyList(); +#else + simulation::Paths.TrackBusyList(); +#endif break; case 9: // ponowne wysłanie informacji o zajętych odcinkach izolowanych CommLog(Now() + " " + to_string(pRozkaz->iComm) + " all busy isolated" + " rcvd"); - Ground.IsolatedBusyList(); +#ifdef EU07_USE_OLD_GROUNDCODE + Ground.IsolatedBusyList(); +#else + simulation::Paths.IsolatedBusyList(); +#endif break; case 10: // badanie zajętości jednego odcinka izolowanego CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd"); +#ifdef EU07_USE_OLD_GROUNDCODE Ground.IsolatedBusy(std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0]))); +#else + simulation::Paths.IsolatedBusy( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); +#endif break; case 11: // ustawienie parametrów ruchu pojazdu // Ground.IsolatedBusy(AnsiString(pRozkaz->cString+1,(unsigned)(pRozkaz->cString[0]))); @@ -2096,7 +2120,25 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) } } #else - // TODO: implement + if( pRozkaz->cString[ 1 ] ) // jeśli długość nazwy jest niezerowa + { // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #13 + auto *lookup = ( + pRozkaz->cString[ 2 ] == '*' ? + simulation::Vehicles.find( Global::asHumanCtrlVehicle ) : // nazwa pojazdu użytkownika + simulation::Vehicles.find( std::string( pRozkaz->cString + 2, (unsigned)pRozkaz->cString[ 1 ] ) ) ); // nazwa pojazdu + if( lookup == nullptr ) { break; } // nothing found, nothing to do + auto *d { lookup }; + while( d != nullptr ) { + d->Damage( pRozkaz->cString[ 0 ] ); + d = d->Next(); // pozostałe też + } + d = lookup->Prev(); + while( d != nullptr ) { + d->Damage( pRozkaz->cString[ 0 ] ); + d = d->Prev(); // w drugą stronę też + } + multiplayer::WyslijUszkodzenia( lookup->asName, lookup->MoverParameters->EngDmgFlag ); // zwrot informacji o pojeździe + } #endif break; } diff --git a/messaging.cpp b/messaging.cpp index 5024f6ee..94781707 100644 --- a/messaging.cpp +++ b/messaging.cpp @@ -93,6 +93,7 @@ WyslijWolny(const std::string &t) WyslijString(t, 4); // tor wolny } +#ifdef EU07_USE_OLD_GROUNDCODE void WyslijNamiary(TGroundNode *t) { // wysłanie informacji o pojeździe - (float), długość ramki będzie zwiększana w miarę potrzeby @@ -169,6 +170,84 @@ WyslijNamiary(TGroundNode *t) // WriteLog("Ramka poszla!"); CommLog( Now() + " " + std::to_string(r.iComm) + " " + t->asName + " sent"); } +#else +void +WyslijNamiary(TDynamicObject const *Vehicle) +{ // wysłanie informacji o pojeździe - (float), długość ramki będzie zwiększana w miarę potrzeby + // 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() diff --git a/messaging.h b/messaging.h index fd2826ac..c41dab05 100644 --- a/messaging.h +++ b/messaging.h @@ -11,7 +11,11 @@ http://mozilla.org/MPL/2.0/. #include +#ifdef EU07_USE_OLD_GROUNDCODE class TGroundNode; +#else +class TDynamicObject; +#endif namespace multiplayer { @@ -40,7 +44,11 @@ void Navigate( std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lPa void WyslijEvent( const std::string &e, const std::string &d ); void WyslijString( const std::string &t, int n ); void WyslijWolny( const std::string &t ); +#ifdef EU07_USE_OLD_GROUNDCODE void WyslijNamiary( TGroundNode *t ); +#else +void WyslijNamiary( TDynamicObject const *Vehicle ); +#endif void WyslijParam( int nr, int fl ); void WyslijUszkodzenia( const std::string &t, char fl ); void WyslijObsadzone(); // -> skladanie wielu pojazdow diff --git a/renderer.cpp b/renderer.cpp index d4a6b65e..143489df 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -349,6 +349,15 @@ opengl_renderer::Render() { void opengl_renderer::Render_pass( rendermode const Mode ) { +#ifdef EU07_USE_DEBUG_CAMERA + // setup world camera for potential visualization + setup_pass( + m_worldcamera, + rendermode::color, + 0.f, + 1.0, + true ); +#endif setup_pass( m_renderpass, Mode ); switch( m_renderpass.draw_mode ) { @@ -365,14 +374,6 @@ opengl_renderer::Render_pass( rendermode const Mode ) { #endif shadowcamera = m_renderpass.camera; // cache shadow camera placement for visualization setup_pass( m_renderpass, Mode ); // restore draw mode. TBD, TODO: render mode stack -#ifdef EU07_USE_DEBUG_CAMERA - setup_pass( - m_worldcamera, - rendermode::color, - 0.f, - 1.0, - true ); -#endif // setup shadowmap matrix m_shadowtexturematrix = //bias from [-1, 1] to [0, 1] }; @@ -383,7 +384,6 @@ opengl_renderer::Render_pass( rendermode const Mode ) { glm::mat4{ glm::mat3{ shadowcamera.modelview() } }, glm::vec3{ m_renderpass.camera.position() - shadowcamera.position() } ); } - if( ( true == m_environmentcubetexturesupport ) && ( true == World.InitPerformed() ) ) { // potentially update environmental cube map @@ -391,7 +391,6 @@ opengl_renderer::Render_pass( rendermode const Mode ) { setup_pass( m_renderpass, Mode ); // restore draw mode. TBD, TODO: render mode stack } } - ::glViewport( 0, 0, Global::iWindowWidth, Global::iWindowHeight ); if( World.InitPerformed() ) { @@ -420,7 +419,9 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glColor4f( 1.f, 0.9f, 0.8f, 1.f ); ::glDisable( GL_LIGHTING ); ::glDisable( GL_TEXTURE_2D ); - shadowcamera.draw( m_renderpass.camera.position() - shadowcamera.position() ); + if( true == Global::RenderShadows ) { + shadowcamera.draw( m_renderpass.camera.position() - shadowcamera.position() ); + } if( DebugCameraFlag ) { ::glColor4f( 0.8f, 1.f, 0.9f, 1.f ); m_worldcamera.camera.draw( m_renderpass.camera.position() - m_worldcamera.camera.position() ); @@ -1920,7 +1921,7 @@ opengl_renderer::Render( section_sequence::iterator First, section_sequence::ite // render #ifdef EU07_USE_DEBUG_CULLING // debug - float const width = scene::EU07_SECTIONSIZE * 0.5f; + float const width = section->m_area.radius; float const height = scene::EU07_SECTIONSIZE * 0.2f; glDisable( GL_LIGHTING ); glDisable( GL_TEXTURE_2D ); @@ -2008,11 +2009,10 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator // first pass draws elements which we know are located in section banks, to reduce vbo switching while( First != Last ) { - auto const *cell = First->second; -/* - // przeliczenia animacji torów w sektorze - Groundsubcell->RaAnimate( m_framestamp ); -*/ + auto *cell = First->second; + // przeliczenia animacji torów w sektorze + cell->RaAnimate( m_framestamp ); + switch( m_renderpass.draw_mode ) { case rendermode::color: { // since all shapes of the section share center point we can optimize out a few calls here @@ -2023,7 +2023,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator // render #ifdef EU07_USE_DEBUG_CULLING // debug - float const width = scene::EU07_CELLSIZE * 0.5f; + float const width = cell->m_area.radius; float const height = scene::EU07_CELLSIZE * 0.15f; glDisable( GL_LIGHTING ); glDisable( GL_TEXTURE_2D ); @@ -2149,25 +2149,8 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator void opengl_renderer::Render( scene::shape_node const &Shape, bool const Ignorerange ) { -/* - double distancesquared; - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; - break; - } - default: { - distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; - break; - } - } - if( ( distancesquared < Node->fSquareMinRadius ) - || ( distancesquared >= Node->fSquareRadius ) ) { - return false; - } -*/ - auto const &data{ Shape.data() }; + + auto const &data { Shape.data() }; if( false == Ignorerange ) { double distancesquared; @@ -3075,8 +3058,8 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { // render m_geometry.draw( Node->Piece->geometry ); -// ++m_debugstats.lines; -// ++m_debugstats.drawcalls; + ++m_debugstats.lines; + ++m_debugstats.drawcalls; // post-render cleanup ::glPopMatrix(); @@ -3183,8 +3166,9 @@ opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_seque while( first != Last ) { auto const *cell = first->second; - // TODO: include lines here - if( false == cell->m_traction.empty() ) { + + if( ( false == cell->m_traction.empty() + || ( false == cell->m_lines.empty() ) ) ) { // since all shapes of the cell share center point we can optimize out a few calls here ::glPushMatrix(); auto const originoffset { cell->m_area.center - m_renderpass.camera.position() }; @@ -3196,6 +3180,7 @@ opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_seque Bind_Material( null_handle ); // render for( auto *traction : cell->m_traction ) { Render_Alpha( traction ); } + for( auto &lines : cell->m_lines ) { Render_Alpha( lines ); } // post-render cleanup ::glLineWidth( 1.0 ); if( !Global::bSmoothTraction ) { @@ -3287,6 +3272,48 @@ opengl_renderer::Render_Alpha( TTraction *Traction ) { ++m_debugstats.traction; ++m_debugstats.drawcalls; } + +void +opengl_renderer::Render_Alpha( scene::lines_node const &Lines ) { + + auto const &data { Lines.data() }; + + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees + distancesquared = SquareMagnitude( ( data.area.center - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + default: { + distancesquared = SquareMagnitude( ( data.area.center - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + break; + } + } + if( ( distancesquared < data.rangesquared_min ) + || ( distancesquared >= data.rangesquared_max ) ) { + return; + } + // setup + auto const distance { static_cast( std::sqrt( distancesquared ) ) }; + auto const linealpha = ( + data.line_width > 0.f ? + 10.f * data.line_width + / std::max( + 0.5f * data.area.radius + 1.f, + distance - ( 0.5f * data.area.radius ) ) : + 1.f ); // negative width means the lines are always opague + ::glColor4fv( + glm::value_ptr( + glm::vec4{ + glm::vec3{ data.lighting.diffuse * Global::DayLight.ambient }, // w zaleznosci od koloru swiatla + std::min( 1.f, linealpha ) } ) ); + ::glLineWidth( clamp( 0.5f * linealpha + data.line_width * data.area.radius / 1000.f, 1.f, 8.f ) ); + // render + m_geometry.draw( data.geometry ); + ++m_debugstats.lines; + ++m_debugstats.drawcalls; +} #endif bool opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { diff --git a/renderer.h b/renderer.h index 75027568..14efad82 100644 --- a/renderer.h +++ b/renderer.h @@ -340,6 +340,8 @@ private: Render_Alpha( TAnimModel *Instance ); void Render_Alpha( TTraction *Traction ); + void + Render_Alpha( scene::lines_node const &Lines ); #endif bool Render_Alpha( TDynamicObject *Dynamic ); diff --git a/scene.cpp b/scene.cpp index e2587e7d..ec94da08 100644 --- a/scene.cpp +++ b/scene.cpp @@ -130,6 +130,44 @@ basic_cell::update_traction( TDynamicObject *Vehicle, int const Pantographindex } } +// legacy method, triggers radio-stop procedure for all vehicles located on paths in the cell +void +basic_cell::radio_stop() { + + for( auto *path : m_paths ) { + path->RadioStop(); + } +} + +// legacy method, adds specified path to the list of pieces undergoing state change +bool +basic_cell::RaTrackAnimAdd( TTrack *Track ) { + + if( false == m_geometrycreated ) { + // nie ma animacji, gdy nie widać + return true; + } + if (tTrackAnim) + tTrackAnim->RaAnimListAdd(Track); + else + tTrackAnim = Track; + return false; // będzie animowane... +} + +// legacy method, updates geometry for pieces in the animation list +void +basic_cell::RaAnimate( unsigned int const Framestamp ) { + + if( ( tTrackAnim == nullptr ) + || ( Framestamp == m_framestamp ) ) { + // nie ma nic do animowania + return; + } + tTrackAnim = tTrackAnim->RaAnimate(); // przeliczenie animacji kolejnego + + m_framestamp = Framestamp; +} + // adds provided shape to the cell void basic_cell::insert( shape_node Shape ) { @@ -160,15 +198,51 @@ basic_cell::insert( shape_node Shape ) { shapes.emplace_back( Shape ); } +// adds provided lines to the cell +void +basic_cell::insert( lines_node Lines ) { + + m_active = true; + + auto const &linesdata { Lines.data() }; + for( auto &targetlines : m_lines ) { + // try to merge shapes with matching view ranges... + auto const &targetlinesdata { targetlines.data() }; + if( ( linesdata.rangesquared_min == targetlinesdata.rangesquared_min ) + && ( linesdata.rangesquared_max == targetlinesdata.rangesquared_max ) + // ...and located close to each other (within arbitrary limit of 10m) + && ( glm::length( linesdata.area.center - targetlinesdata.area.center ) < 10.0 ) ) { + + if( true == targetlines.merge( Lines ) ) { + // if the shape was merged there's nothing left to do + return; + } + } + } + // otherwise add the shape to the relevant list + Lines.origin( m_area.center ); + m_lines.emplace_back( Lines ); +} + // adds provided path to the cell void basic_cell::insert( TTrack *Path ) { m_active = true; - // TODO: add animation hook Path->origin( m_area.center ); m_paths.emplace_back( Path ); + // animation hook +#ifndef EU07_USE_OLD_GROUNDCODE + Path->RaOwnerSet( this ); +#endif + // re-calculate cell radius, in case track extends outside the cell's boundaries + auto endpoints = Path->endpoints(); + for( auto &endpoint : endpoints ) { + m_area.radius = std::max( + m_area.radius, + glm::length( m_area.center - endpoint ) + 25.f ); // extra margin to prevent driven vehicle from flicking + } } // adds provided traction piece to the cell @@ -179,6 +253,13 @@ basic_cell::insert( TTraction *Traction ) { Traction->origin( m_area.center ); m_traction.emplace_back( Traction ); + // re-calculate cell radius, in case traction piece extends outside the cell's boundaries + auto endpoints = Traction->endpoints(); + for( auto &endpoint : endpoints ) { + m_area.radius = std::max( + m_area.radius, + glm::length( m_area.center - endpoint ) ); // adding arbitrary safety margin + } } // adds provided model instance to the cell @@ -203,6 +284,15 @@ basic_cell::insert( TAnimModel *Instance ) { // opaque pieces m_instancesopaque.emplace_back( Instance ); } + // re-calculate cell radius, in case model extends outside the cell's boundaries + if( Instance->Model() ) { + auto const modelradius{ Instance->Model()->bounding_radius() }; + if( modelradius > 0.f ) { + m_area.radius = std::max( + m_area.radius, + glm::length( m_area.center - Instance->location() ) + modelradius ); // adding arbitrary safety margin + } + } } // adds provided sound instance to the cell @@ -251,7 +341,7 @@ basic_cell::register_end( TTraction *Traction ) { // find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance std::tuple -basic_cell::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) { +basic_cell::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler ) { TDynamicObject *vehiclenearest { nullptr }; float leastdistance { std::numeric_limits::max() }; @@ -264,7 +354,16 @@ basic_cell::find( glm::dvec3 const &Point, float const Radius, bool const Onlyco && ( vehicle->Mechanik == nullptr ) ) { continue; } - distance = glm::length2( glm::dvec3{ vehicle->GetPosition() } - Point ); + if( false == Findbycoupler ) { + // basic search, checks vehicles' center points + distance = glm::length2( glm::dvec3{ vehicle->GetPosition() } - Point ); + } + else { + // alternative search, checks positions of vehicles' couplers + distance = std::min( + glm::length2( glm::dvec3{ vehicle->HeadPosition() } - Point ), + glm::length2( glm::dvec3{ vehicle->RearPosition() } - Point ) ); + } if( ( distance > distancecutoff ) || ( distance > leastdistance ) ){ continue; @@ -372,10 +471,13 @@ basic_cell::create_geometry( geometrybank_handle const &Bank ) { for( auto *path : m_paths ) { path->create_geometry( Bank ); } for( auto *traction : m_traction ) { traction->create_geometry( Bank ); } #endif + for( auto &lines : m_lines ) { lines.create_geometry( Bank ); } // arrange content by assigned materials to minimize state switching std::sort( std::begin( m_paths ), std::end( m_paths ), TTrack::sort_by_material ); + + m_geometrycreated = true; // helper for legacy animation code, get rid of it after refactoring } @@ -384,11 +486,9 @@ basic_cell::create_geometry( geometrybank_handle const &Bank ) { void basic_section::update( glm::dvec3 const &Location, float const Radius ) { - auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + Radius, 2 ) }; - for( auto &cell : m_cells ) { - if( glm::length2( cell.area().center - Location ) < squaredradii ) { + if( glm::length2( cell.area().center - Location ) < ( ( cell.area().radius + Radius ) * ( cell.area().radius + Radius ) ) ) { // we reject cells which aren't within our area of interest cell.update(); } @@ -408,16 +508,28 @@ basic_section::update_traction( TDynamicObject *Vehicle, int const Pantographind auto const pantographposition = position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x ); auto const radius { 0.0 }; // { EU07_CELLSIZE * 0.5 }; // experimentally limited, check if it has any negative effect - auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + radius, 2 ) }; for( auto &cell : m_cells ) { // we reject early cells which aren't within our area of interest - if( glm::length2( cell.area().center - pantographposition ) < squaredradii ) { + if( glm::length2( cell.area().center - pantographposition ) < ( ( cell.area().radius + radius ) * ( cell.area().radius + radius ) ) ) { cell.update_traction( Vehicle, Pantographindex ); } } } +// legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location +void +basic_section::radio_stop( glm::dvec3 const &Location, float const Radius ) { + + for( auto &cell : m_cells ) { + + if( glm::length2( cell.area().center - Location ) < ( ( cell.area().radius + Radius ) * ( cell.area().radius + Radius ) ) ) { + // we reject cells which aren't within our area of interest + cell.radio_stop(); + } + } +} + // adds provided shape to the section void basic_section::insert( shape_node Shape ) { @@ -444,9 +556,16 @@ basic_section::insert( shape_node Shape ) { } } +// adds provided lines to the section +void +basic_section::insert( lines_node Lines ) { + + cell( Lines.data().area.center ).insert( Lines ); +} + // find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance std::tuple -basic_section::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) { +basic_section::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler ) { // go through sections within radius of interest, and pick the nearest candidate TDynamicObject @@ -456,14 +575,12 @@ basic_section::find( glm::dvec3 const &Point, float const Radius, bool const Onl distancefound, distancenearest { std::numeric_limits::max() }; - auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + Radius, 2 ) }; - for( auto &cell : m_cells ) { // we reject early cells which aren't within our area of interest - if( glm::length2( cell.area().center - Point ) > squaredradii ) { + if( glm::length2( cell.area().center - Point ) > ( ( cell.area().radius + Radius ) * ( cell.area().radius + Radius ) ) ) { continue; } - std::tie( vehiclefound, distancefound ) = cell.find( Point, Radius, Onlycontrolled ); + std::tie( vehiclefound, distancefound ) = cell.find( Point, Radius, Onlycontrolled, Findbycoupler ); if( ( vehiclefound != nullptr ) && ( distancefound < distancenearest ) ) { @@ -503,11 +620,10 @@ basic_section::find( glm::dvec3 const &Point, TTraction const *Other, int const endpointnearest { -1 }; auto const radius { 0.0 }; // { EU07_CELLSIZE * 0.5 }; // experimentally limited, check if it has any negative effect - auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + radius, 2 ) }; for( auto &cell : m_cells ) { // we reject early cells which aren't within our area of interest - if( glm::length2( cell.area().center - Point ) > squaredradii ) { + if( glm::length2( cell.area().center - Point ) > ( ( cell.area().radius + radius ) * ( cell.area().radius + radius ) ) ) { continue; } std::tie( tractionfound, endpointfound, distancefound ) = cell.find( Point, Other, Currentdirection ); @@ -618,8 +734,49 @@ basic_region::update_traction( TDynamicObject *Vehicle, int const Pantographinde } } +// legacy method, links specified path piece with potential neighbours void -basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad ) { +basic_region::TrackJoin( TTrack *Track ) { + // wyszukiwanie sąsiednich torów do podłączenia (wydzielone na użytek obrotnicy) + TTrack *matchingtrack; + int endpointid; + if( Track->CurrentPrev() == nullptr ) { + std::tie( matchingtrack, endpointid ) = find_path( Track->CurrentSegment()->FastGetPoint_0(), Track ); + switch( endpointid ) { + case 0: + Track->ConnectPrevPrev( matchingtrack, 0 ); + break; + case 1: + Track->ConnectPrevNext( matchingtrack, 1 ); + break; + } + } + if( Track->CurrentNext() == nullptr ) { + std::tie( matchingtrack, endpointid ) = find_path( Track->CurrentSegment()->FastGetPoint_1(), Track ); + switch( endpointid ) { + case 0: + Track->ConnectNextPrev( matchingtrack, 0 ); + break; + case 1: + Track->ConnectNextNext( matchingtrack, 1 ); + break; + } + } +} + +// legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location +void +basic_region::RadioStop( glm::dvec3 const &Location ) { + + auto const range = 2000.f; + auto const §ionlist = sections( Location, range ); + for( auto *section : sectionlist ) { + section->radio_stop( Location, range ); + } +} + +void +basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad, bool const Transform ) { // shape might need to be split into smaller pieces, so we create list of nodes instead of just single one // using deque so we can do single pass iterating and addding generated pieces without invalidating anything @@ -628,38 +785,41 @@ basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad ) { if( shape.m_data.vertices.empty() ) { return; } // adjust input if necessary: - if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) { - // rotate... - auto const rotation = glm::radians( Scratchpad.location_rotation ); - for( auto &vertex : shape.m_data.vertices ) { - vertex.position = glm::rotateZ( vertex.position, rotation.z ); - vertex.position = glm::rotateX( vertex.position, rotation.x ); - vertex.position = glm::rotateY( vertex.position, rotation.y ); - vertex.normal = glm::rotateZ( vertex.normal, rotation.z ); - vertex.normal = glm::rotateX( vertex.normal, rotation.x ); - vertex.normal = glm::rotateY( vertex.normal, rotation.y ); + if( true == Transform ) { + // shapes generated from legacy terrain come with world space coordinates and don't need processing + if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) { + // rotate... + auto const rotation = glm::radians( Scratchpad.location_rotation ); + for( auto &vertex : shape.m_data.vertices ) { + vertex.position = glm::rotateZ( vertex.position, rotation.z ); + vertex.position = glm::rotateX( vertex.position, rotation.x ); + vertex.position = glm::rotateY( vertex.position, rotation.y ); + vertex.normal = glm::rotateZ( vertex.normal, rotation.z ); + vertex.normal = glm::rotateX( vertex.normal, rotation.x ); + vertex.normal = glm::rotateY( vertex.normal, rotation.y ); + } } - } - if( ( false == Scratchpad.location_offset.empty() ) - && ( Scratchpad.location_offset.top() != glm::dvec3( 0, 0, 0 ) ) ) { - // ...and move - auto const offset = Scratchpad.location_offset.top(); - for( auto &vertex : shape.m_data.vertices ) { - vertex.position += offset; + if( ( false == Scratchpad.location_offset.empty() ) + && ( Scratchpad.location_offset.top() != glm::dvec3( 0, 0, 0 ) ) ) { + // ...and move + auto const offset = Scratchpad.location_offset.top(); + for( auto &vertex : shape.m_data.vertices ) { + vertex.position += offset; + } } - } - // calculate bounding area - for( auto const &vertex : shape.m_data.vertices ) { - shape.m_data.area.center += vertex.position; - } - shape.m_data.area.center /= shape.m_data.vertices.size(); - // trim the shape if needed. trimmed parts will be added to list as separate nodes - for( std::size_t index = 0; index < shapes.size(); ++index ) { - while( true == RaTriangleDivider( shapes[ index ], shapes ) ) { - ; // all work is done during expression check + // calculate bounding area + for( auto const &vertex : shape.m_data.vertices ) { + shape.m_data.area.center += vertex.position; + } + shape.m_data.area.center /= shape.m_data.vertices.size(); + // trim the shape if needed. trimmed parts will be added to list as separate nodes + for( std::size_t index = 0; index < shapes.size(); ++index ) { + while( true == RaTriangleDivider( shapes[ index ], shapes ) ) { + ; // all work is done during expression check + } + // with the trimming done we can calculate shape's bounding radius + shape.compute_radius(); } - // with the trimming done we can calculate shape's bounding radius - shape.compute_radius(); } // move the data into appropriate section(s) for( auto &shape : shapes ) { @@ -679,6 +839,50 @@ basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad ) { } } +// inserts provided lines in the region +void +basic_region::insert_lines( lines_node Lines, scratch_data &Scratchpad ) { + + if( Lines.m_data.vertices.empty() ) { return; } + // transform point coordinates if needed + if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) { + // rotate... + auto const rotation = glm::radians( Scratchpad.location_rotation ); + for( auto &vertex : Lines.m_data.vertices ) { + vertex.position = glm::rotateZ( vertex.position, rotation.z ); + vertex.position = glm::rotateX( vertex.position, rotation.x ); + vertex.position = glm::rotateY( vertex.position, rotation.y ); + } + } + if( ( false == Scratchpad.location_offset.empty() ) + && ( Scratchpad.location_offset.top() != glm::dvec3( 0, 0, 0 ) ) ) { + // ...and move + auto const offset = Scratchpad.location_offset.top(); + for( auto &vertex : Lines.m_data.vertices ) { + vertex.position += offset; + } + } + // calculate bounding area + for( auto const &vertex : Lines.m_data.vertices ) { + Lines.m_data.area.center += vertex.position; + } + Lines.m_data.area.center /= Lines.m_data.vertices.size(); + Lines.compute_radius(); + // move the data into appropriate section + if( point_inside( Lines.m_data.area.center ) ) { + // NOTE: nodes placed outside of region boundaries are discarded + section( Lines.m_data.area.center ).insert( Lines ); + } + else { + ErrorLog( + "Bad scenario: lines node" + ( + Lines.m_name.empty() ? + "" : + " \"" + Lines.m_name + "\"" ) + + " placed in location outside region bounds (" + to_string( Lines.m_data.area.center ) + ")" ); + } +} + // inserts provided track in the region void basic_region::insert_path( TTrack *Path, scratch_data &Scratchpad ) { @@ -776,7 +980,7 @@ basic_region::insert_launcher( TEventLauncher *Launcher, scratch_data &Scratchpa // find a vehicle located neares to specified location, within specified radius, optionally discarding vehicles without drivers std::tuple -basic_region::find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) { +basic_region::find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler ) { auto const §ionlist = sections( Point, Radius ); // go through sections within radius of interest, and pick the nearest candidate @@ -788,7 +992,7 @@ basic_region::find_vehicle( glm::dvec3 const &Point, float const Radius, bool co nearestdistance { std::numeric_limits::max() }; for( auto *section : sectionlist ) { - std::tie( foundvehicle, founddistance ) = section->find( Point, Radius, Onlycontrolled ); + std::tie( foundvehicle, founddistance ) = section->find( Point, Radius, Onlycontrolled, Findbycoupler ); if( ( foundvehicle != nullptr ) && ( founddistance < nearestdistance ) ) { diff --git a/scene.h b/scene.h index 05cfa1d3..999d0514 100644 --- a/scene.h +++ b/scene.h @@ -61,9 +61,21 @@ public: // 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 ); @@ -85,9 +97,9 @@ public: // 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, optionally ignoring vehicles without drivers. reurns: located vehicle and distance + // find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance std::tuple - find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ); + 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 find( glm::dvec3 const &Point, TTrack const *Exclude ); @@ -111,19 +123,19 @@ public: private: // types using shapenode_sequence = std::vector; + using linesnode_sequence = std::vector; using path_sequence = std::vector; -// using path_set = std::unordered_set; using traction_sequence = std::vector; -// using traction_set = std::unordered_set; using instance_sequence = std::vector; using sound_sequence = std::vector; using eventlauncher_sequence = std::vector; // members - scene::bounding_area m_area { glm::dvec3(), static_cast( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) }; + scene::bounding_area m_area { glm::dvec3(), static_cast( 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; @@ -135,6 +147,10 @@ private: 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 @@ -150,22 +166,33 @@ public: // 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 void insert( Type_ *Node ) { - cell( Node->location() ).insert( 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, + 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 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, optionally ignoring vehicles without drivers. reurns: located vehicle and distance + // find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance std::tuple - find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ); + 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 find( glm::dvec3 const &Point, TTrack const *Exclude ); @@ -223,9 +250,18 @@ public: // legacy method, finds and assigns traction piece to specified pantograph of provided vehicle void update_traction( TDynamicObject *Vehicle, int const Pantographindex ); + // 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 ); + 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, scratch_data &Scratchpad ); @@ -241,9 +277,9 @@ public: // 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, optionally ignoring vehicles without drivers. reurns: located vehicle and distance + // find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance std::tuple - find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ); + 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 find_path( glm::dvec3 const &Point, TTrack const *Exclude ); diff --git a/scenenode.cpp b/scenenode.cpp index 1be3d643..f72b1cb2 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -207,6 +207,65 @@ shape_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) { 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::max(); + + if( Submodel->m_geometry == null_handle ) { return *this; } + + int vertexcount { 0 }; + std::vector 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, + 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 ) { @@ -260,6 +319,151 @@ shape_node::compute_radius() { } + +// 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::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(); + 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(); + + } 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( 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().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( std::sqrt( squaredradius ) ); +} + + + /* memory_node & memory_node::deserialize( cParser &Input, node_data const &Nodedata ) { @@ -279,6 +483,8 @@ memory_node::deserialize( cParser &Input, node_data const &Nodedata ) { */ } // scene + + namespace editor { basic_node::basic_node( scene::node_data const &Nodedata ) : diff --git a/scenenode.h b/scenenode.h index 99e44f23..4b23d1ce 100644 --- a/scenenode.h +++ b/scenenode.h @@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/. #include "vertex.h" #include "openglgeometrybank.h" #include "parser.h" +#include "model3d.h" struct lighting_data { @@ -104,6 +105,9 @@ public: // 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 ); @@ -132,6 +136,59 @@ private: 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 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 diff --git a/simulation.cpp b/simulation.cpp index b45c5532..4d286e06 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -352,7 +352,29 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad if( nodedata.range_min < 0.0 ) { // convert and import 3d terrain - // TODO: implement this + 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 @@ -372,14 +394,20 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad || ( nodedata.type == "triangle_strip" ) || ( nodedata.type == "triangle_fan" ) ) { - simulation::Region->insert_shape( scene::shape_node().deserialize( Input, nodedata ), Scratchpad ); + simulation::Region->insert_shape( + scene::shape_node().deserialize( + Input, nodedata ), + Scratchpad, + true ); } else if( ( nodedata.type == "lines" ) || ( nodedata.type == "line_strip" ) || ( nodedata.type == "line_loop" ) ) { - // TODO: implement - skip_until( Input, "endline" ); + simulation::Region->insert_lines( + scene::lines_node().deserialize( + Input, nodedata ), + Scratchpad ); } else if( nodedata.type == "memcell" ) { From 7ff873178d1091a7e47deac9834366dac795c185 Mon Sep 17 00:00:00 2001 From: milek7 Date: Sun, 22 Oct 2017 14:48:41 +0200 Subject: [PATCH 13/31] multiple parser substitutions in one token --- parser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/parser.cpp b/parser.cpp index c570369f..04931fb4 100644 --- a/parser.cpp +++ b/parser.cpp @@ -156,9 +156,8 @@ std::string cParser::readToken(bool ToLower, const char *Break) token = mIncludeParser->readToken(ToLower, Break); if (!token.empty()) { - pos = token.find("(p"); // check if the token is a parameter which should be replaced with stored true value - if (pos != std::string::npos) //!=npos to znalezione + 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 From 7ad760821ffb2c3f9ecde6ded49e61f30ecd5dca Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sun, 22 Oct 2017 19:19:16 +0200 Subject: [PATCH 14/31] build 1020: error fixes and backward compatibility improvements --- AnimModel.cpp | 2 +- AnimModel.h | 3 +- Driver.cpp | 41 ++++++++-- DynObj.cpp | 9 ++- DynObj.h | 3 + Ground.cpp | 11 +-- Ground.h | 5 +- Segment.cpp | 4 +- Track.cpp | 131 ++++++++++++++++++++++++++++++- Traction.cpp | 203 +++++++++++++++++++++++++++++++------------------ Traction.h | 4 +- World.cpp | 9 ++- World.h | 2 + renderer.cpp | 8 +- scene.cpp | 11 ++- simulation.cpp | 70 +++++++---------- version.h | 2 +- 17 files changed, 369 insertions(+), 149 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index ae83516b..07f4aa94 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -791,7 +791,7 @@ void TAnimModel::LightSet(int n, float v) 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 = 0.25; } // standardowy próg zaplania + else { fDark = DefaultDarkThresholdLevel; } // standardowy próg zaplania break; } } diff --git a/AnimModel.h b/AnimModel.h index a1528cdd..3ced5617 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/. #include "DynObj.h" const int iMaxNumLights = 8; +float const DefaultDarkThresholdLevel { 0.325f }; // typy stanu świateł enum TLightState @@ -174,7 +175,7 @@ private: std::string asText; // tekst dla wyświetlacza znakowego TAnimAdvanced *pAdvanced { nullptr }; TLightState lsLights[ iMaxNumLights ]; - float fDark { 0.25f }; // poziom zapalanie światła (powinno być chyba powiązane z danym światłem?) + 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 diff --git a/Driver.cpp b/Driver.cpp index 8746c591..0fb10aa5 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -1268,7 +1268,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 @@ -2412,7 +2412,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. @@ -2427,9 +2427,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( @@ -2986,8 +3001,7 @@ void TController::RecognizeCommand() } 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 +{ // wysłanie komendy przez event PutValues, jak pojazd ma obsadę, to wysyła tutaj, a nie do pojazdu bezpośrednio #ifdef EU07_USE_OLD_GROUNDCODE vector3 sl; sl.x = -NewLocation.X; // zamiana na współrzędne scenerii @@ -2995,7 +3009,7 @@ void TController::PutCommand(std::string NewCommand, double NewValue1, double Ne sl.y = NewLocation.Z; #else // zamiana na współrzędne scenerii - glm::dvec3 sl { -NewLocation.X, NewLocation.Y, NewLocation.Z }; + glm::dvec3 sl { -NewLocation.X, NewLocation.Z, NewLocation.Y }; #endif if (!PutCommand(NewCommand, NewValue1, NewValue2, &sl, reason)) mvOccupied->PutCommand(NewCommand, NewValue1, NewValue2, NewLocation); @@ -3840,8 +3854,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 { @@ -4329,7 +4346,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 { @@ -4372,7 +4394,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 ) ); } } } diff --git a/DynObj.cpp b/DynObj.cpp index b4ec1427..72479e77 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -25,7 +25,6 @@ http://mozilla.org/MPL/2.0/. #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 @@ -49,6 +48,7 @@ std::string const TDynamicObject::MED_labels[] = { "masa: ", "amax: ", "Fzad: ", "FmPN: ", "FmED: ", "FrED: ", "FzPN: ", "nPrF: " }; +bool TDynamicObject::bDynamicRemove { false }; //--------------------------------------------------------------------------- void TAnimPant::AKP_4E() @@ -2922,7 +2922,7 @@ bool TDynamicObject::Update(double dt, double dt1) 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; @@ -5364,6 +5364,7 @@ vehicle_table::update( double Deltatime, int Iterationcount ) { // 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 ) { // TODO: re-implement @@ -5395,7 +5396,7 @@ vehicle_table::update( double Deltatime, int Iterationcount ) { } /* // TODO: re-implement - if (bDynamicRemove) + 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) @@ -5403,7 +5404,7 @@ vehicle_table::update( double Deltatime, int Iterationcount ) { DynamicRemove(Current->DynamicObject); // usunięcie tego i podłączonych Current = nRootDynamic; // sprawdzanie listy od początku } - bDynamicRemove = false; // na razie koniec + TDynamicObject::bDynamicRemove = false; // na razie koniec } */ } diff --git a/DynObj.h b/DynObj.h index 931ba255..fbdbd0da 100644 --- a/DynObj.h +++ b/DynObj.h @@ -151,6 +151,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 diff --git a/Ground.cpp b/Ground.cpp index c628faa3..74f76aa1 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -435,12 +435,13 @@ void TSubRect::LoadNodes() { node = node->nNext2; // następny z sektora } } - +#ifdef EU07_USE_OLD_GROUNDCODE void TSubRect::RenderSounds() { // aktualizacja dźwięków w pojazdach sektora (sektor może nie być wyświetlany) for (int j = 0; j < iTracks; ++j) tTracks[j]->RenderDynSounds(); // dźwięki pojazdów idą niezależnie od wyświetlania }; +#endif //--------------------------------------------------------------------------- //------------------ Kwadrat kilometrowy ------------------------------------ //--------------------------------------------------------------------------- @@ -2723,7 +2724,7 @@ void TGround::InitTraction() if (!Traction->hvParallel) ErrorLog("Missed overhead: " + Traction->asParallel); // logowanie braku } - if (Traction->iTries > 0) // jeśli zaznaczony do podłączenia + if (Traction->iTries == 5) // jeśli zaznaczony do podłączenia // if (!nCurrent->hvTraction->psPower[0]||!nCurrent->hvTraction->psPower[1]) if (zg < iConnection) // zabezpieczenie nEnds[zg++] = nCurrent; // wypełnianie tabeli końców w celu szukania im połączeń @@ -3466,7 +3467,7 @@ bool TGround::Update(double dt, int iter) for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext) Current->DynamicObject->Update(dt, dt); // Ra 2015-01: tylko tu przelicza sieć trakcyjną } - if (bDynamicRemove) + 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) @@ -3474,7 +3475,7 @@ bool TGround::Update(double dt, int iter) DynamicRemove(Current->DynamicObject); // usunięcie tego i podłączonych Current = nRootDynamic; // sprawdzanie listy od początku } - bDynamicRemove = false; // na razie koniec + TDynamicObject::bDynamicRemove = false; // na razie koniec } return true; }; @@ -3985,7 +3986,6 @@ void TGround::IsolatedBusy(const std::string t) } multiplayer::WyslijString(t, 10); // wolny }; -#endif //--------------------------------------------------------------------------- void TGround::Silence(vector3 gdzie) @@ -4006,4 +4006,5 @@ void TGround::Silence(vector3 gdzie) tmp->RenderSounds(); // dźwięki pojazdów by się przydało wyłączyć } }; +#endif //--------------------------------------------------------------------------- diff --git a/Ground.h b/Ground.h index 8844752d..8eba653b 100644 --- a/Ground.h +++ b/Ground.h @@ -162,8 +162,8 @@ class TSubRect : public CMesh #ifdef EU07_USE_OLD_GROUNDCODE bool RaTrackAnimAdd(TTrack *t); // zgłoszenie toru do animacji void RaAnimate( unsigned int const Framestamp ); // przeliczenie animacji torów -#endif void RenderSounds(); // dźwięki pojazdów z niewidocznych sektorów +#endif }; // Ra: trzeba sprawdzić wydajność siatki @@ -243,7 +243,6 @@ class TGround #endif public: - bool bDynamicRemove = false; // czy uruchomić procedurę usuwania pojazdów TGround(); ~TGround(); @@ -308,7 +307,9 @@ public: void DynamicRemove(TDynamicObject *dyn); void TerrainRead(std::string const &f); void TerrainWrite(); +#ifdef EU07_USE_OLD_GROUNDCODE void Silence(vector3 gdzie); +#endif }; //--------------------------------------------------------------------------- diff --git a/Segment.cpp b/Segment.cpp index 293daf57..b8509c0d 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -103,7 +103,7 @@ bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, M fStep = fNewStep; if (fLength <= 0) { - ErrorLog( "Bad geometry: zero length spline \"" + pOwner->name() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" ); + 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ć @@ -205,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->name() + "\" (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; }; diff --git a/Track.cpp b/Track.cpp index 3266e5c0..6d5cc64e 100644 --- a/Track.cpp +++ b/Track.cpp @@ -185,6 +185,7 @@ TTrack::sort_by_material( TTrack const *Left, TTrack const *Right ) { TTrack * TTrack::Create400m(int what, double dx) { // tworzenie toru do wstawiania taboru podczas konwersji na E3D +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *tmp = new TGroundNode(TP_TRACK); // node TTrack *trk = tmp->pTrack; trk->m_visible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur @@ -195,11 +196,22 @@ TTrack * TTrack::Create400m(int what, double dx) TSubRect *r = Global::pGround->GetSubRect(tmp->pCenter.x, tmp->pCenter.z); r->NodeAdd(tmp); // dodanie toru do segmentu r->Sort(); //żeby wyświetlał tabor z dodanego toru +#else + auto *trk = new TTrack( "auto_400m" ); + trk->m_visible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur + trk->iCategoryFlag = what; // taki sam typ plus informacja, że dodatkowy + trk->Init(); // utworzenie segmentu + trk->Segment->Init( vector3( -dx, 0, 0 ), vector3( -dx, 0, 400 ), 10.0, 0, 0 ); // prosty + trk->location( glm::dvec3{ -dx, 0, 200 } ); //środek, aby się mogło wyświetlić + simulation::Paths.insert( trk ); + simulation::Region->insert_path( trk, scene::scratch_data() ); +#endif return trk; }; TTrack * TTrack::NullCreate(int dir) { // tworzenie toru wykolejającego od strony (dir), albo pętli dla samochodów +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *tmp = new TGroundNode( TP_TRACK ); TGroundNode *tmp2 = nullptr; // node TTrack *trk = tmp->pTrack; // tor; UWAGA! obrotnica może generować duże ilości tego @@ -298,6 +310,100 @@ TTrack * TTrack::NullCreate(int dir) r->NodeAdd( tmp2 ); // drugiego też r->Sort(); //żeby wyświetlał tabor z dodanego toru } +#else + TTrack + *trk { nullptr }, + *trk2 { nullptr }; + trk = new TTrack( "auto_null" ); + trk->m_visible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur + trk->iCategoryFlag = (iCategoryFlag & 15) | 0x80; // taki sam typ plus informacja, że dodatkowy + float r1, r2; + Segment->GetRolls(r1, r2); // pobranie przechyłek na początku toru + vector3 p1, cv1, cv2, p2; // będziem tworzyć trajektorię lotu + if (iCategoryFlag & 1) + { // tylko dla kolei + trk->iDamageFlag = 128; // wykolejenie + trk->fVelocity = 0.0; // koniec jazdy + trk->Init(); // utworzenie segmentu + switch (dir) + { //łączenie z nowym torem + case 0: + p1 = Segment->FastGetPoint_0(); + p2 = p1 - 450.0 * Normalize(Segment->GetDirection1()); + // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + trk->Segment->Init(p1, p2, 5, -RadToDeg(r1), 70.0); + ConnectPrevPrev(trk, 0); + break; + case 1: + p1 = Segment->FastGetPoint_1(); + p2 = p1 - 450.0 * Normalize(Segment->GetDirection2()); + // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + trk->Segment->Init(p1, p2, 5, RadToDeg(r2), 70.0); + ConnectNextPrev(trk, 0); + break; + case 3: // na razie nie możliwe + p1 = SwitchExtension->Segments[1]->FastGetPoint_1(); // koniec toru drugiego zwrotnicy + p2 = p1 - 450.0 * Normalize( SwitchExtension->Segments[1]->GetDirection2()); // przedłużenie na wprost + trk->Segment->Init(p1, p2, 5, RadToDeg(r2), 70.0); // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + ConnectNextPrev(trk, 0); + // trk->ConnectPrevNext(trk,dir); + SetConnections(1); // skopiowanie połączeń + Switch(1); // bo się przełączy na 0, a to coś chce się przecież wykoleić na bok + break; // do drugiego zwrotnicy... nie zadziała? + } + } + else + { // tworznie pętelki dla samochodów + trk->fVelocity = 20.0; // zawracanie powoli + trk->fRadius = 20.0; // promień, aby się dodawało do tabelki prędkości i liczyło narastająco + trk->Init(); // utworzenie segmentu + trk2 = new TTrack( "auto_null" ); + trk2->iCategoryFlag = + (iCategoryFlag & 15) | 0x80; // taki sam typ plus informacja, że dodatkowy + trk2->m_visible = false; + trk2->fVelocity = 20.0; // zawracanie powoli + trk2->fRadius = 20.0; // promień, aby się dodawało do tabelki prędkości i liczyło narastająco + trk2->Init(); // utworzenie segmentu + trk->m_name = m_name + ":loopstart"; + trk2->m_name = m_name + ":loopfinish"; + switch (dir) + { //łączenie z nowym torem + case 0: + p1 = Segment->FastGetPoint_0(); + cv1 = -20.0 * Normalize(Segment->GetDirection1()); // pierwszy wektor kontrolny + p2 = p1 + cv1 + cv1; // 40m + // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + trk->Segment->Init(p1, p1 + cv1, p2 + vector3(-cv1.z, cv1.y, cv1.x), p2, 2, -RadToDeg(r1), 0.0); + ConnectPrevPrev(trk, 0); + // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + trk2->Segment->Init(p1, p1 + cv1, p2 + vector3(cv1.z, cv1.y, -cv1.x), p2, 2, -RadToDeg(r1), 0.0); + trk2->iPrevDirection = 0; // zwrotnie do tego samego odcinka + break; + case 1: + p1 = Segment->FastGetPoint_1(); + cv1 = -20.0 * Normalize(Segment->GetDirection2()); // pierwszy wektor kontrolny + p2 = p1 + cv1 + cv1; + // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + trk->Segment->Init(p1, p1 + cv1, p2 + vector3(-cv1.z, cv1.y, cv1.x), p2, 2, RadToDeg(r2), 0.0); + ConnectNextPrev(trk, 0); + // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + trk2->Segment->Init(p1, p1 + cv1, p2 + vector3(cv1.z, cv1.y, -cv1.x), p2, 2, RadToDeg(r2), 0.0); + trk2->iPrevDirection = 1; // zwrotnie do tego samego odcinka + break; + } + trk2->trPrev = this; + trk->ConnectNextNext(trk2, 1); // połączenie dwóch dodatkowych odcinków punktami 2 + } + // trzeba jeszcze dodać do odpowiedniego segmentu, aby się renderowały z niego pojazdy + trk->location( glm::dvec3{ 0.5 * ( p1 + p2 ) } ); //środek, aby się mogło wyświetlić + simulation::Paths.insert( trk ); + simulation::Region->insert_path( trk, scene::scratch_data() ); + if( trk2 ) { + trk2->location( trk->location() ); // ten sam środek jest + simulation::Paths.insert( trk2 ); + simulation::Region->insert_path( trk2, scene::scratch_data() ); + } +#endif return trk; }; @@ -473,7 +579,20 @@ void TTrack::Load(cParser *parser, vector3 pOrigin) null_handle : GfxRenderer.Fetch_Material( str ) ); parser->getTokens(3); - *parser >> fTexHeight1 >> fTexWidth >> fTexSlope; + *parser + >> fTexHeight1 + >> fTexWidth + >> fTexSlope; + if( fTexLength != 4.f ) { + // force defaults for malformed track definitions + bool paramsvalid { true }; + if( fTexHeight1 == 0.f ) { fTexHeight1 = 0.6f; paramsvalid = false; } + if( fTexWidth == 0.f ) { fTexWidth = 0.9f; paramsvalid = false; } + if( fTexSlope == 0.f ) { fTexSlope = 0.9f; paramsvalid = false; } + if( false == paramsvalid ) { + ErrorLog( "Bad track: one or more of texture dimensions set to 0 for track \"" + name() + "\" in file \"" + parser->Name() + "\" (line " + std::to_string( parser->Line() - 1 ) + ")" ); + } + } if (iCategoryFlag & 4) fTexHeight1 = -fTexHeight1; // rzeki mają wysokość odwrotnie niż drogi } @@ -2167,6 +2286,9 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { void TTrack::EnvironmentSet() { // ustawienie zmienionego światła +#ifdef EU07_USE_OLD_GROUNDCODE + ::glColor4f( 1.f, 1.f, 1.f, 1.f ); +#endif switch( eEnvironment ) { case e_canyon: { Global::DayLight.apply_intensity( 0.4f ); @@ -2801,7 +2923,14 @@ path_table::InitTracks() { int connection { -1 }; TTrack *matchingtrack { nullptr }; +#ifdef EU07_IGNORE_LEGACYPROCESSINGORDER for( auto *track : 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 *track = *first; +#endif track->AssignEvents( simulation::Events.FindEvent( track->asEvent0Name ), diff --git a/Traction.cpp b/Traction.cpp index 75686a97..8bab89bb 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -374,55 +374,59 @@ TTraction::create_geometry( geometrybank_handle const &Bank ) { 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 @@ -585,30 +589,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; } @@ -618,6 +633,46 @@ 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; } @@ -660,18 +715,21 @@ traction_table::InitTraction() { } } +#ifdef EU07_IGNORE_LEGACYPROCESSINGORDER for( auto *traction : m_items ) { - - if (!traction->hvNext[0]) { +#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: { - traction->Connect( 0, matchingtraction, 0 ); - break; - } + case 0: case 1: { - traction->Connect( 0, matchingtraction, 1 ); + traction->Connect( 0, matchingtraction, connection ); break; } default: { @@ -691,8 +749,8 @@ traction_table::InitTraction() { //(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 ) ) { + 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 } @@ -704,16 +762,13 @@ traction_table::InitTraction() { } } } - if (!traction->hvNext[1]) { + 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: { - traction->Connect( 1, matchingtraction, 0 ); - break; - } + case 0: case 1: { - traction->Connect( 1, matchingtraction, 1 ); + traction->Connect( 1, matchingtraction, connection ); break; } default: { @@ -770,7 +825,7 @@ traction_table::InitTraction() { if( ( traction->asParallel == "none" ) || ( traction->asParallel == "*" ) ) { // jeśli nieokreślone - traction->iLast = 2; // jakby przedostatni - niech po prostu szuka (iLast już przeliczone) + 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 @@ -792,7 +847,7 @@ traction_table::InitTraction() { } } } - if( traction->iTries > 0 ) { + 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 ); diff --git a/Traction.h b/Traction.h index fab4909d..d1202768 100644 --- a/Traction.h +++ b/Traction.h @@ -27,7 +27,7 @@ class TTraction : public editor::basic_node { 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 @@ -46,7 +46,7 @@ class TTraction : public editor::basic_node { 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; diff --git a/World.cpp b/World.cpp index ab2bafe9..80757136 100644 --- a/World.cpp +++ b/World.cpp @@ -465,7 +465,11 @@ void TWorld::OnKeyDown(int cKey) } else // również przeskakiwanie { // Ra: to z tą kamerą (Camera.Pos i Global::pCameraPosition) jest trochę bez sensu +#ifdef EU07_USE_OLD_GROUNDCODE + // NOTE: does it even work? it seems to render the sounds before the camera is actually moved + // so the listener is still in range... Ground.Silence( Global::pCameraPosition ); // wyciszenie wszystkiego z poprzedniej pozycji +#endif Global::SetCameraPosition( Global::FreeCameraInit[i] ); // nowa pozycja dla generowania obiektów Camera.Init(Global::FreeCameraInit[i], Global::FreeCameraInitAngle[i]); // przestawienie @@ -918,8 +922,9 @@ void TWorld::FollowView(bool wycisz) { if (Controlled) // jest pojazd do prowadzenia? { +#ifdef EU07_USE_OLD_GROUNDCODE Ground.Silence( Camera.Pos ); // wyciszenie dźwięków z poprzedniej pozycji - +#endif if (FreeFlyModeFlag) { // jeżeli poza kabiną, przestawiamy w jej okolicę - OK if( Train ) { @@ -937,7 +942,9 @@ void TWorld::FollowView(bool wycisz) { if( wycisz ) { // wyciszenie dźwięków z poprzedniej pozycji // trzymanie prawego w kabinie daje marny efekt +#ifdef EU07_USE_OLD_GROUNDCODE Ground.Silence( Camera.Pos ); +#endif } Camera.Pos = Train->pMechPosition; Camera.Roll = std::atan(Train->pMechShake.x * Train->fMechRoll); // hustanie kamery na boki diff --git a/World.h b/World.h index 68d75ce6..a646e9ad 100644 --- a/World.h +++ b/World.h @@ -122,7 +122,9 @@ private: TCamera Camera; TCamera DebugCamera; +#ifdef EU07_USE_OLD_GROUNDCODE TGround Ground; +#endif world_environment Environment; TTrain *Train; TDynamicObject *pDynamicNearest; diff --git a/renderer.cpp b/renderer.cpp index 143489df..d4e8929b 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -1921,8 +1921,9 @@ opengl_renderer::Render( section_sequence::iterator First, section_sequence::ite // render #ifdef EU07_USE_DEBUG_CULLING // debug + ::glLineWidth( 2.f ); float const width = section->m_area.radius; - float const height = scene::EU07_SECTIONSIZE * 0.2f; + float const height = section->m_area.radius * 0.2f; glDisable( GL_LIGHTING ); glDisable( GL_TEXTURE_2D ); glColor3ub( 255, 128, 128 ); @@ -1947,6 +1948,7 @@ opengl_renderer::Render( section_sequence::iterator First, section_sequence::ite glColor3ub( 255, 255, 255 ); glEnable( GL_TEXTURE_2D ); glEnable( GL_LIGHTING ); + glLineWidth( 1.f ); #endif // shapes for( auto const &shape : section->m_shapes ) { Render( shape, true ); } @@ -2024,7 +2026,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator #ifdef EU07_USE_DEBUG_CULLING // debug float const width = cell->m_area.radius; - float const height = scene::EU07_CELLSIZE * 0.15f; + float const height = cell->m_area.radius * 0.15f; glDisable( GL_LIGHTING ); glDisable( GL_TEXTURE_2D ); glColor3ub( 255, 255, 0 ); @@ -3253,7 +3255,7 @@ opengl_renderer::Render_Alpha( TTraction *Traction ) { } // rysuj jesli sa druty i nie zerwana if( ( Traction->Wires == 0 ) - || ( true == TestFlag( Traction->DamageFlag, 128 ) ) ) { + || ( true == TestFlag( Traction->DamageFlag, 128 ) ) ) { return; } // setup diff --git a/scene.cpp b/scene.cpp index ec94da08..4cc80791 100644 --- a/scene.cpp +++ b/scene.cpp @@ -65,7 +65,7 @@ basic_cell::update_traction( TDynamicObject *Vehicle, int const Pantographindex auto pantograph = Vehicle->pants[ Pantographindex ].fParamPants; auto const pantographposition = position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x ); - for( auto *traction : m_traction ) { + for( auto *traction : m_directories.traction ) { // współczynniki równania parametrycznego auto const paramfrontdot = glm::dot( traction->vParametric, vFront ); @@ -174,6 +174,11 @@ basic_cell::insert( shape_node Shape ) { m_active = true; + // re-calculate cell radius, in case shape geometry extends outside the cell's boundaries + m_area.radius = std::max( + m_area.radius, + glm::length( m_area.center - Shape.data().area.center ) + Shape.data().area.radius ); + auto const &shapedata { Shape.data() }; auto &shapes = ( shapedata.translucent ? @@ -507,7 +512,7 @@ basic_section::update_traction( TDynamicObject *Vehicle, int const Pantographind auto pantograph = Vehicle->pants[ Pantographindex ].fParamPants; auto const pantographposition = position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x ); - auto const radius { 0.0 }; // { EU07_CELLSIZE * 0.5 }; // experimentally limited, check if it has any negative effect + auto const radius { EU07_CELLSIZE * 0.5 }; for( auto &cell : m_cells ) { // we reject early cells which aren't within our area of interest @@ -728,7 +733,7 @@ basic_region::update_traction( TDynamicObject *Vehicle, int const Pantographinde auto const pant0 = position + ( vLeft * p->vPos.z ) + ( vUp * p->vPos.y ) + ( vFront * p->vPos.x ); p->PantTraction = std::numeric_limits::max(); // taka za duża wartość - auto const §ionlist = sections( pant0, 0.0 ); + auto const §ionlist = sections( pant0, EU07_CELLSIZE * 0.5 ); for( auto *section : sectionlist ) { section->update_traction( Vehicle, Pantographindex ); } diff --git a/simulation.cpp b/simulation.cpp index 4d286e06..9bd163e5 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -308,16 +308,14 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad auto *path { deserialize_path( Input, Scratchpad, nodedata ) }; // duplicates of named tracks are currently experimentally allowed - if( simulation::Paths.insert( path ) ) { - simulation::Region->insert_path( path, Scratchpad ); - } - else { + 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" ) { @@ -325,12 +323,10 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad // traction loading is optional if( traction == nullptr ) { return; } - if( simulation::Traction.insert( traction ) ) { - simulation::Region->insert_traction( traction, Scratchpad ); - } - else { + 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" ) { @@ -338,15 +334,13 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad // traction loading is optional if( powersource == nullptr ) { return; } - if( simulation::Powergrid.insert( powersource ) ) { -/* - // TODO: implement this - simulation::Region.insert_powersource( powersource, Scratchpad ); -*/ - } - else { + 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" ) { @@ -382,12 +376,10 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad // model import can potentially fail if( instance == nullptr ) { return; } - if( simulation::Instances.insert( instance ) ) { - simulation::Region->insert_instance( instance, Scratchpad ); - } - else { + 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" ) @@ -412,42 +404,36 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad else if( nodedata.type == "memcell" ) { auto *memorycell { deserialize_memorycell( Input, Scratchpad, nodedata ) }; - if( simulation::Memory.insert( memorycell ) ) { -/* - // TODO: implement this - simulation::Region.insert_memorycell( memorycell, Scratchpad ); -*/ - } - else { + 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( simulation::Events.insert( 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 ); - } + if( true == eventlauncher->IsGlobal() ) { + simulation::Events.queue( eventlauncher ); } else { - ErrorLog( "Bad scenario: event launcher with duplicate name \"" + eventlauncher->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); + simulation::Region->insert_launcher( eventlauncher, Scratchpad ); } } else if( nodedata.type == "sound" ) { auto *sound { deserialize_sound( Input, Scratchpad, nodedata ) }; - if( simulation::Sounds.insert( sound ) ) { - simulation::Region->insert_sound( sound, Scratchpad ); - } - else { + if( false == simulation::Sounds.insert( sound ) ) { ErrorLog( "Bad scenario: sound node with duplicate name \"" + sound->m_name + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } + simulation::Region->insert_sound( sound, Scratchpad ); } } @@ -476,7 +462,7 @@ state_manager::deserialize_endorigin( cParser &Input, scene::scratch_data &Scrat Scratchpad.location_offset.pop(); } else { - ErrorLog( "Bad origin: endorigin instruction with empty origin stack in file \"" + Input.Name() + "\" (line " + to_string( Input.Line() - 1 ) + ")" ); + ErrorLog( "Bad origin: endorigin instruction with empty origin stack in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" ); } } @@ -530,7 +516,7 @@ state_manager::deserialize_trainset( cParser &Input, scene::scratch_data &Scratc 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 " + to_string( Input.Line() ) + ")" ); + ErrorLog( "Bad scenario: encountered nested trainset definitions in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() ) + ")" ); } Scratchpad.trainset = scene::scratch_data::trainset_data(); @@ -550,7 +536,7 @@ state_manager::deserialize_endtrainset( cParser &Input, scene::scratch_data &Scr 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 " + to_string( Input.Line() - 1 ) + ")" ); + ErrorLog( "Bad trainset: empty trainset defined in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" ); Scratchpad.trainset.is_open = false; return; } diff --git a/version.h b/version.h index bbb0332c..e1dbbd5c 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 916 +#define VERSION_MINOR 1020 #define VERSION_REVISION 0 From 35e526a4548823b4f1dc8fca10344925737a55b6 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sun, 22 Oct 2017 19:25:10 +0200 Subject: [PATCH 15/31] added support for using multiple and/or nested include parameters in one token --- parser.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/parser.cpp b/parser.cpp index c220b0c2..994250e6 100644 --- a/parser.cpp +++ b/parser.cpp @@ -153,21 +153,18 @@ std::string cParser::readToken(bool ToLower, const char *Break) if (mIncludeParser) { token = mIncludeParser->readToken(ToLower, Break); - if (!token.empty()) - { - pos = token.find("(p"); - // check if the token is a parameter which should be replaced with stored true value - if (pos != std::string::npos) //!=npos to znalezione - { - std::string parameter = - token.substr(pos + 2, token.find(")", pos) - pos + 2); // numer parametru + if (!token.empty()) { + + 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 < token.length(); ++pos) + for (; pos < parameters.at( nr ).size(); ++pos) token[pos] = tolower(token[pos]); } else From d92004a8180fe6ae685dc5e4d2ce98595eb3b1ba Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Mon, 23 Oct 2017 15:57:37 +0200 Subject: [PATCH 16/31] support for seasonal texture sets, wireframe mode render speed fix --- Globals.cpp | 19 ++++----------- Globals.h | 65 ++++++++++++++++++++++++++-------------------------- Ground.cpp | 3 ++- Texture.cpp | 7 ++++-- World.cpp | 25 +++++++++++++++++++- World.h | 3 +++ material.cpp | 33 ++++++++++++++++++++++---- material.h | 4 ++-- renderer.cpp | 9 +++++--- 9 files changed, 108 insertions(+), 60 deletions(-) diff --git a/Globals.cpp b/Globals.cpp index 9544503e..914f3b74 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -26,8 +26,9 @@ http://mozilla.org/MPL/2.0/. // namespace Global { // parametry do użytku wewnętrznego -// double Global::tSinceStart=0; +#ifdef EU07_USE_OLD_GROUNDCODE TGround *Global::pGround = NULL; +#endif std::string Global::AppName{ "EU07" }; std::string Global::asCurrentSceneryPath = "scenery/"; std::string Global::asCurrentTexturePath = std::string(szTexturePath); @@ -75,6 +76,8 @@ 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 +std::string Global::Season; // season of the year, based on simulation date + float Global::BaseDrawRange { 2500.f }; opengl_light Global::DayLight; int Global::DynamicLightCount { 3 }; @@ -191,19 +194,6 @@ int Global::iMWDdivider = 5; //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- -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 ); @@ -465,6 +455,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 diff --git a/Globals.h b/Globals.h index 46ce880c..fac2576d 100644 --- a/Globals.h +++ b/Globals.h @@ -150,16 +150,32 @@ 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); +#ifdef EU07_USE_OLD_GROUNDCODE + static TDynamicObject * DynamicNearest(); + static TDynamicObject * CouplerNearest(); +#endif + static bool AddToQuery(TEvent *event, TDynamicObject *who); + 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 FreeCameraInit; // pozycje kamery static std::vector FreeCameraInitAngle; @@ -179,27 +195,24 @@ class Global static bool bLiveTraction; static float Global::fMouseXScale; static float Global::fMouseYScale; - static double fFogStart; - static double fFogEnd; +#ifdef EU07_USE_OLD_GROUNDCODE static TGround *pGround; +#endif 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; @@ -222,6 +235,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; @@ -248,8 +264,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 @@ -293,19 +307,6 @@ class Global //randomizacja static std::mt19937 random_engine; - // metody - static void TrainDelete(TDynamicObject *d); - static void ConfigParse(cParser &parser); - static std::string GetNextSymbol(); -#ifdef EU07_USE_OLD_GROUNDCODE - static TDynamicObject * DynamicNearest(); - static TDynamicObject * CouplerNearest(); -#endif - static bool AddToQuery(TEvent *event, TDynamicObject *who); - static bool DoEvents(); - static std::string Bezogonkow(std::string str, bool _ = false); - static double Min0RSpeed(double vel1, double vel2); - // maciek001: zmienne dla MWD static bool bMWDmasterEnable; // główne włączenie portu COM static bool bMWDdebugEnable; // logowanie pracy diff --git a/Ground.cpp b/Ground.cpp index 74f76aa1..eca03bc1 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -533,8 +533,9 @@ BYTE TempConnectionType[ 200 ]; // Ra: sprzêgi w sk³adzie; ujemne, gdy odwrotn TGround::TGround() { +#ifdef EU07_USE_OLD_GROUNDCODE Global::pGround = this; - +#endif for( int i = 0; i < TP_LAST; ++i ) { nRootOfType[ i ] = nullptr; // zerowanie tablic wyszukiwania } diff --git a/Texture.cpp b/Texture.cpp index f2a294f4..b4c2716c 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -716,8 +716,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( '.' ) ); + } for( char &c : Filename ) { // change forward slashes to windows ones. NOTE: probably not strictly necessary, but eh diff --git a/World.cpp b/World.cpp index 80757136..98a87c8a 100644 --- a/World.cpp +++ b/World.cpp @@ -2322,7 +2322,6 @@ void TWorld::ChangeDynamic() { } Global::changeDynObj = NULL; } -//--------------------------------------------------------------------------- void TWorld::ToggleDaylight() { @@ -2339,6 +2338,30 @@ TWorld::ToggleDaylight() { } } +// calculates current season of the year based on set simulation date +void +TWorld::compute_season( int const Yearday ) const { + + using dayseasonpair = std::pair; + + std::vector seasonsequence { + { 65, "winter" }, + { 158, "spring" }, + { 252, "summer" }, + { 341, "autumn" }, + { 366, "winter" } }; + auto const lookup = + std::lower_bound( + std::begin( seasonsequence ), std::end( seasonsequence ), + clamp( Yearday, 1, seasonsequence.back().first ), + []( dayseasonpair const &Left, const int Right ) { + return Left.first < Right; } ); + + Global::Season = lookup->second + ":"; +} + + + void world_environment::init() { diff --git a/World.h b/World.h index a646e9ad..bd366f8c 100644 --- a/World.h +++ b/World.h @@ -113,6 +113,9 @@ TWorld(); train() const { 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(); diff --git a/material.cpp b/material.cpp index c93ca908..b3870201 100644 --- a/material.cpp +++ b/material.cpp @@ -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( 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( '.' ) ); } diff --git a/material.h b/material.h index 841d1861..6f305cb0 100644 --- a/material.h +++ b/material.h @@ -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 { diff --git a/renderer.cpp b/renderer.cpp index d4e8929b..1ef746c6 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -365,6 +365,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { opengl_camera shadowcamera; // temporary helper, remove once ortho shadowmap code is done if( ( true == Global::RenderShadows ) + && ( false == Global::bWireFrame ) && ( true == World.InitPerformed() ) && ( m_shadowcolor != colors::white ) ) { // run shadowmap pass before color @@ -857,7 +858,7 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const Active_Texture( m_helpertextureunit ); if( ( true == Reflections ) - || ( ( true == Global::RenderShadows ) && ( true == Shadows ) ) ) { + || ( ( true == Global::RenderShadows ) && ( true == Shadows ) && ( false == Global::bWireFrame ) ) ) { // we need to have texture on the helper for either the reflection and shadow generation (or both) if( true == m_environmentcubetexturesupport ) { // bind dynamic environment cube if it's enabled... @@ -880,7 +881,7 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const } } - if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { + if( ( true == Global::RenderShadows ) && ( true == Shadows ) && ( false == Global::bWireFrame ) ) { ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); ::glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, glm::value_ptr( m_shadowcolor ) ); // TODO: dynamically calculated shadow colour, based on sun height @@ -938,6 +939,7 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const if( m_shadowtextureunit >= 0 ) { if( ( true == Global::RenderShadows ) && ( true == Shadows ) + && ( false == Global::bWireFrame ) && ( m_shadowcolor != colors::white ) ) { Active_Texture( m_shadowtextureunit ); @@ -1049,6 +1051,7 @@ opengl_renderer::switch_units( bool const Diffuse, bool const Shadows, bool cons if( ( true == Reflections ) || ( ( true == Global::RenderShadows ) && ( true == Shadows ) + && ( false == Global::bWireFrame ) && ( m_shadowcolor != colors::white ) ) ) { if( true == m_environmentcubetexturesupport ) { ::glEnable( GL_TEXTURE_CUBE_MAP ); @@ -1068,7 +1071,7 @@ opengl_renderer::switch_units( bool const Diffuse, bool const Shadows, bool cons } // shadow texture unit. if( m_shadowtextureunit >= 0 ) { - if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { + if( ( true == Global::RenderShadows ) && ( true == Shadows ) && ( false == Global::bWireFrame ) ) { Active_Texture( m_shadowtextureunit ); ::glEnable( GL_TEXTURE_2D ); From e3915e3c52620bd593d7f653535c437c8685f75c Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Wed, 25 Oct 2017 15:44:41 +0200 Subject: [PATCH 17/31] fix for emptying brake reservoirs --- McZapkie/Mover.cpp | 2 +- McZapkie/Oerlikon_ESt.cpp | 2 -- McZapkie/hamulce.cpp | 38 ++++++++++++++++++++++++++++++++++++++ McZapkie/hamulce.h | 3 +++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index a1f76501..4bee3a02 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -3456,7 +3456,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)); diff --git a/McZapkie/Oerlikon_ESt.cpp b/McZapkie/Oerlikon_ESt.cpp index ef433d64..8ae37298 100644 --- a/McZapkie/Oerlikon_ESt.cpp +++ b/McZapkie/Oerlikon_ESt.cpp @@ -435,8 +435,6 @@ void TNESt3::ForceEmptiness() Miedzypoj->CreatePress(0); CntrlRes->CreatePress(0); - BrakeStatus = 0; - ValveRes->Act(); BrakeRes->Act(); Miedzypoj->Act(); diff --git a/McZapkie/hamulce.cpp b/McZapkie/hamulce.cpp index 06e45445..8c76e06a 100644 --- a/McZapkie/hamulce.cpp +++ b/McZapkie/hamulce.cpp @@ -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 ) @@ -1721,6 +1733,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 ) @@ -2090,6 +2113,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) diff --git a/McZapkie/hamulce.h b/McZapkie/hamulce.h index b23c6c06..805d53d3 100644 --- a/McZapkie/hamulce.h +++ b/McZapkie/hamulce.h @@ -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) From d8c3b5caf4a9982b6b2676ddb4c567a25ac7de14 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 26 Oct 2017 04:54:07 +0200 Subject: [PATCH 18/31] build 171025: finished initial refactoring of ground class, fixes for text parser to prevent some cases of eternal parameter loops --- Driver.cpp | 181 +++++++++++++++++++++++++++------------------------ DynObj.cpp | 59 +++++++++++------ Event.cpp | 10 +-- Ground.cpp | 13 ++-- MemCell.cpp | 13 ++++ MemCell.h | 3 + Segment.cpp | 14 +++- Track.cpp | 65 +++++++++--------- Track.h | 8 +-- World.cpp | 9 ++- parser.cpp | 132 +++++++++++++++++++------------------ parser.h | 2 +- renderer.cpp | 50 +++++++++----- renderer.h | 16 ++++- version.h | 2 +- 15 files changed, 332 insertions(+), 245 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 0fb10aa5..772f78af 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -5367,43 +5367,47 @@ void TController::SetProximityVelocity( double dist, double vel, glm::dvec3 cons 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 @@ -5411,66 +5415,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ąć/stać - { // 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 ruszać + // 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 @@ -5497,28 +5502,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) diff --git a/DynObj.cpp b/DynObj.cpp index 72479e77..e04807f0 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -415,14 +415,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 } ); + } } }; @@ -493,18 +501,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 ) } ); + } } }; @@ -1745,8 +1765,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 { @@ -1770,16 +1790,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 @@ -5367,7 +5387,6 @@ vehicle_table::update( double Deltatime, int Iterationcount ) { 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 ) { - // TODO: re-implement update_traction( vehicle ); } vehicle->MoverParameters->ComputeConstans(); diff --git a/Event.cpp b/Event.cpp index daf24862..8755912d 100644 --- a/Event.cpp +++ b/Event.cpp @@ -1263,16 +1263,8 @@ event_manager::CheckQuery() { + to_string( m_workevent->Params[ 9 ].asMemCell->Value2(), 2 ) + "]" ); } else { -/* - // TODO: re-enable when cell manager is in place // lista wszystkich - for( TGroundNode *Current = nRootOfType[ TP_MEMCELL ]; Current; Current = Current->nNext ) { - WriteLog( "Memcell \"" + Current->asName + "\": " - + Current->MemCell->Text() + " " - + std::to_string( Current->MemCell->Value1() ) + " " - + std::to_string( Current->MemCell->Value2() ) ); - } -*/ + simulation::Memory.log_all(); } break; } diff --git a/Ground.cpp b/Ground.cpp index eca03bc1..174b050d 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -1200,7 +1200,6 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } break; -#endif case TP_MODEL: { if( rmin < 0 ) { // legacy leftover: special case, terrain provided as 3d model @@ -1234,7 +1233,6 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } } else { -#ifdef EU07_USE_OLD_GROUNDCODE // regular 3d model parser->getTokens( 3 ); *parser @@ -1265,11 +1263,9 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) ErrorLog( "Duplicated model: " + tmp->asName ); // to zgłaszać duplikat } } -#endif } break; } -#ifdef EU07_USE_OLD_GROUNDCODE // case TP_GEOMETRY : case GL_TRIANGLES: case GL_TRIANGLE_STRIP: @@ -1472,7 +1468,6 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } // koniec wczytywania trójkątów break; } -#endif case GL_LINES: case GL_LINE_STRIP: case GL_LINE_LOOP: { @@ -1566,6 +1561,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } break; } +#endif } return tmp; } @@ -1933,7 +1929,12 @@ bool TGround::Init(std::string File) } else if (str == "endorigin") { - OriginStack.pop(); + if( true == OriginStack.empty() ) { + // report error here + } + else { + OriginStack.pop(); + } pOrigin = ( OriginStack.empty() ? Math3D::vector3() : OriginStack.top() ); diff --git a/MemCell.cpp b/MemCell.cpp index 63c27b40..e55546e1 100644 --- a/MemCell.cpp +++ b/MemCell.cpp @@ -202,3 +202,16 @@ memory_table::InitCells() { 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 ) + "]" ); + } +} diff --git a/MemCell.h b/MemCell.h index 0364eb89..432be51f 100644 --- a/MemCell.h +++ b/MemCell.h @@ -73,6 +73,9 @@ 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(); }; //--------------------------------------------------------------------------- diff --git a/Segment.cpp b/Segment.cpp index b8509c0d..afc6df24 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -386,7 +386,12 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, fOffset = 0.1 / fLength; // pierwsze 10cm pos1 = glm::dvec3{ FastGetPoint( t ) - Origin }; // wektor początku segmentu dir = glm::dvec3{ FastGetDirection( t, fOffset ) }; // wektor kierunku - parallel1 = glm::normalize( glm::vec3{ -dir.z, 0.f, dir.x } ); // wektor poprzeczny + 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 ); @@ -416,7 +421,12 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, t = fTsBuffer[ i ]; // szybsze od GetTFromS(s); pos2 = glm::dvec3{ FastGetPoint( t ) - Origin }; dir = glm::dvec3{ FastGetDirection( t, fOffset ) }; // nowy wektor kierunku - parallel2 = glm::normalize( glm::vec3{ -dir.z, 0.f, dir.x } ); // wektor poprzeczny + 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 ) { diff --git a/Track.cpp b/Track.cpp index 6d5cc64e..0c2b54ce 100644 --- a/Track.cpp +++ b/Track.cpp @@ -583,16 +583,7 @@ void TTrack::Load(cParser *parser, vector3 pOrigin) >> fTexHeight1 >> fTexWidth >> fTexSlope; - if( fTexLength != 4.f ) { - // force defaults for malformed track definitions - bool paramsvalid { true }; - if( fTexHeight1 == 0.f ) { fTexHeight1 = 0.6f; paramsvalid = false; } - if( fTexWidth == 0.f ) { fTexWidth = 0.9f; paramsvalid = false; } - if( fTexSlope == 0.f ) { fTexSlope = 0.9f; paramsvalid = false; } - if( false == paramsvalid ) { - ErrorLog( "Bad track: one or more of texture dimensions set to 0 for track \"" + name() + "\" in file \"" + parser->Name() + "\" (line " + std::to_string( parser->Line() - 1 ) + ")" ); - } - } + if (iCategoryFlag & 4) fTexHeight1 = -fTexHeight1; // rzeki mają wysokość odwrotnie niż drogi } @@ -1185,13 +1176,15 @@ bool TTrack::InMovement() #ifdef EU07_USE_OLD_GROUNDCODE void TTrack::RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joined) #else -void TTrack::RaAssign( scene::basic_cell *gn, TAnimModel *am, TEvent *done, TEvent *joined ) +void TTrack::RaAssign( TAnimModel *am, TEvent *done, TEvent *joined ) #endif { // Ra: wiązanie toru z modelem obrotnicy if (eType == tt_Table) { SwitchExtension->pModel = am; +#ifdef EU07_USE_OLD_GROUNDCODE SwitchExtension->pMyNode = gn; +#endif SwitchExtension->evMinus = done; // event zakończenia animacji (zadanie nowej przedłuża) SwitchExtension->evPlus = joined; // event potwierdzenia połączenia (gdy nie znajdzie, to się nie połączy) @@ -1218,6 +1211,10 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { hypot1 = 1.f; glm::vec3 const normalup{ 0.f, 1.f, 0.f }; glm::vec3 normal1 { fTexHeight1 / hypot1, fTexSlope / hypot1, 0.f }; // wektor normalny + if( glm::length( normal1 ) == 0.f ) { + // fix normal for vertical surfaces + normal1 = glm::vec3 { 1.f, 0.f, 0.f }; + } glm::vec3 normal2; float fHTW2, side2, slop2, rozp2, fTexHeight2, hypot2; if( iTrapezoid & 2 ) { @@ -1232,6 +1229,10 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { if( hypot2 == 0.f ) hypot2 = 1.f; normal2 = { fTexHeight2 / hypot2, trNext->fTexSlope / hypot2, 0.f }; + if( glm::length( normal2 ) == 0.f ) { + // fix normal for vertical surfaces + normal2 = glm::vec3 { 1.f, 0.f, 0.f }; + } } else { // gdy nie ma następnego albo jest nieodpowiednim końcem podpięty @@ -1645,7 +1646,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { { // standardowo: od zewnątrz pochylenie, a od wewnątrz poziomo rpts1[ 0 ] = { {rozp, -fTexHeight1, 0.f}, - normalup, + { 1.f, 0.f, 0.f }, {0.f, 0.f} }; // lewy brzeg podstawy rpts1[ 1 ] = { {bpts1[ 0 ].position.x + side, bpts1[ 0 ].position.y, 0.f}, @@ -1665,13 +1666,13 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { {0.5f, 0.f} }; // prawa krawędź załamania rpts2[ 2 ] = { {-rozp, -fTexHeight1, 0.f}, - normalup, + { -1.f, 0.f, 0.f }, {0.f, 0.f} }; // prawy brzeg podstawy if (iTrapezoid) { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka rpts1[ 3 ] = { {rozp2, -fTexHeight2, 0.f}, - normalup, + { 1.f, 0.f, 0.f }, {0.f, 0.f} }; // lewy brzeg lewego pobocza rpts1[ 4 ] = { {bpts1[ 2 ].position.x + side2, bpts1[ 2 ].position.y, 0.f}, @@ -1691,7 +1692,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { {0.5f, 0.f} }; rpts2[ 5 ] = { {-rozp2, -fTexHeight2, 0.f}, - normalup, + { -1.f, 0.f, 0.f }, {0.f, 0.f} }; // prawy brzeg prawego pobocza } } @@ -1728,11 +1729,11 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { {0.515625f, 0.f} }; // prawy krawężnik u góry rpts1[ 2 ] = { {bpts1[ 0 ].position.x, bpts1[ 0 ].position.y, 0.f}, - normalup, + { -1.f, 0.f, 0.f }, {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu rpts2[ 0 ] = { {bpts1[ 1 ].position.x, bpts1[ 1 ].position.y, 0.f}, - normalup, + { 1.f, 0.f, 0.f }, {0.484375f + d / 2.56f, 0.f} }; // lewy krawężnik u dołu rpts2[ 1 ] = { {bpts1[ 1 ].position.x - d, bpts1[ 1 ].position.y + h1l, 0.f}, @@ -1776,11 +1777,11 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { {0.515625f, 0.f} }; // prawy krawężnik u góry rpts1[ 5 ] = { {bpts1[ 2 ].position.x, bpts1[ 2 ].position.y, 0.f}, - normalup, + { -1.f, 0.f, 0.f }, {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu rpts2[ 3 ] = { {bpts1[ 3 ].position.x, bpts1[ 3 ].position.y, 0.f}, - normalup, + { 1.f, 0.f, 0.f }, {0.484375f + d / 2.56f, 0.f} }; // lewy krawężnik u dołu rpts2[ 4 ] = { {bpts1[ 3 ].position.x - d, bpts1[ 3 ].position.y + h2l, 0.f}, @@ -1912,7 +1913,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { { // standardowo: od zewnątrz pochylenie, a od wewnątrz poziomo rpts1[ 0 ] = { {rozp, -fTexHeight1, 0.f}, - normalup, + { 1.f, 0.f, 0.f }, {0.f, 0.f} }; // lewy brzeg podstawy rpts1[ 1 ] = { {bpts1[ 0 ].position.x + side, bpts1[ 0 ].position.y, 0.f}, @@ -1932,13 +1933,13 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { {0.5f, 0.f} }; // prawa krawędź załamania rpts2[ 2 ] = { {-rozp, -fTexHeight1, 0.f}, - normalup, + { -1.f, 0.f, 0.f }, {0.f, 0.f} }; // prawy brzeg podstawy // if (iTrapezoid) //trapez albo przechyłki { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka rpts1[ 3 ] = { {rozp2, -fTexHeight2, 0.f}, - normalup, + { 1.f, 0.f, 0.f }, {0.f, 0.f} }; // lewy brzeg lewego pobocza rpts1[ 4 ] = { {bpts1[ 2 ].position.x + side2, bpts1[ 2 ].position.y, 0.f}, @@ -1958,7 +1959,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { {0.5f, 0.f} }; rpts2[ 5 ] = { {-rozp2, -fTexHeight2, 0.f}, - normalup, + { -1.f, 0.f, 0.f }, {0.f, 0.f} }; // prawy brzeg prawego pobocza } } @@ -1987,11 +1988,11 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { {0.515625f, 0.f} }; // prawy krawężnik u góry rpts1[ 2 ] = { {bpts1[ 0 ].position.x, bpts1[ 0 ].position.y, 0.f}, - normalup, + { -1.f, 0.f, 0.f }, {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu rpts2[ 0 ] = { {bpts1[ 1 ].position.x, bpts1[ 1 ].position.y, 0.f}, - normalup, + { 1.f, 0.f, 0.f }, {0.484375f + d / 2.56f, 0.f} }; // lewy krawężnik u dołu rpts2[ 1 ] = { {bpts1[ 1 ].position.x - d, bpts1[ 1 ].position.y - fTexHeight1, 0.f}, @@ -2026,11 +2027,11 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { {0.515625f, 0.f} }; // prawy krawężnik u góry rpts1[ 5 ] = { {bpts1[ 2 ].position.x, bpts1[ 2 ].position.y, 0.f}, - normalup, + { -1.f, 0.f, 0.f }, {0.515625f - d / 2.56f, 0.f} }; // prawy krawężnik u dołu rpts2[ 3 ] = { {bpts1[ 3 ].position.x, bpts1[ 3 ].position.y, 0.f}, - normalup, + { 1.f, 0.f, 0.f }, {0.484375f + d / 2.56, 0.f} }; // lewy krawężnik u dołu rpts2[ 4 ] = { {bpts1[ 3 ].position.x - d, bpts1[ 3 ].position.y - fTexHeight2, 0.f}, @@ -2664,11 +2665,11 @@ TTrack * TTrack::RaAnimate() #ifdef EU07_USE_OLD_GROUNDCODE SwitchExtension->pMyNode->pCenter + #else - SwitchExtension->pMyNode->area().center + + location() + #endif SwitchExtension->vTrans; // SwitchExtension->Segments[0]->FastGetPoint(0.5); Segment->Init(middle + vector3(sina, 0.0, cosa), - middle - vector3(sina, 0.0, cosa), 5.0); // nowy odcinek + middle - vector3(sina, 0.0, cosa), 10.0); // nowy odcinek for( auto dynamic : Dynamics ) { // minimalny ruch, aby przeliczyć pozycję dynamic->Move( 0.000001 ); @@ -2957,18 +2958,19 @@ path_table::InitTracks() { } switch (track->eType) { -/* // TODO: re-enable case tt_Table: { // obrotnicę też łączymy na starcie z innymi torami // szukamy modelu o tej samej nazwie auto *instance = simulation::Instances.find( trackname ); // wiązanie toru z modelem obrotnicy +#ifdef EU07_USE_OLD_GROUNDCODE +#else track->RaAssign( - Current, instance, simulation::Events.FindEvent( trackname + ":done" ), simulation::Events.FindEvent( trackname + ":joined" ) ); +#endif if( instance == nullptr ) { // jak nie ma modelu to pewnie jest wykolejnica, a ta jest domyślnie zamknięta i wykoleja break; @@ -2976,7 +2978,6 @@ path_table::InitTracks() { // no break on purpose: // jak coś pójdzie źle, to robimy z tego normalny tor } -*/ case tt_Normal: { // tylko proste są podłączane do rozjazdów, stąd dwa rozjazdy się nie połączą ze sobą if( track->CurrentPrev() == nullptr ) { diff --git a/Track.h b/Track.h index 59102064..8d23c127 100644 --- a/Track.h +++ b/Track.h @@ -75,13 +75,11 @@ class TSwitchExtension }; struct { // zmienne potrzebne tylko dla obrotnicy/przesuwnicy + // TAnimContainer *pAnim; //animator modelu dla obrotnicy + TAnimModel *pModel; // na razie model #ifdef EU07_USE_OLD_GROUNDCODE TGroundNode *pMyNode; // dla obrotnicy do wtórnego podłączania torów -#else - scene::basic_cell *pMyNode; // TODO: convert this to observer pattern #endif - // TAnimContainer *pAnim; //animator modelu dla obrotnicy - TAnimModel *pModel; // na razie model }; struct { // zmienne dla skrzyżowania @@ -270,7 +268,7 @@ public: #ifdef EU07_USE_OLD_GROUNDCODE void RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joined); #else - void RaAssign( scene::basic_cell *gn, TAnimModel *am, TEvent *done, TEvent *joined ); + void RaAssign( TAnimModel *am, TEvent *done, TEvent *joined ); #endif void RaAnimListAdd(TTrack *t); TTrack * RaAnimate(); diff --git a/World.cpp b/World.cpp index 98a87c8a..9d427367 100644 --- a/World.cpp +++ b/World.cpp @@ -1312,7 +1312,7 @@ void TWorld::Update_UI() { UITable->text_lines.clear(); - std::string uitextline1, uitextline2, uitextline3, uitextline4; + std::string uitextline1, uitextline2, uitextline3, uitextline4; UILayer.set_tooltip( "" ); if( ( Train != nullptr ) && ( false == FreeFlyModeFlag ) ) { @@ -1328,7 +1328,14 @@ TWorld::Update_UI() { } if( ( true == Global::ControlPicking ) && ( true == FreeFlyModeFlag ) && ( true == DebugModeFlag ) ) { auto const scenerynode = GfxRenderer.Pick_Node(); +#ifdef EU07_USE_OLD_GROUNDCODE UILayer.set_tooltip( ( scenerynode ? scenerynode->asName : "" ) ); +#else + UILayer.set_tooltip( + ( scenerynode ? + scenerynode->name() : + "" ) ); +#endif } switch( Global::iTextMode ) { diff --git a/parser.cpp b/parser.cpp index 994250e6..52cde35b 100644 --- a/parser.cpp +++ b/parser.cpp @@ -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 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; @@ -59,6 +60,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 @@ -145,86 +150,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()) { +std::string cParser::readToken( bool ToLower, const char *Break ) { - 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 - } - return token; - } - else - { - mIncludeParser = NULL; - parameters.clear(); + 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( 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( 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 + } + + 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 ) ) ) { - - std::string parameter = readToken(false); // w parametrach nie zmniejszamy - while( (parameter.empty() == false) - && (parameter.compare("end") != 0) ) - { - parameters.emplace_back(parameter); - parameter = readToken(false); + // get parameter list for the child parser + std::vector 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( 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(includefile, buffer_FILE, mPath, LoadTraction); - if (mIncludeParser->mSize <= 0) - ErrorLog("Bad include: can't open file \"" + 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; } diff --git a/parser.h b/parser.h index 5709d16c..4799a7d2 100644 --- a/parser.h +++ b/parser.h @@ -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 Parameters = std::vector() ); // destructor: virtual ~cParser(); // methods: diff --git a/renderer.cpp b/renderer.cpp index 1ef746c6..f2436148 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -2060,7 +2060,6 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator // tracks // TODO: update after path node refactoring Render( std::begin( cell->m_paths ), std::end( cell->m_paths ) ); - // TODO: add other content types // post-render cleanup ::glPopMatrix(); @@ -2076,7 +2075,8 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator // render // opaque non-instanced shapes for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } - // TODO: add other content types + // tracks + Render( std::begin( cell->m_paths ), std::end( cell->m_paths ) ); // post-render cleanup ::glPopMatrix(); @@ -2091,6 +2091,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render // opaque non-instanced shapes + // non-interactive scenery elements get neutral colour ::glColor3fv( glm::value_ptr( colors::none ) ); for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } // tracks @@ -2099,7 +2100,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); Render( path ); } - // TODO: add other content types + // post-render cleanup ::glPopMatrix(); @@ -2134,11 +2135,12 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator } case rendermode::pickscenery: { // opaque parts of instanced models + // same procedure like with regular render, but each node receives custom colour used for picking for( auto *instance : cell->m_instancesopaque ) { ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); Render( instance ); } - // TODO: add remaining content types + // vehicles aren't included in scenery picking for the time being break; } case rendermode::reflections: @@ -2224,19 +2226,18 @@ opengl_renderer::Render( TAnimModel *Instance ) { || ( distancesquared >= Instance->m_rangesquaredmax ) ) { return; } -/* - // TODO: enable adding items to the picking list + switch( m_renderpass.draw_mode ) { case rendermode::pickscenery: { // add the node to the pick list - m_picksceneryitems.emplace_back( Node ); + m_picksceneryitems.emplace_back( Instance ); break; } default: { break; } } -*/ + Instance->RaAnimate( m_framestamp ); // jednorazowe przeliczenie animacji Instance->RaPrepare(); if( Instance->pModel ) { @@ -2767,8 +2768,15 @@ opengl_renderer::Render( TTrack *Track ) { Track->EnvironmentReset(); break; } - case rendermode::shadows: + case rendermode::shadows: { + // shadow pass includes trackbeds but not tracks themselves due to low resolution of the map + // TODO: implement + break; + } case rendermode::pickscenery: { + // add the node to the pick list + m_picksceneryitems.emplace_back( Track ); + if( Track->m_material1 != 0 ) { Bind_Material( Track->m_material1 ); m_geometry.draw( std::begin( Track->Geometry1 ), std::end( Track->Geometry1 ) ); @@ -2816,12 +2824,8 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, track->EnvironmentReset(); break; } - case rendermode::shadows: { - Bind_Material( track->m_material1 ); - m_geometry.draw( std::begin( track->Geometry1 ), std::end( track->Geometry1 ) ); - break; - } - case rendermode::pickscenery: // pick scenery should use track-by-track approach + case rendermode::shadows: // shadows are calculated only for trackbeds + case rendermode::pickscenery: // pick scenery mode uses piece-by-piece approach case rendermode::pickcontrols: default: { break; @@ -2850,11 +2854,16 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, break; } case rendermode::shadows: { + if( ( track->iCategoryFlag != 1 ) + || ( track->eType != tt_Normal ) ) { + // shadows are only calculated for trackbeds + continue; + } Bind_Material( track->m_material2 ); m_geometry.draw( std::begin( track->Geometry2 ), std::end( track->Geometry2 ) ); break; } - case rendermode::pickscenery: // pick scenery should use track-by-track approach + case rendermode::pickscenery: // pick scenery mode uses piece-by-piece approach case rendermode::pickcontrols: default: { break; @@ -3121,6 +3130,7 @@ opengl_renderer::Render_Alpha( scene::basic_region *Region ) { void opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_sequence::reverse_iterator Last ) { + // NOTE: this method is launched only during color pass therefore we don't bother with mode test here // first pass draws elements which we know are located in section banks, to reduce vbo switching { auto first { First }; @@ -3690,7 +3700,11 @@ opengl_renderer::Update_Pick_Control() { return control; } +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode const * +#else +editor::basic_node const * +#endif opengl_renderer::Update_Pick_Node() { #ifdef EU07_USE_PICKING_FRAMEBUFFER @@ -3725,7 +3739,11 @@ opengl_renderer::Update_Pick_Node() { unsigned char pickreadout[4]; ::glReadPixels( pickbufferpos.x, pickbufferpos.y, 1, 1, GL_BGRA, GL_UNSIGNED_BYTE, pickreadout ); auto const nodeindex = pick_index( glm::ivec3{ pickreadout[ 2 ], pickreadout[ 1 ], pickreadout[ 0 ] } ); +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode const *node { nullptr }; +#else + editor::basic_node const *node { nullptr }; +#endif if( ( nodeindex > 0 ) && ( nodeindex <= m_picksceneryitems.size() ) ) { node = m_picksceneryitems[ nodeindex - 1 ]; diff --git a/renderer.h b/renderer.h index 14efad82..d0c7970c 100644 --- a/renderer.h +++ b/renderer.h @@ -192,14 +192,22 @@ public: // utility methods TSubModel const * Pick_Control() const { return m_pickcontrolitem; } +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode const * +#else + editor::basic_node const * +#endif Pick_Node() const { return m_picksceneryitem; } // maintenance jobs void Update( double const Deltatime ); TSubModel const * Update_Pick_Control(); +#ifdef EU07_USE_OLD_GROUNDCODE TGroundNode const * +#else + editor::basic_node const * +#endif Update_Pick_Node(); // debug performance string std::string const & @@ -420,11 +428,15 @@ private: section_sequence m_sectionqueue; // list of sections in current render pass cell_sequence m_cellqueue; #endif - - std::vector m_picksceneryitems; std::vector m_pickcontrolsitems; TSubModel const *m_pickcontrolitem { nullptr }; +#ifdef EU07_USE_OLD_GROUNDCODE + std::vector m_picksceneryitems; TGroundNode const *m_picksceneryitem { nullptr }; +#else + std::vector m_picksceneryitems; + editor::basic_node const *m_picksceneryitem { nullptr }; +#endif #ifdef EU07_USE_DEBUG_CAMERA renderpass_config m_worldcamera; // debug item #endif diff --git a/version.h b/version.h index e1dbbd5c..e45b45fa 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 1020 +#define VERSION_MINOR 1025 #define VERSION_REVISION 0 From 3fe968544add18d4d9b466e7a214476a1eb59f2d Mon Sep 17 00:00:00 2001 From: Kamil Lewan Date: Thu, 26 Oct 2017 19:50:48 +0200 Subject: [PATCH 19/31] TAirCoupler - partial refactor - rename private members - refactor TAirCoupler::GetStatus() - add some English comments - add 'doc/' in .gitingore TODO: - rename arguments when refactor other files (as TModel3d, cParser). --- .gitignore | 2 ++ AirCoupler.cpp | 85 +++++++++++++++++++++++++++++--------------------- AirCoupler.h | 36 ++++++++++----------- 3 files changed, 68 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index d995046f..73131621 100644 --- a/.gitignore +++ b/.gitignore @@ -74,4 +74,6 @@ builds/build_* builds/*.zip builds/deps_* +doc/ + CMakeLists.txt.user diff --git a/AirCoupler.cpp b/AirCoupler.cpp index 5c6c0b09..f8e0c02c 100644 --- a/AirCoupler.cpp +++ b/AirCoupler.cpp @@ -19,60 +19,73 @@ TAirCoupler::~TAirCoupler() { } +// zwraca 1, jeśli istnieje model prosty, 2 gdy skośny +/** + * Returns 0 when straight model exists, + * 2 when oblique model exists, and 0 when neither of them exist. + */ int TAirCoupler::GetStatus() -{ // zwraca 1, jeśli istnieje model prosty, 2 gdy skośny - int x = 0; - if (pModelOn) - x = 1; - if (pModelxOn) - x = 2; - return x; +{ + if (ModelOn) return 1; + if (ModelxOn) return 2; + return 0; } +// zerowanie wskaźników +/** + * Reset pointers. + */ void TAirCoupler::Clear() -{ // zerowanie wskaźników - pModelOn = NULL; - pModelOff = NULL; - pModelxOn = NULL; - bOn = false; - bxOn = false; +{ + ModelOn = NULL; + ModelOff = NULL; + ModelxOn = NULL; + On = false; + xOn = false; } - -void TAirCoupler::Init(std::string const &asName, TModel3d *pModel) -{ // wyszukanie submodeli - if (!pModel) +// wyszukanie submodeli +/** + * Looks for submodels. + */ +void TAirCoupler::Init(std::string const &asName, TModel3d *Model) +{ + if (!Model) return; // nie ma w czym szukać - pModelOn = pModel->GetFromName( asName + "_on" ); // połączony na wprost - pModelOff = pModel->GetFromName( asName + "_off" ); // odwieszony - pModelxOn = pModel->GetFromName( asName + "_xon" ); // połączony na skos + // polaczony na wprost + /** Straight connect. */ + ModelOn = Model->GetFromName(asName + "_on"); + // odwieszony + /** Not connected. Hung up. */ + ModelOff = Model->GetFromName(asName + "_off"); + // polaczony na skos + /** Oblique connect. */ + ModelxOn = Model->GetFromName(asName + "_xon"); } -void TAirCoupler::Load(cParser *Parser, TModel3d *pModel) +void TAirCoupler::Load(cParser *Parser, TModel3d *Model) { std::string name = Parser->getToken(); - if( pModel ) { - - Init( name, pModel ); + if(Model) + { + Init(name, Model); } else { - pModelOn = NULL; - pModelxOn = NULL; - pModelOff = NULL; + ModelOn = NULL; + ModelxOn = NULL; + ModelOff = NULL; } } void TAirCoupler::Update() { - // if ((pModelOn!=NULL) && (pModelOn!=NULL)) + // if ((pModelOn!=NULL) && (pModelOn!=NULL)) // legacy bullshitt alert { - if (pModelOn) - pModelOn->iVisible = bOn; - if (pModelOff) - pModelOff->iVisible = !(bOn || bxOn); - if (pModelxOn) - pModelxOn->iVisible = bxOn; + if (ModelOn) + ModelOn->iVisible = On; + if (ModelOff) + ModelOff->iVisible = !(On || xOn); + if (ModelxOn) + ModelxOn->iVisible = xOn; } } - -//--------------------------------------------------------------------------- diff --git a/AirCoupler.h b/AirCoupler.h index 8cdebaea..3b2cb9a2 100644 --- a/AirCoupler.h +++ b/AirCoupler.h @@ -14,40 +14,38 @@ http://mozilla.org/MPL/2.0/. class TAirCoupler { - private: - // TButtonType eType; - TSubModel *pModelOn, *pModelOff, *pModelxOn; - bool bOn; - bool bxOn; +private: + // TButtonType eType; //? + TSubModel *ModelOn, *ModelOff, *ModelxOn; + bool On; + bool xOn; void Update(); - public: +public: TAirCoupler(); ~TAirCoupler(); + void Clear(); + void Init(std::string const &asName, TModel3d *Model); + void Load(cParser *Parser, TModel3d *Model); + // inline bool Active() { if ((ModelOn)||(ModelOff)) return true; return false;}; + int GetStatus(); inline void TurnOn() { - bOn = true; - bxOn = false; + On = true; + xOn = false; Update(); }; inline void TurnOff() { - bOn = false; - bxOn = false; + On = false; + xOn = false; Update(); }; inline void TurnxOn() { - bOn = false; - bxOn = true; + On = false; + xOn = true; Update(); }; - // inline bool Active() { if ((pModelOn)||(pModelOff)) return true; return false;}; - int GetStatus(); - void Init(std::string const &asName, TModel3d *pModel); - void Load(cParser *Parser, TModel3d *pModel); - // bool Render(); }; - -//--------------------------------------------------------------------------- From 7422beffcc856dd4c849dfc75a6564be2c51c496 Mon Sep 17 00:00:00 2001 From: Kamil Lewan Date: Thu, 26 Oct 2017 20:30:38 +0200 Subject: [PATCH 20/31] TAirCoupler - Delete old comments. --- AirCoupler.cpp | 15 ++++++--------- AirCoupler.h | 3 +-- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/AirCoupler.cpp b/AirCoupler.cpp index f8e0c02c..57809933 100644 --- a/AirCoupler.cpp +++ b/AirCoupler.cpp @@ -79,13 +79,10 @@ void TAirCoupler::Load(cParser *Parser, TModel3d *Model) void TAirCoupler::Update() { - // if ((pModelOn!=NULL) && (pModelOn!=NULL)) // legacy bullshitt alert - { - if (ModelOn) - ModelOn->iVisible = On; - if (ModelOff) - ModelOff->iVisible = !(On || xOn); - if (ModelxOn) - ModelxOn->iVisible = xOn; - } + if (ModelOn) + ModelOn->iVisible = On; + if (ModelOff) + ModelOff->iVisible = !(On || xOn); + if (ModelxOn) + ModelxOn->iVisible = xOn; } diff --git a/AirCoupler.h b/AirCoupler.h index 3b2cb9a2..93b504a7 100644 --- a/AirCoupler.h +++ b/AirCoupler.h @@ -15,7 +15,7 @@ http://mozilla.org/MPL/2.0/. class TAirCoupler { private: - // TButtonType eType; //? + // TButtonType eType; TSubModel *ModelOn, *ModelOff, *ModelxOn; bool On; bool xOn; @@ -28,7 +28,6 @@ public: void Clear(); void Init(std::string const &asName, TModel3d *Model); void Load(cParser *Parser, TModel3d *Model); - // inline bool Active() { if ((ModelOn)||(ModelOff)) return true; return false;}; int GetStatus(); inline void TurnOn() { From 899381ae32c75b20cf3a1ab226f87fe5263c2d9d Mon Sep 17 00:00:00 2001 From: Kamil Lewan Date: Thu, 26 Oct 2017 20:26:53 +0200 Subject: [PATCH 21/31] Rename TAirCoupler class to AirCoupler. --- AirCoupler.cpp | 14 +++++++------- AirCoupler.h | 6 +++--- DynObj.h | 16 ++++++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/AirCoupler.cpp b/AirCoupler.cpp index 57809933..2c873365 100644 --- a/AirCoupler.cpp +++ b/AirCoupler.cpp @@ -10,12 +10,12 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "AirCoupler.h" -TAirCoupler::TAirCoupler() +AirCoupler::AirCoupler() { Clear(); } -TAirCoupler::~TAirCoupler() +AirCoupler::~AirCoupler() { } @@ -24,7 +24,7 @@ TAirCoupler::~TAirCoupler() * Returns 0 when straight model exists, * 2 when oblique model exists, and 0 when neither of them exist. */ -int TAirCoupler::GetStatus() +int AirCoupler::GetStatus() { if (ModelOn) return 1; if (ModelxOn) return 2; @@ -35,7 +35,7 @@ int TAirCoupler::GetStatus() /** * Reset pointers. */ -void TAirCoupler::Clear() +void AirCoupler::Clear() { ModelOn = NULL; ModelOff = NULL; @@ -47,7 +47,7 @@ void TAirCoupler::Clear() /** * Looks for submodels. */ -void TAirCoupler::Init(std::string const &asName, TModel3d *Model) +void AirCoupler::Init(std::string const &asName, TModel3d *Model) { if (!Model) return; // nie ma w czym szukać @@ -62,7 +62,7 @@ void TAirCoupler::Init(std::string const &asName, TModel3d *Model) ModelxOn = Model->GetFromName(asName + "_xon"); } -void TAirCoupler::Load(cParser *Parser, TModel3d *Model) +void AirCoupler::Load(cParser *Parser, TModel3d *Model) { std::string name = Parser->getToken(); if(Model) @@ -77,7 +77,7 @@ void TAirCoupler::Load(cParser *Parser, TModel3d *Model) } } -void TAirCoupler::Update() +void AirCoupler::Update() { if (ModelOn) ModelOn->iVisible = On; diff --git a/AirCoupler.h b/AirCoupler.h index 93b504a7..58e03c7e 100644 --- a/AirCoupler.h +++ b/AirCoupler.h @@ -12,7 +12,7 @@ http://mozilla.org/MPL/2.0/. #include "Model3d.h" #include "parser.h" -class TAirCoupler +class AirCoupler { private: // TButtonType eType; @@ -22,8 +22,8 @@ private: void Update(); public: - TAirCoupler(); - ~TAirCoupler(); + AirCoupler(); + ~AirCoupler(); void Clear(); void Init(std::string const &asName, TModel3d *Model); diff --git a/DynObj.h b/DynObj.h index c8369855..ef706724 100644 --- a/DynObj.h +++ b/DynObj.h @@ -266,15 +266,15 @@ public: // modele składowe pojazdu TButton btCoupler1; // sprzegi TButton btCoupler2; - TAirCoupler + AirCoupler 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 - TAirCoupler btPneumatic1; // ABu: sprzegi powietrzne zolte - TAirCoupler btPneumatic2; - TAirCoupler btPneumatic1r; // ABu: analogicznie jak 4 linijki wyzej - TAirCoupler btPneumatic2r; + AirCoupler btCPneumatic2; + AirCoupler btCPneumatic1r; // ABu: to zeby nie bylo problemow przy laczeniu wagonow, + AirCoupler btCPneumatic2r; // jesli beda polaczone sprzegami 1<->1 lub 0<->0 + AirCoupler btPneumatic1; // ABu: sprzegi powietrzne zolte + AirCoupler btPneumatic2; + AirCoupler btPneumatic1r; // ABu: analogicznie jak 4 linijki wyzej + AirCoupler btPneumatic2r; TButton btCCtrl1; // sprzegi sterowania TButton btCCtrl2; From a4c30a4837ea13df6fa7617713153c96180fb2ae Mon Sep 17 00:00:00 2001 From: Kamil Lewan Date: Fri, 27 Oct 2017 11:05:17 +0200 Subject: [PATCH 22/31] AirCoupler - comments update. --- AirCoupler.cpp | 34 ++++++++++++++++------------------ AirCoupler.h | 11 ++++++----- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/AirCoupler.cpp b/AirCoupler.cpp index 2c873365..32f814fd 100644 --- a/AirCoupler.cpp +++ b/AirCoupler.cpp @@ -19,10 +19,10 @@ AirCoupler::~AirCoupler() { } -// zwraca 1, jeśli istnieje model prosty, 2 gdy skośny /** - * Returns 0 when straight model exists, - * 2 when oblique model exists, and 0 when neither of them exist. + * \return 1 when \(straight\) TModel3d \c ModelOn exists + * \return 2 when \(slanted\) TModel3d \c ModelxOn exists + * \return 0 when neither of them exist */ int AirCoupler::GetStatus() { @@ -31,9 +31,8 @@ int AirCoupler::GetStatus() return 0; } -// zerowanie wskaźników /** - * Reset pointers. + * Reset pointers and variables. */ void AirCoupler::Clear() { @@ -43,25 +42,23 @@ void AirCoupler::Clear() On = false; xOn = false; } -// wyszukanie submodeli + /** - * Looks for submodels. + * Looks for submodels in the model and updates pointers. */ void AirCoupler::Init(std::string const &asName, TModel3d *Model) { if (!Model) - return; // nie ma w czym szukać - // polaczony na wprost - /** Straight connect. */ - ModelOn = Model->GetFromName(asName + "_on"); - // odwieszony - /** Not connected. Hung up. */ - ModelOff = Model->GetFromName(asName + "_off"); - // polaczony na skos - /** Oblique connect. */ - ModelxOn = Model->GetFromName(asName + "_xon"); + return; + ModelOn = Model->GetFromName(asName + "_on"); // Straight connect. + ModelOff = Model->GetFromName(asName + "_off"); // Not connected. Hung up. + ModelxOn = Model->GetFromName(asName + "_xon"); // Slanted connect. } - +/** + * Gets name of submodel \(from cParser \b *Parser\), + * looks for it in the TModel3d \b *Model and update pointers. + * If submodel is not found, reset pointers. +*/ void AirCoupler::Load(cParser *Parser, TModel3d *Model) { std::string name = Parser->getToken(); @@ -77,6 +74,7 @@ void AirCoupler::Load(cParser *Parser, TModel3d *Model) } } +// Update submodels visibility. void AirCoupler::Update() { if (ModelOn) diff --git a/AirCoupler.h b/AirCoupler.h index 58e03c7e..fbb2bd1c 100644 --- a/AirCoupler.h +++ b/AirCoupler.h @@ -15,7 +15,6 @@ http://mozilla.org/MPL/2.0/. class AirCoupler { private: - // TButtonType eType; TSubModel *ModelOn, *ModelOff, *ModelxOn; bool On; bool xOn; @@ -24,24 +23,26 @@ private: public: AirCoupler(); ~AirCoupler(); - + ///Reset members. void Clear(); + ///Looks for submodels. void Init(std::string const &asName, TModel3d *Model); + ///Loads info about coupler. void Load(cParser *Parser, TModel3d *Model); int GetStatus(); - inline void TurnOn() + inline void TurnOn() ///Turns on straight coupler. { On = true; xOn = false; Update(); }; - inline void TurnOff() + inline void TurnOff() ///Turns on disconnected coupler. { On = false; xOn = false; Update(); }; - inline void TurnxOn() + inline void TurnxOn() ///Turns on slanted coupler. { On = false; xOn = true; From 4ce975a656aa633209cd59e518777a20dbe3b7fc Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 28 Oct 2017 00:56:10 +0200 Subject: [PATCH 23/31] build 171027: bounding area calculation fixes, novice ui obstacle indicator, minor traction render enhancement, shadow calculations for tall enough platforms --- AnimModel.cpp | 9 +++++ AnimModel.h | 5 +++ EvLaunch.cpp | 11 +++++- EvLaunch.h | 5 +++ Ground.cpp | 3 ++ MemCell.cpp | 10 ++--- MemCell.h | 21 ++++++----- Model3d.cpp | 18 ++++++--- Track.cpp | 16 +++++++- Track.h | 11 ++++-- Traction.cpp | 14 ++++++- Traction.h | 7 +++- TractionPower.cpp | 6 +-- Train.cpp | 2 +- World.cpp | 8 +++- renderer.cpp | 82 ++++++++++++++++++++++++++++++++--------- scene.cpp | 86 +++++++++++++++++++++++++------------------ scene.h | 27 +++++++++++--- scenenode.cpp | 22 ++++++++++- scenenode.h | 11 ++++-- simulation.cpp | 94 ++++++++++++++++++++++++++++------------------- simulation.h | 2 +- version.h | 2 +- 23 files changed, 334 insertions(+), 138 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index 07f4aa94..fd994fe4 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -583,6 +583,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 diff --git a/AnimModel.h b/AnimModel.h index 3ced5617..acc9545f 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -157,6 +157,11 @@ public: // 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 diff --git a/EvLaunch.cpp b/EvLaunch.cpp index 349a29db..6abe7350 100644 --- a/EvLaunch.cpp +++ b/EvLaunch.cpp @@ -121,7 +121,7 @@ bool TEventLauncher::Load(cParser *parser) *parser >> token; } return true; -}; +} bool TEventLauncher::check_conditions() { //"renderowanie" wyzwalacza @@ -184,6 +184,13 @@ bool TEventLauncher::IsGlobal() const { && ( 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 ); +} //--------------------------------------------------------------------------- diff --git a/EvLaunch.h b/EvLaunch.h index 1dbeae97..d6fe9324 100644 --- a/EvLaunch.h +++ b/EvLaunch.h @@ -37,6 +37,11 @@ public: int iCheckMask { 0 }; double dRadius { 0.0 }; +protected: + // calculates node's bounding radius + void + radius_(); + private: // members int iKey { 0 }; diff --git a/Ground.cpp b/Ground.cpp index 174b050d..b457259f 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -1562,6 +1562,9 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) break; } #endif + default: { + break; + } } return tmp; } diff --git a/MemCell.cpp b/MemCell.cpp index e55546e1..972f89df 100644 --- a/MemCell.cpp +++ b/MemCell.cpp @@ -29,8 +29,8 @@ http://mozilla.org/MPL/2.0/. TMemCell::TMemCell(vector3 *p) { fValue1 = fValue2 = 0; - m_location = - p ? *p : glm::dvec3(); // ustawienie współrzędnych, bo do TGroundNode nie ma dostępu + location( + p ? *p : glm::dvec3() ); // ustawienie współrzędnych, bo do TGroundNode nie ma dostępu bCommand = false; // komenda wysłana OnSent = NULL; } @@ -110,9 +110,9 @@ bool TMemCell::Load(cParser *parser) #else parser->getTokens( 6, false ); *parser - >> m_location.x - >> m_location.y - >> m_location.z + >> m_area.center.x + >> m_area.center.y + >> m_area.center.z #endif >> szText >> fValue1 diff --git a/MemCell.h b/MemCell.h index 432be51f..a89d7580 100644 --- a/MemCell.h +++ b/MemCell.h @@ -16,16 +16,7 @@ http://mozilla.org/MPL/2.0/. class TMemCell : public editor::basic_node { - 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 - public: +public: std::string asTrackName; // McZapkie-100302 - zeby nazwe toru na ktory jest Putcommand wysylane pamietac TMemCell( scene::node_data const &Nodedata ); @@ -63,6 +54,16 @@ class TMemCell : public editor::basic_node { 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 }; diff --git a/Model3d.cpp b/Model3d.cpp index 0cbdfb17..a24ebc2e 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1008,7 +1008,10 @@ TSubModel::create_geometry( std::size_t &Dataoffset, geometrybank_handle const & if( m_geometry != NULL ) { // calculate bounding radius while we're at it // NOTE: doesn't take into account transformation hierarchy TODO: implement it - float squaredradius{ 0.f }; + 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( glm::length2( vertex.position ) ); if( squaredradius > m_boundingradius ) { @@ -1016,12 +1019,15 @@ TSubModel::create_geometry( std::size_t &Dataoffset, geometrybank_handle const & } } if( m_boundingradius > 0.f ) { m_boundingradius = std::sqrt( m_boundingradius ); } - if( Parent ) { - // propagate radius up the chain - Parent->m_boundingradius = std::max( - Parent->m_boundingradius, - 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; } + root->m_boundingradius = std::max( + root->m_boundingradius, + m_boundingradius ); } if( Next ) diff --git a/Track.cpp b/Track.cpp index 0c2b54ce..a560e0b9 100644 --- a/Track.cpp +++ b/Track.cpp @@ -867,11 +867,11 @@ void TTrack::Load(cParser *parser, vector3 pOrigin) } // calculate path location - m_location = glm::dvec3{ ( + m_area.center = ( glm::dvec3{ ( CurrentSegment()->FastGetPoint_0() + CurrentSegment()->FastGetPoint( 0.5 ) + CurrentSegment()->FastGetPoint_1() ) - / 3.0 }; + / 3.0 } ); } // TODO: refactor this mess @@ -2821,6 +2821,18 @@ TTrack::endpoints() const { } } +// calculates path's bounding radius +void +TTrack::radius_() { + + auto const points = endpoints(); + for( auto &point : points ) { + m_area.radius = std::max( + m_area.radius, + static_cast( glm::length( m_area.center - point ) ) ); // extra margin to prevent driven vehicle from flicking + } +} + void TTrack::MovedUp1(float const dh) { // poprawienie przechyłki wymaga wydłużenia podsypki fTexHeight1 += dh; diff --git a/Track.h b/Track.h index 8d23c127..8deb6aea 100644 --- a/Track.h +++ b/Track.h @@ -182,9 +182,9 @@ public: TEnvironmentType eEnvironment = e_flat; // dźwięk i oświetlenie 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 @@ -287,7 +287,12 @@ public: double VelocityGet(); void ConnectionsLog(); - private: +protected: + // calculates path's bounding radius + void + radius_(); + +private: void EnvironmentSet(); void EnvironmentReset(); }; diff --git a/Traction.cpp b/Traction.cpp index 8bab89bb..a0739107 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -169,7 +169,7 @@ TTraction::Load( cParser *parser, glm::dvec3 const &pOrigin ) { Init(); // przeliczenie parametrów // calculate traction location - m_location = interpolate( pPoint2, pPoint1, 0.5 ); + location( interpolate( pPoint2, pPoint1, 0.5 ) ); } // retrieves list of the track's end points @@ -543,6 +543,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( glm::length( m_area.center - point ) ) ); + } +} + glm::vec3 TTraction::wire_color() const { diff --git a/Traction.h b/Traction.h index d1202768..8ec2131a 100644 --- a/Traction.h +++ b/Traction.h @@ -64,7 +64,6 @@ class TTraction : public editor::basic_node { // retrieves list of the track's end points std::vector 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 #ifdef EU07_USE_OLD_GROUNDCODE @@ -79,6 +78,12 @@ class TTraction : public editor::basic_node { 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; }; diff --git a/TractionPower.cpp b/TractionPower.cpp index 0b6ee2c9..e4d60783 100644 --- a/TractionPower.cpp +++ b/TractionPower.cpp @@ -42,9 +42,9 @@ bool TTractionPowerSource::Load(cParser *parser) { #else parser->getTokens( 10, false ); *parser - >> m_location.x - >> m_location.y - >> m_location.z + >> m_area.center.x + >> m_area.center.y + >> m_area.center.z #endif >> NominalVoltage >> VoltageFrequency diff --git a/Train.cpp b/Train.cpp index 3d1c34db..67a641e6 100644 --- a/Train.cpp +++ b/Train.cpp @@ -7029,7 +7029,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 gauges = { + std::unordered_map const gauges = { { "mainctrl:", ggMainCtrl }, { "scndctrl:", ggScndCtrl }, { "dirkey:" , ggDirKey }, diff --git a/World.cpp b/World.cpp index 9d427367..06be5fff 100644 --- a/World.cpp +++ b/World.cpp @@ -1352,7 +1352,8 @@ TWorld::Update_UI() { uitextline1 += " (paused)"; } - if( Controlled && ( Controlled->Mechanik != nullptr ) ) { + if( ( Controlled != nullptr ) + && ( Controlled->Mechanik != nullptr ) ) { auto const &mover = Controlled->MoverParameters; auto const &driver = Controlled->Mechanik; @@ -1374,6 +1375,11 @@ TWorld::Update_UI() { uitextline3 += " Pressure: " + to_string( mover->BrakePress * 100.0, 2 ) + " kPa" + " (train pipe: " + to_string( mover->PipePress * 100.0, 2 ) + " kPa)"; + + auto const trackblockdistance{ std::abs( Controlled->Mechanik->TrackBlock() ) }; + if( trackblockdistance <= 75.0 ) { + uitextline4 = " Another vehicle ahead (distance: " + to_string( trackblockdistance, 1 ) + " m)"; + } } } diff --git a/renderer.cpp b/renderer.cpp index f2436148..e00f83a2 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -420,7 +420,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glColor4f( 1.f, 0.9f, 0.8f, 1.f ); ::glDisable( GL_LIGHTING ); ::glDisable( GL_TEXTURE_2D ); - if( true == Global::RenderShadows ) { + if( ( true == Global::RenderShadows ) && ( false == Global::bWireFrame ) ) { shadowcamera.draw( m_renderpass.camera.position() - shadowcamera.position() ); } if( DebugCameraFlag ) { @@ -2214,11 +2214,11 @@ opengl_renderer::Render( TAnimModel *Instance ) { switch( m_renderpass.draw_mode ) { case rendermode::shadows: { // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Instance->m_location - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + distancesquared = SquareMagnitude( ( Instance->location() - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } default: { - distancesquared = SquareMagnitude( ( Instance->m_location - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + distancesquared = SquareMagnitude( ( Instance->location() - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } } @@ -2246,7 +2246,7 @@ opengl_renderer::Render( TAnimModel *Instance ) { Instance->pModel, Instance->Material(), distancesquared, - Instance->m_location - m_renderpass.camera.position(), + Instance->location() - m_renderpass.camera.position(), Instance->vAngle ); } } @@ -2799,6 +2799,17 @@ void opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last ) { ::glColor3fv( glm::value_ptr( colors::white ) ); + // setup + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // NOTE: roads-based platforms tend to miss parts of shadows if rendered with either back or front culling + ::glDisable( GL_CULL_FACE ); + break; + } + default: { + break; + } + } // first pass, material 1 for( auto first { First }; first != Last; ++first ) { @@ -2824,7 +2835,16 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, track->EnvironmentReset(); break; } - case rendermode::shadows: // shadows are calculated only for trackbeds + case rendermode::shadows: { + if( ( std::abs( track->fTexHeight1 ) < 0.35f ) + || ( track->iCategoryFlag != 2 ) ) { + // shadows are only calculated for high enough roads, typically meaning track platforms + continue; + } + Bind_Material( track->m_material1 ); + m_geometry.draw( std::begin( track->Geometry1 ), std::end( track->Geometry1 ) ); + break; + } case rendermode::pickscenery: // pick scenery mode uses piece-by-piece approach case rendermode::pickcontrols: default: { @@ -2854,9 +2874,10 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, break; } case rendermode::shadows: { - if( ( track->iCategoryFlag != 1 ) - || ( track->eType != tt_Normal ) ) { - // shadows are only calculated for trackbeds + if( ( std::abs( track->fTexHeight1 ) < 0.35f ) + || ( ( track->iCategoryFlag == 1 ) + && ( track->eType != tt_Normal ) ) ) { + // shadows are only calculated for high enough trackbeds continue; } Bind_Material( track->m_material2 ); @@ -2870,6 +2891,16 @@ opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, } } } + // post-render reset + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + ::glEnable( GL_CULL_FACE ); + break; + } + default: { + break; + } + } } void @@ -3218,11 +3249,11 @@ opengl_renderer::Render_Alpha( TAnimModel *Instance ) { switch( m_renderpass.draw_mode ) { case rendermode::shadows: { // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Instance->m_location - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + distancesquared = SquareMagnitude( ( Instance->location() - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } default: { - distancesquared = SquareMagnitude( ( Instance->m_location - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + distancesquared = SquareMagnitude( ( Instance->location() - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } } @@ -3238,7 +3269,7 @@ opengl_renderer::Render_Alpha( TAnimModel *Instance ) { Instance->pModel, Instance->Material(), distancesquared, - Instance->m_location - m_renderpass.camera.position(), + Instance->location() - m_renderpass.camera.position(), Instance->vAngle ); } } @@ -3250,11 +3281,11 @@ opengl_renderer::Render_Alpha( TTraction *Traction ) { switch( m_renderpass.draw_mode ) { case rendermode::shadows: { // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Traction->m_location - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; + distancesquared = SquareMagnitude( ( Traction->location() - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } default: { - distancesquared = SquareMagnitude( ( Traction->m_location - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; + distancesquared = SquareMagnitude( ( Traction->location() - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; break; } } @@ -3272,15 +3303,29 @@ opengl_renderer::Render_Alpha( TTraction *Traction ) { return; } // setup +/* float const linealpha = static_cast( std::min( 1.25, 5000 * Traction->WireThickness / ( distancesquared + 1.0 ) ) ); // zbyt grube nie są dobre ::glLineWidth( linealpha ); +*/ + auto const distance { static_cast( std::sqrt( distancesquared ) ) }; + auto const linealpha { + 20.f * Traction->WireThickness + / std::max( + 0.5f * Traction->radius() + 1.f, + distance - ( 0.5f * Traction->radius() ) ) }; + ::glLineWidth( + clamp( + 0.5f * linealpha + Traction->WireThickness * Traction->radius() / 1000.f, + 1.f, 1.5f ) ); // McZapkie-261102: kolor zalezy od materialu i zasniedzenia - auto const color { Traction->wire_color() }; - ::glColor4f( color.r, color.g, color.b, linealpha ); - + ::glColor4fv( + glm::value_ptr( + glm::vec4{ + Traction->wire_color(), + linealpha } ) ); // render m_geometry.draw( Traction->m_geometry ); // debug data @@ -3318,12 +3363,15 @@ opengl_renderer::Render_Alpha( scene::lines_node const &Lines ) { 0.5f * data.area.radius + 1.f, distance - ( 0.5f * data.area.radius ) ) : 1.f ); // negative width means the lines are always opague + ::glLineWidth( + clamp( + 0.5f * linealpha + data.line_width * data.area.radius / 1000.f, + 1.f, 8.f ) ); ::glColor4fv( glm::value_ptr( glm::vec4{ glm::vec3{ data.lighting.diffuse * Global::DayLight.ambient }, // w zaleznosci od koloru swiatla std::min( 1.f, linealpha ) } ) ); - ::glLineWidth( clamp( 0.5f * linealpha + data.line_width * data.area.radius / 1000.f, 1.f, 8.f ) ); // render m_geometry.draw( data.geometry ); ++m_debugstats.lines; diff --git a/scene.cpp b/scene.cpp index 4cc80791..e9834f2b 100644 --- a/scene.cpp +++ b/scene.cpp @@ -242,12 +242,9 @@ basic_cell::insert( TTrack *Path ) { Path->RaOwnerSet( this ); #endif // re-calculate cell radius, in case track extends outside the cell's boundaries - auto endpoints = Path->endpoints(); - for( auto &endpoint : endpoints ) { - m_area.radius = std::max( - m_area.radius, - glm::length( m_area.center - endpoint ) + 25.f ); // extra margin to prevent driven vehicle from flicking - } + m_area.radius = std::max( + m_area.radius, + static_cast( glm::length( m_area.center - Path->location() ) + Path->radius() + 25.f ) ); // extra margin to prevent driven vehicle from flicking } // adds provided traction piece to the cell @@ -258,13 +255,8 @@ basic_cell::insert( TTraction *Traction ) { Traction->origin( m_area.center ); m_traction.emplace_back( Traction ); - // re-calculate cell radius, in case traction piece extends outside the cell's boundaries - auto endpoints = Traction->endpoints(); - for( auto &endpoint : endpoints ) { - m_area.radius = std::max( - m_area.radius, - glm::length( m_area.center - endpoint ) ); // adding arbitrary safety margin - } + // re-calculate cell bounding area, in case traction piece extends outside the cell's boundaries + enclose_area( Traction ); } // adds provided model instance to the cell @@ -289,15 +281,8 @@ basic_cell::insert( TAnimModel *Instance ) { // opaque pieces m_instancesopaque.emplace_back( Instance ); } - // re-calculate cell radius, in case model extends outside the cell's boundaries - if( Instance->Model() ) { - auto const modelradius{ Instance->Model()->bounding_radius() }; - if( modelradius > 0.f ) { - m_area.radius = std::max( - m_area.radius, - glm::length( m_area.center - Instance->location() ) + modelradius ); // adding arbitrary safety margin - } - } + // re-calculate cell bounding area, in case model extends outside the cell's boundaries + enclose_area( Instance ); } // adds provided sound instance to the cell @@ -316,6 +301,8 @@ basic_cell::insert( TEventLauncher *Launcher ) { m_active = true; m_eventlaunchers.emplace_back( Launcher ); + // re-calculate cell bounding area, in case launcher range extends outside the cell's boundaries + enclose_area( Launcher ); } // registers provided path in the lookup directory of the cell @@ -485,6 +472,15 @@ basic_cell::create_geometry( geometrybank_handle const &Bank ) { m_geometrycreated = true; // helper for legacy animation code, get rid of it after refactoring } +// adjusts cell bounding area to enclose specified node +void +basic_cell::enclose_area( editor::basic_node *Node ) { + + m_area.radius = std::max( + m_area.radius, + static_cast( glm::length( m_area.center - Node->location() ) + Node->radius() ) ); +} + // legacy method, updates sounds and polls event launchers within radius around specified point @@ -512,7 +508,7 @@ basic_section::update_traction( TDynamicObject *Vehicle, int const Pantographind auto pantograph = Vehicle->pants[ Pantographindex ].fParamPants; auto const pantographposition = position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x ); - auto const radius { EU07_CELLSIZE * 0.5 }; + auto const radius { EU07_CELLSIZE * 0.5 }; // redius around point of interest for( auto &cell : m_cells ) { // we reject early cells which aren't within our area of interest @@ -548,6 +544,11 @@ basic_section::insert( shape_node Shape ) { } else { // large, opaque shapes are placed on section level + // re-calculate section radius, in case shape geometry extends outside the section's boundaries + m_area.radius = std::max( + m_area.radius, + static_cast( glm::length( m_area.center - Shape.data().area.center ) + Shape.data().area.radius ) ); + for( auto &shape : m_shapes ) { // check first if the shape can't be merged with one of the shapes already present in the section if( true == shape.merge( Shape ) ) { @@ -739,6 +740,19 @@ basic_region::update_traction( TDynamicObject *Vehicle, int const Pantographinde } } +// stores content of the class in file with specified name +void +basic_region::serialize( std::string const &Scenariofile ) { + // TODO: implement +} + +// restores content of the class from file with specified name. returns: true on success, false otherwise +bool +basic_region::deserialize( std::string const &Scenariofile ) { + // TODO: implement + return false; +} + // legacy method, links specified path piece with potential neighbours void basic_region::TrackJoin( TTrack *Track ) { @@ -792,9 +806,9 @@ basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad, bool con // adjust input if necessary: if( true == Transform ) { // shapes generated from legacy terrain come with world space coordinates and don't need processing - if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) { + if( Scratchpad.location.rotation != glm::vec3( 0, 0, 0 ) ) { // rotate... - auto const rotation = glm::radians( Scratchpad.location_rotation ); + auto const rotation = glm::radians( Scratchpad.location.rotation ); for( auto &vertex : shape.m_data.vertices ) { vertex.position = glm::rotateZ( vertex.position, rotation.z ); vertex.position = glm::rotateX( vertex.position, rotation.x ); @@ -804,10 +818,10 @@ basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad, bool con vertex.normal = glm::rotateY( vertex.normal, rotation.y ); } } - if( ( false == Scratchpad.location_offset.empty() ) - && ( Scratchpad.location_offset.top() != glm::dvec3( 0, 0, 0 ) ) ) { - // ...and move - auto const offset = Scratchpad.location_offset.top(); + if( ( false == Scratchpad.location.offset.empty() ) + && ( Scratchpad.location.offset.top() != glm::dvec3( 0, 0, 0 ) ) ) { + // ...and move + auto const offset = Scratchpad.location.offset.top(); for( auto &vertex : shape.m_data.vertices ) { vertex.position += offset; } @@ -850,19 +864,19 @@ basic_region::insert_lines( lines_node Lines, scratch_data &Scratchpad ) { if( Lines.m_data.vertices.empty() ) { return; } // transform point coordinates if needed - if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) { + if( Scratchpad.location.rotation != glm::vec3( 0, 0, 0 ) ) { // rotate... - auto const rotation = glm::radians( Scratchpad.location_rotation ); + auto const rotation = glm::radians( Scratchpad.location.rotation ); for( auto &vertex : Lines.m_data.vertices ) { vertex.position = glm::rotateZ( vertex.position, rotation.z ); vertex.position = glm::rotateX( vertex.position, rotation.x ); vertex.position = glm::rotateY( vertex.position, rotation.y ); } } - if( ( false == Scratchpad.location_offset.empty() ) - && ( Scratchpad.location_offset.top() != glm::dvec3( 0, 0, 0 ) ) ) { + if( ( false == Scratchpad.location.offset.empty() ) + && ( Scratchpad.location.offset.top() != glm::dvec3( 0, 0, 0 ) ) ) { // ...and move - auto const offset = Scratchpad.location_offset.top(); + auto const offset = Scratchpad.location.offset.top(); for( auto &vertex : Lines.m_data.vertices ) { vertex.position += offset; } @@ -1073,7 +1087,7 @@ basic_region::sections( glm::dvec3 const &Point, float const Radius ) { int const originx = centerx - sectioncount / 2; int const originz = centerz - sectioncount / 2; - auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_SECTIONSIZE + 0.25 * EU07_SECTIONSIZE ) + Radius, 2 ) }; + auto const padding { 0.0 }; // { EU07_SECTIONSIZE * 0.25 }; // TODO: check if we can get away with padding of 0 for( int row = originz; row <= originz + sectioncount; ++row ) { if( row < 0 ) { continue; } @@ -1084,7 +1098,7 @@ basic_region::sections( glm::dvec3 const &Point, float const Radius ) { auto *section { m_sections[ row * EU07_REGIONSIDESECTIONCOUNT + column ] }; if( ( section != nullptr ) - && ( glm::length2( section->area().center - Point ) < squaredradii ) ) { + && ( glm::length2( section->area().center - Point ) <= ( ( section->area().radius + padding + Radius ) * ( section->area().radius + padding + Radius ) ) ) ) { m_scratchpad.sections.emplace_back( section ); } diff --git a/scene.h b/scene.h index 999d0514..8de91971 100644 --- a/scene.h +++ b/scene.h @@ -29,8 +29,16 @@ int const EU07_REGIONSIDESECTIONCOUNT = 500; // number of sections along a side struct scratch_data { - std::stack location_offset; - glm::vec3 location_rotation; + struct binary_data { + + bool terrain{ false }; + } binary; + + struct location_data { + + std::stack offset; + glm::vec3 rotation; + } location; struct trainset_data { @@ -129,6 +137,9 @@ private: using instance_sequence = std::vector; using sound_sequence = std::vector; using eventlauncher_sequence = std::vector; +// methods + void + enclose_area( editor::basic_node *Node ); // members scene::bounding_area m_area { glm::dvec3(), static_cast( 0.5 * M_SQRT2 * EU07_CELLSIZE ) }; bool m_active { false }; // whether the cell holds any actual data @@ -182,9 +193,9 @@ public: 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 = std::max( m_area.radius, - glm::length( m_area.center - targetcell.area().center ) + targetcell.area().radius ); } + static_cast( 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 void @@ -223,7 +234,7 @@ private: cell( glm::dvec3 const &Location ); // members // placement and visibility - scene::bounding_area m_area { glm::dvec3(), static_cast( 0.5 * M_SQRT2 * EU07_SECTIONSIZE + 0.25 * EU07_SECTIONSIZE ) }; + scene::bounding_area m_area { glm::dvec3(), static_cast( 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 @@ -250,6 +261,12 @@ public: // 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 ); diff --git a/scenenode.cpp b/scenenode.cpp index f72b1cb2..fc20188f 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -259,9 +259,9 @@ shape_node::convert( TSubModel const *Submodel ) { squareradius, glm::length2( vertex.position - m_data.area.center ) ); } - m_data.area.radius = std::max( + m_data.area.radius = std::max( m_data.area.radius, - std::sqrt( squareradius ) ); + static_cast( std::sqrt( squareradius ) ) ); return *this; } @@ -497,5 +497,23 @@ basic_node::basic_node( scene::node_data const &Nodedata ) : std::numeric_limits::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 //--------------------------------------------------------------------------- diff --git a/scenenode.h b/scenenode.h index 4b23d1ce..d30b420b 100644 --- a/scenenode.h +++ b/scenenode.h @@ -288,10 +288,12 @@ public: return m_name; } void location( glm::dvec3 const Location ) { - m_location = Location; } + m_area.center = Location; } glm::dvec3 const & location() const { - return m_location; }; + return m_area.center; }; + float const & + radius(); void visible( bool const Visible ) { m_visible = Visible; } @@ -300,8 +302,11 @@ public: return m_visible; } protected: +// methods + // radius() subclass details, calculates node's bounding radius + virtual void radius_(); // members - glm::dvec3 m_location; + 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 diff --git a/simulation.cpp b/simulation.cpp index 9bd163e5..1a99189c 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -38,11 +38,16 @@ state_manager::deserialize( std::string const &Scenariofile ) { // 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 ); + 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; @@ -67,9 +72,8 @@ state_manager::update( double const Deltatime, int Iterationcount ) { // restores class data from provided stream void -state_manager::deserialize( cParser &Input ) { +state_manager::deserialize( cParser &Input, scene::scratch_data &Scratchpad ) { - scene::scratch_data importscratchpad; // 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 &); @@ -98,7 +102,7 @@ state_manager::deserialize( cParser &Input ) { std::string, deserializefunctionbind> functionmap; for( auto &function : functionlist ) { - functionmap.emplace( function.first, std::bind( function.second, this, std::ref( Input ), std::ref( importscratchpad ) ) ); + functionmap.emplace( function.first, std::bind( function.second, this, std::ref( Input ), std::ref( Scratchpad ) ) ); } // deserialize content from the provided input @@ -127,9 +131,9 @@ state_manager::deserialize( cParser &Input ) { token = Input.getToken(); } - if( false == importscratchpad.initialized ) { + if( false == Scratchpad.initialized ) { // manually perform scenario initialization - deserialize_firstinit( Input, importscratchpad ); + deserialize_firstinit( Input, Scratchpad ); } } @@ -236,12 +240,12 @@ state_manager::deserialize_event( cParser &Input, scene::scratch_data &Scratchpa // 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() ? + Scratchpad.location.offset.empty() ? Math3D::vector3() : Math3D::vector3( - Scratchpad.location_offset.top().x, - Scratchpad.location_offset.top().y, - Scratchpad.location_offset.top().z ) ); + 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 ) ) { @@ -386,20 +390,34 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad || ( nodedata.type == "triangle_strip" ) || ( nodedata.type == "triangle_fan" ) ) { - simulation::Region->insert_shape( - scene::shape_node().deserialize( - Input, nodedata ), - Scratchpad, - true ); + 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" ) ) { - simulation::Region->insert_lines( - scene::lines_node().deserialize( - Input, nodedata ), - Scratchpad ); + 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" ) { @@ -448,18 +466,18 @@ state_manager::deserialize_origin( cParser &Input, scene::scratch_data &Scratchp >> offset.y >> offset.z; // sumowanie całkowitego przesunięcia - Scratchpad.location_offset.emplace( + Scratchpad.location.offset.emplace( offset + ( - Scratchpad.location_offset.empty() ? + Scratchpad.location.offset.empty() ? glm::dvec3() : - Scratchpad.location_offset.top() ) ); + 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(); + 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 ) + ")" ); @@ -471,9 +489,9 @@ state_manager::deserialize_rotate( cParser &Input, scene::scratch_data &Scratchp Input.getTokens( 3 ); Input - >> Scratchpad.location_rotation.x - >> Scratchpad.location_rotation.y - >> Scratchpad.location_rotation.z; + >> Scratchpad.location.rotation.x + >> Scratchpad.location.rotation.y + >> Scratchpad.location.rotation.z; } void @@ -584,12 +602,12 @@ state_manager::deserialize_path( cParser &Input, scene::scratch_data &Scratchpad // 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() ? + Scratchpad.location.offset.empty() ? Math3D::vector3() : Math3D::vector3( - Scratchpad.location_offset.top().x, - Scratchpad.location_offset.top().y, - Scratchpad.location_offset.top().z ) ); + Scratchpad.location.offset.top().x, + Scratchpad.location.offset.top().y, + Scratchpad.location.offset.top().z ) ); track->Load( &Input, offset ); return track; @@ -605,9 +623,9 @@ state_manager::deserialize_traction( cParser &Input, scene::scratch_data &Scratc // 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() ? + Scratchpad.location.offset.empty() ? glm::dvec3() : - Scratchpad.location_offset.top() ); + Scratchpad.location.offset.top() ); traction->Load( &Input, offset ); return traction; @@ -670,7 +688,7 @@ state_manager::deserialize_model( cParser &Input, scene::scratch_data &Scratchpa >> rotation.y; auto *instance = new TAnimModel( Nodedata ); - instance->RaAnglesSet( Scratchpad.location_rotation + rotation ); // dostosowanie do pochylania linii + instance->RaAnglesSet( Scratchpad.location.rotation + rotation ); // dostosowanie do pochylania linii if( false == instance->Load( &Input, false ) ) { // model nie wczytał się - ignorowanie node @@ -838,12 +856,12 @@ state_manager::skip_until( cParser &Input, std::string const &Token ) { 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 ); + if( Scratchpad.location.rotation != glm::vec3( 0, 0, 0 ) ) { + auto const rotation = glm::radians( Scratchpad.location.rotation ); Location = glm::rotateY( Location, rotation.y ); // Ra 2014-11: uwzględnienie rotacji } - if( false == Scratchpad.location_offset.empty() ) { - Location += Scratchpad.location_offset.top(); + if( false == Scratchpad.location.offset.empty() ) { + Location += Scratchpad.location.offset.top(); } return Location; } diff --git a/simulation.h b/simulation.h index 14740ecd..4addbe49 100644 --- a/simulation.h +++ b/simulation.h @@ -40,7 +40,7 @@ public: private: // methods // restores class data from provided stream - void deserialize( cParser &Input ); + 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 ); diff --git a/version.h b/version.h index e45b45fa..6fe44f69 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 1025 +#define VERSION_MINOR 1027 #define VERSION_REVISION 0 From da396836e0081e9ee72d755b2a0b7b26575bff76 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 28 Oct 2017 16:18:53 +0200 Subject: [PATCH 24/31] maintenance: removed deprecated ground code --- AnimModel.cpp | 25 +- Driver.cpp | 16 - Driver.h | 8 - DynObj.cpp | 26 +- EU07.cpp | 7 +- Event.cpp | 35 +- Event.h | 6 - Globals.cpp | 34 +- Globals.h | 8 - MemCell.cpp | 23 +- MemCell.h | 4 - Model3d.cpp | 60 --- Model3d.h | 6 - Names.h | 56 --- Track.cpp | 186 +-------- Track.h | 21 -- Traction.cpp | 14 +- Traction.h | 4 - TractionPower.cpp | 7 +- TrkFoll.cpp | 25 +- World.cpp | 643 +++++++++++--------------------- World.h | 4 - maszyna.vcxproj.filters | 6 - messaging.cpp | 80 ---- messaging.h | 8 - Ground.cpp => old/Ground.cpp | 0 Ground.h => old/Ground.h | 0 renderer.cpp | 704 +---------------------------------- renderer.h | 41 -- scene.cpp | 6 +- stdafx.h | 2 - 31 files changed, 288 insertions(+), 1777 deletions(-) rename Ground.cpp => old/Ground.cpp (100%) rename Ground.h => old/Ground.h (100%) diff --git a/AnimModel.cpp b/AnimModel.cpp index fd994fe4..b26d91a0 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -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(){}; @@ -234,11 +231,7 @@ void TAnimContainer::UpdateModel() { iAnim &= ~2; // wyłączyć zmianę pozycji submodelu if( evDone ) { // wykonanie eventu informującego o zakończeniu -#ifdef EU07_USE_OLD_GROUNDCODE - Global::AddToQuery( evDone, NULL ); -#else simulation::Events.AddToQuery( evDone, nullptr ); -#endif } } } @@ -306,11 +299,7 @@ void TAnimContainer::UpdateModel() { fRotateSpeed = 0.0; if( evDone ) { // wykonanie eventu informującego o zakończeniu -#ifdef EU07_USE_OLD_GROUNDCODE - Global::AddToQuery( evDone, NULL ); -#else simulation::Events.AddToQuery( evDone, nullptr ); -#endif } } } @@ -340,11 +329,7 @@ void TAnimContainer::PrepareModel() fAngleSpeed = 0.0; // wyłączenie przeliczania wektora if( evDone ) { // wykonanie eventu informującego o zakończeniu -#ifdef EU07_USE_OLD_GROUNDCODE - Global::AddToQuery( evDone, NULL ); -#else simulation::Events.AddToQuery( evDone, nullptr ); -#endif } } else diff --git a/Driver.cpp b/Driver.cpp index 772f78af..585423ae 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -21,7 +21,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" @@ -3002,24 +3001,13 @@ void TController::RecognizeCommand() 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 -#ifdef EU07_USE_OLD_GROUNDCODE - vector3 sl; - sl.x = -NewLocation.X; // zamiana na współrzędne scenerii - sl.z = NewLocation.Y; - sl.y = NewLocation.Z; -#else // zamiana na współrzędne scenerii glm::dvec3 sl { -NewLocation.X, NewLocation.Z, NewLocation.Y }; -#endif if (!PutCommand(NewCommand, NewValue1, NewValue2, &sl, reason)) mvOccupied->PutCommand(NewCommand, NewValue1, NewValue2, NewLocation); } -#ifdef EU07_USE_OLD_GROUNDCODE -bool TController::PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const vector3 *NewLocation, TStopReason reason) -#else bool TController::PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *NewLocation, TStopReason reason ) -#endif { // analiza komendy if (NewCommand == "CabSignal") { // SHP wyzwalane jest przez człon z obsadą, ale obsługiwane przez silnikowy @@ -5339,11 +5327,7 @@ TTrack * TController::BackwardTraceRoute(double &fDistance, double &fDirection, } // sprawdzanie zdarzeń semaforów i ograniczeń szlakowych -#ifdef EU07_USE_OLD_GROUNDCODE -void TController::SetProximityVelocity(double dist, double vel, const vector3 *pos) -#else void TController::SetProximityVelocity( double dist, double vel, glm::dvec3 const *pos ) -#endif { // Ra:przeslanie do AI prędkości /* //!!!! zastąpić prawidłową reakcją AI na SetProximityVelocity !!!! diff --git a/Driver.h b/Driver.h index d669b498..e4ee16e6 100644 --- a/Driver.h +++ b/Driver.h @@ -314,11 +314,7 @@ private: Mtable::TTrainParameters *Timetable() { return TrainParams; }; void PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const TLocation &NewLocation, TStopReason reason = stopComm); -#ifdef EU07_USE_OLD_GROUNDCODE - bool PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const vector3 *NewLocation, TStopReason reason = stopComm); -#else bool PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *NewLocation, TStopReason reason = stopComm ); -#endif void UpdateSituation(double dt); // uruchamiac przynajmniej raz na sekundę // procedury dotyczace rozkazow dla maszynisty // uaktualnia informacje o prędkości @@ -378,11 +374,7 @@ private: bool BackwardTrackBusy(TTrack *Track); TEvent *CheckTrackEventBackward(double fDirection, TTrack *Track); TTrack *BackwardTraceRoute(double &fDistance, double &fDirection, TTrack *Track, TEvent *&Event); -#ifdef EU07_USE_OLD_GROUNDCODE - void SetProximityVelocity(double dist, double vel, const vector3 *pos); -#else void SetProximityVelocity( double dist, double vel, glm::dvec3 const *pos ); -#endif TCommandType BackwardScan(); public: diff --git a/DynObj.cpp b/DynObj.cpp index e04807f0..92a8a6ef 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -15,32 +15,18 @@ 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 "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 "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° @@ -4909,12 +4895,8 @@ void TDynamicObject::RadioStop() if( ( MoverParameters->SecuritySystem.RadioStop ) && ( MoverParameters->Radio ) ) { // jeśli pojazd ma RadioStop i jest on aktywny -#ifdef EU07_USE_OLD_GROUNDCODE - Mechanik->PutCommand( "Emergency_brake", 1.0, 1.0, &vPosition, stopRadio ); -#else // HAX cast until math types unification Mechanik->PutCommand( "Emergency_brake", 1.0, 1.0, &static_cast(vPosition), stopRadio ); -#endif // add onscreen notification for human driver // TODO: do it selectively for the 'local' driver once the multiplayer is in if( false == Mechanik->AIControllFlag ) { diff --git a/EU07.cpp b/EU07.cpp index 590890fa..eba5e362 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -20,17 +20,16 @@ Stele, firleju, szociu, hunter, ZiomalCl, OLI_EU and others #include #endif +#include "World.h" +#include "simulation.h" #include "Globals.h" +#include "timer.h" #include "Logs.h" #include "keyboardinput.h" #include "mouseinput.h" #include "gamepadinput.h" #include "Console.h" #include "PyInt.h" -#include "World.h" -#include "Mover.h" -#include "usefull.h" -#include "timer.h" #include "uilayer.h" #ifdef EU07_BUILD_STATIC diff --git a/Event.cpp b/Event.cpp index 8755912d..15acc047 100644 --- a/Event.cpp +++ b/Event.cpp @@ -16,18 +16,10 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "event.h" -#include "Globals.h" -#include "Logs.h" -#include "Usefull.h" -#include "parser.h" -#include "Timer.h" -#include "MemCell.h" -#include "Ground.h" -#include "McZapkie\mctools.h" -#include "animmodel.h" -#include "dynobj.h" -#include "driver.h" -#include "tractionpower.h" +#include "simulation.h" +#include "globals.h" +#include "timer.h" +#include "logs.h" TEvent::TEvent( std::string const &m ) : asNodeName( m ) @@ -745,7 +737,7 @@ event_manager::update() { // NOTE: we're presuming global events aren't going to use event2 WriteLog( "Eventlauncher " + launcher->name() ); if( launcher->Event1 ) { - Global::AddToQuery( launcher->Event1, nullptr ); + AddToQuery( launcher->Event1, nullptr ); } } } @@ -850,11 +842,7 @@ event_manager::AddToQuery( TEvent *Event, TDynamicObject *Owner ) { for( auto dynamic : Event->Params[ 6 ].asTrack->Dynamics ) { Event->Params[ 5 ].asMemCell->PutCommand( dynamic->Mechanik, -#ifdef EU07_USE_OLD_GROUNDCODE - &Event->Params[ 4 ].nGroundNode->pCenter ); -#else Event->Params[ 4 ].asLocation ); -#endif } //if (DebugModeFlag) WriteLog( @@ -952,11 +940,7 @@ event_manager::CheckQuery() { for( auto dynamic : m_workevent->Params[ 6 ].asTrack->Dynamics ) { m_workevent->Params[ 5 ].asMemCell->PutCommand( dynamic->Mechanik, -#ifdef EU07_USE_OLD_GROUNDCODE - &m_workevent->Params[ 4 ].nGroundNode->pCenter ); -#else m_workevent->Params[ 4 ].asLocation ); -#endif } //if (DebugModeFlag) WriteLog("Type: UpdateValues & Track command - [" + @@ -980,11 +964,7 @@ event_manager::CheckQuery() { } m_workevent->Params[ 9 ].asMemCell->PutCommand( m_workevent->Activator->Mechanik, -#ifdef EU07_USE_OLD_GROUNDCODE - &m_workevent->Params[ 8 ].nGroundNode->pCenter ); -#else m_workevent->Params[ 8 ].asLocation ); -#endif } WriteLog( "Type: GetValues" ); break; @@ -1028,13 +1008,8 @@ event_manager::CheckQuery() { break; } case tp_Visible: { -#ifdef EU07_USE_OLD_GROUNDCODE - if( m_workevent->Params[ 9 ].nGroundNode ) - m_workevent->Params[ 9 ].nGroundNode->bVisible = ( m_workevent->Params[ i ].asInt > 0 ); -#else if( m_workevent->Params[ 9 ].asEditorNode ) m_workevent->Params[ 9 ].asEditorNode->visible( m_workevent->Params[ i ].asInt > 0 ); -#endif break; } case tp_Velocity: { diff --git a/Event.h b/Event.h index ad32a5b0..d8c90562 100644 --- a/Event.h +++ b/Event.h @@ -63,9 +63,6 @@ union TParam { void *asPointer; TMemCell *asMemCell; -#ifdef EU07_USE_OLD_GROUNDCODE - TGroundNode *nGroundNode; -#endif editor::basic_node *asEditorNode; glm::dvec3 const *asLocation; TTrack *asTrack; @@ -94,9 +91,6 @@ class TEvent // zmienne: ev* bool bEnabled = false; // false gdy ma nie być dodawany do kolejki (skanowanie sygnałów) int iQueued = 0; // ile razy dodany do kolejki TEvent *evNext = nullptr; // następny w kolejce -#ifdef EU07_USE_OLD_GROUNDCODE - TEvent *evNext2 = nullptr; -#endif TEventType Type = tp_Unknown; double fStartTime = 0.0; double fDelay = 0.0; diff --git a/Globals.cpp b/Globals.cpp index 914f3b74..43c66c17 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -12,23 +12,17 @@ http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" +#include "globals.h" -#include "Globals.h" -#include "usefull.h" -//#include "Mover.h" +#include "world.h" +#include "simulation.h" +#include "logs.h" #include "Console.h" -#include "Driver.h" -#include "Logs.h" #include "PyInt.h" -#include "World.h" -#include "parser.h" // namespace Global { // parametry do użytku wewnętrznego -#ifdef EU07_USE_OLD_GROUNDCODE -TGround *Global::pGround = NULL; -#endif std::string Global::AppName{ "EU07" }; std::string Global::asCurrentSceneryPath = "scenery/"; std::string Global::asCurrentTexturePath = std::string(szTexturePath); @@ -987,26 +981,6 @@ void Global::TrainDelete(TDynamicObject *d) pWorld->TrainDelete(d); }; -#ifdef EU07_USE_OLD_GROUNDCODE -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); -}; -#endif -bool Global::AddToQuery(TEvent *event, TDynamicObject *who) -{ -#ifdef EU07_USE_OLD_GROUNDCODE - return pGround->AddToQuery(event, who); -#else - return simulation::Events.AddToQuery( event, who ); -#endif -}; - //--------------------------------------------------------------------------- bool Global::DoEvents() diff --git a/Globals.h b/Globals.h index fac2576d..478ec2a3 100644 --- a/Globals.h +++ b/Globals.h @@ -161,11 +161,6 @@ public: static void SetCameraPosition(Math3D::vector3 pNewCameraPosition); static void SetCameraRotation(double Yaw); static void TrainDelete(TDynamicObject *d); -#ifdef EU07_USE_OLD_GROUNDCODE - static TDynamicObject * DynamicNearest(); - static TDynamicObject * CouplerNearest(); -#endif - static bool AddToQuery(TEvent *event, TDynamicObject *who); static bool DoEvents(); static std::string Bezogonkow(std::string str, bool _ = false); static double Min0RSpeed(double vel1, double vel2); @@ -195,9 +190,6 @@ public: static bool bLiveTraction; static float Global::fMouseXScale; static float Global::fMouseYScale; -#ifdef EU07_USE_OLD_GROUNDCODE - static TGround *pGround; -#endif static std::string szDefaultExt; static std::string SceneryFile; static std::string AppName; diff --git a/MemCell.cpp b/MemCell.cpp index 972f89df..8850c4fa 100644 --- a/MemCell.cpp +++ b/MemCell.cpp @@ -14,14 +14,10 @@ http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" -#include "MemCell.h" +#include "memcell.h" -#include "Globals.h" -#include "Logs.h" -#include "Usefull.h" -#include "Driver.h" -#include "Event.h" -#include "parser.h" +#include "simulation.h" +#include "logs.h" //--------------------------------------------------------------------------- @@ -104,16 +100,11 @@ TCommandType TMemCell::CommandCheck() bool TMemCell::Load(cParser *parser) { std::string token; -#ifdef EU07_USE_OLD_GROUNDCODE - parser->getTokens( 3, false ); - *parser -#else parser->getTokens( 6, false ); *parser >> m_area.center.x >> m_area.center.y >> m_area.center.z -#endif >> szText >> fValue1 >> fValue2; @@ -129,11 +120,7 @@ bool TMemCell::Load(cParser *parser) return true; } -#ifdef EU07_USE_OLD_GROUNDCODE -void TMemCell::PutCommand(TController *Mech, vector3 *Loc) -#else void TMemCell::PutCommand( TController *Mech, glm::dvec3 const *Loc ) -#endif { // wysłanie zawartości komórki do AI if (Mech) Mech->PutCommand(szText, fValue1, fValue2, Loc); @@ -178,11 +165,7 @@ void TMemCell::StopCommandSent() bCommand = false; if( OnSent ) { // jeśli jest event -#ifdef EU07_USE_OLD_GROUNDCODE - Global::AddToQuery( OnSent, NULL ); -#else simulation::Events.AddToQuery( OnSent, nullptr ); -#endif } }; diff --git a/MemCell.h b/MemCell.h index a89d7580..27e4cf17 100644 --- a/MemCell.h +++ b/MemCell.h @@ -28,11 +28,7 @@ public: bool Load(cParser *parser); void -#ifdef EU07_USE_OLD_GROUNDCODE - PutCommand( TController *Mech, Math3D::vector3 *Loc ); -#else PutCommand( TController *Mech, glm::dvec3 const *Loc ); -#endif bool Compare( std::string const &szTestText, double const fTestValue1, double const fTestValue2, int const CheckMask ); std::string const & diff --git a/Model3d.cpp b/Model3d.cpp index a24ebc2e..9e9200b5 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -19,9 +19,6 @@ Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others #include "logs.h" #include "mczapkie/mctools.h" #include "Usefull.h" -#ifdef EU07_USE_OLD_GROUNDCODE -#include "ground.h" -#endif #include "renderer.h" #include "Timer.h" #include "mtable.h" @@ -1033,64 +1030,7 @@ TSubModel::create_geometry( std::size_t &Dataoffset, geometrybank_handle const & if( Next ) Next->create_geometry( Dataoffset, Bank ); } -#ifdef EU07_USE_OLD_GROUNDCODE -// 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; } - - int vertexcount { 0 }; - std::vector 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 ); - } - // start a new triangle - vertexcount = -1; - } - ++vertexcount; - } - if( Groundnode.Piece == nullptr ) { - Groundnode.Piece = new piece_node(); - } - Groundnode.iNumVerts = importedvertices.size(); - if( Groundnode.iNumVerts > 0 ) { - // assign imported geometry to the node... - Groundnode.Piece->vertices.swap( importedvertices ); - // ...and calculate center... - for( auto const &vertex : Groundnode.Piece->vertices ) { - Groundnode.pCenter += vertex.position; - } - Groundnode.pCenter /= Groundnode.iNumVerts; - // ...and bounding area - double squareradius { 0.0 }; - for( auto const &vertex : Groundnode.Piece->vertices ) { - squareradius = std::max( squareradius, glm::length2( vertex.position - glm::dvec3{ Groundnode.pCenter } ) ); - } - Groundnode.fSquareRadius += squareradius; - } -} -#endif void TSubModel::ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ) { // ustawienie kolorów dla modelu terenu f4Ambient = glm::vec4( Ambient, 1.0f ); diff --git a/Model3d.h b/Model3d.h index 863c962d..8a0cb8b5 100644 --- a/Model3d.h +++ b/Model3d.h @@ -50,9 +50,6 @@ enum TAnimType // rodzaj animacji at_Undefined = 0x800000FF // animacja chwilowo nieokreślona }; -#ifdef EU07_USE_OLD_GROUNDCODE -class TGroundNode; -#endif namespace scene { class shape_node; } @@ -216,9 +213,6 @@ public: std::vector&); void serialize_geometry( std::ostream &Output ) const; // places contained geometry in provided ground node -#ifdef EU07_USE_OLD_GROUNDCODE - void convert( TGroundNode &Groundnode ) const; -#endif }; class TModel3d : public CMesh diff --git a/Names.h b/Names.h index e28028fd..209ed9c3 100644 --- a/Names.h +++ b/Names.h @@ -12,62 +12,6 @@ http://mozilla.org/MPL/2.0/. #include #include -#ifdef EU07_USE_OLD_GROUNDCODE -template -class TNames { - -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 - 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 - 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 &map = find_map( Type ); - auto const lookup = map.find( Name ); - if( lookup != map.end() ) { return lookup->second; } - else { return nullptr; } - } - -private: -// types: - typedef std::unordered_map type_map; - typedef std::unordered_map typemap_map; - -// 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 -}; -#endif - template class basic_table { diff --git a/Track.cpp b/Track.cpp index a560e0b9..fcc415ca 100644 --- a/Track.cpp +++ b/Track.cpp @@ -13,20 +13,12 @@ http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" -#include "Track.h" -#include "Globals.h" -#include "Logs.h" -#include "Usefull.h" -#include "renderer.h" -#include "Timer.h" -#include "Ground.h" -#include "parser.h" -#include "Mover.h" -#include "DynObj.h" -#include "AnimModel.h" -#include "MemCell.h" -#include "Event.h" +#include "track.h" + #include "simulation.h" +#include "globals.h" +#include "timer.h" +#include "logs.h" // 101206 Ra: trapezoidalne drogi i tory // 110720 Ra: rozprucie zwrotnicy i odcinki izolowane @@ -37,13 +29,11 @@ static float const fMaxOffset = 0.1f; // double(0.1f)==0.100000001490116 const int iLewo4[4] = {5, 3, 4, 6}; // segmenty (1..6) do skręcania w lewo const int iPrawo4[4] = {-4, -6, -3, -5}; // segmenty (1..6) do skręcania w prawo const int iProsto4[4] = {1, -1, 2, -2}; // segmenty (1..6) do jazdy prosto -const int iEnds4[13] = {3, 0, 2, 1, 2, 0, -1, - 1, 3, 2, 0, 3, 1}; // numer sąsiedniego toru na końcu segmentu "-1" +const int iEnds4[13] = {3, 0, 2, 1, 2, 0, -1, 1, 3, 2, 0, 3, 1}; // numer sąsiedniego toru na końcu segmentu "-1" const int iLewo3[4] = {1, 3, 2, 1}; // segmenty do skręcania w lewo const int iPrawo3[4] = {-2, -1, -3, -2}; // segmenty do skręcania w prawo const int iProsto3[4] = {1, -1, 2, 1}; // segmenty do jazdy prosto -const int iEnds3[13] = {3, 0, 2, 1, 2, 0, -1, - 1, 0, 2, 0, 3, 1}; // numer sąsiedniego toru na końcu segmentu "-1" +const int iEnds3[13] = {3, 0, 2, 1, 2, 0, -1, 1, 0, 2, 0, 3, 1}; // numer sąsiedniego toru na końcu segmentu "-1" TIsolated *TIsolated::pRoot = NULL; TSwitchExtension::TSwitchExtension(TTrack *owner, int const what) @@ -114,7 +104,7 @@ void TIsolated::Modify(int i, TDynamicObject *o) if (!iAxles) { // jeśli po zmianie nie ma żadnej osi na odcinku izolowanym if (evFree) - Global::AddToQuery(evFree, o); // dodanie zwolnienia do kolejki + simulation::Events.AddToQuery(evFree, o); // dodanie zwolnienia do kolejki if (Global::iMultiplayer) // jeśli multiplayer multiplayer::WyslijString(asName, 10); // wysłanie pakietu o zwolnieniu if (pMemCell) // w powiązanej komórce @@ -128,7 +118,7 @@ void TIsolated::Modify(int i, TDynamicObject *o) if (iAxles) { if (evBusy) - Global::AddToQuery(evBusy, o); // dodanie zajętości do kolejki + simulation::Events.AddToQuery(evBusy, o); // dodanie zajętości do kolejki if (Global::iMultiplayer) // jeśli multiplayer multiplayer::WyslijString(asName, 11); // wysłanie pakietu o zajęciu if (pMemCell) // w powiązanej komórce @@ -185,18 +175,6 @@ TTrack::sort_by_material( TTrack const *Left, TTrack const *Right ) { TTrack * TTrack::Create400m(int what, double dx) { // tworzenie toru do wstawiania taboru podczas konwersji na E3D -#ifdef EU07_USE_OLD_GROUNDCODE - TGroundNode *tmp = new TGroundNode(TP_TRACK); // node - TTrack *trk = tmp->pTrack; - trk->m_visible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur - trk->iCategoryFlag = what; // taki sam typ plus informacja, że dodatkowy - trk->Init(); // utworzenie segmentu - trk->Segment->Init(vector3(-dx, 0, 0), vector3(-dx, 0, 400), 10.0, 0, 0); // prosty - tmp->pCenter = vector3(-dx, 0, 200); //środek, aby się mogło wyświetlić - TSubRect *r = Global::pGround->GetSubRect(tmp->pCenter.x, tmp->pCenter.z); - r->NodeAdd(tmp); // dodanie toru do segmentu - r->Sort(); //żeby wyświetlał tabor z dodanego toru -#else auto *trk = new TTrack( "auto_400m" ); trk->m_visible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur trk->iCategoryFlag = what; // taki sam typ plus informacja, że dodatkowy @@ -205,112 +183,11 @@ TTrack * TTrack::Create400m(int what, double dx) trk->location( glm::dvec3{ -dx, 0, 200 } ); //środek, aby się mogło wyświetlić simulation::Paths.insert( trk ); simulation::Region->insert_path( trk, scene::scratch_data() ); -#endif return trk; }; TTrack * TTrack::NullCreate(int dir) { // tworzenie toru wykolejającego od strony (dir), albo pętli dla samochodów -#ifdef EU07_USE_OLD_GROUNDCODE - TGroundNode *tmp = new TGroundNode( TP_TRACK ); - TGroundNode *tmp2 = nullptr; // node - TTrack *trk = tmp->pTrack; // tor; UWAGA! obrotnica może generować duże ilości tego - trk->m_visible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur - // trk->iTrapezoid=1; //są przechyłki do uwzględniania w rysowaniu - trk->iCategoryFlag = (iCategoryFlag & 15) | 0x80; // taki sam typ plus informacja, że dodatkowy - float r1, r2; - Segment->GetRolls(r1, r2); // pobranie przechyłek na początku toru - vector3 p1, cv1, cv2, p2; // będziem tworzyć trajektorię lotu - if (iCategoryFlag & 1) - { // tylko dla kolei - trk->iDamageFlag = 128; // wykolejenie - trk->fVelocity = 0.0; // koniec jazdy - trk->Init(); // utworzenie segmentu - switch (dir) - { //łączenie z nowym torem - case 0: - p1 = Segment->FastGetPoint_0(); - p2 = p1 - 450.0 * Normalize(Segment->GetDirection1()); - // bo prosty, kontrolne wyliczane przy zmiennej przechyłce - trk->Segment->Init(p1, p2, 5, -RadToDeg(r1), 70.0); - ConnectPrevPrev(trk, 0); - break; - case 1: - p1 = Segment->FastGetPoint_1(); - p2 = p1 - 450.0 * Normalize(Segment->GetDirection2()); - // bo prosty, kontrolne wyliczane przy zmiennej przechyłce - trk->Segment->Init(p1, p2, 5, RadToDeg(r2), 70.0); - ConnectNextPrev(trk, 0); - break; - case 3: // na razie nie możliwe - p1 = SwitchExtension->Segments[1]->FastGetPoint_1(); // koniec toru drugiego zwrotnicy - p2 = p1 - 450.0 * Normalize( SwitchExtension->Segments[1]->GetDirection2()); // przedłużenie na wprost - trk->Segment->Init(p1, p2, 5, RadToDeg(r2), 70.0); // bo prosty, kontrolne wyliczane przy zmiennej przechyłce - ConnectNextPrev(trk, 0); - // trk->ConnectPrevNext(trk,dir); - SetConnections(1); // skopiowanie połączeń - Switch(1); // bo się przełączy na 0, a to coś chce się przecież wykoleić na bok - break; // do drugiego zwrotnicy... nie zadziała? - } - } - else - { // tworznie pętelki dla samochodów - trk->fVelocity = 20.0; // zawracanie powoli - trk->fRadius = 20.0; // promień, aby się dodawało do tabelki prędkości i liczyło narastająco - trk->Init(); // utworzenie segmentu - tmp2 = new TGroundNode(TP_TRACK); // drugi odcinek do zapętlenia - TTrack *trk2 = tmp2->pTrack; - trk2->iCategoryFlag = - (iCategoryFlag & 15) | 0x80; // taki sam typ plus informacja, że dodatkowy - trk2->m_visible = false; - trk2->fVelocity = 20.0; // zawracanie powoli - trk2->fRadius = 20.0; // promień, aby się dodawało do tabelki prędkości i liczyło - // narastająco - trk2->Init(); // utworzenie segmentu - trk->m_name = m_name + ":loopstart"; - trk2->m_name = m_name + ":loopfinish"; - switch (dir) - { //łączenie z nowym torem - case 0: - p1 = Segment->FastGetPoint_0(); - cv1 = -20.0 * Normalize(Segment->GetDirection1()); // pierwszy wektor kontrolny - p2 = p1 + cv1 + cv1; // 40m - // bo prosty, kontrolne wyliczane przy zmiennej przechyłce - trk->Segment->Init(p1, p1 + cv1, p2 + vector3(-cv1.z, cv1.y, cv1.x), p2, 2, -RadToDeg(r1), 0.0); - ConnectPrevPrev(trk, 0); - // bo prosty, kontrolne wyliczane przy zmiennej przechyłce - trk2->Segment->Init(p1, p1 + cv1, p2 + vector3(cv1.z, cv1.y, -cv1.x), p2, 2, -RadToDeg(r1), 0.0); - trk2->iPrevDirection = 0; // zwrotnie do tego samego odcinka - break; - case 1: - p1 = Segment->FastGetPoint_1(); - cv1 = -20.0 * Normalize(Segment->GetDirection2()); // pierwszy wektor kontrolny - p2 = p1 + cv1 + cv1; - // bo prosty, kontrolne wyliczane przy zmiennej przechyłce - trk->Segment->Init(p1, p1 + cv1, p2 + vector3(-cv1.z, cv1.y, cv1.x), p2, 2, RadToDeg(r2), 0.0); - ConnectNextPrev(trk, 0); - // bo prosty, kontrolne wyliczane przy zmiennej przechyłce - trk2->Segment->Init(p1, p1 + cv1, p2 + vector3(cv1.z, cv1.y, -cv1.x), p2, 2, RadToDeg(r2), 0.0); - trk2->iPrevDirection = 1; // zwrotnie do tego samego odcinka - break; - } - trk2->trPrev = this; - trk->ConnectNextNext(trk2, 1); // połączenie dwóch dodatkowych odcinków punktami 2 - tmp2->pCenter = (0.5 * (p1 + p2)); //środek, aby się mogło wyświetlić - } - // trzeba jeszcze dodać do odpowiedniego segmentu, aby się renderowały z niego pojazdy - tmp->pCenter = (0.5 * (p1 + p2)); //środek, aby się mogło wyświetlić - if (tmp2) - tmp2->pCenter = tmp->pCenter; // ten sam środek jest - // Ra: to poniżej to porażka, ale na razie się nie da inaczej - TSubRect *r = Global::pGround->GetSubRect(tmp->pCenter.x, tmp->pCenter.z); - if( r != nullptr ) { - r->NodeAdd( tmp ); // dodanie toru do segmentu - if( tmp2 ) - r->NodeAdd( tmp2 ); // drugiego też - r->Sort(); //żeby wyświetlał tabor z dodanego toru - } -#else TTrack *trk { nullptr }, *trk2 { nullptr }; @@ -403,7 +280,6 @@ TTrack * TTrack::NullCreate(int dir) simulation::Paths.insert( trk2 ); simulation::Region->insert_path( trk2, scene::scratch_data() ); } -#endif return trk; }; @@ -1173,18 +1049,11 @@ bool TTrack::InMovement() return false; }; -#ifdef EU07_USE_OLD_GROUNDCODE -void TTrack::RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joined) -#else void TTrack::RaAssign( TAnimModel *am, TEvent *done, TEvent *joined ) -#endif { // Ra: wiązanie toru z modelem obrotnicy if (eType == tt_Table) { SwitchExtension->pModel = am; -#ifdef EU07_USE_OLD_GROUNDCODE - SwitchExtension->pMyNode = gn; -#endif SwitchExtension->evMinus = done; // event zakończenia animacji (zadanie nowej przedłuża) SwitchExtension->evPlus = joined; // event potwierdzenia połączenia (gdy nie znajdzie, to się nie połączy) @@ -1195,12 +1064,7 @@ void TTrack::RaAssign( TAnimModel *am, TEvent *done, TEvent *joined ) }; // wypełnianie tablic VBO - -#ifdef EU07_USE_OLD_GROUNDCODE -void TTrack::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ) { -#else void TTrack::create_geometry( geometrybank_handle const &Bank ) { -#endif // Ra: trzeba rozdzielić szyny od podsypki, aby móc grupować wg tekstur auto const fHTW = 0.5f * std::abs(fTrackWidth); auto const side = std::abs(fTexWidth); // szerokść podsypki na zewnątrz szyny albo pobocza @@ -1245,12 +1109,6 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { normal2 = normal1; } -#ifdef EU07_USE_OLD_GROUNDCODE - if( Bank != 0 ) { - m_origin = Origin; - } -#endif - float roll1, roll2; switch (iCategoryFlag & 15) { @@ -2287,9 +2145,6 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { void TTrack::EnvironmentSet() { // ustawienie zmienionego światła -#ifdef EU07_USE_OLD_GROUNDCODE - ::glColor4f( 1.f, 1.f, 1.f, 1.f ); -#endif switch( eEnvironment ) { case e_canyon: { Global::DayLight.apply_intensity( 0.4f ); @@ -2408,11 +2263,7 @@ bool TTrack::Switch(int i, float const t, float const d) RaAnimate(); // ostatni etap animowania // zablokowanie pozycji i połączenie do sąsiednich torów // TODO: register new position of the path endpoints with the region -#ifdef EU07_USE_OLD_GROUNDCODE - Global::pGround->TrackJoin(SwitchExtension->pMyNode); -#else simulation::Region->TrackJoin( this ); -#endif if (trNext || trPrev) { fVelocity = 6.0; // jazda dozwolona @@ -2427,7 +2278,7 @@ bool TTrack::Switch(int i, float const t, float const d) } if( SwitchExtension->evPlus ) { // w starych sceneriach może nie być // potwierdzenie wykonania (np. odpala WZ) - Global::AddToQuery( SwitchExtension->evPlus, nullptr ); + simulation::Events.AddToQuery( SwitchExtension->evPlus, nullptr ); } } } @@ -2463,11 +2314,11 @@ bool TTrack::SwitchForced(int i, TDynamicObject *o) { case 0: if (SwitchExtension->evPlus) - Global::AddToQuery(SwitchExtension->evPlus, o); // dodanie do kolejki + simulation::Events.AddToQuery(SwitchExtension->evPlus, o); // dodanie do kolejki break; case 1: if (SwitchExtension->evMinus) - Global::AddToQuery(SwitchExtension->evMinus, o); // dodanie do kolejki + simulation::Events.AddToQuery(SwitchExtension->evMinus, o); // dodanie do kolejki break; } Switch(i); // jeśli się tu nie przełączy, to każdy pojazd powtórzy event rozrprucia @@ -2662,11 +2513,7 @@ TTrack * TTrack::RaAnimate() cosa = -hlen * std::cos(glm::radians(SwitchExtension->fOffset)); SwitchExtension->vTrans = ac->TransGet(); vector3 middle = -#ifdef EU07_USE_OLD_GROUNDCODE - SwitchExtension->pMyNode->pCenter + -#else location() + -#endif SwitchExtension->vTrans; // SwitchExtension->Segments[0]->FastGetPoint(0.5); Segment->Init(middle + vector3(sina, 0.0, cosa), middle - vector3(sina, 0.0, cosa), 10.0); // nowy odcinek @@ -2675,11 +2522,7 @@ TTrack * TTrack::RaAnimate() dynamic->Move( 0.000001 ); } // NOTE: passing empty handle is a bit of a hack here. could be refactored into something more elegant -#ifdef EU07_USE_OLD_GROUNDCODE - create_geometry( geometrybank_handle(), glm::dvec3() ); -#else create_geometry( geometrybank_handle() ); -#endif } // animacja trwa nadal } else @@ -2976,19 +2819,16 @@ path_table::InitTracks() { // szukamy modelu o tej samej nazwie auto *instance = simulation::Instances.find( trackname ); // wiązanie toru z modelem obrotnicy -#ifdef EU07_USE_OLD_GROUNDCODE -#else track->RaAssign( instance, simulation::Events.FindEvent( trackname + ":done" ), simulation::Events.FindEvent( trackname + ":joined" ) ); -#endif if( instance == nullptr ) { // jak nie ma modelu to pewnie jest wykolejnica, a ta jest domyślnie zamknięta i wykoleja break; } // no break on purpose: - // jak coś pójdzie źle, to robimy z tego normalny tor + // "jak coś pójdzie źle, to robimy z tego normalny tor" } case tt_Normal: { // tylko proste są podłączane do rozjazdów, stąd dwa rozjazdy się nie połączą ze sobą diff --git a/Track.h b/Track.h index 8deb6aea..23ac5c10 100644 --- a/Track.h +++ b/Track.h @@ -77,9 +77,6 @@ class TSwitchExtension { // zmienne potrzebne tylko dla obrotnicy/przesuwnicy // TAnimContainer *pAnim; //animator modelu dla obrotnicy TAnimModel *pModel; // na razie model -#ifdef EU07_USE_OLD_GROUNDCODE - TGroundNode *pMyNode; // dla obrotnicy do wtórnego podłączania torów -#endif }; struct { // zmienne dla skrzyżowania @@ -89,11 +86,7 @@ class TSwitchExtension }; }; bool bMovement = false; // czy w trakcie animacji -#ifdef EU07_USE_OLD_GROUNDCODE - TSubRect *pOwner = nullptr; // sektor, któremu trzeba zgłosić animację -#else scene::basic_cell *pOwner = nullptr; // TODO: convert this to observer pattern -#endif TTrack *pNextAnim = nullptr; // następny tor do animowania TEvent *evPlus = nullptr, *evMinus = nullptr; // zdarzenia sygnalizacji rozprucia @@ -249,27 +242,13 @@ public: std::vector endpoints() const; -#ifdef EU07_USE_OLD_GROUNDCODE - void create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ); // wypełnianie VBO -#else void create_geometry( geometrybank_handle const &Bank ); // wypełnianie VBO -#endif void RenderDynSounds(); // odtwarzanie dźwięków pojazdów jest niezależne od ich wyświetlania -#ifdef EU07_USE_OLD_GROUNDCODE - void RaOwnerSet(TSubRect *o) { - if (SwitchExtension) - SwitchExtension->pOwner = o; }; -#else void RaOwnerSet( scene::basic_cell *o ) { if( SwitchExtension ) { SwitchExtension->pOwner = o; } }; -#endif bool InMovement(); // czy w trakcie animacji? -#ifdef EU07_USE_OLD_GROUNDCODE - void RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joined); -#else void RaAssign( TAnimModel *am, TEvent *done, TEvent *joined ); -#endif void RaAnimListAdd(TTrack *t); TTrack * RaAnimate(); diff --git a/Traction.cpp b/Traction.cpp index a0739107..4cfa5053 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -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" //--------------------------------------------------------------------------- /* @@ -180,21 +180,11 @@ TTraction::endpoints() const { } std::size_t -#ifdef EU07_USE_OLD_GROUNDCODE -TTraction::create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ) { -#else TTraction::create_geometry( geometrybank_handle const &Bank ) { -#endif if( m_geometry != null_handle ) { return GfxRenderer.Vertices( m_geometry ).size() / 2; } -#ifdef EU07_USE_OLD_GROUNDCODE - if( Bank != 0 ) { - m_origin = Origin; - } -#endif - vertex_array vertices; double ddp = std::hypot( pPoint2.x - pPoint1.x, pPoint2.z - pPoint1.z ); diff --git a/Traction.h b/Traction.h index 8ec2131a..9ccbf078 100644 --- a/Traction.h +++ b/Traction.h @@ -66,11 +66,7 @@ class TTraction : public editor::basic_node { 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 -#ifdef EU07_USE_OLD_GROUNDCODE - std::size_t create_geometry( geometrybank_handle const &Bank, glm::dvec3 const &Origin ); // wypełnianie VBO -#else std::size_t create_geometry( geometrybank_handle const &Bank ); -#endif int TestPoint(glm::dvec3 const &Point); void Connect(int my, TTraction *with, int to); void Init(); diff --git a/TractionPower.cpp b/TractionPower.cpp index e4d60783..8513ce47 100644 --- a/TractionPower.cpp +++ b/TractionPower.cpp @@ -15,8 +15,8 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "TractionPower.h" + #include "Logs.h" -#include "Ground.h" //--------------------------------------------------------------------------- @@ -36,16 +36,11 @@ void TTractionPowerSource::Init(double const u, double const i) bool TTractionPowerSource::Load(cParser *parser) { -#ifdef EU07_USE_OLD_GROUNDCODE - parser->getTokens( 7, false ); - *parser -#else parser->getTokens( 10, false ); *parser >> m_area.center.x >> m_area.center.y >> m_area.center.z -#endif >> NominalVoltage >> VoltageFrequency >> InternalRes diff --git a/TrkFoll.cpp b/TrkFoll.cpp index ceddcb1c..ddfa4fee 100644 --- a/TrkFoll.cpp +++ b/TrkFoll.cpp @@ -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; diff --git a/World.cpp b/World.cpp index 06be5fff..b282fb90 100644 --- a/World.cpp +++ b/World.cpp @@ -249,17 +249,7 @@ bool TWorld::Init( GLFWwindow *Window ) { GfxRenderer.Render(); WriteLog( "World setup..." ); -#ifdef EU07_USE_OLD_GROUNDCODE - if( true == Ground.Init( Global::SceneryFile ) ) { - WriteLog( "...world setup done" ); - } - else { - ErrorLog( "...world setup failed" ); - return false; - } -#else if( false == simulation::State.deserialize( Global::SceneryFile ) ) { return false; } -#endif simulation::Time.init(); @@ -269,25 +259,13 @@ bool TWorld::Init( GLFWwindow *Window ) { UILayer.set_progress( "Preparing train / Przygotowanie kabiny" ); WriteLog( "Player train init: " + Global::asHumanCtrlVehicle ); -#ifdef EU07_USE_OLD_GROUNDCODE - TGroundNode *nPlayerTrain = NULL; -#else TDynamicObject *nPlayerTrain; -#endif if( Global::asHumanCtrlVehicle != "ghostview" ) -#ifdef EU07_USE_OLD_GROUNDCODE - nPlayerTrain = Ground.DynamicFind( Global::asHumanCtrlVehicle ); // szukanie w tych z obsadą -#else nPlayerTrain = simulation::Vehicles.find( Global::asHumanCtrlVehicle ); -#endif if (nPlayerTrain) { Train = new TTrain(); -#ifdef EU07_USE_OLD_GROUNDCODE - if (Train->Init(nPlayerTrain->DynamicObject)) -#else if( Train->Init( nPlayerTrain ) ) -#endif { Controlled = Train->Dynamic(); mvControlled = Controlled->ControlledFind()->MoverParameters; @@ -324,18 +302,6 @@ bool TWorld::Init( GLFWwindow *Window ) { // if (!Global::bMultiplayer) //na razie włączone { // eventy aktywowane z klawiatury tylko dla jednego użytkownika -#ifdef EU07_USE_OLD_GROUNDCODE - KeyEvents[0] = Ground.FindEvent("keyctrl00"); - KeyEvents[1] = Ground.FindEvent("keyctrl01"); - KeyEvents[2] = Ground.FindEvent("keyctrl02"); - KeyEvents[3] = Ground.FindEvent("keyctrl03"); - KeyEvents[4] = Ground.FindEvent("keyctrl04"); - KeyEvents[5] = Ground.FindEvent("keyctrl05"); - KeyEvents[6] = Ground.FindEvent("keyctrl06"); - KeyEvents[7] = Ground.FindEvent("keyctrl07"); - KeyEvents[8] = Ground.FindEvent("keyctrl08"); - KeyEvents[9] = Ground.FindEvent("keyctrl09"); -#else KeyEvents[ 0 ] = simulation::Events.FindEvent( "keyctrl00" ); KeyEvents[ 1 ] = simulation::Events.FindEvent( "keyctrl01" ); KeyEvents[ 2 ] = simulation::Events.FindEvent( "keyctrl02" ); @@ -346,7 +312,6 @@ bool TWorld::Init( GLFWwindow *Window ) { KeyEvents[ 7 ] = simulation::Events.FindEvent( "keyctrl07" ); KeyEvents[ 8 ] = simulation::Events.FindEvent( "keyctrl08" ); KeyEvents[ 9 ] = simulation::Events.FindEvent( "keyctrl09" ); -#endif } WriteLog( "Load time: " + @@ -430,15 +395,12 @@ void TWorld::OnKeyDown(int cKey) if( ( cKey >= GLFW_KEY_0 ) && ( cKey <= GLFW_KEY_9 ) ) // klawisze cyfrowe { int i = cKey - GLFW_KEY_0; // numer klawisza - if (Global::shiftState) - { // z [Shift] uruchomienie eventu - if (!Global::iPause) // podczas pauzy klawisze nie działają - if (KeyEvents[i]) -#ifdef EU07_USE_OLD_GROUNDCODE - Ground.AddToQuery(KeyEvents[i], NULL); -#else - simulation::Events.AddToQuery( KeyEvents[ i ], NULL ); -#endif + if (Global::shiftState) { + // z [Shift] uruchomienie eventu + if( ( false == Global::iPause ) // podczas pauzy klawisze nie działają + && ( KeyEvents[ i ] != nullptr ) ) { + simulation::Events.AddToQuery( KeyEvents[ i ], NULL ); + } } else // zapamiętywanie kamery może działać podczas pauzy if (FreeFlyModeFlag) // w trybie latania można przeskakiwać do ustawionych kamer @@ -465,11 +427,6 @@ void TWorld::OnKeyDown(int cKey) } else // również przeskakiwanie { // Ra: to z tą kamerą (Camera.Pos i Global::pCameraPosition) jest trochę bez sensu -#ifdef EU07_USE_OLD_GROUNDCODE - // NOTE: does it even work? it seems to render the sounds before the camera is actually moved - // so the listener is still in range... - Ground.Silence( Global::pCameraPosition ); // wyciszenie wszystkiego z poprzedniej pozycji -#endif Global::SetCameraPosition( Global::FreeCameraInit[i] ); // nowa pozycja dla generowania obiektów Camera.Init(Global::FreeCameraInit[i], Global::FreeCameraInitAngle[i]); // przestawienie @@ -546,11 +503,8 @@ void TWorld::OnKeyDown(int cKey) break; } -#ifdef EU07_USE_OLD_GROUNDCODE - TDynamicObject *tmp = Ground.DynamicNearest( Camera.Pos, 50, true ); //łapiemy z obsadą -#else TDynamicObject *tmp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 50, true, false ) ); -#endif + if( ( tmp != nullptr ) && ( tmp != Controlled ) ) { @@ -695,11 +649,7 @@ void TWorld::OnKeyDown(int cKey) else if( ( cKey == GLFW_KEY_PAUSE ) && ( Global::ctrlState ) && ( Global::shiftState ) ) { //[Ctrl]+[Break] hamowanie wszystkich pojazdów w okolicy // added shift to prevent odd issue with glfw producing pause presses on its own if( Controlled->MoverParameters->Radio ) -#ifdef EU07_USE_OLD_GROUNDCODE - Ground.RadioStop( Camera.Pos ); -#else simulation::Region->RadioStop( Camera.Pos ); -#endif } else if (!Global::iPause) //||(cKey==VK_F4)) //podczas pauzy sterownaie nie działa, F4 tak if (Train) @@ -732,15 +682,11 @@ void TWorld::OnKeyDown(int cKey) */ if (cKey == Global::Keys[k_Heating]) // Ra: klawisz nie jest najszczęśliwszy { // zmiana próżny/ładowny; Ra: zabrane z kabiny -#ifdef EU07_USE_OLD_GROUNDCODE - TDynamicObject *temp = Global::DynamicNearest(); -#else - TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, false ) ); -#endif - if (temp) + auto *vehicle { std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, false ) ) }; + if (vehicle) { - if (Global::shiftState ? temp->MoverParameters->IncBrakeMult() : - temp->MoverParameters->DecBrakeMult()) + if (Global::shiftState ? vehicle->MoverParameters->IncBrakeMult() : + vehicle->MoverParameters->DecBrakeMult()) if (Train) { // dźwięk oczywiście jest w kabinie Train->play_sound( Train->dsbSwitch ); @@ -749,31 +695,26 @@ void TWorld::OnKeyDown(int cKey) } else if (cKey == Global::Keys[k_EndSign]) { // Ra 2014-07: zabrane z kabiny -#ifdef EU07_USE_OLD_GROUNDCODE - TDynamicObject *tmp = Global::CouplerNearest(); // domyślnie wyszukuje do 20m -#else - TDynamicObject *tmp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, true ) ); -#endif - if (tmp) + auto *vehicle { std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, true ) ) }; + if (vehicle) { - int CouplNr = (LengthSquared3(tmp->HeadPosition() - Camera.Pos) > - LengthSquared3(tmp->RearPosition() - Camera.Pos) ? + int CouplNr = (LengthSquared3(vehicle->HeadPosition() - Camera.Pos) > + LengthSquared3(vehicle->RearPosition() - Camera.Pos) ? 1 : -1) * - tmp->DirectionGet(); + vehicle->DirectionGet(); if (CouplNr < 0) CouplNr = 0; // z [-1,1] zrobić [0,1] - int mask, set = 0; // Ra: [Shift]+[Ctrl]+[T] odpala mi jakąś idiotyczną zmianę - // tapety pulpitu :/ + int mask, set = 0; // Ra: [Shift]+[Ctrl]+[T] odpala mi jakąś idiotyczną zmianę tapety pulpitu :/ if (Global::shiftState) // z [Shift] zapalanie - set = mask = 64; // bez [Ctrl] założyć tabliczki + set = mask = TMoverParameters::light::rearendsignals; // bez [Ctrl] założyć tabliczki else if (Global::ctrlState) - set = mask = 2 + 32; // z [Ctrl] zapalić światła czerwone + set = mask = ( TMoverParameters::light::redmarker_left | TMoverParameters::light::redmarker_right ); // z [Ctrl] zapalić światła czerwone else mask = 2 + 32 + 64; // wyłączanie ściąga wszystko - if (((tmp->iLights[CouplNr]) & mask) != set) + if (((vehicle->iLights[CouplNr]) & mask) != set) { - tmp->iLights[CouplNr] = (tmp->iLights[CouplNr] & ~mask) | set; + vehicle->iLights[CouplNr] = (vehicle->iLights[CouplNr] & ~mask) | set; if (Train) { // Ra: ten dźwięk z kabiny to przegięcie, ale na razie zostawiam Train->play_sound( Train->dsbSwitch ); @@ -783,21 +724,17 @@ void TWorld::OnKeyDown(int cKey) } else if (cKey == Global::Keys[k_IncLocalBrakeLevel]) { // zahamowanie dowolnego pojazdu -#ifdef EU07_USE_OLD_GROUNDCODE - TDynamicObject *temp = Global::DynamicNearest(); -#else - TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, false ) ); -#endif - if (temp) + auto *vehicle { std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, false ) ) }; + if (vehicle) { if (Global::ctrlState) - if ((temp->MoverParameters->LocalBrake == ManualBrake) || - (temp->MoverParameters->MBrake == true)) - temp->MoverParameters->IncManualBrakeLevel(1); + if ((vehicle->MoverParameters->LocalBrake == ManualBrake) || + (vehicle->MoverParameters->MBrake == true)) + vehicle->MoverParameters->IncManualBrakeLevel(1); else ; - else if (temp->MoverParameters->LocalBrake != ManualBrake) - if (temp->MoverParameters->IncLocalBrakeLevelFAST()) + else if (vehicle->MoverParameters->LocalBrake != ManualBrake) + if (vehicle->MoverParameters->IncLocalBrakeLevelFAST()) if (Train) { // dźwięk oczywiście jest w kabinie Train->play_sound( Train->dsbPneumaticRelay ); @@ -806,21 +743,17 @@ void TWorld::OnKeyDown(int cKey) } else if (cKey == Global::Keys[k_DecLocalBrakeLevel]) { // odhamowanie dowolnego pojazdu -#ifdef EU07_USE_OLD_GROUNDCODE - TDynamicObject *temp = Global::DynamicNearest(); -#else - TDynamicObject *temp = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, false ) ); -#endif - if (temp) + auto *vehicle { std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, false ) ) }; + if (vehicle) { if (Global::ctrlState) - if ((temp->MoverParameters->LocalBrake == ManualBrake) || - (temp->MoverParameters->MBrake == true)) - temp->MoverParameters->DecManualBrakeLevel(1); + if ((vehicle->MoverParameters->LocalBrake == ManualBrake) || + (vehicle->MoverParameters->MBrake == true)) + vehicle->MoverParameters->DecManualBrakeLevel(1); else ; - else if (temp->MoverParameters->LocalBrake != ManualBrake) - if (temp->MoverParameters->DecLocalBrakeLevelFAST()) + else if (vehicle->MoverParameters->LocalBrake != ManualBrake) + if (vehicle->MoverParameters->DecLocalBrakeLevelFAST()) if (Train) { // dźwięk oczywiście jest w kabinie Train->play_sound( Train->dsbPneumaticRelay ); @@ -922,9 +855,6 @@ void TWorld::FollowView(bool wycisz) { if (Controlled) // jest pojazd do prowadzenia? { -#ifdef EU07_USE_OLD_GROUNDCODE - Ground.Silence( Camera.Pos ); // wyciszenie dźwięków z poprzedniej pozycji -#endif if (FreeFlyModeFlag) { // jeżeli poza kabiną, przestawiamy w jej okolicę - OK if( Train ) { @@ -942,9 +872,7 @@ void TWorld::FollowView(bool wycisz) { if( wycisz ) { // wyciszenie dźwięków z poprzedniej pozycji // trzymanie prawego w kabinie daje marny efekt -#ifdef EU07_USE_OLD_GROUNDCODE - Ground.Silence( Camera.Pos ); -#endif + // TODO: re-implement, old one kinda didn't really work } Camera.Pos = Train->pMechPosition; Camera.Roll = std::atan(Train->pMechShake.x * Train->fMechRoll); // hustanie kamery na boki @@ -1046,32 +974,6 @@ bool TWorld::Update() { // NOTE: experimentally changing this to prevent the desync. // TODO: test what happens if we hit more than 20 * 0.01 sec slices, i.e. less than 5 fps Timer::subsystem.sim_dynamics.start(); -#ifdef EU07_USE_OLD_GROUNDCODE - if( true == Global::FullPhysics ) { - // mixed calculation mode, steps calculated in ~0.05s chunks - while( updatecount >= 5 ) { - Ground.Update( stepdeltatime, 5 ); - updatecount -= 5; - } - if( updatecount ) { - Ground.Update( stepdeltatime, updatecount ); - } - } - else { - // simplified calculation mode; faster but can lead to errors - Ground.Update( stepdeltatime, updatecount ); - } - // yB dodał przyspieszacz fizyki - if( (true == DebugModeFlag) - && (true == Global::bActive) // nie przyspieszać, gdy jedzie w tle :) - && ( glfwGetKey( window, GLFW_KEY_PAUSE ) == GLFW_PRESS ) ) { - - Ground.Update( dt, 1 ); - Ground.Update( dt, 1 ); - Ground.Update( dt, 1 ); - Ground.Update( dt, 1 ); // 5 razy - } -#else if( true == Global::FullPhysics ) { // mixed calculation mode, steps calculated in ~0.05s chunks while( updatecount >= 5 ) { @@ -1086,7 +988,6 @@ bool TWorld::Update() { // simplified calculation mode; faster but can lead to errors simulation::State.update( stepdeltatime, updatecount ); } -#endif Timer::subsystem.sim_dynamics.stop(); // secondary fixed step simulation time routines @@ -1127,14 +1028,8 @@ bool TWorld::Update() { TSubModel::iInstance = 0; } -#ifdef EU07_USE_OLD_GROUNDCODE - Ground.CheckQuery(); - Ground.Update_Hidden(); -#else simulation::Events.update(); simulation::Region->update(); -#endif - simulation::Lights.update(); // render time routines follow: @@ -1186,15 +1081,10 @@ TWorld::Update_Camera( double const Deltatime ) { Camera.LookAt = Controlled->GetPosition(); } else { -#ifdef EU07_USE_OLD_GROUNDCODE - TDynamicObject *d = Ground.DynamicNearest( Camera.Pos, 300 ); // szukaj w promieniu 300m - if( !d ) - d = Ground.DynamicNearest( Camera.Pos, 1000 ); // dalej szukanie, jesli bliżej nie ma -#else TDynamicObject *d = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 300, false, false ) ); if( !d ) d = std::get( simulation::Region->find_vehicle( Global::pCameraPosition, 1000, false, false ) ); // dalej szukanie, jesli bliżej nie ma -#endif + if( d && pDynamicNearest ) { // jeśli jakiś jest znaleziony wcześniej if( 100.0 * LengthSquared3( d->GetPosition() - Camera.Pos ) > LengthSquared3( pDynamicNearest->GetPosition() - Camera.Pos ) ) { @@ -1328,14 +1218,10 @@ TWorld::Update_UI() { } if( ( true == Global::ControlPicking ) && ( true == FreeFlyModeFlag ) && ( true == DebugModeFlag ) ) { auto const scenerynode = GfxRenderer.Pick_Node(); -#ifdef EU07_USE_OLD_GROUNDCODE - UILayer.set_tooltip( ( scenerynode ? scenerynode->asName : "" ) ); -#else UILayer.set_tooltip( ( scenerynode ? scenerynode->name() : "" ) ); -#endif } switch( Global::iTextMode ) { @@ -1388,21 +1274,17 @@ TWorld::Update_UI() { case( GLFW_KEY_F2 ) : { // timetable - TDynamicObject *tmp = + auto *vehicle { ( FreeFlyModeFlag ? -#ifdef EU07_USE_OLD_GROUNDCODE - Ground.DynamicNearest( Camera.Pos ) : -#else - std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false, false ) ) : -#endif - Controlled ); // w trybie latania lokalizujemy wg mapy + std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false, false ) ) : + Controlled ) }; // w trybie latania lokalizujemy wg mapy - if( tmp == nullptr ) { break; } + if( vehicle == nullptr ) { break; } // if the nearest located vehicle doesn't have a direct driver, try to query its owner auto const owner = ( - ( ( tmp->Mechanik != nullptr ) && ( tmp->Mechanik->Primary() ) ) ? - tmp->Mechanik : - tmp->ctOwner ); + ( ( vehicle->Mechanik != nullptr ) && ( vehicle->Mechanik->Primary() ) ) ? + vehicle->Mechanik : + vehicle->ctOwner ); if( owner == nullptr ){ break; } auto const table = owner->Timetable(); @@ -1473,53 +1355,48 @@ TWorld::Update_UI() { case( GLFW_KEY_F3 ) : { - TDynamicObject *tmp = + auto *vehicle{ ( FreeFlyModeFlag ? -#ifdef EU07_USE_OLD_GROUNDCODE - Ground.DynamicNearest( Camera.Pos ) : -#else std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false, false ) ) : -#endif - Controlled ); // w trybie latania lokalizujemy wg mapy + Controlled ) }; // w trybie latania lokalizujemy wg mapy - if( tmp != nullptr ) { - // + if( vehicle != nullptr ) { // jeśli domyślny ekran po pierwszym naciśnięciu - uitextline1 = "Vehicle name: " + tmp->MoverParameters->Name; + uitextline1 = "Vehicle name: " + vehicle->MoverParameters->Name; - if( ( tmp->Mechanik == nullptr ) && ( tmp->ctOwner ) ) { + if( ( vehicle->Mechanik == nullptr ) && ( vehicle->ctOwner ) ) { // for cars other than leading unit indicate the leader - uitextline1 += ", owned by " + tmp->ctOwner->OwnerName(); + uitextline1 += ", owned by " + vehicle->ctOwner->OwnerName(); } - uitextline1 += "; Status: " + tmp->MoverParameters->EngineDescription( 0 ); + uitextline1 += "; Status: " + vehicle->MoverParameters->EngineDescription( 0 ); // informacja o sprzęgach uitextline1 += "; C0:" + - ( tmp->PrevConnected ? - tmp->PrevConnected->name() + ":" + to_string( tmp->MoverParameters->Couplers[ 0 ].CouplingFlag ) + ( - tmp->MoverParameters->Couplers[ 0 ].CouplingFlag == 0 ? - " (" + to_string( tmp->MoverParameters->Couplers[ 0 ].CoupleDist, 1 ) + " m)" : + ( vehicle->PrevConnected ? + vehicle->PrevConnected->name() + ":" + to_string( vehicle->MoverParameters->Couplers[ 0 ].CouplingFlag ) + ( + vehicle->MoverParameters->Couplers[ 0 ].CouplingFlag == 0 ? + " (" + to_string( vehicle->MoverParameters->Couplers[ 0 ].CoupleDist, 1 ) + " m)" : "" ) : "none" ); uitextline1 += " C1:" + - ( tmp->NextConnected ? - tmp->NextConnected->name() + ":" + to_string( tmp->MoverParameters->Couplers[ 1 ].CouplingFlag ) + ( - tmp->MoverParameters->Couplers[ 1 ].CouplingFlag == 0 ? - " (" + to_string( tmp->MoverParameters->Couplers[ 1 ].CoupleDist, 1 ) + " m)" : + ( vehicle->NextConnected ? + vehicle->NextConnected->name() + ":" + to_string( vehicle->MoverParameters->Couplers[ 1 ].CouplingFlag ) + ( + vehicle->MoverParameters->Couplers[ 1 ].CouplingFlag == 0 ? + " (" + to_string( vehicle->MoverParameters->Couplers[ 1 ].CoupleDist, 1 ) + " m)" : "" ) : "none" ); // equipment flags - uitextline2 = ( tmp->MoverParameters->Battery ? "B" : "." ); - uitextline2 += ( tmp->MoverParameters->Mains ? "M" : "." ); - uitextline2 += ( tmp->MoverParameters->PantRearUp ? ( tmp->MoverParameters->PantRearVolt > 0.0 ? "O" : "o" ) : "." );; - uitextline2 += ( tmp->MoverParameters->PantFrontUp ? ( tmp->MoverParameters->PantFrontVolt > 0.0 ? "P" : "p" ) : "." );; - uitextline2 += ( tmp->MoverParameters->PantPressLockActive ? "!" : ( tmp->MoverParameters->PantPressSwitchActive ? "*" : "." ) ); - uitextline2 += ( false == tmp->MoverParameters->ConverterAllowLocal ? "-" : ( tmp->MoverParameters->ConverterAllow ? ( tmp->MoverParameters->ConverterFlag ? "X" : "x" ) : "." ) ); - uitextline2 += ( tmp->MoverParameters->ConvOvldFlag ? "!" : "." ); - uitextline2 += ( false == tmp->MoverParameters->CompressorAllowLocal ? "-" : ( tmp->MoverParameters->CompressorAllow ? ( tmp->MoverParameters->CompressorFlag ? "C" : "c" ) : "." ) ); - uitextline2 += ( tmp->MoverParameters->CompressorGovernorLock ? "!" : "." ); + uitextline2 = ( vehicle->MoverParameters->Battery ? "B" : "." ); + uitextline2 += ( vehicle->MoverParameters->Mains ? "M" : "." ); + uitextline2 += ( vehicle->MoverParameters->PantRearUp ? ( vehicle->MoverParameters->PantRearVolt > 0.0 ? "O" : "o" ) : "." );; + uitextline2 += ( vehicle->MoverParameters->PantFrontUp ? ( vehicle->MoverParameters->PantFrontVolt > 0.0 ? "P" : "p" ) : "." );; + uitextline2 += ( vehicle->MoverParameters->PantPressLockActive ? "!" : ( vehicle->MoverParameters->PantPressSwitchActive ? "*" : "." ) ); + uitextline2 += ( false == vehicle->MoverParameters->ConverterAllowLocal ? "-" : ( vehicle->MoverParameters->ConverterAllow ? ( vehicle->MoverParameters->ConverterFlag ? "X" : "x" ) : "." ) ); + uitextline2 += ( vehicle->MoverParameters->ConvOvldFlag ? "!" : "." ); + uitextline2 += ( false == vehicle->MoverParameters->CompressorAllowLocal ? "-" : ( vehicle->MoverParameters->CompressorAllow ? ( vehicle->MoverParameters->CompressorFlag ? "C" : "c" ) : "." ) ); + uitextline2 += ( vehicle->MoverParameters->CompressorGovernorLock ? "!" : "." ); /* uitextline2 += " AnlgB: " + to_string( tmp->MoverParameters->AnPos, 1 ) @@ -1527,72 +1404,72 @@ TWorld::Update_UI() { + to_string( tmp->MoverParameters->LocalBrakePosA, 1 ) */ uitextline2 += " Bdelay: "; - if( ( tmp->MoverParameters->BrakeDelayFlag & bdelay_G ) == bdelay_G ) + if( ( vehicle->MoverParameters->BrakeDelayFlag & bdelay_G ) == bdelay_G ) uitextline2 += "G"; - if( ( tmp->MoverParameters->BrakeDelayFlag & bdelay_P ) == bdelay_P ) + if( ( vehicle->MoverParameters->BrakeDelayFlag & bdelay_P ) == bdelay_P ) uitextline2 += "P"; - if( ( tmp->MoverParameters->BrakeDelayFlag & bdelay_R ) == bdelay_R ) + if( ( vehicle->MoverParameters->BrakeDelayFlag & bdelay_R ) == bdelay_R ) uitextline2 += "R"; - if( ( tmp->MoverParameters->BrakeDelayFlag & bdelay_M ) == bdelay_M ) + if( ( vehicle->MoverParameters->BrakeDelayFlag & bdelay_M ) == bdelay_M ) uitextline2 += "+Mg"; - uitextline2 += ", Load: " + to_string( tmp->MoverParameters->LoadFlag, 0 ); + uitextline2 += ", Load: " + to_string( vehicle->MoverParameters->LoadFlag, 0 ); uitextline2 += "; Pant: " - + to_string( tmp->MoverParameters->PantPress, 2 ) - + ( tmp->MoverParameters->bPantKurek3 ? "-ZG" : "|ZG" ); + + to_string( vehicle->MoverParameters->PantPress, 2 ) + + ( vehicle->MoverParameters->bPantKurek3 ? "-ZG" : "|ZG" ); uitextline2 += - "; Ft: " + to_string( tmp->MoverParameters->Ft * 0.001f * tmp->MoverParameters->ActiveCab, 1 ) - + ", Fb: " + to_string( tmp->MoverParameters->Fb * 0.001f, 1 ) - + ", Fr: " + to_string( tmp->MoverParameters->RunningTrack.friction, 2 ) - + ( tmp->MoverParameters->SlippingWheels ? " (!)" : "" ); + "; Ft: " + to_string( vehicle->MoverParameters->Ft * 0.001f * vehicle->MoverParameters->ActiveCab, 1 ) + + ", Fb: " + to_string( vehicle->MoverParameters->Fb * 0.001f, 1 ) + + ", Fr: " + to_string( vehicle->MoverParameters->RunningTrack.friction, 2 ) + + ( vehicle->MoverParameters->SlippingWheels ? " (!)" : "" ); - if( tmp->Mechanik ) { - uitextline2 += "; Ag: " + to_string( tmp->Mechanik->fAccGravity, 2 ); + if( vehicle->Mechanik ) { + uitextline2 += "; Ag: " + to_string( vehicle->Mechanik->fAccGravity, 2 ); } uitextline2 += "; TC:" - + to_string( tmp->MoverParameters->TotalCurrent, 0 ); + + to_string( vehicle->MoverParameters->TotalCurrent, 0 ); auto const frontcouplerhighvoltage = - to_string( tmp->MoverParameters->Couplers[ TMoverParameters::side::front ].power_high.voltage, 0 ) + to_string( vehicle->MoverParameters->Couplers[ TMoverParameters::side::front ].power_high.voltage, 0 ) + "@" - + to_string( tmp->MoverParameters->Couplers[ TMoverParameters::side::front ].power_high.current, 0 ); + + to_string( vehicle->MoverParameters->Couplers[ TMoverParameters::side::front ].power_high.current, 0 ); auto const rearcouplerhighvoltage = - to_string( tmp->MoverParameters->Couplers[ TMoverParameters::side::rear ].power_high.voltage, 0 ) + to_string( vehicle->MoverParameters->Couplers[ TMoverParameters::side::rear ].power_high.voltage, 0 ) + "@" - + to_string( tmp->MoverParameters->Couplers[ TMoverParameters::side::rear ].power_high.current, 0 ); + + to_string( vehicle->MoverParameters->Couplers[ TMoverParameters::side::rear ].power_high.current, 0 ); uitextline2 += ", HV: "; - if( tmp->MoverParameters->Couplers[ TMoverParameters::side::front ].power_high.local == false ) { + if( vehicle->MoverParameters->Couplers[ TMoverParameters::side::front ].power_high.local == false ) { uitextline2 += "(" + frontcouplerhighvoltage + ")-" - + ":F" + ( tmp->DirectionGet() ? "<<" : ">>" ) + "R:" + + ":F" + ( vehicle->DirectionGet() ? "<<" : ">>" ) + "R:" + "-(" + rearcouplerhighvoltage + ")"; } else { uitextline2 += frontcouplerhighvoltage - + ":F" + ( tmp->DirectionGet() ? "<<" : ">>" ) + "R:" + + ":F" + ( vehicle->DirectionGet() ? "<<" : ">>" ) + "R:" + rearcouplerhighvoltage; } uitextline3 += - "TrB: " + to_string( tmp->MoverParameters->BrakePress, 2 ) - + ", " + to_hex_str( tmp->MoverParameters->Hamulec->GetBrakeStatus(), 2 ); + "TrB: " + to_string( vehicle->MoverParameters->BrakePress, 2 ) + + ", " + to_hex_str( vehicle->MoverParameters->Hamulec->GetBrakeStatus(), 2 ); uitextline3 += - "; LcB: " + to_string( tmp->MoverParameters->LocBrakePress, 2 ) - + "; pipes: " + to_string( tmp->MoverParameters->PipePress, 2 ) - + "/" + to_string( tmp->MoverParameters->ScndPipePress, 2 ) - + "/" + to_string( tmp->MoverParameters->EqvtPipePress, 2 ) - + ", MT: " + to_string( tmp->MoverParameters->CompressedVolume, 3 ) - + ", BT: " + to_string( tmp->MoverParameters->Volume, 3 ) - + ", CtlP: " + to_string( tmp->MoverParameters->CntrlPipePress, 3 ) - + ", CtlT: " + to_string( tmp->MoverParameters->Hamulec->GetCRP(), 3 ); + "; LcB: " + to_string( vehicle->MoverParameters->LocBrakePress, 2 ) + + "; pipes: " + to_string( vehicle->MoverParameters->PipePress, 2 ) + + "/" + to_string( vehicle->MoverParameters->ScndPipePress, 2 ) + + "/" + to_string( vehicle->MoverParameters->EqvtPipePress, 2 ) + + ", MT: " + to_string( vehicle->MoverParameters->CompressedVolume, 3 ) + + ", BT: " + to_string( vehicle->MoverParameters->Volume, 3 ) + + ", CtlP: " + to_string( vehicle->MoverParameters->CntrlPipePress, 3 ) + + ", CtlT: " + to_string( vehicle->MoverParameters->Hamulec->GetCRP(), 3 ); - if( tmp->MoverParameters->ManualBrakePos > 0 ) { + if( vehicle->MoverParameters->ManualBrakePos > 0 ) { uitextline3 += "; manual brake on"; } @@ -1606,51 +1483,51 @@ TWorld::Update_UI() { uitextline3 += ", local brake off"; } */ - if( tmp->Mechanik ) { + if( vehicle->Mechanik ) { // o ile jest ktoś w środku std::string flags = "cpapcplhhndoiefgvdpseil "; // flagi AI (definicja w Driver.h) for( int i = 0, j = 1; i < 23; ++i, j <<= 1 ) - if( false == ( tmp->Mechanik->DrivigFlags() & j ) ) // jak bit ustawiony + if( false == ( vehicle->Mechanik->DrivigFlags() & j ) ) // jak bit ustawiony flags[ i ] = '.';// std::toupper( flags[ i ] ); // ^= 0x20; // to zmiana na wielką literę uitextline4 = flags; uitextline4 += - "Driver: Vd=" + to_string( tmp->Mechanik->VelDesired, 0 ) - + " Ad=" + to_string( tmp->Mechanik->AccDesired, 2 ) - + " Ah=" + to_string( tmp->Mechanik->fAccThreshold, 2 ) - + "@" + to_string( tmp->Mechanik->fBrake_a0[0], 2 ) - + "+" + to_string( tmp->Mechanik->fBrake_a1[0], 2 ) - + " Bd=" + to_string( tmp->Mechanik->fBrakeDist, 0 ) - + " Pd=" + to_string( tmp->Mechanik->ActualProximityDist, 0 ) - + " Vn=" + to_string( tmp->Mechanik->VelNext, 0 ) - + " VSl=" + to_string( tmp->Mechanik->VelSignalLast, 0 ) - + " VLl=" + to_string( tmp->Mechanik->VelLimitLast, 0 ) - + " VRd=" + to_string( tmp->Mechanik->VelRoad, 0 ); + "Driver: Vd=" + to_string( vehicle->Mechanik->VelDesired, 0 ) + + " Ad=" + to_string( vehicle->Mechanik->AccDesired, 2 ) + + " Ah=" + to_string( vehicle->Mechanik->fAccThreshold, 2 ) + + "@" + to_string( vehicle->Mechanik->fBrake_a0[0], 2 ) + + "+" + to_string( vehicle->Mechanik->fBrake_a1[0], 2 ) + + " Bd=" + to_string( vehicle->Mechanik->fBrakeDist, 0 ) + + " Pd=" + to_string( vehicle->Mechanik->ActualProximityDist, 0 ) + + " Vn=" + to_string( vehicle->Mechanik->VelNext, 0 ) + + " VSl=" + to_string( vehicle->Mechanik->VelSignalLast, 0 ) + + " VLl=" + to_string( vehicle->Mechanik->VelLimitLast, 0 ) + + " VRd=" + to_string( vehicle->Mechanik->VelRoad, 0 ); - if( ( tmp->Mechanik->VelNext == 0.0 ) - && ( tmp->Mechanik->eSignNext ) ) { + if( ( vehicle->Mechanik->VelNext == 0.0 ) + && ( vehicle->Mechanik->eSignNext ) ) { // jeśli ma zapamiętany event semafora, nazwa eventu semafora uitextline4 += " (" - + Global::Bezogonkow( tmp->Mechanik->eSignNext->asName ) + + Global::Bezogonkow( vehicle->Mechanik->eSignNext->asName ) + ")"; } // biezaca komenda dla AI - uitextline4 += ", command: " + tmp->Mechanik->OrderCurrent(); + uitextline4 += ", command: " + vehicle->Mechanik->OrderCurrent(); } if( Global::iScreenMode[ Global::iTextMode - GLFW_KEY_F1 ] == 1 ) { // f2 screen, track scan mode - if( tmp->Mechanik == nullptr ) { + if( vehicle->Mechanik == nullptr ) { //żeby była tabelka, musi być AI break; } - std::size_t i = 0; std::size_t const speedtablesize = clamp( static_cast( tmp->Mechanik->TableSize() ) - 1, 0, 30 ); + std::size_t i = 0; std::size_t const speedtablesize = clamp( static_cast( vehicle->Mechanik->TableSize() ) - 1, 0, 30 ); do { - std::string scanline = tmp->Mechanik->TableText( i ); + std::string scanline = vehicle->Mechanik->TableText( i ); if( scanline.empty() ) { break; } UITable->text_lines.emplace_back( Global::Bezogonkow( scanline ), Global::UITextColor ); ++i; @@ -1741,68 +1618,64 @@ TWorld::Update_UI() { // ... unless we're in debug mode if( DebugModeFlag ) { - TDynamicObject *tmp = + auto *vehicle { ( FreeFlyModeFlag ? -#ifdef EU07_USE_OLD_GROUNDCODE - Ground.DynamicNearest( Camera.Pos ) : -#else std::get( simulation::Region->find_vehicle( Camera.Pos, 20, false, false ) ) : -#endif - Controlled ); // w trybie latania lokalizujemy wg mapy - if( tmp == nullptr ) { + Controlled ) }; // w trybie latania lokalizujemy wg mapy + if( vehicle == nullptr ) { break; } uitextline1 = - "vel: " + to_string( tmp->GetVelocity(), 2 ) + " km/h" + ( tmp->MoverParameters->SlippingWheels ? " (!)" : "" ) - + "; dist: " + to_string( tmp->MoverParameters->DistCounter, 2 ) + " km" + "vel: " + to_string( vehicle->GetVelocity(), 2 ) + " km/h" + ( vehicle->MoverParameters->SlippingWheels ? " (!)" : "" ) + + "; dist: " + to_string( vehicle->MoverParameters->DistCounter, 2 ) + " km" + "; pos: (" - + to_string( tmp->GetPosition().x, 2 ) + ", " - + to_string( tmp->GetPosition().y, 2 ) + ", " - + to_string( tmp->GetPosition().z, 2 ) + + to_string( vehicle->GetPosition().x, 2 ) + ", " + + to_string( vehicle->GetPosition().y, 2 ) + ", " + + to_string( vehicle->GetPosition().z, 2 ) + ")"; uitextline2 = - "HamZ=" + to_string( tmp->MoverParameters->fBrakeCtrlPos, 2 ) - + "; HamP=" + std::to_string( tmp->MoverParameters->LocalBrakePos ) + "/" + to_string( tmp->MoverParameters->LocalBrakePosA, 2 ) - + "; NasJ=" + std::to_string( tmp->MoverParameters->MainCtrlPos ) + "(" + std::to_string( tmp->MoverParameters->MainCtrlActualPos ) + ")" - + "; NasB=" + std::to_string( tmp->MoverParameters->ScndCtrlPos ) + "(" + std::to_string( tmp->MoverParameters->ScndCtrlActualPos ) + ")" + "HamZ=" + to_string( vehicle->MoverParameters->fBrakeCtrlPos, 2 ) + + "; HamP=" + std::to_string( vehicle->MoverParameters->LocalBrakePos ) + "/" + to_string( vehicle->MoverParameters->LocalBrakePosA, 2 ) + + "; NasJ=" + std::to_string( vehicle->MoverParameters->MainCtrlPos ) + "(" + std::to_string( vehicle->MoverParameters->MainCtrlActualPos ) + ")" + + "; NasB=" + std::to_string( vehicle->MoverParameters->ScndCtrlPos ) + "(" + std::to_string( vehicle->MoverParameters->ScndCtrlActualPos ) + ")" + "; I=" + - ( tmp->MoverParameters->TrainType == dt_EZT ? - std::to_string( int( tmp->MoverParameters->ShowCurrent( 0 ) ) ) : - std::to_string( int( tmp->MoverParameters->Im ) ) ) - + "; U=" + to_string( int( tmp->MoverParameters->RunningTraction.TractionVoltage + 0.5 ) ) + ( vehicle->MoverParameters->TrainType == dt_EZT ? + std::to_string( int( vehicle->MoverParameters->ShowCurrent( 0 ) ) ) : + std::to_string( int( vehicle->MoverParameters->Im ) ) ) + + "; U=" + to_string( int( vehicle->MoverParameters->RunningTraction.TractionVoltage + 0.5 ) ) + "; R=" + - ( std::abs( tmp->MoverParameters->RunningShape.R ) > 10000.0 ? + ( std::abs( vehicle->MoverParameters->RunningShape.R ) > 10000.0 ? "~0.0" : - to_string( tmp->MoverParameters->RunningShape.R, 1 ) ) - + " An=" + to_string( tmp->MoverParameters->AccN, 2 ); // przyspieszenie poprzeczne + to_string( vehicle->MoverParameters->RunningShape.R, 1 ) ) + + " An=" + to_string( vehicle->MoverParameters->AccN, 2 ); // przyspieszenie poprzeczne if( tprev != simulation::Time.data().wSecond ) { tprev = simulation::Time.data().wSecond; - Acc = ( tmp->MoverParameters->Vel - VelPrev ) / 3.6; - VelPrev = tmp->MoverParameters->Vel; + Acc = ( vehicle->MoverParameters->Vel - VelPrev ) / 3.6; + VelPrev = vehicle->MoverParameters->Vel; } uitextline2 += ( "; As=" ) + to_string( Acc, 2 ); // przyspieszenie wzdłużne uitextline3 = - "cyl.ham. " + to_string( tmp->MoverParameters->BrakePress, 2 ) - + "; prz.gl. " + to_string( tmp->MoverParameters->PipePress, 2 ) - + "; zb.gl. " + to_string( tmp->MoverParameters->CompressedVolume, 2 ) + "cyl.ham. " + to_string( vehicle->MoverParameters->BrakePress, 2 ) + + "; prz.gl. " + to_string( vehicle->MoverParameters->PipePress, 2 ) + + "; zb.gl. " + to_string( vehicle->MoverParameters->CompressedVolume, 2 ) // youBy - drugi wezyk - + "; p.zas. " + to_string( tmp->MoverParameters->ScndPipePress, 2 ); + + "; p.zas. " + to_string( vehicle->MoverParameters->ScndPipePress, 2 ); // McZapkie: warto wiedziec w jakim stanie sa przelaczniki - if( tmp->MoverParameters->ConvOvldFlag ) + if( vehicle->MoverParameters->ConvOvldFlag ) uitextline3 += " C! "; - else if( tmp->MoverParameters->FuseFlag ) + else if( vehicle->MoverParameters->FuseFlag ) uitextline3 += " F! "; - else if( !tmp->MoverParameters->Mains ) + else if( !vehicle->MoverParameters->Mains ) uitextline3 += " () "; else { switch( - tmp->MoverParameters->ActiveDir * - ( tmp->MoverParameters->Imin == tmp->MoverParameters->IminLo ? + vehicle->MoverParameters->ActiveDir * + ( vehicle->MoverParameters->Imin == vehicle->MoverParameters->IminLo ? 1 : 2 ) ) { case 2: { uitextline3 += " >> "; break; } @@ -1813,50 +1686,50 @@ TWorld::Update_UI() { } } // McZapkie: predkosc szlakowa - if( tmp->MoverParameters->RunningTrack.Velmax == -1 ) { + if( vehicle->MoverParameters->RunningTrack.Velmax == -1 ) { uitextline3 += " Vtrack=Vmax"; } else { - uitextline3 += " Vtrack " + to_string( tmp->MoverParameters->RunningTrack.Velmax, 2 ); + uitextline3 += " Vtrack " + to_string( vehicle->MoverParameters->RunningTrack.Velmax, 2 ); } - if( ( tmp->MoverParameters->EnginePowerSource.SourceType == CurrentCollector ) - || ( tmp->MoverParameters->TrainType == dt_EZT ) ) { + if( ( vehicle->MoverParameters->EnginePowerSource.SourceType == CurrentCollector ) + || ( vehicle->MoverParameters->TrainType == dt_EZT ) ) { uitextline3 += - "; pant. " + to_string( tmp->MoverParameters->PantPress, 2 ) - + ( tmp->MoverParameters->bPantKurek3 ? "=" : "^" ) + "ZG"; + "; pant. " + to_string( vehicle->MoverParameters->PantPress, 2 ) + + ( vehicle->MoverParameters->bPantKurek3 ? "=" : "^" ) + "ZG"; } // McZapkie: komenda i jej parametry - if( tmp->MoverParameters->CommandIn.Command != ( "" ) ) { + if( vehicle->MoverParameters->CommandIn.Command != ( "" ) ) { uitextline4 = - "C:" + tmp->MoverParameters->CommandIn.Command - + " V1=" + to_string( tmp->MoverParameters->CommandIn.Value1, 0 ) - + " V2=" + to_string( tmp->MoverParameters->CommandIn.Value2, 0 ); + "C:" + vehicle->MoverParameters->CommandIn.Command + + " V1=" + to_string( vehicle->MoverParameters->CommandIn.Value1, 0 ) + + " V2=" + to_string( vehicle->MoverParameters->CommandIn.Value2, 0 ); } - if( ( tmp->Mechanik ) - && ( tmp->Mechanik->AIControllFlag == AIdriver ) ) { + if( ( vehicle->Mechanik ) + && ( vehicle->Mechanik->AIControllFlag == AIdriver ) ) { uitextline4 += - "AI: Vd=" + to_string( tmp->Mechanik->VelDesired, 0 ) - + " ad=" + to_string(tmp->Mechanik->AccDesired, 2) - + "/" + to_string(tmp->Mechanik->AccDesired*tmp->Mechanik->BrakeAccFactor(), 2) - + " atrain=" + to_string(tmp->Mechanik->fBrake_a0[0], 2) - + "+" + to_string(tmp->Mechanik->fBrake_a1[0], 2) - + " aS=" + to_string(tmp->Mechanik->AbsAccS_pub, 2) - + " Pd=" + to_string( tmp->Mechanik->ActualProximityDist, 0 ) - + " Vn=" + to_string( tmp->Mechanik->VelNext, 0 ); + "AI: Vd=" + to_string( vehicle->Mechanik->VelDesired, 0 ) + + " ad=" + to_string(vehicle->Mechanik->AccDesired, 2) + + "/" + to_string(vehicle->Mechanik->AccDesired*vehicle->Mechanik->BrakeAccFactor(), 2) + + " atrain=" + to_string(vehicle->Mechanik->fBrake_a0[0], 2) + + "+" + to_string(vehicle->Mechanik->fBrake_a1[0], 2) + + " aS=" + to_string(vehicle->Mechanik->AbsAccS_pub, 2) + + " Pd=" + to_string( vehicle->Mechanik->ActualProximityDist, 0 ) + + " Vn=" + to_string( vehicle->Mechanik->VelNext, 0 ); } // induction motor data - if( tmp->MoverParameters->EngineType == ElectricInductionMotor ) { + if( vehicle->MoverParameters->EngineType == ElectricInductionMotor ) { UITable->text_lines.emplace_back( " eimc: eimv: press:", Global::UITextColor ); for( int i = 0; i <= 20; ++i ) { std::string parameters = - tmp->MoverParameters->eimc_labels[ i ] + to_string( tmp->MoverParameters->eimc[ i ], 2, 9 ) + vehicle->MoverParameters->eimc_labels[ i ] + to_string( vehicle->MoverParameters->eimc[ i ], 2, 9 ) + " | " - + tmp->MoverParameters->eimv_labels[ i ] + to_string( tmp->MoverParameters->eimv[ i ], 2, 9 ); + + vehicle->MoverParameters->eimv_labels[ i ] + to_string( vehicle->MoverParameters->eimv[ i ], 2, 9 ); if( i < 10 ) { parameters += " | " + Train->fPress_labels[i] + to_string( Train->fPress[ i ][ 0 ], 2, 9 ); @@ -1865,7 +1738,7 @@ TWorld::Update_UI() { parameters += " med:"; } else if( i >= 13 ) { - parameters += " | " + tmp->MED_labels[ i - 13 ] + to_string( tmp->MED[ 0 ][ i - 13 ], 2, 9 ); + parameters += " | " + vehicle->MED_labels[ i - 13 ] + to_string( vehicle->MED[ 0 ][ i - 13 ], 2, 9 ); } UITable->text_lines.emplace_back( parameters, Global::UITextColor ); @@ -1938,16 +1811,7 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) // event CommLog( Now() + " " + std::to_string( pRozkaz->iComm ) + " " + std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) + " rcvd" ); -#ifdef EU07_USE_OLD_GROUNDCODE - if( Global::iMultiplayer ) { - TEvent *e = Ground.FindEvent( - std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); - if( e ) - if( ( e->Type == tp_Multiple ) || ( e->Type == tp_Lights ) || - ( e->evJoined != 0 ) ) // tylko jawne albo niejawne Multiple - Ground.AddToQuery( e, NULL ); // drugi parametr to dynamic wywołujący - tu brak - } -#else + if( Global::iMultiplayer ) { auto *event = simulation::Events.FindEvent( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); if( event != nullptr ) { @@ -1959,31 +1823,16 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) } } } -#endif break; } case 3: // rozkaz dla AI if (Global::iMultiplayer) { - int i = - int(pRozkaz->cString[8]); // długość pierwszego łańcucha (z przodu dwa floaty) + int i = int(pRozkaz->cString[8]); // długość pierwszego łańcucha (z przodu dwa floaty) CommLog( Now() + " " + to_string(pRozkaz->iComm) + " " + std::string(pRozkaz->cString + 11 + i, (unsigned)(pRozkaz->cString[10 + i])) + " rcvd"); -#ifdef EU07_USE_OLD_GROUNDCODE - TGroundNode *t = Ground.DynamicFind( - std::string(pRozkaz->cString + 11 + i, - (unsigned)pRozkaz->cString[10 + i])); // nazwa pojazdu jest druga - if (t) - if (t->DynamicObject->Mechanik) - { - t->DynamicObject->Mechanik->PutCommand(std::string(pRozkaz->cString + 9, i), - pRozkaz->fPar[0], pRozkaz->fPar[1], - NULL, stopExt); // floaty są z przodu - WriteLog("AI command: " + std::string(pRozkaz->cString + 9, i)); - } -#else // nazwa pojazdu jest druga auto *vehicle = simulation::Vehicles.find( { pRozkaz->cString + 11 + i, (unsigned)pRozkaz->cString[ 10 + i ] } ); if( ( vehicle != nullptr ) @@ -1995,25 +1844,18 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) stopExt ); // floaty są z przodu WriteLog("AI command: " + std::string(pRozkaz->cString + 9, i)); } -#endif } break; case 4: // badanie zajętości toru { CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd"); -#ifdef EU07_USE_OLD_GROUNDCODE - TGroundNode *t = Ground.FindGroundNode( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ), TP_TRACK ); - if (t) - if (t->pTrack->IsEmpty()) - multiplayer::WyslijWolny(t->asName); -#else + auto *track = simulation::Paths.find( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); if( ( track != nullptr ) && ( track->IsEmpty() ) ) { multiplayer::WyslijWolny( track->name() ); } -#endif } break; case 5: // ustawienie parametrów @@ -2046,18 +1888,6 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) + " rcvd" ); if (pRozkaz->cString[0]) { // jeśli długość nazwy jest niezerowa szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #7 -#ifdef EU07_USE_OLD_GROUNDCODE - TGroundNode *t; - if (pRozkaz->cString[1] == '*') - t = Ground.DynamicFind( - Global::asHumanCtrlVehicle); // nazwa pojazdu użytkownika - else - t = Ground.DynamicFindAny(std::string( - pRozkaz->cString + 1, (unsigned)pRozkaz->cString[0])); // nazwa pojazdu - if (t) - multiplayer::WyslijNamiary(t); // wysłanie informacji o pojeździe -#else - // TODO: re-enable when messaging component is in place auto *vehicle = ( pRozkaz->cString[ 1 ] == '*' ? simulation::Vehicles.find( Global::asHumanCtrlVehicle ) : @@ -2065,42 +1895,25 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) if( vehicle != nullptr ) { multiplayer::WyslijNamiary( vehicle ); // wysłanie informacji o pojeździe } -#endif } else { // dla pustego wysyłamy ramki 6 z nazwami pojazdów AI (jeśli potrzebne wszystkie, to rozpoznać np. "*") -#ifdef EU07_USE_OLD_GROUNDCODE - Ground.DynamicList(); -#else simulation::Vehicles.DynamicList(); -#endif } } break; case 8: // ponowne wysłanie informacji o zajętych odcinkach toru CommLog(Now() + " " + to_string(pRozkaz->iComm) + " all busy track" + " rcvd"); -#ifdef EU07_USE_OLD_GROUNDCODE - Ground.TrackBusyList(); -#else simulation::Paths.TrackBusyList(); -#endif break; case 9: // ponowne wysłanie informacji o zajętych odcinkach izolowanych CommLog(Now() + " " + to_string(pRozkaz->iComm) + " all busy isolated" + " rcvd"); -#ifdef EU07_USE_OLD_GROUNDCODE - Ground.IsolatedBusyList(); -#else simulation::Paths.IsolatedBusyList(); -#endif break; case 10: // badanie zajętości jednego odcinka izolowanego CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd"); -#ifdef EU07_USE_OLD_GROUNDCODE - Ground.IsolatedBusy(std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0]))); -#else simulation::Paths.IsolatedBusy( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) ); -#endif break; case 11: // ustawienie parametrów ruchu pojazdu // Ground.IsolatedBusy(AnsiString(pRozkaz->cString+1,(unsigned)(pRozkaz->cString[0]))); @@ -2111,56 +1924,31 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz) // Ground.IsolatedBusy(AnsiString(pRozkaz->cString+1,(unsigned)(pRozkaz->cString[0]))); break; case 13: // ramka uszkodzenia i innych stanow pojazdu, np. wylaczenie CA, wlaczenie recznego itd. - CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + - std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + - " rcvd"); -#ifdef EU07_USE_OLD_GROUNDCODE - if( pRozkaz->cString[ 1 ] ) // jeśli długość nazwy jest niezerowa - { // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #13 - TGroundNode *t; - if( pRozkaz->cString[ 2 ] == '*' ) - t = Ground.DynamicFind( - Global::asHumanCtrlVehicle ); // nazwa pojazdu użytkownika - else - t = Ground.DynamicFindAny( - std::string( pRozkaz->cString + 2, - (unsigned)pRozkaz->cString[ 1 ] ) ); // nazwa pojazdu - if( t ) { - TDynamicObject *d = t->DynamicObject; - while( d ) { - d->Damage( pRozkaz->cString[ 0 ] ); - d = d->Next(); // pozostałe też - } - d = t->DynamicObject->Prev(); - while( d ) { - d->Damage( pRozkaz->cString[ 0 ] ); - d = d->Prev(); // w drugą stronę też - } - multiplayer::WyslijUszkodzenia( t->asName, t->DynamicObject->MoverParameters->EngDmgFlag ); // zwrot informacji o pojeździe - } - } -#else - if( pRozkaz->cString[ 1 ] ) // jeśli długość nazwy jest niezerowa - { // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #13 - auto *lookup = ( - pRozkaz->cString[ 2 ] == '*' ? - simulation::Vehicles.find( Global::asHumanCtrlVehicle ) : // nazwa pojazdu użytkownika - simulation::Vehicles.find( std::string( pRozkaz->cString + 2, (unsigned)pRozkaz->cString[ 1 ] ) ) ); // nazwa pojazdu - if( lookup == nullptr ) { break; } // nothing found, nothing to do - auto *d { lookup }; - while( d != nullptr ) { - d->Damage( pRozkaz->cString[ 0 ] ); - d = d->Next(); // pozostałe też - } - d = lookup->Prev(); - while( d != nullptr ) { - d->Damage( pRozkaz->cString[ 0 ] ); - d = d->Prev(); // w drugą stronę też - } - multiplayer::WyslijUszkodzenia( lookup->asName, lookup->MoverParameters->EngDmgFlag ); // zwrot informacji o pojeździe - } -#endif + CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " + + std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + + " rcvd"); + if( pRozkaz->cString[ 1 ] ) // jeśli długość nazwy jest niezerowa + { // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #13 + auto *lookup = ( + pRozkaz->cString[ 2 ] == '*' ? + simulation::Vehicles.find( Global::asHumanCtrlVehicle ) : // nazwa pojazdu użytkownika + simulation::Vehicles.find( std::string( pRozkaz->cString + 2, (unsigned)pRozkaz->cString[ 1 ] ) ) ); // nazwa pojazdu + if( lookup == nullptr ) { break; } // nothing found, nothing to do + auto *d { lookup }; + while( d != nullptr ) { + d->Damage( pRozkaz->cString[ 0 ] ); + d = d->Next(); // pozostałe też + } + d = lookup->Prev(); + while( d != nullptr ) { + d->Damage( pRozkaz->cString[ 0 ] ); + d = d->Prev(); // w drugą stronę też + } + multiplayer::WyslijUszkodzenia( lookup->asName, lookup->MoverParameters->EngDmgFlag ); // zwrot informacji o pojeździe + } break; + default: + break; } }; @@ -2207,10 +1995,9 @@ void TWorld::CreateE3D(std::string const &Path, bool Dynamic) shift += 10.0; // następny tor będzie deczko dalej, aby nie zabić FPS at = 400.0; } - TGroundNode *tmp = new TGroundNode(); - tmp->DynamicObject = new TDynamicObject(); + auto *dynamic = new TDynamicObject(); - at -= tmp->DynamicObject->Init( + at -= dynamic->Init( "", Path.substr( 8, Path.size() - 9 ), // skip leading "dynamic/" and trailing slash "none", @@ -2220,7 +2007,7 @@ void TWorld::CreateE3D(std::string const &Path, bool Dynamic) "nobody", 0.0, "none", 0.0, "", false, "" ); // po wczytaniu CHK zrobić pętlę po ładunkach, aby każdy z nich skonwertować - cParser loadparser( tmp->DynamicObject->MoverParameters->LoadAccepted ); // typy ładunków + cParser loadparser( dynamic->MoverParameters->LoadAccepted ); // typy ładunków std::string loadname; loadparser.getTokens( 1, true, "," ); loadparser >> loadname; while( loadname != "" ) { @@ -2228,7 +2015,7 @@ void TWorld::CreateE3D(std::string const &Path, bool Dynamic) if( ( true == FileExists( Path + loadname + ".t3d" ) ) && ( false == FileExists( Path + loadname + ".e3d" ) ) ) { // a nie ma jeszcze odpowiednika binarnego - at -= tmp->DynamicObject->Init( + at -= dynamic->Init( "", Path.substr( 8, Path.size() - 9 ), // skip leading "dynamic/" and trailing slash "none", @@ -2242,20 +2029,20 @@ void TWorld::CreateE3D(std::string const &Path, bool Dynamic) loadparser.getTokens( 1, true, "," ); loadparser >> loadname; } - if( tmp->DynamicObject->iCabs ) { // jeśli ma jakąkolwiek kabinę + if( dynamic->iCabs ) { // jeśli ma jakąkolwiek kabinę delete Train; Train = new TTrain(); - if( tmp->DynamicObject->iCabs & 1 ) { - tmp->DynamicObject->MoverParameters->ActiveCab = 1; - Train->Init( tmp->DynamicObject, true ); + if( dynamic->iCabs & 1 ) { + dynamic->MoverParameters->ActiveCab = 1; + Train->Init( dynamic, true ); } - if( tmp->DynamicObject->iCabs & 4 ) { - tmp->DynamicObject->MoverParameters->ActiveCab = -1; - Train->Init( tmp->DynamicObject, true ); + if( dynamic->iCabs & 4 ) { + dynamic->MoverParameters->ActiveCab = -1; + Train->Init( dynamic, true ); } - if( tmp->DynamicObject->iCabs & 2 ) { - tmp->DynamicObject->MoverParameters->ActiveCab = 0; - Train->Init( tmp->DynamicObject, true ); + if( dynamic->iCabs & 2 ) { + dynamic->MoverParameters->ActiveCab = 0; + Train->Init( dynamic, true ); } } // z powrotem defaultowa sciezka do tekstur diff --git a/World.h b/World.h index bd366f8c..9acc2581 100644 --- a/World.h +++ b/World.h @@ -13,7 +13,6 @@ http://mozilla.org/MPL/2.0/. #include #include "Camera.h" -#include "Ground.h" #include "scene.h" #include "sky.h" #include "sun.h" @@ -125,9 +124,6 @@ private: TCamera Camera; TCamera DebugCamera; -#ifdef EU07_USE_OLD_GROUNDCODE - TGround Ground; -#endif world_environment Environment; TTrain *Train; TDynamicObject *pDynamicNearest; diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index ab330c49..5deecf08 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -72,9 +72,6 @@ Source Files - - Source Files - Source Files @@ -278,9 +275,6 @@ Header Files - - Header Files - Header Files diff --git a/messaging.cpp b/messaging.cpp index 94781707..2cab175e 100644 --- a/messaging.cpp +++ b/messaging.cpp @@ -12,7 +12,6 @@ http://mozilla.org/MPL/2.0/. #include "globals.h" #include "simulation.h" -#include "ground.h" #include "mtable.h" #include "logs.h" @@ -93,84 +92,6 @@ WyslijWolny(const std::string &t) WyslijString(t, 4); // tor wolny } -#ifdef EU07_USE_OLD_GROUNDCODE -void -WyslijNamiary(TGroundNode *t) -{ // wysłanie informacji o pojeździe - (float), długość ramki będzie zwiększana w miarę potrzeby - // 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 = t->asName.length(); - r.iPar[0] = i; // ilość danych liczbowych - r.fPar[1] = Global::fTimeAngleDeg / 360.0; // aktualny czas (1.0=doba) - r.fPar[2] = t->DynamicObject->MoverParameters->Loc.X; // pozycja X - r.fPar[3] = t->DynamicObject->MoverParameters->Loc.Y; // pozycja Y - r.fPar[4] = t->DynamicObject->MoverParameters->Loc.Z; // pozycja Z - r.fPar[5] = t->DynamicObject->MoverParameters->V; // prędkość ruchu X - r.fPar[6] = t->DynamicObject->MoverParameters->nrot * M_PI * - t->DynamicObject->MoverParameters->WheelDiameter; // prędkość obrotowa kóŁ - r.fPar[7] = 0; // prędkość ruchu Z - r.fPar[8] = t->DynamicObject->MoverParameters->AccS; // przyspieszenie X - r.fPar[9] = t->DynamicObject->MoverParameters->AccN; // przyspieszenie Y //na razie nie - r.fPar[10] = t->DynamicObject->MoverParameters->AccV; // przyspieszenie Z - r.fPar[11] = t->DynamicObject->MoverParameters->DistCounter; // przejechana odległość w km - r.fPar[12] = t->DynamicObject->MoverParameters->PipePress; // ciśnienie w PG - r.fPar[13] = t->DynamicObject->MoverParameters->ScndPipePress; // ciśnienie w PZ - r.fPar[14] = t->DynamicObject->MoverParameters->BrakePress; // ciśnienie w CH - r.fPar[15] = t->DynamicObject->MoverParameters->Compressor; // ciśnienie w ZG - r.fPar[16] = t->DynamicObject->MoverParameters->Itot; // Prąd całkowity - r.iPar[17] = t->DynamicObject->MoverParameters->MainCtrlPos; // Pozycja NJ - r.iPar[18] = t->DynamicObject->MoverParameters->ScndCtrlPos; // Pozycja NB - r.iPar[19] = t->DynamicObject->MoverParameters->MainCtrlActualPos; // Pozycja jezdna - r.iPar[20] = t->DynamicObject->MoverParameters->ScndCtrlActualPos; // Pozycja bocznikowania - r.iPar[21] = t->DynamicObject->MoverParameters->ScndCtrlActualPos; // Pozycja bocznikowania - r.iPar[22] = t->DynamicObject->MoverParameters->ResistorsFlag * 1 + - t->DynamicObject->MoverParameters->ConverterFlag * 2 + - +t->DynamicObject->MoverParameters->CompressorFlag * 4 + - t->DynamicObject->MoverParameters->Mains * 8 + - +t->DynamicObject->MoverParameters->DoorLeftOpened * 16 + - t->DynamicObject->MoverParameters->DoorRightOpened * 32 + - +t->DynamicObject->MoverParameters->FuseFlag * 64 + - t->DynamicObject->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 < t->DynamicObject->iAnimType[ANIM_PANTS]) - { - r.fPar[23 + p] = t->DynamicObject->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] = - t->DynamicObject->MoverParameters->ShowCurrent(p + 1); // amperomierze kolejnych grup - // WriteLog("zapisalem prady"); - r.iPar[30] = t->DynamicObject->MoverParameters->WarningSignal; // trabienie - r.fPar[31] = t->DynamicObject->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, t->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) + " " + t->asName + " sent"); -} -#else void WyslijNamiary(TDynamicObject const *Vehicle) { // wysłanie informacji o pojeździe - (float), długość ramki będzie zwiększana w miarę potrzeby @@ -247,7 +168,6 @@ WyslijNamiary(TDynamicObject const *Vehicle) // WriteLog("Ramka poszla!"); CommLog( Now() + " " + std::to_string(r.iComm) + " " + Vehicle->asName + " sent"); } -#endif void WyslijObsadzone() diff --git a/messaging.h b/messaging.h index c41dab05..6bd7a0e3 100644 --- a/messaging.h +++ b/messaging.h @@ -11,11 +11,7 @@ http://mozilla.org/MPL/2.0/. #include -#ifdef EU07_USE_OLD_GROUNDCODE -class TGroundNode; -#else class TDynamicObject; -#endif namespace multiplayer { @@ -44,11 +40,7 @@ void Navigate( std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lPa void WyslijEvent( const std::string &e, const std::string &d ); void WyslijString( const std::string &t, int n ); void WyslijWolny( const std::string &t ); -#ifdef EU07_USE_OLD_GROUNDCODE -void WyslijNamiary( TGroundNode *t ); -#else void WyslijNamiary( TDynamicObject const *Vehicle ); -#endif void WyslijParam( int nr, int fl ); void WyslijUszkodzenia( const std::string &t, char fl ); void WyslijObsadzone(); // -> skladanie wielu pojazdow diff --git a/Ground.cpp b/old/Ground.cpp similarity index 100% rename from Ground.cpp rename to old/Ground.cpp diff --git a/Ground.h b/old/Ground.h similarity index 100% rename from Ground.h rename to old/Ground.h diff --git a/renderer.cpp b/renderer.cpp index e00f83a2..e66f60b5 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -440,18 +440,10 @@ opengl_renderer::Render_pass( rendermode const Mode ) { } #endif switch_units( true, true, true ); -#ifdef EU07_USE_OLD_GROUNDCODE - Render( &World.Ground ); -#else Render( simulation::Region ); -#endif // ...translucent parts setup_drawing( true ); -#ifdef EU07_USE_OLD_GROUNDCODE - Render_Alpha( &World.Ground ); -#else Render_Alpha( simulation::Region ); -#endif if( World.Train != nullptr ) { // cab render is performed without shadows, due to low resolution and number of models without windows :| switch_units( true, false, false ); @@ -501,11 +493,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { #else setup_units( false, false, false ); #endif -#ifdef EU07_USE_OLD_GROUNDCODE - Render( &World.Ground ); -#else Render( simulation::Region ); -#endif // post-render restore ::glDisable( GL_POLYGON_OFFSET_FILL ); ::glDisable( GL_SCISSOR_TEST ); @@ -534,11 +522,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { // opaque parts... setup_drawing( false ); setup_units( true, true, true ); -#ifdef EU07_USE_OLD_GROUNDCODE - Render( &World.Ground ); -#else Render( simulation::Region ); -#endif /* // reflections are limited to sky and ground only, the update rate is too low for anything else // ...translucent parts @@ -591,11 +575,7 @@ opengl_renderer::Render_pass( rendermode const Mode ) { // opaque parts... setup_drawing( false ); setup_units( false, false, false ); -#ifdef EU07_USE_OLD_GROUNDCODE - Render( &World.Ground ); -#else Render( simulation::Region ); -#endif // post-render cleanup } break; @@ -1365,450 +1345,6 @@ opengl_renderer::Texture( texture_handle const Texture ) const { return m_textures.texture( Texture ); } -#ifdef EU07_USE_OLD_GROUNDCODE -bool -opengl_renderer::Render( TGround *Ground ) { - - m_cellqueue.clear(); - - glm::vec3 const cameraposition { m_renderpass.camera.position() }; - int const camerax = static_cast( std::floor( cameraposition.x / 1000.0f ) + iNumRects / 2 ); - int const cameraz = static_cast( std::floor( cameraposition.z / 1000.0f ) + iNumRects / 2 ); - int const segmentcount = 2 * static_cast(std::ceil( m_renderpass.draw_range * Global::fDistanceFactor / 1000.0f )); - int const originx = std::max( 0, camerax - segmentcount / 2 ); - int const originz = std::max( 0, cameraz - segmentcount / 2 ); - - switch( m_renderpass.draw_mode ) { - case rendermode::color: { - - Update_Lights( simulation::Lights ); - - for( int column = originx; column <= originx + segmentcount; ++column ) { - for( int row = originz; row <= originz + segmentcount; ++row ) { - - auto *cell = &Ground->Rects[ column ][ row ]; -#ifdef EU07_USE_DEBUG_CULLING - if( m_worldcamera.camera.visible( cell->m_area ) ) { -#else - if( m_renderpass.camera.visible( cell->m_area ) ) { -#endif - Render( cell ); - } - } - } - // draw queue was filled while rendering content of ground cells. now sort the nodes based on their distance to viewer... - std::sort( - std::begin( m_cellqueue ), - std::end( m_cellqueue ), - []( distancesubcell_pair const &Left, distancesubcell_pair const &Right ) { - return ( Left.first ) < ( Right.first ); } ); - // ...then render the opaque content of the visible subcells. - for( auto subcellpair : m_cellqueue ) { - Render( subcellpair.second ); - } - break; - } - case rendermode::reflections: { - // reflections render only terrain geometry - for( int column = originx; column <= originx + segmentcount; ++column ) { - for( int row = originz; row <= originz + segmentcount; ++row ) { - - auto *cell = &Ground->Rects[ column ][ row ]; - if( m_renderpass.camera.visible( cell->m_area ) ) { - Render( cell ); - } - } - } - break; - } - case rendermode::shadows: - case rendermode::pickscenery: { - // these render modes don't bother with anything non-visual, or lights - for( int column = originx; column <= originx + segmentcount; ++column ) { - for( int row = originz; row <= originz + segmentcount; ++row ) { - - auto *cell = &Ground->Rects[ column ][ row ]; - if( m_renderpass.camera.visible( cell->m_area ) ) { - Render( cell ); - } - } - } - // they can also skip queue sorting, as they only deal with opaque geometry - // NOTE: there's benefit from rendering front-to-back, but is it significant enough? TODO: investigate - for( auto subcellpair : m_cellqueue ) { - Render( subcellpair.second ); - } - break; - } - case rendermode::pickcontrols: - default: { - break; - } - } - - return true; -} - -bool -opengl_renderer::Render( TGroundRect *Groundcell ) { - - bool result { false }; // will be true if we do any rendering - - Groundcell->LoadNodes(); // ewentualne tworzenie siatek - - switch( m_renderpass.draw_mode ) { - case rendermode::pickscenery: { - // non-interactive scenery elements get neutral colour - ::glColor3fv( glm::value_ptr( colors::none ) ); - } - case rendermode::color: - case rendermode::reflections: { - if( Groundcell->nRenderRect != nullptr ) { - // nieprzezroczyste trójkąty kwadratu kilometrowego - for( TGroundNode *node = Groundcell->nRenderRect; node != nullptr; node = node->nNext3 ) { - Render( node ); - } - result = true; - } - break; - } - case rendermode::shadows: { - if( Groundcell->nRenderRect != nullptr ) { - // experimental, for shadows render both back and front faces, to supply back faces of the 'forest strips' - ::glDisable( GL_CULL_FACE ); - // nieprzezroczyste trójkąty kwadratu kilometrowego - for( TGroundNode *node = Groundcell->nRenderRect; node != nullptr; node = node->nNext3 ) { - Render( node ); - } - result = true; - ::glEnable( GL_CULL_FACE ); - } - } - case rendermode::pickcontrols: - default: { - break; - } - } - - // add the subcells of the cell to the draw queue - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::shadows: - case rendermode::pickscenery: { - if( Groundcell->pSubRects != nullptr ) { - for( std::size_t subcellindex = 0; subcellindex < iNumSubRects * iNumSubRects; ++subcellindex ) { - auto subcell = Groundcell->pSubRects + subcellindex; - if( subcell->iNodeCount ) { - // o ile są jakieś obiekty, bo po co puste sektory przelatywać - m_cellqueue.emplace_back( - glm::length2( m_renderpass.camera.position() - glm::dvec3( subcell->m_area.center ) ), - subcell ); - } - } - } - break; - } - case rendermode::reflections: - case rendermode::pickcontrols: - default: { - break; - } - } - return result; -} - -bool -opengl_renderer::Render( TSubRect *Groundsubcell ) { - - // oznaczanie aktywnych sektorów - Groundsubcell->LoadNodes(); - - // przeliczenia animacji torów w sektorze - Groundsubcell->RaAnimate( m_framestamp ); - - TGroundNode *node; - - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::shadows: { - // nieprzezroczyste obiekty terenu - for( node = Groundsubcell->nRenderRect; node != nullptr; node = node->nNext3 ) { - Render( node ); - } - // nieprzezroczyste obiekty (oprócz pojazdów) - for( node = Groundsubcell->nRender; node != nullptr; node = node->nNext3 ) { - Render( node ); - } - // nieprzezroczyste z mieszanych modeli - for( node = Groundsubcell->nRenderMixed; node != nullptr; node = node->nNext3 ) { - Render( node ); - } - // nieprzezroczyste fragmenty pojazdów na torach - for( int trackidx = 0; trackidx < Groundsubcell->iTracks; ++trackidx ) { - for( auto dynamic : Groundsubcell->tTracks[ trackidx ]->Dynamics ) { - Render( dynamic ); - } - } -#ifdef EU07_SCENERY_EDITOR - // memcells - if( EditorModeFlag ) { - for( auto const memcell : Groundsubcell->m_memcells ) { - Render( memcell ); - } - } -#endif - break; - } - case rendermode::pickscenery: { - // same procedure like with regular render, but each node receives custom colour used for picking - // nieprzezroczyste obiekty terenu - for( node = Groundsubcell->nRenderRect; node != nullptr; node = node->nNext3 ) { - ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); - Render( node ); - } - // nieprzezroczyste obiekty (oprócz pojazdów) - for( node = Groundsubcell->nRender; node != nullptr; node = node->nNext3 ) { - ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); - Render( node ); - } - // nieprzezroczyste z mieszanych modeli - for( node = Groundsubcell->nRenderMixed; node != nullptr; node = node->nNext3 ) { - ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); - Render( node ); - } - // nieprzezroczyste fragmenty pojazdów na torach - for( int trackidx = 0; trackidx < Groundsubcell->iTracks; ++trackidx ) { - for( auto dynamic : Groundsubcell->tTracks[ trackidx ]->Dynamics ) { - ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); - Render( dynamic ); - } - } -#ifdef EU07_SCENERY_EDITOR - // memcells - if( EditorModeFlag ) { - for( auto const memcell : Groundsubcell->m_memcells ) { - ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); - Render( memcell ); - } - } -#endif - break; - } - case rendermode::pickcontrols: - default: { - break; - } - } - - return true; -} - -bool -opengl_renderer::Render( TGroundNode *Node ) { - - double distancesquared; - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; - break; - } - default: { - distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; - break; - } - } - if( ( distancesquared < Node->fSquareMinRadius ) - || ( distancesquared >= Node->fSquareRadius ) ) { - return false; - } - - switch (Node->iType) { - - case TP_TRACK: { - // setup - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - return false; - } - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Node ); - break; - } - default: { - break; - } - } - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - // render - Render( Node->pTrack ); - // debug - ++m_debugstats.paths; - ++m_debugstats.drawcalls; - // post-render cleanup - ::glPopMatrix(); - return true; - } - - case TP_MODEL: { - switch( m_renderpass.draw_mode ) { - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Node ); - break; - } - default: { - break; - } - } - Node->Model->RaAnimate( m_framestamp ); // jednorazowe przeliczenie animacji - Node->Model->RaPrepare(); - if( Node->Model->pModel ) { - // renderowanie rekurencyjne submodeli - Render( - Node->Model->pModel, - Node->Model->Material(), - distancesquared, - Node->pCenter - m_renderpass.camera.position(), - Node->Model->vAngle ); - } - return true; - } - - case GL_LINES: { - if( ( Node->Piece->geometry == null_handle ) - || ( Node->fLineThickness > 0.0 ) ) { - return false; - } - // setup - auto const distance = std::sqrt( distancesquared ); - auto const linealpha = - 10.0 * Node->fLineThickness - / std::max( - 0.5 * Node->m_radius + 1.0, - distance - ( 0.5 * Node->m_radius ) ); - switch( m_renderpass.draw_mode ) { - // wire colouring is disabled for modes other than colour - case rendermode::color: { - ::glColor4fv( - glm::value_ptr( - glm::vec4( - Node->Diffuse * glm::vec3( Global::DayLight.ambient ), // w zaleznosci od koloru swiatla - 1.0 ) ) ); // if the thickness is defined negative, lines are always drawn opaque - break; - } - case rendermode::shadows: - case rendermode::pickcontrols: - case rendermode::pickscenery: - default: { - break; - } - } - auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 8.0 ); - if( linewidth > 1.0 ) { - ::glLineWidth( static_cast( linewidth ) ); - } - - GfxRenderer.Bind_Material( null_handle ); - - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - switch( m_renderpass.draw_mode ) { - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Node ); - break; - } - default: { - break; - } - } - // render - m_geometry.draw( Node->Piece->geometry ); - // debug -// ++m_debugstats.lines; -// ++m_debugstats.drawcalls; - // post-render cleanup - ::glPopMatrix(); - - if( linewidth > 1.0 ) { ::glLineWidth( 1.0f ); } - - return true; - } - - case GL_TRIANGLES: { - if( ( Node->Piece->geometry == null_handle ) - || ( ( Node->iFlags & 0x10 ) == 0 ) ) { - return false; - } - // setup - Bind_Material( Node->m_material ); - switch( m_renderpass.draw_mode ) { - case rendermode::color: { - ::glColor3fv( glm::value_ptr( Node->Diffuse ) ); - break; - } - // pick modes get custom colours, and shadow pass doesn't use any - case rendermode::shadows: - case rendermode::pickcontrols: - case rendermode::pickscenery: - default: { - break; - } - } - - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - switch( m_renderpass.draw_mode ) { - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Node ); - break; - } - default: { - break; - } - } - // render - m_geometry.draw( Node->Piece->geometry ); - // debug - ++m_debugstats.shapes; - ++m_debugstats.drawcalls; - - // post-render cleanup - ::glPopMatrix(); - - return true; - } - - case TP_MEMCELL: { - switch( m_renderpass.draw_mode ) { - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Node ); - break; - } - default: { - break; - } - } - Render( Node->MemCell ); - return true; - } - - default: { break; } - } - // in theory we shouldn't ever get here but, eh - return false; -} -#else void opengl_renderer::Render( scene::basic_region *Region ) { @@ -2060,7 +1596,15 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator // tracks // TODO: update after path node refactoring Render( std::begin( cell->m_paths ), std::end( cell->m_paths ) ); - +#ifdef EU07_SCENERY_EDITOR + // TODO: re-implement + // memcells + if( EditorModeFlag ) { + for( auto const memcell : Groundsubcell->m_memcells ) { + Render( memcell ); + } + } +#endif // post-render cleanup ::glPopMatrix(); @@ -2095,12 +1639,20 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator ::glColor3fv( glm::value_ptr( colors::none ) ); for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } // tracks - // TODO: add path to the node picking list for( auto *path : cell->m_paths ) { ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); Render( path ); } - +#ifdef EU07_SCENERY_EDITOR + // memcells + // TODO: re-implement + if( EditorModeFlag ) { + for( auto const memcell : Groundsubcell->m_memcells ) { + ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); + Render( memcell ); + } + } +#endif // post-render cleanup ::glPopMatrix(); @@ -2250,7 +1802,6 @@ opengl_renderer::Render( TAnimModel *Instance ) { Instance->vAngle ); } } -#endif bool opengl_renderer::Render( TDynamicObject *Dynamic ) { @@ -2938,213 +2489,6 @@ opengl_renderer::Render( TMemCell *Memcell ) { ::glPopMatrix(); } -#ifdef EU07_USE_OLD_GROUNDCODE -bool -opengl_renderer::Render_Alpha( TGround *Ground ) { - - TGroundNode *node; - TSubRect *tmp; - // Ra: renderowanie progresywne - zależne od FPS oraz kierunku patrzenia - for( auto subcellpair = std::rbegin( m_cellqueue ); subcellpair != std::rend( m_cellqueue ); ++subcellpair ) { - // przezroczyste trójkąty w oddzielnym cyklu przed modelami - tmp = subcellpair->second; - for( node = tmp->nRenderRectAlpha; node; node = node->nNext3 ) { - Render_Alpha( node ); - } - } - for( auto subcellpair = std::rbegin( m_cellqueue ); subcellpair != std::rend( m_cellqueue ); ++subcellpair ) - { // renderowanie przezroczystych modeli oraz pojazdów - Render_Alpha( subcellpair->second ); - } - - ::glDisable( GL_LIGHTING ); // linie nie powinny świecić - - for( auto subcellpair = std::rbegin( m_cellqueue ); subcellpair != std::rend( m_cellqueue ); ++subcellpair ) { - // druty na końcu, żeby się nie robiły białe plamy na tle lasu - tmp = subcellpair->second; - for( node = tmp->nRenderWires; node; node = node->nNext3 ) { - Render_Alpha( node ); - } - } - - ::glEnable( GL_LIGHTING ); - - return true; -} - -bool -opengl_renderer::Render_Alpha( TSubRect *Groundsubcell ) { - - TGroundNode *node; - for( node = Groundsubcell->nRenderMixed; node; node = node->nNext3 ) - Render_Alpha( node ); // przezroczyste z mieszanych modeli - for( node = Groundsubcell->nRenderAlpha; node; node = node->nNext3 ) - Render_Alpha( node ); // przezroczyste modele - for( int trackidx = 0; trackidx < Groundsubcell->iTracks; ++trackidx ) { - for( auto dynamic : Groundsubcell->tTracks[ trackidx ]->Dynamics ) { - Render_Alpha( dynamic ); // przezroczyste fragmenty pojazdów na torach - } - } - - return true; -} - -bool -opengl_renderer::Render_Alpha( TGroundNode *Node ) { - - double distancesquared; - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor; - break; - } - default: { - distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor; - break; - } - } - if( ( distancesquared < Node->fSquareMinRadius ) - || ( distancesquared >= Node->fSquareRadius ) ) { - return false; - } - - switch (Node->iType) - { - case TP_TRACTION: { - if( Node->bVisible ) { - // rysuj jesli sa druty i nie zerwana - if( ( Node->hvTraction->Wires == 0 ) - || ( true == TestFlag( Node->hvTraction->DamageFlag, 128 ) ) ) { - return false; - } - // setup - if( !Global::bSmoothTraction ) { - // na liniach kiepsko wygląda - robi gradient - ::glDisable( GL_LINE_SMOOTH ); - } - float const linealpha = static_cast( - std::min( - 1.25, - 5000 * Node->hvTraction->WireThickness / ( distancesquared + 1.0 ) ) ); // zbyt grube nie są dobre - ::glLineWidth( linealpha ); - // McZapkie-261102: kolor zalezy od materialu i zasniedzenia - auto const color { Node->hvTraction->wire_color() }; - ::glColor4f( color.r, color.g, color.b, linealpha ); - - Bind_Material( null_handle ); - - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - // render - m_geometry.draw( Node->hvTraction->m_geometry ); - // debug data - ++m_debugstats.traction; - ++m_debugstats.drawcalls; - - // post-render cleanup - ::glPopMatrix(); - - ::glLineWidth( 1.0 ); - if( !Global::bSmoothTraction ) { - ::glEnable( GL_LINE_SMOOTH ); - } - - return true; - } - else { - return false; - } - } - case TP_MODEL: { - - Node->Model->RaPrepare(); - if( Node->Model->pModel ) { - // renderowanie rekurencyjne submodeli - Render_Alpha( - Node->Model->pModel, - Node->Model->Material(), - distancesquared, - Node->pCenter - m_renderpass.camera.position(), - Node->Model->vAngle ); - } - return true; - } - - case GL_LINES: { - if( ( Node->Piece->geometry == null_handle ) - || ( Node->fLineThickness < 0.0 ) ) { - return false; - } - // setup - auto const distance = std::sqrt( distancesquared ); - auto const linealpha = - 10.0 * Node->fLineThickness - / std::max( - 0.5 * Node->m_radius + 1.0, - distance - ( 0.5 * Node->m_radius ) ); - ::glColor4fv( - glm::value_ptr( - glm::vec4( - Node->Diffuse * glm::vec3( Global::DayLight.ambient ), // w zaleznosci od koloru swiatla - std::min( 1.0, linealpha ) ) ) ); - auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 8.0 ); - if( linewidth > 1.0 ) { - ::glLineWidth( static_cast(linewidth) ); - } - - GfxRenderer.Bind_Material( null_handle ); - - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - // render - m_geometry.draw( Node->Piece->geometry ); - ++m_debugstats.lines; - ++m_debugstats.drawcalls; - - // post-render cleanup - ::glPopMatrix(); - - if( linewidth > 1.0 ) { ::glLineWidth( 1.0f ); } - - return true; - } - - case GL_TRIANGLES: { - if( ( Node->Piece->geometry == null_handle ) - || ( ( Node->iFlags & 0x20 ) == 0 ) ) { - return false; - } - // setup - ::glColor3fv( glm::value_ptr( Node->Diffuse ) ); - - Bind_Material( Node->m_material ); - - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - // render - m_geometry.draw( Node->Piece->geometry ); - // debug data - ++m_debugstats.shapes; - ++m_debugstats.drawcalls; - // post-render cleanup - ::glPopMatrix(); - - return true; - } - - default: { break; } - } - // in theory we shouldn't ever get here but, eh - return false; -} -#else void opengl_renderer::Render_Alpha( scene::basic_region *Region ) { @@ -3377,7 +2721,7 @@ opengl_renderer::Render_Alpha( scene::lines_node const &Lines ) { ++m_debugstats.lines; ++m_debugstats.drawcalls; } -#endif + bool opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { @@ -3748,11 +3092,7 @@ opengl_renderer::Update_Pick_Control() { return control; } -#ifdef EU07_USE_OLD_GROUNDCODE -TGroundNode const * -#else editor::basic_node const * -#endif opengl_renderer::Update_Pick_Node() { #ifdef EU07_USE_PICKING_FRAMEBUFFER @@ -3787,11 +3127,7 @@ opengl_renderer::Update_Pick_Node() { unsigned char pickreadout[4]; ::glReadPixels( pickbufferpos.x, pickbufferpos.y, 1, 1, GL_BGRA, GL_UNSIGNED_BYTE, pickreadout ); auto const nodeindex = pick_index( glm::ivec3{ pickreadout[ 2 ], pickreadout[ 1 ], pickreadout[ 0 ] } ); -#ifdef EU07_USE_OLD_GROUNDCODE - TGroundNode const *node { nullptr }; -#else editor::basic_node const *node { nullptr }; -#endif if( ( nodeindex > 0 ) && ( nodeindex <= m_picksceneryitems.size() ) ) { node = m_picksceneryitems[ nodeindex - 1 ]; diff --git a/renderer.h b/renderer.h index d0c7970c..15f41e85 100644 --- a/renderer.h +++ b/renderer.h @@ -192,22 +192,14 @@ public: // utility methods TSubModel const * Pick_Control() const { return m_pickcontrolitem; } -#ifdef EU07_USE_OLD_GROUNDCODE - TGroundNode const * -#else editor::basic_node const * -#endif Pick_Node() const { return m_picksceneryitem; } // maintenance jobs void Update( double const Deltatime ); TSubModel const * Update_Pick_Control(); -#ifdef EU07_USE_OLD_GROUNDCODE - TGroundNode const * -#else editor::basic_node const * -#endif Update_Pick_Node(); // debug performance string std::string const & @@ -248,13 +240,9 @@ private: int drawcalls { 0 }; }; -#ifdef EU07_USE_OLD_GROUNDCODE - using distancesubcell_pair = std::pair< double, TSubRect * >; -#else using section_sequence = std::vector; using distancecell_pair = std::pair; using cell_sequence = std::vector; -#endif struct renderpass_config { @@ -295,16 +283,6 @@ private: Render_reflections(); bool Render( world_environment *Environment ); -#ifdef EU07_USE_OLD_GROUNDCODE - bool - Render( TGround *Ground ); - bool - Render( TGroundRect *Groundcell ); - bool - Render( TSubRect *Groundsubcell ); - bool - Render( TGroundNode *Node ); -#else void Render( scene::basic_region *Region ); void @@ -315,7 +293,6 @@ private: Render( scene::shape_node const &Shape, bool const Ignorerange ); void Render( TAnimModel *Instance ); -#endif bool Render( TDynamicObject *Dynamic ); bool @@ -332,14 +309,6 @@ private: Render_cab( TDynamicObject *Dynamic, bool const Alpha = false ); void Render( TMemCell *Memcell ); -#ifdef EU07_USE_OLD_GROUNDCODE - bool - Render_Alpha( TGround *Ground ); - bool - Render_Alpha( TSubRect *Groundsubcell ); - bool - Render_Alpha( TGroundNode *Node ); -#else void Render_Alpha( scene::basic_region *Region ); void @@ -350,7 +319,6 @@ private: Render_Alpha( TTraction *Traction ); void Render_Alpha( scene::lines_node const &Lines ); -#endif bool Render_Alpha( TDynamicObject *Dynamic ); bool @@ -422,21 +390,12 @@ private: bool m_renderspecular{ false }; // controls whether to include specular component in the calculations renderpass_config m_renderpass; -#ifdef EU07_USE_OLD_GROUNDCODE - std::vector m_cellqueue; // list of subcells to be drawn in current render pass -#else section_sequence m_sectionqueue; // list of sections in current render pass cell_sequence m_cellqueue; -#endif std::vector m_pickcontrolsitems; TSubModel const *m_pickcontrolitem { nullptr }; -#ifdef EU07_USE_OLD_GROUNDCODE - std::vector m_picksceneryitems; - TGroundNode const *m_picksceneryitem { nullptr }; -#else std::vector m_picksceneryitems; editor::basic_node const *m_picksceneryitem { nullptr }; -#endif #ifdef EU07_USE_DEBUG_CAMERA renderpass_config m_worldcamera; // debug item #endif diff --git a/scene.cpp b/scene.cpp index e9834f2b..1919dbe4 100644 --- a/scene.cpp +++ b/scene.cpp @@ -10,9 +10,9 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "scene.h" +#include "simulation.h" #include "globals.h" #include "timer.h" -#include "renderer.h" #include "logs.h" namespace scene { @@ -238,9 +238,7 @@ basic_cell::insert( TTrack *Path ) { Path->origin( m_area.center ); m_paths.emplace_back( Path ); // animation hook -#ifndef EU07_USE_OLD_GROUNDCODE Path->RaOwnerSet( this ); -#endif // re-calculate cell radius, in case track extends outside the cell's boundaries m_area.radius = std::max( m_area.radius, @@ -459,10 +457,8 @@ basic_cell::create_geometry( geometrybank_handle const &Bank ) { for( auto &shape : m_shapesopaque ) { shape.create_geometry( Bank ); } for( auto &shape : m_shapestranslucent ) { shape.create_geometry( Bank ); } -#ifndef EU07_USE_OLD_GROUNDCODE for( auto *path : m_paths ) { path->create_geometry( Bank ); } for( auto *traction : m_traction ) { traction->create_geometry( Bank ); } -#endif for( auto &lines : m_lines ) { lines.create_geometry( Bank ); } // arrange content by assigned materials to minimize state switching std::sort( diff --git a/stdafx.h b/stdafx.h index c08f2fed..9a043076 100644 --- a/stdafx.h +++ b/stdafx.h @@ -94,5 +94,3 @@ #include #include "openglmatrixstack.h" - -//#define EU07_USE_OLD_GROUNDCODE From ab31610afe5649664cb94f8af762fb3513379ced Mon Sep 17 00:00:00 2001 From: milek7 Date: Sat, 28 Oct 2017 19:21:37 +0200 Subject: [PATCH 25/31] clang-format changes --- .clang-format | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 9c9c0863..be220828 100644 --- a/.clang-format +++ b/.clang-format @@ -10,6 +10,8 @@ BreakBeforeTernaryOperators: false AllowShortBlocksOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty -ColumnLimit: 100 +ColumnLimit: 200 SortIncludes: false +UseTab: ForIndentation +TabWidth: 4 --- From 2fece17ca0ad84368ab08d9d66c7f6b606767cc8 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sun, 29 Oct 2017 14:56:41 +0100 Subject: [PATCH 26/31] build 171028: post-merge fixes, ai shunt mode driving tweaks --- Driver.cpp | 21 +++++++++++++++------ DynObj.cpp | 34 +++++++++++++++++----------------- McZapkie/Mover.cpp | 8 -------- Names.h | 2 +- scene.h | 2 +- version.h | 2 +- 6 files changed, 35 insertions(+), 34 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 2a481489..01478ac6 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -4323,11 +4323,11 @@ TController::UpdateSituation(double dt) { vehicle->fTrackBlock ); double k = coupler->Connected->Vel; // prędkość pojazdu z przodu (zakładając, // że jedzie w tę samą stronę!!!) - if( k < vel + 10 ) { + if( k - vel < 10 ) { // porównanie modułów prędkości [km/h] // zatroszczyć się trzeba, jeśli tamten nie jedzie znacząco szybciej double const distance = vehicle->fTrackBlock - fMaxProximityDist - ( fBrakeDist * 1.15 ); // odległość bezpieczna zależy od prędkości - if( distance < 0 ) { + if( distance < 0.0 ) { // jeśli odległość jest zbyt mała if( k < 10.0 ) // k - prędkość tego z przodu { // jeśli tamten porusza się z niewielką prędkością albo stoi @@ -4533,10 +4533,19 @@ TController::UpdateSituation(double dt) { */ if( VelNext == 0.0 ) { if( mvOccupied->CategoryFlag & 1 ) { - // hamowanie tak, aby stanąć - VelDesired = VelNext; - AccDesired = ( VelNext * VelNext - vel * vel ) / ( 25.92 * ( ActualProximityDist + 0.1 - 0.5*fMinProximityDist ) ); - AccDesired = std::min( AccDesired, fAccThreshold ); + // trains + if( ( OrderCurrentGet() & Shunt ) + && ( pVehicles[0]->fTrackBlock < 50.0 ) ) { + // crude detection of edge case, if approaching another vehicle coast slowly until min distance + // this should allow to bunch up trainsets more on sidings + VelDesired = Global::Min0RSpeed( VelDesired, 5.0 ); + } + else { + // hamowanie tak, aby stanąć + VelDesired = VelNext; + AccDesired = ( VelNext * VelNext - vel * vel ) / ( 25.92 * ( ActualProximityDist + 0.1 - 0.5*fMinProximityDist ) ); + AccDesired = std::min( AccDesired, fAccThreshold ); + } } else { // for cars (and others) coast at low speed until we hit min proximity range diff --git a/DynObj.cpp b/DynObj.cpp index 492ca704..678915bc 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -1461,7 +1461,7 @@ void TDynamicObject::ABuScanObjects( int Direction, double Distance ) if( distance < 100.0 ) { // at short distances start to calculate range between couplers directly // odległość do najbliższego pojazdu w linii prostej - fTrackBlock = std::min( fTrackBlock, MoverParameters->Couplers[ mycoupler ].CoupleDist ); + fTrackBlock = MoverParameters->Couplers[ mycoupler ].CoupleDist; } if( ( false == TestFlag( track->iCategoryFlag, 1 ) ) && ( distance > 50.0 ) ) { @@ -2605,14 +2605,14 @@ bool TDynamicObject::Update(double dt, double dt1) // if (Global::bLiveTraction) { // Ra 2013-12: to niżej jest chyba trochę bez sensu double v = MoverParameters->PantRearVolt; - if (v == 0.0) - { + if (v == 0.0) { v = MoverParameters->PantFrontVolt; - if (v == 0.0) - if ((MoverParameters->TrainType & (dt_EZT | dt_ET40 | dt_ET41 | dt_ET42)) && - MoverParameters->EngineType != - ElectricInductionMotor) // dwuczłony mogą mieć sprzęg WN + if( v == 0.0 ) { + if( MoverParameters->TrainType & ( dt_EZT | dt_ET40 | dt_ET41 | dt_ET42 ) ) { + // dwuczłony mogą mieć sprzęg WN v = MoverParameters->GetTrainsetVoltage(); // ostatnia szansa + } + } } if (v != 0.0) { // jeśli jest zasilanie @@ -2627,21 +2627,21 @@ bool TDynamicObject::Update(double dt, double dt1) 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->PantFrontUp + || MoverParameters->PantRearUp ) { + 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 + && ( 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( - "Bad traction: " + MoverParameters->Name + "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 // tymczasowa, a napięcie zerowane od razu diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 4e899f3a..16ec52c6 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -4698,14 +4698,6 @@ double TMoverParameters::TractionForce(double dt) dmoment = eimv[eimv_Fful]; // NOTE: the commands to operate the sandbox are likely to conflict with other similar ai decisions // TODO: gather these in single place so they can be resolved together - if( ( std::abs( ( PosRatio + 9.66 * dizel_fill ) * dmoment * 100 ) > 0.95 * Adhesive( RunningTrack.friction ) * TotalMassxg ) ) { - PosRatio = 0; - tmp = 4; - } // przeciwposlizg - if( ( std::abs( ( PosRatio + 9.80 * dizel_fill ) * dmoment * 100 ) > 0.95 * Adhesive( RunningTrack.friction ) * TotalMassxg ) ) { - PosRatio = 0; - tmp = 9; - } // przeciwposlizg if( ( SlippingWheels ) ) { PosRatio = 0; tmp = 9; diff --git a/Names.h b/Names.h index 209ed9c3..1af3e4ca 100644 --- a/Names.h +++ b/Names.h @@ -57,7 +57,7 @@ protected: public: // data access - typename type_sequence & + type_sequence & sequence() { return m_items; } diff --git a/scene.h b/scene.h index 8de91971..e09e8a3a 100644 --- a/scene.h +++ b/scene.h @@ -47,7 +47,7 @@ struct scratch_data { float offset { 0.f }; float velocity { 0.f }; std::vector vehicles; - std::vector couplings; + std::vector couplings; TDynamicObject * driver { nullptr }; bool is_open { false }; } trainset; diff --git a/version.h b/version.h index 6fe44f69..2fced4fe 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 1027 +#define VERSION_MINOR 1028 #define VERSION_REVISION 0 From ffcd3b40c98dbbe6a10d50e484463882491ce78f Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sun, 29 Oct 2017 20:56:40 +0100 Subject: [PATCH 27/31] audio synchronization fix --- DynObj.cpp | 2 +- World.cpp | 3 +- scene.cpp | 131 ++++++++++++++++++++++++++++++++--------------------- scene.h | 31 ++++++++----- 4 files changed, 103 insertions(+), 64 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 678915bc..4156d8be 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -3511,7 +3511,7 @@ void TDynamicObject::RenderSounds() // McZapkie-010302: ulepszony dzwiek silnika double freq; double vol = 0; - double dt = Timer::GetDeltaTime(); + double dt = Timer::GetDeltaRenderTime(); // double sounddist; // sounddist=SquareMagnitude(Global::pCameraPosition-vPosition); diff --git a/World.cpp b/World.cpp index d1c38aae..7949b346 100644 --- a/World.cpp +++ b/World.cpp @@ -1029,7 +1029,7 @@ bool TWorld::Update() { } simulation::Events.update(); - simulation::Region->update(); + simulation::Region->update_events(); simulation::Lights.update(); // render time routines follow: @@ -1062,6 +1062,7 @@ bool TWorld::Update() { Timer::subsystem.sim_total.stop(); + simulation::Region->update_sounds(); GfxRenderer.Update( dt ); ResourceSweep(); diff --git a/scene.cpp b/scene.cpp index 1919dbe4..df72f81d 100644 --- a/scene.cpp +++ b/scene.cpp @@ -17,42 +17,6 @@ http://mozilla.org/MPL/2.0/. namespace scene { -// legacy method, updates sounds and polls event launchers within radius around specified point -void -basic_cell::update() { - - // sounds - auto const deltatime = Timer::GetDeltaTime(); - for( auto *sound : m_sounds ) { - - if( ( sound->GetStatus() & DSBSTATUS_PLAYING ) == DSBPLAY_LOOPING ) { - sound->Play( 1, DSBPLAY_LOOPING, true, sound->vSoundPosition ); - sound->AdjFreq( 1.0, deltatime ); - } - } - // event launchers - for( auto *launcher : m_eventlaunchers ) { - if( ( true == launcher->check_conditions() ) - && ( SquareMagnitude( launcher->location() - Global::pCameraPosition ) < launcher->dRadius ) ) { - - WriteLog( "Eventlauncher " + launcher->name() ); - if( ( true == Global::shiftState ) - && ( launcher->Event2 != nullptr ) ) { - simulation::Events.AddToQuery( launcher->Event2, nullptr ); - } - else if( launcher->Event1 ) { - simulation::Events.AddToQuery( launcher->Event1, nullptr ); - } - } - } - - // TBD, TODO: move to sound renderer - for( auto *path : m_paths ) { - // dźwięki pojazdów, również niewidocznych - path->RenderDynSounds(); - } -} - // legacy method, finds and assigns traction piece to specified pantograph of provided vehicle void basic_cell::update_traction( TDynamicObject *Vehicle, int const Pantographindex ) { @@ -130,6 +94,47 @@ basic_cell::update_traction( TDynamicObject *Vehicle, int const Pantographindex } } +// legacy method, updates sounds and polls event launchers within radius around specified point +void +basic_cell::update_events() { + + // event launchers + for( auto *launcher : m_eventlaunchers ) { + if( ( true == launcher->check_conditions() ) + && ( SquareMagnitude( launcher->location() - Global::pCameraPosition ) < launcher->dRadius ) ) { + + WriteLog( "Eventlauncher " + launcher->name() ); + if( ( true == Global::shiftState ) + && ( launcher->Event2 != nullptr ) ) { + simulation::Events.AddToQuery( launcher->Event2, nullptr ); + } + else if( launcher->Event1 ) { + simulation::Events.AddToQuery( launcher->Event1, nullptr ); + } + } + } +} + +// legacy method, updates sounds and polls event launchers within radius around specified point +void +basic_cell::update_sounds() { + + // sounds + auto const deltatime = Timer::GetDeltaRenderTime(); + for( auto *sound : m_sounds ) { + + if( ( sound->GetStatus() & DSBSTATUS_PLAYING ) == DSBPLAY_LOOPING ) { + sound->Play( 1, DSBPLAY_LOOPING, true, sound->vSoundPosition ); + sound->AdjFreq( 1.0, deltatime ); + } + } + // TBD, TODO: move to sound renderer + for( auto *path : m_paths ) { + // dźwięki pojazdów, również niewidocznych + path->RenderDynSounds(); + } +} + // legacy method, triggers radio-stop procedure for all vehicles located on paths in the cell void basic_cell::radio_stop() { @@ -479,19 +484,6 @@ basic_cell::enclose_area( editor::basic_node *Node ) { -// legacy method, updates sounds and polls event launchers within radius around specified point -void -basic_section::update( glm::dvec3 const &Location, float const Radius ) { - - for( auto &cell : m_cells ) { - - if( glm::length2( cell.area().center - Location ) < ( ( cell.area().radius + Radius ) * ( cell.area().radius + Radius ) ) ) { - // we reject cells which aren't within our area of interest - cell.update(); - } - } -} - // legacy method, finds and assigns traction piece(s) to pantographs of provided vehicle void basic_section::update_traction( TDynamicObject *Vehicle, int const Pantographindex ) { @@ -514,6 +506,32 @@ basic_section::update_traction( TDynamicObject *Vehicle, int const Pantographind } } +// legacy method, polls event launchers within radius around specified point +void +basic_section::update_events( glm::dvec3 const &Location, float const Radius ) { + + for( auto &cell : m_cells ) { + + if( glm::length2( cell.area().center - Location ) < ( ( cell.area().radius + Radius ) * ( cell.area().radius + Radius ) ) ) { + // we reject cells which aren't within our area of interest + cell.update_events(); + } + } +} + +// legacy method, updates sounds within radius around specified point +void +basic_section::update_sounds( glm::dvec3 const &Location, float const Radius ) { + + for( auto &cell : m_cells ) { + + if( glm::length2( cell.area().center - Location ) < ( ( cell.area().radius + Radius ) * ( cell.area().radius + Radius ) ) ) { + // we reject cells which aren't within our area of interest + cell.update_sounds(); + } + } +} + // legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location void basic_section::radio_stop( glm::dvec3 const &Location, float const Radius ) { @@ -706,14 +724,25 @@ basic_region::~basic_region() { for( auto *section : m_sections ) { if( section != nullptr ) { delete section; } } } +// legacy method, polls event launchers around camera +void +basic_region::update_events() { + // render events and sounds from sectors near enough to the viewer + auto const range = EU07_SECTIONSIZE; // arbitrary range + auto const §ionlist = sections( Global::pCameraPosition, range ); + for( auto *section : sectionlist ) { + section->update_events( Global::pCameraPosition, range ); + } +} + // legacy method, updates sounds and polls event launchers around camera void -basic_region::update() { +basic_region::update_sounds() { // render events and sounds from sectors near enough to the viewer auto const range = 2750.f; // audible range of 100 db sound auto const §ionlist = sections( Global::pCameraPosition, range ); for( auto *section : sectionlist ) { - section->update( Global::pCameraPosition, range ); + section->update_sounds( Global::pCameraPosition, range ); } } diff --git a/scene.h b/scene.h index e09e8a3a..05cd4838 100644 --- a/scene.h +++ b/scene.h @@ -63,12 +63,15 @@ class basic_cell { 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, polls event launchers within radius around specified point + void + update_events(); + // legacy method, updates sounds within radius around specified point + void + update_sounds(); // legacy method, triggers radio-stop procedure for all vehicles located on paths in the cell void radio_stop(); @@ -171,12 +174,15 @@ class basic_section { 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 +// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle void update_traction( TDynamicObject *Vehicle, int const Pantographindex ); + // legacy method, updates sounds and polls event launchers within radius around specified point + void + update_events( glm::dvec3 const &Location, float const Radius ); + // legacy method, updates sounds and polls event launchers within radius around specified point + void + update_sounds( glm::dvec3 const &Location, float const Radius ); // 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 ); @@ -255,12 +261,15 @@ public: // 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 +// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle void update_traction( TDynamicObject *Vehicle, int const Pantographindex ); + // legacy method, polls event launchers around camera + void + update_events(); + // legacy method, updates sounds around camera + void + update_sounds(); // stores content of the class in file with specified name void serialize( std::string const &Scenariofile ); From 9edf9124058b552d8ac179d807d5124dd9e17fa0 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Mon, 30 Oct 2017 02:34:54 +0100 Subject: [PATCH 28/31] maintenance: minor code cleanup --- AnimModel.cpp | 8 --- AnimModel.h | 1 - MemCell.cpp | 10 --- MemCell.h | 2 - Track.cpp | 16 ++--- Track.h | 2 - Traction.cpp | 5 -- Traction.h | 2 - TractionPower.cpp | 4 -- TractionPower.h | 2 - scenenode.h | 164 +++++++++++++++++++++++++--------------------- 11 files changed, 95 insertions(+), 121 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index b26d91a0..77429ef0 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -413,14 +413,6 @@ TAnimModel::TAnimModel( scene::node_data const &Nodedata ) : basic_node( Nodedat } } -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 - } -} - TAnimModel::~TAnimModel() { delete pAdvanced; // nie ma zaawansowanej animacji diff --git a/AnimModel.h b/AnimModel.h index acc9545f..adcd8d9d 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -126,7 +126,6 @@ class TAnimModel : public editor::basic_node { public: // constructors TAnimModel( scene::node_data const &Nodedata ); - TAnimModel(); // destructor ~TAnimModel(); // methods diff --git a/MemCell.cpp b/MemCell.cpp index 8850c4fa..51bc2194 100644 --- a/MemCell.cpp +++ b/MemCell.cpp @@ -21,16 +21,6 @@ http://mozilla.org/MPL/2.0/. //--------------------------------------------------------------------------- -// legacy constructor -TMemCell::TMemCell(vector3 *p) -{ - fValue1 = fValue2 = 0; - location( - p ? *p : glm::dvec3() ); // ustawienie współrzędnych, bo do TGroundNode nie ma dostępu - bCommand = false; // komenda wysłana - OnSent = NULL; -} - 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 ) diff --git a/MemCell.h b/MemCell.h index 27e4cf17..97e1eb01 100644 --- a/MemCell.h +++ b/MemCell.h @@ -20,8 +20,6 @@ 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 UpdateValues( std::string const &szNewText, double const fNewValue1, double const fNewValue2, int const CheckMask ); diff --git a/Track.cpp b/Track.cpp index fcc415ca..0ea4b3c8 100644 --- a/Track.cpp +++ b/Track.cpp @@ -130,12 +130,6 @@ void TIsolated::Modify(int i, TDynamicObject *o) // tworzenie nowego odcinka ruchu TTrack::TTrack( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {} -// legacy constructor -TTrack::TTrack( std::string Name ) { - - m_name = Name; -} - TTrack::~TTrack() { // likwidacja odcinka if( eType == tt_Cross ) { @@ -175,7 +169,9 @@ TTrack::sort_by_material( TTrack const *Left, TTrack const *Right ) { TTrack * TTrack::Create400m(int what, double dx) { // tworzenie toru do wstawiania taboru podczas konwersji na E3D - auto *trk = new TTrack( "auto_400m" ); + scene::node_data nodedata; + nodedata.name = "auto_400m"; // track isn't visible so only name is needed + auto *trk = new TTrack( nodedata ); trk->m_visible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur trk->iCategoryFlag = what; // taki sam typ plus informacja, że dodatkowy trk->Init(); // utworzenie segmentu @@ -191,7 +187,9 @@ TTrack * TTrack::NullCreate(int dir) TTrack *trk { nullptr }, *trk2 { nullptr }; - trk = new TTrack( "auto_null" ); + scene::node_data nodedata; + nodedata.name = "auto_null"; // track isn't visible so only name is needed + trk = new TTrack( nodedata ); trk->m_visible = false; // nie potrzeba pokazywać, zresztą i tak nie ma tekstur trk->iCategoryFlag = (iCategoryFlag & 15) | 0x80; // taki sam typ plus informacja, że dodatkowy float r1, r2; @@ -234,7 +232,7 @@ TTrack * TTrack::NullCreate(int dir) trk->fVelocity = 20.0; // zawracanie powoli trk->fRadius = 20.0; // promień, aby się dodawało do tabelki prędkości i liczyło narastająco trk->Init(); // utworzenie segmentu - trk2 = new TTrack( "auto_null" ); + trk2 = new TTrack( nodedata ); trk2->iCategoryFlag = (iCategoryFlag & 15) | 0x80; // taki sam typ plus informacja, że dodatkowy trk2->m_visible = false; diff --git a/Track.h b/Track.h index 23ac5c10..250ebd96 100644 --- a/Track.h +++ b/Track.h @@ -185,8 +185,6 @@ public: 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(); void Init(); diff --git a/Traction.cpp b/Traction.cpp index 4cfa5053..348e9e22 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -91,11 +91,6 @@ sekcji z sąsiedniego przęsła). */ 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 diff --git a/Traction.h b/Traction.h index 9ccbf078..20ac20d2 100644 --- a/Traction.h +++ b/Traction.h @@ -53,8 +53,6 @@ class TTraction : public editor::basic_node { 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 diff --git a/TractionPower.cpp b/TractionPower.cpp index 8513ce47..c83eb49a 100644 --- a/TractionPower.cpp +++ b/TractionPower.cpp @@ -22,10 +22,6 @@ http://mozilla.org/MPL/2.0/. TTractionPowerSource::TTractionPowerSource( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {} // legacy constructor -TTractionPowerSource::TTractionPowerSource( std::string Name ) { - - m_name = Name; -} void TTractionPowerSource::Init(double const u, double const i) { // ustawianie zasilacza przy braku w scenerii diff --git a/TractionPower.h b/TractionPower.h index ae9bb18e..f170e738 100644 --- a/TractionPower.h +++ b/TractionPower.h @@ -40,8 +40,6 @@ public: bool bSection = false; // czy jest sekcją TTractionPowerSource( scene::node_data const &Nodedata ); - // legacy constructor - TTractionPowerSource( std::string Name ); void Init(double const u, double const i); bool Load(cParser *parser); diff --git a/scenenode.h b/scenenode.h index d30b420b..2978c1a9 100644 --- a/scenenode.h +++ b/scenenode.h @@ -29,12 +29,14 @@ bool operator==( lighting_data const &Left, lighting_data const &Right ) { return ( ( Left.diffuse == Right.diffuse ) && ( Left.ambient == Right.ambient ) - && ( Left.specular == Right.specular ) ); } + && ( Left.specular == Right.specular ) ); +} inline bool operator!=( lighting_data const &Left, lighting_data const &Right ) { - return !( Left == Right ); } + return !( Left == Right ); +} namespace scene { @@ -49,26 +51,6 @@ struct bounding_area { radius( Radius ) {} }; -/* -enum nodetype { - - unknown, - model, - triangles, - lines, - dynamic, - track, - traction, - powersource, - sound, - memorycell, - eventlauncher -}; - -class node_manager { - -}; -*/ struct node_data { @@ -119,16 +101,13 @@ public: compute_radius(); // set visibility void - visible( bool State ) { - m_data.visible = State; } + visible( bool State ); // set origin point void - origin( glm::dvec3 Origin ) { - m_data.origin = Origin; } + origin( glm::dvec3 Origin ); // data access shapenode_data const & - data() const { - return m_data; } + data() const; private: // members @@ -136,6 +115,27 @@ private: shapenode_data m_data; }; +// set visibility +inline +void +shape_node::visible( bool State ) { + m_data.visible = State; +} +// set origin point +inline +void +shape_node::origin( glm::dvec3 Origin ) { + m_data.origin = Origin; +} +// data access +inline +shape_node::shapenode_data const & +shape_node::data() const { + return m_data; +} + + + // holds a group of untextured lines class lines_node { @@ -173,16 +173,13 @@ public: compute_radius(); // set visibility void - visible( bool State ) { - m_data.visible = State; } + visible( bool State ); // set origin point void - origin( glm::dvec3 Origin ) { - m_data.origin = Origin; } + origin( glm::dvec3 Origin ); // data access linesnode_data const & - data() const { - return m_data; } + data() const; private: // members @@ -190,6 +187,26 @@ private: linesnode_data m_data; }; +// set visibility +inline +void +lines_node::visible( bool State ) { + m_data.visible = State; +} +// set origin point +inline +void +lines_node::origin( glm::dvec3 Origin ) { + m_data.origin = Origin; +} +// data access +inline +lines_node::linesnode_data const & +lines_node::data() const { + return m_data; +} + + /* // holds geometry for specific piece of track/road/waterway class path_node { @@ -238,39 +255,10 @@ private: 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 @@ -278,28 +266,22 @@ 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; } + name() const; void - location( glm::dvec3 const Location ) { - m_area.center = Location; } + location( glm::dvec3 const Location ); glm::dvec3 const & - location() const { - return m_area.center; }; + location() const; float const & radius(); void - visible( bool const Visible ) { - m_visible = Visible; } + visible( bool const Visible ); bool - visible() const { - return m_visible; } + visible() const; protected: // methods @@ -313,6 +295,36 @@ protected: std::string m_name; }; +inline +std::string const & +basic_node::name() const { + return m_name; +} + +inline +void +basic_node::location( glm::dvec3 const Location ) { + m_area.center = Location; +} + +inline +glm::dvec3 const & +basic_node::location() const { + return m_area.center; +} + +inline +void +basic_node::visible( bool const Visible ) { + m_visible = Visible; +} + +inline +bool +basic_node::visible() const { + return m_visible; +} + } // editor //--------------------------------------------------------------------------- From 071a15652784d36e59c0d15b6ce209d9ca2c1f7e Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Mon, 30 Oct 2017 23:41:22 +0100 Subject: [PATCH 29/31] build 171030: binary serialization for region terrain --- Gauge.cpp | 6 +- material.cpp | 8 +-- scene.cpp | 164 ++++++++++++++++++++++++++++++++++++++++-- scene.h | 14 +++- scenenode.cpp | 192 ++++++++++++++++++++++++++++++++++++++++++++++++- scenenode.h | 59 ++++++++++++--- simulation.cpp | 43 ++++++----- sn_utils.cpp | 48 ++++++++++++- sn_utils.h | 6 ++ version.h | 2 +- vertex.cpp | 31 ++++++++ vertex.h | 2 + 12 files changed, 531 insertions(+), 44 deletions(-) diff --git a/Gauge.cpp b/Gauge.cpp index 4def85c2..01232954 100644 --- a/Gauge.cpp +++ b/Gauge.cpp @@ -137,10 +137,10 @@ TGauge::Load_mapping( cParser &Input ) { TSoundsManager::GetFromName( value, true ) : nullptr ); } - else if( key.find( "sound" ) == 0 ) { + else if( key.compare( 0, std::min( key.size(), 5 ), "sound" ) == 0 ) { // sounds assigned to specific gauge values, defined by key soundFoo: where Foo = value - auto const indexstart = key.find_first_of( "-1234567890" ); - auto const indexend = key.find_first_not_of( "-1234567890", indexstart ); + auto const indexstart { key.find_first_of( "-1234567890" ) }; + auto const indexend { key.find_first_not_of( "-1234567890", indexstart ) }; if( indexstart != std::string::npos ) { m_soundfxvalues.emplace( std::stoi( key.substr( indexstart, indexend - indexstart ) ), diff --git a/material.cpp b/material.cpp index b3870201..96100286 100644 --- a/material.cpp +++ b/material.cpp @@ -94,10 +94,10 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) { } filename += ".mat"; - for( char &c : filename ) { - // change forward slashes to windows ones. NOTE: probably not strictly necessary, but eh - c = ( c == '/' ? '\\' : c ); - } + // change forward slashes to windows ones. NOTE: probably not strictly necessary, but eh + std::replace( + std::begin( filename ), std::end( filename ), + '/', '\\' ); if( filename.find( '\\' ) == std::string::npos ) { // jeśli bieżaca ścieżka do tekstur nie została dodana to dodajemy domyślną filename = szTexturePath + filename; diff --git a/scene.cpp b/scene.cpp index df72f81d..00a9cddf 100644 --- a/scene.cpp +++ b/scene.cpp @@ -14,9 +14,12 @@ http://mozilla.org/MPL/2.0/. #include "globals.h" #include "timer.h" #include "logs.h" +#include "sn_utils.h" namespace scene { +std::string const EU07_FILEEXTENSION_REGION { ".sbt" }; + // legacy method, finds and assigns traction piece to specified pantograph of provided vehicle void basic_cell::update_traction( TDynamicObject *Vehicle, int const Pantographindex ) { @@ -173,6 +176,56 @@ basic_cell::RaAnimate( unsigned int const Framestamp ) { m_framestamp = Framestamp; } +// sends content of the class to provided stream +void +basic_cell::serialize( std::ostream &Output ) const { + + // region file version 0, cell data + // bounding area + m_area.serialize( Output ); + // NOTE: cell activation flag is set dynamically on load + // cell shapes + // shape count followed by opaque shape data + sn_utils::ls_uint32( Output, m_shapesopaque.size() ); + for( auto const &shape : m_shapesopaque ) { + shape.serialize( Output ); + } + // shape count followed by translucent shape data + sn_utils::ls_uint32( Output, m_shapestranslucent.size() ); + for( auto const &shape : m_shapestranslucent ) { + shape.serialize( Output ); + } + // cell lines + // line count followed by lines data + sn_utils::ls_uint32( Output, m_lines.size() ); + for( auto const &lines : m_lines ) { + lines.serialize( Output ); + } +} + +// restores content of the class from provided stream +void +basic_cell::deserialize( std::istream &Input ) { + + // region file version 0, cell data + // bounding area + m_area.deserialize( Input ); + // cell shapes + // shape count followed by opaque shape data + auto itemcount { sn_utils::ld_uint32( Input ) }; + while( itemcount-- ) { + m_shapesopaque.emplace_back( shape_node().deserialize( Input ) ); + } + itemcount = sn_utils::ld_uint32( Input ); + while( itemcount-- ) { + m_shapestranslucent.emplace_back( shape_node().deserialize( Input ) ); + } + itemcount = sn_utils::ld_uint32( Input ); + while( itemcount-- ) { + m_lines.emplace_back( lines_node().deserialize( Input ) ); + } +} + // adds provided shape to the cell void basic_cell::insert( shape_node Shape ) { @@ -545,6 +598,51 @@ basic_section::radio_stop( glm::dvec3 const &Location, float const Radius ) { } } +// sends content of the class to provided stream +void +basic_section::serialize( std::ostream &Output ) const { + + auto const sectionstartpos { Output.tellp() }; + + // region file version 0, section data + // section size + sn_utils::ls_uint32( Output, 0 ); + // bounding area + m_area.serialize( Output ); + // section shapes: shape count followed by shape data + sn_utils::ls_uint32( Output, m_shapes.size() ); + for( auto const &shape : m_shapes ) { + shape.serialize( Output ); + } + // partitioned data + for( auto const &cell : m_cells ) { + cell.serialize( Output ); + } + // all done; calculate and record section size + auto const sectionendpos { Output.tellp() }; + Output.seekp( sectionstartpos ); + sn_utils::ls_uint32( Output, static_cast( ( sizeof( uint32_t ) + ( sectionendpos - sectionstartpos ) ) ) ); + Output.seekp( sectionendpos ); +} + +// restores content of the class from provided stream +void +basic_section::deserialize( std::istream &Input ) { + + // region file version 0, section data + // bounding area + m_area.deserialize( Input ); + // section shapes: shape count followed by shape data + auto shapecount { sn_utils::ld_uint32( Input ) }; + while( shapecount-- ) { + m_shapes.emplace_back( shape_node().deserialize( Input ) ); + } + // partitioned data + for( auto &cell : m_cells ) { + cell.deserialize( Input ); + } +} + // adds provided shape to the section void basic_section::insert( shape_node Shape ) { @@ -767,15 +865,73 @@ basic_region::update_traction( TDynamicObject *Vehicle, int const Pantographinde // stores content of the class in file with specified name void -basic_region::serialize( std::string const &Scenariofile ) { - // TODO: implement +basic_region::serialize( std::string const &Scenariofile ) const { + + auto filename { Global::asCurrentSceneryPath + Scenariofile }; + if( ( filename.rfind( '.' ) != std::string::npos ) + && ( filename.rfind( '.' ) != filename.rfind( ".." ) + 1 ) ) { + // trim extension, it's typically going to be for different file type + filename.erase( filename.rfind( '.' ) ); + } + filename += EU07_FILEEXTENSION_REGION; + + std::ofstream output { filename, std::ios::binary }; + + // region file version 0 + // header: EU07SBT + version (0-255) + sn_utils::ls_uint32( output, MAKE_ID4( 'E', 'U', '0', '7' ) ); + sn_utils::ls_uint32( output, MAKE_ID4( 'S', 'B', 'T', 0 ) ); + // sections + // TBD, TODO: build table of sections and file offsets, if we postpone section loading until they're within range + for( auto section : m_sections ) { + // length of section data, followed by section data (if any) + if( section != nullptr ) { + section->serialize( output ); } + else { + sn_utils::ls_uint32( output, 0 ); } + } } // restores content of the class from file with specified name. returns: true on success, false otherwise bool basic_region::deserialize( std::string const &Scenariofile ) { - // TODO: implement - return false; + + auto filename { Global::asCurrentSceneryPath + Scenariofile }; + if( ( filename.rfind( '.' ) != std::string::npos ) + && ( filename.rfind( '.' ) != filename.rfind( ".." ) + 1 ) ) { + // trim extension, it's typically going to be for different file type + filename.erase( filename.rfind( '.' ) ); + } + filename += EU07_FILEEXTENSION_REGION; + + if( false == FileExists( filename ) ) { + return false; + } + // region file version 0 + // file type and version check + std::ifstream input( filename, std::ios::binary ); + + uint32_t headermain { sn_utils::ld_uint32( input ) }; + uint32_t headertype { sn_utils::ld_uint32( input ) }; + + if( ( headermain != MAKE_ID4( 'E', 'U', '0', '7' ) + || ( headertype != MAKE_ID4( 'S', 'B', 'T', 0 ) ) ) ) { + // wrong file type + ErrorLog( "Bad file: \"" + filename + "\" is of either unrecognized type or version" ); + return false; + } + // sections + // TBD, TODO: build table of sections and file offsets, if we postpone section loading until they're within range + for( auto §ion : m_sections ) { + // length of section data, followed by section data (if any) + auto const sectionsize { sn_utils::ld_uint32( input ) }; + if( sectionsize != 0 ) { + section = new basic_section(); + section->deserialize( input ); + } + } + + return true; } // legacy method, links specified path piece with potential neighbours diff --git a/scene.h b/scene.h index 05cd4838..06bcd6d6 100644 --- a/scene.h +++ b/scene.h @@ -81,6 +81,12 @@ public: // legacy method, updates geometry for pieces in the animation list void RaAnimate( unsigned int const Framestamp ); + // sends content of the class to provided stream + void + serialize( std::ostream &Output ) const; + // restores content of the class from provided stream + void + deserialize( std::istream &Input ); // adds provided shape to the cell void insert( shape_node Shape ); @@ -186,6 +192,12 @@ public: // 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 ); + // sends content of the class to provided stream + void + serialize( std::ostream &Output ) const; + // restores content of the class from provided stream + void + deserialize( std::istream &Input ); // adds provided shape to the section void insert( shape_node Shape ); @@ -272,7 +284,7 @@ public: update_sounds(); // stores content of the class in file with specified name void - serialize( std::string const &Scenariofile ); + serialize( std::string const &Scenariofile ) const; // restores content of the class from file with specified name. returns: true on success, false otherwise bool deserialize( std::string const &Scenariofile ); diff --git a/scenenode.cpp b/scenenode.cpp index fc20188f..5feef77e 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -12,10 +12,126 @@ http://mozilla.org/MPL/2.0/. #include "renderer.h" #include "logs.h" +#include "sn_utils.h" + + +// stores content of the struct in provided output stream +void +lighting_data::serialize( std::ostream &Output ) const { + + sn_utils::s_vec4( Output, diffuse ); + sn_utils::s_vec4( Output, ambient ); + sn_utils::s_vec4( Output, specular ); +} + +// restores content of the struct from provided input stream +void +lighting_data::deserialize( std::istream &Input ) { + + diffuse = sn_utils::d_vec4( Input ); + ambient = sn_utils::d_vec4( Input ); + specular = sn_utils::d_vec4( Input ); +} + + namespace scene { -// restores content of the node from provded input stream +// stores content of the struct in provided output stream +void +bounding_area::serialize( std::ostream &Output ) const { + // center + sn_utils::s_dvec3( Output, center ); + // radius + sn_utils::ls_float32( Output, radius ); +} + +// restores content of the struct from provided input stream +void +bounding_area::deserialize( std::istream &Input ) { + + center = sn_utils::d_dvec3( Input ); + radius = sn_utils::ld_float32( Input ); +} + + + +// sends content of the struct to provided stream +void +shape_node::shapenode_data::serialize( std::ostream &Output ) const { + // bounding area + area.serialize( Output ); + // visibility + sn_utils::ls_float64( Output, rangesquared_min ); + sn_utils::ls_float64( Output, rangesquared_max ); + sn_utils::s_bool( Output, visible ); + // material + sn_utils::s_bool( Output, translucent ); + // NOTE: material handle is created dynamically on load + sn_utils::s_str( + Output, + ( material != null_handle ? + GfxRenderer.Material( material ).name : + "" ) ); + lighting.serialize( Output ); + // geometry + sn_utils::s_dvec3( Output, origin ); + // NOTE: geometry handle is created dynamically on load + // vertex count, followed by vertex data + sn_utils::ls_uint32( Output, vertices.size() ); + for( auto const &vertex : vertices ) { + vertex.serialize( Output ); + } +} + +// restores content of the struct from provided input stream +void +shape_node::shapenode_data::deserialize( std::istream &Input ) { + // bounding area + area.deserialize( Input ); + // visibility + rangesquared_min = sn_utils::ld_float64( Input ); + rangesquared_max = sn_utils::ld_float64( Input ); + visible = sn_utils::d_bool( Input ); + // material + translucent = sn_utils::d_bool( Input ); + auto const materialname { sn_utils::d_str( Input ) }; + if( false == materialname.empty() ) { + material = GfxRenderer.Fetch_Material( materialname ); + } + lighting.deserialize( Input ); + // geometry + origin = sn_utils::d_dvec3( Input ); + // NOTE: geometry handle is acquired during geometry creation + // vertex data + vertices.resize( sn_utils::ld_uint32( Input ) ); + for( auto &vertex : vertices ) { + vertex.deserialize( Input ); + } +} + + +// sends content of the class to provided stream +void +shape_node::serialize( std::ostream &Output ) const { + // name + sn_utils::s_str( Output, m_name ); + // node data + m_data.serialize( Output ); +} + +// restores content of the node from provided input stream +shape_node & +shape_node::deserialize( std::istream &Input ) { + // name + m_name = sn_utils::d_str( Input ); + // node data + m_data.deserialize( Input ); + + return *this; +} + +// restores content of the node from provided input stream shape_node & shape_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) { @@ -104,7 +220,9 @@ shape_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) { triangles, triangle_strip, triangle_fan - } const nodetype = ( + }; + + subtype const nodetype = ( Nodedata.type == "triangles" ? triangles : Nodedata.type == "triangle_strip" ? triangle_strip : triangle_fan ); @@ -320,6 +438,72 @@ shape_node::compute_radius() { +// sends content of the struct to provided stream +void +lines_node::linesnode_data::serialize( std::ostream &Output ) const { + // bounding area + area.serialize( Output ); + // visibility + sn_utils::ls_float64( Output, rangesquared_min ); + sn_utils::ls_float64( Output, rangesquared_max ); + sn_utils::s_bool( Output, visible ); + // material + sn_utils::ls_float32( Output, line_width ); + lighting.serialize( Output ); + // geometry + sn_utils::s_dvec3( Output, origin ); + // NOTE: geometry handle is created dynamically on load + // vertex count, followed by vertex data + sn_utils::ls_uint32( Output, vertices.size() ); + for( auto const &vertex : vertices ) { + vertex.serialize( Output ); + } +} + +// restores content of the struct from provided input stream +void +lines_node::linesnode_data::deserialize( std::istream &Input ) { + // bounding area + area.deserialize( Input ); + // visibility + rangesquared_min = sn_utils::ld_float64( Input ); + rangesquared_max = sn_utils::ld_float64( Input ); + visible = sn_utils::d_bool( Input ); + // material + line_width = sn_utils::ld_float32( Input ); + lighting.deserialize( Input ); + // geometry + origin = sn_utils::d_dvec3( Input ); + // NOTE: geometry handle is acquired during geometry creation + // vertex data + vertices.resize( sn_utils::ld_uint32( Input ) ); + for( auto &vertex : vertices ) { + vertex.deserialize( Input ); + } +} + + + +// sends content of the class to provided stream +void +lines_node::serialize( std::ostream &Output ) const { + // name + sn_utils::s_str( Output, m_name ); + // node data + m_data.serialize( Output ); +} + +// restores content of the node from provided input stream +lines_node & +lines_node::deserialize( std::istream &Input ) { + // name + m_name = sn_utils::d_str( Input ); + // node data + m_data.deserialize( Input ); + + return *this; +} + // restores content of the node from provded input stream lines_node & lines_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) { @@ -350,7 +534,9 @@ lines_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) { lines, line_strip, line_loop - } const nodetype = ( + }; + + subtype const nodetype = ( Nodedata.type == "lines" ? lines : Nodedata.type == "line_strip" ? line_strip : line_loop ); diff --git a/scenenode.h b/scenenode.h index 2978c1a9..449575b3 100644 --- a/scenenode.h +++ b/scenenode.h @@ -22,6 +22,13 @@ 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 }; + + // stores content of the struct in provided output stream + void + serialize( std::ostream &Output ) const; + // restores content of the struct from provided input stream + void + deserialize( std::istream &Input ); }; inline @@ -50,6 +57,12 @@ struct bounding_area { center( Center ), radius( Radius ) {} + // stores content of the struct in provided output stream + void + serialize( std::ostream &Output ) const; + // restores content of the struct from provided input stream + void + deserialize( std::istream &Input ); }; struct node_data { @@ -68,23 +81,37 @@ class shape_node { public: // types struct shapenode_data { + // members: // 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 + bool visible { true }; // visibility flag // material data - material_handle material { 0 }; - lighting_data lighting; bool translucent { false }; // whether opaque or translucent + material_handle material { null_handle }; + lighting_data lighting; // geometry data - std::vector 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 + std::vector vertices; // world space source data of the geometry + // methods: + // sends content of the struct to provided stream + void + serialize( std::ostream &Output ) const; + // restores content of the struct from provided input stream + void + deserialize( std::istream &Input ); }; // methods - // restores content of the node from provded input stream + // sends content of the class to provided stream + void + serialize( std::ostream &Output ) const; + // restores content of the node from provided input stream + shape_node & + deserialize( std::istream &Input ); + // restores content of the node from provided input stream shape_node & deserialize( cParser &Input, scene::node_data const &Nodedata ); // imports data from provided submodel @@ -144,22 +171,36 @@ class lines_node { public: // types struct linesnode_data { + // members: // 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 + bool visible { true }; // visibility flag // material data + float line_width { 1.f }; // thickness of stored lines lighting_data lighting; - float line_width; // thickness of stored lines // geometry data - std::vector 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 + std::vector vertices; // world space source data of the geometry + // methods: + // sends content of the struct to provided stream + void + serialize( std::ostream &Output ) const; + // restores content of the struct from provided input stream + void + deserialize( std::istream &Input ); }; // methods - // restores content of the node from provded input stream + // sends content of the class to provided stream + void + serialize( std::ostream &Output ) const; + // restores content of the node from provided input stream + lines_node & + deserialize( std::istream &Input ); + // restores content of the node from provided 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 diff --git a/simulation.cpp b/simulation.cpp index 1a99189c..1c469dd0 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -349,30 +349,37 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &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 ) { + // 3d terrain + if( false == Scratchpad.binary.terrain ) { + // if we're loading data from text .scn file convert and import + 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 ); - submodel = submodel->NextGet(); + // 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 { + // if binary terrain file was present, we already have this data + skip_until( Input, "endmodel" ); } - // with the import done we can get rid of the source model - delete instance; } else { // regular instance of 3d mesh diff --git a/sn_utils.cpp b/sn_utils.cpp index f454fc3c..9906d9c1 100644 --- a/sn_utils.cpp +++ b/sn_utils.cpp @@ -75,6 +75,28 @@ std::string sn_utils::d_str(std::istream &s) return r; } +bool sn_utils::d_bool(std::istream& s) +{ + return ( ld_uint16( s ) == 1 ); +} + +glm::dvec3 sn_utils::d_dvec3(std::istream& s) +{ + return { + ld_float64(s), + ld_float64(s), + ld_float64(s) }; +} + +glm::vec4 sn_utils::d_vec4( std::istream& s) +{ + return { + ld_float32(s), + ld_float32(s), + ld_float32(s), + ld_float32(s) }; +} + void sn_utils::ls_uint16(std::ostream &s, uint16_t v) { uint8_t buf[2]; @@ -133,4 +155,28 @@ void sn_utils::s_str(std::ostream &s, std::string v) { const char* buf = v.c_str(); s.write(buf, v.size() + 1); -} \ No newline at end of file +} + +void sn_utils::s_bool(std::ostream &s, bool v) +{ + ls_uint16( + s, + ( true == v ? + 1 : + 0 ) ); +} + +void sn_utils::s_dvec3(std::ostream &s, glm::dvec3 const &v) +{ + ls_float64(s, v.x); + ls_float64(s, v.y); + ls_float64(s, v.z); +} + +void sn_utils::s_vec4(std::ostream &s, glm::vec4 const &v) +{ + ls_float32(s, v.x); + ls_float32(s, v.y); + ls_float32(s, v.z); + ls_float32(s, v.w); +} diff --git a/sn_utils.h b/sn_utils.h index dd269121..e11d1b31 100644 --- a/sn_utils.h +++ b/sn_utils.h @@ -13,6 +13,9 @@ public: static float ld_float32(std::istream&); static double ld_float64(std::istream&); static std::string d_str(std::istream&); + static bool d_bool(std::istream&); + static glm::dvec3 d_dvec3(std::istream&); + static glm::vec4 d_vec4(std::istream&); static void ls_uint16(std::ostream&, uint16_t); static void ls_uint32(std::ostream&, uint32_t); @@ -20,4 +23,7 @@ public: static void ls_float32(std::ostream&, float); static void ls_float64(std::ostream&, double); static void s_str(std::ostream&, std::string); + static void s_bool(std::ostream&, bool); + static void s_dvec3(std::ostream&, glm::dvec3 const &); + static void s_vec4(std::ostream&, glm::vec4 const &); }; \ No newline at end of file diff --git a/version.h b/version.h index 2fced4fe..405dacc3 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 1028 +#define VERSION_MINOR 1030 #define VERSION_REVISION 0 diff --git a/vertex.cpp b/vertex.cpp index 79093460..e1d60fd2 100644 --- a/vertex.cpp +++ b/vertex.cpp @@ -11,6 +11,37 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "vertex.h" +#include "sn_utils.h" + +void +world_vertex::serialize( std::ostream &s ) const { + + sn_utils::ls_float64( s, position.x ); + sn_utils::ls_float64( s, position.y ); + sn_utils::ls_float64( s, position.z ); + + sn_utils::ls_float32( s, normal.x ); + sn_utils::ls_float32( s, normal.y ); + sn_utils::ls_float32( s, normal.z ); + + sn_utils::ls_float32( s, texture.x ); + sn_utils::ls_float32( s, texture.y ); +} + +void +world_vertex::deserialize( std::istream &s ) { + + position.x = sn_utils::ld_float64( s ); + position.y = sn_utils::ld_float64( s ); + position.z = sn_utils::ld_float64( s ); + + normal.x = sn_utils::ld_float32( s ); + normal.y = sn_utils::ld_float32( s ); + normal.z = sn_utils::ld_float32( s ); + + texture.x = sn_utils::ld_float32( s ); + texture.y = sn_utils::ld_float32( s ); +} template <> world_vertex & diff --git a/vertex.h b/vertex.h index ee983439..0dcfbbb9 100644 --- a/vertex.h +++ b/vertex.h @@ -50,6 +50,8 @@ struct world_vertex { Left *= Right; return Left; } // methods + void serialize( std::ostream& ) const; + void deserialize( std::istream& ); // wyliczenie współrzędnych i mapowania punktu na środku odcinka v1<->v2 void set_half( world_vertex const &Vertex1, world_vertex const &Vertex2 ) { From 6d9e718a958756884b8d916bc1e1adabd3d229a8 Mon Sep 17 00:00:00 2001 From: milek7 Date: Tue, 31 Oct 2017 20:25:17 +0100 Subject: [PATCH 30/31] fixes --- CMakeLists.txt | 4 ---- Event.cpp | 2 ++ simulation.cpp | 7 ++++++- sound.cpp | 2 -- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c8165b52..268511df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,7 +139,3 @@ target_link_libraries(${PROJECT_NAME} ${LUAJIT_LIBRARIES}) find_package(libserialport REQUIRED) include_directories(${libserialport_INCLUDE_DIR}) target_link_libraries(${PROJECT_NAME} ${libserialport_LIBRARY}) - -if (UNIX) - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") -endif() diff --git a/Event.cpp b/Event.cpp index b363bedc..81a3bd68 100644 --- a/Event.cpp +++ b/Event.cpp @@ -1018,6 +1018,8 @@ event_manager::CheckQuery() { return false; } case tp_Sound: { + if (m_workevent->Params[ 9 ].tsTextSound == nullptr) + break; switch( m_workevent->Params[ 0 ].asInt ) { // trzy możliwe przypadki: case 0: { diff --git a/simulation.cpp b/simulation.cpp index 97a355d7..84fc3394 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -846,7 +846,12 @@ state_manager::deserialize_sound( cParser &Input, scene::scratch_data &Scratchpa auto const soundname { Input.getToken() }; auto *sound = sound_man->create_text_sound(soundname); - sound->position((glm::vec3)location).dist(Nodedata.range_max); + sound->position((glm::vec3)location); + if (Nodedata.range_max != -1.0) + sound->dist(Nodedata.range_max); + else + sound->set_mode(sound::global); + skip_until( Input, "endsound" ); return sound; diff --git a/sound.cpp b/sound.cpp index 6d61badd..5baed1a3 100644 --- a/sound.cpp +++ b/sound.cpp @@ -350,8 +350,6 @@ sound& sound::position(glm::vec3 p) glm::vec3 sound::location() { - if (mode == global) - throw std::runtime_error("sound: bad state, no position"); return pos; } From ffbaacdb0e368a130e851fbae1635c8ea0cc2b32 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Tue, 31 Oct 2017 21:43:31 +0100 Subject: [PATCH 31/31] build 171031: reduced footprint for binary region terrain file --- McZapkie/mctools.cpp | 23 +++++++++++++++++++++- McZapkie/mctools.h | 3 +++ scene.cpp | 45 +++++++++++++++++++++++++++++--------------- scenenode.cpp | 24 +++++++++++++++++++---- simulation.cpp | 13 ++++++++++--- version.h | 2 +- 6 files changed, 86 insertions(+), 24 deletions(-) diff --git a/McZapkie/mctools.cpp b/McZapkie/mctools.cpp index 2b63e841..7cb32c88 100644 --- a/McZapkie/mctools.cpp +++ b/McZapkie/mctools.cpp @@ -13,6 +13,17 @@ Copyright (C) 2007-2014 Maciej Cierniak */ #include "stdafx.h" #include "mctools.h" + +#include +#include +#ifndef WIN32 +#include +#endif + +#ifdef WIN32 +#define stat _stat +#endif + #include "Globals.h" /*================================================*/ @@ -275,8 +286,18 @@ extract_value( bool &Variable, std::string const &Key, std::string const &Input, } } -bool FileExists( std::string const &Filename ) { +bool +FileExists( std::string const &Filename ) { std::ifstream file( Filename ); return( true == file.is_open() ); } + +// returns time of last modification for specified file +__time64_t +last_modified( std::string const &Filename ) { + + struct stat filestat; + if( ::stat( Filename.c_str(), &filestat ) == 0 ) { return filestat.st_mtime; } + else { return 0; } +} diff --git a/McZapkie/mctools.h b/McZapkie/mctools.h index 6692710b..518cd77d 100644 --- a/McZapkie/mctools.h +++ b/McZapkie/mctools.h @@ -158,3 +158,6 @@ bool extract_value( bool &Variable, std::string const &Key, std::string const &Input, std::string const &Default ); bool FileExists( std::string const &Filename ); + +// returns time of last modification for specified file +__time64_t last_modified( std::string const &Filename ); \ No newline at end of file diff --git a/scene.cpp b/scene.cpp index 00a9cddf..cdf7e511 100644 --- a/scene.cpp +++ b/scene.cpp @@ -224,6 +224,11 @@ basic_cell::deserialize( std::istream &Input ) { while( itemcount-- ) { m_lines.emplace_back( lines_node().deserialize( Input ) ); } + // cell activation flag + m_active = ( + ( false == m_shapesopaque.empty() ) + || ( false == m_shapestranslucent.empty() ) + || ( false == m_lines.empty() ) ); } // adds provided shape to the cell @@ -877,18 +882,27 @@ basic_region::serialize( std::string const &Scenariofile ) const { std::ofstream output { filename, std::ios::binary }; - // region file version 0 + // region file version 1 // header: EU07SBT + version (0-255) sn_utils::ls_uint32( output, MAKE_ID4( 'E', 'U', '0', '7' ) ); - sn_utils::ls_uint32( output, MAKE_ID4( 'S', 'B', 'T', 0 ) ); + sn_utils::ls_uint32( output, MAKE_ID4( 'S', 'B', 'T', 1 ) ); // sections // TBD, TODO: build table of sections and file offsets, if we postpone section loading until they're within range - for( auto section : m_sections ) { - // length of section data, followed by section data (if any) + std::uint32_t sectioncount { 0 }; + for( auto *section : m_sections ) { if( section != nullptr ) { + ++sectioncount; + } + } + // section count, followed by section data + sn_utils::ls_uint32( output, sectioncount ); + std::uint32_t sectionindex { 0 }; + for( auto *section : m_sections ) { + // section data: section index, followed by length of section data, followed by section data + if( section != nullptr ) { + sn_utils::ls_uint32( output, sectionindex ); section->serialize( output ); } - else { - sn_utils::ls_uint32( output, 0 ); } + ++sectionindex; } } @@ -907,7 +921,7 @@ basic_region::deserialize( std::string const &Scenariofile ) { if( false == FileExists( filename ) ) { return false; } - // region file version 0 + // region file version 1 // file type and version check std::ifstream input( filename, std::ios::binary ); @@ -915,20 +929,21 @@ basic_region::deserialize( std::string const &Scenariofile ) { uint32_t headertype { sn_utils::ld_uint32( input ) }; if( ( headermain != MAKE_ID4( 'E', 'U', '0', '7' ) - || ( headertype != MAKE_ID4( 'S', 'B', 'T', 0 ) ) ) ) { + || ( headertype != MAKE_ID4( 'S', 'B', 'T', 1 ) ) ) ) { // wrong file type - ErrorLog( "Bad file: \"" + filename + "\" is of either unrecognized type or version" ); + WriteLog( "Bad file: \"" + filename + "\" is of either unrecognized type or version" ); return false; } // sections // TBD, TODO: build table of sections and file offsets, if we postpone section loading until they're within range - for( auto §ion : m_sections ) { - // length of section data, followed by section data (if any) + // section count + auto sectioncount { sn_utils::ld_uint32( input ) }; + while( sectioncount-- ) { + // section index, followed by section data size, followed by section data + auto *§ion { m_sections[ sn_utils::ld_uint32( input ) ] }; auto const sectionsize { sn_utils::ld_uint32( input ) }; - if( sectionsize != 0 ) { - section = new basic_section(); - section->deserialize( input ); - } + section = new basic_section(); + section->deserialize( input ); } return true; diff --git a/scenenode.cpp b/scenenode.cpp index 5feef77e..f223986c 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -80,7 +80,11 @@ shape_node::shapenode_data::serialize( std::ostream &Output ) const { // vertex count, followed by vertex data sn_utils::ls_uint32( Output, vertices.size() ); for( auto const &vertex : vertices ) { - vertex.serialize( Output ); + basic_vertex( + glm::vec3{ vertex.position - origin }, + vertex.normal, + vertex.texture ) + .serialize( Output ); } } @@ -105,8 +109,12 @@ shape_node::shapenode_data::deserialize( std::istream &Input ) { // NOTE: geometry handle is acquired during geometry creation // vertex data vertices.resize( sn_utils::ld_uint32( Input ) ); + basic_vertex localvertex; for( auto &vertex : vertices ) { - vertex.deserialize( Input ); + localvertex.deserialize( Input ); + vertex.position = origin + glm::dvec3{ localvertex.position }; + vertex.normal = localvertex.normal; + vertex.texture = localvertex.texture; } } @@ -456,7 +464,11 @@ lines_node::linesnode_data::serialize( std::ostream &Output ) const { // vertex count, followed by vertex data sn_utils::ls_uint32( Output, vertices.size() ); for( auto const &vertex : vertices ) { - vertex.serialize( Output ); + basic_vertex( + glm::vec3{ vertex.position - origin }, + vertex.normal, + vertex.texture ) + .serialize( Output ); } } @@ -477,8 +489,12 @@ lines_node::linesnode_data::deserialize( std::istream &Input ) { // NOTE: geometry handle is acquired during geometry creation // vertex data vertices.resize( sn_utils::ld_uint32( Input ) ); + basic_vertex localvertex; for( auto &vertex : vertices ) { - vertex.deserialize( Input ); + localvertex.deserialize( Input ); + vertex.position = origin + glm::dvec3{ localvertex.position }; + vertex.normal = localvertex.normal; + vertex.texture = localvertex.texture; } } diff --git a/simulation.cpp b/simulation.cpp index 1c469dd0..181e22b3 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -39,15 +39,22 @@ state_manager::deserialize( std::string const &Scenariofile ) { // 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 ); + if( Scenariofile != "$.scn" ) { + // compilation to binary file isn't supported for rainsted-created overrides + 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 ); } + if( ( false == importscratchpad.binary.terrain ) + && ( Scenariofile != "$.scn" ) ) { + // if we didn't find usable binary version of the scenario files, create them now for future use + // as long as the scenario file wasn't rainsted-created base file override + Region->serialize( Scenariofile ); + } Global::iPause &= ~0x10; // koniec pauzy wczytywania return true; diff --git a/version.h b/version.h index 405dacc3..e7a0964f 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 1030 +#define VERSION_MINOR 1031 #define VERSION_REVISION 0