diff --git a/CMakeLists.txt b/CMakeLists.txt index 48fd634a..016bc89c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,7 @@ set(SOURCES "sky.cpp" "sun.cpp" "stars.cpp" +"lightarray.cpp" "skydome.cpp" "Sound.cpp" "Spring.cpp" diff --git a/DynObj.cpp b/DynObj.cpp index a3be1c23..cf31b015 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -3758,12 +3758,14 @@ void TDynamicObject::Render() // double ObjDist= SquareMagnitude(Global::pCameraPosition-pos); if (this == Global::pUserDynamic) { // specjalne ustawienie, aby nie trzęsło +#ifdef EU07_USE_OLD_LIGHTING_MODEL if (Global::bSmudge) { // jak jest widoczna smuga, to pojazd renderować po // wyrenderowaniu smugi glPopMatrix(); // a to trzeba zebrać przed wyjściem return; } +#endif // if (Global::pWorld->) //tu trzeba by ustawić animacje na modelu // zewnętrznym glLoadIdentity(); // zacząć od macierzy jedynkowej diff --git a/DynObj.h b/DynObj.h index 2c675cc0..b5ff3e04 100644 --- a/DynObj.h +++ b/DynObj.h @@ -420,7 +420,7 @@ public: // modele składowe pojazdu void Render(); void RenderAlpha(); void RenderSounds(); - inline vector3 GetPosition() + inline vector3 GetPosition() const { return vPosition; }; @@ -436,7 +436,7 @@ public: // modele składowe pojazdu { return iAxleFirst ? Axle1.pPosition : Axle0.pPosition; }; - inline vector3 VectorFront() + inline vector3 VectorFront() const { return vFront; }; @@ -456,7 +456,7 @@ public: // modele składowe pojazdu { return MoverParameters->Vel; }; - inline double GetLength() + inline double GetLength() const { return MoverParameters->Dim.L; }; diff --git a/Float3d.h b/Float3d.h index 43ee6651..d6011163 100644 --- a/Float3d.h +++ b/Float3d.h @@ -79,6 +79,11 @@ inline float DotProduct( float3 const &v1, float3 const &v2 ) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } +inline float3 Interpolate( float3 const &First, float3 const &Second, float const Factor ) { + + return ( First * ( 1.0f - Factor ) ) + ( Second * Factor ); +} + class float4 { // kwaternion obrotu public: diff --git a/Globals.cpp b/Globals.cpp index 5a3df2e4..341028b9 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -104,6 +104,7 @@ GLfloat Global::diffuseLight[] = {0.85f, 0.85f, 0.80f, 1.0f}; GLfloat Global::specularLight[] = {0.95f, 0.94f, 0.90f, 1.0f}; #else opengl_light Global::DayLight; +int Global::DynamicLightCount{ 3 }; #endif GLfloat Global::whiteLight[] = {1.00f, 1.00f, 1.00f, 1.0f}; GLfloat Global::noLight[] = {0.00f, 0.00f, 0.00f, 1.0f}; @@ -575,7 +576,14 @@ void Global::ConfigParse(cParser &Parser) std::tm *localtime = std::localtime(&timenow); Global::fMoveLight = localtime->tm_yday + 1; // numer bieżącego dnia w roku } - // TODO: calculate lights single time here for static setup. or get rid of static setup + } + else if( token == "dynamiclights" ) { + // number of dynamic lights in the scene + Parser.getTokens( 1, false ); + Parser >> Global::DynamicLightCount; + // clamp the light number + Global::DynamicLightCount = std::min( 7, Global::DynamicLightCount ); // max 8 lights per opengl specs, and one used for sun + Global::DynamicLightCount = std::max( 1, Global::DynamicLightCount ); // at least one light for controlled vehicle } else if (token == "smoothtraction") { diff --git a/Globals.h b/Globals.h index 8e2f4109..3fe324cc 100644 --- a/Globals.h +++ b/Globals.h @@ -232,7 +232,9 @@ class Global static GLfloat diffuseLight[]; static GLfloat specularLight[]; #else + // TODO: put these things in the renderer static opengl_light DayLight; + static int DynamicLightCount; #endif static GLfloat whiteLight[]; static GLfloat noLight[]; diff --git a/Ground.cpp b/Ground.cpp index 45afb8b2..b1f5227b 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -1834,29 +1834,20 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) case TP_DYNAMIC: tmp->DynamicObject = new TDynamicObject(); // tmp->DynamicObject->Load(Parser); - parser->getTokens(); - *parser >> token; - str1 = token; // katalog - // McZapkie: doszedl parametr ze zmienialna skora - parser->getTokens(); - *parser >> token; - Skin = token; // tekstura wymienna - parser->getTokens(); - *parser >> token; - str3 = token; // McZapkie-131102: model w MMD + parser->getTokens(3); + *parser + >> str1 // katalog + >> Skin // tekstura wymienna + >> str3; // McZapkie-131102: model w MMD if (bTrainSet) { // jeśli pojazd jest umieszczony w składzie str = asTrainSetTrack; - parser->getTokens(); - *parser >> tf1; // Ra: -1 oznacza odwrotne wstawienie, normalnie w składzie 0 - parser->getTokens(); - *parser >> token; - DriverType = token; // McZapkie:010303 - w przyszlosci rozne - // konfiguracje mechanik/pomocnik itp + parser->getTokens(3); + *parser + >> tf1 // Ra: -1 oznacza odwrotne wstawienie, normalnie w składzie 0 + >> DriverType // McZapkie:010303 - w przyszlosci rozne konfiguracje mechanik/pomocnik itp + >> str4; tf3 = fTrainSetVel; // prędkość - parser->getTokens(); - *parser >> token; - str4 = token; int2 = str4.find("."); // yB: wykorzystuje tutaj zmienna, ktora potem bedzie ladunkiem if (int2 != string::npos) // yB: jesli znalazl kropke, to ja przetwarza jako parametry { @@ -1885,17 +1876,12 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) fTrainSetDist = 0; // zerowanie dodatkowego przesunięcia asTrainName = ""; // puste oznacza jazdę pojedynczego bez rozkładu, "none" jest dla // składu (trainset) - parser->getTokens(); - *parser >> token; - str = token; // track - parser->getTokens(); - *parser >> tf1; // Ra: -1 oznacza odwrotne wstawienie - parser->getTokens(); - *parser >> token; - DriverType = token; // McZapkie:010303: obsada - parser->getTokens(); - *parser >> - tf3; // prędkość, niektórzy wpisują tu "3" jako sprzęg, żeby nie było tabliczki + parser->getTokens(4); + *parser + >> str // track + >> tf1 // Ra: -1 oznacza odwrotne wstawienie + >> DriverType // McZapkie:010303: obsada + >> tf3; // prędkość, niektórzy wpisują tu "3" jako sprzęg, żeby nie było tabliczki iTrainSetWehicleNumber = 0; TempConnectionType[iTrainSetWehicleNumber] = 3; // likwidacja tabliczki na końcu? } @@ -1904,8 +1890,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) if (int2 > 0) { // jeżeli ładunku jest więcej niż 0, to rozpoznajemy jego typ parser->getTokens(); - *parser >> token; - str2 = token; // LoadType + *parser >> str2; // LoadType if (str2 == "enddynamic") // idiotoodporność: ładunek bez podanego typu { str2 = ""; @@ -1979,6 +1964,16 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } if (token.compare("enddynamic") != 0) Error("enddynamic statement missing"); +/* + if( tmp->DynamicObject->MoverParameters->LightPowerSource.SourceType != TPowerSource::NotDefined ) { + // if the vehicle has defined light source, it can (potentially) emit light, so add it to the light array +*/ + if( tmp->DynamicObject->MoverParameters->SecuritySystem.SystemType != 0 ) { + // we check for presence of security system, as a way to determine whether the vehicle is a controllable engine + // NOTE: this isn't 100% precise, e.g. middle EZT module comes with security system, while it has no lights + m_lights.insert( tmp->DynamicObject ); + } + break; case TP_MODEL: if (rmin < 0) @@ -4544,6 +4539,25 @@ bool TGround::Update(double dt, int iter) return true; }; +// updates scene lights array +void +TGround::Update_Lights() { + + m_lights.update(); + // arrange the light array from closest to farthest from current position of the camera + auto const camera = Global::pCameraPosition; + std::sort( + m_lights.data.begin(), + m_lights.data.end(), + [&]( light_array::light_record const &Left, light_array::light_record const &Right ) { + // move lights which are off at the end... + if( Left.intensity == 0.0f ) { return false; } + if( Right.intensity == 0.0f ) { return true; } + // ...otherwise prefer closer and/or brigher light sources + return ((camera - Left.position).LengthSquared() * (1.0f - Left.intensity)) < ((camera - Right.position).LengthSquared() * (1.0f - Right.intensity)); + } ); +} + // Winger 170204 - szukanie trakcji nad pantografami bool TGround::GetTraction(TDynamicObject *model) { // aktualizacja drutu zasilającego dla każdego pantografu, żeby odczytać napięcie @@ -4794,6 +4808,27 @@ bool TGround::GetTraction(TDynamicObject *model) return true; }; +bool +TGround::Render( Math3D::vector3 const &Camera ) { + + GfxRenderer.Update_Lights( m_lights ); + + if( Global::bUseVBO ) { // renderowanie przez VBO + if( !RenderVBO( Camera ) ) + return false; + if( !RenderAlphaVBO( Camera ) ) + return false; + } + else { // renderowanie przez Display List + if( !RenderDL( Camera ) ) + return false; + if( !RenderAlphaDL( Camera ) ) + return false; + } + + return true; +} + bool TGround::RenderDL(vector3 pPosition) { // renderowanie scenerii z Display List - faza nieprzezroczystych glDisable(GL_BLEND); @@ -5330,6 +5365,8 @@ void TGround::DynamicRemove(TDynamicObject *dyn) node = (*n); // zapamiętanie węzła, aby go usunąć (*n) = node->nNext; // pominięcie na liście Global::TrainDelete(d); + // remove potential entries in the light array + m_lights.remove( d ); d = d->Next(); // przejście do kolejnego pojazdu, póki jeszcze jest delete node; // usuwanie fizyczne z pamięci } diff --git a/Ground.h b/Ground.h index baa7ac2c..6ce96bf6 100644 --- a/Ground.h +++ b/Ground.h @@ -16,7 +16,9 @@ http://mozilla.org/MPL/2.0/. #include "ResourceManager.h" #include "Texture.h" #include "dumb3d.h" +#include "Float3d.h" #include "Names.h" +#include "lightarray.h" using namespace Math3D; @@ -303,11 +305,11 @@ class TGround TNames *sTracks = nullptr; // posortowane nazwy torów i eventów #else typedef std::unordered_map event_map; -// typedef std::unordered_map groundnode_map; event_map m_eventmap; -// groundnode_map m_memcellmap, m_modelmap, m_trackmap; TNames m_trackmap; #endif + light_array m_lights; // collection of dynamic light sources present in the scene + private: // metody prywatne bool EventConditon(TEvent *e); @@ -350,8 +352,10 @@ class TGround void MoveGroundNode(vector3 pPosition); void UpdatePhys(double dt, int iter); // aktualizacja fizyki stałym krokiem bool Update(double dt, int iter); // aktualizacja przesunięć zgodna z FPS + void Update_Lights(); // updates scene lights array bool AddToQuery(TEvent *Event, TDynamicObject *Node); bool GetTraction(TDynamicObject *model); + bool Render( Math3D::vector3 const &Camera ); bool RenderDL(vector3 pPosition); bool RenderAlphaDL(vector3 pPosition); bool RenderVBO(vector3 pPosition); diff --git a/Model3d.cpp b/Model3d.cpp index 5deba44c..d006bd17 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -207,43 +207,44 @@ inline void readMatrix(cParser &parser, float4x4 &matrix) 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" + 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.size() >= 3) + && (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 diff --git a/Segment.cpp b/Segment.cpp index e2656d53..900d4380 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -21,6 +21,11 @@ http://mozilla.org/MPL/2.0/. // 101206 Ra: trapezoidalne drogi // 110806 Ra: odwrócone mapowanie wzdłuż - Point1 == 1.0 +float Interpolate( float const First, float const Second, float const Factor ) { + + return ( First * ( 1.0f - Factor ) ) + ( Second * Factor ); +} + std::string Where(vector3 p) { // zamiana współrzędnych na tekst, używana w błędach return std::to_string(p.x) + " " + std::to_string(p.y) + " " + std::to_string(p.z); @@ -116,7 +121,11 @@ bool TSegment::Init(vector3 &NewPoint1, vector3 NewCPointOut, vector3 NewCPointI fStoop = atan2((Point2.y - Point1.y), fLength); // pochylenie toru prostego, żeby nie liczyć wielokrotnie SafeDeleteArray(fTsBuffer); +/* if ((bCurve) && (fStep > 0)) +*/ + // we're enforcing sub-division of even straight track, to have dense enough mesh for the spotlight to work with + if( fStep > 0 ) { // Ra: prosty dostanie podział, jak ma różną przechyłkę na końcach double s = 0; int i = 0; @@ -454,6 +463,7 @@ void TSegment::RenderLoft(const vector6 *ShapePoints, int iNumShapePoints, doubl } } else +#ifdef EU07_USE_OLD_LIGHTING_MODEL { // gdy prosty, nie modyfikujemy wektora kierunkowego i poprzecznego pos1 = FastGetPoint((fStep * iSkip) / fLength); pos2 = FastGetPoint_1(); @@ -498,6 +508,91 @@ void TSegment::RenderLoft(const vector6 *ShapePoints, int iNumShapePoints, doubl } glEnd(); } +#else +{ + Math3D::vector3 const pos0 = FastGetPoint( ( fStep * iSkip ) / fLength ); + Math3D::vector3 const pos1 = FastGetPoint_1(); + dir = GetDirection(); + Math3D::vector3 const parallel = Normalize( vector3( -dir.z, 0.0, dir.x ) ); // wektor poprzeczny + + Math3D::vector3 startnormal0, startnormal1, endnormal0, endnormal1; + Math3D::vector3 startvertex0, startvertex1, endvertex0, endvertex1; + float startuv0, startuv1, enduv0, enduv1; + + for( j = 0; j < iNumShapePoints - 1; ++j ) { + + startnormal0 = ShapePoints[ j ].n.x * parallel; + startnormal0.y += ShapePoints[ j ].n.y; + startvertex0 = parallel * ShapePoints[ j ].x + pos0; + startvertex0.y += ShapePoints[ j ].y; + startuv0 = ShapePoints[ j ].z; + startnormal1 = ShapePoints[ j + 1 ].n.x * parallel; + startnormal1.y += ShapePoints[ j + 1 ].n.y; + startvertex1 = parallel * ShapePoints[ j + 1 ].x + pos0; + startvertex1.y += ShapePoints[ j + 1 ].y; + startuv1 = ShapePoints[ j + 1 ].z; + + if( trapez == false ) { + // single profile throughout + endnormal0 = startnormal0; + endvertex0 = startvertex0 + ( pos1 - pos0 ); + enduv0 = startuv0; + endnormal1 = startnormal1; + endvertex1 = startvertex1 + ( pos1 - pos0 ); + enduv1 = startuv1; + } + else { + // end profile is different + endnormal0 = ShapePoints[ j + iNumShapePoints ].n.x * parallel; + endnormal0.y += ShapePoints[ j + iNumShapePoints ].n.y; + endvertex0 = parallel * ShapePoints[ j + iNumShapePoints ].x + pos1; // odsunięcie + endvertex0.y += ShapePoints[ j + iNumShapePoints ].y; // wysokość + enduv0 = ShapePoints[ j + iNumShapePoints ].z; + endnormal1 = ShapePoints[ j + iNumShapePoints + 1 ].n.x * parallel; + endnormal1.y += ShapePoints[ j + iNumShapePoints + 1 ].n.y; + endvertex1 = parallel * ShapePoints[ j + iNumShapePoints + 1 ].x + pos1; // odsunięcie + endvertex1.y += ShapePoints[ j + iNumShapePoints + 1 ].y; // wysokość + enduv1 = ShapePoints[ j + iNumShapePoints + 1 ].z; + } + // now build strips, lerping from start to endpoint + step = 10.0; // arbitrary segment size for straights + float s = 0.0, + t = 0.0, + uv = 0.0; + glBegin( GL_TRIANGLE_STRIP ); + while( s < fLength ) { + + t = s / fLength; + uv = s / fTextureLength; + + auto const normal1lerp = Interpolate( startnormal1, endnormal1, t ); + glNormal3dv( &normal1lerp.x ); + auto const uv1lerp = Interpolate( startuv1, enduv1, t ); + glTexCoord2f( uv1lerp, uv ); + auto const vertex1lerp = Interpolate( startvertex1, endvertex1, t ); + glVertex3dv( &vertex1lerp.x ); + + auto const normal0lerp = Interpolate( startnormal0, endnormal0, t ); + glNormal3dv( &normal0lerp.x ); + auto const uv0lerp = Interpolate( startuv0, enduv0, t ); + glTexCoord2f( uv0lerp, uv ); + auto const vertex0lerp = Interpolate( startvertex0, endvertex0, t ); + glVertex3dv( &vertex0lerp.x ); + + s += step; + } + // add ending vertex pair if needed + glNormal3dv( &endnormal1.x ); + glTexCoord2f( enduv1, fLength / fTextureLength ); + glVertex3dv( &endvertex1.x ); + glNormal3dv( &endnormal0.x ); + glTexCoord2f( enduv0, fLength / fTextureLength ); + glVertex3dv( &endvertex0.x ); + + glEnd(); + } +} +#endif }; void TSegment::RenderSwitchRail(const vector6 *ShapePoints1, const vector6 *ShapePoints2, diff --git a/World.cpp b/World.cpp index b9cfd631..9cf8263b 100644 --- a/World.cpp +++ b/World.cpp @@ -251,89 +251,34 @@ bool TWorld::Init(GLFWwindow *w) WriteLog("Max texture size: " + std::to_string(Global::iMaxTextureSize)); } /*-----------------------Render Initialization----------------------*/ - if (Global::fOpenGL >= 1.2) // poniższe nie działa w 1.1 - glTexEnvf(TEXTURE_FILTER_CONTROL_EXT, TEXTURE_LOD_BIAS_EXT, -1); - GLfloat FogColor[] = {1.0f, 1.0f, 1.0f, 1.0f}; - glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear screen and depth buffer + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); - // WriteLog("glClearColor (FogColor[0], FogColor[1], FogColor[2], 0.0); "); - // glClearColor (1.0, 0.0, 0.0, 0.0); // Background Color - // glClearColor (FogColor[0], FogColor[1], FogColor[2], 0.0); - // Background // Color - glClearColor(Global::Background[0], Global::Background[1], Global::Background[2], 1.0); // Background Color - WriteLog("glFogfv(GL_FOG_COLOR, FogColor);"); - glFogfv(GL_FOG_COLOR, FogColor); // Set Fog Color + glClearDepth( 1.0f ); // ZBuffer Value + glClearColor( 51.0f / 255.0f, 106.0f / 255.0f, 85.0f / 255.0f, 1.0f ); // Background Color + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // Clear screen and depth buffer - WriteLog("glClearDepth(1.0f); "); - glClearDepth(1.0f); // ZBuffer Value - - // glEnable(GL_NORMALIZE); - // glEnable(GL_RESCALE_NORMAL); - - // glEnable(GL_CULL_FACE); - WriteLog("glEnable(GL_TEXTURE_2D);"); glEnable(GL_TEXTURE_2D); // Enable Texture Mapping - WriteLog("glShadeModel(GL_SMOOTH);"); glShadeModel(GL_SMOOTH); // Enable Smooth Shading - WriteLog("glEnable(GL_DEPTH_TEST);"); glEnable(GL_DEPTH_TEST); // McZapkie:261102-uruchomienie polprzezroczystosci (na razie linie) pod kierunkiem Marcina // if (Global::bRenderAlpha) //Ra: wywalam tę flagę { - WriteLog("glEnable(GL_BLEND);"); glEnable(GL_BLEND); - WriteLog("glEnable(GL_ALPHA_TEST);"); glEnable(GL_ALPHA_TEST); - WriteLog("glAlphaFunc(GL_GREATER,0.04);"); glAlphaFunc(GL_GREATER, 0.04f); - WriteLog("glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);"); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - WriteLog("glDepthFunc(GL_LEQUAL);"); glDepthFunc(GL_LEQUAL); } - /* - else - { - WriteLog("glEnable(GL_ALPHA_TEST);"); - glEnable(GL_ALPHA_TEST); - WriteLog("glAlphaFunc(GL_GREATER,0.5);"); - glAlphaFunc(GL_GREATER,0.5); - WriteLog("glDepthFunc(GL_LEQUAL);"); - glDepthFunc(GL_LEQUAL); - WriteLog("glDisable(GL_BLEND);"); - glDisable(GL_BLEND); - } - */ - /* zakomentowanie to co bylo kiedys mieszane - WriteLog("glEnable(GL_ALPHA_TEST);"); - glEnable(GL_ALPHA_TEST);//glGetIntegerv() - WriteLog("glAlphaFunc(GL_GREATER,0.5);"); - // glAlphaFunc(GL_LESS,0.5); - glAlphaFunc(GL_GREATER,0.5); - // glBlendFunc(GL_SRC_ALPHA,GL_ONE); - WriteLog("glDepthFunc(GL_LEQUAL);"); - glDepthFunc(GL_LEQUAL);//EQUAL); - // The Type Of Depth Testing To Do - // glEnable(GL_BLEND); - // glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - */ - WriteLog("glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);"); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations - - WriteLog("glPolygonMode(GL_FRONT, GL_FILL);"); glPolygonMode(GL_FRONT, GL_FILL); - WriteLog("glFrontFace(GL_CCW);"); glFrontFace(GL_CCW); // Counter clock-wise polygons face out - WriteLog("glEnable(GL_CULL_FACE); "); glEnable(GL_CULL_FACE); // Cull back-facing triangles - WriteLog("glLineWidth(1.0f);"); glLineWidth(1.0f); - // glLineWidth(2.0f); - WriteLog("glPointSize(3.0f);"); glPointSize(3.0f); #ifdef EU07_USE_OLD_LIGHTING_MODEL // ----------- LIGHTING SETUP ----------- @@ -365,35 +310,18 @@ bool TWorld::Init(GLFWwindow *w) glEnable(GL_LIGHT0); #endif // glColor() ma zmieniać kolor wybrany w glColorMaterial() - WriteLog("glEnable(GL_COLOR_MATERIAL);"); glEnable(GL_COLOR_MATERIAL); - WriteLog("glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);"); glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); + glColor4fv(Global::whiteLight); - // WriteLog("glMaterialfv( GL_FRONT, GL_AMBIENT, whiteLight );"); - // glMaterialfv( GL_FRONT, GL_AMBIENT, Global::whiteLight ); - - WriteLog("glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, whiteLight );"); - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, Global::whiteLight); - - /* - WriteLog("glMaterialfv( GL_FRONT, GL_SPECULAR, noLight );"); - glMaterialfv( GL_FRONT, GL_SPECULAR, Global::noLight ); - */ - - WriteLog("glEnable(GL_LIGHTING);"); glEnable(GL_LIGHTING); - WriteLog("glFogi(GL_FOG_MODE, GL_LINEAR);"); glFogi(GL_FOG_MODE, GL_LINEAR); // Fog Mode - WriteLog("glFogfv(GL_FOG_COLOR, FogColor);"); + GLfloat FogColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; glFogfv(GL_FOG_COLOR, FogColor); // Set Fog Color - WriteLog("glFogf(GL_FOG_START, 1000.0f);"); glFogf(GL_FOG_START, 10.0f); // Fog Start Depth - WriteLog("glFogf(GL_FOG_END, 2000.0f);"); glFogf(GL_FOG_END, 200.0f); // Fog End Depth - WriteLog("glEnable(GL_FOG);"); glEnable(GL_FOG); // Enables GL_FOG // Ra: ustawienia testowe @@ -456,7 +384,7 @@ bool TWorld::Init(GLFWwindow *w) glLoadIdentity(); // glColor4f(0.3f,0.0f,0.0f,0.0f); - glTranslatef(0.0f, 0.0f, -0.50f); +// glTranslatef(0.0f, 0.0f, -0.50f); // glRasterPos2f(-0.25f, -0.10f); #ifdef EU07_USE_OLD_LIGHTING_MODEL glColor3f( 3.0f, 3.0f, 3.0f ); @@ -467,23 +395,25 @@ bool TWorld::Init(GLFWwindow *w) auto logo = GfxRenderer.GetTextureId( "logo", szTexturePath, 6 ); GfxRenderer.Bind(logo); // Select our texture + float const widthratio = ( 4.0f / 3.0f ) / ( (float)Global::iWindowWidth / Global::iWindowHeight ); + glBegin(GL_QUADS); // Drawing using triangles glTexCoord2f(0.0f, 0.0f); - glVertex3f(-1.0f, -1.0f, 0.0f); // bottom left of the texture and quad + glVertex2f( -widthratio, -1.0f ); // bottom left of the texture and quad glTexCoord2f(1.0f, 0.0f); - glVertex3f(1.0f, -1.0f, 0.0f); // bottom right of the texture and quad + glVertex2f(widthratio, -1.0f); // bottom right of the texture and quad glTexCoord2f(1.0f, 1.0f); - glVertex3f(1.0f, 1.0f, 0.0f); // top right of the texture and quad + glVertex2f(widthratio, 1.0f); // top right of the texture and quad glTexCoord2f(0.0f, 1.0f); - glVertex3f(-1.0f, 1.0f, 0.0f); // top left of the texture and quad + glVertex2f( -widthratio, 1.0f ); // top left of the texture and quad glEnd(); //~logo; Ra: to jest bez sensu zapis glColor3f(0.0f, 0.0f, 1.0f); if (Global::detonatoryOK) { - glRasterPos2f(-0.85f, -0.25f); + glRasterPos2f(-0.85f * widthratio, -0.25f); glPrint("Uruchamianie / Initializing..."); - glRasterPos2f(-0.85f, -0.30f); + glRasterPos2f(-0.85f * widthratio, -0.30f); glPrint("Dzwiek / Sound..."); } glfwSwapBuffers(window); @@ -496,7 +426,7 @@ bool TWorld::Init(GLFWwindow *w) WriteLog("Sound Init OK"); if (Global::detonatoryOK) { - glRasterPos2f(-0.25f, -0.30f); + glRasterPos2f(-0.25f * widthratio, -0.30f); glPrint("OK."); } @@ -506,7 +436,7 @@ bool TWorld::Init(GLFWwindow *w) WriteLog("Textures init"); if (Global::detonatoryOK) { - glRasterPos2f(-0.85f, -0.35f); + glRasterPos2f(-0.85f * widthratio, -0.35f); glPrint("Tekstury / Textures..."); } glfwSwapBuffers(window); @@ -514,7 +444,7 @@ bool TWorld::Init(GLFWwindow *w) WriteLog("Textures init OK"); if (Global::detonatoryOK) { - glRasterPos2f(-0.25f, -0.35f); + glRasterPos2f( -0.25f * widthratio, -0.35f ); glPrint("OK."); } glfwSwapBuffers(window); @@ -522,7 +452,7 @@ bool TWorld::Init(GLFWwindow *w) WriteLog("Models init"); if (Global::detonatoryOK) { - glRasterPos2f(-0.85f, -0.40f); + glRasterPos2f( -0.85f * widthratio, -0.40f ); glPrint("Modele / Models..."); } glfwSwapBuffers(window); @@ -532,7 +462,7 @@ bool TWorld::Init(GLFWwindow *w) WriteLog("Models init OK"); if (Global::detonatoryOK) { - glRasterPos2f(-0.25f, -0.40f); + glRasterPos2f( -0.25f * widthratio, -0.40f ); glPrint("OK."); } glfwSwapBuffers(window); @@ -540,7 +470,7 @@ bool TWorld::Init(GLFWwindow *w) WriteLog("Ground init"); if (Global::detonatoryOK) { - glRasterPos2f(-0.85f, -0.45f); + glRasterPos2f( -0.85f * widthratio, -0.45f ); glPrint("Sceneria / Scenery (please wait)..."); } glfwSwapBuffers(window); @@ -568,7 +498,7 @@ bool TWorld::Init(GLFWwindow *w) WriteLog("Ground init OK"); if (Global::detonatoryOK) { - glRasterPos2f(-0.25f, -0.45f); + glRasterPos2f( -0.25f * widthratio, -0.45f ); glPrint("OK."); } glfwSwapBuffers(window); @@ -585,7 +515,7 @@ bool TWorld::Init(GLFWwindow *w) char buff[255] = "Player train init: "; if (Global::detonatoryOK) { - glRasterPos2f(-0.85f, -0.50f); + glRasterPos2f( -0.85f * widthratio, -0.50f ); glPrint("Przygotowanie kabiny do sterowania..."); } glfwSwapBuffers(window); @@ -606,7 +536,7 @@ bool TWorld::Init(GLFWwindow *w) WriteLog("Player train init OK"); if (Global::detonatoryOK) { - glRasterPos2f(-0.25f, -0.50f); + glRasterPos2f( -0.25f * widthratio, -0.50f ); glPrint("OK."); } FollowView(); @@ -618,7 +548,7 @@ bool TWorld::Init(GLFWwindow *w) FreeFlyModeFlag = true; // Ra: automatycznie włączone latanie if (Global::detonatoryOK) { - glRasterPos2f(-0.85f, -0.50f); + glRasterPos2f( -0.85f * widthratio, -0.50f ); glPrint("Blad inicjalizacji sterowanego pojazdu!"); } glfwSwapBuffers(window); @@ -634,7 +564,7 @@ bool TWorld::Init(GLFWwindow *w) Error("Player train not exist!"); if (Global::detonatoryOK) { - glRasterPos2f(-0.85f, -0.55f); + glRasterPos2f( -0.85f * widthratio, -0.55f ); glPrint("Wybrany pojazd nie istnieje w scenerii!"); } } @@ -731,49 +661,102 @@ void TWorld::OnKeyDown(int cKey) } else if (cKey >= GLFW_KEY_F1 && cKey <= GLFW_KEY_F12) { - switch (cKey) - { - case GLFW_KEY_F1: // czas i relacja - case GLFW_KEY_F3: - case GLFW_KEY_F5: // przesiadka do innego pojazdu - case GLFW_KEY_F8: // FPS - case GLFW_KEY_F9: // wersja, typ wyświetlania, błędy OpenGL - case GLFW_KEY_F10: - if (Global::iTextMode == cKey) - Global::iTextMode = - (Global::iPause && (cKey != GLFW_KEY_F1) ? GLFW_KEY_F1 : - 0); // wyłączenie napisów, chyba że pauza - else - Global::iTextMode = cKey; - break; - case GLFW_KEY_F2: // parametry pojazdu - if (Global::iTextMode == cKey) // jeśli kolejne naciśnięcie - ++Global::iScreenMode[cKey - GLFW_KEY_F1]; // kolejny ekran - else - { // pierwsze naciśnięcie daje pierwszy (tzn. zerowy) ekran - Global::iTextMode = cKey; - Global::iScreenMode[cKey - GLFW_KEY_F1] = 0; + switch (cKey) { + + case GLFW_KEY_F1: { + if( DebugModeFlag ) { + // additional time speedup keys in debug mode + if(Global::ctrlState) { + // ctrl-f3 + GlobalTime->UpdateMTableTime( 20.0 * 60.0 ); + } + else if(Global::shiftState) { + // shift-f3 + GlobalTime->UpdateMTableTime( 5.0 * 60.0 ); + } + } + if( (Global::ctrlState) + && (Global::shiftState) ) { + // czas i relacja + if( Global::iTextMode == cKey ) { + // wyłączenie napisów, chyba że pauza + Global::iTextMode = + ( Global::iPause && ( cKey != GLFW_KEY_F1 ) ? + GLFW_KEY_F1 : + 0 ); + } + else + Global::iTextMode = cKey; + } + break; } - break; - case GLFW_KEY_F12: - if (Global::ctrlState && Global::shiftState) - DebugModeFlag = !DebugModeFlag; // taka opcjonalna funkcja, może się czasem przydać - else + case GLFW_KEY_F2: { + // parametry pojazdu + if( Global::iTextMode == cKey ) { + // jeśli kolejne naciśnięcie + ++Global::iScreenMode[ cKey - GLFW_KEY_F1 ]; // kolejny ekran + } + else { + // pierwsze naciśnięcie daje pierwszy (tzn. zerowy) ekran + Global::iTextMode = cKey; + Global::iScreenMode[ cKey - GLFW_KEY_F1 ] = 0; + } + break; + } + case GLFW_KEY_F3: { Global::iTextMode = cKey; - break; - case GLFW_KEY_F4: - InOutKey(); - break; - case GLFW_KEY_F6: - if (DebugModeFlag) - { // przyspieszenie symulacji do testowania scenerii... uwaga na FPS! - // Global::iViewMode=GLFW_KEY_F6; - if (Global::ctrlState) - Global::fTimeSpeed = (Global::shiftState ? 10.0 : 5.0); + break; + } + case GLFW_KEY_F4: { + InOutKey(); + break; + } + case GLFW_KEY_F5: { + // przesiadka do innego pojazdu + Global::iTextMode = cKey; + break; + } + case GLFW_KEY_F6: { + Global::iTextMode = cKey; + // przyspieszenie symulacji do testowania scenerii... uwaga na FPS! + if( DebugModeFlag ) { + + if( Global::ctrlState ) { Global::fTimeSpeed = (Global::shiftState ? 60.0 : 20.0 ); } + else { Global::fTimeSpeed = ( Global::shiftState ? 5.0 : 1.0 ); } + } + break; + } + case GLFW_KEY_F8: { + Global::iTextMode = cKey; + // FPS + break; + } + case GLFW_KEY_F9: { + Global::iTextMode = cKey; + // wersja, typ wyświetlania, błędy OpenGL + break; + } + case GLFW_KEY_F10: { + if( Global::iTextMode == cKey ) { + Global::iTextMode = + ( Global::iPause && ( cKey != GLFW_KEY_F1 ) ? + GLFW_KEY_F1 : + 0 ); // wyłączenie napisów, chyba że pauza + } + else { + Global::iTextMode = cKey; + } + break; + } + case GLFW_KEY_F12: { + // coś tam jeszcze + if( Global::ctrlState + && Global::shiftState ) + DebugModeFlag = !DebugModeFlag; // taka opcjonalna funkcja, może się czasem przydać else - Global::fTimeSpeed = (Global::shiftState ? 2.0 : 1.0); + Global::iTextMode = cKey; + break; } - break; } // if (cKey!=GLFW_KEY_F4) return; // nie są przekazywane do pojazdu wcale @@ -1112,7 +1095,8 @@ bool TWorld::Update() iCheckFPS = 0.25 * Timer::GetFPS(); // tak za 0.25 sekundy sprawdzić ponownie (jeszcze przycina?) } Timer::UpdateTimers(Global::iPause != 0); - if (!Global::iPause) + if( (Global::iPause == false) + || (m_init == false) ) { // jak pauza, to nie ma po co tego przeliczać GlobalTime->UpdateMTableTime(Timer::GetDeltaTime()); // McZapkie-300302: czas rozkladowy // Ra 2014-07: przeliczenie kąta czasu (do animacji zależnych od czasu) @@ -1196,6 +1180,8 @@ bool TWorld::Update() Ground.CheckQuery(); + Ground.Update_Lights(); + if( Train != nullptr ) { TSubModel::iInstance = reinterpret_cast( Train->Dynamic() ); Train->Update( dt ); @@ -1207,13 +1193,17 @@ bool TWorld::Update() // przy 0.25 smuga gaśnie o 6:37 w Quarku, a mogłaby już 5:40 // Ra 2014-12: przy 0.15 się skarżyli, że nie widać smug => zmieniłem na 0.25 // changed light activation threshold to 0.5, paired with strength reduction in daylight - if (Train) // jeśli nie usunięty + if( Train ) { + // jeśli nie usunięty Global::bSmudge = ( FreeFlyModeFlag ? false : ( Train->Dynamic()->fShade <= 0.0 ? - (Global::fLuminance <= 0.5) : - (Train->Dynamic()->fShade * Global::fLuminance <= 0.5) ) ); + ( Global::fLuminance <= 0.5 ) : + ( Train->Dynamic()->fShade * Global::fLuminance <= 0.5 ) ) ); + } + + m_init = true; if (!Render()) return false; @@ -1484,31 +1474,11 @@ bool TWorld::Render() Environment.render(); } - if (Global::bUseVBO) - { // renderowanie przez VBO - if (!Ground.RenderVBO(Camera.Pos)) - return false; - if (!Ground.RenderAlphaVBO(Camera.Pos)) - return false; - } - else - { // renderowanie przez Display List - if (!Ground.RenderDL(Camera.Pos)) - return false; - if (!Ground.RenderAlphaDL(Camera.Pos)) - return false; - } -/* - TSubModel::iInstance = (int)(Train ? Train->Dynamic() : 0); //żeby nie robić cudzych animacji - // if (Camera.Type==tp_Follow) - if (Train) - Train->Update(); -*/ + if( false == Ground.Render( Camera.Pos ) ) { return false; } + Render_Cab(); Render_UI(); -// glFlush(); - // Global::bReCompile=false; //Ra: już zrobiona rekompilacja ResourceManager::Sweep( Timer::GetSimulationTime() ); return true; @@ -1533,6 +1503,7 @@ TWorld::Render_Cab() { // ABu: Rendering kabiny jako ostatniej, zeby bylo widac przez szyby, tylko w widoku ze srodka return; } + /* // ABu: Rendering kabiny jako ostatniej, zeby bylo widac przez szyby, tylko w widoku ze srodka if( ( Train->Dynamic()->mdKabina != Train->Dynamic()->mdModel ) && @@ -1546,10 +1517,11 @@ TWorld::Render_Cab() { glLoadIdentity(); // zacząć od macierzy jedynkowej Camera.SetCabMatrix( pos ); // widok z kamery po przesunięciu glMultMatrixd( dynamic->mMatrix.getArray() ); // ta macierz nie ma przesunięcia - +/* //*yB: moje smuuugi 1 if( Global::bSmudge ) { // Ra: uwzględniłem zacienienie pojazdu przy zapalaniu smug - // 1. warunek na smugę wyznaczyc wcześniej + + // 1. warunek na smugę wyznaczyc wcześniej // 2. jeśli smuga włączona, nie renderować pojazdu użytkownika w DynObj // 3. jeśli smuga właczona, wyrenderować pojazd użytkownia po dodaniu smugi do sceny auto const &frontlights = Train->Controlled()->iLights[ 0 ]; @@ -1622,24 +1594,25 @@ TWorld::Render_Cab() { //float ddl = (0.15*Global::diffuseDayLight[0]+0.295*Global::diffuseDayLight[1]+0.055*Global::diffuseDayLight[2]); //0.24:0 glBegin( GL_QUADS ); float fSmudge = dynamic->MoverParameters->DimHalf.y + 7; // gdzie zaczynać smugę - if( frontlightstrength > 0.f ) { // wystarczy jeden zapalony z przodu - + if( frontlightstrength > 0.f ) { + // wystarczy jeden zapalony z przodu for( int i = 15; i <= 35; i++ ) { float z = i * i * i * 0.01f;//25/4; //float C = (36 - i*0.5)*0.005*(1.5 - sqrt(ddl)); float C = ( 36 - i*0.5 )*0.005*sqrt( ( 1 / sqrt( Global::fLuminance + 0.015 ) ) - 1 ) * frontlightstrength; glColor4f( C, C, C, 1.0f );// *frontlightstrength ); - glTexCoord2f( 0, 0 ); glVertex3f( -10 / 2 - 2 * i / 4, 6.0 + 0.3*z, 13 + 1.7*z / 3 ); - glTexCoord2f( 1, 0 ); glVertex3f( 10 / 2 + 2 * i / 4, 6.0 + 0.3*z, 13 + 1.7*z / 3 ); - glTexCoord2f( 1, 1 ); glVertex3f( 10 / 2 + 2 * i / 4, -5.0 - 0.5*z, 13 + 1.7*z / 3 ); + glTexCoord2f( 0, 0 ); glVertex3f( -10 / 2 - 2 * i / 4, 6.0 + 0.3*z, 13 + 1.7*z / 3 ); + glTexCoord2f( 1, 0 ); glVertex3f( 10 / 2 + 2 * i / 4, 6.0 + 0.3*z, 13 + 1.7*z / 3 ); + glTexCoord2f( 1, 1 ); glVertex3f( 10 / 2 + 2 * i / 4, -5.0 - 0.5*z, 13 + 1.7*z / 3 ); glTexCoord2f( 0, 1 ); glVertex3f( -10 / 2 - 2 * i / 4, -5.0 - 0.5*z, 13 + 1.7*z / 3 ); } } - if( rearlightstrength > 0.f ) { // wystarczy jeden zapalony z tyłu + if( rearlightstrength > 0.f ) { + // wystarczy jeden zapalony z tyłu for( int i = 15; i <= 35; i++ ) { float z = i * i * i * 0.01f;//25/4; float C = ( 36 - i*0.5 )*0.005*sqrt( ( 1 / sqrt( Global::fLuminance + 0.015 ) ) - 1 ) * rearlightstrength; - glColor4f( C, C, C, 0.35f );// *rearlightstrength ); + glColor4f( C, C, C, 1.0f );// *rearlightstrength ); glTexCoord2f( 0, 0 ); glVertex3f( 10 / 2 + 2 * i / 4, 6.0 + 0.3*z, -13 - 1.7*z / 3 ); glTexCoord2f( 1, 0 ); glVertex3f( -10 / 2 - 2 * i / 4, 6.0 + 0.3*z, -13 - 1.7*z / 3 ); glTexCoord2f( 1, 1 ); glVertex3f( -10 / 2 - 2 * i / 4, -5.0 - 0.5*z, -13 - 1.7*z / 3 ); @@ -1656,14 +1629,17 @@ TWorld::Render_Cab() { glEnable( GL_FOG ); } } + glEnable( GL_LIGHTING ); // po renderowaniu smugi jest to wyłączone // Ra: pojazd użytkownika należało by renderować po smudze, aby go nie rozświetlała + Global::bSmudge = false; // aby model użytkownika się teraz wyrenderował dynamic->Render(); dynamic->RenderAlpha(); // przezroczyste fragmenty pojazdów na torach - } // yB: moje smuuugi 1 - koniec*/ + + } // yB: moje smuuugi 1 - koniec else - glEnable( GL_LIGHTING ); // po renderowaniu drutów może być to wyłączone +*/ glEnable( GL_LIGHTING ); // po renderowaniu drutów może być to wyłączone. TODO: have the wires render take care of its own shit if( dynamic->mdKabina ) // bo mogła zniknąć przy przechodzeniu do innego pojazdu { @@ -1794,9 +1770,9 @@ TWorld::Render_UI() { glLoadIdentity(); if( DebugModeFlag && !Global::iTextMode ) { - OutText1 = " FPS: "; + OutText1 = "FPS: "; OutText1 += to_string( Timer::GetFPS(), 2 ); - OutText1 += Global::iSlowMotion ? "s" : "n"; + OutText1 += Global::iSlowMotion ? "(s)" : "(n)"; OutText1 += ( Timer::GetDeltaTime() >= 0.2 ) ? "!" : " "; // if (GetDeltaTime()>=0.2) //Ra: to za bardzo miota tekstem! @@ -1811,7 +1787,7 @@ TWorld::Render_UI() { if( Global::iTextMode == GLFW_KEY_F8 ) { Global::iViewMode = GLFW_KEY_F8; - OutText1 = " FPS: "; + OutText1 = "FPS: "; OutText1 += to_string( Timer::GetFPS(), 2 ); //OutText1 += sprintf(); if( Global::iSlowMotion ) @@ -2248,7 +2224,7 @@ TWorld::Render_UI() { OutText1 += " " + std::string( "S SEE NEN NWW SW" ) - .substr( 1 + 2 * floor( fmod( 8 + ( Camera.Yaw + 0.5 * M_PI_4 ) / M_PI_4, 8 ) ), 2 ); + .substr( 0 + 2 * floor( fmod( 8 + ( Camera.Yaw + 0.5 * M_PI_4 ) / M_PI_4, 8 ) ), 2 ); // current luminance level OutText2 = "Light level: " + to_string( Global::fLuminance, 3 ); } @@ -2963,16 +2939,14 @@ world_environment::update() { auto const position = m_sun.getPosition(); // ...update the global data to match new sun state... Global::SunAngle = m_sun.getAngle(); - Global::DayLight.position[ 0 ] = position.x; - Global::DayLight.position[ 1 ] = position.y; - Global::DayLight.position[ 2 ] = position.z; + Global::DayLight.set_position( position ); Global::DayLight.direction = -1.0 * m_sun.getDirection(); // ...update skydome to match the current sun position as well... m_skydome.Update( position ); // ...retrieve current sky colour and brightness... auto const skydomecolour = m_skydome.GetAverageColor(); auto const skydomehsv = RGBtoHSV( skydomecolour ); - auto const intensity = std::min( 0.05f + m_sun.getIntensity() + skydomehsv.z, 1.25f ); + auto const intensity = std::min( 1.15f * (0.05f + m_sun.getIntensity() + skydomehsv.z), 1.25f ); // ...update light colours and intensity. // NOTE: intensity combines intensity of the sun and the light reflected by the sky dome // it'd be more technically correct to have just the intensity of the sun here, @@ -3009,7 +2983,7 @@ world_environment::render() { m_skydome.Render(); m_stars.render(); -// m_clouds.Render(); + m_clouds.Render( m_skydome.GetAverageColor() * 2.5f ); if( DebugModeFlag == true ) { // mark sun position for easier debugging diff --git a/World.h b/World.h index cc89d087..05bc89fb 100644 --- a/World.h +++ b/World.h @@ -82,6 +82,7 @@ class TWorld double VelPrev; // poprzednia prędkość int tprev; // poprzedni czas double Acc; // przyspieszenie styczne + bool m_init{ false }; // indicates whether initial update of the world was performed public: void ModifyTGA(std::string const &dir = ""); void CreateE3D(std::string const &dir = "", bool dyn = false); diff --git a/dumb3d.h b/dumb3d.h index 0ed4f67f..23dd5ce5 100644 --- a/dumb3d.h +++ b/dumb3d.h @@ -83,7 +83,8 @@ class vector3 void inline Normalize(); void inline SafeNormalize(); - double inline Length(); + double inline Length() const; + double inline LengthSquared() const; void inline Zero() { x = y = z = 0.0; @@ -415,6 +416,11 @@ inline vector3 CrossProduct(const vector3 &v1, const vector3 &v2) return vector3(v1.y * v2.z - v1.z * v2.y, v2.x * v1.z - v2.z * v1.x, v1.x * v2.y - v1.y * v2.x); } +inline vector3 Interpolate( vector3 const &First, vector3 const &Second, float const Factor ) { + + return ( First * ( 1.0f - Factor ) ) + ( Second * Factor ); +} + inline vector3 operator*(const matrix4x4 &m, const vector3 &v) { return vector3(v.x * m[0][0] + v.y * m[1][0] + v.z * m[2][0] + m[3][0], @@ -430,11 +436,16 @@ void inline vector3::Normalize() z *= il; } -double inline vector3::Length() +double inline vector3::Length() const { return SQRT_FUNCTION(x * x + y * y + z * z); } +double inline vector3::LengthSquared() const { + + return ( x * x + y * y + z * z ); +} + inline bool operator==(const matrix4x4 &m1, const matrix4x4 &m2) { for (int x = 0; x < 4; ++x) diff --git a/lightarray.cpp b/lightarray.cpp new file mode 100644 index 00000000..6ad61a7f --- /dev/null +++ b/lightarray.cpp @@ -0,0 +1,75 @@ +/* +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 and others + +*/ + +#include "stdafx.h" +#include "lightarray.h" +#include "dynobj.h" + +void +light_array::insert( TDynamicObject const *Owner ) { + + // we're only storing lights for locos, which have two sets of lights, front and rear + // for a more generic role this function would have to be tweaked to add vehicle type-specific light combinations + data.emplace_back( Owner, 0 ); + data.emplace_back( Owner, 1 ); +} + +void +light_array::remove( TDynamicObject const *Owner ) { + + data.erase( + std::remove_if( + data.begin(), + data.end(), + [=]( light_record const &light ){ return light.owner == Owner; } ), + data.end() ); +} + +// updates records in the collection +void +light_array::update() { + + for( auto &light : data ) { + // update light parameters to match current data of the owner + if( light.index == 0 ) { + // front light set + light.position = light.owner->GetPosition() + ( light.owner->VectorFront() * light.owner->GetLength() * 0.45 ); + light.direction = light.owner->VectorFront(); + } + else { + // rear light set + light.position = light.owner->GetPosition() - ( light.owner->VectorFront() * light.owner->GetLength() * 0.45 ); + light.direction = light.owner->VectorFront(); + light.direction.x = -light.direction.x; + light.direction.z = -light.direction.z; + } + // determine intensity of this light set + if( true == light.owner->MoverParameters->Battery ) { + // with battery on, the intensity depends on the state of activated switches + auto const &lightbits = light.owner->iLights[ light.index ]; + light.count = 0 + + ( ( lightbits & 1 ) ? 1 : 0 ) + + ( ( lightbits & 4 ) ? 1 : 0 ) + + ( ( lightbits & 16 ) ? 1 : 0 ); + + light.intensity = 0.15f * light.count; // TODO: intensity can be affected further by dim switch or other factors + } + else { + // with battery off the lights are off + light.intensity = 0.0f; + light.count = 0; + } + } +} diff --git a/lightarray.h b/lightarray.h new file mode 100644 index 00000000..34b317d9 --- /dev/null +++ b/lightarray.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include "dumb3d.h" +#include "float3d.h" +#include "dynobj.h" + +// collection of virtual light sources present in the scene +// used by the renderer to determine most suitable placement for actual light sources during render +struct light_array { + +public: +// types + struct light_record { + + light_record( TDynamicObject const *Owner, int const Lightindex) : + owner(Owner), index(Lightindex) + {}; + + TDynamicObject const *owner; // the object in world which 'carries' the light + int index{ -1 }; // 0: front lights, 1: rear lights + Math3D::vector3 position; // position of the light in 3d scene + Math3D::vector3 direction; // direction of the light in 3d scene + float3 color{ 255.0f / 255.0f, 241.0f / 255.0f, 224.0f / 255.0f }; // color of the light, default is halogen light + float intensity{ 0.0f }; // (combined) intensity of the light(s) + int count{ 0 }; // number (or pattern) of active light(s) + }; + +// methods + // adds records for lights of specified owner to the collection + void + insert( TDynamicObject const *Owner ); + // removes records for lights of specified owner from the collection + void + remove( TDynamicObject const *Owner ); + // updates records in the collection + void + update(); + +// types + typedef std::vector lightrecord_array; + +// members + lightrecord_array data; +}; diff --git a/renderer.cpp b/renderer.cpp index 13fdb85b..2b38d068 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -9,7 +9,85 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "renderer.h" +#include "globals.h" opengl_renderer GfxRenderer; +void +opengl_renderer::Init() { + + // create dynamic light pool + for( int idx = 0; idx < Global::DynamicLightCount; ++idx ) { + + opengl_light light; + light.id = GL_LIGHT1 + idx; + + light.position[ 3 ] = 1.0f; + ::glLightf( light.id, GL_SPOT_CUTOFF, 20.0f ); + ::glLightf( light.id, GL_SPOT_EXPONENT, 10.0f ); + ::glLightf( light.id, GL_CONSTANT_ATTENUATION, 0.0f ); + ::glLightf( light.id, GL_LINEAR_ATTENUATION, 0.035f ); + + m_lights.emplace_back( light ); + } +} + +void +opengl_renderer::Update_Lights( light_array const &Lights ) { + + size_t const count = std::min( m_lights.size(), Lights.data.size() ); + if( count == 0 ) { return; } + + auto renderlight = m_lights.begin(); + + for( auto const &scenelight : Lights.data ) { + + if( renderlight == m_lights.end() ) { + // we ran out of lights to assign + break; + } + if( scenelight.intensity == 0.0f ) { + // all lights past this one are bound to be off + break; + } + if( ( Global::pCameraPosition - scenelight.position ).Length() > 1000.0f ) { + // we don't care about lights past arbitrary limit of 1 km. + // but there could still be weaker lights which are closer, so keep looking + continue; + } + // if the light passed tests so far, it's good enough + renderlight->set_position( scenelight.position ); + renderlight->direction = scenelight.direction; + + renderlight->diffuse[ 0 ] = scenelight.color.x; + renderlight->diffuse[ 1 ] = scenelight.color.y; + renderlight->diffuse[ 2 ] = scenelight.color.z; + renderlight->ambient[ 0 ] = scenelight.color.x * scenelight.intensity; + renderlight->ambient[ 1 ] = scenelight.color.y * scenelight.intensity; + renderlight->ambient[ 2 ] = scenelight.color.z * scenelight.intensity; + + ::glLightf( renderlight->id, GL_LINEAR_ATTENUATION, 0.3f / std::pow( scenelight.count, 2 ) ); + ::glEnable( renderlight->id ); + + renderlight->apply_intensity(); + renderlight->apply_angle(); + + ++renderlight; + } + + while( renderlight != m_lights.end() ) { + // if we went through all scene lights and there's still opengl lights remaining, kill these + ::glDisable( renderlight->id ); + ++renderlight; + } +} + +void +opengl_renderer::Disable_Lights() { + + for( int idx = 0; idx < m_lights.size() + 1; ++idx ) { + + ::glDisable( GL_LIGHT0 + idx ); + } +} //--------------------------------------------------------------------------- diff --git a/renderer.h b/renderer.h index 234fe913..719d78ae 100644 --- a/renderer.h +++ b/renderer.h @@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "GL/glew.h" #include "texture.h" +#include "lightarray.h" #include "dumb3d.h" struct opengl_light { @@ -23,10 +24,10 @@ struct opengl_light { GLfloat specular[ 4 ]; opengl_light() { - position[ 0 ] = position[ 1 ] = position[ 2 ] = 0.0f; position[ 3 ] = 1.0f; - ambient[ 0 ] = ambient[ 1 ] = ambient[ 2 ] = ambient[ 3 ] = 1.0f; - diffuse[ 0 ] = diffuse[ 1 ] = diffuse[ 2 ] = diffuse[ 3 ] = 1.0f; - specular[ 0 ] = specular[ 1 ] = specular[ 2 ] = specular[ 3 ] = 1.0f; + position[ 0 ] = position[ 1 ] = position[ 2 ] = 0.0f; position[ 3 ] = 1.0f; // 0,0,0,1 + ambient[ 0 ] = ambient[ 1 ] = ambient[ 2 ] = 0.0f; ambient[ 3 ] = 1.0f; // 0,0,0,1 + diffuse[ 0 ] = diffuse[ 1 ] = diffuse[ 2 ] = diffuse[ 3 ] = 1.0f; // 1,1,1,1 + specular[ 0 ] = specular[ 1 ] = specular[ 2 ] = specular[ 3 ] = 1.0f; // 1,1,1,1 } inline @@ -45,6 +46,13 @@ struct opengl_light { glLightfv( id, GL_SPOT_DIRECTION, directionarray ); } } + inline + void set_position( Math3D::vector3 const &Position ) { + + position[ 0 ] = Position.x; + position[ 1 ] = Position.y; + position[ 2 ] = Position.z; + } }; // encapsulates basic rendering setup. @@ -65,32 +73,50 @@ struct opengl_material { class opengl_renderer { public: - GLenum static const sunlight{ GL_LIGHT0 }; -// GLenum static const ambientlight{ GL_LIGHT1 }; +// types + +// methods + void + Init(); + + void + Update_Lights( light_array const &Lights ); + + void + Disable_Lights(); texture_manager::size_type GetTextureId( std::string Filename, std::string const &Dir, int const Filter = -1, bool const Loadnow = true ) { return m_textures.GetTextureId( Filename, Dir, Filter, Loadnow ); } + void Bind( texture_manager::size_type const Id ) { // temporary until we separate the renderer m_textures.Bind( Id ); } + opengl_texture & Texture( texture_manager::size_type const Id ) { return m_textures.Texture( Id ); } +// members + GLenum static const sunlight{ GL_LIGHT0 }; + private: +// types enum class rendermode { color }; + typedef std::vector opengllight_array; + +// members rendermode renderpass{ rendermode::color }; - + opengllight_array m_lights; texture_manager m_textures; }; diff --git a/sky.cpp b/sky.cpp index 710adbdb..7c3aa79f 100644 --- a/sky.cpp +++ b/sky.cpp @@ -22,21 +22,22 @@ TSky::TSky(){}; void TSky::Init() { - WriteLog(Global::asSky.c_str()); - WriteLog("init"); + WriteLog( "Clouds init" ); if ((Global::asSky != "1") && (Global::asSky != "0")) - // { - mdCloud = TModelsManager::GetModel(Global::asSky.c_str()); - // } + mdCloud = TModelsManager::GetModel( Global::asSky ); }; -void TSky::Render() +void TSky::Render( float3 const &Tint ) { if (mdCloud) { // jeśli jest model nieba #ifdef EU07_USE_OLD_LIGHTING_MODEL // TODO: re-implement this glLightfv(GL_LIGHT0, GL_POSITION, lightPos); +#else + ::glEnable( GL_LIGHTING ); + GfxRenderer.Disable_Lights(); + ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, &Tint.x ); #endif if (Global::bUseVBO) { // renderowanie z VBO @@ -48,10 +49,15 @@ void TSky::Render() mdCloud->Render(100, 0); mdCloud->RenderAlpha(100, 0); } - glPopMatrix(); #ifdef EU07_USE_OLD_LIGHTING_MODEL + glPopMatrix(); // TODO: re-implement this glLightfv(GL_LIGHT0, GL_POSITION, Global::lightPos); +#else + GLfloat noambient[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, noambient ); + ::glEnable( GL_LIGHT0 ); // other lights will be enabled during lights update + ::glDisable( GL_LIGHTING ); #endif } }; diff --git a/sky.h b/sky.h index 709c2887..1117957a 100644 --- a/sky.h +++ b/sky.h @@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/. #pragma once #include "Model3d.h" +#include "Float3d.h" class TSky { @@ -21,7 +22,7 @@ class TSky TSky(); ~TSky(); void Init(); - void Render(); + void Render( float3 const &Tint = float3(1.0f, 1.0f, 1.0f) ); }; //--------------------------------------------------------------------------- diff --git a/skydome.cpp b/skydome.cpp index 7b8e8f8d..560349d1 100644 --- a/skydome.cpp +++ b/skydome.cpp @@ -61,9 +61,9 @@ CSkyDome::CSkyDome (int const Tesselation) : m_tesselation( Tesselation ) { // SetSunPosition( Math3D::vector3(75.0f, 0.0f, 0.0f) ); - SetTurbidity( 3.5f ); - SetExposure( true, 16.0f ); - SetOvercastFactor( 0.08f ); + SetTurbidity( 3.0f ); + SetExposure( true, 20.0f ); + SetOvercastFactor( 0.05f ); SetGammaCorrection( 2.2f ); Generate(); } @@ -75,7 +75,8 @@ CSkyDome::~CSkyDome() { void CSkyDome::Generate() { // radius of dome - float const radius = 1.0f; // 100.0f; + float const radius = 1.0f; + float const offset = 0.1f * radius; // horizontal offset, a cheap way to prevent a gap between ground and horizon // create geometry chunk int const latitudes = m_tesselation / 2; @@ -98,11 +99,11 @@ void CSkyDome::Generate() { float x = std::cos( longitude ); float y = std::sin( longitude ); - m_vertices.emplace_back( float3( x * zr0, y * zr0 - 0.1f, z0 ) * radius ); + m_vertices.emplace_back( float3( x * zr0, y * zr0 - offset, z0 ) * radius ); // m_normals.emplace_back( float3( -x * zr0, -y * zr0, -z0 ) ); m_colours.emplace_back( float3( 0.75f, 0.75f, 0.75f ) ); - m_vertices.emplace_back( float3( x * zr1, y * zr1 - 0.1f, z1 ) * radius ); + m_vertices.emplace_back( float3( x * zr1, y * zr1 - offset, z1 ) * radius ); // m_normals.emplace_back( float3( -x * zr1, -y * zr1, -z1 ) ); m_colours.emplace_back( float3( 0.75f, 0.75f, 0.75f ) ); } @@ -156,7 +157,7 @@ bool CSkyDome::SetSunPosition( Math3D::vector3 const &Direction ) { m_sundirection = sundirection; m_thetasun = std::acosf( m_sundirection.y ); - m_phisun = std::atan2( m_sundirection.z , m_sundirection.x ); + m_phisun = std::atan2( m_sundirection.z, m_sundirection.x ); return true; } @@ -249,7 +250,7 @@ void CSkyDome::RebuildColors() { // trough all vertices float3 vertex; - float3 color, colorconverter; + float3 color, colorconverter, shiftedcolor; for ( unsigned int i = 0; i < m_vertices.size(); ++i ) { // grab it @@ -295,9 +296,24 @@ void CSkyDome::RebuildColors() { // exp scale colorconverter.z = 1.0f - exp( -m_expfactor * colorconverter.z ); } + + // override the hue, based on sun height above the horizon. crude way to deal with model shortcomings + // correction begins when the sun is higher than 10 degrees above the horizon, and fully in effect at 10+15 degrees + auto const degreesabovehorizon = 90.0f - m_thetasun * ( 180.0f / M_PI ); + auto const sunbasedphase = clamp( (1.0f / 15.0f) * ( degreesabovehorizon - 10.0f ), 0.0f, 1.0f ); + // correction is applied in linear manner from the bottom, becomes fully in effect for vertices with y = 0.50 + auto const heightbasedphase = clamp( vertex.y * 2.0f, 0.0f, 1.0f ); + // this height-based factor is reduced the farther the sky is up in the sky + float const shiftfactor = clamp( interpolate(heightbasedphase, sunbasedphase, sunbasedphase), 0.0f, 1.0f ); + // h = 210 makes for 'typical' sky tone + shiftedcolor = float3( 210.0f, colorconverter.y, colorconverter.z ); + shiftedcolor = HSVtoRGB( shiftedcolor ); + color = HSVtoRGB(colorconverter); + + color = Interpolate( color, shiftedcolor, shiftfactor ); /* - // gamma control + // gamma control color.x = std::pow( color.x, m_gammacorrection ); color.x = std::pow( color.y, m_gammacorrection ); color.x = std::pow( color.z, m_gammacorrection ); diff --git a/skydome.h b/skydome.h index df0125e4..0fcfb329 100644 --- a/skydome.h +++ b/skydome.h @@ -42,7 +42,7 @@ private: // data int const m_tesselation; std::vector m_vertices; - std::vector m_normals; +// std::vector m_normals; std::vector m_colours; static float m_distributionluminance[ 5 ][ 2 ]; diff --git a/sun.cpp b/sun.cpp index 78fd9e4f..b21ef312 100644 --- a/sun.cpp +++ b/sun.cpp @@ -32,8 +32,8 @@ cSun::update() { move(); Math3D::vector3 position( 0.0f, 0.0f, -2000.0f ); - position.RotateX( (float)( m_body.elevref * ( M_PI / 180.0 ) ) ); - position.RotateY( (float)( ( 90.0 - m_body.hrang ) * ( M_PI / 180.0 ) ) ); + position.RotateX( (float)( m_body.elevref * ( M_PI / 180.0 ) ) ); + position.RotateY( (float)( -m_body.hrang * ( M_PI / 180.0 ) ) ); m_position = position; } @@ -69,8 +69,8 @@ Math3D::vector3 cSun::getDirection() { Math3D::vector3 position( 0.f, 0.f, -1.f ); - position.RotateX( (float)(m_body.elevref * (M_PI/180.0)) ); - position.RotateY( (float)((90.0 - m_body.hrang) * (M_PI/180.0)) ); + position.RotateX( (float)( m_body.elevref * (M_PI/180.0)) ); + position.RotateY( (float)( -m_body.hrang * (M_PI/180.0)) ); position.Normalize(); return position; }