//--------------------------------------------------------------------------- /* MaSzyna EU07 locomotive simulator Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others */ #include "system.hpp" #include "classes.hpp" #pragma hdrstop #include "DynObj.h" #include "Timer.h" #include "Usefull.h" //McZapkie-260202 #include "Globals.h" #include "Texture.h" #include "AirCoupler.h" #include "TractionPower.h" #include "Ground.h" //bo Global::pGround->bDynamicRemove #include "Event.h" #include "Driver.h" #include "Camera.h" //bo likwidujemy trzęsienie #include "Console.h" #include "Traction.h" #pragma package(smart_init) //Ra: taki zapis funkcjonuje lepiej, ale może nie jest optymalny #define vWorldFront vector3(0,0,1) #define vWorldUp vector3(0,1,0) #define vWorldLeft CrossProduct(vWorldUp,vWorldFront) //Ra: bo te poniżej to się powielały w każdym module odobno //vector3 vWorldFront=vector3(0,0,1); //vector3 vWorldUp=vector3(0,1,0); //vector3 vWorldLeft=CrossProduct(vWorldUp,vWorldFront); #define M_2PI 6.283185307179586476925286766559; const float maxrot=(M_PI/3.0); //60° //--------------------------------------------------------------------------- void __fastcall TAnimPant::AKP_4E() {//ustawienie wymiarów dla pantografu AKP-4E vPos=vector3(0,0,0); //przypisanie domyśnych współczynników do pantografów fLenL1=1.22; //1.176289 w modelach fLenU1=1.755; //1.724482197 w modelach fHoriz=0.535; //0.54555075 przesunięcie ślizgu w długości pojazdu względem osi obrotu dolnego ramienia fHeight=0.07; //wysokość ślizgu ponad oś obrotu fWidth=0.635; //połowa szerokości ślizgu, 0.635 dla AKP-1 i AKP-4E fAngleL0=DegToRad(2.8547285515689267247882521833308); fAngleL=fAngleL0; //początkowy kąt dolnego ramienia //fAngleU0=acos((1.22*cos(fAngleL)+0.535)/1.755); //górne ramię fAngleU0=acos((fLenL1*cos(fAngleL)+fHoriz)/fLenU1); //górne ramię fAngleU=fAngleU0; //początkowy kąt //PantWys=1.22*sin(fAngleL)+1.755*sin(fAngleU); //wysokość początkowa PantWys=fLenL1*sin(fAngleL)+fLenU1*sin(fAngleU)+fHeight; //wysokość początkowa PantTraction=PantWys; hvPowerWire=NULL; fWidthExtra=0.381; //(2.032m-1.027)/2 //poza obszarem roboczym jest aproksymacja łamaną o 5 odcinkach fHeightExtra[0]= 0.0; //+0.0762 fHeightExtra[1]=-0.01; //+0.1524 fHeightExtra[2]=-0.03; //+0.2286 fHeightExtra[3]=-0.07; //+0.3048 fHeightExtra[4]=-0.15; //+0.3810 }; //--------------------------------------------------------------------------- int __fastcall TAnim::TypeSet(int i,int fl) {//ustawienie typu animacji i zależnej od niego ilości animowanych submodeli fMaxDist=-1.0; //normalnie nie pokazywać switch (i) {//maska 0x000F: ile używa wskaźników na submodele (0 gdy jeden, wtedy bez tablicy) //maska 0x00F0: 0-osie,1-drzwi,2-obracane,3-zderzaki,4-wózki,5-pantografy,6-tłoki //maska 0xFF00: ile używa liczb float dla współczynników i stanu case 0: iFlags=0x000; break; //0-oś case 1: iFlags=0x010; break; //1-drzwi case 2: iFlags=0x020; fParam=fl?new float[fl]:NULL; iFlags+=fl<<8; break; //2-wahacz, dźwignia itp. case 3: iFlags=0x030; break; //3-zderzak case 4: iFlags=0x040; break; //4-wózek case 5: //5-pantograf - 5 submodeli iFlags=0x055; fParamPants=new TAnimPant(); fParamPants->AKP_4E(); break; case 6: iFlags=0x068; break; //6-tłok i rozrząd - 8 submodeli default: iFlags=0; } yUpdate=NULL; return iFlags&15; //ile wskaźników rezerwować dla danego typu animacji }; __fastcall TAnim::TAnim() {//potrzebne to w ogóle? iFlags=-1; //nieznany typ - destruktor nic nie usuwa }; __fastcall TAnim::~TAnim() {//usuwanie animacji switch (iFlags&0xF0) {//usuwanie struktur, zależnie ile zostało stworzonych case 0x20: //2-wahacz, dźwignia itp. delete fParam; break; case 0x50: //5-pantograf delete fParamPants; break; case 0x60: //6-tłok i rozrząd break; } }; void __fastcall TAnim::Parovoz() {//animowanie tłoka i rozrządu parowozu }; //--------------------------------------------------------------------------- TDynamicObject* __fastcall TDynamicObject::FirstFind(int &coupler_nr) {//szukanie skrajnego połączonego pojazdu w pociagu //od strony sprzegu (coupler_nr) obiektu (start) TDynamicObject* temp=this; for (int i=0;i<300;i++) //ograniczenie do 300 na wypadek zapętlenia składu { if (!temp) return NULL; //Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów if (temp->MoverParameters->Couplers[coupler_nr].CouplingFlag==0) return temp; //nic nie ma już dalej podłączone if (coupler_nr==0) {//jeżeli szukamy od sprzęgu 0 if (temp->PrevConnected) //jeśli mamy coś z przodu { if (temp->PrevConnectedNo==0) //jeśli pojazd od strony sprzęgu 0 jest odwrócony coupler_nr=1-coupler_nr; //to zmieniamy kierunek sprzęgu temp=temp->PrevConnected; //ten jest od strony 0 } else return temp; //jeśli jednak z przodu nic nie ma } else { if (temp->NextConnected) {if (temp->NextConnectedNo==1) //jeśli pojazd od strony sprzęgu 1 jest odwrócony coupler_nr=1-coupler_nr; //to zmieniamy kierunek sprzęgu temp=temp->NextConnected; //ten pojazd jest od strony 1 } else return temp; //jeśli jednak z tyłu nic nie ma } } return NULL; //to tylko po wyczerpaniu pętli }; //--------------------------------------------------------------------------- float __fastcall TDynamicObject::GetEPP() {//szukanie skrajnego połączonego pojazdu w pociagu //od strony sprzegu (coupler_nr) obiektu (start) TDynamicObject* temp=this; int coupler_nr=0; float eq=0,am=0; for (int i=0;i<300;i++) //ograniczenie do 300 na wypadek zapętlenia składu { if (!temp) break; //Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów eq+=temp->MoverParameters->PipePress*temp->MoverParameters->Dim.L; am+=temp->MoverParameters->Dim.L; if ((temp->MoverParameters->Couplers[coupler_nr].CouplingFlag&2)!=2) break; //nic nie ma już dalej podłączone if (coupler_nr==0) {//jeżeli szukamy od sprzęgu 0 if (temp->PrevConnected) //jeśli mamy coś z przodu { if (temp->PrevConnectedNo==0) //jeśli pojazd od strony sprzęgu 0 jest odwrócony coupler_nr=1-coupler_nr; //to zmieniamy kierunek sprzęgu temp=temp->PrevConnected; //ten jest od strony 0 } else break; //jeśli jednak z przodu nic nie ma } else { if (temp->NextConnected) {if (temp->NextConnectedNo==1) //jeśli pojazd od strony sprzęgu 1 jest odwrócony coupler_nr=1-coupler_nr; //to zmieniamy kierunek sprzęgu temp=temp->NextConnected; //ten pojazd jest od strony 1 } else break; //jeśli jednak z tyłu nic nie ma } } temp=this; coupler_nr=1; for (int i=0;i<300;i++) //ograniczenie do 300 na wypadek zapętlenia składu { if (!temp) break; //Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów eq+=temp->MoverParameters->PipePress*temp->MoverParameters->Dim.L; am+=temp->MoverParameters->Dim.L; if ((temp->MoverParameters->Couplers[coupler_nr].CouplingFlag&2)!=2) break; //nic nie ma już dalej podłączone if (coupler_nr==0) {//jeżeli szukamy od sprzęgu 0 if (temp->PrevConnected) //jeśli mamy coś z przodu { if (temp->PrevConnectedNo==0) //jeśli pojazd od strony sprzęgu 0 jest odwrócony coupler_nr=1-coupler_nr; //to zmieniamy kierunek sprzęgu temp=temp->PrevConnected; //ten jest od strony 0 } else break; //jeśli jednak z przodu nic nie ma } else { if (temp->NextConnected) {if (temp->NextConnectedNo==1) //jeśli pojazd od strony sprzęgu 1 jest odwrócony coupler_nr=1-coupler_nr; //to zmieniamy kierunek sprzęgu temp=temp->NextConnected; //ten pojazd jest od strony 1 } else break; //jeśli jednak z tyłu nic nie ma } } eq-=MoverParameters->PipePress*MoverParameters->Dim.L; am-=MoverParameters->Dim.L; return eq/am; }; //--------------------------------------------------------------------------- TDynamicObject* __fastcall TDynamicObject::GetFirstDynamic(int cpl_type) {//Szukanie skrajnego połączonego pojazdu w pociagu //od strony sprzegu (cpl_type) obiektu szukajacego //Ra: wystarczy jedna funkcja do szukania w obu kierunkach return FirstFind(cpl_type); //używa referencji }; /* TDynamicObject* __fastcall TDynamicObject::GetFirstCabDynamic(int cpl_type) {//ZiomalCl: szukanie skrajnego obiektu z kabiną TDynamicObject* temp=this; int coupler_nr=cpl_type; for (int i=0;i<300;i++) //ograniczenie do 300 na wypadek zapętlenia składu { if (!temp) return NULL; //Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów if (temp->MoverParameters->CabNo!=0&&temp->MoverParameters->SandCapacity!=0) return temp; //nic nie ma już dalej podłączone if (temp->MoverParameters->Couplers[coupler_nr].CouplingFlag==0) return NULL; if (coupler_nr==0) {//jeżeli szukamy od sprzęgu 0 if (temp->PrevConnectedNo==0) //jeśli pojazd od strony sprzęgu 0 jest odwrócony coupler_nr=1-coupler_nr; //to zmieniamy kierunek sprzęgu if (temp->PrevConnected) temp=temp->PrevConnected; //ten jest od strony 0 } else { if (temp->NextConnectedNo==1) //jeśli pojazd od strony sprzęgu 1 jest odwrócony coupler_nr=1-coupler_nr; //to zmieniamy kierunek sprzęgu if (temp->NextConnected) temp=temp->NextConnected; //ten pojazd jest od strony 1 } } return NULL; //to tylko po wyczerpaniu pętli }; */ void TDynamicObject::ABuSetModelShake(vector3 mShake) { modelShake=mShake; }; int __fastcall TDynamicObject::GetPneumatic(bool front, bool red) { int x,y,z; //1=prosty, 2=skośny if (red) { if (front) { x=btCPneumatic1.GetStatus(); y=btCPneumatic1r.GetStatus(); } else { x=btCPneumatic2.GetStatus(); y=btCPneumatic2r.GetStatus(); } } else if (front) { x=btPneumatic1.GetStatus(); y=btPneumatic1r.GetStatus(); } else { x=btPneumatic2.GetStatus(); y=btPneumatic2r.GetStatus(); } z=0; //brak węży? if ((x==1)&&(y==1)) z=3; //dwa proste if ((x==2)&&(y==0)) z=1; //lewy skośny, brak prawego if ((x==0)&&(y==2)) z=2; //brak lewego, prawy skośny return z; } void __fastcall TDynamicObject::SetPneumatic(bool front,bool red) { int x=0,ten,tamten; ten=GetPneumatic(front,red); //1=lewy skos,2=prawy skos,3=dwa proste if (front) if (PrevConnected) //pojazd od strony sprzęgu 0 tamten=PrevConnected->GetPneumatic((PrevConnectedNo==0?true:false),red); if (!front) if (NextConnected) //pojazd od strony sprzęgu 1 tamten=NextConnected->GetPneumatic((NextConnectedNo==0?true:false),red); if (ten==tamten) //jeśli układ jest symetryczny switch (ten) { case 1: x=2; break; //mamy lewy skos, dać lewe skosy case 2: x=3; break; //mamy prawy skos, dać prawe skosy case 3: //wszystkie cztery na prosto if (MoverParameters->Couplers[front?0:1].Render) x=1; else x=4; break; } else { if (ten==2) x=4; if (ten==1) x=1; if (ten==3) if (tamten==1) x=4; else x=1; } if (front) {if (red) cp1=x; else sp1=x;} //który pokazywać z przodu else {if (red) cp2=x; else sp2=x;} //który pokazywać z tyłu } void TDynamicObject::UpdateAxle(TAnim *pAnim) {//animacja osi pAnim->smAnimated->SetRotate(float3(1,0,0),*pAnim->dWheelAngle); }; void TDynamicObject::UpdateBoogie(TAnim *pAnim) {//animacja wózka pAnim->smAnimated->SetRotate(float3(1,0,0),*pAnim->dWheelAngle); }; void TDynamicObject::UpdateDoorTranslate(TAnim *pAnim) {//animacja drzwi - przesuw //WriteLog("Dla drzwi nr:", i); //WriteLog("Wspolczynnik", DoorSpeedFactor[i]); //Ra: te współczynniki są bez sensu, bo modyfikują wektor przesunięcia //w efekcie drzwi otwierane na zewnątrz będą odlatywac dowolnie daleko :) //ograniczyłem zakres ruchu funkcją max if (pAnim->smAnimated) { if (pAnim->iNumber&1) pAnim->smAnimated->SetTranslate(vector3(0,0,Max0R(dDoorMoveR*pAnim->fSpeed,dDoorMoveR))); else pAnim->smAnimated->SetTranslate(vector3(0,0,Max0R(dDoorMoveL*pAnim->fSpeed,dDoorMoveL))); } }; void TDynamicObject::UpdateDoorRotate(TAnim *pAnim) {//animacja drzwi - obrót if (pAnim->smAnimated) {//if (MoverParameters->DoorOpenMethod==2) //obrotowe albo dwójłomne (trzeba kombinowac submodelami i ShiftL=90,R=180) if (pAnim->iNumber&1) pAnim->smAnimated->SetRotate(float3(1,0,0),dDoorMoveR); else pAnim->smAnimated->SetRotate(float3(1,0,0),dDoorMoveL); } }; void TDynamicObject::UpdateDoorFold(TAnim *pAnim) {//animacja drzwi - obrót if (pAnim->smAnimated) {//if (MoverParameters->DoorOpenMethod==2) //obrotowe albo dwójłomne (trzeba kombinowac submodelami i ShiftL=90,R=180) if (pAnim->iNumber&1) {pAnim->smAnimated->SetRotate(float3(0,0,1),dDoorMoveR); TSubModel *sm=pAnim->smAnimated->ChildGet(); //skrzydło mniejsze if (sm) {sm->SetRotate(float3(0,0,1),-dDoorMoveR-dDoorMoveR); //skrzydło większe sm=sm->ChildGet(); if (sm) sm->SetRotate(float3(0,1,0),dDoorMoveR); //podnóżek? } } else {pAnim->smAnimated->SetRotate(float3(0,0,1),dDoorMoveL); //SubModel->SetRotate(float3(0,1,0),fValue*360.0); TSubModel *sm=pAnim->smAnimated->ChildGet(); //skrzydło mniejsze if (sm) {sm->SetRotate(float3(0,0,1),-dDoorMoveL-dDoorMoveL); //skrzydło większe sm=sm->ChildGet(); if (sm) sm->SetRotate(float3(0,1,0),dDoorMoveL); //podnóżek? } } } }; void TDynamicObject::UpdatePant(TAnim *pAnim) {//animacja pantografu - 4 obracane ramiona, ślizg piąty float a,b,c; a=RadToDeg(pAnim->fParamPants->fAngleL-pAnim->fParamPants->fAngleL0); b=RadToDeg(pAnim->fParamPants->fAngleU-pAnim->fParamPants->fAngleU0); c=a+b; if (pAnim->smElement[0]) pAnim->smElement[0]->SetRotate(float3(-1,0,0),a); //dolne ramię if (pAnim->smElement[1]) pAnim->smElement[1]->SetRotate(float3(1,0,0),a); if (pAnim->smElement[2]) pAnim->smElement[2]->SetRotate(float3(1,0,0),c); //górne ramię if (pAnim->smElement[3]) pAnim->smElement[3]->SetRotate(float3(-1,0,0),c); if (pAnim->smElement[4]) pAnim->smElement[4]->SetRotate(float3(-1,0,0),b); //ślizg }; void TDynamicObject::UpdateLeverDouble(TAnim *pAnim) {//animacja gałki zależna od double pAnim->smAnimated->SetRotate(float3(1,0,0),pAnim->fSpeed**pAnim->fDoubleBase); }; void TDynamicObject::UpdateLeverFloat(TAnim *pAnim) {//animacja gałki zależna od float pAnim->smAnimated->SetRotate(float3(1,0,0),pAnim->fSpeed**pAnim->fFloatBase); }; void TDynamicObject::UpdateLeverInt(TAnim *pAnim) {//animacja gałki zależna od int pAnim->smAnimated->SetRotate(float3(1,0,0),pAnim->fSpeed**pAnim->iIntBase); }; void TDynamicObject::UpdateLeverEnum(TAnim *pAnim) {//ustawienie kąta na wartość wskazaną przez int z tablicy fParam //pAnim->fParam[0]; - dodać lepkość pAnim->smAnimated->SetRotate(float3(1,0,0),pAnim->fParam[*pAnim->iIntBase]); }; //ABu 29.01.05 przeklejone z render i renderalpha: ********************* void __inline TDynamicObject::ABuLittleUpdate(double ObjSqrDist) {//ABu290105: pozbierane i uporzadkowane powtarzajace sie rzeczy z Render i RenderAlpha //dodatkowy warunek, if (ObjSqrDist<...) zeby niepotrzebnie nie zmianiec w obiektach, //ktorych i tak nie widac //NBMX wrzesien, MC listopad: zuniwersalnione btnOn=false; //czy przywrócić stan domyślny po renderowaniu if (mdLoad) //tymczasowo ładunek na poziom podłogi if (vFloor.z>0.0) mdLoad->GetSMRoot()->SetTranslate(modelShake+vFloor); if (ObjSqrDist<160000) //gdy bliżej niż 400m { for (int i=0;iGetSMRoot()->SetTranslate(modelShake); if (mdKabina) mdKabina->GetSMRoot()->SetTranslate(modelShake); if (mdLoad) mdLoad->GetSMRoot()->SetTranslate(modelShake+vFloor); if (mdLowPolyInt) mdLowPolyInt->GetSMRoot()->SetTranslate(modelShake); if (mdPrzedsionek) mdPrzedsionek->GetSMRoot()->SetTranslate(modelShake); //ABu: koniec rzucania //ABu011104: liczenie obrotow wozkow ABuBogies(); //Mczapkie-100402: rysowanie lub nie - sprzegow //ABu-240105: Dodatkowy warunek: if (...).Render, zeby rysowal tylko jeden //z polaczonych sprzegow if ((TestFlag(MoverParameters->Couplers[0].CouplingFlag,ctrain_coupler)) &&(MoverParameters->Couplers[0].Render)) {btCoupler1.TurnOn(); btnOn=true;} //else btCoupler1.TurnOff(); if ((TestFlag(MoverParameters->Couplers[1].CouplingFlag,ctrain_coupler)) &&(MoverParameters->Couplers[1].Render)) {btCoupler2.TurnOn(); btnOn=true;} //else btCoupler2.TurnOff(); //******************************************************************************** //przewody powietrzne j.w., ABu: decyzja czy rysowac tylko na podstawie 'render' - juz nie //przewody powietrzne, yB: decyzja na podstawie polaczen w t3d if (Global::bnewAirCouplers) { SetPneumatic(false,false); //wczytywanie z t3d ulozenia wezykow SetPneumatic(true,false); //i zapisywanie do zmiennej SetPneumatic(true,true); //ktore z nich nalezy SetPneumatic(false,true); //wyswietlic w tej klatce if (TestFlag(MoverParameters->Couplers[0].CouplingFlag,ctrain_pneumatic)) { switch (cp1) { case 1: btCPneumatic1.TurnOn(); break; case 2: btCPneumatic1.TurnxOn(); break; case 3: btCPneumatic1r.TurnxOn(); break; case 4: btCPneumatic1r.TurnOn(); break; } btnOn=true; } //else //{ // btCPneumatic1.TurnOff(); // btCPneumatic1r.TurnOff(); //} if (TestFlag(MoverParameters->Couplers[1].CouplingFlag,ctrain_pneumatic)) { switch (cp2) { case 1: btCPneumatic2.TurnOn(); break; case 2: btCPneumatic2.TurnxOn(); break; case 3: btCPneumatic2r.TurnxOn(); break; case 4: btCPneumatic2r.TurnOn(); break; } btnOn=true; } //else //{ // btCPneumatic2.TurnOff(); // btCPneumatic2r.TurnOff(); //} //przewody zasilajace, j.w. (yB) if (TestFlag(MoverParameters->Couplers[0].CouplingFlag,ctrain_scndpneumatic)) { switch (sp1) { case 1: btPneumatic1.TurnOn(); break; case 2: btPneumatic1.TurnxOn(); break; case 3: btPneumatic1r.TurnxOn(); break; case 4: btPneumatic1r.TurnOn(); break; } btnOn=true; } //else //{ // btPneumatic1.TurnOff(); // btPneumatic1r.TurnOff(); //} if (TestFlag(MoverParameters->Couplers[1].CouplingFlag,ctrain_scndpneumatic)) { switch (sp2) { case 1: btPneumatic2.TurnOn(); break; case 2: btPneumatic2.TurnxOn(); break; case 3: btPneumatic2r.TurnxOn(); break; case 4: btPneumatic2r.TurnOn(); break; } btnOn=true; } //else //{ // btPneumatic2.TurnOff(); // btPneumatic2r.TurnOff(); //} } //*********************************************************************************/ else //po staremu ABu'oewmu { //przewody powietrzne j.w., ABu: decyzja czy rysowac tylko na podstawie 'render' if (TestFlag(MoverParameters->Couplers[0].CouplingFlag,ctrain_pneumatic)) { if (MoverParameters->Couplers[0].Render) btCPneumatic1.TurnOn(); else btCPneumatic1r.TurnOn(); btnOn=true; } //else //{ // btCPneumatic1.TurnOff(); // btCPneumatic1r.TurnOff(); //} if (TestFlag(MoverParameters->Couplers[1].CouplingFlag,ctrain_pneumatic)) { if (MoverParameters->Couplers[1].Render) btCPneumatic2.TurnOn(); else btCPneumatic2r.TurnOn(); btnOn=true; } //else //{ // btCPneumatic2.TurnOff(); // btCPneumatic2r.TurnOff(); //} //przewody powietrzne j.w., ABu: decyzja czy rysowac tylko na podstawie 'render' //yB - zasilajace if (TestFlag(MoverParameters->Couplers[0].CouplingFlag,ctrain_scndpneumatic)) { if (MoverParameters->Couplers[0].Render) btPneumatic1.TurnOn(); else btPneumatic1r.TurnOn(); btnOn=true; } //else //{ // btPneumatic1.TurnOff(); // btPneumatic1r.TurnOff(); //} if (TestFlag(MoverParameters->Couplers[1].CouplingFlag,ctrain_scndpneumatic)) { if (MoverParameters->Couplers[1].Render) btPneumatic2.TurnOn(); else btPneumatic2r.TurnOn(); btnOn=true; } //else //{ // btPneumatic2.TurnOff(); // btPneumatic2r.TurnOff(); //} } //*************************************************************/// koniec wezykow // uginanie zderzakow for (int i=0; i<2; i++) { double dist=MoverParameters->Couplers[i].Dist/2.0; if (smBuforLewy[i]) if (dist<0) smBuforLewy[i]->SetTranslate(vector3(dist,0,0)); if (smBuforPrawy[i]) if (dist<0) smBuforPrawy[i]->SetTranslate(vector3(dist,0,0)); } } //Winger 160204 - podnoszenie pantografow //przewody sterowania ukrotnionego if (TestFlag(MoverParameters->Couplers[0].CouplingFlag,ctrain_controll)) {btCCtrl1.TurnOn(); btnOn=true;} //else btCCtrl1.TurnOff(); if (TestFlag(MoverParameters->Couplers[1].CouplingFlag,ctrain_controll)) {btCCtrl2.TurnOn(); btnOn=true;} //else btCCtrl2.TurnOff(); //McZapkie-181103: mostki przejsciowe if (TestFlag(MoverParameters->Couplers[0].CouplingFlag,ctrain_passenger)) {btCPass1.TurnOn(); btnOn=true;} //else btCPass1.TurnOff(); if (TestFlag(MoverParameters->Couplers[1].CouplingFlag,ctrain_passenger)) {btCPass2.TurnOn(); btnOn=true;} //else btCPass2.TurnOff(); if (MoverParameters->Battery) {//sygnaly konca pociagu if (btEndSignals1.Active()) { if (TestFlag(iLights[0],2) ||TestFlag(iLights[0],32)) {btEndSignals1.TurnOn(); btnOn=true;} //else btEndSignals1.TurnOff(); } else { if (TestFlag(iLights[0],2)) {btEndSignals11.TurnOn(); btnOn=true;} //else btEndSignals11.TurnOff(); if (TestFlag(iLights[0],32)) {btEndSignals13.TurnOn(); btnOn=true;} //else btEndSignals13.TurnOff(); } if (btEndSignals2.Active()) { if (TestFlag(iLights[1],2) ||TestFlag(iLights[1],32)) {btEndSignals2.TurnOn(); btnOn=true;} //else btEndSignals2.TurnOff(); } else { if (TestFlag(iLights[1],2)) {btEndSignals21.TurnOn(); btnOn=true;} //else btEndSignals21.TurnOff(); if (TestFlag(iLights[1],32)) {btEndSignals23.TurnOn(); btnOn=true;} //else btEndSignals23.TurnOff(); } } //tablice blaszane: if (TestFlag(iLights[0],64)) {btEndSignalsTab1.TurnOn(); btnOn=true;} //else btEndSignalsTab1.TurnOff(); if (TestFlag(iLights[1],64)) {btEndSignalsTab2.TurnOn(); btnOn=true;} //else btEndSignalsTab2.TurnOff(); //McZapkie-181002: krecenie wahaczem (korzysta z kata obrotu silnika) if (iAnimType[ANIM_LEVERS]) for (int i=0;i<4;++i) if (smWahacze[i]) smWahacze[i]->SetRotate(float3(1,0,0),fWahaczeAmp*cos(MoverParameters->eAngle)); if (Mechanik&&(Controller!=Humandriver)) {//rysowanie figurki mechanika if (smMechanik0) //mechanik od strony sprzęgu 0 if (smMechanik1) //jak jest drugi, to pierwszego jedynie pokazujemy smMechanik0->iVisible=MoverParameters->ActiveCab>0; else {//jak jest tylko jeden, to do drugiej kabiny go obracamy smMechanik0->iVisible=(MoverParameters->ActiveCab!=0); smMechanik0->SetRotate(float3(0,0,1),MoverParameters->ActiveCab>=0?0:180); //obrót względem osi Z } if (smMechanik1) //mechanik od strony sprzęgu 1 smMechanik1->iVisible=MoverParameters->ActiveCab<0; } //ABu: Przechyly na zakretach //Ra: przechyłkę załatwiamy na etapie przesuwania modelu //if (ObjSqrDist<80000) ABuModelRoll(); //przechyłki od 400m } if (MoverParameters->Battery) {//sygnały czoła pociagu //Ra: wyświetlamy bez ograniczeń odległości, by były widoczne z daleka if (TestFlag(iLights[0],1)) {btHeadSignals11.TurnOn(); btnOn=true;} //else btHeadSignals11.TurnOff(); if (TestFlag(iLights[0],4)) {btHeadSignals12.TurnOn(); btnOn=true;} //else btHeadSignals12.TurnOff(); if (TestFlag(iLights[0],16)) {btHeadSignals13.TurnOn(); btnOn=true;} //else btHeadSignals13.TurnOff(); if (TestFlag(iLights[1],1)) {btHeadSignals21.TurnOn(); btnOn=true;} //else btHeadSignals21.TurnOff(); if (TestFlag(iLights[1],4)) {btHeadSignals22.TurnOn(); btnOn=true;} //else btHeadSignals22.TurnOff(); if (TestFlag(iLights[1],16)) {btHeadSignals23.TurnOn(); btnOn=true;} //else btHeadSignals23.TurnOff(); } } //ABu 29.01.05 koniec przeklejenia ************************************* double __fastcall ABuAcos(const vector3 &calc_temp) { //Odpowiednik funkcji Arccos, bo cos mi tam nie dzialalo. return atan2(-calc_temp.x,calc_temp.z); //Ra: tak prościej } TDynamicObject* __fastcall TDynamicObject::ABuFindNearestObject(TTrack *Track,TDynamicObject *MyPointer,int &CouplNr) {//zwraca wskaznik do obiektu znajdujacego sie na torze (Track), którego sprzęg jest najblizszy kamerze //służy np. do łączenia i rozpinania sprzęgów //WE: Track - tor, na ktorym odbywa sie poszukiwanie // MyPointer - wskaznik do obiektu szukajacego //WY: CouplNr - który sprzęg znalezionego obiektu jest bliższy kamerze //Uwaga! Jesli CouplNr==-2 to szukamy njblizszego obiektu, a nie sprzegu!!! if ((Track->iNumDynamics)>0) {//o ile w ogóle jest co przeglądać na tym torze //vector3 poz; //pozycja pojazdu XYZ w scenerii //vector3 kon; //wektor czoła względem środka pojazdu wzglęem początku toru vector3 tmp; //wektor pomiędzy kamerą i sprzęgiem double dist; //odległość for (int i=0;iiNumDynamics;i++) { if (CouplNr==-2) {//wektor [kamera-obiekt] - poszukiwanie obiektu tmp=Global::GetCameraPosition()-Track->Dynamics[i]->vPosition; dist=tmp.x*tmp.x+tmp.y*tmp.y+tmp.z*tmp.z; //odległość do kwadratu if (dist<100.0) //10 metrów return Track->Dynamics[i]; } else //jeśli (CouplNr) inne niz -2, szukamy sprzęgu {//wektor [kamera-sprzeg0], potem [kamera-sprzeg1] //Powinno byc wyliczone, ale nie zaszkodzi drugi raz: //(bo co, jesli nie wykonuje sie obrotow wozkow?) - Ra: ale zawsze są liczone współrzędne sprzęgów //Track->Dynamics[i]->modelRot.z=ABuAcos(Track->Dynamics[i]->Axle0.pPosition-Track->Dynamics[i]->Axle1.pPosition); //poz=Track->Dynamics[i]->vPosition; //pozycja środka pojazdu //kon=vector3( //położenie przodu względem środka // -((0.5*Track->Dynamics[i]->MoverParameters->Dim.L)*sin(Track->Dynamics[i]->modelRot.z)), // 0, //yyy... jeśli duże pochylenie i długi pojazd, to może być problem // +((0.5*Track->Dynamics[i]->MoverParameters->Dim.L)*cos(Track->Dynamics[i]->modelRot.z)) //); tmp=Global::GetCameraPosition()-Track->Dynamics[i]->vCoulpler[0]; //Ra: pozycje sprzęgów też są zawsze liczone dist=tmp.x*tmp.x+tmp.y*tmp.y+tmp.z*tmp.z; //odległość do kwadratu if (dist<25.0) //5 metrów { CouplNr=0; return Track->Dynamics[i]; } tmp=Global::GetCameraPosition()-Track->Dynamics[i]->vCoulpler[1]; dist=tmp.x*tmp.x+tmp.y*tmp.y+tmp.z*tmp.z; //odległość do kwadratu if (dist<25.0) //5 metrów { CouplNr=1; return Track->Dynamics[i]; } } } return NULL; } return NULL; } TDynamicObject* __fastcall TDynamicObject::ABuScanNearestObject(TTrack *Track,double ScanDir,double ScanDist,int &CouplNr) {//skanowanie toru w poszukiwaniu obiektu najblizszego kamerze //double MyScanDir=ScanDir; //Moja orientacja na torze. //Ra: nie używane if (ABuGetDirection()<0) ScanDir=-ScanDir; TDynamicObject* FoundedObj; FoundedObj=ABuFindNearestObject(Track,this,CouplNr); //zwraca numer sprzęgu znalezionego pojazdu if (FoundedObj==NULL) { double ActDist; //Przeskanowana odleglosc. double CurrDist=0; //Aktualna dlugosc toru. if (ScanDir>=0) ActDist=Track->Length()-RaTranslationGet(); //???-przesunięcie wózka względem Point1 toru else ActDist=RaTranslationGet(); //przesunięcie wózka względem Point1 toru while (ActDist0) //do przodu { if (Track->iNextDirection) { Track=Track->CurrentNext(); ScanDir=-ScanDir; } else Track=Track->CurrentNext(); } else //do tyłu { if (Track->iPrevDirection) Track=Track->CurrentPrev(); else { Track=Track->CurrentPrev(); ScanDir=-ScanDir; } } if (Track!=NULL) { //jesli jest kolejny odcinek toru CurrDist=Track->Length(); FoundedObj=ABuFindNearestObject(Track, this, CouplNr); if (FoundedObj!=NULL) ActDist=ScanDist; } else //Jesli nie ma, to wychodzimy. ActDist=ScanDist; } } //Koniec szukania najblizszego toru z jakims obiektem. return FoundedObj; } //ABu 01.11.04 poczatek wyliczania przechylow pudla ********************** void __fastcall TDynamicObject::ABuModelRoll() {//ustawienie przechyłki pojazdu i jego zawartości // Ra: przechyłkę załatwiamy na etapie przesuwania modelu } //ABu 06.05.04 poczatek wyliczania obrotow wozkow ********************** void __fastcall TDynamicObject::ABuBogies() {//Obracanie wozkow na zakretach. Na razie uwzględnia tylko zakręty, //bez zadnych gorek i innych przeszkod. if ((smBogie[0]!=NULL)&&(smBogie[1]!=NULL)) { //modelRot.z=ABuAcos(Axle0.pPosition-Axle1.pPosition); //kąt obrotu pojazdu [rad] //bogieRot[0].z=ABuAcos(Axle0.pPosition-Axle3.pPosition); bogieRot[0].z=Axle0.vAngles.z; bogieRot[0]=RadToDeg(modelRot-bogieRot[0]); //mnożenie wektora przez stałą smBogie[0]->SetRotateXYZ(bogieRot[0]); //bogieRot[1].z=ABuAcos(Axle2.pPosition-Axle1.pPosition); bogieRot[1].z=Axle1.vAngles.z; bogieRot[1]=RadToDeg(modelRot-bogieRot[1]); smBogie[1]->SetRotateXYZ(bogieRot[1]); } }; //ABu 06.05.04 koniec wyliczania obrotow wozkow ************************ //ABu 16.03.03 sledzenie toru przed obiektem: ************************** void __fastcall TDynamicObject::ABuCheckMyTrack() {//Funkcja przypisujaca obiekt prawidlowej tablicy Dynamics, //bo gdzies jest jakis blad i wszystkie obiekty z danego //pociagu na poczatku stawiane sa na jednym torze i wpisywane //do jednej tablicy. Wykonuje sie tylko raz - po to 'ABuChecked' TTrack* OldTrack=MyTrack; TTrack* NewTrack=Axle0.GetTrack(); if ((NewTrack!=OldTrack)&&OldTrack) { OldTrack->RemoveDynamicObject(this); NewTrack->AddDynamicObject(this); } iAxleFirst=0; //pojazd powiązany z przednią osią - Axle0 } //Ra: w poniższej funkcji jest problem ze sprzęgami TDynamicObject* __fastcall TDynamicObject::ABuFindObject(TTrack *Track,int ScanDir,Byte &CouplFound,double &dist) {//Zwraca wskaźnik najbliższego obiektu znajdującego się //na torze w określonym kierunku, ale tylko wtedy, kiedy //obiekty mogą się zderzyć, tzn. nie mijają się. //WE: Track - tor, na ktorym odbywa sie poszukiwanie, // MyPointer - wskaznik do obiektu szukajacego. //Ra: zamieniłem na "this" // ScanDir - kierunek szukania na torze (+1:w stronę Point2, -1:w stronę Point1) // MyScanDir - kierunek szukania obiektu szukajacego (na jego torze); Ra: nie potrzebne // MyCouplFound - nr sprzegu obiektu szukajacego; Ra: nie potrzebne //WY: wskaznik do znalezionego obiektu. // CouplFound - nr sprzegu znalezionego obiektu if (Track->iNumDynamics>0) {//sens szukania na tym torze jest tylko, gdy są na nim pojazdy double ObjTranslation; //pozycja najblizszego obiektu na torze double MyTranslation; //pozycja szukającego na torze double MinDist=Track->Length(); //najmniejsza znaleziona odleglość (zaczynamy od długości toru) double TestDist; //robocza odległość od kolejnych pojazdów na danym odcinku int iMinDist=-1; //indeks wykrytego obiektu //if (Track->iNumDynamics>1) // iMinDist+=0; //tymczasowo pułapka if (MyTrack==Track) //gdy szukanie na tym samym torze MyTranslation=RaTranslationGet(); //położenie wózka względem Point1 toru else //gdy szukanie na innym torze if (ScanDir>0) MyTranslation=0; //szukanie w kierunku Point2 (od zera) - jesteśmy w Point1 else MyTranslation=MinDist; //szukanie w kierunku Point1 (do zera) - jesteśmy w Point2 if (ScanDir>=0) {//jeśli szukanie w kierunku Point2 for (int i=0;iiNumDynamics;i++) {//pętla po pojazdach if (Track->Dynamics[i]!=this) //szukający się nie liczy { TestDist=(Track->Dynamics[i]->RaTranslationGet())-MyTranslation; //odległogłość tamtego od szukającego if ((TestDist>0)&&(TestDist<=MinDist)) {//gdy jest po właściwej stronie i bliżej niż jakiś wcześniejszy CouplFound=(Track->Dynamics[i]->RaDirectionGet()>0)?1:0; //to, bo (ScanDir>=0) if (Track->iCategoryFlag&254) //trajektoria innego typu niż tor kolejowy {//dla torów nie ma sensu tego sprawdzać, rzadko co jedzie po jednej szynie i się mija //Ra: mijanie samochodów wcale nie jest proste // Przesuniecie wzgledne pojazdow. Wyznaczane, zeby sprawdzic, // czy pojazdy faktycznie sie zderzaja (moga byc przesuniete // w/m siebie tak, ze nie zachodza na siebie i wtedy sie mijaja). double RelOffsetH; //wzajemna odległość poprzeczna if (CouplFound) //my na tym torze byśmy byli w kierunku Point2 //dla CouplFound=1 są zwroty zgodne - istotna różnica przesunięć RelOffsetH=(MoverParameters->OffsetTrackH-Track->Dynamics[i]->MoverParameters->OffsetTrackH); else //dla CouplFound=0 są zwroty przeciwne - przesunięcia sumują się RelOffsetH=(MoverParameters->OffsetTrackH+Track->Dynamics[i]->MoverParameters->OffsetTrackH); if (RelOffsetH<0) RelOffsetH=-RelOffsetH; if (RelOffsetH+RelOffsetH>MoverParameters->Dim.W+Track->Dynamics[i]->MoverParameters->Dim.W) continue; //odległość większa od połowy sumy szerokości - kolizji nie będzie //jeśli zahaczenie jest niewielkie, a jest miejsce na poboczu, to zjechać na pobocze } iMinDist=i; //potencjalna kolizja MinDist=TestDist; //odleglość pomiędzy aktywnymi osiami pojazdów } } } } else //(ScanDir<0) { for (int i=0;iiNumDynamics;i++) { if (Track->Dynamics[i]!=this) { TestDist=MyTranslation-(Track->Dynamics[i]->RaTranslationGet()); //???-przesunięcie wózka względem Point1 toru if ((TestDist>0)&&(TestDistDynamics[i]->RaDirectionGet()>0)?0:1; //odwrotnie, bo (ScanDir<0) if (Track->iCategoryFlag&254) //trajektoria innego typu niż tor kolejowy {//dla torów nie ma sensu tego sprawdzać, rzadko co jedzie po jednej szynie i się mija //Ra: mijanie samochodów wcale nie jest proste // Przesunięcie względne pojazdów. Wyznaczane, żeby sprawdzić, // czy pojazdy faktycznie się zderzają (mogą być przesunięte // w/m siebie tak, że nie zachodzą na siebie i wtedy sie mijają). double RelOffsetH; //wzajemna odległość poprzeczna if (CouplFound) //my na tym torze byśmy byli w kierunku Point1 //dla CouplFound=1 są zwroty zgodne - istotna różnica przesunięć RelOffsetH=(MoverParameters->OffsetTrackH-Track->Dynamics[i]->MoverParameters->OffsetTrackH); else //dla CouplFound=0 są zwroty przeciwne - przesunięcia sumują się RelOffsetH=(MoverParameters->OffsetTrackH+Track->Dynamics[i]->MoverParameters->OffsetTrackH); if (RelOffsetH<0) RelOffsetH=-RelOffsetH; if (RelOffsetH+RelOffsetH>MoverParameters->Dim.W+Track->Dynamics[i]->MoverParameters->Dim.W) continue; //odległość większa od połowy sumy szerokości - kolizji nie będzie } iMinDist=i; //potencjalna kolizja MinDist=TestDist; //odleglość pomiędzy aktywnymi osiami pojazdów } } } } dist+=MinDist; //doliczenie odległości przeszkody albo długości odcinka do przeskanowanej odległości return (iMinDist>=0)?Track->Dynamics[iMinDist]:NULL; } dist+=Track->Length(); //doliczenie długości odcinka do przeskanowanej odległości return NULL; //nie ma pojazdów na torze, to jest NULL } int TDynamicObject::DettachStatus(int dir) {//sprawdzenie odległości sprzęgów rzeczywistych od strony (dir): 0=przód,1=tył //Ra: dziwne, że ta funkcja nie jest używana if (!MoverParameters->Couplers[dir].CouplingFlag) return 0; //jeśli nic nie podłączone, to jest OK return (MoverParameters->DettachStatus(dir)); //czy jest w odpowiedniej odległości? } int TDynamicObject::Dettach(int dir) {//rozłączenie sprzęgów rzeczywistych od strony (dir): 0=przód,1=tył //zwraca maskę bitową aktualnych sprzegów (0 jeśli rozłączony) if (ctOwner) {//jeśli pojazd ma przypisany obiekt nadzorujący skład, to póki są wskaźniki TDynamicObject *d=this; while (d) { d->ctOwner=NULL; //usuwanie właściciela d=d->Prev(); } d=Next(); while (d) { d->ctOwner=NULL; //usuwanie właściciela d=d->Next(); //i w drugą stronę } } if (MoverParameters->Couplers[dir].CouplingFlag) //odczepianie, o ile coś podłączone MoverParameters->Dettach(dir); return MoverParameters->Couplers[dir].CouplingFlag; //sprzęg po rozłączaniu (czego się nie da odpiąć } void TDynamicObject::CouplersDettach(double MinDist,int MyScanDir) {//funkcja rozłączajaca podłączone sprzęgi, jeśli odległość przekracza (MinDist) //MinDist - dystans minimalny, dla ktorego mozna rozłączać if (MyScanDir>0) { if (PrevConnected) //pojazd od strony sprzęgu 0 { if (MoverParameters->Couplers[0].CoupleDist>MinDist) //sprzęgi wirtualne zawsze przekraczają { if ((PrevConnectedNo?PrevConnected->NextConnected:PrevConnected->PrevConnected)==this) {//Ra: nie rozłączamy znalezionego, jeżeli nie do nas podłączony (może jechać w innym kierunku) PrevConnected->MoverParameters->Couplers[PrevConnectedNo].Connected=NULL; if (PrevConnectedNo==0) { PrevConnected->PrevConnectedNo=2; //sprzęg 0 nie podłączony PrevConnected->PrevConnected=NULL; } else if (PrevConnectedNo==1) { PrevConnected->NextConnectedNo=2; //sprzęg 1 nie podłączony PrevConnected->NextConnected=NULL; } } //za to zawsze odłączamy siebie PrevConnected=NULL; PrevConnectedNo=2; //sprzęg 0 nie podłączony MoverParameters->Couplers[0].Connected=NULL; } } } else { if (NextConnected) //pojazd od strony sprzęgu 1 { if (MoverParameters->Couplers[1].CoupleDist>MinDist) //sprzęgi wirtualne zawsze przekraczają { if ((NextConnectedNo?NextConnected->NextConnected:NextConnected->PrevConnected)==this) {//Ra: nie rozłączamy znalezionego, jeżeli nie do nas podłączony (może jechać w innym kierunku) NextConnected->MoverParameters->Couplers[NextConnectedNo].Connected=NULL; if (NextConnectedNo==0) { NextConnected->PrevConnectedNo=2; //sprzęg 0 nie podłączony NextConnected->PrevConnected=NULL; } else if (NextConnectedNo==1) { NextConnected->NextConnectedNo=2; //sprzęg 1 nie podłączony NextConnected->NextConnected=NULL; } } NextConnected=NULL; NextConnectedNo=2; //sprzęg 1 nie podłączony MoverParameters->Couplers[1].Connected=NULL; } } } } void TDynamicObject::ABuScanObjects(int ScanDir,double ScanDist) {//skanowanie toru w poszukiwaniu kolidujących pojazdów //ScanDir - określa kierunek poszukiwania zależnie od zwrotu prędkości pojazdu // ScanDir=1 - od strony Coupler0, ScanDir=-1 - od strony Coupler1 int MyScanDir=ScanDir; //zapamiętanie kierunku poszukiwań na torze początkowym, względem sprzęgów TTrackFollower *FirstAxle=(MyScanDir>0?&Axle0:&Axle1); //można by to trzymać w trainset TTrack *Track=FirstAxle->GetTrack(); //tor na którym "stoi" skrajny wózek (może być inny niż tor pojazdu) if (FirstAxle->GetDirection()<0) //czy oś jest ustawiona w stronę Point1? ScanDir=-ScanDir; //jeśli tak, to kierunek szukania będzie przeciwny (teraz względem toru) Byte MyCouplFound; //numer sprzęgu do podłączenia w obiekcie szukajacym MyCouplFound=(MyScanDir<0)?1:0; Byte CouplFound; //numer sprzęgu w znalezionym obiekcie (znaleziony wypełni) TDynamicObject *FoundedObj; //znaleziony obiekt double ActDist=0; //przeskanowana odleglość; odległość do zawalidrogi FoundedObj=ABuFindObject(Track,ScanDir,CouplFound,ActDist); //zaczynamy szukać na tym samym torze /* if (FoundedObj) //jak coś znajdzie, to śledzimy {//powtórzenie wyszukiwania tylko do zastawiania pułepek podczas testów if (ABuGetDirection()<0) ScanDir=ScanDir; //ustalenie kierunku względem toru FoundedObj=ABuFindObject(Track,this,ScanDir,CouplFound); } */ if (DebugModeFlag) if (FoundedObj) //kod służący do logowania błędów if (CouplFound==0) { if (FoundedObj->PrevConnected) if (FoundedObj->PrevConnected!=this) //odświeżenie tego samego się nie liczy WriteLog("0! Coupler warning on "+asName+":"+AnsiString(MyCouplFound)+" - "+FoundedObj->asName+":0 connected to "+FoundedObj->PrevConnected->asName+":"+AnsiString(FoundedObj->PrevConnectedNo)); } else { if (FoundedObj->NextConnected) if (FoundedObj->NextConnected!=this) //odświeżenie tego samego się nie liczy WriteLog("0! Coupler warning on "+asName+":"+AnsiString(MyCouplFound)+" - "+FoundedObj->asName+":1 connected to "+FoundedObj->NextConnected->asName+":"+AnsiString(FoundedObj->NextConnectedNo)); } if (FoundedObj==NULL) //jeśli nie ma na tym samym, szukamy po okolicy {//szukanie najblizszego toru z jakims obiektem //praktycznie przeklejone z TraceRoute()... //double CurrDist=0; //aktualna dlugosc toru if (ScanDir>=0) //uwzględniamy kawalek przeanalizowanego wcześniej toru ActDist=Track->Length()-FirstAxle->GetTranslation(); //odległość osi od Point2 toru else ActDist=FirstAxle->GetTranslation(); //odległość osi od Point1 toru while (ActDist0) //w kierunku Point2 toru { if (Track?Track->iNextDirection:false) //jeśli następny tor jest podpięty od Point2 ScanDir=-ScanDir; //to zmieniamy kierunek szukania na tym torze Track=Track->CurrentNext(); //potem dopiero zmieniamy wskaźnik } else //w kierunku Point1 { if (Track?!Track->iPrevDirection:true) //jeśli poprzedni tor nie jest podpięty od Point2 ScanDir=-ScanDir; //to zmieniamy kierunek szukania na tym torze Track=Track->CurrentPrev(); //potem dopiero zmieniamy wskaźnik } if (Track) {//jesli jest kolejny odcinek toru //CurrDist=Track->Length(); //doliczenie tego toru do przejrzanego dystandu FoundedObj=ABuFindObject(Track,ScanDir,CouplFound,ActDist); //przejrzenie pojazdów tego toru if (FoundedObj) { //ActDist=ScanDist; //wyjście z pętli poszukiwania break; } } else //jeśli toru nie ma, to wychodzimy { ActDist=ScanDist+1.0; //koniec przeglądania torów break; } } } // Koniec szukania najbliższego toru z jakimś obiektem. //teraz odczepianie i jeśli coś się znalazło, doczepianie. if (MyScanDir>0?PrevConnected:NextConnected) if ((MyScanDir>0?PrevConnected:NextConnected)!=FoundedObj) CouplersDettach(1.0,MyScanDir); //odłączamy, jeśli dalej niż metr // i łączenie sprzęgiem wirtualnym if (FoundedObj) {//siebie można bezpiecznie podłączyć jednostronnie do znalezionego MoverParameters->Attach(MyCouplFound,CouplFound,FoundedObj->MoverParameters,ctrain_virtual); //MoverParameters->Couplers[MyCouplFound].Render=false; //wirtualnego nie renderujemy if (MyCouplFound==0) { PrevConnected=FoundedObj; //pojazd od strony sprzęgu 0 PrevConnectedNo=CouplFound; } else { NextConnected=FoundedObj; //pojazd od strony sprzęgu 1 NextConnectedNo=CouplFound; } if (FoundedObj->MoverParameters->Couplers[CouplFound].CouplingFlag==ctrain_virtual) {//Ra: wpinamy się wirtualnym tylko jeśli znaleziony ma wirtualny sprzęg FoundedObj->MoverParameters->Attach(CouplFound,MyCouplFound,this->MoverParameters,ctrain_virtual); if (CouplFound==0) //jeśli widoczny sprzęg 0 znalezionego { if (DebugModeFlag) if (FoundedObj->PrevConnected) if (FoundedObj->PrevConnected!=this) WriteLog("1! Coupler warning on "+asName+":"+AnsiString(MyCouplFound)+" - "+FoundedObj->asName+":0 connected to "+FoundedObj->PrevConnected->asName+":"+AnsiString(FoundedObj->PrevConnectedNo)); FoundedObj->PrevConnected=this; FoundedObj->PrevConnectedNo=MyCouplFound; } else //jeśli widoczny sprzęg 1 znalezionego { if (DebugModeFlag) if (FoundedObj->NextConnected) if (FoundedObj->NextConnected!=this) WriteLog("1! Coupler warning on "+asName+":"+AnsiString(MyCouplFound)+" - "+FoundedObj->asName+":1 connected to "+FoundedObj->NextConnected->asName+":"+AnsiString(FoundedObj->NextConnectedNo)); FoundedObj->NextConnected=this; FoundedObj->NextConnectedNo=MyCouplFound; } } //Ra: jeśli dwa samochody się mijają na odcinku przed zawrotką, to odległość między nimi nie może być liczona w linii prostej! fTrackBlock=MoverParameters->Couplers[MyCouplFound].CoupleDist; //odległość do najbliższego pojazdu w linii prostej if (Track->iCategoryFlag>1) //jeśli samochód if (ActDist>MoverParameters->Dim.L+FoundedObj->MoverParameters->Dim.L) //przeskanowana odległość większa od długości pojazdów //else if (ActDistasName); } else //nic nie znalezione, to nie ma przeszkód fTrackBlock=10000.0; } //----------ABu: koniec skanowania pojazdow __fastcall TDynamicObject::TDynamicObject() { modelShake=vector3(0,0,0); fTrackBlock=10000.0; //brak przeszkody na drodze btnOn=false; vUp=vWorldUp; vFront=vWorldFront; vLeft=vWorldLeft; iNumAxles=0; MoverParameters=NULL; Mechanik=NULL; MechInside=false; //McZapkie-270202 Controller=AIdriver; bDisplayCab=false; //030303 bBrakeAcc=false; NextConnected=PrevConnected=NULL; NextConnectedNo=PrevConnectedNo=2; //ABu: Numery sprzegow. 2=nie podłączony CouplCounter=50; //będzie sprawdzać na początku asName=""; bEnabled=true; MyTrack=NULL; //McZapkie-260202 dRailLength=25.0; for (int i=0;iiLights; //wskaźnik na stan własnych świateł (zmienimy dla rozrządczych EZT) //McZapkie: TypeName musi byc nazwą CHK/MMD pojazdu if (!MoverParameters->LoadChkFile(asBaseDir)) {//jak wczytanie CHK się nie uda, to błąd if (ConversionError==-8) ErrorLog("Missed file: "+BaseDir+"\\"+Type_Name+".fiz"); Error("Cannot load dynamic object "+asName+" from:\r\n"+BaseDir+"\\"+Type_Name+".fiz\r\nError "+ConversionError+" in line "+LineCount); return 0.0; //zerowa długość to brak pojazdu } bool driveractive=(fVel!=0.0); //jeśli prędkość niezerowa, to aktywujemy ruch if (!MoverParameters->CheckLocomotiveParameters(driveractive,(fVel>0?1:-1)*Cab*(iDirection?1:-1))) //jak jedzie lub obsadzony to gotowy do drogi { Error("Parameters mismatch: dynamic object "+asName+" from\n"+BaseDir+"\\"+Type_Name); return 0.0; //zerowa długość to brak pojazdu } MoverParameters->BrakeLevelSet(MoverParameters->BrakeCtrlPos); //poprawienie hamulca po ewentualnym przestawieniu przez Pascal //dodatkowe parametry yB MoreParams+="."; //wykonuje o jedną iterację za mało, więc trzeba mu dodać kropkę na koniec int kropka=MoreParams.Pos("."); //znajdź kropke AnsiString ActPar; //na parametry while (kropka>0) //jesli sa kropki jeszcze { int dlugosc=MoreParams.Length(); ActPar=MoreParams.SubString(1,kropka-1).UpperCase(); //pierwszy parametr; MoreParams=MoreParams.SubString(kropka+1,dlugosc-kropka); //reszta do dalszej obrobki kropka=MoreParams.Pos("."); if(ActPar.SubString(1,1)=="B") //jesli hamulce { //sprawdzanie kolejno nastaw WriteLog("Wpis hamulca: " + ActPar); if (ActPar.Pos("G")>0) {MoverParameters->BrakeDelaySwitch(bdelay_G);} if (ActPar.Pos("P")>0) {MoverParameters->BrakeDelaySwitch(bdelay_P);} if (ActPar.Pos("R")>0) {MoverParameters->BrakeDelaySwitch(bdelay_R);} if (ActPar.Pos("M")>0) {MoverParameters->BrakeDelaySwitch(bdelay_R); MoverParameters->BrakeDelaySwitch(bdelay_R+bdelay_M);} //wylaczanie hamulca if (ActPar.Pos("<>")>0) //wylaczanie na probe hamowania naglego { MoverParameters->BrakeStatus|=128; //wylacz } if (ActPar.Pos("0")>0) //wylaczanie na sztywno { MoverParameters->BrakeStatus|=128; //wylacz MoverParameters->Hamulec->ForceEmptiness(); MoverParameters->BrakeReleaser(1); //odluznij automatycznie } if (ActPar.Pos("E")>0) //oprozniony { MoverParameters->Hamulec->ForceEmptiness(); MoverParameters->BrakeReleaser(1); //odluznij automatycznie MoverParameters->Pipe->CreatePress(0); MoverParameters->Pipe2->CreatePress(0); } if (ActPar.Pos("Q")>0) //oprozniony { // MoverParameters->Hamulec->ForceEmptiness(); //TODO: sprawdzic, dlaczego pojawia sie blad przy uzyciu tej linijki w lokomotywie MoverParameters->BrakeReleaser(1); //odluznij automatycznie MoverParameters->Pipe->CreatePress(0.0); MoverParameters->PipePress=0.0; MoverParameters->Pipe2->CreatePress(0.0); MoverParameters->ScndPipePress=0.0; MoverParameters->PantVolume=1; MoverParameters->PantPress=0; MoverParameters->CompressedVolume=0; } if (ActPar.Pos("1")>0) //wylaczanie 10% { if (random(10)<1) //losowanie 1/10 { MoverParameters->BrakeStatus|=128; //wylacz MoverParameters->Hamulec->ForceEmptiness(); MoverParameters->BrakeReleaser(1); //odluznij automatycznie } } if (ActPar.Pos("X")>0) //agonalny wylaczanie 20%, usrednienie przekladni { if (random(100)<20) //losowanie 20/100 { MoverParameters->BrakeStatus|=128; //wylacz MoverParameters->Hamulec->ForceEmptiness(); MoverParameters->BrakeReleaser(1); //odluznij automatycznie } if (MoverParameters->BrakeCylMult[2]*MoverParameters->BrakeCylMult[1]>0.01) //jesli jest nastawiacz mechaniczny PL { float rnd=random(100); if (rnd<20) //losowanie 20/100 usrednienie { MoverParameters->BrakeCylMult[2]=MoverParameters->BrakeCylMult[1]=(MoverParameters->BrakeCylMult[2]+MoverParameters->BrakeCylMult[1])/2; } else if (rnd<70) //losowanie 70/100-20/100 oslabienie { MoverParameters->BrakeCylMult[1]=MoverParameters->BrakeCylMult[1]*0.50; MoverParameters->BrakeCylMult[2]=MoverParameters->BrakeCylMult[2]*0.75; } else if (rnd<80) //losowanie 80/100-70/100 tylko prozny { MoverParameters->BrakeCylMult[2]=MoverParameters->BrakeCylMult[1]; } else //tylko ladowny { MoverParameters->BrakeCylMult[1]=MoverParameters->BrakeCylMult[2]; } } } //nastawianie ladunku if (ActPar.Pos("T")>0) //prozny { MoverParameters->DecBrakeMult(); MoverParameters->DecBrakeMult(); } //dwa razy w dol if (ActPar.Pos("H")>0) //ladowny I (dla P-Ł dalej prozny) { MoverParameters->IncBrakeMult(); MoverParameters->IncBrakeMult(); MoverParameters->DecBrakeMult(); } //dwa razy w gore i obniz if (ActPar.Pos("F")>0) //ladowny II { MoverParameters->IncBrakeMult(); MoverParameters->IncBrakeMult(); } //dwa razy w gore if (ActPar.Pos("N")>0) //parametr neutralny { } } //koniec hamulce else if(ActPar.SubString(1,1)=="") //tu mozna wpisac inny prefiks i inne rzeczy { //jakies inne prefiksy } } //koniec while kropka if (MoverParameters->CategoryFlag&2) //jeśli samochód {//ustawianie samochodow na poboczu albo na środku drogi if (Track->fTrackWidth<3.5) //jeśli droga wąska MoverParameters->OffsetTrackH=0.0; //to stawiamy na środku, niezależnie od stanu ruchu else if (driveractive) //od 3.5m do 8.0m jedzie po środku pasa, dla szerszych w odległości 1.5m MoverParameters->OffsetTrackH=Track->fTrackWidth<=8.0?-Track->fTrackWidth*0.25:-1.5; else //jak stoi, to kołem na poboczu i pobieramy szerokość razem z poboczem, ale nie z chodnikiem MoverParameters->OffsetTrackH=-0.5*(Track->WidthTotal()-MoverParameters->Dim.W)+0.05; iHornWarning=0; //nie będzie trąbienia po podaniu zezwolenia na jazdę if (fDist<0.0) //-0.5*MoverParameters->Dim.L) //jeśli jest przesunięcie do tyłu if (!Track->CurrentPrev()) //a nie ma tam odcinka i trzeba by coś wygenerować fDist=-fDist; //to traktujemy, jakby przesunięcie było w drugą stronę } //w wagonie tez niech jedzie //if (MoverParameters->MainCtrlPosNo>0 && // if (MoverParameters->CabNo!=0) if (DriverType!="") {//McZapkie-040602: jeśli coś siedzi w pojeździe if (Name==AnsiString(Global::asHumanCtrlVehicle)) //jeśli pojazd wybrany do prowadzenia { if (DebugModeFlag?false:MoverParameters->EngineType!=Dumb) //jak nie Debugmode i nie jest dumbem Controller=Humandriver; //wsadzamy tam sterującego else //w przeciwnym razie trzeba włączyć pokazywanie kabiny bDisplayCab=true; } //McZapkie-151102: rozkład jazdy czytany z pliku *.txt z katalogu w którym jest sceneria if (DriverType.Pos("1")||DriverType.Pos("2")) {//McZapkie-110303: mechanik i rozklad tylko gdy jest obsada //MoverParameters->ActiveCab=MoverParameters->CabNo; //ustalenie aktywnej kabiny (rozrząd) Mechanik=new TController(Controller,this,Aggressive); if (TrainName.IsEmpty()) //jeśli nie w składzie { Mechanik->DirectionInitial(); //załączenie rozrządu (wirtualne kabiny) itd. Mechanik->PutCommand("Timetable:",iDirection?-fVel:fVel,0,NULL); //tryb pociągowy z ustaloną prędkością (względem sprzęgów) } //if (TrainName!="none") // Mechanik->PutCommand("Timetable:"+TrainName,fVel,0,NULL); } else if (DriverType=="p") {//obserwator w charakterze pasażera //Ra: to jest niebezpieczne, bo w razie co będzie pomagał hamulcem bezpieczeństwa Mechanik=new TController(Controller,this,Easyman,false); } } // McZapkie-250202 iAxles=(MaxAxlesNAxles)?MaxAxles:MoverParameters->NAxles; //ilość osi //wczytywanie z pliku nazwatypu.mmd, w tym model LoadMMediaFile(asBaseDir,Type_Name,asReplacableSkin); //McZapkie-100402: wyszukiwanie submodeli sprzegów btCoupler1.Init("coupler1",mdModel,false); //false - ma być wyłączony btCoupler2.Init("coupler2",mdModel,false); btCPneumatic1.Init("cpneumatic1",mdModel); btCPneumatic2.Init("cpneumatic2",mdModel); btCPneumatic1r.Init("cpneumatic1r",mdModel); btCPneumatic2r.Init("cpneumatic2r",mdModel); btPneumatic1.Init("pneumatic1",mdModel); btPneumatic2.Init("pneumatic2",mdModel); btPneumatic1r.Init("pneumatic1r",mdModel); btPneumatic2r.Init("pneumatic2r",mdModel); btCCtrl1.Init("cctrl1",mdModel,false); btCCtrl2.Init("cctrl2",mdModel,false); btCPass1.Init("cpass1",mdModel,false); btCPass2.Init("cpass2",mdModel,false); //sygnaly //ABu 060205: Zmiany dla koncowek swiecacych: btEndSignals11.Init("endsignal13",mdModel,false); btEndSignals21.Init("endsignal23",mdModel,false); btEndSignals13.Init("endsignal12",mdModel,false); btEndSignals23.Init("endsignal22",mdModel,false); iInventory|=btEndSignals11.Active() ?0x01:0; //informacja, czy ma poszczególne światła iInventory|=btEndSignals21.Active() ?0x02:0; iInventory|=btEndSignals13.Active() ?0x04:0; iInventory|=btEndSignals23.Active() ?0x08:0; //ABu: to niestety zostawione dla kompatybilnosci modeli: btEndSignals1.Init("endsignals1",mdModel,false); btEndSignals2.Init("endsignals2",mdModel,false); btEndSignalsTab1.Init("endtab1",mdModel,false); btEndSignalsTab2.Init("endtab2",mdModel,false); iInventory|=btEndSignals1.Active() ?0x10:0; iInventory|=btEndSignals2.Active() ?0x20:0; iInventory|=btEndSignalsTab1.Active()?0x40:0; //tabliczki blaszane iInventory|=btEndSignalsTab2.Active()?0x80:0; //ABu Uwaga! tu zmienic w modelu! btHeadSignals11.Init("headlamp13",mdModel,false); //lewe btHeadSignals12.Init("headlamp11",mdModel,false); //górne btHeadSignals13.Init("headlamp12",mdModel,false); //prawe btHeadSignals21.Init("headlamp23",mdModel,false); btHeadSignals22.Init("headlamp21",mdModel,false); btHeadSignals23.Init("headlamp22",mdModel,false); TurnOff(); //resetowanie zmiennych submodeli //wyszukiwanie zderzakow if (mdModel) //jeśli ma w czym szukać for (int i=0;i<2;i++) { asAnimName=AnsiString("buffer_left0")+(i+1); smBuforLewy[i]=mdModel->GetFromName(asAnimName.c_str()); if (smBuforLewy[i]) smBuforLewy[i]->WillBeAnimated(); //ustawienie flagi animacji asAnimName=AnsiString("buffer_right0")+(i+1); smBuforPrawy[i]=mdModel->GetFromName(asAnimName.c_str()); if (smBuforPrawy[i]) smBuforPrawy[i]->WillBeAnimated(); } for (int i=0;iDim.L))+fDist; //McZapkie-250202 end. Track->AddDynamicObject(this); //wstawiamy do toru na pozycję 0, a potem przesuniemy //McZapkie: zmieniono na ilosc osi brane z chk //iNumAxles=(MoverParameters->NAxles>3 ? 4 : 2 ); iNumAxles=2; //McZapkie-090402: odleglosc miedzy czopami skretu lub osiami fAxleDist=Max0R(MoverParameters->BDist,MoverParameters->ADist); if (fAxleDist<0.2) fAxleDist=0.2; //żeby się dało wektory policzyć if (fAxleDist>MoverParameters->Dim.L-0.2) //nie mogą być za daleko fAxleDist=MoverParameters->Dim.L-0.2; //bo będzie "walenie w mur" double fAxleDistHalf=fAxleDist*0.5; //WriteLog("Dynamic "+Type_Name+" of length "+MoverParameters->Dim.L+" at "+AnsiString(fDist)); //if (Cab) //jeśli ma obsadę - zgodność wstecz, jeśli tor startowy ma Event0 // if (Track->Event0) //jeśli tor ma Event0 // if (fDist>=0.0) //jeśli jeśli w starych sceneriach początek składu byłby wysunięty na ten tor // if (fDist<=0.5*MoverParameters->Dim.L+0.2) //ale nie jest wysunięty // fDist+=0.5*MoverParameters->Dim.L+0.2; //wysunąć go na ten tor //przesuwanie pojazdu tak, aby jego początek był we wskazanym miejcu fDist-=0.5*MoverParameters->Dim.L; //dodajemy pół długości pojazdu, bo ustawiamy jego środek (zliczanie na minus) switch (iNumAxles) {//Ra: pojazdy wstawiane są na tor początkowy, a potem przesuwane case 2: //ustawianie osi na torze Axle0.Init(Track,this,iDirection?1:-1); Axle0.Move((iDirection?fDist:-fDist)+fAxleDistHalf,false); Axle1.Init(Track,this,iDirection?1:-1); Axle1.Move((iDirection?fDist:-fDist)-fAxleDistHalf,false); //false, żeby nie generować eventów //Axle2.Init(Track,this,iDirection?1:-1); //Axle2.Move((iDirection?fDist:-fDist)-fAxleDistHalft+0.01),false); //Axle3.Init(Track,this,iDirection?1:-1); //Axle3.Move((iDirection?fDist:-fDist)+fAxleDistHalf-0.01),false); break; case 4: Axle0.Init(Track,this,iDirection?1:-1); Axle0.Move((iDirection?fDist:-fDist)+(fAxleDistHalf+MoverParameters->ADist*0.5),false); Axle1.Init(Track,this,iDirection?1:-1); Axle1.Move((iDirection?fDist:-fDist)-(fAxleDistHalf+MoverParameters->ADist*0.5),false); //Axle2.Init(Track,this,iDirection?1:-1); //Axle2.Move((iDirection?fDist:-fDist)-(fAxleDistHalf-MoverParameters->ADist*0.5),false); //Axle3.Init(Track,this,iDirection?1:-1); //Axle3.Move((iDirection?fDist:-fDist)+(fAxleDistHalf-MoverParameters->ADist*0.5),false); break; } Move(0.0001); //potrzebne do wyliczenia aktualnej pozycji; nie może być zero, bo nie przeliczy pozycji //teraz jeszcze trzeba przypisać pojazdy do nowego toru, bo przesuwanie początkowe osi nie zrobiło tego ABuCheckMyTrack(); //zmiana toru na ten, co oś Axle0 (oś z przodu) TLocation loc; //Ra: ustawienie pozycji do obliczania sprzęgów loc.X=-vPosition.x; loc.Y=vPosition.z; loc.Z=vPosition.y; MoverParameters->Loc=loc; //normalnie przesuwa ComputeMovement() w Update() //pOldPos4=Axle1.pPosition; //Ra: nie używane //pOldPos1=Axle0.pPosition; //ActualTrack= GetTrack(); //McZapkie-030303 //ABuWozki 060504 if (mdModel) //jeśli ma w czym szukać { smBogie[0]=mdModel->GetFromName("bogie1"); //Ra: bo nazwy są małymi smBogie[1]=mdModel->GetFromName("bogie2"); if (!smBogie[0]) smBogie[0]=mdModel->GetFromName("boogie01"); //Ra: alternatywna nazwa if (!smBogie[1]) smBogie[1]=mdModel->GetFromName("boogie02"); //Ra: alternatywna nazwa if (smBogie[0]) smBogie[0]->WillBeAnimated(); if (smBogie[1]) smBogie[1]->WillBeAnimated(); } //ABu: zainicjowanie zmiennej, zeby nic sie nie ruszylo //w pierwszej klatce, potem juz liczona prawidlowa wartosc masy MoverParameters->ComputeConstans(); /*Ra: to nie działa - Event0 musi być wykonywany ciągle if (fVel==0.0) //jeśli stoi if (MoverParameters->CabNo!=0) //i ma kogoś w kabinie if (Track->Event0) //a jest w tym torze event od stania RaAxleEvent(Track->Event0); //dodanie eventu stania do kolejki */ vFloor=vector3(0,0,MoverParameters->Floor); //wektor podłogi dla wagonów, przesuwa ładunek return MoverParameters->Dim.L; //długość większa od zera oznacza OK; 2mm docisku? } void __fastcall TDynamicObject::FastMove(double fDistance) { MoverParameters->dMoveLen=MoverParameters->dMoveLen+fDistance; } void __fastcall TDynamicObject::Move(double fDistance) {//przesuwanie pojazdu po trajektorii polega na przesuwaniu poszczególnych osi //Ra: wartość prędkości 2km/h ma ograniczyć aktywację eventów w przypadku drgań if (Axle0.GetTrack()==Axle1.GetTrack()) //przed przesunięciem {//powiązanie pojazdu z osią można zmienić tylko wtedy, gdy skrajne osie są na tym samym torze if (MoverParameters->Vel>2) //|[km/h]| nie ma sensu zmiana osi, jesli pojazd drga na postoju iAxleFirst=(MoverParameters->V>=0.0)?1:0; //[m/s] ?1:0 - aktywna druga oś w kierunku jazdy //aktualnie eventy aktywuje druga oś, żeby AI nie wyłączało sobie semafora za szybko } if (fDistance>0.0) {//gdy ruch w stronę sprzęgu 0, doliczyć korektę do osi 1 bEnabled&=Axle0.Move(fDistance,!iAxleFirst); //oś z przodu pojazdu bEnabled&=Axle1.Move(fDistance/*-fAdjustment*/,iAxleFirst); //oś z tyłu pojazdu } else if (fDistance<0.0) {//gdy ruch w stronę sprzęgu 1, doliczyć korektę do osi 0 bEnabled&=Axle1.Move(fDistance,iAxleFirst); //oś z tyłu pojazdu prusza się pierwsza bEnabled&=Axle0.Move(fDistance/*-fAdjustment*/,!iAxleFirst); //oś z przodu pojazdu } //Axle2.Move(fDistance,false); //te nigdy pierwsze nie są //Axle3.Move(fDistance,false); if (fDistance!=0.0) //nie liczyć ponownie, jeśli stoi {//liczenie pozycji pojazdu tutaj, bo jest używane w wielu miejscach vPosition=0.5*(Axle1.pPosition+Axle0.pPosition); //środek między skrajnymi osiami vFront=Axle0.pPosition-Axle1.pPosition; //wektor pomiędzy skrajnymi osiami //Ra 2F1J: to nie jest stabilne (powoduje rzucanie taborem) i wymaga dopracowania fAdjustment=vFront.Length()-fAxleDist; //na łuku będzie ujemny //if (fabs(fAdjustment)>0.02) //jeśli jest zbyt dużo, to rozłożyć na kilka przeliczeń (wygasza drgania?) //{//parę centymetrów trzeba by już skorygować; te błędy mogą się też generować na ostrych łukach // fAdjustment*=0.5; //w jednym kroku korygowany jest ułamek błędu //} //else // fAdjustment=0.0; vFront=Normalize(vFront); //kierunek ustawienia pojazdu (wektor jednostkowy) vLeft=Normalize(CrossProduct(vWorldUp,vFront)); //wektor poziomy w lewo, normalizacja potrzebna z powodu pochylenia (vFront) vUp=CrossProduct(vFront,vLeft); //wektor w górę, będzie jednostkowy modelRot.z=atan2(-vFront.x,vFront.z); //kąt obrotu pojazdu [rad]; z ABuBogies() double a=((Axle1.GetRoll()+Axle0.GetRoll())); //suma przechyłek if (a!=0.0) {//wyznaczanie przechylenia tylko jeśli jest przechyłka //można by pobrać wektory normalne z toru... mMatrix.Identity(); //ta macierz jest potrzebna głównie do wyświetlania mMatrix.Rotation(a*0.5,vFront); //obrót wzdłuż osi o przechyłkę vUp=mMatrix*vUp; //wektor w górę pojazdu (przekręcenie na przechyłce) //vLeft=mMatrix*DynamicObject->vLeft; //vUp=CrossProduct(vFront,vLeft); //wektor w górę //vLeft=Normalize(CrossProduct(vWorldUp,vFront)); //wektor w lewo vLeft=Normalize(CrossProduct(vUp,vFront)); //wektor w lewo //vUp=CrossProduct(vFront,vLeft); //wektor w górę } mMatrix.Identity(); //to też można by od razu policzyć, ale potrzebne jest do wyświetlania mMatrix.BasisChange(vLeft,vUp,vFront); //przesuwanie jest jednak rzadziej niż renderowanie mMatrix=Inverse(mMatrix); //wyliczenie macierzy dla pojazdu (potrzebna tylko do wyświetlania?) //if (MoverParameters->CategoryFlag&2) {//przesunięcia są używane po wyrzuceniu pociągu z toru vPosition.x+=MoverParameters->OffsetTrackH*vLeft.x; //dodanie przesunięcia w bok vPosition.z+=MoverParameters->OffsetTrackH*vLeft.z; //vLeft jest wektorem poprzecznym //if () na przechyłce będzie dodatkowo zmiana wysokości samochodu vPosition.y+=MoverParameters->OffsetTrackV; //te offsety są liczone przez moverparam } //Ra: skopiowanie pozycji do fizyki, tam potrzebna do zrywania sprzęgów //MoverParameters->Loc.X=-vPosition.x; //robi to {Fast}ComputeMovement() //MoverParameters->Loc.Y= vPosition.z; //MoverParameters->Loc.Z= vPosition.y; //obliczanie pozycji sprzęgów do liczenia zderzeń vector3 dir=(0.5*MoverParameters->Dim.L)*vFront; //wektor sprzęgu vCoulpler[0]=vPosition+dir; //współrzędne sprzęgu na początku vCoulpler[1]=vPosition-dir; //współrzędne sprzęgu na końcu MoverParameters->vCoulpler[0]=vCoulpler[0]; //tymczasowo kopiowane na inny poziom MoverParameters->vCoulpler[1]=vCoulpler[1]; //bCameraNear= //if (bCameraNear) //jeśli istotne są szczegóły (blisko kamery) {//przeliczenie cienia TTrack *t0=Axle0.GetTrack(); //już po przesunięciu TTrack *t1=Axle1.GetTrack(); if ((t0->eEnvironment==e_flat)&&(t1->eEnvironment==e_flat)) //może być e_bridge... fShade=0.0; //standardowe oświetlenie else {//jeżeli te tory mają niestandardowy stopień zacienienia (e_canyon, e_tunnel) if (t0->eEnvironment==t1->eEnvironment) {switch (t0->eEnvironment) {//typ zmiany oświetlenia case e_canyon: fShade=0.65; break; //zacienienie w kanionie case e_tunnel: fShade=0.20; break; //zacienienie w tunelu } } else //dwa różne {//liczymy proporcję double d=Axle0.GetTranslation(); //aktualne położenie na torze if (Axle0.GetDirection()<0) d=t0->fTrackLength-d; //od drugiej strony liczona długość d/=fAxleDist; //rozsataw osi procentowe znajdowanie się na torze switch (t0->eEnvironment) {//typ zmiany oświetlenia - zakładam, że drugi tor ma e_flat case e_canyon: fShade=(d*0.65)+(1.0-d); break; //zacienienie w kanionie case e_tunnel: fShade=(d*0.20)+(1.0-d); break; //zacienienie w tunelu } switch (t1->eEnvironment) {//typ zmiany oświetlenia - zakładam, że pierwszy tor ma e_flat case e_canyon: fShade=d+(1.0-d)*0.65; break; //zacienienie w kanionie case e_tunnel: fShade=d+(1.0-d)*0.20; break; //zacienienie w tunelu } } } } } }; void __fastcall TDynamicObject::AttachPrev(TDynamicObject *Object, int iType) {//Ra: doczepia Object na końcu składu (nazwa funkcji może być myląca) //Ra: używane tylko przy wczytywaniu scenerii /* //Ra: po wstawieniu pojazdu do scenerii nie miał on ustawionej pozycji, teraz już ma TLocation loc; loc.X=-vPosition.x; loc.Y=vPosition.z; loc.Z=vPosition.y; MoverParameters->Loc=loc; //Ra: do obliczania sprzęgów, na starcie nie są przesunięte loc.X=-Object->vPosition.x; loc.Y=Object->vPosition.z; loc.Z=Object->vPosition.y; Object->MoverParameters->Loc=loc; //ustawienie dodawanego pojazdu */ MoverParameters->Attach(iDirection,Object->iDirection^1,Object->MoverParameters,iType,true); MoverParameters->Couplers[iDirection].Render=false; Object->MoverParameters->Attach(Object->iDirection^1,iDirection,MoverParameters,iType,true); Object->MoverParameters->Couplers[Object->iDirection^1].Render=true; //rysowanie sprzęgu w dołączanym if (iDirection) {//łączenie standardowe NextConnected=Object; //normalnie doczepiamy go sobie do sprzęgu 1 NextConnectedNo=Object->iDirection^1; } else {//łączenie odwrotne PrevConnected=Object; //doczepiamy go sobie do sprzęgu 0, gdy stoimy odwrotnie PrevConnectedNo=Object->iDirection^1; } if (Object->iDirection) {//dołączany jest normalnie ustawiany Object->PrevConnected=this; //on ma nas z przodu Object->PrevConnectedNo=iDirection; } else {//dołączany jest odwrotnie ustawiany Object->NextConnected=this; //on ma nas z tyłu Object->NextConnectedNo=iDirection; } if (MoverParameters->TrainType&dt_EZT) //w przypadku łączenia członów, światła w rozrządczym zależą od stanu w silnikowym if (MoverParameters->Couplers[iDirection].AllowedFlag&ctrain_depot) //gdy sprzęgi łączone warsztatowo (powiedzmy) if ((MoverParameters->Power<1.0)&&(Object->MoverParameters->Power>1.0)) //my nie mamy mocy, ale ten drugi ma iLights=Object->MoverParameters->iLights; //to w tym z mocą będą światła załączane, a w tym bez tylko widoczne else if ((MoverParameters->Power>1.0)&&(Object->MoverParameters->Power<1.0)) //my mamy moc, ale ten drugi nie ma Object->iLights=MoverParameters->iLights; //to w tym z mocą będą światła załączane, a w tym bez tylko widoczne return; //SetPneumatic(1,1); //Ra: to i tak się nie wykonywało po return //SetPneumatic(1,0); //SetPneumatic(0,1); //SetPneumatic(0,0); } bool __fastcall TDynamicObject::UpdateForce(double dt, double dt1, bool FullVer) { if (!bEnabled) return false; if (dt>0) MoverParameters->ComputeTotalForce(dt,dt1,FullVer); //wywalenie WS zależy od ustawienia kierunku return true; } void __fastcall TDynamicObject::LoadUpdate() {//przeładowanie modelu ładunku // Ra: nie próbujemy wczytywać modeli miliony razy podczas renderowania!!! if ((mdLoad==NULL)&&(MoverParameters->Load>0)) { AnsiString asLoadName=asBaseDir+MoverParameters->LoadType+".t3d"; //zapamiętany katalog pojazdu //asLoadName=MoverParameters->LoadType; //if (MoverParameters->LoadType!=AnsiString("passengers")) Global::asCurrentTexturePath=asBaseDir; //bieżąca ścieżka do tekstur to dynamic/... mdLoad=TModelsManager::GetModel(asLoadName.c_str()); //nowy ładunek Global::asCurrentTexturePath=AnsiString(szTexturePath); //z powrotem defaultowa sciezka do tekstur //Ra: w MMD można by zapisać położenie modelu ładunku (np. węgiel) w zależności od załadowania } else if (MoverParameters->Load==0) mdLoad=NULL; //nie ma ładunku //if ((mdLoad==NULL)&&(MoverParameters->Load>0)) // { // mdLoad=NULL; //Ra: to jest tu bez sensu - co autor miał na myśli? // } MoverParameters->LoadStatus&=3; //po zakończeniu będzie równe zero }; /* double __fastcall ComputeRadius(double p1x, double p1z, double p2x, double p2z, double p3x, double p3z, double p4x, double p4z) { double v1z= p1x-p2x; double v1x= p1z-p2z; double v4z= p3x-p4x; double v4x= p3z-p4z; double A1= p2z-p1z; double B1= p1x-p2x; double C1= -p1z*B1-p1x*A1; double A2= p4z-p3z; double B2= p3x-p4x; double C2= -p3z*B1-p3x*A1; double y= (A1*C2/A2-C1)/(B1-A1*B2/A2); double x= (-B2*y-C2)/A2; } */ double __fastcall TDynamicObject::ComputeRadius(vector3 p1, vector3 p2, vector3 p3, vector3 p4) { // vector3 v1 // TLine l1= TLine(p1,p1-p2); // TLine l4= TLine(p4,p4-p3); // TPlane p1= l1.GetPlane(); // vector3 pt; // CrossPoint(pt,l4,p1); double R= 0.0; vector3 p12= p1-p2; vector3 p34= p3-p4; p12= CrossProduct(p12,vector3(0.0,0.1,0.0)); p12=Normalize(p12); p34= CrossProduct(p34,vector3(0.0,0.1,0.0)); p34=Normalize(p34); if (fabs(p1.x-p2.x)>0.01) { if (fabs(p12.x-p34.x)>0.001) R=(p1.x-p4.x)/(p34.x-p12.x); } else { if (fabs(p12.z-p34.z)>0.001) R=(p1.z-p4.z)/(p34.z-p12.z); } return(R); } /* double __fastcall TDynamicObject::ComputeRadius() { double L=0; double d=0; d=sqrt(SquareMagnitude(Axle0.pPosition-Axle1.pPosition)); L=Axle1.GetLength(Axle1.pPosition,Axle1.pPosition-Axle2.pPosition,Axle0.pPosition-Axle3.pPosition,Axle0.pPosition); double eps=0.01; double R= 0; double L_d; if ((L>0) || (d>0)) { L_d= L-d; if (L_d>eps) { R=L*sqrt(L/(24*(L_d))); } } return R; } */ /* Ra: na razie nie potrzebne void __fastcall TDynamicObject::UpdatePos() { MoverParameters->Loc.X= -vPosition.x; MoverParameters->Loc.Y= vPosition.z; MoverParameters->Loc.Z= vPosition.y; } */ /* Ra: Powinny być dwie funkcje wykonujące aktualizację fizyki. Jedna wykonująca krok obliczeń, powtarzana odpowiednią liczbę razy, a druga wykonująca zbiorczą aktualzację mniej istotnych elementów. Ponadto należało by ustalić odległość składów od kamery i jeśli przekracza ona np. 10km, to traktować składy jako uproszczone, np. bez wnikania w siły na sprzęgach, opóźnienie działania hamulca itp. Oczywiście musi mieć to pewną histerezę czasową, aby te tryby pracy nie przełączały się zbyt szybko. */ bool __fastcall TDynamicObject::Update(double dt, double dt1) { if (dt==0) return true; //Ra: pauza if (!MoverParameters->PhysicActivation&&!MechInside) //to drugie, bo będąc w maszynowym blokuje się fizyka return true; //McZapkie: wylaczanie fizyki gdy nie potrzeba if (!MyTrack) return false; //pojazdy postawione na torach portalowych mają MyTrack==NULL if (!bEnabled) return false; //a normalnie powinny mieć bEnabled==false //Ra: przeniosłem - no już lepiej tu, niż w wyświetlaniu! //if ((MoverParameters->ConverterFlag==false) && (MoverParameters->TrainType!=dt_ET22)) //Ra: to nie może tu być, bo wyłącza sprężarkę w rozrządczym EZT! //if ((MoverParameters->ConverterFlag==false)&&(MoverParameters->CompressorPower!=0)) // MoverParameters->CompressorFlag=false; //if (MoverParameters->CompressorPower==2) // MoverParameters->CompressorAllow=MoverParameters->ConverterFlag; //McZapkie-260202 if ((MoverParameters->EnginePowerSource.SourceType==CurrentCollector)&&(MoverParameters->Power>1.0)) //aby rozrządczy nie opuszczał silnikowemu if ((MechInside)||(MoverParameters->TrainType==dt_EZT)) { //if ((!MoverParameters->PantCompFlag)&&(MoverParameters->CompressedVolume>=2.8)) // MoverParameters->PantVolume=MoverParameters->CompressedVolume; if (MoverParameters->PantPress<(MoverParameters->TrainType==dt_EZT?2.4:3.5)) {// 3.5 wg http://www.transportszynowy.pl/eu06-07pneumat.php //"Wyłączniki ciśnieniowe odbieraków prądu wyłączają sterowanie wyłącznika szybkiego oraz uniemożliwiają podniesienie odbieraków prądu, gdy w instalacji rozrządu ciśnienie spadnie poniżej wartości 3,5 bara." //Ra 2013-12: Niebugocław mówi, że w EZT podnoszą się przy 2.5 //if (!MoverParameters->PantCompFlag) // MoverParameters->PantVolume=MoverParameters->CompressedVolume; MoverParameters->PantFront(false); //opuszczenie pantografów przy niskim ciśnieniu MoverParameters->PantRear(false); //to idzie w ukrotnieniu, a nie powinno... } //Winger - automatyczne wylaczanie malej sprezarki else if (MoverParameters->PantPress>=4.8) MoverParameters->PantCompFlag=false; } //Ra: do Mover to trzeba przenieść, żeby AI też mogło sobie podpompować double dDOMoveLen; TLocation l; l.X=-vPosition.x; //przekazanie pozycji do fizyki l.Y=vPosition.z; l.Z=vPosition.y; TRotation r; r.Rx=r.Ry=r.Rz=0; //McZapkie: parametry powinny byc pobierane z toru //TTrackShape ts; //ts.R=MyTrack->fRadius; //if (ABuGetDirection()<0) ts.R=-ts.R; // ts.R=MyTrack->fRadius; //ujemne promienie są już zamienione przy wczytywaniu if (Axle0.vAngles.z!=Axle1.vAngles.z) {//wyliczenie promienia z obrotów osi - modyfikację zgłosił youBy ts.R=Axle0.vAngles.z-Axle1.vAngles.z; //różnica może dawać stałą ±M_2PI if (ts.R>M_PI) ts.R-=M_2PI else if (ts.R<-M_PI) ts.R+=M_2PI; //normalizacja // ts.R=fabs(0.5*MoverParameters->BDist/sin(ts.R*0.5)); ts.R=-0.5*MoverParameters->BDist/sin(ts.R*0.5); if ((ts.R>15000.0)||(ts.R<-15000.0)) ts.R=0.0; //szkoda czasu na zbyt duże promienie, 4km to promień nie wymagający przechyłki } else ts.R=0.0; //ts.R=ComputeRadius(Axle1.pPosition,Axle2.pPosition,Axle3.pPosition,Axle0.pPosition); //Ra: składową pochylenia wzdłużnego mamy policzoną w jednostkowym wektorze vFront ts.Len=1.0; //Max0R(MoverParameters->BDist,MoverParameters->ADist); ts.dHtrack=-vFront.y; //Axle1.pPosition.y-Axle0.pPosition.y; //wektor między skrajnymi osiami (!!!odwrotny) ts.dHrail=(Axle1.GetRoll()+Axle0.GetRoll())*0.5; //średnia przechyłka pudła //TTrackParam tp; tp.Width=MyTrack->fTrackWidth; //McZapkie-250202 tp.friction=MyTrack->fFriction*Global::fFriction; tp.CategoryFlag=MyTrack->iCategoryFlag&15; tp.DamageFlag=MyTrack->iDamageFlag; tp.QualityFlag=MyTrack->iQualityFlag; if ((MoverParameters->Couplers[0].CouplingFlag>0) &&(MoverParameters->Couplers[1].CouplingFlag>0)) { MoverParameters->InsideConsist=true; } else { MoverParameters->InsideConsist=false; } //napiecie sieci trakcyjnej //Ra 15-01: przeliczenie poboru prądu powinno być robione wcześniej, żeby na tym etapie były znane napięcia //TTractionParam tmpTraction; //tmpTraction.TractionVoltage=0; if (MoverParameters->EnginePowerSource.SourceType==CurrentCollector) {//dla EZT tylko silnikowy //if (Global::bLiveTraction) {//Ra 2013-12: to niżej jest chyba trochę bez sensu double v=MoverParameters->PantRearVolt; if (v==0.0) {v=MoverParameters->PantFrontVolt; if (v==0.0) if (MoverParameters->TrainType&(dt_EZT|dt_ET40|dt_ET41|dt_ET42)) //dwuczłony mogą mieć sprzęg WN v=MoverParameters->GetTrainsetVoltage(); //ostatnia szansa } if (v!=0.0) {//jeśli jest zasilanie NoVoltTime=0; tmpTraction.TractionVoltage=v; } else { /* if (MoverParameters->Vel>0.1f) //jeśli jedzie if (NoVoltTime==0.0) //tylko przy pierwszym zaniku napięcia if (MoverParameters->PantFrontUp||MoverParameters->PantRearUp) //if ((pants[0].fParamPants->PantTraction>1.0)||(pants[1].fParamPants->PantTraction>1.0)) {//wspomagacz usuwania problemów z siecią if (!Global::iPause) {//Ra: tymczasowa teleportacja do miejsca, gdzie brakuje prądu Global::SetCameraPosition(vPosition+vector3(0,0,5)); //nowa pozycja dla generowania obiektów Global::pCamera->Init(vPosition+vector3(0,0,5),Global::pFreeCameraInitAngle[0]); //przestawienie } Global:l::pGround->Silence(Global::pCamera->Pos); //wyciszenie wszystkiego z poprzedniej pozycji Globa:iPause|=1; //tymczasowe zapauzowanie, gdy problem z siecią } */ NoVoltTime=NoVoltTime+dt; if (NoVoltTime>0.2) //jeśli brak zasilania dłużej niż 0.2 sekundy (25km/h pod izolatorem daje 0.15s) {//Ra 2F1H: prowizorka, trzeba przechować napięcie, żeby nie wywalało WS pod izolatorem if (MoverParameters->Vel>0.5) //jeśli jedzie if (MoverParameters->PantFrontUp||MoverParameters->PantRearUp) //Ra 2014-07: doraźna blokada logowania zimnych lokomotyw - zrobić to trzeba inaczej //if (NoVoltTime>0.02) //tu można ograniczyć czas rozłączenia //if (DebugModeFlag) //logowanie nie zawsze if (MoverParameters->Mains) {//Ra 15-01: logować tylko, jeśli WS załączony //if (MoverParameters->PantFrontUp&&pants) //Ra 15-01: bezwzględne współrzędne pantografu nie są dostępne, więc lepiej się tego nie zaloguje ErrorLog("Voltage loss: by "+MoverParameters->Name+" at "+FloatToStrF(vPosition.x,ffFixed,7,2)+" "+FloatToStrF(vPosition.y,ffFixed,7,2)+" "+FloatToStrF(vPosition.z,ffFixed,7,2)+", time "+FloatToStrF(NoVoltTime,ffFixed,7,2)); //if (MoverParameters->PantRearUp) // if (iAnimType[ANIM_PANTS]>1) // if (pants[1]) // ErrorLog("Voltage loss: by "+MoverParameters->Name+" at "+FloatToStrF(vPosition.x,ffFixed,7,2)+" "+FloatToStrF(vPosition.y,ffFixed,7,2)+" "+FloatToStrF(vPosition.z,ffFixed,7,2)+", time "+FloatToStrF(NoVoltTime,ffFixed,7,2)); } //Ra 2F1H: nie było sensu wpisywać tu zera po upływie czasu, bo zmienna była tymczasowa, a napięcie zerowane od razu tmpTraction.TractionVoltage=0; //Ra 2013-12: po co tak? //pControlled->MainSwitch(false); //może tak? } } } //else //Ra: nie no, trzeba podnieść pantografy, jak nie będzie drutu, to będą miały prąd po osiągnięciu 1.4m // tmpTraction.TractionVoltage=0.95*MoverParameters->EnginePowerSource.MaxVoltage; } else tmpTraction.TractionVoltage=0.95*MoverParameters->EnginePowerSource.MaxVoltage; tmpTraction.TractionFreq=0; tmpTraction.TractionMaxCurrent=7500; //Ra: chyba za dużo? powinno wywalać przy 1500 tmpTraction.TractionResistivity=0.3; //McZapkie: predkosc w torze przekazac do TrackParam //McZapkie: Vel ma wymiar [km/h] (absolutny), V ma wymiar [m/s], taka przyjalem notacje tp.Velmax=MyTrack->VelocityGet(); if (Mechanik) {//Ra 2F3F: do Driver.cpp to przenieść? MoverParameters->EqvtPipePress=GetEPP(); //srednie cisnienie w PG if ((Mechanik->Primary())&&(MoverParameters->EngineType==ElectricInductionMotor)) //jesli glowny i z asynchronami, to niech steruje { //hamulcem lacznie dla calego pociagu/ezt //1. ustal wymagana sile hamowania calego pociagu // - opoznienie moze byc ustalane na podstawie charakterystyki // - opoznienie moze byc ustalane na podstawie mas i cisnien granicznych // //2. ustal mozliwa do realizacji sile hamowania ED // - w szczegolnosci powinien brac pod uwage rozne sily hamowania float FED=0; // for(TDynamicObject *p=GetFirstDynamic(4);p;p->NextC(4)) // FED+=p->MoverParameters->eimv[eimv_Fmax]; //3. ustaw pojazdom sile hamowania ED // - proporcjonalnie do mozliwosci //4. ustal potrzebne dohamowanie pneumatyczne // - od sily zadanej trzeba odjac realizowana przez ED //5. w razie potrzeby wlacz hamulec utrzymujacy // - gdy zahamowany ma ponizej 2 km/h //6. ustaw pojazdom sile hamowania ep // - proporcjonalnie do masy, do liczby osi, rowne cisnienia - jak bedzie, tak bedzie dobrze } //yB: cos (AI) tu jest nie kompatybilne z czyms (hamulce) // if (Controller!=Humandriver) // if (Mechanik->LastReactionTime>0.5) // { // MoverParameters->BrakeCtrlPos=0; // Mechanik->LastReactionTime=0; // } Mechanik->UpdateSituation(dt1); //przebłyski świadomości AI } //fragment "z EXE Kursa" if (MoverParameters->Mains) //nie wchodzić w funkcję bez potrzeby if ((!MoverParameters->Battery)&&(Controller==Humandriver)&&(MoverParameters->EngineType!=DieselEngine)&&(MoverParameters->EngineType!=WheelsDriven)) {//jeśli bateria wyłączona, a nie diesel ani drezyna reczna if (MoverParameters->MainSwitch(false)) //wyłączyć zasilanie MoverParameters->EventFlag=true; } if (MoverParameters->TrainType==dt_ET42) {//powinny być wszystkie dwuczłony oraz EZT /* //Ra: to jest bez sensu, bo wyłącza WS przy przechodzeniu przez "wewnętrzne" kabiny (z powodu ActiveCab) //trzeba to zrobić inaczej, np. dla członu A sprawdzać, czy jest B //albo sprawdzać w momencie załączania WS i zmiany w sprzęgach if (((TestFlag(MoverParameters->Couplers[1].CouplingFlag,ctrain_controll))&&(MoverParameters->ActiveCab>0)&&(NextConnected->MoverParameters->TrainType!=dt_ET42))||((TestFlag(MoverParameters->Couplers[0].CouplingFlag,ctrain_controll))&&(MoverParameters->ActiveCab<0)&&(PrevConnected->MoverParameters->TrainType!=dt_ET42))) {//sprawdzenie, czy z tyłu kabiny mamy drugi człon if (MoverParameters->MainSwitch(false)) MoverParameters->EventFlag=true; } if ((!(TestFlag(MoverParameters->Couplers[1].CouplingFlag,ctrain_controll))&&(MoverParameters->ActiveCab>0))||(!(TestFlag(MoverParameters->Couplers[0].CouplingFlag,ctrain_controll))&&(MoverParameters->ActiveCab<0))) { if (MoverParameters->MainSwitch(false)) MoverParameters->EventFlag=true; } */ } //McZapkie-260202 - dMoveLen przyda sie przy stukocie kol dDOMoveLen=GetdMoveLen()+MoverParameters->ComputeMovement(dt,dt1,ts,tp,tmpTraction,l,r); //yB: zeby zawsze wrzucalo w jedna strone zakretu MoverParameters->AccN*=-ABuGetDirection(); //if (dDOMoveLen!=0.0) //Ra: nie może być, bo blokuje Event0 Move(dDOMoveLen); if (!bEnabled) //usuwane pojazdy nie mają toru {//pojazd do usunięcia Global::pGround->bDynamicRemove=true; //sprawdzić return false; } Global::ABuDebug=dDOMoveLen/dt1; ResetdMoveLen(); //McZapkie-260202 //tupot mew, tfu, stukot kol: DWORD stat; //taka prowizorka zeby sciszyc stukot dalekiej lokomotywy double ObjectDist; double vol=0; //double freq; //Ra: nie używane ObjectDist=SquareMagnitude(Global::pCameraPosition-vPosition); //McZapkie-270202 if (MyTrack->fSoundDistance!=-1) { if (ObjectDistiDamageFlag)/21; if (MyTrack->eEnvironment==e_tunnel) { vol*=1.1; //freq=1.02; } else if (MyTrack->eEnvironment==e_bridge) { vol*=1.2; //freq=0.99; //MC: stukot w zaleznosci od tego gdzie jest tor } if (MyTrack->fSoundDistance!=dRailLength) { dRailLength=MyTrack->fSoundDistance; for (int i=0; iDim.L; } } if (dRailLength!=-1) { if (abs(MoverParameters->V)>0) { for (int i=0; iAccV+= 0.5*GetVelocity()/(1+MoverParameters->Vmax); } else {rsStukot[i+1].Stop();} rsStukot[i].Play(vol,0,MechInside,vPosition); //poprawic pozycje o uklad osi if (i==1) MoverParameters->AccV-= 0.5*GetVelocity()/(1+MoverParameters->Vmax); dRailPosition[i]+=dRailLength; } } } } } } //McZapkie-260202 end //yB: przyspieszacz (moze zadziala, ale dzwiek juz jest) int flag=MoverParameters->Hamulec->GetSoundFlag(); if ((bBrakeAcc)&&(TestFlag(flag,sf_Acc))&&(ObjectDist<2500)) { sBrakeAcc->SetVolume(-ObjectDist*3-(FreeFlyModeFlag?0:2000)); sBrakeAcc->Play(0,0,0); sBrakeAcc->SetPan(10000*sin(ModCamRot)); } if ((rsUnbrake.AM!=0)&&(ObjectDist<5000)) { if ((TestFlag(flag,sf_CylU)) && ((MoverParameters->BrakePress*MoverParameters->MaxBrakePress[3])>0.05)) { vol=Min0R(0.2+1.6*sqrt((MoverParameters->BrakePress>0?MoverParameters->BrakePress:0)/MoverParameters->MaxBrakePress[3]),1); vol=vol+(FreeFlyModeFlag?0:-0.5)-ObjectDist/5000; rsUnbrake.SetPan(10000*sin(ModCamRot)); rsUnbrake.Play(vol,DSBPLAY_LOOPING,MechInside,GetPosition()); } else rsUnbrake.Stop(); } //fragment z EXE Kursa /* if (MoverParameters->TrainType==dt_ET42) { if ((MoverParameters->DynamicBrakeType=dbrake_switch) && ((MoverParameters->BrakePress > 0.2) || ( MoverParameters->PipePress < 0.36 ))) { MoverParameters->StLinFlag=true; } else if ((MoverParameters->DynamicBrakeType=dbrake_switch) && (MoverParameters->BrakePress < 0.1)) { MoverParameters->StLinFlag=false; } } */ if ((MoverParameters->TrainType==dt_ET40) || (MoverParameters->TrainType==dt_EP05)) {//dla ET40 i EU05 automatyczne cofanie nastawnika - i tak nie będzie to działać dobrze... /* if ((MoverParameters->MainCtrlPos>MoverParameters->MainCtrlActualPos)&&(abs(MoverParameters->Im)>MoverParameters->IminHi)) { MoverParameters->DecMainCtrl(1); } */ if (( !Console::Pressed(Global::Keys[k_IncMainCtrl]))&&(MoverParameters->MainCtrlPos>MoverParameters->MainCtrlActualPos)) { MoverParameters->DecMainCtrl(1); } if (( !Console::Pressed(Global::Keys[k_DecMainCtrl]))&&(MoverParameters->MainCtrlPosMainCtrlActualPos)) { MoverParameters->IncMainCtrl(1); //Ra 15-01: a to nie miało być tylko cofanie? } } if (MoverParameters->Vel!=0) {//McZapkie-050402: krecenie kolami: dWheelAngle[0]+=114.59155902616464175359630962821*MoverParameters->V*dt1/MoverParameters->WheelDiameterL; //przednie toczne dWheelAngle[1]+=MoverParameters->nrot*dt1*360.0; //napędne dWheelAngle[2]+=114.59155902616464175359630962821*MoverParameters->V*dt1/MoverParameters->WheelDiameterT; //tylne toczne if (dWheelAngle[0]>360.0) dWheelAngle[0]-=360.0; //a w drugą stronę jak się kręcą? if (dWheelAngle[1]>360.0) dWheelAngle[1]-=360.0; if (dWheelAngle[2]>360.0) dWheelAngle[2]-=360.0; } if (pants) //pantograf może być w wagonie kuchennym albo pojeździe rewizyjnym (np. SR61) {//przeliczanie kątów dla pantografów double k; //tymczasowy kąt double PantDiff; TAnimPant *p; //wskaźnik do obiektu danych pantografu double fCurrent=(MoverParameters->DynamicBrakeFlag&&MoverParameters->ResistorsFlag?0:fabs(MoverParameters->Itot))+MoverParameters->TotalCurrent; //prąd pobierany przez pojazd - bez sensu z tym (TotalCurrent) //fCurrent+=fabs(MoverParameters->Voltage)*1e-6; //prąd płynący przez woltomierz, rozładowuje kondensator orgromowy 4µF double fPantCurrent=fCurrent; //normalnie cały prąd przez jeden pantograf if (pants) if (iAnimType[ANIM_PANTS]>1) //a jeśli są dwa pantografy //Ra 1014-11: proteza, trzeba zrobić sensowniej if (pants[0].fParamPants->hvPowerWire&&pants[1].fParamPants->hvPowerWire) //i oba podłączone do drutów fPantCurrent=fCurrent*0.5; //to dzielimy prąd równo na oba (trochę bez sensu, ale lepiej tak niż podwoić prąd) for (int i=0;iPantWys<0) {//patograf został połamany, liczony nie będzie if (p->fAngleL>p->fAngleL0) p->fAngleL-=0.2*dt1; //nieco szybciej niż jak dla opuszczania if (p->fAngleLfAngleL0) p->fAngleL=p->fAngleL0; //kąt graniczny if (p->fAngleUfAngleU+=0.5*dt1; //górne się musi ruszać szybciej. if (p->fAngleU>M_PI) p->fAngleU=M_PI; if (i&1) //zgłoszono, że po połamaniu potrafi zostać zasilanie MoverParameters->PantRearVolt=0.0; else MoverParameters->PantFrontVolt=0.0; continue; //reszta wtedy nie jest wykonywana } PantDiff=p->PantTraction-p->PantWys; //docelowy-aktualny switch (i) //numer pantografu {//trzeba usunąć to rozróżnienie case 0: if (Global::bLiveTraction?false:!p->hvPowerWire) //jeśli nie ma drutu, może pooszukiwać MoverParameters->PantFrontVolt=(p->PantWys>=1.2)?0.95*MoverParameters->EnginePowerSource.MaxVoltage:0.0; else if (MoverParameters->PantFrontUp?(PantDiff<0.01):false) //tolerancja niedolegania { if ((MoverParameters->PantFrontVolt==0.0)&&(MoverParameters->PantRearVolt==0.0)) sPantUp.Play(vol,0,MechInside,vPosition); if (p->hvPowerWire) //TODO: wyliczyć trzeba prąd przypadający na pantograf i wstawić do GetVoltage() {MoverParameters->PantFrontVolt=p->hvPowerWire->VoltageGet(MoverParameters->Voltage,fPantCurrent); fCurrent-=fPantCurrent; //taki prąd płynie przez powyższy pantograf } else MoverParameters->PantFrontVolt=0.0; } else MoverParameters->PantFrontVolt=0.0; break; case 1: if (Global::bLiveTraction?false:!p->hvPowerWire) //jeśli nie ma drutu, może pooszukiwać MoverParameters->PantRearVolt=(p->PantWys>=1.2)?0.95*MoverParameters->EnginePowerSource.MaxVoltage:0.0; else if (MoverParameters->PantRearUp?(PantDiff<0.01):false) { if ((MoverParameters->PantRearVolt==0.0)&&(MoverParameters->PantFrontVolt==0.0)) sPantUp.Play(vol,0,MechInside,vPosition); if (p->hvPowerWire) //TODO: wyliczyć trzeba prąd przypadający na pantograf i wstawić do GetVoltage() {MoverParameters->PantRearVolt=p->hvPowerWire->VoltageGet(MoverParameters->Voltage,fPantCurrent); fCurrent-=fPantCurrent; //taki prąd płynie przez powyższy pantograf } else MoverParameters->PantRearVolt=0.0; } else MoverParameters->PantRearVolt=0.0; break; } //pozostałe na razie nie obsługiwane if (MoverParameters->PantPress>(MoverParameters->TrainType==dt_EZT?2.5:3.3)) //Ra 2013-12: Niebugocław mówi, że w EZT podnoszą się przy 2.5 pantspeedfactor=0.015*(MoverParameters->PantPress)*dt1; //z EXE Kursa //Ra: wysokość zależy od ciśnienia !!! else pantspeedfactor=0.0; if (pantspeedfactor<0) pantspeedfactor=0; k=p->fAngleL; if (i?MoverParameters->PantRearUp:MoverParameters->PantFrontUp) //jeśli ma być podniesiony {if (PantDiff>0.001) //jeśli nie dolega do drutu {//jeśli poprzednia wysokość jest mniejsza niż pożądana, zwiększyć kąt dolnego ramienia zgodnie z ciśnieniem if (pantspeedfactor>0.55*PantDiff) //0.55 to około pochodna kąta po wysokości k+=0.55*PantDiff; //ograniczenie "skoku" w danej klatce else k+=pantspeedfactor; //dolne ramię //jeśli przekroczono kąt graniczny, zablokować pantograf (wymaga interwencji pociągu sieciowego) } else if (PantDiff<-0.001) {//drut się obniżył albo pantograf podniesiony za wysoko //jeśli wysokość jest zbyt duża, wyznaczyć zmniejszenie kąta //jeśli zmniejszenie kąta jest zbyt duże, przejść do trybu łamania pantografu //if (PantFrontDiff<-0.05) //skok w dół o 5cm daje złąmanie pantografu k+=0.4*PantDiff; //mniej niż pochodna kąta po wysokości } //jeśli wysokość jest dobra, nic więcej nie liczyć } else {//jeśli ma być na dole if (k>p->fAngleL0) //jeśli wyżej niż położenie wyjściowe k-=0.15*dt1; //ruch w dół if (kfAngleL0) k=p->fAngleL0; //położenie minimalne } if (k!=p->fAngleL) {//żeby nie liczyć w kilku miejscach ani gdy nie potrzeba if (k+p->fAngleUfAngleL=k; //zmieniony kąt //wyliczyć kąt górnego ramienia z wzoru (a)cosinusowego //=acos((b*cos()+c)/a) //p->dPantAngleT=acos((1.22*cos(k)+0.535)/1.755); //górne ramię p->fAngleU=acos((p->fLenL1*cos(k)+p->fHoriz)/p->fLenU1); //górne ramię //wyliczyć aktualną wysokość z wzoru sinusowego //h=a*sin()+b*sin() p->PantWys=p->fLenL1*sin(k)+p->fLenU1*sin(p->fAngleU)+p->fHeight; //wysokość całości } } } //koniec pętli po pantografach if ((MoverParameters->PantFrontSP==false)&&(MoverParameters->PantFrontUp==false)) { sPantDown.Play(vol,0,MechInside,vPosition); MoverParameters->PantFrontSP=true; } if ((MoverParameters->PantRearSP==false)&&(MoverParameters->PantRearUp==false)) { sPantDown.Play(vol,0,MechInside,vPosition); MoverParameters->PantRearSP=true; } if (MoverParameters->EnginePowerSource.SourceType==CurrentCollector) {//Winger 240404 - wylaczanie sprezarki i przetwornicy przy braku napiecia if (tmpTraction.TractionVoltage==0) {//to coś wyłączało dźwięk silnika w ST43! MoverParameters->ConverterFlag=false; MoverParameters->CompressorFlag=false; //Ra: to jest wątpliwe - wyłączenie sprężarki powinno być w jednym miejscu! } } } else if (MoverParameters->EnginePowerSource.SourceType==InternalSource) if (MoverParameters->EnginePowerSource.PowerType==SteamPower) //if (smPatykird1[0]) {//Ra: animacja rozrządu parowozu, na razie nieoptymalizowane /* //Ra: tymczasowo wyłączone ze względu na porządkowanie animacji pantografów double fi,dx,c2,ka,kc; double sin_fi,cos_fi; double L1=1.6688888888888889; double L2=5.6666666666666667; //2550/450 double Lc=0.4; double L=5.686422222; //2558.89/450 double G1,G2,G3,ksi,sin_ksi,gam; double G1_2,G2_2,G3_2; //kwadraty //ruch tłoków oraz korbowodów for (int i=0;i<=1;++i) {//obie strony w ten sam sposób fi=DegToRad(dWheelAngle[1]+(i?pant2x:pant1x)); //kąt obrotu koła dla tłoka 1 sin_fi=sin(fi); cos_fi=cos(fi); dx=panty*cos_fi+sqrt(panth*panth-panty*panty*sin_fi*sin_fi)-panth; //nieoptymalne if (smPatykird1[i]) //na razie zabezpieczenie smPatykird1[i]->SetTranslate(float3(dx,0,0)); ka=-asin(panty/panth)*sin_fi; if (smPatykirg1[i]) //na razie zabezpieczenie smPatykirg1[i]->SetRotateXYZ(vector3(RadToDeg(ka),0,0)); //smPatykirg1[0]->SetRotate(float3(0,1,0),RadToDeg(fi)); //obracamy //ruch drążka mimośrodkowego oraz jarzma //korzystałem z pliku PDF "mm.pdf" (opis czworoboku korbowo-wahaczowego): //"MECHANIKA MASZYN. Szkic wykładu i laboratorium komputerowego." //Prof. dr hab. inż. Jerzy Zajączkowski, 2007, Politechnika Łódzka //L1 - wysokość (w pionie) osi jarzma ponad osią koła //L2 - odległość w poziomie osi jarzma od osi koła //Lc - długość korby mimośrodu na kole //Lr - promień jarzma =1.0 (pozostałe przeliczone proporcjonalnie) //L - długość drążka mimośrodowego //fi - kąt obrotu koła //ksi - kąt obrotu jarzma (od pionu) //gam - odchylenie drążka mimośrodowego od poziomu //G1=(Lr*Lr+L1*L1+L2*L2+Kc*Lc-L*L-2.0*Lc*L2*cos(fi)+2.0*Lc*L1*sin(fi))/(Lr*Lr); //G2=2.0*(L2-Lc*cos(fi))/Lr; //G3=2.0*(L1-Lc*sin(fi))/Lr; fi=DegToRad(dWheelAngle[1]+(i?pant2x:pant1x)-96.77416667); //kąt obrotu koła dla tłoka 1 //1) dla dWheelAngle[1]=0° korba jest w dół, a mimośród w stronę jarzma, czyli fi=-7° //2) dla dWheelAngle[1]=90° korba jest do tyłu, a mimośród w dół, czyli fi=83° sin_fi=sin(fi); cos_fi=cos(fi); G1=(1.0+L1*L1+L2*L2+Lc*Lc-L*L-2.0*Lc*L2*cos_fi+2.0*Lc*L1*sin_fi); G1_2=G1*G1; G2=2.0*(L2-Lc*cos_fi); G2_2=G2*G2; G3=2.0*(L1-Lc*sin_fi); G3_2=G3*G3; sin_ksi=(G1*G2-G3*_fm_sqrt(G2_2+G3_2-G1_2))/(G2_2+G3_2); //x1 (minus delta) ksi=asin(sin_ksi); //kąt jarzma if (smPatykirg2[i]) smPatykirg2[i]->SetRotateXYZ(vector3(RadToDeg(ksi),0,0)); //obrócenie jarzma //1) ksi=-23°, gam= //2) ksi=10°, gam= //gam=acos((L2-sin_ksi-Lc*cos_fi)/L); //kąt od poziomu, liczony względem poziomu //gam=asin((L1-cos_ksi-Lc*sin_fi)/L); //kąt od poziomu, liczony względem pionu gam=atan2((L1-cos(ksi)+Lc*sin_fi),(L2-sin_ksi+Lc*cos_fi)); //kąt od poziomu if (smPatykird2[i]) //na razie zabezpieczenie smPatykird2[i]->SetRotateXYZ(vector3(RadToDeg(-gam-ksi),0,0)); //obrócenie drążka mimośrodowego } */ } //NBMX Obsluga drzwi, MC: zuniwersalnione if ((dDoorMoveLDoorMaxShiftL) && (MoverParameters->DoorLeftOpened)) dDoorMoveL+=dt1*0.5*MoverParameters->DoorOpenSpeed; if ((dDoorMoveL>0) && (!MoverParameters->DoorLeftOpened)) { dDoorMoveL-=dt1*MoverParameters->DoorCloseSpeed; if (dDoorMoveL<0) dDoorMoveL=0; } if ((dDoorMoveRDoorMaxShiftR) && (MoverParameters->DoorRightOpened)) dDoorMoveR+=dt1*0.5*MoverParameters->DoorOpenSpeed; if ((dDoorMoveR>0) && (!MoverParameters->DoorRightOpened)) { dDoorMoveR-=dt1*MoverParameters->DoorCloseSpeed; if (dDoorMoveR<0) dDoorMoveR=0; } //ABu-160303 sledzenie toru przed obiektem: ******************************* //Z obserwacji: v>0 -> Coupler 0; v<0 ->coupler1 (Ra: prędkość jest związana z pojazdem) //Rozroznienie jest tutaj, zeby niepotrzebnie nie skakac do funkcji. Nie jest uzaleznione //od obecnosci AI, zeby uwzglednic np. jadace bez lokomotywy wagony. //Ra: można by przenieść na poziom obiektu reprezentującego skład, aby nie sprawdzać środkowych if (CouplCounter>25) //licznik, aby nie robić za każdym razem {//poszukiwanie czegoś do zderzenia się fTrackBlock=10000.0; //na razie nie ma przeszkód (na wypadek nie uruchomienia skanowania) //jeśli nie ma zwrotnicy po drodze, to tylko przeliczyć odległość? if (MoverParameters->V>0.03) //[m/s] jeśli jedzie do przodu (w kierunku Coupler 0) {if (MoverParameters->Couplers[0].CouplingFlag==ctrain_virtual) //brak pojazdu podpiętego? {ABuScanObjects(1,fScanDist); //szukanie czegoś do podłączenia //WriteLog(asName+" - block 0: "+AnsiString(fTrackBlock)); } } else if (MoverParameters->V<-0.03) //[m/s] jeśli jedzie do tyłu (w kierunku Coupler 1) if (MoverParameters->Couplers[1].CouplingFlag==ctrain_virtual) //brak pojazdu podpiętego? {ABuScanObjects(-1,fScanDist); //WriteLog(asName+" - block 1: "+AnsiString(fTrackBlock)); } CouplCounter=random(20); //ponowne sprawdzenie po losowym czasie } if (MoverParameters->Vel>0.1) //[km/h] ++CouplCounter; //jazda sprzyja poszukiwaniu połączenia else {CouplCounter=25; //a bezruch nie, ale trzeba zaktualizować odległość, bo zawalidroga może sobie pojechać } if (MoverParameters->DerailReason>0) {switch (MoverParameters->DerailReason) {case 1: ErrorLog("Bad driving: "+asName+" derailed due to end of track"); break; case 2: ErrorLog("Bad driving: "+asName+" derailed due to too high speed"); break; case 3: ErrorLog("Bad dynamic: "+asName+" derailed due to track width"); break; //błąd w scenerii case 4: ErrorLog("Bad dynamic: "+asName+" derailed due to wrong track type"); break; //błąd w scenerii } MoverParameters->DerailReason=0; //żeby tylko raz } if (MoverParameters->LoadStatus) LoadUpdate(); //zmiana modelu ładunku return true; //Ra: chyba tak? } bool __fastcall TDynamicObject::FastUpdate(double dt) { if (dt==0.0) return true; //Ra: pauza double dDOMoveLen; if (!MoverParameters->PhysicActivation) return true; //McZapkie: wylaczanie fizyki gdy nie potrzeba if (!bEnabled) return false; TLocation l; l.X=-vPosition.x; l.Y=vPosition.z; l.Z=vPosition.y; TRotation r; r.Rx=r.Ry=r.Rz= 0; //McZapkie: parametry powinny byc pobierane z toru //ts.R=MyTrack->fRadius; //ts.Len= Max0R(MoverParameters->BDist,MoverParameters->ADist); //ts.dHtrack=Axle1.pPosition.y-Axle0.pPosition.y; //ts.dHrail=((Axle1.GetRoll())+(Axle0.GetRoll()))*0.5f; //tp.Width=MyTrack->fTrackWidth; //McZapkie-250202 //tp.friction= MyTrack->fFriction; //tp.CategoryFlag= MyTrack->iCategoryFlag&15; //tp.DamageFlag=MyTrack->iDamageFlag; //tp.QualityFlag=MyTrack->iQualityFlag; dDOMoveLen=MoverParameters->FastComputeMovement(dt,ts,tp,l,r); // ,ts,tp,tmpTraction); //Move(dDOMoveLen); //ResetdMoveLen(); FastMove(dDOMoveLen); if (MoverParameters->LoadStatus) LoadUpdate(); //zmiana modelu ładunku return true; //Ra: chyba tak? } //McZapkie-040402: liczenie pozycji uwzgledniajac wysokosc szyn itp. //vector3 __fastcall TDynamicObject::GetPosition() //{//Ra: pozycja pojazdu jest liczona zaraz po przesunięciu // return vPosition; //}; void __fastcall TDynamicObject::TurnOff() {//wyłączenie rysowania submodeli zmiennych dla egemplarza pojazdu btnOn=false; btCoupler1.TurnOff(); btCoupler2.TurnOff(); btCPneumatic1.TurnOff(); btCPneumatic1r.TurnOff(); btCPneumatic2.TurnOff(); btCPneumatic2r.TurnOff(); btPneumatic1.TurnOff(); btPneumatic1r.TurnOff(); btPneumatic2.TurnOff(); btPneumatic2r.TurnOff(); btCCtrl1.TurnOff(); btCCtrl2.TurnOff(); btCPass1.TurnOff(); btCPass2.TurnOff(); btEndSignals11.TurnOff(); btEndSignals13.TurnOff(); btEndSignals21.TurnOff(); btEndSignals23.TurnOff(); btEndSignals1.TurnOff(); btEndSignals2.TurnOff(); btEndSignalsTab1.TurnOff(); btEndSignalsTab2.TurnOff(); btHeadSignals11.TurnOff(); btHeadSignals12.TurnOff(); btHeadSignals13.TurnOff(); btHeadSignals21.TurnOff(); btHeadSignals22.TurnOff(); btHeadSignals23.TurnOff(); }; void __fastcall TDynamicObject::Render() {//rysowanie elementów nieprzezroczystych //youBy - sprawdzamy, czy jest sens renderowac double modelrotate; vector3 tempangle; // zmienne renderme=false; //przeklejka double ObjSqrDist=SquareMagnitude(Global::pCameraPosition-vPosition); //koniec przeklejki if (ObjSqrDist<500) //jak jest blisko - do 70m modelrotate=0.01; //mały kąt, żeby nie znikało else {//Global::pCameraRotation to kąt bewzględny w świecie (zero - na północ) tempangle=(vPosition-Global::pCameraPosition); //wektor od kamery modelrotate=ABuAcos(tempangle); //określenie kąta //if (modelrotate>M_PI) modelrotate-=(2*M_PI); modelrotate+=Global::pCameraRotation; } if (modelrotate>M_PI) modelrotate-=(2*M_PI); if (modelrotate<-M_PI) modelrotate+=(2*M_PI); ModCamRot=modelrotate; modelrotate=abs(modelrotate); if (modelrotate) //tu trzeba by ustawić animacje na modelu zewnętrznym glLoadIdentity(); //zacząć od macierzy jedynkowej Global::pCamera->SetCabMatrix(vPosition); //specjalne ustawienie kamery } else glTranslated(vPosition.x,vPosition.y,vPosition.z); //standardowe przesunięcie względem początku scenerii glMultMatrixd(mMatrix.getArray()); if (fShade>0.0) {//Ra: 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}; //trochę problem z ambientem w wykopie... for (int li=0;li<3;li++) { ambientLight[li]= Global::ambientDayLight[li]*fShade; diffuseLight[li]= Global::diffuseDayLight[li]*fShade; specularLight[li]=Global::specularDayLight[li]*fShade; } glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight); glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight); glLightfv(GL_LIGHT0,GL_SPECULAR,specularLight); } if (Global::bUseVBO) {//wersja VBO if (mdLowPolyInt) if (FreeFlyModeFlag?true:!mdKabina||!bDisplayCab) mdLowPolyInt->RaRender(ObjSqrDist,ReplacableSkinID,iAlpha); mdModel->RaRender(ObjSqrDist,ReplacableSkinID,iAlpha); if (mdLoad) //renderowanie nieprzezroczystego ładunku mdLoad->RaRender(ObjSqrDist,ReplacableSkinID,iAlpha); if (mdPrzedsionek) mdPrzedsionek->RaRender(ObjSqrDist,ReplacableSkinID,iAlpha); } else {//wersja Display Lists if (mdLowPolyInt) if (FreeFlyModeFlag?true:!mdKabina||!bDisplayCab) mdLowPolyInt->Render(ObjSqrDist,ReplacableSkinID,iAlpha); mdModel->Render(ObjSqrDist,ReplacableSkinID,iAlpha); if (mdLoad) //renderowanie nieprzezroczystego ładunku mdLoad->Render(ObjSqrDist,ReplacableSkinID,iAlpha); if (mdPrzedsionek) mdPrzedsionek->Render(ObjSqrDist,ReplacableSkinID,iAlpha); } //Ra: czy ta kabina tu ma sens? //Ra: czy nie renderuje się dwukrotnie? //Ra: dlaczego jest zablokowana w przezroczystych? if (mdKabina) //jeśli ma model kabiny if ((mdKabina!=mdModel) && bDisplayCab && FreeFlyModeFlag) {//rendering kabiny gdy jest oddzielnym modelem i ma byc wyswietlana //ABu: tylko w trybie FreeFly, zwykly tryb w world.cpp //Ra: świetła są ustawione dla zewnętrza danego pojazdu //oswietlenie kabiny GLfloat ambientCabLight[4]= { 0.5f, 0.5f, 0.5f, 1.0f }; GLfloat diffuseCabLight[4]= { 0.5f, 0.5f, 0.5f, 1.0f }; GLfloat specularCabLight[4]= { 0.5f, 0.5f, 0.5f, 1.0f }; for (int li=0; li<3; li++) { ambientCabLight[li]= Global::ambientDayLight[li]*0.9; diffuseCabLight[li]= Global::diffuseDayLight[li]*0.5; specularCabLight[li]=Global::specularDayLight[li]*0.5; } switch (MyTrack->eEnvironment) { case e_canyon: { for (int li=0; li<3; li++) { diffuseCabLight[li]*= 0.6; specularCabLight[li]*= 0.7; } } break; case e_tunnel: { for (int li=0; li<3; li++) { ambientCabLight[li]*= 0.3; diffuseCabLight[li]*= 0.1; specularCabLight[li]*= 0.2; } } break; } glLightfv(GL_LIGHT0,GL_AMBIENT,ambientCabLight); glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseCabLight); glLightfv(GL_LIGHT0,GL_SPECULAR,specularCabLight); if (Global::bUseVBO) mdKabina->RaRender(ObjSqrDist,0); else mdKabina->Render(ObjSqrDist,0); glLightfv(GL_LIGHT0,GL_AMBIENT,Global::ambientDayLight); glLightfv(GL_LIGHT0,GL_DIFFUSE,Global::diffuseDayLight); glLightfv(GL_LIGHT0,GL_SPECULAR,Global::specularDayLight); } if (fShade!=0.0) //tylko jeśli było zmieniane {//przywrócenie standardowego oświetlenia glLightfv(GL_LIGHT0,GL_AMBIENT,Global::ambientDayLight); glLightfv(GL_LIGHT0,GL_DIFFUSE,Global::diffuseDayLight); glLightfv(GL_LIGHT0,GL_SPECULAR,Global::specularDayLight); } glPopMatrix(); if (btnOn) TurnOff(); //przywrócenie domyślnych pozycji submodeli } //yB - koniec mieszania z grafika }; void __fastcall TDynamicObject::RenderSounds() {//przeliczanie dźwięków, bo będzie słychać bez wyświetlania sektora z pojazdem //McZapkie-010302: ulepszony dzwiek silnika double freq; double vol=0; double dt=Timer::GetDeltaTime(); // double sounddist; // sounddist=SquareMagnitude(Global::pCameraPosition-vPosition); if (MoverParameters->Power>0) { if ((rsSilnik.AM!=0) && ((MoverParameters->Mains) || (MoverParameters->EngineType==DieselEngine))) //McZapkie-280503: zeby dla dumb dzialal silnik na jalowych obrotach { if ((fabs(MoverParameters->enrot)>0.01) || (MoverParameters->EngineType==Dumb)) //&& (MoverParameters->EnginePower>0.1)) { freq=rsSilnik.FM*fabs(MoverParameters->enrot)+rsSilnik.FA; if (MoverParameters->EngineType==Dumb) freq=freq-0.2*MoverParameters->EnginePower/(1+MoverParameters->Power*1000); rsSilnik.AdjFreq(freq,dt); if (MoverParameters->EngineType==DieselEngine) { if (MoverParameters->enrot>0) { if (MoverParameters->EnginePower>0) vol=rsSilnik.AM*MoverParameters->dizel_fill+rsSilnik.AA; else vol=rsSilnik.AM*fabs(MoverParameters->enrot/MoverParameters->nmax)+rsSilnik.AA*0.9; } else vol=0; } else if (MoverParameters->EngineType==DieselElectric) vol=rsSilnik.AM*(MoverParameters->EnginePower/1000/MoverParameters->Power)+0.2*(MoverParameters->enrot*60)/(MoverParameters->DElist[MoverParameters->MainCtrlPosNo].RPM)+rsSilnik.AA; else if (MoverParameters->EngineType==ElectricInductionMotor) vol=rsSilnik.AM*(MoverParameters->EnginePower+fabs(MoverParameters->enrot*2))+rsSilnik.AA; else vol=rsSilnik.AM*(MoverParameters->EnginePower/1000+fabs(MoverParameters->enrot)*60.0)+rsSilnik.AA; // McZapkie-250302 - natezenie zalezne od obrotow i mocy if ((vol<1) && (MoverParameters->EngineType==ElectricSeriesMotor) && (MoverParameters->EnginePower<100)) { float volrnd=random(100)*MoverParameters->enrot/(1+MoverParameters->nmax); if (volrnd<2) vol=vol+volrnd/200.0; } switch (MyTrack->eEnvironment) { case e_tunnel: { vol+=0.1; } break; case e_canyon: { vol+=0.05; } break; } if ((MoverParameters->DynamicBrakeFlag) && (MoverParameters->EnginePower>0.1) && (MoverParameters->EngineType==ElectricSeriesMotor)) //Szociu - 29012012 - jeżeli uruchomiony jest hamulec elektrodynamiczny, odtwarzany jest dźwięk silnika vol +=0.8; if (enginevolume>0.0001) if (MoverParameters->EngineType!=DieselElectric) { rsSilnik.Play(enginevolume,DSBPLAY_LOOPING,MechInside,GetPosition()); } else { sConverter.UpdateAF(vol,freq,MechInside,GetPosition()); float fincvol; fincvol=0; if ((MoverParameters->ConverterFlag)&&(MoverParameters->enrot*60>MoverParameters->DElist[0].RPM)) { fincvol=(MoverParameters->DElist[MoverParameters->MainCtrlPos].RPM-(MoverParameters->enrot*60)); fincvol/=(0.05*MoverParameters->DElist[0].RPM); }; if (fincvol>0.02) rsDiesielInc.Play(fincvol,DSBPLAY_LOOPING,MechInside,GetPosition()); else rsDiesielInc.Stop(); } } else rsSilnik.Stop(); } enginevolume=(enginevolume+vol)/2; if (enginevolume<0.01) rsSilnik.Stop(); if ((MoverParameters->EngineType==ElectricSeriesMotor)||(MoverParameters->EngineType==ElectricInductionMotor) && rsWentylator.AM!=0) { if (MoverParameters->RventRot>0.1) { freq=rsWentylator.FM*MoverParameters->RventRot+rsWentylator.FA; rsWentylator.AdjFreq(freq,dt); if (MoverParameters->EngineType==ElectricInductionMotor) vol=rsWentylator.AM*sqrt(fabs(MoverParameters->dizel_fill))+rsWentylator.AA; else vol=rsWentylator.AM*MoverParameters->RventRot+rsWentylator.AA; rsWentylator.Play(vol,DSBPLAY_LOOPING,MechInside,GetPosition()); } else rsWentylator.Stop(); } if (MoverParameters->TrainType==dt_ET40) { if (MoverParameters->Vel>0.1) { freq=rsPrzekladnia.FM*(MoverParameters->Vel)+rsPrzekladnia.FA; rsPrzekladnia.AdjFreq(freq,dt); vol=rsPrzekladnia.AM*(MoverParameters->Vel)+rsPrzekladnia.AA; rsPrzekladnia.Play(vol,DSBPLAY_LOOPING,MechInside,GetPosition()); } else rsPrzekladnia.Stop(); } } //youBy: dzwiek ostrych lukow i ciasnych zwrotek if ((ts.R*ts.R>1)&&(MoverParameters->Vel>0)) vol=MoverParameters->AccN*MoverParameters->AccN; else vol=0; // vol+=(50000/ts.R*ts.R); if (vol>0.001) { rscurve.Play(2*vol,DSBPLAY_LOOPING,MechInside,GetPosition()); } else rscurve.Stop(); //McZapkie-280302 - pisk mocno zacisnietych hamulcow - trzeba jeszcze zabezpieczyc przed brakiem deklaracji w mmedia.dta if (rsPisk.AM!=0) { if ((MoverParameters->Vel>(rsPisk.GetStatus()!=0?0.01:0.5)) && (!MoverParameters->SlippingWheels) && (MoverParameters->UnitBrakeForce>rsPisk.AM)) { vol=MoverParameters->UnitBrakeForce/(rsPisk.AM+1)+rsPisk.AA; rsPisk.Play(vol,DSBPLAY_LOOPING,MechInside,GetPosition()); } else rsPisk.Stop(); } //if ((MoverParameters->ConverterFlag==false) && (MoverParameters->TrainType!=dt_ET22)) //if ((MoverParameters->ConverterFlag==false)&&(MoverParameters->CompressorPower!=0)) // MoverParameters->CompressorFlag=false; //Ra: wywalić to stąd, tu tylko dla wyświetlanych! //Ra: no to już wiemy, dlaczego pociągi jeżdżą lepiej, gdy się na nie patrzy! //if (MoverParameters->CompressorPower==2) // MoverParameters->CompressorAllow=MoverParameters->ConverterFlag; // McZapkie! - dzwiek compressor.wav tylko gdy dziala sprezarka if (MoverParameters->VeselVolume!=0) { if (MoverParameters->CompressorFlag) sCompressor.TurnOn(MechInside,GetPosition()); else sCompressor.TurnOff(MechInside,GetPosition()); sCompressor.Update(MechInside,GetPosition()); } if (MoverParameters->PantCompFlag) // Winger 160404 - dzwiek malej sprezarki sSmallCompressor.TurnOn(MechInside,GetPosition()); else sSmallCompressor.TurnOff(MechInside,GetPosition()); sSmallCompressor.Update(MechInside,GetPosition()); //youBy - przenioslem, bo diesel tez moze miec turbo if ((MoverParameters->MainCtrlPos)>=(MoverParameters->TurboTest)) //hunter-250312: dlaczego zakomentowane? Ra: bo nie działało dobrze { //udawanie turbo: (6.66*(eng_vol-0.85)) if (eng_turbo>6.66*(enginevolume-0.8)+0.2*dt) eng_turbo=eng_turbo-0.2*dt; //0.125 else if (eng_turbo<6.66*(enginevolume-0.8)-0.4*dt) eng_turbo=eng_turbo+0.4*dt; //0.333 else eng_turbo=6.66*(enginevolume-0.8); sTurbo.TurnOn(MechInside,GetPosition()); //sTurbo.UpdateAF(eng_turbo,0.7+(eng_turbo*0.6),MechInside,GetPosition()); sTurbo.UpdateAF(3*eng_turbo-1,0.4+eng_turbo*0.4,MechInside,GetPosition()); // eng_vol_act=enginevolume; //eng_frq_act=eng_frq; } else sTurbo.TurnOff(MechInside,GetPosition()); if (MoverParameters->TrainType==dt_PseudoDiesel) { //ABu: udawanie woodwarda dla lok. spalinowych //jesli silnik jest podpiety pod dzwiek przetwornicy if (MoverParameters->ConverterFlag) //NBMX dzwiek przetwornicy { sConverter.TurnOn(MechInside,GetPosition()); } else sConverter.TurnOff(MechInside,GetPosition()); //glosnosc zalezy od stosunku mocy silnika el. do mocy max double eng_vol; if (MoverParameters->Power>1) //0.85+0.000015*(...) eng_vol=0.8+0.00002*(MoverParameters->EnginePower/MoverParameters->Power); else eng_vol=1; eng_dfrq=eng_dfrq+(eng_vol_act-eng_vol); if(eng_dfrq>0) { eng_dfrq=eng_dfrq-0.025*dt; if(eng_dfrq<0.025*dt) eng_dfrq=0; } else if(eng_dfrq<0) { eng_dfrq=eng_dfrq+0.025*dt; if(eng_dfrq>-0.025*dt) eng_dfrq=0; } double defrot; if (MoverParameters->MainCtrlPos!=0) { double CtrlPos=MoverParameters->MainCtrlPos; double CtrlPosNo=MoverParameters->MainCtrlPosNo; //defrot=1+0.4*(CtrlPos/CtrlPosNo); defrot=1+0.5*(CtrlPos/CtrlPosNo); } else defrot=1; if (eng_frq_actMainCtrlPos==1) eng_frq_act=eng_frq_act+0.1*dt; eng_frq_act=eng_frq_act+0.4*dt; //0.05 if (eng_frq_act>defrot-0.4*dt) eng_frq_act=defrot; } else if (eng_frq_act>defrot) { eng_frq_act=eng_frq_act-0.1*dt; //0.05 if (eng_frq_act6.66*(eng_vol-0.8)+0.2*dt) eng_turbo=eng_turbo-0.2*dt; //0.125 else if (eng_turbo<6.66*(eng_vol-0.8)-0.4*dt) eng_turbo=eng_turbo+0.4*dt; //0.333 else eng_turbo=6.66*(eng_vol-0.8); sTurbo.TurnOn(MechInside,GetPosition()); //sTurbo.UpdateAF(eng_turbo,0.7+(eng_turbo*0.6),MechInside,GetPosition()); sTurbo.UpdateAF(3*eng_turbo-1,0.4+eng_turbo*0.4,MechInside,GetPosition()); eng_vol_act=eng_vol; //eng_frq_act=eng_frq; } else { if (MoverParameters->ConverterFlag) //NBMX dzwiek przetwornicy sConverter.TurnOn(MechInside,GetPosition()); else sConverter.TurnOff(MechInside,GetPosition()); sConverter.Update(MechInside,GetPosition()); } if (MoverParameters->WarningSignal>0) { if (TestFlag(MoverParameters->WarningSignal,1)) sHorn1.TurnOn(MechInside,GetPosition()); else sHorn1.TurnOff(MechInside,GetPosition()); if (TestFlag(MoverParameters->WarningSignal,2)) sHorn2.TurnOn(MechInside,GetPosition()); else sHorn2.TurnOff(MechInside,GetPosition()); } else { sHorn1.TurnOff(MechInside,GetPosition()); sHorn2.TurnOff(MechInside,GetPosition()); } if (MoverParameters->DoorClosureWarning) { if (MoverParameters->DepartureSignal) //NBMX sygnal odjazdu, MC: pod warunkiem ze jest zdefiniowane w chk sDepartureSignal.TurnOn(MechInside,GetPosition()); else sDepartureSignal.TurnOff(MechInside,GetPosition()); sDepartureSignal.Update(MechInside,GetPosition()); } sHorn1.Update(MechInside,GetPosition()); sHorn2.Update(MechInside,GetPosition()); //McZapkie: w razie wykolejenia if (MoverParameters->EventFlag) { if (TestFlag(MoverParameters->DamageFlag,dtrain_out) && GetVelocity()>0) rsDerailment.Play(1,0,true,GetPosition()); if (GetVelocity()==0) rsDerailment.Stop(); } /* //Ra: dwa razy? if (MoverParameters->EventFlag) { if (TestFlag(MoverParameters->DamageFlag,dtrain_out) && GetVelocity()>0) rsDerailment.Play(1,0,true,GetPosition()); if (GetVelocity()==0) rsDerailment.Stop(); } */ }; void __fastcall TDynamicObject::RenderAlpha() {//rysowanie elementów półprzezroczystych if (renderme) { TSubModel::iInstance=(int)this; //żeby nie robić cudzych animacji double ObjSqrDist=SquareMagnitude(Global::pCameraPosition-vPosition); ABuLittleUpdate(ObjSqrDist); //ustawianie zmiennych submodeli dla wspólnego modelu glPushMatrix(); if (this==Global::pUserDynamic) {//specjalne ustawienie, aby nie trzęsło if (Global::bSmudge) {//jak smuga, to rysować po smudze glPopMatrix(); //to trzeba zebrać przed wyściem return; } glLoadIdentity(); //zacząć od macierzy jedynkowej Global::pCamera->SetCabMatrix(vPosition); //specjalne ustawienie kamery } else glTranslated(vPosition.x,vPosition.y,vPosition.z); //standardowe przesunięcie względem początku scenerii glMultMatrixd(mMatrix.getArray()); if (fShade>0.0) {//Ra: 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}; //trochę problem z ambientem w wykopie... for (int li=0;li<3;li++) { ambientLight[li]= Global::ambientDayLight[li]*fShade; diffuseLight[li]= Global::diffuseDayLight[li]*fShade; specularLight[li]=Global::specularDayLight[li]*fShade; } glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight); glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight); glLightfv(GL_LIGHT0,GL_SPECULAR,specularLight); } if (Global::bUseVBO) {//wersja VBO if (mdLowPolyInt) if (FreeFlyModeFlag?true:!mdKabina||!bDisplayCab) mdLowPolyInt->RaRenderAlpha(ObjSqrDist,ReplacableSkinID,iAlpha); mdModel->RaRenderAlpha(ObjSqrDist,ReplacableSkinID,iAlpha); if (mdLoad) mdLoad->RaRenderAlpha(ObjSqrDist,ReplacableSkinID,iAlpha); //if (mdPrzedsionek) //Ra: przedsionków tu wcześniej nie było - włączyć? // mdPrzedsionek->RaRenderAlpha(ObjSqrDist,ReplacableSkinID,iAlpha); } else {//wersja Display Lists if (mdLowPolyInt) if (FreeFlyModeFlag?true:!mdKabina||!bDisplayCab) mdLowPolyInt->RenderAlpha(ObjSqrDist,ReplacableSkinID,iAlpha); mdModel->RenderAlpha(ObjSqrDist,ReplacableSkinID,iAlpha); if (mdLoad) mdLoad->RenderAlpha(ObjSqrDist,ReplacableSkinID,iAlpha); //if (mdPrzedsionek) //Ra: przedsionków tu wcześniej nie było - włączyć? // mdPrzedsionek->RenderAlpha(ObjSqrDist,ReplacableSkinID,iAlpha); } /* skoro false to można wyciąc //ABu: Tylko w trybie freefly if (false)//((mdKabina!=mdModel) && bDisplayCab && FreeFlyModeFlag) { //oswietlenie kabiny GLfloat ambientCabLight[4]= { 0.5f, 0.5f, 0.5f, 1.0f }; GLfloat diffuseCabLight[4]= { 0.5f, 0.5f, 0.5f, 1.0f }; GLfloat specularCabLight[4]= { 0.5f, 0.5f, 0.5f, 1.0f }; for (int li=0; li<3; li++) { ambientCabLight[li]= Global::ambientDayLight[li]*0.9; diffuseCabLight[li]= Global::diffuseDayLight[li]*0.5; specularCabLight[li]= Global::specularDayLight[li]*0.5; } switch (MyTrack->eEnvironment) { case e_canyon: { for (int li=0; li<3; li++) { diffuseCabLight[li]*= 0.6; specularCabLight[li]*= 0.8; } } break; case e_tunnel: { for (int li=0; li<3; li++) { ambientCabLight[li]*= 0.3; diffuseCabLight[li]*= 0.1; specularCabLight[li]*= 0.2; } } break; } // dorobic swiatlo od drugiej strony szyby glLightfv(GL_LIGHT0,GL_AMBIENT,ambientCabLight); glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseCabLight); glLightfv(GL_LIGHT0,GL_SPECULAR,specularCabLight); mdKabina->RenderAlpha(ObjSqrDist,0); //smierdzi // mdModel->RenderAlpha(SquareMagnitude(Global::pCameraPosition-pos),0); glLightfv(GL_LIGHT0,GL_AMBIENT,Global::ambientDayLight); glLightfv(GL_LIGHT0,GL_DIFFUSE,Global::diffuseDayLight); glLightfv(GL_LIGHT0,GL_SPECULAR,Global::specularDayLight); } */ if (fShade!=0.0) //tylko jeśli było zmieniane {//przywrócenie standardowego oświetlenia glLightfv(GL_LIGHT0,GL_AMBIENT,Global::ambientDayLight); glLightfv(GL_LIGHT0,GL_DIFFUSE,Global::diffuseDayLight); glLightfv(GL_LIGHT0,GL_SPECULAR,Global::specularDayLight); } glPopMatrix(); if (btnOn) TurnOff(); //przywrócenie domyślnych pozycji submodeli } return; } //koniec renderalpha //McZapkie-250202 //wczytywanie pliku z danymi multimedialnymi (dzwieki) void __fastcall TDynamicObject::LoadMMediaFile(AnsiString BaseDir,AnsiString TypeName,AnsiString ReplacableSkin) { double dSDist; TFileStream *fs; //asBaseDir=BaseDir; Global::asCurrentDynamicPath=BaseDir; AnsiString asFileName=BaseDir+TypeName+".mmd"; AnsiString asLoadName=BaseDir+MoverParameters->LoadType+".t3d"; if (!FileExists(asFileName)) { ErrorLog("Missed file: "+asFileName); //brak MMD return; } fs=new TFileStream(asFileName,fmOpenRead|fmShareCompat); if (!fs) return; int size=fs->Size; if (!size) {return delete fs;}; AnsiString asAnimName; bool Stop_InternalData=false; char* buf=new char[size+1]; //ciąg bajtów o długości równej rozmiwarowi pliku buf[size]='\0'; //zakończony zerem na wszelki wypadek fs->Read(buf,size); delete fs; TQueryParserComp *Parser; Parser=new TQueryParserComp(NULL); Parser->TextToParse=AnsiString(buf); delete[] buf; AnsiString str; //Parser->LoadStringToParse(asFile); Parser->First(); //DecimalSeparator= '.'; pants=NULL; //wskaźnik pierwszego obiektu animującego dla pantografów int i; while (!Parser->EndOfFile && !Stop_InternalData) { str=Parser->GetNextSymbol().LowerCase(); if (str==AnsiString("models:")) //modele i podmodele { iMultiTex=0; //czy jest wiele tekstur wymiennych? asModel=Parser->GetNextSymbol().LowerCase(); if (asModel.Pos("#")==asModel.Length()) //Ra 2015-01: nie podoba mi się to {//model wymaga wielu tekstur wymiennych iMultiTex=1; asModel=asModel.SubString(1,asModel.Length()-1); } if ((i=asModel.Pos(","))>0) {//Ra 2015-01: może szukać przecinka w nazwie modelu, a po przecinku była by liczba tekstur? if (i1) iMultiTex=1; //na razie ustawiamy na 1 } asModel=BaseDir+asModel; //McZapkie 2002-07-20: dynamics maja swoje modele w dynamics/basedir Global::asCurrentTexturePath=BaseDir; //biezaca sciezka do tekstur to dynamic/... mdModel=TModelsManager::GetModel(asModel.c_str(),true); if (ReplacableSkin!=AnsiString("none")) {//tekstura wymienna jest raczej jedynie w "dynamic\" ReplacableSkin=Global::asCurrentTexturePath+ReplacableSkin; //skory tez z dynamic/... if ((i=ReplacableSkin.Pos("|"))>0) //replacable dzielone {iMultiTex=-1; ReplacableSkinID[-iMultiTex]=TTexturesManager::GetTextureID(NULL,NULL,ReplacableSkin.SubString(1,i-1).c_str(),Global::iDynamicFiltering); ReplacableSkin.Delete(1,i); //usunięcie razem z pionową kreską ReplacableSkin=Global::asCurrentTexturePath+ReplacableSkin; //odtworzenie początku ścieżki //sprawdzić, ile jest i ustawić iMultiTex na liczbę podanych tekstur if (!ReplacableSkin.IsEmpty()) {//próba wycięcia drugiej nazwy iMultiTex=-2; //skoro zostało coś po kresce, to są co najmniej dwie if ((i=ReplacableSkin.Pos("|"))==0) //gdy nie ma już kreski ReplacableSkinID[-iMultiTex]=TTexturesManager::GetTextureID(NULL,NULL,ReplacableSkin.SubString(1,i-1).c_str(),Global::iDynamicFiltering); else {//jak jest kreska, to wczytać drugą i próbować trzecią ReplacableSkinID[-iMultiTex]=TTexturesManager::GetTextureID(NULL,NULL,ReplacableSkin.SubString(1,i-1).c_str(),Global::iDynamicFiltering); ReplacableSkin.Delete(1,i); //usunięcie razem z pionową kreską ReplacableSkin=Global::asCurrentTexturePath+ReplacableSkin; //odtworzenie początku ścieżki if (!ReplacableSkin.IsEmpty()) {//próba wycięcia trzeciej nazwy iMultiTex=-3; //skoro zostało coś po kresce, to są co najmniej trzy if ((i=ReplacableSkin.Pos("|"))==0) //gdy nie ma już kreski ReplacableSkinID[-iMultiTex]=TTexturesManager::GetTextureID(NULL,NULL,ReplacableSkin.SubString(1,i-1).c_str(),Global::iDynamicFiltering); else {//jak jest kreska, to wczytać trzecią i próbować czwartą ReplacableSkinID[-iMultiTex]=TTexturesManager::GetTextureID(NULL,NULL,ReplacableSkin.SubString(1,i-1).c_str(),Global::iDynamicFiltering); ReplacableSkin.Delete(1,i); //usunięcie razem z pionową kreską ReplacableSkin=Global::asCurrentTexturePath+ReplacableSkin; //odtworzenie początku ścieżki if (!ReplacableSkin.IsEmpty()) {//próba wycięcia trzeciej nazwy iMultiTex=-4; //skoro zostało coś po kresce, to są co najmniej cztery ReplacableSkinID[-iMultiTex]=TTexturesManager::GetTextureID(NULL,NULL,ReplacableSkin.SubString(1,i-1).c_str(),Global::iDynamicFiltering); //więcej na razie nie zadziała, a u tak trzeba to do modeli przenieść } } } } } } if (iMultiTex>0) {//jeśli model ma 4 tekstury ReplacableSkinID[1]=TTexturesManager::GetTextureID(NULL,NULL,(ReplacableSkin+",1").c_str(),Global::iDynamicFiltering); if (ReplacableSkinID[1]) {//pierwsza z zestawu znaleziona ReplacableSkinID[2]=TTexturesManager::GetTextureID(NULL,NULL,(ReplacableSkin+",2").c_str(),Global::iDynamicFiltering); if (ReplacableSkinID[2]) {iMultiTex=2; //już są dwie ReplacableSkinID[3]=TTexturesManager::GetTextureID(NULL,NULL,(ReplacableSkin+",3").c_str(),Global::iDynamicFiltering); if (ReplacableSkinID[3]) {iMultiTex=3; //a teraz nawet trzy ReplacableSkinID[4]=TTexturesManager::GetTextureID(NULL,NULL,(ReplacableSkin+",4").c_str(),Global::iDynamicFiltering); if (ReplacableSkinID[4]) iMultiTex=4; //jak są cztery, to blokujemy podmianę tekstury rozkładem } } } else {//zestaw nie zadziałał, próbujemy normanie iMultiTex=0; ReplacableSkinID[1]=TTexturesManager::GetTextureID(NULL,NULL,ReplacableSkin.c_str(),Global::iDynamicFiltering); } } else ReplacableSkinID[1]=TTexturesManager::GetTextureID(NULL,NULL,ReplacableSkin.c_str(),Global::iDynamicFiltering); if (TTexturesManager::GetAlpha(ReplacableSkinID[1])) iAlpha=0x31310031; //tekstura -1 z kanałem alfa - nie renderować w cyklu nieprzezroczystych else iAlpha=0x30300030; //wszystkie tekstury nieprzezroczyste - nie renderować w cyklu przezroczystych if (ReplacableSkinID[2]) if (TTexturesManager::GetAlpha(ReplacableSkinID[2])) iAlpha|=0x02020002; //tekstura -2 z kanałem alfa - nie renderować w cyklu nieprzezroczystych if (ReplacableSkinID[3]) if (TTexturesManager::GetAlpha(ReplacableSkinID[3])) iAlpha|=0x04040004; //tekstura -3 z kanałem alfa - nie renderować w cyklu nieprzezroczystych if (ReplacableSkinID[4]) if (TTexturesManager::GetAlpha(ReplacableSkinID[4])) iAlpha|=0x08080008; //tekstura -4 z kanałem alfa - nie renderować w cyklu nieprzezroczystych } //Winger 040304 - ladowanie przedsionkow dla EZT if (MoverParameters->TrainType==dt_EZT) { asModel="przedsionki.t3d"; asModel=BaseDir+asModel; mdPrzedsionek=TModelsManager::GetModel(asModel.c_str(),true); } if (!MoverParameters->LoadAccepted.IsEmpty()) //if (MoverParameters->LoadAccepted!=AnsiString("")); // && MoverParameters->LoadType!=AnsiString("passengers")) if (MoverParameters->EnginePowerSource.SourceType==CurrentCollector) {//wartość niby "pantstate" - nazwa dla formalności, ważna jest ilość if (MoverParameters->Load==1) MoverParameters->PantFront(true); else if (MoverParameters->Load==2) MoverParameters->PantRear(true); else if (MoverParameters->Load==3) { MoverParameters->PantFront(true); MoverParameters->PantRear(true); } else if (MoverParameters->Load==4) MoverParameters->DoubleTr=-1; else if (MoverParameters->Load==5) { MoverParameters->DoubleTr=-1; MoverParameters->PantRear(true); } else if (MoverParameters->Load==6) { MoverParameters->DoubleTr=-1; MoverParameters->PantFront(true); } else if (MoverParameters->Load==7) { MoverParameters->DoubleTr=-1; MoverParameters->PantFront(true); MoverParameters->PantRear(true); } } else //Ra: tu wczytywanie modelu ładunku jest w porządku mdLoad=TModelsManager::GetModel(asLoadName.c_str(),true); //ladunek Global::asCurrentTexturePath=AnsiString(szTexturePath); //z powrotem defaultowa sciezka do tekstur while (!Parser->EndOfFile && str!=AnsiString("endmodels")) { str=Parser->GetNextSymbol().LowerCase(); if (str==AnsiString("animations:")) {//Ra: ustawienie ilości poszczególnych animacji - musi być jako pierwsze, inaczej ilości będą domyślne if (!pAnimations) {//jeśli nie ma jeszcze tabeli animacji, można odczytać nowe ilości int co=0,ile; iAnimations=0; do {//kolejne liczby to ilość animacj, -1 to znacznik końca ile=Parser->GetNextSymbol().ToIntDef(-1); //ilość danego typu animacji //if (co==ANIM_PANTS) // if (!Global::bLoadTraction) // if (!DebugModeFlag) //w debugmode pantografy mają "niby działać" // ile=0; //wyłączenie animacji pantografów if (co=0) {iAnimType[co]=ile; //zapamiętanie iAnimations+=ile; //ogólna ilość animacji } ++co; } while (ile>=0); //-1 to znacznik końca while (coGetNextSymbol().LowerCase(); } //WriteLog("Total animations: "+AnsiString(iAnimations)); } if (!pAnimations) {//Ra: tworzenie tabeli animacji, jeśli jeszcze nie było if (!iAnimations) //jeśli nie podano jawnie, ile ma być animacji iAnimations=28; //tyle było kiedyś w każdym pojeździe (2 wiązary wypadły) /* //pojazd może mieć pantograf do innych celów niż napęd if (MoverParameters->EnginePowerSource.SourceType!=CurrentCollector) {//nie będzie pantografów, to się trochę uprości iAnimations-=iAnimType[ANIM_PANTS]; //domyślnie były 2 pantografy iAnimType[ANIM_PANTS]=0; } */ pAnimations=new TAnim[iAnimations]; int i,j,k=0,sm=0; for (j=0;jGetNextSymbol().LowerCase(); asModel=BaseDir+asModel; //McZapkie-200702 - dynamics maja swoje modele w dynamic/basedir Global::asCurrentTexturePath=BaseDir; //biezaca sciezka do tekstur to dynamic/... mdLowPolyInt=TModelsManager::GetModel(asModel.c_str(),true); //Global::asCurrentTexturePath=AnsiString(szTexturePath); //kiedyś uproszczone wnętrze mieszało tekstury nieba } if (str==AnsiString("brakemode:")) {//Ra 15-01: gałka nastawy hamulca asAnimName=Parser->GetNextSymbol().LowerCase(); smBrakeMode=mdModel->GetFromName(asAnimName.c_str()); //jeszcze wczytać kąty obrotu dla poszczególnych ustawień } if (str==AnsiString("loadmode:")) {//Ra 15-01: gałka nastawy hamulca asAnimName=Parser->GetNextSymbol().LowerCase(); smLoadMode=mdModel->GetFromName(asAnimName.c_str()); //jeszcze wczytać kąty obrotu dla poszczególnych ustawień } else if (str==AnsiString("animwheelprefix:")) {//prefiks kręcących się kół int i,j,k,m; str=Parser->GetNextSymbol(); for (i=0;iGetFromName(asAnimName.c_str()); //ustalenie submodelu if (pAnimations[i].smAnimated) {//++iAnimatedAxles; pAnimations[i].smAnimated->WillBeAnimated(); //wyłączenie optymalizacji transformu pAnimations[i].yUpdate=UpdateAxle; //animacja osi pAnimations[i].fMaxDist=50*MoverParameters->WheelDiameter; //nie kręcić w większej odległości pAnimations[i].fMaxDist*=pAnimations[i].fMaxDist*MoverParameters->WheelDiameter; //50m do kwadratu, a średnica do trzeciej pAnimations[i].fMaxDist*=Global::fDistanceFactor; //współczynnik przeliczeniowy jakości ekranu } } //Ra: ustawianie indeksów osi for (i=0;iWheelDiameterL!=MoverParameters->WheelDiameter)||(MoverParameters->WheelDiameterT!=MoverParameters->WheelDiameter)) {//obsługa różnych średnic, o ile występują while ((iAxleArangement.Length())) {//wersja ze wskaźnikami jest bardziej elastyczna na nietypowe układy if ((k>='A')&&(k<='J')) //10 chyba maksimum? {pAnimations[i++].dWheelAngle=dWheelAngle+1; //obrót osi napędzających --k; //następna będzie albo taka sama, albo bierzemy kolejny znak m=2; //następujące toczne będą miały inną średnicę } else if ((k>='1')&&(k<='9')) {pAnimations[i++].dWheelAngle=dWheelAngle+m; //obrót osi tocznych --k; //następna będzie albo taka sama, albo bierzemy kolejny znak } else k=MoverParameters->AxleArangement[j++]; //pobranie kolejnego znaku } } } //else if (str==AnsiString("animrodprefix:")) //prefiks wiazarow dwoch // { // str= Parser->GetNextSymbol(); // for (int i=1; i<=2; i++) // {//McZapkie-050402: wyszukiwanie max 2 wiazarow o nazwie str* // asAnimName=str+i; // smWiazary[i-1]=mdModel->GetFromName(asAnimName.c_str()); // smWiazary[i-1]->WillBeAnimated(); // } // } else if (str==AnsiString("animpantprefix:")) {//Ra: pantografy po nowemu mają literki i numerki } //Pantografy - Winger 160204 if (str==AnsiString("animpantrd1prefix:")) {//prefiks ramion dolnych 1 str=Parser->GetNextSymbol(); float4x4 m; //macierz do wyliczenia pozycji i wektora ruchu pantografu TSubModel *sm; if (pants) for (int i=0;iGetFromName(asAnimName.c_str()); pants[i].smElement[0]=sm; //jak NULL, to nie będzie animowany if (sm) {//w EP09 wywalało się tu z powodu NULL sm->WillBeAnimated(); sm->ParentMatrix(&m); //pobranie macierzy transformacji //m(3)[1]=m[3][1]+0.054; //w górę o wysokość ślizgu (na razie tak) if ((mdModel->Flags()&0x8000)==0) //jeśli wczytano z T3D m.InitialRotate(); //może być potrzebny dodatkowy obrót, jeśli wczytano z T3D, tzn. przed wykonaniem Init() pants[i].fParamPants->vPos.z=m[3][0]; //przesunięcie w bok (asymetria) pants[i].fParamPants->vPos.y=m[3][1]; //przesunięcie w górę odczytane z modelu if ((sm=pants[i].smElement[0]->ChildGet())!=NULL) {//jeśli ma potomny, można policzyć długość (odległość potomnego od osi obrotu) m=float4x4(*sm->GetMatrix()); //wystarczyłby wskaźnik, nie trzeba kopiować //może trzeba: pobrać macierz dolnego ramienia, wyzerować przesunięcie, przemnożyć przez macierz górnego pants[i].fParamPants->fHoriz=-fabs(m[3][1]); pants[i].fParamPants->fLenL1=hypot(m[3][1],m[3][2]); //po osi OX nie potrzeba pants[i].fParamPants->fAngleL0=atan2(fabs(m[3][2]),fabs(m[3][1])); //if (pants[i].fParamPants->fAngleL0fAngleL0+=M_PI; //gdyby w odwrotną stronę wyszło //if ((pants[i].fParamPants->fAngleL0<0.03)||(pants[i].fParamPants->fAngleL0>0.09)) //normalnie ok. 0.05 // pants[i].fParamPants->fAngleL0=pants[i].fParamPants->fAngleL; pants[i].fParamPants->fAngleL=pants[i].fParamPants->fAngleL0; //początkowy kąt dolnego ramienia if ((sm=sm->ChildGet())!=NULL) {//jeśli dalej jest ślizg, można policzyć długość górnego ramienia m=float4x4(*sm->GetMatrix()); //wystarczyłby wskaźnik, nie trzeba kopiować //trzeba by uwzględnić macierz dolnego ramienia, żeby uzyskać kąt do poziomu... pants[i].fParamPants->fHoriz+=fabs(m(3)[1]); //różnica długości rzutów ramion na płaszczyznę podstawy (jedna dodatnia, druga ujemna) pants[i].fParamPants->fLenU1=hypot(m[3][1],m[3][2]); //po osi OX nie potrzeba //pants[i].fParamPants->pantu=acos((1.22*cos(pants[i].fParamPants->fAngleL)+0.535)/1.755); //górne ramię //pants[i].fParamPants->fAngleU0=acos((1.176289*cos(pants[i].fParamPants->fAngleL)+0.54555075)/1.724482197); //górne ramię pants[i].fParamPants->fAngleU0=atan2(fabs(m[3][2]),fabs(m[3][1])); //początkowy kąt górnego ramienia, odczytany z modelu //if (pants[i].fParamPants->fAngleU0fAngleU0+=M_PI; //gdyby w odwrotną stronę wyszło //if (pants[i].fParamPants->fAngleU0<0) // pants[i].fParamPants->fAngleU0=-pants[i].fParamPants->fAngleU0; //if ((pants[i].fParamPants->fAngleU0<0.00)||(pants[i].fParamPants->fAngleU0>0.09)) //normalnie ok. 0.07 // pants[i].fParamPants->fAngleU0=acos((pants[i].fParamPants->fLenL1*cos(pants[i].fParamPants->fAngleL)+pants[i].fParamPants->fHoriz)/pants[i].fParamPants->fLenU1); pants[i].fParamPants->fAngleU=pants[i].fParamPants->fAngleU0; //początkowy kąt //Ra: ze względu na to, że niektóre modele pantografów są zrąbane, ich mierzenie ma obecnie ograniczony sens sm->ParentMatrix(&m); //pobranie macierzy transformacji pivota ślizgu względem wstawienia pojazdu if ((mdModel->Flags()&0x8000)==0) //jeśli wczytano z T3D m.InitialRotate(); //może być potrzebny dodatkowy obrót, jeśli wczytano z T3D, tzn. przed wykonaniem Init() float det=Det(m); if (fabs(det-1.0)<0.001) //dopuszczamy 1 promil błędu na skalowaniu ślizgu {//skalowanie jest w normie, można pobrać wymiary z modelu pants[i].fParamPants->fHeight=sm->MaxY(m); //przeliczenie maksimum wysokości wierzchołków względem macierzy pants[i].fParamPants->fHeight-=m[3][1]; //odjęcie wysokości pivota ślizgu pants[i].fParamPants->vPos.x=m[3][2]; //przy okazji odczytać z modelu pozycję w długości //ErrorLog("Model OK: "+asModel+", height="+pants[i].fParamPants->fHeight); //ErrorLog("Model OK: "+asModel+", pos.x="+pants[i].fParamPants->vPos.x); } else {//gdy ktoś przesadził ze skalowaniem pants[i].fParamPants->fHeight=0.0; //niech będzie odczyt z pantfactors: ErrorLog("Bad model: "+asModel+", scale of "+AnsiString(sm->pName)+" is "+AnsiString(100.0*det)+"%"); } } } } else ErrorLog("Bad model: "+asFileName+" - missed submodel "+asAnimName); //brak ramienia } } else if (str==AnsiString("animpantrd2prefix:")) {//prefiks ramion dolnych 2 str=Parser->GetNextSymbol(); float4x4 m; //macierz do wyliczenia pozycji i wektora ruchu pantografu TSubModel *sm; if (pants) for (int i=0;iGetFromName(asAnimName.c_str()); pants[i].smElement[1]=sm; //jak NULL, to nie będzie animowany if (sm) {//w EP09 wywalało się tu z powodu NULL sm->WillBeAnimated(); if (pants[i].fParamPants->vPos.y==0.0) {//jeśli pierwsze ramię nie ustawiło tej wartości, próbować drugim //!!!! docelowo zrobić niezależną animację ramion z każdej strony m=float4x4(*sm->GetMatrix()); //skopiowanie, bo będziemy mnożyć m(3)[1]=m[3][1]+0.054; //w górę o wysokość ślizgu (na razie tak) while (sm->Parent) { if (sm->Parent->GetMatrix()) m=*sm->Parent->GetMatrix()*m; sm=sm->Parent; } pants[i].fParamPants->vPos.z=m[3][0]; //przesunięcie w bok (asymetria) pants[i].fParamPants->vPos.y=m[3][1]; //przesunięcie w górę odczytane z modelu } } else ErrorLog("Bad model: "+asFileName+" - missed submodel "+asAnimName); //brak ramienia } } else if (str==AnsiString("animpantrg1prefix:")) {//prefiks ramion górnych 1 str=Parser->GetNextSymbol(); if (pants) for (int i=0;iGetFromName(asAnimName.c_str()); pants[i].smElement[2]->WillBeAnimated(); } } else if (str==AnsiString("animpantrg2prefix:")) {//prefiks ramion górnych 2 str=Parser->GetNextSymbol(); if (pants) for (int i=0;iGetFromName(asAnimName.c_str()); pants[i].smElement[3]->WillBeAnimated(); } } else if (str==AnsiString("animpantslprefix:")) {//prefiks ślizgaczy str=Parser->GetNextSymbol(); if (pants) for (int i=0;iGetFromName(asAnimName.c_str()); pants[i].smElement[4]->WillBeAnimated(); pants[i].yUpdate=UpdatePant; pants[i].fMaxDist=300*300; //nie podnosić w większej odległości pants[i].iNumber=i; } } else if (str==AnsiString("pantfactors:")) {//Winger 010304: parametry pantografow double pant1x=Parser->GetNextSymbol().ToDouble(); double pant2x=Parser->GetNextSymbol().ToDouble(); double pant1h=Parser->GetNextSymbol().ToDouble(); //wysokość pierwszego ślizgu double pant2h=Parser->GetNextSymbol().ToDouble(); //wysokość drugiego ślizgu if (pant1h>0.5) pant1h=pant2h; //tu może być zbyt duża wartość if ((pant1x<0)&&(pant2x>0)) //pierwsza powinna być dodatnia, a druga ujemna {pant1x=-pant1x; pant2x=-pant2x;} if (pants) for (int i=0;ifAngleL=pants[i].fParamPants->fAngleL0; //początkowy kąt dolnego ramienia pants[i].fParamPants->fAngleU=pants[i].fParamPants->fAngleU0; //początkowy kąt //pants[i].fParamPants->PantWys=1.22*sin(pants[i].fParamPants->fAngleL)+1.755*sin(pants[i].fParamPants->fAngleU); //wysokość początkowa //pants[i].fParamPants->PantWys=1.176289*sin(pants[i].fParamPants->fAngleL)+1.724482197*sin(pants[i].fParamPants->fAngleU); //wysokość początkowa if (pants[i].fParamPants->fHeight==0.0) //gdy jest nieprawdopodobna wartość (np. nie znaleziony ślizg) {//gdy pomiary modelu nie udały się, odczyt podanych parametrów z MMD pants[i].fParamPants->vPos.x=(i&1)?pant2x:pant1x; pants[i].fParamPants->fHeight=(i&1)?pant2h:pant1h; //wysokość ślizgu jest zapisana w MMD } pants[i].fParamPants->PantWys=pants[i].fParamPants->fLenL1*sin(pants[i].fParamPants->fAngleL)+pants[i].fParamPants->fLenU1*sin(pants[i].fParamPants->fAngleU)+pants[i].fParamPants->fHeight; //wysokość początkowa //pants[i].fParamPants->vPos.y=panty-panth-pants[i].fParamPants->PantWys; //np. 4.429-0.097=4.332=~4.335 //pants[i].fParamPants->vPos.z=0; //niezerowe dla pantografów asymetrycznych pants[i].fParamPants->PantTraction=pants[i].fParamPants->PantWys; pants[i].fParamPants->fWidth=0.5*MoverParameters->EnginePowerSource.CollectorParameters.CSW; //połowa szerokości ślizgu; jest w "Power: CSW=" } } else if (str==AnsiString("animpistonprefix:")) {//prefiks tłoczysk - na razie używamy modeli pantografów str=Parser->GetNextSymbol(); for (int i=1;i<=2;i++) { //asAnimName=str+i; //smPatykird1[i-1]=mdModel->GetFromName(asAnimName.c_str()); //smPatykird1[i-1]->WillBeAnimated(); } } else if (str==AnsiString("animconrodprefix:")) {//prefiks korbowodów - na razie używamy modeli pantografów str=Parser->GetNextSymbol(); for (int i=1;i<=2;i++) { //asAnimName=str+i; //smPatykirg1[i-1]=mdModel->GetFromName(asAnimName.c_str()); //smPatykirg1[i-1]->WillBeAnimated(); } } else if (str==AnsiString("pistonfactors:")) {//Ra: parametry silnika parowego (tłoka) /* //Ra: tymczasowo wyłączone ze względu na porządkowanie animacji pantografów pant1x=Parser->GetNextSymbol().ToDouble(); //kąt przesunięcia dla pierwszego tłoka pant2x=Parser->GetNextSymbol().ToDouble(); //kąt przesunięcia dla drugiego tłoka panty=Parser->GetNextSymbol().ToDouble(); //długość korby (r) panth=Parser->GetNextSymbol().ToDouble(); //długoś korbowodu (k) */ MoverParameters->EnginePowerSource.PowerType=SteamPower; //Ra: po chamsku, ale z CHK nie działa } else if (str==AnsiString("animreturnprefix:")) {//prefiks drążka mimośrodowego - na razie używamy modeli pantografów str=Parser->GetNextSymbol(); for (int i=1;i<=2;i++) { //asAnimName=str+i; //smPatykird2[i-1]=mdModel->GetFromName(asAnimName.c_str()); //smPatykird2[i-1]->WillBeAnimated(); } } else if (str==AnsiString("animexplinkprefix:")) //animreturnprefix: {//prefiks jarzma - na razie używamy modeli pantografów str=Parser->GetNextSymbol(); for (int i=1;i<=2;i++) { //asAnimName=str+i; //smPatykirg2[i-1]=mdModel->GetFromName(asAnimName.c_str()); //smPatykirg2[i-1]->WillBeAnimated(); } } else if (str==AnsiString("animpendulumprefix:")) {//prefiks wahaczy str=Parser->GetNextSymbol(); asAnimName=""; for (int i=1; i<=4; i++) {//McZapkie-050402: wyszukiwanie max 4 wahaczy o nazwie str* asAnimName=str+AnsiString(i); smWahacze[i-1]=mdModel->GetFromName(asAnimName.c_str()); smWahacze[i-1]->WillBeAnimated(); } str=Parser->GetNextSymbol().LowerCase(); if (str==AnsiString("pendulumamplitude:")) fWahaczeAmp=Parser->GetNextSymbol().ToDouble(); } else if (str==AnsiString("engineer:")) {//nazwa submodelu maszynisty str=Parser->GetNextSymbol(); smMechanik0=mdModel->GetFromName(str.c_str()); if (!smMechanik0) {//jak nie ma bez numerka, to może jest z numerkiem? smMechanik0=mdModel->GetFromName(AnsiString(str+"1").c_str()); smMechanik1=mdModel->GetFromName(AnsiString(str+"2").c_str()); } //aby dało się go obracać, musi mieć włączoną animację w T3D! //if (!smMechanik1) //jeśli drugiego nie ma // if (smMechanik0) //a jest pierwszy // smMechanik0->WillBeAnimated(); //to będziemy go obracać } else if (str==AnsiString("animdoorprefix:")) {//nazwa animowanych drzwi int i,j,k,m; str=Parser->GetNextSymbol(); for (i=0,j=0;iGetFromName(asAnimName.c_str()); //ustalenie submodelu if (pAnimations[i+j].smAnimated) {//++iAnimatedDoors; pAnimations[i+j].smAnimated->WillBeAnimated(); //wyłączenie optymalizacji transformu switch (MoverParameters->DoorOpenMethod) {//od razu zapinamy potrzebny typ animacji case 1: pAnimations[i+j].yUpdate=UpdateDoorTranslate; break; case 2: pAnimations[i+j].yUpdate=UpdateDoorRotate; break; case 3: pAnimations[i+j].yUpdate=UpdateDoorFold; break; //obrót 3 kolejnych submodeli } pAnimations[i+j].iNumber=i; //parzyste działają inaczej niż nieparzyste pAnimations[i+j].fMaxDist=300*300; //drzwi to z daleka widać pAnimations[i+j].fSpeed=random(150); //oryginalny koncept z DoorSpeedFactor pAnimations[i+j].fSpeed=(pAnimations[i+j].fSpeed+100)/100; //Ra: te współczynniki są bez sensu, bo modyfikują wektor przesunięcia } } } } } else if (str==AnsiString("sounds:")) //dzwieki while (!Parser->EndOfFile && str!=AnsiString("endsounds")) { str= Parser->GetNextSymbol().LowerCase(); if (str==AnsiString("wheel_clatter:")) //polozenia osi w/m srodka pojazdu { dSDist=Parser->GetNextSymbol().ToDouble(); for (int i=0; iGetNextSymbol(); dWheelsPosition[i]=str.ToDouble(); str= Parser->GetNextSymbol().LowerCase(); if (str!=AnsiString("end")) rsStukot[i].Init(str.c_str(),dSDist,GetPosition().x,GetPosition().y+dWheelsPosition[i],GetPosition().z,true); } if (str!=AnsiString("end")) str= Parser->GetNextSymbol(); } else if ((str==AnsiString("engine:")) && (MoverParameters->Power>0)) //plik z dzwiekiem silnika, mnozniki i ofsety amp. i czest. { str= Parser->GetNextSymbol(); rsSilnik.Init(str.c_str(),Parser->GetNextSymbol().ToDouble(),GetPosition().x,GetPosition().y,GetPosition().z,true,true); if (rsSilnik.GetWaveTime()==0) ErrorLog("Missed sound: \""+str+"\" for "+asFileName); if (MoverParameters->EngineType==DieselEngine) rsSilnik.AM=Parser->GetNextSymbol().ToDouble()/(MoverParameters->Power+MoverParameters->nmax*60); else if (MoverParameters->EngineType==DieselElectric) rsSilnik.AM=Parser->GetNextSymbol().ToDouble()/(MoverParameters->Power*3); else rsSilnik.AM=Parser->GetNextSymbol().ToDouble()/(MoverParameters->Power+MoverParameters->nmax*60+MoverParameters->Power+MoverParameters->Power); rsSilnik.AA=Parser->GetNextSymbol().ToDouble(); rsSilnik.FM=Parser->GetNextSymbol().ToDouble();//MoverParameters->nmax; rsSilnik.FA=Parser->GetNextSymbol().ToDouble(); } else if (((str==AnsiString("ventilator:")) && ((MoverParameters->EngineType==ElectricSeriesMotor)||(MoverParameters->EngineType==ElectricInductionMotor)))) //plik z dzwiekiem wentylatora, mnozniki i ofsety amp. i czest. { str= Parser->GetNextSymbol(); rsWentylator.Init(str.c_str(),Parser->GetNextSymbol().ToDouble(),GetPosition().x,GetPosition().y,GetPosition().z,true,true); rsWentylator.AM=Parser->GetNextSymbol().ToDouble()/MoverParameters->RVentnmax; rsWentylator.AA=Parser->GetNextSymbol().ToDouble(); rsWentylator.FM=Parser->GetNextSymbol().ToDouble()/MoverParameters->RVentnmax; rsWentylator.FA=Parser->GetNextSymbol().ToDouble(); } else if ((str==AnsiString("transmission:")) && (MoverParameters->EngineType==ElectricSeriesMotor)) //plik z dzwiekiem, mnozniki i ofsety amp. i czest. { str= Parser->GetNextSymbol(); rsPrzekladnia.Init(str.c_str(),Parser->GetNextSymbol().ToDouble(),GetPosition().x,GetPosition().y,GetPosition().z,true); rsPrzekladnia.AM=0.029; rsPrzekladnia.AA=0.1; rsPrzekladnia.FM=0.005; rsPrzekladnia.FA=1.0; } else if (str==AnsiString("brake:")) //plik z piskiem hamulca, mnozniki i ofsety amplitudy. { str= Parser->GetNextSymbol(); rsPisk.Init(str.c_str(),Parser->GetNextSymbol().ToDouble(),GetPosition().x,GetPosition().y,GetPosition().z,true); rsPisk.AM=Parser->GetNextSymbol().ToDouble(); rsPisk.AA=Parser->GetNextSymbol().ToDouble()*(105-random(10))/100; rsPisk.FM=1.0; rsPisk.FA=0.0; } else if (str==AnsiString("brakeacc:")) //plik z przyspieszaczem (upust po zlapaniu hamowania) { str= Parser->GetNextSymbol(); // sBrakeAcc.Init(str.c_str(),Parser->GetNextSymbol().ToDouble(),GetPosition().x,GetPosition().y,GetPosition().z,true); sBrakeAcc=TSoundsManager::GetFromName(str.c_str(),true); bBrakeAcc=true; // sBrakeAcc.AM=1.0; // sBrakeAcc.AA=0.0; // sBrakeAcc.FM=1.0; // sBrakeAcc.FA=0.0; } else if (str==AnsiString("unbrake:")) //plik z piskiem hamulca, mnozniki i ofsety amplitudy. { str= Parser->GetNextSymbol(); rsUnbrake.Init(str.c_str(),Parser->GetNextSymbol().ToDouble(),GetPosition().x,GetPosition().y,GetPosition().z,true); rsUnbrake.AM=1.0; rsUnbrake.AA=0.0; rsUnbrake.FM=1.0; rsUnbrake.FA=0.0; } else if (str==AnsiString("derail:")) //dzwiek przy wykolejeniu { str= Parser->GetNextSymbol(); rsDerailment.Init(str.c_str(),Parser->GetNextSymbol().ToDouble(),GetPosition().x,GetPosition().y,GetPosition().z,true); rsDerailment.AM=1.0; rsDerailment.AA=0.0; rsDerailment.FM=1.0; rsDerailment.FA=0.0; } else if (str==AnsiString("dieselinc:")) //dzwiek przy wlazeniu na obroty woodwarda { str= Parser->GetNextSymbol(); rsDiesielInc.Init(str.c_str(),Parser->GetNextSymbol().ToDouble(),GetPosition().x,GetPosition().y,GetPosition().z,true); rsDiesielInc.AM=1.0; rsDiesielInc.AA=0.0; rsDiesielInc.FM=1.0; rsDiesielInc.FA=0.0; } else if (str==AnsiString("curve:")) { str= Parser->GetNextSymbol(); rscurve.Init(str.c_str(),Parser->GetNextSymbol().ToDouble(),GetPosition().x,GetPosition().y,GetPosition().z,true); rscurve.AM=1.0; rscurve.AA=0.0; rscurve.FM=1.0; rscurve.FA=0.0; } else if (str==AnsiString("horn1:")) //pliki z trabieniem { sHorn1.Load(Parser,GetPosition()); } if (str==AnsiString("horn2:")) //pliki z trabieniem wysokoton. { sHorn2.Load(Parser,GetPosition()); if (iHornWarning) iHornWarning=2; //numer syreny do użycia po otrzymaniu sygnału do jazdy } if (str==AnsiString("departuresignal:")) //pliki z sygnalem odjazdu { sDepartureSignal.Load(Parser,GetPosition()); } if (str==AnsiString("pantographup:")) //pliki dzwiekow pantografow { str=Parser->GetNextSymbol(); sPantUp.Init(str.c_str(),50,GetPosition().x,GetPosition().y,GetPosition().z,true); sPantUp.AM=50000; sPantUp.AA=-1*(105-random(10))/100; sPantUp.FM=1.0; sPantUp.FA=0.0; } if (str==AnsiString("pantographdown:")) //pliki dzwiekow pantografow { str= Parser->GetNextSymbol(); sPantDown.Init(str.c_str(),50,GetPosition().x,GetPosition().y,GetPosition().z,true); sPantDown.AM=50000; sPantDown.AA=-1*(105-random(10))/100; sPantDown.FM=1.0; sPantDown.FA=0.0; } else if (str==AnsiString("compressor:")) //pliki ze sprezarka { sCompressor.Load(Parser,GetPosition()); } else if (str==AnsiString("converter:")) //pliki z przetwornica { //if (MoverParameters->EngineType==DieselElectric) //będzie modulowany? sConverter.Load(Parser,GetPosition()); } else if (str==AnsiString("turbo:")) //pliki z turbogeneratorem { sTurbo.Load(Parser,GetPosition()); } else if (str==AnsiString("small-compressor:")) //pliki z przetwornica { sSmallCompressor.Load(Parser,GetPosition()); } else if (str==AnsiString("dooropen:")) { str=Parser->GetNextSymbol(); rsDoorOpen.Init(str.c_str(),50,GetPosition().x,GetPosition().y,GetPosition().z,true); rsDoorOpen.AM=50000; rsDoorOpen.AA=-1*(105-random(10))/100; rsDoorOpen.FM=1.0; rsDoorOpen.FA=0.0; } else if (str==AnsiString("doorclose:")) { str=Parser->GetNextSymbol(); rsDoorClose.Init(str.c_str(),50,GetPosition().x,GetPosition().y,GetPosition().z,true); rsDoorClose.AM=50000; rsDoorClose.AA=-1*(105-random(10))/100; rsDoorClose.FM=1.0; rsDoorClose.FA=0.0; } } else if (str==AnsiString("internaldata:")) //dalej nie czytaj {while (!Parser->EndOfFile) {//zbieranie informacji o kabinach str=Parser->GetNextSymbol().LowerCase(); if (str=="cab0model:") {str=Parser->GetNextSymbol(); if (str!="none") iCabs=2; } else if (str=="cab1model:") {str=Parser->GetNextSymbol(); if (str!="none") iCabs=1; } else if (str=="cab2model:") {str=Parser->GetNextSymbol(); if (str!="none") iCabs=4; } } Stop_InternalData=true; } } //ABu 050205 - tego wczesniej nie bylo i uciekala pamiec: delete Parser; if (mdModel) mdModel->Init(); //obrócenie modelu oraz optymalizacja, również zapisanie binarnego if (mdLoad) mdLoad->Init(); if (mdPrzedsionek) mdPrzedsionek->Init(); if (mdLowPolyInt) mdLowPolyInt->Init(); //sHorn2.CopyIfEmpty(sHorn1); //żeby jednak trąbił też drugim Global::asCurrentTexturePath=AnsiString(szTexturePath); //kiedyś uproszczone wnętrze mieszało tekstury nieba } //--------------------------------------------------------------------------- void __fastcall TDynamicObject::RadioStop() {//zatrzymanie pojazdu if (Mechanik) //o ile ktoś go prowadzi if (MoverParameters->SecuritySystem.RadioStop) //jeśli pojazd ma RadioStop i jest on aktywny Mechanik->PutCommand("Emergency_brake",1.0,1.0,&vPosition,stopRadio); }; void __fastcall TDynamicObject::RaLightsSet(int head,int rear) {//zapalenie świateł z przodu i z tyłu, zależne od kierunku pojazdu if (!MoverParameters) return; //może tego nie być na początku if (rear==2+32+64) {//jeśli koniec pociągu, to trzeba ustalić, czy jest tam czynna lokomotywa //EN57 może nie mieć końcówek od środka członu if (MoverParameters->Power>1.0) //jeśli ma moc napędową if (!MoverParameters->ActiveDir) //jeśli nie ma ustawionego kierunku {//jeśli ma zarówno światła jak i końcówki, ustalić, czy jest w stanie aktywnym //np. lokomotywa na zimno będzie mieć końcówki a nie światła rear=64; //tablice blaszane //trzeba to uzależnić od "załączenia baterii" w pojeździe } if (rear==2+32+64) //jeśli nadal obydwie możliwości if (iInventory&(iDirection?0x2A:0x15)) //czy ma jakieś światła czerowone od danej strony rear=2+32; //dwa światła czerwone else rear=64; //tablice blaszane } if (iDirection) //w zależności od kierunku pojazdu w składzie {//jesli pojazd stoi sprzęgiem 0 w stronę czoła if (head>=0) iLights[0]=head; if (rear>=0) iLights[1]=rear; } else {//jak jest odwrócony w składzie (-1), to zapalamy odwrotnie if (head>=0) iLights[1]=head; if (rear>=0) iLights[0]=rear; } }; int __fastcall TDynamicObject::DirectionSet(int d) {//ustawienie kierunku w składzie (wykonuje AI) iDirection=d>0?1:0; //d:1=zgodny,-1=przeciwny; iDirection:1=zgodny,0=przeciwny; CouplCounter=20; //żeby normalnie skanować kolizje, to musi ruszyć z miejsca if (MyTrack) {//podczas wczytywania wstawiane jest AI, ale może jeszcze nie być toru //AI ustawi kierunek ponownie po uruchomieniu silnika if (iDirection) //jeśli w kierunku Coupler 0 {if (MoverParameters->Couplers[0].CouplingFlag==ctrain_virtual) //brak pojazdu podpiętego? ABuScanObjects(1,300); //szukanie czegoś do podłączenia } else if (MoverParameters->Couplers[1].CouplingFlag==ctrain_virtual) //brak pojazdu podpiętego? ABuScanObjects(-1,300); } return 1-(iDirection?NextConnectedNo:PrevConnectedNo); //informacja o położeniu następnego }; TDynamicObject* __fastcall TDynamicObject::PrevAny() {//wskaźnik na poprzedni, nawet wirtualny return iDirection?PrevConnected:NextConnected; }; TDynamicObject* __fastcall TDynamicObject::Prev() { if (MoverParameters->Couplers[iDirection^1].CouplingFlag) return iDirection?PrevConnected:NextConnected; return NULL; //gdy sprzęg wirtualny, to jakby nic nie było }; TDynamicObject* __fastcall TDynamicObject::Next() { if (MoverParameters->Couplers[iDirection].CouplingFlag) return iDirection?NextConnected:PrevConnected; return NULL; //gdy sprzęg wirtualny, to jakby nic nie było }; TDynamicObject* __fastcall TDynamicObject::NextC(int C) { if (MoverParameters->Couplers[iDirection].CouplingFlag&C) return iDirection?NextConnected:PrevConnected; return NULL; //gdy sprzęg inny, to jakby nic nie było }; double __fastcall TDynamicObject::NextDistance(double d) {//ustalenie odległości do następnego pojazdu, potrzebne do wstecznego skanowania if (!MoverParameters->Couplers[iDirection].Connected) return d; //jeśli nic nie ma, zwrócenie domyślnej wartości if ((d<=0.0)||(MoverParameters->Couplers[iDirection].CoupleDistCouplers[iDirection].Dist; else return d; }; TDynamicObject* __fastcall TDynamicObject::Neightbour(int &dir) {//ustalenie następnego (1) albo poprzedniego (0) w składzie bez względu na prawidłowość iDirection int d=dir; //zapamiętanie kierunku dir=1-(dir?NextConnectedNo:PrevConnectedNo); //nowa wartość return (d?(MoverParameters->Couplers[1].CouplingFlag?NextConnected:NULL):(MoverParameters->Couplers[0].CouplingFlag?PrevConnected:NULL)); }; void __fastcall TDynamicObject::CoupleDist() {//obliczenie odległości sprzęgów if (MyTrack?(MyTrack->iCategoryFlag&1):true) //jeśli nie ma przypisanego toru, to liczyć jak dla kolei {//jeśli jedzie po szynach (również unimog), liczenie kul wystarczy MoverParameters->SetCoupleDist(); } else {//na drodze trzeba uwzględnić wektory ruchu double d0=MoverParameters->Couplers[0].CoupleDist; //double d1=MoverParameters->Couplers[1].CoupleDist; //sprzęg z tyłu samochodu można olać, dopóki nie jeździ na wstecznym vector3 p1,p2; double d,w; //dopuszczalny dystans w poprzek MoverParameters->SetCoupleDist(); //liczenie standardowe if (MoverParameters->Couplers[0].Connected) //jeśli cokolwiek podłączone if (MoverParameters->Couplers[0].CouplingFlag==0) //jeśli wirtualny if (MoverParameters->Couplers[0].CoupleDist<300.0) //i mniej niż 300m {//przez MoverParameters->Couplers[0].Connected nie da się dostać do DynObj, stąd prowizorka //WriteLog("Collision of "+AnsiString(MoverParameters->Couplers[0].CoupleDist)+"m detected by "+asName+":0."); w=0.5*(MoverParameters->Couplers[0].Connected->Dim.W+MoverParameters->Dim.W); //minimalna odległość minięcia d=-DotProduct(vLeft,vCoulpler[0]); //odległość prostej ruchu od początku układu współrzędnych d=fabs(DotProduct(vLeft,((TMoverParameters*)(MoverParameters->Couplers[0].Connected))->vCoulpler[MoverParameters->Couplers[0].ConnectedNr])+d); //WriteLog("Distance "+AnsiString(d)+"m from "+asName+":0."); if (d>w) MoverParameters->Couplers[0].CoupleDist=(d0<10?50:d0); //przywrócenie poprzedniej } if (MoverParameters->Couplers[1].Connected) //jeśli cokolwiek podłączone if (MoverParameters->Couplers[1].CouplingFlag==0) //jeśli wirtualny if (MoverParameters->Couplers[1].CoupleDist<300.0) //i mniej niż 300m { //WriteLog("Collision of "+AnsiString(MoverParameters->Couplers[1].CoupleDist)+"m detected by "+asName+":1."); w=0.5*(MoverParameters->Couplers[1].Connected->Dim.W+MoverParameters->Dim.W); //minimalna odległość minięcia d=-DotProduct(vLeft,vCoulpler[1]); //odległość prostej ruchu od początku układu współrzędnych d=fabs(DotProduct(vLeft,((TMoverParameters*)(MoverParameters->Couplers[1].Connected))->vCoulpler[MoverParameters->Couplers[1].ConnectedNr])+d); //WriteLog("Distance "+AnsiString(d)+"m from "+asName+":1."); if (d>w) MoverParameters->Couplers[0].CoupleDist=(d0<10?50:d0); //przywrócenie poprzedniej } } }; TDynamicObject* __fastcall TDynamicObject::ControlledFind() {//taka proteza: chcę podłączyć kabinę EN57 bezpośrednio z silnikowym, aby nie robić tego przez ukrotnienie //drugi silnikowy i tak musi być ukrotniony, podobnie jak kolejna jednostka //lepiej by było przesyłać komendy sterowania, co jednak wymaga przebudowy transmisji komend (LD) //problem się robi ze światłami, które będą zapalane w silnikowym, ale muszą świecić się w rozrządczych //dla EZT światłą czołowe będą "zapalane w silnikowym", ale widziane z rozrządczych //również wczytywanie MMD powinno dotyczyć aktualnego członu //problematyczna może być kwestia wybranej kabiny (w silnikowym...) //jeśli silnikowy będzie zapięty odwrotnie (tzn. -1), to i tak powinno jeździć dobrze //również hamowanie wykonuje się zaworem w członie, a nie w silnikowym... TDynamicObject *d=this; //zaczynamy od aktualnego if (d->MoverParameters->TrainType&dt_EZT) //na razie dotyczy to EZT if (d->NextConnected?d->MoverParameters->Couplers[1].AllowedFlag&ctrain_depot:false) {//gdy jest człon od sprzęgu 1, a sprzęg łączony warsztatowo (powiedzmy) if ((d->MoverParameters->Power<1.0)&&(d->NextConnected->MoverParameters->Power>1.0)) //my nie mamy mocy, ale ten drugi ma d=d->NextConnected; //będziemy sterować tym z mocą } else if (d->PrevConnected?d->MoverParameters->Couplers[0].AllowedFlag&ctrain_depot:false) {//gdy jest człon od sprzęgu 0, a sprzęg łączony warsztatowo (powiedzmy) if ((d->MoverParameters->Power<1.0)&&(d->PrevConnected->MoverParameters->Power>1.0)) //my nie mamy mocy, ale ten drugi ma d=d->PrevConnected; //będziemy sterować tym z mocą } return d; }; //--------------------------------------------------------------------------- void __fastcall TDynamicObject::ParamSet(int what,int into) {//ustawienie lokalnego parametru (what) na stan (into) switch (what&0xFF00) { case 0x0100: //to np. są drzwi, bity 0..7 określają numer 1..254 albo maskę dla 8 różnych if (what&1) //na razie mamy lewe oraz prawe, czyli używamy maskę 1=lewe, 2=prawe, 3=wszystkie if (MoverParameters->DoorLeftOpened) {//są otwarte if (!into) //jeśli zamykanie { //dźwięk zamykania } } else {//są zamknięte if (into) //jeśli otwieranie { //dźwięk otwierania } } if (what&2) //prawe działają niezależnie od lewych if (MoverParameters->DoorRightOpened) {//są otwarte if (!into) //jeśli zamykanie { //dźwięk zamykania } } else {//są zamknięte if (into) //jeśli otwieranie { //dźwięk otwierania } } break; } }; int __fastcall TDynamicObject::RouteWish(TTrack *tr) {//zapytanie do AI, po którym segmencie (-6..6) jechać na skrzyżowaniu (tr) return Mechanik?Mechanik->CrossRoute(tr):0; //wg AI albo prosto }; AnsiString __fastcall TDynamicObject::TextureTest(AnsiString &name) {//Ra 2015-01: sprawdzenie dostępności tekstury o podanej nazwie AnsiString x=name+".dds"; //na razie prymitywnie if (FileExists(x)) return x; else {x=name+".tga"; //w zasadzie to należałoby uwzględnić deklarowaną kolejność if (FileExists(x)) return x; else {x=name+".bmp"; if (FileExists(x)) return x; } } return ""; //nie znaleziona }; void __fastcall TDynamicObject::DestinationSet(AnsiString to) {//ustawienie stacji docelowej oraz wymiennej tekstury 4, jeśli istnieje plik //w zasadzie, to każdy wagon mógłby mieć inną stację docelową //zwłaszcza w towarowych, pod kątem zautomatyzowania maewrów albo pracy górki //ale to jeszcze potrwa, zanim będzie możliwe, na razie można wpisać stację z rozkładu if (abs(iMultiTex)>=4) return; //jak są 4 tekstury wymienne, to nie zmieniać rozkładem asDestination=to; to=Global::Bezogonkow(to); //do szukania pliku obcinamy ogonki AnsiString x; if (to.IsEmpty()) to="nowhere"; x=TextureTest(asBaseDir+to+"@"+MoverParameters->TypeName); //w pierwszej kolejności z nazwą FIZ/MMD if (!x.IsEmpty()) { ReplacableSkinID[4]=TTexturesManager::GetTextureID(NULL,NULL,x.c_str(),9); //rozmywania 0,1,4,5 nie nadają się return; } x=TextureTest(asBaseDir+to); //na razie prymitywnie if (!x.IsEmpty()) ReplacableSkinID[4]=TTexturesManager::GetTextureID(NULL,NULL,x.c_str(),9); //rozmywania 0,1,4,5 nie nadają się else ReplacableSkinID[4]=0; //0 to brak? -1 odpada, bo inaczej się będzie mapować //Ra 2015-01: żeby zalogować błąd, trzeba by mieć pewność, że model używa tekstury nr 4 }; void __fastcall TDynamicObject::OverheadTrack(float o) {//ewentualne wymuszanie jazdy bezprądowej z powodu informacji w torze if (ctOwner) //jeśli ma obiekt nadzorujący {//trzeba zaktualizować mapę flag bitowych jazdy bezprądowej if (o<0.0) {//normalna jazda po tym torze ctOwner->iOverheadZero&=~iOverheadMask; //zerowanie bitu - może pobierać prąd ctOwner->iOverheadDown&=~iOverheadMask; //zerowanie bitu - może podnieść pantograf } else if (o>0.0) {//opuszczenie pantografów ctOwner->iOverheadZero|=iOverheadMask; //ustawienie bitu - ma jechać bez pobierania prądu ctOwner->iOverheadDown|=iOverheadMask; //ustawienie bitu - ma opuścić pantograf } else {//jazda bezprądowa z podniesionym pantografem ctOwner->iOverheadZero|=iOverheadMask; //ustawienie bitu - ma jechać bez pobierania prądu ctOwner->iOverheadDown&=~iOverheadMask; //zerowanie bitu - może podnieść pantograf } } };