/* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* MaSzyna EU07 locomotive simulator Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others */ #include "stdafx.h" #include "Model3d.h" #include "Globals.h" #include "logs.h" #include "mczapkie/mctools.h" #include "Usefull.h" #include "Texture.h" #include "Timer.h" #include "mtable.h" //--------------------------------------------------------------------------- using namespace Mtable; 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 std::string *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; i < n; ++i) { // wyszukiwanie łańcuchów nie jest zbyt optymalne, ale nie musi być while (*ptr) ++ptr; // wyszukiwanie zera ++ptr; // pominięcie zera if (ptr > data + max) return NULL; // zbyt wysoki numer } return ptr; }; TSubModel::TSubModel() { ZeroMemory(this, sizeof(TSubModel)); // istotne przy zapisywaniu wersji binarnej FirstInit(); }; void 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 }; 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 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 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 TSubModel::SeekFaceNormal(DWORD *Masks, int f,DWORD dwMask,vector3 // *pt,GLVERTEX // *Vertices) int TSubModel::SeekFaceNormal(unsigned int *Masks, int f, unsigned int 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; i < iNumFaces; ++i) // pętla po trójkątach, od trójkąta (f) if (Masks[i] & dwMask) // jeśli wspólna maska powierzchni { p = Vertices + 3 * i; if (p->Point == *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(false); return ( static_cast(value) / base ); }; template inline void readColor(cParser &parser, ColorT *color) { double discard; parser.getTokens( 4, false ); parser >> discard >> color[ 0 ] >> color[ 1 ] >> color[ 2 ]; }; inline void readColor(cParser &parser, int &color) { int r, g, b, discard; parser.getTokens( 4, false ); parser >> discard >> r >> g >> 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 parser.getTokens( 16, false ); for( int x = 0; x <= 3; ++x ) // wiersze for( int y = 0; y <= 3; ++y ) // kolumny parser >> matrix( x )[ y ]; }; int 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(); 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.compare(0, 8, "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(); 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 (eType < TP_ROTATOR) readColor(parser, f4Ambient); // ignoruje token przed readColor(parser, f4Diffuse); if (eType < TP_ROTATOR) readColor(parser, f4Specular); parser.ignoreTokens(1); // zignorowanie nazwy "SelfIllum:" { std::string light = parser.getToken(); if (light == "true") fLight = 2.0; // zawsze świeci else if (light == "false") fLight = -1.0; // zawsze ciemy else fLight = std::stod(light); }; if (eType == TP_FREESPOTLIGHT) { if( !parser.expectToken( "nearattenstart:" ) ) { Error( "Model light parse failure!" ); } std::string discard; parser.getTokens( 13, false ); parser >> fNearAttenStart >> discard >> fNearAttenEnd >> discard >> bUseNearAtten >> discard >> iFarAttenDecay >> discard >> fFarDecayRadius >> discard >> fCosFalloffAngle // kąt liczony dla średnicy, a nie promienia >> discard >> fCosHotspotAngle; // kąt liczony dla średnicy, a nie promienia fCosFalloffAngle = cos( DegToRad( 0.5 * fCosFalloffAngle ) ); fCosHotspotAngle = cos( DegToRad( 0.5 * fCosHotspotAngle ) ); iNumVerts = 1; iFlags |= 0x4010; // rysowane w cyklu nieprzezroczystych, macierz musi // zostać bez zmiany } else if (eType < TP_ROTATOR) { std::string discard; parser.getTokens( 5, false ); parser >> discard >> bWire >> discard >> fWireSize >> discard; Opacity = readIntAsDouble(parser, 100.0f); // wymagane jest 0 dla szyb, 100 idzie w nieprzezroczyste if (Opacity > 1.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(); 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, const_cast(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; std::string discard; parser.getTokens( 5, false ); parser >> discard >> fSquareMaxDist >> discard >> fSquareMinDist >> discard; if( fSquareMaxDist >= 0.0 ) { fSquareMaxDist *= fSquareMaxDist; } else { fSquareMaxDist = 15000 * 15000; }// 15km to więcej, niż się obecnie wyświetla fSquareMinDist *= fSquareMinDist; 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 unsigned int *sg; // maski przynależności trójkątów do powierzchni if (eType < TP_ROTATOR) { // wczytywanie wierzchołków parser.getTokens( 2, false ); parser >> discard >> token; // Ra 15-01: to wczytać jako tekst - jeśli pierwszy znak zawiera "*", to // dalej będzie nazwa wcześniejszego submodelu, z którego należy wziąć // wierzchołki // zapewni to jakąś zgodność wstecz, bo zamiast liczby będzie ciąg, którego // wartość powinna być uznana jako zerowa // parser.getToken(iNumVerts); if (token[0] == '*') { // jeśli pierwszy znak jest gwiazdką, poszukać // submodelu o nazwie bez tej gwiazdki i wziąć z // niego wierzchołki Error("Verticles reference not yet supported!"); } else { // normalna lista wierzchołków iNumVerts = atoi(token.c_str()); if (iNumVerts % 3) { iNumVerts = 0; Error("Mesh error, (iNumVertices=" + std::to_string(iNumVerts) + ")%3<>0"); return 0; } // Vertices=new GLVERTEX[iNumVerts]; if (iNumVerts) { Vertices = new float8[iNumVerts]; iNumFaces = iNumVerts / 3; sg = new unsigned int[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; i < iNumVerts; i++) { // Ra: z konwersją na układ scenerii - będzie wydajniejsze // wyświetlanie wsp[i] = -1; // wektory normalne nie są policzone dla tego wierzchołka if ((i % 3) == 0) { // jeśli będzie maska -1, to dalej będą // wierzchołki z wektorami normalnymi, podanymi // jawnie maska = parser.getToken(false); // maska powierzchni trójkąta sg[i / 3] = (maska == -1) ? 0 : maska; // dla maski -1 będzie 0, // czyli nie ma wspólnych // wektorów normalnych } parser.getTokens( 3, false ); parser >> Vertices[i].Point.x >> Vertices[i].Point.y >> Vertices[i].Point.z; if (maska == -1) { // jeśli wektory normalne podane jawnie parser.getTokens( 3, false ); parser >> Vertices[i].Normal.x >> Vertices[i].Normal.y >> Vertices[i].Normal.z; wsp[i] = i; // wektory normalne "są już policzone" } parser.getTokens( 2, false ); parser >> Vertices[i].tu >> Vertices[i].tv; if (i % 3 == 2) // jeżeli wczytano 3 punkty { if (Vertices[i].Point == Vertices[i - 1].Point || Vertices[i - 1].Point == Vertices[i - 2].Point || Vertices[i - 2].Point == Vertices[i].Point) { // jeżeli punkty się nakładają na siebie --iNumFaces; // o jeden trójkąt mniej iNumVerts -= 3; // czyli o 3 wierzchołki i -= 3; // wczytanie kolejnego w to miejsce WriteLog(std::string("Degenerated triangle ignored in: \"") + pName + "\", verticle " + std::to_string(i)); } if (i > 0) // 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( std::string("Too large triangle ignored in: \"") + 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 < iNumFaces; i++) // pętla po trójkątach - będzie // szybciej, jak wstępnie przeliczymy // normalne trójkątów n[i] = SafeNormalize( CrossProduct(Vertices[i * 3].Point - Vertices[i * 3 + 1].Point, Vertices[i * 3].Point - Vertices[i * 3 + 2].Point)); int v; // indeks dla wierzchołków int f; // numer trójkąta stycznego float3 norm; // roboczy wektor normalny for (v = 0; v < iNumVerts; v++) { // pętla po wierzchołkach trójkątów if (wsp[v] >= 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 std::string discard; parser.getTokens( 2, false ); parser >> discard >> iNumVerts; // Vertices=new GLVERTEX[iNumVerts]; Vertices = new float8[iNumVerts]; int i, j; for (i = 0; i < iNumVerts; i++) { if( i % 3 == 0 ) { parser.ignoreToken(); // maska powierzchni trójkąta } parser.getTokens( 5, false ); parser >> Vertices[ i ].Point.x >> Vertices[ i ].Point.y >> Vertices[ i ].Point.z >> j // zakodowany kolor >> discard; Vertices[i].Normal.x = ((j)&0xFF) / 255.0; // R Vertices[i].Normal.y = ((j >> 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 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 * 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 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 (eType < TP_ROTATOR) { if (iNumVerts > 0) { 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; i < iNumVerts; i++) { /* glNormal3dv(&Vertices[i].Normal.x); glTexCoord2f(Vertices[i].tu,Vertices[i].tv); glVertex3dv(&Vertices[i].Point.x); */ glNormal3fv(&Vertices[i].Normal.x); glTexCoord2f(Vertices[i].tu, Vertices[i].tv); glVertex3fv(&Vertices[i].Point.x); }; glEnd(); #endif glEndList(); } } else if (eType == TP_FREESPOTLIGHT) { uiDisplayList = glGenLists(1); glNewList(uiDisplayList, GL_COMPILE); 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); glVertex3f(0, 0, 0); glEnd(); glEnable(GL_LIGHTING); glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); glMaterialfv(GL_FRONT, GL_EMISSION, emm2); glEndList(); } else if (eType == TP_STARS) { // punkty świecące dookólnie uiDisplayList = glGenLists(1); glNewList(uiDisplayList, GL_COMPILE); glBindTexture(GL_TEXTURE_2D, 0); // tekstury nie ma glColorMaterial(GL_FRONT, GL_EMISSION); glDisable(GL_LIGHTING); // Tolaris-030603: bo mu punkty swiecace sie blendowaly glBegin(GL_POINTS); for (int i = 0; i < iNumVerts; i++) { glColor3f(Vertices[i].Normal.x, Vertices[i].Normal.y, Vertices[i].Normal.z); // glVertex3dv(&Vertices[i].Point.x); glVertex3fv(&Vertices[i].Point.x); }; glEnd(); glEnable(GL_LIGHTING); glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); glMaterialfv(GL_FRONT, GL_EMISSION, emm2); glEndList(); } // SafeDeleteArray(Vertices); //przy VBO muszą zostać do załadowania całego // modelu if (Child) Child->DisplayLists(); if (Next) Next->DisplayLists(); }; void 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; i < iNumVerts; ++i) Vertices[i].Point = (*mat) * Vertices[i].Point; (*mat)(3)[0] = (*mat)(3)[1] = (*mat)(3)[2] = 0.0; // zerujemy przesunięcie przed obracaniem normalnych if (eType != TP_STARS) // gwiazdki mają kolory zamiast normalnych, to // ich wtedy nie ruszamy for (int i = 0; i < iNumVerts; ++i) Vertices[i].Normal = SafeNormalize((*mat) * Vertices[i].Normal); } mat->Identity(); // 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; i < iNumVerts; ++i) { Vertices[i].Point.x = -Vertices[i].Point.x; // zmiana znaku X t = Vertices[i].Point.y; // zamiana Y i Z Vertices[i].Point.y = Vertices[i].Point.z; Vertices[i].Point.z = t; // wektory normalne również trzeba przekształcić, bo się źle oświetlają Vertices[i].Normal.x = -Vertices[i].Normal.x; // zmiana znaku X t = Vertices[i].Normal.y; // zamiana Y i Z Vertices[i].Normal.y = Vertices[i].Normal.z; Vertices[i].Normal.z = t; } if (Child) Child->InitialRotate(doit); // potomne ewentualnie obrócimy } if (Next) Next->InitialRotate(doit); }; void 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 TSubModel::NextAdd(TSubModel *SubModel) { // dodanie submodelu kolejnego (wspólny przodek) if (Next) Next->NextAdd(SubModel); else Next = SubModel; }; int 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 = 0; 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 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 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 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 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 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 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 * TSubModel::GetFromName(std::string const &search, bool i) { return GetFromName(search.c_str(), i); }; TSubModel * TSubModel::GetFromName(char const *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 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]) >= '0') if ((sm->pName[0]) <= '5') // zegarek ma 6 cyfr maksymalnie sm->SetRotate(float3(0, 1, 0), -Global::fClockAngleDeg[(sm->pName[0]) - '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 TSubModel::RenderDL() { // główna procedura renderowania przez DL if (iVisible && (fSquareDist >= fSquareMinDist) && (fSquareDist < fSquareMaxDist)) { if (iFlags & 0xC000) { glPushMatrix(); if (fMatrix) glMultMatrixf(fMatrix->readArray()); if (b_Anim) RaAnimation(b_Anim); } if (eType < TP_ROTATOR) { // renderowanie obiektów OpenGL if (iAlpha & iFlags & 0x1F) // rysuj gdy element nieprzezroczysty { if (TextureID < 0) // && (ReplacableSkinId!=0)) { // zmienialne skóry glBindTexture(GL_TEXTURE_2D, ReplacableSkinId[-TextureID]); // TexAlpha=!(iAlpha&1); //zmiana tylko w przypadku wymienej tekstury } else glBindTexture(GL_TEXTURE_2D, TextureID); // również 0 if (Global::fLuminance < fLight) { glMaterialfv(GL_FRONT, GL_EMISSION, f4Diffuse); // zeby swiecilo na kolorowo glCallList(uiDisplayList); // tylko dla siatki glMaterialfv(GL_FRONT, GL_EMISSION, emm2); } else glCallList(uiDisplayList); // tylko dla siatki } } else if (eType == TP_FREESPOTLIGHT) { // wersja DL matrix4x4 mat; // macierz opisuje układ renderowania względem kamery glGetDoublev(GL_MODELVIEW_MATRIX, mat.getArray()); // kąt między kierunkiem światła a współrzędnymi kamery vector3 gdzie = mat * vector3(0, 0, 0); // pozycja punktu świecącego względem kamery fCosViewAngle = DotProduct(Normalize(mat * vector3(0, 0, 1) - gdzie), Normalize(gdzie)); if (fCosViewAngle > fCosFalloffAngle) // kąt większy niż maksymalny stożek swiatła { double Distdimm = 1.0; if (fCosViewAngle < fCosHotspotAngle) // zmniejszona jasność między Hotspot a Falloff if (fCosFalloffAngle < fCosHotspotAngle) Distdimm = 1.0 - (fCosHotspotAngle - fCosViewAngle) / (fCosHotspotAngle - fCosFalloffAngle); glColor3f(f4Diffuse[0] * Distdimm, f4Diffuse[1] * Distdimm, f4Diffuse[2] * Distdimm); /* TODO: poprawic to zeby dzialalo 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; 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::fLuminance < fLight) { glMaterialfv(GL_FRONT, GL_EMISSION, f4Diffuse); // zeby swiecilo na kolorowo glCallList(uiDisplayList); // narysuj naraz wszystkie punkty z DL glMaterialfv(GL_FRONT, GL_EMISSION, emm2); } } if (Child != NULL) if (iAlpha & iFlags & 0x001F0000) Child->RenderDL(); if (iFlags & 0xC000) glPopMatrix(); } if (b_Anim < at_SecondsJump) b_Anim = at_None; // wyłączenie animacji dla kolejnego użycia subm if (Next) if (iAlpha & iFlags & 0x1F000000) Next->RenderDL(); // dalsze rekurencyjnie }; // Render void TSubModel::RenderAlphaDL() { // renderowanie przezroczystych przez DL if (iVisible && (fSquareDist >= fSquareMinDist) && (fSquareDist < fSquareMaxDist)) { if (iFlags & 0xC000) { glPushMatrix(); if (fMatrix) glMultMatrixf(fMatrix->readArray()); if (b_aAnim) RaAnimation(b_aAnim); } if (eType < TP_ROTATOR) { // renderowanie obiektów OpenGL if (iAlpha & iFlags & 0x2F) // rysuj gdy element przezroczysty { if (TextureID < 0) // && (ReplacableSkinId!=0)) { // zmienialne skóry glBindTexture(GL_TEXTURE_2D, ReplacableSkinId[-TextureID]); // TexAlpha=iAlpha&1; //zmiana tylko w przypadku wymienej tekstury } else glBindTexture(GL_TEXTURE_2D, TextureID); // również 0 if (Global::fLuminance < fLight) { glMaterialfv(GL_FRONT, GL_EMISSION, f4Diffuse); // zeby swiecilo na kolorowo glCallList(uiDisplayList); // tylko dla siatki glMaterialfv(GL_FRONT, GL_EMISSION, emm2); } else glCallList(uiDisplayList); // tylko dla siatki } } else if (eType == TP_FREESPOTLIGHT) { // dorobić aureolę! } if (Child != NULL) if (eType == TP_TEXT) { // tekst renderujemy w specjalny sposób, zamiast // submodeli z łańcucha Child int i, j = pasText->size(); 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_aAnim < at_SecondsJump) b_aAnim = at_None; // wyłączenie animacji dla kolejnego użycia submodelu if (Next != NULL) if (iAlpha & iFlags & 0x2F000000) Next->RenderAlphaDL(); }; // RenderAlpha void TSubModel::RenderVBO() { // główna procedura renderowania przez VBO if (iVisible && (fSquareDist >= fSquareMinDist) && (fSquareDist < fSquareMaxDist)) { if (iFlags & 0xC000) { glPushMatrix(); if (fMatrix) glMultMatrixf(fMatrix->readArray()); if (b_Anim) RaAnimation(b_Anim); } if (eType < TP_ROTATOR) { // renderowanie obiektów OpenGL if (iAlpha & iFlags & 0x1F) // rysuj gdy element nieprzezroczysty { if (TextureID < 0) // && (ReplacableSkinId!=0)) { // zmienialne skóry glBindTexture(GL_TEXTURE_2D, ReplacableSkinId[-TextureID]); // TexAlpha=!(iAlpha&1); //zmiana tylko w przypadku wymienej tekstury } else glBindTexture(GL_TEXTURE_2D, TextureID); // również 0 glColor3fv(f4Diffuse); // McZapkie-240702: zamiast ub // glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,f4Diffuse); //to samo, // co glColor if (Global::fLuminance < fLight) { glMaterialfv(GL_FRONT, GL_EMISSION, f4Diffuse); // zeby swiecilo na kolorowo glDrawArrays(eType, iVboPtr, iNumVerts); // narysuj naraz wszystkie trójkąty z VBO glMaterialfv(GL_FRONT, GL_EMISSION, emm2); } else glDrawArrays(eType, iVboPtr, iNumVerts); // narysuj naraz wszystkie trójkąty z VBO } } else if (eType == TP_FREESPOTLIGHT) { // wersja VBO matrix4x4 mat; // macierz opisuje układ renderowania względem kamery glGetDoublev(GL_MODELVIEW_MATRIX, mat.getArray()); // kąt między kierunkiem światła a współrzędnymi kamery vector3 gdzie = mat * vector3(0, 0, 0); // pozycja punktu świecącego względem kamery fCosViewAngle = DotProduct(Normalize(mat * vector3(0, 0, 1) - gdzie), Normalize(gdzie)); if (fCosViewAngle > fCosFalloffAngle) // kąt większy niż maksymalny stożek swiatła { double Distdimm = 1.0; if (fCosViewAngle < fCosHotspotAngle) // zmniejszona jasność między Hotspot a Falloff if (fCosFalloffAngle < fCosHotspotAngle) Distdimm = 1.0 - (fCosHotspotAngle - fCosViewAngle) / (fCosHotspotAngle - fCosFalloffAngle); /* TODO: poprawic to zeby dzialalo 2- Inverse (Applies inverse decay. The formula is luminance=R0/R, where R0 is the radial source of the light if no attenuation is used, or the Near End value of the light if Attenuation is used. R is the radial distance of the illuminated surface from R0.) 3- Inverse Square (Applies inverse-square decay. The formula for this is (R0/R)^2. This is actually the "real-world" decay of light, but you might find it too dim in the world of computer graphics.) .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::fLuminance < fLight) { // Ra: pewnie można by to zrobić // lepiej, bez powtarzania StartVBO() pRoot->EndVBO(); // 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_Anim < at_SecondsJump) b_Anim = at_None; // wyłączenie animacji dla kolejnego użycia submodelu if (Next) if (iAlpha & iFlags & 0x1F000000) Next->RenderVBO(); // dalsze rekurencyjnie }; // RaRender void TSubModel::RenderAlphaVBO() { // renderowanie przezroczystych przez VBO if (iVisible && (fSquareDist >= fSquareMinDist) && (fSquareDist < fSquareMaxDist)) { if (iFlags & 0xC000) { glPushMatrix(); // zapamiętanie matrycy if (fMatrix) glMultMatrixf(fMatrix->readArray()); if (b_aAnim) RaAnimation(b_aAnim); } glColor3fv(f4Diffuse); if (eType < TP_ROTATOR) { // renderowanie obiektów OpenGL if (iAlpha & iFlags & 0x2F) // rysuj gdy element przezroczysty { if (TextureID < 0) // && (ReplacableSkinId!=0)) { // zmienialne skory glBindTexture(GL_TEXTURE_2D, ReplacableSkinId[-TextureID]); // TexAlpha=iAlpha&1; //zmiana tylko w przypadku wymienej tekstury } else glBindTexture(GL_TEXTURE_2D, TextureID); // również 0 if (Global::fLuminance < fLight) { glMaterialfv(GL_FRONT, GL_EMISSION, f4Diffuse); // zeby swiecilo na kolorowo glDrawArrays(eType, iVboPtr, iNumVerts); // narysuj naraz wszystkie trójkąty z VBO glMaterialfv(GL_FRONT, GL_EMISSION, emm2); } else glDrawArrays(eType, iVboPtr, iNumVerts); // narysuj naraz wszystkie trójkąty z VBO } } else if (eType == TP_FREESPOTLIGHT) { // dorobić aureolę! } if (Child) if (iAlpha & iFlags & 0x002F0000) Child->RenderAlphaVBO(); if (iFlags & 0xC000) glPopMatrix(); } if (b_aAnim < at_SecondsJump) b_aAnim = at_None; // wyłączenie animacji dla kolejnego użycia submodelu if (Next) if (iAlpha & iFlags & 0x2F000000) Next->RenderAlphaVBO(); }; // RaRenderAlpha //--------------------------------------------------------------------------- void TSubModel::RaArrayFill(CVertNormTex *Vert) { // wypełnianie tablic VBO if (Child) Child->RaArrayFill(Vert); if ((eType < TP_ROTATOR) || (eType == TP_STARS)) for (int i = 0; i < iNumVerts; ++i) { Vert[iVboPtr + i].x = Vertices[i].Point.x; Vert[iVboPtr + i].y = Vertices[i].Point.y; Vert[iVboPtr + i].z = Vertices[i].Point.z; Vert[iVboPtr + i].nx = Vertices[i].Normal.x; Vert[iVboPtr + i].ny = Vertices[i].Normal.y; Vert[iVboPtr + i].nz = Vertices[i].Normal.z; Vert[iVboPtr + i].u = Vertices[i].tu; Vert[iVboPtr + i].v = Vertices[i].tv; } else if (eType == TP_FREESPOTLIGHT) Vert[iVboPtr].x = Vert[iVboPtr].y = Vert[iVboPtr].z = 0.0; if (Next) Next->RaArrayFill(Vert); }; void 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; i < info->iCurrent; ++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) std::string t( pTexture ); // trim extension if( t.substr( t.rfind( '.' ) ) == ".tga" ) { t.erase( t.rfind( '.' ) ); } else if( t.substr( t.rfind( '.' ) ) == ".dds" ) { t.erase( t.rfind( '.' ) ); } if (t != std::string(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.size() + 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 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 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); std::string name(pName); if( false == name.empty() ) { // jeśli dany submodel jest zgaszonym światłem, to // domyślnie go ukrywamy if( (name.size() >= 8) && (name.substr( 0, 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( (name.size() >= 3) && (name.substr( name.size() - 3, 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); std::string tex = pTexture; if (tex.find_last_of("/\\") == std::string::npos) tex.insert(0, Global::asCurrentTexturePath); TextureID = TTexturesManager::GetTextureID( szTexturePath, const_cast(Global::asCurrentTexturePath.c_str()), tex); // 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 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 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 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 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; i < iNumVerts; ++i) { y = m[0][1] * Vertices[i].Point.x + m[1][1] * Vertices[i].Point.y + m[2][1] * Vertices[i].Point.z + m[3][1]; if (my < y) my = y; } return my; }; //--------------------------------------------------------------------------- TModel3d::TModel3d() { // Materials=NULL; // MaterialsCount=0; Root = NULL; iFlags = 0; iSubModelsCount = 0; iModel = NULL; // tylko jak wczytany model binarny iNumVerts = 0; // nie ma jeszcze wierzchołków }; /* TModel3d::TModel3d(char *FileName) { // Root=NULL; // Materials=NULL; // MaterialsCount=0; Root=NULL; SubModelsCount=0; iFlags=0; LoadFromFile(FileName); }; */ TModel3d::~TModel3d() { // SafeDeleteArray(Materials); if (iFlags & 0x0200) { // wczytany z pliku tekstowego, submodele sprzątają // same SafeDelete(Root); // submodele się usuną rekurencyjnie } else { // wczytano z pliku binarnego (jest właścicielem tablic) m_pVNT = NULL; // nie usuwać tego, bo wskazuje na iModel Root = NULL; delete[] iModel; // usuwamy cały wczytany plik i to wystarczy } // później się jeszcze usuwa obiekt z którego dziedziczymy tabelę VBO }; TSubModel * TModel3d::AddToNamed(const char *Name, TSubModel *SubModel) { TSubModel *sm = Name ? GetFromName(Name) : NULL; AddTo(sm, SubModel); // szukanie nadrzędnego return sm; // zwracamy wskaźnik do nadrzędnego submodelu }; void TModel3d::AddTo(TSubModel *tmp, TSubModel *SubModel) { // jedyny poprawny sposób dodawania // submodeli, inaczej mogą zginąć // przy zapisie E3D if (tmp) { // jeśli znaleziony, podłączamy mu jako potomny tmp->ChildAdd(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 * 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* TModel3d::GetMaterialFromName(char *sName) { AnsiString tmp=AnsiString(sName).Trim(); for (int i=0; i 0) : false; // brak pliku albo problem z wczytaniem }; void TModel3d::LoadFromBinFile(std::string const &FileName, bool dynamic) { // wczytanie modelu z pliku binarnego WriteLog("Loading - binary model: " + 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; */ { std::ifstream file( FileName, std::ios::binary | std::ios::ate ); file.unsetf( std::ios::skipws ); size = file.tellg(); // ios::ate already positioned us at the end of the file iModel = new int[ size >> 2 ]; // ten wskaźnik musi być w modelu, aby zwolnić pamięć file.seekg( 0, std::ios::beg ); // rewind the caret afterwards file.read( reinterpret_cast(iModel), size ); } float4x4 *m = NULL; // transformy // zestaw kromek: while ((i << 2) < size) // w pliku może być kilka modeli { ch = iModel[i]; // nazwa kromki j = i + (iModel[i + 1] >> 2); // początek następnej kromki if( ch == MAKE_ID4('E','3','D','0')) // główna: 'E3D0',len,pod-kromki { // tylko tę kromkę znamy, może kiedyś jeszcze DOF się zrobi i += 2; while (i < j) { // przetwarzanie kromek wewnętrznych ch = iModel[ i ]; // nazwa kromki k = (iModel[i + 1] >> 2); // długość aktualnej kromki switch (ch) { case MAKE_ID4('M','D','L','0'): // zmienne modelu: 'E3D0',len,(informacje o modelu) break; case MAKE_ID4('V','N','T','0'): // wierzchołki: 'VNT0',len,(32 bajty na wierzchołek) iNumVerts = (k - 2) >> 3; m_nVertexCount = iNumVerts; m_pVNT = (CVertNormTex *)(iModel + i + 2); break; case MAKE_ID4('S','U','B','0'): // 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 MAKE_ID4('S','U','B','1'): // 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 < iSubModelsCount; ++ch) // trzeba przesunąć bliżej, bo 256 wystarczy MoveMemory(((char *)Root) + 256 * ch, ((char *)Root) + 320 * ch, 256); break; case MAKE_ID4('T','R','A','0'): // transformy: 'TRA0',len,(64 bajty na transform) m = (float4x4 *)(iModel + i + 2); // tabela transformów break; case MAKE_ID4('T','R','A','1'): // transformy: 'TRA1',len,(128 bajtów na transform) m = (float4x4 *)(iModel + i + 2); // tabela transformów for (ch = 0; ch < ((k - 2) >> 1); ++ch) *(((float *)m) + ch) = *(((double *)m) + ch); // przepisanie double do float break; case MAKE_ID4('I','D','X','1'): // indeksy 1B: 'IDX2',len,(po bajcie na numer wierzchołka) break; case MAKE_ID4('I','D','X','2'): // indeksy 2B: 'IDX2',len,(po 2 bajty na numer wierzchołka) break; case MAKE_ID4('I','D','X','4'): // indeksy 4B: 'IDX4',len,(po 4 bajty na numer wierzchołka) break; case MAKE_ID4('T','E','X','0'): // tekstury: 'TEX0',len,(łańcuchy zakończone zerem - pliki // tekstur) Textures.Init((char *)(iModel + i)); //łącznie z nagłówkiem break; case MAKE_ID4('T','I','X','0'): // indeks nazw tekstur Textures.InitIndex((int *)(iModel + i)); //łącznie z nagłówkiem break; case MAKE_ID4('N','A','M','0'): // nazwy: 'NAM0',len,(łańcuchy zakończone zerem - nazwy // submodeli) Names.Init((char *)(iModel + i)); //łącznie z nagłówkiem break; case MAKE_ID4('N','I','X','0'): // 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; i < iSubModelsCount; ++i) { // aktualizacja wskaźników w submodelach Root[i].BinInit(Root, m, (float8 *)m_pVNT, &Textures, &Names, dynamic); if (Root[i].ChildGet()) Root[i].ChildGet()->Parent = 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 TModel3d::LoadFromTextFile(std::string const &FileName, bool dynamic) { // wczytanie submodelu z pliku tekstowego WriteLog("Loading - text model: " + 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(); 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.getTokens(); parser >> 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 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 (false == asBinary.empty()) // 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 TModel3d::SaveToBinFile(char const *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; i < iSubModelsCount; ++i) { tex += info[i].iTextureLen; nam += info[i].iNameLen; } if (tex) tex += 9; // 8 na nagłówek i jeden ciąg pusty (tylko znacznik końca) if (nam) nam += 8; len = 8 + sub + tra + vnt + tex + ((-tex) & 3) + nam + ((-nam) & 3); TSubModel *roboczy = new TSubModel(); // bufor używany do zapisywania // AnsiString *asN=&roboczy->asName,*asT=&roboczy->asTexture; // roboczy->FirstInit(); //żeby delete nie usuwało czego nie powinno /* TFileStream *fs = new TFileStream(AnsiString(FileName), fmCreate); */ { std::ofstream file( FileName, std::ios::binary ); file.unsetf( std::ios::skipws ); file.write( "E3D0", 4 ); // kromka główna file.write( reinterpret_cast( &len ), 4 ); file.write( "SUB0", 4 ); // dane submodeli file.write( reinterpret_cast( &sub ), 4 ); for( i = 0; i < iSubModelsCount; ++i ) { roboczy->InfoSet( info + i ); file.write( reinterpret_cast( roboczy ), sizeof( TSubModel ) ); // zapis jednego submodelu } if( tra ) { // zapis transformów file.write( "TRA0", 4 ); // transformy file.write( reinterpret_cast( &tra ), 4 ); for( i = 0; i < iSubModelsCount; ++i ) if( info[ i ].iTransform >= 0 ) file.write( reinterpret_cast( info[ i ].pSubModel->GetMatrix() ), 16 * 4 ); } { // zapis wierzchołków MakeArray( iNumVerts ); // tworzenie tablic dla VBO Root->RaArrayFill( m_pVNT ); // wypełnianie tablicy file.write( "VNT0", 4 ); // wierzchołki file.write( reinterpret_cast( &vnt ), 4 ); file.write( reinterpret_cast( m_pVNT ), 32 * iNumVerts ); } if( tex ) // może być jeden submodel ze zmienną teksturą i nazwy nie będzie { // zapis nazw tekstur file.write( "TEX0", 4 ); // nazwy tekstur i = ( tex + 3 ) & ~3; // zaokrąglenie w górę file.write( reinterpret_cast( &i ), 4 ); file.write( reinterpret_cast( &zero ), 1 ); // ciąg o numerze zero nie jest używany, ma tylko znacznik końca for( i = 0; i < iSubModelsCount; ++i ) if( info[ i ].iTextureLen ) file.write( info[ i ].pSubModel->pTexture, info[ i ].iTextureLen ); if( ( -tex ) & 3 ) file.write( reinterpret_cast( &zero ), ( ( -tex ) & 3 ) ); // wyrównanie do wielokrotności 4 bajtów } if( nam ) // może być jeden anonimowy submodel w modelu { // zapis nazw submodeli file.write( "NAM0", 4 ); // nazwy submodeli i = ( nam + 3 ) & ~3; // zaokrąglenie w górę file.write( reinterpret_cast( &i ), 4 ); for( i = 0; i < iSubModelsCount; ++i ) if( info[ i ].iNameLen ) file.write( info[ i ].pSubModel->pName, info[ i ].iNameLen ); if( ( -nam ) & 3 ) file.write( reinterpret_cast( &zero ), ( ( -nam ) & 3 ) ); // wyrównanie do wielokrotności 4 bajtów } } // file autocloses on getting out of scope // 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 TModel3d::BreakHierarhy() { Error("Not implemented yet :("); }; /* void 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 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 TModel3d::RenderAlpha(double fSquareDistance, GLuint *ReplacableSkinId, int iAlpha) { if (iAlpha & iFlags & 0x2F2F002F) { TSubModel::fSquareDist = fSquareDistance; // zmienna globalna! Root->ReplacableSet(ReplacableSkinId, iAlpha); Root->RenderAlphaDL(); } }; /* void 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 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 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 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 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 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 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 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 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 * TModel3d::TerrainSquare(int n) { // pobieranie wskaźnika do submodelu (n) int i = 0; TSubModel *r = Root; while (i < n) { r = r->NextGet(); ++i; } r->UnFlagNext(); // blokowanie wyświetlania po Next głównej listy return r; }; void 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(); };