From 846ed2139429e020b93b8e9ba47820b70b4ea723 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 10 Aug 2017 18:19:21 +0200 Subject: [PATCH] build 170810. multi-texture materials, reflection map support --- AnimModel.cpp | 17 +-- AnimModel.h | 2 +- Driver.cpp | 34 +++--- DynObj.cpp | 68 +++++------ DynObj.h | 2 +- Ground.cpp | 39 ++++-- Ground.h | 4 +- Model3d.cpp | 97 +++++++-------- Model3d.h | 20 ++-- PyInt.cpp | 25 ++-- Segment.cpp | 2 +- Sound.cpp | 40 +------ Texture.cpp | 123 ++++++++----------- Texture.h | 25 +++- Track.cpp | 60 +++++----- Track.h | 10 +- maszyna.vcxproj.filters | 6 + material.cpp | 143 ++++++++++++++++++++++ material.h | 64 ++++++++++ openglgeometrybank.cpp | 24 +++- openglgeometrybank.h | 3 +- renderer.cpp | 257 +++++++++++++++++++++++++--------------- renderer.h | 42 ++++--- uilayer.cpp | 6 +- version.h | 4 +- 25 files changed, 687 insertions(+), 430 deletions(-) create mode 100644 material.cpp create mode 100644 material.h diff --git a/AnimModel.cpp b/AnimModel.cpp index 02e33d9c..09a53eb9 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -440,14 +440,15 @@ bool TAnimModel::Init(TModel3d *pNewModel) bool TAnimModel::Init(std::string const &asName, std::string const &asReplacableTexture) { - if (asReplacableTexture.substr(0, 1) == - "*") // od gwiazdki zaczynają się teksty na wyświetlaczach - asText = asReplacableTexture.substr(1, asReplacableTexture.length() - 1); // zapamiętanie tekstu - else if (asReplacableTexture != "none") - m_materialdata.replacable_skins[1] = - GfxRenderer.Fetch_Texture( asReplacableTexture, "" ); - if( ( m_materialdata.replacable_skins[ 1 ] != 0 ) - && ( GfxRenderer.Texture( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) ) { + if( asReplacableTexture.substr( 0, 1 ) == "*" ) { + // od gwiazdki zaczynają się teksty na wyświetlaczach + asText = asReplacableTexture.substr( 1, asReplacableTexture.length() - 1 ); // zapamiętanie tekstu + } + else if( asReplacableTexture != "none" ) { + m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Material( asReplacableTexture ); + } + if( ( m_materialdata.replacable_skins[ 1 ] != NULL ) + && ( GfxRenderer.Material( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) ) { // tekstura z kanałem alfa - nie renderować w cyklu nieprzezroczystych m_materialdata.textures_alpha = 0x31310031; } diff --git a/AnimModel.h b/AnimModel.h index 73bad8f7..cd5018ef 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -15,7 +15,7 @@ http://mozilla.org/MPL/2.0/. #pragma once #include "Model3d.h" -#include "Texture.h" +#include "material.h" #include "DynObj.h" const int iMaxNumLights = 8; diff --git a/Driver.cpp b/Driver.cpp index 185affc7..d8f60a17 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -4591,20 +4591,20 @@ bool TController::UpdateSituation(double dt) // if not MinVelFlag) if (fBrakeTime < 0 ? true : (AccDesired < fAccGravity - 0.3) || (mvOccupied->BrakeCtrlPos <= 0)) - if (!IncBrake()) // jeśli upłynął czas reakcji hamulca, chyba że - // nagłe albo luzował + if( false == IncBrake() ) { + // jeśli upłynął czas reakcji hamulca, chyba że nagłe albo luzował MinVelFlag = true; - else - { + } + else { MinVelFlag = false; fBrakeTime = - 3 + - 0.5 * - (mvOccupied - ->BrakeDelay[2 + 2 * mvOccupied->BrakeDelayFlag] - - 3); - // Ra: ten czas należy zmniejszyć, jeśli czas dojazdu do - // zatrzymania jest mniejszy + 3.0 + + 0.5 * ( ( + mvOccupied->BrakeDelayFlag > bdelay_G ? + mvOccupied->BrakeDelay[ 1 ] : + mvOccupied->BrakeDelay[ 3 ] ) + - 3.0 ); + // Ra: ten czas należy zmniejszyć, jeśli czas dojazdu do zatrzymania jest mniejszy fBrakeTime *= 0.5; // Ra: tymczasowo, bo przeżyna S1 } if ((AccDesired < fAccGravity - 0.05) && (AbsAccS < AccDesired - 0.2)) @@ -4613,11 +4613,15 @@ bool TController::UpdateSituation(double dt) // if (fBrakeTime<0) { // jak hamuje, to nie tykaj kranu za często // yB: luzuje hamulec dopiero przy różnicy opóźnień rzędu 0.2 - if (OrderList[OrderPos] != - Disconnect) // przy odłączaniu nie zwalniamy tu hamulca + if( OrderList[ OrderPos ] != Disconnect ) { + // przy odłączaniu nie zwalniamy tu hamulca DecBrake(); // tutaj zmniejszało o 1 przy odczepianiu - fBrakeTime = - (mvOccupied->BrakeDelay[1 + 2 * mvOccupied->BrakeDelayFlag]) / 3.0; + } + fBrakeTime = ( + mvOccupied->BrakeDelayFlag > bdelay_G ? + mvOccupied->BrakeDelay[ 0 ] : + mvOccupied->BrakeDelay[ 2 ] ) + / 3.0; fBrakeTime *= 0.5; // Ra: tymczasowo, bo przeżyna S1 } } diff --git a/DynObj.cpp b/DynObj.cpp index 0f670004..01f263e6 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -4036,7 +4036,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, { std::string nowheretexture = TextureTest(Global::asCurrentTexturePath + "nowhere"); // na razie prymitywnie if( false == nowheretexture.empty() ) { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( nowheretexture, "", 9 ); + m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Material( nowheretexture ); } if (m_materialdata.multi_textures > 0) { @@ -4048,7 +4048,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, int skinindex = 0; std::string texturename; nameparser >> texturename; while( ( texturename != "" ) && ( skinindex < 4 ) ) { - m_materialdata.replacable_skins[ skinindex + 1 ] = GfxRenderer.Fetch_Texture( Global::asCurrentTexturePath + texturename, "" ); + m_materialdata.replacable_skins[ skinindex + 1 ] = GfxRenderer.Fetch_Material( Global::asCurrentTexturePath + texturename ); ++skinindex; texturename = ""; nameparser >> texturename; } @@ -4058,24 +4058,24 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, // otherwise try the basic approach int skinindex = 0; do { - texture_handle texture = GfxRenderer.Fetch_Texture( Global::asCurrentTexturePath + ReplacableSkin + "," + std::to_string( skinindex + 1 ), "", Global::iDynamicFiltering, true ); - if( texture == NULL ) { + material_handle material = GfxRenderer.Fetch_Material( Global::asCurrentTexturePath + ReplacableSkin + "," + std::to_string( skinindex + 1 ), true ); + if( material == NULL ) { break; } - m_materialdata.replacable_skins[ skinindex + 1 ] = texture; + m_materialdata.replacable_skins[ skinindex + 1 ] = material; ++skinindex; } while( skinindex < 4 ); m_materialdata.multi_textures = skinindex; if( m_materialdata.multi_textures == 0 ) { // zestaw nie zadziałał, próbujemy normanie - m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Texture( Global::asCurrentTexturePath + ReplacableSkin, "", Global::iDynamicFiltering ); + m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Material( Global::asCurrentTexturePath + ReplacableSkin ); } } } else { - m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Texture( Global::asCurrentTexturePath + ReplacableSkin, "", Global::iDynamicFiltering ); + m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Material( Global::asCurrentTexturePath + ReplacableSkin ); } - if( GfxRenderer.Texture( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) { + if( GfxRenderer.Material( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) { // tekstura -1 z kanałem alfa - nie renderować w cyklu nieprzezroczystych m_materialdata.textures_alpha = 0x31310031; } @@ -4085,17 +4085,17 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, } if( ( m_materialdata.replacable_skins[ 2 ] ) - && ( GfxRenderer.Texture( m_materialdata.replacable_skins[ 2 ] ).has_alpha ) ) { + && ( GfxRenderer.Material( m_materialdata.replacable_skins[ 2 ] ).has_alpha ) ) { // tekstura -2 z kanałem alfa - nie renderować w cyklu nieprzezroczystych m_materialdata.textures_alpha |= 0x02020002; } if( ( m_materialdata.replacable_skins[ 3 ] ) - && ( GfxRenderer.Texture( m_materialdata.replacable_skins[ 3 ] ).has_alpha ) ) { + && ( GfxRenderer.Material( m_materialdata.replacable_skins[ 3 ] ).has_alpha ) ) { // tekstura -3 z kanałem alfa - nie renderować w cyklu nieprzezroczystych m_materialdata.textures_alpha |= 0x04040004; } if( ( m_materialdata.replacable_skins[ 4 ] ) - && ( GfxRenderer.Texture( m_materialdata.replacable_skins[ 4 ] ).has_alpha ) ) { + && ( GfxRenderer.Material( m_materialdata.replacable_skins[ 4 ] ).has_alpha ) ) { // tekstura -4 z kanałem alfa - nie renderować w cyklu nieprzezroczystych m_materialdata.textures_alpha |= 0x08080008; } @@ -5371,7 +5371,7 @@ int TDynamicObject::RouteWish(TTrack *tr) std::string TDynamicObject::TextureTest(std::string const &name) { // Ra 2015-01: sprawdzenie dostępności tekstury o podanej nazwie - std::vector extensions = { ".dds", ".tga", ".bmp" }; + std::vector extensions = { ".mat", ".dds", ".tga", ".bmp" }; for( auto const &extension : extensions ) { if( true == FileExists( name + extension ) ) { return name + extension; @@ -5393,36 +5393,22 @@ void TDynamicObject::DestinationSet(std::string to, std::string numer) numer = Global::Bezogonkow(numer); asDestination = to; to = Global::Bezogonkow(to); // do szukania pliku obcinamy ogonki - std::string x = TextureTest(asBaseDir + numer + "@" + MoverParameters->TypeName); - if (!x.empty()) - { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); // rozmywania 0,1,4,5 nie nadają się - return; + + std::vector destinations = { + asBaseDir + numer + "@" + MoverParameters->TypeName, + asBaseDir + numer, + asBaseDir + to + "@" + MoverParameters->TypeName, + asBaseDir + to, + asBaseDir + "nowhere" }; + + for( auto const &destination : destinations ) { + + auto material = TextureTest( destination ); + if( false == material.empty() ) { + m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Material( material ); + break; + } } - x = TextureTest(asBaseDir + numer ); - if (!x.empty()) - { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); // rozmywania 0,1,4,5 nie nadają się - return; - } - if (to.empty()) - to = "nowhere"; - x = TextureTest(asBaseDir + to + "@" + MoverParameters->TypeName); // w pierwszej kolejności z nazwą FIZ/MMD - if (!x.empty()) - { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); // rozmywania 0,1,4,5 nie nadają się - return; - } - x = TextureTest(asBaseDir + to); // na razie prymitywnie - if (!x.empty()) - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); // rozmywania 0,1,4,5 nie nadają się - else - { - x = TextureTest(asBaseDir + "nowhere"); // jak nie znalazł dedykowanej, to niech daje nowhere - if (!x.empty()) - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); - } - // Ra 2015-01: żeby zalogować błąd, trzeba by mieć pewność, że model używa tekstury nr 4 }; void TDynamicObject::OverheadTrack(float o) diff --git a/DynObj.h b/DynObj.h index 3509696a..f4b7c329 100644 --- a/DynObj.h +++ b/DynObj.h @@ -143,7 +143,7 @@ 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 ] = { NULL, NULL, NULL, NULL, NULL }; // McZapkie:zmienialne nadwozie + material_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 }; diff --git a/Ground.cpp b/Ground.cpp index 57b76e36..d4725ff4 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -67,7 +67,7 @@ TGroundNode::TGroundNode() nNext = nNext2 = NULL; iCount = 0; // wierzchołków w trójkącie // iNumPts=0; //punktów w linii - TextureID = 0; + m_material = 0; iFlags = 0; // tryb przezroczystości nie zbadany Pointer = NULL; // zerowanie wskaźnika kontekstowego bVisible = false; // czy widoczny @@ -508,7 +508,7 @@ 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 ) + return ( ( Left.m_material == Right.m_material ) && ( Left.Ambient == Right.Ambient ) && ( Left.Diffuse == Right.Diffuse ) && ( Left.Specular == Right.Specular ) ); @@ -776,7 +776,7 @@ void TGround::RaTriangleDivider(TGroundNode *node) TGroundNode *ntri; // wskaźnik na nowy trójkąt ntri = new TGroundNode(GL_TRIANGLES); // a ten jest nowy // kopiowanie parametrów, przydałby się konstruktor kopiujący - ntri->TextureID = node->TextureID; + ntri->m_material = node->m_material; ntri->iFlags = node->iFlags; ntri->Ambient = node->Ambient; ntri->Diffuse = node->Diffuse; @@ -1376,23 +1376,36 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) *parser >> token; } str = token; - tmp->TextureID = GfxRenderer.Fetch_Texture( str ); + tmp->m_material = GfxRenderer.Fetch_Material( str ); + auto const texturehandle = ( + tmp->m_material != NULL ? + GfxRenderer.Material( tmp->m_material ).texture1 : + NULL ); + auto const &texture = ( + texturehandle ? + GfxRenderer.Texture( texturehandle ) : + opengl_texture() ); // dirty workaround for lack of better api bool const clamps = ( - tmp->TextureID ? - GfxRenderer.Texture( tmp->TextureID ).traits.find( 's' ) != std::string::npos : + texturehandle ? + texture.traits.find( 's' ) != std::string::npos : false ); bool const clampt = ( - tmp->TextureID ? - GfxRenderer.Texture( tmp->TextureID ).traits.find( 't' ) != std::string::npos : + texturehandle ? + texture.traits.find( 't' ) != std::string::npos : false ); tmp->iFlags |= 200; // z usuwaniem // remainder of legacy 'problend' system -- geometry assigned a texture with '@' in its name is treated as translucent, opaque otherwise - tmp->iFlags |= ( - ( ( str.find( '@' ) != std::string::npos ) - && ( true == GfxRenderer.Texture( tmp->TextureID ).has_alpha ) ) ? - 0x20 : - 0x10 ); + if( texturehandle != NULL ) { + tmp->iFlags |= ( + ( ( texture.name.find( '@' ) != std::string::npos ) + && ( true == texture.has_alpha ) ) ? + 0x20 : + 0x10 ); + } + else { + tmp->iFlags |= 0x10; + } TGroundVertex vertex, vertex1, vertex2; std::size_t vertexcount{ 0 }; diff --git a/Ground.h b/Ground.h index d8b02ad9..13bc46cd 100644 --- a/Ground.h +++ b/Ground.h @@ -15,7 +15,7 @@ http://mozilla.org/MPL/2.0/. #include "VBO.h" #include "Classes.h" #include "ResourceManager.h" -#include "Texture.h" +#include "material.h" #include "dumb3d.h" #include "Float3d.h" #include "Names.h" @@ -140,7 +140,7 @@ public: int iCount; // dla terenu }; int iFlags; // tryb przezroczystości: 0x10-nieprz.,0x20-przezroczysty,0x30-mieszany - texture_handle TextureID; // główna (jedna) tekstura obiektu + material_handle m_material; // główna (jedna) tekstura obiektu glm::vec3 Ambient{ 1.0f, 1.0f, 1.0f }, Diffuse{ 1.0f, 1.0f, 1.0f }, diff --git a/Model3d.cpp b/Model3d.cpp index 2d1442af..be23304f 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -63,16 +63,16 @@ TSubModel::~TSubModel() // wyświetlania }; -void TSubModel::TextureNameSet(std::string const &Name) +void TSubModel::Name_Material(std::string const &Name) { // ustawienie nazwy submodelu, o // ile nie jest wczytany z E3D if (iFlags & 0x0200) { // tylko jeżeli submodel zosta utworzony przez new - pTexture = Name; + m_materialname = Name; } }; -void TSubModel::NameSet(std::string const &Name) +void TSubModel::Name(std::string const &Name) { // ustawienie nazwy submodelu, o ile // nie jest wczytany z E3D if (iFlags & 0x0200) @@ -177,7 +177,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic std::string token; parser.getTokens(1, false); // nazwa submodelu bez zmieny na małe parser >> token; - NameSet(token); + Name(token); if (dynamic) { // dla pojazdu, blokujemy załączone submodele, które mogą być nieobsługiwane if( ( token.size() >= 3 ) @@ -305,50 +305,51 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic */ if (!parser.expectToken("map:")) Error("Model map parse failure!"); - std::string texture = parser.getToken(); - if (texture == "none") + std::string material = parser.getToken(); + if (material == "none") { // rysowanie podanym kolorem - TextureID = 0; + m_material = NULL; iFlags |= 0x10; // rysowane w cyklu nieprzezroczystych } - else if (texture.find("replacableskin") != texture.npos) + else if (material.find("replacableskin") != material.npos) { // McZapkie-060702: zmienialne skory modelu - TextureID = -1; + m_material = -1; iFlags |= (Opacity < 1.0) ? 1 : 0x10; // zmienna tekstura 1 } - else if (texture == "-1") + else if (material == "-1") { - TextureID = -1; + m_material = -1; iFlags |= (Opacity < 1.0) ? 1 : 0x10; // zmienna tekstura 1 } - else if (texture == "-2") + else if (material == "-2") { - TextureID = -2; + m_material = -2; iFlags |= (Opacity < 1.0) ? 2 : 0x10; // zmienna tekstura 2 } - else if (texture == "-3") + else if (material == "-3") { - TextureID = -3; + m_material = -3; iFlags |= (Opacity < 1.0) ? 4 : 0x10; // zmienna tekstura 3 } - else if (texture == "-4") + else if (material == "-4") { - TextureID = -4; + m_material = -4; iFlags |= (Opacity < 1.0) ? 8 : 0x10; // zmienna tekstura 4 } else { // jeśli tylko nazwa pliku, to dawać bieżącą ścieżkę do tekstur - TextureNameSet(texture); - if( texture.find_first_of( "/\\" ) == texture.npos ) { - texture.insert( 0, Global::asCurrentTexturePath ); + Name_Material(material); + if( material.find_first_of( "/\\" ) == material.npos ) { + material.insert( 0, Global::asCurrentTexturePath ); } - TextureID = GfxRenderer.Fetch_Texture( texture ); + m_material = GfxRenderer.Fetch_Material( material ); // renderowanie w cyklu przezroczystych tylko jeśli: // 1. Opacity=0 (przejściowo <1, czy tam <100) oraz // 2. tekstura ma przezroczystość iFlags |= ( ( ( Opacity < 1.0 ) - && ( GfxRenderer.Texture(TextureID).has_alpha ) ) ? + && ( ( m_material != NULL ) + && ( GfxRenderer.Material( m_material ).has_alpha ) ) ) ? 0x20 : 0x10 ); // 0x10-nieprzezroczysta, 0x20-przezroczysta }; @@ -591,10 +592,10 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic return iNumVerts; // do określenia wielkości VBO }; -int TSubModel::TriangleAdd(TModel3d *m, texture_handle tex, int tri) +int TSubModel::TriangleAdd(TModel3d *m, material_handle tex, int tri) { // dodanie trójkątów do submodelu, używane przy tworzeniu E3D terenu TSubModel *s = this; - while (s ? (s->TextureID != tex) : false) + while (s ? (s->m_material != tex) : false) { // szukanie submodelu o danej teksturze if (s == this) s = Child; @@ -603,15 +604,15 @@ int TSubModel::TriangleAdd(TModel3d *m, texture_handle tex, int tri) } if (!s) { - if (TextureID <= 0) + if (m_material <= 0) s = this; // użycie głównego else { // dodanie nowego submodelu do listy potomnych s = new TSubModel(); m->AddTo(this, s); } - s->TextureNameSet(GfxRenderer.Texture(tex).name); - s->TextureID = tex; + s->Name_Material(GfxRenderer.Material(tex).name); + s->m_material = tex; s->eType = GL_TRIANGLES; } if (s->iNumVerts < 0) @@ -672,7 +673,7 @@ void TSubModel::DisplayLists() glVertex3dv(&Vertices[i].Point.x); */ glNormal3fv(glm::value_ptr(Vertices[i].normal)); - glTexCoord2fv(glm::value_ptr(Vertices[i].texture)); + glTexCoord2fv(glm::value_ptr(Vertices[i].material)); glVertex3fv(glm::value_ptr(Vertices[i].position)); }; glEnd(); @@ -806,8 +807,8 @@ int TSubModel::FlagsCheck() int i = 0; if (Child) { // Child jest renderowany po danym submodelu - if (Child->TextureID) // o ile ma teksturę - if (Child->TextureID != TextureID) // i jest ona inna niż rodzica + if (Child->m_material) // o ile ma teksturę + if (Child->m_material != m_material) // i jest ona inna niż rodzica Child->iFlags |= 0x80; // to trzeba sprawdzać, jak z teksturami jest i = Child->FlagsCheck(); iFlags |= 0x00FF0000 & ((i << 16) | (i) | (i >> 8)); // potomny, rodzeństwo i dzieci @@ -825,8 +826,8 @@ int TSubModel::FlagsCheck() if (Next) { // Next jest renderowany po danym submodelu (kolejność odwrócona // po wczytaniu T3D) - if (TextureID) // o ile dany ma teksturę - if ((TextureID != Next->TextureID) || + if (m_material) // o ile dany ma teksturę + if ((m_material != Next->m_material) || (i & 0x00800000)) // a ma inną albo dzieci zmieniają iFlags |= 0x80; // to dany submodel musi sobie ją ustawiać i = Next->FlagsCheck(); @@ -1085,9 +1086,9 @@ TSubModel::convert( TGroundNode &Groundnode ) const { Groundnode.Ambient = f4Ambient; Groundnode.Diffuse = f4Diffuse; Groundnode.Specular = f4Specular; - Groundnode.TextureID = TextureID; + Groundnode.m_material = m_material; Groundnode.iFlags = ( - ( true == GfxRenderer.Texture( TextureID ).has_alpha ) ? + ( true == GfxRenderer.Material( m_material ).has_alpha ) ? 0x20 : 0x10 ); @@ -1379,10 +1380,10 @@ void TSubModel::serialize(std::ostream &s, sn_utils::ls_int32(s, iNumVerts); sn_utils::ls_int32(s, tVboPtr); - if (TextureID <= 0) - sn_utils::ls_int32(s, TextureID); + if (m_material <= 0) + sn_utils::ls_int32(s, m_material); else - sn_utils::ls_int32(s, (int32_t)get_container_pos(textures, pTexture)); + sn_utils::ls_int32(s, (int32_t)get_container_pos(textures, m_materialname)); sn_utils::ls_float32(s, fVisible); sn_utils::ls_float32(s, fLight); @@ -1727,27 +1728,29 @@ void TSubModel::BinInit(TSubModel *s, float4x4 *m, std::vector *t, pName = ""; if (iTexture > 0) { // obsługa stałej tekstury - auto const textureindex = static_cast( iTexture ); - if( textureindex < t->size() ) { - pTexture = t->at( textureindex ); - if( pTexture.find_last_of( "/\\" ) == std::string::npos ) - pTexture.insert( 0, Global::asCurrentTexturePath ); - TextureID = GfxRenderer.Fetch_Texture( pTexture ); + auto const materialindex = static_cast( iTexture ); + if( materialindex < t->size() ) { + m_materialname = t->at( materialindex ); + if( m_materialname.find_last_of( "/\\" ) == std::string::npos ) { + m_materialname = Global::asCurrentTexturePath + m_materialname; + } + m_material = GfxRenderer.Fetch_Material( m_materialname ); if( ( iFlags & 0x30 ) == 0 ) { // texture-alpha based fallback if for some reason we don't have opacity flag set yet - iFlags |= - ( GfxRenderer.Texture( TextureID ).has_alpha ? + iFlags |= ( + ( ( m_material != NULL ) + && ( GfxRenderer.Material( m_material ).has_alpha ) ) ? 0x20 : 0x10 ); // 0x10-nieprzezroczysta, 0x20-przezroczysta } } else { ErrorLog( "Bad model: reference to non-existent texture index in sub-model" + ( pName.empty() ? "" : " \"" + pName + "\"" ) ); - TextureID = NULL; + m_material = NULL; } } else - TextureID = iTexture; + m_material = iTexture; b_aAnim = b_Anim; // skopiowanie animacji do drugiego cyklu diff --git a/Model3d.h b/Model3d.h index 4e72b3c4..17efe8d2 100644 --- a/Model3d.h +++ b/Model3d.h @@ -14,7 +14,7 @@ http://mozilla.org/MPL/2.0/. #include "dumb3d.h" #include "Float3d.h" #include "VBO.h" -#include "Texture.h" +#include "material.h" using namespace Math3D; @@ -122,7 +122,7 @@ private: 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 + material_handle m_material { NULL }; // numer tekstury, -1 wymienna, 0 brak bool bWire { false }; // nie używane, ale wczytywane float Opacity { 1.0f }; float f_Angle { 0.0f }; @@ -143,7 +143,7 @@ public: TSubModel **smLetter{ nullptr }; // wskaźnik na tablicę submdeli do generoania tekstu (docelowo zapisać do E3D) TSubModel *Parent{ nullptr }; // nadrzędny, np. do wymnażania macierzy int iVisible{ 1 }; // roboczy stan widoczności - std::string pTexture; // robocza nazwa tekstury do zapisania w pliku binarnym + std::string m_materialname; // robocza nazwa tekstury do zapisania w pliku binarnym std::string pName; // robocza nazwa private: int SeekFaceNormal( std::vector const &Masks, int const Startface, unsigned int const Mask, glm::vec3 const &Position, vertex_array const &Vertices ); @@ -151,7 +151,7 @@ private: public: static size_t iInstance; // identyfikator egzemplarza, który aktualnie renderuje model - static texture_handle const *ReplacableSkinId; + static material_handle const *ReplacableSkinId; static int iAlpha; // maska bitowa dla danego przebiegu static double fSquareDist; static TModel3d *pRoot; @@ -162,7 +162,7 @@ public: void NextAdd(TSubModel *SubModel); TSubModel * NextGet() { return Next; }; TSubModel * ChildGet() { return Child; }; - int TriangleAdd(TModel3d *m, texture_handle tex, int tri); + int TriangleAdd(TModel3d *m, material_handle tex, int tri); /* basic_vertex * TrianglePtr(int tex, int pos, glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ); */ @@ -186,13 +186,13 @@ public: }; void InitialRotate(bool doit); void BinInit(TSubModel *s, float4x4 *m, std::vector *t, std::vector *n, bool dynamic); - void ReplacableSet(texture_handle const *r, int a) + void ReplacableSet(material_handle const *r, int a) { ReplacableSkinId = r; iAlpha = a; }; - void TextureNameSet( std::string const &Name ); - void NameSet( std::string const &Name ); + void Name_Material( std::string const &Name ); + void Name( std::string const &Name ); // Ra: funkcje do budowania terenu z E3D int Flags() { return iFlags; }; void UnFlagNext() { iFlags &= 0x00FFFFFF; }; @@ -203,8 +203,8 @@ public: return fMatrix ? *(fMatrix->TranslationGet()) + v_TransVector : v_TransVector; } inline float3 Translation2Get() { return *(fMatrix->TranslationGet()) + Child->Translation1Get(); } - int GetTextureId() { - return TextureID; } + material_handle GetMaterial() { + return m_material; } void ParentMatrix(float4x4 *m); float MaxY( float4x4 const &m ); void AdjustDist(); diff --git a/PyInt.cpp b/PyInt.cpp index 8a6a74b4..5203afbb 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -291,21 +291,16 @@ void TPythonScreenRenderer::updateTexture() sprintf(buff, "Sending texture id: %d w: %d h: %d", _textureId, width, height); WriteLog(buff); #endif // _PY_INT_MORE_LOG - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - GfxRenderer.Bind(_textureId); +/* + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glPixelStorei( GL_PACK_ALIGNMENT, 1 ); +*/ + GfxRenderer.Bind_Material(_textureId); // setup texture parameters - if( GLEW_VERSION_1_4 ) { - - glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); - glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0 ); - } - else { - - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - } + glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); + glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0 ); // build texture glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData ); @@ -457,7 +452,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 } - auto textureId = subModel->GetTextureId(); + auto textureId = subModel->GetMaterial(); if (textureId <= 0) { WriteLog( "Python Screen: invalid texture id " + std::to_string(textureId) + " - Ignoring screen" ); diff --git a/Segment.cpp b/Segment.cpp index ea9a8e6b..a6b84785 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -454,7 +454,7 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, void TSegment::Render() { vector3 pt; - GfxRenderer.Bind(0); + GfxRenderer.Bind_Material( NULL ); if (bCurve) { diff --git a/Sound.cpp b/Sound.cpp index dc559fe4..71198f62 100644 --- a/Sound.cpp +++ b/Sound.cpp @@ -244,50 +244,14 @@ bool TSoundsManager::Init(HWND hWnd) { pDS = nullptr; pDSNotify = nullptr; - HRESULT hr; //=222; - LPDIRECTSOUNDBUFFER pDSBPrimary = nullptr; - // Create IDirectSound using the primary sound device - hr = DirectSoundCreate(NULL, &pDS, NULL); + auto hr = ::DirectSoundCreate(NULL, &pDS, NULL); if (hr != DS_OK) { return false; }; - // Set coop level to DSSCL_PRIORITY - // if( FAILED( hr = pDS->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ) ) ); - // return ; - pDS->SetCooperativeLevel(hWnd, DSSCL_PRIORITY); - - // Get the primary buffer - DSBUFFERDESC dsbd; - ZeroMemory(&dsbd, sizeof(DSBUFFERDESC)); - dsbd.dwSize = sizeof(DSBUFFERDESC); - dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; - if (!Global::bInactivePause) // jeśli przełączony w tło ma nadal działać - dsbd.dwFlags |= DSBCAPS_GLOBALFOCUS; // to dźwięki mają być również słyszalne - dsbd.dwBufferBytes = 0; - dsbd.lpwfxFormat = NULL; - - if( FAILED( hr = pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) ) { - return false; - } - - // Set primary buffer format to 22kHz and 16-bit output. - WAVEFORMATEX wfx; - ZeroMemory(&wfx, sizeof(WAVEFORMATEX)); - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nChannels = 2; - wfx.nSamplesPerSec = 44100; - wfx.wBitsPerSample = 16; - wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; - wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; - - if( FAILED( hr = pDSBPrimary->SetFormat( &wfx ) ) ) { - return false; - } - - SAFE_RELEASE(pDSBPrimary); + pDS->SetCooperativeLevel(hWnd, DSSCL_NORMAL); return true; }; diff --git a/Texture.cpp b/Texture.cpp index ce557936..1f603e97 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -506,18 +506,15 @@ opengl_texture::load_TGA() { return; } -resource_state +bool opengl_texture::bind() { - if( false == is_ready ) { - - create(); - if( data_state != resource_state::good ) { - return data_state; - } + if( ( false == is_ready ) + && ( false == create() ) ) { + return false; } ::glBindTexture( GL_TEXTURE_2D, id ); - return data_state; + return true; } bool @@ -645,40 +642,7 @@ opengl_texture::set_filtering() { switch( trait ) { case '#': { sharpen = true; break; } -/* - // legacy filter modes. TODO, TBD: get rid of them? - // let's just turn them off and see if anyone notices. - case '4': { - // najbliższy z tekstury - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - break; - } - case '5': { - //średnia z tekstury - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - break; - } - case '6': { - // najbliższy z mipmapy - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST ); - break; - } - case '7': { - //średnia z mipmapy - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST ); - break; - } - case '8': { - // najbliższy z dwóch mipmap - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR ); - break; - } - case '9': { - //średnia z dwóch mipmap - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); - break; - } -*/ + default: { break; } } } @@ -718,9 +682,27 @@ opengl_texture::downsize( GLuint const Format ) { }; } +void +texture_manager::assign_units( GLint const Helper, GLint const Shadows, GLint const Normals, GLint const Diffuse ) { + + m_units[ 0 ].unit = Helper; + m_units[ 1 ].unit = Shadows; + m_units[ 2 ].unit = Normals; + m_units[ 3 ].unit = Diffuse; +} + +void +texture_manager::unit( GLint const Textureunit ) { + + if( m_activeunit == Textureunit ) { return; } + + m_activeunit = Textureunit; + ::glActiveTexture( Textureunit ); +} + // ustalenie numeru tekstury, wczytanie jeśli jeszcze takiej nie było texture_handle -texture_manager::create( std::string Filename, std::string const &Dir, int const Filter, bool const Loadnow ) { +texture_manager::create( std::string Filename, bool const Loadnow ) { if( Filename.find( '|' ) != std::string::npos ) Filename.erase( Filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury @@ -801,11 +783,7 @@ texture_manager::create( std::string Filename, std::string const &Dir, int const auto texture = new opengl_texture(); texture->name = filename; - if( ( Filter > 0 ) && ( Filter < 10 ) ) { - // temporary. TODO, TBD: check how it's used and possibly get rid of it - traits += std::to_string( ( Filter < 4 ? Filter + 4 : Filter ) ); - } - if( Filename.find('#') !=std::string::npos ) { + if( Filename.find('#') != std::string::npos ) { // temporary code for legacy assets -- textures with names beginning with # are to be sharpened traits += '#'; } @@ -830,33 +808,37 @@ texture_manager::create( std::string Filename, std::string const &Dir, int const }; void -texture_manager::bind( texture_handle const Texture ) { +texture_manager::bind( std::size_t const Unit, texture_handle const Texture ) { m_textures[ Texture ].second = m_garbagecollector.timestamp(); - if( ( Texture != 0 ) && ( Texture == m_activetexture ) ) { + if( Texture == m_units[ Unit ].texture ) { // don't bind again what's already active return; } - // TODO: do binding in texture object, add support for other types - if( Texture != 0 ) { + // TBD, TODO: do binding in texture object, add support for other types than 2d + if( m_units[ Unit ].unit == NULL ) { return; } + unit( m_units[ Unit ].unit ); + if( Texture != NULL ) { #ifndef EU07_DEFERRED_TEXTURE_UPLOAD // NOTE: we could bind dedicated 'error' texture here if the id isn't valid ::glBindTexture( GL_TEXTURE_2D, texture(Texture).id ); - m_activetexture = Texture; + m_units[ Unit ].texture = Texture; #else - if( texture( Texture ).bind() == resource_state::good ) { - m_activetexture = Texture; + if( true == texture( Texture ).bind() ) { + m_units[ Unit ].texture = Texture; } else { // TODO: bind a special 'error' texture on failure + ::glBindTexture( GL_TEXTURE_2D, NULL ); + m_units[ Unit ].texture = NULL; } #endif } else { - ::glBindTexture( GL_TEXTURE_2D, 0 ); - m_activetexture = 0; + ::glBindTexture( GL_TEXTURE_2D, NULL ); + m_units[ Unit ].texture = NULL; } // all done return; @@ -879,7 +861,9 @@ void texture_manager::update() { if( m_garbagecollector.sweep() > 0 ) { - m_activetexture = -1; + for( auto &unit : m_units ) { + unit.texture = -1; + } } } @@ -943,21 +927,10 @@ texture_manager::find_in_databank( std::string const &Texturename ) const { std::string texture_manager::find_on_disk( std::string const &Texturename ) const { - { - std::ifstream file( Texturename ); - if( true == file.is_open() ) { - // success - return Texturename; - } - } - // if we fail make a last ditch attempt in the default textures directory - { - std::ifstream file( szTexturePath + Texturename ); - if( true == file.is_open() ) { - // success - return szTexturePath + Texturename; - } - } - // no results either way, report failure - return ""; + return( + FileExists( Texturename ) ? Texturename : + FileExists( szTexturePath + Texturename ) ? szTexturePath + Texturename : + "" ); } + +//--------------------------------------------------------------------------- diff --git a/Texture.h b/Texture.h index 69119271..41d1407e 100644 --- a/Texture.h +++ b/Texture.h @@ -31,7 +31,7 @@ struct opengl_texture { // methods void load(); - resource_state + bool bind(); bool create(); @@ -85,12 +85,17 @@ public: texture_manager(); ~texture_manager() { delete_textures(); } - texture_handle - create( std::string Filename, std::string const &Dir, int const Filter = -1, bool const Loadnow = true ); void - bind( texture_handle const Texture ); + assign_units( GLint const Helper, GLint const Shadows, GLint const Normals, GLint const Diffuse ); + void + unit( GLint const Textureunit ); + texture_handle + create( std::string Filename, bool const Loadnow = true ); + // binds specified texture to specified texture unit + void + bind( std::size_t const Unit, texture_handle const Texture ); opengl_texture & - texture( texture_handle const Texture ) { return *(m_textures[ Texture ].first); } + texture( texture_handle const Texture ) const { return *(m_textures[ Texture ].first); } // performs a resource sweep void update(); @@ -108,6 +113,11 @@ private: typedef std::unordered_map index_map; + struct texture_unit { + GLint unit { NULL }; + texture_handle texture { NULL }; // current (most recently bound) texture + }; + // methods: // checks whether specified texture is in the texture bank. returns texture id, or npos. texture_handle @@ -123,7 +133,8 @@ private: texturetimepointpair_sequence m_textures; index_map m_texturemappings; garbage_collector m_garbagecollector { m_textures, 600, 60, "texture" }; - texture_handle m_activetexture { 0 }; // last i.e. currently bound texture + std::array m_units; + GLint m_activeunit { NULL }; }; // reduces provided data image to half of original size, using basic 2x2 average @@ -182,3 +193,5 @@ downsample( std::size_t const Width, std::size_t const Height, char *Imagedata ) } } } + +//--------------------------------------------------------------------------- diff --git a/Track.cpp b/Track.cpp index 27d41f9d..da96f6d2 100644 --- a/Track.cpp +++ b/Track.cpp @@ -483,20 +483,20 @@ void TTrack::Load(cParser *parser, vector3 pOrigin, std::string name) { parser->getTokens(); *parser >> str; // railtex - TextureID1 = ( + m_material1 = ( str == "none" ? NULL : - GfxRenderer.Fetch_Texture( str ) ); + GfxRenderer.Fetch_Material( str ) ); parser->getTokens(); *parser >> fTexLength; // tex tile length if (fTexLength < 0.01) fTexLength = 4; // Ra: zabezpiecznie przed zawieszeniem parser->getTokens(); *parser >> str; // sub || railtex - TextureID2 = ( + m_material2 = ( str == "none" ? NULL : - GfxRenderer.Fetch_Texture( str ) ); + GfxRenderer.Fetch_Material( str ) ); parser->getTokens(3); *parser >> fTexHeight1 >> fTexWidth >> fTexSlope; if (iCategoryFlag & 4) @@ -547,15 +547,15 @@ void TTrack::Load(cParser *parser, vector3 pOrigin, std::string name) SwitchExtension->Segments[0]->Init(p1, p2, segsize); // kopia oryginalnego toru } else if (iCategoryFlag & 2) - if (TextureID1 && fTexLength) + if (m_material1 && fTexLength) { // dla drogi trzeba ustalić proporcje boków nawierzchni float w, h; - GfxRenderer.Bind(TextureID1); + GfxRenderer.Bind_Material(m_material1); glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); if (h != 0.0) fTexRatio1 = w / h; // proporcja boków - GfxRenderer.Bind(TextureID2); + GfxRenderer.Bind_Material(m_material2); glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); if (h != 0.0) @@ -1205,7 +1205,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { { case tt_Table: // obrotnica jak zwykły tor, tylko animacja dochodzi case tt_Normal: - if (TextureID2) + if (m_material2) { // podsypka z podkładami jest tylko dla zwykłego toru vector6 bpts1[8]; // punkty głównej płaszczyzny nie przydają się do robienia boków if (fTexLength == 4.0) // jeśli stare mapowanie @@ -1275,7 +1275,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { GfxRenderer.Replace( vertices, Geometry2[ 0 ] ); } } - if (TextureID1) + if (m_material1) { // szyny - generujemy dwie, najwyżej rysować się będzie jedną vertex_array vertices; if( ( Bank != 0 ) && ( true == Geometry1.empty() ) ) { @@ -1296,7 +1296,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { } break; case tt_Switch: // dla zwrotnicy dwa razy szyny - if( TextureID1 || TextureID2 ) + if( m_material1 || m_material2 ) { // iglice liczone tylko dla zwrotnic vector6 rpts3[24], rpts4[24]; for (i = 0; i < 12; ++i) @@ -1326,7 +1326,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { if (SwitchExtension->RightSwitch) { // nowa wersja z SPKS, ale odwrotnie lewa/prawa vertex_array vertices; - if( TextureID1 ) { + if( m_material1 ) { // fixed parts SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts2, nnumPts, fTexLength ); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); @@ -1339,7 +1339,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - if( TextureID2 ) { + if( m_material2 ) { // fixed parts SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts1, nnumPts, fTexLength ); Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); @@ -1356,7 +1356,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { else { // lewa działa lepiej niż prawa vertex_array vertices; - if( TextureID1 ) { + if( m_material1 ) { // fixed parts SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts1, nnumPts, fTexLength ); // lewa szyna normalna cała Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); @@ -1369,7 +1369,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); vertices.clear(); } - if( TextureID2 ) { + if( m_material2 ) { // fixed parts SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts2, nnumPts, fTexLength ); // prawa szyna normalnie cała Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); @@ -1394,7 +1394,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { case tt_Normal: // drogi proste, bo skrzyżowania osobno { vector6 bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków - if (TextureID1 || TextureID2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma + if (m_material1 || m_material2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma { // double max=2.0*(fHTW>fHTW2?fHTW:fHTW2); //z szerszej strony jest 100% double max = fTexRatio1 * fTexLength; // test: szerokość proporcjonalna do długości double map1 = max > 0.0 ? fHTW / max : 0.5; // obcięcie tekstury od strony 1 @@ -1413,13 +1413,13 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { bpts1[ 1 ] = vector6( -fHTW, 0.0, 0.5 + map1, 0.0, 1.0, 0.0 ); } } - if (TextureID1) // jeśli podana była tekstura, generujemy trójkąty + if (m_material1) // jeśli podana była tekstura, generujemy trójkąty { // tworzenie trójkątów nawierzchni szosy vertex_array vertices; Segment->RenderLoft(vertices, origin, bpts1, iTrapezoid ? -2 : 2, fTexLength); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } - if (TextureID2) + if (m_material2) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) vector6 rpts1[6], @@ -1574,7 +1574,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { nullptr : SwitchExtension->vPoints; // zmienna robocza, NULL gdy tablica punktów już jest wypełniona vector6 bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków - if (TextureID1 || TextureID2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma + if (m_material1 || m_material2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma { // double max=2.0*(fHTW>fHTW2?fHTW:fHTW2); //z szerszej strony jest 100% double max = fTexRatio1 * fTexLength; // test: szerokość proporcjonalna do długości double map1 = max > 0.0 ? fHTW / max : 0.5; // obcięcie tekstury od strony 1 @@ -1592,7 +1592,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { // problem ze skrzyżowaniami jest taki, że teren chce się pogrupować wg tekstur, ale zaczyna od nawierzchni // sama nawierzchnia nie wypełni tablicy punktów, bo potrzebne są pobocza // ale pobocza renderują się później, więc nawierzchnia nie załapuje się na renderowanie w swoim czasie - if( TextureID2 ) + if( m_material2 ) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) vector6 rpts1[6], @@ -1651,7 +1651,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { rpts2[5] = vector6(bpts1[3].x - side2, bpts1[3].y - fTexHeight2, 0.484375 - map2l); // lewy brzeg lewego chodnika } } - bool render = ( TextureID2 != 0 ); // renderować nie trzeba, ale trzeba wyznaczyć punkty brzegowe nawierzchni + bool render = ( m_material2 != 0 ); // renderować nie trzeba, ale trzeba wyznaczyć punkty brzegowe nawierzchni vertex_array vertices; if (SwitchExtension->iRoads == 4) { // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka @@ -1707,7 +1707,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { SwitchExtension->bPoints = true; // tablica punktów została wypełniona } - if( TextureID1 ) { + if( m_material1 ) { vertex_array vertices; // jeśli podana tekstura nawierzchni // we start with a vertex in the middle... @@ -1759,7 +1759,7 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { case tt_Normal: // drogi proste, bo skrzyżowania osobno { vector6 bpts1[4]; // punkty głównej płaszczyzny przydają się do robienia boków - if (TextureID1 || TextureID2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma + if (m_material1 || m_material2) // punkty się przydadzą, nawet jeśli nawierzchni nie ma { // double max=2.0*(fHTW>fHTW2?fHTW:fHTW2); //z szerszej strony jest 100% double max = (iCategoryFlag & 4) ? 0.0 : @@ -1784,13 +1784,13 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { bpts1[1] = vector6(-fHTW, 0.0, 0.5 + map1); } } - if (TextureID1) // jeśli podana była tekstura, generujemy trójkąty + if (m_material1) // jeśli podana była tekstura, generujemy trójkąty { // tworzenie trójkątów nawierzchni szosy vertex_array vertices; Segment->RenderLoft(vertices, origin, bpts1, iTrapezoid ? -2 : 2, fTexLength); Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); } - if (TextureID2) + if (m_material2) { // pobocze drogi - poziome przy przechyłce (a może krawężnik i chodnik zrobić jak w Midtown Madness 2?) vertex_array vertices; vector6 rpts1[6], @@ -2105,8 +2105,8 @@ TTrack * TTrack::RaAnimate() } } // skip the geometry update if no geometry for this track was generated yet - if( ( ( TextureID1 != 0 ) - || ( TextureID2 != 0 ) ) + if( ( ( m_material1 != 0 ) + || ( m_material2 != 0 ) ) && ( ( false == Geometry1.empty() ) || ( false == Geometry2.empty() ) ) ) { // iglice liczone tylko dla zwrotnic @@ -2143,13 +2143,13 @@ TTrack * TTrack::RaAnimate() if (SwitchExtension->RightSwitch) { // nowa wersja z SPKS, ale odwrotnie lewa/prawa - if( TextureID1 ) { + if( m_material1 ) { // left blade SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, SwitchExtension->fOffset2 ); GfxRenderer.Replace( vertices, Geometry1[ 2 ] ); vertices.clear(); } - if( TextureID2 ) { + if( m_material2 ) { // right blade SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -fMaxOffset + SwitchExtension->fOffset1 ); GfxRenderer.Replace( vertices, Geometry2[ 2 ] ); @@ -2157,13 +2157,13 @@ TTrack * TTrack::RaAnimate() } } else { // lewa działa lepiej niż prawa - if( TextureID1 ) { + if( m_material1 ) { // right blade SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -SwitchExtension->fOffset2 ); GfxRenderer.Replace( vertices, Geometry1[ 2 ] ); vertices.clear(); } - if( TextureID2 ) { + if( m_material2 ) { // left blade SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, fMaxOffset - SwitchExtension->fOffset1 ); GfxRenderer.Replace( vertices, Geometry2[ 2 ] ); diff --git a/Track.h b/Track.h index 573fca5f..2211ff10 100644 --- a/Track.h +++ b/Track.h @@ -13,7 +13,7 @@ http://mozilla.org/MPL/2.0/. #include "GL/glew.h" #include "ResourceManager.h" #include "Segment.h" -#include "Texture.h" +#include "material.h" typedef enum { @@ -142,8 +142,8 @@ private: /* GLuint DisplayListID = 0; */ - texture_handle TextureID1 = 0; // tekstura szyn albo nawierzchni - texture_handle TextureID2 = 0; // tekstura automatycznej podsypki albo pobocza + material_handle m_material1 = 0; // tekstura szyn albo nawierzchni + material_handle m_material2 = 0; // tekstura automatycznej podsypki albo pobocza typedef std::vector geometryhandle_sequence; geometryhandle_sequence Geometry1; // geometry chunks textured with texture 1 geometryhandle_sequence Geometry2; // geometry chunks textured with texture 2 @@ -257,8 +257,8 @@ public: GLuint TextureGet(int i) { return ( i ? - TextureID1 : - TextureID2 ); }; + m_material1 : + m_material2 ); }; bool IsGroupable(); int TestPoint(vector3 *Point); void MovedUp1(float const dh); diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index e4762bc0..00ac0856 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -225,6 +225,9 @@ Source Files + + Source Files + @@ -440,6 +443,9 @@ Header Files + + Header Files + diff --git a/material.cpp b/material.cpp new file mode 100644 index 00000000..c9c82eed --- /dev/null +++ b/material.cpp @@ -0,0 +1,143 @@ +/* +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/. +*/ + +#include "stdafx.h" + +#include "material.h" +#include "renderer.h" +#include "usefull.h" +#include "globals.h" + +bool +opengl_material::deserialize( cParser &Input, bool const Loadnow ) { + + bool result { false }; + while( true == deserialize_mapping( Input, Loadnow ) ) { + result = true; // once would suffice but, eh + } + + has_alpha = ( + texture1 != NULL ? + GfxRenderer.Texture( texture1 ).has_alpha : + false ); + + return result; +} + +// imports member data pair from the config file +bool +opengl_material::deserialize_mapping( cParser &Input, bool const Loadnow ) { + + if( false == Input.getTokens( 2, true, "\n\r\t;, " ) ) { + return false; + } + + std::string path; + if( name.rfind( '\\' ) != std::string::npos ) { + path = name.substr( 0, name.rfind( '\\' ) + 1 ); + } + + std::string key, value; + Input + >> key + >> value; + + if( key == "texture1:" ) { texture1 = GfxRenderer.Fetch_Texture( path + value, Loadnow ); } + else if( key == "texture2:" ) { texture2 = GfxRenderer.Fetch_Texture( path + value, Loadnow ); } + + return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized +} + + +// create material object from data stored in specified file. +// NOTE: the deferred load parameter is passed to textures defined by material, the material itself is always loaded immediately +material_handle +material_manager::create( std::string const &Filename, bool const Loadnow ) { + + auto filename { Filename }; + + if( filename.find( '|' ) != std::string::npos ) + filename.erase( filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury + + if( filename.rfind( '.' ) != std::string::npos ) { + // we can get extension for .mat or, in legacy files, some image format. just trim it and set it to material file extension + filename.erase( filename.rfind( '.' ) ); + } + filename += ".mat"; + + for( char &c : filename ) { + // change forward slashes to windows ones. NOTE: probably not strictly necessary, but eh + c = ( c == '/' ? '\\' : c ); + } + if( filename.find( '\\' ) == std::string::npos ) { + // jeśli bieżaca ścieżka do tekstur nie została dodana to dodajemy domyślną + filename = szTexturePath + filename; + } + + // try to locate requested material in the databank + auto const databanklookup = find_in_databank( filename ); + if( databanklookup != npos ) { + return databanklookup; + } + // if this fails, try to look for it on disk + opengl_material material; + material.name = filename; + auto const disklookup = find_on_disk( filename ); + if( disklookup != "" ) { + cParser materialparser( disklookup, cParser::buffer_FILE ); + if( false == material.deserialize( materialparser, Loadnow ) ) { + // deserialization failed but the .mat file does exist, so we give up at this point + return NULL; + } + } + else { + // if there's no .mat file, this could be legacy method of referring just to diffuse texture directly, make a material out of it in such case + material.texture1 = GfxRenderer.Fetch_Texture( Filename, Loadnow ); + if( material.texture1 == NULL ) { + // if there's also no texture, give up + return NULL; + } + material.has_alpha = GfxRenderer.Texture( material.texture1 ).has_alpha; + } + + material_handle handle = m_materials.size(); + m_materials.emplace_back( material ); + m_materialmappings.emplace( material.name, handle ); + return handle; +}; + +// checks whether specified texture is in the texture bank. returns texture id, or npos. +material_handle +material_manager::find_in_databank( std::string const &Materialname ) const { + + auto lookup = m_materialmappings.find( Materialname ); + if( lookup != m_materialmappings.end() ) { + return lookup->second; + } + // jeszcze próba z dodatkową ścieżką + lookup = m_materialmappings.find( szTexturePath + Materialname ); + + return ( + lookup != m_materialmappings.end() ? + lookup->second : + npos ); +} + +// checks whether specified file exists. +// NOTE: this is direct copy of the method used by texture manager. TBD, TODO: refactor into common routine? +std::string +material_manager::find_on_disk( std::string const &Materialname ) const { + + return( + FileExists( Materialname ) ? Materialname : + FileExists( szTexturePath + Materialname ) ? szTexturePath + Materialname : + "" ); +} + +//--------------------------------------------------------------------------- diff --git a/material.h b/material.h new file mode 100644 index 00000000..52e863f1 --- /dev/null +++ b/material.h @@ -0,0 +1,64 @@ +/* +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/. +*/ + +#pragma once + +#include "texture.h" +#include "parser.h" + +typedef int material_handle; + +// a collection of parameters for the rendering setup. +// for modern opengl this translates to set of attributes for the active shaders, +// for legacy opengl this is basically just texture(s) assigned to geometry +struct opengl_material { + + texture_handle texture1 { NULL }; // primary texture, typically diffuse+apha + texture_handle texture2 { NULL }; // secondary texture, typically normal+reflection + + bool has_alpha { false }; // alpha state, calculated from presence of alpha in texture1 + std::string name; +// methods: + bool + deserialize( cParser &Input, bool const Loadnow ); +private: + // imports member data pair from the config file + bool + deserialize_mapping( cParser &Input, bool const Loadnow ); +}; + +class material_manager { + +public: + material_manager() { m_materials.emplace_back( opengl_material() ); } // empty bindings for null material + + material_handle + create( std::string const &Filename, bool const Loadnow ); + opengl_material const & + material( material_handle const Material ) const { return m_materials[ Material ]; } + +private: +// types + typedef std::vector material_sequence; + typedef std::unordered_map index_map; +// methods: + // checks whether specified texture is in the texture bank. returns texture id, or npos. + texture_handle + find_in_databank( std::string const &Materialname ) const; + // checks whether specified file exists. returns name of the located file, or empty string. + std::string + find_on_disk( std::string const &Materialname ) const; +// members: + material_handle const npos { -1 }; + material_sequence m_materials; + index_map m_materialmappings; + +}; + +//--------------------------------------------------------------------------- diff --git a/openglgeometrybank.cpp b/openglgeometrybank.cpp index 0a046ead..9d622f3a 100644 --- a/openglgeometrybank.cpp +++ b/openglgeometrybank.cpp @@ -120,6 +120,7 @@ geometry_bank::vertices( geometry_handle const &Geometry ) const { GLuint opengl_vbogeometrybank::m_activebuffer { NULL }; // buffer bound currently on the opengl end, if any unsigned int opengl_vbogeometrybank::m_activestreams { stream::none }; // currently enabled data type pointers +std::vector opengl_vbogeometrybank::m_activetexturearrays; // currently enabled texture coord arrays // create() subclass details void @@ -274,12 +275,19 @@ opengl_vbogeometrybank::bind_streams( stream_units const &Units, unsigned int co ::glDisableClientState( GL_COLOR_ARRAY ); } if( Streams & stream::texture ) { - ::glClientActiveTexture( Units.texture ); - ::glTexCoordPointer( 2, GL_FLOAT, sizeof( basic_vertex ), static_cast( nullptr ) + 24 ); - ::glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + for( auto unit : Units.texture ) { + ::glClientActiveTexture( unit ); + ::glTexCoordPointer( 2, GL_FLOAT, sizeof( basic_vertex ), static_cast( nullptr ) + 24 ); + ::glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + } + m_activetexturearrays = Units.texture; } else { - ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + for( auto unit : Units.texture ) { + ::glClientActiveTexture( unit ); + ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + } + m_activetexturearrays.clear(); // NOTE: we're simplifying here, since we always toggle the same texture coord sets } m_activestreams = Streams; @@ -291,9 +299,13 @@ opengl_vbogeometrybank::release_streams() { ::glDisableClientState( GL_VERTEX_ARRAY ); ::glDisableClientState( GL_NORMAL_ARRAY ); ::glDisableClientState( GL_COLOR_ARRAY ); - ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + for( auto unit : m_activetexturearrays ) { + ::glClientActiveTexture( unit ); + ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + } m_activestreams = stream::none; + m_activetexturearrays.clear(); } // opengl display list based variant of the geometry bank @@ -331,7 +343,7 @@ opengl_dlgeometrybank::draw_( geometry_handle const &Geometry, stream_units cons for( auto const &vertex : chunk.vertices ) { if( Streams & stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); } else if( Streams & stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); } - if( Streams & stream::texture ) { ::glMultiTexCoord2fv( Units.texture, glm::value_ptr( vertex.texture ) ); } + if( Streams & stream::texture ) { for( auto unit : Units.texture ) { ::glMultiTexCoord2fv( unit, glm::value_ptr( vertex.texture ) ); } } if( Streams & stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); } } ::glEnd(); diff --git a/openglgeometrybank.h b/openglgeometrybank.h index 90bec5a0..9a4a7773 100644 --- a/openglgeometrybank.h +++ b/openglgeometrybank.h @@ -46,7 +46,7 @@ unsigned int const color_streams { stream::position | stream::color | stream::te struct stream_units { - GLint texture { GL_TEXTURE0 }; // unit associated with main texture data stream. TODO: allow multiple units per stream + std::vector texture { GL_TEXTURE0 }; // unit associated with main texture data stream. TODO: allow multiple units per stream }; typedef std::vector vertex_array; @@ -207,6 +207,7 @@ private: // members: static GLuint m_activebuffer; // buffer bound currently on the opengl end, if any static unsigned int m_activestreams; + static std::vector m_activetexturearrays; GLuint m_buffer { NULL }; // id of the buffer holding data on the opengl end std::size_t m_buffercapacity{ 0 }; // total capacity of the last established buffer chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order diff --git a/renderer.cpp b/renderer.cpp index 081bdd91..1085ee52 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -98,9 +98,10 @@ opengl_renderer::Init( GLFWwindow *Window ) { glEnable( GL_CULL_FACE ); // Cull back-facing triangles glShadeModel( GL_SMOOTH ); // Enable Smooth Shading - glActiveTexture( m_diffusetextureunit ); - m_geometry.units().texture = m_diffusetextureunit; + m_geometry.units().texture = std::vector{ m_normaltextureunit, m_diffusetextureunit }; + m_textures.assign_units( m_helpertextureunit, m_shadowtextureunit, m_normaltextureunit, m_diffusetextureunit ); // TODO: add reflections unit UILayer.set_unit( m_diffusetextureunit ); + Active_Texture( m_diffusetextureunit ); ::glDepthFunc( GL_LEQUAL ); glEnable( GL_DEPTH_TEST ); @@ -215,7 +216,7 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE ); ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL ); ::glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE ); - ::glBindTexture( GL_TEXTURE_2D, 0 ); + ::glBindTexture( GL_TEXTURE_2D, NULL ); #ifdef EU07_USE_DEBUG_SHADOWMAP ::glGenTextures( 1, &m_shadowdebugtexture ); ::glBindTexture( GL_TEXTURE_2D, m_shadowdebugtexture ); @@ -577,24 +578,12 @@ opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, f case rendermode::pickscenery: { // TODO: scissor test for pick modes // projection - if( true == m_framebuffersupport ) { - auto const angle = Global::FieldOfView / Global::ZoomFactor; - auto const height = std::max( 1.0f, (float)Global::iWindowWidth ) / std::max( 1.0f, (float)Global::iWindowHeight ) / ( Global::iWindowWidth / EU07_PICKBUFFERSIZE ); - camera.projection() *= - glm::perspective( - glm::radians( Global::FieldOfView / Global::ZoomFactor ), - std::max( 1.f, (float)Global::iWindowWidth ) / std::max( 1.0f, (float)Global::iWindowHeight ) / ( Global::iWindowWidth / EU07_PICKBUFFERSIZE ), - 0.1f * Global::ZoomFactor, - Config.draw_range * Global::fDistanceFactor ); - } - else { - camera.projection() *= - glm::perspective( - glm::radians( Global::FieldOfView / Global::ZoomFactor ), - std::max( 1.f, (float)Global::iWindowWidth ) / std::max( 1.f, (float)Global::iWindowHeight ), - 0.1f * Global::ZoomFactor, - Config.draw_range * Global::fDistanceFactor ); - } + camera.projection() *= + glm::perspective( + glm::radians( Global::FieldOfView / Global::ZoomFactor ), + std::max( 1.f, (float)Global::iWindowWidth ) / std::max( 1.f, (float)Global::iWindowHeight ), + 0.1f * Global::ZoomFactor, + Config.draw_range * Global::fDistanceFactor ); // modelview camera.position() = Global::pCameraPosition; World.Camera.SetMatrix( viewmatrix ); @@ -674,17 +663,20 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const // helper texture unit. // darkens previous stage, preparing data for the shadow texture unit to select from if( m_helpertextureunit >= 0 ) { - if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { - // setup reflection texture unit - ::glActiveTexture( m_helpertextureunit ); - Bind( m_reflectiontexture ); // TBD, TODO: move to reflection unit setup + + Active_Texture( m_helpertextureunit ); + + if( ( true == Reflections ) + || ( ( true == Global::RenderShadows ) && ( true == Shadows ) ) ) { + m_textures.bind( textureunit::helper, m_reflectiontexture ); ::glEnable( GL_TEXTURE_2D ); -/* - glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); - glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); - glEnable( GL_TEXTURE_GEN_S ); - glEnable( GL_TEXTURE_GEN_T ); -*/ + } + else { + ::glDisable( GL_TEXTURE_2D ); + } + + if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); ::glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, glm::value_ptr( m_shadowcolor ) ); // TODO: dynamically calculated shadow colour, based on sun height @@ -698,14 +690,24 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); } else { - ::glActiveTexture( m_helpertextureunit ); - ::glDisable( GL_TEXTURE_2D ); -/* + + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE ); // pass the previous stage colour down the chain + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); + + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); // pass the previous stage alpha down the chain + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); + } + + if( true == Reflections ) { + ::glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); + ::glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); + ::glEnable( GL_TEXTURE_GEN_S ); + ::glEnable( GL_TEXTURE_GEN_T ); + } + else { ::glDisable( GL_TEXTURE_GEN_S ); ::glDisable( GL_TEXTURE_GEN_T ); - ::glDisable( GL_TEXTURE_GEN_R ); - ::glDisable( GL_TEXTURE_GEN_Q ); -*/ } } // shadow texture unit. @@ -715,7 +717,8 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const && ( true == Shadows ) && ( m_shadowcolor != colors::white ) ) { - ::glActiveTexture( m_shadowtextureunit ); + Active_Texture( m_shadowtextureunit ); + // NOTE: shadowmap isn't part of regular texture system, so we use direct bind call here ::glBindTexture( GL_TEXTURE_2D, m_shadowtexture ); ::glEnable( GL_TEXTURE_2D ); // s @@ -745,7 +748,7 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const } else { // turn off shadow map tests - ::glActiveTexture( m_shadowtextureunit ); + Active_Texture( m_shadowtextureunit ); ::glDisable( GL_TEXTURE_2D ); ::glDisable( GL_TEXTURE_GEN_S ); @@ -754,9 +757,35 @@ opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const ::glDisable( GL_TEXTURE_GEN_Q ); } } + // reflections/normals texture unit + // NOTE: comes after diffuse stage in the operation chain + if( m_normaltextureunit >= 0 ) { + + Active_Texture( m_normaltextureunit ); + + if( true == Reflections ) { + ::glEnable( GL_TEXTURE_2D ); + + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE ); // blend between object colour and env.reflection + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0 ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE ); // alpha channel of the normal map controls reflection strength + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA ); + + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); // pass the previous stage alpha down the chain + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); + } + else { + ::glDisable( GL_TEXTURE_2D ); + } + } // diffuse texture unit. // NOTE: diffuse texture mapping is never fully disabled, alpha channel information is always included - ::glActiveTexture( m_diffusetextureunit ); + Active_Texture( m_diffusetextureunit ); + ::glEnable( GL_TEXTURE_2D ); if( true == Diffuse ) { // default behaviour, modulate with previous stage ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); @@ -788,14 +817,15 @@ void opengl_renderer::switch_units( bool const Diffuse, bool const Shadows, bool const Reflections ) { // helper texture unit. if( m_helpertextureunit >= 0 ) { - if( ( true == Global::RenderShadows ) - && ( true == Shadows ) - && ( m_shadowcolor != colors::white ) ) { - ::glActiveTexture( m_helpertextureunit ); + if( ( true == Reflections ) + || ( ( true == Global::RenderShadows ) + && ( true == Shadows ) + && ( m_shadowcolor != colors::white ) ) ) { + Active_Texture( m_helpertextureunit ); ::glEnable( GL_TEXTURE_2D ); } else { - ::glActiveTexture( m_helpertextureunit ); + Active_Texture( m_helpertextureunit ); ::glDisable( GL_TEXTURE_2D ); } } @@ -803,25 +833,35 @@ opengl_renderer::switch_units( bool const Diffuse, bool const Shadows, bool cons if( m_shadowtextureunit >= 0 ) { if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { - ::glActiveTexture( m_shadowtextureunit ); + Active_Texture( m_shadowtextureunit ); ::glEnable( GL_TEXTURE_2D ); } else { - ::glActiveTexture( m_shadowtextureunit ); + Active_Texture( m_shadowtextureunit ); ::glDisable( GL_TEXTURE_2D ); } } + // normal/reflection texture unit + if( true == Reflections ) { + + Active_Texture( m_normaltextureunit ); + ::glEnable( GL_TEXTURE_2D ); + } + else { + Active_Texture( m_normaltextureunit ); + ::glDisable( GL_TEXTURE_2D ); + } // diffuse texture unit. // NOTE: toggle actually disables diffuse texture mapping, unlike setup counterpart if( true == Diffuse ) { - ::glActiveTexture( m_diffusetextureunit ); + Active_Texture( m_diffusetextureunit ); ::glEnable( GL_TEXTURE_2D ); } else { - ::glActiveTexture( m_diffusetextureunit ); + Active_Texture( m_diffusetextureunit ); ::glDisable( GL_TEXTURE_2D ); } } @@ -829,10 +869,9 @@ opengl_renderer::switch_units( bool const Diffuse, bool const Shadows, bool cons void opengl_renderer::setup_shadow_color( glm::vec4 const &Shadowcolor ) { - ::glActiveTexture( m_helpertextureunit ); + Active_Texture( m_helpertextureunit ); ::glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, glm::value_ptr( Shadowcolor ) ); // in-shadow colour multiplier - ::glActiveTexture( m_diffusetextureunit ); - + Active_Texture( m_diffusetextureunit ); } bool @@ -854,7 +893,7 @@ opengl_renderer::Render( world_environment *Environment ) { return false; } - Bind( NULL ); + Bind_Material( NULL ); ::glDisable( GL_LIGHTING ); ::glDisable( GL_DEPTH_TEST ); ::glDepthMask( GL_FALSE ); @@ -903,7 +942,7 @@ opengl_renderer::Render( world_environment *Environment ) { auto const &modelview = OpenGLMatrices.data( GL_MODELVIEW ); // sun { - Bind( m_suntexture ); + Bind_Texture( m_suntexture ); ::glColor4f( suncolor.x, suncolor.y, suncolor.z, 1.0f ); auto const sunvector = Environment->m_sun.getDirection(); auto const sunposition = modelview * glm::vec4( sunvector.x, sunvector.y, sunvector.z, 1.0f ); @@ -924,7 +963,7 @@ opengl_renderer::Render( world_environment *Environment ) { } // moon { - Bind( m_moontexture ); + Bind_Texture( m_moontexture ); glm::vec3 mooncolor( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ); ::glColor4f( mooncolor.x, mooncolor.y, mooncolor.z, static_cast( 1.0 - Global::fLuminance * 0.5 ) ); @@ -1032,21 +1071,48 @@ opengl_renderer::Vertices( geometry_handle const &Geometry ) const { return m_geometry.vertices( Geometry ); } -// texture methods -texture_handle -opengl_renderer::Fetch_Texture( std::string const &Filename, std::string const &Dir, int const Filter, bool const Loadnow ) { +// material methods +material_handle +opengl_renderer::Fetch_Material( std::string const &Filename, bool const Loadnow ) { - return m_textures.create( Filename, Dir, Filter, Loadnow ); + return m_materials.create( Filename, Loadnow ); } void -opengl_renderer::Bind( texture_handle const Texture ) { - // temporary until we separate the renderer - m_textures.bind( Texture ); +opengl_renderer::Bind_Material( material_handle const Material ) { + + auto const &material = m_materials.material( Material ); + m_textures.bind( textureunit::normals, material.texture2 ); + m_textures.bind( textureunit::diffuse, material.texture1 ); +} + +opengl_material const & +opengl_renderer::Material( material_handle const Material ) const { + + return m_materials.material( Material ); +} + +// texture methods +void +opengl_renderer::Active_Texture( GLint const Textureunit ) { + + return m_textures.unit( Textureunit ); +} + +texture_handle +opengl_renderer::Fetch_Texture( std::string const &Filename, bool const Loadnow ) { + + return m_textures.create( Filename, Loadnow ); +} + +void +opengl_renderer::Bind_Texture( texture_handle const Texture ) { + + m_textures.bind( textureunit::diffuse, Texture ); } opengl_texture const & -opengl_renderer::Texture( texture_handle const Texture ) { +opengl_renderer::Texture( texture_handle const Texture ) const { return m_textures.texture( Texture ); } @@ -1431,7 +1497,7 @@ opengl_renderer::Render( TGroundNode *Node ) { ::glLineWidth( static_cast( linewidth ) ); } - GfxRenderer.Bind( NULL ); + GfxRenderer.Bind_Material( NULL ); ::glPushMatrix(); auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); @@ -1464,7 +1530,7 @@ opengl_renderer::Render( TGroundNode *Node ) { return false; } // setup - Bind( Node->TextureID ); + Bind_Material( Node->m_material ); switch( m_renderpass.draw_mode ) { case rendermode::color: { ::glColor3fv( glm::value_ptr( Node->Diffuse ) ); @@ -1784,12 +1850,12 @@ opengl_renderer::Render( TSubModel *Submodel ) { #endif // material configuration: // textures... - if( Submodel->TextureID < 0 ) { // zmienialne skóry - Bind( Submodel->ReplacableSkinId[ -Submodel->TextureID ] ); + if( Submodel->m_material < 0 ) { // zmienialne skóry + Bind_Material( Submodel->ReplacableSkinId[ -Submodel->m_material ] ); } else { // również 0 - Bind( Submodel->TextureID ); + Bind_Material( Submodel->m_material ); } // ...colors... ::glColor3fv( glm::value_ptr( Submodel->f4Diffuse ) ); // McZapkie-240702: zamiast ub @@ -1839,12 +1905,12 @@ opengl_renderer::Render( TSubModel *Submodel ) { // scenery picking and shadow both use enforced colour and no frills // material configuration: // textures... - if( Submodel->TextureID < 0 ) { // zmienialne skóry - Bind( Submodel->ReplacableSkinId[ -Submodel->TextureID ] ); + if( Submodel->m_material < 0 ) { // zmienialne skóry + Bind_Material( Submodel->ReplacableSkinId[ -Submodel->m_material ] ); } else { // również 0 - Bind( Submodel->TextureID ); + Bind_Material( Submodel->m_material ); } // main draw call m_geometry.draw( Submodel->m_geometry ); @@ -1857,12 +1923,12 @@ opengl_renderer::Render( TSubModel *Submodel ) { m_pickcontrolsitems.emplace_back( Submodel ); ::glColor3fv( glm::value_ptr( pick_color( m_pickcontrolsitems.size() ) ) ); // textures... - if( Submodel->TextureID < 0 ) { // zmienialne skóry - Bind( Submodel->ReplacableSkinId[ -Submodel->TextureID ] ); + if( Submodel->m_material < 0 ) { // zmienialne skóry + Bind_Material( Submodel->ReplacableSkinId[ -Submodel->m_material ] ); } else { // również 0 - Bind( Submodel->TextureID ); + Bind_Material( Submodel->m_material ); } // main draw call m_geometry.draw( Submodel->m_geometry ); @@ -1902,7 +1968,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { // material configuration: ::glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT ); - Bind( NULL ); + Bind_Material( NULL ); ::glPointSize( std::max( 3.f, 5.f * distancefactor * anglefactor ) ); ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], lightlevel * anglefactor ); ::glDisable( GL_LIGHTING ); @@ -1941,7 +2007,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { // material configuration: ::glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT ); - Bind( NULL ); + Bind_Material( NULL ); ::glDisable( GL_LIGHTING ); // main draw call @@ -1976,20 +2042,20 @@ opengl_renderer::Render( TSubModel *Submodel ) { void opengl_renderer::Render( TTrack *Track ) { - if( ( Track->TextureID1 == 0 ) - && ( Track->TextureID2 == 0 ) ) { + if( ( Track->m_material1 == 0 ) + && ( Track->m_material2 == 0 ) ) { return; } switch( m_renderpass.draw_mode ) { case rendermode::color: { Track->EnvironmentSet(); - if( Track->TextureID1 != 0 ) { - Bind( Track->TextureID1 ); + if( Track->m_material1 != 0 ) { + Bind_Material( Track->m_material1 ); m_geometry.draw( std::begin( Track->Geometry1 ), std::end( Track->Geometry1 ) ); } - if( Track->TextureID2 != 0 ) { - Bind( Track->TextureID2 ); + if( Track->m_material2 != 0 ) { + Bind_Material( Track->m_material2 ); m_geometry.draw( std::begin( Track->Geometry2 ), std::end( Track->Geometry2 ) ); } Track->EnvironmentReset(); @@ -1997,12 +2063,12 @@ opengl_renderer::Render( TTrack *Track ) { } case rendermode::pickscenery: case rendermode::shadows: { - if( Track->TextureID1 != 0 ) { - Bind( Track->TextureID1 ); + if( Track->m_material1 != 0 ) { + Bind_Material( Track->m_material1 ); m_geometry.draw( std::begin( Track->Geometry1 ), std::end( Track->Geometry1 ) ); } - if( Track->TextureID2 != 0 ) { - Bind( Track->TextureID2 ); + if( Track->m_material2 != 0 ) { + Bind_Material( Track->m_material2 ); m_geometry.draw( std::begin( Track->Geometry2 ), std::end( Track->Geometry2 ) ); } break; @@ -2141,7 +2207,7 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { auto const color { Node->hvTraction->wire_color() }; ::glColor4f( color.r, color.g, color.b, linealpha ); - Bind( NULL ); + Bind_Material( NULL ); ::glPushMatrix(); auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); @@ -2217,7 +2283,7 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { ::glLineWidth( static_cast(linewidth) ); } - GfxRenderer.Bind( NULL ); + GfxRenderer.Bind_Material( NULL ); ::glPushMatrix(); auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); @@ -2242,7 +2308,7 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { // setup ::glColor3fv( glm::value_ptr( Node->Diffuse ) ); - Bind( Node->TextureID ); + Bind_Material( Node->m_material ); ::glPushMatrix(); auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); @@ -2423,12 +2489,12 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { } #endif // textures... - if( Submodel->TextureID < 0 ) { // zmienialne skóry - Bind( Submodel->ReplacableSkinId[ -Submodel->TextureID ] ); + if( Submodel->m_material < 0 ) { // zmienialne skóry + Bind_Material( Submodel->ReplacableSkinId[ -Submodel->m_material ] ); } else { // również 0 - Bind( Submodel->TextureID ); + Bind_Material( Submodel->m_material ); } // ...colors... ::glColor3fv( glm::value_ptr(Submodel->f4Diffuse) ); // McZapkie-240702: zamiast ub @@ -2495,7 +2561,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // setup ::glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT ); - Bind( m_glaretexture ); + Bind_Texture( m_glaretexture ); ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], glarelevel ); ::glDisable( GL_LIGHTING ); ::glBlendFunc( GL_SRC_ALPHA, GL_ONE ); @@ -2585,9 +2651,8 @@ opengl_renderer::Update_Pick_Control() { if( true == m_framebuffersupport ) { // ::glReadBuffer( GL_COLOR_ATTACHMENT0_EXT ); pickbufferpos = glm::ivec2{ - mousepos.x * EU07_PICKBUFFERSIZE / Global::iWindowWidth, - mousepos.y * EU07_PICKBUFFERSIZE / Global::iWindowHeight - }; + mousepos.x * EU07_PICKBUFFERSIZE / std::max( 1, Global::iWindowWidth ), + mousepos.y * EU07_PICKBUFFERSIZE / std::max( 1, Global::iWindowHeight ) }; } else { // ::glReadBuffer( GL_BACK ); diff --git a/renderer.h b/renderer.h index 30e5dcad..2ecad49c 100644 --- a/renderer.h +++ b/renderer.h @@ -11,7 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "GL/glew.h" #include "openglgeometrybank.h" -#include "texture.h" +#include "material.h" #include "lightarray.h" #include "dumb3d.h" #include "frustum.h" @@ -73,13 +73,6 @@ struct opengl_technique { }; -// a collection of parameters for the rendering setup. -// for modern opengl this translates to set of attributes for the active shaders, -// for legacy opengl this is basically just texture(s) assigned to geometry -struct opengl_material { - -}; - // simple camera object. paired with 'virtual camera' in the scene class opengl_camera { @@ -175,13 +168,22 @@ public: // provides direct access to vertex data of specfied chunk vertex_array const & Vertices( geometry_handle const &Geometry ) const; - // texture methods - texture_handle - Fetch_Texture( std::string const &Filename, std::string const &Dir = szTexturePath, int const Filter = -1, bool const Loadnow = true ); + // material methods + material_handle + Fetch_Material( std::string const &Filename, bool const Loadnow = true ); void - Bind( texture_handle const Texture ); + Bind_Material( material_handle const Material ); + opengl_material const & + Material( material_handle const Material ) const; + // texture methods + void + Active_Texture( GLint const Textureunit ); + texture_handle + Fetch_Texture( std::string const &Filename, bool const Loadnow = true ); + void + Bind_Texture( texture_handle const Texture ); opengl_texture const & - Texture( texture_handle const Texture ); + Texture( texture_handle const Texture ) const; // light methods void Disable_Lights(); @@ -215,6 +217,13 @@ private: pickscenery }; + enum textureunit { + helper = 0, + shadows, + normals, + diffuse + }; + typedef std::pair< double, TSubRect * > distancesubcell_pair; struct renderpass_config { @@ -292,6 +301,7 @@ private: // members GLFWwindow *m_window { nullptr }; geometrybank_manager m_geometry; + material_manager m_materials; texture_manager m_textures; opengllight_array m_lights; @@ -316,9 +326,10 @@ private: #endif glm::mat4 m_shadowtexturematrix; // conversion from camera-centric world space to light-centric clip space - int m_shadowtextureunit { GL_TEXTURE1 }; int m_helpertextureunit { GL_TEXTURE0 }; - int m_diffusetextureunit { GL_TEXTURE2 }; + int m_shadowtextureunit { GL_TEXTURE1 }; + int m_normaltextureunit { GL_TEXTURE2 }; + int m_diffusetextureunit{ GL_TEXTURE3 }; float m_drawtime { 1000.f / 30.f * 20.f }; // start with presumed 'neutral' average of 30 fps std::chrono::steady_clock::time_point m_drawstart; // cached start time of previous frame @@ -327,6 +338,7 @@ private: float m_drawtimeshadowpass { 0.f }; double m_updateaccumulator { 0.0 }; std::string m_debuginfo; + std::string m_pickdebuginfo; glm::vec4 m_baseambient { 0.0f, 0.0f, 0.0f, 1.0f }; glm::vec4 m_shadowcolor { 0.5f, 0.5f, 0.5f, 1.f }; diff --git a/uilayer.cpp b/uilayer.cpp index 4680a728..d05adbcc 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -216,7 +216,7 @@ 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 ); + GfxRenderer.Bind_Texture( m_background ); auto const height { 768.0f }; auto const &texture = GfxRenderer.Texture( m_background ); float const width = ( @@ -239,7 +239,7 @@ ui_layer::render_texture() { ::glColor4f( 1.f, 1.f, 1.f, 1.f ); ::glDisable( GL_BLEND ); - GfxRenderer.Bind( NULL ); + GfxRenderer.Bind_Texture( NULL ); ::glBindTexture( GL_TEXTURE_2D, m_texture ); auto const size = 512.f; @@ -254,6 +254,8 @@ ui_layer::render_texture() { glEnd(); + ::glBindTexture( GL_TEXTURE_2D, NULL ); + ::glEnable( GL_BLEND ); } } diff --git a/version.h b/version.h index a572b462..48e719be 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 805 -#define VERSION_REVISION 0 +#define VERSION_MINOR 810 +#define VERSION_REVISION 1