From 5d7b3fb036b4ddb240dbe511a87c9fe01bd011f3 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Mon, 26 Jun 2017 16:57:25 +0200 Subject: [PATCH] build 170626. individual self-illumination levels for interior sections, automatic merging of suitable terrain geometry during load from text files, unused resource release by geometry bank manager, support for ui backgrounds with width:height ratio other than 4:3, comment parsing optimization, minor bug fixes --- DynObj.cpp | 89 ++++++++++ DynObj.h | 28 +-- Ground.cpp | 390 ++++++++++++++++++++++------------------- Ground.h | 45 ++--- Model3d.cpp | 54 +++--- Model3d.h | 83 ++++----- PyInt.cpp | 2 +- Texture.cpp | 8 +- Texture.h | 14 +- Track.cpp | 2 - Train.cpp | 9 +- openglgeometrybank.cpp | 76 ++++++-- openglgeometrybank.h | 39 ++++- parser.cpp | 17 +- parser.h | 2 +- renderer.cpp | 65 ++++--- uilayer.cpp | 22 ++- version.h | 2 +- 18 files changed, 608 insertions(+), 339 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 1442639c..0e6742f1 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -527,6 +527,39 @@ void TDynamicObject::UpdateLeverEnum(TAnim *pAnim) pAnim->smAnimated->SetRotate(float3(1, 0, 0), pAnim->fParam[*pAnim->iIntBase]); }; +// sets light levels for registered interior sections +void +TDynamicObject::toggle_lights() { + + if( true == SectionLightsActive ) { + // switch all lights off + for( auto §ionlight : SectionLightLevels ) { + sectionlight.level = 0.0f; + } + SectionLightsActive = false; + } + else { + std::string compartmentname; + // set lights with probability depending on the compartment type. TODO: expose this in .mmd file + for( auto §ionlight : SectionLightLevels ) { + + compartmentname = sectionlight.compartment->pName; + if( ( compartmentname.find( "corridor" ) != std::string::npos ) + || ( compartmentname.find( "korytarz" ) != std::string::npos ) ) { + // corridors are lit 100% of time + sectionlight.level = 0.75f; + } + else if( + ( compartmentname.find( "compartment" ) != std::string::npos ) + || ( compartmentname.find( "przedzial" ) != std::string::npos ) ) { + // compartments are lit with 75% probability + sectionlight.level = ( Random() < 0.75 ? 0.75f : 0.05f ); + } + } + SectionLightsActive = true; + } +} + // ABu 29.01.05 przeklejone z render i renderalpha: ********************* void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) { // ABu290105: pozbierane i uporzadkowane powtarzajace @@ -946,6 +979,13 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) } // else btHeadSignals23.TurnOff(); } + // interior light levels + for( auto const §ion : SectionLightLevels ) { + section.compartment->SetLightLevel( section.level, true ); + if( section.load != nullptr ) { + section.load->SetLightLevel( section.level, true ); + } + } } // ABu 29.01.05 koniec przeklejenia ************************************* @@ -1949,6 +1989,41 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" btMechanik1.Init("mechanik1", mdLowPolyInt, false); btMechanik2.Init("mechanik2", mdLowPolyInt, false); TurnOff(); // resetowanie zmiennych submodeli + + if( mdLowPolyInt != nullptr ) { + // check the low poly interior for potential compartments of interest, ie ones which can be individually lit + // TODO: definition of relevant compartments in the .mmd file + TSubModel *submodel { nullptr }; + if( ( submodel = mdLowPolyInt->GetFromName( "cab1" ) ) != nullptr ) { SectionLightLevels.emplace_back( submodel, nullptr, 0.0f ); } + if( ( submodel = mdLowPolyInt->GetFromName( "cab2" ) ) != nullptr ) { SectionLightLevels.emplace_back( submodel, nullptr, 0.0f ); } + if( ( submodel = mdLowPolyInt->GetFromName( "cab0" ) ) != nullptr ) { SectionLightLevels.emplace_back( submodel, nullptr, 0.0f ); } + // passenger car compartments + std::vector nameprefixes = { "corridor", "korytarz", "compartment", "przedzial" }; + int compartmentindex; + std::string compartmentname; + for( auto const &nameprefix : nameprefixes ) { + compartmentindex = 0; + do { + compartmentname = + nameprefix + ( + compartmentindex < 10 ? + "0" + std::to_string( compartmentindex ) : + std::to_string( compartmentindex ) ); + submodel = mdLowPolyInt->GetFromName( compartmentname.c_str() ); + if( submodel != nullptr ) { + // if specified compartment was found we check also for potential matching section in the currently assigned load + // NOTE: if the load gets changed this will invalidate stored pointers. TODO: rebuild the table on load change + SectionLightLevels.emplace_back( + submodel, + ( mdLoad != nullptr ? + mdLoad->GetFromName( compartmentname.c_str() ): + nullptr ), + 0.0f ); + } + ++compartmentindex; + } while( submodel != nullptr ); + } + } // wyszukiwanie zderzakow if (mdModel) // jeśli ma w czym szukać for (int i = 0; i < 2; i++) @@ -3416,6 +3491,20 @@ bool TDynamicObject::Update(double dt, double dt1) dDoorMoveR = 0; } + // compartment lights +/* + if( ( ctOwner != nullptr ? + ctOwner->Controlling()->Battery != SectionLightsActive : + MoverParameters->Battery != SectionLightsActive ) ) { + // if the vehicle has a controller, we base the light state on state of the controller otherwise we check the vehicle itself +*/ + // the version above won't work as the vehicles don't turn batteries off :| + if( ( ctOwner != nullptr ? + ctOwner->Controlling()->Battery != SectionLightsActive : + SectionLightsActive == true ) ) { // without controller lights are off. NOTE: this likely mess up the EMU + toggle_lights(); + } + // ABu-160303 sledzenie toru przed obiektem: ******************************* // Z obserwacji: v>0 -> Coupler 0; v<0 ->coupler1 (Ra: prędkość jest związana // z pojazdem) diff --git a/DynObj.h b/DynObj.h index 9b9cd735..3509696a 100644 --- a/DynObj.h +++ b/DynObj.h @@ -143,12 +143,8 @@ class TAnim struct material_data { int textures_alpha{ 0x30300030 }; // maska przezroczystości tekstur. default: tekstury wymienne nie mają przezroczystości - texture_handle replacable_skins[ 5 ]; // McZapkie:zmienialne nadwozie + texture_handle replacable_skins[ 5 ] = { NULL, NULL, NULL, NULL, NULL }; // McZapkie:zmienialne nadwozie int multi_textures{ 0 }; //<0 tekstury wskazane wpisem, >0 tekstury z przecinkami, =0 jedna - - material_data() { - ::SecureZeroMemory( replacable_skins, sizeof( replacable_skins ) ); - } }; class TDynamicObject { // klasa pojazdu @@ -169,8 +165,7 @@ private: // położenie pojazdu w świecie oraz parametry ruchu float fAxleDist; // rozstaw wózków albo osi do liczenia proporcji zacienienia vector3 modelRot; // obrot pudła względem świata - do przeanalizowania, czy potrzebne!!! // bool bCameraNear; //blisko kamer są potrzebne dodatkowe obliczenia szczegółów - TDynamicObject * ABuFindNearestObject( TTrack *Track, TDynamicObject *MyPointer, - int &CouplNr ); + TDynamicObject * ABuFindNearestObject( TTrack *Track, TDynamicObject *MyPointer, int &CouplNr ); public: // parametry położenia pojazdu dostępne publicznie std::string asTrack; // nazwa toru początkowego; wywalić? @@ -191,8 +186,18 @@ public: // modele składowe pojazdu TModel3d *mdLoad; // model zmiennego ładunku TModel3d *mdKabina; // model kabiny dla użytkownika; McZapkie-030303: to z train.h TModel3d *mdLowPolyInt; // ABu 010305: wnetrze lowpoly - float3 InteriorLight{ 0.9f * 255.0f / 255.0f, 0.9f * 216.0f / 255.0f, 0.9f * 176.0f / 255.0f }; // tungsten light. TODO: allow definition of light type? - float InteriorLightLevel{ 0.0f }; // current level of interior lighting + float3 InteriorLight { 0.9f * 255.0f / 255.0f, 0.9f * 216.0f / 255.0f, 0.9f * 176.0f / 255.0f }; // tungsten light. TODO: allow definition of light type? + float InteriorLightLevel { 0.0f }; // current level of interior lighting + struct section_light { + TSubModel *compartment; + TSubModel *load; + float level; + section_light( TSubModel *Compartment, TSubModel *Load, float const Level ) : + compartment(Compartment), load(Load), level(Level) + {} + }; + std::vector SectionLightLevels; // table of light levels for specific compartments of associated 3d model + bool SectionLightsActive { false }; // flag indicating whether section lights were set. float fShade; // zacienienie: 0:normalnie, -1:w ciemności, +1:dodatkowe światło (brak koloru?) private: // zmienne i metody do animacji submodeli; Ra: sprzatam animacje w pojeździe @@ -200,7 +205,9 @@ public: // modele składowe pojazdu public: inline - material_data const *Material() const { return &m_materialdata; } + material_data const + *Material() const { + return &m_materialdata; } // tymczasowo udostępnione do wyszukiwania drutu int iAnimType[ ANIM_TYPES ]; // 0-osie,1-drzwi,2-obracane,3-zderzaki,4-wózki,5-pantografy,6-tłoki private: @@ -226,6 +233,7 @@ public: // modele składowe pojazdu void UpdateLeverFloat(TAnim *pAnim); // animacja gałki zależna od float void UpdateLeverInt(TAnim *pAnim); // animacja gałki zależna od int (wartość) void UpdateLeverEnum(TAnim *pAnim); // animacja gałki zależna od int (lista kątów) + void toggle_lights(); // switch light levels for registered interior sections private: // Ra: ciąg dalszy animacji, dopiero do ogarnięcia // ABuWozki 060504 vector3 bogieRot[2]; // Obroty wozkow w/m korpusu diff --git a/Ground.cpp b/Ground.cpp index 76666103..4f37e50b 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -67,7 +67,6 @@ std::string LogComment; TGroundNode::TGroundNode() { // nowy obiekt terenu - pusty iType = GL_POINTS; - Vertices = NULL; nNext = nNext2 = NULL; iCount = 0; // wierzchołków w trójkącie // iNumPts=0; //punktów w linii @@ -115,9 +114,6 @@ TGroundNode::~TGroundNode() break; case TP_TERRAIN: { // pierwsze nNode zawiera model E3D, reszta to trójkąty - for (int i = 1; i < iCount; ++i) - nNode->Vertices = - NULL; // zerowanie wskaźników w kolejnych elementach, bo nie są do usuwania delete[] nNode; // usunięcie tablicy i pierwszego elementu } case TP_SUBMODEL: // dla formalności, nie wymaga usuwania @@ -125,33 +121,31 @@ TGroundNode::~TGroundNode() case GL_LINES: case GL_LINE_STRIP: case GL_LINE_LOOP: - SafeDeleteArray(Points); - break; case GL_TRIANGLE_STRIP: case GL_TRIANGLE_FAN: case GL_TRIANGLES: - SafeDeleteArray(Vertices); + SafeDelete( Piece ); break; } } - +/* void TGroundNode::Init(int n) { // utworzenie tablicy wierzchołków bVisible = false; iNumVerts = n; Vertices = new TGroundVertex[iNumVerts]; } - -TGroundNode::TGroundNode( TGroundNodeType t, int n ) : +*/ +TGroundNode::TGroundNode( TGroundNodeType t ) : TGroundNode() { // utworzenie obiektu - iNumVerts = n; - if( iNumVerts ) { - Vertices = new TGroundVertex[ iNumVerts ]; - } iType = t; switch (iType) { // zależnie od typu + case GL_TRIANGLES: { + Piece = new piece_node; + break; + } case TP_TRACK: { pTrack = new TTrack( this ); break; @@ -171,36 +165,36 @@ void TGroundNode::InitNormals() switch (iType) { case GL_TRIANGLE_STRIP: - v1 = Vertices[0].position - Vertices[1].position; - v2 = Vertices[1].position - Vertices[2].position; + v1 = Piece->vertices[0].position - Piece->vertices[1].position; + v2 = Piece->vertices[1].position - Piece->vertices[2].position; n1 = glm::normalize(glm::cross(v1, v2)); - if (Vertices[0].normal == glm::vec3()) - Vertices[0].normal = n1; - v3 = Vertices[2].position - Vertices[3].position; + if (Piece->vertices[0].normal == glm::vec3()) + Piece->vertices[0].normal = n1; + v3 = Piece->vertices[2].position - Piece->vertices[3].position; n2 = glm::normalize(glm::cross(v3, v2)); - if (Vertices[1].normal == glm::vec3()) - Vertices[1].normal = (n1 + n2) * 0.5f; + if (Piece->vertices[1].normal == glm::vec3()) + Piece->vertices[1].normal = (n1 + n2) * 0.5f; for ( i = 2; i < iNumVerts - 2; i += 2) { - v4 = Vertices[i - 1].position - Vertices[i].position; - v5 = Vertices[i].position - Vertices[i + 1].position; + v4 = Piece->vertices[i - 1].position - Piece->vertices[i].position; + v5 = Piece->vertices[i].position - Piece->vertices[i + 1].position; n3 = glm::normalize(glm::cross(v3, v4)); n4 = glm::normalize(glm::cross(v5, v4)); - if (Vertices[i].normal == glm::vec3()) - Vertices[i].normal = (n1 + n2 + n3) / 3.0f; - if (Vertices[i + 1].normal == glm::vec3()) - Vertices[i + 1].normal = (n2 + n3 + n4) / 3.0f; + if (Piece->vertices[i].normal == glm::vec3()) + Piece->vertices[i].normal = (n1 + n2 + n3) / 3.0f; + if (Piece->vertices[i + 1].normal == glm::vec3()) + Piece->vertices[i + 1].normal = (n2 + n3 + n4) / 3.0f; n1 = n3; n2 = n4; v3 = v5; } - if (Vertices[i].normal == glm::vec3()) - Vertices[i].normal = (n1 + n2) / 2.0f; + if (Piece->vertices[i].normal == glm::vec3()) + Piece->vertices[i].normal = (n1 + n2) / 2.0f; if (i + 1 < iNumVerts) { - if (Vertices[i + 1].normal == glm::vec3()) - Vertices[i + 1].normal = n2; + if (Piece->vertices[i + 1].normal == glm::vec3()) + Piece->vertices[i + 1].normal = n2; } else WriteLog("odd number of vertices, normals may be wrong!"); @@ -212,21 +206,21 @@ void TGroundNode::InitNormals() case GL_TRIANGLES: for (i = 0; i < iNumVerts; i += 3) { - v1 = Vertices[i + 0].position - Vertices[i + 1].position; - v2 = Vertices[i + 1].position - Vertices[i + 2].position; + v1 = Piece->vertices[i + 0].position - Piece->vertices[i + 1].position; + v2 = Piece->vertices[i + 1].position - Piece->vertices[i + 2].position; n1 = glm::normalize(glm::cross(v1, v2)); - if (Vertices[i + 0].normal == glm::vec3()) - Vertices[i + 0].normal = (n1); - if (Vertices[i + 1].normal == glm::vec3()) - Vertices[i + 1].normal = (n1); - if (Vertices[i + 2].normal == glm::vec3()) - Vertices[i + 2].normal = (n1); + if( Piece->vertices[i + 0].normal == glm::vec3() ) + Piece->vertices[i + 0].normal = (n1); + if( Piece->vertices[i + 1].normal == glm::vec3() ) + Piece->vertices[i + 1].normal = (n1); + if( Piece->vertices[i + 2].normal == glm::vec3() ) + Piece->vertices[i + 2].normal = (n1); t1 = glm::vec2( - std::floor( Vertices[ i + 0 ].texture.s ), - std::floor( Vertices[ i + 0 ].texture.t ) ); - Vertices[ i + 1 ].texture -= t1; - Vertices[ i + 2 ].texture -= t1; - Vertices[ i + 0 ].texture -= t1; + std::floor( Piece->vertices[ i + 0 ].texture.s ), + std::floor( Piece->vertices[ i + 0 ].texture.t ) ); + Piece->vertices[ i + 1 ].texture -= t1; + Piece->vertices[ i + 2 ].texture -= t1; + Piece->vertices[ i + 0 ].texture -= t1; } break; } @@ -349,8 +343,10 @@ void TSubRect::NodeAdd(TGroundNode *Node) } #endif case TP_TRACTIONPOWERSOURCE: // a te w ogóle pomijamy +/* // case TP_ISOLATED: //lista torów w obwodzie izolowanym - na razie ignorowana break; +*/ case TP_DYNAMIC: return; // tych nie dopisujemy wcale } @@ -380,22 +376,14 @@ void TSubRect::Sort() { TTrack * TSubRect::FindTrack(vector3 *Point, int &iConnection, TTrack *Exclude) { // szukanie toru, którego koniec jest najbliższy (*Point) - for (int i = 0; i < iTracks; ++i) - if (tTracks[i] != Exclude) // można użyć tabelę torów, bo jest mniejsza + for( int i = 0; i < iTracks; ++i ) { + if( tTracks[ i ] != Exclude ) // można użyć tabelę torów, bo jest mniejsza { - iConnection = tTracks[i]->TestPoint(Point); - if (iConnection >= 0) - return tTracks[i]; // szukanie TGroundNode nie jest potrzebne + iConnection = tTracks[ i ]->TestPoint( Point ); + if( iConnection >= 0 ) + return tTracks[ i ]; // szukanie TGroundNode nie jest potrzebne } - /* - TGroundNode *Current; - for (Current=nRootNode;Current;Current=Current->Next) - if ((Current->iType==TP_TRACK)&&(Current->pTrack!=Exclude)) //można użyć tabelę torów - { - iConnection=Current->pTrack->TestPoint(Point); - if (iConnection>=0) return Current; - } - */ + } return NULL; }; @@ -449,30 +437,20 @@ void TSubRect::LoadNodes() { auto *node { nRootNode }; while( node != nullptr ) { switch (node->iType) { - case GL_TRIANGLES: { - vertex_array vertices; - for( int idx = 0; idx < node->iNumVerts; ++idx ) { - vertices.emplace_back( - node->Vertices[ idx ].position - node->m_rootposition, - node->Vertices[ idx ].normal, - node->Vertices[ idx ].texture ); - } - node->m_geometry = GfxRenderer.Insert( vertices, m_geometrybank, GL_TRIANGLES ); - SafeDeleteArray( node->Vertices ); - node->iNumVerts = 0; - break; - } + case GL_TRIANGLES: case GL_LINES: { vertex_array vertices; - for( int idx = 0; idx < node->iNumPts; ++idx ) { + for( auto const &vertex : node->Piece->vertices ) { vertices.emplace_back( - node->Points[ idx ] - node->m_rootposition, - glm::vec3(), - glm::vec2() ); + vertex.position - node->m_rootposition, + vertex.normal, + vertex.texture ); } - node->m_geometry = GfxRenderer.Insert( vertices, m_geometrybank, GL_LINES ); - SafeDeleteArray( node->Vertices ); - node->iNumPts = 0; + node->Piece->geometry = GfxRenderer.Insert( vertices, m_geometrybank, node->iType ); + node->Piece->vertices.swap( std::vector() ); // hipster shrink_to_fit + // TODO: get rid of the vertex counters, they're obsolete at this point + if( node->iType == GL_LINES ) { node->iNumVerts = 0; } + else { node->iNumPts = 0; } break; } case TP_TRACK: @@ -529,6 +507,59 @@ TGroundRect::Init() { } }; +// dodanie obiektu do sektora na etapie rozdzielania na sektory +void +TGroundRect::NodeAdd( TGroundNode *Node ) { + + // override visibility ranges, to ensure the content is drawn from far enough + Node->fSquareRadius = 50000.0 * 50000.0; + Node->fSquareMinRadius = 0.0; + + // if the cell already has a node with matching material settings, we can just add the new geometry to it + if( ( Node->iType == GL_TRIANGLES ) ) { + // cell node only receives opaque geometry, so we can skip transparency test + auto matchingnode { nRenderRect }; + while( ( matchingnode != nullptr ) + && ( false == mergeable( *Node, *matchingnode ) ) ) { + // search will get us either a matching node, or a nullptr + matchingnode = matchingnode->nNext3; + } + if( matchingnode != nullptr ) { + // a valid match, so dump the content into it + // updating centre points isn't strictly necessary since render is based off the cell's middle, but, eh + matchingnode->pCenter = + interpolate( + matchingnode->pCenter, Node->pCenter, + static_cast( Node->iNumVerts ) / ( Node->iNumVerts + matchingnode->iNumVerts ) ); + matchingnode->iNumVerts += Node->iNumVerts; + matchingnode->Piece->vertices.resize( matchingnode->iNumVerts, TGroundVertex() ); + matchingnode->Piece->vertices.insert( + std::end( matchingnode->Piece->vertices ), + std::begin( Node->Piece->vertices ), std::end( Node->Piece->vertices ) ); + // clear content of the node we're copying. a minor memory saving at best, but still a saving + Node->Piece->vertices.swap( std::vector() ); + Node->iNumVerts = 0; + // since we've put the data in existing node we can skip adding the new one... + return; + // ...for others, they'll go through the regular procedure, along with other non-mergeable types + } + } + + return TSubRect::NodeAdd( Node ); +} + +// compares two provided nodes, returns true if their content can be merged +bool +TGroundRect::mergeable( TGroundNode const &Left, TGroundNode const &Right ) { + // since view ranges and transparency type for all nodes put through this method are guaranteed to be equal, + // we can skip their tests and only do the material check. + // TODO, TBD: material as dedicated type, and refactor this method into a simple equality test + return ( ( Left.TextureID == Right.TextureID ) + && ( Left.Ambient == Right.Ambient ) + && ( Left.Diffuse == Right.Diffuse ) + && ( Left.Specular == Right.Specular ) ); +} + //--------------------------------------------------------------------------- BYTE TempConnectionType[ 200 ]; // Ra: sprzêgi w sk³adzie; ujemne, gdy odwrotnie @@ -594,19 +625,19 @@ void TGround::Free() nRootDynamic = NULL; } -TGroundNode * TGround::DynamicFindAny(std::string asNameToFind) +TGroundNode * TGround::DynamicFindAny(std::string const &Name) { // wyszukanie pojazdu o podanej nazwie, szukanie po wszystkich (użyć drzewa!) for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext) - if ((Current->asName == asNameToFind)) + if ((Current->asName == Name)) return Current; return NULL; }; -TGroundNode * TGround::DynamicFind(std::string asNameToFind) +TGroundNode * TGround::DynamicFind(std::string const &Name) { // wyszukanie pojazdu z obsadą o podanej nazwie (użyć drzewa!) for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext) if (Current->DynamicObject->Mechanik) - if ((Current->asName == asNameToFind)) + if ((Current->asName == Name)) return Current; return NULL; }; @@ -685,12 +716,12 @@ void TGround::RaTriangleDivider(TGroundNode *node) double x1 = x0 + 1400.0; double z0 = 1000.0 * std::floor(0.001 * node->pCenter.z) - 200.0; double z1 = z0 + 1400.0; - if ((node->Vertices[0].position.x >= x0) && (node->Vertices[0].position.x <= x1) && - (node->Vertices[0].position.z >= z0) && (node->Vertices[0].position.z <= z1) && - (node->Vertices[1].position.x >= x0) && (node->Vertices[1].position.x <= x1) && - (node->Vertices[1].position.z >= z0) && (node->Vertices[1].position.z <= z1) && - (node->Vertices[2].position.x >= x0) && (node->Vertices[2].position.x <= x1) && - (node->Vertices[2].position.z >= z0) && (node->Vertices[2].position.z <= z1)) + if ((node->Piece->vertices[0].position.x >= x0) && (node->Piece->vertices[0].position.x <= x1) && + (node->Piece->vertices[0].position.z >= z0) && (node->Piece->vertices[0].position.z <= z1) && + (node->Piece->vertices[1].position.x >= x0) && (node->Piece->vertices[1].position.x <= x1) && + (node->Piece->vertices[1].position.z >= z0) && (node->Piece->vertices[1].position.z <= z1) && + (node->Piece->vertices[2].position.x >= x0) && (node->Piece->vertices[2].position.x <= x1) && + (node->Piece->vertices[2].position.z >= z0) && (node->Piece->vertices[2].position.z <= z1)) return; // trójkąt wystający mniej niż 200m z kw. kilometrowego jest do przyjęcia // Ra: przerobić na dzielenie na 2 trójkąty, podział w przecięciu z siatką kilometrową // Ra: i z rekurencją będzie dzielić trzy trójkąty, jeśli będzie taka potrzeba @@ -700,47 +731,46 @@ void TGround::RaTriangleDivider(TGroundNode *node) x1 -= 200.0; // przestawienie na siatkę z0 += 200.0; z1 -= 200.0; - mul = (node->Vertices[0].position.x - x0) * (node->Vertices[1].position.x - x0); // AB na wschodzie + mul = (node->Piece->vertices[0].position.x - x0) * (node->Piece->vertices[1].position.x - x0); // AB na wschodzie if (mul < min) min = mul, divide = 0; - mul = (node->Vertices[1].position.x - x0) * (node->Vertices[2].position.x - x0); // BC na wschodzie + mul = (node->Piece->vertices[1].position.x - x0) * (node->Piece->vertices[2].position.x - x0); // BC na wschodzie if (mul < min) min = mul, divide = 1; - mul = (node->Vertices[2].position.x - x0) * (node->Vertices[0].position.x - x0); // CA na wschodzie + mul = (node->Piece->vertices[2].position.x - x0) * (node->Piece->vertices[0].position.x - x0); // CA na wschodzie if (mul < min) min = mul, divide = 2; - mul = (node->Vertices[0].position.x - x1) * (node->Vertices[1].position.x - x1); // AB na zachodzie + mul = (node->Piece->vertices[0].position.x - x1) * (node->Piece->vertices[1].position.x - x1); // AB na zachodzie if (mul < min) min = mul, divide = 8; - mul = (node->Vertices[1].position.x - x1) * (node->Vertices[2].position.x - x1); // BC na zachodzie + mul = (node->Piece->vertices[1].position.x - x1) * (node->Piece->vertices[2].position.x - x1); // BC na zachodzie if (mul < min) min = mul, divide = 9; - mul = (node->Vertices[2].position.x - x1) * (node->Vertices[0].position.x - x1); // CA na zachodzie + mul = (node->Piece->vertices[2].position.x - x1) * (node->Piece->vertices[0].position.x - x1); // CA na zachodzie if (mul < min) min = mul, divide = 10; - mul = (node->Vertices[0].position.z - z0) * (node->Vertices[1].position.z - z0); // AB na południu + mul = (node->Piece->vertices[0].position.z - z0) * (node->Piece->vertices[1].position.z - z0); // AB na południu if (mul < min) min = mul, divide = 4; - mul = (node->Vertices[1].position.z - z0) * (node->Vertices[2].position.z - z0); // BC na południu + mul = (node->Piece->vertices[1].position.z - z0) * (node->Piece->vertices[2].position.z - z0); // BC na południu if (mul < min) min = mul, divide = 5; - mul = (node->Vertices[2].position.z - z0) * (node->Vertices[0].position.z - z0); // CA na południu + mul = (node->Piece->vertices[2].position.z - z0) * (node->Piece->vertices[0].position.z - z0); // CA na południu if (mul < min) min = mul, divide = 6; - mul = (node->Vertices[0].position.z - z1) * (node->Vertices[1].position.z - z1); // AB na północy + mul = (node->Piece->vertices[0].position.z - z1) * (node->Piece->vertices[1].position.z - z1); // AB na północy if (mul < min) min = mul, divide = 12; - mul = (node->Vertices[1].position.z - z1) * (node->Vertices[2].position.z - z1); // BC na północy + mul = (node->Piece->vertices[1].position.z - z1) * (node->Piece->vertices[2].position.z - z1); // BC na północy if (mul < min) min = mul, divide = 13; - mul = (node->Vertices[2].position.z - z1) * (node->Vertices[0].position.z - z1); // CA na północy + mul = (node->Piece->vertices[2].position.z - z1) * (node->Piece->vertices[0].position.z - z1); // CA na północy if (mul < min) divide = 14; // tworzymy jeden dodatkowy trójkąt, dzieląc jeden bok na przecięciu siatki kilometrowej TGroundNode *ntri; // wskaźnik na nowy trójkąt - ntri = new TGroundNode(); // a ten jest nowy - ntri->iType = GL_TRIANGLES; // kopiowanie parametrów, przydałby się konstruktor kopiujący - ntri->Init(3); + ntri = new TGroundNode(GL_TRIANGLES); // a ten jest nowy + // kopiowanie parametrów, przydałby się konstruktor kopiujący ntri->TextureID = node->TextureID; ntri->iFlags = node->iFlags; ntri->Ambient = node->Ambient; @@ -753,42 +783,44 @@ void TGround::RaTriangleDivider(TGroundNode *node) ntri->nNext = nRootOfType[GL_TRIANGLES]; nRootOfType[GL_TRIANGLES] = ntri; // dopisanie z przodu do listy ++iNumNodes; + ntri->iNumVerts = 3; + ntri->Piece->vertices.resize( 3 ); switch (divide & 3) { // podzielenie jednego z boków, powstaje wierzchołek D case 0: // podział AB (0-1) -> ADC i DBC - ntri->Vertices[2] = node->Vertices[2]; // wierzchołek C jest wspólny - ntri->Vertices[1] = node->Vertices[1]; // wierzchołek B przechodzi do nowego + ntri->Piece->vertices[2] = node->Piece->vertices[2]; // wierzchołek C jest wspólny + ntri->Piece->vertices[1] = node->Piece->vertices[1]; // wierzchołek B przechodzi do nowego // node->Vertices[1].HalfSet(node->Vertices[0],node->Vertices[1]); //na razie D tak if (divide & 4) - node->Vertices[1].SetByZ(node->Vertices[0], node->Vertices[1], (divide & 8) ? z1 : z0); + node->Piece->vertices[1].SetByZ(node->Piece->vertices[0], node->Piece->vertices[1], (divide & 8) ? z1 : z0); else - node->Vertices[1].SetByX(node->Vertices[0], node->Vertices[1], (divide & 8) ? x1 : x0); - ntri->Vertices[0] = node->Vertices[1]; // wierzchołek D jest wspólny + node->Piece->vertices[1].SetByX(node->Piece->vertices[0], node->Piece->vertices[1], (divide & 8) ? x1 : x0); + ntri->Piece->vertices[0] = node->Piece->vertices[1]; // wierzchołek D jest wspólny break; case 1: // podział BC (1-2) -> ABD i ADC - ntri->Vertices[0] = node->Vertices[0]; // wierzchołek A jest wspólny - ntri->Vertices[2] = node->Vertices[2]; // wierzchołek C przechodzi do nowego + ntri->Piece->vertices[0] = node->Piece->vertices[0]; // wierzchołek A jest wspólny + ntri->Piece->vertices[2] = node->Piece->vertices[2]; // wierzchołek C przechodzi do nowego // node->Vertices[2].HalfSet(node->Vertices[1],node->Vertices[2]); //na razie D tak if (divide & 4) - node->Vertices[2].SetByZ(node->Vertices[1], node->Vertices[2], (divide & 8) ? z1 : z0); + node->Piece->vertices[2].SetByZ(node->Piece->vertices[1], node->Piece->vertices[2], (divide & 8) ? z1 : z0); else - node->Vertices[2].SetByX(node->Vertices[1], node->Vertices[2], (divide & 8) ? x1 : x0); - ntri->Vertices[1] = node->Vertices[2]; // wierzchołek D jest wspólny + node->Piece->vertices[2].SetByX(node->Piece->vertices[1], node->Piece->vertices[2], (divide & 8) ? x1 : x0); + ntri->Piece->vertices[1] = node->Piece->vertices[2]; // wierzchołek D jest wspólny break; case 2: // podział CA (2-0) -> ABD i DBC - ntri->Vertices[1] = node->Vertices[1]; // wierzchołek B jest wspólny - ntri->Vertices[2] = node->Vertices[2]; // wierzchołek C przechodzi do nowego + ntri->Piece->vertices[1] = node->Piece->vertices[1]; // wierzchołek B jest wspólny + ntri->Piece->vertices[2] = node->Piece->vertices[2]; // wierzchołek C przechodzi do nowego // node->Vertices[2].HalfSet(node->Vertices[2],node->Vertices[0]); //na razie D tak if (divide & 4) - node->Vertices[2].SetByZ(node->Vertices[2], node->Vertices[0], (divide & 8) ? z1 : z0); + node->Piece->vertices[2].SetByZ(node->Piece->vertices[2], node->Piece->vertices[0], (divide & 8) ? z1 : z0); else - node->Vertices[2].SetByX(node->Vertices[2], node->Vertices[0], (divide & 8) ? x1 : x0); - ntri->Vertices[0] = node->Vertices[2]; // wierzchołek D jest wspólny + node->Piece->vertices[2].SetByX(node->Piece->vertices[2], node->Piece->vertices[0], (divide & 8) ? x1 : x0); + ntri->Piece->vertices[0] = node->Piece->vertices[2]; // wierzchołek D jest wspólny break; } // przeliczenie środków ciężkości obu - node->pCenter = ( node->Vertices[ 0 ].position + node->Vertices[ 1 ].position + node->Vertices[ 2 ].position ) / 3.0; - ntri->pCenter = ( ntri->Vertices[ 0 ].position + ntri->Vertices[ 1 ].position + ntri->Vertices[ 2 ].position ) / 3.0; + node->pCenter = ( node->Piece->vertices[ 0 ].position + node->Piece->vertices[ 1 ].position + node->Piece->vertices[ 2 ].position ) / 3.0; + ntri->pCenter = ( ntri->Piece->vertices[ 0 ].position + ntri->Piece->vertices[ 1 ].position + ntri->Piece->vertices[ 2 ].position ) / 3.0; RaTriangleDivider(node); // rekurencja, bo nawet na TD raz nie wystarczy RaTriangleDivider(ntri); }; @@ -1218,7 +1250,6 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) tmp->nNode[i].iFlags = 0x10; // nieprzezroczyste; nie usuwany tmp->nNode[i].bVisible = true; tmp->nNode[i].pCenter = tmp->pCenter; // nie przesuwamy w inne miejsce - // tmp->nNode[i].asName= } } else if (!tmp->asName.empty()) // jest pusta gdy "none" @@ -1291,18 +1322,6 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) && ( true == GfxRenderer.Texture( tmp->TextureID ).has_alpha ) ) ? 0x20 : 0x10 ); - - if( (tmp->iType == GL_TRIANGLES) - && (tmp->iFlags & 0x10) - && (Global::pTerrainCompact->TerrainLoaded()) ) { - // jeśli jest tekstura nieprzezroczysta, a teren załadowany, to pomijamy trójkąty - do { - // pomijanie trójkątów - parser->getTokens(); - *parser >> token; - } while (token.compare("endtri") != 0); - } - else { TGroundVertex vertex, vertex1, vertex2; std::size_t vertexcount { 0 }; @@ -1346,9 +1365,17 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) if( vertexcount == 0 ) { vertex1 = vertex; } else if( vertexcount == 1 ) { vertex2 = vertex; } else if( vertexcount >= 2 ) { - importedvertices.emplace_back( vertex1 ); - importedvertices.emplace_back( vertex2 ); + // swap order every other triangle, to maintain consistent winding + if( vertexcount % 2 == 0 ) { + importedvertices.emplace_back( vertex1 ); + importedvertices.emplace_back( vertex2 ); + } + else { + importedvertices.emplace_back( vertex2 ); + importedvertices.emplace_back( vertex1 ); + } importedvertices.emplace_back( vertex ); + vertex1 = vertex2; vertex2 = vertex; } @@ -1363,25 +1390,27 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } while (token.compare("endtri") != 0); tmp->iType = GL_TRIANGLES; + tmp->Piece = new piece_node(); + tmp->iNumVerts = importedvertices.size(); - auto const nv = importedvertices.size(); - tmp->Init(nv); // utworzenie tablicy wierzchołków + if( tmp->iNumVerts > 0 ) { - for( std::size_t i = 0; i < nv; ++i ) { - tmp->pCenter += importedvertices[ i ].position; + tmp->Piece->vertices.swap( importedvertices ); + + for( auto const &vertex : tmp->Piece->vertices ) { + tmp->pCenter += vertex.position; + } + tmp->pCenter /= tmp->iNumVerts; + + r = 0; + for( auto const &vertex : tmp->Piece->vertices ) { + tf = SquareMagnitude( vertex.position - tmp->pCenter ); + if( tf > r ) + r = tf; + } + tmp->fSquareRadius += r; + RaTriangleDivider( tmp ); // Ra: dzielenie trójkątów jest teraz całkiem wydajne } - tmp->pCenter /= (nv > 0 ? nv : 1); - - r = 0; - for (std::size_t i = 0; i < nv; ++i) - { - tmp->Vertices[i] = importedvertices[i]; - tf = SquareMagnitude( tmp->Vertices[ i ].position - tmp->pCenter ); - if (tf > r) - r = tf; - } - tmp->fSquareRadius += r; - RaTriangleDivider(tmp); // Ra: dzielenie trójkątów jest teraz całkiem wydajne } // koniec wczytywania trójkątów break; case GL_LINES: @@ -1394,6 +1423,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) >> tmp->Diffuse.b >> tmp->fLineThickness; tmp->Diffuse /= 255.0f; + tmp->fLineThickness = std::min( 30.0, tmp->fLineThickness ); // 30 pix equals rougly width of a signal pole viewed from ~1m away TGroundVertex vertex, vertex0, vertex1; std::size_t vertexcount{ 0 }; @@ -1455,15 +1485,25 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) importedvertices.pop_back(); } tmp->iType = GL_LINES; + tmp->Piece = new piece_node(); + tmp->iNumPts = importedvertices.size(); - auto const nv = importedvertices.size(); - tmp->Points = new glm::dvec3[ nv ]; - tmp->iNumPts = nv; - for( std::size_t i = 0; i < nv; ++i ) { - tmp->Points[ i ] = importedvertices[ i ].position; - tmp->pCenter += importedvertices[ i ].position; + if( false == importedvertices.empty() ) { + + tmp->Piece->vertices.swap( importedvertices ); + + glm::dvec3 minpoint( std::numeric_limits::max(), 0.0, std::numeric_limits::max() ); + glm::dvec3 maxpoint( std::numeric_limits::lowest(), 0.0, std::numeric_limits::lowest() ); + for( auto const &vertex : tmp->Piece->vertices ) { + tmp->pCenter += vertex.position; + minpoint.x = std::min( minpoint.x, vertex.position.x ); + minpoint.z = std::min( minpoint.z, vertex.position.z ); + maxpoint.x = std::max( maxpoint.x, vertex.position.x ); + maxpoint.z = std::max( maxpoint.z, vertex.position.z ); + } + tmp->pCenter /= tmp->iNumPts; + tmp->m_radius = static_cast( glm::distance( maxpoint, minpoint ) * 0.5 ); } - tmp->pCenter /= ( nv > 0 ? nv : 1 ); break; } } @@ -1559,11 +1599,6 @@ void TGround::FirstInit() } else { // dodajemy do kwadratu kilometrowego - // override visibility ranges, to ensure the content is drawn from far enough - // TODO: subclass NodeAdd() for the ground cell and specialize it, instead of this - // TODO: given their view ranges are the same, combine the geometry in single per-material nodes - Current->fSquareRadius = 50000.0 * 50000.0; - Current->fSquareMinRadius = 0.0; GetRect( Current->pCenter.x, Current->pCenter.z )->NodeAdd( Current ); } } @@ -1632,7 +1667,7 @@ bool TGround::Init(std::string File) switch( LastNode->iType ) { // validate the new node case GL_TRIANGLES: { - if( LastNode->Vertices == nullptr ) { + if( true == LastNode->Piece->vertices.empty() ) { SafeDelete( LastNode ); } break; @@ -1692,7 +1727,7 @@ bool TGround::Init(std::string File) } else { - Error("Scene parse error near " + token); + ErrorLog("Scene parsing error in file \"" + parser.Name() + "\", unexpected token \"" + token + "\""); // break; } } @@ -1722,20 +1757,21 @@ bool TGround::Init(std::string File) fTrainSetVel, 0, NULL); } } - if (LastNode) // ostatni wczytany obiekt - if (LastNode->iType == - TP_DYNAMIC) // o ile jest pojazdem (na ogół jest, ale kto wie...) - if (iTrainSetWehicleNumber ? !TempConnectionType[iTrainSetWehicleNumber - 1] : - false) // jeśli ostatni pojazd ma sprzęg 0 - LastNode->DynamicObject->RaLightsSet(-1, 2 + 32 + 64); // to założymy mu - // końcówki blaszane - // (jak AI się - // odpali, to sobie - // poprawi) + if( LastNode ) { + // ostatni wczytany obiekt + if( LastNode->iType == TP_DYNAMIC ) { + // o ile jest pojazdem (na ogół jest, ale kto wie...) + if( ( iTrainSetWehicleNumber > 0 ) + && ( TempConnectionType[ iTrainSetWehicleNumber - 1 ] == 0 ) ) { + // jeśli ostatni pojazd ma sprzęg 0 to założymy mu końcówki blaszane + // (jak AI się odpali, to sobie poprawi) + LastNode->DynamicObject->RaLightsSet( -1, 2 + 32 + 64 ); + } + } + } bTrainSet = false; fTrainSetVel = 0; - // iTrainSetConnection=0; - nTrainSetNode = nTrainSetDriver = NULL; + nTrainSetNode = nTrainSetDriver = nullptr; iTrainSetWehicleNumber = 0; } else if (str == "event") diff --git a/Ground.h b/Ground.h index 4edf8976..04e66754 100644 --- a/Ground.h +++ b/Ground.h @@ -93,15 +93,15 @@ struct TGroundVertex texture = interpolate( v1.texture, v2.texture, static_cast( factor ) ); } }; -/* + // ground node holding single, unique piece of 3d geometry. TBD, TODO: unify this with basic 3d model node -struct mesh_node { +struct piece_node { std::vector vertices; - geometry_handle geometry; // geometry prepared for drawing + geometry_handle geometry { 0,0 }; // geometry prepared for drawing }; -*/ -class TGroundNode /*: public Resource*/ -{ // obiekt scenerii + +// obiekt scenerii +class TGroundNode { friend class opengl_renderer; @@ -117,9 +117,8 @@ public: TSubModel *smTerrain; // modele terenu (kwadratow kilometrowych) TAnimModel *Model; // model z animacjami TDynamicObject *DynamicObject; // pojazd - glm::dvec3 *Points; // punkty dla linii + piece_node *Piece; // non-instanced piece of geometry TTrack *pTrack; // trajektoria ruchu - TGroundVertex *Vertices; // wierzchołki dla trójkątów TMemCell *MemCell; // komórka pamięci TEventLauncher *EvLaunch; // wyzwalacz zdarzeń TTraction *hvTraction; // drut zasilający @@ -128,6 +127,7 @@ public: TGroundNode *nNode; // obiekt renderujący grupowo ma tu wskaźnik na listę obiektów }; Math3D::vector3 pCenter; // współrzędne środka do przydzielenia sektora + float m_radius { 0.0f }; // bounding radius of geometry stored in the node. TODO: reuse bounding_area struct for radius and center glm::dvec3 m_rootposition; // position of the ground (sub)rectangle holding the node, in the 3d world // visualization-related data // TODO: wrap these in a struct, when cleaning objects up @@ -139,9 +139,6 @@ public: int iNumPts; // dla linii int iCount; // dla terenu }; - // NOTE: geometry handle is duplicated in (anim)model(3d), as well as in track and traction type nodes - // TODO: clean this up when node types are refactored into an inheritance/composition scheme - geometry_handle m_geometry; // geometry of the submodel int iFlags; // tryb przezroczystości: 0x10-nieprz.,0x20-przezroczysty,0x30-mieszany texture_handle TextureID; // główna (jedna) tekstura obiektu glm::vec3 @@ -152,9 +149,11 @@ public: bool bVisible; TGroundNode(); - TGroundNode(TGroundNodeType t, int n = 0); + TGroundNode(TGroundNodeType t); ~TGroundNode(); +/* void Init(int n); +*/ void InitNormals(); /* void Release(); @@ -200,7 +199,7 @@ class TSubRect : /*public Resource,*/ public CMesh /* virtual void Release(); // zwalnianie VBO sektora */ - void NodeAdd(TGroundNode *Node); // dodanie obiektu do sektora na etapie rozdzielania na sektory + virtual void NodeAdd(TGroundNode *Node); // dodanie obiektu do sektora na etapie rozdzielania na sektory void Sort(); // optymalizacja obiektów w sektorze (sortowanie wg tekstur) TTrack * FindTrack(vector3 *Point, int &iConnection, TTrack *Exclude); TTraction * FindTraction(glm::dvec3 const &Point, int &iConnection, TTraction *Exclude); @@ -242,17 +241,19 @@ public: }; // pobranie wskaźnika do małego kwadratu, bez tworzenia jeśli nie ma TSubRect * FastGetSubRect(int iCol, int iRow) { - return (pSubRects ? pSubRects + iRow * iNumSubRects + iCol : NULL); - }; + return ( + pSubRects ? + pSubRects + iRow * iNumSubRects + iCol : + nullptr); }; + void NodeAdd( TGroundNode *Node ); // dodanie obiektu do sektora na etapie rozdzielania na sektory + // compares two provided nodes, returns true if their content can be merged + bool mergeable( TGroundNode const &Left, TGroundNode const &Right ); // optymalizacja obiektów w sektorach void Optimize() { if( pSubRects ) { for( int i = iNumSubRects * iNumSubRects - 1; i >= 0; --i ) { // optymalizacja obiektów w sektorach - pSubRects[ i ].Sort(); - } - } - }; + pSubRects[ i ].Sort(); } } }; static int iFrameNumber; // numer kolejny wyświetlanej klatki TGroundNode *nTerrain { nullptr }; // model terenu z E3D - użyć nRootMesh? @@ -308,8 +309,8 @@ class TGround bool AddToQuery(TEvent *Event, TDynamicObject *Node); bool GetTraction(TDynamicObject *model); bool CheckQuery(); - TGroundNode * DynamicFindAny(std::string asNameToFind); - TGroundNode * DynamicFind(std::string asNameToFind); + TGroundNode * DynamicFindAny(std::string const &Name); + TGroundNode * DynamicFind(std::string const &Name); void DynamicList(bool all = false); TGroundNode * FindGroundNode(std::string asNameToFind, TGroundNodeType iNodeType); TGroundRect * GetRect(double x, double z) @@ -343,7 +344,9 @@ class TGround public: void WyslijEvent(const std::string &e, const std::string &d); +/* int iRendered; // ilość renderowanych sektorów, pobierana przy pokazywniu FPS +*/ void WyslijString(const std::string &t, int n); void WyslijWolny(const std::string &t); void WyslijNamiary(TGroundNode *t); diff --git a/Model3d.cpp b/Model3d.cpp index e7617e06..1974ba5c 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -78,6 +78,23 @@ void TSubModel::NameSet(std::string const &Name) pName = Name; }; +// sets light level (alpha component of illumination color) to specified value +void +TSubModel::SetLightLevel( float const Level, bool const Includechildren, bool const Includesiblings ) { + + f4Emision.a = Level; + if( true == Includesiblings ) { + auto sibling { this }; + while( ( sibling = sibling->Next ) != nullptr ) { + sibling->f4Emision.a = Level; + } + } + if( ( true == Includechildren ) + && ( Child != nullptr ) ) { + Child->SetLightLevel( Level, true, true ); // node's children include child's siblings and children + } +} + int TSubModel::SeekFaceNormal(std::vector const &Masks, int const Startface, unsigned int const Mask, glm::vec3 const &Position, vertex_array const &Vertices) { // szukanie punktu stycznego do (pt), zwraca numer wierzchołka, a nie trójkąta int facecount = iNumVerts / 3; // bo maska powierzchni jest jedna na trójkąt @@ -315,9 +332,9 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic } else { // jeśli tylko nazwa pliku, to dawać bieżącą ścieżkę do tekstur - TextureNameSet(texture.c_str()); + TextureNameSet(texture); if( texture.find_first_of( "/\\" ) == texture.npos ) { - texture.insert( 0, Global::asCurrentTexturePath.c_str() ); + texture.insert( 0, Global::asCurrentTexturePath ); } TextureID = GfxRenderer.GetTextureId( texture, szTexturePath ); // renderowanie w cyklu przezroczystych tylko jeśli: @@ -429,7 +446,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic --facecount; // o jeden trójkąt mniej iNumVerts -= 3; // czyli o 3 wierzchołki i -= 3; // wczytanie kolejnego w to miejsce - WriteLog("Degenerated triangle ignored in: \"" + pName + "\", verticle " + std::to_string(i)); + WriteLog("Degenerated triangle ignored in: \"" + pName + "\", vertice " + 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ć @@ -487,6 +504,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic glm::vec3() ); // przepisanie do wierzchołka trójkąta } } + Vertices.resize( iNumVerts ); // in case we had some degenerate triangles along the way /* delete[] wsp; delete[] n; @@ -534,6 +552,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic else if( eType == TP_FREESPOTLIGHT ) { // single light points only have single data point, duh Vertices.emplace_back(); + iNumVerts = 1; } // Visible=true; //się potem wyłączy w razie potrzeby // iFlags|=0x0200; //wczytano z pliku tekstowego (jest właścicielem tablic) @@ -711,25 +730,20 @@ void TSubModel::InitialRotate(bool doit) { // jeśli jest jednostkowy transform, to przeliczamy // wierzchołki, a mnożenie podajemy dalej float swapcopy; -// if( false == Vertices.empty() ) { -/* - for( auto &vertex : Vertices ) { -*/ for( auto &vertex : Vertices ) { - vertex.position.x = -vertex.position.x; // zmiana znaku X - swapcopy = vertex.position.y; // zamiana Y i Z - vertex.position.y = vertex.position.z; - vertex.position.z = swapcopy; - // wektory normalne również trzeba przekształcić, bo się źle oświetlają - if( eType != TP_STARS ) { - // gwiazdki mają kolory zamiast normalnych, to // ich wtedy nie ruszamy - vertex.normal.x = -vertex.normal.x; // zmiana znaku X - swapcopy = vertex.normal.y; // zamiana Y i Z - vertex.normal.y = vertex.normal.z; - vertex.normal.z = swapcopy; - } + vertex.position.x = -vertex.position.x; // zmiana znaku X + swapcopy = vertex.position.y; // zamiana Y i Z + vertex.position.y = vertex.position.z; + vertex.position.z = swapcopy; + // wektory normalne również trzeba przekształcić, bo się źle oświetlają + if( eType != TP_STARS ) { + // gwiazdki mają kolory zamiast normalnych, to // ich wtedy nie ruszamy + vertex.normal.x = -vertex.normal.x; // zmiana znaku X + swapcopy = vertex.normal.y; // zamiana Y i Z + vertex.normal.y = vertex.normal.z; + vertex.normal.z = swapcopy; } - // } + } if (Child) Child->InitialRotate(doit); // potomne ewentualnie obrócimy } diff --git a/Model3d.h b/Model3d.h index fab31e2e..3c74b353 100644 --- a/Model3d.h +++ b/Model3d.h @@ -56,6 +56,7 @@ class TSubModel friend class opengl_renderer; friend class TModel3d; // temporary workaround. TODO: clean up class content/hierarchy + friend class TDynamicObject; // temporary etc private: int iNext{ NULL }; @@ -85,47 +86,41 @@ private: float4x4 *fMatrix = nullptr; // pojedyncza precyzja wystarcza int iMatrix; // w pliku binarnym jest numer matrycy }; - int iNumVerts{ -1 }; // ilość wierzchołków (1 dla FreeSpotLight) + int iNumVerts { -1 }; // ilość wierzchołków (1 dla FreeSpotLight) int tVboPtr; // początek na liście wierzchołków albo indeksów - int iTexture{ 0 }; // numer nazwy tekstury, -1 wymienna, 0 brak - float fVisible{ 0.0f }; // próg jasności światła do załączenia submodelu - float fLight{ -1.0f }; // próg jasności światła do zadziałania selfillum + int iTexture { 0 }; // numer nazwy tekstury, -1 wymienna, 0 brak + float fVisible { 0.0f }; // próg jasności światła do załączenia submodelu + float fLight { -1.0f }; // próg jasności światła do zadziałania selfillum glm::vec4 - f4Ambient{ 1.0f,1.0f,1.0f,1.0f }, - f4Diffuse{ 1.0f,1.0f,1.0f,1.0f }, - f4Specular{ 0.0f,0.0f,0.0f,1.0f }, - f4Emision{ 1.0f,1.0f,1.0f,1.0f }; - float fWireSize{ 0.0f }; // nie używane, ale wczytywane - float fSquareMaxDist{ 10000.0f * 10000.0f }; - float fSquareMinDist{ 0.0f }; + f4Ambient { 1.0f,1.0f,1.0f,1.0f }, + f4Diffuse { 1.0f,1.0f,1.0f,1.0f }, + f4Specular { 0.0f,0.0f,0.0f,1.0f }, + f4Emision { 1.0f,1.0f,1.0f,1.0f }; + float fWireSize { 0.0f }; // nie używane, ale wczytywane + float fSquareMaxDist { 10000.0f * 10000.0f }; + float fSquareMinDist { 0.0f }; // McZapkie-050702: parametry dla swiatla: - float fNearAttenStart{ 40.0f }; - float fNearAttenEnd{ 80.0f }; - bool bUseNearAtten{ false }; // te 3 zmienne okreslaja rysowanie aureoli wokol zrodla swiatla - int iFarAttenDecay{ 0 }; // ta zmienna okresla typ zaniku natezenia swiatla (0:brak, 1,2: potega 1/R) - float fFarDecayRadius{ 100.0f }; // normalizacja j.w. - float fCosFalloffAngle{ 0.5f }; // cosinus kąta stożka pod którym widać światło - float fCosHotspotAngle{ 0.3f }; // cosinus kąta stożka pod którym widać aureolę i zwiększone natężenie światła - float fCosViewAngle{ 0.0f }; // cos kata pod jakim sie teraz patrzy + float fNearAttenStart { 40.0f }; + float fNearAttenEnd { 80.0f }; + bool bUseNearAtten { false }; // te 3 zmienne okreslaja rysowanie aureoli wokol zrodla swiatla + int iFarAttenDecay { 0 }; // ta zmienna okresla typ zaniku natezenia swiatla (0:brak, 1,2: potega 1/R) + float fFarDecayRadius { 100.0f }; // normalizacja j.w. + float fCosFalloffAngle { 0.5f }; // cosinus kąta stożka pod którym widać światło + float fCosHotspotAngle { 0.3f }; // cosinus kąta stożka pod którym widać aureolę i zwiększone natężenie światła + float fCosViewAngle { 0.0f }; // cos kata pod jakim sie teraz patrzy - TSubModel *Next{ nullptr }; - TSubModel *Child{ nullptr }; -/* - intptr_t iVboPtr; -*/ - geometry_handle m_geometry{ NULL, NULL }; // geometry of the submodel - texture_handle TextureID{ NULL }; // numer tekstury, -1 wymienna, 0 brak - bool bWire{ false }; // nie używane, ale wczytywane -/* - GLuint uiDisplayList; // roboczy numer listy wyświetlania -*/ - float Opacity{ 1.0f }; - float f_Angle{ 0.0f }; - float3 v_RotateAxis{ 0.0f, 0.0f, 0.0f }; + TSubModel *Next { nullptr }; + TSubModel *Child { nullptr }; + geometry_handle m_geometry { NULL, NULL }; // geometry of the submodel + texture_handle TextureID { NULL }; // numer tekstury, -1 wymienna, 0 brak + bool bWire { false }; // nie używane, ale wczytywane + float Opacity { 1.0f }; + float f_Angle { 0.0f }; + float3 v_RotateAxis { 0.0f, 0.0f, 0.0f }; float3 v_Angles { 0.0f, 0.0f, 0.0f }; public: // chwilowo - float3 v_TransVector{ 0.0f, 0.0f, 0.0f }; + float3 v_TransVector { 0.0f, 0.0f, 0.0f }; /* basic_vertex *Vertices; // roboczy wskaźnik - wczytanie T3D do VBO */ @@ -192,18 +187,14 @@ public: int Flags() { return iFlags; }; void UnFlagNext() { iFlags &= 0x00FFFFFF; }; void ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ); - inline float3 Translation1Get() - { - return fMatrix ? *(fMatrix->TranslationGet()) + v_TransVector : v_TransVector; - } - inline float3 Translation2Get() - { - return *(fMatrix->TranslationGet()) + Child->Translation1Get(); - } - int GetTextureId() - { - return TextureID; - } + // sets light level (alpha component of illumination color) to specified value + void SetLightLevel( float const Level, bool const Includechildren = false, bool const Includesiblings = false ); + inline float3 Translation1Get() { + return fMatrix ? *(fMatrix->TranslationGet()) + v_TransVector : v_TransVector; } + inline float3 Translation2Get() { + return *(fMatrix->TranslationGet()) + Child->Translation1Get(); } + int GetTextureId() { + return TextureID; } void ParentMatrix(float4x4 *m); float MaxY( float4x4 const &m ); void AdjustDist(); diff --git a/PyInt.cpp b/PyInt.cpp index c55e371f..8a6a74b4 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -457,7 +457,7 @@ void TPythonScreens::init(cParser &parser, TModel3d *model, std::string const &n WriteLog( "Python Screen: submodel " + subModelName + " not found - Ignoring screen" ); return; // nie ma takiego sub modelu w danej kabinie pomijamy } - int textureId = GfxRenderer.Texture(subModel->GetTextureId()).id; + auto textureId = subModel->GetTextureId(); if (textureId <= 0) { WriteLog( "Python Screen: invalid texture id " + std::to_string(textureId) + " - Ignoring screen" ); diff --git a/Texture.cpp b/Texture.cpp index 9856a152..64bd8d8f 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -817,8 +817,8 @@ texture_manager::bind( texture_handle const Texture ) { ::glBindTexture( GL_TEXTURE_2D, texture(Texture).id ); m_activetexture = texture(Texture).id; #else - if( Texture( Id ).bind() == resource_state::good ) { - m_activetexture = Id; + if( texture( Texture ).bind() == resource_state::good ) { + m_activetexture = Texture; } else { // TODO: bind a special 'error' texture on failure @@ -839,7 +839,7 @@ texture_manager::delete_textures() { for( auto const &texture : m_textures ) { // usunięcie wszyskich tekstur (bez usuwania struktury) if( ( texture.id > 0 ) - && ( texture.id != -1 ) ) { + && ( texture.id != -1 ) ) { ::glDeleteTextures( 1, &texture.id ); } } @@ -876,7 +876,7 @@ texture_manager::info() const { + std::to_string( readytexturecount ) + " (" + to_string( readytexturesize / 1024.0f, 2 ) + " mb)" - + " in vram, "; + + " in vram, " #endif + std::to_string( totaltexturecount ) + " (" diff --git a/Texture.h b/Texture.h index 8ed49627..17da9bca 100644 --- a/Texture.h +++ b/Texture.h @@ -29,8 +29,18 @@ struct opengl_texture { // methods void load(); - resource_state bind(); - resource_state create(); + resource_state + bind(); + resource_state + create(); + inline + int + width() const { + return data_width; } + inline + int + height() const { + return data_height; } // members GLuint id{ (GLuint)-1 }; // associated GL resource bool has_alpha{ false }; // indicates the texture has alpha channel diff --git a/Track.cpp b/Track.cpp index 5386a839..59a7a44f 100644 --- a/Track.cpp +++ b/Track.cpp @@ -2156,7 +2156,6 @@ TTrack * TTrack::RaAnimate() if (SwitchExtension->RightSwitch) { // nowa wersja z SPKS, ale odwrotnie lewa/prawa - vertex_array vertices; if( TextureID1 ) { // left blade SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, SwitchExtension->fOffset2 ); @@ -2171,7 +2170,6 @@ TTrack * TTrack::RaAnimate() } } else { // lewa działa lepiej niż prawa - vertex_array vertices; if( TextureID1 ) { // right blade SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -SwitchExtension->fOffset2 ); diff --git a/Train.cpp b/Train.cpp index 5a572617..d37ddb84 100644 --- a/Train.cpp +++ b/Train.cpp @@ -5889,9 +5889,8 @@ bool TTrain::Update( double const Deltatime ) } case 2: { //światło wewnętrzne zapalone (255 216 176) - if( mvOccupied->ConverterFlag == - true ) // jasnosc dla zalaczonej przetwornicy - { + if( mvOccupied->ConverterFlag == true ) { + // jasnosc dla zalaczonej przetwornicy DynamicObject->InteriorLightLevel = 1.0f; } else { @@ -6441,6 +6440,10 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) { DynamicObject->mdKabina->Init(); // obrócenie modelu oraz optymalizacja, również zapisanie binarnego set_cab_controls(); + // HACK: for some reason simulation at the start is slow until a sound is played + // until we do a proper fix, try to play a 'silent' sound when cab is entered + // TBD: it could be instead a legit sound of door closing + play_sound( dsbSwitch, DSBVOLUME_MIN ); return true; } return (token == "none"); diff --git a/openglgeometrybank.cpp b/openglgeometrybank.cpp index 5c9fe8b0..759ffdeb 100644 --- a/openglgeometrybank.cpp +++ b/openglgeometrybank.cpp @@ -55,7 +55,7 @@ geometry_bank::create( vertex_array &Vertices, unsigned int const Type ) { m_chunks.emplace_back( Vertices, Type ); // NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication geometry_handle chunkhandle { 0, static_cast(m_chunks.size()) }; - // template method + // template method implementation create_( chunkhandle ); // all done return chunkhandle; @@ -81,7 +81,7 @@ geometry_bank::replace( vertex_array &Vertices, geometry_handle const &Geometry, chunk.vertices.resize( Offset + Vertices.size(), basic_vertex() ); chunk.vertices.insert( std::end( chunk.vertices ), std::begin( Vertices ), std::end( Vertices ) ); } - // template method + // template method implementation replace_( Geometry ); // all done return true; @@ -99,10 +99,17 @@ geometry_bank::append( vertex_array &Vertices, geometry_handle const &Geometry ) // draws geometry stored in specified chunk void geometry_bank::draw( geometry_handle const &Geometry, unsigned int const Streams ) { - // template method + // template method implementation draw_( Geometry, Streams ); } +// frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time +void +geometry_bank::release() { + // template method implementation + release_(); +} + vertex_array const & geometry_bank::vertices( geometry_handle const &Geometry ) const { @@ -209,6 +216,13 @@ opengl_vbogeometrybank::draw_( geometry_handle const &Geometry, unsigned int con */ } +// release () subclass details +void +opengl_vbogeometrybank::release_() { + + delete_buffer(); +} + void opengl_vbogeometrybank::bind_buffer() { @@ -225,6 +239,7 @@ opengl_vbogeometrybank::delete_buffer() { ::glDeleteBuffers( 1, &m_buffer ); if( m_activebuffer == m_buffer ) { m_activebuffer = NULL; + bind_streams( stream::none ); } m_buffer = NULL; m_buffercapacity = 0; @@ -314,6 +329,19 @@ opengl_dlgeometrybank::draw_( geometry_handle const &Geometry, unsigned int cons ::glCallList( chunkrecord.list ); } +// release () subclass details +void +opengl_dlgeometrybank::release_() { + + for( auto &chunkrecord : m_chunkrecords ) { + if( chunkrecord.list != 0 ) { + ::glDeleteLists( chunkrecord.list, 1 ); + chunkrecord.list = 0; + } + chunkrecord.streams = stream::none; + } +} + void opengl_dlgeometrybank::delete_list( geometry_handle const &Geometry ) { // NOTE: given it's our own internal method we trust it to be called with valid parameters @@ -327,12 +355,37 @@ opengl_dlgeometrybank::delete_list( geometry_handle const &Geometry ) { // geometry bank manager, holds collection of geometry banks +// performs a resource sweep +void +geometrybank_manager::update() { + + m_resourcetimestamp = std::chrono::steady_clock::now(); + // garbage collection sweep is limited to a number of records per call, to reduce impact on framerate + auto const sweeplastindex = + std::min( + m_resourcesweepindex + geometrybank_manager::unusedresourcesweepsize, + m_geometrybanks.size() ); + auto const blanktimestamp { std::chrono::steady_clock::time_point() }; + for( auto bankindex = m_resourcesweepindex; bankindex < sweeplastindex; ++bankindex ) { + if( ( m_geometrybanks[ bankindex ].second != blanktimestamp ) + && ( m_resourcetimestamp - m_geometrybanks[ bankindex ].second > geometrybank_manager::unusedresourcetimetolive ) ) { + + m_geometrybanks[ bankindex ].first->release(); + m_geometrybanks[ bankindex ].second = blanktimestamp; + } + } + m_resourcesweepindex = ( + m_resourcesweepindex + geometrybank_manager::unusedresourcesweepsize >= m_geometrybanks.size() ? + 0 : // if the next sweep chunk is beyond actual data, so start anew + m_resourcesweepindex + geometrybank_manager::unusedresourcesweepsize ); +} + // creates a new geometry bank. returns: handle to the bank or NULL geometrybank_handle geometrybank_manager::create_bank() { - if( true == Global::bUseVBO ) { m_geometrybanks.emplace_back( std::make_shared() ); } - else { m_geometrybanks.emplace_back( std::make_shared() ); } + if( true == Global::bUseVBO ) { m_geometrybanks.emplace_back( std::make_shared(), std::chrono::steady_clock::time_point() ); } + else { m_geometrybanks.emplace_back( std::make_shared(), std::chrono::steady_clock::time_point() ); } // NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication return geometrybank_handle( m_geometrybanks.size(), 0 ); } @@ -341,7 +394,7 @@ geometrybank_manager::create_bank() { geometry_handle geometrybank_manager::create_chunk( vertex_array &Vertices, geometrybank_handle const &Geometry, int const Type ) { - auto const newchunkhandle = bank( Geometry )->create( Vertices, Type ); + auto const newchunkhandle = bank( Geometry ).first->create( Vertices, Type ); if( newchunkhandle.chunk != 0 ) { return geometry_handle( Geometry.bank, newchunkhandle.chunk ); } else { return geometry_handle( 0, 0 ); } @@ -351,14 +404,14 @@ geometrybank_manager::create_chunk( vertex_array &Vertices, geometrybank_handle bool geometrybank_manager::replace( vertex_array &Vertices, geometry_handle const &Geometry, std::size_t const Offset ) { - return bank( Geometry )->replace( Vertices, Geometry, Offset ); + return bank( Geometry ).first->replace( Vertices, Geometry, Offset ); } // adds supplied vertex data at the end of specified chunk bool geometrybank_manager::append( vertex_array &Vertices, geometry_handle const &Geometry ) { - return bank( Geometry )->append( Vertices, Geometry ); + return bank( Geometry ).first->append( Vertices, Geometry ); } // draws geometry stored in specified chunk void @@ -366,12 +419,15 @@ geometrybank_manager::draw( geometry_handle const &Geometry, unsigned int const if( Geometry == NULL ) { return; } - return bank( Geometry )->draw( Geometry, Streams ); + auto &bankrecord = bank( Geometry ); + + bankrecord.second = m_resourcetimestamp; + bankrecord.first->draw( Geometry, Streams ); } // provides direct access to vertex data of specfied chunk vertex_array const & geometrybank_manager::vertices( geometry_handle const &Geometry ) const { - return bank( Geometry )->vertices( Geometry ); + return bank( Geometry ).first->vertices( Geometry ); } diff --git a/openglgeometrybank.h b/openglgeometrybank.h index 0b174566..d799650b 100644 --- a/openglgeometrybank.h +++ b/openglgeometrybank.h @@ -102,6 +102,9 @@ public: template void draw( Iterator_ First, Iterator_ Last, unsigned int const Streams = basic_streams ) { while( First != Last ) { draw( *First, Streams ); ++First; } } + // frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time + void + release(); // provides direct access to vertex data of specfied chunk vertex_array const & vertices( geometry_handle const &Geometry ) const; @@ -142,6 +145,8 @@ private: virtual void replace_( geometry_handle const &Geometry ) = 0; // draw() subclass details virtual void draw_( geometry_handle const &Geometry, unsigned int const Streams ) = 0; + // resource release subclass details + virtual void release_() = 0; }; // opengl vbo-based variant of the geometry bank @@ -149,6 +154,8 @@ private: class opengl_vbogeometrybank : public geometry_bank { public: +// constructors: + opengl_vbogeometrybank() = default; // destructor ~opengl_vbogeometrybank() { delete_buffer(); } @@ -179,6 +186,9 @@ private: // draw() subclass details void draw_( geometry_handle const &Geometry, unsigned int const Streams ); + // release () subclass details + void + release_(); void bind_buffer(); void @@ -200,7 +210,9 @@ private: class opengl_dlgeometrybank : public geometry_bank { public: -// methods: +// constructors: + opengl_dlgeometrybank() = default; +// destructor: ~opengl_dlgeometrybank() { for( auto &chunkrecord : m_chunkrecords ) { ::glDeleteLists( chunkrecord.list, 1 ); } } @@ -224,6 +236,9 @@ private: // draw() subclass details void draw_( geometry_handle const &Geometry, unsigned int const Streams ); + // release () subclass details + void + release_(); void delete_list( geometry_handle const &Geometry ); @@ -240,6 +255,8 @@ class geometrybank_manager { public: // methods: + // performs a resource sweep + void update(); // creates a new geometry bank. returns: handle to the bank or NULL geometrybank_handle create_bank(); @@ -267,23 +284,31 @@ public: private: // types: - typedef std::deque< std::shared_ptr > geometrybank_sequence; + typedef std::pair< + std::shared_ptr, + std::chrono::steady_clock::time_point > geometrybanktimepoint_pair; + + typedef std::deque< geometrybanktimepoint_pair > geometrybanktimepointpair_sequence; // members: - geometrybank_sequence m_geometrybanks; + std::chrono::nanoseconds const unusedresourcetimetolive { std::chrono::seconds { 60 } }; + geometrybanktimepointpair_sequence::size_type const unusedresourcesweepsize { 300 }; + geometrybanktimepointpair_sequence m_geometrybanks; + geometrybanktimepointpair_sequence::size_type m_resourcesweepindex { 0 }; + std::chrono::steady_clock::time_point m_resourcetimestamp { std::chrono::steady_clock::now() }; // methods inline bool valid( geometry_handle const &Geometry ) { - return ( ( Geometry.bank != 0 ) - && ( Geometry.bank <= m_geometrybanks.size() ) ); } + return ( ( Geometry.bank != 0 ) + && ( Geometry.bank <= m_geometrybanks.size() ) ); } inline - geometrybank_sequence::value_type & + geometrybanktimepointpair_sequence::value_type & bank( geometry_handle const Geometry ) { return m_geometrybanks[ Geometry.bank - 1 ]; } inline - geometrybank_sequence::value_type const & + geometrybanktimepointpair_sequence::value_type const & bank( geometry_handle const Geometry ) const { return m_geometrybanks[ Geometry.bank - 1 ]; } diff --git a/parser.cpp b/parser.cpp index 4b71b7cd..b6b27dfd 100644 --- a/parser.cpp +++ b/parser.cpp @@ -201,14 +201,19 @@ std::string cParser::readQuotes(char const Quote) { // read the stream until spe return token; } -std::string cParser::readComment( std::string const &Break ) { // pobieranie znaków aż do znalezienia znacznika końca - std::string token = ""; +void cParser::skipComment( std::string const &Endmark ) { // pobieranie znaków aż do znalezienia znacznika końca + std::string input = ""; + auto const endmarksize = Endmark.size(); while( mStream->peek() != EOF ) { // o ile nie koniec pliku - token += mStream->get(); // pobranie znaku - if( token.rfind( Break ) != std::string::npos ) // szukanie znacznika końca + input += mStream->get(); // pobranie znaku + if( input.find( Endmark ) != std::string::npos ) // szukanie znacznika końca break; + if( input.size() >= endmarksize ) { + // keep the read text short, to avoid pointless string re-allocations on longer comments + input = input.substr( 1 ); + } } - return token; + return; } bool cParser::findQuotes( std::string &String ) { @@ -228,7 +233,7 @@ bool cParser::trimComments(std::string &String) { if (String.rfind((*cmIt).first) != std::string::npos) { - readComment((*cmIt).second); + skipComment((*cmIt).second); String.resize(String.rfind((*cmIt).first)); return true; } diff --git a/parser.h b/parser.h index 1212fcc5..7582c0e1 100644 --- a/parser.h +++ b/parser.h @@ -92,7 +92,7 @@ class cParser //: public std::stringstream // methods: std::string readToken(bool ToLower = true, const char *Break = "\n\r\t ;"); std::string readQuotes( char const Quote = '\"' ); - std::string readComment( std::string const &Break = "\n\r\t ;" ); + void skipComment( std::string const &Endmark ); bool findQuotes( std::string &String ); bool trimComments( std::string &String ); std::size_t count(); diff --git a/renderer.cpp b/renderer.cpp index 27167c1e..bc380ecb 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -157,6 +157,7 @@ opengl_renderer::Render() { // frustum tests are performed in 'world space' but after we set up frustum // we no longer need camera translation, only rotation ::glMultMatrixd( glm::value_ptr( glm::dmat4( glm::dmat3( worldcamera )))); + Render( &World.Environment ); Render( &World.Ground ); @@ -538,19 +539,26 @@ opengl_renderer::Render( TGroundNode *Node ) { } case GL_LINES: { - if( ( Node->m_geometry == NULL ) + if( ( Node->Piece->geometry == NULL ) || ( Node->fLineThickness > 0.0 ) ) { return false; } // setup - // w zaleznosci od koloru swiatla + auto const distance = std::sqrt( distancesquared ); + auto const linealpha = + 10.0 * Node->fLineThickness + / std::max( + 0.5 * Node->m_radius + 1.0, + distance - ( 0.5 * Node->m_radius ) ); ::glColor4fv( glm::value_ptr( glm::vec4( - Node->Diffuse * glm::make_vec3( Global::DayLight.ambient ), - std::min( - 1.0, - 1000.0 * Node->fLineThickness / ( distancesquared + 1.0 ) ) ) ) ); + Node->Diffuse * glm::make_vec3( Global::DayLight.ambient ), // w zaleznosci od koloru swiatla + 1.0 ) ) ); // if the thickness is defined negative, lines are always drawn opaque + auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 32.0 ); + if( linewidth > 1.0 ) { + ::glLineWidth( static_cast( linewidth ) ); + } GfxRenderer.Bind( 0 ); @@ -559,16 +567,18 @@ opengl_renderer::Render( TGroundNode *Node ) { ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render - m_geometry.draw( Node->m_geometry ); + m_geometry.draw( Node->Piece->geometry ); // post-render cleanup ::glPopMatrix(); + if( linewidth > 1.0 ) { ::glLineWidth( 1.0f ); } + return true; } case GL_TRIANGLES: { - if( ( Node->m_geometry == NULL ) + if( ( Node->Piece->geometry == NULL ) || ( ( Node->iFlags & 0x10 ) == 0 ) ) { return false; } @@ -582,7 +592,7 @@ opengl_renderer::Render( TGroundNode *Node ) { ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render - m_geometry.draw( Node->m_geometry ); + m_geometry.draw( Node->Piece->geometry ); // post-render cleanup ::glPopMatrix(); @@ -750,7 +760,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { // ...luminance if( Global::fLuminance < Submodel->fLight ) { // zeby swiecilo na kolorowo - ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr(Submodel->f4Diffuse) ); + ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( Submodel->f4Diffuse * Submodel->f4Emision.a ) ); } // main draw call @@ -772,7 +782,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { if( Submodel->fCosViewAngle > Submodel->fCosFalloffAngle ) // kąt większy niż maksymalny stożek swiatła { - float lightlevel = 1.0f; + float lightlevel = 1.0f; // TODO, TBD: parameter to control light strength // view angle attenuation float const anglefactor = ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( 1.0f - Submodel->fCosFalloffAngle ); // distance attenuation. NOTE: since it's fixed pipeline with built-in gamma correction we're using linear attenuation @@ -946,7 +956,7 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { } float const linealpha = static_cast( std::min( - 1.2, + 1.25, 5000 * Node->hvTraction->WireThickness / ( distancesquared + 1.0 ) ) ); // zbyt grube nie są dobre ::glLineWidth( linealpha ); // McZapkie-261102: kolor zalezy od materialu i zasniedzenia @@ -982,17 +992,23 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { } case GL_LINES: { - if( ( Node->m_geometry == NULL ) + if( ( Node->Piece->geometry == NULL ) || ( Node->fLineThickness < 0.0 ) ) { return false; } // setup - auto const linewidth = clamp( 100.0 * Node->fLineThickness / ( distancesquared + 1.0 ), 1.0, 10.0 ); + auto const distance = std::sqrt( distancesquared ); + auto const linealpha = + 10.0 * Node->fLineThickness + / std::max( + 0.5 * Node->m_radius + 1.0, + distance - ( 0.5 * Node->m_radius ) ); ::glColor4fv( glm::value_ptr( glm::vec4( Node->Diffuse * glm::make_vec3( Global::DayLight.ambient ), // w zaleznosci od koloru swiatla - std::min( 1.0, linewidth ) ) ) ); + std::min( 1.0, linealpha ) ) ) ); + auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 32.0 ); if( linewidth > 1.0 ) { ::glLineWidth( static_cast(linewidth) ); } @@ -1004,20 +1020,18 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render - m_geometry.draw( Node->m_geometry ); + m_geometry.draw( Node->Piece->geometry ); // post-render cleanup - if( linewidth > 1.0 ) { - ::glLineWidth( 1.0f ); - } - ::glPopMatrix(); + if( linewidth > 1.0 ) { ::glLineWidth( 1.0f ); } + return true; } case GL_TRIANGLES: { - if( ( Node->m_geometry == NULL ) + if( ( Node->Piece->geometry == NULL ) || ( ( Node->iFlags & 0x20 ) == 0 ) ) { return false; } @@ -1031,7 +1045,7 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render - m_geometry.draw( Node->m_geometry ); + m_geometry.draw( Node->Piece->geometry ); // post-render cleanup ::glPopMatrix(); @@ -1188,7 +1202,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // ...luminance if( Global::fLuminance < Submodel->fLight ) { // zeby swiecilo na kolorowo - ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr(Submodel->f4Diffuse) ); + ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( Submodel->f4Diffuse * Submodel->f4Emision.a ) ); } // main draw call @@ -1311,7 +1325,7 @@ opengl_renderer::Update ( double const Deltatime ) { else if( framerate > 60.0 ) { targetsegments = 225; targetfactor = 1.5f; } else if( framerate > 30.0 ) { targetsegments = 90; targetfactor = Global::ScreenHeight / 768.0f; } else { targetsegments = 9; targetfactor = Global::ScreenHeight / 768.0f * 0.75f; } - +/* if( targetsegments > Global::iSegmentsRendered ) { Global::iSegmentsRendered = std::min( targetsegments, Global::iSegmentsRendered + 5 ); @@ -1320,6 +1334,7 @@ opengl_renderer::Update ( double const Deltatime ) { Global::iSegmentsRendered = std::max( targetsegments, Global::iSegmentsRendered - 5 ); } +*/ if( targetfactor > Global::fDistanceFactor ) { Global::fDistanceFactor = std::min( targetfactor, Global::fDistanceFactor + 0.05f ); @@ -1343,6 +1358,8 @@ opengl_renderer::Update ( double const Deltatime ) { } // TODO: add garbage collection and other less frequent works here + m_geometry.update(); + if( true == DebugModeFlag ) { m_debuginfo = m_textures.info(); } diff --git a/uilayer.cpp b/uilayer.cpp index dcad5b58..7fb5f212 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -164,12 +164,22 @@ void ui_layer::render_background() { if( m_background == 0 ) return; - // NOTE: we limit/expect the background to come with 4:3 ratio. // TODO, TBD: if we expose texture width or ratio from texture object, this limitation could be lifted - GfxRenderer.Bind( m_background ); - quad( float4( 0.0f, 0.0f, 1024.0f, 768.0f ), float4( 1.0f, 1.0f, 1.0f, 1.0f ) ); + auto const height { 768.0f }; + auto const &texture = GfxRenderer.Texture( m_background ); + float const width = ( + texture.width() == texture.height() ? + 1024.0f : // legacy mode, square texture displayed as 4:3 image + texture.width() / ( texture.height() / 768.0f ) ); + quad( + float4( + ( 1024.0f * 0.5f ) - ( width * 0.5f ), + ( 768.0f * 0.5f ) - ( height * 0.5f ), + ( 1024.0f * 0.5f ) - ( width * 0.5f ) + width, + ( 768.0f * 0.5f ) - ( height * 0.5f ) + height ), + float4( 1.0f, 1.0f, 1.0f, 1.0f ) ); } void @@ -200,7 +210,11 @@ ui_layer::quad( float4 const &Coordinates, float4 const &Color ) { Global::iWindowHeight / 768.0 : Global::iWindowHeight / 768.0 * screenratio / ( 4.0f / 3.0f ) ); float const height = 768.0f * heightratio; - +/* + float const heightratio = Global::iWindowHeight / 768.0f; + float const height = 768.0f * heightratio; + float const width = Global::iWindowWidth * heightratio; +*/ glColor4fv(&Color.x); glBegin( GL_TRIANGLE_STRIP ); diff --git a/version.h b/version.h index 252fa070..9031f529 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 617 +#define VERSION_MINOR 626 #define VERSION_REVISION 0