//--------------------------------------------------------------------------- /* MaSzyna EU07 locomotive simulator Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others */ #include "system.hpp" #include "classes.hpp" #pragma hdrstop #include "Model3d.h" #include "Usefull.h" #include "Texture.h" #include "logs.h" #include "Globals.h" #include "Timer.h" #include "mtable.hpp" //--------------------------------------------------------------------------- #pragma package(smart_init) double TSubModel::fSquareDist=0; int TSubModel::iInstance; //numer renderowanego egzemplarza obiektu GLuint *TSubModel::ReplacableSkinId=NULL; int TSubModel::iAlpha=0x30300030; //maska do testowania flag tekstur wymiennych TModel3d* TSubModel::pRoot; //Ra: tymczasowo wskaźnik na model widoczny z submodelu AnsiString* TSubModel::pasText; //przykłady dla TSubModel::iAlpha: // 0x30300030 - wszystkie bez kanału alfa // 0x31310031 - tekstura -1 używana w danym cyklu, pozostałe nie // 0x32320032 - tekstura -2 używana w danym cyklu, pozostałe nie // 0x34340034 - tekstura -3 używana w danym cyklu, pozostałe nie // 0x38380038 - tekstura -4 używana w danym cyklu, pozostałe nie // 0x3F3F003F - wszystkie wymienne tekstury używane w danym cyklu //Ale w TModel3d okerśla przezroczystość tekstur wymiennych! int TSubModelInfo::iTotalTransforms=0; //ilość transformów int TSubModelInfo::iTotalNames=0; //długość obszaru nazw int TSubModelInfo::iTotalTextures=0; //długość obszaru tekstur int TSubModelInfo::iCurrent=0; //aktualny obiekt TSubModelInfo* TSubModelInfo::pTable=NULL; //tabele obiektów pomocniczych char* TStringPack::String(int n) {//zwraca wskaźnik do łańcucha o podanym numerze if (index?n<(index[1]>>2)-2:false) return data+8+index[n+2]; //indeks upraszcza kwestię wyszukiwania //jak nie ma indeksu, to trzeba szukać int max=*((int*)(data+4)); //długość obszaru łańcuchów char* ptr=data+8; //począek obszaru łańcuchów for (int i=0;idata+max) return NULL; //zbyt wysoki numer } return ptr; }; __fastcall TSubModel::TSubModel() { ZeroMemory(this,sizeof(TSubModel)); //istotne przy zapisywaniu wersji binarnej FirstInit(); }; void __fastcall TSubModel::FirstInit() { eType=TP_ROTATOR; Vertices=NULL; uiDisplayList=0; iNumVerts=-1; //do sprawdzenia iVboPtr=-1; fLight=-1.0; //świetcenie wyłączone v_RotateAxis=float3(0,0,0); v_TransVector=float3(0,0,0); f_Angle=0; b_Anim=at_None; b_aAnim=at_None; fVisible=0.0; //zawsze widoczne iVisible=1; fMatrix=NULL; //to samo co iMatrix=0; Next=NULL; Child=NULL; TextureID=0; //TexAlpha=false; iFlags=0x0200; //bit 9=1: submodel został utworzony a nie ustawiony na wczytany plik //TexHash=false; //Hits=NULL; //CollisionPts=NULL; //CollisionPtsCount=0; Opacity=1.0; //przy wczytywaniu modeli było dzielone przez 100... bWire=false; fWireSize=0; fNearAttenStart=40; fNearAttenEnd=80; bUseNearAtten=false; iFarAttenDecay=0; fFarDecayRadius=100; fCosFalloffAngle=0.5; //120°? fCosHotspotAngle=0.3; //145°? fCosViewAngle=0; fSquareMaxDist=10000*10000; //10km fSquareMinDist=0; iName=-1; //brak nazwy iTexture=0; //brak tekstury //asName=""; //asTexture=""; pName=pTexture=NULL; f4Ambient[0]=f4Ambient[1]=f4Ambient[2]=f4Ambient[3]=1.0; //{1,1,1,1}; f4Diffuse[0]=f4Diffuse[1]=f4Diffuse[2]=f4Diffuse[3]=1.0; //{1,1,1,1}; f4Specular[0]=f4Specular[1]=f4Specular[2]=0.0; f4Specular[3]=1.0; //{0,0,0,1}; f4Emision[0]=f4Emision[1]=f4Emision[2]=f4Emision[3]=1.0; smLetter=NULL; //używany tylko roboczo dla TP_TEXT, do przyspieszenia wyświetlania }; __fastcall TSubModel::~TSubModel() { if (uiDisplayList) glDeleteLists(uiDisplayList,1); if (iFlags&0x0200) {//wczytany z pliku tekstowego musi sam posprzątać //SafeDeleteArray(Indices); SafeDelete(Next); SafeDelete(Child); delete fMatrix; //własny transform trzeba usunąć (zawsze jeden) delete[] Vertices; delete[] pTexture; delete[] pName; } /* else {//wczytano z pliku binarnego (nie jest właścicielem tablic) } */ delete[] smLetter; //używany tylko roboczo dla TP_TEXT, do przyspieszenia wyświetlania }; void __fastcall TSubModel::TextureNameSet(const char *n) {//ustawienie nazwy submodelu, o ile nie jest wczytany z E3D if (iFlags&0x0200) {//tylko jeżeli submodel zosta utworzony przez new delete[] pTexture; //usunięcie poprzedniej int i=strlen(n); if (i) {//utworzenie nowej pTexture=new char[i+1]; strcpy(pTexture,n); } else pTexture=NULL; } }; void __fastcall TSubModel::NameSet(const char *n) {//ustawienie nazwy submodelu, o ile nie jest wczytany z E3D if (iFlags&0x0200) {//tylko jeżeli submodel zosta utworzony przez new delete[] pName; //usunięcie poprzedniej int i=strlen(n); if (i) {//utworzenie nowej pName=new char[i+1]; strcpy(pName,n); } else pName=NULL; } }; //int __fastcall TSubModel::SeekFaceNormal(DWORD *Masks, int f,DWORD dwMask,vector3 *pt,GLVERTEX *Vertices) int __fastcall TSubModel::SeekFaceNormal(DWORD *Masks,int f,DWORD dwMask,float3 *pt,float8 *Vertices) {//szukanie punktu stycznego do (pt), zwraca numer wierzchołka, a nie trójkąta int iNumFaces=iNumVerts/3; //bo maska powierzchni jest jedna na trójkąt //GLVERTEX *p; //roboczy wskaźnik float8 *p; //roboczy wskaźnik for (int i=f;iPoint==*pt) return 3*i; if ((++p)->Point==*pt) return 3*i+1; if ((++p)->Point==*pt) return 3*i+2; } return -1; //nie znaleziono stycznego wierzchołka } float emm1[]={1,1,1,0}; float emm2[]={0,0,0,1}; inline double readIntAsDouble(cParser& parser,int base=255) { int value; parser.getToken(value); return double(value)/base; }; template inline void readColor(cParser& parser,ColorT* color) { parser.ignoreToken(); color[0]=readIntAsDouble(parser); color[1]=readIntAsDouble(parser); color[2]=readIntAsDouble(parser); }; inline void readColor(cParser& parser,int &color) { int r,g,b; parser.ignoreToken(); parser.getToken(r); parser.getToken(g); parser.getToken(b); color=r+(g<<8)+(b<<16); }; /* inline void readMatrix(cParser& parser,matrix4x4& matrix) {//Ra: wczytanie transforma for (int x=0;x<=3;x++) //wiersze for (int y=0;y<=3;y++) //kolumny parser.getToken(matrix(x)[y]); }; */ inline void readMatrix(cParser& parser,float4x4& matrix) {//Ra: wczytanie transforma for (int x=0;x<=3;x++) //wiersze for (int y=0;y<=3;y++) //kolumny parser.getToken(matrix(x)[y]); }; int __fastcall TSubModel::Load(cParser& parser,TModel3d *Model,int Pos,bool dynamic) {//Ra: VBO tworzone na poziomie modelu, a nie submodeli iNumVerts=0; iVboPtr=Pos; //pozycja w VBO //TMaterialColorf Ambient,Diffuse,Specular; //GLuint TextureID; //char *extName; if (!parser.expectToken("type:")) Error("Model type parse failure!"); { std::string type; parser.getToken(type); if (type=="mesh") eType=GL_TRIANGLES; //submodel - trójkaty else if (type=="point") eType=GL_POINTS; //co to niby jest? else if (type=="freespotlight") eType=TP_FREESPOTLIGHT; //światełko else if (type=="text") eType=TP_TEXT; //wyświetlacz tekstowy (generator napisów) else if (type=="stars") eType=TP_STARS; //wiele punktów świetlnych }; parser.ignoreToken(); std::string token; //parser.getToken(token1); //ze zmianą na małe! parser.getTokens(1,false); //nazwa submodelu bez zmieny na małe parser >> token; NameSet(token.c_str()); if (dynamic) {//dla pojazdu, blokujemy załączone submodele, które mogą być nieobsługiwane if (token.find("_on")+3==token.length()) //jeśli nazwa kończy się na "_on" iVisible=0; //to domyślnie wyłączyć, żeby się nie nakładało z obiektem "_off" } else //dla pozostałych modeli blokujemy zapalone światła, które mogą być nieobsługiwane if (token.find("Light_On")==0) //jeśli nazwa zaczyna się od "Light_On" iVisible=0; //to domyślnie wyłączyć, żeby się nie nakładało z obiektem "Light_Off" if (parser.expectToken("anim:")) //Ra: ta informacja by się przydała! {//rodzaj animacji std::string type; parser.getToken(type); if (type!="false") {iFlags|=0x4000; //jak animacja, to trzeba przechowywać macierz zawsze if (type=="seconds_jump") b_Anim=b_aAnim=at_SecondsJump; //sekundy z przeskokiem else if (type=="minutes_jump") b_Anim=b_aAnim=at_MinutesJump; //minuty z przeskokiem else if (type=="hours_jump") b_Anim=b_aAnim=at_HoursJump; //godziny z przeskokiem else if (type=="hours24_jump") b_Anim=b_aAnim=at_Hours24Jump; //godziny z przeskokiem else if (type=="seconds") b_Anim=b_aAnim=at_Seconds; //minuty płynnie else if (type=="minutes") b_Anim=b_aAnim=at_Minutes; //minuty płynnie else if (type=="hours") b_Anim=b_aAnim=at_Hours; //godziny płynnie else if (type=="hours24") b_Anim=b_aAnim=at_Hours24; //godziny płynnie else if (type=="billboard") b_Anim=b_aAnim=at_Billboard; //obrót w pionie do kamery else if (type=="wind") b_Anim=b_aAnim=at_Wind; //ruch pod wpływem wiatru else if (type=="sky") b_Anim=b_aAnim=at_Sky; //aniamacja nieba else if (type=="ik") b_Anim=b_aAnim=at_IK; //IK: zadający else if (type=="ik11") b_Anim=b_aAnim=at_IK11; //IK: kierunkowany else if (type=="ik21") b_Anim=b_aAnim=at_IK21; //IK: kierunkowany else if (type=="ik22") b_Anim=b_aAnim=at_IK22; //IK: kierunkowany else if (type=="digital") b_Anim=b_aAnim=at_Digital; //licznik mechaniczny else if (type=="digiclk") b_Anim=b_aAnim=at_DigiClk; //zegar cyfrowy else b_Anim=b_aAnim=at_Undefined; //nieznana forma animacji } } if (eType1.0) Opacity*=0.01; //w 2013 był błąd i aby go obejść, trzeba było wpisać 10000.0 if ((Global::iConvertModels&1)==0) //dla zgodności wstecz Opacity=0.0; //wszystko idzie w przezroczyste albo zależnie od tekstury if (!parser.expectToken("map:")) Error("Model map parse failure!"); std::string texture; parser.getToken(texture); if (texture=="none") {//rysowanie podanym kolorem TextureID=0; iFlags|=0x10; //rysowane w cyklu nieprzezroczystych } else if (texture.find("replacableskin")!=texture.npos) {// McZapkie-060702: zmienialne skory modelu TextureID=-1; iFlags|=(Opacity<1.0)?1:0x10; //zmienna tekstura 1 } else if (texture=="-1") { TextureID=-1; iFlags|=(Opacity<1.0)?1:0x10; //zmienna tekstura 1 } else if (texture=="-2") { TextureID=-2; iFlags|=(Opacity<1.0)?2:0x10; //zmienna tekstura 2 } else if (texture=="-3") { TextureID=-3; iFlags|=(Opacity<1.0)?4:0x10; //zmienna tekstura 3 } else if (texture=="-4") { TextureID=-4; iFlags|=(Opacity<1.0)?8:0x10; //zmienna tekstura 4 } else {//jeśli tylko nazwa pliku, to dawać bieżącą ścieżkę do tekstur //asTexture=AnsiString(texture.c_str()); //zapamiętanie nazwy tekstury TextureNameSet(texture.c_str()); if (texture.find_first_of("/\\")==texture.npos) texture.insert(0,Global::asCurrentTexturePath.c_str()); TextureID=TTexturesManager::GetTextureID(szTexturePath,Global::asCurrentTexturePath.c_str(),texture); //TexAlpha=TTexturesManager::GetAlpha(TextureID); //iFlags|=TexAlpha?0x20:0x10; //0x10-nieprzezroczysta, 0x20-przezroczysta if (Opacity<1.0) //przezroczystość z tekstury brana tylko dla Opacity 0! iFlags|=TTexturesManager::GetAlpha(TextureID)?0x20:0x10; //0x10-nieprzezroczysta, 0x20-przezroczysta else iFlags|=0x10; //normalnie nieprzezroczyste //renderowanie w cyklu przezroczystych tylko jeśli: //1. Opacity=0 (przejściowo <1, czy tam <100) oraz //2. tekstura ma przezroczystość }; } else iFlags|=0x10; parser.ignoreToken(); parser.getToken(fSquareMaxDist); if (fSquareMaxDist>=0.0) fSquareMaxDist*=fSquareMaxDist; else fSquareMaxDist=15000*15000; //15km to więcej, niż się obecnie wyświetla parser.ignoreToken(); parser.getToken(fSquareMinDist); fSquareMinDist*=fSquareMinDist; parser.ignoreToken(); fMatrix=new float4x4(); readMatrix(parser,*fMatrix); //wczytanie transform if (!fMatrix->IdentityIs()) iFlags|=0x8000; //transform niejedynkowy - trzeba go przechować int iNumFaces; //ilość trójkątów DWORD *sg; //maski przynależności trójkątów do powierzchni if (eType0"); return 0; } //Vertices=new GLVERTEX[iNumVerts]; if (iNumVerts) {Vertices=new float8[iNumVerts]; iNumFaces=iNumVerts/3; sg=new DWORD[iNumFaces]; //maski powierzchni: 0 oznacza brak użredniania wektorów normalnych int *wsp=new int[iNumVerts]; //z którego wierzchołka kopiować wektor normalny int maska=0; for (int i=0;i0) //jeśli pierwszy trójkąt będzie zdegenerowany, to zostanie usunięty i nie ma co sprawdzać if (((Vertices[i ].Point-Vertices[i-1].Point).Length()>1000.0) || ((Vertices[i-1].Point-Vertices[i-2].Point).Length()>1000.0) || ((Vertices[i-2].Point-Vertices[i ].Point).Length()>1000.0)) {//jeżeli są dalej niż 2km od siebie //Ra 15-01: obiekt wstawiany nie powinien być większy niż 300m (trójkąty terenu w E3D mogą mieć 1.5km) --iNumFaces; //o jeden trójkąt mniej iNumVerts-=3; //czyli o 3 wierzchołki i-=3; //wczytanie kolejnego w to miejsce WriteLog(AnsiString("Too large triangle ignored in: \"")+AnsiString(pName)+"\""); } } } int i; //indeks dla trójkątów float3 *n=new float3[iNumFaces]; //tablica wektorów normalnych dla trójkątów for (i=0;i=0) //jeśli już był liczony wektor normalny z użyciem tego wierzchołka Vertices[v].Normal=Vertices[wsp[v]].Normal; //to wystarczy skopiować policzony wcześniej else {//inaczej musimy dopiero policzyć i=v/3; //numer trójkąta norm=float3(0,0,0); //liczenie zaczynamy od zera f=v; //zaczynamy dodawanie wektorów normalnych od własnego while (f>=0) {//sumowanie z wektorem normalnym sąsiada (włącznie ze sobą) wsp[f]=v; //informacja, że w tym wierzchołku jest już policzony wektor normalny norm+=n[f/3]; f=SeekFaceNormal(sg,f/3+1,sg[i],&Vertices[v].Point,Vertices); //i szukanie od kolejnego trójkąta } //Ra 15-01: należało by jeszcze uwzględnić skalowanie wprowadzane przez transformy, aby normalne po przeskalowaniu były jednostkowe Vertices[v].Normal=SafeNormalize(norm); //przepisanie do wierzchołka trójkąta } } delete[] wsp; delete[] n; delete[] sg; } else //gdy brak wierzchołków {eType=TP_ROTATOR; //submodel pomocniczy, ma tylko macierz przekształcenia iVboPtr=iNumVerts=0; //dla formalności } } //obsługa submodelu z własną listą wierzchołków } else if (eType==TP_STARS) {//punkty świecące dookólnie - składnia jak dla smt_Mesh parser.ignoreToken(); parser.getToken(iNumVerts); //Vertices=new GLVERTEX[iNumVerts]; Vertices=new float8[iNumVerts]; int i,j; for (i=0;i> 8)&0xFF)/255.0; //G Vertices[i].Normal.z=((j>>16)&0xFF)/255.0; //B } } //Visible=true; //się potem wyłączy w razie potrzeby //iFlags|=0x0200; //wczytano z pliku tekstowego (jest właścicielem tablic) if (iNumVerts<1) iFlags&=~0x3F; //cykl renderowania uzależniony od potomnych return iNumVerts; //do określenia wielkości VBO }; int __fastcall TSubModel::TriangleAdd(TModel3d *m,int tex,int tri) {//dodanie trójkątów do submodelu, używane przy tworzeniu E3D terenu TSubModel *s=this; while (s?(s->TextureID!=tex):false) {//szukanie submodelu o danej teksturze if (s==this) s=Child; else s=s->Next; } if (!s) {if (TextureID<=0) s=this; //użycie głównego else {//dodanie nowego submodelu do listy potomnych s=new TSubModel(); m->AddTo(this,s); } //s->asTexture=AnsiString(TTexturesManager::GetName(tex).c_str()); s->TextureNameSet(TTexturesManager::GetName(tex).c_str()); s->TextureID=tex; s->eType=GL_TRIANGLES; //iAnimOwner=0; //roboczy wskaźnik na wierzchołek } if (s->iNumVerts<0) s->iNumVerts=tri; //bo na początku jest -1, czyli że nie wiadomo else s->iNumVerts+=tri; //aktualizacja ilości wierzchołków return s->iNumVerts-tri; //zwraca pozycję tych trójkątów w submodelu }; float8* __fastcall TSubModel::TrianglePtr(int tex,int pos,int *la,int *ld,int*ls) {//zwraca wskaźnik do wypełnienia tabeli wierzchołków, używane przy tworzeniu E3D terenu TSubModel *s=this; while (s?s->TextureID!=tex:false) {//szukanie submodelu o danej teksturze if (s==this) s=Child; else s=s->Next; } if (!s) return NULL; //coś nie tak poszło if (!s->Vertices) {//utworznie tabeli trójkątów s->Vertices=new float8[s->iNumVerts]; //iVboPtr=pos; //pozycja submodelu w tabeli wierzchołków //pos+=iNumVerts; //rezerwacja miejsca w tabeli s->iVboPtr=iInstance; //pozycja submodelu w tabeli wierzchołków iInstance+=s->iNumVerts; //pozycja dla następnego } s->ColorsSet(la,ld,ls); //ustawienie kolorów świateł return s->Vertices+pos; //wskaźnik na wolne miejsce w tabeli wierzchołków }; void __fastcall TSubModel::DisplayLists() {//utworznie po jednej skompilowanej liście dla każdego submodelu if (Global::bUseVBO) return; //Ra: przy VBO to się nie przyda //iFlags|=0x4000; //wyłączenie przeliczania wierzchołków, bo nie są zachowane if (eType0) { uiDisplayList=glGenLists(1); glNewList(uiDisplayList,GL_COMPILE); glColor3fv(f4Diffuse); //McZapkie-240702: zamiast ub #ifdef USE_VERTEX_ARRAYS // ShaXbee-121209: przekazywanie wierzcholkow hurtem glVertexPointer(3,GL_DOUBLE,sizeof(GLVERTEX),&Vertices[0].Point.x); glNormalPointer(GL_DOUBLE,sizeof(GLVERTEX),&Vertices[0].Normal.x); glTexCoordPointer(2,GL_FLOAT,sizeof(GLVERTEX),&Vertices[0].tu); glDrawArrays(eType,0,iNumVerts); #else glBegin(eType); for (int i=0;iDisplayLists(); if (Next) Next->DisplayLists(); }; void __fastcall TSubModel::InitialRotate(bool doit) {//konwersja układu współrzędnych na zgodny ze scenerią if (iFlags&0xC000) //jeśli jest animacja albo niejednostkowy transform {//niejednostkowy transform jest mnożony i wystarczy zabawy if (doit) {//obrót lewostronny if (!fMatrix) //macierzy może nie być w dodanym "bananie" {fMatrix=new float4x4(); //tworzy macierz o przypadkowej zawartości fMatrix->Identity(); //a zaczynamy obracanie od jednostkowej } iFlags|=0x8000; //po obróceniu będzie raczej niejedynkowy matrix fMatrix->InitialRotate(); //zmiana znaku X oraz zamiana Y i Z if (fMatrix->IdentityIs()) iFlags&=~0x8000; //jednak jednostkowa po obróceniu } if (Child) Child->InitialRotate(false); //potomnych nie obracamy już, tylko ewentualnie optymalizujemy else if (Global::iConvertModels&2) //optymalizacja jest opcjonalna if ((iFlags&0xC000)==0x8000) //o ile nie ma animacji {//jak nie ma potomnych, można wymnożyć przez transform i wyjedynkować go float4x4 *mat=GetMatrix(); //transform submodelu if (Vertices) {for (int i=0;iIdentity(); //jedynkowanie transformu po przeliczeniu wierzchołków iFlags&=~0x8000; //transform jedynkowy } } else //jak jest jednostkowy i nie ma animacji if (doit) {//jeśli jest jednostkowy transform, to przeliczamy wierzchołki, a mnożenie podajemy dalej double t; if (Vertices) for (int i=0;iInitialRotate(doit); //potomne ewentualnie obrócimy } if (Next) Next->InitialRotate(doit); }; void __fastcall TSubModel::ChildAdd(TSubModel *SubModel) {//dodanie submodelu potemnego (uzależnionego) //Ra: zmiana kolejności, żeby kolejne móc renderować po aktualnym (było przed) if (SubModel) SubModel->NextAdd(Child); //Ra: zmiana kolejności renderowania Child=SubModel; }; void __fastcall TSubModel::NextAdd(TSubModel *SubModel) {//dodanie submodelu kolejnego (wspólny przodek) if (Next) Next->NextAdd(SubModel); else Next=SubModel; }; int __fastcall TSubModel::FlagsCheck() {//analiza koniecznych zmian pomiędzy submodelami //samo pomijanie glBindTexture() nie poprawi wydajności //ale można sprawdzić, czy można w ogóle pominąć kod do tekstur (sprawdzanie replaceskin) int i; if (Child) {//Child jest renderowany po danym submodelu if (Child->TextureID) //o ile ma teksturę if (Child->TextureID!=TextureID) //i jest ona inna niż rodzica Child->iFlags|=0x80; //to trzeba sprawdzać, jak z teksturami jest i=Child->FlagsCheck(); iFlags|=0x00FF0000&((i<<16)|(i)|(i>>8)); //potomny, rodzeństwo i dzieci if (eType==TP_TEXT) {//wyłączenie renderowania Next dla znaków wyświetlacza tekstowego TSubModel *p=Child; while (p) {p->iFlags&=0xC0FFFFFF; p=p->Next; } } } if (Next) {//Next jest renderowany po danym submodelu (kolejność odwrócona po wczytaniu T3D) if (TextureID) //o ile dany ma teksturę if ((TextureID!=Next->TextureID)||(i&0x00800000)) //a ma inną albo dzieci zmieniają iFlags|=0x80; //to dany submodel musi sobie ją ustawiać i=Next->FlagsCheck(); iFlags|=0xFF000000&((i<<24)|(i<<8)|(i)); //następny, kolejne i ich dzieci //tekstury nie ustawiamy tylko wtedy, gdy jest taka sama jak Next i jego dzieci nie zmieniają } return iFlags; }; void __fastcall TSubModel::SetRotate(float3 vNewRotateAxis,float fNewAngle) {//obrócenie submodelu wg podanej osi (np. wskazówki w kabinie) v_RotateAxis=vNewRotateAxis; f_Angle=fNewAngle; if (fNewAngle!=0.0) {b_Anim=at_Rotate; b_aAnim=at_Rotate; } iAnimOwner=iInstance; //zapamiętanie czyja jest animacja } void __fastcall TSubModel::SetRotateXYZ(float3 vNewAngles) {//obrócenie submodelu o podane kąty wokół osi lokalnego układu v_Angles=vNewAngles; b_Anim=at_RotateXYZ; b_aAnim=at_RotateXYZ; iAnimOwner=iInstance; //zapamiętanie czyja jest animacja } void __fastcall TSubModel::SetRotateXYZ(vector3 vNewAngles) {//obrócenie submodelu o podane kąty wokół osi lokalnego układu v_Angles.x=vNewAngles.x; v_Angles.y=vNewAngles.y; v_Angles.z=vNewAngles.z; b_Anim=at_RotateXYZ; b_aAnim=at_RotateXYZ; iAnimOwner=iInstance; //zapamiętanie czyja jest animacja } void __fastcall TSubModel::SetTranslate(float3 vNewTransVector) {//przesunięcie submodelu (np. w kabinie) v_TransVector=vNewTransVector; b_Anim=at_Translate; b_aAnim=at_Translate; iAnimOwner=iInstance; //zapamiętanie czyja jest animacja } void __fastcall TSubModel::SetTranslate(vector3 vNewTransVector) {//przesunięcie submodelu (np. w kabinie) v_TransVector.x=vNewTransVector.x; v_TransVector.y=vNewTransVector.y; v_TransVector.z=vNewTransVector.z; b_Anim=at_Translate; b_aAnim=at_Translate; iAnimOwner=iInstance; //zapamiętanie czyja jest animacja } void __fastcall TSubModel::SetRotateIK1(float3 vNewAngles) {//obrócenie submodelu o podane kąty wokół osi lokalnego układu v_Angles=vNewAngles; iAnimOwner=iInstance; //zapamiętanie czyja jest animacja } struct ToLower { char operator()(char input) { return tolower(input); } }; TSubModel* __fastcall TSubModel::GetFromName(AnsiString search,bool i) { return GetFromName(search.c_str(),i); }; TSubModel* __fastcall TSubModel::GetFromName(char *search,bool i) { TSubModel* result; //std::transform(search.begin(),search.end(),search.begin(),ToLower()); //search=search.LowerCase(); //AnsiString name=AnsiString(); if (pName&&search) if ((i?stricmp(pName,search):strcmp(pName,search))==0) return this; else if (pName==search) return this; //oba NULL if (Next) { result=Next->GetFromName(search); if (result) return result; } if (Child) { result=Child->GetFromName(search); if (result) return result; } return NULL; }; //WORD hbIndices[18]={3,0,1,5,4,2,1,0,4,1,5,3,2,3,5,2,4,0}; void __fastcall TSubModel::RaAnimation(TAnimType a) {//wykonanie animacji niezależnie od renderowania switch (a) {//korekcja położenia, jeśli submodel jest animowany case at_Translate: //Ra: było "true" if (iAnimOwner!=iInstance) break; //cudza animacja glTranslatef(v_TransVector.x,v_TransVector.y,v_TransVector.z); break; case at_Rotate: //Ra: było "true" if (iAnimOwner!=iInstance) break; //cudza animacja glRotatef(f_Angle,v_RotateAxis.x,v_RotateAxis.y,v_RotateAxis.z); break; case at_RotateXYZ: if (iAnimOwner!=iInstance) break; //cudza animacja glTranslatef(v_TransVector.x,v_TransVector.y,v_TransVector.z); glRotatef(v_Angles.x,1.0,0.0,0.0); glRotatef(v_Angles.y,0.0,1.0,0.0); glRotatef(v_Angles.z,0.0,0.0,1.0); break; case at_SecondsJump: //sekundy z przeskokiem glRotatef(floor(GlobalTime->mr)*6.0,0.0,1.0,0.0); break; case at_MinutesJump: //minuty z przeskokiem glRotatef(GlobalTime->mm*6.0,0.0,1.0,0.0); break; case at_HoursJump: //godziny skokowo 12h/360° glRotatef(GlobalTime->hh*30.0*0.5,0.0,1.0,0.0); break; case at_Hours24Jump: //godziny skokowo 24h/360° glRotatef(GlobalTime->hh*15.0*0.25,0.0,1.0,0.0); break; case at_Seconds: //sekundy płynnie glRotatef(GlobalTime->mr*6.0,0.0,1.0,0.0); break; case at_Minutes: //minuty płynnie glRotatef(GlobalTime->mm*6.0+GlobalTime->mr*0.1,0.0,1.0,0.0); break; case at_Hours: //godziny płynnie 12h/360° //glRotatef(GlobalTime->hh*30.0+GlobalTime->mm*0.5+GlobalTime->mr/120.0,0.0,1.0,0.0); glRotatef(2.0*Global::fTimeAngleDeg,0.0,1.0,0.0); break; case at_Hours24: //godziny płynnie 24h/360° //glRotatef(GlobalTime->hh*15.0+GlobalTime->mm*0.25+GlobalTime->mr/240.0,0.0,1.0,0.0); glRotatef(Global::fTimeAngleDeg,0.0,1.0,0.0); break; case at_Billboard: //obrót w pionie do kamery {matrix4x4 mat; //potrzebujemy współrzędne przesunięcia środka układu współrzędnych submodelu glGetDoublev(GL_MODELVIEW_MATRIX,mat.getArray()); //pobranie aktualnej matrycy float3 gdzie=float3(mat[3][0],mat[3][1],mat[3][2]); //początek układu współrzędnych submodelu względem kamery glLoadIdentity(); //macierz jedynkowa glTranslatef(gdzie.x,gdzie.y,gdzie.z); //początek układu zostaje bez zmian glRotated(atan2(gdzie.x,gdzie.z)*180.0/M_PI,0.0,1.0,0.0); //jedynie obracamy w pionie o kąt } break; case at_Wind: //ruch pod wpływem wiatru (wiatr będziemy liczyć potem...) glRotated(1.5*sin(M_PI*GlobalTime->mr/6.0),0.0,1.0,0.0); break; case at_Sky: //animacja nieba glRotated(Global::fLatitudeDeg,1.0,0.0,0.0); //ustawienie osi OY na północ //glRotatef(Global::fTimeAngleDeg,0.0,1.0,0.0); //obrót dobowy osi OX glRotated(-fmod(Global::fTimeAngleDeg,360.0),0.0,1.0,0.0); //obrót dobowy osi OX break; case at_IK11: //ostatni element animacji szkieletowej (podudzie, stopa) glRotatef(v_Angles.z,0.0,1.0,0.0); //obrót względem osi pionowej (azymut) glRotatef(v_Angles.x,1.0,0.0,0.0); //obrót względem poziomu (deklinacja) break; case at_DigiClk: //animacja zegara cyfrowego {//ustawienie animacji w submodelach potomnych TSubModel *sm=ChildGet(); do {//pętla po submodelach potomnych i obracanie ich o kąt zależy od czasu if (sm->pName) {//musi mieć niepustą nazwę if ((*sm->pName)>='0') if ((*sm->pName)<='5') //zegarek ma 6 cyfr maksymalnie sm->SetRotate(float3(0,1,0),-Global::fClockAngleDeg[(*sm->pName)-'0']); } sm=sm->NextGet(); } while (sm); } break; } if (mAnimMatrix) //można by to dać np. do at_Translate {glMultMatrixf(mAnimMatrix->readArray()); mAnimMatrix=NULL; //jak animator będzie potrzebował, to ustawi ponownie } }; void __fastcall TSubModel::RenderDL() {//główna procedura renderowania przez DL if (iVisible && (fSquareDist>=fSquareMinDist) && (fSquareDistreadArray()); if (b_Anim) RaAnimation(b_Anim); } if (eTypefCosFalloffAngle) //kąt większy niż maksymalny stożek swiatła { double Distdimm=1.0; if (fCosViewAngle0) switch (iFarAttenDecay) { case 1: Distdimm=fFarDecayRadius/(1+sqrt(fSquareDist)); //dorobic od kata break; case 2: Distdimm=fFarDecayRadius/(1+fSquareDist); //dorobic od kata break; } if (Distdimm>1) Distdimm=1; glColor3f(Diffuse[0]*Distdimm,Diffuse[1]*Distdimm,Diffuse[2]*Distdimm); */ // glPopMatrix(); // return; glCallList(uiDisplayList); //wyświetlenie warunkowe } } else if (eType==TP_STARS) { //glDisable(GL_LIGHTING); //Tolaris-030603: bo mu punkty swiecace sie blendowaly if (Global::fLuminanceRenderDL(); if (iFlags&0xC000) glPopMatrix(); } if (b_AnimRenderDL(); //dalsze rekurencyjnie }; //Render void __fastcall TSubModel::RenderAlphaDL() {//renderowanie przezroczystych przez DL if (iVisible && (fSquareDist>=fSquareMinDist) && (fSquareDistreadArray()); if (b_aAnim) RaAnimation(b_aAnim); } if (eTypeLength(); TSubModel *p; char c; if (!smLetter) {//jeśli nie ma tablicy, to ją stworzyć; miejsce nieodpowiednie, ale tymczasowo może być smLetter=new TSubModel*[256]; //tablica wskaźników submodeli dla wyświetlania tekstu ZeroMemory(smLetter,256*sizeof(TSubModel*)); //wypełnianie zerami p=Child; while (p) { smLetter[*p->pName]=p; p=p->Next; //kolejny znak } } for (i=1;i<=j;++i) { p=smLetter[(*pasText)[i]]; //znak do wyświetlenia if (p) {//na razie tylko jako przezroczyste p->RenderAlphaDL(); if (p->fMatrix) glMultMatrixf(p->fMatrix->readArray()); //przesuwanie widoku } } } else if (iAlpha&iFlags&0x002F0000) Child->RenderAlphaDL(); if (iFlags&0xC000) glPopMatrix(); } if (b_aAnimRenderAlphaDL(); }; //RenderAlpha void __fastcall TSubModel::RenderVBO() {//główna procedura renderowania przez VBO if (iVisible && (fSquareDist>=fSquareMinDist) && (fSquareDistreadArray()); if (b_Anim) RaAnimation(b_Anim); } if (eTypefCosFalloffAngle) //kąt większy niż maksymalny stożek swiatła { double Distdimm=1.0; if (fCosViewAngle.DecayRadius -- The distance over which the decay occurs. if (iFarAttenDecay>0) switch (iFarAttenDecay) { case 1: Distdimm=fFarDecayRadius/(1+sqrt(fSquareDist)); //dorobic od kata break; case 2: Distdimm=fFarDecayRadius/(1+fSquareDist); //dorobic od kata break; } if (Distdimm>1) Distdimm=1; */ glBindTexture(GL_TEXTURE_2D,0); //nie teksturować //glColor3f(f4Diffuse[0],f4Diffuse[1],f4Diffuse[2]); //glColorMaterial(GL_FRONT,GL_EMISSION); float color[4]={f4Diffuse[0]*Distdimm,f4Diffuse[1]*Distdimm,f4Diffuse[2]*Distdimm,0}; //glColor3f(f4Diffuse[0]*Distdimm,f4Diffuse[1]*Distdimm,f4Diffuse[2]*Distdimm); glColorMaterial(GL_FRONT,GL_EMISSION); glDisable(GL_LIGHTING); //Tolaris-030603: bo mu punkty swiecace sie blendowaly glColor3fv(color); //inaczej są białe glMaterialfv(GL_FRONT,GL_EMISSION,color); glDrawArrays(GL_POINTS,iVboPtr,iNumVerts); //narysuj wierzchołek z VBO glEnable(GL_LIGHTING); glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE); //co ma ustawiać glColor glMaterialfv(GL_FRONT,GL_EMISSION,emm2); //bez tego słupy się świecą } } else if (eType==TP_STARS) { //glDisable(GL_LIGHTING); //Tolaris-030603: bo mu punkty swiecace sie blendowaly if (Global::fLuminanceEndVBO(); //Ra: to też nie jest zbyt ładne if (pRoot->StartColorVBO()) {//wyświetlanie kolorowych punktów zamiast trójkątów glBindTexture(GL_TEXTURE_2D,0); //tekstury nie ma glColorMaterial(GL_FRONT,GL_EMISSION); glDisable(GL_LIGHTING); //Tolaris-030603: bo mu punkty swiecace sie blendowaly //glMaterialfv(GL_FRONT,GL_EMISSION,f4Diffuse); //zeby swiecilo na kolorowo glDrawArrays(GL_POINTS,iVboPtr,iNumVerts); //narysuj naraz wszystkie punkty z VBO glEnable(GL_LIGHTING); glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE); //glMaterialfv(GL_FRONT,GL_EMISSION,emm2); pRoot->EndVBO(); pRoot->StartVBO(); } } } /*Ra: tu coś jest bez sensu... else { glBindTexture(GL_TEXTURE_2D, 0); // if (eType==smt_FreeSpotLight) // { // if (iFarAttenDecay==0) // glColor3f(Diffuse[0],Diffuse[1],Diffuse[2]); // } // else //TODO: poprawic zeby dzialalo glColor3f(f4Diffuse[0],f4Diffuse[1],f4Diffuse[2]); glColorMaterial(GL_FRONT,GL_EMISSION); glDisable(GL_LIGHTING); //Tolaris-030603: bo mu punkty swiecace sie blendowaly //glBegin(GL_POINTS); glDrawArrays(GL_POINTS,iVboPtr,iNumVerts); //narysuj wierzchołek z VBO // glVertex3f(0,0,0); //glEnd(); glEnable(GL_LIGHTING); glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE); glMaterialfv(GL_FRONT,GL_EMISSION,emm2); //glEndList(); } */ if (Child!=NULL) if (iAlpha&iFlags&0x001F0000) Child->RenderVBO(); if (iFlags&0xC000) glPopMatrix(); } if (b_AnimRenderVBO(); //dalsze rekurencyjnie }; //RaRender void __fastcall TSubModel::RenderAlphaVBO() {//renderowanie przezroczystych przez VBO if (iVisible && (fSquareDist>=fSquareMinDist) && (fSquareDistreadArray()); if (b_aAnim) RaAnimation(b_aAnim); } glColor3fv(f4Diffuse); if (eTypeRenderAlphaVBO(); if (iFlags&0xC000) glPopMatrix(); } if (b_aAnimRenderAlphaVBO(); }; //RaRenderAlpha //--------------------------------------------------------------------------- void __fastcall TSubModel::RaArrayFill(CVertNormTex *Vert) {//wypełnianie tablic VBO if (Child) Child->RaArrayFill(Vert); if ((eTypeRaArrayFill(Vert); }; void __fastcall TSubModel::Info() {//zapisanie informacji o submodelu do obiektu pomocniczego TSubModelInfo *info=TSubModelInfo::pTable+TSubModelInfo::iCurrent; info->pSubModel=this; if (fMatrix&&(iFlags&0x8000)) //ma matrycę i jest ona niejednostkowa info->iTransform=info->iTotalTransforms++; if (TextureID>0) {//jeśli ma teksturę niewymienną for (int i=0;iiCurrent;++i) if (TextureID==info->pTable[i].pSubModel->TextureID) //porównanie z wcześniejszym {info->iTexture=info->pTable[i].iTexture; //taki jaki już był break; //koniec sprawdzania } if (info->iTexture<0) //jeśli nie znaleziono we wcześniejszych {info->iTexture=++info->iTotalTextures; //przydzielenie numeru tekstury w pliku (od 1) AnsiString t=AnsiString(pTexture); if (t.SubString(t.Length()-3,4)==".tga") t.Delete(t.Length()-3,4); else if (t.SubString(t.Length()-3,4)==".dds") t.Delete(t.Length()-3,4); if (t!=AnsiString(pTexture)) {//jeśli się zmieniło //pName=new char[token.length()+1]; //nie ma sensu skracać tabeli strcpy(pTexture,t.c_str()); } info->iTextureLen=t.Length()+1; //przygotowanie do zapisania, z zerem na końcu } } else info->iTexture=TextureID; //nie ma albo wymienna //if (asName.Length()) if (pName) {info->iName=info->iTotalNames++; //przydzielenie numeru nazwy w pliku (od 0) info->iNameLen=strlen(pName)+1; //z zerem na końcu } ++info->iCurrent; //przejście do kolejnego obiektu pomocniczego if (Child) {info->iChild=info->iCurrent; Child->Info(); } if (Next) {info->iNext=info->iCurrent; Next->Info(); } }; void __fastcall TSubModel::InfoSet(TSubModelInfo *info) {//ustawienie danych wg obiektu pomocniczego do zapisania w pliku int ile=(char*)&uiDisplayList-(char*)&eType; //ilość bajtów pomiędzy tymi zmiennymi ZeroMemory(this,sizeof(TSubModel)); //zerowaie całości CopyMemory(this,info->pSubModel,ile); //skopiowanie pamięci 1:1 iTexture=info->iTexture;//numer nazwy tekstury, a nie numer w OpenGL TextureID=info->iTexture;//numer tekstury w OpenGL iName=info->iName; //numer nazwy w obszarze nazw iMatrix=info->iTransform; //numer macierzy Next=(TSubModel*)info->iNext; //numer następnego Child=(TSubModel*)info->iChild; //numer potomnego iFlags&=~0x200; //nie jest wczytany z tekstowego //asTexture=asName=""; pTexture=pName=NULL; }; void __fastcall TSubModel::BinInit(TSubModel *s,float4x4 *m,float8 *v,TStringPack *t,TStringPack *n,bool dynamic) {//ustawienie wskaźników w submodelu iVisible=1; //tymczasowo używane Child=((int)Child>0)?s+(int)Child:NULL; //zerowy nie może być potomnym Next=((int)Next>0)?s+(int)Next:NULL; //zerowy nie może być następnym fMatrix=((iMatrix>=0)&&m)?m+iMatrix:NULL; //if (n&&(iName>=0)) asName=AnsiString(n->String(iName)); else asName=""; if (n&&(iName>=0)) {pName=n->String(iName); AnsiString s=AnsiString(pName); if (!s.IsEmpty()) {//jeśli dany submodel jest zgaszonym światłem, to domyślnie go ukrywamy if (s.SubString(1,8)=="Light_On") //jeśli jest światłem numerowanym iVisible=0; //to domyślnie wyłączyć, żeby się nie nakładało z obiektem "Light_Off" else if (dynamic) //inaczej wyłączało smugę w latarniach if (s.SubString(s.Length()-2,3)=="_on") //jeśli jest kontrolką w stanie zapalonym iVisible=0; //to domyślnie wyłączyć, żeby się nie nakładało z obiektem "_off" } } else pName=NULL; if (iTexture>0) {//obsługa stałej tekstury //TextureID=TTexturesManager::GetTextureID(t->String(TextureID)); //asTexture=AnsiString(t->String(iTexture)); pTexture=t->String(iTexture); AnsiString t=AnsiString(pTexture); if (t.LastDelimiter("/\\")==0) t.Insert(Global::asCurrentTexturePath,1); TextureID=TTexturesManager::GetTextureID(szTexturePath,Global::asCurrentTexturePath.c_str(),t.c_str()); //TexAlpha=TTexturesManager::GetAlpha(TextureID); //zmienna robocza //ustawienie cyklu przezroczyste/nieprzezroczyste zależnie od własności stałej tekstury //iFlags=(iFlags&~0x30)|(TTexturesManager::GetAlpha(TextureID)?0x20:0x10); //0x10-nieprzezroczysta, 0x20-przezroczysta if (Opacity<1.0) //przezroczystość z tekstury brana tylko dla Opacity 0! iFlags|=TTexturesManager::GetAlpha(TextureID)?0x20:0x10; //0x10-nieprzezroczysta, 0x20-przezroczysta else iFlags|=0x10; //normalnie nieprzezroczyste } b_aAnim=b_Anim; //skopiowanie animacji do drugiego cyklu iFlags&=~0x0200; //wczytano z pliku binarnego (nie jest właścicielem tablic) Vertices=v+iVboPtr; //if (!iNumVerts) eType=-1; //tymczasowo zmiana typu, żeby się nie renderowało na siłę }; void __fastcall TSubModel::AdjustDist() {//aktualizacja odległości faz LoD, zależna od rozdzielczości pionowej oraz multisamplingu if (fSquareMaxDist>0.0) fSquareMaxDist*=Global::fDistanceFactor; if (fSquareMinDist>0.0) fSquareMinDist*=Global::fDistanceFactor; //if (fNearAttenStart>0.0) fNearAttenStart*=Global::fDistanceFactor; //if (fNearAttenEnd>0.0) fNearAttenEnd*=Global::fDistanceFactor; if (Child) Child->AdjustDist(); if (Next) Next->AdjustDist(); }; void __fastcall TSubModel::ColorsSet(int *a,int *d,int*s) {//ustawienie kolorów dla modelu terenu int i; if (a) for (i=0;i<4;++i) f4Ambient[i]=a[i]/255.0; if (d) for (i=0;i<4;++i) f4Diffuse[i]=d[i]/255.0; if (s) for (i=0;i<4;++i) f4Specular[i]=s[i]/255.0; }; void __fastcall TSubModel::ParentMatrix(float4x4 *m) {//pobranie transformacji względem wstawienia modelu //jeśli nie zostało wykonane Init() (tzn. zaraz po wczytaniu T3D), to dodatkowy obrót //obrót T3D jest wymagany np. do policzenia wysokości pantografów *m=float4x4(*fMatrix); //skopiowanie, bo będziemy mnożyć //m(3)[1]=m[3][1]+0.054; //w górę o wysokość ślizgu (na razie tak) TSubModel *sm=this; while (sm->Parent) {//przenieść tę funkcję do modelu if (sm->Parent->GetMatrix()) *m=*sm->Parent->GetMatrix()**m; sm=sm->Parent; } //dla ostatniego może być potrzebny dodatkowy obrót, jeśli wczytano z T3D, a nie obrócono jeszcze }; float __fastcall TSubModel::MaxY(const float4x4 &m) {//obliczenie maksymalnej wysokości, na początek ślizgu w pantografie if (eType!=4) return 0; //tylko dla trójkątów liczymy if (iNumVerts<1) return 0; if (!Vertices) return 0; float y,my=m[0][1]*Vertices[0].Point.x+m[1][1]*Vertices[0].Point.y+m[2][1]*Vertices[0].Point.z+m[3][1]; for (int i=1;iChildAdd(SubModel); } else {//jeśli nie znaleziony, podczepiamy do łańcucha głównego SubModel->NextAdd(Root); //Ra: zmiana kolejności renderowania wymusza zmianę tu Root=SubModel; } ++iSubModelsCount; //teraz jest o 1 submodel więcej iFlags|=0x0200; //submodele są oddzielne }; TSubModel* __fastcall TModel3d::GetFromName(const char *sName) {//wyszukanie submodelu po nazwie if (!sName) return Root; //potrzebne do terenu z E3D if (iFlags&0x0200) //wczytany z pliku tekstowego, wyszukiwanie rekurencyjne return Root?Root->GetFromName(sName):NULL; else //wczytano z pliku binarnego, można wyszukać iteracyjnie { //for (int i=0;iGetFromName(sName):NULL; } }; /* TMaterial* __fastcall TModel3d::GetMaterialFromName(char *sName) { AnsiString tmp=AnsiString(sName).Trim(); for (int i=0; i0):false; //brak pliku albo problem z wczytaniem }; void __fastcall TModel3d::LoadFromBinFile(char *FileName,bool dynamic) {//wczytanie modelu z pliku binarnego WriteLog("Loading - binary model: "+AnsiString(FileName)); int i=0,j,k,ch,size; TFileStream *fs=new TFileStream(AnsiString(FileName),fmOpenRead); size=fs->Size>>2; iModel=new int[size]; //ten wskaźnik musi być w modelu, aby zwolnić pamięć fs->Read(iModel,fs->Size); //wczytanie pliku delete fs; float4x4 *m=NULL; //transformy //zestaw kromek: while ((i<<2)>2); //początek następnej kromki if (ch=='E3D0') //główna: 'E3D0',len,pod-kromki {//tylko tę kromkę znamy, może kiedyś jeszcze DOF się zrobi i+=2; while (i>2); //długość aktualnej kromki switch (ch) {case 'MDL0': //zmienne modelu: 'E3D0',len,(informacje o modelu) break; case 'VNT0': //wierzchołki: 'VNT0',len,(32 bajty na wierzchołek) iNumVerts=(k-2)>>3; m_nVertexCount=iNumVerts; m_pVNT=(CVertNormTex*)(iModel+i+2); break; case 'SUB0': //submodele: 'SUB0',len,(256 bajtów na submodel) iSubModelsCount=(k-2)/64; Root=(TSubModel*)(iModel+i+2); //numery na wskaźniki przetworzymy później break; case 'SUB1': //submodele: 'SUB1',len,(320 bajtów na submodel) iSubModelsCount=(k-2)/80; Root=(TSubModel*)(iModel+i+2); //numery na wskaźniki przetworzymy później for (ch=1;ch>1);++ch) *(((float*)m)+ch)=*(((double*)m)+ch); //przepisanie double do float break; case 'IDX1': //indeksy 1B: 'IDX2',len,(po bajcie na numer wierzchołka) break; case 'IDX2': //indeksy 2B: 'IDX2',len,(po 2 bajty na numer wierzchołka) break; case 'IDX4': //indeksy 4B: 'IDX4',len,(po 4 bajty na numer wierzchołka) break; case 'TEX0': //tekstury: 'TEX0',len,(łańcuchy zakończone zerem - pliki tekstur) Textures.Init((char*)(iModel+i)); //łącznie z nagłówkiem break; case 'TIX0': //indeks nazw tekstur Textures.InitIndex((int*)(iModel+i)); //łącznie z nagłówkiem break; case 'NAM0': //nazwy: 'NAM0',len,(łańcuchy zakończone zerem - nazwy submodeli) Names.Init((char*)(iModel+i)); //łącznie z nagłówkiem break; case 'NIX0': //indeks nazw submodeli Names.InitIndex((int*)(iModel+i)); //łącznie z nagłówkiem break; } i+=k; //przejście do kolejnej kromki } } i=j; } for (i=0;iParent=Root+i; //wpisanie wskaźnika nadrzędnego do potmnego if (Root[i].NextGet()) Root[i].NextGet()->Parent=Root[i].Parent; //skopiowanie wskaźnika nadrzędnego do kolejnego } iFlags&=~0x0200; return; }; void __fastcall TModel3d::LoadFromTextFile(char *FileName,bool dynamic) {//wczytanie submodelu z pliku tekstowego WriteLog("Loading - text model: "+AnsiString(FileName)); iFlags|=0x0200; //wczytano z pliku tekstowego (właścicielami tablic są submodle) cParser parser(FileName,cParser::buffer_FILE); //Ra: tu powinno być "models\\"... TSubModel *SubModel; std::string token; parser.getToken(token); iNumVerts=0; //w konstruktorze to jest while (token!="" || parser.eof()) { std::string parent; //parser.getToken(parent); parser.getTokens(1,false); //nazwa submodelu nadrzędnego bez zmieny na małe parser >> parent; if (parent=="") break; SubModel=new TSubModel(); iNumVerts+=SubModel->Load(parser,this,iNumVerts,dynamic); SubModel->Parent=AddToNamed(parent.c_str(),SubModel); //będzie potrzebne do wyliczenia pozycji, np. pantografu //iSubModelsCount++; parser.getToken(token); } //Ra: od wersji 334 przechylany jest cały model, a nie tylko pierwszy submodel //ale bujanie kabiny nadal używa bananów :( od 393 przywrócone, ale z dodatkowym warunkiem if (Global::iConvertModels&4) {//automatyczne banany czasem psuły przechylanie kabin... if (dynamic&&Root) {if (Root->NextGet()) //jeśli ma jakiekolwiek kolejne {//dynamic musi mieć "banana", bo tylko pierwszy obiekt jest animowany, a następne nie SubModel=new TSubModel(); //utworzenie pustego SubModel->ChildAdd(Root); Root=SubModel; ++iSubModelsCount; } Root->WillBeAnimated(); //bo z tym jest dużo problemów } } } void __fastcall TModel3d::Init() {//obrócenie początkowe układu współrzędnych, dla pojazdów wykonywane po analizie animacji if (iFlags&0x8000) return; //operacje zostały już wykonane if (Root) {if (iFlags&0x0200) //jeśli wczytano z pliku tekstowego {//jest jakiś dziwny błąd, że obkręcany ma być tylko ostatni submodel głównego łańcucha //TSubModel *p=Root; //do //{p->InitialRotate(true); //ostatniemu należy się konwersja układu współrzędnych // p=p->NextGet(); //} //while (p->NextGet()) //Root->InitialRotate(false); //a poprzednim tylko optymalizacja Root->InitialRotate(true); //argumet określa, czy wykonać pierwotny obrót } iFlags|=Root->FlagsCheck()|0x8000; //flagi całego modelu if (!asBinary.IsEmpty()) //jeśli jest podana nazwa {if (Global::iConvertModels) //i włączony zapis SaveToBinFile(asBinary.c_str()); //utworzy tablicę (m_pVNT) asBinary=""; //zablokowanie powtórnego zapisu } if (iNumVerts) { if (Global::fDistanceFactor!=1.0) //trochę zaoszczędzi czasu na modelach z wieloma submocelami Root->AdjustDist(); //aktualizacja odległości faz LoD, zależnie od rozdzielczości pionowej oraz multisamplingu if (Global::bUseVBO) {if (!m_pVNT) //jeśli nie ma jeszcze tablicy (wczytano z pliku tekstowego) {//tworzenie tymczasowej tablicy z wierzchołkami całego modelu MakeArray(iNumVerts); //tworzenie tablic dla VBO Root->RaArrayFill(m_pVNT); //wypełnianie tablicy BuildVBOs(); //tworzenie VBO i usuwanie tablicy z pamięci } else BuildVBOs(false); //tworzenie VBO bez usuwania tablicy z pamięci } else {//przygotowanie skompilowanych siatek dla DisplayLists Root->DisplayLists(); //tworzenie skompilowanej listy dla submodelu } //if (Root->TextureID) //o ile ma teksturę // Root->iFlags|=0x80; //konieczność ustawienia tekstury } } }; void __fastcall TModel3d::SaveToBinFile(char *FileName) {//zapis modelu binarnego WriteLog("Saving E3D binary model."); int i,zero=0; TSubModelInfo *info=new TSubModelInfo[iSubModelsCount]; info->Reset(); Root->Info(); //zebranie informacji o submodelach int len; //łączna długość pliku int sub; //ilość submodeli (w bajtach) int tra; //wielkość obszaru transformów int vnt; //wielkość obszaru wierzchołków int tex=0; //wielkość obszaru nazw tekstur int nam=0; //wielkość obszaru nazw submodeli sub=8+sizeof(TSubModel)*iSubModelsCount; tra=info->iTotalTransforms?8+64*info->iTotalTransforms:0; vnt=8+32*iNumVerts; for (i=0;iasName,*asT=&roboczy->asTexture; //roboczy->FirstInit(); //żeby delete nie usuwało czego nie powinno TFileStream *fs=new TFileStream(AnsiString(FileName),fmCreate); fs->Write("E3D0",4); //kromka główna fs->Write(&len,4); {fs->Write("SUB0",4); //dane submodeli fs->Write(&sub,4); for (i=0;iInfoSet(info+i); fs->Write(roboczy,sizeof(TSubModel)); //zapis jednego submodelu } } if (tra) {//zapis transformów fs->Write("TRA0",4); //transformy fs->Write(&tra,4); for (i=0;i=0) fs->Write(info[i].pSubModel->GetMatrix(),16*4); } {//zapis wierzchołków MakeArray(iNumVerts); //tworzenie tablic dla VBO Root->RaArrayFill(m_pVNT); //wypełnianie tablicy fs->Write("VNT0",4); //wierzchołki fs->Write(&vnt,4); fs->Write(m_pVNT,32*iNumVerts); } if (tex) //może być jeden submodel ze zmienną teksturą i nazwy nie będzie {//zapis nazw tekstur fs->Write("TEX0",4); //nazwy tekstur i=(tex+3)&~3; //zaokrąglenie w górę fs->Write(&i,4); fs->Write(&zero,1); //ciąg o numerze zero nie jest używany, ma tylko znacznik końca for (i=0;iWrite(info[i].pSubModel->pTexture,info[i].iTextureLen); if ((-tex)&3) fs->Write(&zero,((-tex)&3)); //wyrównanie do wielokrotności 4 bajtów } if (nam) //może być jeden anonimowy submodel w modelu {//zapis nazw submodeli fs->Write("NAM0",4); //nazwy submodeli i=(nam+3)&~3; //zaokrąglenie w górę fs->Write(&i,4); for (i=0;iWrite(info[i].pSubModel->pName,info[i].iNameLen); if ((-nam)&3) fs->Write(&zero,((-nam)&3)); //wyrównanie do wielokrotności 4 bajtów } delete fs; //roboczy->FirstInit(); //żeby delete nie usuwało czego nie powinno //roboczy->iFlags=0; //żeby delete nie usuwało czego nie powinno //roboczy->asName)=asN; //&roboczy->asTexture=asT; delete roboczy; delete[] info; }; void __fastcall TModel3d::BreakHierarhy() { Error("Not implemented yet :("); }; /* void __fastcall TModel3d::Render(vector3 pPosition,double fAngle,GLuint ReplacableSkinId,int iAlpha) { // glColor3f(1.0f,1.0f,1.0f); // glColor3f(0.0f,0.0f,0.0f); glPushMatrix(); glTranslated(pPosition.x,pPosition.y,pPosition.z); if (fAngle!=0) glRotatef(fAngle,0,1,0); /* matrix4x4 Identity; Identity.Identity(); matrix4x4 CurrentMatrix; glGetdoublev(GL_MODELVIEW_MATRIX,CurrentMatrix.getArray()); vector3 pos=vector3(0,0,0); pos=CurrentMatrix*pos; fSquareDist=SquareMagnitude(pos); * / fSquareDist=SquareMagnitude(pPosition-Global::GetCameraPosition()); #ifdef _DEBUG if (Root) Root->Render(ReplacableSkinId,iAlpha); #else Root->Render(ReplacableSkinId,iAlpha); #endif glPopMatrix(); }; */ void __fastcall TModel3d::Render(double fSquareDistance,GLuint *ReplacableSkinId,int iAlpha) { iAlpha^=0x0F0F000F; //odwrócenie flag tekstur, aby wyłapać nieprzezroczyste if (iAlpha&iFlags&0x1F1F001F) //czy w ogóle jest co robić w tym cyklu? {TSubModel::fSquareDist=fSquareDistance; //zmienna globalna! Root->ReplacableSet(ReplacableSkinId,iAlpha); Root->RenderDL(); } }; void __fastcall TModel3d::RenderAlpha(double fSquareDistance,GLuint *ReplacableSkinId,int iAlpha) { if (iAlpha&iFlags&0x2F2F002F) { TSubModel::fSquareDist=fSquareDistance; //zmienna globalna! Root->ReplacableSet(ReplacableSkinId,iAlpha); Root->RenderAlphaDL(); } }; /* void __fastcall TModel3d::RaRender(vector3 pPosition,double fAngle,GLuint *ReplacableSkinId,int iAlpha) { // glColor3f(1.0f,1.0f,1.0f); // glColor3f(0.0f,0.0f,0.0f); glPushMatrix(); //zapamiętanie matrycy przekształcenia glTranslated(pPosition.x,pPosition.y,pPosition.z); if (fAngle!=0) glRotatef(fAngle,0,1,0); /* matrix4x4 Identity; Identity.Identity(); matrix4x4 CurrentMatrix; glGetdoublev(GL_MODELVIEW_MATRIX,CurrentMatrix.getArray()); vector3 pos=vector3(0,0,0); pos=CurrentMatrix*pos; fSquareDist=SquareMagnitude(pos); */ /* fSquareDist=SquareMagnitude(pPosition-Global::GetCameraPosition()); //zmienna globalna! if (StartVBO()) {//odwrócenie flag, aby wyłapać nieprzezroczyste Root->ReplacableSet(ReplacableSkinId,iAlpha^0x0F0F000F); Root->RaRender(); EndVBO(); } glPopMatrix(); //przywrócenie ustawień przekształcenia }; */ void __fastcall TModel3d::RaRender(double fSquareDistance,GLuint *ReplacableSkinId,int iAlpha) {//renderowanie specjalne, np. kabiny iAlpha^=0x0F0F000F; //odwrócenie flag tekstur, aby wyłapać nieprzezroczyste if (iAlpha&iFlags&0x1F1F001F) //czy w ogóle jest co robić w tym cyklu? {TSubModel::fSquareDist=fSquareDistance; //zmienna globalna! if (StartVBO()) {//odwrócenie flag, aby wyłapać nieprzezroczyste Root->ReplacableSet(ReplacableSkinId,iAlpha); Root->pRoot=this; Root->RenderVBO(); EndVBO(); } } }; void __fastcall TModel3d::RaRenderAlpha(double fSquareDistance,GLuint *ReplacableSkinId,int iAlpha) {//renderowanie specjalne, np. kabiny if (iAlpha&iFlags&0x2F2F002F) //czy w ogóle jest co robić w tym cyklu? {TSubModel::fSquareDist=fSquareDistance; //zmienna globalna! if (StartVBO()) {Root->ReplacableSet(ReplacableSkinId,iAlpha); Root->RenderAlphaVBO(); EndVBO(); } } }; /* void __fastcall TModel3d::RaRenderAlpha(vector3 pPosition,double fAngle,GLuint *ReplacableSkinId,int iAlpha) { glPushMatrix(); glTranslatef(pPosition.x,pPosition.y,pPosition.z); if (fAngle!=0) glRotatef(fAngle,0,1,0); fSquareDist=SquareMagnitude(pPosition-Global::GetCameraPosition()); //zmienna globalna! if (StartVBO()) {Root->ReplacableSet(ReplacableSkinId,iAlpha); Root->RaRenderAlpha(); EndVBO(); } glPopMatrix(); }; */ //----------------------------------------------------------------------------- //2011-03-16 cztery nowe funkcje renderowania z możliwością pochylania obiektów //----------------------------------------------------------------------------- void __fastcall TModel3d::Render(vector3 *vPosition,vector3 *vAngle,GLuint *ReplacableSkinId,int iAlpha) {//nieprzezroczyste, Display List glPushMatrix(); glTranslated(vPosition->x,vPosition->y,vPosition->z); if (vAngle->y!=0.0) glRotated(vAngle->y,0.0,1.0,0.0); if (vAngle->x!=0.0) glRotated(vAngle->x,1.0,0.0,0.0); if (vAngle->z!=0.0) glRotated(vAngle->z,0.0,0.0,1.0); TSubModel::fSquareDist=SquareMagnitude(*vPosition-Global::GetCameraPosition()); //zmienna globalna! //odwrócenie flag, aby wyłapać nieprzezroczyste Root->ReplacableSet(ReplacableSkinId,iAlpha^0x0F0F000F); Root->RenderDL(); glPopMatrix(); }; void __fastcall TModel3d::RenderAlpha(vector3* vPosition,vector3* vAngle,GLuint *ReplacableSkinId,int iAlpha) {//przezroczyste, Display List glPushMatrix(); glTranslated(vPosition->x,vPosition->y,vPosition->z); if (vAngle->y!=0.0) glRotated(vAngle->y,0.0,1.0,0.0); if (vAngle->x!=0.0) glRotated(vAngle->x,1.0,0.0,0.0); if (vAngle->z!=0.0) glRotated(vAngle->z,0.0,0.0,1.0); TSubModel::fSquareDist=SquareMagnitude(*vPosition-Global::GetCameraPosition()); //zmienna globalna! Root->ReplacableSet(ReplacableSkinId,iAlpha); Root->RenderAlphaDL(); glPopMatrix(); }; void __fastcall TModel3d::RaRender(vector3* vPosition,vector3* vAngle,GLuint *ReplacableSkinId,int iAlpha) {//nieprzezroczyste, VBO glPushMatrix(); glTranslated(vPosition->x,vPosition->y,vPosition->z); if (vAngle->y!=0.0) glRotated(vAngle->y,0.0,1.0,0.0); if (vAngle->x!=0.0) glRotated(vAngle->x,1.0,0.0,0.0); if (vAngle->z!=0.0) glRotated(vAngle->z,0.0,0.0,1.0); TSubModel::fSquareDist=SquareMagnitude(*vPosition-Global::GetCameraPosition()); //zmienna globalna! if (StartVBO()) {//odwrócenie flag, aby wyłapać nieprzezroczyste Root->ReplacableSet(ReplacableSkinId,iAlpha^0x0F0F000F); Root->RenderVBO(); EndVBO(); } glPopMatrix(); }; void __fastcall TModel3d::RaRenderAlpha(vector3* vPosition,vector3* vAngle,GLuint *ReplacableSkinId,int iAlpha) {//przezroczyste, VBO glPushMatrix(); glTranslated(vPosition->x,vPosition->y,vPosition->z); if (vAngle->y!=0.0) glRotated(vAngle->y,0.0,1.0,0.0); if (vAngle->x!=0.0) glRotated(vAngle->x,1.0,0.0,0.0); if (vAngle->z!=0.0) glRotated(vAngle->z,0.0,0.0,1.0); TSubModel::fSquareDist=SquareMagnitude(*vPosition-Global::GetCameraPosition()); //zmienna globalna! if (StartVBO()) {Root->ReplacableSet(ReplacableSkinId,iAlpha); Root->RenderAlphaVBO(); EndVBO(); } glPopMatrix(); }; //----------------------------------------------------------------------------- //2012-02 funkcje do tworzenia terenu z E3D //----------------------------------------------------------------------------- int __fastcall TModel3d::TerrainCount() {//zliczanie kwadratów kilometrowych (główna linia po Next) do tworznia tablicy int i=0; TSubModel *r=Root; while (r) {r=r->NextGet(); ++i; } return i; }; TSubModel* __fastcall TModel3d::TerrainSquare(int n) {//pobieranie wskaźnika do submodelu (n) int i=0; TSubModel *r=Root; while (iNextGet(); ++i; } r->UnFlagNext(); //blokowanie wyświetlania po Next głównej listy return r; }; void __fastcall TModel3d::TerrainRenderVBO(int n) {//renderowanie terenu z VBO glPushMatrix(); //glTranslated(vPosition->x,vPosition->y,vPosition->z); //if (vAngle->y!=0.0) glRotated(vAngle->y,0.0,1.0,0.0); //if (vAngle->x!=0.0) glRotated(vAngle->x,1.0,0.0,0.0); //if (vAngle->z!=0.0) glRotated(vAngle->z,0.0,0.0,1.0); //TSubModel::fSquareDist=SquareMagnitude(*vPosition-Global::GetCameraPosition()); //zmienna globalna! if (StartVBO()) {//odwrócenie flag, aby wyłapać nieprzezroczyste //Root->ReplacableSet(ReplacableSkinId,iAlpha^0x0F0F000F); TSubModel *r=Root; while (r) { if (r->iVisible==n) //tylko jeśli ma być widoczny w danej ramce (problem dla 0==false) r->RenderVBO(); //sub kolejne (Next) się nie wyrenderują r=r->NextGet(); } EndVBO(); } glPopMatrix(); };