//--------------------------------------------------------------------------- /* MaSzyna EU07 locomotive simulator Copyright (C) 2001-2004 Marcin Wozniak and others */ //nagłówki identyczne w każdym pliku... #pragma hdrstop #include "Track.h" #include "Usefull.h" #include "Texture.h" #include "Timer.h" #include "Globals.h" #include "Ground.h" #include "parser.h" #include "Mover.h" #include "DynObj.h" #include "AnimModel.h" #include "MemCell.h" #include "Event.h" #pragma package(smart_init) //101206 Ra: trapezoidalne drogi i tory //110720 Ra: rozprucie zwrotnicy i odcinki izolowane static const double fMaxOffset=0.1; //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 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 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" TIsolated *TIsolated::pRoot=NULL; __fastcall TSwitchExtension::TSwitchExtension(TTrack *owner,int what) {//na początku wszystko puste CurrentIndex=0; pNexts[0]=NULL; //wskaźniki do kolejnych odcinków ruchu pNexts[1]=NULL; pPrevs[0]=NULL; pPrevs[1]=NULL; fOffsetSpeed=0.1; //prędkość liniowa iglic fOffsetDelay=0.05; //dodatkowy ruch drugiej iglicy po zablokowaniu pierwszej na opornicy fOffset1=fOffset=fDesiredOffset=-fOffsetDelay; //położenie zasadnicze fOffset2=0.0; //w zasadniczym wewnętrzna iglica dolega pOwner=NULL; pNextAnim=NULL; bMovement=false; //nie potrzeba przeliczać fOffset1 Segments[0]=new TSegment(owner); //z punktu 1 do 2 Segments[1]=new TSegment(owner); //z punktu 3 do 4 (1=3 dla zwrotnic; odwrócony dla skrzyżowań, ewentualnie 1=4) Segments[2]=(what>=3)?new TSegment(owner):NULL; //z punktu 2 do 4 skrzyżowanie od góry: wersja "-1": Segments[3]=(what>=4)?new TSegment(owner):NULL; //z punktu 4 do 1 1 1=4 0 0=3 Segments[4]=(what>=5)?new TSegment(owner):NULL; //z punktu 1 do 3 4 x 3 3 3 x 2 2 Segments[5]=(what>=6)?new TSegment(owner):NULL; //z punktu 3 do 2 2 2 1 1 evPlus=evMinus=NULL; fVelocity=-1.0; //maksymalne ograniczenie prędkości (ustawianej eventem) vTrans=vector3(0,0,0); //docelowa translacja przesuwnicy } __fastcall TSwitchExtension::~TSwitchExtension() {//nie ma nic do usuwania //delete Segments[0]; //delete Segments[1]; delete Segments[2]; delete Segments[3]; delete Segments[4]; delete Segments[5]; } __fastcall TIsolated::TIsolated() {//utworznie pustego TIsolated("none",NULL); }; __fastcall TIsolated::TIsolated(const AnsiString &n,TIsolated *i) {//utworznie obwodu izolowanego asName=n; pNext=i; iAxles=0; evBusy=evFree=NULL; pMemCell=NULL; //podpiąć istniejącą albo utworzyć pustą }; __fastcall TIsolated::~TIsolated() {//usuwanie /* TIsolated *p=pRoot; while (pRoot) { p=pRoot; p->pNext=NULL; delete p; } */ }; TIsolated* __fastcall TIsolated::Find(const AnsiString &n) {//znalezienie obiektu albo utworzenie nowego TIsolated *p=pRoot; while (p) {//jeśli się znajdzie, to podać wskaźnik if (p->asName==n) return p; p=p->pNext; } pRoot=new TIsolated(n,pRoot); return pRoot; }; void __fastcall TIsolated::Modify(int i,TDynamicObject *o) {//dodanie lub odjęcie osi if (iAxles) {//grupa zajęta iAxles+=i; if (!iAxles) {//jeśli po zmianie nie ma żadnej osi na odcinku izolowanym 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 if (pMemCell) //w powiązanej komórce pMemCell->UpdateValues(NULL,0,int(pMemCell->Value2())&~0xFF,update_memval2); //"zerujemy" ostatnią wartość } } else {//grupa była wolna iAxles+=i; if (iAxles) { 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 if (pMemCell) //w powiązanej komórce pMemCell->UpdateValues(NULL,0,int(pMemCell->Value2())|1,update_memval2); //zmieniamy ostatnią wartość na nieparzystą } } }; __fastcall TTrack::TTrack(TGroundNode *g) {//tworzenie nowego odcinka ruchu trNext=trPrev=NULL; //sąsiednie Segment=NULL; //dane odcinka SwitchExtension=NULL; //dodatkowe parametry zwrotnicy i obrotnicy TextureID1=0; //tekstura szyny fTexLength=4.0; //powtarzanie tekstury TextureID2=0; //tekstura podsypki albo drugiego toru zwrotnicy fTexHeight1=0.6; //nowy profil podsypki ;) fTexWidth=0.9; fTexSlope=0.9; eType=tt_Normal; //domyślnie zwykły iCategoryFlag=1; //1-tor, 2-droga, 4-rzeka, 8-samolot? fTrackWidth=1.435; //rozstaw toru, szerokość nawierzchni fFriction=0.15; //współczynnik tarcia fSoundDistance=-1; iQualityFlag=20; iDamageFlag=0; eEnvironment=e_flat; bVisible=true; iEvents=0; //Ra: flaga informująca o obecności eventów evEvent0=NULL; evEvent1=NULL; evEvent2=NULL; evEventall0=NULL; evEventall1=NULL; evEventall2=NULL; fVelocity=-1; //ograniczenie prędkości fTrackLength=100.0; fRadius=0; //promień wybranego toru zwrotnicy fRadiusTable[0]=0; //dwa promienie nawet dla prostego fRadiusTable[1]=0; iNumDynamics=0; ScannedFlag=false; DisplayListID=0; iTrapezoid=0; //parametry kształtu: 0-standard, 1-przechyłka, 2-trapez, 3-oba hvOverhead=NULL; //drut zasilający, najbliższy Point1 toru fTexRatio1=1.0; //proporcja boków tekstury nawierzchni (żeby zaoszczędzić na rozmiarach tekstur...) fTexRatio2=1.0; //proporcja boków tekstury chodnika (żeby zaoszczędzić na rozmiarach tekstur...) iPrevDirection=0; //domyślnie wirtualne odcinki dołączamy stroną od Point1 iNextDirection=0; pIsolated=NULL; pMyNode=g; //Ra: proteza, żeby tor znał swoją nazwę TODO: odziedziczyć TTrack z TGroundNode iAction=0; //normalnie może być pomijany podczas skanowania fOverhead=-1.0; //można normalnie pobierać prąd (0 dla jazdy bezprądowej po danym odcinku nFouling[0]=NULL; //ukres albo kozioł od strony Point1 nFouling[1]=NULL; //ukres albo kozioł od strony Point2 trColides=NULL; //tor kolizyjny, na którym trzeba sprawdzać pojazdy pod kątem zderzenia } __fastcall TTrack::~TTrack() {//likwidacja odcinka 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 __fastcall TTrack::Init() {//tworzenie pomocniczych danych switch (eType) { case tt_Switch: SwitchExtension=new TSwitchExtension(this,2); //na wprost i na bok break; case tt_Cross: //tylko dla skrzyżowania dróg SwitchExtension=new TSwitchExtension(this,6); //6 połączeń SwitchExtension->vPoints=NULL; //brak tablicy punktów SwitchExtension->iPoints=0; SwitchExtension->bPoints=false; //tablica punktów nie wypełniona SwitchExtension->iRoads=4; //domyślnie 4 break; case tt_Normal: Segment=new TSegment(this); break; case tt_Table: //oba potrzebne SwitchExtension=new TSwitchExtension(this,1); //kopia oryginalnego toru Segment=new TSegment(this); break; } } TTrack* __fastcall 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->iCategoryFlag=what; //taki sam typ plus informacja, że dodatkowy trk->Init(); //utworzenie segmentu trk->Segment->Init(vector3(-dx,0,0),vector3(-dx,0,400),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 r->Release(); //usunięcie skompilowanych zasobów return trk; }; TTrack* __fastcall 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 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->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; 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()); trk->Segment->Init(p1,p2,5,-RadToDeg(r1),70.0); //bo prosty, kontrolne wyliczane przy zmiennej przechyłce 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 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->bVisible=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 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 trk->Segment->Init(p1,p1+cv1,p2+vector3(-cv1.z,cv1.y,cv1.x),p2,2,-RadToDeg(r1),0.0); //bo prosty, kontrolne wyliczane przy zmiennej przechyłce ConnectPrevPrev(trk,0); trk2->Segment->Init(p1,p1+cv1,p2+vector3(cv1.z,cv1.y,-cv1.x),p2,2,-RadToDeg(r1),0.0); //bo prosty, kontrolne wyliczane przy zmiennej przechyłce 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; trk->Segment->Init(p1,p1+cv1,p2+vector3(-cv1.z,cv1.y,cv1.x),p2,2,RadToDeg(r2),0.0); //bo prosty, kontrolne wyliczane przy zmiennej przechyłce ConnectNextPrev(trk,0); trk2->Segment->Init(p1,p1+cv1,p2+vector3(cv1.z,cv1.y,-cv1.x),p2,2,RadToDeg(r2),0.0); //bo prosty, kontrolne wyliczane przy zmiennej przechyłce 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); r->NodeAdd(tmp); //dodanie toru do segmentu if (tmp2) r->NodeAdd(tmp2); //drugiego też r->Sort(); //żeby wyświetlał tabor z dodanego toru r->Release(); //usunięcie skompilowanych zasobów return trk; }; void __fastcall TTrack::ConnectPrevPrev(TTrack *pTrack,int typ) {//łączenie torów - Point1 własny do Point1 cudzego if (pTrack) {//(pTrack) może być zwrotnicą, a (this) tylko zwykłym odcinkiem trPrev=pTrack; iPrevDirection=((pTrack->eType==tt_Switch)?0:(typ&2)); pTrack->trPrev=this; pTrack->iPrevDirection=0; } } void __fastcall TTrack::ConnectPrevNext(TTrack *pTrack,int typ) {//łaczenie torów - Point1 własny do Point2 cudzego if (pTrack) { trPrev=pTrack; iPrevDirection=typ|1; //1:zwykły lub pierwszy zwrotnicy, 3:drugi zwrotnicy pTrack->trNext=this; pTrack->iNextDirection=0; if (bVisible) if (pTrack->bVisible) if (eType==tt_Normal) //jeśli łączone są dwa normalne if (pTrack->eType==tt_Normal) if ((fTrackWidth!=pTrack->fTrackWidth) //Ra: jeśli kolejny ma inne wymiary || (fTexHeight1!=pTrack->fTexHeight1) || (fTexWidth!=pTrack->fTexWidth) || (fTexSlope!=pTrack->fTexSlope)) pTrack->iTrapezoid|=2; //to rysujemy potworka } } void __fastcall TTrack::ConnectNextPrev(TTrack *pTrack,int typ) {//łaczenie torów - Point2 własny do Point1 cudzego if (pTrack) { trNext=pTrack; iNextDirection=((pTrack->eType==tt_Switch)?0:(typ&2)); pTrack->trPrev=this; pTrack->iPrevDirection=1; if (bVisible) if (pTrack->bVisible) if (eType==tt_Normal) //jeśli łączone są dwa normalne if (pTrack->eType==tt_Normal) if ((fTrackWidth!=pTrack->fTrackWidth) //Ra: jeśli kolejny ma inne wymiary || (fTexHeight1!=pTrack->fTexHeight1) || (fTexWidth!=pTrack->fTexWidth) || (fTexSlope!=pTrack->fTexSlope)) iTrapezoid|=2; //to rysujemy potworka } } void __fastcall TTrack::ConnectNextNext(TTrack *pTrack,int typ) {//łaczenie torów - Point2 własny do Point2 cudzego if (pTrack) { trNext=pTrack; iNextDirection=typ|1; //1:zwykły lub pierwszy zwrotnicy, 3:drugi zwrotnicy pTrack->trNext=this; pTrack->iNextDirection=1; } } vector3 __fastcall 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 __fastcall 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 __fastcall TTrack::Load(cParser *parser,vector3 pOrigin,AnsiString name) {//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,d1,d2,a; AnsiString str; bool bCurve; int i;//,state; //Ra: teraz już nie ma początkowego stanu zwrotnicy we wpisie std::string token; parser->getTokens(); *parser >> token; str=AnsiString(token.c_str()); //typ toru if (str=="normal") { eType=tt_Normal; iCategoryFlag=1; } else if (str=="switch") { eType=tt_Switch; iCategoryFlag=1; } else if (str=="turn") {//Ra: to jest obrotnica eType=tt_Table; iCategoryFlag=1; } else if (str=="table") {//Ra: obrotnica, przesuwnica albo wywrotnica eType=tt_Table; iCategoryFlag=1; } else if (str=="road") { eType=tt_Normal; iCategoryFlag=2; } else if (str=="cross") {//Ra: to będzie skrzyżowanie dróg eType=tt_Cross; iCategoryFlag=2; } else if (str=="river") {eType=tt_Normal; iCategoryFlag=4; } else if (str=="tributary") {eType=tt_Tributary; iCategoryFlag=4; } else eType=tt_Unknown; if (Global::iWriteLogEnabled&4) WriteLog(str.c_str()); parser->getTokens(4); *parser >> fTrackLength >> fTrackWidth >> fFriction >> fSoundDistance; // fTrackLength=Parser->GetNextSymbol().ToDouble(); //track length 100502 // fTrackWidth=Parser->GetNextSymbol().ToDouble(); //track width // fFriction=Parser->GetNextSymbol().ToDouble(); //friction coeff. // fSoundDistance=Parser->GetNextSymbol().ToDouble(); //snd fTrackWidth2=fTrackWidth; //rozstaw/szerokość w punkcie 2, na razie taka sama parser->getTokens(2); *parser >> iQualityFlag >> iDamageFlag; // iQualityFlag=Parser->GetNextSymbol().ToInt(); //McZapkie: qualityflag // iDamageFlag=Parser->GetNextSymbol().ToInt(); //damage if (iDamageFlag&128) iAction|=0x80; //flaga wykolejania z powodu uszkodzenia parser->getTokens(); *parser >> token; str=AnsiString(token.c_str()); //environment if (str=="flat") eEnvironment=e_flat; else if (str=="mountains" || str=="mountain") eEnvironment=e_mountains; else if (str=="canyon") eEnvironment=e_canyon; else if (str=="tunnel") eEnvironment=e_tunnel; else if (str=="bridge") eEnvironment=e_bridge; else if (str=="bank") eEnvironment=e_bank; else { eEnvironment=e_unknown; Error("Unknown track environment: \""+str+"\""); } parser->getTokens(); *parser >> token; bVisible=(token.compare( "vis" )==0); //visible if (bVisible) { parser->getTokens(); *parser >> token; str=AnsiString(token.c_str()); //railtex TextureID1=(str=="none"?0:TTexturesManager::GetTextureID(szTexturePath,szSceneryPath,str.c_str(),(iCategoryFlag&1)?Global::iRailProFiltering:Global::iBallastFiltering)); parser->getTokens(); *parser >> fTexLength; //tex tile length if (fTexLength<0.01) fTexLength=4; //Ra: zabezpiecznie przed zawieszeniem parser->getTokens(); *parser >> token; str=AnsiString(token.c_str()); //sub || railtex TextureID2=(str=="none"?0:TTexturesManager::GetTextureID(szTexturePath,szSceneryPath,str.c_str(),(eType==tt_Normal)?Global::iBallastFiltering:Global::iRailProFiltering)); parser->getTokens(3); *parser >> fTexHeight1 >> fTexWidth >> fTexSlope; // fTexHeight=Parser->GetNextSymbol().ToDouble(); //tex sub height // fTexWidth=Parser->GetNextSymbol().ToDouble(); //tex sub width // fTexSlope=Parser->GetNextSymbol().ToDouble(); //tex sub slope width if (iCategoryFlag&4) fTexHeight1=-fTexHeight1; //rzeki mają wysokość odwrotnie niż drogi } else if (Global::iWriteLogEnabled&4) WriteLog("unvis"); Init(); //ustawia SwitchExtension double segsize=5.0; //długość odcinka segmentowania switch (eType) {//Ra: łuki segmentowane co 5m albo 314-kątem foremnym case tt_Table: //obrotnica jest prawie jak zwykły tor iAction|=2; //flaga zmiany położenia typu obrotnica case tt_Normal: p1=LoadPoint(parser)+pOrigin; //pobranie współrzędnych P1 parser->getTokens(); *parser >> r1; //pobranie przechyłki w P1 cp1=LoadPoint(parser); //pobranie współrzędnych punktów kontrolnych cp2=LoadPoint(parser); p2=LoadPoint(parser)+pOrigin; //pobranie współrzędnych P2 parser->getTokens(2); *parser >> r2 >> fRadius; //pobranie przechyłki w P1 i promienia fRadius=fabs(fRadius); //we wpisie może być ujemny if (iCategoryFlag&1) {//zero na główce szyny p1.y+=0.18; p2.y+=0.18; //na przechyłce doliczyć jeszcze pół przechyłki } if (fRadius!=0) //gdy podany promień segsize=Min0R(5.0,0.2+fabs(fRadius)*0.02); //do 250m - 5, potem 1 co 50m if ((((p1+p1+p2)/3.0-p1-cp1).Length()<0.02)||(((p1+p2+p2)/3.0-p2+cp1).Length()<0.02)) cp1=cp2=vector3(0,0,0); //"prostowanie" prostych z kontrolnymi, dokładność 2cm if ((cp1==vector3(0,0,0)) && (cp2==vector3(0,0,0))) //Ra: hm, czasem dla prostego są podane... Segment->Init(p1,p2,segsize,r1,r2); //gdy prosty, kontrolne wyliczane przy zmiennej przechyłce else Segment->Init(p1,cp1+p1,cp2+p2,p2,segsize,r1,r2); //gdy łuk (ustawia bCurve=true) if ((r1!=0)||(r2!=0)) iTrapezoid=1; //są przechyłki do uwzględniania w rysowaniu if (eType==tt_Table) //obrotnica ma doklejkę {//SwitchExtension=new TSwitchExtension(this,1); //dodatkowe zmienne dla obrotnicy SwitchExtension->Segments[0]->Init(p1,p2,segsize); //kopia oryginalnego toru } else if (iCategoryFlag&2) if (TextureID1&&fTexLength) {//dla drogi trzeba ustalić proporcje boków nawierzchni float w,h; glBindTexture(GL_TEXTURE_2D,TextureID1); glGetTexLevelParameterfv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&w); glGetTexLevelParameterfv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&h); if (h!=0.0) fTexRatio1=w/h; //proporcja boków glBindTexture(GL_TEXTURE_2D,TextureID2); glGetTexLevelParameterfv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&w); glGetTexLevelParameterfv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&h); if (h!=0.0) fTexRatio2=w/h; //proporcja boków } break; case tt_Cross: //skrzyżowanie dróg - 4 punkty z wektorami kontrolnymi segsize=1.0; //specjalne segmentowanie ze względu na małe promienie case tt_Tributary: //dopływ case tt_Switch: //zwrotnica iAction|=1; //flaga zmiany położenia typu zwrotnica lub skrzyżowanie dróg //problemy z animacją iglic powstaje, gdzy odcinek prosty ma zmienną przechyłkę //wtedy dzieli się na dodatkowe odcinki (po 0.2m, bo R=0) i animację diabli biorą //Ra: na razie nie podejmuję się przerabiania iglic //SwitchExtension=new TSwitchExtension(this,eType==tt_Cross?6:2); //zwrotnica ma doklejkę p1=LoadPoint(parser)+pOrigin; //pobranie współrzędnych P1 parser->getTokens(); *parser >> r1; cp1=LoadPoint(parser); cp2=LoadPoint(parser); p2=LoadPoint(parser)+pOrigin; //pobranie współrzędnych P2 parser->getTokens(2); *parser >> r2 >> fRadiusTable[0]; fRadiusTable[0]=fabs(fRadiusTable[0]); //we wpisie może być ujemny if (iCategoryFlag&1) {//zero na główce szyny p1.y+=0.18; p2.y+=0.18; //na przechyłce doliczyć jeszcze pół przechyłki? } if (fRadiusTable[0]>0) segsize=Min0R(5.0,0.2+fRadiusTable[0]*0.02); else if (eType!=tt_Cross) //dla skrzyżowań muszą być podane kontrolne {//jak promień zerowy, to przeliczamy punkty kontrolne cp1=(p1+p1+p2)/3.0-p1; //jak jest prosty, to się zoptymalizuje cp2=(p1+p2+p2)/3.0-p2; segsize=5.0; } //ułomny prosty if (!(cp1==vector3(0,0,0)) && !(cp2==vector3(0,0,0))) SwitchExtension->Segments[0]->Init(p1,p1+cp1,p2+cp2,p2,segsize,r1,r2); else SwitchExtension->Segments[0]->Init(p1,p2,segsize,r1,r2); p3=LoadPoint(parser)+pOrigin; //pobranie współrzędnych P3 parser->getTokens(); *parser >> r3; cp3=LoadPoint(parser); cp4=LoadPoint(parser); p4=LoadPoint(parser)+pOrigin; //pobranie współrzędnych P4 parser->getTokens(2); *parser >> r4 >> fRadiusTable[1]; fRadiusTable[1]=fabs(fRadiusTable[1]); //we wpisie może być ujemny if (iCategoryFlag&1) {//zero na główce szyny p3.y+=0.18; p4.y+=0.18; //na przechyłce doliczyć jeszcze pół przechyłki? } if (fRadiusTable[1]>0) segsize=Min0R(5.0,0.2+fRadiusTable[1]*0.02); else if (eType!=tt_Cross) //dla skrzyżowań muszą być podane kontrolne {//jak promień zerowy, to przeliczamy punkty kontrolne cp3=(p3+p3+p4)/3.0-p3; //jak jest prosty, to się zoptymalizuje cp4=(p3+p4+p4)/3.0-p4; segsize=5.0; } //ułomny prosty if (!(cp3==vector3(0,0,0)) && !(cp4==vector3(0,0,0))) {//dla skrzyżowania dróg dać odwrotnie końce, żeby brzegi generować lewym if (eType!=tt_Cross) SwitchExtension->Segments[1]->Init(p3,p3+cp3,p4+cp4,p4,segsize,r3,r4); else SwitchExtension->Segments[1]->Init(p4,p4+cp4,p3+cp3,p3,segsize,r4,r3); //odwrócony } else SwitchExtension->Segments[1]->Init(p3,p4,segsize,r3,r4); if (eType==tt_Cross) {//Ra 2014-07: dla skrzyżowań będą dodatkowe segmenty SwitchExtension->Segments[2]->Init(p2,cp2+p2,cp4+p4,p4,segsize,r2,r4); //z punktu 2 do 4 if (LengthSquared3(p3-p1)<0.01) //gdy mniej niż 10cm, to mamy skrzyżowanie trzech dróg SwitchExtension->iRoads=3; else //dla 4 dróg będą dodatkowe 3 segmenty {SwitchExtension->Segments[3]->Init(p4,p4+cp4,p1+cp1,p1,segsize,r4,r1); //z punktu 4 do 1 SwitchExtension->Segments[4]->Init(p1,p1+cp1,p3+cp3,p3,segsize,r1,r3); //z punktu 1 do 3 SwitchExtension->Segments[5]->Init(p3,p3+cp3,p2+cp2,p2,segsize,r3,r2); //z punktu 3 do 2 } } Switch(0); //na stałe w położeniu 0 - nie ma początkowego stanu zwrotnicy we wpisie //Ra: zamienić później na iloczyn wektorowy {vector3 v1,v2; double a1,a2; v1=SwitchExtension->Segments[0]->FastGetPoint_1()-SwitchExtension->Segments[0]->FastGetPoint_0(); v2=SwitchExtension->Segments[1]->FastGetPoint_1()-SwitchExtension->Segments[1]->FastGetPoint_0(); a1=atan2(v1.x,v1.z); a2=atan2(v2.x,v2.z); a2=a2-a1; while (a2>M_PI) a2=a2-2*M_PI; while (a2<-M_PI) a2=a2+2*M_PI; SwitchExtension->RightSwitch=a2<0; //lustrzany układ OXY... } break; } parser->getTokens(); *parser >> token; str=AnsiString(token.c_str()); while (str!="endtrack") { if (str=="event0") { parser->getTokens(); *parser >> token; asEvent0Name=AnsiString(token.c_str()); } else if (str=="event1") { parser->getTokens(); *parser >> token; asEvent1Name=AnsiString(token.c_str()); } else if (str=="event2") { parser->getTokens(); *parser >> token; asEvent2Name=AnsiString(token.c_str()); } else if (str=="eventall0") { parser->getTokens(); *parser >> token; asEventall0Name=AnsiString(token.c_str()); } else if (str=="eventall1") { parser->getTokens(); *parser >> token; asEventall1Name=AnsiString(token.c_str()); } else if (str=="eventall2") { parser->getTokens(); *parser >> token; asEventall2Name=AnsiString(token.c_str()); } else if (str=="velocity") { parser->getTokens(); *parser >> fVelocity; //*0.28; McZapkie-010602 if (SwitchExtension) //jeśli tor ruchomy if (fabs(fVelocity)>=1.0) //żeby zero nie ograniczało dożywotnio SwitchExtension->fVelocity=fVelocity; //zapamiętanie głównego ograniczenia; a np. -40 ogranicza tylko na bok } else if (str=="isolated") {//obwód izolowany, do którego tor należy parser->getTokens(); *parser >> token; pIsolated=TIsolated::Find(AnsiString(token.c_str())); } else if (str=="angle1") {//kąt ścięcia końca od strony 1 parser->getTokens(); *parser >> a1; Segment->AngleSet(0,a1); } else if (str=="angle2") {//kąt ścięcia końca od strony 2 parser->getTokens(); *parser >> a2; Segment->AngleSet(1,a2); } else if (str=="fouling1") {//wskazanie modelu ukresu w kierunku 1 parser->getTokens(); *parser >> token; //nFouling[0]= } else if (str=="fouling2") {//wskazanie modelu ukresu w kierunku 2 parser->getTokens(); *parser >> token; //nFouling[1]= } else if (str=="overhead") {//informacja o stanie sieci: 0-jazda bezprądowa, >0-z opuszczonym i ograniczeniem prędkości parser->getTokens(); *parser >> fOverhead; if (fOverhead>0.0) iAction|=0x40; //flaga opuszczenia pantografu (tor uwzględniany w skanowaniu jako ograniczenie dla pantografujących) } else if (str=="colides") {//informacja o stanie sieci: 0-jazda bezprądowa, >0-z opuszczonym i ograniczeniem prędkości parser->getTokens(); *parser >> token; //trColides=; //tor kolizyjny, na którym trzeba sprawdzać pojazdy pod kątem zderzenia } else ErrorLog("Unknown property: \""+str+"\" in track \""+name+"\""); parser->getTokens(); *parser >> token; str=AnsiString(token.c_str()); } //alternatywny zapis nazwy odcinka izolowanego - po znaku "@" w nazwie toru if (!pIsolated) if ((i=name.Pos("@"))>0) if (ievPlus=NewEventPlus; if (NewEventMinus) SwitchExtension->evMinus=NewEventMinus; return true; } return false; }; AnsiString __fastcall TTrack::IsolatedName() {//podaje nazwę odcinka izolowanego, jesli nie ma on jeszcze przypisanych zdarzeń if (pIsolated) if (!pIsolated->evBusy&&!pIsolated->evFree) return pIsolated->asName; return ""; }; bool __fastcall TTrack::IsolatedEventsAssign(TEvent *busy, TEvent *free) {//ustawia zdarzenia dla odcinka izolowanego if (pIsolated) {if (busy) pIsolated->evBusy=busy; if (free) pIsolated->evFree=free; return true; } return false; }; //ABu: przeniesione z Track.h i poprawione!!! bool __fastcall TTrack::AddDynamicObject(TDynamicObject *Dynamic) {//dodanie pojazdu do trajektorii //Ra: tymczasowo wysyłanie informacji o zajętości konkretnego toru //Ra: usunąć po upowszechnieniu się odcinków izolowanych if (iCategoryFlag&0x100) //jeśli usuwaczek {Dynamic->MyTrack=NULL; //trzeba by to uzależnić od kierunku ruchu... return true; } if (Global::iMultiplayer) //jeśli multiplayer if (!iNumDynamics) //pierwszy zajmujący if (pMyNode->asName!="none") Global::pGround->WyslijString(pMyNode->asName,8); //przekazanie informacji o zajętości toru if (iNumDynamicsMyTrack=this; //ABu: na ktorym torze jesteśmy if (Dynamic->iOverheadMask) //jeśli ma pantografy Dynamic->OverheadTrack(fOverhead); //przekazanie informacji o jeździe bezprądowej na tym odcinku toru return true; } else { Error("Too many dynamics on track "+pMyNode->asName); return false; } }; void __fastcall TTrack::MoveMe(vector3 pPosition) {//to nie jest używane if (SwitchExtension) { SwitchExtension->Segments[0]->MoveMe(1*pPosition); SwitchExtension->Segments[1]->MoveMe(1*pPosition); SwitchExtension->Segments[2]->MoveMe(3*pPosition); //Ra: 3 razy? SwitchExtension->Segments[3]->MoveMe(4*pPosition); } else { Segment->MoveMe(pPosition); }; ResourceManager::Unregister(this); }; const int numPts=4; const int nnumPts=12; /* const vector6 szyna[nnumPts]= //szyna - vextor6(x,y,mapowanie tekstury,xn,yn,zn) {pierwotna szyna, opracował youBy, zmiany w celu uzyskania symetrii vector6( 0.111,-0.180,0.00, 1.000, 0.000,0.000), vector6( 0.045,-0.155,0.15, 0.707, 0.707,0.000), vector6( 0.045,-0.070,0.25, 0.707,-0.707,0.000), vector6( 0.071,-0.040,0.35, 0.707,-0.707,0.000), //albo tu 0.073 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.040,0.65,-0.707,-0.707,0.000), //albo tu -0.001 vector6( 0.027,-0.070,0.75,-0.707,-0.707,0.000), //albo zostanie asymetryczna vector6( 0.027,-0.155,0.85,-0.707, 0.707,0.000), vector6(-0.039,-0.180,1.00,-1.000, 0.000,0.000) }; */ 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) }; 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? }; void __fastcall TTrack::Compile(GLuint tex) {//generowanie treści dla Display Lists - model proceduralny if (!tex) {//jeśli nie podana tekstura, to każdy tor ma wlasne DL if (DisplayListID) Release(); //zwolnienie zasobów w celu ponownego utworzenia if (Global::bManageNodes) { DisplayListID=glGenLists(1); //otwarcie nowej listy glNewList(DisplayListID,GL_COMPILE); }; } glColor3f(1.0f,1.0f,1.0f); //to tutaj potrzebne? //Ra: nie zmieniamy oświetlenia przy kompilowaniu, ponieważ ono się zmienia w czasie! //trochę podliczonych zmiennych, co się potem przydadzą double fHTW=0.5*fabs(fTrackWidth); //połowa szerokości double side=fabs(fTexWidth); //szerokść podsypki na zewnątrz szyny albo pobocza double slop=fabs(fTexSlope); //szerokość pochylenia double rozp=fHTW+side+slop; //brzeg zewnętrzny double hypot1=hypot(slop,fTexHeight1); //rozmiar pochylenia do liczenia normalnych if (hypot1==0.0) hypot1=1.0; vector3 normal1=vector3(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*fabs(trNext->fTrackWidth); //połowa rozstawu/nawierzchni side2=fabs(trNext->fTexWidth); slop2=fabs(trNext->fTexSlope); rozp2=fHTW2+side2+slop2; //szerokość podstawy fTexHeight2=trNext->fTexHeight1; hypot2=hypot(slop2,fTexHeight2); if (hypot2==0.0) hypot2=1.0; normal2=vector3(trNext->fTexSlope/hypot2,fTexHeight2/hypot2,0.0); } else //gdy nie ma następnego albo jest nieodpowiednim końcem podpięty {fHTW2=fHTW; side2=side; slop2=slop; rozp2=rozp; fTexHeight2=fTexHeight1; hypot2=hypot1; normal2=normal1;} double roll1,roll2; switch (iCategoryFlag&15) { case 1: //tor { Segment->GetRolls(roll1,roll2); double sin1=sin(roll1),cos1=cos(roll1),sin2=sin(roll2),cos2=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); } if (iTrapezoid) //jak 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, animacja wykonywana w RaAnimate(), tutaj tylko regeneracja siatek case tt_Normal: if (TextureID2) if (tex?TextureID2==tex:true) //jeśli pasuje do grupy (tex) {//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 na profil 0.2 0.5 1.1 (również 6-9-9/noil) {//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 //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,normal1.x,-normal1.y,0.0); //lewy brzeg bpts1[1]=vector6((fHTW+side)*cos1, -(fHTW+side)*sin1-0.18, 0.33,0.0,1.0,0.0); //krawędź załamania bpts1[2]=vector6(-bpts1[1].x, +(fHTW+side)*sin1-0.18, 0.67,-normal1.x,-normal1.y,0.0); //prawy brzeg początku symetrycznie bpts1[3]=vector6(-rozp, -fTexHeight1-0.18, 1.00,-normal1.x,-normal1.y,0.0); //prawy skos //przekrój końcowy bpts1[4]=vector6(rozp2, -fTexHeight2-0.18, 0.00,normal2.x,-normal2.y,0.0); //lewy brzeg bpts1[5]=vector6((fHTW2+side2)*cos2,-(fHTW2+side2)*sin2-0.18,0.33,0.0,1.0,0.0); //krawędź załamania bpts1[6]=vector6(-bpts1[5].x, +(fHTW2+side2)*sin2-0.18,0.67,0.0,1.0,0.0); //prawy brzeg początku symetrycznie bpts1[7]=vector6(-rozp2, -fTexHeight2-0.18, 1.00,-normal2.x,-normal2.y,0.0); //prawy skos } else {bpts1[0]=vector6(rozp, -fTexHeight1-0.18,0.0,+normal1.x,-normal1.y,0.0); //lewy brzeg bpts1[1]=vector6(fHTW+side, -0.18,0.33, +normal1.x,-normal1.y,0.0); //krawędź załamania bpts1[2]=vector6(-fHTW-side,-0.18,0.67, -normal1.x,-normal1.y,0.0); //druga bpts1[3]=vector6(-rozp, -fTexHeight1-0.18,1.0,-normal1.x,-normal1.y,0.0); //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 //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 //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 } 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 } } if (!tex) glBindTexture(GL_TEXTURE_2D,TextureID2); Segment->RenderLoft(bpts1,iTrapezoid?-4:4,fTexLength); } if (TextureID1) if (tex?TextureID1==tex:true) //jeśli pasuje do grupy (tex) {// szyny if (!tex) glBindTexture(GL_TEXTURE_2D,TextureID1); Segment->RenderLoft(rpts1,iTrapezoid?-nnumPts:nnumPts,fTexLength); Segment->RenderLoft(rpts2,iTrapezoid?-nnumPts:nnumPts,fTexLength); } break; case tt_Switch: //dla zwrotnicy dwa razy szyny if (TextureID1) //zwrotnice nie są grupowane, aby prościej było je animować {//iglice liczone tylko dla zwrotnic //Ra: TODO: oddzielna animacja każdej iglicy, opór na docisku 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,+iglica[i].n.x*cos1+iglica[i].n.y*sin1,-iglica[i].n.x*sin1+iglica[i].n.y*cos1,0.0); 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,-iglica[i].n.x*cos1+iglica[i].n.y*sin1,+iglica[i].n.x*sin1+iglica[i].n.y*cos1,0.0); } if (iTrapezoid) //trapez albo przechyłki, to oddzielne punkty na końcu for (i=0;i<12;++i) {rpts3[12+i]=vector6(( fHTW2+iglica[i].x)*cos2+iglica[i].y*sin2,-( fHTW2+iglica[i].x)*sin2+iglica[i].y*cos2,iglica[i].z,+iglica[i].n.x*cos2+iglica[i].n.y*sin2,-iglica[i].n.x*sin2+iglica[i].n.y*cos2,0.0); rpts4[23-i]=vector6((-fHTW2-iglica[i].x)*cos2+iglica[i].y*sin2,-(-fHTW2-iglica[i].x)*sin2+iglica[i].y*cos2,iglica[i].z,-iglica[i].n.x*cos2+iglica[i].n.y*sin2,+iglica[i].n.x*sin2+iglica[i].n.y*cos2,0.0); } //McZapkie-130302 - poprawione rysowanie szyn if (SwitchExtension->RightSwitch) {//zwrotnica prawa glBindTexture(GL_TEXTURE_2D,TextureID1); SwitchExtension->Segments[0]->RenderLoft(rpts1,nnumPts,fTexLength,2); //prawa szyna za iglicą SwitchExtension->Segments[0]->RenderSwitchRail(rpts1,rpts3,nnumPts,fTexLength,2,SwitchExtension->fOffset2); //prawa iglica SwitchExtension->Segments[0]->RenderLoft(rpts2,nnumPts,fTexLength); //lewa szyna normalnie cała if (TextureID2!=TextureID1) //nie wiadomo, czy OpenGL to optymalizuje glBindTexture(GL_TEXTURE_2D,TextureID2); SwitchExtension->Segments[1]->RenderLoft(rpts1,nnumPts,fTexLength); //prawa szyna normalna cała SwitchExtension->Segments[1]->RenderLoft(rpts2,nnumPts,fTexLength,2); //lewa szyna za iglicą SwitchExtension->Segments[1]->RenderSwitchRail(rpts2,rpts4,nnumPts,fTexLength,2,-fMaxOffset+SwitchExtension->fOffset1); //lewa iglica } else {//lewa kiedyś działała lepiej niż prawa glBindTexture(GL_TEXTURE_2D,TextureID1); SwitchExtension->Segments[0]->RenderLoft(rpts1,nnumPts,fTexLength); //prawa szyna normalna cała SwitchExtension->Segments[0]->RenderLoft(rpts2,nnumPts,fTexLength,2); //lewa szyna za iglicą SwitchExtension->Segments[0]->RenderSwitchRail(rpts2,rpts4,nnumPts,fTexLength,2,-SwitchExtension->fOffset2); //lewa iglica if (TextureID2!=TextureID1) //nie wiadomo, czy OpenGL to optymalizuje glBindTexture(GL_TEXTURE_2D,TextureID2); SwitchExtension->Segments[1]->RenderLoft(rpts1,nnumPts,fTexLength,2); //prawa szyna za iglicą SwitchExtension->Segments[1]->RenderSwitchRail(rpts1,rpts3,nnumPts,fTexLength,2,fMaxOffset-SwitchExtension->fOffset1); //prawa iglica SwitchExtension->Segments[1]->RenderLoft(rpts2,nnumPts,fTexLength); //lewa szyna normalnie cała } } break; } } //koniec obsługi torów break; case 2: //McZapkie-260302 - droga - rendering //McZapkie:240702-zmieniony zakres widzialnosci switch (eType) //dalej zależnie od typu { case tt_Normal: //drogi proste, bo skrzyżowania osobno {vector6 bpts1[4]; //punkty głównej płaszczyzny przydają się do robienia boków if (TextureID1||TextureID2) //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 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 } 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); } } if (TextureID1) //jeśli podana była tekstura, generujemy trójkąty if (tex?TextureID1==tex:true) //jeśli pasuje do grupy (tex) {//tworzenie trójkątów nawierzchni szosy if (!tex) glBindTexture(GL_TEXTURE_2D,TextureID1); Segment->RenderLoft(bpts1,iTrapezoid?-2:2,fTexLength); } if (TextureID2) if (tex?TextureID2==tex:true) //jeśli pasuje do grupy (tex) {//pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) if (!tex) glBindTexture(GL_TEXTURE_2D,TextureID2); vector6 rpts1[6],rpts2[6]; //współrzędne przekroju i mapowania dla prawej i lewej strony 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 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 } } 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 if (iTrapezoid) //trapez albo przechyłki {//pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka slop2=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 } } if (iTrapezoid) //trapez albo przechyłki {//pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka if ((fTexHeight1>=0.0)?true:(slop!=0.0)) Segment->RenderLoft(rpts1,-3,fTexLength); //tylko jeśli jest z prawej if ((fTexHeight1>=0.0)?true:(side!=0.0)) Segment->RenderLoft(rpts2,-3,fTexLength); //tylko jeśli jest z lewej } else {//pobocza zwykłe, brak przechyłki if ((fTexHeight1>=0.0)?true:(slop!=0.0)) Segment->RenderLoft(rpts1,3,fTexLength); if ((fTexHeight1>=0.0)?true:(side!=0.0)) Segment->RenderLoft(rpts2,3,fTexLength); } } break; } case tt_Cross: //skrzyżowanie dróg rysujemy inaczej {//ustalenie współrzędnych środka - przecięcie Point1-Point2 z CV4-Point4 double a[4]; //kąty osi ulic wchodzących vector3 p[4]; //punkty się przydadzą do obliczeń //na razie połowa odległości pomiędzy Point1 i Point2, potem się dopracuje a[0]=a[1]=0.5; //parametr do poszukiwania przecięcia łuków //modyfikować a[0] i a[1] tak, aby trafić na przecięcie odcinka 34 p[0]=SwitchExtension->Segments[0]->FastGetPoint(a[0]); //współrzędne środka pierwszego odcinka p[1]=SwitchExtension->Segments[1]->FastGetPoint(a[1]); //-//- drugiego //p[2]=p[1]-p[0]; //jeśli różne od zera, przeliczyć a[0] i a[1] i wyznaczyć nowe punkty vector3 oxz=p[0]; //punkt mapowania środka tekstury skrzyżowania p[0]=SwitchExtension->Segments[0]->GetDirection1(); //Point1 - pobranie wektorów kontrolnych p[1]=SwitchExtension->Segments[1]->GetDirection2(); //Point3 (bo zamienione) p[2]=SwitchExtension->Segments[0]->GetDirection2(); //Point2 p[3]=SwitchExtension->Segments[1]->GetDirection1(); //Point4 (bo zamienione) a[0]=atan2(-p[0].x,p[0].z); // kąty stycznych osi dróg a[1]=atan2(-p[1].x,p[1].z); a[2]=atan2(-p[2].x,p[2].z); a[3]=atan2(-p[3].x,p[3].z); p[0]=SwitchExtension->Segments[0]->FastGetPoint_0(); //Point1 - pobranie współrzędnych końców p[1]=SwitchExtension->Segments[1]->FastGetPoint_1(); //Point3 p[2]=SwitchExtension->Segments[0]->FastGetPoint_1(); //Point2 p[3]=SwitchExtension->Segments[1]->FastGetPoint_0(); //Point4 - przy trzech drogach pokrywa się z Point1 //2014-07: na początek rysować brzegi jak dla łuków //punkty brzegu nawierzchni uzyskujemy podczas renderowania boków (bez sensu, ale najszybciej było zrobić) int i,j; //ile punktów (może byc różna ilość punktów między drogami) if (!SwitchExtension->vPoints) {//jeśli tablica punktów nie jest jeszcze utworzona, zliczamy punkty i tworzymy ją if (SwitchExtension->iRoads==3) //mogą być tylko 3 drogi zamiast 4 SwitchExtension->iPoints=5+SwitchExtension->Segments[0]->RaSegCount()+SwitchExtension->Segments[1]->RaSegCount()+SwitchExtension->Segments[2]->RaSegCount(); else SwitchExtension->iPoints=5+SwitchExtension->Segments[2]->RaSegCount()+SwitchExtension->Segments[3]->RaSegCount()+SwitchExtension->Segments[4]->RaSegCount()+SwitchExtension->Segments[5]->RaSegCount(); //mogą być tylko 3 drogi SwitchExtension->vPoints=new vector3[SwitchExtension->iPoints]; //tablica utworzona z zapasem, ale nie wypełniona współrzędnymi } vector3 *b=SwitchExtension->bPoints?NULL: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 if (TextureID1||TextureID2) //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 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 } } //najpierw renderowanie poboczy i zapamiętywanie punktów //problem ze skrzyżowaniami jest taki, że teren chce się pogrupować wg tekstur, ale zaczyna od nawierzchni //sama nawierzchnia nie wypełni tablicy punktów, bo potrzebne są pobocza //ale pobocza renderują się później, więc nawierzchnia nie załapuje się na renderowanie w swoim czasie //if (TextureID2) //if (tex?TextureID2==tex:true) //jeśli pasuje do grupy (tex) {//pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) if (TextureID2) if (!tex) glBindTexture(GL_TEXTURE_2D,TextureID2); vector6 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 //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 } } 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 //if (iTrapezoid) //trapez albo przechyłki {//pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka slop2=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 } } bool render=TextureID2?(tex?TextureID2==tex:true):false; //renderować nie trzeba, ale trzeba wyznaczyć punkty brzegowe nawierzchni //if (iTrapezoid) //trapez albo przechyłki if (SwitchExtension->iRoads==4) {//pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka if ((fTexHeight1>=0.0)?true:(side!=0.0)) SwitchExtension->Segments[2]->RenderLoft(rpts2,-3,fTexLength,0,1,&b,render); //tylko jeśli jest z lewej if ((fTexHeight1>=0.0)?true:(side!=0.0)) SwitchExtension->Segments[3]->RenderLoft(rpts2,-3,fTexLength,0,1,&b,render); //tylko jeśli jest z lewej if ((fTexHeight1>=0.0)?true:(side!=0.0)) SwitchExtension->Segments[4]->RenderLoft(rpts2,-3,fTexLength,0,1,&b,render); //tylko jeśli jest z lewej if ((fTexHeight1>=0.0)?true:(side!=0.0)) SwitchExtension->Segments[5]->RenderLoft(rpts2,-3,fTexLength,0,1,&b,render); //tylko jeśli jest z lewej } else //to będzie ewentualnie dla prostego na skrzyżowaniu trzech dróg {//punkt 3 pokrywa się z punktem 1, jak w zwrotnicy; połączenie 1->2 nie musi być prostoliniowe if ((fTexHeight1>=0.0)?true:(side!=0.0)) //OK SwitchExtension->Segments[2]->RenderLoft(rpts2,-3,fTexLength,0,1,&b,render); //z P2 do P4 if ((fTexHeight1>=0.0)?true:(side!=0.0)) //OK SwitchExtension->Segments[1]->RenderLoft(rpts2,-3,fTexLength,0,1,&b,render); //z P4 do P3=P1 (odwrócony) if ((fTexHeight1>=0.0)?true:(side!=0.0)) //OK SwitchExtension->Segments[0]->RenderLoft(rpts2,-3,fTexLength,0,1,&b,render); //z P1 do P2 /* if ((fTexHeight1>=0.0)?true:(slop!=0.0)) Segment->RenderLoft(rpts1,3,fTexLength); if ((fTexHeight1>=0.0)?true:(side!=0.0)) Segment->RenderLoft(rpts2,3,fTexLength); */ } } //renderowanie nawierzchni na końcu double sina0=sin(a[0]),cosa0=cos(a[0]); double u,v; if (!SwitchExtension->bPoints) //jeśli tablica nie wypełniona if (b) //ale jest wskaźnik do tablicy - może nie być? {//coś się gubi w obliczeniach na wskaźnikach i=(int((void*)(b))-int((void*)(SwitchExtension->vPoints)))/sizeof(vector3); //ustalenie liczby punktów, bo mogło wyjść inaczej niż policzone z góry if (i>0) {//jeśli zostało to właśnie utworzone if (SwitchExtension->iPoints>i) //jeśli wyszło mniej niż było miejsca SwitchExtension->iPoints=i; //domknięcie wachlarza else --SwitchExtension->iPoints; //jak tutaj wejdzie, to błąd jest - zrobić miejsce na powtórzenie pierwszego punktu na końcu SwitchExtension->vPoints[SwitchExtension->iPoints++]=SwitchExtension->vPoints[0]; SwitchExtension->bPoints=true; //tablica punktów została wypełniona } } if (TextureID1) //jeśli podana tekstura nawierzchni if (tex?TextureID1==tex:true) //jeśli pasuje do grupy (tex) {if (!tex) glBindTexture(GL_TEXTURE_2D,TextureID1); glBegin(GL_TRIANGLE_FAN); //takie kółeczko będzie glNormal3f(0,1,0); glTexCoord2f(0.5,0.5); //środek tekstury na środku skrzyżowania glVertex3f(oxz.x,oxz.y,oxz.z); for (i=SwitchExtension->iPoints-1;i>=0;--i) //for (i=0;iiPoints;++i) {glNormal3f(0,1,0); u=(SwitchExtension->vPoints[i].x-oxz.x)/fTexLength; //mapowanie we współrzędnych scenerii v=(SwitchExtension->vPoints[i].z-oxz.z)/(fTexRatio1*fTexLength); glTexCoord2f(cosa0*u+sina0*v+0.5,-sina0*u+cosa0*v+0.5); glVertex3f(SwitchExtension->vPoints[i].x,SwitchExtension->vPoints[i].y,SwitchExtension->vPoints[i].z); } glEnd(); } break; } } break; case 4: //McZapkie-260302 - rzeka- rendering //Ra: rzeki na razie bez zmian, przechyłki na pewno nie mają //Ra: przemyśleć wyrównanie u góry trawą do czworoboku vector6 bpts1[numPts]={vector6( fHTW,0.0,0.0), vector6( fHTW,0.2,0.33), vector6(-fHTW,0.0,0.67),vector6(-fHTW,0.0,1.0 ) }; //Ra: dziwnie ten kształt wygląda if (TextureID1) if (tex?TextureID1==tex:true) //jeśli pasuje do grupy (tex) { if (!tex) glBindTexture(GL_TEXTURE_2D,TextureID1); Segment->RenderLoft(bpts1,numPts,fTexLength); } if (TextureID2) if (tex?TextureID2==tex:true) //jeśli pasuje do grupy (tex) {//brzegi rzeki prawie jak pobocze derogi, tylko inny znak ma wysokość //znak jest zmieniany przy wczytywaniu, więc tu musi byc minus fTexHeight vector6 rpts1[3]={ vector6(rozp,-fTexHeight1,0.0), vector6(fHTW+side,0.0,0.5), vector6(fHTW,0.0,1.0) }; vector6 rpts2[3]={ vector6(-fHTW,0.0,1.0), vector6(-fHTW-side,0.0,0.5), vector6(-rozp,-fTexHeight1,0.0) }; //Ra: po kiego 0.1? if (!tex) glBindTexture(GL_TEXTURE_2D,TextureID2); //brzeg rzeki Segment->RenderLoft(rpts1,3,fTexLength); Segment->RenderLoft(rpts2,3,fTexLength); } break; } if (!tex) if (Global::bManageNodes) glEndList(); }; void TTrack::Release() { if (DisplayListID) glDeleteLists(DisplayListID,1); DisplayListID=0; }; void __fastcall TTrack::Render() { if (bVisible) //Ra: tory są renderowane sektorami i nie ma sensu każdorazowo liczyć odległości { if (!DisplayListID) { Compile(); if (Global::bManageNodes) ResourceManager::Register(this); }; SetLastUsage(Timer::GetSimulationTime()); EnvironmentSet(); //oświetlenie nie może być skompilowane, bo może się zmieniać z czasem glCallList(DisplayListID); EnvironmentReset(); //ustawienie oświetlenia na zwykłe if (InMovement()) Release(); //zwrotnica w trakcie animacji do odrysowania }; //#ifdef _DEBUG #if 0 if (DebugModeFlag && ScannedFlag) //McZapkie-230702 //if (iNumDynamics) //będzie kreska na zajętym torze { vector3 pos1,pos2,pos3; glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glColor3ub(255,0,0); glBindTexture(GL_TEXTURE_2D,0); glBegin(GL_LINE_STRIP); pos1=Segment->FastGetPoint_0(); pos2=Segment->FastGetPoint(0.5); pos3=Segment->FastGetPoint_1(); glVertex3f(pos1.x,pos1.y,pos1.z); glVertex3f(pos2.x,pos2.y+10,pos2.z); glVertex3f(pos3.x,pos3.y,pos3.z); glEnd(); glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); ScannedFlag=false; } #endif //glLightfv(GL_LIGHT0,GL_AMBIENT,Global::ambientDayLight); //glLightfv(GL_LIGHT0,GL_DIFFUSE,Global::diffuseDayLight); //glLightfv(GL_LIGHT0,GL_SPECULAR,Global::specularDayLight); }; bool __fastcall TTrack::CheckDynamicObject(TDynamicObject *Dynamic) {//sprawdzenie, czy pojazd jest przypisany do toru for (int i=0;iasName!="none") Global::pGround->WyslijString(pMyNode->asName,9); //przekazanie informacji o zwolnieniu toru return true; } } Error("Cannot remove dynamic from track"); return false; } bool __fastcall TTrack::InMovement() {//tory animowane (zwrotnica, obrotnica) mają SwitchExtension if (SwitchExtension) {if (eType==tt_Switch) return SwitchExtension->bMovement; //ze zwrotnicą łatwiej if (eType==tt_Table) if (SwitchExtension->pModel) {if (!SwitchExtension->CurrentIndex) return false; //0=zablokowana się nie animuje //trzeba każdorazowo porównywać z kątem modelu TAnimContainer *ac=SwitchExtension->pModel?SwitchExtension->pModel->GetContainer(NULL):NULL; return ac?(ac->AngleGet()!=SwitchExtension->fOffset)||!(ac->TransGet()==SwitchExtension->vTrans):false; //return true; //jeśli jest taki obiekt } } return false; }; void __fastcall TTrack::RaAssign(TGroundNode *gn,TAnimContainer *ac) {//Ra: wiązanie toru z modelem obrotnicy //if (eType==tt_Table) SwitchExtension->pAnim=p; }; void __fastcall TTrack::RaAssign(TGroundNode *gn,TAnimModel *am,TEvent *done,TEvent *joined) {//Ra: wiązanie toru z modelem obrotnicy if (eType==tt_Table) {SwitchExtension->pModel=am; SwitchExtension->pMyNode=gn; 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) if (am) if (am->GetContainer(NULL)) //może nie być? am->GetContainer(NULL)->EventAssign(done); //zdarzenie zakończenia animacji } }; int __fastcall TTrack::RaArrayPrepare() {//przygotowanie tablic do skopiowania do VBO (zliczanie wierzchołków) if (bVisible) //o ile w ogóle widać switch (iCategoryFlag&15) { case 1: //tor if (eType==tt_Switch) //dla zwrotnicy tylko szyny return 48*((TextureID1?SwitchExtension->Segments[0]->RaSegCount():0)+(TextureID2?SwitchExtension->Segments[1]->RaSegCount():0)); else //dla toru podsypka plus szyny return (Segment->RaSegCount())*((TextureID1?48:0)+(TextureID2?8:0)); case 2: //droga if (eType==tt_Cross) //tylko dla skrzyżowania dróg {//specjalny sposób obliczania liczby wierzchołków w skrzyżowaniu //int n=0; //wierzchołki wewnętrzne do generowania nawierzchni //int b=0; //wierzchołki do generowania boków if (fTexHeight1>=0) //jeśli fTexHeight1<0, to są chodniki i może któregoś nie być {//normalne pobocze, na razie się składa z return (Segment->RaSegCount())*((TextureID1?4:0)+(TextureID2?12:0)); } else return (Segment->RaSegCount())*((TextureID1?4:0)+(TextureID2?(fTexWidth!=0.0?6:0)+(fTexSlope!=0.0?6:0):0)); } else //standardowo dla zwykłej drogi if (fTexHeight1>=0) //jeśli fTexHeight1<0, to są chodniki i może któregoś nie być return (Segment->RaSegCount())*((TextureID1?4:0)+(TextureID2?12:0)); //może nie być poziomego! else return (Segment->RaSegCount())*((TextureID1?4:0)+(TextureID2?(fTexWidth!=0.0?6:0)+(fTexSlope!=0.0?6:0):0)); case 4: //rzeki do przemyślenia return (Segment->RaSegCount())*((TextureID1?4:0)+(TextureID2?12:0)); } return 0; }; void __fastcall TTrack::RaArrayFill(CVertNormTex *Vert,const CVertNormTex *Start) {//wypełnianie tablic VBO //Ra: trzeba rozdzielić szyny od podsypki, aby móc grupować wg tekstur double fHTW=0.5*fabs(fTrackWidth); double side=fabs(fTexWidth); //szerokść podsypki na zewnątrz szyny albo pobocza double slop=fabs(fTexSlope); //brzeg zewnętrzny double rozp=fHTW+side+slop; //brzeg zewnętrzny double hypot1=hypot(slop,fTexHeight1); //rozmiar pochylenia do liczenia normalnych if (hypot1==0.0) hypot1=1.0; vector3 normal1=vector3(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*fabs(trNext->fTrackWidth); //połowa rozstawu/nawierzchni side2=fabs(trNext->fTexWidth); slop2=fabs(trNext->fTexSlope); //nie jest używane później rozp2=fHTW2+side2+slop2; fTexHeight2=trNext->fTexHeight1; hypot2=hypot(slop2,fTexHeight2); if (hypot2==0.0) hypot2=1.0; normal2=vector3(trNext->fTexSlope/hypot2,fTexHeight2/hypot2,0.0); } else //gdy nie ma następnego albo jest nieodpowiednim końcem podpięty {fHTW2=fHTW; side2=side; slop2=slop; rozp2=rozp; fTexHeight2=fTexHeight1; hypot2=hypot1; normal2=normal1;} double roll1,roll2; switch (iCategoryFlag&15) { case 1: //tor { if (Segment) Segment->GetRolls(roll1,roll2); else roll1=roll2=0.0; //dla zwrotnic double sin1=sin(roll1),cos1=cos(roll1),sin2=sin(roll2),cos2=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); } 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 SwitchExtension->iLeftVBO=Vert-Start; //indeks toru obrotnicy case tt_Normal: if (TextureID2) {//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 //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 //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 } 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 {//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 //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 //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 } 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 } } Segment->RaRenderLoft(Vert,bpts1,iTrapezoid?-4:4,fTexLength); } if (TextureID1) {// szyny - generujemy dwie, najwyżej rysować się będzie jedną Segment->RaRenderLoft(Vert,rpts1,iTrapezoid?-nnumPts:nnumPts,fTexLength); Segment->RaRenderLoft(Vert,rpts2,iTrapezoid?-nnumPts:nnumPts,fTexLength); } break; case tt_Switch: //dla zwrotnicy dwa razy szyny if (TextureID1) //Ra: !!!! tu jest do poprawienia {//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 (SwitchExtension->RightSwitch) {//nowa wersja z SPKS, ale odwrotnie lewa/prawa SwitchExtension->iLeftVBO=Vert-Start; //indeks lewej iglicy SwitchExtension->Segments[0]->RaRenderLoft(Vert,rpts3,-nnumPts,fTexLength,0,2,SwitchExtension->fOffset2); SwitchExtension->Segments[0]->RaRenderLoft(Vert,rpts1,nnumPts,fTexLength,2); SwitchExtension->Segments[0]->RaRenderLoft(Vert,rpts2,nnumPts,fTexLength); SwitchExtension->Segments[1]->RaRenderLoft(Vert,rpts1,nnumPts,fTexLength); SwitchExtension->iRightVBO=Vert-Start; //indeks prawej iglicy SwitchExtension->Segments[1]->RaRenderLoft(Vert,rpts4,-nnumPts,fTexLength,0,2,-fMaxOffset+SwitchExtension->fOffset1); SwitchExtension->Segments[1]->RaRenderLoft(Vert,rpts2,nnumPts,fTexLength,2); } else {//lewa działa lepiej niż prawa SwitchExtension->Segments[0]->RaRenderLoft(Vert,rpts1,nnumPts,fTexLength); //lewa szyna normalna cała SwitchExtension->iLeftVBO=Vert-Start; //indeks lewej iglicy SwitchExtension->Segments[0]->RaRenderLoft(Vert,rpts4,-nnumPts,fTexLength,0,2,-SwitchExtension->fOffset2); //prawa iglica SwitchExtension->Segments[0]->RaRenderLoft(Vert,rpts2,nnumPts,fTexLength,2); //prawa szyna za iglicą SwitchExtension->iRightVBO=Vert-Start; //indeks prawej iglicy SwitchExtension->Segments[1]->RaRenderLoft(Vert,rpts3,-nnumPts,fTexLength,0,2,fMaxOffset-SwitchExtension->fOffset1); //lewa iglica SwitchExtension->Segments[1]->RaRenderLoft(Vert,rpts1,nnumPts,fTexLength,2); //lewa szyna za iglicą SwitchExtension->Segments[1]->RaRenderLoft(Vert,rpts2,nnumPts,fTexLength); //prawa szyna normalnie cała } } break; } } //koniec obsługi torów break; case 2: //McZapkie-260302 - droga - rendering switch (eType) //dalej zależnie od typu { case tt_Normal: //drogi proste, bo skrzyżowania osobno {vector6 bpts1[4]; //punkty głównej płaszczyzny przydają się do robienia boków if (TextureID1||TextureID2) //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 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 } else {bpts1[0]=vector6( fHTW,0.0,0.5-map1); //zawsze standardowe mapowanie bpts1[1]=vector6(-fHTW,0.0,0.5+map1); } } if (TextureID1) //jeśli podana była tekstura, generujemy trójkąty {//tworzenie trójkątów nawierzchni szosy Segment->RaRenderLoft(Vert,bpts1,iTrapezoid?-2:2,fTexLength); } if (TextureID2) {//pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) vector6 rpts1[6],rpts2[6]; //współrzędne przekroju i mapowania dla prawej i lewej strony 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 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 Segment->RaRenderLoft(Vert,rpts1,-3,fTexLength); Segment->RaRenderLoft(Vert,rpts2,-3,fTexLength); } else {//pobocza zwykłe, brak przechyłki Segment->RaRenderLoft(Vert,rpts1,3,fTexLength); Segment->RaRenderLoft(Vert,rpts2,3,fTexLength); } } 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 double h1r=(slop>d)?-fTexHeight1:0; double 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 slop2=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 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 if (slop!=0.0) Segment->RaRenderLoft(Vert,rpts1,-3,fTexLength); if (side!=0.0) Segment->RaRenderLoft(Vert,rpts2,-3,fTexLength); } else {//pobocza zwykłe, brak przechyłki if (slop!=0.0) Segment->RaRenderLoft(Vert,rpts1,3,fTexLength); if (side!=0.0) Segment->RaRenderLoft(Vert,rpts2,3,fTexLength); } } } } } break; case 4: //Ra: rzeki na razie jak drogi, przechyłki na pewno nie mają switch (eType) //dalej zależnie od typu { case tt_Normal: //drogi proste, bo skrzyżowania osobno {vector6 bpts1[4]; //punkty głównej płaszczyzny przydają się do robienia boków if (TextureID1||TextureID2) //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 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 } else {bpts1[0]=vector6( fHTW,0.0,0.5-map1); //zawsze standardowe mapowanie bpts1[1]=vector6(-fHTW,0.0,0.5+map1); } } if (TextureID1) //jeśli podana była tekstura, generujemy trójkąty {//tworzenie trójkątów nawierzchni szosy Segment->RaRenderLoft(Vert,bpts1,iTrapezoid?-2:2,fTexLength); } if (TextureID2) {//pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) vector6 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 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 Segment->RaRenderLoft(Vert,rpts1,-3,fTexLength); Segment->RaRenderLoft(Vert,rpts2,-3,fTexLength); } else {//pobocza zwykłe, brak przechyłki Segment->RaRenderLoft(Vert,rpts1,3,fTexLength); Segment->RaRenderLoft(Vert,rpts2,3,fTexLength); } } } } break; } }; void __fastcall TTrack::RaRenderVBO(int iPtr) {//renderowanie z użyciem VBO //Ra 2014-07: trzeba wymienić GL_TRIANGLE_STRIP na GL_TRIANGLES i renderować trójkąty sektora dla kolejnych tekstur! EnvironmentSet(); int seg; int i; switch (iCategoryFlag&15) { case 1: //tor if (eType==tt_Switch) //dla zwrotnicy tylko szyny {if (TextureID1) if ((seg=SwitchExtension->Segments[0]->RaSegCount())>0) {glBindTexture(GL_TEXTURE_2D,TextureID1); //szyny + for (i=0;iSegments[1]->RaSegCount())>0) {glBindTexture(GL_TEXTURE_2D,TextureID2); //szyny - for (i=0;iRaSegCount())>0) {if (TextureID2) {glBindTexture(GL_TEXTURE_2D,TextureID2); //podsypka for (i=0;iRaSegCount())>0) {if (TextureID1) {glBindTexture(GL_TEXTURE_2D,TextureID1); //nawierzchnia for (i=0;i=0.0) {//normalna droga z poboczem for (i=0;iRaSegCount())>0) {if (TextureID1) {glBindTexture(GL_TEXTURE_2D,TextureID1); //nawierzchnia for (i=0;iRender(); //sam sprawdza, czy VBO; zmienia kontekst VBO! //EnvironmentReset(); }; void __fastcall TTrack::RenderDynAlpha() {//renderowanie przezroczystych fragmentów pojazdów if (!iNumDynamics) return; //po co kombinować, jeśli nie ma pojazdów? //EnvironmentSet(); //Ra: pojazdy sobie same teraz liczą cienie for (int i=0;iRenderAlpha(); //sam sprawdza, czy VBO; zmienia kontekst VBO! //EnvironmentReset(); }; void __fastcall TTrack::RenderDynSounds() {//odtwarzanie dźwięków pojazdów jest niezależne od ich wyświetlania for (int i=0;iRenderSounds(); }; //--------------------------------------------------------------------------- bool __fastcall TTrack::SetConnections(int i) {//przepisanie aktualnych połączeń toru do odpowiedniego segmentu if (SwitchExtension) { SwitchExtension->pNexts[i]=trNext; SwitchExtension->pPrevs[i]=trPrev; SwitchExtension->iNextDirection[i]=iNextDirection; SwitchExtension->iPrevDirection[i]=iPrevDirection; if (eType==tt_Switch) {//zwrotnica jest wyłącznie w punkcie 1, więc tor od strony Prev jest zawsze ten sam SwitchExtension->pPrevs[i^1]=trPrev; SwitchExtension->iPrevDirection[i^1]=iPrevDirection; } else if (eType==tt_Cross) if (SwitchExtension->iRoads==3) { } if (i) Switch(0); //po przypisaniu w punkcie 4 włączyć stan zasadniczy return true; } Error("Cannot set connections"); return false; } bool __fastcall TTrack::Switch(int i,double t,double 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 SwitchExtension->fOffsetSpeed=t; //prędkość łatwiej zgrać z animacją modelu if (d>=0.0) //dodatkowy ruch drugiej iglicy (zamknięcie nastawnicze) SwitchExtension->fOffsetDelay=d; i&=1; //ograniczenie błędów !!!! SwitchExtension->fDesiredOffset=i?fMaxOffset+SwitchExtension->fOffsetDelay:-SwitchExtension->fOffsetDelay; SwitchExtension->CurrentIndex=i; Segment=SwitchExtension->Segments[i]; //wybranie aktywnej drogi - potrzebne to? trNext=SwitchExtension->pNexts[i]; //przełączenie końców trPrev=SwitchExtension->pPrevs[i]; iNextDirection=SwitchExtension->iNextDirection[i]; iPrevDirection=SwitchExtension->iPrevDirection[i]; fRadius=fRadiusTable[i]; //McZapkie: wybor promienia toru if (SwitchExtension->fVelocity<=-2) //-1 oznacza maksymalną prędkość, a dalsze ujemne to ograniczenie na bok fVelocity=i?-SwitchExtension->fVelocity:-1; if (SwitchExtension->pOwner?SwitchExtension->pOwner->RaTrackAnimAdd(this):true) //jeśli nie dodane do animacji {//nie ma się co bawić SwitchExtension->fOffset=SwitchExtension->fDesiredOffset; RaAnimate(); //przeliczenie położenia iglic; czy zadziała na niewyświetlanym sektorze w VBO? } return true; } else if (eType==tt_Table) {//blokowanie (0, szukanie torów) lub odblokowanie (1, rozłączenie) obrotnicy if (i) {//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 trPrev->trNext=NULL; //rozłączamy od Point2 else trPrev->trPrev=NULL; //rozłączamy od Point1 if (trNext) //jeśli jest tor od Point2 obrotnicy if (iNextDirection) //0:dołączony Point1, 1:dołączony Point2 trNext->trNext=NULL; //rozłączamy od Point2 else trNext->trPrev=NULL; //rozłączamy od Point1 trNext=trPrev=NULL; //na końcu rozłączamy obrotnicę (wkaźniki do sąsiadów już niepotrzebne) fVelocity=0.0; //AI, nie ruszaj się! if (SwitchExtension->pOwner) SwitchExtension->pOwner->RaTrackAnimAdd(this); //dodanie do listy animacyjnej } 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 Global::pGround->TrackJoin(SwitchExtension->pMyNode); 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) } } SwitchExtension->CurrentIndex=i; //zapamiętanie stanu zablokowania return true; } else if (eType==tt_Cross) {//to jest przydatne tylko do łączenia odcinków i&=1; SwitchExtension->CurrentIndex=i; Segment=SwitchExtension->Segments[i]; //wybranie aktywnej drogi - potrzebne to? trNext=SwitchExtension->pNexts[i]; //przełączenie końców trPrev=SwitchExtension->pPrevs[i]; iNextDirection=SwitchExtension->iNextDirection[i]; iPrevDirection=SwitchExtension->iPrevDirection[i]; return true; } if (iCategoryFlag==1) iDamageFlag=(iDamageFlag&127)+128*(i&1); //przełączanie wykolejenia else Error("Cannot switch normal track"); return false; }; bool __fastcall TTrack::SwitchForced(int i,TDynamicObject *o) {//rozprucie rozjazdu if (SwitchExtension) if (eType==tt_Switch) {// if (i!=SwitchExtension->CurrentIndex) {switch (i) {case 0: if (SwitchExtension->evPlus) Global::AddToQuery(SwitchExtension->evPlus,o); //dodanie do kolejki break; case 1: if (SwitchExtension->evMinus) Global::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 } } else if (eType==tt_Cross) {//ustawienie wskaźnika na wskazany segment Segment=SwitchExtension->Segments[i]; } return true; }; int __fastcall TTrack::CrossSegment(int from,int into) {//ustawienie wskaźnika na segement w pożądanym kierunku (into) od strony (from) //zwraca kod segmentu, z kierunkiem jazdy jako znakiem ± int i=0; switch (into) {case 0: //stop //WriteLog("Crossing from P"+AnsiString(from+1)+" into stop on "+pMyNode->asName); break; case 1: //left //WriteLog("Crossing from P"+AnsiString(from+1)+" to left 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); i=(SwitchExtension->iRoads==4)?iPrawo4[from]:iPrawo3[from]; break; case 3: //stright //WriteLog("Crossing from P"+AnsiString(from+1)+" to straight on "+pMyNode->asName); i=(SwitchExtension->iRoads==4)?iProsto4[from]:iProsto3[from]; break; } if (i) {Segment=SwitchExtension->Segments[abs(i)-1]; //WriteLog("Selected segment: "+AnsiString(abs(i)-1)); } return i; }; void __fastcall TTrack::RaAnimListAdd(TTrack *t) {//dodanie toru do listy animacyjnej if (SwitchExtension) {if (t==this) return; //siebie nie dodajemy drugi raz do listy if (!t->SwitchExtension) return; //nie podlega animacji if (SwitchExtension->pNextAnim) {if (SwitchExtension->pNextAnim==t) return; //gdy już taki jest else SwitchExtension->pNextAnim->RaAnimListAdd(t); } else {SwitchExtension->pNextAnim=t; t->SwitchExtension->pNextAnim=NULL; //nowo dodawany nie może mieć ogona } } }; TTrack* __fastcall TTrack::RaAnimate() {//wykonanie rekurencyjne animacji, wywoływane przed wyświetleniem sektora //zwraca wskaźnik toru wymagającego dalszej animacji if (SwitchExtension->pNextAnim) SwitchExtension->pNextAnim=SwitchExtension->pNextAnim->RaAnimate(); bool m=true; //animacja trwa if (eType==tt_Switch) //dla zwrotnicy tylko szyny {double 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) 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) {//jak na pierwszy z torów if (SwitchExtension->fOffset<=SwitchExtension->fDesiredOffset) {SwitchExtension->fOffset=SwitchExtension->fDesiredOffset; m=false; //koniec animacji } } else {//jak na drugi z torów if (SwitchExtension->fOffset>=SwitchExtension->fDesiredOffset) {SwitchExtension->fOffset=SwitchExtension->fDesiredOffset; m=false; //koniec animacji } } if (Global::bUseVBO) {//dla OpenGL 1.4 odświeży się cały sektor, w późniejszych poprawiamy fragment if (Global::bOpenGL_1_5) //dla OpenGL 1.4 to się nie wykona poprawnie if (TextureID1) //Ra: !!!! tu jest do poprawienia {//iglice liczone tylko dla zwrotnic vector6 rpts3[24],rpts4[24]; 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: ... 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); } CVertNormTex Vert[2*2*12]; //na razie 2 segmenty CVertNormTex *v=Vert; //bo RaAnimate() modyfikuje wskaźnik glGetBufferSubData(GL_ARRAY_BUFFER,SwitchExtension->iLeftVBO*sizeof(CVertNormTex),2*2*12*sizeof(CVertNormTex),&Vert);//pobranie fragmentu bufora VBO if (SwitchExtension->RightSwitch) {//nowa wersja z SPKS, ale odwrotnie lewa/prawa SwitchExtension->Segments[0]->RaAnimate(v,rpts3,-nnumPts,fTexLength,0,2,SwitchExtension->fOffset2); glBufferSubData(GL_ARRAY_BUFFER,SwitchExtension->iLeftVBO*sizeof(CVertNormTex),2*2*12*sizeof(CVertNormTex),&Vert); //wysłanie fragmentu bufora VBO v=Vert; glGetBufferSubData(GL_ARRAY_BUFFER,SwitchExtension->iRightVBO*sizeof(CVertNormTex),2*2*12*sizeof(CVertNormTex),&Vert);//pobranie fragmentu bufora VBO SwitchExtension->Segments[1]->RaAnimate(v,rpts4,-nnumPts,fTexLength,0,2,-fMaxOffset+SwitchExtension->fOffset1); } else {//oryginalnie lewa działała lepiej niż prawa SwitchExtension->Segments[0]->RaAnimate(v,rpts4,-nnumPts,fTexLength,0,2,-SwitchExtension->fOffset2); //prawa iglica glBufferSubData(GL_ARRAY_BUFFER,SwitchExtension->iLeftVBO*sizeof(CVertNormTex),2*2*12*sizeof(CVertNormTex),&Vert);//wysłanie fragmentu bufora VBO v=Vert; glGetBufferSubData(GL_ARRAY_BUFFER,SwitchExtension->iRightVBO*sizeof(CVertNormTex),2*2*12*sizeof(CVertNormTex),&Vert); //pobranie fragmentu bufora VBO SwitchExtension->Segments[1]->RaAnimate(v,rpts3,-nnumPts,fTexLength,0,2,fMaxOffset-SwitchExtension->fOffset1); //lewa iglica } glBufferSubData(GL_ARRAY_BUFFER,SwitchExtension->iRightVBO*sizeof(CVertNormTex),2*2*12*sizeof(CVertNormTex),&Vert); //wysłanie fragmentu bufora VBO } } else //gdy Display List Release(); //niszczenie skompilowanej listy, aby się wygenerowała nowa } else if (eType==tt_Table) //dla obrotnicy - szyny i podsypka { if (SwitchExtension->pModel&&SwitchExtension->CurrentIndex) //0=zablokowana się nie animuje {//trzeba każdorazowo porównywać z kątem modelu //SwitchExtension->fOffset1=SwitchExtension->pAnim?SwitchExtension->pAnim->AngleGet():0.0; //pobranie kąta z modelu TAnimContainer *ac=SwitchExtension->pModel?SwitchExtension->pModel->GetContainer(NULL):NULL; //pobranie głównego submodelu //if (ac) ac->EventAssign(SwitchExtension->evMinus); //event zakończenia animacji, trochę bez sensu tutaj if (ac) if ((ac->AngleGet()!=SwitchExtension->fOffset)||!(ac->TransGet()==SwitchExtension->vTrans)) //czy przemieściło się od ostatniego sprawdzania {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)); SwitchExtension->vTrans=ac->TransGet(); vector3 middle=SwitchExtension->pMyNode->pCenter+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 for (int i=0;iMove(0.000001); //minimalny ruch, aby przeliczyć pozycję i kąty if (Global::bUseVBO) {//dla OpenGL 1.4 odświeży się cały sektor, w późniejszych poprawiamy fragment //aktualizacja pojazdów na torze if (Global::bOpenGL_1_5) //dla OpenGL 1.4 to się nie wykona poprawnie {int size=RaArrayPrepare(); //wielkość tabeli potrzebna dla tej obrotnicy CVertNormTex *Vert=new CVertNormTex[size]; //bufor roboczy //CVertNormTex *v=Vert; //zmieniane przez RaArrayFill(Vert,Vert-SwitchExtension->iLeftVBO); //iLeftVBO powinno zostać niezmienione glBufferSubData(GL_ARRAY_BUFFER,SwitchExtension->iLeftVBO*sizeof(CVertNormTex),size*sizeof(CVertNormTex),Vert); //wysłanie fragmentu bufora VBO } } else //gdy Display List Release(); //niszczenie skompilowanej listy, aby się wygenerowała nowa } //animacja trwa nadal } else m=false; //koniec animacji albo w ogóle nie połączone z modelem } return m?this:SwitchExtension->pNextAnim; //zwraca obiekt do dalszej animacji }; //--------------------------------------------------------------------------- void __fastcall TTrack::RadioStop() {//przekazanie pojazdom rozkazu zatrzymania for (int i=0;iRadioStop(); }; double __fastcall TTrack::WidthTotal() {//szerokość z poboczem if (iCategoryFlag&2) //jesli droga if (fTexHeight1>=0.0) //i ma boki zagięte w dół (chodnik jest w górę) return 2.0*fabs(fTexWidth)+0.5*fabs(fTrackWidth+fTrackWidth2); //dodajemy pobocze return 0.5*fabs(fTrackWidth+fTrackWidth2); //a tak tylko zwykła średnia szerokość }; bool __fastcall TTrack::IsGroupable() {//czy wyświetlanie toru może być zgrupwane z innymi if ((eType==tt_Switch)||(eType==tt_Table)) return false; //tory ruchome nie są grupowane if ((eEnvironment==e_canyon)||(eEnvironment==e_tunnel)) return false; //tory ze zmianą światła return true; }; bool __fastcall Equal(vector3 v1, vector3 *v2) {//sprawdzenie odległości punktów //Ra: powinno być do 100cm wzdłuż toru i ze 2cm w poprzek (na prostej może nie być długiego kawałka) //Ra: z automatycznie dodawanym stukiem, jeśli dziura jest większa niż 2mm. if (fabs(v1.x-v2->x)>0.02) return false; //sześcian zamiast kuli if (fabs(v1.z-v2->z)>0.02) return false; if (fabs(v1.y-v2->y)>0.02) return false; return true; //return (SquareMagnitude(v1-*v2)<0.00012); //0.011^2=0.00012 }; int __fastcall TTrack::TestPoint(vector3 *Point) {//sprawdzanie, czy tory można połączyć switch (eType) { case tt_Normal: //zwykły odcinek if (trPrev==NULL) if (Equal(Segment->FastGetPoint_0(),Point)) return 0; if (trNext==NULL) if (Equal(Segment->FastGetPoint_1(),Point)) return 1; break; case tt_Switch: //zwrotnica {int state=GetSwitchState(); //po co? //Ra: TODO: jak się zmieni na bezpośrednie odwołania do segmentow zwrotnicy, //to się wykoleja, ponieważ trNext zależy od przełożenia Switch(0); if (trPrev==NULL) //if (Equal(SwitchExtension->Segments[0]->FastGetPoint_0(),Point)) if (Equal(Segment->FastGetPoint_0(),Point)) { Switch(state); return 2; } if (trNext==NULL) //if (Equal(SwitchExtension->Segments[0]->FastGetPoint_1(),Point)) if (Equal(Segment->FastGetPoint_1(),Point)) { Switch(state); return 3; } Switch(1); //można by się pozbyć tego przełączania if (trPrev==NULL) //Ra: z tym chyba nie potrzeba łączyć //if (Equal(SwitchExtension->Segments[1]->FastGetPoint_0(),Point)) if (Equal(Segment->FastGetPoint_0(),Point)) { Switch(state);//Switch(0); return 4; } if (trNext==NULL) //TODO: to zależy od przełożenia zwrotnicy //if (Equal(SwitchExtension->Segments[1]->FastGetPoint_1(),Point)) if (Equal(Segment->FastGetPoint_1(),Point)) { Switch(state);//Switch(0); return 5; } Switch(state); } break; case tt_Cross: //skrzyżowanie dróg //if (trPrev==NULL) if (Equal(SwitchExtension->Segments[0]->FastGetPoint_0(),Point)) return 2; //if (trNext==NULL) if (Equal(SwitchExtension->Segments[0]->FastGetPoint_1(),Point)) return 3; //if (trPrev==NULL) if (Equal(SwitchExtension->Segments[1]->FastGetPoint_0(),Point)) return 4; //if (trNext==NULL) if (Equal(SwitchExtension->Segments[1]->FastGetPoint_1(),Point)) return 5; break; } return -1; }; void __fastcall TTrack::MovedUp1(double dh) {//poprawienie przechyłki wymaga wydłużenia podsypki fTexHeight1+=dh; }; AnsiString __fastcall TTrack::NameGet() {//ustalenie nazwy toru if (this) if (pMyNode) return pMyNode->asName; return "none"; }; void __fastcall TTrack::VelocitySet(float v) {//ustawienie prędkości z ograniczeniem do pierwotnej wartości (zapisanej w scenerii) if (SwitchExtension?SwitchExtension->fVelocity>=0.0:false) {//zwrotnica może mieć odgórne ograniczenie, nieprzeskakiwalne eventem if (v>SwitchExtension->fVelocity?true:v<0.0) return void(fVelocity=SwitchExtension->fVelocity); //maksymalnie tyle, ile było we wpisie } fVelocity=v; //nie ma ograniczenia }; float __fastcall TTrack::VelocityGet() {//pobranie dozwolonej prędkości podczas skanowania return ((iDamageFlag&128)?0.0f:fVelocity); //tor uszkodzony = prędkość zerowa }; void __fastcall TTrack::ConnectionsLog() {//wypisanie informacji o połączeniach int i; WriteLog("--> tt_Cross named "+pMyNode->asName); if (eType==tt_Cross) for (i=0;i<2;++i) { if (SwitchExtension->pPrevs[i]) WriteLog("Point "+AnsiString(i+i+1)+" -> track "+SwitchExtension->pPrevs[i]->pMyNode->asName+":"+AnsiString(int(SwitchExtension->iPrevDirection[i]))); if (SwitchExtension->pNexts[i]) WriteLog("Point "+AnsiString(i+i+2)+" -> track "+SwitchExtension->pNexts[i]->pMyNode->asName+":"+AnsiString(int(SwitchExtension->iNextDirection[i]))); } }; TTrack* __fastcall TTrack::Neightbour(int s,double &d) {//zwraca wskaźnik na sąsiedni tor, w kierunku określonym znakiem (s), odwraca (d) w razie niezgodności kierunku torów TTrack *t; //nie zmieniamy kierunku (d), jeśli nie ma toru dalej if (eType!=tt_Cross) {//jeszcze trzeba sprawdzić zgodność t=(s>0)?trNext:trPrev; if (t) //o ile jest na co przejść, zmieniamy znak kierunku na nowym torze if (t->eType==tt_Cross) {//jeśli wjazd na skrzyżowanie, trzeba ustalić segment, bo od tego zależy zmiana kierunku (d) //if (r) //gdy nie podano (r), to nie zmieniać (d) // if (s*t->CrossSegment(((s>0)?iNextDirection:iPrevDirection),r)<0) // d=-d; } else {if ((s>0)?iNextDirection:!iPrevDirection) d=-d; //następuje zmiana kierunku wózka albo kierunku skanowania //s=((s>0)?iNextDirection:iPrevDirection)?-1:1; //kierunek toru po zmianie } return (t); //zwrotnica ma odpowiednio ustawione (trNext) } switch ((SwitchExtension->iRoads==4)?iEnds4[s+6]:iEnds3[s+6]) //numer końca 0..3, -1 to błąd {//zjazd ze skrzyżowania case 0: if (SwitchExtension->pPrevs[0]) if ((s>0)==SwitchExtension->iPrevDirection[0]) d=-d; return SwitchExtension->pPrevs[0]; case 1: if (SwitchExtension->pNexts[0]) if ((s>0)==SwitchExtension->iNextDirection[0]) d=-d; return SwitchExtension->pNexts[0]; case 2: if (SwitchExtension->pPrevs[1]) if ((s>0)==SwitchExtension->iPrevDirection[1]) d=-d; return SwitchExtension->pPrevs[1]; case 3: if (SwitchExtension->pNexts[1]) if ((s>0)==SwitchExtension->iNextDirection[1]) d=-d; return SwitchExtension->pNexts[1]; } return NULL; };