diff --git a/Driver.cpp b/Driver.cpp index ed05f08e..8fae3dc0 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2698,6 +2698,7 @@ bool TController::PrepareEngine() if (AIControllFlag) { // część wykonawcza dla sterowania przez komputer mvOccupied->BatterySwitch( true ); + mvOccupied->Radio = true; if( ( mvControlling->EngineType == TEngineType::DieselElectric ) || ( mvControlling->EngineType == TEngineType::DieselEngine ) ) { mvControlling->OilPumpSwitch( true ); @@ -2941,6 +2942,7 @@ bool TController::ReleaseEngine() { mvOccupied->IncManualBrakeLevel( ManualBrakePosNo ); } // switch off remaining power + mvOccupied->Radio = false; mvOccupied->BatterySwitch( false ); } } @@ -3143,7 +3145,10 @@ bool TController::IncBrakeEIM() { case 0: { if( mvOccupied->MED_amax != 9.81 ) { - auto const brakeposition{ clamp( -1.0 * mvOccupied->AIHintLocalBrakeAccFactor * AccDesired / mvOccupied->MED_amax, 0.0, 1.0 ) }; + auto const maxpos{mvOccupied->EIMCtrlEmergency ? 0.9 : 1.0 }; + auto const brakelimit{ -2.2 * AccDesired / mvOccupied->MED_amax - 1.0}; //additional limit when hinted is too low + auto const brakehinted{ -1.0 * mvOccupied->AIHintLocalBrakeAccFactor * AccDesired / mvOccupied->MED_amax }; //preffered by AI + auto const brakeposition{ maxpos * clamp(std::max(brakelimit, brakehinted), 0.0, 1.0)}; OK = ( brakeposition != mvOccupied->LocalBrakePosA ); mvOccupied->LocalBrakePosA = brakeposition; } @@ -3887,9 +3892,11 @@ void TController::SpeedSet() void TController::SpeedCntrl(double DesiredSpeed) { - while (mvControlling->SpeedCtrlUnit.DesiredPower < mvControlling->SpeedCtrlUnit.MaxPower) - { - mvControlling->SpeedCtrlPowerInc(); + if (mvControlling->SpeedCtrlUnit.PowerStep > 0) { + while (mvControlling->SpeedCtrlUnit.DesiredPower < mvControlling->SpeedCtrlUnit.MaxPower) + { + mvControlling->SpeedCtrlPowerInc(); + } } if (mvControlling->EngineType == TEngineType::DieselEngine) { @@ -6879,6 +6886,10 @@ TController::UpdateSituation(double dt) { if( mvOccupied->LocalBrakePosA < 1.0 ) { // dodatkowy na pozycję 1 mvOccupied->IncLocalBrakeLevel( LocalBrakePosNo ); + if (mvOccupied->EIMCtrlEmergency) + { + mvOccupied->DecLocalBrakeLevel(1); + } } } else { diff --git a/DynObj.cpp b/DynObj.cpp index 57aaa0a7..658c02a4 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -1890,6 +1890,9 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" if( MoverParameters->LocalBrake != TLocalBrake::ManualBrake ) { if( fVel < 1.0 ) { MoverParameters->IncLocalBrakeLevel( LocalBrakePosNo ); + if ( MoverParameters->EIMCtrlEmergency ) { + MoverParameters->DecLocalBrakeLevel(1); + } } } } @@ -3172,8 +3175,9 @@ bool TDynamicObject::Update(double dt, double dt1) (LBR < 0.01); } auto Fzad = amax * LBR * masa; - if ((MoverParameters->BrakeCtrlPos == MoverParameters->Handle->GetPos(bh_EB)) - && (MoverParameters->eimc[eimc_p_abed] < 0.001)) + if (((MoverParameters->BrakeCtrlPos == MoverParameters->Handle->GetPos(bh_EB)) + && (MoverParameters->eimc[eimc_p_abed] < 0.001)) || + (MoverParameters->EmergencyValveFlow > 0)) Fzad = amax * masa; //pętla bezpieczeństwa - pełne służbowe if ((MoverParameters->ScndS) && (MoverParameters->Vel > MoverParameters->eimc[eimc_p_Vh1]) && (FmaxED > 0)) @@ -3904,7 +3908,7 @@ bool TDynamicObject::Update(double dt, double dt1) } // mirrors - if( MoverParameters->Vel > 5.0 ) { + if( MoverParameters->Vel > MoverParameters->MirrorVelClose ) { // automatically fold mirrors when above velocity threshold if( dMirrorMoveL > 0.0 ) { dMirrorMoveL = std::max( diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index c28e2671..88089f0b 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1044,7 +1044,7 @@ public: start_t BatteryStart = start_t::manual; bool EpFuse = true; /*Czy sa zalavzone baterie*/ bool Signalling = false; /*Czy jest zalaczona sygnalizacja hamowania ostatniego wagonu*/ - bool Radio = true; /*Czy jest zalaczony radiotelefon*/ + bool Radio = false; /*Czy jest zalaczony radiotelefon*/ float NominalBatteryVoltage = 0.f; /*Winger - baterie w elektrykach*/ TDimension Dim; /*wymiary*/ double Cx = 0.0; /*wsp. op. aerodyn.*/ @@ -1130,6 +1130,7 @@ public: double LockPipeOn = -1.0; double LockPipeOff = -1.0; double HandleUnlock = -3.0; + bool EmergencyCutsOffHandle = false; int CompressorListPosNo = 0; int CompressorListDefPos = 1; bool CompressorListWrap = false; @@ -1321,6 +1322,7 @@ public: int PlatformOpenMethod { 2 }; /*sposob animacji stopnia*/ #endif double MirrorMaxShift { 90.0 }; + double MirrorVelClose { 5.0 }; bool ScndS = false; /*Czy jest bocznikowanie na szeregowej*/ bool SpeedCtrl = false; /*czy jest tempomat*/ speed_control SpeedCtrlUnit; /*parametry tempomatu*/ @@ -1559,6 +1561,7 @@ public: bool SpeedCtrlTypeTime = false; /*czy tempomat sterowany czasowo*/ int SpeedCtrlAutoTurnOffFlag = 0; /*czy tempomat sam się wyłącza*/ bool EIMCtrlAdditionalZeros = false; /*czy ma dodatkowe zero jazdy i zero hamowania */ + bool EIMCtrlEmergency = false; /*czy ma dodatkowe zero jazdy i zero hamowania */ double eimv_pr = 0; /*realizowany procent dostepnej sily rozruchu/hamowania*/ double eimv[21]; static std::vector const eimv_labels; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 6395685b..cf37efb4 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -2279,7 +2279,7 @@ bool TMoverParameters::IncMainCtrl(int CtrlSpeed) else { ++MainCtrlPos; OK = true; - if ((EIMCtrlType == 0) && (SpeedCtrlAutoTurnOffFlag == 1) && (MainCtrlActualPos != MainCtrlPos)) + if ((EIMCtrlType == 0) && (SpeedCtrlAutoTurnOffFlag & 1 == 1) && (MainCtrlActualPos != MainCtrlPos)) { DecScndCtrl(2); SpeedCtrlUnit.IsActive = false; @@ -2448,7 +2448,7 @@ bool TMoverParameters::DecMainCtrl(int CtrlSpeed) { MainCtrlPos--; OK = true; - if ((EIMCtrlType == 0) && (SpeedCtrlAutoTurnOffFlag == 1) && (MainCtrlActualPos != MainCtrlPos)) { + if ((EIMCtrlType == 0) && (SpeedCtrlAutoTurnOffFlag & 1 == 1) && (MainCtrlActualPos != MainCtrlPos)) { DecScndCtrl(2); SpeedCtrlUnit.IsActive = false; } @@ -2612,7 +2612,7 @@ bool TMoverParameters::IncScndCtrl(int CtrlSpeed) if ((OK) && (EngineType == TEngineType::ElectricInductionMotor) && (ScndCtrlPosNo == 1) && (MainCtrlPos>0)) { SpeedCtrlValue = Vel; - if ((EIMCtrlType == 0)&&(SpeedCtrlAutoTurnOffFlag == 1)) + if ((EIMCtrlType == 0)&&(SpeedCtrlAutoTurnOffFlag & 1 == 1)) { MainCtrlActualPos = MainCtrlPos; } @@ -2841,7 +2841,7 @@ bool TMoverParameters::Sandbox( bool const State, range_t const Notify ) if( SandDose != State ) { if( SandDose == false ) { // switch on - if( Sand > 0 ) { + if(( Sand > 0 ) && ( DirActive != 0 )) { SandDose = true; result = true; } @@ -4214,7 +4214,8 @@ void TMoverParameters::UpdatePipePressure(double dt) dpLocalValve = LocHandle->GetPF(LocalBrakePosAEIM, Hamulec->GetBCP(), ScndPipePress, dt, 0); LockPipe = PipePress < (LockPipe ? LockPipeOff : LockPipeOn); - bool lock_new = (LockPipe && !UnlockPipe && (BrakeCtrlPosR > HandleUnlock)); //new simple codition based on .fiz + bool lock_new = (LockPipe && !UnlockPipe && (BrakeCtrlPosR > HandleUnlock)) + || ((EmergencyCutsOffHandle) && (EmergencyValveFlow > 0)); //new simple codition based on .fiz bool lock_old = ((BrakeHandle == TBrakeHandle::FV4a) //old complex condition based on assumptions && ((PipePress < 2.75) && ((Hamulec->GetStatus() & b_rls) == 0)) @@ -4252,6 +4253,10 @@ void TMoverParameters::UpdatePipePressure(double dt) dpMainValve = Handle->GetPF( 0, PipePress, temp, dt, EqvtPipePress ); } } + else if (BrakeCtrlPos == Handle->GetPos(bh_EB)) + { + dpMainValve = Handle->GetPF(BrakeCtrlPosR, PipePress, temp, dt, EqvtPipePress); + } if (dpMainValve < 0) // && (PipePressureVal > 0.01) //50 if (Compressor > ScndPipePress) @@ -4270,6 +4275,8 @@ void TMoverParameters::UpdatePipePressure(double dt) if( ( true == RadioStopFlag ) || ( true == AlarmChainFlag ) + || (( true == EIMCtrlEmergency) + && (LocalBrakePosA >= 1.0)) || SecuritySystem.is_braking() ) /* // NOTE: disabled because 32 is 'load destroyed' flag, what does this have to do with emergency brake? @@ -6915,17 +6922,21 @@ bool TMoverParameters::DropAllPantographs( bool const State, range_t const Notif void TMoverParameters::CheckEIMIC(double dt) { + double offset = EIMCtrlAdditionalZeros ? 1.0 : 0.0; + double multiplier = (EIMCtrlEmergency ? 1.0 : 0.0) + offset; switch (EIMCtrlType) { case 0: eimic = (LocalBrakeRatio() > 0.01 ? -LocalBrakeRatio() : (double)MainCtrlPos / (double)MainCtrlPosNo); - if (EIMCtrlAdditionalZeros) + if (EIMCtrlAdditionalZeros || EIMCtrlEmergency) { if (eimic > 0.001) - eimic = std::max(0.002, eimic * (double)MainCtrlPosNo / ((double)MainCtrlPosNo - 1.0) - 1.0 / ((double)MainCtrlPosNo - 1.0)); + eimic = std::max(0.002, eimic * (double)MainCtrlPosNo / ((double)MainCtrlPosNo - offset) - offset / ((double)MainCtrlPosNo - offset)); if ((eimic < -0.001) && (BrakeHandle != TBrakeHandle::MHZ_EN57)) - eimic = std::min(-0.002, eimic * (double)LocalBrakePosNo / ((double)LocalBrakePosNo - 1.0) + 1.0 / ((double)LocalBrakePosNo - 1.0)); + eimic = std::min(-0.002, eimic * (double)LocalBrakePosNo / ((double)LocalBrakePosNo - multiplier) + offset / ((double)LocalBrakePosNo - multiplier)); } + if ((eimic > 0.001) && (SpeedCtrlUnit.IsActive)) + eimic = std::max(eimic, SpeedCtrlUnit.MinPower); break; case 1: switch (MainCtrlPos) @@ -7054,6 +7065,10 @@ void TMoverParameters::CheckEIMIC(double dt) void TMoverParameters::CheckSpeedCtrl(double dt) { + if (EIMCtrlType == 0) + { + SpeedCtrlUnit.DesiredPower = std::max(eimic, 0.0); + } double accfactor = SpeedCtrlUnit.DesiredPower; if (EIMCtrlType >= 2) { if (MainCtrlPos < MainCtrlPosNo - 2) { @@ -7075,7 +7090,7 @@ void TMoverParameters::CheckSpeedCtrl(double dt) if (true) { if ((!SpeedCtrlUnit.Standby)) { if (SpeedCtrlUnit.ManualStateOverride) { - if (eimic > 0.009) eimic = 1.0; + if (eimic > 0.0009) eimic = 1.0; } double error = (std::max(SpeedCtrlValue + SpeedCtrlUnit.Offset, 0.0) - Vel); double factorP = error > 0 ? SpeedCtrlUnit.FactorPpos : SpeedCtrlUnit.FactorPneg; @@ -7123,6 +7138,11 @@ void TMoverParameters::CheckSpeedCtrl(double dt) else eimicSpeedCtrl = clamp(0.5 * (SpeedCtrlValue * 2 - Vel), -1.0, 1.0); } + if (((SpeedCtrlAutoTurnOffFlag & 2) == 2) && (Hamulec->GetEDBCP() > 0.25)) + { + DecScndCtrl(2); + SpeedCtrlUnit.IsActive = false; + } } else { eimicSpeedCtrl = 1; @@ -9726,6 +9746,7 @@ void TMoverParameters::LoadFIZ_Brake( std::string const &line ) { extract_value( LockPipeOn, "LPOn", line, "-1"); extract_value( LockPipeOff, "LPOff", line, "-1"); extract_value( HandleUnlock, "HandlePipeUnlockPos", line, "-3"); + extract_value( EmergencyCutsOffHandle, "EmergencyCutsOffHandle", line, ""); { std::map compressorpowers{ { "Main", 0 }, @@ -9841,6 +9862,8 @@ void TMoverParameters::LoadFIZ_Doors( std::string const &line ) { if( platformopenmethod == "Shift" ) { Doors.step_type = 1; } // przesuw extract_value( MirrorMaxShift, "MirrorMaxShift", line, "" ); + extract_value( MirrorVelClose, "MirrorVelClose", line, ""); + } void TMoverParameters::LoadFIZ_BuffCoupl( std::string const &line, int const Index ) { @@ -10099,6 +10122,7 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { EIMCtrlType = clamp( EIMCtrlType, 0, 3 ); extract_value( LocHandleTimeTraxx, "LocalBrakeTraxx", line, "" ); extract_value( EIMCtrlAdditionalZeros, "EIMCtrlAddZeros", line, "" ); + extract_value( EIMCtrlEmergency, "EIMCtrlEmergency", line, ""); extract_value( ScndS, "ScndS", line, "" ); // brak pozycji rownoleglej przy niskiej nastawie PSR diff --git a/Model3d.cpp b/Model3d.cpp index a822590c..9bce20be 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -171,7 +171,7 @@ void TSubModel::SetSelfIllum( float const Threshold, bool const Includechildren, int TSubModel::SeekFaceNormal(std::vector const &Masks, int const Startface, unsigned int const Mask, glm::vec3 const &Position, gfx::vertex_array const &Vertices) { // szukanie punktu stycznego do (pt), zwraca numer wierzchołka, a nie trójkąta - int facecount = iNumVerts / 3; // bo maska powierzchni jest jedna na trójkąt + int facecount = m_geometry.vertex_count / 3; for( int faceidx = Startface; faceidx < facecount; ++faceidx ) { // pętla po trójkątach, od trójkąta (f) if( Masks[ faceidx ] & Mask ) { @@ -209,16 +209,12 @@ inline void readMatrix(cParser &parser, float4x4 &matrix) parser >> matrix(x)[y]; }; -int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic) +std::pair TSubModel::Load( cParser &parser, bool dynamic ) { // Ra: VBO tworzone na poziomie modelu, a nie submodeli - iNumVerts = 0; -/* - iVboPtr = Pos; // pozycja w VBO -*/ auto token { parser.getToken() }; if( token != "type:" ) { std::string errormessage { - "Bad model: expected submodel type definition not found while loading model \"" + Model->NameGet() + "\"" + "Bad model: expected submodel type definition not found while loading model \"" + parser.Name() + "\"" + "\ncurrent model data stream content: \"" }; auto count { 10 }; while( ( true == parser.getTokens() ) @@ -231,7 +227,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic } errormessage += "(...)\""; ErrorLog( errormessage ); - return 0; + return { 0, 0 }; } { auto const type { parser.getToken() }; @@ -345,10 +341,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic if( fCosHotspotAngle > 1.0 ) { fCosHotspotAngle = std::cos( DegToRad( 0.5f * fCosHotspotAngle ) ); } - iNumVerts = 1; -/* - iFlags |= 0x4010; // rysowane w cyklu nieprzezroczystych, macierz musi zostać bez zmiany -*/ + m_geometry.vertex_count = 1; iFlags |= 0x4030; // drawn both in solid (light point) and transparent (light glare) phases } else if (eType < TP_ROTATOR) @@ -479,7 +472,10 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic // visibility range std::string discard; parser.getTokens(5, false); - parser >> discard >> fSquareMaxDist >> discard >> fSquareMinDist >> discard; + parser + >> discard >> fSquareMaxDist + >> discard >> fSquareMinDist + >> discard; if( fSquareMaxDist <= 0.0 ) { // 15km to więcej, niż się obecnie wyświetla @@ -511,168 +507,197 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic } if (eType < TP_ROTATOR) { // wczytywanie wierzchołków - parser.getTokens(2, false); - parser >> discard >> token; - // Ra 15-01: to wczytać jako tekst - jeśli pierwszy znak zawiera "*", to - // dalej będzie nazwa wcześniejszego submodelu, z którego należy wziąć - // wierzchołki - // zapewni to jakąś zgodność wstecz, bo zamiast liczby będzie ciąg, którego - // wartość powinna być uznana jako zerowa - // parser.getToken(iNumVerts); - if (token.front() == '*') - { // jeśli pierwszy znak jest gwiazdką, poszukać - // submodelu o nazwie bez tej gwiazdki i wziąć z - // niego wierzchołki - Error("Vertices reference not yet supported!"); - } - else + token = parser.getToken(); + if( token == "numindices:" ) // optional block, potentially preceeding vertex list + { + m_geometry.index_count = parser.getToken( false ); + Indices.resize( m_geometry.index_count ); + for( auto idx = 0; idx < m_geometry.index_count; ++idx ) { + Indices[ idx ] = parser.getToken( false ); + } + token = parser.getToken(); + } + if( ( token == "numverts:" ) || ( token == "numverts" ) ) { // normalna lista wierzchołków - iNumVerts = std::atoi(token.c_str()); - if (iNumVerts % 3) +/* + // Ra 15-01: to wczytać jako tekst - jeśli pierwszy znak zawiera "*", to + // dalej będzie nazwa wcześniejszego submodelu, z którego należy wziąć wierzchołki + // zapewni to jakąś zgodność wstecz, bo zamiast liczby będzie ciąg, którego + // wartość powinna być uznana jako zerowa + token = parser.getToken(); + if( token.front() == '*' ) { + // jeśli pierwszy znak jest gwiazdką, poszukać submodelu o nazwie bez tej gwiazdki i wziąć z niego wierzchołki + Error( "Vertices reference not yet supported!" ); + } +*/ + m_geometry.vertex_count = parser.getToken(false); + if( ( m_geometry.index_count <= 0 ) + && ( m_geometry.vertex_count % 3 != 0 ) ) { - iNumVerts = 0; - Error("Mesh error, (iNumVertices=" + std::to_string(iNumVerts) + ")%3<>0"); - return 0; + m_geometry.vertex_count = 0; + Error("Bad model: incomplete triangle encountered in submodel \"" + pName + "\""); + return { 0, 0 }; } - // Vertices=new GLVERTEX[iNumVerts]; - if (iNumVerts) { -/* - Vertices = new basic_vertex[iNumVerts]; -*/ - Vertices.resize( iNumVerts ); - int facecount = iNumVerts / 3; -/* - unsigned int *sg; // maski przynależności trójkątów do powierzchni - sg = new unsigned int[iNumFaces]; // maski powierzchni: 0 oznacza brak użredniania wektorów normalnych - int *wsp = new int[iNumVerts]; // z którego wierzchołka kopiować wektor normalny -*/ - std::vector sg; sg.resize( facecount ); // maski przynależności trójkątów do powierzchni - std::vector wsp; wsp.resize( iNumVerts );// z którego wierzchołka kopiować wektor normalny - int maska = 0; - int rawvertexcount = 0; // used to keep track of vertex indices in source file - for (int i = 0; i < iNumVerts; ++i) { - ++rawvertexcount; - // Ra: z konwersją na układ scenerii - będzie wydajniejsze wyświetlanie - wsp[i] = -1; // wektory normalne nie są policzone dla tego wierzchołka - if ((i % 3) == 0) { - // jeśli będzie maska -1, to dalej będą wierzchołki z wektorami normalnymi, podanymi jawnie - maska = parser.getToken(false); // maska powierzchni trójkąta - // dla maski -1 będzie 0, czyli nie ma wspólnych wektorów normalnych - sg[i / 3] = ( - ( maska == -1 ) ? - 0 : - maska ); - } - parser.getTokens(3, false); - parser - >> Vertices[i].position.x - >> Vertices[i].position.y - >> Vertices[i].position.z; - if (maska == -1) - { // jeśli wektory normalne podane jawnie - parser.getTokens(3, false); - parser - >> Vertices[i].normal.x - >> Vertices[i].normal.y - >> Vertices[i].normal.z; - if( glm::length2( Vertices[ i ].normal ) > 0.0f ) { - glm::normalize( Vertices[ i ].normal ); - } - else { - WriteLog( "Bad model: zero length normal vector specified in: \"" + pName + "\", vertex " + std::to_string(i), logtype::model ); - } - wsp[i] = i; // wektory normalne "są już policzone" - } - parser.getTokens(2, false); - parser - >> Vertices[i].texture.s - >> Vertices[i].texture.t; - if (i % 3 == 2) { - // jeżeli wczytano 3 punkty - if( true == degenerate( Vertices[ i ].position, Vertices[ i - 1 ].position, Vertices[ i - 2 ].position ) ) { - // jeżeli punkty się nakładają na siebie - --facecount; // o jeden trójkąt mniej - iNumVerts -= 3; // czyli o 3 wierzchołki - i -= 3; // wczytanie kolejnego w to miejsce - WriteLog("Bad model: degenerated triangle ignored in: \"" + pName + "\", vertices " + std::to_string(rawvertexcount-2) + "-" + std::to_string(rawvertexcount), logtype::model ); - } - if (i > 0) { - // jeśli pierwszy trójkąt będzie zdegenerowany, to zostanie usunięty i nie ma co sprawdzać - if ((glm::length(Vertices[i ].position - Vertices[i - 1].position) > 1000.0) - || (glm::length(Vertices[i - 1].position - Vertices[i - 2].position) > 1000.0) - || (glm::length(Vertices[i - 2].position - Vertices[i ].position) > 1000.0)) { - // jeżeli są dalej niż 2km od siebie //Ra 15-01: - // obiekt wstawiany nie powinien być większy niż 300m (trójkąty terenu w E3D mogą mieć 1.5km) - --facecount; // o jeden trójkąt mniej - iNumVerts -= 3; // czyli o 3 wierzchołki - i -= 3; // wczytanie kolejnego w to miejsce - WriteLog( "Bad model: too large triangle ignored in: \"" + pName + "\"", logtype::model ); - } - } - } - } -/* - glm::vec3 *n = new glm::vec3[iNumFaces]; // tablica wektorów normalnych dla trójkątów -*/ - std::vector facenormals; facenormals.reserve( facecount ); - for( int i = 0; i < facecount; ++i ) { - // pętla po trójkątach - będzie szybciej, jak wstępnie przeliczymy normalne trójkątów - auto facenormal = - glm::cross( - Vertices[ i * 3 ].position - Vertices[ i * 3 + 1 ].position, - Vertices[ i * 3 ].position - Vertices[ i * 3 + 2 ].position ); - facenormals.emplace_back( - glm::length2( facenormal ) > 0.0f ? - glm::normalize( facenormal ) : - glm::vec3() ); - } - glm::vec3 vertexnormal; // roboczy wektor normalny - for (int vertexidx = 0; vertexidx < iNumVerts; ++vertexidx) { - // pętla po wierzchołkach trójkątów - if( wsp[ vertexidx ] >= 0 ) { - // jeśli już był liczony wektor normalny z użyciem tego wierzchołka to wystarczy skopiować policzony wcześniej - Vertices[ vertexidx ].normal = Vertices[ wsp[ vertexidx ] ].normal; + if ( m_geometry.vertex_count ) { + Vertices.resize( m_geometry.vertex_count ); + if( m_geometry.index_count ) { + // indexed geometry chunks are expected to come with pre-generated normals and tangents, to avoid hassle required to generate them manually + auto vertices { std::begin( Vertices ) }; + for( auto idx = 0; idx < m_geometry.vertex_count; ++idx ) { + auto vertex { vertices + idx }; + parser.getTokens( 3 + 3 + 2 + 4, false ); + parser + >> vertex->position.x + >> vertex->position.y + >> vertex->position.z + >> vertex->normal.x + >> vertex->normal.y + >> vertex->normal.z + >> vertex->texture.s + >> vertex->texture.t + >> vertex->tangent.x + >> vertex->tangent.y + >> vertex->tangent.z + >> vertex->tangent.w; } - else { - // inaczej musimy dopiero policzyć - auto const faceidx = vertexidx / 3; // numer trójkąta - vertexnormal = glm::vec3(); // liczenie zaczynamy od zera - auto adjacenvertextidx = vertexidx; // zaczynamy dodawanie wektorów normalnych od własnego - while (adjacenvertextidx >= 0) { - // sumowanie z wektorem normalnym sąsiada (włącznie ze sobą) - if( glm::dot( vertexnormal, facenormals[ adjacenvertextidx / 3 ] ) > -0.99f ) { - wsp[ adjacenvertextidx ] = vertexidx; // informacja, że w tym wierzchołku jest już policzony wektor normalny - vertexnormal += facenormals[ adjacenvertextidx / 3 ]; + } + else { + // legacy geometry, more or less incomplete + // to stay on the safe side we'll potentially need to convert indexed geometry to raw triangle form, cal + auto vertices { std::begin( Vertices ) }; + int facecount = m_geometry.vertex_count / 3; + std::vector sg; sg.resize( facecount ); // maski przynależności trójkątów do powierzchni + std::vector wsp; wsp.resize( m_geometry.vertex_count );// z którego wierzchołka kopiować wektor normalny + int maska = 0; + int vertexidx = 0; // used to keep track of vertex indices in source file + for (auto idx = 0; idx < m_geometry.vertex_count; ++idx) { + ++vertexidx; + // Ra: z konwersją na układ scenerii - będzie wydajniejsze wyświetlanie + wsp[idx] = -1; // wektory normalne nie są policzone dla tego wierzchołka + if ((idx % 3) == 0) { + // jeśli będzie maska -1, to dalej będą wierzchołki z wektorami normalnymi, podanymi jawnie + maska = parser.getToken(false); // maska powierzchni trójkąta + // dla maski -1 będzie 0, czyli nie ma wspólnych wektorów normalnych + sg[idx / 3] = ( + ( maska == -1 ) ? + 0 : + maska ); + } + auto vertex { vertices + idx }; + parser.getTokens(3, false); + parser + >> vertex->position.x + >> vertex->position.y + >> vertex->position.z; + if (maska == -1) + { // jeśli wektory normalne podane jawnie + parser.getTokens(3, false); + parser + >> vertex->normal.x + >> vertex->normal.y + >> vertex->normal.z; + if( glm::length2( vertex->normal ) > 0.0f ) { + glm::normalize( vertex->normal ); } - /* else { - ErrorLog( "Bad model: opposite normals in the same smoothing group, check sub-model \"" + pName + "\" for two-sided faces and/or scaling", logtype::model ); + WriteLog( "Bad model: zero length normal vector specified in submodel \"" + pName + "\", vertex " + std::to_string(idx), logtype::model ); } - */ - // i szukanie od kolejnego trójkąta - adjacenvertextidx = SeekFaceNormal(sg, adjacenvertextidx / 3 + 1, sg[faceidx], Vertices[vertexidx].position, Vertices); + wsp[idx] = idx; // wektory normalne "są już policzone" + } + parser.getTokens(2, false); + parser + >> vertex->texture.s + >> vertex->texture.t; + + if (idx % 3 == 2) { + // jeżeli wczytano 3 punkty + if( true == degenerate( vertex->position, (vertex - 1)->position, (vertex - 2)->position ) ) { + // jeżeli punkty się nakładają na siebie + --facecount; // o jeden trójkąt mniej + m_geometry.vertex_count -= 3; // czyli o 3 wierzchołki + idx -= 3; // wczytanie kolejnego w to miejsce + WriteLog("Bad model: degenerated triangle ignored in: \"" + pName + "\", vertices " + std::to_string(vertexidx-2) + "-" + std::to_string(vertexidx), logtype::model ); + } + if (idx > 0) { + // jeśli pierwszy trójkąt będzie zdegenerowany, to zostanie usunięty i nie ma co sprawdzać + if ((glm::length((vertex )->position - (vertex-1)->position) > 1000.0) + || (glm::length((vertex-1)->position - (vertex-2)->position) > 1000.0) + || (glm::length((vertex-2)->position - (vertex )->position) > 1000.0)) { + // jeżeli są dalej niż 2km od siebie //Ra 15-01: + // obiekt wstawiany nie powinien być większy niż 300m (trójkąty terenu w E3D mogą mieć 1.5km) + --facecount; // o jeden trójkąt mniej + m_geometry.vertex_count -= 3; // czyli o 3 wierzchołki + idx -= 3; // wczytanie kolejnego w to miejsce + WriteLog( "Bad model: too large triangle ignored in: \"" + pName + "\"", logtype::model ); + } + } + } + } + + std::vector facenormals; facenormals.reserve( facecount ); + for( int i = 0; i < facecount; ++i ) { + // pętla po trójkątach - będzie szybciej, jak wstępnie przeliczymy normalne trójkątów + auto const vertex { vertices + ( i * 3 ) }; + auto facenormal = + glm::cross( + vertex->position - (vertex + 1)->position, + vertex->position - (vertex + 2)->position ); + facenormals.emplace_back( + glm::length2( facenormal ) > 0.0f ? + glm::normalize( facenormal ) : + glm::vec3() ); + } + glm::vec3 vertexnormal; // roboczy wektor normalny + for (int vertexidx = 0; vertexidx < m_geometry.vertex_count; ++vertexidx) { + // pętla po wierzchołkach trójkątów + auto vertex { vertices + vertexidx }; + if( wsp[ vertexidx ] >= 0 ) { + // jeśli już był liczony wektor normalny z użyciem tego wierzchołka to wystarczy skopiować policzony wcześniej + vertex->normal = ( vertices + wsp[ vertexidx ] )->normal; } - // Ra 15-01: należało by jeszcze uwzględnić skalowanie wprowadzane przez transformy, aby normalne po przeskalowaniu były jednostkowe - if( glm::length2( vertexnormal ) == 0.0f ) { - WriteLog( "Bad model: zero length normal vector generated for sub-model \"" + pName + "\"", logtype::model ); - } - Vertices[ vertexidx ].normal = ( - glm::length2( vertexnormal ) > 0.0f ? - glm::normalize( vertexnormal ) : - facenormals[ vertexidx / 3 ] ); // przepisanie do wierzchołka trójkąta - } - } - Vertices.resize( iNumVerts ); // in case we had some degenerate triangles along the way -/* - delete[] wsp; - delete[] n; - delete[] sg; -*/ - } + else { + // inaczej musimy dopiero policzyć + auto const faceidx = vertexidx / 3; // numer trójkąta + vertexnormal = glm::vec3(); // liczenie zaczynamy od zera + auto adjacenvertextidx = vertexidx; // zaczynamy dodawanie wektorów normalnych od własnego + while (adjacenvertextidx >= 0) { + // sumowanie z wektorem normalnym sąsiada (włącznie ze sobą) + if( glm::dot( vertexnormal, facenormals[ adjacenvertextidx / 3 ] ) > -0.99f ) { + wsp[ adjacenvertextidx ] = vertexidx; // informacja, że w tym wierzchołku jest już policzony wektor normalny + vertexnormal += facenormals[ adjacenvertextidx / 3 ]; + } + /* + else { + ErrorLog( "Bad model: opposite normals in the same smoothing group, check sub-model \"" + pName + "\" for two-sided faces and/or scaling", logtype::model ); + } + */ + // i szukanie od kolejnego trójkąta + adjacenvertextidx = SeekFaceNormal(sg, adjacenvertextidx / 3 + 1, sg[faceidx], vertex->position, Vertices); + } + // Ra 15-01: należało by jeszcze uwzględnić skalowanie wprowadzane przez transformy, aby normalne po przeskalowaniu były jednostkowe + if( glm::length2( vertexnormal ) == 0.0f ) { + WriteLog( "Bad model: zero length normal vector generated for sub-model \"" + pName + "\"", logtype::model ); + } + vertex->normal = ( + glm::length2( vertexnormal ) > 0.0f ? + glm::normalize( vertexnormal ) : + facenormals[ vertexidx / 3 ] ); // przepisanie do wierzchołka trójkąta + } + } + Vertices.resize( m_geometry.vertex_count ); // in case we had some degenerate triangles along the way + gfx::calculate_tangents( Vertices, GL_TRIANGLES ); + gfx::calculate_indices( Indices, Vertices ); + // update values potentially changed by indexing + m_geometry.index_count = Indices.size(); + m_geometry.vertex_count = Vertices.size(); + } + } else // gdy brak wierzchołków { eType = TP_ROTATOR; // submodel pomocniczy, ma tylko macierz przekształcenia - /*iVboPtr =*/ iNumVerts = 0; // dla formalności + // dla formalności + m_geometry.vertex_offset = 0; + m_geometry.vertex_count = 0; } } // obsługa submodelu z własną listą wierzchołków } @@ -681,44 +706,46 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic // dla smt_Mesh std::string discard; parser.getTokens(2, false); - parser >> discard >> iNumVerts; -/* - // Vertices=new GLVERTEX[iNumVerts]; - Vertices = new basic_vertex[iNumVerts]; -*/ - Vertices.resize( iNumVerts ); - int i; + parser >> discard >> m_geometry.vertex_count; + Vertices.resize( m_geometry.vertex_count ); + int idx; unsigned int color; - for (i = 0; i < iNumVerts; ++i) + auto vertices { std::begin( Vertices ) }; + for (idx = 0; idx < m_geometry.vertex_count; ++idx) { - if (i % 3 == 0) + if (idx % 3 == 0) { parser.ignoreToken(); // maska powierzchni trójkąta } - parser.getTokens(5, false); + auto vertex { vertices + idx }; + parser.getTokens(5, false); parser - >> Vertices[i].position.x - >> Vertices[i].position.y - >> Vertices[i].position.z + >> vertex->position.x + >> vertex->position.y + >> vertex->position.z >> color // zakodowany kolor >> discard; - Vertices[i].normal.x = ((color) & 0xff) / 255.0f; // R - Vertices[i].normal.y = ((color >> 8) & 0xff) / 255.0f; // G - Vertices[i].normal.z = ((color >> 16) & 0xff) / 255.0f; // B + vertex->normal = { ( ( color ) & 0xff ) / 255.0f, // R + ( ( color >> 8 ) & 0xff ) / 255.0f, // G + ( ( color >> 16 ) & 0xff ) / 255.0f }; // B } } else if( eType == TP_FREESPOTLIGHT ) { // single light points only have single data point, duh Vertices.emplace_back(); - iNumVerts = 1; + m_geometry.vertex_count = 1; } // Visible=true; //się potem wyłączy w razie potrzeby // iFlags|=0x0200; //wczytano z pliku tekstowego (jest właścicielem tablic) - if (iNumVerts < 1) + if (m_geometry.vertex_count < 1) iFlags &= ~0x3F; // cykl renderowania uzależniony od potomnych - return iNumVerts; // do określenia wielkości VBO + + return { + m_geometry.index_count, + m_geometry.vertex_count }; // do określenia wielkości VBO }; +/* 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; @@ -748,6 +775,7 @@ int TSubModel::TriangleAdd(TModel3d *m, material_handle tex, int tri) s->iNumVerts += tri; // aktualizacja ilości wierzchołków return s->iNumVerts - tri; // zwraca pozycję tych trójkątów w submodelu }; +*/ /* basic_vertex *TSubModel::TrianglePtr(int tex, int pos, glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ) { // zwraca wskaźnik do wypełnienia tabeli wierzchołków, używane przy tworzeniu E3D terenu @@ -1164,49 +1192,111 @@ void TSubModel::RaAnimation(glm::mat4 &m, TAnimType a) //--------------------------------------------------------------------------- -void TSubModel::serialize_geometry( std::ostream &Output ) const { +void TSubModel::serialize_geometry( std::ostream &Output, bool const Packed, bool const Indexed ) const { if( Child ) { - Child->serialize_geometry( Output ); + Child->serialize_geometry( Output, Packed, Indexed ); } - if( m_geometry != null_handle ) { - for( auto const &vertex : GfxRenderer->Vertices( m_geometry ) ) { - vertex.serialize( Output ); + if( m_geometry.handle != null_handle ) { + if( Packed ) { + for( auto const &vertex : GfxRenderer->Vertices( m_geometry.handle ) ) { + vertex.serialize_packed( Output, Indexed ); + } + } + else { + for( auto const &vertex : GfxRenderer->Vertices( m_geometry.handle ) ) { + vertex.serialize( Output, Indexed ); + } } } if( Next ) { - Next->serialize_geometry( Output ); + Next->serialize_geometry( Output, Packed, Indexed ); + } +}; + +int TSubModel::index_size() const { + + int size { 1 }; + if( Child ) { + size = std::max( size, Child->index_size() ); + } + if( ( size < 4 ) && ( m_geometry.handle != null_handle ) ) { + auto const indexcount { GfxRenderer->Indices( m_geometry.handle ).size() }; + size = ( + indexcount >= ( 1 << 16 ) ? 4 : + indexcount >= ( 1 << 8 ) ? 2 : + 1 ); + } + if( ( size < 4 ) && ( Next ) ) { + size = std::max( size, Next->index_size() ); + } + return size; +} + +void TSubModel::serialize_indices( std::ostream &Output, int const Size ) const { + + if( Child ) { + Child->serialize_indices( Output, Size ); + } + if( m_geometry.handle != null_handle ) { + switch( Size ) { + case 1: { + for( auto const &index : GfxRenderer->Indices( m_geometry.handle ) ) { + sn_utils::s_uint8( Output, index ); + } + break; + } + case 2: { + for( auto const &index : GfxRenderer->Indices( m_geometry.handle ) ) { + sn_utils::ls_uint16( Output, index ); + } + break; + } + case 4: { + for( auto const &index : GfxRenderer->Indices( m_geometry.handle ) ) { + sn_utils::ls_uint32( Output, index ); + } + break; + } + default: { break; } + } + } + if( Next ) { + Next->serialize_indices( Output, Size ); } }; void -TSubModel::create_geometry( std::size_t &Dataoffset, gfx::geometrybank_handle const &Bank ) { +TSubModel::create_geometry( std::size_t &Indexoffset, std::size_t &Vertexoffset, gfx::geometrybank_handle const &Bank ) { // data offset is used to determine data offset of each submodel into single shared geometry bank // (the offsets are part of legacy system which we now need to work around for backward compatibility) - if( Child ) - Child->create_geometry( Dataoffset, Bank ); + if( Child ) { + Child->create_geometry( Indexoffset, Vertexoffset, Bank ); + } if( false == Vertices.empty() ) { - tVboPtr = static_cast( Dataoffset ); - Dataoffset += Vertices.size(); + m_geometry.index_offset = static_cast( Indexoffset ); + Indexoffset += Indices.size(); + m_geometry.vertex_offset = static_cast( Vertexoffset ); + Vertexoffset += Vertices.size(); // conveniently all relevant custom node types use GL_POINTS, or we'd have to determine the type on individual basis auto type = ( eType < TP_ROTATOR ? eType : GL_POINTS ); - m_geometry = GfxRenderer->Insert( Vertices, Bank, type ); + m_geometry.handle = GfxRenderer->Insert( Indices, Vertices, Bank, type ); } - if( m_geometry != 0 ) { + if( m_geometry.handle != 0 ) { // calculate bounding radius while we're at it float squaredradius {}; // if this happens to be root node it may already have non-squared radius of the largest child // since we're comparing squared radii, we need to square it back for correct results m_boundingradius *= m_boundingradius; auto const submodeloffset { offset( std::numeric_limits::max() ) }; - for( auto const &vertex : GfxRenderer->Vertices( m_geometry ) ) { + for( auto const &vertex : GfxRenderer->Vertices( m_geometry.handle ) ) { squaredradius = glm::length2( submodeloffset + vertex.position ); if( squaredradius > m_boundingradius ) { m_boundingradius = squaredradius; @@ -1224,8 +1314,9 @@ TSubModel::create_geometry( std::size_t &Dataoffset, gfx::geometrybank_handle co m_boundingradius ); } - if( Next ) - Next->create_geometry( Dataoffset, Bank ); + if( Next ) { + Next->create_geometry( Indexoffset, Vertexoffset, Bank ); + } } void TSubModel::ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ) @@ -1312,13 +1403,13 @@ void TSubModel::ReplaceMaterial(const std::string &name) // obliczenie maksymalnej wysokości, na początek ślizgu w pantografie float TSubModel::MaxY( float4x4 const &m ) { // tylko dla trójkątów liczymy - if( eType != 4 ) { return 0; } + if( eType != GL_TRIANGLES ) { return 0; } auto maxy { 0.0f }; // binary and text models invoke this function at different stages, either after or before geometry data was sent to the geometry manager - if( m_geometry != null_handle ) { + if( m_geometry.handle != null_handle ) { - for( auto const &vertex : GfxRenderer->Vertices( m_geometry ) ) { + for( auto const &vertex : GfxRenderer->Vertices( m_geometry.handle ) ) { maxy = std::max( maxy, m[ 0 ][ 1 ] * vertex.position.x @@ -1343,14 +1434,6 @@ float TSubModel::MaxY( float4x4 const &m ) { }; //--------------------------------------------------------------------------- -TModel3d::TModel3d() -{ - Root = NULL; - iFlags = 0; - iSubModelsCount = 0; - iNumVerts = 0; // nie ma jeszcze wierzchołków -}; - TModel3d::~TModel3d() { if (iFlags & 0x0200) { @@ -1430,8 +1513,8 @@ TSubModel::offset( float const Geometrytestoffsetthreshold ) const { // for such cases we resort to an estimate from submodel geometry // TODO: do proper bounding area calculation for submodel when loading mesh and grab the centre point from it here auto const &vertices { ( - m_geometry != null_handle ? - GfxRenderer->Vertices( m_geometry ) : + m_geometry.handle != null_handle ? + GfxRenderer->Vertices( m_geometry.handle ) : Vertices ) }; if( false == vertices.empty() ) { // transformation matrix for the submodel can still contain rotation and/or scaling, @@ -1541,16 +1624,15 @@ void TSubModel::serialize(std::ostream &s, sn_utils::ls_uint32(s, iFlags); sn_utils::ls_int32(s, (int32_t)get_container_pos(transforms, *fMatrix)); - sn_utils::ls_int32(s, iNumVerts); - sn_utils::ls_int32(s, tVboPtr); + sn_utils::ls_int32( s, m_geometry.vertex_count ); + sn_utils::ls_int32( s, m_geometry.vertex_offset ); if (m_material <= 0) sn_utils::ls_int32(s, m_material); else sn_utils::ls_int32(s, (int32_t)get_container_pos(textures, m_materialname)); -// sn_utils::ls_float32(s, fVisible); - sn_utils::ls_float32(s, 1.f); + sn_utils::ls_float32(s, 1.f); // fVisible sn_utils::ls_float32(s, fLight); sn_utils::s_vec4(s, f4Ambient); @@ -1572,6 +1654,9 @@ void TSubModel::serialize(std::ostream &s, sn_utils::ls_float32(s, fCosHotspotAngle); sn_utils::ls_float32(s, fCosViewAngle); + sn_utils::ls_int32( s, m_geometry.index_count ); + sn_utils::ls_int32( s, m_geometry.index_offset ); + size_t fill = end - s.tellp(); for (size_t i = 0; i < fill; i++) s.put(0); @@ -1615,9 +1700,21 @@ void TModel3d::SaveToBinFile(std::string const &FileName) for (size_t i = 0; i < transforms.size(); i++) transforms[i].serialize_float32(s); - sn_utils::ls_uint32(s, MAKE_ID4('V', 'N', 'T', '0')); - sn_utils::ls_uint32(s, 8 + iNumVerts * 32); - Root->serialize_geometry( s ); + auto const isindexed { m_indexcount > 0 }; + if( isindexed ) { + auto const indexsize { Root->index_size() }; + sn_utils::ls_uint32( s, MAKE_ID4( 'I', 'D', 'X', '0' + indexsize ) ); + sn_utils::ls_uint32( s, 8 + m_indexcount * indexsize ); + Root->serialize_indices( s, indexsize ); + sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '1' ) ); + sn_utils::ls_uint32( s, 8 + m_vertexcount * 20 ); + Root->serialize_geometry( s, true, true ); + } + else { + sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' ) ); + sn_utils::ls_uint32( s, 8 + m_vertexcount * 32 ); + Root->serialize_geometry( s, false, false ); + } if (textures.size()) { @@ -1666,12 +1763,11 @@ void TSubModel::deserialize(std::istream &s) iFlags = sn_utils::ld_uint32(s); iMatrix = sn_utils::ld_int32(s); - iNumVerts = sn_utils::ld_int32(s); - tVboPtr = sn_utils::ld_int32(s); + m_geometry.vertex_count = sn_utils::ld_int32( s ); + m_geometry.vertex_offset = sn_utils::ld_int32( s ); iTexture = sn_utils::ld_int32(s); -// fVisible = sn_utils::ld_float32(s); - auto discard = sn_utils::ld_float32(s); + auto discard = sn_utils::ld_float32(s); // fVisible fLight = sn_utils::ld_float32(s); f4Ambient = sn_utils::d_vec4(s); @@ -1691,7 +1787,9 @@ void TSubModel::deserialize(std::istream &s) fCosFalloffAngle = sn_utils::ld_float32(s); fCosHotspotAngle = sn_utils::ld_float32(s); fCosViewAngle = sn_utils::ld_float32(s); - + // HACK: the values will be 0 also when reading legacy chunk + m_geometry.index_count = sn_utils::ld_int32( s ); + m_geometry.index_offset = sn_utils::ld_int32( s ); // necessary rotations were already done during t3d->e3d conversion m_rotation_init_done = true; } @@ -1711,7 +1809,7 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) uint32_t size = sn_utils::ld_uint32(s) - 8; std::streampos end = s.tellg() + (std::streampos)size; - if ((type & 0x00FFFFFF) == MAKE_ID4('S', 'U', 'B', 0)) + if ((type & 0x00FFFFFF) == MAKE_ID4('S', 'U', 'B', 0)) // submodels { if (Root != nullptr) throw std::runtime_error("e3d: duplicated SUB chunk"); @@ -1727,68 +1825,104 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) Root[i].deserialize(s); } } - else if (type == MAKE_ID4('V', 'N', 'T', '0')) + else if ((type & 0x00FFFFFF) == MAKE_ID4('V', 'N', 'T', 0)) // geometry vertices { -/* - if (m_pVNT != nullptr) - throw std::runtime_error("e3d: duplicated VNT chunk"); - - size_t vt_cnt = size / 32; - iNumVerts = (int)vt_cnt; - m_nVertexCount = (int)vt_cnt; - m_pVNT.resize( vt_cnt ); - for (size_t i = 0; i < vt_cnt; i++) - m_pVNT[i].deserialize(s); -*/ // we rely on the SUB chunk coming before the vertex data, and on the overall vertex count matching the size of data in the chunk. // geometry associated with chunks isn't stored in the same order as the chunks themselves, so we need to sort that out first if( Root == nullptr ) throw std::runtime_error( "e3d: VNT chunk encountered before SUB chunk" ); std::vector< std::pair > submodeloffsets; // vertex data offset, submodel index submodeloffsets.reserve( iSubModelsCount ); - for( int submodelindex = 0; submodelindex < iSubModelsCount; ++submodelindex ) { - auto const &submodel = Root[ submodelindex ]; - if( submodel.iNumVerts <= 0 ) { continue; } - submodeloffsets.emplace_back( submodel.tVboPtr, submodelindex ); + for( auto submodelindex = 0; submodelindex < iSubModelsCount; ++submodelindex ) { + auto const &submodelgeometry { Root[ submodelindex ].m_geometry }; + if( submodelgeometry.vertex_count <= 0 ) { continue; } + submodeloffsets.emplace_back( submodelgeometry.vertex_offset, submodelindex ); } std::sort( - submodeloffsets.begin(), - submodeloffsets.end(), + std::begin( submodeloffsets ), + std::end( submodeloffsets ), []( std::pair const &Left, std::pair const &Right ) { return (Left.first) < (Right.first); } ); // once sorted we can grab geometry as it comes, and assign it to the chunks it belongs to + size_t const vertextype { ( ( ( type & 0xFF000000 ) >> 24 ) - '0' ) }; for( auto const &submodeloffset : submodeloffsets ) { - auto &submodel = Root[ submodeloffset.second ]; - gfx::vertex_array vertices; vertices.resize( submodel.iNumVerts ); - iNumVerts += submodel.iNumVerts; - for( auto &vertex : vertices ) { - vertex.deserialize( s ); - if( submodel.eType < TP_ROTATOR ) { - // normal vectors debug routine - auto normallength = glm::length2( vertex.normal ); - if( ( false == submodel.m_normalizenormals ) - && ( std::abs( normallength - 1.0f ) > 0.01f ) ) { - submodel.m_normalizenormals = TSubModel::normalize; // we don't know if uniform scaling would suffice - WriteLog( "Bad model: non-unit normal vector(s) encountered during sub-model geometry deserialization", logtype::model ); + auto &submodel { Root[ submodeloffset.second ] }; + auto const &submodelgeometry { submodel.m_geometry }; + submodel.Vertices.resize( submodelgeometry.vertex_count ); + m_vertexcount += submodelgeometry.vertex_count; + if( vertextype > 0 ) { + // expanded chunk formats + for( auto &vertex : submodel.Vertices ) { + vertex.deserialize_packed( s, vertextype > 0 ); + } + } + else { + // legacy vnt0 format + for( auto &vertex : submodel.Vertices ) { + vertex.deserialize( s, vertextype > 0 ); + if( submodel.eType < TP_ROTATOR ) { + // normal vectors debug routine + if( ( false == submodel.m_normalizenormals ) + && ( std::abs( glm::length2( vertex.normal ) - 1.0f ) > 0.01f ) ) { + submodel.m_normalizenormals = TSubModel::normalize; // we don't know if uniform scaling would suffice + WriteLog( "Bad model: non-unit normal vector(s) encountered during sub-model geometry deserialization", logtype::model ); + } } } } - // remap geometry type for custom type submodels - int type; - switch( submodel.eType ) { - case TP_FREESPOTLIGHT: - case TP_STARS: { - type = GL_POINTS; - break; } + } + } + else if( ( type & 0x00FFFFFF ) == MAKE_ID4( 'I', 'D', 'X', 0 ) ) // geometry indices + { + // handled similarly to vertex data chunk + // we rely on the SUB chunk coming before the vertex data, and on the overall vertex count matching the size of data in the chunk. + // geometry associated with chunks isn't stored in the same order as the chunks themselves, so we need to sort that out first + if( Root == nullptr ) + throw std::runtime_error( "e3d: IDX chunk encountered before SUB chunk" ); + std::vector< std::pair > submodeloffsets; // index data offset, submodel index + submodeloffsets.reserve( iSubModelsCount ); + for( auto submodelindex = 0; submodelindex < iSubModelsCount; ++submodelindex ) { + auto const &submodelgeometry { Root[ submodelindex ].m_geometry }; + if( submodelgeometry.index_count <= 0 ) { continue; } + submodeloffsets.emplace_back( submodelgeometry.index_offset, submodelindex ); + } + std::sort( + std::begin( submodeloffsets ), + std::end( submodeloffsets ), + []( std::pair const &Left, std::pair const &Right ) { + return (Left.first) < (Right.first); } ); + // once sorted we can grab indices in a continuous read, and assign them to the chunks they belong to + size_t const indexsize { ( ( ( type & 0xFF000000 ) >> 24 ) - '0' ) }; + for( auto const &submodeloffset : submodeloffsets ) { + auto &submodel { Root[ submodeloffset.second ] }; + auto const &submodelgeometry { submodel.m_geometry }; + submodel.Indices.resize( submodelgeometry.index_count ); + m_indexcount += submodelgeometry.index_count; + switch( indexsize ) { + case 1: { + for( auto &index : submodel.Indices ) { + index = sn_utils::d_uint8( s ); + } + break; + } + case 2: { + for( auto &index : submodel.Indices ) { + index = sn_utils::ld_uint16( s ); + } + break; + } + case 4: { + for( auto &index : submodel.Indices ) { + index = sn_utils::ld_uint32( s ); + } + break; + } default: { - type = submodel.eType; break; } } - submodel.m_geometry = GfxRenderer->Insert( vertices, m_geometrybank, type ); } - - } + } else if (type == MAKE_ID4('T', 'R', 'A', '0')) { if( false == Matrices.empty() ) @@ -1842,6 +1976,20 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) Root[i].ChildGet()->Parent = &Root[i]; if (Root[i].NextGet()) Root[i].NextGet()->Parent = Root[i].Parent; + + // remap geometry type for custom type submodels + int type; + switch( Root[i].eType ) { + case TP_FREESPOTLIGHT: + case TP_STARS: { + type = GL_POINTS; + break; } + default: { + type = Root[i].eType; + break; + } + } + Root[i].m_geometry.handle = GfxRenderer->Insert( Root[i].Indices, Root[i].Vertices, m_geometrybank, type ); } } @@ -1996,11 +2144,13 @@ void TModel3d::LoadFromBinFile(std::string const &FileName, bool dynamic) TSubModel* TModel3d::AppendChildFromGeometry(const std::string &name, const std::string &parent, const gfx::vertex_array &data) { + // todo: indexed geometry + iFlags |= 0x0200; TSubModel *sm = new TSubModel(); sm->Parent = AddToNamed(parent.c_str(), sm); - sm->iNumVerts = data.size(); + sm->m_geometry.vertex_count = data.size(); sm->eType = GL_TRIANGLES; sm->pName = name; sm->m_material = GfxRenderer->Fetch_Material("colored"); @@ -2012,7 +2162,7 @@ TSubModel* TModel3d::AppendChildFromGeometry(const std::string &name, const std: if (data.empty()) sm->iFlags &= ~0x3F; sm->Vertices = data; - iNumVerts += data.size(); + m_vertexcount += data.size(); if (!Root) Root = sm; @@ -2027,7 +2177,6 @@ void TModel3d::LoadFromTextFile(std::string const &FileName, bool dynamic) cParser parser(FileName, cParser::buffer_FILE); // Ra: tu powinno być "models/"... TSubModel *SubModel; std::string token = parser.getToken(); - iNumVerts = 0; // w konstruktorze to jest while (token != "" || parser.eof()) { std::string parent; @@ -2038,8 +2187,11 @@ void TModel3d::LoadFromTextFile(std::string const &FileName, bool dynamic) break; } SubModel = new TSubModel(); - iNumVerts += SubModel->Load(parser, this, /*iNumVerts,*/ dynamic); - + { + auto const result { SubModel->Load( parser, dynamic ) }; + m_indexcount += result.first; + m_vertexcount += result.second; + } // będzie potrzebne do wyliczenia pozycji, np. pantografu SubModel->Parent = AddToNamed(parent.c_str(), SubModel); @@ -2078,12 +2230,13 @@ void TModel3d::Init() Root->InitialRotate(true); } iFlags |= Root->FlagsCheck() | 0x8000; // flagi całego modelu - if (iNumVerts) { + if (m_vertexcount) { if( m_geometrybank == null_handle ) { m_geometrybank = GfxRenderer->Create_Bank(); } - std::size_t dataoffset = 0; - Root->create_geometry( dataoffset, m_geometrybank ); + std::size_t indexoffset = 0; + std::size_t vertexoffset = 0; + Root->create_geometry( indexoffset, vertexoffset, m_geometrybank ); } // determine final bounding radius from the root-level siblings auto const *root { Root }; diff --git a/Model3d.h b/Model3d.h index ba40ce6c..d80c27aa 100644 --- a/Model3d.h +++ b/Model3d.h @@ -16,6 +16,8 @@ http://mozilla.org/MPL/2.0/. #include "material.h" #include "gl/query.h" +#define EU07_USE_GEOMETRYINDEXING + // Ra: specjalne typy submodeli, poza tym GL_TRIANGLES itp. const int TP_ROTATOR = 256; const int TP_FREESPOTLIGHT = 257; @@ -67,6 +69,13 @@ public: rescale, normalize }; + struct geometry_data { + gfx::geometry_handle handle; + int vertex_offset; + int vertex_count; + int index_offset; + int index_count; + }; private: int iNext{ 0 }; @@ -96,8 +105,6 @@ private: float4x4 *fMatrix = nullptr; // pojedyncza precyzja wystarcza int iMatrix; // w pliku binarnym jest numer matrycy }; - int iNumVerts { -1 }; // ilość wierzchołków (1 dla FreeSpotLight) - int tVboPtr; // początek na liście wierzchołków albo indeksów int iTexture { 0 }; // numer nazwy tekstury, -1 wymienna, 0 brak float fLight { -1.0f }; // próg jasności światła do zadziałania selfillum glm::vec4 @@ -124,8 +131,6 @@ private: TSubModel *Next { nullptr }; TSubModel *Child { nullptr }; -public: // temporary access, clean this up during refactoring - gfx::geometry_handle m_geometry { 0, 0 }; // geometry of the submodel private: material_handle m_material { null_handle }; // numer tekstury, -1 wymienna, 0 brak bool bWire { false }; // nie używane, ale wczytywane @@ -136,7 +141,9 @@ private: public: // chwilowo float3 v_TransVector { 0.0f, 0.0f, 0.0f }; + geometry_data m_geometry { /*this,*/ { 0, 0 }, 0, 0, 0, 0 }; gfx::vertex_array Vertices; + gfx::index_array Indices; float m_boundingradius { 0 }; std::uintptr_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację TAnimType b_aAnim{ TAnimType::at_None }; // kody animacji oddzielnie, bo zerowane @@ -163,7 +170,7 @@ public: static std::string *pasText; // tekst dla wyświetlacza (!!!! do przemyślenia) TSubModel() = default; ~TSubModel(); - int Load(cParser &Parser, TModel3d *Model, /*int Pos,*/ bool dynamic); + std::pair Load(cParser &Parser, /*TModel3d *Model, int Pos,*/ bool dynamic); void ChildAdd(TSubModel *SubModel); void NextAdd(TSubModel *SubModel); TSubModel * NextGet() { return Next; }; @@ -174,7 +181,9 @@ public: std::tuple find_replacable4(); // locates particle emitter submodels and adds them to provided list void find_smoke_sources( nameoffset_sequence &Sourcelist ) const; +#ifndef EU07_USE_GEOMETRYINDEXING int TriangleAdd(TModel3d *m, material_handle tex, int tri); +#endif void SetRotate(float3 vNewRotateAxis, float fNewAngle); void SetRotateXYZ( Math3D::vector3 vNewAngles); void SetRotateXYZ(float3 vNewAngles); @@ -188,8 +197,8 @@ public: glm::vec3 offset( float const Geometrytestoffsetthreshold = 0.f ) const; inline void Hide() { iVisible = 0; }; - void create_geometry( std::size_t &Dataoffset, gfx::geometrybank_handle const &Bank ); - uint32_t FlagsCheck(); + void create_geometry( std::size_t &Indexoffset, std::size_t &Vertexoffset, gfx::geometrybank_handle const &Bank ); + uint32_t FlagsCheck(); void WillBeAnimated() { iFlags |= 0x4000; }; void InitialRotate(bool doit); @@ -233,7 +242,9 @@ public: std::vector&, std::vector&, std::vector&); - void serialize_geometry( std::ostream &Output ) const; + void serialize_geometry( std::ostream &Output, bool const Packed, bool const Indexed ) const; + int index_size() const; + void serialize_indices( std::ostream &Output, int const Size ) const; // places contained geometry in provided ground node }; @@ -243,23 +254,23 @@ class TModel3d friend opengl33_renderer; private: - TSubModel *Root; // drzewo submodeli - uint32_t iFlags; // Ra: czy submodele mają przezroczyste tekstury + TSubModel *Root { nullptr }; // drzewo submodeli + uint32_t iFlags { 0 }; // Ra: czy submodele mają przezroczyste tekstury public: // Ra: tymczasowo - int iNumVerts; // ilość wierzchołków (gdy nie ma VBO, to m_nVertexCount=0) gfx::geometrybank_handle m_geometrybank; - bool m_geometrycreated { false }; + int m_indexcount { 0 }; + int m_vertexcount { 0 }; // ilość wierzchołków private: std::vector Textures; // nazwy tekstur std::vector Names; // nazwy submodeli std::vector Matrices; // submodel matrices - int iSubModelsCount; // Ra: używane do tworzenia binarnych + int iSubModelsCount { 0 }; // Ra: używane do tworzenia binarnych std::string asBinary; // nazwa pod którą zapisać model binarny std::string m_filename; nameoffset_sequence m_smokesources; // list of particle sources defined in the model public: - TModel3d(); + TModel3d() = default; ~TModel3d(); float bounding_radius() const { return ( diff --git a/Train.cpp b/Train.cpp index da4d21f4..46acc2be 100644 --- a/Train.cpp +++ b/Train.cpp @@ -594,15 +594,13 @@ dictionary_source *TTrain::GetTrainState() { dict->insert( "manual_brake", ( mvOccupied->ManualBrakePos > 0 ) ); bool const bEP = ( mvControlled->LocHandle->GetCP() > 0.2 ) || ( fEIMParams[ 0 ][ 5 ] > 0.01 ); dict->insert( "dir_brake", bEP ); - bool bPN; + bool bPN { false }; if( ( typeid( *mvOccupied->Hamulec ) == typeid( TLSt ) ) || ( typeid( *mvOccupied->Hamulec ) == typeid( TEStED ) ) ) { TBrake* temp_ham = mvOccupied->Hamulec.get(); bPN = ( static_cast( temp_ham )->GetEDBCP() > 0.2 ); } - else - bPN = false; dict->insert( "indir_brake", bPN ); dict->insert( "brake_delay_flag", mvOccupied->BrakeDelayFlag ); dict->insert( "brake_op_mode_flag", mvOccupied->BrakeOpModeFlag ); diff --git a/geometrybank.cpp b/geometrybank.cpp index bc836f8e..ff4fcc29 100644 --- a/geometrybank.cpp +++ b/geometrybank.cpp @@ -17,39 +17,57 @@ http://mozilla.org/MPL/2.0/. namespace gfx { void -basic_vertex::serialize( std::ostream &s ) const { - - sn_utils::ls_float32( s, position.x ); - sn_utils::ls_float32( s, position.y ); - sn_utils::ls_float32( s, position.z ); - - sn_utils::ls_float32( s, normal.x ); - sn_utils::ls_float32( s, normal.y ); - sn_utils::ls_float32( s, normal.z ); +basic_vertex::serialize( std::ostream &s, bool const Tangent ) const { + sn_utils::s_vec3( s, position ); + sn_utils::s_vec3( s, normal ); sn_utils::ls_float32( s, texture.x ); sn_utils::ls_float32( s, texture.y ); + if( Tangent ) { + sn_utils::s_vec4( s, tangent ); + } } void -basic_vertex::deserialize( std::istream &s ) { - - position.x = sn_utils::ld_float32( s ); - position.y = sn_utils::ld_float32( s ); - position.z = sn_utils::ld_float32( s ); - - normal.x = sn_utils::ld_float32( s ); - normal.y = sn_utils::ld_float32( s ); - normal.z = sn_utils::ld_float32( s ); +basic_vertex::deserialize( std::istream &s, bool const Tangent ) { + position = sn_utils::d_vec3( s ); + normal = sn_utils::d_vec3( s ); texture.x = sn_utils::ld_float32( s ); texture.y = sn_utils::ld_float32( s ); + if( Tangent ) { + tangent = sn_utils::d_vec4( s ); + } +} + +void +basic_vertex::serialize_packed( std::ostream &s, bool const Tangent ) const { + + sn_utils::ls_uint64( s, glm::packHalf4x16( { position, 0.f } ) ); + sn_utils::ls_uint32( s, glm::packSnorm3x10_1x2( { normal, 0.f } ) ); + sn_utils::ls_uint16( s, glm::packHalf1x16( texture.x ) ); + sn_utils::ls_uint16( s, glm::packHalf1x16( texture.y ) ); + if( Tangent ) { + sn_utils::ls_uint32( s, glm::packSnorm3x10_1x2( tangent ) ); + } +} + +void +basic_vertex::deserialize_packed( std::istream &s, bool const Tangent ) { + + position = glm::unpackHalf4x16( sn_utils::ld_uint64( s ) ); + normal = glm::unpackSnorm3x10_1x2( sn_utils::ld_uint32( s ) ); + texture.x = glm::unpackHalf1x16( sn_utils::ld_uint16( s ) ); + texture.y = glm::unpackHalf1x16( sn_utils::ld_uint16( s ) ); + if( Tangent ) { + tangent = glm::unpackSnorm3x10_1x2( sn_utils::ld_uint32( s ) ); + } } // based on // Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”. // Terathon Software, 2001. http://terathon.com/code/tangent.html -void calculate_tangent(vertex_array &vertices, int type) +void calculate_tangents(vertex_array &vertices, int type) { size_t vertex_count = vertices.size(); @@ -157,9 +175,46 @@ void calculate_tangent(vertex_array &vertices, int type) } } +void calculate_indices( index_array &Indices, vertex_array &Vertices ) { + + Indices.resize( Vertices.size() ); + std::iota( std::begin( Indices ), std::end( Indices ), 0 ); + // gather instances of used verices, replace the original vertex bank with it after you're done + vertex_array indexedvertices; + indexedvertices.reserve( std::max( 100, Vertices.size() / 3 ) ); // optimistic guesstimate, but should reduce re-allocation somewhat + auto const matchtolerance { 1e-5f }; + for( auto idx = 0; idx < Indices.size(); ++idx ) { + if( Indices[ idx ] < idx ) { + // this index is pointing to a vertex out of linear order, i.e. it's an already processed duplicate we can skip + continue; + } + // due to duplicate removal our vertex will likely have different index in the processed set + Indices[ idx ] = indexedvertices.size(); + // see if there's any pointers in the remaining index subrange to similar enough vertices + // if found, remap these to use our current vertex instead + auto vertex { Vertices[ idx ] }; + auto matchiter { std::cbegin( Vertices ) + idx }; + for( auto matchidx = idx + 1; matchidx < Indices.size(); ++matchidx ) { + ++matchiter; + if( ( glm::all( glm::epsilonEqual( vertex.position, matchiter->position, matchtolerance ) ) ) + && ( glm::all( glm::epsilonEqual( vertex.normal, matchiter->normal, matchtolerance ) ) ) + && ( glm::all( glm::epsilonEqual( vertex.texture, matchiter->texture, matchtolerance ) ) ) + && ( vertex.tangent.w == matchiter->tangent.w ) ) { + Indices[ matchidx ] = Indices[ idx ]; + // HACK, TODO: tangent math winged/adapted from opengl-tutorial.org 13, check if it makes any sense + vertex.tangent += glm::vec4{ glm::vec3( matchiter->tangent ), 0.f }; + } + } + vertex.tangent = { glm::normalize( glm::vec3{ vertex.tangent } ), vertex.tangent.w }; + indexedvertices.emplace_back( vertex ); + } + // done indexing, swap the source vertex bank with the processed one + Vertices.swap( indexedvertices ); +} + // generic geometry bank class, allows storage, update and drawing of geometry chunks -// creates a new geometry chunk of specified type from supplied vertex data. returns: handle to the chunk +// creates a new geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL gfx::geometry_handle geometry_bank::create( gfx::vertex_array &Vertices, unsigned int const Type ) { @@ -174,6 +229,21 @@ geometry_bank::create( gfx::vertex_array &Vertices, unsigned int const Type ) { return chunkhandle; } +// creates a new indexed geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL +gfx::geometry_handle +geometry_bank::create( gfx::index_array &Indices, gfx::vertex_array &Vertices, unsigned int const Type ) { + + if( true == Vertices.empty() ) { return { 0, 0 }; } + + m_chunks.emplace_back( Indices, Vertices, Type ); + // NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication + gfx::geometry_handle chunkhandle { 0, static_cast(m_chunks.size()) }; + // template method implementation + create_( chunkhandle ); + // all done + return chunkhandle; +} + // replaces data of specified chunk with the supplied vertex data, starting from specified offset bool geometry_bank::replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset ) { @@ -223,6 +293,14 @@ geometry_bank::release() { release_(); } +// provides direct access to indexdata of specfied chunk +index_array const & +geometry_bank::indices( gfx::geometry_handle const &Geometry ) const { + + return geometry_bank::chunk( Geometry ).indices; +} + +// provides direct access to vertex data of specfied chunk vertex_array const & geometry_bank::vertices( gfx::geometry_handle const &Geometry ) const { @@ -247,7 +325,7 @@ geometrybank_manager::register_bank(std::unique_ptr bank) { return { static_cast( m_geometrybanks.size() ), 0 }; } -// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL +// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL gfx::geometry_handle geometrybank_manager::create_chunk(vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) { @@ -257,6 +335,16 @@ geometrybank_manager::create_chunk(vertex_array &Vertices, gfx::geometrybank_han else { return { 0, 0 }; } } +// creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL +gfx::geometry_handle +geometrybank_manager::create_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, unsigned int const Type ) { + + auto const newchunkhandle = bank( Geometry ).first->create( Indices, Vertices, Type ); + + if( newchunkhandle.chunk != 0 ) { return { Geometry.bank, newchunkhandle.chunk }; } + else { return { 0, 0 }; } +} + // replaces data of specified chunk with the supplied vertex data, starting from specified offset bool geometrybank_manager::replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset ) { @@ -282,6 +370,13 @@ geometrybank_manager::draw( gfx::geometry_handle const &Geometry, unsigned int c m_primitivecount += bankrecord.first->draw( Geometry, m_units, Streams ); } +// provides direct access to index data of specfied chunk +gfx::index_array const & +geometrybank_manager::indices( gfx::geometry_handle const &Geometry ) const { + + return bank( Geometry ).first->indices( Geometry ); +} + // provides direct access to vertex data of specfied chunk gfx::vertex_array const & geometrybank_manager::vertices( gfx::geometry_handle const &Geometry ) const { diff --git a/geometrybank.h b/geometrybank.h index 2d31cf83..5d603477 100644 --- a/geometrybank.h +++ b/geometrybank.h @@ -24,8 +24,10 @@ struct basic_vertex { basic_vertex( glm::vec3 Position, glm::vec3 Normal, glm::vec2 Texture ) : position( Position ), normal( Normal ), texture( Texture ) {} - void serialize( std::ostream& ) const; - void deserialize( std::istream& ); + void serialize( std::ostream&, bool const Tangent = false ) const; + void deserialize( std::istream&, bool const Tangent = false ); + void serialize_packed( std::ostream&, bool const Tangent = false ) const; + void deserialize_packed( std::istream&, bool const Tangent = false ); }; // data streams carried in a vertex @@ -45,9 +47,13 @@ struct stream_units { std::vector texture { GL_TEXTURE0 }; // unit associated with main texture data stream. TODO: allow multiple units per stream }; -using vertex_array = std::vector; +using basic_index = std::uint32_t; -void calculate_tangent( vertex_array &vertices, int type ); +using vertex_array = std::vector; +using index_array = std::vector; + +void calculate_tangents( vertex_array &vertices, int type ); +void calculate_indices( index_array &Indices, vertex_array &Vertices ); // generic geometry bank class, allows storage, update and drawing of geometry chunks @@ -90,9 +96,11 @@ public: ~geometry_bank() {} // methods: - // creates a new geometry chunk of specified type from supplied vertex data. returns: handle to the chunk or NULL - auto create(vertex_array &Vertices, unsigned int const Type ) -> gfx::geometry_handle; - // replaces data of specified chunk with the supplied vertex data, starting from specified offset + // creates a new geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL + auto create( gfx::vertex_array &Vertices, unsigned int const Type ) -> gfx::geometry_handle; + // creates a new indexed geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL + auto create( gfx::index_array &Indices, gfx::vertex_array &Vertices, unsigned int const Type ) -> gfx::geometry_handle; + // replaces vertex data of specified chunk with the supplied data, starting from specified offset auto replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool; // adds supplied vertex data at the end of specified chunk auto append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry ) -> bool; @@ -107,6 +115,8 @@ public: return count; } // frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time void release(); + // provides direct access to index data of specfied chunk + auto indices( gfx::geometry_handle const &Geometry ) const -> gfx::index_array const &; // provides direct access to vertex data of specfied chunk auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &; @@ -115,12 +125,20 @@ protected: struct geometry_chunk { unsigned int type; // kind of geometry used by the chunk gfx::vertex_array vertices; // geometry data - // NOTE: constructor doesn't copy provided vertex data, but moves it + gfx::index_array indices; // index data + // NOTE: constructor doesn't copy provided geometry data, but moves it geometry_chunk( gfx::vertex_array &Vertices, unsigned int Type ) : type( Type ) { vertices.swap( Vertices ); } + // NOTE: constructor doesn't copy provided geometry data, but moves it + geometry_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, unsigned int Type ) : + type( Type ) + { + vertices.swap( Vertices ); + indices.swap( Indices ); + } }; using geometrychunk_sequence = std::vector; @@ -160,10 +178,12 @@ public: // methods: // performs a resource sweep void update(); - // creates a new geometry bank. returns: handle to the bank or NULL + // registers a new geometry bank. returns: handle to the bank auto register_bank(std::unique_ptr bank) -> gfx::geometrybank_handle; - // creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL + // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL auto create_chunk( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle; + // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL + auto create_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, unsigned int const Type ) -> gfx::geometry_handle; // replaces data of specified chunk with the supplied vertex data, starting from specified offset auto replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool; // adds supplied vertex data at the end of specified chunk @@ -175,6 +195,8 @@ public: while( First != Last ) { draw( *First, Streams ); ++First; } } + // provides direct access to index data of specfied chunk + auto indices( gfx::geometry_handle const &Geometry ) const -> gfx::index_array const &; // provides direct access to vertex data of specfied chunk auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &; // sets target texture unit for the texture data stream diff --git a/opengl33geometrybank.cpp b/opengl33geometrybank.cpp index 07b3adad..05ecd1df 100644 --- a/opengl33geometrybank.cpp +++ b/opengl33geometrybank.cpp @@ -33,7 +33,7 @@ opengl33_vaogeometrybank::replace_( gfx::geometry_handle const &Geometry ) { auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ]; chunkrecord.is_good = false; // if the overall length of the chunk didn't change we can get away with reusing the old buffer... - if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.size ) { + if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.vertex_count ) { // ...but otherwise we'll need to allocate a new one // TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one, // but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it? @@ -43,51 +43,62 @@ opengl33_vaogeometrybank::replace_( gfx::geometry_handle const &Geometry ) { void opengl33_vaogeometrybank::setup_buffer() { - if( !m_buffer ) { - // if there's no buffer, we'll have to make one - // NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data - // may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste - if( true == m_chunks.empty() ) { return; } + if( m_vertexbuffer ) { return; } + // if there's no buffer, we'll have to make one + // NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data + // may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste + if( true == m_chunks.empty() ) { return; } - std::size_t datasize{ 0 }; - auto chunkiterator = m_chunks.cbegin(); - for( auto &chunkrecord : m_chunkrecords ) { - // fill records for all chunks, based on the chunk data - chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one - chunkrecord.offset = datasize; - chunkrecord.size = chunkiterator->vertices.size(); - datasize += chunkrecord.size; - ++chunkiterator; - } - // the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist - if( datasize == 0 ) { return; } - // try to set up the buffer we need - m_buffer.emplace(); - - // NOTE: we're using static_draw since it's generally true for all we have implemented at the moment - // TODO: allow to specify usage hint at the object creation, and pass it here - m_buffer->allocate(gl::buffer::ARRAY_BUFFER, datasize * sizeof(gfx::basic_vertex), GL_STATIC_DRAW); + std::size_t + vertexcount{ 0 }, + indexcount{ 0 }; + auto chunkiterator = m_chunks.cbegin(); + for( auto &chunkrecord : m_chunkrecords ) { + // fill records for all chunks, based on the chunk data + chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one + chunkrecord.vertex_offset = vertexcount; + chunkrecord.vertex_count = chunkiterator->vertices.size(); + vertexcount += chunkrecord.vertex_count; + chunkrecord.index_offset = indexcount; + chunkrecord.index_count = chunkiterator->indices.size(); + indexcount += chunkrecord.index_count; + ++chunkiterator; + } + // the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist + if( vertexcount == 0 ) { return; } + if( !m_vao ) { + m_vao.emplace(); + } + m_vao->bind(); + // try to set up the buffers we need: + // optional index buffer... + if( indexcount > 0 ) { + m_indexbuffer.emplace(); + m_indexbuffer->allocate( gl::buffer::ELEMENT_ARRAY_BUFFER, indexcount * sizeof( gfx::basic_index ), GL_STATIC_DRAW ); if( ::glGetError() == GL_OUT_OF_MEMORY ) { - ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" ); + ErrorLog( "openGL error: out of memory; failed to create a geometry index buffer" ); throw std::bad_alloc(); } - m_buffercapacity = datasize; + m_vao->setup_ebo( *m_indexbuffer ); } - - if (!m_vao) - { - m_vao.emplace(); - - m_vao->setup_attrib(*m_buffer, 0, 3, GL_FLOAT, sizeof(basic_vertex), 0 * sizeof(float)); - // NOTE: normal and color streams share the data - m_vao->setup_attrib(*m_buffer, 1, 3, GL_FLOAT, sizeof(basic_vertex), 3 * sizeof(float)); - m_vao->setup_attrib(*m_buffer, 2, 2, GL_FLOAT, sizeof(basic_vertex), 6 * sizeof(float)); - m_vao->setup_attrib(*m_buffer, 3, 4, GL_FLOAT, sizeof(basic_vertex), 8 * sizeof(float)); - - m_buffer->unbind(gl::buffer::ARRAY_BUFFER); - m_vao->unbind(); + else { + gl::buffer::unbind( gl::buffer::ELEMENT_ARRAY_BUFFER ); } + // ...and geometry buffer + m_vertexbuffer.emplace(); + // NOTE: we're using static_draw since it's generally true for all we have implemented at the moment + // TODO: allow to specify usage hint at the object creation, and pass it here + m_vertexbuffer->allocate( gl::buffer::ARRAY_BUFFER, vertexcount * sizeof( gfx::basic_vertex ), GL_STATIC_DRAW ); + if( ::glGetError() == GL_OUT_OF_MEMORY ) { + ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" ); + throw std::bad_alloc(); + } + m_vao->setup_attrib( *m_vertexbuffer, 0, 3, GL_FLOAT, sizeof( basic_vertex ), 0 * sizeof( float ) ); + // NOTE: normal and color streams share the data + m_vao->setup_attrib( *m_vertexbuffer, 1, 3, GL_FLOAT, sizeof( basic_vertex ), 3 * sizeof( float ) ); + m_vao->setup_attrib( *m_vertexbuffer, 2, 2, GL_FLOAT, sizeof( basic_vertex ), 6 * sizeof( float ) ); + m_vao->setup_attrib( *m_vertexbuffer, 3, 4, GL_FLOAT, sizeof( basic_vertex ), 8 * sizeof( float ) ); } // draw() subclass details @@ -100,23 +111,44 @@ opengl33_vaogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stre auto &chunkrecord = m_chunkrecords.at(Geometry.chunk - 1); // sanity check; shouldn't be needed but, eh - if( chunkrecord.size == 0 ) + if( chunkrecord.vertex_count == 0 ) return 0; + + m_vao->bind(); + auto const &chunk = gfx::geometry_bank::chunk( Geometry ); if( false == chunkrecord.is_good ) { // we may potentially need to upload new buffer data before we can draw it - m_buffer->upload(gl::buffer::ARRAY_BUFFER, chunk.vertices.data(), - chunkrecord.offset * sizeof( gfx::basic_vertex ), - chunkrecord.size * sizeof( gfx::basic_vertex )); + if( chunkrecord.index_count > 0 ) { + m_indexbuffer->upload( gl::buffer::ELEMENT_ARRAY_BUFFER, chunk.indices.data(), chunkrecord.index_offset * sizeof( gfx::basic_index ), chunkrecord.index_count * sizeof( gfx::basic_index ) ); + } + m_vertexbuffer->upload( gl::buffer::ARRAY_BUFFER, chunk.vertices.data(), chunkrecord.vertex_offset * sizeof( gfx::basic_vertex ), chunkrecord.vertex_count * sizeof( gfx::basic_vertex ) ); chunkrecord.is_good = true; } // render - m_vao->bind(); - ::glDrawArrays( chunk.type, chunkrecord.offset, chunkrecord.size ); - + if( chunkrecord.index_count > 0 ) { +/* + ::glDrawElementsBaseVertex( + chunk.type, + chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast( chunkrecord.index_offset * sizeof( gfx::basic_index ) ), + chunkrecord.vertex_offset ); +*/ + ::glDrawRangeElementsBaseVertex( + chunk.type, + 0, chunkrecord.vertex_count, + chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast( chunkrecord.index_offset * sizeof( gfx::basic_index ) ), + chunkrecord.vertex_offset ); + } + else { + ::glDrawArrays( chunk.type, chunkrecord.vertex_offset, chunkrecord.vertex_count ); + } +/* + m_vao->unbind(); +*/ + auto const vertexcount { ( chunkrecord.index_count > 0 ? chunkrecord.index_count : chunkrecord.vertex_count ) }; switch( chunk.type ) { - case GL_TRIANGLES: { return chunkrecord.size / 3; } - case GL_TRIANGLE_STRIP: { return chunkrecord.size - 2; } + case GL_TRIANGLES: { return vertexcount / 3; } + case GL_TRIANGLE_STRIP: { return vertexcount - 2; } default: { return 0; } } } @@ -131,14 +163,11 @@ opengl33_vaogeometrybank::release_() { void opengl33_vaogeometrybank::delete_buffer() { - if( m_buffer ) { - - m_vao.reset(); - m_buffer.reset(); - m_buffercapacity = 0; - // NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well - // instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice - } + m_vao.reset(); + m_vertexbuffer.reset(); + m_indexbuffer.reset(); + // NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well + // instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice } } // namespace gfx diff --git a/opengl33geometrybank.h b/opengl33geometrybank.h index 3eb60d51..9d43f788 100644 --- a/opengl33geometrybank.h +++ b/opengl33geometrybank.h @@ -32,9 +32,11 @@ public: private: // types: - struct chunk_record{ - std::size_t offset{ 0 }; // beginning of the chunk data as offset from the beginning of the last established buffer - std::size_t size{ 0 }; // size of the chunk in the last established buffer + struct chunk_record { + std::size_t vertex_offset{ 0 }; // beginning of the chunk vertex data as offset from the beginning of the last established buffer + std::size_t vertex_count{ 0 }; // size of the chunk in the last established buffer + std::size_t index_offset{ 0 }; + std::size_t index_count{ 0 }; bool is_good{ false }; // true if local content of the chunk matches the data on the opengl end }; @@ -59,14 +61,10 @@ private: delete_buffer(); // members: - std::optional m_buffer; // buffer data on the opengl end + std::optional m_vertexbuffer; // vertex buffer data on the opengl end + std::optional m_indexbuffer; // index buffer data on the opengl end std::optional m_vao; - 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 - // vectors for glMultiDrawArrays in class scope - // to don't waste time on reallocating - std::vector m_offsets; - std::vector m_counts; }; } // namespace gfx diff --git a/opengl33renderer.cpp b/opengl33renderer.cpp index 4d512a8e..2fdeabda 100644 --- a/opengl33renderer.cpp +++ b/opengl33renderer.cpp @@ -22,6 +22,8 @@ http://mozilla.org/MPL/2.0/. #include "AnimModel.h" #include "opengl33geometrybank.h" +//#define EU07_DEBUG_OPENGL + int const EU07_PICKBUFFERSIZE{ 1024 }; // size of (square) textures bound with the pick framebuffer int const EU07_REFLECTIONFIDELITYOFFSET { 250 }; // artificial increase of range for reflection pass detail reduction @@ -518,9 +520,6 @@ bool opengl33_renderer::Render() glfwMakeContextCurrent(m_window); gl::buffer::unbind(); m_current_viewport = &(*m_viewports.front()); - - m_drawcount = m_cellqueue.size(); - m_debugtimestext.clear(); /* m_debugtimestext += "cpu: " + to_string(Timer::subsystem.gfx_color.average(), 2) + " ms (" + std::to_string(m_cellqueue.size()) + " sectors)\n" += "cpu swap: " + to_string(Timer::subsystem.gfx_swap.average(), 2) + " ms\n" += "uilayer: " + to_string(Timer::subsystem.gfx_gui.average(), 2) + "ms\n" += @@ -536,11 +535,52 @@ bool opengl33_renderer::Render() m_debugtimestext += "gpu: " + to_string((double)(m_gllasttime / 1000ULL) / 1000.0, 3) + "ms"; */ } - + + ++m_framestamp; + Timer::subsystem.gfx_total.stop(); + + // swapbuffers() could unbind current buffers so we prepare for it on our end + gfx::opengl33_vaogeometrybank::reset(); + + return true; // for now always succeed +} + +void opengl33_renderer::SwapBuffers() +{ + Timer::subsystem.gfx_swap.start(); + + for (auto &viewport : m_viewports) { + if (viewport->window && viewport->real_window) + glfwSwapBuffers(viewport->window); + } + + if (vr) + vr->finish_frame(); + + Timer::subsystem.gfx_swap.stop(); + + m_debugtimestext.clear(); + m_debugtimestext = + "cpu frame total: " + to_string( Timer::subsystem.gfx_color.average() + Timer::subsystem.gfx_shadows.average() + Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n" + + " color: " + to_string( Timer::subsystem.gfx_color.average(), 2 ) + " ms (" + std::to_string( m_cellqueue.size() ) + " sectors)\n"; + if( Global.gfx_shadowmap_enabled ) { + m_debugtimestext += + " shadows: " + to_string( Timer::subsystem.gfx_shadows.average(), 2 ) + " ms\n"; + } + m_debugtimestext += " swap: " + to_string( Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n"; + if( !Global.gfx_usegles ) { + if (m_gllasttime) + m_debugtimestext += "gpu frame total: " + to_string((double)(m_gllasttime / 1000ULL) / 1000.0, 3) + " ms\n"; + } + m_debugtimestext += "uilayer: " + to_string( Timer::subsystem.gfx_gui.average(), 2 ) + " ms\n"; + if( DebugModeFlag ) + m_debugtimestext += m_textures.info(); + debug_stats shadowstats; for( auto const &shadowpass : m_shadowpass ) { shadowstats += shadowpass.draw_stats; } + m_debugstatstext = "triangles: " + to_string( static_cast(m_geometry.primitives_count()), 7 ) + "\n" + "vehicles: " + to_string( m_colorpass.draw_stats.dynamics, 7 ) + " +" + to_string( shadowstats.dynamics, 7 ) @@ -558,45 +598,6 @@ bool opengl33_renderer::Render() + " traction: " + to_string( m_colorpass.draw_stats.traction, 7 ) + "\n" + " lines: " + to_string( m_colorpass.draw_stats.lines, 7 ) + "\n" + "particles: " + to_string( m_colorpass.draw_stats.particles, 7 ); - - ++m_framestamp; - - Timer::subsystem.gfx_total.stop(); - - m_debugtimestext += - "cpu frame total: " + to_string( Timer::subsystem.gfx_color.average() + Timer::subsystem.gfx_shadows.average() + Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n" - + " color: " + to_string( Timer::subsystem.gfx_color.average(), 2 ) + " ms (" + std::to_string( m_cellqueue.size() ) + " sectors)\n"; - if( Global.gfx_shadowmap_enabled ) { - m_debugtimestext += - " shadows: " + to_string( Timer::subsystem.gfx_shadows.average(), 2 ) + " ms\n"; - } - m_debugtimestext += " swap: " + to_string( Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n"; - if( !Global.gfx_usegles ) { - if (m_gllasttime) - m_debugtimestext += "gpu frame total: " + to_string((double)(m_gllasttime / 1000ULL) / 1000.0, 3) + " ms\n"; - } - m_debugtimestext += "uilayer: " + to_string( Timer::subsystem.gfx_gui.average(), 2 ) + " ms\n"; - if( DebugModeFlag ) - m_debugtimestext += m_textures.info(); - - return true; // for now always succeed -} - -void opengl33_renderer::SwapBuffers() -{ - Timer::subsystem.gfx_swap.start(); - - for (auto &viewport : m_viewports) { - if (viewport->window && viewport->real_window) - glfwSwapBuffers(viewport->window); - } - - if (vr) - vr->finish_frame(); - - // swapbuffers() could unbind current buffers so we prepare for it on our end - gfx::opengl33_vaogeometrybank::reset(); - Timer::subsystem.gfx_swap.stop(); } void opengl33_renderer::draw_debug_ui() @@ -1849,10 +1850,17 @@ gfx::geometrybank_handle opengl33_renderer::Create_Bank() return m_geometry.register_bank(std::make_unique()); } -// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL +// creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL +gfx::geometry_handle opengl33_renderer::Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) +{ + // NOTE: we expect indexed geometry to come with calculated tangents + return m_geometry.create_chunk( Indices, Vertices, Geometry, Type ); +} + +// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL gfx::geometry_handle opengl33_renderer::Insert(gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type) { - gfx::calculate_tangent(Vertices, Type); + gfx::calculate_tangents(Vertices, Type); return m_geometry.create_chunk(Vertices, Geometry, Type); } @@ -1860,7 +1868,7 @@ gfx::geometry_handle opengl33_renderer::Insert(gfx::vertex_array &Vertices, gfx: // replaces data of specified chunk with the supplied vertex data, starting from specified offset bool opengl33_renderer::Replace(gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset) { - gfx::calculate_tangent(Vertices, Type); + gfx::calculate_tangents(Vertices, Type); return m_geometry.replace(Vertices, Geometry, Offset); } @@ -1868,15 +1876,20 @@ bool opengl33_renderer::Replace(gfx::vertex_array &Vertices, gfx::geometry_handl // adds supplied vertex data at the end of specified chunk bool opengl33_renderer::Append(gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type) { - gfx::calculate_tangent(Vertices, Type); + gfx::calculate_tangents(Vertices, Type); return m_geometry.append(Vertices, Geometry); } +// provides direct access to index data of specfied chunk +gfx::index_array const & opengl33_renderer::Indices(gfx::geometry_handle const &Geometry) const +{ + return m_geometry.indices(Geometry); +} + // provides direct access to vertex data of specfied chunk gfx::vertex_array const &opengl33_renderer::Vertices(gfx::geometry_handle const &Geometry) const { - return m_geometry.vertices(Geometry); } @@ -1916,10 +1929,8 @@ void opengl33_renderer::Bind_Material( material_handle const Material, TSubModel memcpy(&model_ubs.param[0], &material.params[0], sizeof(model_ubs.param)); - for (size_t i = 0; i < material.params_state.size(); i++) + for( auto const &entry : material.params_state ) { - gl::shader::param_entry entry = material.params_state[i]; - glm::vec4 src(1.0f); // submodel-based parameters @@ -3017,7 +3028,7 @@ void opengl33_renderer::Render(TSubModel *Submodel) model_ubs.emission = Submodel->f4Emision.a; // main draw call - draw(Submodel->m_geometry); + draw(Submodel->m_geometry.handle); // post-draw reset model_ubs.emission = 0.0f; @@ -3037,13 +3048,13 @@ void opengl33_renderer::Render(TSubModel *Submodel) // również 0 Bind_Material_Shadow(Submodel->m_material); } - draw(Submodel->m_geometry); + draw(Submodel->m_geometry.handle); break; } case rendermode::pickscenery: { m_pick_shader->bind(); - draw(Submodel->m_geometry); + draw(Submodel->m_geometry.handle); break; } case rendermode::pickcontrols: @@ -3061,7 +3072,7 @@ void opengl33_renderer::Render(TSubModel *Submodel) model_ubs.param[0] = glm::vec4(pick_color(m_pickcontrolsitems.size()), 1.0f); } - draw(Submodel->m_geometry); + draw(Submodel->m_geometry.handle); break; } default: @@ -3087,7 +3098,7 @@ void opengl33_renderer::Render(TSubModel *Submodel) // main draw call model_ubs.param[1].x = 8.0f; - draw(Submodel->m_geometry); + draw(Submodel->m_geometry.handle); } break; } @@ -3864,7 +3875,7 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel) model_ubs.emission = Submodel->f4Emision.a; // main draw call - draw(Submodel->m_geometry); + draw(Submodel->m_geometry.handle); model_ubs.emission = 0.0f; break; @@ -3879,7 +3890,7 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel) { Bind_Material_Shadow(Submodel->m_material); } - draw(Submodel->m_geometry); + draw(Submodel->m_geometry.handle); break; } default: @@ -4000,7 +4011,7 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel) model_ubs.param[1].x = pointsize * fogfactor * 4.0f; model_ubs.param[0] = glm::vec4(glm::vec3(lightcolor), Submodel->fVisible * std::min(1.f, lightlevel) * 0.5f); - draw(Submodel->m_geometry); + draw(Submodel->m_geometry.handle); } model_ubs.param[1].x = pointsize * 4.0f; model_ubs.param[0] = glm::vec4(glm::vec3(lightcolor), Submodel->fVisible * std::min(1.f, lightlevel)); @@ -4011,7 +4022,7 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel) Submodel->occlusion_query->begin(); } - draw(Submodel->m_geometry); + draw(Submodel->m_geometry.handle); if (gl::vao::use_vao) Submodel->occlusion_query->end(); diff --git a/opengl33renderer.h b/opengl33renderer.h index a3664cad..61d6cdff 100644 --- a/opengl33renderer.h +++ b/opengl33renderer.h @@ -47,6 +47,8 @@ class opengl33_renderer : public gfx_renderer { // main draw call. returns false on error bool Render() override; + void + SwapBuffers() override; inline float Framerate() override { return m_framerate; } @@ -55,7 +57,10 @@ class opengl33_renderer : public gfx_renderer { // creates a new geometry bank. returns: handle to the bank or NULL gfx::geometrybank_handle Create_Bank() override; - // creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL + // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL + gfx::geometry_handle + Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override; + // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL gfx::geometry_handle Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override; // replaces data of specified chunk with the supplied vertex data, starting from specified offset @@ -64,6 +69,9 @@ class opengl33_renderer : public gfx_renderer { // adds supplied vertex data at the end of specified chunk bool Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) override; + // provides direct access to index data of specfied chunk + gfx::index_array const & + Indices( gfx::geometry_handle const &Geometry ) const override; // provides direct access to vertex data of specfied chunk gfx::vertex_array const & Vertices( gfx::geometry_handle const &Geometry ) const override; @@ -118,7 +126,6 @@ class opengl33_renderer : public gfx_renderer { opengl_material & Material( material_handle const Material ); - void SwapBuffers() override; // draws supplied geometry handles void Draw_Geometry(std::vector::iterator begin, std::vector::iterator end); void Draw_Geometry(const gfx::geometrybank_handle &handle); @@ -128,7 +135,6 @@ class opengl33_renderer : public gfx_renderer { // members GLenum static const sunlight{0}; - std::size_t m_drawcount{0}; static std::unique_ptr create_func(); diff --git a/openglgeometrybank.cpp b/openglgeometrybank.cpp index 53052c6e..89692b57 100644 --- a/openglgeometrybank.cpp +++ b/openglgeometrybank.cpp @@ -16,7 +16,7 @@ namespace gfx { // opengl vbo-based variant of the geometry bank -GLuint opengl_vbogeometrybank::m_activebuffer { 0 }; // buffer bound currently on the opengl end, if any +GLuint opengl_vbogeometrybank::m_activevertexbuffer { 0 }; // buffer bound currently on the opengl end, if any unsigned int opengl_vbogeometrybank::m_activestreams { gfx::stream::none }; // currently enabled data type pointers std::vector opengl_vbogeometrybank::m_activetexturearrays; // currently enabled texture coord arrays @@ -37,7 +37,7 @@ opengl_vbogeometrybank::replace_( gfx::geometry_handle const &Geometry ) { auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ]; chunkrecord.is_good = false; // if the overall length of the chunk didn't change we can get away with reusing the old buffer... - if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.size ) { + if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.vertex_count ) { // ...but otherwise we'll need to allocate a new one // TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one, // but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it? @@ -53,18 +53,25 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream auto &chunkrecord { m_chunkrecords[ Geometry.chunk - 1 ] }; // sanity check; shouldn't be needed but, eh - if( chunkrecord.size == 0 ) { return 0; } + if( chunkrecord.vertex_count == 0 ) { return 0; } // setup... - if( m_activebuffer != m_buffer ) { + if( m_activevertexbuffer != m_vertexbuffer ) { bind_buffer(); } auto const &chunk = gfx::geometry_bank::chunk( Geometry ); if( false == chunkrecord.is_good ) { // we may potentially need to upload new buffer data before we can draw it + if( chunkrecord.index_count > 0 ) { + ::glBufferSubData( + GL_ELEMENT_ARRAY_BUFFER, + chunkrecord.index_offset * sizeof( gfx::basic_index ), + chunkrecord.index_count * sizeof( gfx::basic_index ), + chunk.indices.data() ); + } ::glBufferSubData( GL_ARRAY_BUFFER, - chunkrecord.offset * sizeof( gfx::basic_vertex ), - chunkrecord.size * sizeof( gfx::basic_vertex ), + chunkrecord.vertex_offset * sizeof( gfx::basic_vertex ), + chunkrecord.vertex_count * sizeof( gfx::basic_vertex ), chunk.vertices.data() ); chunkrecord.is_good = true; } @@ -72,7 +79,22 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream bind_streams( Units, Streams ); } // ...render... - ::glDrawArrays( chunk.type, chunkrecord.offset, chunkrecord.size ); + if( chunkrecord.index_count > 0 ) { +/* + ::glDrawElementsBaseVertex( + chunk.type, + chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast( chunkrecord.index_offset * sizeof( gfx::basic_index ) ), + chunkrecord.vertex_offset ); +*/ + ::glDrawRangeElementsBaseVertex( + chunk.type, + 0, chunkrecord.vertex_count, + chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast( chunkrecord.index_offset * sizeof( gfx::basic_index ) ), + chunkrecord.vertex_offset ); + } + else { + ::glDrawArrays( chunk.type, chunkrecord.vertex_offset, chunkrecord.vertex_count ); + } // ...post-render cleanup /* ::glDisableClientState( GL_VERTEX_ARRAY ); @@ -80,9 +102,10 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); m_activebuffer = 0; */ + auto const vertexcount { ( chunkrecord.index_count > 0 ? chunkrecord.index_count : chunkrecord.vertex_count ) }; switch( chunk.type ) { - case GL_TRIANGLES: { return chunkrecord.size / 3; } - case GL_TRIANGLE_STRIP: { return chunkrecord.size - 2; } + case GL_TRIANGLES: { return vertexcount / 3; } + case GL_TRIANGLE_STRIP: { return vertexcount - 2; } default: { return 0; } } } @@ -97,33 +120,57 @@ opengl_vbogeometrybank::release_() { void opengl_vbogeometrybank::setup_buffer() { - if( m_buffer != 0 ) { return; } + if( m_vertexbuffer != 0 ) { return; } // if there's no buffer, we'll have to make one // NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data // may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste if( true == m_chunks.empty() ) { return; } - std::size_t datasize{ 0 }; + std::size_t + vertexcount{ 0 }, + indexcount{ 0 }; auto chunkiterator = m_chunks.cbegin(); for( auto &chunkrecord : m_chunkrecords ) { // fill records for all chunks, based on the chunk data chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one - chunkrecord.offset = datasize; - chunkrecord.size = chunkiterator->vertices.size(); - datasize += chunkrecord.size; + chunkrecord.vertex_offset = vertexcount; + chunkrecord.vertex_count = chunkiterator->vertices.size(); + vertexcount += chunkrecord.vertex_count; + chunkrecord.index_offset = indexcount; + chunkrecord.index_count = chunkiterator->indices.size(); + indexcount += chunkrecord.index_count; ++chunkiterator; } // the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist - if( datasize == 0 ) { return; } - // try to set up the buffer we need - ::glGenBuffers( 1, &m_buffer ); + if( vertexcount == 0 ) { return; } + // try to set up the buffers we need + if( ( indexcount > 0 ) && ( m_indexbuffer == 0 ) ) { + ::glGenBuffers( 1, &m_indexbuffer ); + if( m_indexbuffer == 0 ) { return; } // if we didn't get a buffer we'll try again during the next draw call + } + if( m_vertexbuffer == 0 ) { + ::glGenBuffers( 1, &m_vertexbuffer ); + if( m_vertexbuffer == 0 ) { return; } // if we didn't get a buffer we'll try again during the next draw call + } bind_buffer(); - if( m_buffer == 0 ) { return; } // if we didn't get a buffer we'll try again during the next draw call // NOTE: we're using static_draw since it's generally true for all we have implemented at the moment // TODO: allow to specify usage hint at the object creation, and pass it here + if( indexcount > 0 ) { + ::glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + indexcount * sizeof( gfx::basic_index ), + nullptr, + GL_STATIC_DRAW ); + if( ::glGetError() == GL_OUT_OF_MEMORY ) { + // TBD: throw a bad_alloc? + ErrorLog( "openGL error: out of memory; failed to create a geometry index buffer" ); + delete_buffer(); + return; + } + } ::glBufferData( GL_ARRAY_BUFFER, - datasize * sizeof( gfx::basic_vertex ), + vertexcount * sizeof( gfx::basic_vertex ), nullptr, GL_STATIC_DRAW ); if( ::glGetError() == GL_OUT_OF_MEMORY ) { @@ -132,29 +179,32 @@ opengl_vbogeometrybank::setup_buffer() { delete_buffer(); return; } - m_buffercapacity = datasize; } void opengl_vbogeometrybank::bind_buffer() { - ::glBindBuffer( GL_ARRAY_BUFFER, m_buffer ); - m_activebuffer = m_buffer; + ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer ); + ::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer ); + m_activevertexbuffer = m_vertexbuffer; m_activestreams = gfx::stream::none; } void opengl_vbogeometrybank::delete_buffer() { - if( m_buffer != 0 ) { + if( m_indexbuffer != 0 ) { + ::glDeleteBuffers( 1, &m_indexbuffer ); + m_indexbuffer = 0; + } + if( m_vertexbuffer != 0 ) { - ::glDeleteBuffers( 1, &m_buffer ); - if( m_activebuffer == m_buffer ) { - m_activebuffer = 0; + ::glDeleteBuffers( 1, &m_vertexbuffer ); + if( m_activevertexbuffer == m_vertexbuffer ) { + m_activevertexbuffer = 0; release_streams(); } - m_buffer = 0; - m_buffercapacity = 0; + m_vertexbuffer = 0; // NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well // instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice } @@ -252,18 +302,32 @@ opengl_dlgeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream_ ::glNewList( chunkrecord.list, GL_COMPILE ); ::glBegin( chunk.type ); - for( auto const &vertex : chunk.vertices ) { - if( Streams & gfx::stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); } - else if( Streams & gfx::stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); } - if( Streams & gfx::stream::texture ) { for( auto unit : Units.texture ) { ::glMultiTexCoord2fv( unit, glm::value_ptr( vertex.texture ) ); } } - if( Streams & gfx::stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); } + if( chunk.indices.size() > 0 ) { + // indexed geometry + for( auto const &index : chunk.indices ) { + auto const &vertex { chunk.vertices[ index ] }; + if( Streams & gfx::stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); } + else if( Streams & gfx::stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); } + if( Streams & gfx::stream::texture ) { for( auto unit : Units.texture ) { ::glMultiTexCoord2fv( unit, glm::value_ptr( vertex.texture ) ); } } + if( Streams & gfx::stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); } + } + } + else { + // raw geometry + for( auto const &vertex : chunk.vertices ) { + if( Streams & gfx::stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); } + else if( Streams & gfx::stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); } + if( Streams & gfx::stream::texture ) { for( auto unit : Units.texture ) { ::glMultiTexCoord2fv( unit, glm::value_ptr( vertex.texture ) ); } } + if( Streams & gfx::stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); } + } } ::glEnd(); ::glEndList(); + auto const vertexcount { ( chunk.indices.empty() ? chunk.vertices.size() : chunk.indices.size() ) }; switch( chunk.type ) { - case GL_TRIANGLES: { chunkrecord.primitive_count += chunk.vertices.size() / 3; break; } - case GL_TRIANGLE_STRIP: { chunkrecord.primitive_count += chunk.vertices.size() - 2; break; } + case GL_TRIANGLES: { chunkrecord.primitive_count += vertexcount / 3; break; } + case GL_TRIANGLE_STRIP: { chunkrecord.primitive_count += vertexcount - 2; break; } default: { break; } } } diff --git a/openglgeometrybank.h b/openglgeometrybank.h index 31dde3c2..53843f55 100644 --- a/openglgeometrybank.h +++ b/openglgeometrybank.h @@ -27,14 +27,16 @@ public: static void reset() { - m_activebuffer = 0; + m_activevertexbuffer = 0; m_activestreams = gfx::stream::none; } private: // types: - struct chunk_record{ - std::size_t offset{ 0 }; // beginning of the chunk data as offset from the beginning of the last established buffer - std::size_t size{ 0 }; // size of the chunk in the last established buffer + struct chunk_record { + std::size_t vertex_offset{ 0 }; // beginning of the chunk vertex data as offset from the beginning of the last established buffer + std::size_t vertex_count{ 0 }; // size of the chunk in the last established buffer + std::size_t index_offset{ 0 }; + std::size_t index_count{ 0 }; bool is_good{ false }; // true if local content of the chunk matches the data on the opengl end }; @@ -67,11 +69,11 @@ private: release_streams(); // members: - static GLuint m_activebuffer; // buffer bound currently on the opengl end, if any + static GLuint m_activevertexbuffer; // buffer bound currently on the opengl end, if any static unsigned int m_activestreams; static std::vector m_activetexturearrays; - GLuint m_buffer { 0 }; // id of the buffer holding data on the opengl end - std::size_t m_buffercapacity{ 0 }; // total capacity of the last established buffer + GLuint m_vertexbuffer { 0 }; // id of the buffer holding vertex data on the opengl end + GLuint m_indexbuffer { 0 }; // id of the buffer holding index data on the opengl end chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order }; diff --git a/openglrenderer.cpp b/openglrenderer.cpp index 62e802ac..bb12824e 100644 --- a/openglrenderer.cpp +++ b/openglrenderer.cpp @@ -333,7 +333,6 @@ opengl_renderer::Render() { m_renderpass.draw_mode = rendermode::none; // force setup anew m_renderpass.draw_stats = debug_stats(); m_geometry.primitives_count() = 0; - m_debugtimestext.clear(); Render_pass( rendermode::color ); Timer::subsystem.gfx_color.stop(); // add user interface @@ -343,9 +342,24 @@ opengl_renderer::Render() { ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); Application.render_ui(); ::glPopClientAttrib(); + if( Global.bUseVBO ) { + // swapbuffers() will unbind current buffers so we prepare for it on our end + gfx::opengl_vbogeometrybank::reset(); + } + ++m_framestamp; - m_drawcount = m_cellqueue.size(); - m_debugtimestext += + return true; // for now always succeed +} + +void +opengl_renderer::SwapBuffers() { + + Timer::subsystem.gfx_swap.start(); + glfwSwapBuffers( m_window ); + Timer::subsystem.gfx_swap.stop(); + + m_debugtimestext.clear(); + m_debugtimestext = "cpu frame total: " + to_string( Timer::subsystem.gfx_color.average() + Timer::subsystem.gfx_shadows.average() + Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n" + " color: " + to_string( Timer::subsystem.gfx_color.average(), 2 ) + " ms (" + std::to_string( m_cellqueue.size() ) + " sectors)\n"; if( Global.RenderShadows ) { @@ -372,22 +386,6 @@ opengl_renderer::Render() { + " traction: " + to_string( m_colorpass.draw_stats.traction, 7 ) + "\n" + " lines: " + to_string( m_colorpass.draw_stats.lines, 7 ) + "\n" + "particles: " + to_string( m_colorpass.draw_stats.particles, 7 ); - - ++m_framestamp; - - return true; // for now always succeed -} - -void -opengl_renderer::SwapBuffers() -{ - if( Global.bUseVBO ) { - // swapbuffers() will unbind current buffers so we prepare for it on our end - gfx::opengl_vbogeometrybank::reset(); - } - Timer::subsystem.gfx_swap.start(); - glfwSwapBuffers( m_window ); - Timer::subsystem.gfx_swap.stop(); } // runs jobs needed to generate graphics for specified render pass @@ -1675,7 +1673,14 @@ opengl_renderer::Create_Bank() { return m_geometry.register_bank(std::make_unique()); } -// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL +// creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL +gfx::geometry_handle +opengl_renderer::Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) { + + return m_geometry.create_chunk( Indices, Vertices, Geometry, Type ); +} + +// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL gfx::geometry_handle opengl_renderer::Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) { @@ -1696,6 +1701,13 @@ opengl_renderer::Append( gfx::vertex_array &Vertices, gfx::geometry_handle const return m_geometry.append( Vertices, Geometry ); } +// provides direct access to vertex data of specfied chunk +gfx::index_array const & +opengl_renderer::Indices( gfx::geometry_handle const &Geometry ) const { + + return m_geometry.indices( Geometry ); +} + // provides direct access to vertex data of specfied chunk gfx::vertex_array const & opengl_renderer::Vertices( gfx::geometry_handle const &Geometry ) const { @@ -2755,7 +2767,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { } // main draw call - m_geometry.draw( Submodel->m_geometry ); + m_geometry.draw( Submodel->m_geometry.handle ); /* if( DebugModeFlag ) { auto const & vertices { m_geometry.vertices( Submodel->m_geometry ) }; @@ -2817,7 +2829,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { Bind_Material( Submodel->m_material ); } // main draw call - m_geometry.draw( Submodel->m_geometry ); + m_geometry.draw( Submodel->m_geometry.handle ); // post-draw reset break; } @@ -2835,7 +2847,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { Bind_Material( Submodel->m_material ); } // main draw call - m_geometry.draw( Submodel->m_geometry ); + m_geometry.draw( Submodel->m_geometry.handle ); // post-draw reset break; } @@ -2923,7 +2935,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { lightcolor[ 2 ], Submodel->fVisible * std::min( 1.f, lightlevel ) * 0.5f ); ::glDepthMask( GL_FALSE ); - m_geometry.draw( Submodel->m_geometry ); + m_geometry.draw( Submodel->m_geometry.handle ); ::glDepthMask( GL_TRUE ); } ::glPointSize( pointsize * resolutionratio ); @@ -2932,7 +2944,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { lightcolor[ 1 ], lightcolor[ 2 ], Submodel->fVisible * std::min( 1.f, lightlevel ) ); - m_geometry.draw( Submodel->m_geometry ); + m_geometry.draw( Submodel->m_geometry.handle ); // post-draw reset switch_units( unitstate.diffuse, unitstate.shadows, unitstate.reflections ); @@ -2964,7 +2976,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { ::glDisable( GL_LIGHTING ); // main draw call - m_geometry.draw( Submodel->m_geometry, gfx::color_streams ); + m_geometry.draw( Submodel->m_geometry.handle, gfx::color_streams ); // post-draw reset ::glPopAttrib(); @@ -3799,7 +3811,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { } // main draw call - m_geometry.draw( Submodel->m_geometry ); + m_geometry.draw( Submodel->m_geometry.handle ); // post-draw reset if( opacity != 0.f ) { @@ -3845,7 +3857,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { Bind_Material( Submodel->m_material ); } // main draw call - m_geometry.draw( Submodel->m_geometry ); + m_geometry.draw( Submodel->m_geometry.handle ); // post-draw reset break; } diff --git a/openglrenderer.h b/openglrenderer.h index 3e18e225..3ef6ff3c 100644 --- a/openglrenderer.h +++ b/openglrenderer.h @@ -43,11 +43,12 @@ public: // main draw call. returns false on error bool Render() override; + void + SwapBuffers() override; inline float Framerate() override { return m_framerate; } - void SwapBuffers() override; bool AddViewport(const global_settings::extraviewport_config &conf) override { return false; } bool Debug_Ui_State(std::optional) override { return false; } void Shutdown() override {} @@ -57,7 +58,10 @@ public: // creates a new geometry bank. returns: handle to the bank or NULL gfx::geometrybank_handle Create_Bank() override; - // creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL + // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL + gfx::geometry_handle + Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override; + // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL gfx::geometry_handle Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override; // replaces data of specified chunk with the supplied vertex data, starting from specified offset @@ -66,6 +70,9 @@ public: // adds supplied vertex data at the end of specified chunk bool Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) override; + // provides direct access to index data of specfied chunk + gfx::index_array const & + Indices( gfx::geometry_handle const &Geometry ) const override; // provides direct access to vertex data of specfied chunk gfx::vertex_array const & Vertices( gfx::geometry_handle const &Geometry ) const override; @@ -117,7 +124,6 @@ public: // members GLenum static const sunlight { GL_LIGHT0 }; - std::size_t m_drawcount { 0 }; static std::unique_ptr create_func(); diff --git a/renderer.h b/renderer.h index 78f8bcb2..05741923 100644 --- a/renderer.h +++ b/renderer.h @@ -28,18 +28,22 @@ public: virtual void Shutdown() = 0; // main draw call. returns false on error virtual auto Render() -> bool = 0; - virtual auto Framerate() -> float = 0; virtual void SwapBuffers() = 0; + virtual auto Framerate() -> float = 0; // geometry methods // NOTE: hands-on geometry management is exposed as a temporary measure; ultimately all visualization data should be generated/handled automatically by the renderer itself // creates a new geometry bank. returns: handle to the bank or NULL virtual auto Create_Bank() -> gfx::geometrybank_handle = 0; - // creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL + // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL + virtual auto Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle = 0; + // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL virtual auto Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle = 0; // replaces data of specified chunk with the supplied vertex data, starting from specified offset virtual auto Replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset = 0 ) -> bool = 0; // adds supplied vertex data at the end of specified chunk virtual auto Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) -> bool = 0; + // provides direct access to index data of specfied chunk + virtual auto Indices( gfx::geometry_handle const &Geometry ) const->gfx::index_array const & = 0; // provides direct access to vertex data of specfied chunk virtual auto Vertices( gfx::geometry_handle const &Geometry ) const ->gfx::vertex_array const & = 0; // material methods diff --git a/scenenode.cpp b/scenenode.cpp index da5fc087..4eb5323a 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -348,12 +348,12 @@ shape_node::convert( TSubModel const *Submodel ) { // NOTE: we set unlimited view range typical for terrain, because we don't expect to convert any other 3d models m_data.rangesquared_max = std::numeric_limits::max(); - if( Submodel->m_geometry == null_handle ) { return *this; } + if( Submodel->m_geometry.handle == null_handle ) { return *this; } int vertexcount { 0 }; std::vector importedvertices; world_vertex vertex, vertex1, vertex2; - for( auto const &sourcevertex : GfxRenderer->Vertices( Submodel->m_geometry ) ) { + for( auto const &sourcevertex : GfxRenderer->Vertices( Submodel->m_geometry.handle ) ) { vertex.position = sourcevertex.position; vertex.normal = sourcevertex.normal; vertex.texture = sourcevertex.texture; diff --git a/sn_utils.cpp b/sn_utils.cpp index d96dc74a..6b6ec740 100644 --- a/sn_utils.cpp +++ b/sn_utils.cpp @@ -38,6 +38,18 @@ int32_t sn_utils::ld_int32(std::istream &s) return reinterpret_cast(v); } +// deserialize little endian uint64 +uint64_t sn_utils::ld_uint64(std::istream &s) +{ + uint8_t buf[8]; + s.read((char*)buf, 8); + uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) | + ((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) | + ((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) | + ((uint64_t)buf[1] << 8) | (uint64_t)buf[0]; + return reinterpret_cast(v); +} + // deserialize little endian int64 int64_t sn_utils::ld_int64(std::istream &s) { @@ -100,7 +112,7 @@ glm::dvec3 sn_utils::d_dvec3(std::istream& s) ld_float64(s) }; } -glm::vec3 sn_utils::d_vec3( std::istream& s) +glm::vec3 sn_utils::d_vec3( std::istream& s ) { return { ld_float32(s), @@ -108,7 +120,7 @@ glm::vec3 sn_utils::d_vec3( std::istream& s) ld_float32(s) }; } -glm::vec4 sn_utils::d_vec4( std::istream& s) +glm::vec4 sn_utils::d_vec4( std::istream& s ) { return { ld_float32(s), @@ -117,6 +129,14 @@ glm::vec4 sn_utils::d_vec4( std::istream& s) ld_float32(s) }; } +uint8_t sn_utils::d_uint8( std::istream& s ) { + + uint8_t buf; + s.read((char*)buf, 1); + return buf; +} + + void sn_utils::ls_uint16(std::ostream &s, uint16_t v) { uint8_t buf[2]; @@ -145,6 +165,20 @@ void sn_utils::ls_int32(std::ostream &s, int32_t v) s.write((char*)buf, 4); } +void sn_utils::ls_uint64(std::ostream &s, uint64_t v) +{ + uint8_t buf[8]; + buf[0] = v; + buf[1] = v >> 8; + buf[2] = v >> 16; + buf[3] = v >> 24; + buf[4] = v >> 32; + buf[5] = v >> 40; + buf[6] = v >> 48; + buf[7] = v >> 56; + s.write((char*)buf, 8); +} + void sn_utils::ls_int64(std::ostream &s, int64_t v) { uint8_t buf[8]; @@ -185,6 +219,11 @@ void sn_utils::ls_float64(std::ostream &s, double t) s.write((char*)buf, 8); } +void sn_utils::s_uint8(std::ostream &s, uint8_t v) +{ + s.write((char*)v, 1); +} + void sn_utils::s_str(std::ostream &s, std::string v) { const char* buf = v.c_str(); diff --git a/sn_utils.h b/sn_utils.h index fff3125a..54005c6c 100644 --- a/sn_utils.h +++ b/sn_utils.h @@ -10,9 +10,11 @@ public: static uint16_t ld_uint16(std::istream&); static uint32_t ld_uint32(std::istream&); static int32_t ld_int32(std::istream&); - static int64_t ld_int64(std::istream&); + static uint64_t ld_uint64(std::istream&); + static int64_t ld_int64(std::istream&); static float ld_float32(std::istream&); static double ld_float64(std::istream&); + static uint8_t d_uint8(std::istream&); static std::string d_str(std::istream&); static bool d_bool(std::istream&); static glm::dvec3 d_dvec3(std::istream&); @@ -22,10 +24,12 @@ public: static void ls_uint16(std::ostream&, uint16_t); static void ls_uint32(std::ostream&, uint32_t); static void ls_int32(std::ostream&, int32_t); + static void ls_uint64(std::ostream&, uint64_t); static void ls_int64(std::ostream&, int64_t); static void ls_float32(std::ostream&, float); static void ls_float64(std::ostream&, double); - static void s_str(std::ostream&, std::string); + static void s_uint8(std::ostream&, uint8_t); + static void s_str(std::ostream&, std::string); static void s_bool(std::ostream&, bool); static void s_dvec3(std::ostream&, glm::dvec3 const &); static void s_vec3(std::ostream&, glm::vec3 const &); diff --git a/stdafx.h b/stdafx.h index 22fae84f..2c8661ef 100644 --- a/stdafx.h +++ b/stdafx.h @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,7 @@ #include #include #include +#include #include #include #include