Files
maszyna/Track.cpp
2015-04-03 13:34:06 +00:00

2619 lines
116 KiB
C++

//---------------------------------------------------------------------------
/*
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 (i<name.Length()) //nie może być puste
{pIsolated=TIsolated::Find(name.SubString(i+1,name.Length()));
name=name.SubString(1,i-1); //usunięcie z nazwy
}
}
bool __fastcall TTrack::AssignEvents(TEvent *NewEvent0,TEvent *NewEvent1,TEvent *NewEvent2)
{
bool bError=false;
if (!evEvent0)
{
if (NewEvent0)
{
evEvent0=NewEvent0;
asEvent0Name="";
iEvents|=1; //sumaryczna informacja o eventach
}
else
{
if (!asEvent0Name.IsEmpty())
{
ErrorLog(AnsiString("Bad track: Event0 \"")+asEvent0Name+AnsiString("\" does not exist"));
bError=true;
}
}
}
else
{
ErrorLog(AnsiString("Bad track: Event0 cannot be assigned to track, track already has one"));
bError=true;
}
if (!evEvent1)
{
if (NewEvent1)
{
evEvent1=NewEvent1;
asEvent1Name="";
iEvents|=2; //sumaryczna informacja o eventach
}
else if (!asEvent1Name.IsEmpty())
{//Ra: tylko w logu informacja
ErrorLog(AnsiString("Bad track: Event1 \"")+asEvent1Name+AnsiString("\" does not exist").c_str());
bError=true;
}
}
else
{
ErrorLog(AnsiString("Bad track: Event1 cannot be assigned to track, track already has one"));
bError=true;
}
if (!evEvent2)
{
if (NewEvent2)
{
evEvent2=NewEvent2;
asEvent2Name="";
iEvents|=4; //sumaryczna informacja o eventach
}
else if (!asEvent2Name.IsEmpty())
{//Ra: tylko w logu informacja
ErrorLog(AnsiString("Bad track: Event2 \"")+asEvent2Name+AnsiString("\" does not exist"));
bError=true;
}
}
else
{
ErrorLog(AnsiString("Bad track: Event2 cannot be assigned to track, track already has one"));
bError=true;
}
return !bError;
}
bool __fastcall TTrack::AssignallEvents(TEvent *NewEvent0,TEvent *NewEvent1,TEvent *NewEvent2)
{
bool bError=false;
if (!evEventall0)
{
if (NewEvent0)
{
evEventall0=NewEvent0;
asEventall0Name="";
iEvents|=8; //sumaryczna informacja o eventach
}
else
{
if (!asEvent0Name.IsEmpty())
{
Error(AnsiString("Eventall0 \"")+asEventall0Name+AnsiString("\" does not exist"));
bError=true;
}
}
}
else
{
Error(AnsiString("Eventall0 cannot be assigned to track, track already has one"));
bError=true;
}
if (!evEventall1)
{
if (NewEvent1)
{
evEventall1=NewEvent1;
asEventall1Name="";
iEvents|=16; //sumaryczna informacja o eventach
}
else
{
if (!asEvent0Name.IsEmpty())
{//Ra: tylko w logu informacja
WriteLog(AnsiString("Eventall1 \"")+asEventall1Name+AnsiString("\" does not exist"));
bError=true;
}
}
}
else
{
Error(AnsiString("Eventall1 cannot be assigned to track, track already has one"));
bError=true;
}
if (!evEventall2)
{
if (NewEvent2)
{
evEventall2=NewEvent2;
asEventall2Name="";
iEvents|=32; //sumaryczna informacja o eventach
}
else
{
if (!asEvent0Name.IsEmpty())
{//Ra: tylko w logu informacja
WriteLog(AnsiString("Eventall2 \"")+asEventall2Name+AnsiString("\" does not exist"));
bError=true;
}
}
}
else
{
Error(AnsiString("Eventall2 cannot be assigned to track, track already has one"));
bError=true;
}
return !bError;
}
bool __fastcall TTrack::AssignForcedEvents(TEvent *NewEventPlus, TEvent *NewEventMinus)
{//ustawienie eventów sygnalizacji rozprucia
if (SwitchExtension)
{if (NewEventPlus)
SwitchExtension->evPlus=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 (iNumDynamics<iMaxNumDynamics)
{//jeśli jest miejsce, dajemy na koniec
Dynamics[iNumDynamics++]=Dynamic;
Dynamic->MyTrack=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;i<SwitchExtension->iPoints;++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;i<iNumDynamics;i++)
if (Dynamic==Dynamics[i])
return true;
return false;
};
bool __fastcall TTrack::RemoveDynamicObject(TDynamicObject *Dynamic)
{//usunięcie pojazdu z listy przypisanych do toru
for (int i=0;i<iNumDynamics;i++)
{//sprawdzanie wszystkich po kolei
if (Dynamic==Dynamics[i])
{//znaleziony, przepisanie następnych, żeby dziur nie było
--iNumDynamics;
for (i;i<iNumDynamics;i++)
Dynamics[i]=Dynamics[i+1];
if (Global::iMultiplayer) //jeśli multiplayer
if (!iNumDynamics) //jeśli już nie ma żadnego
if (pMyNode->asName!="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;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+24*i,24);
iPtr+=24*seg; //pominięcie lewej szyny
for (i=0;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+24*i,24);
iPtr+=24*seg; //pominięcie prawej szyny
}
if (TextureID2)
if ((seg=SwitchExtension->Segments[1]->RaSegCount())>0)
{glBindTexture(GL_TEXTURE_2D,TextureID2); //szyny -
for (i=0;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+24*i,24);
iPtr+=24*seg; //pominięcie lewej szyny
for (i=0;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+24*i,24);
}
}
else //dla toru podsypka plus szyny
{if ((seg=Segment->RaSegCount())>0)
{if (TextureID2)
{glBindTexture(GL_TEXTURE_2D,TextureID2); //podsypka
for (i=0;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+8*i,8);
iPtr+=8*seg; //pominięcie podsypki
}
if (TextureID1)
{glBindTexture(GL_TEXTURE_2D,TextureID1); //szyny
for (i=0;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+24*i,24);
iPtr+=24*seg; //pominięcie lewej szyny
for (i=0;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+24*i,24);
}
}
}
break;
case 2: //droga
if ((seg=Segment->RaSegCount())>0)
{if (TextureID1)
{glBindTexture(GL_TEXTURE_2D,TextureID1); //nawierzchnia
for (i=0;i<seg;++i)
{glDrawArrays(GL_TRIANGLE_STRIP,iPtr,4); iPtr+=4;}
}
if (TextureID2)
{glBindTexture(GL_TEXTURE_2D,TextureID2); //pobocze
if (fTexHeight1>=0.0)
{//normalna droga z poboczem
for (i=0;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+6*i,6);
iPtr+=6*seg; //pominięcie lewego pobocza
for (i=0;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+6*i,6);
}
else
{//z chodnikami o różnych szerokociach
if (fTexWidth!=0.0)
{for (i=0;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+6*i,6);
iPtr+=6*seg; //pominięcie lewego pobocza
}
if (fTexSlope!=0.0)
for (i=0;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+6*i,6);
}
}
}
break;
case 4: //rzeki - jeszcze do przemyślenia
if ((seg=Segment->RaSegCount())>0)
{if (TextureID1)
{glBindTexture(GL_TEXTURE_2D,TextureID1); //nawierzchnia
for (i=0;i<seg;++i)
{glDrawArrays(GL_TRIANGLE_STRIP,iPtr,4); iPtr+=4;}
}
if (TextureID2)
{glBindTexture(GL_TEXTURE_2D,TextureID2); //pobocze
for (i=0;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+6*i,6);
iPtr+=6*seg; //pominięcie lewego pobocza
for (i=0;i<seg;++i)
glDrawArrays(GL_TRIANGLE_STRIP,iPtr+6*i,6);
}
}
break;
}
EnvironmentReset();
};
void __fastcall TTrack::EnvironmentSet()
{//ustawienie zmienionego światła
glColor3f(1.0f,1.0f,1.0f); //Ra: potrzebne to?
if (eEnvironment)
{//McZapkie-310702: zmiana oswietlenia w tunelu, wykopie
GLfloat ambientLight[4]= {0.5f,0.5f,0.5f,1.0f};
GLfloat diffuseLight[4]= {0.5f,0.5f,0.5f,1.0f};
GLfloat specularLight[4]={0.5f,0.5f,0.5f,1.0f};
switch (eEnvironment)
{//modyfikacje oświetlenia zależnie od środowiska
case e_canyon:
for (int li=0;li<3;li++)
{
//ambientLight[li]= Global::ambientDayLight[li]*0.8; //0.7
diffuseLight[li]= Global::diffuseDayLight[li]*0.4; //0.3
specularLight[li]=Global::specularDayLight[li]*0.5; //0.4
}
//glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);
glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);
glLightfv(GL_LIGHT0,GL_SPECULAR,specularLight);
break;
case e_tunnel:
for (int li=0;li<3;li++)
{
ambientLight[li]= Global::ambientDayLight[li]*0.2;
diffuseLight[li]= Global::diffuseDayLight[li]*0.1;
specularLight[li]=Global::specularDayLight[li]*0.2;
}
glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);
glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);
glLightfv(GL_LIGHT0,GL_SPECULAR,specularLight);
break;
}
}
};
void __fastcall TTrack::EnvironmentReset()
{//przywrócenie domyślnego światła
switch (eEnvironment)
{//przywrócenie globalnych ustawień światła, o ile było zmienione
case e_canyon: //wykop
case e_tunnel: //tunel
glLightfv(GL_LIGHT0,GL_AMBIENT,Global::ambientDayLight);
glLightfv(GL_LIGHT0,GL_DIFFUSE,Global::diffuseDayLight);
glLightfv(GL_LIGHT0,GL_SPECULAR,Global::specularDayLight);
}
};
void __fastcall TTrack::RenderDyn()
{//renderowanie nieprzezroczystych 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;i<iNumDynamics;i++)
Dynamics[i]->Render(); //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;i<iNumDynamics;i++)
Dynamics[i]->RenderAlpha(); //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;i<iNumDynamics;i++)
Dynamics[i]->RenderSounds();
};
//---------------------------------------------------------------------------
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;i<iNumDynamics;i++)
Dynamics[i]->Move(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;i<iNumDynamics;i++)
Dynamics[i]->RadioStop();
};
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;
};