From a28b3d5af9242184ec9cef6072681fc96903ede2 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 1 Jun 2017 16:21:17 +0200 Subject: [PATCH 1/4] build 170531. fix for pantograph pressure switch activation, partial fix for event sorting --- Driver.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++----------- Driver.h | 4 +-- DynObj.cpp | 42 ++++++++++++++++++------------ Event.cpp | 6 +++-- Ground.cpp | 37 +++++++++++++------------- version.h | 2 +- 6 files changed, 113 insertions(+), 54 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index dbb4b171..35daf905 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -469,6 +469,16 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) } else { if( iTableDirection == 0 ) { return; } + // NOTE: provisory fix for BUG: sempahor indices no longer matching table size + // TODO: find and really fix the reason it happens + if( ( SemNextIndex != -1 ) + && ( SemNextIndex >= sSpeedTable.size() ) ) { + SemNextIndex = -1; + } + if( ( SemNextStopIndex != -1 ) + && ( SemNextStopIndex >= sSpeedTable.size() ) ) { + SemNextStopIndex = -1; + } // kontynuacja skanowania od ostatnio sprawdzonego toru (w ostatniej pozycji zawsze jest tor) if( ( SemNextStopIndex != -1 ) && ( sSpeedTable[SemNextStopIndex].fVelNext < 1.0 ) ) { @@ -1335,7 +1345,6 @@ void TController::TablePurger() // we can only update pointers safely after new table is finalized, so record their indices until then for( std::size_t idx = 0; idx < sSpeedTable.size() - 1; ++idx ) { // cache placement of semaphors in the new table, if we encounter them - // NOTE: we rely on the semaphor actually getting added in the next step rather than skipped, but it's a pretty safe bet if( idx == SemNextIndex ) { SemNextIndex = trimmedtable.size(); } @@ -1346,6 +1355,13 @@ void TController::TablePurger() if( ( 0 == ( speedpoint.iFlags & spEnabled ) ) || ( ( speedpoint.iFlags & ( spElapsed | spTrack | spCurve | spSwitch ) == ( spElapsed | spTrack | spCurve ) ) && ( speedpoint.fVelNext < 0.0 ) ) ) { + // if the trimmed point happens to be currently active semaphor we need to invalidate their placements + if( idx == SemNextIndex ) { + SemNextIndex = -1; + } + if( idx == SemNextStopIndex ) { + SemNextStopIndex = -1; + } continue; } // we're left with useful speed point record we should copy @@ -3365,8 +3381,8 @@ bool TController::UpdateSituation(double dt) } ElapsedTime += dt; WaitingTime += dt; - fBrakeTime -= - dt; // wpisana wartość jest zmniejszana do 0, gdy ujemna należy zmienić nastawę hamulca + // wpisana wartość jest zmniejszana do 0, gdy ujemna należy zmienić nastawę hamulca + fBrakeTime -= dt; fStopTime += dt; // zliczanie czasu postoju, nie ruszy dopóki ujemne fActionTime += dt; // czas używany przy regulacji prędkości i zamykaniu drzwi if (WriteLogFlag) @@ -3384,7 +3400,7 @@ bool TController::UpdateSituation(double dt) LastUpdatedTime = LastUpdatedTime + dt; } // Ra: skanowanie również dla prowadzonego ręcznie, aby podpowiedzieć prędkość - if ((LastReactionTime > Min0R(ReactionTime, 2.0))) + if ((LastReactionTime > std::min(ReactionTime, 2.0))) { // Ra: nie wiem czemu ReactionTime potrafi dostać 12 sekund, to jest przegięcie, bo przeżyna // STÓJ @@ -3429,18 +3445,48 @@ bool TController::UpdateSituation(double dt) if (AIControllFlag) { // tu bedzie logika sterowania if (mvOccupied->CommandIn.Command != "") - if (!mvOccupied->RunInternalCommand()) // rozpoznaj komende bo lokomotywa jej nie - // rozpoznaje + if( !mvOccupied->RunInternalCommand() ) { + // rozpoznaj komende bo lokomotywa jej nie rozpoznaje RecognizeCommand(); // samo czyta komendę wstawioną do pojazdu? - if (mvOccupied->SecuritySystem.Status > 1) // jak zadziałało CA/SHP - if (!mvOccupied->SecuritySystemReset()) // to skasuj - // if - // ((TestFlag(mvOccupied->SecuritySystem.Status,s_ebrake))&&(mvOccupied->BrakeCtrlPos==0)&&(AccDesired>0.0)) - if ((TestFlag(mvOccupied->SecuritySystem.Status, s_SHPebrake) || - TestFlag(mvOccupied->SecuritySystem.Status, s_CAebrake)) && - (mvOccupied->BrakeCtrlPos == 0) && (AccDesired > 0.0)) - mvOccupied->BrakeLevelSet( - 0); //!!! hm, może po prostu normalnie sterować hamulcem? + } + if( mvOccupied->SecuritySystem.Status > 1 ) { + // jak zadziałało CA/SHP + if( !mvOccupied->SecuritySystemReset() ) { // to skasuj + if( ( mvOccupied->BrakeCtrlPos == 0 ) + && ( AccDesired > 0.0 ) + && ( ( TestFlag( mvOccupied->SecuritySystem.Status, s_SHPebrake ) ) + || ( TestFlag( mvOccupied->SecuritySystem.Status, s_CAebrake ) ) ) ) { + //!!! hm, może po prostu normalnie sterować hamulcem? + mvOccupied->BrakeLevelSet( 0 ); + } + } + } + // basic emergency stop handling, while at it + if( ( true == mvOccupied->EmergencyBrakeFlag ) // radio-stop + && ( mvOccupied->Vel < 0.01 ) // and actual stop + && ( true == mvOccupied->Radio ) ) { // and we didn't touch the radio yet + // turning off the radio should reset the flag, during security system check + if( m_radiocontroltime > 2.5 ) { + // arbitrary 2.5 sec delay between stop and disabling the radio + mvOccupied->Radio = false; + m_radiocontroltime = 0.0; + } + else { + m_radiocontroltime += LastReactionTime; + } + } + if( ( false == mvOccupied->Radio ) + && ( false == mvOccupied->EmergencyBrakeFlag ) ) { + // otherwise if it's safe to do so, turn the radio back on + if( m_radiocontroltime > 5.0 ) { + // arbitrary 5 sec delay before switching radio back on + mvOccupied->Radio = true; + m_radiocontroltime = 0.0; + } + else { + m_radiocontroltime += LastReactionTime; + } + } } switch (OrderList[OrderPos]) { // ustalenie prędkości przy doczepianiu i odczepianiu, dystansów w pozostałych przypadkach diff --git a/Driver.h b/Driver.h index ea961098..512e1d23 100644 --- a/Driver.h +++ b/Driver.h @@ -201,8 +201,7 @@ class TController public: double fLastStopExpDist = -1.0; // odległość wygasania ostateniego przystanku double ReactionTime = 0.0; // czas reakcji Ra: czego i na co? świadomości AI - double fBrakeTime = 0.0; // wpisana wartość jest zmniejszana do 0, gdy ujemna należy zmienić nastawę - // hamulca + double fBrakeTime = 0.0; // wpisana wartość jest zmniejszana do 0, gdy ujemna należy zmienić nastawę hamulca private: double fReady = 0.0; // poziom odhamowania wagonów bool Ready = false; // ABu: stan gotowosci do odjazdu - sprawdzenie odhamowania wagonow @@ -211,6 +210,7 @@ class TController double deltalog = 0.05; // przyrost czasu double LastReactionTime = 0.0; double fActionTime = 0.0; // czas używany przy regulacji prędkości i zamykaniu drzwi + double m_radiocontroltime{ 0.0 }; // timer used to control speed of radio operations TAction eAction = actSleep; // aktualny stan bool HelpMeFlag = false; // wystawiane True jesli cos niedobrego sie dzieje public: diff --git a/DynObj.cpp b/DynObj.cpp index 16229e1d..d13a2b7e 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2472,10 +2472,15 @@ bool TDynamicObject::Update(double dt, double dt1) } else { if( MoverParameters->PantPress >= 4.6 ) { - // prime the pressure switch - MoverParameters->PantPressSwitchActive = true; - // turn off the subsystems lock - MoverParameters->PantPressLockActive = false; + // NOTE: we require active low power source to prime the pressure switch + // this is a work-around for potential isssues caused by the switch activating on otherwise idle vehicles, but should check whether it's accurate + if( ( true == MoverParameters->Battery ) + || ( true == MoverParameters->ConverterFlag ) ) { + // prime the pressure switch + MoverParameters->PantPressSwitchActive = true; + // turn off the subsystems lock + MoverParameters->PantPressLockActive = false; + } if( MoverParameters->PantPress >= 4.8 ) { // Winger - automatyczne wylaczanie malej sprezarki @@ -5233,22 +5238,27 @@ TDynamicObject * TDynamicObject::ControlledFind() // jeździć dobrze // również hamowanie wykonuje się zaworem w członie, a nie w silnikowym... TDynamicObject *d = this; // zaczynamy od aktualnego - if (d->MoverParameters->TrainType & dt_EZT) // na razie dotyczy to EZT - if (d->NextConnected ? d->MoverParameters->Couplers[1].AllowedFlag & ctrain_depot : false) - { // gdy jest człon od sprzęgu 1, a sprzęg łączony - // warsztatowo (powiedzmy) - if ((d->MoverParameters->Power < 1.0) && (d->NextConnected->MoverParameters->Power > - 1.0)) // my nie mamy mocy, ale ten drugi ma + if( d->MoverParameters->TrainType & dt_EZT ) { + // na razie dotyczy to EZT + if( ( d->NextConnected != nullptr ) + && ( true == TestFlag( d->MoverParameters->Couplers[ 1 ].AllowedFlag, coupling::permanent ) ) ) { + // gdy jest człon od sprzęgu 1, a sprzęg łączony warsztatowo (powiedzmy) + if( ( d->MoverParameters->Power < 1.0 ) + && ( d->NextConnected->MoverParameters->Power > 1.0 ) ) { + // my nie mamy mocy, ale ten drugi ma d = d->NextConnected; // będziemy sterować tym z mocą + } } - else if (d->PrevConnected ? d->MoverParameters->Couplers[0].AllowedFlag & ctrain_depot : - false) - { // gdy jest człon od sprzęgu 0, a sprzęg łączony - // warsztatowo (powiedzmy) - if ((d->MoverParameters->Power < 1.0) && (d->PrevConnected->MoverParameters->Power > - 1.0)) // my nie mamy mocy, ale ten drugi ma + else if( ( d->PrevConnected != nullptr ) + && ( true == TestFlag( d->MoverParameters->Couplers[ 0 ].AllowedFlag, coupling::permanent ) ) ) { + // gdy jest człon od sprzęgu 0, a sprzęg łączony warsztatowo (powiedzmy) + if( ( d->MoverParameters->Power < 1.0 ) + && ( d->PrevConnected->MoverParameters->Power > 1.0 ) ) { + // my nie mamy mocy, ale ten drugi ma d = d->PrevConnected; // będziemy sterować tym z mocą + } } + } return d; }; //--------------------------------------------------------------------------- diff --git a/Event.cpp b/Event.cpp index c27f9a4a..bcca997f 100644 --- a/Event.cpp +++ b/Event.cpp @@ -612,8 +612,10 @@ void TEvent::Load(cParser *parser, vector3 *org) void TEvent::AddToQuery(TEvent *e) { // dodanie eventu do kolejki - if (evNext ? (e->fStartTime >= evNext->fStartTime) : false) - evNext->AddToQuery(e); // sortowanie wg czasu + if( ( evNext != nullptr ) + && ( e->fStartTime > evNext->fStartTime ) ) { + evNext->AddToQuery( e ); // sortowanie wg czasu + } else { // dodanie z przodu e->evNext = evNext; diff --git a/Ground.cpp b/Ground.cpp index 4dfbaa9f..91ee13e3 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -3178,9 +3178,9 @@ bool TGround::AddToQuery(TEvent *Event, TDynamicObject *Node) if (!Event->iQueued) // jeśli nie dodany jeszcze do kolejki { // kolejka eventów jest posortowana względem (fStartTime) Event->Activator = Node; - if (Event->Type == tp_AddValues ? (Event->fDelay == 0.0) : false) - { // eventy AddValues trzeba wykonywać natychmiastowo, inaczej kolejka może zgubić - // jakieś dodawanie + 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)) @@ -3208,11 +3208,10 @@ bool TGround::AddToQuery(TEvent *Event, TDynamicObject *Node) std::to_string(Event->Params[1].asdouble) + " " + std::to_string(Event->Params[2].asdouble)); } - Event = - Event - ->evJoined; // jeśli jest kolejny o takiej samej nazwie, to idzie do kolejki + // 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) + Event = Event->evJoined; } - if (Event) + if( Event != nullptr ) { // standardowe dodanie do kolejki WriteLog("EVENT ADDED TO QUEUE: " + Event->asName + (Node ? (" by " + Node->asName) : "")); Event->fStartTime = @@ -3221,10 +3220,13 @@ bool TGround::AddToQuery(TEvent *Event, TDynamicObject *Node) Event->fStartTime += Event->fRandomDelay * Random(10000) * 0.0001; // doliczenie losowego czasu opóźnienia ++Event->iQueued; // zabezpieczenie przed podwójnym dodaniem do kolejki - if (QueryRootEvent ? Event->fStartTime >= QueryRootEvent->fStartTime : false) - QueryRootEvent->AddToQuery(Event); // dodanie gdzieś w środku - else - { // dodanie z przodu: albo nic nie ma, albo ma być wykonany szybciej niż pierwszy + if( ( QueryRootEvent != nullptr ) + && ( Event->fStartTime > QueryRootEvent->fStartTime ) ) { + // dodanie gdzieś w środku + QueryRootEvent->AddToQuery( Event ); + } + else { + // dodanie z przodu: albo nic nie ma, albo ma być wykonany szybciej niż pierwszy Event->evNext = QueryRootEvent; QueryRootEvent = Event; } @@ -3355,11 +3357,9 @@ bool TGround::CheckQuery() { // to teraz on będzie następny do wykonania QueryRootEvent = QueryRootEvent->evJoined; // następny będzie ten doczepiony QueryRootEvent->evNext = tmpEvent->evNext; // pamiętając o następnym z kolejki - QueryRootEvent->fStartTime = - tmpEvent->fStartTime; // czas musi być ten sam, bo nie jest aktualizowany + QueryRootEvent->fStartTime = tmpEvent->fStartTime; // czas musi być ten sam, bo nie jest aktualizowany QueryRootEvent->Activator = tmpEvent->Activator; // pojazd aktywujący - // w sumie można by go dodać normalnie do kolejki, ale trzeba te połączone posortować wg - // czasu wykonania + // 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 @@ -3880,9 +3880,10 @@ bool TGround::GetTraction(TDynamicObject *model) node->hvTraction ->vParametric; // współczynniki równania parametrycznego fRaParam = -DotProduct(pant0, vFront); - fRaParam = -(DotProduct(node->hvTraction->pPoint1, vFront) + - fRaParam) / - DotProduct(vParam, vFront); + auto const paramfrontdot = DotProduct( vParam, vFront ); + fRaParam = + -( DotProduct( 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 diff --git a/version.h b/version.h index 7667fb27..cbf2eb2e 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 525 +#define VERSION_MINOR 531 #define VERSION_REVISION 0 From 5f0069bf609f2ab9bfd87a1a18faca8a02100957 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Fri, 2 Jun 2017 16:35:36 +0200 Subject: [PATCH 2/4] build 170602. completed fix for infinite event loop, modified horn controls to work independent of each other --- Event.cpp | 34 +++++++--- Event.h | 2 +- Ground.cpp | 189 ++++++++++++++++------------------------------------- Ground.h | 4 +- Train.cpp | 10 +++ version.h | 2 +- 6 files changed, 96 insertions(+), 145 deletions(-) diff --git a/Event.cpp b/Event.cpp index bcca997f..ee34478a 100644 --- a/Event.cpp +++ b/Event.cpp @@ -610,16 +610,32 @@ void TEvent::Load(cParser *parser, vector3 *org) } }; -void TEvent::AddToQuery(TEvent *e) -{ // dodanie eventu do kolejki - if( ( evNext != nullptr ) - && ( e->fStartTime > evNext->fStartTime ) ) { - evNext->AddToQuery( e ); // sortowanie wg czasu +void TEvent::AddToQuery( TEvent *Event, TEvent *&Start ) { + + TEvent *target( Start ); + TEvent *previous( nullptr ); + while( ( Event->fStartTime >= target->fStartTime ) + && ( target->evNext != nullptr ) ) { + previous = target; + target = target->evNext; } - else - { // dodanie z przodu - e->evNext = evNext; - evNext = e; + // the new event will be either before or after the one we located + if( Event->fStartTime >= target->fStartTime ) { + assert( target->evNext == nullptr ); + target->evNext = Event; + // if we have resurrected event land at the end of list, the link from previous run could potentially "add" unwanted events to the queue + Event->evNext = nullptr; + } + else { + if( previous != nullptr ) { + previous->evNext = Event; + Event->evNext = target; + } + else { + // special case, we're inserting our event before the provided start point + Event->evNext = Start; + Start = Event; + } } } diff --git a/Event.h b/Event.h index 110e3abc..7d020046 100644 --- a/Event.h +++ b/Event.h @@ -105,7 +105,7 @@ class TEvent // zmienne: ev* ~TEvent(); void Init(); void Load(cParser *parser, vector3 *org); - void AddToQuery(TEvent *e); + static void AddToQuery( TEvent *Event, TEvent *&Start ); std::string CommandGet(); TCommandType Command(); double ValueGet(int n); diff --git a/Ground.cpp b/Ground.cpp index 91ee13e3..54662956 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -3065,17 +3065,6 @@ TTrack * TGround::FindTrack(vector3 Point, int &iConnection, TGroundNode *Exclud if ((tmp = sr->FindTrack(&Point, iConnection, Exclude->pTrack)) != NULL) return tmp; } -#if 0 - //wyszukiwanie czołgowe (po wszystkich jak leci) - nie ma chyba sensu - for (Current=nRootOfType[TP_TRACK];Current;Current=Current->Next) - { - if ((Current->iType==TP_TRACK) && (Current!=Exclude)) - { - iConnection=Current->pTrack->TestPoint(&Point); - if (iConnection>=0) return Current->pTrack; - } - } -#endif return NULL; } @@ -3174,22 +3163,21 @@ TTraction * TGround::TractionNearestFind(vector3 &p, int dir, TGroundNode *n) bool TGround::AddToQuery(TEvent *Event, TDynamicObject *Node) { - 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 + 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 = Node; 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 + && ( 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( @@ -3197,41 +3185,47 @@ bool TGround::AddToQuery(TEvent *Event, TDynamicObject *Node) &Event->Params[ 4 ].nGroundNode->pCenter ); } //if (DebugModeFlag) - WriteLog("EVENT EXECUTED: AddValues & Track command - " + - std::string(Event->Params[0].asText) + " " + - std::to_string(Event->Params[1].asdouble) + " " + - std::to_string(Event->Params[2].asdouble)); + WriteLog( + "EVENT EXECUTED" + ( Node ? ( " by " + Node->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: AddValues - " + - std::string(Event->Params[0].asText) + " " + - std::to_string(Event->Params[1].asdouble) + " " + - std::to_string(Event->Params[2].asdouble)); + WriteLog( + "EVENT EXECUTED" + ( Node ? ( " by " + Node->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) - Event = Event->evJoined; + 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 - WriteLog("EVENT ADDED TO QUEUE: " + Event->asName + (Node ? (" by " + Node->asName) : "")); - Event->fStartTime = - fabs(Event->fDelay) + Timer::GetTime(); // czas od uruchomienia scenerii - if (Event->fRandomDelay > 0.0) - Event->fStartTime += Event->fRandomDelay * Random(10000) * - 0.0001; // doliczenie losowego czasu opóźnienia + if( Event != nullptr ) { + // standardowe dodanie do kolejki ++Event->iQueued; // zabezpieczenie przed podwójnym dodaniem do kolejki - if( ( QueryRootEvent != nullptr ) - && ( Event->fStartTime > QueryRootEvent->fStartTime ) ) { - // dodanie gdzieś w środku - QueryRootEvent->AddToQuery( Event ); + WriteLog( "EVENT ADDED TO QUEUE" + ( Node ? ( " by " + Node->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 { - // dodanie z przodu: albo nic nie ma, albo ma być wykonany szybciej niż pierwszy - Event->evNext = QueryRootEvent; QueryRootEvent = Event; + QueryRootEvent->evNext = nullptr; } } } + } return true; } @@ -3308,49 +3302,8 @@ bool TGround::CheckQuery() { // sprawdzenie kolejki eventów oraz wykonanie tych, którym czas minął TLocation loc; int i; - /* //Ra: to w ogóle jakiś chory kod jest; wygląda jak wyszukanie eventu z najlepszym czasem - Double evtime,evlowesttime; //Ra: co to za typ? - //evlowesttime=1000000; - if (QueryRootEvent) - { - OldQRE=QueryRootEvent; - tmpEvent=QueryRootEvent; - } - if (QueryRootEvent) - { - for (i=0;i<90;++i) - { - evtime=((tmpEvent->fStartTime)-(Timer::GetTime())); //pobranie wartości zmiennej - if (evtimeNext) - tmpEvent=tmpEvent->Next; - else - i=100; - } - if (OldQRE!=tmp2Event) - { - QueryRootEvent->AddToQuery(QueryRootEvent); - QueryRootEvent=tmp2Event; - } - } - */ - /* - if (QueryRootEvent) - {//wypisanie kolejki - tmpEvent=QueryRootEvent; - WriteLog("--> Event queue:"); - while (tmpEvent) - { - WriteLog(tmpEvent->asName+" "+AnsiString(tmpEvent->fStartTime)); - tmpEvent=tmpEvent->Next; - } - } - */ - while (QueryRootEvent ? QueryRootEvent->fStartTime < Timer::GetTime() : false) + while( ( QueryRootEvent != nullptr ) + && ( QueryRootEvent->fStartTime < Timer::GetTime() ) ) { // eventy są posortowana wg czasu wykonania tmpEvent = QueryRootEvent; // wyjęcie eventu z kolejki if (QueryRootEvent->evJoined) // jeśli jest kolejny o takiej samej nazwie @@ -3359,16 +3312,15 @@ bool TGround::CheckQuery() QueryRootEvent->evNext = tmpEvent->evNext; // pamiętając o następnym z kolejki QueryRootEvent->fStartTime = tmpEvent->fStartTime; // czas musi być ten sam, bo nie jest aktualizowany QueryRootEvent->Activator = tmpEvent->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 (tmpEvent->bEnabled) { // w zasadzie te wyłączone są skanowane i nie powinny się nigdy w kolejce znaleźć - WriteLog("EVENT LAUNCHED: " + tmpEvent->asName + - (tmpEvent->Activator ? - std::string(" by " + tmpEvent->Activator->asName) : - "" )); + --tmpEvent->iQueued; // teraz moze być ponownie dodany do kolejki + WriteLog( "EVENT LAUNCHED" + ( tmpEvent->Activator ? ( " by " + tmpEvent->Activator->asName ) : "" ) + ": " + tmpEvent->asName ); switch (tmpEvent->Type) { case tp_CopyValues: // skopiowanie wartości z innej komórki @@ -3530,29 +3482,20 @@ bool TGround::CheckQuery() conditional_anyelse)) // warunek spelniony albo było użyte else { WriteLog("Multiple passed"); - for (i = 0; i < 8; ++i) - { // dodawane do kolejki w kolejności zapisania - if (tmpEvent->Params[i].asEvent) - if (bCondition != (((tmpEvent->iFlags & (conditional_else << i)) != 0))) - { - if (tmpEvent->Params[i].asEvent != tmpEvent) - AddToQuery(tmpEvent->Params[i].asEvent, - tmpEvent->Activator); // normalnie dodać - else // jeśli ma być rekurencja - if (tmpEvent->fDelay >= - 5.0) // to musi mieć sensowny okres powtarzania - if (tmpEvent->iQueued < 2) - { // trzeba zrobić wyjątek, aby event mógł się sam dodać do - // kolejki, raz już jest, ale będzie usunięty - // pętla eventowa może być uruchomiona wiele razy, ale tylko - // pierwsze uruchomienie zadziała - tmpEvent->iQueued = - 0; // tymczasowo, aby był ponownie dodany do kolejki - AddToQuery(tmpEvent, tmpEvent->Activator); - tmpEvent->iQueued = - 2; // kolejny raz już absolutnie nie dodawać + for (i = 0; i < 8; ++i) { + // dodawane do kolejki w kolejności zapisania + if( tmpEvent->Params[ i ].asEvent ) { + if( bCondition != ( ( ( tmpEvent->iFlags & ( conditional_else << i ) ) != 0 ) ) ) { + if( tmpEvent->Params[ i ].asEvent != tmpEvent ) + AddToQuery( tmpEvent->Params[ i ].asEvent, tmpEvent->Activator ); // normalnie dodać + else { + // jeśli ma być rekurencja to musi mieć sensowny okres powtarzania + if( tmpEvent->fDelay >= 5.0 ) { + AddToQuery( tmpEvent, tmpEvent->Activator ); } + } } + } } if (Global::iMultiplayer) // dajemy znać do serwera o wykonaniu if ((tmpEvent->iFlags & conditional_anyelse) == @@ -3635,22 +3578,6 @@ bool TGround::CheckQuery() break; } // switch (tmpEvent->Type) } // if (tmpEvent->bEnabled) - --tmpEvent->iQueued; // teraz moze być ponownie dodany do kolejki - /* - if (QueryRootEvent->eJoined) //jeśli jest kolejny o takiej samej nazwie - {//to teraz jego dajemy do wykonania - QueryRootEvent->eJoined->Next=QueryRootEvent->Next; //pamiętając o następnym z kolejki - QueryRootEvent->eJoined->fStartTime=QueryRootEvent->fStartTime; //czas musi być ten sam, - bo nie jest aktualizowany - //QueryRootEvent->fStartTime=0; - QueryRootEvent=QueryRootEvent->eJoined; //a wykonać ten doczepiony - } - else - {//a jak nazwa jest unikalna, to kolejka idzie dalej - //QueryRootEvent->fStartTime=0; - QueryRootEvent=QueryRootEvent->Next; //NULL w skrajnym przypadku - } - */ } // while return true; } diff --git a/Ground.h b/Ground.h index dd408880..d8d62ac5 100644 --- a/Ground.h +++ b/Ground.h @@ -274,9 +274,7 @@ class TGround TGroundRect Rects[iNumRects][iNumRects]; // mapa kwadratów kilometrowych TEvent *RootEvent = nullptr; // lista zdarzeń TEvent *QueryRootEvent = nullptr, - *tmpEvent = nullptr, - *tmp2Event = nullptr, - *OldQRE = nullptr; + *tmpEvent = nullptr; TSubRect *pRendered[1500]; // lista renderowanych sektorów int iNumNodes = 0; vector3 pOrigin; diff --git a/Train.cpp b/Train.cpp index 75bd2f53..61bc9138 100644 --- a/Train.cpp +++ b/Train.cpp @@ -2881,10 +2881,12 @@ void TTrain::OnCommand_hornlowactivate( TTrain *Train, command_data const &Comma if( false == TestFlag( Train->mvOccupied->WarningSignal, 1 ) ) { // turn on Train->mvOccupied->WarningSignal |= 1; +/* if( true == TestFlag( Train->mvOccupied->WarningSignal, 2 ) ) { // low and high horn are treated as mutually exclusive Train->mvControlled->WarningSignal &= ~2; } +*/ // audio feedback if( ( Train->ggHornButton.GetValue() > -0.5 ) || ( Train->ggHornLowButton.GetValue() < 0.5 ) ) { @@ -2897,8 +2899,11 @@ void TTrain::OnCommand_hornlowactivate( TTrain *Train, command_data const &Comma } else if( Command.action == GLFW_RELEASE ) { // turn off +/* // NOTE: we turn off both low and high horn, due to unreliability of release event when shift key is involved Train->mvOccupied->WarningSignal &= ~( 1 | 2 ); +*/ + Train->mvOccupied->WarningSignal &= ~1; // audio feedback if( ( Train->ggHornButton.GetValue() < -0.5 ) || ( Train->ggHornLowButton.GetValue() > 0.5 ) ) { @@ -2925,10 +2930,12 @@ void TTrain::OnCommand_hornhighactivate( TTrain *Train, command_data const &Comm if( false == TestFlag( Train->mvOccupied->WarningSignal, 2 ) ) { // turn on Train->mvOccupied->WarningSignal |= 2; +/* if( true == TestFlag( Train->mvOccupied->WarningSignal, 1 ) ) { // low and high horn are treated as mutually exclusive Train->mvControlled->WarningSignal &= ~1; } +*/ // audio feedback if( Train->ggHornButton.GetValue() < 0.5 ) { Train->play_sound( Train->dsbSwitch ); @@ -2940,8 +2947,11 @@ void TTrain::OnCommand_hornhighactivate( TTrain *Train, command_data const &Comm } else if( Command.action == GLFW_RELEASE ) { // turn off +/* // NOTE: we turn off both low and high horn, due to unreliability of release event when shift key is involved Train->mvOccupied->WarningSignal &= ~( 1 | 2 ); +*/ + Train->mvOccupied->WarningSignal &= ~2; // audio feedback if( Train->ggHornButton.GetValue() > 0.5 ) { Train->play_sound( Train->dsbSwitch ); diff --git a/version.h b/version.h index cbf2eb2e..005e6949 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 531 +#define VERSION_MINOR 602 #define VERSION_REVISION 0 From 135ac514f4922fcdd65a8d93ccd021923f0aab93 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 3 Jun 2017 17:14:02 +0200 Subject: [PATCH 3/4] fix for trace route early return on full speed signal, fix for tachometers initialization --- Driver.cpp | 2 +- Ground.cpp | 8 ++++---- Train.cpp | 36 ++---------------------------------- Train.h | 22 ++++++++-------------- version.h | 2 +- 5 files changed, 16 insertions(+), 54 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 35daf905..98709cc7 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -481,7 +481,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) } // kontynuacja skanowania od ostatnio sprawdzonego toru (w ostatniej pozycji zawsze jest tor) if( ( SemNextStopIndex != -1 ) - && ( sSpeedTable[SemNextStopIndex].fVelNext < 1.0 ) ) { + && ( sSpeedTable[SemNextStopIndex].fVelNext == 0.0 ) ) { // znaleziono semafor lub tarczę lub tor z prędkością zero, trzeba sprawdzić czy to nadał semafor // jeśli jest następny semafor to sprawdzamy czy to on nadał zero if( ( OrderCurrentGet() & Obey_train ) diff --git a/Ground.cpp b/Ground.cpp index 54662956..c9fb6c68 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -3169,10 +3169,10 @@ bool TGround::AddToQuery(TEvent *Event, TDynamicObject *Node) { // kolejka eventów jest posortowana względem (fStartTime) Event->Activator = Node; 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ć + && ( 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, diff --git a/Train.cpp b/Train.cpp index 61bc9138..5a572617 100644 --- a/Train.cpp +++ b/Train.cpp @@ -4400,16 +4400,6 @@ bool TTrain::Update( double const Deltatime ) { // ustawienie zmiennych dla silnika spalinowego fEngine[1] = mvControlled->ShowEngineRotation(1); fEngine[2] = mvControlled->ShowEngineRotation(2); - // if (ggEnrot1m.SubModel) - //{ - // ggEnrot1m.UpdateValue(mvControlled->ShowEngineRotation(1)); - // ggEnrot1m.Update(); - //} - // if (ggEnrot2m.SubModel) - //{ - // ggEnrot2m.UpdateValue(mvControlled->ShowEngineRotation(2)); - // ggEnrot2m.Update(); - //} } else if (mvControlled->EngineType == DieselEngine) @@ -4417,34 +4407,12 @@ bool TTrain::Update( double const Deltatime ) fEngine[1] = mvControlled->ShowEngineRotation(1); fEngine[2] = mvControlled->ShowEngineRotation(2); fEngine[3] = mvControlled->ShowEngineRotation(3); - // if (ggEnrot1m.SubModel) - //{ - // ggEnrot1m.UpdateValue(mvControlled->ShowEngineRotation(1)); - // ggEnrot1m.Update(); - //} - // if (ggEnrot2m.SubModel) - //{ - // ggEnrot2m.UpdateValue(mvControlled->ShowEngineRotation(2)); - // ggEnrot2m.Update(); - //} - // if (ggEnrot3m.SubModel) - // if (mvControlled->Couplers[1].Connected) - // { - // ggEnrot3m.UpdateValue(mvControlled->ShowEngineRotation(3)); - // ggEnrot3m.Update(); - // } - // if (ggEngageRatio.SubModel) - //{ - // ggEngageRatio.UpdateValue(mvControlled->dizel_engage); - // ggEngageRatio.Update(); - //} if (ggMainGearStatus.SubModel) { if (mvControlled->Mains) - ggMainGearStatus.UpdateValue(1.1 - - fabs(mvControlled->dizel_automaticgearstatus)); + ggMainGearStatus.UpdateValue(1.1 - std::abs(mvControlled->dizel_automaticgearstatus)); else - ggMainGearStatus.UpdateValue(0); + ggMainGearStatus.UpdateValue(0.0); ggMainGearStatus.Update(); } if (ggIgnitionKey.SubModel) diff --git a/Train.h b/Train.h index aab20177..c4b6b574 100644 --- a/Train.h +++ b/Train.h @@ -449,23 +449,17 @@ public: // reszta może by?publiczna int CAflag; // hunter-131211: dla osobnego zbijania CA i SHP double fPoslizgTimer; - // double fShpTimer; - // double fDblClickTimer; - // ABu: Przeniesione do public. - Wiem, ze to nieladnie... - // bool CabChange(int iDirection); - // bool InitializeCab(int NewCabNo, AnsiString asFileName); TTrack *tor; int keybrakecount; // McZapkie-240302 - przyda sie do tachometru - float fTachoVelocity; - float fTachoVelocityJump; // ze skakaniem - float fTachoTimer; - float fTachoCount; - float fHVoltage; // napi?cie dla dynamicznych ga?ek - float fHCurrent[4]; // pr?dy: suma i amperomierze 1,2,3 - float fEngine[4]; // obroty te? trzeba pobra? - int iCarNo, iPowerNo, iUnitNo; // liczba pojazdow, czlonow napednych i jednostek spiętych ze - // sobą + float fTachoVelocity{ 0.0f }; + float fTachoVelocityJump{ 0.0f }; // ze skakaniem + float fTachoTimer{ 0.0f }; + float fTachoCount{ 0.0f }; + float fHVoltage{ 0.0f }; // napi?cie dla dynamicznych ga?ek + float fHCurrent[ 4 ] = { 0.0f, 0.0f, 0.0f, 0.0f }; // pr?dy: suma i amperomierze 1,2,3 + float fEngine[ 4 ] = { 0.0f, 0.0f, 0.0f, 0.0f }; // obroty te? trzeba pobra? + int iCarNo, iPowerNo, iUnitNo; // liczba pojazdow, czlonow napednych i jednostek spiętych ze sobą bool bDoors[20][3]; // drzwi dla wszystkich czlonow int iUnits[20]; // numer jednostki int iDoorNo[20]; // liczba drzwi diff --git a/version.h b/version.h index 005e6949..b41d9f32 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 602 +#define VERSION_MINOR 603 #define VERSION_REVISION 0 From ba8c0dd96f1a8cb3574dfc58394d256ef3a37deb Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 3 Jun 2017 18:29:07 +0200 Subject: [PATCH 4/4] pre-merge with renderer_unification branch --- openglgeometrybank.cpp | 218 +++++++++++++++++++++++++++++++++++++++++ openglgeometrybank.h | 122 +++++++++++++++++++++++ 2 files changed, 340 insertions(+) create mode 100644 openglgeometrybank.cpp create mode 100644 openglgeometrybank.h diff --git a/openglgeometrybank.cpp b/openglgeometrybank.cpp new file mode 100644 index 00000000..ec25b023 --- /dev/null +++ b/openglgeometrybank.cpp @@ -0,0 +1,218 @@ +/* +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 "openglgeometrybank.h" + +#include "sn_utils.h" + +void +basic_vertex::serialize( std::ostream &s ) { + + sn_utils::ls_float32( s, position.x ); + sn_utils::ls_float32( s, position.y ); + sn_utils::ls_float32( 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 +basic_vertex::deserialize( std::istream &s ) { + + position.x = sn_utils::ld_float32( s ); + position.y = sn_utils::ld_float32( s ); + position.z = sn_utils::ld_float32( 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 ); +} + +// generic geometry bank class, allows storage, update and drawing of geometry chunks + +// creates a new geometry chunk of specified type from supplied vertex data. returns: handle to the chunk +geometrychunk_handle +geometry_bank::create( vertex_array &Vertices, int const Datatype ) { + + if( true == Vertices.empty() ) { return NULL; } + + m_chunks.emplace_back( Vertices, Datatype ); + // NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication + return m_chunks.size(); +} + +// replaces data of specified chunk with the supplied vertex data, starting from specified offset +bool +geometry_bank::replace( vertex_array &Vertices, geometrychunk_handle const Chunk, std::size_t const Offset ) { + + if( ( Chunk == 0 ) || ( Chunk > m_chunks.size() ) ) { return false; } + + auto &chunk = m_chunks[ Chunk - 1 ]; + + if( ( Offset == 0 ) + && ( Vertices.size() == chunk.vertices.size() ) ) { + // check first if we can get away with a simple swap... + chunk.vertices.swap( Vertices ); + } + else { + // ...otherwise we need to do some legwork + // NOTE: if the offset is larger than existing size of the chunk, it'll bridge the gap with 'blank' vertices + // TBD: we could bail out with an error instead if such request occurs + chunk.vertices.resize( Offset + Vertices.size(), basic_vertex() ); + chunk.vertices.insert( std::end( chunk.vertices ), std::begin( Vertices ), std::end( Vertices ) ); + } + return true; +} + +vertex_array & +geometry_bank::data( geometrychunk_handle const Chunk ) { + + return m_chunks.at( Chunk - 1 ).vertices; +} + +// opengl vbo-based variant of the geometry bank + +GLuint opengl_vbogeometrybank::m_activebuffer{ NULL }; // buffer bound currently on the opengl end, if any + +// creates a new geometry chunk of specified type from supplied vertex data. returns: handle to the chunk +geometrychunk_handle +opengl_vbogeometrybank::create( vertex_array &Vertices, int const Datatype ) { + + auto const handle = geometry_bank::create( Vertices, Datatype ); + if( handle == NULL ) { + // if we didn't get anything, bail early + return handle; + } + // adding a chunk means we'll be (re)building the buffer, which will fill the chunk records, amongst other things + // so we don't need to initialize the values here + m_chunkrecords.emplace_back( chunk_record() ); + // kiss the existing buffer goodbye, new overall data size means we'll be making a new one + delete_buffer(); + + return handle; +} + +// replaces data of specified chunk with the supplied vertex data, starting from specified offset +bool +opengl_vbogeometrybank::replace( vertex_array &Vertices, geometrychunk_handle const Chunk, std::size_t const Offset ) { + + auto const result = geometry_bank::replace( Vertices, Chunk, Offset ); + if( false == result ) { + // if nothing happened we can bail out early + return result; + } + auto &chunkrecord = m_chunkrecords[ Chunk - 1 ]; + chunkrecord.is_good = false; + // if the overall length of the chunk didn't change we can get away with reusing the old buffer... + if( m_chunks[ Chunk - 1 ].vertices.size() != chunkrecord.size ) { + // ...but otherwise we'll need to allocate a new one + // TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one, + // but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it? + delete_buffer(); + } + return result; +} + +// draws geometry stored in specified chunk +void +opengl_vbogeometrybank::draw( geometrychunk_handle const Chunk ) { + + if( m_buffer == NULL ) { + // if there's no buffer, we'll have to make one + // NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data + // may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste + if( true == m_chunks.empty() ) { return; } + + std::size_t datasize{ 0 }; + auto &chunkrecord = m_chunkrecords.begin(); + for( auto &chunk : m_chunks ) { + // fill all chunk records, based on the chunk data + chunkrecord->offset = datasize; + chunkrecord->size = chunk.vertices.size(); + datasize += chunkrecord->size; + ++chunkrecord; + } + // the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist + if( datasize == 0 ) { return; } + // try to set up the buffer we need + ::glGenBuffers( 1, &m_buffer ); + bind_buffer(); + // NOTE: we're using static_draw since it's generally true for all we have implemented at the moment + // TODO: allow to specify usage hint at the object creation, and pass it here + ::glBufferData( + GL_ARRAY_BUFFER, + datasize * sizeof( basic_vertex ), + nullptr, + GL_STATIC_DRAW ); + m_buffercapacity = datasize; + } + // actual draw procedure starts here + // setup... + if( m_activebuffer != m_buffer ) { + bind_buffer(); + } + auto &chunkrecord = m_chunkrecords[ Chunk - 1 ]; + auto const &chunk = m_chunks[ Chunk - 1 ]; + if( false == chunkrecord.is_good ) { + // we may potentially need to upload new buffer data before we can draw it + ::glBufferSubData( + GL_ARRAY_BUFFER, + chunkrecord.offset * sizeof( basic_vertex ), + chunkrecord.size * sizeof( basic_vertex ), + chunk.vertices.data() ); + chunkrecord.is_good = true; + } + // ...render... + ::glDrawArrays( chunk.type, chunkrecord.offset, chunkrecord.size ); + // ...post-render cleanup +/* + ::glDisableClientState( GL_VERTEX_ARRAY ); + ::glDisableClientState( GL_NORMAL_ARRAY ); + ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); m_activebuffer = 0; +*/ +} + +void +opengl_vbogeometrybank::bind_buffer() { + + ::glBindBuffer( GL_ARRAY_BUFFER, m_buffer ); + // TODO: allow specifying other vertex data setups + ::glVertexPointer( 3, GL_FLOAT, sizeof( basic_vertex ), static_cast( nullptr ) ); + ::glNormalPointer( GL_FLOAT, sizeof( basic_vertex ), static_cast( nullptr ) + sizeof(float) * 3 ); // normalne + ::glTexCoordPointer( 2, GL_FLOAT, sizeof( basic_vertex ), static_cast( nullptr ) + 24 ); // wierzchołki + // TODO: allow specifying other vertex data setups, either in the draw() parameters or during chunk or buffer creation + ::glEnableClientState( GL_VERTEX_ARRAY ); + ::glEnableClientState( GL_NORMAL_ARRAY ); + ::glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + m_activebuffer = m_buffer; +} + +void +opengl_vbogeometrybank::delete_buffer() { + + if( m_buffer != NULL ) { + + ::glDeleteBuffers( 1, &m_buffer ); + if( m_activebuffer == m_buffer ) { + m_activebuffer = NULL; + } + m_buffer = NULL; + m_buffercapacity = 0; + } +} diff --git a/openglgeometrybank.h b/openglgeometrybank.h new file mode 100644 index 00000000..91969a29 --- /dev/null +++ b/openglgeometrybank.h @@ -0,0 +1,122 @@ +/* +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 "GL/glew.h" +#ifdef _WINDOWS +#include "GL/wglew.h" +#endif + +struct basic_vertex { + glm::vec3 position; // 3d space + glm::vec3 normal; // 3d space + glm::vec2 texture; // uv space + + void serialize( std::ostream& ); + void deserialize( std::istream& ); +}; + +typedef std::vector vertex_array; + +// generic geometry bank class, allows storage, update and drawing of geometry chunks + +typedef std::size_t geometrychunk_handle; + +class geometry_bank { + +public: +// types: + +// constructors: + +// destructor: + virtual + ~geometry_bank() { ; } + +// methods: + // creates a new geometry chunk of specified type from supplied vertex data. returns: handle to the chunk or NULL + virtual + geometrychunk_handle + create( vertex_array &Vertices, int const Datatype ); + // replaces data of specified chunk with the supplied vertex data, starting from specified offset + virtual + bool + replace( vertex_array &Vertices, geometrychunk_handle const Chunk, std::size_t const Offset = 0 ); + // draws geometry stored in specified chunk + virtual + void + draw( geometrychunk_handle const Chunk ) = 0; + // draws geometry stored in supplied list of chunks + template + void + draw( _Iterator First, _Iterator Last ) { while( First != Last ) { draw( *First ); ++First; } } + vertex_array & + data( geometrychunk_handle const Chunk ); + +protected: +// types: + struct geometry_chunk { + int type; // kind of geometry used by the chunk + vertex_array vertices; // geometry data + + geometry_chunk( vertex_array &Vertices, int const Datatype ) : + vertices( Vertices ), type( Datatype ) + {} + }; + + typedef std::vector geometrychunk_sequence; + +// members: + geometrychunk_sequence m_chunks; + +}; + +// opengl vbo-based variant of the geometry bank + +class opengl_vbogeometrybank : public geometry_bank { + +public: +// methods: + // creates a new geometry chunk of specified type from supplied vertex data. returns: handle to the chunk or NULL + geometrychunk_handle + create( vertex_array &Vertices, int const Datatype ); + // replaces data of specified chunk with the supplied vertex data, starting from specified offset + bool + replace( vertex_array &Vertices, geometrychunk_handle const Chunk, std::size_t const Offset = 0 ); + // draws geometry stored in specified chunk + void + draw( geometrychunk_handle const Chunk ); + +private: +// types: + struct chunk_record{ + std::size_t offset{ 0 }; // beginning of the chunk data as offset from the beginning of the last established buffer + std::size_t size{ 0 }; // size of the chunk in the last established buffer + bool is_good{ false }; // true if local content of the chunk matches the data on the opengl end + }; + + typedef std::vector chunkrecord_sequence; + +// methods: + void + bind_buffer(); + void + delete_buffer(); + +// members: + static GLuint m_activebuffer; // buffer bound currently on the opengl end, if any + GLuint m_buffer{ NULL }; // id of the buffer holding data on the opengl end + std::size_t m_buffercapacity{ 0 }; // total capacity of the last established buffer + chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order + +};