From c07aeb7498d1524563b47da71b0cc1a4593c6875 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 2 Nov 2018 09:51:44 +0100 Subject: [PATCH 01/39] - use a single TArray to allocate the memory for the lump manager's hash lists. --- src/w_wad.cpp | 48 +++++++----------------------------------------- src/w_wad.h | 4 ++-- 2 files changed, 9 insertions(+), 43 deletions(-) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index 41b57811da..9c474ceb82 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -101,10 +101,6 @@ void uppercopy (char *to, const char *from) } FWadCollection::FWadCollection () -: FirstLumpIndex(NULL), NextLumpIndex(NULL), - FirstLumpIndex_FullName(NULL), NextLumpIndex_FullName(NULL), - FirstLumpIndex_NoExt(NULL), NextLumpIndex_NoExt(NULL), - NumLumps(0) { } @@ -115,37 +111,6 @@ FWadCollection::~FWadCollection () void FWadCollection::DeleteAll () { - if (FirstLumpIndex != NULL) - { - delete[] FirstLumpIndex; - FirstLumpIndex = NULL; - } - if (NextLumpIndex != NULL) - { - delete[] NextLumpIndex; - NextLumpIndex = NULL; - } - if (FirstLumpIndex_FullName != NULL) - { - delete[] FirstLumpIndex_FullName; - FirstLumpIndex_FullName = NULL; - } - if (NextLumpIndex_FullName != NULL) - { - delete[] NextLumpIndex_FullName; - NextLumpIndex_FullName = NULL; - } - if (FirstLumpIndex_NoExt != NULL) - { - delete[] FirstLumpIndex_NoExt; - FirstLumpIndex_NoExt = NULL; - } - if (NextLumpIndex_NoExt != NULL) - { - delete[] NextLumpIndex_NoExt; - NextLumpIndex_NoExt = NULL; - } - LumpInfo.Clear(); NumLumps = 0; @@ -193,12 +158,13 @@ void FWadCollection::InitMultipleFiles (TArray &filenames) FixMacHexen(); // [RH] Set up hash table - FirstLumpIndex = new uint32_t[NumLumps]; - NextLumpIndex = new uint32_t[NumLumps]; - FirstLumpIndex_FullName = new uint32_t[NumLumps]; - NextLumpIndex_FullName = new uint32_t[NumLumps]; - FirstLumpIndex_NoExt = new uint32_t[NumLumps]; - NextLumpIndex_NoExt = new uint32_t[NumLumps]; + Hashes.Resize(6 * NumLumps); + FirstLumpIndex = &Hashes[0]; + NextLumpIndex = &Hashes[NumLumps]; + FirstLumpIndex_FullName = &Hashes[NumLumps*2]; + NextLumpIndex_FullName = &Hashes[NumLumps*3]; + FirstLumpIndex_NoExt = &Hashes[NumLumps*4]; + NextLumpIndex_NoExt = &Hashes[NumLumps*5]; InitHashChains (); LumpInfo.ShrinkToFit(); Files.ShrinkToFit(); diff --git a/src/w_wad.h b/src/w_wad.h index 30d73d33eb..dca4bd8feb 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -188,6 +188,7 @@ protected: TArray Files; TArray LumpInfo; + TArray Hashes; // one allocation for all hash lists. uint32_t *FirstLumpIndex; // [RH] Hashing stuff moved out of lumpinfo structure uint32_t *NextLumpIndex; @@ -197,12 +198,11 @@ protected: uint32_t *FirstLumpIndex_NoExt; // The same information for fully qualified paths from .zips uint32_t *NextLumpIndex_NoExt; - uint32_t NumLumps; // Not necessarily the same as LumpInfo.Size() + uint32_t NumLumps = 0; // Not necessarily the same as LumpInfo.Size() uint32_t NumWads; int IwadIndex; - void SkinHack (int baselump); void InitHashChains (); // [RH] Set up the lumpinfo hashing private: From 1ccbbcb81db43b405119c11c2fd117b3f5582000 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 2 Nov 2018 10:20:12 +0100 Subject: [PATCH 02/39] - added a method to FileReader to read the contents into an array and used it on the MIDI sources for testing. --- src/files.h | 8 +++ src/sound/midisources/midisource.h | 9 ++-- src/sound/midisources/midisource_hmi.cpp | 64 ++++++++++++------------ src/sound/midisources/midisource_smf.cpp | 34 ++++++------- src/sound/midisources/midisource_xmi.cpp | 18 +++---- 5 files changed, 62 insertions(+), 71 deletions(-) diff --git a/src/files.h b/src/files.h index 89e6a091d0..4b281438f1 100644 --- a/src/files.h +++ b/src/files.h @@ -184,6 +184,14 @@ public: return mReader->Read(buffer, (long)len); } + TArray Read() + { + TArray buffer(mReader->Length, true); + Size length = mReader->Read(&buffer[0], mReader->Length); + if (length < mReader->Length) buffer.Clear(); + return buffer; + } + char *Gets(char *strbuf, Size len) { return mReader->Gets(strbuf, (int)len); diff --git a/src/sound/midisources/midisource.h b/src/sound/midisources/midisource.h index 12e0188b4a..01c0c28085 100644 --- a/src/sound/midisources/midisource.h +++ b/src/sound/midisources/midisource.h @@ -121,8 +121,7 @@ private: uint32_t *SendCommand (uint32_t *event, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom); TrackInfo *FindNextDue (); - uint8_t *MusHeader; - int SongLen; + TArray MusHeader; TrackInfo *Tracks; TrackInfo *TrackDue; int NumTracks; @@ -181,8 +180,7 @@ private: static uint32_t ReadVarLenHMI(TrackInfo *); static uint32_t ReadVarLenHMP(TrackInfo *); - uint8_t *MusHeader; - int SongLen; + TArray MusHeader; int NumTracks; TrackInfo *Tracks; TrackInfo *TrackDue; @@ -218,8 +216,7 @@ private: uint32_t *SendCommand (uint32_t *event, EventSource track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom); EventSource FindNextDue(); - uint8_t *MusHeader; - int SongLen; // length of the entire file + TArray MusHeader; int NumSongs; TrackInfo *Songs; TrackInfo *CurrSong; diff --git a/src/sound/midisources/midisource_hmi.cpp b/src/sound/midisources/midisource_hmi.cpp index 1fe08e9cce..f0c4a3fb81 100644 --- a/src/sound/midisources/midisource_hmi.cpp +++ b/src/sound/midisources/midisource_hmi.cpp @@ -129,19 +129,17 @@ HMISong::HMISong (FileReader &reader) { // Way too small to be HMI. return; } - MusHeader = new uint8_t[len]; - SongLen = len; + MusHeader = reader.Read(); NumTracks = 0; - if (reader.Read(MusHeader, len) != len) + if (MusHeader.Size() == 0) return; // Do some validation of the MIDI file - if (memcmp(MusHeader, HMI_SONG_MAGIC, sizeof(HMI_SONG_MAGIC)) == 0) + if (memcmp(&MusHeader[0], HMI_SONG_MAGIC, sizeof(HMI_SONG_MAGIC)) == 0) { SetupForHMI(len); } - else if (((uint32_t *)MusHeader)[0] == MAKE_ID('H','M','I','M') && - ((uint32_t *)MusHeader)[1] == MAKE_ID('I','D','I','P')) + else if (memcmp(&MusHeader[0], "HMIMIDIP", 8) == 0) { SetupForHMP(len); } @@ -155,14 +153,10 @@ HMISong::HMISong (FileReader &reader) HMISong::~HMISong() { - if (Tracks != NULL) + if (Tracks != nullptr) { delete[] Tracks; } - if (MusHeader != NULL) - { - delete[] MusHeader; - } } //========================================================================== @@ -175,8 +169,10 @@ void HMISong::SetupForHMI(int len) { int i, p; + auto MusPtr = &MusHeader[0]; + ReadVarLen = ReadVarLenHMI; - NumTracks = GetShort(MusHeader + HMI_TRACK_COUNT_OFFSET); + NumTracks = GetShort(MusPtr + HMI_TRACK_COUNT_OFFSET); if (NumTracks <= 0) { @@ -187,16 +183,16 @@ void HMISong::SetupForHMI(int len) // HMI files have two values here, a full value and a quarter value. Some games, // notably Quarantines, have identical values for some reason, so it's safer to // use the quarter value and multiply it by four than to trust the full value. - Division = GetShort(MusHeader + HMI_DIVISION_OFFSET) << 2; + Division = GetShort(MusPtr + HMI_DIVISION_OFFSET) << 2; Tempo = InitialTempo = 4000000; Tracks = new TrackInfo[NumTracks + 1]; - int track_dir = GetInt(MusHeader + HMI_TRACK_DIR_PTR_OFFSET); + int track_dir = GetInt(MusPtr + HMI_TRACK_DIR_PTR_OFFSET); // Gather information about each track for (i = 0, p = 0; i < NumTracks; ++i) { - int start = GetInt(MusHeader + track_dir + i*4); + int start = GetInt(MusPtr + track_dir + i*4); int tracklen, datastart; if (start > len - HMITRACK_DESIGNATION_OFFSET - 4) @@ -205,7 +201,7 @@ void HMISong::SetupForHMI(int len) } // BTW, HMI does not actually check the track header. - if (memcmp(MusHeader + start, TRACK_MAGIC, 13) != 0) + if (memcmp(MusPtr + start, TRACK_MAGIC, 13) != 0) { continue; } @@ -218,7 +214,7 @@ void HMISong::SetupForHMI(int len) } else { - tracklen = GetInt(MusHeader + track_dir + i*4 + 4) - start; + tracklen = GetInt(MusPtr + track_dir + i*4 + 4) - start; } // Clamp incomplete tracks to the end of the file. tracklen = MIN(tracklen, len - start); @@ -228,7 +224,7 @@ void HMISong::SetupForHMI(int len) } // Offset to actual MIDI events. - datastart = GetInt(MusHeader + start + HMITRACK_DATA_PTR_OFFSET); + datastart = GetInt(MusPtr + start + HMITRACK_DATA_PTR_OFFSET); tracklen -= datastart; if (tracklen <= 0) { @@ -236,7 +232,7 @@ void HMISong::SetupForHMI(int len) } // Store track information - Tracks[p].TrackBegin = MusHeader + start + datastart; + Tracks[p].TrackBegin = MusPtr + start + datastart; Tracks[p].TrackP = 0; Tracks[p].MaxTrackP = tracklen; @@ -244,7 +240,7 @@ void HMISong::SetupForHMI(int len) // connected to the MIDI device. for (int ii = 0; ii < NUM_HMI_DESIGNATIONS; ++ii) { - Tracks[p].Designation[ii] = GetShort(MusHeader + start + HMITRACK_DESIGNATION_OFFSET + ii*2); + Tracks[p].Designation[ii] = GetShort(MusPtr + start + HMITRACK_DESIGNATION_OFFSET + ii*2); } p++; @@ -266,12 +262,14 @@ void HMISong::SetupForHMP(int len) int track_data; int i, p; + auto MusPtr = &MusHeader[0]; + ReadVarLen = ReadVarLenHMP; - if (MusHeader[8] == 0) + if (MusPtr[8] == 0) { track_data = HMP_TRACK_OFFSET_0; } - else if (memcmp(MusHeader + 8, HMP_NEW_DATE, sizeof(HMP_NEW_DATE)) == 0) + else if (memcmp(MusPtr + 8, HMP_NEW_DATE, sizeof(HMP_NEW_DATE)) == 0) { track_data = HMP_TRACK_OFFSET_1; } @@ -280,7 +278,7 @@ void HMISong::SetupForHMP(int len) return; } - NumTracks = GetInt(MusHeader + HMP_TRACK_COUNT_OFFSET); + NumTracks = GetInt(MusPtr + HMP_TRACK_COUNT_OFFSET); if (NumTracks <= 0) { @@ -288,7 +286,7 @@ void HMISong::SetupForHMP(int len) } // The division is the number of pulses per quarter note (PPQN). - Division = GetInt(MusHeader + HMP_DIVISION_OFFSET); + Division = GetInt(MusPtr + HMP_DIVISION_OFFSET); Tempo = InitialTempo = 1000000; Tracks = new TrackInfo[NumTracks + 1]; @@ -304,7 +302,7 @@ void HMISong::SetupForHMP(int len) break; } - tracklen = GetInt(MusHeader + start + HMPTRACK_LEN_OFFSET); + tracklen = GetInt(MusPtr + start + HMPTRACK_LEN_OFFSET); track_data += tracklen; // Clamp incomplete tracks to the end of the file. @@ -322,7 +320,7 @@ void HMISong::SetupForHMP(int len) } // Store track information - Tracks[p].TrackBegin = MusHeader + start + HMPTRACK_MIDI_DATA_OFFSET; + Tracks[p].TrackBegin = MusPtr + start + HMPTRACK_MIDI_DATA_OFFSET; Tracks[p].TrackP = 0; Tracks[p].MaxTrackP = tracklen; @@ -333,14 +331,14 @@ void HMISong::SetupForHMP(int len) // HMI files. Some songs contain nothing but zeroes for this data, so I'd rather // not go around using it without confirmation. - Printf("Track %d: %d %08x %d: \034I", i, GetInt(MusHeader + start), - GetInt(MusHeader + start + 4), GetInt(MusHeader + start + 8)); + Printf("Track %d: %d %08x %d: \034I", i, GetInt(MusPtr + start), + GetInt(MusPtr + start + 4), GetInt(MusPtr + start + 8)); int designations = HMP_DESIGNATIONS_OFFSET + - GetInt(MusHeader + start + HMPTRACK_DESIGNATION_OFFSET) * 4 * NUM_HMP_DESIGNATIONS; + GetInt(MusPtr + start + HMPTRACK_DESIGNATION_OFFSET) * 4 * NUM_HMP_DESIGNATIONS; for (int ii = 0; ii < NUM_HMP_DESIGNATIONS; ++ii) { - Printf(" %04x", GetInt(MusHeader + designations + ii*4)); + Printf(" %04x", GetInt(MusPtr + designations + ii*4)); } Printf("\n"); #endif @@ -474,7 +472,7 @@ void HMISong :: DoRestart() bool HMISong::CheckDone() { - return TrackDue == NULL; + return TrackDue == nullptr; } //========================================================================== @@ -964,7 +962,7 @@ void NoteOffQueue::Heapify() // // HMISong :: FindNextDue // -// Scans every track for the next event to play. Returns NULL if all events +// Scans every track for the next event to play. Returns nullptr if all events // have been consumed. // //========================================================================== @@ -987,7 +985,7 @@ HMISong::TrackInfo *HMISong::FindNextDue () } // Check regular tracks. - track = NULL; + track = nullptr; best = 0xFFFFFFFF; for (i = 0; i < NumTracks; ++i) { diff --git a/src/sound/midisources/midisource_smf.cpp b/src/sound/midisources/midisource_smf.cpp index 42299b7f83..0b189e27d4 100644 --- a/src/sound/midisources/midisource_smf.cpp +++ b/src/sound/midisources/midisource_smf.cpp @@ -98,13 +98,11 @@ struct MIDISong2::TrackInfo MIDISong2::MIDISong2 (FileReader &reader) : MusHeader(0), Tracks(0) { - int p; + unsigned p; int i; - SongLen = (int)reader.GetLength(); - MusHeader = new uint8_t[SongLen]; - if (reader.Read(MusHeader, SongLen) != SongLen) - return; + MusHeader = reader.Read(); + if (MusHeader.Size() == 0) return; // Do some validation of the MIDI file if (MusHeader[4] != 0 || MusHeader[5] != 0 || MusHeader[6] != 0 || MusHeader[7] != 6) @@ -134,7 +132,7 @@ MIDISong2::MIDISong2 (FileReader &reader) Tracks = new TrackInfo[NumTracks]; // Gather information about each track - for (i = 0, p = 14; i < NumTracks && p < SongLen + 8; ++i) + for (i = 0, p = 14; i < NumTracks && p < MusHeader.Size() + 8; ++i) { uint32_t chunkLen = (MusHeader[p+4]<<24) | @@ -142,9 +140,9 @@ MIDISong2::MIDISong2 (FileReader &reader) (MusHeader[p+6]<<8) | (MusHeader[p+7]); - if (chunkLen + p + 8 > (uint32_t)SongLen) + if (chunkLen + p + 8 > MusHeader.Size()) { // Track too long, so truncate it - chunkLen = SongLen - p - 8; + chunkLen = MusHeader.Size() - p - 8; } if (MusHeader[p+0] == 'M' && @@ -152,7 +150,7 @@ MIDISong2::MIDISong2 (FileReader &reader) MusHeader[p+2] == 'r' && MusHeader[p+3] == 'k') { - Tracks[i].TrackBegin = MusHeader + p + 8; + Tracks[i].TrackBegin = &MusHeader[p + 8]; Tracks[i].TrackP = 0; Tracks[i].MaxTrackP = chunkLen; } @@ -178,14 +176,10 @@ MIDISong2::MIDISong2 (FileReader &reader) MIDISong2::~MIDISong2 () { - if (Tracks != NULL) + if (Tracks != nullptr) { delete[] Tracks; } - if (MusHeader != NULL) - { - delete[] MusHeader; - } } //========================================================================== @@ -273,7 +267,7 @@ void MIDISong2 :: DoRestart() bool MIDISong2::CheckDone() { - return TrackDue == NULL; + return TrackDue == nullptr; } //========================================================================== @@ -750,7 +744,7 @@ uint32_t MIDISong2::TrackInfo::ReadVarLen () // // MIDISong2 :: FindNextDue // -// Scans every track for the next event to play. Returns NULL if all events +// Scans every track for the next event to play. Returns nullptr if all events // have been consumed. // //========================================================================== @@ -770,10 +764,10 @@ MIDISong2::TrackInfo *MIDISong2::FindNextDue () switch (Format) { case 0: - return Tracks[0].Finished ? NULL : Tracks; + return Tracks[0].Finished ? nullptr : Tracks; case 1: - track = NULL; + track = nullptr; best = 0xFFFFFFFF; for (i = 0; i < NumTracks; ++i) { @@ -794,9 +788,9 @@ MIDISong2::TrackInfo *MIDISong2::FindNextDue () { track++; } - return track < &Tracks[NumTracks] ? track : NULL; + return track < &Tracks[NumTracks] ? track : nullptr; } - return NULL; + return nullptr; } diff --git a/src/sound/midisources/midisource_xmi.cpp b/src/sound/midisources/midisource_xmi.cpp index 3e2fcac87f..4f32f85ed9 100644 --- a/src/sound/midisources/midisource_xmi.cpp +++ b/src/sound/midisources/midisource_xmi.cpp @@ -104,13 +104,11 @@ struct XMISong::TrackInfo XMISong::XMISong (FileReader &reader) : MusHeader(0), Songs(0) { - SongLen = (int)reader.GetLength(); - MusHeader = new uint8_t[SongLen]; - if (reader.Read(MusHeader, SongLen) != SongLen) - return; + MusHeader = reader.Read(); + if (MusHeader.Size() == 0) return; // Find all the songs in this file. - NumSongs = FindXMIDforms(MusHeader, SongLen, NULL); + NumSongs = FindXMIDforms(&MusHeader[0], MusHeader.Size(), nullptr); if (NumSongs == 0) { return; @@ -128,7 +126,7 @@ XMISong::XMISong (FileReader &reader) Songs = new TrackInfo[NumSongs]; memset(Songs, 0, sizeof(*Songs) * NumSongs); - FindXMIDforms(MusHeader, SongLen, Songs); + FindXMIDforms(&MusHeader[0], MusHeader.Size(), Songs); CurrSong = Songs; DPrintf(DMSG_SPAMMY, "XMI song count: %d\n", NumSongs); } @@ -141,14 +139,10 @@ XMISong::XMISong (FileReader &reader) XMISong::~XMISong () { - if (Songs != NULL) + if (Songs != nullptr) { delete[] Songs; } - if (MusHeader != NULL) - { - delete[] MusHeader; - } } //========================================================================== @@ -172,7 +166,7 @@ int XMISong::FindXMIDforms(const uint8_t *chunk, int len, TrackInfo *songs) cons { if (GetNativeInt(chunk + p + 8) == MAKE_ID('X','M','I','D')) { - if (songs != NULL) + if (songs != nullptr) { FoundXMID(chunk + p + 12, chunklen - 4, songs + count); } From 53bf598aee63d6e525c5f0c8423c2bf426d77c74 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 3 Nov 2018 13:09:30 +0200 Subject: [PATCH 03/39] - restored screen clear in Cocoa backend when setting video mode This still doesn't work well in windowed mode In fullscreen the effect is quite noticeable thought --- src/posix/cocoa/i_video.mm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index 8cc8fe0183..cb54f76c92 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -355,6 +355,9 @@ SystemGLFrameBuffer::SystemGLFrameBuffer(void*, const bool fullscreen) assert(frameBuffer == nullptr); frameBuffer = this; + // To be able to use OpenGL functions in SetMode() + ogl_LoadFunctions(); + FConsoleWindow::GetInstance().Show(false); } @@ -527,6 +530,14 @@ void SystemGLFrameBuffer::SetMode(const bool fullscreen, const bool hiDPI) SetWindowedMode(); } + const NSSize viewSize = I_GetContentViewSize(m_window); + + glViewport(0, 0, static_cast(viewSize.width), static_cast(viewSize.height)); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + [[NSOpenGLContext currentContext] flushBuffer]; + [m_window updateTitle]; if (![m_window isKeyWindow]) From ce8c52942206fb3aca376c3e4d3e9bec794c7646 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 3 Nov 2018 13:11:59 +0200 Subject: [PATCH 04/39] - use Xcode 10.1 for Travis CI builds --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 39b3ee0868..d7d62ef05e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ matrix: - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Debug -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9" - os: osx - osx_image: xcode10 + osx_image: xcode10.1 env: - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9" From b1d35eb0b32fe49743cc6149e3b75df8ded32f05 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Fri, 2 Nov 2018 11:14:02 +0600 Subject: [PATCH 05/39] Extend SKYEXPLODE flag for LineAttack --- src/p_local.h | 3 ++- src/p_map.cpp | 18 +++++++++++++++--- src/p_mobj.cpp | 6 +++++- src/p_trace.cpp | 21 +++++++++++++++++++++ src/p_trace.h | 2 ++ wadsrc/static/zscript/actor.txt | 3 ++- wadsrc/static/zscript/base.txt | 4 +++- wadsrc/static/zscript/constants.txt | 3 ++- 8 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/p_local.h b/src/p_local.h index e553def292..06748c76a6 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -110,7 +110,8 @@ enum EPuffFlags PF_MELEERANGE = 2, PF_TEMPORARY = 4, PF_HITTHINGBLEED = 8, - PF_NORANDOMZ = 16 + PF_NORANDOMZ = 16, + PF_HITSKY = 32 }; AActor *P_SpawnPuff(AActor *source, PClassActor *pufftype, const DVector3 &pos, DAngle hitdir, DAngle particledir, int updown, int flags = 0, AActor *vict = NULL); diff --git a/src/p_map.cpp b/src/p_map.cpp index bb7164ef7c..ed4f0d0896 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -4623,9 +4623,13 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, damageType = puffDefaults->DamageType; } - int tflags; - if (nointeract || (puffDefaults && puffDefaults->flags6 & MF6_NOTRIGGER)) tflags = TRACE_NoSky; - else tflags = TRACE_NoSky | TRACE_Impact; + uint32_t tflags = TRACE_NoSky | TRACE_Impact; + if (nointeract || (puffDefaults && puffDefaults->flags6 & MF6_NOTRIGGER)) tflags &= ~TRACE_Impact; + if (spawnSky) + { + tflags &= ~TRACE_NoSky; + tflags |= TRACE_HitSky; + } // [MC] Check the flags and set the position according to what is desired. // LAF_ABSPOSITION: Treat the offset parameters as direct coordinates. @@ -4683,8 +4687,16 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, { if (trace.HitType != TRACE_HitActor) { + + if (trace.HitType == TRACE_HasHitSky || (trace.HitType == TRACE_HitWall + && trace.Line->special == Line_Horizon && spawnSky)) + { + puffFlags |= PF_HITSKY; + } + P_GeometryLineAttack(trace, t1, damage, damageType); + // position a bit closer for puffs if (nointeract || trace.HitType != TRACE_HitWall || ((trace.Line->special != Line_Horizon) || spawnSky)) { diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index fe40348c16..596a9fc01a 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -6245,7 +6245,11 @@ AActor *P_SpawnPuff (AActor *source, PClassActor *pufftype, const DVector3 &pos1 // it will enter the crash state. This is used by the StrifeSpark // and BlasterPuff. FState *crashstate; - if (!(flags & PF_HITTHING) && (crashstate = puff->FindState(NAME_Crash)) != NULL) + if ((flags & PF_HITSKY) && (crashstate = puff->FindState(NAME_Death, NAME_Sky, true)) != NULL) + { + puff->SetState (crashstate); + } + else if (!(flags & PF_HITTHING) && (crashstate = puff->FindState(NAME_Crash)) != NULL) { puff->SetState (crashstate); } diff --git a/src/p_trace.cpp b/src/p_trace.cpp index 48e49dba07..d2f3e71f40 100644 --- a/src/p_trace.cpp +++ b/src/p_trace.cpp @@ -938,6 +938,27 @@ bool FTraceInfo::CheckPlane (const secplane_t &plane) static bool EditTraceResult (uint32_t flags, FTraceResults &res) { + if (flags & TRACE_HitSky) + { // Throw away sky hits + if (res.HitType == TRACE_HitFloor || res.HitType == TRACE_HitCeiling) + { + if (res.HitTexture == skyflatnum) + { + res.HitType = TRACE_HasHitSky; + return true; + } + } + else if (res.HitType == TRACE_HitWall) + { + if (res.Tier == TIER_Upper && + res.Line->frontsector->GetTexture(sector_t::ceiling) == skyflatnum && + res.Line->backsector->GetTexture(sector_t::ceiling) == skyflatnum) + { + res.HitType = TRACE_HasHitSky; + return true; + } + } + } if (flags & TRACE_NoSky) { // Throw away sky hits if (res.HitType == TRACE_HitFloor || res.HitType == TRACE_HitCeiling) diff --git a/src/p_trace.h b/src/p_trace.h index 92a0f30a53..5e733b4c4c 100644 --- a/src/p_trace.h +++ b/src/p_trace.h @@ -52,6 +52,7 @@ enum ETraceResult TRACE_HitWall, TRACE_HitActor, TRACE_CrossingPortal, + TRACE_HasHitSky, }; enum @@ -98,6 +99,7 @@ enum TRACE_PortalRestrict= 0x0008, // Cannot go through portals without a static link offset. TRACE_ReportPortals = 0x0010, // Report any portal crossing to the TraceCallback TRACE_3DCallback = 0x0020, // [ZZ] use TraceCallback to determine whether we need to go through a line to do 3D floor check, or not. without this, only line flag mask is used + TRACE_HitSky = 0x0040, // Hitting the sky returns TRACE_HasHitSky }; // return values from callback diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 767b87e0f8..c30d6cb249 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -38,7 +38,8 @@ struct FLineTraceData TRACE_HitCeiling, TRACE_HitWall, TRACE_HitActor, - TRACE_CrossingPortal + TRACE_CrossingPortal, + TRACE_HasHitSky }; Actor HitActor; diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 314b59b3a3..fa7a8182d5 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -503,6 +503,7 @@ enum ETraceFlags TRACE_PortalRestrict = 0x0008, // Cannot go through portals without a static link offset. TRACE_ReportPortals = 0x0010, // Report any portal crossing to the TraceCallback //TRACE_3DCallback = 0x0020, // [ZZ] use TraceCallback to determine whether we need to go through a line to do 3D floor check, or not. without this, only line flag mask is used + TRACE_HitSky = 0x0040 // Hitting the sky returns TRACE_HasHitSky } @@ -513,7 +514,8 @@ enum ETraceResult TRACE_HitCeiling, TRACE_HitWall, TRACE_HitActor, - TRACE_CrossingPortal + TRACE_CrossingPortal, + TRACE_HasHitSky } enum ELineTier diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index dc394a0d20..92f5483558 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -1122,7 +1122,8 @@ enum EPuffFlags PF_MELEERANGE = 2, PF_TEMPORARY = 4, PF_HITTHINGBLEED = 8, - PF_NORANDOMZ = 16 + PF_NORANDOMZ = 16, + PF_HITSKY = 32 }; enum EPlayerCheats From a6dbfcf9c2106d94b815b833eb03c1d5345cf676 Mon Sep 17 00:00:00 2001 From: Player701 Date: Sun, 4 Nov 2018 20:30:40 +0300 Subject: [PATCH 06/39] - Added a new field to the Actor class which stores the amount of ticks passed since the game started on the moment the actor was spawned. - Added a function to the Actor class to get its spawn time relative to the current level. - Added spawn time information to the output of the "info" console command. --- src/actor.h | 5 +++++ src/p_mobj.cpp | 29 +++++++++++++++++++++++++++-- src/version.h | 2 +- wadsrc/static/zscript/actor.txt | 2 ++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/actor.h b/src/actor.h index 043da9b7ca..f357e6c9ff 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1033,6 +1033,8 @@ public: void AttachLight(unsigned int count, const FLightDefaults *lightdef); void SetDynamicLights(); + // When was this actor spawned? (relative to the current level) + int GetLevelSpawnTime() const; // info for drawing // NOTE: The first member variable *must* be snext. @@ -1262,6 +1264,9 @@ public: int PrevPortalGroup; TArray > AttachedLights; + // When was this actor spawned? + int SpawnTime; + // ThingIDs static void ClearTIDHashes (); void AddToHash (); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 596a9fc01a..a1e7cbac87 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -348,6 +348,7 @@ DEFINE_FIELD(AActor, BloodTranslation) DEFINE_FIELD(AActor, RenderHidden) DEFINE_FIELD(AActor, RenderRequired) DEFINE_FIELD(AActor, friendlyseeblocks) +DEFINE_FIELD(AActor, SpawnTime) //========================================================================== // @@ -528,8 +529,9 @@ void AActor::Serialize(FSerializer &arc) A("selfdamagefactor", SelfDamageFactor) A("stealthalpha", StealthAlpha) A("renderhidden", RenderHidden) - A("renderrequired", RenderRequired); - A("friendlyseeblocks", friendlyseeblocks); + A("renderrequired", RenderRequired) + A("friendlyseeblocks", friendlyseeblocks) + A("spawntime", SpawnTime); } #undef A @@ -5006,6 +5008,7 @@ AActor *AActor::StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t a AActor *actor; actor = static_cast(const_cast(type)->CreateNew ()); + actor->SpawnTime = level.totaltime; // Set default dialogue actor->ConversationRoot = GetConversation(actor->GetClass()->TypeName); @@ -8063,6 +8066,25 @@ void AActor::SetTranslation(FName trname) // silently ignore if the name does not exist, this would create some insane message spam otherwise. } +//========================================================================== +// +// AActor :: GetLevelSpawnTime +// +// Returns the time when this actor was spawned, +// relative to the current level. +// +//========================================================================== +int AActor::GetLevelSpawnTime() const +{ + return SpawnTime - level.totaltime + level.time; +} + +DEFINE_ACTION_FUNCTION(AActor, GetLevelSpawnTime) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_INT(self->GetLevelSpawnTime()); +} + //--------------------------------------------------------------------------- // // PROP A_RestoreSpecialPosition @@ -8587,5 +8609,8 @@ void PrintMiscActorInfo(AActor *query) Printf("FriendlySeeBlocks: %d\n", query->friendlyseeblocks); Printf("Target: %s\n", query->target ? query->target->GetClass()->TypeName.GetChars() : "-"); Printf("Last enemy: %s\n", query->lastenemy ? query->lastenemy->GetClass()->TypeName.GetChars() : "-"); + Printf("Spawn time: %d ticks (%f seconds) after game start, %d ticks (%f seconds) after level start\n", + query->SpawnTime, (double) query->SpawnTime / TICRATE, + query->GetLevelSpawnTime(), (double) query->GetLevelSpawnTime() / TICRATE); } } diff --git a/src/version.h b/src/version.h index 61c6ceff8e..36707442e2 100644 --- a/src/version.h +++ b/src/version.h @@ -91,7 +91,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4552 +#define SAVEVER 4553 // This is so that derivates can use the same savegame versions without worrying about engine compatibility #define GAMESIG "GZDOOM" diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index c30d6cb249..70ea25e915 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -234,6 +234,7 @@ class Actor : Thinker native native int RenderHidden; native int RenderRequired; native readonly int FriendlySeeBlocks; + native readonly int SpawnTime; meta String Obituary; // Player was killed by this actor meta String HitObituary; // Player was killed by this actor in melee @@ -708,6 +709,7 @@ class Actor : Thinker native native void GiveSecret(bool printmsg = true, bool playsound = true); native clearscope double GetCameraHeight() const; native clearscope double GetGravity() const; + native clearscope int GetLevelSpawnTime() const; native bool CheckClass(class checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false); native void AddInventory(Inventory inv); From 56f76a141edbc1df423b9c441c553fca25320c49 Mon Sep 17 00:00:00 2001 From: Player701 Date: Sun, 4 Nov 2018 20:55:52 +0300 Subject: [PATCH 07/39] - Added a function to get the actor's age in ticks. --- src/actor.h | 2 ++ src/p_mobj.cpp | 18 ++++++++++++++++++ wadsrc/static/zscript/actor.txt | 1 + 3 files changed, 21 insertions(+) diff --git a/src/actor.h b/src/actor.h index f357e6c9ff..9123da4faf 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1035,6 +1035,8 @@ public: // When was this actor spawned? (relative to the current level) int GetLevelSpawnTime() const; + // How many ticks passed since this actor was spawned? + int GetAge() const; // info for drawing // NOTE: The first member variable *must* be snext. diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index a1e7cbac87..67d934060c 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -8085,6 +8085,24 @@ DEFINE_ACTION_FUNCTION(AActor, GetLevelSpawnTime) ACTION_RETURN_INT(self->GetLevelSpawnTime()); } +//========================================================================== +// +// AActor :: GetAge +// +// Returns the number of ticks passed since this actor was spawned. +// +//========================================================================== +int AActor::GetAge() const +{ + return level.totaltime - SpawnTime; +} + +DEFINE_ACTION_FUNCTION(AActor, GetAge) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_INT(self->GetAge()); +} + //--------------------------------------------------------------------------- // // PROP A_RestoreSpecialPosition diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 70ea25e915..48172b8212 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -710,6 +710,7 @@ class Actor : Thinker native native clearscope double GetCameraHeight() const; native clearscope double GetGravity() const; native clearscope int GetLevelSpawnTime() const; + native clearscope int GetAge() const; native bool CheckClass(class checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false); native void AddInventory(Inventory inv); From 9b7114a96da320307503b480b4433796978d3612 Mon Sep 17 00:00:00 2001 From: Player701 Date: Sun, 4 Nov 2018 21:34:00 +0300 Subject: [PATCH 08/39] - undid the save version bump --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index 36707442e2..61c6ceff8e 100644 --- a/src/version.h +++ b/src/version.h @@ -91,7 +91,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4553 +#define SAVEVER 4552 // This is so that derivates can use the same savegame versions without worrying about engine compatibility #define GAMESIG "GZDOOM" From 49bfdbef9f0643e495c80734762822f429586df5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Nov 2018 16:47:45 +0100 Subject: [PATCH 09/39] - create an intermediate structure between sectors and subsectors. A section is a continuous part of a sector or in some case of several nearby continuous parts. For sectors with far away parts multiple sections will be created, especially when they lie in disjoint parts of the map. This is mainly supposed to cut down on time for linking dynamic lights. Since they need to traverse subsectors to find all touching sidedefs a more coarse data structure that only contains the info needed for this is more suitable. In particular, this does not contain any intra-sector lines, i.e. those with both sides in the same sector. --- src/CMakeLists.txt | 1 + src/am_map.cpp | 2 +- src/hwrenderer/data/hw_sections.cpp | 764 +++++++++++++++++++++++++ src/hwrenderer/data/hw_sections.h | 126 +++++ src/p_setup.cpp | 2 + src/p_setup.h | 3 + src/r_data/renderinfo.cpp | 244 +++++++- src/r_defs.h | 1 + src/tarray.h | 141 +++++ unused/gl_sections.cpp | 845 ---------------------------- unused/gl_sections.h | 57 -- 11 files changed, 1282 insertions(+), 904 deletions(-) create mode 100644 src/hwrenderer/data/hw_sections.cpp create mode 100644 src/hwrenderer/data/hw_sections.h delete mode 100644 unused/gl_sections.cpp delete mode 100644 unused/gl_sections.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5ebda5b16..559379a6d9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1049,6 +1049,7 @@ set (PCH_SOURCES gl/system/gl_buffers.cpp gl/textures/gl_hwtexture.cpp gl/textures/gl_samplers.cpp + hwrenderer/data/hw_sections.cpp hwrenderer/data/flatvertices.cpp hwrenderer/data/hw_viewpointbuffer.cpp hwrenderer/dynlights/hw_aabbtree.cpp diff --git a/src/am_map.cpp b/src/am_map.cpp index e0296e797a..58ac470855 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -2072,7 +2072,7 @@ void AM_drawSubsectors() continue; } - if ((!(subsectors[i].flags & SSECMF_DRAWN) || (subsectors[i].render_sector->MoreFlags & SECMF_HIDDEN)) && am_cheat == 0) + if ((!(subsectors[i].flags & SSECMF_DRAWN) || (subsectors[i].flags & SSECF_HOLE) || (subsectors[i].render_sector->MoreFlags & SECMF_HIDDEN)) && am_cheat == 0) { continue; } diff --git a/src/hwrenderer/data/hw_sections.cpp b/src/hwrenderer/data/hw_sections.cpp new file mode 100644 index 0000000000..9364b9712b --- /dev/null +++ b/src/hwrenderer/data/hw_sections.cpp @@ -0,0 +1,764 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2008-2018 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + + + +#include +#include "i_system.h" +#include "p_local.h" +#include "c_dispatch.h" +#include "r_defs.h" +#include "g_levellocals.h" +#include "hw_sections.h" +#include "earcut.hpp" +#include "stats.h" +#include "p_setup.h" +#include "c_dispatch.h" +#include "memarena.h" + +using DoublePoint = std::pair; + +template<> struct THashTraits +{ + // Use all bits when hashing doubles instead of converting them to ints. + hash_t Hash(const DoublePoint &key) + { + return (hash_t)SuperFastHash((const char*)(const void*)&key, sizeof(key)); + } + int Compare(const DoublePoint &left, const DoublePoint &right) { return left != right; } +}; + + +struct WorkSectionLine +{ + vertex_t *start; + vertex_t *end; + side_t *sidedef; + seg_t *refseg; + int tempindex; + int mygroup; + WorkSectionLine *partner; + bool flagged; +}; + + +struct WorkSection +{ + int sectorindex; + int mapsection; + bool hasminisegs; + TArraysegments; + TArray originalSides; // The segs will lose some of these while working on them. +}; + +struct TriangleWorkData +{ + BoundingRect boundingBox; + unsigned boundingLoopStart; +}; + +struct GroupWork +{ + WorkSection *section; + TriangleWorkData *work; + int index; +}; + +struct Group +{ + TArray groupedSections; + TMap sideMap; + TArray segments; +}; + +class FSectionCreator +{ + FMemArena allocator; + TArrayAllAllocatedLines; + + bool verbose = false; + TMap> subsectormap; + TArray> rawsections; // list of unprocessed subsectors. Sector and mapsection can be retrieved from the elements so aren't stored. + TArray sections; + + TArray triangles; + + TArray groups; + TArray groupForSection; + +public: + + FSectionCreator() + { + // These must be manually destroyed but not deleted. + for (auto line : AllAllocatedLines) + { + line->~WorkSectionLine(); + } + } + + // Allocate these from a static allocator so that they remain fixed in memory and avoid excessive small allocations. + WorkSectionLine *NewLine() + { + void *mem = allocator.Alloc(sizeof(WorkSectionLine)); + auto line = new(mem) WorkSectionLine; + AllAllocatedLines.Push(line); + return line; + } + + int IndexOf(WorkSectionLine *line) + { + return AllAllocatedLines.Find(line); // slow, but only used for debugging. + } + + int MakeKey(int sector, int mapsection) + { + return sector + (mapsection << 16); + } + + int MakeKey(const subsector_t &sub) + { + return MakeKey(sub.render_sector->Index(), sub.mapsection); + } + + int SectorFromKey(int key) + { + return key & 65535; + } + + int MapsectionFromKey(int key) + { + return key >> 16; + } + + //========================================================================== + // + // Groups subsectors by both sectors and mapsection + // The parts of a sector in a single map section is the upper boundary + // for what a sector section may contain. + // + //========================================================================== + + void GroupSubsectors() + { + for (auto &sub : level.subsectors) + { + int key = MakeKey(sub); + auto &array = subsectormap[key]; + array.Push(sub.Index()); + } + } + + //========================================================================== + // + // Go through the list and split each element further into + // connected groups of subsectors. + // + //========================================================================== + + void CompileSections() + { + TMap>::Pair *pair; + TMap>::Iterator it(subsectormap); + + while (it.NextPair(pair)) + { + CompileSections(pair->Value); + } + subsectormap.Clear(); + } + + //========================================================================== + // + // Find all groups of connected subsectors and put them into the group list + // + //========================================================================== + + void CompileSections(TArray &list) + { + TArray sublist; + TArray seglist; + while (list.Size() > 0) + { + sublist.Clear(); + seglist.Clear(); + int index; + list.Pop(index); + auto sub = &level.subsectors[index]; + + auto collect = [&](subsector_t *sub) + { + sublist.Push(sub->Index()); + for (unsigned i = 0; i < sub->numlines; i++) + { + if (sub->firstline[i].PartnerSeg && sub->firstline[i].Subsector->render_sector == sub->firstline[i].PartnerSeg->Subsector->render_sector) + { + seglist.Push(sub->firstline[i].PartnerSeg); + } + } + }; + + collect(sub); + + for (unsigned i = 0; i < seglist.Size(); i++) + { + auto subi = seglist[i]->Subsector->Index(); + + for (unsigned j = 0; j < list.Size(); j++) + { + if (subi == list[j]) + { + collect(&level.subsectors[subi]); + list.Delete(j); + j--; + } + } + } + rawsections.Push(std::move(sublist)); + } + } + + + //========================================================================== + // + // + //========================================================================== + + void MakeOutlines() + { + TArray lineForSeg(level.segs.Size(), true); + memset(lineForSeg.Data(), 0, sizeof(WorkSectionLine*) * level.segs.Size()); + for (auto &list : rawsections) + { + MakeOutline(list, lineForSeg); + } + + // Assign partners after everything has been collected + for (auto §ion : sections) + { + for (auto seg : section.segments) + { + if (seg->refseg && seg->refseg->PartnerSeg) + { + seg->partner = lineForSeg[seg->refseg->PartnerSeg->Index()]; + } + } + } + } + + //========================================================================== + // + // Creates an outline for a given section + // + //========================================================================== + + void MakeOutline(TArray &rawsection, TArray &lineForSeg) + { + TArray foundsides; + TArray outersegs; + TArray loopedsegs; + bool hasminisegs = false; + + // Collect all the segs that make up the outline of this section. + for (auto j : rawsection) + { + auto sub = &level.subsectors[j]; + + for (unsigned i = 0; i < sub->numlines; i++) + { + if (!sub->firstline[i].PartnerSeg || sub->firstline[i].Subsector->render_sector != sub->firstline[i].PartnerSeg->Subsector->render_sector) + { + outersegs.Push(&sub->firstline[i]); + if (sub->firstline[i].sidedef == nullptr) hasminisegs = true; + } + if (sub->firstline[i].sidedef) + { + foundsides.Push(sub->firstline[i].sidedef); // This may contain duplicate. Let's deal with those later. + } + } + } + + // Loop until all segs have been used. + seg_t *seg = nullptr; + unsigned startindex = 0; + while (outersegs.Size() > 0) + { + if (seg == nullptr) + { + for (unsigned i = 0; i < outersegs.Size(); i++) + { + if (outersegs[i]->sidedef != nullptr && outersegs[i]->sidedef->V1() == outersegs[i]->v1) + { + seg = outersegs[i]; + outersegs.Delete(i); + break; + } + } + if (seg == nullptr) + { + // There's only minisegs left. Most likely this is just node garbage. + // Todo: Need to check. + seg = outersegs[0]; + outersegs.Delete(0); + } + startindex = loopedsegs.Push(seg); + } + + // Find the next seg in the loop. + + auto segangle = VecToAngle(seg->v2->fPos() - seg->v1->fPos()); + + seg_t *pick = nullptr; + int pickindex = -1; + for (unsigned i = 0; i < outersegs.Size(); i++) + { + auto secondseg = outersegs[i]; + if (secondseg->v1->fPos() == seg->v2->fPos()) + { + // This should never choose a miniseg over a real sidedef. + if (pick == nullptr || (pick->sidedef == nullptr && secondseg->sidedef != nullptr)) + { + pick = secondseg; + pickindex = i; + } + else if (pick->sidedef == nullptr || secondseg->sidedef != nullptr) + { + // If there's more than one pick the one with the smallest angle. + auto pickangle = deltaangle(segangle, VecToAngle(pick->v2->fPos() - pick->v1->fPos())); + auto secondangle = deltaangle(segangle, VecToAngle(secondseg->v2->fPos() - secondseg->v1->fPos())); + + if (secondangle < pickangle) + { + pick = secondseg; + pickindex = i; + } + } + } + } + if (pick) + { + loopedsegs.Push(pick); + outersegs.Delete(pickindex); + seg = pick; + } + else + { + // There was no more seg connecting to the last one. We should have reached the beginning of the loop again. + if (loopedsegs.Last() == nullptr || loopedsegs[startindex]->v1->fPos() != loopedsegs.Last()->v2->fPos()) + { + // Did not find another one but have an unclosed loop. This should never happen and would indicate broken nodes. + // Error out and let the calling code deal with it. + I_Error("Unclosed loop in sector %d at position (%d, %d)\n", loopedsegs[0]->Subsector->render_sector->Index(), (int)loopedsegs[0]->v1->fX(), (int)loopedsegs[0]->v1->fY()); + } + seg = nullptr; + loopedsegs.Push(nullptr); // A separator is not really needed but useful for debugging. + } + } + if (loopedsegs.Size() > 0) + { + auto sector = loopedsegs[0]->Subsector->render_sector->Index(); + auto mapsec = loopedsegs[0]->Subsector->mapsection; + + TArray sectionlines(loopedsegs.Size(), true); + for (unsigned i = 0; i < loopedsegs.Size(); i++) + { + if (loopedsegs[i]) + { + sectionlines[i] = NewLine(); + *sectionlines[i] = { loopedsegs[i]->v1, loopedsegs[i]->v2, loopedsegs[i]->sidedef, loopedsegs[i], -1, (int)sections.Size(), nullptr }; + lineForSeg[loopedsegs[i]->Index()] = sectionlines[i]; + } + else + { + sectionlines[i] = NewLine(); + *sectionlines[i] = { nullptr, nullptr, nullptr, nullptr, -1, (int)sections.Size(), nullptr }; + } + } + + sections.Push({ sector, mapsec, hasminisegs, std::move(sectionlines), std::move(foundsides) }); + } + } + + + //============================================================================= + // + // Tries to merge continuous segs on the same sidedef + // + //============================================================================= + void MergeLines() + { + for (auto &build : sections) + { + for (int i = (int)build.segments.Size() - 1; i > 0; i--) + { + auto ln1 = build.segments[i]; + auto ln2 = build.segments[i - 1]; + + if (ln1->sidedef && ln2->sidedef && ln1->sidedef == ln2->sidedef) + { + if (ln1->partner == nullptr && ln2->partner == nullptr) + { + // identical references. These 2 lines can be merged. + ln2->end = ln1->end; + build.segments.Delete(i); + } + else if (ln1->partner && ln2->partner && ln1->partner->mygroup == ln2->partner->mygroup) + { + auto §ion = sections[ln1->partner->mygroup]; + auto index1 = section.segments.Find(ln1->partner); // note that ln1 and ln2 are ordered backward, so the partners are ordered forward. + auto index2 = section.segments.Find(ln2->partner); + if (index2 == index1 + 1 && index2 < section.segments.Size()) + { + // Merge both sides at once to ensure the data remains consistent. + ln2->partner->start = ln1->partner->start; + section.segments.Delete(index1); + ln2->end = ln1->end; + build.segments.Delete(i); + } + } + } + } + } + } + + + //============================================================================= + // + // + // + //============================================================================= + + void FindOuterLoops() + { + triangles.Resize(sections.Size()); + for (unsigned int i = 0; i < sections.Size(); i++) + { + auto §ion = sections[i]; + auto &work = triangles[i]; + + BoundingRect bounds = { 1e32, 1e32, -1e32, -1e32 }; + BoundingRect loopBounds = { 1e32, 1e32, -1e32, -1e32 }; + BoundingRect outermostBounds = { 1e32, 1e32, -1e32, -1e32 }; + unsigned outermoststart = ~0u; + unsigned loopstart = 0; + bool ispolyorg = false; + for (unsigned i = 0; i <= section.segments.Size(); i++) + { + if (i < section.segments.Size() && section.segments[i]->refseg) + { + ispolyorg |= !!(section.segments[i]->refseg->Subsector->flags & SSECF_POLYORG); + loopBounds.addVertex(section.segments[i]->start->fX(), section.segments[i]->start->fY()); + loopBounds.addVertex(section.segments[i]->end->fX(), section.segments[i]->end->fY()); + bounds.addVertex(section.segments[i]->start->fX(), section.segments[i]->start->fY()); + bounds.addVertex(section.segments[i]->end->fX(), section.segments[i]->end->fY()); + } + else + { + if (outermostBounds.left == 1e32 || loopBounds.contains(outermostBounds)) + { + outermostBounds = loopBounds; + outermoststart = loopstart; + loopstart = i + 1; + } + loopBounds = { 1e32, 1e32, -1e32, -1e32 }; + } + } + work.boundingLoopStart = outermoststart; + work.boundingBox = outermostBounds; + } + } + + //============================================================================= + // + // + // + //============================================================================= + + void GroupSections() + { + TArray workingSet; + + GroupWork first = { §ions[0], &triangles[0], 0 }; + workingSet.Push(first); + groupForSection.Resize(sections.Size()); + for (auto &i : groupForSection) i = -1; + + for (unsigned i = 1; i < sections.Size(); i++) + { + auto sect = §ions[i]; + if (sect->segments.Size() == 0) continue; + if (sect->sectorindex == first.section->sectorindex && sect->mapsection == first.section->mapsection) + { + workingSet.Push({ sect, &triangles[i], (int)i }); + } + else + { + GroupWorkingSet(workingSet); + workingSet.Clear(); + first = { §ions[i], &triangles[i], (int)i }; + workingSet.Push(first); + } + } + GroupWorkingSet(workingSet); + } + + //============================================================================= + // + // + // + //============================================================================= + + void GroupWorkingSet(TArray &workingSet) + { + const double MAX_GROUP_DIST = 256; + TArray build; + + if (workingSet.Size() == 1) + { + groupForSection[workingSet[0].index] = groups.Size(); + Group g; + g.groupedSections = std::move(workingSet); + groups.Push(std::move(g)); + return; + } + + while (workingSet.Size() > 0) + { + build.Clear(); + build.Push(workingSet[0]); + groupForSection[workingSet[0].index] = groups.Size(); + workingSet.Delete(0); + + // Don't use iterators here. These arrays are modified inside. + for (unsigned j = 0; j < build.Size(); j++) + { + auto ¤t = build[j]; + for (int i = 0; i < (int)workingSet.Size(); i++) + { + // Are both sections close together? + double dist = current.work->boundingBox.distanceTo(workingSet[i].work->boundingBox); + if (dist < MAX_GROUP_DIST) + { + build.Push(workingSet[i]); + groupForSection[workingSet[i].index] = groups.Size(); + workingSet.Delete(i); + i--; + continue; + } + // Also put in the same group if they share a sidedef. + bool sharing_sd = CheckForSharedSidedef(*current.section, *workingSet[i].section); + if (sharing_sd) + { + build.Push(workingSet[i]); + groupForSection[workingSet[i].index] = groups.Size(); + workingSet.Delete(i); + i--; + continue; + } + } + } + Group g; + g.groupedSections = std::move(build); + groups.Push(std::move(g)); + } + } + + //============================================================================= + // + // + // + //============================================================================= + + bool CheckForSharedSidedef(WorkSection &set1, WorkSection &set2) + { + for (auto seg : set1.segments) + { + if (seg->sidedef != nullptr) + { + for (auto seg2 : set2.segments) + { + if (seg2->sidedef == seg->sidedef) return true; + } + } + } + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + void ConstructOutput(FSectionContainer &output) + { + output.allSections.Resize(groups.Size()); + output.allIndices.Resize(level.subsectors.Size() + level.sides.Size()); + output.sectionForSubsectorPtr = &output.allIndices[0]; + output.sectionForSidedefPtr = &output.allIndices[level.subsectors.Size()]; + + unsigned numsegments = 0; + unsigned numsides = 0; + + // First index all the line segments here so that we can later do some quick referencing via the global array and count them and the distinct number of sidedefs in the section. + for (auto &group : groups) + { + for (auto &work : group.groupedSections) + { + auto §ion = *work.section; + for (auto segment : section.segments) + { + if (segment->refseg) + { + segment->tempindex = numsegments++; + group.segments.Push(segment); + } + } + for (auto side : section.originalSides) + { + group.sideMap[side->Index()] = true; + } + } + numsides += group.sideMap.CountUsed(); + } + output.allLines.Resize(numsegments); + output.allSides.Resize(numsides); + + numsegments = 0; + numsides = 0; + + // Now piece it all together + unsigned curgroup = 0; + for (auto &group : groups) + { + FSection &dest = output.allSections[curgroup]; + dest.sector = &level.sectors[group.groupedSections[0].section->sectorindex]; + dest.mapsection = (short)group.groupedSections[0].section->mapsection; + dest.hacked = false; + dest.lighthead = nullptr; + dest.validcount = 0; + dest.segments.Set(&output.allLines[numsegments], group.segments.Size()); + dest.sides.Set(&output.allSides[numsides], group.sideMap.CountUsed()); + dest.bounds = {1e32, 1e32, -1e32, -1e32}; + numsegments += group.segments.Size(); + + for (auto &segment : group.segments) + { + // Use the indices calculated above to store these elements. + auto &fseg = output.allLines[segment->tempindex]; + fseg.start = segment->start; + fseg.end = segment->end; + fseg.partner = segment->partner == nullptr ? nullptr : &output.allLines[segment->partner->tempindex]; + fseg.sidedef = segment->sidedef; + dest.bounds.addVertex(fseg.start->fX(), fseg.start->fY()); + dest.bounds.addVertex(fseg.end->fX(), fseg.end->fY()); + } + TMap::Iterator it(group.sideMap); + TMap::Pair *pair; + while (it.NextPair(pair)) + { + output.allSides[numsides++] = &level.sides[pair->Key]; + } + curgroup++; + } + } +}; + + + +//============================================================================= +// +// +// +//============================================================================= + +void PrintSections(FSectionContainer &container) +{ + for (unsigned i = 0; i < container.allSections.Size(); i++) + { + auto §ion = container.allSections[i]; + + Printf(PRINT_LOG, "\n\nStart of section %d sector %d, mapsection %d, bounds=(%2.3f, %2.3f, %2.3f, %2.3f)\n", i, section.sector->Index(), section.mapsection, + section.bounds.left, section.bounds.top, section.bounds.right, section.bounds.bottom); + + for (unsigned j = 0; j < section.segments.Size(); j++) + { + auto &seg = section.segments[j]; + if (j > 0 && seg.start != section.segments[j - 1].end) + { + Printf(PRINT_LOG, "\n"); + } + FString partnerstring; + if (seg.partner) + { + if (seg.partner->sidedef) partnerstring.Format(", partner = %d (line %d)", seg.partner->sidedef->Index(), seg.partner->sidedef->linedef->Index()); + else partnerstring = ", partner = seg"; + } + else if (seg.sidedef && seg.sidedef->linedef) + { + partnerstring = ", one-sided line"; + } + if (seg.sidedef) + { + Printf(PRINT_LOG, "segment for sidedef %d (line %d) from (%2.6f, %2.6f) to (%2.6f, %2.6f)%s\n", + seg.sidedef->Index(), seg.sidedef->linedef->Index(), seg.start->fX(), seg.start->fY(), seg.end->fX(), seg.end->fY(), partnerstring.GetChars()); + } + else + { + Printf(PRINT_LOG, "segment for seg from (%2.6f, %2.6f) to (%2.6f, %2.6f)%s\n", + seg.start->fX(), seg.start->fY(), seg.end->fX(), seg.end->fY(), partnerstring.GetChars()); + } + } + } + Printf(PRINT_LOG, "%d sectors, %d subsectors, %d sections\n", level.sectors.Size(), level.subsectors.Size(), container.allSections.Size()); +} + + +//============================================================================= +// +// +// +//============================================================================= + +void MakeSections() +{ + FSectionContainer container; + cycle_t timer; + timer.Reset(); + timer.Clock(); + FSectionCreator creat; + creat.GroupSubsectors(); + creat.CompileSections(); + creat.MakeOutlines(); + creat.MergeLines(); + creat.FindOuterLoops(); + creat.GroupSections(); + creat.ConstructOutput(container); + timer.Unclock(); + PrintSections(container); + Printf("Time = %2.3f ms\n", timer.TimeMS()); +} + +CCMD(makesections) +{ + MakeSections(); +} \ No newline at end of file diff --git a/src/hwrenderer/data/hw_sections.h b/src/hwrenderer/data/hw_sections.h new file mode 100644 index 0000000000..1cc6f2efa2 --- /dev/null +++ b/src/hwrenderer/data/hw_sections.h @@ -0,0 +1,126 @@ + +#ifndef __GL_SECTIONS_H +#define __GL_SECTIONS_H + +#include "tarray.h" +#include "r_defs.h" + +//========================================================================== +// +// +// +//========================================================================== + +struct BoundingRect +{ + double left, top, right, bottom; + + bool contains(const BoundingRect & other) + { + return left <= other.left && top <= other.top && right >= other.right && bottom >= other.bottom; + } + + bool intersects(const BoundingRect & other) + { + return !(other.left > right || + other.right < left || + other.top > bottom || + other.bottom < top); + } + + void Union(const BoundingRect & other) + { + if (other.left < left) left = other.left; + if (other.right > right) right = other.right; + if (other.top < top) top = other.top; + if (other.bottom > bottom) bottom = other.bottom; + } + + double distanceTo(const BoundingRect &other) + { + if (intersects(other)) return 0; + return std::max(std::min(fabs(left - other.right), fabs(right - other.left)), + std::min(fabs(top - other.bottom), fabs(bottom - other.top))); + } + + void addVertex(double x, double y) + { + if (x < left) left = x; + if (x > right) right = x; + if (y < top) top = y; + if (y > bottom) bottom = y; + } + + bool operator == (const BoundingRect &other) + { + return left == other.left && top == other.top && right == other.right && bottom == other.bottom; + } +}; + + +struct FSectionLine +{ + vertex_t *start; + vertex_t *end; + FSectionLine *partner; + side_t *sidedef; +}; + +struct FSection +{ + // tbd: Do we need a list of subsectors here? Ideally the subsectors should not be used anywhere anymore except for finding out where a location is. + TArrayView segments; + TArrayView sides; // contains all sidedefs, including the internal ones that do not make up the outer shape. (this list is not exclusive. A sidedef can be in multiple sections!) + sector_t *sector; + FLightNode *lighthead; // Light nodes (blended and additive) + BoundingRect bounds; + int validcount; + short mapsection; + char hacked; // 1: is part of a render hack +}; + +class FSectionContainer +{ +public: + TArray allLines; + TArray allSections; + //TArray allVertices; + TArray allSides; + TArray allIndices; + + int *sectionForSubsectorPtr; // stored inside allIndices + int *sectionForSidedefPtr; // also stored inside allIndices; + + FSection *SectionForSubsector(subsector_t *sub) + { + return SectionForSubsector(sub->Index()); + } + FSection *SectionForSubsector(int ssindex) + { + return ssindex < 0 ? nullptr : &allSections[sectionForSubsectorPtr[ssindex]]; + } + FSection *SectionForSidedef(side_t *side) + { + return SectionForSidedef(side->Index()); + } + FSection *SectionForSidedef(int sindex) + { + return sindex < 0 ? nullptr : &allSections[sectionForSidedefPtr[sindex]]; + } + void Clear() + { + allLines.Clear(); + allSections.Clear(); + allIndices.Clear(); + } + void Reset() + { + Clear(); + allLines.ShrinkToFit(); + allSections.ShrinkToFit(); + allIndices.ShrinkToFit(); + } +}; + + +#endif \ No newline at end of file diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 4a48afbb64..d3ff4c4999 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -4055,6 +4055,8 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame) if (hasglnodes) { P_SetRenderSector(); + FixMinisegReferences(); + FixHoles(); } bodyqueslot = 0; diff --git a/src/p_setup.h b/src/p_setup.h index 31612894b8..1f773e951f 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -160,6 +160,9 @@ bool P_LoadGLNodes(MapData * map); bool P_CheckNodes(MapData * map, bool rebuilt, int buildtime); bool P_CheckForGLNodes(); void P_SetRenderSector(); +void FixMinisegReferences(); +void FixHoles(); +void ReportUnpairedMinisegs(); struct sidei_t // [RH] Only keep BOOM sidedef init stuff around for init diff --git a/src/r_data/renderinfo.cpp b/src/r_data/renderinfo.cpp index f0b8600fec..bf59ec0c0a 100644 --- a/src/r_data/renderinfo.cpp +++ b/src/r_data/renderinfo.cpp @@ -247,7 +247,7 @@ static void PrepareSectorData() for (auto &sub : level.subsectors) { - sub.sectorindex = (uint16_t)sub.render_sector->subsectorcount++; + sub.render_sector->subsectorcount++; } for (auto &sec : level.sectors) @@ -573,7 +573,244 @@ void InitRenderInfo() } +//========================================================================== +// +// FixMinisegReferences +// +// Sometimes it can happen that two matching minisegs do not have their partner set. +// Fix that here. +// +//========================================================================== +void FixMinisegReferences() +{ + TArray bogussegs; + + for (unsigned i = 0; i < level.segs.Size(); i++) + { + if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr) + { + bogussegs.Push(&level.segs[i]); + } + } + + for (unsigned i = 0; i < bogussegs.Size(); i++) + { + auto seg1 = bogussegs[i]; + seg_t *pick = nullptr; + unsigned int picki = -1; + + // Try to fix the reference: If there's exactly one other seg in the set which matches as a partner link those two segs together. + for (unsigned j = i + 1; j < bogussegs.Size(); j++) + { + auto seg2 = bogussegs[j]; + if (seg1->v1 == seg2->v2 && seg2->v1 == seg1->v2 && seg1->Subsector->render_sector == seg2->Subsector->render_sector) + { + pick = seg2; + picki = j; + break; + } + } + if (pick) + { + DPrintf(DMSG_NOTIFY, "Linking miniseg pair from (%2.3f, %2.3f) -> (%2.3f, %2.3f) in sector %d\n", pick->v2->fX(), pick->v2->fY(), pick->v1->fX(), pick->v1->fY(), pick->frontsector->Index()); + pick->PartnerSeg = seg1; + seg1->PartnerSeg = pick; + assert(seg1->v1 == pick->v2 && pick->v1 == seg1->v2); + bogussegs.Delete(picki); + bogussegs.Delete(i); + i--; + } + } +} + +//========================================================================== +// +// FixHoles +// +// ZDBSP can leave holes in the node tree on extremely detailed maps. +// To help out the triangulator these are filled with dummy subsectors +// so that it can process the area correctly. +// +//========================================================================== + +void FixHoles() +{ + TArray bogussegs; + TArray> segloops; + + for (unsigned i = 0; i < level.segs.Size(); i++) + { + if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr) + { + bogussegs.Push(&level.segs[i]); + } + } + + while (bogussegs.Size() > 0) + { + segloops.Reserve(1); + auto *segloop = &segloops.Last(); + + seg_t *startseg; + seg_t *checkseg; + while (bogussegs.Size() > 0) + { + bool foundsome = false; + if (segloop->Size() == 0) + { + bogussegs.Pop(startseg); + segloop->Push(startseg); + checkseg = startseg; + } + for (unsigned i = 0; i < bogussegs.Size(); i++) + { + auto seg1 = bogussegs[i]; + + if (seg1->v1 == checkseg->v2 && seg1->Subsector->render_sector == checkseg->Subsector->render_sector) + { + foundsome = true; + segloop->Push(seg1); + bogussegs.Delete(i); + i--; + checkseg = seg1; + + if (seg1->v2 == startseg->v1) + { + // The loop is complete. Start a new one + segloops.Reserve(1); + segloop = &segloops.Last(); + } + } + } + if (!foundsome) + { + if ((*segloop)[0]->v1 != segloop->Last()->v2) + { + // There was no connected seg, leaving an unclosed loop. + // Clear this and continue looking. + segloop->Clear(); + } + } + } + for (unsigned i = 0; i < segloops.Size(); i++) + { + if (segloops[i].Size() == 0) + { + segloops.Delete(i); + i--; + } + } + + // Add dummy entries to the level's seg and subsector arrays + if (segloops.Size() > 0) + { + // cound the number of segs to add. + unsigned segcount = 0; + for (auto &segloop : segloops) + segcount += segloop.Size(); + + seg_t *oldsegstartptr = &level.segs[0]; + subsector_t *oldssstartptr = &level.subsectors[0]; + + unsigned newsegstart = level.segs.Reserve(segcount); + unsigned newssstart = level.subsectors.Reserve(segloops.Size()); + + seg_t *newsegstartptr = &level.segs[0]; + subsector_t *newssstartptr = &level.subsectors[0]; + + // Now fix all references to these in the level data. + // Note that the Index() method does not work here due to the reallocation. + for (auto &seg : level.segs) + { + if (seg.PartnerSeg) seg.PartnerSeg = newsegstartptr + (seg.PartnerSeg - oldsegstartptr); + seg.Subsector = newssstartptr + (seg.Subsector - oldssstartptr); + } + for (auto &sub : level.subsectors) + { + sub.firstline = newsegstartptr + (sub.firstline - oldsegstartptr); + } + for (auto &node : level.nodes) + { + // How hideous... :( + for (auto & p : node.children) + { + auto intp = (intptr_t)p; + if (intp & 1) + { + subsector_t *sp = (subsector_t*)(intp - 1); + sp = newssstartptr + (sp - oldssstartptr); + intp = intptr_t(sp) + 1; + p = (void*)intp; + } + } + } + for (auto &segloop : segloops) + { + for (auto &seg : segloop) + { + seg = newsegstartptr + (seg - oldsegstartptr); + } + } + + // The seg lists in the sidedefs and the subsector lists in the sectors are not set yet when this gets called. + + // Add the new data. This doesn't care about convexity. It is never directly used to generate a primitive. + for (auto &segloop : segloops) + { + Printf("Adding dummy subsector for sector %d\n", segloop[0]->Subsector->render_sector->Index()); + + subsector_t &sub = level.subsectors[newssstart++]; + memset(&sub, 0, sizeof(sub)); + sub.sector = segloop[0]->frontsector; + sub.render_sector = segloop[0]->Subsector->render_sector; + sub.numlines = segloop.Size(); + sub.firstline = &level.segs[newsegstart]; + sub.flags = SSECF_HOLE; + + for (auto otherseg : segloop) + { + Printf(" Adding seg from (%2.3f, %2.3f) -> (%2.3f, %2.3f)\n", otherseg->v2->fX(), otherseg->v2->fY(), otherseg->v1->fX(), otherseg->v1->fY()); + seg_t &seg = level.segs[newsegstart++]; + memset(&seg, 0, sizeof(seg)); + seg.v1 = otherseg->v2; + seg.v2 = otherseg->v1; + seg.frontsector = seg.backsector = otherseg->backsector = otherseg->frontsector; + seg.PartnerSeg = otherseg; + otherseg->PartnerSeg = &seg; + seg.Subsector = ⊂ + } + } + } + } +} + +//========================================================================== +// +// ReportUnpairedMinisegs +// +// Debug routine +// reports all unpaired minisegs that couldn't be fixed by either +// explicitly pairing them or combining them to a dummy subsector +// +//========================================================================== + +void ReportUnpairedMinisegs() +{ + int bogus = 0; + for (unsigned i = 0; i < level.segs.Size(); i++) + { + if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr) + { + Printf("Unpaired miniseg %d, sector %d, (%d: %2.6f, %2.6f) -> (%d: %2.6f, %2.6f)\n", + i, level.segs[i].Subsector->render_sector->Index(), + level.segs[i].v1->Index(), level.segs[i].v1->fX(), level.segs[i].v1->fY(), + level.segs[i].v2->Index(), level.segs[i].v2->fX(), level.segs[i].v2->fY()); + bogus++; + } + } + if (bogus > 0) Printf("%d unpaired minisegs found\n", bogus); +} //========================================================================== // // @@ -594,3 +831,8 @@ CCMD(listmapsections) } } } + +CCMD(listbadminisegs) +{ + ReportUnpairedMinisegs(); +} diff --git a/src/r_defs.h b/src/r_defs.h index f3da27262d..4bccef5269 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -1437,6 +1437,7 @@ enum SSECF_DEGENERATE = 1, SSECMF_DRAWN = 2, SSECF_POLYORG = 4, + SSECF_HOLE = 8, }; struct FPortalCoverage diff --git a/src/tarray.h b/src/tarray.h index 153fcd0ba5..79b8853bd0 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -250,6 +250,12 @@ public: return Array[Count-1]; } + // returns address of first element + T *Data() const + { + return &Array[0]; + } + unsigned int Find(const T& item) const { unsigned int i; @@ -267,6 +273,7 @@ public: ::new((void*)&Array[Count]) T(item); return Count++; } + void Append(const TArray &item) { unsigned start = Reserve(item.Size()); @@ -350,6 +357,18 @@ public: ::new ((void *)&Array[index]) T(item); } } + + // Reserves a range of entries in the middle of the array, shifting elements as needed + void ReserveAt(unsigned int index, unsigned int amount) + { + Grow(amount); + memmove(&Array[index + amount], &Array[index], sizeof(T)*(Count - index - amount)); + for (unsigned i = 0; i < amount; i++) + { + ::new ((void *)&Array[index + i]) T(); + } + } + void ShrinkToFit () { if (Most > Count) @@ -1332,3 +1351,125 @@ public: memset(&bytes[0], 0, bytes.Size()); } }; + + +// A wrapper to externally stored data. +// I would have expected something for this in the stl, but std::span is only in C++20. +template +class TArrayView +{ +public: + + typedef TIterator iterator; + typedef TIterator const_iterator; + + iterator begin() + { + return &Array[0]; + } + const_iterator begin() const + { + return &Array[0]; + } + const_iterator cbegin() const + { + return &Array[0]; + } + + iterator end() + { + return &Array[Count]; + } + const_iterator end() const + { + return &Array[Count]; + } + const_iterator cend() const + { + return &Array[Count]; + } + + + //////// + TArrayView() = default; // intended to keep this type trivial. + TArrayView(T *data, unsigned count = 0) + { + Count = count; + Array = data; + } + TArrayView(const TArrayView &other) + { + Count = other.Count; + Array = other.Array; + } + TArrayView &operator= (const TArrayView &other) + { + Count = other.Count; + Array = other.Array; + return *this; + } + // Check equality of two arrays + bool operator==(const TArrayView &other) const + { + if (Count != other.Count) + { + return false; + } + for (unsigned int i = 0; i < Count; ++i) + { + if (Array[i] != other.Array[i]) + { + return false; + } + } + return true; + } + // Return a reference to an element + T &operator[] (size_t index) const + { + return Array[index]; + } + // Returns a reference to the last element + T &Last() const + { + return Array[Count - 1]; + } + + // returns address of first element + T *Data() const + { + return &Array[0]; + } + + unsigned Size() const + { + return Count; + } + + unsigned int Find(const T& item) const + { + unsigned int i; + for (i = 0; i < Count; ++i) + { + if (Array[i] == item) + break; + } + return i; + } + + void Set(T *data, unsigned count) + { + Array = data; + Count = count; + } + + void Clear() + { + Count = 0; + Array = nullptr; + } +private: + T *Array; + unsigned int Count; +}; + diff --git a/unused/gl_sections.cpp b/unused/gl_sections.cpp deleted file mode 100644 index 08a68be8e8..0000000000 --- a/unused/gl_sections.cpp +++ /dev/null @@ -1,845 +0,0 @@ -/* -** gl_sections.cpp -** Splits sectors into continuous separate parts -** -**--------------------------------------------------------------------------- -** Copyright 2008 Christoph Oelckers -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be -** covered by the terms of the GNU Lesser General Public License as published -** by the Free Software Foundation; either version 2.1 of the License, or (at -** your option) any later version. -** 5. Full disclosure of the entire project's source code, except for third -** party libraries is mandatory. (NOTE: This clause is non-negotiable!) -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#include "gl/system/gl_system.h" -#include -#include "i_system.h" -#include "p_local.h" -#include "c_dispatch.h" -#include "gl/data/gl_sections.h" - -typedef void (CALLBACK *tessFunc)(); - -TArray SectionLines; -TArray SectionLoops; -TArray Sections; -TArray SectionForSubsector; - -CVAR (Bool, dumpsections, false, 0) - -#define ISDONE(no, p) (p[(no)>>3] & (1 << ((no)&7))) -#define SETDONE(no, p) p[(no)>>3] |= (1 << ((no)&7)) - -inline vertex_t *V1(side_t *s) -{ - line_t *ln = s->linedef; - return s == ln->sidedef[0]? ln->v1: ln->v2; -} - -inline vertex_t *V2(side_t *s) -{ - line_t *ln = s->linedef; - return s == ln->sidedef[0]? ln->v2: ln->v1; -} - -//========================================================================== -// -// -// -//========================================================================== - -class FSectionCreator -{ - static FSectionCreator *creator; - - uint8_t *processed_segs; - uint8_t *processed_subsectors; - int *section_for_segs; - - vertex_t *v1_l1, *v2_l1; - - FGLSectionLoop *loop; - FGLSection *section; // current working section - -public: - //========================================================================== - // - // - // - //========================================================================== - - FSectionCreator() - { - processed_segs = new uint8_t[(numsegs+7)/8]; - processed_subsectors = new uint8_t[(numsubsectors+7)/8]; - - memset(processed_segs, 0, (numsegs+7)/8); - memset(processed_subsectors, 0, (numsubsectors+7)/8); - - section_for_segs = new int[numsegs]; - memset(section_for_segs, -1, numsegs * sizeof(int)); - } - - //========================================================================== - // - // - // - //========================================================================== - - ~FSectionCreator() - { - delete [] processed_segs; - delete [] processed_subsectors; - delete [] section_for_segs; - } - - //========================================================================== - // - // - // - //========================================================================== - - void NewLoop() - { - section->numloops++; - loop = &SectionLoops[SectionLoops.Reserve(1)]; - loop->startline = SectionLines.Size(); - loop->numlines = 0 ; - } - - void NewSection(sector_t *sec) - { - section = &Sections[Sections.Reserve(1)]; - section->sector = sec; - section->subsectors.Clear(); - section->numloops = 0; - section->startloop = SectionLoops.Size(); - section->validcount = -1; - NewLoop(); - } - - void FinalizeSection() - { - } - - //========================================================================== - // - // - // - //========================================================================== - - bool AddSeg(seg_t *seg) - { - FGLSectionLine &line = SectionLines[SectionLines.Reserve(1)]; - - - bool firstline = loop->numlines == 0; - - if (ISDONE(seg-segs, processed_segs)) - { - // should never happen! - DPrintf("Tried to add seg %d to Sections twice. Cannot create Sections.\n", seg-segs); - return false; - } - - SETDONE(seg-segs, processed_segs); - section_for_segs[seg-segs] = Sections.Size()-1; - - line.start = seg->v1; - line.end = seg->v2; - line.sidedef = seg->sidedef; - line.linedef = seg->linedef; - line.refseg = seg; - line.polysub = NULL; - line.otherside = -1; - - if (loop->numlines == 0) - { - v1_l1 = seg->v1; - v2_l1 = seg->v2; - } - loop->numlines++; - return true; - } - - //========================================================================== - // - // Utility stuff - // - //========================================================================== - - sector_t *FrontRenderSector(seg_t *seg) - { - return seg->Subsector->render_sector; - } - - sector_t *BackRenderSector(seg_t *seg) - { - if (seg->PartnerSeg == NULL) return NULL; - return seg->PartnerSeg->Subsector->render_sector; - } - - bool IntraSectorSeg(seg_t *seg) - { - return FrontRenderSector(seg) == BackRenderSector(seg); - } - - - //========================================================================== - // - // returns the seg whose partner seg determines where this - // section continues - // - //========================================================================== - bool AddSubSector(subsector_t *subsec, vertex_t *startpt, seg_t **pNextSeg) - { - unsigned i = 0; - if (startpt != NULL) - { - // find the seg in this subsector that starts at the given vertex - for(i = 0; i < subsec->numlines; i++) - { - if (subsec->firstline[i].v1 == startpt) break; - } - if (i == subsec->numlines) - { - DPrintf("Vertex not found in subsector %d. Cannot create Sections.\n", subsec-subsectors); - return false; // Nodes are bad - } - } - else - { - // Find the first unprocessed non-miniseg - for(i = 0; i < subsec->numlines; i++) - { - seg_t *seg = subsec->firstline + i; - - if (seg->sidedef == NULL) continue; - if (IntraSectorSeg(seg)) continue; - if (ISDONE(seg-segs, processed_segs)) continue; - break; - } - if (i == subsec->numlines) - { - DPrintf("Unable to find a start seg. Cannot create Sections.\n"); - return false; // Nodes are bad - } - - startpt = subsec->firstline[i].v1; - } - - seg_t *thisseg = subsec->firstline + i; - if (IntraSectorSeg(thisseg)) - { - SETDONE(thisseg-segs, processed_segs); - // continue with the loop in the adjoining subsector - *pNextSeg = thisseg; - return true; - } - - while(1) - { - if (loop->numlines > 0 && thisseg->v1 == v1_l1 && thisseg->v2 == v2_l1) - { - // This loop is complete - *pNextSeg = NULL; - return true; - } - - if (!AddSeg(thisseg)) return NULL; - - i = (i+1) % subsec->numlines; - seg_t *nextseg = subsec->firstline + i; - - if (thisseg->v2 != nextseg->v1) - { - DPrintf("Segs in subsector %d are not continuous. Cannot create Sections.\n", subsec-subsectors); - return false; // Nodes are bad - } - - if (IntraSectorSeg(nextseg)) - { - SETDONE(nextseg-segs, processed_segs); - // continue with the loop in the adjoining subsector - *pNextSeg = nextseg; - return true; - } - thisseg = nextseg; - } - } - - //============================================================================= - // - // - // - //============================================================================= - - bool FindNextSeg(seg_t **pSeg) - { - // find an unprocessed non-miniseg or a miniseg with an unprocessed - // partner subsector that belongs to the same rendersector - for (unsigned i = 0; i < section->subsectors.Size(); i++) - { - for(unsigned j = 0; j < section->subsectors[i]->numlines; j++) - { - seg_t *seg = section->subsectors[i]->firstline + j; - bool intra = IntraSectorSeg(seg); - - if (!intra && !ISDONE(seg-segs, processed_segs)) - { - *pSeg = seg; - return true; - } - else if (intra && - !ISDONE(seg->PartnerSeg->Subsector-subsectors, processed_subsectors)) - { - *pSeg = seg->PartnerSeg; - return true; - } - } - } - *pSeg = NULL; - return true; - } - - //============================================================================= - // - // all segs and subsectors must be grouped into Sections - // - //============================================================================= - bool CheckSections() - { - bool res = true; - for (int i = 0; i < numsegs; i++) - { - if (segs[i].sidedef != NULL && !ISDONE(i, processed_segs) && !IntraSectorSeg(&segs[i])) - { - Printf("Seg %d (Linedef %d) not processed during section creation\n", i, segs[i].linedef-lines); - res = false; - } - } - for (int i = 0; i < numsubsectors; i++) - { - if (!ISDONE(i, processed_subsectors)) - { - Printf("Subsector %d (Sector %d) not processed during section creation\n", i, subsectors[i].sector-sectors); - res = false; - } - } - return res; - } - - //============================================================================= - // - // - // - //============================================================================= - void DeleteLine(int i) - { - SectionLines.Delete(i); - for(int i = SectionLoops.Size() - 1; i >= 0; i--) - { - FGLSectionLoop *loop = &SectionLoops[i]; - if (loop->startline > i) loop->startline--; - } - } - - //============================================================================= - // - // - // - //============================================================================= - void MergeLines(FGLSectionLoop *loop) - { - int i; - int deleted = 0; - FGLSectionLine *ln1; - FGLSectionLine *ln2; - // Merge identical lines in the list - for(i = loop->numlines - 1; i > 0; i--) - { - ln1 = loop->GetLine(i); - ln2 = loop->GetLine(i-1); - - if (ln1->sidedef == ln2->sidedef && ln1->otherside == ln2->otherside) - { - // identical references. These 2 lines can be merged. - ln2->end = ln1->end; - SectionLines.Delete(loop->startline + i); - loop->numlines--; - deleted++; - } - } - - // If we started in the middle of a sidedef the first and last lines - // may reference the same sidedef. check that, too. - - int loopstart = 0; - - ln1 = loop->GetLine(0); - for(i = loop->numlines - 1; i > 0; i--) - { - ln2 = loop->GetLine(i); - if (ln1->sidedef != ln2->sidedef || ln1->otherside != ln2->otherside) - break; - } - if (i < loop->numlines-1) - { - i++; - ln2 = loop->GetLine(i); - ln1->start = ln2->start; - SectionLines.Delete(loop->startline + i, loop->numlines - i); - deleted += loop->numlines - i; - loop->numlines = i; - } - - // Adjust all following loops - for(unsigned ii = unsigned(loop - &SectionLoops[0]) + 1; ii < SectionLoops.Size(); ii++) - { - SectionLoops[ii].startline -= deleted; - } - } - - //============================================================================= - // - // - // - //============================================================================= - void SetReferences() - { - for(unsigned i = 0; i < SectionLines.Size(); i++) - { - FGLSectionLine *ln = &SectionLines[i]; - seg_t *seg = ln->refseg; - - if (seg != NULL) - { - seg_t *partner = seg->PartnerSeg; - - if (seg->PartnerSeg == NULL) - { - ln->otherside = -1; - } - else - { - ln->otherside = section_for_segs[partner-segs]; - } - } - else - { - ln->otherside = -1; - } - } - - for(unsigned i = 0; i < SectionLoops.Size(); i++) - { - MergeLines(&SectionLoops[i]); - } - } - - //============================================================================= - // - // cbTessBegin - // - // called when the tesselation of a new loop starts - // - //============================================================================= - - static void CALLBACK cbTessBegin(GLenum type, void *section) - { - FGLSection *sect = (FGLSection*)section; - sect->primitives.Push(type); - sect->primitives.Push(sect->vertices.Size()); - } - - //============================================================================= - // - // cbTessError - // - // called when the tesselation failed - // - //============================================================================= - - static void CALLBACK cbTessError(GLenum error, void *section) - { - } - - //============================================================================= - // - // cbTessCombine - // - // called when the two or more vertexes are on the same coordinate - // - //============================================================================= - - static void CALLBACK cbTessCombine( GLdouble coords[3], void *vert[4], GLfloat w[4], void **dataOut ) - { - *dataOut = vert[0]; - } - - //============================================================================= - // - // cbTessVertex - // - // called when a vertex is found - // - //============================================================================= - - static void CALLBACK cbTessVertex( void *vert, void *section ) - { - FGLSection *sect = (FGLSection*)section; - sect->vertices.Push(int(intptr_t(vert))); - } - - //============================================================================= - // - // cbTessEnd - // - // called when the tesselation of a the current loop ends - // - //============================================================================= - - static void CALLBACK cbTessEnd(void *section) - { - } - - //============================================================================= - // - // - // - //============================================================================= - void tesselateSections() - { - // init tesselator - GLUtesselator *tess = gluNewTess(); - if (!tess) - { - return; - } - // set callbacks - gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (tessFunc)cbTessBegin); - gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (tessFunc)cbTessVertex); - gluTessCallback(tess, GLU_TESS_ERROR_DATA, (tessFunc)cbTessError); - gluTessCallback(tess, GLU_TESS_COMBINE, (tessFunc)cbTessCombine); - gluTessCallback(tess, GLU_TESS_END_DATA, (tessFunc)cbTessEnd); - - for(unsigned int i=0;inumloops; j++) - { - gluTessBeginContour(tess); - FGLSectionLoop *loop = sect->GetLoop(j); - for(int k=0; knumlines; k++) - { - FGLSectionLine *line = loop->GetLine(k); - vertex_t *vert = line->start; - GLdouble v[3] = { - -(double)vert->x/(double)FRACUNIT, // negate to get proper winding - 0.0, - (double)vert->y/(double)FRACUNIT - }; - gluTessVertex(tess, v, (void*)(vert - vertexes)); - } - gluTessEndContour(tess); - } - gluTessEndPolygon(tess); - sect->vertices.Push(-1337); - sect->vertices.ShrinkToFit(); - } - gluDeleteTess(tess); - } - - - //============================================================================= - // - // First mark all subsectors that have no outside boundaries as processed - // No line in such a subsector will ever be part of a section's border - // - //============================================================================= - - void MarkInternalSubsectors() - { - for(int i=0; i < numsubsectors; i++) - { - subsector_t *sub = &subsectors[i]; - int j; - - for(j=0; j < sub->numlines; j++) - { - seg_t *seg = sub->firstline + j; - if (!IntraSectorSeg(seg)) break; - } - if (j==sub->numlines) - { - // All lines are intra-sector so mark this as processed - SETDONE(i, processed_subsectors); - for(j=0; j < sub->numlines; j++) - { - seg_t *seg = sub->firstline + j; - SETDONE((sub->firstline-segs)+j, processed_segs); - if (seg->PartnerSeg != NULL) - { - SETDONE(int(seg->PartnerSeg - segs), processed_segs); - } - } - } - } - } - - //============================================================================= - // - // - // - //============================================================================= - bool CreateSections() - { - int pick = 0; - - MarkInternalSubsectors(); - while (pick < numsubsectors) - { - if (ISDONE(pick, processed_subsectors)) - { - pick++; - continue; - } - - - subsector_t *subsector = &subsectors[pick]; - - seg_t *workseg = NULL; - vertex_t *startpt = NULL; - - NewSection(subsector->render_sector); - while (1) - { - if (!ISDONE(subsector-subsectors, processed_subsectors)) - { - SETDONE(subsector-subsectors, processed_subsectors); - section->subsectors.Push(subsector); - SectionForSubsector[subsector - subsectors] = int(section - &Sections[0]); - } - - bool result = AddSubSector(subsector, startpt, &workseg); - - if (!result) - { - return false; // couldn't create Sections - } - else if (workseg != NULL) - { - // crossing into another subsector - seg_t *partner = workseg->PartnerSeg; - if (workseg->v2 != partner->v1) - { - DPrintf("Inconsistent subsector references in seg %d. Cannot create Sections.\n", workseg-segs); - return false; - } - subsector = partner->Subsector; - startpt = workseg->v1; - } - else - { - // loop complete. Check adjoining subsectors for other loops to - // be added to this section - if (!FindNextSeg(&workseg)) - { - return false; - } - else if (workseg == NULL) - { - // No more subsectors found. This section is complete! - FinalizeSection(); - break; - } - else - { - subsector = workseg->Subsector; - // If this is a regular seg, start there, otherwise start - // at the subsector's first seg - startpt = workseg->sidedef == NULL? NULL : workseg->v1; - - NewLoop(); - } - } - } - } - - if (!CheckSections()) return false; - SetReferences(); - - Sections.ShrinkToFit(); - SectionLoops.ShrinkToFit(); - SectionLines.ShrinkToFit(); - - tesselateSections(); - - return true; - } -}; - -FSectionCreator *FSectionCreator::creator; - - -//============================================================================= -// -// -// -//============================================================================= - -void DumpSection(int no, FGLSection *sect) -{ - Printf(PRINT_LOG, "Section %d, sector %d\n{\n", no, sect->sector->sectornum); - - for(int i = 0; i < sect->numloops; i++) - { - Printf(PRINT_LOG, "\tLoop %d\n\t{\n", i); - - FGLSectionLoop *loop = sect->GetLoop(i); - - for(int i = 0; i < loop->numlines; i++) - { - FGLSectionLine *ln = loop->GetLine(i); - if (ln->sidedef != NULL) - { - vertex_t *v1 = V1(ln->sidedef); - vertex_t *v2 = V2(ln->sidedef); - double dx = FIXED2DBL(v2->x-v1->x); - double dy = FIXED2DBL(v2->y-v1->y); - double dx1 = FIXED2DBL(ln->start->x-v1->x); - double dy1 = FIXED2DBL(ln->start->y-v1->y); - double dx2 = FIXED2DBL(ln->end->x-v1->x); - double dy2 = FIXED2DBL(ln->end->y-v1->y); - double d = sqrt(dx*dx+dy*dy); - double d1 = sqrt(dx1*dx1+dy1*dy1); - double d2 = sqrt(dx2*dx2+dy2*dy2); - - Printf(PRINT_LOG, "\t\tLinedef %d, %s: Start (%1.2f, %1.2f), End (%1.2f, %1.2f)", - ln->linedef - lines, ln->sidedef == ln->linedef->sidedef[0]? "front":"back", - ln->start->x/65536.f, ln->start->y/65536.f, - ln->end->x/65536.f, ln->end->y/65536.f); - - if (ln->otherside != -1) - { - Printf (PRINT_LOG, ", other side = %d", ln->otherside); - } - if (d1 > 0.005 || d2 < 0.995) - { - Printf(PRINT_LOG, ", Range = %1.3f, %1.3f", d1/d, d2/d); - } - } - else - { - Printf(PRINT_LOG, "\t\tMiniseg: Start (%1.3f, %1.3f), End (%1.3f, %1.3f)\n", - ln->start->x/65536.f, ln->start->y/65536.f, ln->end->x/65536.f, ln->end->y/65536.f); - - if (ln->otherside != -1) - { - Printf (PRINT_LOG, ", other side = %d", ln->otherside); - } - } - Printf(PRINT_LOG, "\n"); - } - Printf(PRINT_LOG, "\t}\n"); - } - int prim = 1; - for(unsigned i = 0; i < sect->vertices.Size(); i++) - { - int v = sect->vertices[i]; - if (v < 0) - { - if (i > 0) - { - Printf(PRINT_LOG, "\t}\n"); - } - switch (v) - { - case -GL_TRIANGLE_FAN: - Printf(PRINT_LOG, "\t%d: Triangle fan\n\t{\n", prim); - break; - - case -GL_TRIANGLE_STRIP: - Printf(PRINT_LOG, "\t%d: Triangle strip\n\t{\n", prim); - break; - - case -GL_TRIANGLES: - Printf(PRINT_LOG, "\t%d: Triangles\n\t{\n", prim); - break; - - default: - break; - } - prim++; - } - else - { - Printf(PRINT_LOG, "\t\tVertex %d: (%1.2f, %1.2f)\n", - v, vertexes[v].x/65536.f, vertexes[v].y/65536.f); - } - } - Printf(PRINT_LOG, "}\n\n"); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DumpSections() -{ - for(unsigned i = 0; i < Sections.Size(); i++) - { - DumpSection(i, &Sections[i]); - } -} - -//============================================================================= -// -// -// -//============================================================================= - -void gl_CreateSections() -{ - SectionLines.Clear(); - SectionLoops.Clear(); - Sections.Clear(); - SectionForSubsector.Resize(numsubsectors); - memset(&SectionForSubsector[0], -1, numsubsectors * sizeof(SectionForSubsector[0])); - FSectionCreator creat; - creat.CreateSections(); - if (dumpsections) DumpSections(); -} - diff --git a/unused/gl_sections.h b/unused/gl_sections.h deleted file mode 100644 index 1974fab467..0000000000 --- a/unused/gl_sections.h +++ /dev/null @@ -1,57 +0,0 @@ - -#ifndef __GL_SECTIONS_H -#define __GL_SECTIONS_H - -#include "tarray.h" -#include "r_defs.h" - -struct FGLSectionLine -{ - vertex_t *start; - vertex_t *end; - side_t *sidedef; - line_t *linedef; - seg_t *refseg; // we need to reference at least one seg for each line. - subsector_t *polysub; // If this is part of a polyobject we need a reference to the containing subsector - int otherside; -}; - -struct FGLSectionLoop -{ - int startline; - int numlines; - - FGLSectionLine *GetLine(int no); -}; - -struct FGLSection -{ - sector_t *sector; - TArray subsectors; - TArray primitives; // each primitive has 2 entries: Primitive type and vertex count - TArray vertices; - int startloop; - int numloops; - int validcount; - - FGLSectionLoop *GetLoop(int no); -}; - -extern TArray SectionLines; -extern TArray SectionLoops; -extern TArray Sections; -extern TArray SectionForSubsector; - -inline FGLSectionLine *FGLSectionLoop::GetLine(int no) -{ - return &SectionLines[startline + no]; -} - -inline FGLSectionLoop *FGLSection::GetLoop(int no) -{ - return &SectionLoops[startloop + no]; -} - -void gl_CreateSections(); - -#endif \ No newline at end of file From c847180bdc4eeca7a2ad06f01c2288d8b02048b6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Nov 2018 20:56:44 +0100 Subject: [PATCH 10/39] - reinstated the sector light clamping threshold from before version 3.3. It turned out that without the clamping the feature does not work well, thanks to a poorly chosen scale of the original arguments. --- src/g_shared/a_dynlight.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_shared/a_dynlight.cpp b/src/g_shared/a_dynlight.cpp index 98d57c4ab4..fcdde32761 100644 --- a/src/g_shared/a_dynlight.cpp +++ b/src/g_shared/a_dynlight.cpp @@ -324,7 +324,7 @@ void ADynamicLight::Tick() if (scale == 0.f) scale = 1.f; intensity = Sector->lightlevel * scale; - intensity = clamp(intensity, 0.f, 1024.f); + intensity = clamp(intensity, 0.f, 255.f); m_currentRadius = intensity; break; From 705c87c6ccceb1aed6b1fee9bb9aba233850534e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Nov 2018 21:33:35 +0100 Subject: [PATCH 11/39] - fixed bad case in #include. --- src/win32/win32basevideo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win32/win32basevideo.cpp b/src/win32/win32basevideo.cpp index 42863da86c..300905b09b 100644 --- a/src/win32/win32basevideo.cpp +++ b/src/win32/win32basevideo.cpp @@ -49,7 +49,7 @@ #include "v_text.h" #include "m_argv.h" #include "doomerrors.h" -#include "Win32BaseVideo.h" +#include "win32basevideo.h" #include "gl/system/gl_framebuffer.h" From 0deb388a757f6716d703fb400ce0b15f3cfcdabd Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Nov 2018 22:19:11 +0100 Subject: [PATCH 12/39] - automatically create sections and store them with the level data. - added subsector indexing to sections. This is needed for finding a section from a point. --- src/actor.h | 1 + src/g_levellocals.h | 3 +++ src/hwrenderer/data/hw_sections.cpp | 32 ++++++++++++++++++++--------- src/hwrenderer/data/hw_sections.h | 14 +++++++++++++ src/p_maputl.cpp | 1 + src/p_setup.cpp | 1 + src/r_data/renderinfo.cpp | 2 ++ 7 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/actor.h b/src/actor.h index 043da9b7ca..d87c51e98a 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1083,6 +1083,7 @@ public: FBlockNode *BlockNode; // links in blocks (if needed) struct sector_t *Sector; subsector_t * subsector; + FSection * section; double floorz, ceilingz; // closest together of contacted secs double dropoffz; // killough 11/98: the lowest floor over all contacted Sectors. diff --git a/src/g_levellocals.h b/src/g_levellocals.h index dfdb6dbef6..6a42c12c5d 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -42,6 +42,7 @@ #include "p_blockmap.h" #include "p_local.h" #include "p_destructible.h" +#include "hwrenderer/data/hw_sections.h" struct FLevelLocals { @@ -94,6 +95,8 @@ struct FLevelLocals TArray linkedPortals; // only the linked portals, this is used to speed up looking for them in P_CollectConnectedGroups. TArray portalGroups; TArray linePortalSpans; + FSectionContainer sections; + int NumMapSections; TArray Zones; diff --git a/src/hwrenderer/data/hw_sections.cpp b/src/hwrenderer/data/hw_sections.cpp index 9364b9712b..e56736191a 100644 --- a/src/hwrenderer/data/hw_sections.cpp +++ b/src/hwrenderer/data/hw_sections.cpp @@ -88,6 +88,7 @@ struct Group TArray groupedSections; TMap sideMap; TArray segments; + TArray subsectors; }; class FSectionCreator @@ -531,12 +532,14 @@ public: { const double MAX_GROUP_DIST = 256; TArray build; + TArray subsectorcopy; if (workingSet.Size() == 1) { groupForSection[workingSet[0].index] = groups.Size(); Group g; g.groupedSections = std::move(workingSet); + g.subsectors = std::move(rawsections[workingSet[0].index]); groups.Push(std::move(g)); return; } @@ -546,8 +549,10 @@ public: build.Clear(); build.Push(workingSet[0]); groupForSection[workingSet[0].index] = groups.Size(); + subsectorcopy = std::move(rawsections[workingSet[0].index]); workingSet.Delete(0); + // Don't use iterators here. These arrays are modified inside. for (unsigned j = 0; j < build.Size(); j++) { @@ -560,6 +565,7 @@ public: { build.Push(workingSet[i]); groupForSection[workingSet[i].index] = groups.Size(); + subsectorcopy.Append(rawsections[workingSet[i].index]); workingSet.Delete(i); i--; continue; @@ -570,6 +576,7 @@ public: { build.Push(workingSet[i]); groupForSection[workingSet[i].index] = groups.Size(); + subsectorcopy.Append(rawsections[workingSet[i].index]); workingSet.Delete(i); i--; continue; @@ -578,6 +585,7 @@ public: } Group g; g.groupedSections = std::move(build); + g.subsectors = std::move(subsectorcopy); groups.Push(std::move(g)); } } @@ -615,6 +623,7 @@ public: output.allIndices.Resize(level.subsectors.Size() + level.sides.Size()); output.sectionForSubsectorPtr = &output.allIndices[0]; output.sectionForSidedefPtr = &output.allIndices[level.subsectors.Size()]; + memset(output.sectionForSubsectorPtr, -1, sizeof(int) * level.subsectors.Size()); unsigned numsegments = 0; unsigned numsides = 0; @@ -642,9 +651,11 @@ public: } output.allLines.Resize(numsegments); output.allSides.Resize(numsides); + output.allSubsectors.Resize(level.subsectors.Size()); numsegments = 0; numsides = 0; + unsigned numsubsectors = 0; // Now piece it all together unsigned curgroup = 0; @@ -658,6 +669,7 @@ public: dest.validcount = 0; dest.segments.Set(&output.allLines[numsegments], group.segments.Size()); dest.sides.Set(&output.allSides[numsides], group.sideMap.CountUsed()); + dest.subsectors.Set(&output.allSubsectors[numsubsectors]); dest.bounds = {1e32, 1e32, -1e32, -1e32}; numsegments += group.segments.Size(); @@ -677,7 +689,14 @@ public: while (it.NextPair(pair)) { output.allSides[numsides++] = &level.sides[pair->Key]; + output.sectionForSidedefPtr[pair->Key] = curgroup; } + memcpy(&output.allSubsectors[numsubsectors], &group.subsectors[0], group.subsectors.Size() * sizeof(subsector_t*)); + for (auto ssi : group.subsectors) + { + output.sectionForSubsectorPtr[ssi] = curgroup; + } + numsubsectors += group.subsectors.Size(); curgroup++; } } @@ -739,12 +758,8 @@ void PrintSections(FSectionContainer &container) // //============================================================================= -void MakeSections() +void CreateSections(FSectionContainer &container) { - FSectionContainer container; - cycle_t timer; - timer.Reset(); - timer.Clock(); FSectionCreator creat; creat.GroupSubsectors(); creat.CompileSections(); @@ -753,12 +768,9 @@ void MakeSections() creat.FindOuterLoops(); creat.GroupSections(); creat.ConstructOutput(container); - timer.Unclock(); - PrintSections(container); - Printf("Time = %2.3f ms\n", timer.TimeMS()); } -CCMD(makesections) +CCMD(printsections) { - MakeSections(); + PrintSections(level.sections); } \ No newline at end of file diff --git a/src/hwrenderer/data/hw_sections.h b/src/hwrenderer/data/hw_sections.h index 1cc6f2efa2..6edda1143d 100644 --- a/src/hwrenderer/data/hw_sections.h +++ b/src/hwrenderer/data/hw_sections.h @@ -2,9 +2,19 @@ #ifndef __GL_SECTIONS_H #define __GL_SECTIONS_H +#include #include "tarray.h" #include "r_defs.h" +// Undefine Windows garbage. +#ifdef min +#undef min +#endif + +#ifdef max +#undef max +#endif + //========================================================================== // // @@ -71,6 +81,7 @@ struct FSection // tbd: Do we need a list of subsectors here? Ideally the subsectors should not be used anywhere anymore except for finding out where a location is. TArrayView segments; TArrayView sides; // contains all sidedefs, including the internal ones that do not make up the outer shape. (this list is not exclusive. A sidedef can be in multiple sections!) + TArrayView subsectors; // contains all sidedefs, including the internal ones that do not make up the outer shape. (this list is not exclusive. A sidedef can be in multiple sections!) sector_t *sector; FLightNode *lighthead; // Light nodes (blended and additive) BoundingRect bounds; @@ -86,6 +97,7 @@ public: TArray allSections; //TArray allVertices; TArray allSides; + TArray allSubsectors; TArray allIndices; int *sectionForSubsectorPtr; // stored inside allIndices @@ -123,4 +135,6 @@ public: }; +void CreateSections(FSectionContainer &container); + #endif \ No newline at end of file diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index c94d80073f..19c9496d33 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -470,6 +470,7 @@ void AActor::LinkToWorld(FLinkContext *ctx, bool spawningmapthing, sector_t *sec Sector = sector; subsector = R_PointInSubsector(Pos()); // this is from the rendering nodes, not the gameplay nodes! + section = level.sections.SectionForSubsector(subsector->Index()); if (!(flags & MF_NOSECTOR)) { diff --git a/src/p_setup.cpp b/src/p_setup.cpp index d3ff4c4999..a204144efb 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -3559,6 +3559,7 @@ void P_FreeLevelData () FBehavior::StaticUnloadModules (); + level.sections.Clear(); level.segs.Clear(); level.sectors.Clear(); level.lines.Clear(); diff --git a/src/r_data/renderinfo.cpp b/src/r_data/renderinfo.cpp index bf59ec0c0a..f50c32d518 100644 --- a/src/r_data/renderinfo.cpp +++ b/src/r_data/renderinfo.cpp @@ -532,6 +532,8 @@ static void PrepareSegs() void InitRenderInfo() { + CreateSections(level.sections); + PrepareSegs(); PrepareSectorData(); InitVertexData(); From d7db00d92e0bc400eb6e60e244b9d60a118fadf7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 5 Nov 2018 01:01:48 +0100 Subject: [PATCH 13/39] - sector rendering refactoring for sections - work in progress. --- src/actor.h | 1 + src/hwrenderer/data/hw_sections.cpp | 123 +++++++++++++++++++++++- src/hwrenderer/data/hw_sections.h | 51 +++++++++- src/hwrenderer/scene/hw_bsp.cpp | 5 +- src/hwrenderer/scene/hw_drawinfo.cpp | 4 +- src/hwrenderer/scene/hw_drawinfo.h | 2 +- src/hwrenderer/scene/hw_drawstructs.h | 2 + src/hwrenderer/scene/hw_flats.cpp | 6 +- src/hwrenderer/scene/hw_renderhacks.cpp | 3 +- 9 files changed, 185 insertions(+), 12 deletions(-) diff --git a/src/actor.h b/src/actor.h index d87c51e98a..6974107113 100644 --- a/src/actor.h +++ b/src/actor.h @@ -53,6 +53,7 @@ struct FBlockNode; struct FPortalGroupArray; struct visstyle_t; class FLightDefaults; +struct FSection; // // NOTES: AActor // diff --git a/src/hwrenderer/data/hw_sections.cpp b/src/hwrenderer/data/hw_sections.cpp index e56736191a..2c698386c2 100644 --- a/src/hwrenderer/data/hw_sections.cpp +++ b/src/hwrenderer/data/hw_sections.cpp @@ -34,6 +34,7 @@ #include "p_setup.h" #include "c_dispatch.h" #include "memarena.h" +#include "flatvertices.h" using DoublePoint = std::pair; @@ -611,6 +612,114 @@ public: return false; } + + //============================================================================= + // + // + // + //============================================================================= + + // Temporary data for creating an indexed buffer + struct VertexIndexGenerationInfo + { + TArray vertices; + TMap vertexmap; + + TArray indices; + + uint32_t AddVertex(vertex_t *vert) + { + auto check = vertexmap.CheckKey(vert); + if (check != nullptr) return *check; + auto index = vertices.Push(vert); + vertexmap[vert] = index; + return index; + } + + uint32_t GetIndex(vertex_t *vert) + { + auto check = vertexmap.CheckKey(vert); + if (check != nullptr) return *check; + return ~0u; + } + + uint32_t AddIndexForVertex(vertex_t *vert) + { + return indices.Push(GetIndex(vert)); + } + + uint32_t AddIndex(uint32_t indx) + { + return indices.Push(indx); + } + }; + + //============================================================================= + // + // + // + //============================================================================= + + void CreateIndexedSubsectorVertices(subsector_t *sub, VertexIndexGenerationInfo &gen) + { + if (sub->numlines < 3) return; + + uint32_t startindex = gen.indices.Size(); + + if ((sub->flags & SSECF_HOLE) && sub->numlines > 3) + { + // Hole filling "subsectors" are not necessarily convex so they require real triangulation. + // These things are extremely rare so performance is secondary here. + + using Point = std::pair; + std::vector> polygon; + std::vector *curPoly; + + for (unsigned i = 0; i < sub->numlines; i++) + { + polygon.resize(1); + curPoly = &polygon.back(); + curPoly->push_back({ sub->firstline[i].v1->fX(), sub->firstline[i].v1->fY() }); + } + auto indices = mapbox::earcut(polygon); + for (auto vti : indices) + { + gen.AddIndexForVertex(sub->firstline[vti].v1); + } + } + else + { + int firstndx = gen.GetIndex(sub->firstline[0].v1); + int secondndx = gen.GetIndex(sub->firstline[1].v1); + for (unsigned int k = 2; k < sub->numlines; k++) + { + gen.AddIndex(firstndx); + gen.AddIndex(secondndx); + auto ndx = gen.GetIndex(sub->firstline[k].v1); + gen.AddIndex(ndx); + secondndx = ndx; + } + } + } + + + void CreateVerticesForSection(FSectionContainer &output, FSection §ion) + { + VertexIndexGenerationInfo gen; + + for (auto sub : section.subsectors) + { + CreateIndexedSubsectorVertices(sub, gen); + } + section.vertexindex = output.allVertices.Size(); + section.vertexcount = gen.vertices.Size(); + section.indexindex = output.allVertexIndices.Size(); + section.indexcount = gen.indices.Size(); + output.allVertices.Append(gen.vertices); + output.allVertexIndices.Append(gen.indices); + } + + //============================================================================= // // @@ -620,10 +729,14 @@ public: void ConstructOutput(FSectionContainer &output) { output.allSections.Resize(groups.Size()); - output.allIndices.Resize(level.subsectors.Size() + level.sides.Size()); + output.allIndices.Resize(level.subsectors.Size() + level.sides.Size() + 2*level.sectors.Size()); output.sectionForSubsectorPtr = &output.allIndices[0]; output.sectionForSidedefPtr = &output.allIndices[level.subsectors.Size()]; + output.firstSectionForSectorPtr = &output.allIndices[level.subsectors.Size() + level.sides.Size()]; + output.numberOfSectionForSectorPtr = &output.allIndices[level.subsectors.Size() + level.sides.Size() + level.sectors.Size()]; memset(output.sectionForSubsectorPtr, -1, sizeof(int) * level.subsectors.Size()); + memset(output.firstSectionForSectorPtr, -1, sizeof(int) * level.sectors.Size()); + memset(output.numberOfSectionForSectorPtr, 0, sizeof(int) * level.sectors.Size()); unsigned numsegments = 0; unsigned numsides = 0; @@ -669,10 +782,15 @@ public: dest.validcount = 0; dest.segments.Set(&output.allLines[numsegments], group.segments.Size()); dest.sides.Set(&output.allSides[numsides], group.sideMap.CountUsed()); - dest.subsectors.Set(&output.allSubsectors[numsubsectors]); + dest.subsectors.Set(&output.allSubsectors[numsubsectors], group.subsectors.Size()); + dest.vertexindex = -1; + dest.vertexcount = 0; dest.bounds = {1e32, 1e32, -1e32, -1e32}; numsegments += group.segments.Size(); + if (output.firstSectionForSectorPtr[dest.sector->Index()] == -1) + output.firstSectionForSectorPtr[dest.sector->Index()] = curgroup; + for (auto &segment : group.segments) { // Use the indices calculated above to store these elements. @@ -697,6 +815,7 @@ public: output.sectionForSubsectorPtr[ssi] = curgroup; } numsubsectors += group.subsectors.Size(); + CreateVerticesForSection(output, dest); curgroup++; } } diff --git a/src/hwrenderer/data/hw_sections.h b/src/hwrenderer/data/hw_sections.h index 6edda1143d..7e1e94b6ce 100644 --- a/src/hwrenderer/data/hw_sections.h +++ b/src/hwrenderer/data/hw_sections.h @@ -80,11 +80,15 @@ struct FSection { // tbd: Do we need a list of subsectors here? Ideally the subsectors should not be used anywhere anymore except for finding out where a location is. TArrayView segments; - TArrayView sides; // contains all sidedefs, including the internal ones that do not make up the outer shape. (this list is not exclusive. A sidedef can be in multiple sections!) - TArrayView subsectors; // contains all sidedefs, including the internal ones that do not make up the outer shape. (this list is not exclusive. A sidedef can be in multiple sections!) + TArrayView sides; // contains all sidedefs, including the internal ones that do not make up the outer shape. + TArrayView subsectors; // contains all subsectors making up this section sector_t *sector; - FLightNode *lighthead; // Light nodes (blended and additive) + FLightNode *lighthead; // Light nodes (blended and additive) BoundingRect bounds; + int vertexindex; + int vertexcount; // index and length of this section's entry in the allVertices array + int indexindex; + int indexcount; // index and length of this section's entry in the allVertices array int validcount; short mapsection; char hacked; // 1: is part of a render hack @@ -95,13 +99,16 @@ class FSectionContainer public: TArray allLines; TArray allSections; - //TArray allVertices; + TArray allVertices; + TArray allVertexIndices; TArray allSides; TArray allSubsectors; TArray allIndices; int *sectionForSubsectorPtr; // stored inside allIndices int *sectionForSidedefPtr; // also stored inside allIndices; + int *firstSectionForSectorPtr; // ditto. + int *numberOfSectionForSectorPtr; // ditto. FSection *SectionForSubsector(subsector_t *sub) { @@ -111,6 +118,14 @@ public: { return ssindex < 0 ? nullptr : &allSections[sectionForSubsectorPtr[ssindex]]; } + int SectionNumForSubsector(subsector_t *sub) + { + return SectionNumForSubsector(sub->Index()); + } + int SectionNumForSubsector(int ssindex) + { + return ssindex < 0 ? -1 : sectionForSubsectorPtr[ssindex]; + } FSection *SectionForSidedef(side_t *side) { return SectionForSidedef(side->Index()); @@ -119,11 +134,35 @@ public: { return sindex < 0 ? nullptr : &allSections[sectionForSidedefPtr[sindex]]; } + int SectionNumForSidedef(side_t *side) + { + return SectionNumForSidedef(side->Index()); + } + int SectionNumForSidedef(int sindex) + { + return sindex < 0 ? -1 : sectionForSidedefPtr[sindex]; + } + TArrayView SectionsForSector(sector_t *sec) + { + return SectionsForSector(sec->Index()); + } + TArrayView SectionsForSector(int sindex) + { + return sindex < 0 ? TArrayView(0) : TArrayView(&allSections[firstSectionForSectorPtr[sindex]], numberOfSectionForSectorPtr[sindex]); + } + int SectionIndex(const FSection *sect) + { + return int(sect - allSections.Data()); + } void Clear() { allLines.Clear(); allSections.Clear(); allIndices.Clear(); + allVertexIndices.Clear(); + allVertices.Clear(); + allSides.Clear(); + allSubsectors.Clear(); } void Reset() { @@ -131,6 +170,10 @@ public: allLines.ShrinkToFit(); allSections.ShrinkToFit(); allIndices.ShrinkToFit(); + allVertexIndices.ShrinkToFit(); + allVertices.ShrinkToFit(); + allSides.ShrinkToFit(); + allSubsectors.ShrinkToFit(); } }; diff --git a/src/hwrenderer/scene/hw_bsp.cpp b/src/hwrenderer/scene/hw_bsp.cpp index 75be620f41..92b9117814 100644 --- a/src/hwrenderer/scene/hw_bsp.cpp +++ b/src/hwrenderer/scene/hw_bsp.cpp @@ -155,6 +155,7 @@ void HWDrawInfo::WorkerThread() { GLFlat flat; SetupFlat.Clock(); + flat.section = level.sections.SectionForSubsector(job->sub); front = hw_FakeFlat(job->sub->render_sector, &fakefront, in_area, false); flat.ProcessSector(this, front); SetupFlat.Unclock(); @@ -679,7 +680,8 @@ void HWDrawInfo::DoSubsector(subsector_t * sub) fakesector = hw_FakeFlat(sector, &fake, in_area, false); } - uint8_t &srf = sectorrenderflags[sub->render_sector->sectornum]; + auto secnum = level.sections.SectionNumForSubsector(sub); + uint8_t &srf = section_renderflags[secnum]; if (!(srf & SSRF_PROCESSED)) { srf |= SSRF_PROCESSED; @@ -691,6 +693,7 @@ void HWDrawInfo::DoSubsector(subsector_t * sub) else { GLFlat flat; + flat.section = level.sections.SectionForSubsector(sub); SetupFlat.Clock(); flat.ProcessSector(this, fakesector); SetupFlat.Unclock(); diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index d781fc64d9..128b1d8fa5 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -214,11 +214,11 @@ void HWDrawInfo::ClearBuffers() CurrentMapSections.Resize(level.NumMapSections); CurrentMapSections.Zero(); - sectorrenderflags.Resize(level.sectors.Size()); + section_renderflags.Resize(level.sections.allSections.Size()); ss_renderflags.Resize(level.subsectors.Size()); no_renderflags.Resize(level.subsectors.Size()); - memset(§orrenderflags[0], 0, level.sectors.Size() * sizeof(sectorrenderflags[0])); + memset(§ion_renderflags[0], 0, level.sections.allSections.Size() * sizeof(section_renderflags[0])); memset(&ss_renderflags[0], 0, level.subsectors.Size() * sizeof(ss_renderflags[0])); memset(&no_renderflags[0], 0, level.nodes.Size() * sizeof(no_renderflags[0])); diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 1eb4f62821..c3a9ddfb39 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -170,7 +170,7 @@ struct HWDrawInfo TArray HandledSubsectors; - TArray sectorrenderflags; + TArray section_renderflags; TArray ss_renderflags; TArray no_renderflags; diff --git a/src/hwrenderer/scene/hw_drawstructs.h b/src/hwrenderer/scene/hw_drawstructs.h index 38514a6bf7..194de2c50b 100644 --- a/src/hwrenderer/scene/hw_drawstructs.h +++ b/src/hwrenderer/scene/hw_drawstructs.h @@ -27,6 +27,7 @@ struct FSpriteModelFrame; struct particle_t; class FRenderState; struct GLDecal; +struct FSection; enum area_t : int; enum HWRenderStyle @@ -293,6 +294,7 @@ class GLFlat { public: sector_t * sector; + FSection *section; float z; // the z position of the flat (only valid for non-sloped planes) FMaterial *gltexture; diff --git a/src/hwrenderer/scene/hw_flats.cpp b/src/hwrenderer/scene/hw_flats.cpp index 7763461140..db9edfccbb 100644 --- a/src/hwrenderer/scene/hw_flats.cpp +++ b/src/hwrenderer/scene/hw_flats.cpp @@ -189,6 +189,8 @@ void GLFlat::DrawSubsectors(HWDrawInfo *di, FRenderState &state) SetupLights(di, sector->lighthead, lightdata, sector->PortalGroup); } state.SetLightIndex(dynlightindex); + + if (vcount > 0 && !di->ClipLineShouldBeActive()) { state.DrawIndexed(DT_Triangles, iboindex, vcount); @@ -197,6 +199,7 @@ void GLFlat::DrawSubsectors(HWDrawInfo *di, FRenderState &state) } else { + /* int index = iboindex; bool applied = false; for (int i = 0; i < sector->subsectorcount; i++) @@ -213,6 +216,7 @@ void GLFlat::DrawSubsectors(HWDrawInfo *di, FRenderState &state) } index += (sub->numlines - 2) * 3; } + */ } if (!(renderflags&SSRF_RENDER3DPLANES)) @@ -473,7 +477,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) extsector_t::xfloor &x = sector->e->XFloor; dynlightindex = -1; - uint8_t &srf = di->sectorrenderflags[sector->sectornum]; + uint8_t &srf = di->section_renderflags[level.sections.SectionIndex(section)]; const auto &vp = di->Viewpoint; // diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index f6899b4341..afc8aaab89 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -850,7 +850,8 @@ void HWDrawInfo::PrepareUnhandledMissingTextures() if (seg->linedef->validcount == validcount) continue; // already done seg->linedef->validcount = validcount; - if (!(sectorrenderflags[seg->backsector->sectornum] & SSRF_RENDERFLOOR)) continue; + int section = level.sections.SectionNumForSidedef(seg->sidedef); + if (!(section_renderflags[section] & SSRF_RENDERFLOOR)) continue; if (seg->frontsector->GetPlaneTexZ(sector_t::floor) > Viewpoint.Pos.Z) continue; // out of sight if (seg->backsector->transdoor) continue; if (seg->backsector->GetTexture(sector_t::floor) == skyflatnum) continue; From 950ed07ae6627040359264e96d714a5822275f6a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 5 Nov 2018 15:30:50 +0100 Subject: [PATCH 14/39] WIP --- src/hwrenderer/data/flatvertices.cpp | 81 ++----- src/hwrenderer/data/flatvertices.h | 33 +-- src/hwrenderer/data/hw_sections.cpp | 302 +++++++++++++++++---------- src/hwrenderer/data/hw_sections.h | 20 +- 4 files changed, 228 insertions(+), 208 deletions(-) diff --git a/src/hwrenderer/data/flatvertices.cpp b/src/hwrenderer/data/flatvertices.cpp index 20e139c586..c19169db36 100644 --- a/src/hwrenderer/data/flatvertices.cpp +++ b/src/hwrenderer/data/flatvertices.cpp @@ -35,6 +35,13 @@ #include "hwrenderer/data/buffers.h" #include "hwrenderer/scene/hw_renderstate.h" +namespace VertexBuilder +{ + TArray BuildVertices(); +} + +using VertexContainers = TArray; + //========================================================================== // @@ -158,54 +165,22 @@ static F3DFloor *Find3DFloor(sector_t *target, sector_t *model) // //========================================================================== -int FFlatVertexBuffer::CreateIndexedSubsectorVertices(subsector_t *sub, const secplane_t &plane, int floor, int vi, FFlatVertexBuffer::FIndexGenerationInfo &gen) +int FFlatVertexBuffer::CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, VertexContainer &verts) { - if (sub->numlines < 3) return -1; - - int idx = ibo_data.Reserve((sub->numlines - 2) * 3); - int idxc = idx; - int firstndx = gen.GetIndex(sub->firstline[0].v1); - int secondndx = gen.GetIndex(sub->firstline[1].v1); - for (unsigned int k = 2; knumlines; k++) - { - auto ndx = gen.GetIndex(sub->firstline[k].v1); - - ibo_data[idx++] = vi + firstndx; - ibo_data[idx++] = vi + secondndx; - ibo_data[idx++] = vi + ndx; - secondndx = ndx; - } - return idx; -} - -//========================================================================== -// -// Creates the vertices for one plane in one subsector -// -//========================================================================== - -int FFlatVertexBuffer::CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, FFlatVertexBuffer::FIndexGenerationInfo &gen) -{ - int rt = ibo_data.Size(); - int vi = vbo_shadowdata.Reserve(gen.vertices.Size()); + int vi = vbo_shadowdata.Reserve(verts.vertices.Size()); float diff; // Create the actual vertices. if (sec->transdoor && floor) diff = -1.f; else diff = 0.f; - for (unsigned i = 0; i < gen.vertices.Size(); i++) + for (unsigned i = 0; i < verts.vertices.Size(); i++) { - vbo_shadowdata[vi + i].SetFlatVertex(gen.vertices[i], plane); + vbo_shadowdata[vi + i].SetFlatVertex(verts.vertices[i].vertex, plane); vbo_shadowdata[vi + i].z += diff; } - - // Create the indices for the subsectors - for (int j = 0; jsubsectorcount; j++) - { - subsector_t *sub = sec->subsectors[j]; - CreateIndexedSubsectorVertices(sub, plane, floor, vi, gen); - } - sec->ibocount = ibo_data.Size() - rt; + + int rt = ibo_data.size(); + ibo_data.Append(verts.indices); return rt; } @@ -215,19 +190,20 @@ int FFlatVertexBuffer::CreateIndexedSectorVertices(sector_t *sec, const secplane // //========================================================================== -int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, TArray &gen) +int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainers &verts) { sec->vboindex[h] = vbo_shadowdata.Size(); // First calculate the vertices for the sector itself sec->vboheight[h] = sec->GetPlaneTexZ(h); - sec->iboindex[h] = CreateIndexedSectorVertices(sec, plane, floor, gen[sec->Index()]); + sec->ibocount = verts.indices.Size(); + sec->iboindex[h] = CreateIndexedSectorVertices(sec, plane, floor, verts[sec->Index()]); // Next are all sectors using this one as heightsec TArray &fakes = sec->e->FakeFloor.Sectors; for (unsigned g = 0; g < fakes.Size(); g++) { sector_t *fsec = fakes[g]; - fsec->iboindex[2 + h] = CreateIndexedSectorVertices(fsec, plane, false, gen[fsec->Index()]); + fsec->iboindex[2 + h] = CreateIndexedSectorVertices(fsec, plane, false, verts[fsec->Index()]); } // and finally all attached 3D floors @@ -244,7 +220,7 @@ int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplan if (dotop || dobottom) { - auto ndx = CreateIndexedSectorVertices(fsec, plane, false, gen[fsec->Index()]); + auto ndx = CreateIndexedSectorVertices(fsec, plane, false, verts[fsec->Index()]); if (dotop) ffloor->top.vindex = ndx; if (dobottom) ffloor->bottom.vindex = ndx; } @@ -263,26 +239,13 @@ int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplan void FFlatVertexBuffer::CreateIndexedFlatVertices() { - TArray gen; - gen.Resize(level.sectors.Size()); - // This must be generated up front so that the following code knows how many vertices a sector contains. - for (unsigned i = 0; i < level.sectors.Size(); i++) - { - for (int j = 0; j < level.sectors[i].subsectorcount; j++) - { - auto sub = level.sectors[i].subsectors[j]; - for (unsigned k = 0; k < sub->numlines; k++) - { - auto vert = sub->firstline[k].v1; - gen[i].AddVertex(vert); - } - } - } + auto verts = VertexBuilder::BuildVertices(); + for (int h = sector_t::floor; h <= sector_t::ceiling; h++) { for (auto &sec : level.sectors) { - CreateIndexedVertices(h, &sec, sec.GetSecPlane(h), h == sector_t::floor, gen); + CreateIndexedVertices(h, &sec, sec.GetSecPlane(h), h == sector_t::floor, verts); } } diff --git a/src/hwrenderer/data/flatvertices.h b/src/hwrenderer/data/flatvertices.h index c554483792..13a8d31307 100644 --- a/src/hwrenderer/data/flatvertices.h +++ b/src/hwrenderer/data/flatvertices.h @@ -62,31 +62,6 @@ class FFlatVertexBuffer static const unsigned int BUFFER_SIZE = 2000000; static const unsigned int BUFFER_SIZE_TO_USE = 1999500; - - // Temporary data for creating an indexed buffer - struct FIndexGenerationInfo - { - TArray vertices; - TMap vertexmap; - - uint32_t AddVertex(vertex_t *vert) - { - auto check = vertexmap.CheckKey(vert); - if (check != nullptr) return *check; - auto index = vertices.Push(vert); - vertexmap[vert] = index; - return index; - } - - uint32_t GetIndex(vertex_t *vert) - { - auto check = vertexmap.CheckKey(vert); - if (check != nullptr) return *check; - return ~0; - } - }; - - public: enum { @@ -140,9 +115,9 @@ public: } private: - int CreateIndexedSubsectorVertices(subsector_t *sub, const secplane_t &plane, int floor, int vi, FIndexGenerationInfo &gen); - int CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, FIndexGenerationInfo &gen); - int CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, TArray &gen); + int CreateIndexedSectionVertices(subsector_t *sub, const secplane_t &plane, int floor, VertexContainer &cont); + int CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, VertexContainer &cont); + int CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainers &cont); void CreateIndexedFlatVertices(); void UpdatePlaneVertices(sector_t *sec, int plane); @@ -154,4 +129,4 @@ public: }; -#endif \ No newline at end of file +#endif diff --git a/src/hwrenderer/data/hw_sections.cpp b/src/hwrenderer/data/hw_sections.cpp index 2c698386c2..31c1986e6a 100644 --- a/src/hwrenderer/data/hw_sections.cpp +++ b/src/hwrenderer/data/hw_sections.cpp @@ -40,7 +40,6 @@ using DoublePoint = std::pair; template<> struct THashTraits { - // Use all bits when hashing doubles instead of converting them to ints. hash_t Hash(const DoublePoint &key) { return (hash_t)SuperFastHash((const char*)(const void*)&key, sizeof(key)); @@ -48,6 +47,15 @@ template<> struct THashTraits int Compare(const DoublePoint &left, const DoublePoint &right) { return left != right; } }; +template<> struct THashTraits +{ + hash_t Hash(const FSectionVertex &key) + { + return (int)(((intptr_t)key.vertex) >> 4) ^ (key.qualifier << 16); + } + int Compare(const FSectionVertex &left, const FSectionVertex &right) { return left.vertex != right.vertex && left.qualifier != right.qualifier; } +}; + struct WorkSectionLine { @@ -613,112 +621,6 @@ public: } - //============================================================================= - // - // - // - //============================================================================= - - // Temporary data for creating an indexed buffer - struct VertexIndexGenerationInfo - { - TArray vertices; - TMap vertexmap; - - TArray indices; - - uint32_t AddVertex(vertex_t *vert) - { - auto check = vertexmap.CheckKey(vert); - if (check != nullptr) return *check; - auto index = vertices.Push(vert); - vertexmap[vert] = index; - return index; - } - - uint32_t GetIndex(vertex_t *vert) - { - auto check = vertexmap.CheckKey(vert); - if (check != nullptr) return *check; - return ~0u; - } - - uint32_t AddIndexForVertex(vertex_t *vert) - { - return indices.Push(GetIndex(vert)); - } - - uint32_t AddIndex(uint32_t indx) - { - return indices.Push(indx); - } - }; - - //============================================================================= - // - // - // - //============================================================================= - - void CreateIndexedSubsectorVertices(subsector_t *sub, VertexIndexGenerationInfo &gen) - { - if (sub->numlines < 3) return; - - uint32_t startindex = gen.indices.Size(); - - if ((sub->flags & SSECF_HOLE) && sub->numlines > 3) - { - // Hole filling "subsectors" are not necessarily convex so they require real triangulation. - // These things are extremely rare so performance is secondary here. - - using Point = std::pair; - std::vector> polygon; - std::vector *curPoly; - - for (unsigned i = 0; i < sub->numlines; i++) - { - polygon.resize(1); - curPoly = &polygon.back(); - curPoly->push_back({ sub->firstline[i].v1->fX(), sub->firstline[i].v1->fY() }); - } - auto indices = mapbox::earcut(polygon); - for (auto vti : indices) - { - gen.AddIndexForVertex(sub->firstline[vti].v1); - } - } - else - { - int firstndx = gen.GetIndex(sub->firstline[0].v1); - int secondndx = gen.GetIndex(sub->firstline[1].v1); - for (unsigned int k = 2; k < sub->numlines; k++) - { - gen.AddIndex(firstndx); - gen.AddIndex(secondndx); - auto ndx = gen.GetIndex(sub->firstline[k].v1); - gen.AddIndex(ndx); - secondndx = ndx; - } - } - } - - - void CreateVerticesForSection(FSectionContainer &output, FSection §ion) - { - VertexIndexGenerationInfo gen; - - for (auto sub : section.subsectors) - { - CreateIndexedSubsectorVertices(sub, gen); - } - section.vertexindex = output.allVertices.Size(); - section.vertexcount = gen.vertices.Size(); - section.indexindex = output.allVertexIndices.Size(); - section.indexcount = gen.indices.Size(); - output.allVertices.Append(gen.vertices); - output.allVertexIndices.Append(gen.indices); - } - //============================================================================= // @@ -815,7 +717,7 @@ public: output.sectionForSubsectorPtr[ssi] = curgroup; } numsubsectors += group.subsectors.Size(); - CreateVerticesForSection(output, dest); + CreateVerticesForSection(output, dest, true); curgroup++; } } @@ -892,4 +794,186 @@ void CreateSections(FSectionContainer &container) CCMD(printsections) { PrintSections(level.sections); -} \ No newline at end of file +} + + + +//============================================================================= +// +// One sector's vertex data. +// +//============================================================================= + +struct VertexContainer +{ + TArray vertices; + TMap vertexmap; + bool perSubsector = false; + + TArray indices; + + uint32_t AddVertex(FSectionVertex *vert) + { + auto check = vertexmap.CheckKey(vert); + if (check != nullptr) return *check; + auto index = vertices.Push(*vert); + vertexmap[vert] = index; + return index; + } + + uint32_t AddVertex(vertex_t *vert, int qualifier) + { + FSectionVertex vertx = { vert, qualifier}; + return AddVertex(&vertx); + } + + uint32_t GetIndex(FSectionVertex *vert) + { + auto check = vertexmap.CheckKey(vert); + if (check != nullptr) return *check; + return ~0u; + } + + uint32_t GetIndex(vertex_t *vert, int qualifier) + { + FSectionVertex vertx = { vert, qualifier}; + return GetIndex(&vertx); + } + + uint32_t AddIndexForVertex(FSectionVertex *vert) + { + return indices.Push(GetIndex(vert)); + } + + uint32_t AddIndexForVertex(vertex_t *vert, int qualifier) + { + return indices.Push(GetIndex(vert, qualifier)); + } + + uint32_t AddIndex(uint32_t indx) + { + return indices.Push(indx); + } +}; + + +//============================================================================= +// +// Creates vertex meshes for sector planes +// +//============================================================================= + +namespace VertexBuilder +{ + + //============================================================================= + // + // + // + //============================================================================= + + static void CreateVerticesForSubsector(subsector_t *sub, VertexContainer &gen, int qualifier) + { + if (sub->numlines < 3) return; + + uint32_t startindex = gen.indices.Size(); + + if ((sub->flags & SSECF_HOLE) && sub->numlines > 3) + { + // Hole filling "subsectors" are not necessarily convex so they require real triangulation. + // These things are extremely rare so performance is secondary here. + + using Point = std::pair; + std::vector> polygon; + std::vector *curPoly; + + for (unsigned i = 0; i < sub->numlines; i++) + { + polygon.resize(1); + curPoly = &polygon.back(); + curPoly->push_back({ sub->firstline[i].v1->fX(), sub->firstline[i].v1->fY() }); + } + auto indices = mapbox::earcut(polygon); + for (auto vti : indices) + { + gen.AddIndexForVertex(sub->firstline[vti].v1, qualifier); + } + } + else + { + int firstndx = gen.GetIndex(sub->firstline[0].v1, qualifier); + int secondndx = gen.GetIndex(sub->firstline[1].v1, qualifier); + for (unsigned int k = 2; k < sub->numlines; k++) + { + gen.AddIndex(firstndx); + gen.AddIndex(secondndx); + auto ndx = gen.GetIndex(sub->firstline[k].v1, qualifier); + gen.AddIndex(ndx); + secondndx = ndx; + } + } + } + + //============================================================================= + // + // + // + //============================================================================= + + static void TriangulateSection(FSection §, VertexContainer &gen, int qualifier) + { + if (sect.segments.Size() < 3) return; + + // todo + } + + //============================================================================= + // + // + // + //============================================================================= + + + static void CreateVerticesForSection(FSection §ion, VertexContainer &gen, bool useSubsectors) + { + section.vertexindex = gen.indices.Size(); + + if (useSubsectors) + { + for (auto sub : section.subsectors) + { + CreateVerticesForSubsector(sub, gen, -1); + } + } + else + { + TriangulateSection(section, gen, -1); + } + section.vertexcount = gen.indices.Size() - section.vertexindex; + } + + //========================================================================== + // + // Creates the vertices for one plane in one subsector + // + //========================================================================== + + static void CreateVerticesForSector(sector_t *sec, VertexContainer gen) + { + auto sections = level.sections.SectionsForSector(sec); + for (auto §ion :sections) + { + CreateVerticesForSection( section, gen, true); + } + } + + TArray BuildVertices() + { + TArray verticesPerSector(level.sectors.Size(), true); + for (unsigned i=0; i allLines; TArray allSections; - TArray allVertices; - TArray allVertexIndices; TArray allSides; TArray allSubsectors; TArray allIndices; @@ -159,8 +161,6 @@ public: allLines.Clear(); allSections.Clear(); allIndices.Clear(); - allVertexIndices.Clear(); - allVertices.Clear(); allSides.Clear(); allSubsectors.Clear(); } @@ -170,8 +170,6 @@ public: allLines.ShrinkToFit(); allSections.ShrinkToFit(); allIndices.ShrinkToFit(); - allVertexIndices.ShrinkToFit(); - allVertices.ShrinkToFit(); allSides.ShrinkToFit(); allSubsectors.ShrinkToFit(); } @@ -180,4 +178,4 @@ public: void CreateSections(FSectionContainer &container); -#endif \ No newline at end of file +#endif From 625eb1e76a85afb1871c5485b82cb9e6e5fcba81 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 5 Nov 2018 21:11:54 +0100 Subject: [PATCH 15/39] - FVertexBuilder's output looks correct now. --- src/CMakeLists.txt | 1 + src/earcut.hpp | 790 +++++++++++++++++++++++ src/hwrenderer/data/flatvertices.cpp | 41 +- src/hwrenderer/data/flatvertices.h | 3 +- src/hwrenderer/data/hw_sections.cpp | 228 +------ src/hwrenderer/data/hw_sections.h | 6 - src/hwrenderer/data/hw_vertexbuilder.cpp | 146 +++++ src/hwrenderer/data/hw_vertexbuilder.h | 69 ++ src/p_setup.cpp | 2 + src/r_data/renderinfo.cpp | 2 - src/tarray.h | 4 + 11 files changed, 1064 insertions(+), 228 deletions(-) create mode 100644 src/earcut.hpp create mode 100644 src/hwrenderer/data/hw_vertexbuilder.cpp create mode 100644 src/hwrenderer/data/hw_vertexbuilder.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 559379a6d9..da9b92dda1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1049,6 +1049,7 @@ set (PCH_SOURCES gl/system/gl_buffers.cpp gl/textures/gl_hwtexture.cpp gl/textures/gl_samplers.cpp + hwrenderer/data/hw_vertexbuilder.cpp hwrenderer/data/hw_sections.cpp hwrenderer/data/flatvertices.cpp hwrenderer/data/hw_viewpointbuffer.cpp diff --git a/src/earcut.hpp b/src/earcut.hpp new file mode 100644 index 0000000000..d6a2c97988 --- /dev/null +++ b/src/earcut.hpp @@ -0,0 +1,790 @@ +/* +ISC License + +Copyright (c) 2015, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +*/ +#pragma once + +#include +#include +#include +#include +#include + +namespace mapbox { + +namespace util { + +template struct nth { + inline static typename std::tuple_element::type + get(const T& t) { return std::get(t); }; +}; + +} + +namespace detail { + +template +class Earcut { +public: + std::vector indices; + std::size_t vertices = 0; + + template + void operator()(const Polygon& points); + +private: + struct Node { + Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {} + Node(const Node&) = delete; + Node& operator=(const Node&) = delete; + Node(Node&&) = delete; + Node& operator=(Node&&) = delete; + + const N i; + const double x; + const double y; + + // previous and next vertice nodes in a polygon ring + Node* prev = nullptr; + Node* next = nullptr; + + // z-order curve value + int32_t z = 0; + + // previous and next nodes in z-order + Node* prevZ = nullptr; + Node* nextZ = nullptr; + + // indicates whether this is a steiner point + bool steiner = false; + }; + + template Node* linkedList(const Ring& points, const bool clockwise); + Node* filterPoints(Node* start, Node* end = nullptr); + void earcutLinked(Node* ear, int pass = 0); + bool isEar(Node* ear); + bool isEarHashed(Node* ear); + Node* cureLocalIntersections(Node* start); + void splitEarcut(Node* start); + template Node* eliminateHoles(const Polygon& points, Node* outerNode); + void eliminateHole(Node* hole, Node* outerNode); + Node* findHoleBridge(Node* hole, Node* outerNode); + void indexCurve(Node* start); + Node* sortLinked(Node* list); + int32_t zOrder(const double x_, const double y_); + Node* getLeftmost(Node* start); + bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const; + bool isValidDiagonal(Node* a, Node* b); + double area(const Node* p, const Node* q, const Node* r) const; + bool equals(const Node* p1, const Node* p2); + bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2); + bool intersectsPolygon(const Node* a, const Node* b); + bool locallyInside(const Node* a, const Node* b); + bool middleInside(const Node* a, const Node* b); + Node* splitPolygon(Node* a, Node* b); + template Node* insertNode(std::size_t i, const Point& p, Node* last); + void removeNode(Node* p); + + bool hashing; + double minX, maxX; + double minY, maxY; + double inv_size = 0; + + template > + class ObjectPool { + public: + ObjectPool() { } + ObjectPool(std::size_t blockSize_) { + reset(blockSize_); + } + ~ObjectPool() { + clear(); + } + template + T* construct(Args&&... args) { + if (currentIndex >= blockSize) { + currentBlock = alloc.allocate(blockSize); + allocations.emplace_back(currentBlock); + currentIndex = 0; + } + T* object = ¤tBlock[currentIndex++]; + alloc.construct(object, std::forward(args)...); + return object; + } + void reset(std::size_t newBlockSize) { + for (auto allocation : allocations) alloc.deallocate(allocation, blockSize); + allocations.clear(); + blockSize = std::max(1, newBlockSize); + currentBlock = nullptr; + currentIndex = blockSize; + } + void clear() { reset(blockSize); } + private: + T* currentBlock = nullptr; + std::size_t currentIndex = 1; + std::size_t blockSize = 1; + std::vector allocations; + Alloc alloc; + }; + ObjectPool nodes; +}; + +template template +void Earcut::operator()(const Polygon& points) { + // reset + indices.clear(); + vertices = 0; + + if (points.empty()) return; + + double x; + double y; + int threshold = 80; + std::size_t len = 0; + + for (size_t i = 0; threshold >= 0 && i < points.size(); i++) { + threshold -= static_cast(points[i].size()); + len += points[i].size(); + } + + //estimate size of nodes and indices + nodes.reset(len * 3 / 2); + indices.reserve(len + points[0].size()); + + Node* outerNode = linkedList(points[0], true); + if (!outerNode) return; + + if (points.size() > 1) outerNode = eliminateHoles(points, outerNode); + + // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + hashing = threshold < 0; + if (hashing) { + Node* p = outerNode->next; + minX = maxX = outerNode->x; + minY = maxY = outerNode->y; + do { + x = p->x; + y = p->y; + minX = std::min(minX, x); + minY = std::min(minY, y); + maxX = std::max(maxX, x); + maxY = std::max(maxY, y); + p = p->next; + } while (p != outerNode); + + // minX, minY and size are later used to transform coords into integers for z-order calculation + inv_size = std::max(maxX - minX, maxY - minY); + inv_size = inv_size != .0 ? (1. / inv_size) : .0; + } + + earcutLinked(outerNode); + + nodes.clear(); +} + +// create a circular doubly linked list from polygon points in the specified winding order +template template +typename Earcut::Node* +Earcut::linkedList(const Ring& points, const bool clockwise) { + using Point = typename Ring::value_type; + double sum = 0; + const std::size_t len = points.size(); + std::size_t i, j; + Node* last = nullptr; + + // calculate original winding order of a polygon ring + for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) { + const auto& p1 = points[i]; + const auto& p2 = points[j]; + const double p20 = util::nth<0, Point>::get(p2); + const double p10 = util::nth<0, Point>::get(p1); + const double p11 = util::nth<1, Point>::get(p1); + const double p21 = util::nth<1, Point>::get(p2); + sum += (p20 - p10) * (p11 + p21); + } + + // link points into circular doubly-linked list in the specified winding order + if (clockwise == (sum > 0)) { + for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last); + } else { + for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last); + } + + if (last && equals(last, last->next)) { + removeNode(last); + last = last->next; + } + + vertices += len; + + return last; +} + +// eliminate colinear or duplicate points +template +typename Earcut::Node* +Earcut::filterPoints(Node* start, Node* end) { + if (!end) end = start; + + Node* p = start; + bool again; + do { + again = false; + + if (!p->steiner && (equals(p, p->next) /*|| area(p->prev, p, p->next) == 0*/)) + { + removeNode(p); + p = end = p->prev; + + if (p == p->next) break; + again = true; + + } else { + p = p->next; + } + } while (again || p != end); + + return end; +} + +// main ear slicing loop which triangulates a polygon (given as a linked list) +template +void Earcut::earcutLinked(Node* ear, int pass) { + if (!ear) return; + + // interlink polygon nodes in z-order + if (!pass && hashing) indexCurve(ear); + + Node* stop = ear; + Node* prev; + Node* next; + + int iterations = 0; + + // iterate through ears, slicing them one by one + while (ear->prev != ear->next) { + iterations++; + prev = ear->prev; + next = ear->next; + + if (hashing ? isEarHashed(ear) : isEar(ear)) { + // cut off the triangle + indices.emplace_back(prev->i); + indices.emplace_back(ear->i); + indices.emplace_back(next->i); + + removeNode(ear); + + // skipping the next vertice leads to less sliver triangles + ear = next->next; + stop = next->next; + + continue; + } + + ear = next; + + // if we looped through the whole remaining polygon and can't find any more ears + if (ear == stop) { + // try filtering points and slicing again + if (!pass) earcutLinked(filterPoints(ear), 1); + + // if this didn't work, try curing all small self-intersections locally + else if (pass == 1) { + ear = cureLocalIntersections(ear); + earcutLinked(ear, 2); + + // as a last resort, try splitting the remaining polygon into two + } else if (pass == 2) splitEarcut(ear); + + break; + } + } +} + +// check whether a polygon node forms a valid ear with adjacent nodes +template +bool Earcut::isEar(Node* ear) { + const Node* a = ear->prev; + const Node* b = ear; + const Node* c = ear->next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // now make sure we don't have other points inside the potential ear + Node* p = ear->next->next; + + while (p != ear->prev) { + if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->next; + } + + return true; +} + +template +bool Earcut::isEarHashed(Node* ear) { + const Node* a = ear->prev; + const Node* b = ear; + const Node* c = ear->next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // triangle bbox; min & max are calculated like this for speed + const double minTX = std::min(a->x, std::min(b->x, c->x)); + const double minTY = std::min(a->y, std::min(b->y, c->y)); + const double maxTX = std::max(a->x, std::max(b->x, c->x)); + const double maxTY = std::max(a->y, std::max(b->y, c->y)); + + // z-order range for the current triangle bbox; + const int32_t minZ = zOrder(minTX, minTY); + const int32_t maxZ = zOrder(maxTX, maxTY); + + // first look for points inside the triangle in increasing z-order + Node* p = ear->nextZ; + + while (p && p->z <= maxZ) { + if (p != ear->prev && p != ear->next && + pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->nextZ; + } + + // then look for points in decreasing z-order + p = ear->prevZ; + + while (p && p->z >= minZ) { + if (p != ear->prev && p != ear->next && + pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->prevZ; + } + + return true; +} + +// go through all polygon nodes and cure small local self-intersections +template +typename Earcut::Node* +Earcut::cureLocalIntersections(Node* start) { + Node* p = start; + do { + Node* a = p->prev; + Node* b = p->next->next; + + // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) + if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) { + indices.emplace_back(a->i); + indices.emplace_back(p->i); + indices.emplace_back(b->i); + + // remove two nodes involved + removeNode(p); + removeNode(p->next); + + p = start = b; + } + p = p->next; + } while (p != start); + + return p; +} + +// try splitting polygon into two and triangulate them independently +template +void Earcut::splitEarcut(Node* start) { + // look for a valid diagonal that divides the polygon into two + Node* a = start; + do { + Node* b = a->next->next; + while (b != a->prev) { + if (a->i != b->i && isValidDiagonal(a, b)) { + // split the polygon in two by the diagonal + Node* c = splitPolygon(a, b); + + // filter colinear points around the cuts + a = filterPoints(a, a->next); + c = filterPoints(c, c->next); + + // run earcut on each half + earcutLinked(a); + earcutLinked(c); + return; + } + b = b->next; + } + a = a->next; + } while (a != start); +} + +// link every hole into the outer loop, producing a single-ring polygon without holes +template template +typename Earcut::Node* +Earcut::eliminateHoles(const Polygon& points, Node* outerNode) { + const size_t len = points.size(); + + std::vector queue; + for (size_t i = 1; i < len; i++) { + Node* list = linkedList(points[i], false); + if (list) { + if (list == list->next) list->steiner = true; + queue.push_back(getLeftmost(list)); + } + } + std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) { + return a->x < b->x; + }); + + // process holes from left to right + for (size_t i = 0; i < queue.size(); i++) { + eliminateHole(queue[i], outerNode); + outerNode = filterPoints(outerNode, outerNode->next); + } + + return outerNode; +} + +// find a bridge between vertices that connects hole with an outer ring and and link it +template +void Earcut::eliminateHole(Node* hole, Node* outerNode) { + outerNode = findHoleBridge(hole, outerNode); + if (outerNode) { + Node* b = splitPolygon(outerNode, hole); + filterPoints(b, b->next); + } +} + +// David Eberly's algorithm for finding a bridge between hole and outer polygon +template +typename Earcut::Node* +Earcut::findHoleBridge(Node* hole, Node* outerNode) { + Node* p = outerNode; + double hx = hole->x; + double hy = hole->y; + double qx = -std::numeric_limits::infinity(); + Node* m = nullptr; + + // find a segment intersected by a ray from the hole's leftmost Vertex to the left; + // segment's endpoint with lesser x will be potential connection Vertex + do { + if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) { + double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y); + if (x <= hx && x > qx) { + qx = x; + if (x == hx) { + if (hy == p->y) return p; + if (hy == p->next->y) return p->next; + } + m = p->x < p->next->x ? p : p->next; + } + } + p = p->next; + } while (p != outerNode); + + if (!m) return 0; + + if (hx == qx) return m->prev; + + // look for points inside the triangle of hole Vertex, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex + + const Node* stop = m; + double tanMin = std::numeric_limits::infinity(); + double tanCur = 0; + + p = m->next; + double mx = m->x; + double my = m->y; + + while (p != stop) { + if (hx >= p->x && p->x >= mx && hx != p->x && + pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) { + + tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential + + if ((tanCur < tanMin || (tanCur == tanMin && p->x > m->x)) && locallyInside(p, hole)) { + m = p; + tanMin = tanCur; + } + } + + p = p->next; + } + + return m; +} + +// interlink polygon nodes in z-order +template +void Earcut::indexCurve(Node* start) { + assert(start); + Node* p = start; + + do { + p->z = p->z ? p->z : zOrder(p->x, p->y); + p->prevZ = p->prev; + p->nextZ = p->next; + p = p->next; + } while (p != start); + + p->prevZ->nextZ = nullptr; + p->prevZ = nullptr; + + sortLinked(p); +} + +// Simon Tatham's linked list merge sort algorithm +// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html +template +typename Earcut::Node* +Earcut::sortLinked(Node* list) { + assert(list); + Node* p; + Node* q; + Node* e; + Node* tail; + int i, numMerges, pSize, qSize; + int inSize = 1; + + for (;;) { + p = list; + list = nullptr; + tail = nullptr; + numMerges = 0; + + while (p) { + numMerges++; + q = p; + pSize = 0; + for (i = 0; i < inSize; i++) { + pSize++; + q = q->nextZ; + if (!q) break; + } + + qSize = inSize; + + while (pSize > 0 || (qSize > 0 && q)) { + + if (pSize == 0) { + e = q; + q = q->nextZ; + qSize--; + } else if (qSize == 0 || !q) { + e = p; + p = p->nextZ; + pSize--; + } else if (p->z <= q->z) { + e = p; + p = p->nextZ; + pSize--; + } else { + e = q; + q = q->nextZ; + qSize--; + } + + if (tail) tail->nextZ = e; + else list = e; + + e->prevZ = tail; + tail = e; + } + + p = q; + } + + tail->nextZ = nullptr; + + if (numMerges <= 1) return list; + + inSize *= 2; + } +} + +// z-order of a Vertex given coords and size of the data bounding box +template +int32_t Earcut::zOrder(const double x_, const double y_) { + // coords are transformed into non-negative 15-bit integer range + int32_t x = static_cast(32767.0 * (x_ - minX) * inv_size); + int32_t y = static_cast(32767.0 * (y_ - minY) * inv_size); + + x = (x | (x << 8)) & 0x00FF00FF; + x = (x | (x << 4)) & 0x0F0F0F0F; + x = (x | (x << 2)) & 0x33333333; + x = (x | (x << 1)) & 0x55555555; + + y = (y | (y << 8)) & 0x00FF00FF; + y = (y | (y << 4)) & 0x0F0F0F0F; + y = (y | (y << 2)) & 0x33333333; + y = (y | (y << 1)) & 0x55555555; + + return x | (y << 1); +} + +// find the leftmost node of a polygon ring +template +typename Earcut::Node* +Earcut::getLeftmost(Node* start) { + Node* p = start; + Node* leftmost = start; + do { + if (p->x < leftmost->x) leftmost = p; + p = p->next; + } while (p != start); + + return leftmost; +} + +// check if a point lies within a convex triangle +template +bool Earcut::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const { + return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && + (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && + (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; +} + +// check if a diagonal between two polygon nodes is valid (lies in polygon interior) +template +bool Earcut::isValidDiagonal(Node* a, Node* b) { + return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && + locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b); +} + +// signed area of a triangle +template +double Earcut::area(const Node* p, const Node* q, const Node* r) const { + return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y); +} + +// check if two points are equal +template +bool Earcut::equals(const Node* p1, const Node* p2) { + return p1->x == p2->x && p1->y == p2->y; +} + +// check if two segments intersect +template +bool Earcut::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) { + if ((equals(p1, q1) && equals(p2, q2)) || + (equals(p1, q2) && equals(p2, q1))) return true; + return (area(p1, q1, p2) > 0) != (area(p1, q1, q2) > 0) && + (area(p2, q2, p1) > 0) != (area(p2, q2, q1) > 0); +} + +// check if a polygon diagonal intersects any polygon segments +template +bool Earcut::intersectsPolygon(const Node* a, const Node* b) { + const Node* p = a; + do { + if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i && + intersects(p, p->next, a, b)) return true; + p = p->next; + } while (p != a); + + return false; +} + +// check if a polygon diagonal is locally inside the polygon +template +bool Earcut::locallyInside(const Node* a, const Node* b) { + return area(a->prev, a, a->next) < 0 ? + area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 : + area(a, b, a->prev) < 0 || area(a, a->next, b) < 0; +} + +// check if the middle Vertex of a polygon diagonal is inside the polygon +template +bool Earcut::middleInside(const Node* a, const Node* b) { + const Node* p = a; + bool inside = false; + double px = (a->x + b->x) / 2; + double py = (a->y + b->y) / 2; + do { + if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y && + (px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x)) + inside = !inside; + p = p->next; + } while (p != a); + + return inside; +} + +// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits +// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a +// single ring +template +typename Earcut::Node* +Earcut::splitPolygon(Node* a, Node* b) { + Node* a2 = nodes.construct(a->i, a->x, a->y); + Node* b2 = nodes.construct(b->i, b->x, b->y); + Node* an = a->next; + Node* bp = b->prev; + + a->next = b; + b->prev = a; + + a2->next = an; + an->prev = a2; + + b2->next = a2; + a2->prev = b2; + + bp->next = b2; + b2->prev = bp; + + return b2; +} + +// create a node and util::optionally link it with previous one (in a circular doubly linked list) +template template +typename Earcut::Node* +Earcut::insertNode(std::size_t i, const Point& pt, Node* last) { + Node* p = nodes.construct(static_cast(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt)); + + if (!last) { + p->prev = p; + p->next = p; + + } else { + assert(last); + p->next = last->next; + p->prev = last; + last->next->prev = p; + last->next = p; + } + return p; +} + +template +void Earcut::removeNode(Node* p) { + p->next->prev = p->prev; + p->prev->next = p->next; + + if (p->prevZ) p->prevZ->nextZ = p->nextZ; + if (p->nextZ) p->nextZ->prevZ = p->prevZ; +} +} + +template +std::vector earcut(const Polygon& poly) { + mapbox::detail::Earcut earcut; + earcut(poly); + return std::move(earcut.indices); +} +} diff --git a/src/hwrenderer/data/flatvertices.cpp b/src/hwrenderer/data/flatvertices.cpp index c19169db36..3083cbc098 100644 --- a/src/hwrenderer/data/flatvertices.cpp +++ b/src/hwrenderer/data/flatvertices.cpp @@ -35,14 +35,6 @@ #include "hwrenderer/data/buffers.h" #include "hwrenderer/scene/hw_renderstate.h" -namespace VertexBuilder -{ - TArray BuildVertices(); -} - -using VertexContainers = TArray; - - //========================================================================== // // @@ -179,7 +171,7 @@ int FFlatVertexBuffer::CreateIndexedSectorVertices(sector_t *sec, const secplane vbo_shadowdata[vi + i].z += diff; } - int rt = ibo_data.size(); + int rt = ibo_data.Size(); ibo_data.Append(verts.indices); return rt; } @@ -190,20 +182,20 @@ int FFlatVertexBuffer::CreateIndexedSectorVertices(sector_t *sec, const secplane // //========================================================================== -int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainers &verts) +int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainer &verts) { sec->vboindex[h] = vbo_shadowdata.Size(); // First calculate the vertices for the sector itself sec->vboheight[h] = sec->GetPlaneTexZ(h); sec->ibocount = verts.indices.Size(); - sec->iboindex[h] = CreateIndexedSectorVertices(sec, plane, floor, verts[sec->Index()]); + sec->iboindex[h] = CreateIndexedSectorVertices(sec, plane, floor, verts); // Next are all sectors using this one as heightsec TArray &fakes = sec->e->FakeFloor.Sectors; for (unsigned g = 0; g < fakes.Size(); g++) { sector_t *fsec = fakes[g]; - fsec->iboindex[2 + h] = CreateIndexedSectorVertices(fsec, plane, false, verts[fsec->Index()]); + fsec->iboindex[2 + h] = CreateIndexedSectorVertices(fsec, plane, false, verts); } // and finally all attached 3D floors @@ -220,7 +212,7 @@ int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplan if (dotop || dobottom) { - auto ndx = CreateIndexedSectorVertices(fsec, plane, false, verts[fsec->Index()]); + auto ndx = CreateIndexedSectorVertices(fsec, plane, false, verts); if (dotop) ffloor->top.vindex = ndx; if (dobottom) ffloor->bottom.vindex = ndx; } @@ -239,13 +231,32 @@ int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplan void FFlatVertexBuffer::CreateIndexedFlatVertices() { - auto verts = VertexBuilder::BuildVertices(); + auto verts = BuildVertices(); + + int i = 0; + for (auto &vert : verts) + { + Printf(PRINT_LOG, "Sector %d\n", i); + Printf(PRINT_LOG, "%d vertices, %d indices\n", vert.vertices.Size(), vert.indices.Size()); + int j = 0; + for (auto &v : vert.vertices) + { + Printf(PRINT_LOG, " %d: (%2.3f, %2.3f)\n", j++, v.vertex->fX(), v.vertex->fY()); + } + for (unsigned i=0;i #include @@ -117,7 +118,7 @@ public: private: int CreateIndexedSectionVertices(subsector_t *sub, const secplane_t &plane, int floor, VertexContainer &cont); int CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, VertexContainer &cont); - int CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainers &cont); + int CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainer &cont); void CreateIndexedFlatVertices(); void UpdatePlaneVertices(sector_t *sec, int plane); diff --git a/src/hwrenderer/data/hw_sections.cpp b/src/hwrenderer/data/hw_sections.cpp index 31c1986e6a..4ad20d47a3 100644 --- a/src/hwrenderer/data/hw_sections.cpp +++ b/src/hwrenderer/data/hw_sections.cpp @@ -47,16 +47,6 @@ template<> struct THashTraits int Compare(const DoublePoint &left, const DoublePoint &right) { return left != right; } }; -template<> struct THashTraits -{ - hash_t Hash(const FSectionVertex &key) - { - return (int)(((intptr_t)key.vertex) >> 4) ^ (key.qualifier << 16); - } - int Compare(const FSectionVertex &left, const FSectionVertex &right) { return left.vertex != right.vertex && left.qualifier != right.qualifier; } -}; - - struct WorkSectionLine { vertex_t *start; @@ -77,6 +67,7 @@ struct WorkSection bool hasminisegs; TArraysegments; TArray originalSides; // The segs will lose some of these while working on them. + TArray subsectors; }; struct TriangleWorkData @@ -107,7 +98,6 @@ class FSectionCreator bool verbose = false; TMap> subsectormap; - TArray> rawsections; // list of unprocessed subsectors. Sector and mapsection can be retrieved from the elements so aren't stored. TArray sections; TArray triangles; @@ -185,16 +175,18 @@ public: // //========================================================================== - void CompileSections() + TArray < TArray> CompileSections() { TMap>::Pair *pair; TMap>::Iterator it(subsectormap); + TArray> rawsections; // list of unprocessed subsectors. Sector and mapsection can be retrieved from the elements so aren't stored. while (it.NextPair(pair)) { - CompileSections(pair->Value); + CompileSections(pair->Value, rawsections); } subsectormap.Clear(); + return rawsections; } //========================================================================== @@ -203,7 +195,7 @@ public: // //========================================================================== - void CompileSections(TArray &list) + void CompileSections(TArray &list, TArray>&rawsections) { TArray sublist; TArray seglist; @@ -255,12 +247,15 @@ public: void MakeOutlines() { + auto rawsections = CompileSections(); TArray lineForSeg(level.segs.Size(), true); memset(lineForSeg.Data(), 0, sizeof(WorkSectionLine*) * level.segs.Size()); for (auto &list : rawsections) { MakeOutline(list, lineForSeg); } + rawsections.Clear(); + rawsections.ShrinkToFit(); // Assign partners after everything has been collected for (auto §ion : sections) @@ -403,8 +398,14 @@ public: *sectionlines[i] = { nullptr, nullptr, nullptr, nullptr, -1, (int)sections.Size(), nullptr }; } } - - sections.Push({ sector, mapsec, hasminisegs, std::move(sectionlines), std::move(foundsides) }); + sections.Reserve(1); + auto §ion = sections.Last(); + section.sectorindex = sector; + section.mapsection = mapsec; + section.hasminisegs = hasminisegs; + section.originalSides = std::move(foundsides); + section.segments = std::move(sectionlines); + section.subsectors = std::move(rawsection); } } @@ -547,8 +548,8 @@ public: { groupForSection[workingSet[0].index] = groups.Size(); Group g; + g.subsectors = std::move(workingSet[0].section->subsectors); g.groupedSections = std::move(workingSet); - g.subsectors = std::move(rawsections[workingSet[0].index]); groups.Push(std::move(g)); return; } @@ -558,7 +559,7 @@ public: build.Clear(); build.Push(workingSet[0]); groupForSection[workingSet[0].index] = groups.Size(); - subsectorcopy = std::move(rawsections[workingSet[0].index]); + subsectorcopy = std::move(workingSet[0].section->subsectors); workingSet.Delete(0); @@ -574,7 +575,7 @@ public: { build.Push(workingSet[i]); groupForSection[workingSet[i].index] = groups.Size(); - subsectorcopy.Append(rawsections[workingSet[i].index]); + subsectorcopy.Append(workingSet[i].section->subsectors); workingSet.Delete(i); i--; continue; @@ -585,7 +586,7 @@ public: { build.Push(workingSet[i]); groupForSection[workingSet[i].index] = groups.Size(); - subsectorcopy.Append(rawsections[workingSet[i].index]); + subsectorcopy.Append(workingSet[i].section->subsectors); workingSet.Delete(i); i--; continue; @@ -693,6 +694,8 @@ public: if (output.firstSectionForSectorPtr[dest.sector->Index()] == -1) output.firstSectionForSectorPtr[dest.sector->Index()] = curgroup; + output.numberOfSectionForSectorPtr[dest.sector->Index()]++; + for (auto &segment : group.segments) { // Use the indices calculated above to store these elements. @@ -711,13 +714,11 @@ public: output.allSides[numsides++] = &level.sides[pair->Key]; output.sectionForSidedefPtr[pair->Key] = curgroup; } - memcpy(&output.allSubsectors[numsubsectors], &group.subsectors[0], group.subsectors.Size() * sizeof(subsector_t*)); for (auto ssi : group.subsectors) { + output.allSubsectors[numsubsectors++] = &level.subsectors[ssi]; output.sectionForSubsectorPtr[ssi] = curgroup; } - numsubsectors += group.subsectors.Size(); - CreateVerticesForSection(output, dest, true); curgroup++; } } @@ -783,7 +784,6 @@ void CreateSections(FSectionContainer &container) { FSectionCreator creat; creat.GroupSubsectors(); - creat.CompileSections(); creat.MakeOutlines(); creat.MergeLines(); creat.FindOuterLoops(); @@ -797,183 +797,3 @@ CCMD(printsections) } - -//============================================================================= -// -// One sector's vertex data. -// -//============================================================================= - -struct VertexContainer -{ - TArray vertices; - TMap vertexmap; - bool perSubsector = false; - - TArray indices; - - uint32_t AddVertex(FSectionVertex *vert) - { - auto check = vertexmap.CheckKey(vert); - if (check != nullptr) return *check; - auto index = vertices.Push(*vert); - vertexmap[vert] = index; - return index; - } - - uint32_t AddVertex(vertex_t *vert, int qualifier) - { - FSectionVertex vertx = { vert, qualifier}; - return AddVertex(&vertx); - } - - uint32_t GetIndex(FSectionVertex *vert) - { - auto check = vertexmap.CheckKey(vert); - if (check != nullptr) return *check; - return ~0u; - } - - uint32_t GetIndex(vertex_t *vert, int qualifier) - { - FSectionVertex vertx = { vert, qualifier}; - return GetIndex(&vertx); - } - - uint32_t AddIndexForVertex(FSectionVertex *vert) - { - return indices.Push(GetIndex(vert)); - } - - uint32_t AddIndexForVertex(vertex_t *vert, int qualifier) - { - return indices.Push(GetIndex(vert, qualifier)); - } - - uint32_t AddIndex(uint32_t indx) - { - return indices.Push(indx); - } -}; - - -//============================================================================= -// -// Creates vertex meshes for sector planes -// -//============================================================================= - -namespace VertexBuilder -{ - - //============================================================================= - // - // - // - //============================================================================= - - static void CreateVerticesForSubsector(subsector_t *sub, VertexContainer &gen, int qualifier) - { - if (sub->numlines < 3) return; - - uint32_t startindex = gen.indices.Size(); - - if ((sub->flags & SSECF_HOLE) && sub->numlines > 3) - { - // Hole filling "subsectors" are not necessarily convex so they require real triangulation. - // These things are extremely rare so performance is secondary here. - - using Point = std::pair; - std::vector> polygon; - std::vector *curPoly; - - for (unsigned i = 0; i < sub->numlines; i++) - { - polygon.resize(1); - curPoly = &polygon.back(); - curPoly->push_back({ sub->firstline[i].v1->fX(), sub->firstline[i].v1->fY() }); - } - auto indices = mapbox::earcut(polygon); - for (auto vti : indices) - { - gen.AddIndexForVertex(sub->firstline[vti].v1, qualifier); - } - } - else - { - int firstndx = gen.GetIndex(sub->firstline[0].v1, qualifier); - int secondndx = gen.GetIndex(sub->firstline[1].v1, qualifier); - for (unsigned int k = 2; k < sub->numlines; k++) - { - gen.AddIndex(firstndx); - gen.AddIndex(secondndx); - auto ndx = gen.GetIndex(sub->firstline[k].v1, qualifier); - gen.AddIndex(ndx); - secondndx = ndx; - } - } - } - - //============================================================================= - // - // - // - //============================================================================= - - static void TriangulateSection(FSection §, VertexContainer &gen, int qualifier) - { - if (sect.segments.Size() < 3) return; - - // todo - } - - //============================================================================= - // - // - // - //============================================================================= - - - static void CreateVerticesForSection(FSection §ion, VertexContainer &gen, bool useSubsectors) - { - section.vertexindex = gen.indices.Size(); - - if (useSubsectors) - { - for (auto sub : section.subsectors) - { - CreateVerticesForSubsector(sub, gen, -1); - } - } - else - { - TriangulateSection(section, gen, -1); - } - section.vertexcount = gen.indices.Size() - section.vertexindex; - } - - //========================================================================== - // - // Creates the vertices for one plane in one subsector - // - //========================================================================== - - static void CreateVerticesForSector(sector_t *sec, VertexContainer gen) - { - auto sections = level.sections.SectionsForSector(sec); - for (auto §ion :sections) - { - CreateVerticesForSection( section, gen, true); - } - } - - TArray BuildVertices() - { - TArray verticesPerSector(level.sectors.Size(), true); - for (unsigned i=0; inumlines < 3) return; + + uint32_t startindex = gen.indices.Size(); + + if ((sub->flags & SSECF_HOLE) && sub->numlines > 3) + { + // Hole filling "subsectors" are not necessarily convex so they require real triangulation. + // These things are extremely rare so performance is secondary here. + + using Point = std::pair; + std::vector> polygon; + std::vector *curPoly; + + for (unsigned i = 0; i < sub->numlines; i++) + { + polygon.resize(1); + curPoly = &polygon.back(); + curPoly->push_back({ sub->firstline[i].v1->fX(), sub->firstline[i].v1->fY() }); + } + auto indices = mapbox::earcut(polygon); + for (auto vti : indices) + { + gen.AddIndexForVertex(sub->firstline[vti].v1, qualifier); + } + } + else + { + int firstndx = gen.AddVertex(sub->firstline[0].v1, qualifier); + int secondndx = gen.AddVertex(sub->firstline[1].v1, qualifier); + for (unsigned int k = 2; k < sub->numlines; k++) + { + gen.AddIndex(firstndx); + gen.AddIndex(secondndx); + auto ndx = gen.AddVertex(sub->firstline[k].v1, qualifier); + gen.AddIndex(ndx); + secondndx = ndx; + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +static void TriangulateSection(FSection §, VertexContainer &gen, int qualifier) +{ + if (sect.segments.Size() < 3) return; + + // todo +} + +//============================================================================= +// +// +// +//============================================================================= + + +static void CreateVerticesForSection(FSection §ion, VertexContainer &gen, bool useSubsectors) +{ + section.vertexindex = gen.indices.Size(); + + if (useSubsectors) + { + for (auto sub : section.subsectors) + { + CreateVerticesForSubsector(sub, gen, -1); + } + } + else + { + TriangulateSection(section, gen, -1); + } + section.vertexcount = gen.indices.Size() - section.vertexindex; +} + +//========================================================================== +// +// Creates the vertices for one plane in one subsector +// +//========================================================================== + +static void CreateVerticesForSector(sector_t *sec, VertexContainer &gen) +{ + auto sections = level.sections.SectionsForSector(sec); + for (auto §ion :sections) + { + CreateVerticesForSection( section, gen, true); + } +} + + +TArray BuildVertices() +{ + TArray verticesPerSector(level.sectors.Size(), true); + for (unsigned i=0; i struct THashTraits +{ + hash_t Hash(const FQualifiedVertex &key) + { + return (int)(((intptr_t)key.vertex) >> 4) ^ (key.qualifier << 16); + } + int Compare(const FQualifiedVertex &left, const FQualifiedVertex &right) { return left.vertex != right.vertex || left.qualifier != right.qualifier; } +}; + +//============================================================================= +// +// One sector's vertex data. +// +//============================================================================= + +struct VertexContainer +{ + TArray vertices; + TMap vertexmap; + bool perSubsector = false; + + TArray indices; + + uint32_t AddVertex(FQualifiedVertex *vert) + { + auto check = vertexmap.CheckKey(*vert); + if (check != nullptr) return *check; + auto index = vertices.Push(*vert); + vertexmap[*vert] = index; + return index; + } + + uint32_t AddVertex(vertex_t *vert, int qualifier) + { + FQualifiedVertex vertx = { vert, qualifier}; + return AddVertex(&vertx); + } + + uint32_t AddIndexForVertex(FQualifiedVertex *vert) + { + return indices.Push(AddVertex(vert)); + } + + uint32_t AddIndexForVertex(vertex_t *vert, int qualifier) + { + return indices.Push(AddVertex(vert, qualifier)); + } + + uint32_t AddIndex(uint32_t indx) + { + return indices.Push(indx); + } +}; + +using VertexContainers = TArray; + +VertexContainers BuildVertices(); + + diff --git a/src/p_setup.cpp b/src/p_setup.cpp index a204144efb..15cbb0b543 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -4067,6 +4067,8 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame) for (i = 0; i < BODYQUESIZE; i++) bodyque[i] = NULL; + CreateSections(level.sections); + if (!buildmap) { // [RH] Spawn slope creating things first. diff --git a/src/r_data/renderinfo.cpp b/src/r_data/renderinfo.cpp index f50c32d518..bf59ec0c0a 100644 --- a/src/r_data/renderinfo.cpp +++ b/src/r_data/renderinfo.cpp @@ -532,8 +532,6 @@ static void PrepareSegs() void InitRenderInfo() { - CreateSections(level.sections); - PrepareSegs(); PrepareSectorData(); InitVertexData(); diff --git a/src/tarray.h b/src/tarray.h index 79b8853bd0..4a0d1570fc 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -162,6 +162,10 @@ public: Most = max; Count = reserve? max : 0; Array = (T *)M_Malloc (sizeof(T)*max); + if (reserve) + { + for (unsigned i = 0; i < Count; i++) ::new(&Array[i]) T(); + } } TArray (const TArray &other) { From 50bd9c35944c34612b4eac2f43bd0e9abf6a882b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 5 Nov 2018 21:29:57 +0100 Subject: [PATCH 16/39] - flatvertex generation is working again. --- src/hwrenderer/data/flatvertices.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/hwrenderer/data/flatvertices.cpp b/src/hwrenderer/data/flatvertices.cpp index 3083cbc098..b95e66bdf2 100644 --- a/src/hwrenderer/data/flatvertices.cpp +++ b/src/hwrenderer/data/flatvertices.cpp @@ -159,7 +159,7 @@ static F3DFloor *Find3DFloor(sector_t *target, sector_t *model) int FFlatVertexBuffer::CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, VertexContainer &verts) { - int vi = vbo_shadowdata.Reserve(verts.vertices.Size()); + unsigned vi = vbo_shadowdata.Reserve(verts.vertices.Size()); float diff; // Create the actual vertices. @@ -171,9 +171,12 @@ int FFlatVertexBuffer::CreateIndexedSectorVertices(sector_t *sec, const secplane vbo_shadowdata[vi + i].z += diff; } - int rt = ibo_data.Size(); - ibo_data.Append(verts.indices); - return rt; + unsigned rt = ibo_data.Reserve(verts.indices.Size()); + for (unsigned i = 0; i < verts.indices.Size(); i++) + { + ibo_data[rt + i] = vi + verts.indices[i]; + } + return (int)rt; } //========================================================================== From 375dd7e28fbf1cc6f365755784c01e25547eb03f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 5 Nov 2018 22:14:18 +0100 Subject: [PATCH 17/39] - the sections are now being used as the smallest element to draw flat planes. This also removes one piece of code that was used to cope with the missing clip planes on old ATI cards, so support for those will most likely have to be dropped in the near future. --- src/hwrenderer/scene/hw_drawinfo.h | 5 ---- src/hwrenderer/scene/hw_flats.cpp | 34 +++++-------------------- src/hwrenderer/scene/hw_renderhacks.cpp | 2 -- src/hwrenderer/scene/hw_skydome.cpp | 5 ++++ src/hwrenderer/scene/hw_skydome.h | 1 + 5 files changed, 12 insertions(+), 35 deletions(-) diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index c3a9ddfb39..537c2a2027 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -256,11 +256,6 @@ public: VPUniforms.mClipHeight = 0; } - bool ClipLineShouldBeActive() - { - return (screen->hwcaps & RFL_NO_CLIP_PLANES) && VPUniforms.mClipLine.X > -1000000.f; - } - HWPortal * FindPortal(const void * src); void RenderBSPNode(void *node); void RenderBSP(void *node); diff --git a/src/hwrenderer/scene/hw_flats.cpp b/src/hwrenderer/scene/hw_flats.cpp index db9edfccbb..2278bbd586 100644 --- a/src/hwrenderer/scene/hw_flats.cpp +++ b/src/hwrenderer/scene/hw_flats.cpp @@ -182,8 +182,6 @@ void GLFlat::SetupLights(HWDrawInfo *di, FLightNode * node, FDynLightData &light void GLFlat::DrawSubsectors(HWDrawInfo *di, FRenderState &state) { - auto vcount = sector->ibocount; - if (level.HasDynamicLights && screen->BuffersArePersistent()) { SetupLights(di, sector->lighthead, lightdata, sector->PortalGroup); @@ -191,34 +189,13 @@ void GLFlat::DrawSubsectors(HWDrawInfo *di, FRenderState &state) state.SetLightIndex(dynlightindex); - if (vcount > 0 && !di->ClipLineShouldBeActive()) - { - state.DrawIndexed(DT_Triangles, iboindex, vcount); - flatvertices += vcount; - flatprimitives++; - } - else - { - /* - int index = iboindex; - bool applied = false; - for (int i = 0; i < sector->subsectorcount; i++) - { - subsector_t * sub = sector->subsectors[i]; - if (sub->numlines <= 2) continue; + state.DrawIndexed(DT_Triangles, iboindex + section->vertexindex, section->vertexcount); + flatvertices += section->vertexcount; + flatprimitives++; - if (di->ss_renderflags[sub->Index()] & renderflags) - { - state.DrawIndexed(DT_Triangles, index, (sub->numlines - 2) * 3, !applied); - applied = true; - flatvertices += sub->numlines; - flatprimitives++; - } - index += (sub->numlines - 2) * 3; - } - */ - } +#if 0 + //Temporarily disabled until the render hack code can be redone and refactored into its own draw elements if (!(renderflags&SSRF_RENDER3DPLANES)) { // Draw the subsectors assigned to it due to missing textures @@ -282,6 +259,7 @@ void GLFlat::DrawSubsectors(HWDrawInfo *di, FRenderState &state) } } +#endif } //========================================================================== diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index afc8aaab89..5fbc96aea3 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -1297,7 +1297,6 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area) { subsector_t *sub = HandledSubsectors[j]; ss_renderflags[sub->Index()] &= ~SSRF_RENDERCEILING; - sub->sector->ibocount = -1; // cannot render this sector in one go. if (sub->portalcoverage[sector_t::ceiling].subsectors == NULL) { @@ -1343,7 +1342,6 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area) { subsector_t *sub = HandledSubsectors[j]; ss_renderflags[sub->Index()] &= ~SSRF_RENDERFLOOR; - sub->sector->ibocount = -1; // cannot render this sector in one go. if (sub->portalcoverage[sector_t::floor].subsectors == NULL) { diff --git a/src/hwrenderer/scene/hw_skydome.cpp b/src/hwrenderer/scene/hw_skydome.cpp index 7184b7bad6..27342cf7dc 100644 --- a/src/hwrenderer/scene/hw_skydome.cpp +++ b/src/hwrenderer/scene/hw_skydome.cpp @@ -96,6 +96,11 @@ FSkyVertexBuffer::FSkyVertexBuffer() mVertexBuffer->SetData(mVertices.Size() * sizeof(FSkyVertex), &mVertices[0], true); } +FSkyVertexBuffer::~FSkyVertexBuffer() +{ + delete mVertexBuffer; +} + //----------------------------------------------------------------------------- // // diff --git a/src/hwrenderer/scene/hw_skydome.h b/src/hwrenderer/scene/hw_skydome.h index b441bc8ca7..ed130084cb 100644 --- a/src/hwrenderer/scene/hw_skydome.h +++ b/src/hwrenderer/scene/hw_skydome.h @@ -67,6 +67,7 @@ public: public: FSkyVertexBuffer(); + ~FSkyVertexBuffer(); void SetupMatrices(FMaterial *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelmatrix, VSMatrix &textureMatrix); std::pair GetBufferObjects() const { From 9ddca3c3a923174a126e1df3c7a5f3170ef3544b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 5 Nov 2018 22:35:24 +0100 Subject: [PATCH 18/39] - removed the subsector light lists as a preparation step to move over the light traversal code to use sections instead of subsectors. --- src/g_shared/a_dynlight.cpp | 52 ++----------------------- src/g_shared/a_dynlight.h | 1 - src/hwrenderer/scene/hw_renderhacks.cpp | 2 +- src/hwrenderer/scene/hw_spritelight.cpp | 6 +-- src/hwrenderer/scene/hw_walls.cpp | 2 +- src/polyrenderer/scene/poly_model.cpp | 2 +- src/polyrenderer/scene/poly_plane.cpp | 2 +- src/r_defs.h | 1 - src/swrenderer/things/r_model.cpp | 2 +- 9 files changed, 12 insertions(+), 58 deletions(-) diff --git a/src/g_shared/a_dynlight.cpp b/src/g_shared/a_dynlight.cpp index 98d57c4ab4..b6e83abdcf 100644 --- a/src/g_shared/a_dynlight.cpp +++ b/src/g_shared/a_dynlight.cpp @@ -574,7 +574,7 @@ void ADynamicLight::CollectWithinRadius(const DVector3 &opos, subsector_t *subSe { subSec = collected_ss[i].sub; - touching_subsectors = AddLightNode(&subSec->lighthead, subSec, this, touching_subsectors); + //touching_subsectors = AddLightNode(&subSec->lighthead, subSec, this, touching_subsectors); if (subSec->sector->validcount != ::validcount) { touching_sector = AddLightNode(&subSec->render_sector->lighthead, subSec->sector, this, touching_sector); @@ -682,12 +682,6 @@ void ADynamicLight::LinkLight() { node->lightsource = NULL; node = node->nextTarget; - } - node = touching_subsectors; - while (node) - { - node->lightsource = NULL; - node = node->nextTarget; } node = touching_sector; while (node) @@ -719,17 +713,6 @@ void ADynamicLight::LinkLight() node = node->nextTarget; } - node = touching_subsectors; - while (node) - { - if (node->lightsource == NULL) - { - node = DeleteLightNode(node); - } - else - node = node->nextTarget; - } - node = touching_sector; while (node) { @@ -763,7 +746,6 @@ void ADynamicLight::UnlinkLight () } } while (touching_sides) touching_sides = DeleteLightNode(touching_sides); - while (touching_subsectors) touching_subsectors = DeleteLightNode(touching_subsectors); while (touching_sector) touching_sector = DeleteLightNode(touching_sector); shadowmapped = false; } @@ -918,7 +900,7 @@ void AActor::RecreateAllAttachedLights() CCMD(listlights) { - int walls, sectors, subsecs; + int walls, sectors; int allwalls=0, allsectors=0, allsubsecs = 0; int i=0, shadowcount = 0; ADynamicLight * dl; @@ -928,7 +910,6 @@ CCMD(listlights) { walls=0; sectors=0; - subsecs = 0; Printf("%s at (%f, %f, %f), color = 0x%02x%02x%02x, radius = %f %s %s", dl->target? dl->target->GetClass()->TypeName.GetChars() : dl->GetClass()->TypeName.GetChars(), dl->X(), dl->Y(), dl->Z(), dl->args[LIGHT_RED], @@ -954,14 +935,6 @@ CCMD(listlights) node = node->nextTarget; } - node=dl->touching_subsectors; - - while (node) - { - allsubsecs++; - subsecs++; - node = node->nextTarget; - } node = dl->touching_sector; @@ -971,27 +944,10 @@ CCMD(listlights) sectors++; node = node->nextTarget; } - Printf("- %d walls, %d subsectors, %d sectors\n", walls, subsecs, sectors); + Printf("- %d walls, %d sectors\n", walls, sectors); } - Printf("%i dynamic lights, %d shadowmapped, %d walls, %d subsectors, %d sectors\n\n\n", i, shadowcount, allwalls, allsubsecs, allsectors); -} - -CCMD(listsublights) -{ - for(auto &sub : level.subsectors) - { - int lights = 0; - - FLightNode * node = sub.lighthead; - while (node != NULL) - { - lights++; - node = node->nextLight; - } - - Printf(PRINT_LOG, "Subsector %d - %d lights\n", sub.Index(), lights); - } + Printf("%i dynamic lights, %d shadowmapped, %d walls, %d sectors\n\n\n", i, shadowcount, allwalls, allsectors); } diff --git a/src/g_shared/a_dynlight.h b/src/g_shared/a_dynlight.h index fa9d5840ec..671c7812fc 100644 --- a/src/g_shared/a_dynlight.h +++ b/src/g_shared/a_dynlight.h @@ -206,7 +206,6 @@ public: bool IsSpot() { return !!(lightflags & LF_SPOT); } FState *targetState; FLightNode * touching_sides; - FLightNode * touching_subsectors; FLightNode * touching_sector; private: diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index 5fbc96aea3..05770125de 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -54,7 +54,7 @@ int HWDrawInfo::SetupLightsForOtherPlane(subsector_t * sub, FDynLightData &light if (level.HasDynamicLights && !isFullbrightScene()) { Plane p; - FLightNode * node = sub->lighthead; + FLightNode * node = sub->sector->lighthead; lightdata.Clear(); while (node) diff --git a/src/hwrenderer/scene/hw_spritelight.cpp b/src/hwrenderer/scene/hw_spritelight.cpp index 9ce11f8595..3b5a0c1d84 100644 --- a/src/hwrenderer/scene/hw_spritelight.cpp +++ b/src/hwrenderer/scene/hw_spritelight.cpp @@ -131,11 +131,11 @@ void HWDrawInfo::GetDynSpriteLight(AActor *thing, particle_t *particle, float *o { if (thing != NULL) { - GetDynSpriteLight(thing, (float)thing->X(), (float)thing->Y(), (float)thing->Center(), thing->subsector->lighthead, thing->Sector->PortalGroup, out); + GetDynSpriteLight(thing, (float)thing->X(), (float)thing->Y(), (float)thing->Center(), thing->Sector->lighthead, thing->Sector->PortalGroup, out); } else if (particle != NULL) { - GetDynSpriteLight(NULL, (float)particle->Pos.X, (float)particle->Pos.Y, (float)particle->Pos.Z, particle->subsector->lighthead, particle->subsector->sector->PortalGroup, out); + GetDynSpriteLight(NULL, (float)particle->Pos.X, (float)particle->Pos.Y, (float)particle->Pos.Z, particle->subsector->sector->lighthead, particle->subsector->sector->PortalGroup, out); } } @@ -160,7 +160,7 @@ void hw_GetDynModelLight(AActor *self, FDynLightData &modellightdata) BSPWalkCircle(x, y, radiusSquared, [&](subsector_t *subsector) // Iterate through all subsectors potentially touched by actor { - FLightNode * node = subsector->lighthead; + FLightNode * node = subsector->sector->lighthead; while (node) // check all lights touching a subsector { ADynamicLight *light = node->lightsource; diff --git a/src/hwrenderer/scene/hw_walls.cpp b/src/hwrenderer/scene/hw_walls.cpp index 8c5e90d875..b782c54e92 100644 --- a/src/hwrenderer/scene/hw_walls.cpp +++ b/src/hwrenderer/scene/hw_walls.cpp @@ -302,7 +302,7 @@ void GLWall::SetupLights(HWDrawInfo *di, FDynLightData &lightdata) else if (sub) { // Polobject segs cannot be checked per sidedef so use the subsector instead. - node = sub->lighthead; + node = sub->sector->lighthead; } else node = NULL; diff --git a/src/polyrenderer/scene/poly_model.cpp b/src/polyrenderer/scene/poly_model.cpp index 39695c0162..78520826f8 100644 --- a/src/polyrenderer/scene/poly_model.cpp +++ b/src/polyrenderer/scene/poly_model.cpp @@ -70,7 +70,7 @@ void PolyModelRenderer::AddLights(AActor *actor) BSPWalkCircle(x, y, radiusSquared, [&](subsector_t *subsector) // Iterate through all subsectors potentially touched by actor { - FLightNode * node = subsector->lighthead; + FLightNode * node = subsector->sector->lighthead; while (node) // check all lights touching a subsector { ADynamicLight *light = node->lightsource; diff --git a/src/polyrenderer/scene/poly_plane.cpp b/src/polyrenderer/scene/poly_plane.cpp index 08a8897e76..076a334f9b 100644 --- a/src/polyrenderer/scene/poly_plane.cpp +++ b/src/polyrenderer/scene/poly_plane.cpp @@ -275,7 +275,7 @@ void RenderPolyPlane::SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args, return; } - FLightNode *light_list = sub->lighthead; + FLightNode *light_list = sub->sector->lighthead; auto cameraLight = PolyCameraLight::Instance(); if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr)) diff --git a/src/r_defs.h b/src/r_defs.h index 4bccef5269..9f1e7c265f 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -1460,7 +1460,6 @@ struct subsector_t uint16_t sectorindex; // subsector related GL data - FLightNode * lighthead; // Light nodes (blended and additive) int validcount; short mapsection; char hacked; // 1: is part of a render hack diff --git a/src/swrenderer/things/r_model.cpp b/src/swrenderer/things/r_model.cpp index 1f02e0b6c8..a3ce1a5bbc 100644 --- a/src/swrenderer/things/r_model.cpp +++ b/src/swrenderer/things/r_model.cpp @@ -111,7 +111,7 @@ namespace swrenderer BSPWalkCircle(x, y, radiusSquared, [&](subsector_t *subsector) // Iterate through all subsectors potentially touched by actor { - FLightNode * node = subsector->lighthead; + FLightNode * node = subsector->sector->lighthead; while (node) // check all lights touching a subsector { ADynamicLight *light = node->lightsource; From ba66c0c889dc6bdd687b98775e7da015f2447add Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 6 Nov 2018 00:13:23 +0100 Subject: [PATCH 19/39] - changed dynamic light traversal to use sections instead of the subsectors. This is mostly complete, except for handling intra-section sidedefs. --- src/g_shared/a_dynlight.cpp | 155 ++++++++++++------------ src/g_shared/a_dynlight.h | 5 +- src/hwrenderer/data/hw_sections.cpp | 11 +- src/hwrenderer/data/hw_sections.h | 18 +-- src/hwrenderer/scene/hw_bsp.cpp | 7 +- src/hwrenderer/scene/hw_flats.cpp | 4 +- src/hwrenderer/scene/hw_renderhacks.cpp | 3 +- src/hwrenderer/scene/hw_spritelight.cpp | 9 +- src/hwrenderer/scene/hw_walls.cpp | 2 +- src/p_maputl.cpp | 2 +- src/polyrenderer/scene/poly_model.cpp | 2 +- src/polyrenderer/scene/poly_plane.cpp | 2 +- src/polyrenderer/scene/poly_sprite.cpp | 2 +- src/r_defs.h | 6 +- src/r_utility.cpp | 1 + src/r_utility.h | 1 + src/swrenderer/scene/r_opaque_pass.cpp | 8 +- src/swrenderer/things/r_model.cpp | 2 +- src/swrenderer/things/r_sprite.cpp | 3 +- src/swrenderer/things/r_visiblesprite.h | 1 + 20 files changed, 119 insertions(+), 125 deletions(-) diff --git a/src/g_shared/a_dynlight.cpp b/src/g_shared/a_dynlight.cpp index b6e83abdcf..e8dc871bf1 100644 --- a/src/g_shared/a_dynlight.cpp +++ b/src/g_shared/a_dynlight.cpp @@ -80,7 +80,7 @@ DEFINE_CLASS_PROPERTY(type, S, DynamicLight) { PROP_STRING_PARM(str, 0); static const char * ltype_names[]={ - "Point","Pulse","Flicker","Sector","RandomFlicker", "ColorPulse", "ColorFlicker", "RandomColorFlicker", NULL}; + "Point","Pulse","Flicker","Sector","RandomFlicker", "ColorPulse", "ColorFlicker", "RandomColorFlicker", nullptr}; static const int ltype_values[]={ PointLight, PulseLight, FlickerLight, SectorLight, RandomFlickerLight, ColorPulseLight, ColorFlickerLight, RandomColorFlickerLight }; @@ -182,7 +182,7 @@ void ADynamicLight::PostBeginPlay() if (!(SpawnFlags & MTF_DORMANT)) { - Activate (NULL); + Activate (nullptr); } subsector = R_PointInSubsector(Pos()); @@ -430,14 +430,14 @@ void ADynamicLight::SetOffset(const DVector3 &pos) //========================================================================== // // The target pointer in dynamic lights should never be substituted unless -// notOld is NULL (which indicates that the object was destroyed by force.) +// notOld is nullptr (which indicates that the object was destroyed by force.) // //========================================================================== size_t ADynamicLight::PointerSubstitution (DObject *old, DObject *notOld) { AActor *saved_target = target; size_t ret = Super::PointerSubstitution(old, notOld); - if (notOld != NULL) target = saved_target; + if (notOld != nullptr) target = saved_target; return ret; } @@ -494,7 +494,7 @@ FLightNode * AddLightNode(FLightNode ** thread, void * linkto, ADynamicLight * l // // P_DelSecnode() deletes a sector node from the list of // sectors this object appears in. Returns a pointer to the next node -// on the linked list, or NULL. +// on the linked list, or nullptr. // //============================================================================= @@ -516,7 +516,7 @@ static FLightNode * DeleteLightNode(FLightNode * node) delete node; return(tn); } - return(NULL); + return(nullptr); } // phares 3/13/98 @@ -527,20 +527,20 @@ static FLightNode * DeleteLightNode(FLightNode * node) // //========================================================================== -double ADynamicLight::DistToSeg(const DVector3 &pos, seg_t *seg) +double ADynamicLight::DistToSeg(const DVector3 &pos, FSectionLine *segment) { double u, px, py; - double seg_dx = seg->v2->fX() - seg->v1->fX(); - double seg_dy = seg->v2->fY() - seg->v1->fY(); + double seg_dx = segment->end->fX() - segment->start->fX(); + double seg_dy = segment->end->fY() - segment->start->fY(); double seg_length_sq = seg_dx * seg_dx + seg_dy * seg_dy; - u = (((pos.X - seg->v1->fX()) * seg_dx) + (pos.Y - seg->v1->fY()) * seg_dy) / seg_length_sq; + u = (((pos.X - segment->start->fX()) * seg_dx) + (pos.Y - segment->start->fY()) * seg_dy) / seg_length_sq; if (u < 0.) u = 0.; // clamp the test point to the line segment else if (u > 1.) u = 1.; - px = seg->v1->fX() + (u * seg_dx); - py = seg->v1->fY() + (u * seg_dy); + px = segment->start->fX() + (u * seg_dx); + py = segment->start->fY() + (u * seg_dy); px -= pos.X; py -= pos.Y; @@ -557,108 +557,110 @@ double ADynamicLight::DistToSeg(const DVector3 &pos, seg_t *seg) //========================================================================== struct LightLinkEntry { - subsector_t *sub; + FSection *sect; DVector3 pos; }; static TArray collected_ss; -void ADynamicLight::CollectWithinRadius(const DVector3 &opos, subsector_t *subSec, float radius) +void ADynamicLight::CollectWithinRadius(const DVector3 &opos, FSection *section, float radius) { - if (!subSec) return; + if (!section) return; collected_ss.Clear(); - collected_ss.Push({ subSec, opos }); - subSec->validcount = ::validcount; + collected_ss.Push({ section, opos }); + section->validcount = dl_validcount; bool hitonesidedback = false; for (unsigned i = 0; i < collected_ss.Size(); i++) { - subSec = collected_ss[i].sub; + section = collected_ss[i].sect; - //touching_subsectors = AddLightNode(&subSec->lighthead, subSec, this, touching_subsectors); - if (subSec->sector->validcount != ::validcount) - { - touching_sector = AddLightNode(&subSec->render_sector->lighthead, subSec->sector, this, touching_sector); - subSec->sector->validcount = ::validcount; - } + touching_sector = AddLightNode(§ion->lighthead, section, this, touching_sector); - for (unsigned int j = 0; j < subSec->numlines; ++j) + for (auto &segment : section->segments) { auto &pos = collected_ss[i].pos; - seg_t *seg = subSec->firstline + j; // check distance from x/y to seg and if within radius add this seg and, if present the opposing subsector (lather/rinse/repeat) // If out of range we do not need to bother with this seg. - if (DistToSeg(pos, seg) <= radius) + if (DistToSeg(pos, &segment) <= radius) { - if (seg->sidedef && seg->linedef && seg->linedef->validcount != ::validcount) + if (segment.sidedef) { - // light is in front of the seg - if ((pos.Y - seg->v1->fY()) * (seg->v2->fX() - seg->v1->fX()) + (seg->v1->fX() - pos.X) * (seg->v2->fY() - seg->v1->fY()) <= 0) + auto sidedef = segment.sidedef; + auto linedef = sidedef->linedef; + if (linedef && linedef->validcount != ::validcount) { - seg->linedef->validcount = validcount; - touching_sides = AddLightNode(&seg->sidedef->lighthead, seg->sidedef, this, touching_sides); - } - else if (seg->linedef->sidedef[0] == seg->sidedef && seg->linedef->sidedef[1] == nullptr) - { - hitonesidedback = true; - } - } - if (seg->linedef) - { - FLinePortal *port = seg->linedef->getPortal(); - if (port && port->mType == PORTT_LINKED) - { - line_t *other = port->mDestination; - if (other->validcount != ::validcount) + // light is in front of the seg + if ((pos.Y - segment.start->fY()) * (segment.end->fX() - segment.start->fX()) + (segment.start->fX() - pos.X) * (segment.end->fY() - segment.start->fY()) <= 0) { - subsector_t *othersub = R_PointInSubsector(other->v1->fPos() + other->Delta() / 2); - if (othersub->validcount != ::validcount) + linedef->validcount = ::validcount; + touching_sides = AddLightNode(&sidedef->lighthead, sidedef, this, touching_sides); + } + else if (linedef->sidedef[0] == sidedef && linedef->sidedef[1] == nullptr) + { + hitonesidedback = true; + } + } + if (linedef) + { + FLinePortal *port = linedef->getPortal(); + if (port && port->mType == PORTT_LINKED) + { + line_t *other = port->mDestination; + if (other->validcount != ::validcount) { - othersub->validcount = ::validcount; - collected_ss.Push({ othersub, PosRelative(other) }); + subsector_t *othersub = R_PointInSubsector(other->v1->fPos() + other->Delta() / 2); + FSection *othersect = othersub->section; + if (othersect->validcount != ::validcount) + { + othersect->validcount = ::validcount; + collected_ss.Push({ othersect, PosRelative(other) }); + } } } } } - seg_t *partner = seg->PartnerSeg; + auto partner = segment.partner; if (partner) { - subsector_t *sub = partner->Subsector; - if (sub != NULL && sub->validcount != ::validcount) + FSection *sect = partner->section; + if (sect != nullptr && sect->validcount != dl_validcount) { - sub->validcount = ::validcount; - collected_ss.Push({ sub, pos }); + sect->validcount = dl_validcount; + collected_ss.Push({ sect, pos }); } } } } - sector_t *sec = subSec->sector; + sector_t *sec = section->sector; if (!sec->PortalBlocksSight(sector_t::ceiling)) { - line_t *other = subSec->firstline->linedef; + line_t *other = section->segments[0].sidedef->linedef; if (sec->GetPortalPlaneZ(sector_t::ceiling) < Z() + radius) { DVector2 refpos = other->v1->fPos() + other->Delta() / 2 + sec->GetPortalDisplacement(sector_t::ceiling); subsector_t *othersub = R_PointInSubsector(refpos); - if (othersub->validcount != ::validcount) + FSection *othersect = othersub->section; + if (othersect->validcount != dl_validcount) { - othersub->validcount = ::validcount; - collected_ss.Push({ othersub, PosRelative(othersub->sector) }); + othersect->validcount = dl_validcount; + collected_ss.Push({ othersect, PosRelative(othersub->sector) }); } } } if (!sec->PortalBlocksSight(sector_t::floor)) { - line_t *other = subSec->firstline->linedef; + line_t *other = section->segments[0].sidedef->linedef; if (sec->GetPortalPlaneZ(sector_t::floor) > Z() - radius) { DVector2 refpos = other->v1->fPos() + other->Delta() / 2 + sec->GetPortalDisplacement(sector_t::floor); subsector_t *othersub = R_PointInSubsector(refpos); - if (othersub->validcount != ::validcount) + FSection *othersect = othersub->section; + if (othersect->validcount != dl_validcount) { - othersub->validcount = ::validcount; - collected_ss.Push({ othersub, PosRelative(othersub->sector) }); + othersect->validcount = dl_validcount; + collected_ss.Push({ othersect, PosRelative(othersub->sector) }); } } } @@ -680,32 +682,33 @@ void ADynamicLight::LinkLight() node = touching_sides; while (node) { - node->lightsource = NULL; + node->lightsource = nullptr; node = node->nextTarget; } node = touching_sector; while (node) { - node->lightsource = NULL; + node->lightsource = nullptr; node = node->nextTarget; } if (radius>0) { // passing in radius*radius allows us to do a distance check without any calls to sqrt - subsector_t * subSec = R_PointInSubsector(Pos()); - ::validcount++; - CollectWithinRadius(Pos(), subSec, float(radius*radius)); + FSection *sect = R_PointInSubsector(Pos())->section; + + dl_validcount++; + CollectWithinRadius(Pos(), sect, float(radius*radius)); } // Now delete any nodes that won't be used. These are the ones where - // m_thing is still NULL. + // m_thing is still nullptr. node = touching_sides; while (node) { - if (node->lightsource == NULL) + if (node->lightsource == nullptr) { node = DeleteLightNode(node); } @@ -716,7 +719,7 @@ void ADynamicLight::LinkLight() node = touching_sector; while (node) { - if (node->lightsource == NULL) + if (node->lightsource == nullptr) { node = DeleteLightNode(node); } @@ -733,7 +736,7 @@ void ADynamicLight::LinkLight() //========================================================================== void ADynamicLight::UnlinkLight () { - if (owned && target != NULL) + if (owned && target != nullptr) { // Delete reference in owning actor for(int c=target->AttachedLights.Size()-1; c>=0; c--) @@ -770,7 +773,7 @@ void AActor::AttachLight(unsigned int count, const FLightDefaults *lightdef) if (count < AttachedLights.Size()) { light = barrier_cast(AttachedLights[count]); - assert(light != NULL); + assert(light != nullptr); } else { @@ -797,13 +800,13 @@ void AActor::SetDynamicLights() TArray & LightAssociations = GetInfo()->LightAssociations; unsigned int count = 0; - if (state == NULL) return; + if (state == nullptr) return; if (LightAssociations.Size() > 0) { ADynamicLight *lights, *tmpLight; unsigned int i; - lights = tmpLight = NULL; + lights = tmpLight = nullptr; for (i = 0; i < LightAssociations.Size(); i++) { @@ -816,7 +819,7 @@ void AActor::SetDynamicLights() } if (count == 0 && state->Light > 0) { - for(int i= state->Light; StateLights[i] != NULL; i++) + for(int i= state->Light; StateLights[i] != nullptr; i++) { if (StateLights[i] != (FLightDefaults*)-1) { diff --git a/src/g_shared/a_dynlight.h b/src/g_shared/a_dynlight.h index 671c7812fc..d28b787b1b 100644 --- a/src/g_shared/a_dynlight.h +++ b/src/g_shared/a_dynlight.h @@ -12,6 +12,7 @@ struct seg_t; class ADynamicLight; class FSerializer; +struct FSectionLine; enum ELightType { @@ -209,8 +210,8 @@ public: FLightNode * touching_sector; private: - double DistToSeg(const DVector3 &pos, seg_t *seg); - void CollectWithinRadius(const DVector3 &pos, subsector_t *subSec, float radius); + double DistToSeg(const DVector3 &pos, FSectionLine *seg); + void CollectWithinRadius(const DVector3 &pos, FSection *section, float radius); protected: DVector3 m_off; diff --git a/src/hwrenderer/data/hw_sections.cpp b/src/hwrenderer/data/hw_sections.cpp index 4ad20d47a3..c43a22eeb3 100644 --- a/src/hwrenderer/data/hw_sections.cpp +++ b/src/hwrenderer/data/hw_sections.cpp @@ -633,11 +633,9 @@ public: { output.allSections.Resize(groups.Size()); output.allIndices.Resize(level.subsectors.Size() + level.sides.Size() + 2*level.sectors.Size()); - output.sectionForSubsectorPtr = &output.allIndices[0]; - output.sectionForSidedefPtr = &output.allIndices[level.subsectors.Size()]; - output.firstSectionForSectorPtr = &output.allIndices[level.subsectors.Size() + level.sides.Size()]; - output.numberOfSectionForSectorPtr = &output.allIndices[level.subsectors.Size() + level.sides.Size() + level.sectors.Size()]; - memset(output.sectionForSubsectorPtr, -1, sizeof(int) * level.subsectors.Size()); + output.sectionForSidedefPtr = &output.allIndices[0]; + output.firstSectionForSectorPtr = &output.allIndices[level.sides.Size()]; + output.numberOfSectionForSectorPtr = &output.allIndices[level.sides.Size() + level.sectors.Size()]; memset(output.firstSectionForSectorPtr, -1, sizeof(int) * level.sectors.Size()); memset(output.numberOfSectionForSectorPtr, 0, sizeof(int) * level.sectors.Size()); @@ -704,6 +702,7 @@ public: fseg.end = segment->end; fseg.partner = segment->partner == nullptr ? nullptr : &output.allLines[segment->partner->tempindex]; fseg.sidedef = segment->sidedef; + fseg.section = &dest; dest.bounds.addVertex(fseg.start->fX(), fseg.start->fY()); dest.bounds.addVertex(fseg.end->fX(), fseg.end->fY()); } @@ -717,7 +716,7 @@ public: for (auto ssi : group.subsectors) { output.allSubsectors[numsubsectors++] = &level.subsectors[ssi]; - output.sectionForSubsectorPtr[ssi] = curgroup; + level.subsectors[ssi].section = &output.allSections[curgroup]; } curgroup++; } diff --git a/src/hwrenderer/data/hw_sections.h b/src/hwrenderer/data/hw_sections.h index 178d0ed842..668c2ba397 100644 --- a/src/hwrenderer/data/hw_sections.h +++ b/src/hwrenderer/data/hw_sections.h @@ -73,6 +73,7 @@ struct FSectionLine vertex_t *start; vertex_t *end; FSectionLine *partner; + FSection *section; side_t *sidedef; }; @@ -101,27 +102,10 @@ public: TArray allSubsectors; TArray allIndices; - int *sectionForSubsectorPtr; // stored inside allIndices int *sectionForSidedefPtr; // also stored inside allIndices; int *firstSectionForSectorPtr; // ditto. int *numberOfSectionForSectorPtr; // ditto. - FSection *SectionForSubsector(subsector_t *sub) - { - return SectionForSubsector(sub->Index()); - } - FSection *SectionForSubsector(int ssindex) - { - return ssindex < 0 ? nullptr : &allSections[sectionForSubsectorPtr[ssindex]]; - } - int SectionNumForSubsector(subsector_t *sub) - { - return SectionNumForSubsector(sub->Index()); - } - int SectionNumForSubsector(int ssindex) - { - return ssindex < 0 ? -1 : sectionForSubsectorPtr[ssindex]; - } FSection *SectionForSidedef(side_t *side) { return SectionForSidedef(side->Index()); diff --git a/src/hwrenderer/scene/hw_bsp.cpp b/src/hwrenderer/scene/hw_bsp.cpp index 92b9117814..affca42a85 100644 --- a/src/hwrenderer/scene/hw_bsp.cpp +++ b/src/hwrenderer/scene/hw_bsp.cpp @@ -155,7 +155,7 @@ void HWDrawInfo::WorkerThread() { GLFlat flat; SetupFlat.Clock(); - flat.section = level.sections.SectionForSubsector(job->sub); + flat.section = job->sub->section; front = hw_FakeFlat(job->sub->render_sector, &fakefront, in_area, false); flat.ProcessSector(this, front); SetupFlat.Unclock(); @@ -680,8 +680,7 @@ void HWDrawInfo::DoSubsector(subsector_t * sub) fakesector = hw_FakeFlat(sector, &fake, in_area, false); } - auto secnum = level.sections.SectionNumForSubsector(sub); - uint8_t &srf = section_renderflags[secnum]; + uint8_t &srf = section_renderflags[level.sections.SectionIndex(sub->section)]; if (!(srf & SSRF_PROCESSED)) { srf |= SSRF_PROCESSED; @@ -693,7 +692,7 @@ void HWDrawInfo::DoSubsector(subsector_t * sub) else { GLFlat flat; - flat.section = level.sections.SectionForSubsector(sub); + flat.section = sub->section; SetupFlat.Clock(); flat.ProcessSector(this, fakesector); SetupFlat.Unclock(); diff --git a/src/hwrenderer/scene/hw_flats.cpp b/src/hwrenderer/scene/hw_flats.cpp index 2278bbd586..f7948e080f 100644 --- a/src/hwrenderer/scene/hw_flats.cpp +++ b/src/hwrenderer/scene/hw_flats.cpp @@ -184,7 +184,7 @@ void GLFlat::DrawSubsectors(HWDrawInfo *di, FRenderState &state) { if (level.HasDynamicLights && screen->BuffersArePersistent()) { - SetupLights(di, sector->lighthead, lightdata, sector->PortalGroup); + SetupLights(di, section->lighthead, lightdata, sector->PortalGroup); } state.SetLightIndex(dynlightindex); @@ -346,7 +346,7 @@ inline void GLFlat::PutFlat(HWDrawInfo *di, bool fog) { if (level.HasDynamicLights && gltexture != nullptr) { - SetupLights(di, sector->lighthead, lightdata, sector->PortalGroup); + SetupLights(di, section->lighthead, lightdata, sector->PortalGroup); } } di->AddFlat(this, fog); diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index 05770125de..8f3325f2d8 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -54,7 +54,8 @@ int HWDrawInfo::SetupLightsForOtherPlane(subsector_t * sub, FDynLightData &light if (level.HasDynamicLights && !isFullbrightScene()) { Plane p; - FLightNode * node = sub->sector->lighthead; + + FLightNode * node = sub->section->lighthead; lightdata.Clear(); while (node) diff --git a/src/hwrenderer/scene/hw_spritelight.cpp b/src/hwrenderer/scene/hw_spritelight.cpp index 3b5a0c1d84..a0d76343b2 100644 --- a/src/hwrenderer/scene/hw_spritelight.cpp +++ b/src/hwrenderer/scene/hw_spritelight.cpp @@ -131,11 +131,11 @@ void HWDrawInfo::GetDynSpriteLight(AActor *thing, particle_t *particle, float *o { if (thing != NULL) { - GetDynSpriteLight(thing, (float)thing->X(), (float)thing->Y(), (float)thing->Center(), thing->Sector->lighthead, thing->Sector->PortalGroup, out); + GetDynSpriteLight(thing, (float)thing->X(), (float)thing->Y(), (float)thing->Center(), thing->section->lighthead, thing->Sector->PortalGroup, out); } else if (particle != NULL) { - GetDynSpriteLight(NULL, (float)particle->Pos.X, (float)particle->Pos.Y, (float)particle->Pos.Z, particle->subsector->sector->lighthead, particle->subsector->sector->PortalGroup, out); + GetDynSpriteLight(NULL, (float)particle->Pos.X, (float)particle->Pos.Y, (float)particle->Pos.Z, particle->subsector->section->lighthead, particle->subsector->sector->PortalGroup, out); } } @@ -157,10 +157,13 @@ void hw_GetDynModelLight(AActor *self, FDynLightData &modellightdata) float y = (float)self->Y(); float z = (float)self->Center(); float radiusSquared = (float)(self->renderradius * self->renderradius); + dl_validcount++; BSPWalkCircle(x, y, radiusSquared, [&](subsector_t *subsector) // Iterate through all subsectors potentially touched by actor { - FLightNode * node = subsector->sector->lighthead; + auto section = subsector->section; + if (section->validcount == dl_validcount) return; // already done from a previous subsector. + FLightNode * node = section->lighthead; while (node) // check all lights touching a subsector { ADynamicLight *light = node->lightsource; diff --git a/src/hwrenderer/scene/hw_walls.cpp b/src/hwrenderer/scene/hw_walls.cpp index b782c54e92..b91c38ae1a 100644 --- a/src/hwrenderer/scene/hw_walls.cpp +++ b/src/hwrenderer/scene/hw_walls.cpp @@ -302,7 +302,7 @@ void GLWall::SetupLights(HWDrawInfo *di, FDynLightData &lightdata) else if (sub) { // Polobject segs cannot be checked per sidedef so use the subsector instead. - node = sub->sector->lighthead; + node = sub->section->lighthead; } else node = NULL; diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index 19c9496d33..008edb2108 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -470,7 +470,7 @@ void AActor::LinkToWorld(FLinkContext *ctx, bool spawningmapthing, sector_t *sec Sector = sector; subsector = R_PointInSubsector(Pos()); // this is from the rendering nodes, not the gameplay nodes! - section = level.sections.SectionForSubsector(subsector->Index()); + section = subsector->section; if (!(flags & MF_NOSECTOR)) { diff --git a/src/polyrenderer/scene/poly_model.cpp b/src/polyrenderer/scene/poly_model.cpp index 78520826f8..91c975d1eb 100644 --- a/src/polyrenderer/scene/poly_model.cpp +++ b/src/polyrenderer/scene/poly_model.cpp @@ -70,7 +70,7 @@ void PolyModelRenderer::AddLights(AActor *actor) BSPWalkCircle(x, y, radiusSquared, [&](subsector_t *subsector) // Iterate through all subsectors potentially touched by actor { - FLightNode * node = subsector->sector->lighthead; + FLightNode * node = subsector->section->lighthead; while (node) // check all lights touching a subsector { ADynamicLight *light = node->lightsource; diff --git a/src/polyrenderer/scene/poly_plane.cpp b/src/polyrenderer/scene/poly_plane.cpp index 076a334f9b..18d24cfd76 100644 --- a/src/polyrenderer/scene/poly_plane.cpp +++ b/src/polyrenderer/scene/poly_plane.cpp @@ -275,7 +275,7 @@ void RenderPolyPlane::SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args, return; } - FLightNode *light_list = sub->sector->lighthead; + FLightNode *light_list = sub->section->lighthead; auto cameraLight = PolyCameraLight::Instance(); if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr)) diff --git a/src/polyrenderer/scene/poly_sprite.cpp b/src/polyrenderer/scene/poly_sprite.cpp index 011fa67c66..122f518c68 100644 --- a/src/polyrenderer/scene/poly_sprite.cpp +++ b/src/polyrenderer/scene/poly_sprite.cpp @@ -381,7 +381,7 @@ void RenderPolySprite::SetDynlight(AActor *thing, PolyDrawArgs &args) float lit_red = 0; float lit_green = 0; float lit_blue = 0; - auto node = thing->Sector->lighthead; + auto node = thing->section->lighthead; while (node != nullptr) { ADynamicLight *light = node->lightsource; diff --git a/src/r_defs.h b/src/r_defs.h index 9f1e7c265f..5163e8f832 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -51,6 +51,7 @@ struct FLinePortal; struct seg_t; struct sector_t; class AActor; +struct FSection; #define MAXWIDTH 12000 #define MAXHEIGHT 5000 @@ -1028,7 +1029,6 @@ public: // killough 3/7/98: support flat heights drawn at another sector's heights sector_t *heightsec; // other sector, or NULL if no other sector - FLightNode * lighthead; uint32_t bottommap, midmap, topmap; // killough 4/4/98: dynamic colormaps // [RH] these can also be blend values if @@ -1455,13 +1455,13 @@ struct subsector_t FMiniBSP *BSP; seg_t *firstline; sector_t *render_sector; + FSection *section; uint32_t numlines; uint16_t flags; - uint16_t sectorindex; + short mapsection; // subsector related GL data int validcount; - short mapsection; char hacked; // 1: is part of a render hack void BuildPolyBSP(); diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 8de8727f1c..77c6856264 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -142,6 +142,7 @@ bool setsizeneeded; unsigned int R_OldBlend = ~0; int validcount = 1; // increment every time a check is made +int dl_validcount = 1; // increment every time a check is made FCanvasTextureInfo *FCanvasTextureInfo::List; DVector3a view; diff --git a/src/r_utility.h b/src/r_utility.h index b18588ab91..e5ac4a2b8b 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -66,6 +66,7 @@ extern FViewWindow r_viewwindow; extern int setblocks; extern bool r_NoInterpolate; extern int validcount; +extern int dl_validcount; // For use with FSection. validcount is in use by the renderer and any quick section exclusion needs another variable. extern angle_t LocalViewAngle; // [RH] Added to consoleplayer's angle extern int LocalViewPitch; // [RH] Used directly instead of consoleplayer's pitch diff --git a/src/swrenderer/scene/r_opaque_pass.cpp b/src/swrenderer/scene/r_opaque_pass.cpp index 987b89b5b7..de6922500e 100644 --- a/src/swrenderer/scene/r_opaque_pass.cpp +++ b/src/swrenderer/scene/r_opaque_pass.cpp @@ -558,7 +558,7 @@ namespace swrenderer Fake3DOpaque::Normal, 0); - ceilingplane->AddLights(Thread, frontsector->lighthead); + ceilingplane->AddLights(Thread, sub->section->lighthead); } int adjusted_floorlightlevel = floorlightlevel; @@ -598,7 +598,7 @@ namespace swrenderer Fake3DOpaque::Normal, 0); - floorplane->AddLights(Thread, frontsector->lighthead); + floorplane->AddLights(Thread, sub->section->lighthead); } Add3DFloorPlanes(sub, frontsector, basecolormap, foggy, adjusted_ceilinglightlevel, adjusted_floorlightlevel); @@ -742,7 +742,7 @@ namespace swrenderer Fake3DOpaque::FakeFloor, fakeAlpha); - floorplane3d->AddLights(Thread, tempsec.lighthead); + floorplane3d->AddLights(Thread, sub->section->lighthead); FakeDrawLoop(sub, &tempsec, floorplane3d, nullptr, foggy, basecolormap, Fake3DOpaque::FakeFloor); } @@ -810,7 +810,7 @@ namespace swrenderer Fake3DOpaque::FakeCeiling, fakeAlpha); - ceilingplane3d->AddLights(Thread, tempsec.lighthead); + ceilingplane3d->AddLights(Thread, sub->section->lighthead); FakeDrawLoop(sub, &tempsec, nullptr, ceilingplane3d, foggy, basecolormap, Fake3DOpaque::FakeCeiling); } diff --git a/src/swrenderer/things/r_model.cpp b/src/swrenderer/things/r_model.cpp index a3ce1a5bbc..2ee4169732 100644 --- a/src/swrenderer/things/r_model.cpp +++ b/src/swrenderer/things/r_model.cpp @@ -111,7 +111,7 @@ namespace swrenderer BSPWalkCircle(x, y, radiusSquared, [&](subsector_t *subsector) // Iterate through all subsectors potentially touched by actor { - FLightNode * node = subsector->sector->lighthead; + FLightNode * node = subsector->section->lighthead; while (node) // check all lights touching a subsector { ADynamicLight *light = node->lightsource; diff --git a/src/swrenderer/things/r_sprite.cpp b/src/swrenderer/things/r_sprite.cpp index 36e9ca89f3..1251860d9d 100644 --- a/src/swrenderer/things/r_sprite.cpp +++ b/src/swrenderer/things/r_sprite.cpp @@ -202,6 +202,7 @@ namespace swrenderer // killough 3/27/98: save sector for special clipping later vis->heightsec = heightsec; vis->sector = thing->Sector; + vis->section = thing->section; vis->depth = (float)tz; vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; @@ -251,7 +252,7 @@ namespace swrenderer float lit_red = 0; float lit_green = 0; float lit_blue = 0; - auto node = vis->sector->lighthead; + auto node = vis->section->lighthead; while (node != nullptr) { ADynamicLight *light = node->lightsource; diff --git a/src/swrenderer/things/r_visiblesprite.h b/src/swrenderer/things/r_visiblesprite.h index b1ddebae79..37787a8093 100644 --- a/src/swrenderer/things/r_visiblesprite.h +++ b/src/swrenderer/things/r_visiblesprite.h @@ -73,6 +73,7 @@ namespace swrenderer FVector3 gpos = { 0.0f, 0.0f, 0.0f }; // origin in world coordinates sector_t *sector = nullptr; // sector this sprite is in + FSection *section; ColormapLight Light; float Alpha = 0.0f; From 87973ff50476a58ea06624a5cc7c31e60d29cd4b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 6 Nov 2018 00:47:43 +0100 Subject: [PATCH 20/39] - added handling for intra-sector lines to lighting code. --- src/g_shared/a_dynlight.cpp | 95 ++++++++++++++++------------- src/g_shared/a_dynlight.h | 2 +- src/hwrenderer/data/hw_sections.cpp | 4 +- 3 files changed, 56 insertions(+), 45 deletions(-) diff --git a/src/g_shared/a_dynlight.cpp b/src/g_shared/a_dynlight.cpp index e8dc871bf1..8f5c3ec1a0 100644 --- a/src/g_shared/a_dynlight.cpp +++ b/src/g_shared/a_dynlight.cpp @@ -527,20 +527,20 @@ static FLightNode * DeleteLightNode(FLightNode * node) // //========================================================================== -double ADynamicLight::DistToSeg(const DVector3 &pos, FSectionLine *segment) +double ADynamicLight::DistToSeg(const DVector3 &pos, vertex_t *start, vertex_t *end) { double u, px, py; - double seg_dx = segment->end->fX() - segment->start->fX(); - double seg_dy = segment->end->fY() - segment->start->fY(); + double seg_dx = end->fX() - start->fX(); + double seg_dy = end->fY() - start->fY(); double seg_length_sq = seg_dx * seg_dx + seg_dy * seg_dy; - u = (((pos.X - segment->start->fX()) * seg_dx) + (pos.Y - segment->start->fY()) * seg_dy) / seg_length_sq; + u = (((pos.X - start->fX()) * seg_dx) + (pos.Y - start->fY()) * seg_dy) / seg_length_sq; if (u < 0.) u = 0.; // clamp the test point to the line segment else if (u > 1.) u = 1.; - px = segment->start->fX() + (u * seg_dx); - py = segment->start->fY() + (u * seg_dy); + px = start->fX() + (u * seg_dx); + py = start->fY() + (u * seg_dy); px -= pos.X; py -= pos.Y; @@ -572,53 +572,58 @@ void ADynamicLight::CollectWithinRadius(const DVector3 &opos, FSection *section, bool hitonesidedback = false; for (unsigned i = 0; i < collected_ss.Size(); i++) { + auto &pos = collected_ss[i].pos; section = collected_ss[i].sect; touching_sector = AddLightNode(§ion->lighthead, section, this, touching_sector); + + auto processSide = [&](side_t *sidedef, vertex_t *v1, vertex_t *v2) + { + auto linedef = sidedef->linedef; + if (linedef && linedef->validcount != ::validcount) + { + // light is in front of the seg + if ((pos.Y - v1->fY()) * (v2->fX() - v1->fX()) + (v1->fX() - pos.X) * (v2->fY() - v1->fY()) <= 0) + { + linedef->validcount = ::validcount; + touching_sides = AddLightNode(&sidedef->lighthead, sidedef, this, touching_sides); + } + else if (linedef->sidedef[0] == sidedef && linedef->sidedef[1] == nullptr) + { + hitonesidedback = true; + } + } + if (linedef) + { + FLinePortal *port = linedef->getPortal(); + if (port && port->mType == PORTT_LINKED) + { + line_t *other = port->mDestination; + if (other->validcount != ::validcount) + { + subsector_t *othersub = R_PointInSubsector(other->v1->fPos() + other->Delta() / 2); + FSection *othersect = othersub->section; + if (othersect->validcount != ::validcount) + { + othersect->validcount = ::validcount; + collected_ss.Push({ othersect, PosRelative(other) }); + } + } + } + } + }; + for (auto &segment : section->segments) { - auto &pos = collected_ss[i].pos; - // check distance from x/y to seg and if within radius add this seg and, if present the opposing subsector (lather/rinse/repeat) // If out of range we do not need to bother with this seg. - if (DistToSeg(pos, &segment) <= radius) + if (DistToSeg(pos, segment.start, segment.end) <= radius) { - if (segment.sidedef) + auto sidedef = segment.sidedef; + if (sidedef) { - auto sidedef = segment.sidedef; - auto linedef = sidedef->linedef; - if (linedef && linedef->validcount != ::validcount) - { - // light is in front of the seg - if ((pos.Y - segment.start->fY()) * (segment.end->fX() - segment.start->fX()) + (segment.start->fX() - pos.X) * (segment.end->fY() - segment.start->fY()) <= 0) - { - linedef->validcount = ::validcount; - touching_sides = AddLightNode(&sidedef->lighthead, sidedef, this, touching_sides); - } - else if (linedef->sidedef[0] == sidedef && linedef->sidedef[1] == nullptr) - { - hitonesidedback = true; - } - } - if (linedef) - { - FLinePortal *port = linedef->getPortal(); - if (port && port->mType == PORTT_LINKED) - { - line_t *other = port->mDestination; - if (other->validcount != ::validcount) - { - subsector_t *othersub = R_PointInSubsector(other->v1->fPos() + other->Delta() / 2); - FSection *othersect = othersub->section; - if (othersect->validcount != ::validcount) - { - othersect->validcount = ::validcount; - collected_ss.Push({ othersect, PosRelative(other) }); - } - } - } - } + processSide(sidedef, segment.start, segment.end); } auto partner = segment.partner; @@ -633,6 +638,10 @@ void ADynamicLight::CollectWithinRadius(const DVector3 &opos, FSection *section, } } } + for (auto side : section->sides) + { + processSide(side, side->V1(), side->V2()); + } sector_t *sec = section->sector; if (!sec->PortalBlocksSight(sector_t::ceiling)) { diff --git a/src/g_shared/a_dynlight.h b/src/g_shared/a_dynlight.h index d28b787b1b..369b826e31 100644 --- a/src/g_shared/a_dynlight.h +++ b/src/g_shared/a_dynlight.h @@ -210,7 +210,7 @@ public: FLightNode * touching_sector; private: - double DistToSeg(const DVector3 &pos, FSectionLine *seg); + double DistToSeg(const DVector3 &pos, vertex_t *start, vertex_t *end); void CollectWithinRadius(const DVector3 &pos, FSection *section, float radius); protected: diff --git a/src/hwrenderer/data/hw_sections.cpp b/src/hwrenderer/data/hw_sections.cpp index c43a22eeb3..3ef6502c6a 100644 --- a/src/hwrenderer/data/hw_sections.cpp +++ b/src/hwrenderer/data/hw_sections.cpp @@ -658,7 +658,9 @@ public: } for (auto side : section.originalSides) { - group.sideMap[side->Index()] = true; + // This is only used for attaching lights to those sidedefs which are not part of the outline. + if (side->linedef && side->linedef->sidedef[1] && side->linedef->sidedef[0]->sector == side->linedef->sidedef[1]->sector) + group.sideMap[side->Index()] = true; } } numsides += group.sideMap.CountUsed(); From df52a71475af281fc8fdaa520ff5bf43838220e0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 6 Nov 2018 01:01:33 +0100 Subject: [PATCH 21/39] - fixed validcount. --- src/g_shared/a_dynlight.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/g_shared/a_dynlight.cpp b/src/g_shared/a_dynlight.cpp index 8f5c3ec1a0..50e1d1991a 100644 --- a/src/g_shared/a_dynlight.cpp +++ b/src/g_shared/a_dynlight.cpp @@ -707,6 +707,7 @@ void ADynamicLight::LinkLight() FSection *sect = R_PointInSubsector(Pos())->section; dl_validcount++; + validcount++; CollectWithinRadius(Pos(), sect, float(radius*radius)); } From aee47d23bdd421ed0f9bab45aa646d683078a5d8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 6 Nov 2018 11:53:03 +0100 Subject: [PATCH 22/39] - fixed validcount for real and added a side check for intra-section sides to light code. --- src/g_shared/a_dynlight.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/g_shared/a_dynlight.cpp b/src/g_shared/a_dynlight.cpp index 50e1d1991a..a7aa405cd9 100644 --- a/src/g_shared/a_dynlight.cpp +++ b/src/g_shared/a_dynlight.cpp @@ -578,7 +578,7 @@ void ADynamicLight::CollectWithinRadius(const DVector3 &opos, FSection *section, touching_sector = AddLightNode(§ion->lighthead, section, this, touching_sector); - auto processSide = [&](side_t *sidedef, vertex_t *v1, vertex_t *v2) + auto processSide = [&](side_t *sidedef, const vertex_t *v1, const vertex_t *v2) { auto linedef = sidedef->linedef; if (linedef && linedef->validcount != ::validcount) @@ -640,7 +640,11 @@ void ADynamicLight::CollectWithinRadius(const DVector3 &opos, FSection *section, } for (auto side : section->sides) { - processSide(side, side->V1(), side->V2()); + auto v1 = side->V1(), v2 = side->V2(); + if (DistToSeg(pos, v1, v2) <= radius) + { + processSide(side, v1, v2); + } } sector_t *sec = section->sector; if (!sec->PortalBlocksSight(sector_t::ceiling)) @@ -707,7 +711,7 @@ void ADynamicLight::LinkLight() FSection *sect = R_PointInSubsector(Pos())->section; dl_validcount++; - validcount++; + ::validcount++; CollectWithinRadius(Pos(), sect, float(radius*radius)); } From a6e77ae0944d47d71d00952b635c90eeb80bdd5c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 6 Nov 2018 18:20:59 +0100 Subject: [PATCH 23/39] Refactored the render hack storage so that it can be decoupled from the regular GLFlat render items. Having these in there makes it impossible to change render techniques so these are better done as separate items. --- src/hwrenderer/scene/hw_drawinfo.cpp | 27 +--- src/hwrenderer/scene/hw_drawinfo.h | 36 +---- src/hwrenderer/scene/hw_drawstructs.h | 6 +- src/hwrenderer/scene/hw_flats.cpp | 202 +++++++++++++++--------- src/hwrenderer/scene/hw_renderhacks.cpp | 85 +++++----- 5 files changed, 174 insertions(+), 182 deletions(-) diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index 128b1d8fa5..0dd67634e0 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -56,18 +56,6 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool bac // //========================================================================== -template -inline void DeleteLinkedList(T *node) -{ - while (node) - { - auto n = node; - node = node->next; - delete n; - } -} - - class FDrawInfoList { public: @@ -188,17 +176,10 @@ HWDrawInfo *HWDrawInfo::EndDrawInfo() void HWDrawInfo::ClearBuffers() { - for (auto node : otherfloorplanes) DeleteLinkedList(node); - otherfloorplanes.Clear(); - - for (auto node : otherceilingplanes) DeleteLinkedList(node); - otherceilingplanes.Clear(); - - for (auto node : floodfloorsegs) DeleteLinkedList(node); - floodfloorsegs.Clear(); - - for (auto node : floodceilingsegs) DeleteLinkedList(node); - floodceilingsegs.Clear(); + otherFloorPlanes.Clear(); + otherCeilingPlanes.Clear(); + floodFloorSegs.Clear(); + floodCeilingSegs.Clear(); // clear all the lists that might not have been cleared already MissingUpperTextures.Clear(); diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 537c2a2027..e40c361ef6 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -68,6 +68,8 @@ enum SectorRenderFlags SSRF_RENDERALL = 7, SSRF_PROCESSED = 8, SSRF_SEEN = 16, + SSRF_PLANEHACK = 32, + SSRF_FLOODHACK = 64 }; enum EPortalClip @@ -160,10 +162,10 @@ struct HWDrawInfo TArray SubsectorHacks; - TArray otherfloorplanes; - TArray otherceilingplanes; - TArray floodfloorsegs; - TArray floodceilingsegs; + TMap otherFloorPlanes; + TMap otherCeilingPlanes; + TMap floodFloorSegs; + TMap floodCeilingSegs; TArray CeilingStacks; TArray FloorStacks; @@ -212,32 +214,6 @@ private: void DrawPSprite(HUDSprite *huds, FRenderState &state); public: - gl_subsectorrendernode * GetOtherFloorPlanes(unsigned int sector) - { - if (sectorvertexindex, section->vertexcount); flatvertices += section->vertexcount; flatprimitives++; - - -#if 0 - //Temporarily disabled until the render hack code can be redone and refactored into its own draw elements - if (!(renderflags&SSRF_RENDER3DPLANES)) - { - // Draw the subsectors assigned to it due to missing textures - gl_subsectorrendernode * node = (renderflags&SSRF_RENDERFLOOR) ? - di->GetOtherFloorPlanes(sector->sectornum) : - di->GetOtherCeilingPlanes(sector->sectornum); - - while (node) - { - state.SetLightIndex(node->lightindex); - auto num = node->sub->numlines; - flatvertices += num; - flatprimitives++; - state.Draw(DT_TriangleFan,node->vertexindex, num); - node = node->next; - } - // Flood gaps with the back side's ceiling/floor texture - // This requires a stencil because the projected plane interferes with - // the depth buffer - gl_floodrendernode * fnode = (renderflags&SSRF_RENDERFLOOR) ? - di->GetFloodFloorSegs(sector->sectornum) : - di->GetFloodCeilingSegs(sector->sectornum); - - state.SetLightIndex(dynlightindex); - while (fnode) - { - flatvertices += 12; - flatprimitives += 3; - - // Push bleeding floor/ceiling textures back a little in the z-buffer - // so they don't interfere with overlapping mid textures. - state.SetDepthBias(1, 128); - - // Create stencil - state.SetEffect(EFF_STENCIL); - state.EnableTexture(false); - state.SetStencil(0, SOP_Increment, SF_ColorMaskOff); - state.Draw(DT_TriangleFan,fnode->vertexindex, 4); - - // Draw projected plane into stencil - state.EnableTexture(true); - state.SetEffect(EFF_NONE); - state.SetStencil(1, SOP_Keep, SF_DepthMaskOff); - state.EnableDepthTest(false); - state.Draw(DT_TriangleFan,fnode->vertexindex + 4, 4); - - // clear stencil - state.SetEffect(EFF_STENCIL); - state.EnableTexture(false); - state.SetStencil(1, SOP_Decrement, SF_ColorMaskOff | SF_DepthMaskOff); - state.Draw(DT_TriangleFan,fnode->vertexindex, 4); - - // restore old stencil op. - state.EnableTexture(true); - state.EnableDepthTest(true); - state.SetEffect(EFF_NONE); - state.SetDepthBias(0, 0); - state.SetStencil(0, SOP_Keep, SF_AllOn); - - fnode = fnode->next; - } - - } -#endif } + +//========================================================================== +// +// Drawer for render hacks +// +//========================================================================== + +void GLFlat::DrawOtherPlanes(HWDrawInfo *di, FRenderState &state) +{ + state.SetMaterial(gltexture, CLAMP_XY, 0, -1); + + // Draw the subsectors assigned to it due to missing textures + auto pNode = (renderflags&SSRF_RENDERFLOOR) ? + di->otherFloorPlanes.CheckKey(sector->sectornum) : di->otherCeilingPlanes.CheckKey(sector->sectornum); + + if (!pNode) return; + auto node = *pNode; + + while (node) + { + state.SetLightIndex(node->lightindex); + auto num = node->sub->numlines; + flatvertices += num; + flatprimitives++; + state.Draw(DT_TriangleFan,node->vertexindex, num); + node = node->next; + } +} + +//========================================================================== +// +// Drawer for render hacks +// +//========================================================================== + +void GLFlat::DrawFloodPlanes(HWDrawInfo *di, FRenderState &state) +{ + // Flood gaps with the back side's ceiling/floor texture + // This requires a stencil because the projected plane interferes with + // the depth buffer + + state.SetMaterial(gltexture, CLAMP_XY, 0, -1); + + // Draw the subsectors assigned to it due to missing textures + auto pNode = (renderflags&SSRF_RENDERFLOOR) ? + di->floodFloorSegs.CheckKey(sector->sectornum) : di->floodCeilingSegs.CheckKey(sector->sectornum); + + if (!pNode) return; + auto node = *pNode; + + while (node) + { + + auto pNode = (renderflags&SSRF_RENDERFLOOR) ? + di->floodFloorSegs.CheckKey(sector->sectornum) : di->floodCeilingSegs.CheckKey(sector->sectornum); + if (!pNode) return; + + auto fnode = *pNode; + + state.SetLightIndex(-1); + while (fnode) + { + flatvertices += 12; + flatprimitives += 3; + + // Push bleeding floor/ceiling textures back a little in the z-buffer + // so they don't interfere with overlapping mid textures. + state.SetDepthBias(1, 128); + + // Create stencil + state.SetEffect(EFF_STENCIL); + state.EnableTexture(false); + state.SetStencil(0, SOP_Increment, SF_ColorMaskOff); + state.Draw(DT_TriangleFan,fnode->vertexindex, 4); + + // Draw projected plane into stencil + state.EnableTexture(true); + state.SetEffect(EFF_NONE); + state.SetStencil(1, SOP_Keep, SF_DepthMaskOff); + state.EnableDepthTest(false); + state.Draw(DT_TriangleFan,fnode->vertexindex + 4, 4); + + // clear stencil + state.SetEffect(EFF_STENCIL); + state.EnableTexture(false); + state.SetStencil(1, SOP_Decrement, SF_ColorMaskOff | SF_DepthMaskOff); + state.Draw(DT_TriangleFan,fnode->vertexindex, 4); + + // restore old stencil op. + state.EnableTexture(true); + state.EnableDepthTest(true); + state.SetEffect(EFF_NONE); + state.SetDepthBias(0, 0); + state.SetStencil(0, SOP_Keep, SF_AllOn); + + fnode = fnode->next; + } + + } +} + + //========================================================================== // // @@ -282,8 +316,7 @@ void GLFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent) state.SetColor(lightlevel, rel, di->isFullbrightScene(), Colormap, alpha); state.SetFog(lightlevel, rel, di->isFullbrightScene(), &Colormap, false); - if (!gltexture || !gltexture->tex->isFullbright()) - state.SetObjectColor(FlatColor | 0xff000000); + state.SetObjectColor(FlatColor | 0xff000000); if (!translucent) { @@ -294,7 +327,7 @@ void GLFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent) DrawSubsectors(di, state); state.EnableTextureMatrix(false); } - else + else if (!hacktype) { state.SetMaterial(gltexture, CLAMP_XY, 0, -1); state.SetLightIndex(dynlightindex); @@ -302,6 +335,14 @@ void GLFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent) flatvertices += 4; flatprimitives++; } + else if (hacktype & SSRF_PLANEHACK) + { + DrawOtherPlanes(di, state); + } + else if (hacktype & SSRF_FLOODHACK) + { + DrawFloodPlanes(di, state); + } state.SetObjectColor(0xffffffff); } else @@ -355,8 +396,6 @@ inline void GLFlat::PutFlat(HWDrawInfo *di, bool fog) //========================================================================== // // This draws one flat -// The passed sector does not indicate the area which is rendered. -// It is only used as source for the plane data. // The whichplane boolean indicates if the flat is a floor(false) or a ceiling(true) // //========================================================================== @@ -394,8 +433,8 @@ void GLFlat::Process(HWDrawInfo *di, sector_t * model, int whichplane, bool fog) iboindex = vert.second; } - - PutFlat(di, fog); + // For hacks this won't go into a render list. + if (hacktype == 0) PutFlat(di, fog); rendered_flats++; } @@ -438,7 +477,7 @@ void GLFlat::SetFrom3DFloor(F3DFloor *rover, bool top, bool underside) // //========================================================================== -void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) +void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which) { lightlist_t * light; FSectorPortal *port; @@ -454,6 +493,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) sector = &level.sectors[frontsector->sectornum]; extsector_t::xfloor &x = sector->e->XFloor; dynlightindex = -1; + hacktype = (which & (SSRF_PLANEHACK|SSRF_FLOODHACK)); uint8_t &srf = di->section_renderflags[level.sections.SectionIndex(section)]; const auto &vp = di->Viewpoint; @@ -465,7 +505,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) // // // - if (frontsector->floorplane.ZatPoint(vp.Pos) <= vp.Pos.Z) + if ((which & SSRF_RENDERFLOOR) && frontsector->floorplane.ZatPoint(vp.Pos) <= vp.Pos.Z) { // process the original floor first. @@ -477,10 +517,12 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) port = frontsector->ValidatePortal(sector_t::floor); if ((stack = (port != NULL))) { + /* to be redone in a less invasive manner if (port->mType == PORTS_STACKEDSECTORTHING) { di->AddFloorStack(sector); // stacked sector things require visplane merging. } + */ alpha = frontsector->GetAlpha(sector_t::floor); } else @@ -518,7 +560,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) // // // - if (frontsector->ceilingplane.ZatPoint(vp.Pos) >= vp.Pos.Z) + if ((which & SSRF_RENDERCEILING) && frontsector->ceilingplane.ZatPoint(vp.Pos) >= vp.Pos.Z) { // process the original ceiling first. @@ -530,10 +572,12 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) port = frontsector->ValidatePortal(sector_t::ceiling); if ((stack = (port != NULL))) { + /* as above for floors if (port->mType == PORTS_STACKEDSECTORTHING) { di->AddCeilingStack(sector); } + */ alpha = frontsector->GetAlpha(sector_t::ceiling); } else @@ -572,7 +616,7 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector) // stack = false; - if (x.ffloors.Size()) + if ((which & SSRF_RENDER3DPLANES) && x.ffloors.Size()) { player_t * player = players[consoleplayer].camera->player; diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index 8f3325f2d8..6701b0796d 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -41,6 +41,17 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back); +// Get the nodes from the render data allocator so we don't have to keep track of them ourselves. +static gl_subsectorrendernode *NewSubsectorRenderNode() +{ + return (gl_subsectorrendernode*)RenderDataAllocator.Alloc(sizeof(gl_subsectorrendernode)); +} + +static gl_floodrendernode *NewFloodRenderNode() +{ + return (gl_floodrendernode*)RenderDataAllocator.Alloc(sizeof(gl_floodrendernode)); +} + //========================================================================== // // light setup for render hacks. @@ -110,35 +121,24 @@ int HWDrawInfo::CreateOtherPlaneVertices(subsector_t *sub, const secplane_t *pla void HWDrawInfo::AddOtherFloorPlane(int sector, gl_subsectorrendernode * node) { - int oldcnt = otherfloorplanes.Size(); - - if (oldcnt <= sector) - { - otherfloorplanes.Resize(sector + 1); - for (int i = oldcnt; i <= sector; i++) otherfloorplanes[i] = nullptr; - } - node->next = otherfloorplanes[sector]; + auto pNode = otherFloorPlanes.CheckKey(sector); + + node->next = pNode? *pNode : nullptr; node->lightindex = SetupLightsForOtherPlane(node->sub, lightdata, &level.sectors[sector].floorplane); node->vertexindex = CreateOtherPlaneVertices(node->sub, &level.sectors[sector].floorplane); - otherfloorplanes[sector] = node; + otherFloorPlanes[sector] = node; } void HWDrawInfo::AddOtherCeilingPlane(int sector, gl_subsectorrendernode * node) { - int oldcnt = otherceilingplanes.Size(); - - if (oldcnt <= sector) - { - otherceilingplanes.Resize(sector + 1); - for (int i = oldcnt; i <= sector; i++) otherceilingplanes[i] = nullptr; - } - node->next = otherceilingplanes[sector]; + auto pNode = otherCeilingPlanes.CheckKey(sector); + + node->next = pNode? *pNode : nullptr; node->lightindex = SetupLightsForOtherPlane(node->sub, lightdata, &level.sectors[sector].ceilingplane); node->vertexindex = CreateOtherPlaneVertices(node->sub, &level.sectors[sector].ceilingplane); - otherceilingplanes[sector] = node; + otherCeilingPlanes[sector] = node; } - //========================================================================== // // Collects all sectors that might need a fake ceiling @@ -511,7 +511,7 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area) for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { - gl_subsectorrendernode * node = new gl_subsectorrendernode; + gl_subsectorrendernode * node = NewSubsectorRenderNode(); node->sub = HandledSubsectors[j]; AddOtherCeilingPlane(sec->sectornum, node); @@ -555,7 +555,7 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area) for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { - gl_subsectorrendernode * node = new gl_subsectorrendernode; + gl_subsectorrendernode * node = NewSubsectorRenderNode(); node->sub = HandledSubsectors[j]; AddOtherCeilingPlane(fakesector->sectornum, node); } @@ -583,7 +583,7 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area) for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { - gl_subsectorrendernode * node = new gl_subsectorrendernode; + gl_subsectorrendernode * node = NewSubsectorRenderNode(); node->sub = HandledSubsectors[j]; AddOtherFloorPlane(sec->sectornum, node); } @@ -626,7 +626,7 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area) for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { - gl_subsectorrendernode * node = new gl_subsectorrendernode; + gl_subsectorrendernode * node = NewSubsectorRenderNode(); node->sub = HandledSubsectors[j]; AddOtherFloorPlane(fakesector->sectornum, node); } @@ -730,19 +730,13 @@ void HWDrawInfo::PrepareUpperGap(seg_t * seg) CreateFloodStencilPoly(&ws, vertices.first); CreateFloodPoly(&ws, vertices.first+4, ws.z2, fakebsector, true); - gl_floodrendernode *node = new gl_floodrendernode; - int oldcnt = floodfloorsegs.Size(); - auto sector = fakebsector->sectornum; - if (oldcnt <= sector) - { - floodfloorsegs.Resize(sector + 1); - for (int i = oldcnt; i <= sector; i++) floodfloorsegs[i] = nullptr; - } + gl_floodrendernode *node = NewFloodRenderNode(); + auto pNode = floodFloorSegs.CheckKey(fakebsector->sectornum); - node->next = floodfloorsegs[sector]; + node->next = pNode? *pNode : nullptr; node->seg = seg; node->vertexindex = vertices.second; - floodfloorsegs[sector] = node; + floodFloorSegs[fakebsector->sectornum] = node; } @@ -794,19 +788,14 @@ void HWDrawInfo::PrepareLowerGap(seg_t * seg) CreateFloodStencilPoly(&ws, vertices.first); CreateFloodPoly(&ws, vertices.first+4, ws.z1, fakebsector, false); - gl_floodrendernode *node = new gl_floodrendernode; - int oldcnt = floodceilingsegs.Size(); - auto sector = fakebsector->sectornum; - if (oldcnt <= sector) - { - floodceilingsegs.Resize(sector + 1); - for (int i = oldcnt; i <= sector; i++) floodceilingsegs[i] = nullptr; - } + gl_floodrendernode *node = NewFloodRenderNode(); + auto pNode = floodCeilingSegs.CheckKey(fakebsector->sectornum); + + node->next = pNode? *pNode : nullptr; - node->next = floodceilingsegs[sector]; node->seg = seg; node->vertexindex = vertices.second; - floodceilingsegs[sector] = node; + floodCeilingSegs[fakebsector->sectornum] = node; } //========================================================================== @@ -1122,8 +1111,7 @@ void HWDrawInfo::HandleHackedSubsectors() { for(unsigned int j=0;jsub = HandledSubsectors[j]; AddOtherFloorPlane(sub->render_sector->sectornum, node); } @@ -1145,8 +1133,7 @@ void HWDrawInfo::HandleHackedSubsectors() { for(unsigned int j=0;jsub = HandledSubsectors[j]; AddOtherCeilingPlane(sub->render_sector->sectornum, node); } @@ -1308,7 +1295,7 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area) if (sec->GetAlpha(sector_t::ceiling) != 0 && sec->GetTexture(sector_t::ceiling) != skyflatnum) { - gl_subsectorrendernode * node = new gl_subsectorrendernode; + gl_subsectorrendernode * node = NewSubsectorRenderNode(); node->sub = sub; AddOtherCeilingPlane(sec->sectornum, node); } @@ -1353,7 +1340,7 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area) if (sec->GetAlpha(sector_t::floor) != 0 && sec->GetTexture(sector_t::floor) != skyflatnum) { - gl_subsectorrendernode * node = new gl_subsectorrendernode; + gl_subsectorrendernode * node = NewSubsectorRenderNode(); node->sub = sub; AddOtherFloorPlane(sec->sectornum, node); } From ddc75f7ba5ba18a4db4e41fc57f8dbd47b2ca2b9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 6 Nov 2018 20:31:44 +0100 Subject: [PATCH 24/39] - made the common render hacks functional again as separate render items. --- src/hwrenderer/data/hw_sections.cpp | 8 +- src/hwrenderer/data/hw_sections.h | 17 --- src/hwrenderer/scene/hw_drawinfo.cpp | 2 +- src/hwrenderer/scene/hw_drawinfo.h | 1 + src/hwrenderer/scene/hw_drawlistadd.cpp | 2 +- src/hwrenderer/scene/hw_drawstructs.h | 2 +- src/hwrenderer/scene/hw_flats.cpp | 137 +++++++++++------------- src/hwrenderer/scene/hw_renderhacks.cpp | 61 ++++++++--- src/tarray.h | 5 +- 9 files changed, 122 insertions(+), 113 deletions(-) diff --git a/src/hwrenderer/data/hw_sections.cpp b/src/hwrenderer/data/hw_sections.cpp index 3ef6502c6a..c37b37395c 100644 --- a/src/hwrenderer/data/hw_sections.cpp +++ b/src/hwrenderer/data/hw_sections.cpp @@ -632,10 +632,9 @@ public: void ConstructOutput(FSectionContainer &output) { output.allSections.Resize(groups.Size()); - output.allIndices.Resize(level.subsectors.Size() + level.sides.Size() + 2*level.sectors.Size()); - output.sectionForSidedefPtr = &output.allIndices[0]; - output.firstSectionForSectorPtr = &output.allIndices[level.sides.Size()]; - output.numberOfSectionForSectorPtr = &output.allIndices[level.sides.Size() + level.sectors.Size()]; + output.allIndices.Resize(2*level.sectors.Size()); + output.firstSectionForSectorPtr = &output.allIndices[0]; + output.numberOfSectionForSectorPtr = &output.allIndices[level.sectors.Size()]; memset(output.firstSectionForSectorPtr, -1, sizeof(int) * level.sectors.Size()); memset(output.numberOfSectionForSectorPtr, 0, sizeof(int) * level.sectors.Size()); @@ -713,7 +712,6 @@ public: while (it.NextPair(pair)) { output.allSides[numsides++] = &level.sides[pair->Key]; - output.sectionForSidedefPtr[pair->Key] = curgroup; } for (auto ssi : group.subsectors) { diff --git a/src/hwrenderer/data/hw_sections.h b/src/hwrenderer/data/hw_sections.h index 668c2ba397..96ab3c2500 100644 --- a/src/hwrenderer/data/hw_sections.h +++ b/src/hwrenderer/data/hw_sections.h @@ -102,26 +102,9 @@ public: TArray allSubsectors; TArray allIndices; - int *sectionForSidedefPtr; // also stored inside allIndices; int *firstSectionForSectorPtr; // ditto. int *numberOfSectionForSectorPtr; // ditto. - FSection *SectionForSidedef(side_t *side) - { - return SectionForSidedef(side->Index()); - } - FSection *SectionForSidedef(int sindex) - { - return sindex < 0 ? nullptr : &allSections[sectionForSidedefPtr[sindex]]; - } - int SectionNumForSidedef(side_t *side) - { - return SectionNumForSidedef(side->Index()); - } - int SectionNumForSidedef(int sindex) - { - return sindex < 0 ? -1 : sectionForSidedefPtr[sindex]; - } TArrayView SectionsForSector(sector_t *sec) { return SectionsForSector(sec->Index()); diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index 0dd67634e0..2dc9aef04a 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -441,8 +441,8 @@ void HWDrawInfo::CreateScene() HandleMissingTextures(in_area); // Missing upper/lower textures HandleHackedSubsectors(); // open sector hacks for deep water - ProcessSectorStacks(in_area); // merge visplanes of sector stacks PrepareUnhandledMissingTextures(); + DispatchRenderHacks(); screen->mLights->Unmap(); screen->mVertexData->Unmap(); diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index e40c361ef6..58951473b2 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -264,6 +264,7 @@ public: void CollectSectorStacksCeiling(subsector_t * sub, sector_t * anchor, area_t in_area); void CollectSectorStacksFloor(subsector_t * sub, sector_t * anchor, area_t in_area); + void DispatchRenderHacks(); void AddUpperMissingTexture(side_t * side, subsector_t *sub, float backheight); void AddLowerMissingTexture(side_t * side, subsector_t *sub, float backheight); void HandleMissingTextures(area_t in_area); diff --git a/src/hwrenderer/scene/hw_drawlistadd.cpp b/src/hwrenderer/scene/hw_drawlistadd.cpp index a1657b3961..0ab682bd7e 100644 --- a/src/hwrenderer/scene/hw_drawlistadd.cpp +++ b/src/hwrenderer/scene/hw_drawlistadd.cpp @@ -116,7 +116,7 @@ void HWDrawInfo::AddFlat(GLFlat *flat, bool fog) list = GLDL_PLAINFLATS; } } - else + else //if (flat->hacktype != SSRF_FLOODHACK) // The flood hack may later need different treatment but with the current setup can go into the existing render list. { bool masked = flat->gltexture->isMasked() && ((flat->renderflags&SSRF_RENDER3DPLANES) || flat->stack); list = masked ? GLDL_MASKEDFLATS : GLDL_PLAINFLATS; diff --git a/src/hwrenderer/scene/hw_drawstructs.h b/src/hwrenderer/scene/hw_drawstructs.h index dd63e5bd22..35a6d340e3 100644 --- a/src/hwrenderer/scene/hw_drawstructs.h +++ b/src/hwrenderer/scene/hw_drawstructs.h @@ -320,7 +320,7 @@ public: void PutFlat(HWDrawInfo *di, bool fog = false); void Process(HWDrawInfo *di, sector_t * model, int whichplane, bool notexture); void SetFrom3DFloor(F3DFloor *rover, bool top, bool underside); - void ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which = SSRF_RENDERALL); + void ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which = 7 /*SSRF_RENDERALL*/); // cannot use constant due to circular dependencies. void DrawSubsectors(HWDrawInfo *di, FRenderState &state); void DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent); diff --git a/src/hwrenderer/scene/hw_flats.cpp b/src/hwrenderer/scene/hw_flats.cpp index 4872c6e432..553b934bdc 100644 --- a/src/hwrenderer/scene/hw_flats.cpp +++ b/src/hwrenderer/scene/hw_flats.cpp @@ -203,7 +203,7 @@ void GLFlat::DrawSubsectors(HWDrawInfo *di, FRenderState &state) void GLFlat::DrawOtherPlanes(HWDrawInfo *di, FRenderState &state) { - state.SetMaterial(gltexture, CLAMP_XY, 0, -1); + state.SetMaterial(gltexture, CLAMP_NONE, 0, -1); // Draw the subsectors assigned to it due to missing textures auto pNode = (renderflags&SSRF_RENDERFLOOR) ? @@ -231,68 +231,58 @@ void GLFlat::DrawOtherPlanes(HWDrawInfo *di, FRenderState &state) void GLFlat::DrawFloodPlanes(HWDrawInfo *di, FRenderState &state) { - // Flood gaps with the back side's ceiling/floor texture - // This requires a stencil because the projected plane interferes with - // the depth buffer - - state.SetMaterial(gltexture, CLAMP_XY, 0, -1); - - // Draw the subsectors assigned to it due to missing textures - auto pNode = (renderflags&SSRF_RENDERFLOOR) ? - di->floodFloorSegs.CheckKey(sector->sectornum) : di->floodCeilingSegs.CheckKey(sector->sectornum); - - if (!pNode) return; - auto node = *pNode; - - while (node) - { + // Flood gaps with the back side's ceiling/floor texture + // This requires a stencil because the projected plane interferes with + // the depth buffer - auto pNode = (renderflags&SSRF_RENDERFLOOR) ? - di->floodFloorSegs.CheckKey(sector->sectornum) : di->floodCeilingSegs.CheckKey(sector->sectornum); - if (!pNode) return; - - auto fnode = *pNode; + state.SetMaterial(gltexture, CLAMP_NONE, 0, -1); + + // Draw the subsectors assigned to it due to missing textures + auto pNode = (renderflags&SSRF_RENDERFLOOR) ? + di->floodFloorSegs.CheckKey(sector->sectornum) : di->floodCeilingSegs.CheckKey(sector->sectornum); + if (!pNode) return; + + auto fnode = *pNode; + + state.SetLightIndex(-1); + while (fnode) + { + flatvertices += 12; + flatprimitives += 3; + + // Push bleeding floor/ceiling textures back a little in the z-buffer + // so they don't interfere with overlapping mid textures. + state.SetDepthBias(1, 128); + + // Create stencil + state.SetEffect(EFF_STENCIL); + state.EnableTexture(false); + state.SetStencil(0, SOP_Increment, SF_ColorMaskOff); + state.Draw(DT_TriangleFan, fnode->vertexindex, 4); + + // Draw projected plane into stencil + state.EnableTexture(true); + state.SetEffect(EFF_NONE); + state.SetStencil(1, SOP_Keep, SF_DepthMaskOff); + state.EnableDepthTest(false); + state.Draw(DT_TriangleFan, fnode->vertexindex + 4, 4); + + // clear stencil + state.SetEffect(EFF_STENCIL); + state.EnableTexture(false); + state.SetStencil(1, SOP_Decrement, SF_ColorMaskOff | SF_DepthMaskOff); + state.Draw(DT_TriangleFan, fnode->vertexindex, 4); + + // restore old stencil op. + state.EnableTexture(true); + state.EnableDepthTest(true); + state.SetEffect(EFF_NONE); + state.SetDepthBias(0, 0); + state.SetStencil(0, SOP_Keep, SF_AllOn); + + fnode = fnode->next; + } - state.SetLightIndex(-1); - while (fnode) - { - flatvertices += 12; - flatprimitives += 3; - - // Push bleeding floor/ceiling textures back a little in the z-buffer - // so they don't interfere with overlapping mid textures. - state.SetDepthBias(1, 128); - - // Create stencil - state.SetEffect(EFF_STENCIL); - state.EnableTexture(false); - state.SetStencil(0, SOP_Increment, SF_ColorMaskOff); - state.Draw(DT_TriangleFan,fnode->vertexindex, 4); - - // Draw projected plane into stencil - state.EnableTexture(true); - state.SetEffect(EFF_NONE); - state.SetStencil(1, SOP_Keep, SF_DepthMaskOff); - state.EnableDepthTest(false); - state.Draw(DT_TriangleFan,fnode->vertexindex + 4, 4); - - // clear stencil - state.SetEffect(EFF_STENCIL); - state.EnableTexture(false); - state.SetStencil(1, SOP_Decrement, SF_ColorMaskOff | SF_DepthMaskOff); - state.Draw(DT_TriangleFan,fnode->vertexindex, 4); - - // restore old stencil op. - state.EnableTexture(true); - state.EnableDepthTest(true); - state.SetEffect(EFF_NONE); - state.SetDepthBias(0, 0); - state.SetStencil(0, SOP_Keep, SF_AllOn); - - fnode = fnode->next; - } - - } } @@ -318,7 +308,15 @@ void GLFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent) state.SetFog(lightlevel, rel, di->isFullbrightScene(), &Colormap, false); state.SetObjectColor(FlatColor | 0xff000000); - if (!translucent) + if (hacktype & SSRF_PLANEHACK) + { + DrawOtherPlanes(di, state); + } + else if (hacktype & SSRF_FLOODHACK) + { + DrawFloodPlanes(di, state); + } + else if (!translucent) { if (sector->special != GLSector_Skybox) { @@ -335,14 +333,6 @@ void GLFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent) flatvertices += 4; flatprimitives++; } - else if (hacktype & SSRF_PLANEHACK) - { - DrawOtherPlanes(di, state); - } - else if (hacktype & SSRF_FLOODHACK) - { - DrawFloodPlanes(di, state); - } state.SetObjectColor(0xffffffff); } else @@ -385,7 +375,7 @@ inline void GLFlat::PutFlat(HWDrawInfo *di, bool fog) } else if (!screen->BuffersArePersistent()) { - if (level.HasDynamicLights && gltexture != nullptr) + if (level.HasDynamicLights && gltexture != nullptr && !(hacktype & (SSRF_PLANEHACK|SSRF_FLOODHACK)) ) { SetupLights(di, section->lighthead, lightdata, sector->PortalGroup); } @@ -434,7 +424,7 @@ void GLFlat::Process(HWDrawInfo *di, sector_t * model, int whichplane, bool fog) } // For hacks this won't go into a render list. - if (hacktype == 0) PutFlat(di, fog); + PutFlat(di, fog); rendered_flats++; } @@ -495,7 +485,8 @@ void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which) dynlightindex = -1; hacktype = (which & (SSRF_PLANEHACK|SSRF_FLOODHACK)); - uint8_t &srf = di->section_renderflags[level.sections.SectionIndex(section)]; + uint8_t sink; + uint8_t &srf = hacktype? sink : di->section_renderflags[level.sections.SectionIndex(section)]; const auto &vp = di->Viewpoint; // diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index 6701b0796d..82610c7926 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -41,7 +41,55 @@ sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back); +//========================================================================== +// +// Create render list entries from the data generated below +// +//========================================================================== + +void HWDrawInfo::DispatchRenderHacks() +{ + TMap::Pair *pair; + TMap::Pair *fpair; + TMap::Iterator ofi(otherFloorPlanes); + GLFlat glflat; + sector_t fakesec; + glflat.section = nullptr; + while (ofi.NextPair(pair)) + { + auto sec = hw_FakeFlat(&level.sectors[pair->Key], &fakesec, in_area, false); + glflat.ProcessSector(this, sec, SSRF_RENDERFLOOR | SSRF_PLANEHACK); + } + + TMap::Iterator oci(otherCeilingPlanes); + while (ofi.NextPair(pair)) + { + auto sec = hw_FakeFlat(&level.sectors[pair->Key], &fakesec, in_area, false); + glflat.ProcessSector(this, sec, SSRF_RENDERCEILING | SSRF_PLANEHACK); + } + + TMap::Iterator ffi(floodFloorSegs); + while (ffi.NextPair(fpair)) + { + auto sec = hw_FakeFlat(&level.sectors[fpair->Key], &fakesec, in_area, false); + glflat.ProcessSector(this, sec, SSRF_RENDERFLOOR | SSRF_FLOODHACK); + } + + TMap::Iterator fci(floodCeilingSegs); + while (fci.NextPair(fpair)) + { + auto sec = hw_FakeFlat(&level.sectors[fpair->Key], &fakesec, in_area, false); + glflat.ProcessSector(this, sec, SSRF_RENDERCEILING | SSRF_FLOODHACK); + } +} + + +//========================================================================== +// // Get the nodes from the render data allocator so we don't have to keep track of them ourselves. +// +//========================================================================== + static gl_subsectorrendernode *NewSubsectorRenderNode() { return (gl_subsectorrendernode*)RenderDataAllocator.Alloc(sizeof(gl_subsectorrendernode)); @@ -506,9 +554,6 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area) if (DoOneSectorUpper(MissingUpperTextures[i].sub, MissingUpperTextures[i].Planez, in_area)) { sector_t * sec = MissingUpperTextures[i].seg->backsector; - // The mere fact that this seg has been added to the list means that the back sector - // will be rendered so we can safely assume that it is already in the render list - for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { gl_subsectorrendernode * node = NewSubsectorRenderNode(); @@ -550,9 +595,6 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area) backsub->validcount = validcount; if (DoFakeCeilingBridge(backsub, planez, in_area)) { - // The mere fact that this seg has been added to the list means that the back sector - // will be rendered so we can safely assume that it is already in the render list - for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { gl_subsectorrendernode * node = NewSubsectorRenderNode(); @@ -578,8 +620,6 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area) if (DoOneSectorLower(MissingLowerTextures[i].sub, MissingLowerTextures[i].Planez, in_area)) { sector_t * sec = MissingLowerTextures[i].seg->backsector; - // The mere fact that this seg has been added to the list means that the back sector - // will be rendered so we can safely assume that it is already in the render list for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { @@ -621,9 +661,6 @@ void HWDrawInfo::HandleMissingTextures(area_t in_area) backsub->validcount = validcount; if (DoFakeBridge(backsub, planez, in_area)) { - // The mere fact that this seg has been added to the list means that the back sector - // will be rendered so we can safely assume that it is already in the render list - for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { gl_subsectorrendernode * node = NewSubsectorRenderNode(); @@ -840,8 +877,6 @@ void HWDrawInfo::PrepareUnhandledMissingTextures() if (seg->linedef->validcount == validcount) continue; // already done seg->linedef->validcount = validcount; - int section = level.sections.SectionNumForSidedef(seg->sidedef); - if (!(section_renderflags[section] & SSRF_RENDERFLOOR)) continue; if (seg->frontsector->GetPlaneTexZ(sector_t::floor) > Viewpoint.Pos.Z) continue; // out of sight if (seg->backsector->transdoor) continue; if (seg->backsector->GetTexture(sector_t::floor) == skyflatnum) continue; diff --git a/src/tarray.h b/src/tarray.h index 4a0d1570fc..7e8c53f069 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -536,8 +536,9 @@ public: } }; -// This is not a real dynamic array but just a wrapper around a pointer reference. -// Used for wrapping some memory allocated elsewhere into a VM compatible data structure. +// This is only used for exposing the sector's Lines array to ZScript. +// This also must be trivial so that sector_t remains trivial. +// For other uses TArrayView should be preferred. template class TStaticPointedArray From 085bf0d33f7a180eb901a78fc29c6a5db316d3dd Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 6 Nov 2018 20:53:45 +0100 Subject: [PATCH 25/39] - fixed Transfer_Heights and 3D floors. --- src/hwrenderer/data/flatvertices.cpp | 14 ++++++++------ src/hwrenderer/data/flatvertices.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/hwrenderer/data/flatvertices.cpp b/src/hwrenderer/data/flatvertices.cpp index b95e66bdf2..6ba517bdf9 100644 --- a/src/hwrenderer/data/flatvertices.cpp +++ b/src/hwrenderer/data/flatvertices.cpp @@ -185,20 +185,20 @@ int FFlatVertexBuffer::CreateIndexedSectorVertices(sector_t *sec, const secplane // //========================================================================== -int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainer &verts) +int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainers &verts) { sec->vboindex[h] = vbo_shadowdata.Size(); // First calculate the vertices for the sector itself sec->vboheight[h] = sec->GetPlaneTexZ(h); - sec->ibocount = verts.indices.Size(); - sec->iboindex[h] = CreateIndexedSectorVertices(sec, plane, floor, verts); + sec->ibocount = verts[sec->Index()].indices.Size(); + sec->iboindex[h] = CreateIndexedSectorVertices(sec, plane, floor, verts[sec->Index()]); // Next are all sectors using this one as heightsec TArray &fakes = sec->e->FakeFloor.Sectors; for (unsigned g = 0; g < fakes.Size(); g++) { sector_t *fsec = fakes[g]; - fsec->iboindex[2 + h] = CreateIndexedSectorVertices(fsec, plane, false, verts); + fsec->iboindex[2 + h] = CreateIndexedSectorVertices(fsec, plane, false, verts[fsec->Index()]); } // and finally all attached 3D floors @@ -215,7 +215,7 @@ int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplan if (dotop || dobottom) { - auto ndx = CreateIndexedSectorVertices(fsec, plane, false, verts); + auto ndx = CreateIndexedSectorVertices(fsec, plane, false, verts[fsec->Index()]); if (dotop) ffloor->top.vindex = ndx; if (dobottom) ffloor->bottom.vindex = ndx; } @@ -237,6 +237,7 @@ void FFlatVertexBuffer::CreateIndexedFlatVertices() auto verts = BuildVertices(); int i = 0; + /* for (auto &vert : verts) { Printf(PRINT_LOG, "Sector %d\n", i); @@ -253,13 +254,14 @@ void FFlatVertexBuffer::CreateIndexedFlatVertices() i++; } + */ for (int h = sector_t::floor; h <= sector_t::ceiling; h++) { for (auto &sec : level.sectors) { - CreateIndexedVertices(h, &sec, sec.GetSecPlane(h), h == sector_t::floor, verts[sec.Index()]); + CreateIndexedVertices(h, &sec, sec.GetSecPlane(h), h == sector_t::floor, verts); } } diff --git a/src/hwrenderer/data/flatvertices.h b/src/hwrenderer/data/flatvertices.h index 17458e0640..890e5580c2 100644 --- a/src/hwrenderer/data/flatvertices.h +++ b/src/hwrenderer/data/flatvertices.h @@ -118,7 +118,7 @@ public: private: int CreateIndexedSectionVertices(subsector_t *sub, const secplane_t &plane, int floor, VertexContainer &cont); int CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, VertexContainer &cont); - int CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainer &cont); + int CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainers &cont); void CreateIndexedFlatVertices(); void UpdatePlaneVertices(sector_t *sec, int plane); From f2e593f8bf358a2324bf8cc61a3de5469ac9f802 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 6 Nov 2018 21:41:16 +0100 Subject: [PATCH 26/39] - disabled the hack for fixing the original design of the portal in KDiZD's Z1M1. This portal got fixed in a later re-release of KDiZD and no other portal needs this runtime fix to my knowledge. The main problem here is that this runtime fix requires some manipulation of the render data that does not work anymore. Should other maps need this fix as well they are probably best served with a compatibility entry. --- src/hwrenderer/scene/hw_bsp.cpp | 14 -------------- src/hwrenderer/scene/hw_drawinfo.cpp | 4 ++-- src/hwrenderer/scene/hw_drawinfo.h | 5 ++--- src/hwrenderer/scene/hw_renderhacks.cpp | 6 ++++++ 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/hwrenderer/scene/hw_bsp.cpp b/src/hwrenderer/scene/hw_bsp.cpp index affca42a85..075a93d8f3 100644 --- a/src/hwrenderer/scene/hw_bsp.cpp +++ b/src/hwrenderer/scene/hw_bsp.cpp @@ -702,20 +702,6 @@ void HWDrawInfo::DoSubsector(subsector_t * sub) ss_renderflags[sub->Index()] = (sub->numlines > 2) ? SSRF_PROCESSED|SSRF_RENDERALL : SSRF_PROCESSED; if (sub->hacked & 1) AddHackedSubsector(sub); - - FSectorPortalGroup *portal; - - portal = fakesector->GetPortalGroup(sector_t::ceiling); - if (portal != nullptr) - { - AddSubsectorToPortal(portal, sub); - } - - portal = fakesector->GetPortalGroup(sector_t::floor); - if (portal != nullptr) - { - AddSubsectorToPortal(portal, sub); - } } } } diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index 2dc9aef04a..40bdb8a7a2 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -187,8 +187,8 @@ void HWDrawInfo::ClearBuffers() MissingUpperSegs.Clear(); MissingLowerSegs.Clear(); SubsectorHacks.Clear(); - CeilingStacks.Clear(); - FloorStacks.Clear(); + //CeilingStacks.Clear(); + //FloorStacks.Clear(); HandledSubsectors.Clear(); spriteindex = 0; diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 58951473b2..33074b0fd4 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -167,8 +167,8 @@ struct HWDrawInfo TMap floodFloorSegs; TMap floodCeilingSegs; - TArray CeilingStacks; - TArray FloorStacks; + //TArray CeilingStacks; + //TArray FloorStacks; TArray HandledSubsectors; @@ -300,7 +300,6 @@ public: void DrawPlayerSprites(bool hudModelStep, FRenderState &state); void ProcessLowerMinisegs(TArray &lowersegs); - void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub); void AddWall(GLWall *w); void AddMirrorSurface(GLWall *w); diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index 82610c7926..1558b4bba4 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -1180,6 +1180,11 @@ void HWDrawInfo::HandleHackedSubsectors() SubsectorHacks.Clear(); } +// This code was meant to fix the portal in KDIZD's Z1M1, but later versions of that mod do not need it anymore. +// I am not aware of other portals ever having been set up so badly as this one so it probably is not needed anymore. +// Even if needed this must be done differently because this code depends on hacking the render data in a bad way. +#if 0 + //========================================================================== // // This merges visplanes that lie inside a sector stack together @@ -1406,3 +1411,4 @@ void HWDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *ptg, subsector_t *sub) ptl->AddSubsector(sub); } +#endif From 752a64c84015708a632d642fced8dd380c479a95 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 7 Nov 2018 00:12:03 +0100 Subject: [PATCH 27/39] - fixed typo in sight checking code. --- src/p_sight.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/p_sight.cpp b/src/p_sight.cpp index d533435947..9716ad50e7 100644 --- a/src/p_sight.cpp +++ b/src/p_sight.cpp @@ -587,7 +587,7 @@ bool SightCheck::P_SightTraverseIntercepts () for (auto rover : lastsector->e->XFloor.ffloors) { - if ((rover->flags & FF_SOLID) == myseethrough || !(rover->flags & FF_EXISTS)) continue; + if ((rover->flags & FF_SEETHROUGH) == myseethrough || !(rover->flags & FF_EXISTS)) continue; if ((Flags & SF_IGNOREWATERBOUNDARY) && (rover->flags & FF_SOLID) == 0) continue; double ff_bottom = rover->bottom.plane->ZatPoint(seeingthing); @@ -637,7 +637,8 @@ bool SightCheck::P_SightPathTraverse () for(auto rover : lastsector->e->XFloor.ffloors) { if(!(rover->flags & FF_EXISTS)) continue; - + if ((Flags & SF_IGNOREWATERBOUNDARY) && (rover->flags & FF_SOLID) == 0) continue; + double ff_bottom=rover->bottom.plane->ZatPoint(sightstart); double ff_top=rover->top.plane->ZatPoint(sightstart); From d85e5afdfbb3c51a9bb6ca3afcbe29d6ac292d41 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 4 Nov 2018 06:53:37 +0200 Subject: [PATCH 28/39] Destructible geometry - minor fixes and 3D floor support --- src/actor.h | 1 + src/namedef.h | 2 + src/p_3dfloors.cpp | 10 +- src/p_acs.cpp | 5 + src/p_destructible.cpp | 254 ++++++++++++++++-- src/p_destructible.h | 5 +- src/p_lnspec.cpp | 6 + src/p_map.cpp | 4 + src/p_maputl.cpp | 1 + src/p_maputl.h | 2 + src/p_mobj.cpp | 36 +-- src/p_saveg.cpp | 1 + src/p_udmf.cpp | 8 + src/r_defs.h | 4 + wadsrc/static/mapinfo/common.txt | 2 + wadsrc/static/zscript/actor.txt | 1 + wadsrc/static/zscript/shared/sectoraction.txt | 28 +- 17 files changed, 317 insertions(+), 53 deletions(-) diff --git a/src/actor.h b/src/actor.h index 0dd1035821..248847e5b7 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1188,6 +1188,7 @@ public: AActor *BlockingMobj; // Actor that blocked the last move line_t *BlockingLine; // Line that blocked the last move + sector_t *Blocking3DFloor; // 3D floor that blocked the last move (if any) sector_t *BlockingCeiling; // Sector that blocked the last move (ceiling plane slope) sector_t *BlockingFloor; // Sector that blocked the last move (floor plane slope) diff --git a/src/namedef.h b/src/namedef.h index c66890551c..ecac82b56a 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -670,10 +670,12 @@ xx(Owner) xx(HealthFloor) xx(HealthCeiling) +xx(Health3D) xx(DamageSpecial) xx(DeathSpecial) xx(HealthFloorGroup) xx(HealthCeilingGroup) +xx(Health3DGroup) xx(HealthGroup) // USDF keywords diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index 47506bfc0d..4d9984eaaa 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -358,6 +358,7 @@ bool P_CheckFor3DFloorHit(AActor * mo, double z, bool trigger) if (fabs(z - rover->top.plane->ZatPoint(mo)) < EQUAL_EPSILON) { mo->BlockingFloor = rover->model; + mo->Blocking3DFloor = rover->model; if (trigger) rover->model->TriggerSectorActions (mo, SECSPAC_HitFloor); return true; } @@ -385,6 +386,7 @@ bool P_CheckFor3DCeilingHit(AActor * mo, double z, bool trigger) if(fabs(z - rover->bottom.plane->ZatPoint(mo)) < EQUAL_EPSILON) { mo->BlockingCeiling = rover->model; + mo->Blocking3DFloor = rover->model; if (trigger) rover->model->TriggerSectorActions (mo, SECSPAC_HitCeiling); return true; } @@ -762,8 +764,10 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li FTextureID highestfloorpic; int highestfloorterrain = -1; FTextureID lowestceilingpic; - sector_t *lowestceilingsec = NULL, *highestfloorsec = NULL; + sector_t *lowestceilingsec = nullptr, *highestfloorsec = nullptr; secplane_t *highestfloorplanes[2] = { &open.frontfloorplane, &open.backfloorplane }; + F3DFloor *lowestceilingffloor = nullptr; + F3DFloor *highestfloorffloor = nullptr; highestfloorpic.SetInvalid(); lowestceilingpic.SetInvalid(); @@ -788,6 +792,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li lowestceiling = ff_bottom; lowestceilingpic = *rover->bottom.texture; lowestceilingsec = j == 0 ? linedef->frontsector : linedef->backsector; + lowestceilingffloor = rover; } if(delta1 <= delta2 && (!restrict || thing->Z() >= ff_top)) @@ -798,6 +803,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li highestfloorpic = *rover->top.texture; highestfloorterrain = rover->model->GetTerrain(rover->top.isceiling); highestfloorsec = j == 0 ? linedef->frontsector : linedef->backsector; + highestfloorffloor = rover; } if (ff_top > highestfloorplanes[j]->ZatPoint(x, y)) { @@ -814,6 +820,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li open.floorpic = highestfloorpic; open.floorterrain = highestfloorterrain; open.bottomsec = highestfloorsec; + open.bottomffloor = highestfloorffloor; } if (highestfloorplanes[0] != &open.frontfloorplane) { @@ -831,6 +838,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li open.top = lowestceiling; open.ceilingpic = lowestceilingpic; open.topsec = lowestceilingsec; + open.topffloor = lowestceilingffloor; } open.lowfloor = MIN(lowestfloor[0], lowestfloor[1]); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 712cddf1be..10038cb6d2 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -6862,6 +6862,11 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return (ss->healthfloorgroup && (grp = P_GetHealthGroup(ss->healthfloorgroup))) ? grp->health : ss->healthfloor; } + else if (part == SECPART_3D) + { + return (ss->health3dgroup && (grp = P_GetHealthGroup(ss->health3dgroup))) + ? grp->health : ss->health3d; + } return 0; } diff --git a/src/p_destructible.cpp b/src/p_destructible.cpp index 039ee302cc..3d213b689f 100755 --- a/src/p_destructible.cpp +++ b/src/p_destructible.cpp @@ -37,6 +37,8 @@ void P_SetHealthGroupHealth(int group, int health) lsector->healthceiling = health; if (lsector->healthfloorgroup == group) lsector->healthfloor = health; + if (lsector->health3dgroup == group) + lsector->health3d = health; } } @@ -70,11 +72,20 @@ void P_DamageHealthGroup(FHealthGroup* grp, void* object, AActor* source, int da lsector->healthfloor = grp->health + damage; P_DamageSector(lsector, source, damage, damagetype, SECPART_Floor, position, false); } + + if (lsector->health3dgroup == group && (lsector != object || part != SECPART_3D)) + { + lsector->health3d = grp->health + damage; + P_DamageSector(lsector, source, damage, damagetype, SECPART_3D, position, false); + } } } void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool dogroups) { + if (damage < 0) damage = 0; + if (!damage) return; + line->health -= damage; if (line->health < 0) line->health = 0; @@ -100,16 +111,43 @@ void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool dogroups) { - int sectorhealth = (part == SECPART_Ceiling) ? sector->healthceiling : sector->healthfloor; - int newhealth = sectorhealth - damage; + if (damage < 0) damage = 0; + if (!damage) return; + + int* sectorhealth; + int group; + int dmg; + int dth; + switch (part) + { + case SECPART_Ceiling: + sectorhealth = §or->healthceiling; + group = sector->healthceilinggroup; + dmg = SECSPAC_DamageCeiling; + dth = SECSPAC_DeathCeiling; + break; + case SECPART_Floor: + sectorhealth = §or->healthfloor; + group = sector->healthfloorgroup; + dmg = SECSPAC_DamageFloor; + dth = SECSPAC_DeathFloor; + break; + case SECPART_3D: + sectorhealth = §or->health3d; + group = sector->health3dgroup; + dmg = SECSPAC_Damage3D; + dth = SECSPAC_Death3D; + break; + default: + return; + } + + int newhealth = *sectorhealth - damage; if (newhealth < 0) newhealth = 0; - if (part == SECPART_Ceiling) - sector->healthceiling = newhealth; - else sector->healthfloor = newhealth; + + *sectorhealth = newhealth; // callbacks here - int dmg = (part == SECPART_Ceiling) ? SECSPAC_DamageCeiling : SECSPAC_DamageFloor; - int dth = (part == SECPART_Ceiling) ? SECSPAC_DeathCeiling : SECSPAC_DeathFloor; if (sector->SecActTarget) { sector->TriggerSectorActions(source, dmg); @@ -117,7 +155,6 @@ void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagety sector->TriggerSectorActions(source, dth); } - int group = (part == SECPART_Ceiling) ? sector->healthceilinggroup : sector->healthfloorgroup; if (dogroups && group) { FHealthGroup* grp = P_GetHealthGroup(group); @@ -222,6 +259,14 @@ void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName { // [ZZ] hitscan geometry damage logic // + + // check 3d floor, but still allow the wall to take generic damage + if (trace.HitType == TRACE_HitWall && trace.Tier == TIER_FFloor) + { + if (trace.ffloor && trace.ffloor->model && trace.ffloor->model->health3d) + P_DamageSector(trace.ffloor->model, thing, damage, damageType, SECPART_3D, trace.HitPos); + } + if (trace.HitType == TRACE_HitWall && P_CheckLinedefVulnerable(trace.Line, trace.Side)) { if (trace.Tier == TIER_Lower || trace.Tier == TIER_Upper) // process back sector health if any @@ -246,6 +291,33 @@ void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName } else if (trace.HitType == TRACE_HitFloor || trace.HitType == TRACE_HitCeiling) { + // check for 3d floors. if a 3d floor was hit, it'll block any interaction with the sector planes at the same point, if present. + // i.e. if there are 3d floors at the same height as the sector's real floor/ceiling, and they blocked the shot, then it won't damage. + bool hit3dfloors = false; + sector_t* sector = trace.Sector; + for (auto f : sector->e->XFloor.ffloors) + { + if (!(f->flags & FF_EXISTS)) continue; + if (!(f->flags & FF_SOLID) || (f->flags & FF_SHOOTTHROUGH)) continue; + + if (!f->model) continue; + if (trace.HitType == TRACE_HitFloor && fabs(f->top.plane->ZatPoint(trace.HitPos.XY())-trace.HitPos.Z) <= EQUAL_EPSILON) + { + if (f->model->health3d) + P_DamageSector(f->model, thing, damage, damageType, SECPART_3D, trace.HitPos); + hit3dfloors = true; + } + else if (trace.HitType == TRACE_HitCeiling && fabs(f->bottom.plane->ZatPoint(trace.HitPos.XY())-trace.HitPos.Z) <= EQUAL_EPSILON) + { + if (f->model->health3d) + P_DamageSector(f->model, thing, damage, damageType, SECPART_3D, trace.HitPos); + hit3dfloors = true; + } + } + + if (hit3dfloors) + return; + int sectorhealth = 0; if (trace.HitType == TRACE_HitFloor && trace.Sector->healthfloor > 0 && P_CheckSectorVulnerable(trace.Sector, SECPART_Floor)) sectorhealth = trace.Sector->healthfloor; @@ -290,16 +362,23 @@ static DVector2 PGRA_ClosestPointOnLine2D(DVector2 x, DVector2 p1, DVector2 p2) return p1 + p2p1.Unit() * r; } -static void PGRA_InsertIfCloser(TMap& damageGroupPos, int group, DVector3 pt, DVector3 check, sector_t* checksector, sector_t* sector, line_t* line, int secpart) +static bool PGRA_CheckExplosionBlocked(DVector3 pt, DVector3 check, sector_t* checksector) { // simple solid geometry sight check between "check" and "pt" // expected - Trace hits nothing + check.Z += EQUAL_EPSILON; // this is so that floor under the rocket doesn't block explosion DVector3 ptVec = (pt - check); double ptDst = ptVec.Length() - 0.5; ptVec.MakeUnit(); FTraceResults res; - bool isblocked = Trace(check, checksector, ptVec, ptDst, 0, 0xFFFFFFFF, nullptr, res); - if (isblocked) return; + bool isblocked = Trace(check, checksector, ptVec, ptDst, 0, ML_BLOCKEVERYTHING, nullptr, res); + return isblocked; +} + +static void PGRA_InsertIfCloser(TMap& damageGroupPos, int group, DVector3 pt, DVector3 check, sector_t* checksector, sector_t* sector, line_t* line, int secpart) +{ + if (PGRA_CheckExplosionBlocked(pt, check, checksector)) + return; pgra_data_t* existing = damageGroupPos.CheckKey(group); // not present or distance is closer @@ -337,7 +416,7 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage DVector3 spotTo = bombspot->Pos() - srcsector->ceilingplane.Normal() * dstceiling; int grp = srcsector->healthceilinggroup; if (grp <= 0) - grp = 0x80000000 | (srcsector->sectornum & 0x7FFFFFFF); + grp = 0x80000000 | (srcsector->sectornum & 0x0FFFFFFF); PGRA_InsertIfCloser(damageGroupPos, grp, spotTo, bombspot->Pos(), srcsector, srcsector, nullptr, SECPART_Ceiling); } @@ -347,12 +426,48 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage DVector3 spotTo = bombspot->Pos() - srcsector->floorplane.Normal() * dstfloor; int grp = srcsector->healthfloorgroup; if (grp <= 0) - grp = 0x40000000 | (srcsector->sectornum & 0x7FFFFFFF); + grp = 0x40000000 | (srcsector->sectornum & 0x0FFFFFFF); PGRA_InsertIfCloser(damageGroupPos, grp, spotTo, bombspot->Pos(), srcsector, srcsector, nullptr, SECPART_Floor); } + for (auto f : srcsector->e->XFloor.ffloors) + { + if (!(f->flags & FF_EXISTS)) continue; + if (!(f->flags & FF_SOLID)) continue; + + if (!f->model || !f->model->health3d) continue; + + double ff_top = f->top.plane->ZatPoint(bombspot->Pos()); + double ff_bottom = f->bottom.plane->ZatPoint(bombspot->Pos()); + if (ff_top < ff_bottom) // ignore eldritch geometry + continue; + + int grp = f->model->health3dgroup; + if (grp <= 0) + grp = 0x20000000 | (f->model->sectornum & 0x0FFFFFFF); + + DVector3 spotTo; + + if (bombspot->Z() < ff_bottom) // use bottom plane + { + double dst = f->bottom.plane->Normal() | (bombspot->Pos() + f->bottom.plane->Normal()*f->bottom.plane->D); + spotTo = bombspot->Pos() - f->bottom.plane->Normal() * dst; + } + else if (bombspot->Z() > ff_top) // use top plane + { + double dst = f->top.plane->Normal() | (bombspot->Pos() + f->top.plane->Normal()*f->top.plane->D); + spotTo = bombspot->Pos() - f->top.plane->Normal() * dst; + } + else // explosion right inside the floor. do 100% damage + { + spotTo = bombspot->Pos(); + } + + PGRA_InsertIfCloser(damageGroupPos, grp, spotTo, bombspot->Pos(), srcsector, f->model, nullptr, SECPART_3D); + } + // enumerate all lines around - FBoundingBox bombbox(bombspot->X(), bombspot->Y(), bombdistance * 16); + FBoundingBox bombbox(bombspot->X(), bombspot->Y(), bombdistance); FBlockLinesIterator it(bombbox); line_t* ln; int vc = validcount; @@ -370,7 +485,7 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage sector_t* sector = side->sector; side_t* otherside = ln->sidedef[!sd]; sector_t* othersector = otherside ? otherside->sector : nullptr; - if (!ln->health && (!othersector || (!othersector->healthfloor && !othersector->healthceiling))) + if (!ln->health && (!othersector || (!othersector->healthfloor && !othersector->healthceiling && !othersector->e->XFloor.ffloors.Size()))) continue; // non-interactive geometry DVector2 to2d = PGRA_ClosestPointOnLine2D(bombspot->Pos().XY(), side->V1()->p, side->V2()->p); @@ -378,13 +493,15 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage double distto2d = (to2d - pos2d).Length(); double z_top1, z_top2, z_bottom1, z_bottom2; // here, z_top1 is closest to the ceiling, and z_bottom1 is closest to the floor. z_top1 = sector->ceilingplane.ZatPoint(to2d); + z_top2 = othersector ? othersector->ceilingplane.ZatPoint(to2d) : z_top1; z_bottom1 = sector->floorplane.ZatPoint(to2d); + z_bottom2 = othersector ? othersector->floorplane.ZatPoint(to2d) : z_bottom1; DVector3 to3d_fullheight(to2d.X, to2d.Y, clamp(bombspot->Z(), z_bottom1, z_top1)); if (ln->health && P_CheckLinedefVulnerable(ln, sd)) { bool cantdamage = false; - bool linefullheight = othersector && !!(ln->flags & (ML_BLOCKEVERYTHING)); + bool linefullheight = !othersector || !!(ln->flags & (ML_BLOCKEVERYTHING)); // decide specific position to affect on a line. if (!linefullheight) { @@ -410,7 +527,7 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage { PGRA_InsertIfCloser(damageGroupPos, ln->healthgroup, to3d_fullheight, bombspot->Pos(), srcsector, nullptr, ln, -1); } - else + else if (!PGRA_CheckExplosionBlocked(to3d_fullheight, bombspot->Pos(), srcsector)) { // otherwise just damage line double dst = (to3d_fullheight - bombspot->Pos()).Length(); @@ -429,29 +546,49 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage if (othersector && othersector->healthceiling && P_CheckLinedefVulnerable(ln, sd, SECPART_Ceiling)) { - z_top2 = othersector->ceilingplane.ZatPoint(to2d); if (z_top2 < z_top1) // we have front side to hit against { DVector3 to3d_upper(to2d.X, to2d.Y, clamp(bombspot->Z(), z_top2, z_top1)); int grp = othersector->healthceilinggroup; if (grp <= 0) - grp = 0x80000000 | (othersector->sectornum & 0x7FFFFFFF); + grp = 0x80000000 | (othersector->sectornum & 0x0FFFFFFF); PGRA_InsertIfCloser(damageGroupPos, grp, to3d_upper, bombspot->Pos(), srcsector, othersector, nullptr, SECPART_Ceiling); } } if (othersector && othersector->healthfloor && P_CheckLinedefVulnerable(ln, sd, SECPART_Floor)) { - z_bottom2 = othersector->floorplane.ZatPoint(to2d); if (z_bottom2 > z_bottom1) // we have front side to hit against { DVector3 to3d_lower(to2d.X, to2d.Y, clamp(bombspot->Z(), z_bottom1, z_bottom2)); int grp = othersector->healthfloorgroup; if (grp <= 0) - grp = 0x40000000 | (othersector->sectornum & 0x7FFFFFFF); + grp = 0x40000000 | (othersector->sectornum & 0x0FFFFFFF); PGRA_InsertIfCloser(damageGroupPos, grp, to3d_lower, bombspot->Pos(), srcsector, othersector, nullptr, SECPART_Floor); } } + + // check 3d floors + for (auto f : othersector->e->XFloor.ffloors) + { + if (!(f->flags & FF_EXISTS)) continue; + if (!(f->flags & FF_SOLID)) continue; + + if (!f->model || !f->model->health3d) continue; + + // 3d floors over real ceiling, or under real floor, are ignored + double z_ff_top = clamp(f->top.plane->ZatPoint(to2d), z_bottom2, z_top2); + double z_ff_bottom = clamp(f->bottom.plane->ZatPoint(to2d), z_bottom2, z_top2); + if (z_ff_top < z_ff_bottom) + continue; // also ignore eldritch geometry + + DVector3 to3d_ffloor(to2d.X, to2d.Y, clamp(bombspot->Z(), z_ff_bottom, z_ff_top)); + int grp = f->model->health3dgroup; + if (grp <= 0) + grp = 0x20000000 | (f->model->sectornum & 0x0FFFFFFF); + PGRA_InsertIfCloser(damageGroupPos, grp, to3d_ffloor, bombspot->Pos(), srcsector, f->model, nullptr, SECPART_3D); + } + } // damage health groups and sectors. @@ -487,6 +624,11 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage assert(damageGroupPair->Value.sector != nullptr); P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_Floor, pos); } + else if (grp & 0x20000000) // sector 3d + { + assert(damageGroupPair->Value.sector != nullptr); + P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_3D, pos); + } else { assert((damageGroupPair->Value.sector != nullptr) != (damageGroupPair->Value.line != nullptr)); @@ -497,6 +639,72 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage } } +//========================================================================== +// +// P_ProjectileHitLinedef +// +// Called if P_ExplodeMissile was called against a wall. +//========================================================================== + +void P_ProjectileHitLinedef(AActor* mo, line_t* line) +{ + // detect 3d floor hit + if (mo->Blocking3DFloor) + { + if (mo->Blocking3DFloor->health3d > 0) + P_DamageSector(mo->Blocking3DFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_3D, mo->Pos()); + } + + int wside = P_PointOnLineSide(mo->Pos(), line); + int oside = !wside; + side_t* otherside = line->sidedef[oside]; + // check if hit upper or lower part + if (otherside) + { + sector_t* othersector = otherside->sector; + // find closest pos from line to MO. + // this logic is so that steep slopes work correctly (value at the line is used, instead of value below the rocket actor) + DVector2 moRelPos = line->v1->p - mo->Pos().XY(); + DVector2 lineNormal = line->delta.Rotated90CW().Unit(); + double moRelDst = lineNormal | moRelPos; + DVector2 moPos = mo->Pos().XY() - lineNormal*fabs(moRelDst); + + double otherfloorz = othersector->floorplane.ZatPoint(moPos); + double otherceilingz = othersector->ceilingplane.ZatPoint(moPos); + double zbottom = mo->Pos().Z; + double ztop = mo->Pos().Z + mo->Height; + if (zbottom < (otherfloorz + EQUAL_EPSILON) && othersector->healthfloor > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Floor)) + { + P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos()); + } + if (ztop > (otherceilingz - EQUAL_EPSILON) && othersector->healthceiling > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Ceiling)) + { + P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos()); + } + } + + if (line->health > 0 && P_CheckLinedefVulnerable(line, wside)) + { + P_DamageLinedef(line, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, wside, mo->Pos()); + } +} + +void P_ProjectileHitPlane(AActor* mo, int part) +{ + // detect 3d floor hit + if (mo->Blocking3DFloor) + { + if (mo->Blocking3DFloor->health3d > 0) + P_DamageSector(mo->Blocking3DFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_3D, mo->Pos()); + return; + } + + if (part == SECPART_Floor && mo->Sector->healthfloor > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Floor)) + P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos()); + else if (part == SECPART_Ceiling && mo->Sector->healthceiling > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Ceiling)) + P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos()); +} + //========================================================================== // // P_CheckLinedefVulnerable @@ -525,6 +733,8 @@ bool P_CheckLinedefVulnerable(line_t* line, int side, int sectorpart) bool P_CheckSectorVulnerable(sector_t* sector, int part) { + if (part == SECPART_3D) + return true; FTextureID texture = sector->GetTexture((part == SECPART_Ceiling) ? sector_t::ceiling : sector_t::floor); secplane_t* plane = (part == SECPART_Ceiling) ? §or->ceilingplane : §or->floorplane; if (texture == skyflatnum) @@ -577,4 +787,4 @@ void P_SerializeHealthGroups(FSerializer& arc) arc.EndArray(); } -} \ No newline at end of file +} diff --git a/src/p_destructible.h b/src/p_destructible.h index c9c5e81aba..f1ac1a0dee 100755 --- a/src/p_destructible.h +++ b/src/p_destructible.h @@ -17,7 +17,8 @@ struct FHealthGroup enum { SECPART_Floor = 0, - SECPART_Ceiling = 1 + SECPART_Ceiling = 1, + SECPART_3D = 2 }; void P_InitHealthGroups(); @@ -32,6 +33,8 @@ void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName damageType); void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage, int bombdistance, FName damagetype, int fulldamagedistance); +void P_ProjectileHitLinedef(AActor* projectile, line_t* line); +void P_ProjectileHitPlane(AActor* projectile, int part); bool P_CheckLinedefVulnerable(line_t* line, int side, int part = -1); bool P_CheckSectorVulnerable(sector_t* sector, int part); diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 081c7f4007..2422dfe19e 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -3522,6 +3522,12 @@ FUNC(LS_Sector_SetHealth) if (sector->healthfloorgroup) P_SetHealthGroupHealth(sector->healthfloorgroup, arg2); } + else if (arg1 == SECPART_3D) + { + sector->health3d = arg2; + if (sector->health3dgroup) + P_SetHealthGroupHealth(sector->health3dgroup, arg2); + } } return true; } diff --git a/src/p_map.cpp b/src/p_map.cpp index ed4f0d0896..838612a379 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1071,6 +1071,8 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec tm.ceilingpic = open.ceilingpic; tm.ceilingline = ld; tm.thing->BlockingLine = ld; + if (open.topffloor) + tm.thing->Blocking3DFloor = open.topffloor->model; } } @@ -1086,6 +1088,8 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec tm.touchmidtex = open.touchmidtex; tm.abovemidtex = open.abovemidtex; tm.thing->BlockingLine = ld; + if (open.bottomffloor) + tm.thing->Blocking3DFloor = open.bottomffloor->model; } else if (open.bottom == tm.floorz) { diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index 008edb2108..48396b7cf7 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -242,6 +242,7 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, co open.backfloorplane.SetAtHeight(LINEOPEN_MIN, sector_t::floor); } + open.topffloor = open.bottomffloor = nullptr; // Check 3D floors if (actor != NULL) { diff --git a/src/p_maputl.h b/src/p_maputl.h index 4dfa99e0f0..23e3f3ed62 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -110,6 +110,8 @@ struct FLineOpening int floorterrain; bool touchmidtex; bool abovemidtex; + F3DFloor *topffloor; + F3DFloor *bottomffloor; }; static const double LINEOPEN_MIN = -FLT_MAX; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 67d934060c..1788161ba8 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -296,6 +296,7 @@ DEFINE_FIELD(AActor, lastbump) DEFINE_FIELD(AActor, DesignatedTeam) DEFINE_FIELD(AActor, BlockingMobj) DEFINE_FIELD(AActor, BlockingLine) +DEFINE_FIELD(AActor, Blocking3DFloor) DEFINE_FIELD(AActor, BlockingCeiling) DEFINE_FIELD(AActor, BlockingFloor) DEFINE_FIELD(AActor, PoisonDamage) @@ -481,6 +482,7 @@ void AActor::Serialize(FSerializer &arc) A("smokecounter", smokecounter) ("blockingmobj", BlockingMobj) A("blockingline", BlockingLine) + A("blocking3dfloor", Blocking3DFloor) A("blockingceiling", BlockingCeiling) A("blockingfloor", BlockingFloor) A("visibletoteam", VisibleToTeam) @@ -1947,26 +1949,7 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target, bool onsky) // [ZZ] line damage callback if (line) { - int wside = P_PointOnLineSide(mo->Pos(), line); - int oside = !wside; - side_t* otherside = line->sidedef[oside]; - // check if hit upper or lower part - if (otherside) - { - sector_t* othersector = otherside->sector; - double otherfloorz = othersector->floorplane.ZatPoint(mo->Pos()); - double otherceilingz = othersector->ceilingplane.ZatPoint(mo->Pos()); - double actualz = mo->Pos().Z; - if (actualz < otherfloorz && othersector->healthfloor > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Floor)) - P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos()); - if (actualz > otherceilingz && othersector->healthceiling > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Ceiling)) - P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos()); - } - - if (line->health > 0 && P_CheckLinedefVulnerable(line, wside)) - { - P_DamageLinedef(line, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, wside, mo->Pos()); - } + P_ProjectileHitLinedef(mo, line); } if (mo->flags3 & MF3_EXPLOCOUNT) @@ -2761,13 +2744,11 @@ explode: } if (mo->BlockingCeiling) // hit floor or ceiling while XY movement { - if (mo->BlockingCeiling->healthceiling > 0 && P_CheckSectorVulnerable(mo->BlockingCeiling, SECPART_Ceiling)) - P_DamageSector(mo->BlockingCeiling, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos()); + P_ProjectileHitPlane(mo, SECPART_Ceiling); } if (mo->BlockingFloor) { - if (mo->BlockingFloor->healthfloor > 0 && P_CheckSectorVulnerable(mo->BlockingFloor, SECPART_Floor)) - P_DamageSector(mo->BlockingFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos()); + P_ProjectileHitPlane(mo, SECPART_Floor); } P_ExplodeMissile (mo, mo->BlockingLine, BlockingMobj, onsky); return Oldfloorz; @@ -3163,8 +3144,7 @@ void P_ZMovement (AActor *mo, double oldfloorz) } P_HitFloor (mo); // hit floor: direct damage callback - if (mo->Sector->healthfloor > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Floor)) - P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos()); + P_ProjectileHitPlane(mo, SECPART_Floor); P_ExplodeMissile (mo, NULL, NULL, onsky); return; } @@ -3270,8 +3250,7 @@ void P_ZMovement (AActor *mo, double oldfloorz) else onsky = true; } // hit ceiling: direct damage callback - if (mo->Sector->healthceiling > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Ceiling)) - P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos()); + P_ProjectileHitPlane(mo, SECPART_Ceiling); P_ExplodeMissile (mo, NULL, NULL, onsky); return; } @@ -4521,6 +4500,7 @@ void AActor::Tick () BlockingMobj = nullptr; sector_t* oldBlockingCeiling = BlockingCeiling; sector_t* oldBlockingFloor = BlockingFloor; + Blocking3DFloor = nullptr; BlockingFloor = nullptr; BlockingCeiling = nullptr; double oldfloorz = P_XYMovement (this, cumm); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 93b583c584..53f4959a25 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -297,6 +297,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t ("scrolls", scroll, nul) ("healthfloor", p.healthfloor, def->healthfloor) ("healthceiling", p.healthceiling, def->healthceiling) + ("health3d", p.health3d, def->health3d) // GZDoom exclusive: .Array("reflect", p.reflect, def->reflect, 2, true) .EndObject(); diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index f8b6d2c4d9..11652abfcb 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -1792,6 +1792,10 @@ public: sec->healthceiling = CheckInt(key); break; + case NAME_Health3D: + sec->health3d = CheckInt(key); + break; + case NAME_HealthFloorGroup: sec->healthfloorgroup = CheckInt(key); break; @@ -1799,6 +1803,10 @@ public: case NAME_HealthCeilingGroup: sec->healthceilinggroup = CheckInt(key); break; + + case NAME_Health3DGroup: + sec->health3dgroup = CheckInt(key); + break; default: break; diff --git a/src/r_defs.h b/src/r_defs.h index 5163e8f832..cb394cd410 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -282,6 +282,8 @@ enum SECSPAC_DamageCeiling=1<<12, // Trigger when ceiling is damaged SECSPAC_DeathFloor = 1<<13, // Trigger when floor has 0 hp SECSPAC_DeathCeiling= 1<<14, // Trigger when ceiling has 0 hp + SECSPAC_Damage3D = 1<<15, // Trigger when controlled 3d floor is damaged + SECSPAC_Death3D = 1<<16 // Trigger when controlled 3d floor has 0 hp }; struct secplane_t @@ -1098,8 +1100,10 @@ public: // default is 0, which means no special behavior int healthfloor; int healthceiling; + int health3d; int healthfloorgroup; int healthceilinggroup; + int health3dgroup; }; diff --git a/wadsrc/static/mapinfo/common.txt b/wadsrc/static/mapinfo/common.txt index e83fc78f4d..4f24b51e86 100644 --- a/wadsrc/static/mapinfo/common.txt +++ b/wadsrc/static/mapinfo/common.txt @@ -92,6 +92,8 @@ DoomEdNums 9601 = SecActDamageCeiling 9602 = SecActDeathFloor 9603 = SecActDeathCeiling + 9604 = SecActDamage3D + 9605 = SecActDeath3D 9800 = PointLight 9801 = PointLightPulse 9802 = PointLightFlicker diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 48172b8212..3d4e74fc89 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -184,6 +184,7 @@ class Actor : Thinker native native int DesignatedTeam; native Actor BlockingMobj; native Line BlockingLine; + native Sector Blocking3DFloor; native Sector BlockingCeiling; native Sector BlockingFloor; native int PoisonDamage; diff --git a/wadsrc/static/zscript/shared/sectoraction.txt b/wadsrc/static/zscript/shared/sectoraction.txt index 6b93d9805c..8daada166d 100644 --- a/wadsrc/static/zscript/shared/sectoraction.txt +++ b/wadsrc/static/zscript/shared/sectoraction.txt @@ -18,7 +18,9 @@ class SectorAction : Actor SECSPAC_DamageFloor = 1<<11, SECSPAC_DamageCeiling = 1<<12, SECSPAC_DeathFloor = 1<<13, - SECSPAC_DeathCeiling = 1<<14 + SECSPAC_DeathCeiling = 1<<14, + SECSPAC_Damage3D = 1<<15, + SECSPAC_Death3D = 1<<16 }; default @@ -252,6 +254,30 @@ class SecActDeathCeiling : SectorAction override bool CanTrigger (Actor triggerer) { return !!special; } } +// Triggered when controlled 3d floor is damaged ---------------------------------- +class SecActDamage3D : SectorAction +{ + Default + { + Health SECSPAC_Damage3D; + } + + // [ZZ] damage is unconditional, so this as well + override bool CanTrigger (Actor triggerer) { return !!special; } +} + +// Triggered when controlled 3d floor is reduced to 0 hp ---------------------------------- +class SecActDeath3D : SectorAction +{ + Default + { + Health SECSPAC_Death3D; + } + + // [ZZ] damage is unconditional, so this as well + override bool CanTrigger (Actor triggerer) { return !!special; } +} + //========================================================================== // // Music changer. Uses the sector action class to do its job From ed3355acc641f35e99884e3250a758c32951cea2 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Mon, 5 Nov 2018 17:56:49 +0200 Subject: [PATCH 29/39] Explode bouncing projectiles if hit damageable geometry --- src/p_destructible.cpp | 67 +++++++++++++++++++++++++++++++----------- src/p_destructible.h | 4 +-- src/p_map.cpp | 12 ++++++++ src/p_mobj.cpp | 12 ++++++++ 4 files changed, 76 insertions(+), 19 deletions(-) diff --git a/src/p_destructible.cpp b/src/p_destructible.cpp index 3d213b689f..d881f67282 100755 --- a/src/p_destructible.cpp +++ b/src/p_destructible.cpp @@ -569,24 +569,27 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage } // check 3d floors - for (auto f : othersector->e->XFloor.ffloors) + if (othersector) { - if (!(f->flags & FF_EXISTS)) continue; - if (!(f->flags & FF_SOLID)) continue; + for (auto f : othersector->e->XFloor.ffloors) + { + if (!(f->flags & FF_EXISTS)) continue; + if (!(f->flags & FF_SOLID)) continue; - if (!f->model || !f->model->health3d) continue; + if (!f->model || !f->model->health3d) continue; - // 3d floors over real ceiling, or under real floor, are ignored - double z_ff_top = clamp(f->top.plane->ZatPoint(to2d), z_bottom2, z_top2); - double z_ff_bottom = clamp(f->bottom.plane->ZatPoint(to2d), z_bottom2, z_top2); - if (z_ff_top < z_ff_bottom) - continue; // also ignore eldritch geometry + // 3d floors over real ceiling, or under real floor, are ignored + double z_ff_top = clamp(f->top.plane->ZatPoint(to2d), z_bottom2, z_top2); + double z_ff_bottom = clamp(f->bottom.plane->ZatPoint(to2d), z_bottom2, z_top2); + if (z_ff_top < z_ff_bottom) + continue; // also ignore eldritch geometry - DVector3 to3d_ffloor(to2d.X, to2d.Y, clamp(bombspot->Z(), z_ff_bottom, z_ff_top)); - int grp = f->model->health3dgroup; - if (grp <= 0) - grp = 0x20000000 | (f->model->sectornum & 0x0FFFFFFF); - PGRA_InsertIfCloser(damageGroupPos, grp, to3d_ffloor, bombspot->Pos(), srcsector, f->model, nullptr, SECPART_3D); + DVector3 to3d_ffloor(to2d.X, to2d.Y, clamp(bombspot->Z(), z_ff_bottom, z_ff_top)); + int grp = f->model->health3dgroup; + if (grp <= 0) + grp = 0x20000000 | (f->model->sectornum & 0x0FFFFFFF); + PGRA_InsertIfCloser(damageGroupPos, grp, to3d_ffloor, bombspot->Pos(), srcsector, f->model, nullptr, SECPART_3D); + } } } @@ -646,13 +649,17 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage // Called if P_ExplodeMissile was called against a wall. //========================================================================== -void P_ProjectileHitLinedef(AActor* mo, line_t* line) +bool P_ProjectileHitLinedef(AActor* mo, line_t* line) { + bool washit = false; // detect 3d floor hit if (mo->Blocking3DFloor) { if (mo->Blocking3DFloor->health3d > 0) + { P_DamageSector(mo->Blocking3DFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_3D, mo->Pos()); + washit = true; + } } int wside = P_PointOnLineSide(mo->Pos(), line); @@ -676,33 +683,59 @@ void P_ProjectileHitLinedef(AActor* mo, line_t* line) if (zbottom < (otherfloorz + EQUAL_EPSILON) && othersector->healthfloor > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Floor)) { P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos()); + washit = true; } if (ztop > (otherceilingz - EQUAL_EPSILON) && othersector->healthceiling > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Ceiling)) { P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos()); + washit = true; } } if (line->health > 0 && P_CheckLinedefVulnerable(line, wside)) { P_DamageLinedef(line, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, wside, mo->Pos()); + washit = true; } + + return washit; } -void P_ProjectileHitPlane(AActor* mo, int part) +// part = -1 means "detect from blocking" +bool P_ProjectileHitPlane(AActor* mo, int part) { + if (part < 0) + { + if (mo->BlockingCeiling) + part = SECPART_Ceiling; + else if (mo->BlockingFloor) + part = SECPART_Floor; + } + // detect 3d floor hit if (mo->Blocking3DFloor) { if (mo->Blocking3DFloor->health3d > 0) + { P_DamageSector(mo->Blocking3DFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_3D, mo->Pos()); - return; + return true; + } + + return false; } if (part == SECPART_Floor && mo->Sector->healthfloor > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Floor)) + { P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos()); + return true; + } else if (part == SECPART_Ceiling && mo->Sector->healthceiling > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Ceiling)) + { P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos()); + return true; + } + + return false; } //========================================================================== diff --git a/src/p_destructible.h b/src/p_destructible.h index f1ac1a0dee..1af0886791 100755 --- a/src/p_destructible.h +++ b/src/p_destructible.h @@ -33,8 +33,8 @@ void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName damageType); void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage, int bombdistance, FName damagetype, int fulldamagedistance); -void P_ProjectileHitLinedef(AActor* projectile, line_t* line); -void P_ProjectileHitPlane(AActor* projectile, int part); +bool P_ProjectileHitLinedef(AActor* projectile, line_t* line); +bool P_ProjectileHitPlane(AActor* projectile, int part); bool P_CheckLinedefVulnerable(line_t* line, int side, int part = -1); bool P_CheckSectorVulnerable(sector_t* sector, int part); diff --git a/src/p_map.cpp b/src/p_map.cpp index 838612a379..e092ef80ff 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -3552,6 +3552,18 @@ bool FSlide::BounceWall(AActor *mo) return true; } + // [ZZ] if bouncing missile hits a damageable linedef, it dies + if (P_ProjectileHitLinedef(mo, line) && mo->bouncecount > 0) + { + mo->Vel.Zero(); + mo->Speed = 0; + mo->bouncecount = 0; + if (mo->flags & MF_MISSILE) + P_ExplodeMissile(mo, line, nullptr); + else mo->CallDie(nullptr, nullptr); + return true; + } + // The amount of bounces is limited if (mo->bouncecount>0 && --mo->bouncecount == 0) { diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 1788161ba8..8f2cf64fff 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -2127,6 +2127,18 @@ void AActor::PlayBounceSound(bool onfloor) bool AActor::FloorBounceMissile (secplane_t &plane) { + // [ZZ] if bouncing missile hits a damageable sector(plane), it dies + if (P_ProjectileHitPlane(this, -1) && bouncecount > 0) + { + Vel.Zero(); + Speed = 0; + bouncecount = 0; + if (flags & MF_MISSILE) + P_ExplodeMissile(this, nullptr, nullptr); + else CallDie(nullptr, nullptr); + return true; + } + if (Z() <= floorz && P_HitFloor (this)) { // Landed in some sort of liquid From a276ebfb085b26a85e582e519c73ff7296d795f8 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 6 Nov 2018 04:59:17 +0200 Subject: [PATCH 30/39] Exported destructible geometry to ZScript --- src/events.cpp | 71 +++++ src/events.h | 22 +- src/p_destructible.cpp | 288 ++++++++++++++++-- src/p_destructible.h | 5 +- wadsrc/static/zscript.txt | 1 + wadsrc/static/zscript/destructible.txt | 34 +++ wadsrc/static/zscript/events.txt | 12 +- wadsrc/static/zscript/mapdata.txt | 5 + .../static/zscript/shared/fastprojectile.txt | 2 + 9 files changed, 400 insertions(+), 40 deletions(-) create mode 100755 wadsrc/static/zscript/destructible.txt diff --git a/src/events.cpp b/src/events.cpp index 19034d4957..15ab919ea4 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -416,6 +416,20 @@ void E_WorldLineActivated(line_t* line, AActor* actor, int activationType) handler->WorldLineActivated(line, actor, activationType); } +int E_WorldSectorDamaged(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius) +{ + for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) + damage = handler->WorldSectorDamaged(sector, source, damage, damagetype, part, position, isradius); + return damage; +} + +int E_WorldLineDamaged(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius) +{ + for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) + damage = handler->WorldLineDamaged(line, source, damage, damagetype, side, position, isradius); + return damage; +} + void E_PlayerEntered(int num, bool fromhub) { // this event can happen during savegamerestore. make sure that local handlers don't receive it. @@ -570,6 +584,13 @@ DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageAngle); DEFINE_FIELD_X(WorldEvent, FWorldEvent, ActivatedLine); DEFINE_FIELD_X(WorldEvent, FWorldEvent, ActivationType); DEFINE_FIELD_X(WorldEvent, FWorldEvent, ShouldActivate); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageSectorPart); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageLine); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageSector); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageLineSide); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamagePosition); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageIsRadius); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, NewDamage); DEFINE_FIELD_X(PlayerEvent, FPlayerEvent, PlayerNumber); DEFINE_FIELD_X(PlayerEvent, FPlayerEvent, IsReturn); @@ -662,6 +683,8 @@ DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldThingDamaged) DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldThingDestroyed) DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLinePreActivated) DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLineActivated) +DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldSectorDamaged); +DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLineDamaged); DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldLightning) DEFINE_EMPTY_HANDLER(DStaticEventHandler, WorldTick) @@ -861,6 +884,54 @@ void DStaticEventHandler::WorldLineActivated(line_t* line, AActor* actor, int ac } } +int DStaticEventHandler::WorldSectorDamaged(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius) +{ + IFVIRTUAL(DStaticEventHandler, WorldSectorDamaged) + { + // don't create excessive DObjects if not going to be processed anyway + if (func == DStaticEventHandler_WorldSectorDamaged_VMPtr) + return damage; + FWorldEvent e = E_SetupWorldEvent(); + e.DamageSource = source; + e.DamageSector = sector; + e.NewDamage = e.Damage = damage; + e.DamageType = damagetype; + e.DamageSectorPart = part; + e.DamagePosition = position; + e.DamageIsRadius = isradius; + + VMValue params[2] = { (DStaticEventHandler*)this, &e }; + VMCall(func, params, 2, nullptr, 0); + return e.NewDamage; + } + + return damage; +} + +int DStaticEventHandler::WorldLineDamaged(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius) +{ + IFVIRTUAL(DStaticEventHandler, WorldLineDamaged) + { + // don't create excessive DObjects if not going to be processed anyway + if (func == DStaticEventHandler_WorldLineDamaged_VMPtr) + return damage; + FWorldEvent e = E_SetupWorldEvent(); + e.DamageSource = source; + e.DamageLine = line; + e.NewDamage = e.Damage = damage; + e.DamageType = damagetype; + e.DamageLineSide = side; + e.DamagePosition = position; + e.DamageIsRadius = isradius; + + VMValue params[2] = { (DStaticEventHandler*)this, &e }; + VMCall(func, params, 2, nullptr, 0); + return e.NewDamage; + } + + return damage; +} + void DStaticEventHandler::WorldLightning() { IFVIRTUAL(DStaticEventHandler, WorldLightning) diff --git a/src/events.h b/src/events.h index 392ae0680e..91cf378887 100755 --- a/src/events.h +++ b/src/events.h @@ -50,6 +50,10 @@ void E_WorldThingDestroyed(AActor* actor); void E_WorldLinePreActivated(line_t* line, AActor* actor, int activationType, bool* shouldactivate); // called in P_ActivateLine after successful special execution. void E_WorldLineActivated(line_t* line, AActor* actor, int activationType); +// called in P_DamageSector and P_DamageLinedef before receiving damage to the sector. returns actual damage +int E_WorldSectorDamaged(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius); +// called in P_DamageLinedef before receiving damage to the linedef. returns actual damage +int E_WorldLineDamaged(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius); // same as ACS SCRIPT_Lightning void E_WorldLightning(); // this executes on every tick, before everything, only when in valid level and not paused @@ -157,6 +161,8 @@ public: void WorldThingDestroyed(AActor* actor); void WorldLinePreActivated(line_t* line, AActor* actor, int activationType, bool* shouldactivate); void WorldLineActivated(line_t* line, AActor* actor, int activationType); + int WorldSectorDamaged(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius); + int WorldLineDamaged(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius); void WorldLightning(); void WorldTick(); @@ -215,14 +221,22 @@ struct FWorldEvent AActor* Thing = nullptr; // for thingdied AActor* Inflictor = nullptr; // can be null - for damagemobj AActor* DamageSource = nullptr; // can be null - int Damage = 0; - FName DamageType = NAME_None; - int DamageFlags = 0; - DAngle DamageAngle; + int Damage = 0; // thingdamaged, sector/line damaged + FName DamageType = NAME_None; // thingdamaged, sector/line damaged + int DamageFlags = 0; // thingdamaged + DAngle DamageAngle; // thingdamaged // for line(pre)activated line_t* ActivatedLine = nullptr; int ActivationType = 0; bool ShouldActivate = true; + // for line/sector damaged + int DamageSectorPart = 0; + line_t* DamageLine = nullptr; + sector_t* DamageSector = nullptr; + int DamageLineSide = -1; + DVector3 DamagePosition; + bool DamageIsRadius; // radius damage yes/no + int NewDamage = 0; // sector/line damaged. allows modifying damage }; struct FPlayerEvent diff --git a/src/p_destructible.cpp b/src/p_destructible.cpp index d881f67282..f60c98a005 100755 --- a/src/p_destructible.cpp +++ b/src/p_destructible.cpp @@ -10,15 +10,16 @@ #include "p_maputl.h" #include "c_cvars.h" #include "serializer.h" +#include "vm.h" +#include "events.h" //========================================================================== // // [ZZ] Geometry damage logic callbacks // //========================================================================== -void P_SetHealthGroupHealth(int group, int health) +void P_SetHealthGroupHealth(FHealthGroup* grp, int health) { - FHealthGroup* grp = P_GetHealthGroup(group); if (!grp) return; grp->health = health; @@ -33,16 +34,21 @@ void P_SetHealthGroupHealth(int group, int health) for (unsigned i = 0; i < grp->sectors.Size(); i++) { sector_t* lsector = grp->sectors[i]; - if (lsector->healthceilinggroup == group) + if (lsector->healthceilinggroup == grp->id) lsector->healthceiling = health; - if (lsector->healthfloorgroup == group) + if (lsector->healthfloorgroup == grp->id) lsector->healthfloor = health; - if (lsector->health3dgroup == group) + if (lsector->health3dgroup == grp->id) lsector->health3d = health; } } -void P_DamageHealthGroup(FHealthGroup* grp, void* object, AActor* source, int damage, FName damagetype, int side, int part, DVector3 position) +void P_SetHealthGroupHealth(int id, int health) +{ + P_SetHealthGroupHealth(P_GetHealthGroup(id), health); +} + +void P_DamageHealthGroup(FHealthGroup* grp, void* object, AActor* source, int damage, FName damagetype, int side, int part, DVector3 position, bool isradius) { if (!grp) return; int group = grp->id; @@ -54,7 +60,7 @@ void P_DamageHealthGroup(FHealthGroup* grp, void* object, AActor* source, int da if (lline == object) continue; lline->health = grp->health + damage; - P_DamageLinedef(lline, source, damage, damagetype, side, position, false); + P_DamageLinedef(lline, source, damage, damagetype, side, position, isradius, false); } // for (unsigned i = 0; i < grp->sectors.Size(); i++) @@ -64,26 +70,33 @@ void P_DamageHealthGroup(FHealthGroup* grp, void* object, AActor* source, int da if (lsector->healthceilinggroup == group && (lsector != object || part != SECPART_Ceiling)) { lsector->healthceiling = grp->health + damage; - P_DamageSector(lsector, source, damage, damagetype, SECPART_Ceiling, position, false); + P_DamageSector(lsector, source, damage, damagetype, SECPART_Ceiling, position, isradius, false); } if (lsector->healthfloorgroup == group && (lsector != object || part != SECPART_Floor)) { lsector->healthfloor = grp->health + damage; - P_DamageSector(lsector, source, damage, damagetype, SECPART_Floor, position, false); + P_DamageSector(lsector, source, damage, damagetype, SECPART_Floor, position, isradius, false); } if (lsector->health3dgroup == group && (lsector != object || part != SECPART_3D)) { lsector->health3d = grp->health + damage; - P_DamageSector(lsector, source, damage, damagetype, SECPART_3D, position, false); + P_DamageSector(lsector, source, damage, damagetype, SECPART_3D, position, isradius, false); } } } -void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool dogroups) +void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius, bool dogroups) { if (damage < 0) damage = 0; + + if (dogroups) + { + damage = E_WorldLineDamaged(line, source, damage, damagetype, side, position, isradius); + if (damage < 0) damage = 0; + } + if (!damage) return; line->health -= damage; @@ -103,15 +116,22 @@ void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, FHealthGroup* grp = P_GetHealthGroup(line->healthgroup); if (grp) grp->health = line->health; - P_DamageHealthGroup(grp, line, source, damage, damagetype, side, -1, position); + P_DamageHealthGroup(grp, line, source, damage, damagetype, side, -1, position, isradius); } //Printf("P_DamageLinedef: %d damage (type=%s, source=%p), new health = %d\n", damage, damagetype.GetChars(), source, line->health); } -void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool dogroups) +void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius, bool dogroups) { if (damage < 0) damage = 0; + + if (dogroups) + { + damage = E_WorldSectorDamaged(sector, source, damage, damagetype, part, position, isradius); + if (damage < 0) damage = 0; + } + if (!damage) return; int* sectorhealth; @@ -160,7 +180,7 @@ void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagety FHealthGroup* grp = P_GetHealthGroup(group); if (grp) grp->health = newhealth; - P_DamageHealthGroup(grp, sector, source, damage, damagetype, 0, part, position); + P_DamageHealthGroup(grp, sector, source, damage, damagetype, 0, part, position, isradius); } //Printf("P_DamageSector: %d damage (type=%s, position=%s, source=%p), new health = %d\n", damage, damagetype.GetChars(), (part == SECPART_Ceiling) ? "ceiling" : "floor", source, newhealth); @@ -264,7 +284,7 @@ void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName if (trace.HitType == TRACE_HitWall && trace.Tier == TIER_FFloor) { if (trace.ffloor && trace.ffloor->model && trace.ffloor->model->health3d) - P_DamageSector(trace.ffloor->model, thing, damage, damageType, SECPART_3D, trace.HitPos); + P_DamageSector(trace.ffloor->model, thing, damage, damageType, SECPART_3D, trace.HitPos, false, true); } if (trace.HitType == TRACE_HitWall && P_CheckLinedefVulnerable(trace.Line, trace.Side)) @@ -279,13 +299,13 @@ void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName sectorhealth = backsector->healthceiling; if (sectorhealth > 0) { - P_DamageSector(backsector, thing, damage, damageType, (trace.Tier == TIER_Upper) ? SECPART_Ceiling : SECPART_Floor, trace.HitPos); + P_DamageSector(backsector, thing, damage, damageType, (trace.Tier == TIER_Upper) ? SECPART_Ceiling : SECPART_Floor, trace.HitPos, false, true); } } // always process linedef health if any if (trace.Line->health > 0) { - P_DamageLinedef(trace.Line, thing, damage, damageType, trace.Side, trace.HitPos); + P_DamageLinedef(trace.Line, thing, damage, damageType, trace.Side, trace.HitPos, false, true); } // fake floors are not handled } @@ -304,13 +324,13 @@ void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName if (trace.HitType == TRACE_HitFloor && fabs(f->top.plane->ZatPoint(trace.HitPos.XY())-trace.HitPos.Z) <= EQUAL_EPSILON) { if (f->model->health3d) - P_DamageSector(f->model, thing, damage, damageType, SECPART_3D, trace.HitPos); + P_DamageSector(f->model, thing, damage, damageType, SECPART_3D, trace.HitPos, false, true); hit3dfloors = true; } else if (trace.HitType == TRACE_HitCeiling && fabs(f->bottom.plane->ZatPoint(trace.HitPos.XY())-trace.HitPos.Z) <= EQUAL_EPSILON) { if (f->model->health3d) - P_DamageSector(f->model, thing, damage, damageType, SECPART_3D, trace.HitPos); + P_DamageSector(f->model, thing, damage, damageType, SECPART_3D, trace.HitPos, false, true); hit3dfloors = true; } } @@ -325,7 +345,7 @@ void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName sectorhealth = trace.Sector->healthceiling; if (sectorhealth > 0) { - P_DamageSector(trace.Sector, thing, damage, damageType, (trace.HitType == TRACE_HitCeiling) ? SECPART_Ceiling : SECPART_Floor, trace.HitPos); + P_DamageSector(trace.Sector, thing, damage, damageType, (trace.HitType == TRACE_HitCeiling) ? SECPART_Ceiling : SECPART_Floor, trace.HitPos, false, true); } } } @@ -539,7 +559,7 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage if (bombsource == bombspot) damage = (int)(damage * splashfactor); } - P_DamageLinedef(ln, bombsource, damage, damagetype, sd, to3d_fullheight); + P_DamageLinedef(ln, bombsource, damage, damagetype, sd, to3d_fullheight, true, true); } } } @@ -620,24 +640,24 @@ void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage if (grp & 0x80000000) // sector ceiling { assert(damageGroupPair->Value.sector != nullptr); - P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_Ceiling, pos); + P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_Ceiling, pos, true, true); } else if (grp & 0x40000000) // sector floor { assert(damageGroupPair->Value.sector != nullptr); - P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_Floor, pos); + P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_Floor, pos, true, true); } else if (grp & 0x20000000) // sector 3d { assert(damageGroupPair->Value.sector != nullptr); - P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_3D, pos); + P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, SECPART_3D, pos, true, true); } else { assert((damageGroupPair->Value.sector != nullptr) != (damageGroupPair->Value.line != nullptr)); if (damageGroupPair->Value.line != nullptr) - P_DamageLinedef(damageGroupPair->Value.line, bombsource, damage, damagetype, P_PointOnLineSide(pos.XY(), damageGroupPair->Value.line), pos); - else P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, damageGroupPair->Value.secpart, pos); + P_DamageLinedef(damageGroupPair->Value.line, bombsource, damage, damagetype, P_PointOnLineSide(pos.XY(), damageGroupPair->Value.line), pos, true, true); + else P_DamageSector(damageGroupPair->Value.sector, bombsource, damage, damagetype, damageGroupPair->Value.secpart, pos, true, true); } } } @@ -657,7 +677,7 @@ bool P_ProjectileHitLinedef(AActor* mo, line_t* line) { if (mo->Blocking3DFloor->health3d > 0) { - P_DamageSector(mo->Blocking3DFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_3D, mo->Pos()); + P_DamageSector(mo->Blocking3DFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_3D, mo->Pos(), false, true); washit = true; } } @@ -682,19 +702,19 @@ bool P_ProjectileHitLinedef(AActor* mo, line_t* line) double ztop = mo->Pos().Z + mo->Height; if (zbottom < (otherfloorz + EQUAL_EPSILON) && othersector->healthfloor > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Floor)) { - P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos()); + P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos(), false, true); washit = true; } if (ztop > (otherceilingz - EQUAL_EPSILON) && othersector->healthceiling > 0 && P_CheckLinedefVulnerable(line, wside, SECPART_Ceiling)) { - P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos()); + P_DamageSector(othersector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos(), false, true); washit = true; } } if (line->health > 0 && P_CheckLinedefVulnerable(line, wside)) { - P_DamageLinedef(line, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, wside, mo->Pos()); + P_DamageLinedef(line, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, wside, mo->Pos(), false, true); washit = true; } @@ -717,7 +737,7 @@ bool P_ProjectileHitPlane(AActor* mo, int part) { if (mo->Blocking3DFloor->health3d > 0) { - P_DamageSector(mo->Blocking3DFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_3D, mo->Pos()); + P_DamageSector(mo->Blocking3DFloor, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_3D, mo->Pos(), false, true); return true; } @@ -726,12 +746,12 @@ bool P_ProjectileHitPlane(AActor* mo, int part) if (part == SECPART_Floor && mo->Sector->healthfloor > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Floor)) { - P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos()); + P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Floor, mo->Pos(), false, true); return true; } else if (part == SECPART_Ceiling && mo->Sector->healthceiling > 0 && P_CheckSectorVulnerable(mo->Sector, SECPART_Ceiling)) { - P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos()); + P_DamageSector(mo->Sector, mo, mo->GetMissileDamage((mo->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1), mo->DamageType, SECPART_Ceiling, mo->Pos(), false, true); return true; } @@ -821,3 +841,205 @@ void P_SerializeHealthGroups(FSerializer& arc) arc.EndArray(); } } + +// ===================== zscript interface ===================== +// +// ============================================================= + +DEFINE_FIELD_X(HealthGroup, FHealthGroup, id) +DEFINE_FIELD_X(HealthGroup, FHealthGroup, health) +DEFINE_FIELD_X(HealthGroup, FHealthGroup, sectors) +DEFINE_FIELD_X(HealthGroup, FHealthGroup, lines) + +DEFINE_ACTION_FUNCTION(FHealthGroup, Find) +{ + PARAM_PROLOGUE; + PARAM_INT(id); + FHealthGroup* grp = P_GetHealthGroup(id); + ACTION_RETURN_POINTER(grp); +} + +DEFINE_ACTION_FUNCTION(FHealthGroup, SetHealth) +{ + PARAM_SELF_STRUCT_PROLOGUE(FHealthGroup); + PARAM_INT(health); + P_SetHealthGroupHealth(self, health); + return 0; +} + +// genuine hack. this essentially causes the engine to register a struct called Destructible, and enables use of DEFINE_ACTION_FUNCTION +struct FDestructible { void* none; }; +DEFINE_FIELD_X(Destructible, FDestructible, none); + +DEFINE_ACTION_FUNCTION(FDestructible, DamageSector) +{ + PARAM_PROLOGUE; + PARAM_POINTER(sec, sector_t); + PARAM_OBJECT(source, AActor); + PARAM_INT(damage); + PARAM_NAME(damagetype); + PARAM_INT(part); + PARAM_FLOAT(position_x); + PARAM_FLOAT(position_y); + PARAM_FLOAT(position_z); + PARAM_BOOL(isradius); + P_DamageSector(sec, source, damage, damagetype, part, DVector3(position_x, position_y, position_z), isradius, true); + return 0; +} + +DEFINE_ACTION_FUNCTION(FDestructible, DamageLinedef) +{ + PARAM_PROLOGUE; + PARAM_POINTER(def, line_t); + PARAM_OBJECT(source, AActor); + PARAM_INT(damage); + PARAM_NAME(damagetype); + PARAM_INT(side); + PARAM_FLOAT(position_x); + PARAM_FLOAT(position_y); + PARAM_FLOAT(position_z); + PARAM_BOOL(isradius); + P_DamageLinedef(def, source, damage, damagetype, side, DVector3(position_x, position_y, position_z), isradius, true); + return 0; +} + +DEFINE_ACTION_FUNCTION(FDestructible, GeometryLineAttack) +{ + PARAM_PROLOGUE; + PARAM_POINTER(trace, FTraceResults); + PARAM_OBJECT(thing, AActor); + PARAM_INT(damage); + PARAM_NAME(damagetype); + P_GeometryLineAttack(*trace, thing, damage, damagetype); + return 0; +} + +DEFINE_ACTION_FUNCTION(FDestructible, GeometryRadiusAttack) +{ + PARAM_PROLOGUE; + PARAM_OBJECT(bombspot, AActor); + PARAM_OBJECT(bombsource, AActor); + PARAM_INT(bombdamage); + PARAM_INT(bombdistance); + PARAM_NAME(damagetype); + PARAM_INT(fulldamagedistance); + P_GeometryRadiusAttack(bombspot, bombsource, bombdamage, bombdistance, damagetype, fulldamagedistance); + return 0; +} + +DEFINE_ACTION_FUNCTION(FDestructible, ProjectileHitLinedef) +{ + PARAM_PROLOGUE; + PARAM_OBJECT(projectile, AActor); + PARAM_POINTER(def, line_t); + ACTION_RETURN_BOOL(P_ProjectileHitLinedef(projectile, def)); +} + +DEFINE_ACTION_FUNCTION(FDestructible, ProjectileHitPlane) +{ + PARAM_PROLOGUE; + PARAM_OBJECT(projectile, AActor); + PARAM_INT(part); + ACTION_RETURN_BOOL(P_ProjectileHitPlane(projectile, part)); +} + +DEFINE_ACTION_FUNCTION(FDestructible, CheckLinedefVulnerable) +{ + PARAM_PROLOGUE; + PARAM_POINTER(def, line_t); + PARAM_INT(side); + PARAM_INT(part); + ACTION_RETURN_BOOL(P_CheckLinedefVulnerable(def, side, part)); +} + +DEFINE_ACTION_FUNCTION(FDestructible, CheckSectorVulnerable) +{ + PARAM_PROLOGUE; + PARAM_POINTER(sec, sector_t); + PARAM_INT(part); + ACTION_RETURN_BOOL(P_CheckSectorVulnerable(sec, part)); +} + +DEFINE_ACTION_FUNCTION(_Line, GetHealth) +{ + PARAM_SELF_STRUCT_PROLOGUE(line_t); + if (self->healthgroup) + { + FHealthGroup* grp = P_GetHealthGroup(self->healthgroup); + if (grp) ACTION_RETURN_INT(grp->health); + } + + ACTION_RETURN_INT(self->health); +} + +DEFINE_ACTION_FUNCTION(_Line, SetHealth) +{ + PARAM_SELF_STRUCT_PROLOGUE(line_t); + PARAM_INT(newhealth); + + if (newhealth < 0) + newhealth = 0; + + self->health = newhealth; + if (self->healthgroup) + { + FHealthGroup* grp = P_GetHealthGroup(self->healthgroup); + if (grp) P_SetHealthGroupHealth(grp, newhealth); + } + + return 0; +} + +DEFINE_ACTION_FUNCTION(_Sector, GetHealth) +{ + PARAM_SELF_STRUCT_PROLOGUE(sector_t); + PARAM_INT(part); + + FHealthGroup* grp; + switch (part) + { + case SECPART_Floor: + ACTION_RETURN_INT((self->healthfloorgroup && (grp = P_GetHealthGroup(self->healthfloorgroup))) ? grp->health : self->healthfloor); + case SECPART_Ceiling: + ACTION_RETURN_INT((self->healthceilinggroup && (grp = P_GetHealthGroup(self->healthceilinggroup))) ? grp->health : self->healthceiling); + case SECPART_3D: + ACTION_RETURN_INT((self->health3dgroup && (grp = P_GetHealthGroup(self->health3dgroup))) ? grp->health : self->health3d); + default: + ACTION_RETURN_INT(0); + } +} + +DEFINE_ACTION_FUNCTION(_Sector, SetHealth) +{ + PARAM_SELF_STRUCT_PROLOGUE(sector_t); + PARAM_INT(part); + PARAM_INT(newhealth); + + if (newhealth < 0) + newhealth = 0; + + int group; + int* health; + switch (part) + { + case SECPART_Floor: + group = self->healthfloorgroup; + health = &self->healthfloor; + break; + case SECPART_Ceiling: + group = self->healthceilinggroup; + health = &self->healthceiling; + break; + case SECPART_3D: + group = self->health3dgroup; + health = &self->health3d; + break; + default: + return 0; + } + + FHealthGroup* grp = group ? P_GetHealthGroup(group) : nullptr; + *health = newhealth; + if (grp) P_SetHealthGroupHealth(grp, newhealth); + return 0; +} \ No newline at end of file diff --git a/src/p_destructible.h b/src/p_destructible.h index 1af0886791..99c14b9271 100755 --- a/src/p_destructible.h +++ b/src/p_destructible.h @@ -23,13 +23,14 @@ enum void P_InitHealthGroups(); +void P_SetHealthGroupHealth(FHealthGroup* group, int health); void P_SetHealthGroupHealth(int group, int health); FHealthGroup* P_GetHealthGroup(int id); FHealthGroup* P_GetHealthGroupOrNew(int id, int startinghealth); -void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool dogroups = true); -void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool dogroups = true); +void P_DamageSector(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius, bool dogroups); +void P_DamageLinedef(line_t* line, AActor* source, int damage, FName damagetype, int side, DVector3 position, bool isradius, bool dogroups); void P_GeometryLineAttack(FTraceResults& trace, AActor* thing, int damage, FName damageType); void P_GeometryRadiusAttack(AActor* bombspot, AActor* bombsource, int bombdamage, int bombdistance, FName damagetype, int fulldamagedistance); diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 449bfe054c..41b3851412 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -7,6 +7,7 @@ version "3.7" #include "zscript/actor.txt" #include "zscript/actor_checks.txt" #include "zscript/events.txt" +#include "zscript/destructible.txt" #include "zscript/level_compatibility.txt" #include "zscript/menu/menuitembase.txt" diff --git a/wadsrc/static/zscript/destructible.txt b/wadsrc/static/zscript/destructible.txt new file mode 100755 index 0000000000..3ea0b4f2b4 --- /dev/null +++ b/wadsrc/static/zscript/destructible.txt @@ -0,0 +1,34 @@ +struct HealthGroup native play +{ + static clearscope native HealthGroup Find(int id); + + readonly int id; + readonly int health; + readonly Array sectors; + readonly Array lines; + + native void SetHealth(int newhealth); +} + +enum SectorPart +{ + SECPART_None = -1, + SECPART_Floor = 0, + SECPART_Ceiling = 1, + SECPART_3D = 2 +} + +struct Destructible native play +{ + + static native void DamageSector(Sector sec, Actor source, int damage, Name damagetype, SectorPart part, vector3 position, bool isradius); + static native void DamageLinedef(Line def, Actor source, int damage, Name damagetype, int side, vector3 position, bool isradius); + + static native void GeometryLineAttack(TraceResults trace, Actor thing, int damage, Name damagetype); + static native void GeometryRadiusAttack(Actor bombspot, Actor bombsource, int bombdamage, int bombdistance, Name damagetype, int fulldamagedistance); + static native bool ProjectileHitLinedef(Actor projectile, Line def); + static native bool ProjectileHitPlane(Actor projectile, SectorPart part); + + static clearscope native bool CheckLinedefVulnerable(Line def, int side, SectorPart part); + static clearscope native bool CheckSectorVulnerable(Sector sec, SectorPart part); +} \ No newline at end of file diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index d2d8ed3240..354e981cfe 100755 --- a/wadsrc/static/zscript/events.txt +++ b/wadsrc/static/zscript/events.txt @@ -19,7 +19,7 @@ struct WorldEvent native play version("2.4") native readonly Actor Thing; // for thingdied. can be null native readonly Actor Inflictor; - // for thingdamaged. + // for thingdamaged, line/sector damaged native readonly int Damage; native readonly Actor DamageSource; native readonly Name DamageType; @@ -29,6 +29,14 @@ struct WorldEvent native play version("2.4") native readonly Line ActivatedLine; native readonly int ActivationType; native bool ShouldActivate; + // for line/sector damaged + native readonly SectorPart DamageSectorPart; + native readonly Line DamageLine; + native readonly Sector DamageSector; + native readonly int DamageLineSide; + native readonly vector3 DamagePosition; + native readonly bool DamageIsRadius; + native int NewDamage; } struct PlayerEvent native play version("2.4") @@ -313,6 +321,8 @@ class StaticEventHandler : Object native play version("2.4") virtual native void WorldThingDestroyed(WorldEvent e); virtual native void WorldLinePreActivated(WorldEvent e); virtual native void WorldLineActivated(WorldEvent e); + virtual native void WorldSectorDamaged(WorldEvent e); + virtual native void WorldLineDamaged(WorldEvent e); virtual native void WorldLightning(WorldEvent e); // for the sake of completeness. virtual native void WorldTick(); diff --git a/wadsrc/static/zscript/mapdata.txt b/wadsrc/static/zscript/mapdata.txt index 9dc42da227..549513b4ac 100644 --- a/wadsrc/static/zscript/mapdata.txt +++ b/wadsrc/static/zscript/mapdata.txt @@ -180,6 +180,9 @@ struct Line native play { return Level.GetUDMFString(LevelLocals.UDMF_Line, Index(), nm); } + + native clearscope int GetHealth(); + native void SetHealth(int newhealth); } struct SecPlane native play @@ -479,6 +482,8 @@ struct Sector native play return Level.GetUDMFString(LevelLocals.UDMF_Sector, Index(), nm); } + native clearscope int GetHealth(SectorPart part); + native void SetHealth(SectorPart part, int newhealth); } class SectorTagIterator : Object native diff --git a/wadsrc/static/zscript/shared/fastprojectile.txt b/wadsrc/static/zscript/shared/fastprojectile.txt index 4ba05a7bf4..af01bca6f8 100644 --- a/wadsrc/static/zscript/shared/fastprojectile.txt +++ b/wadsrc/static/zscript/shared/fastprojectile.txt @@ -148,6 +148,7 @@ class FastProjectile : Actor SetZ(floorz); HitFloor (); + Destructible.ProjectileHitPlane(self, SECPART_Floor); ExplodeMissile (NULL, NULL); return; } @@ -161,6 +162,7 @@ class FastProjectile : Actor } SetZ(ceilingz - Height); + Destructible.ProjectileHitPlane(self, SECPART_Ceiling); ExplodeMissile (NULL, NULL); return; } From bad2a7c49b1b6ed3f12ccff6517989751703cede Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 7 Nov 2018 00:43:11 +0100 Subject: [PATCH 31/39] - silenced debug message in standard mode. --- src/r_data/renderinfo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/r_data/renderinfo.cpp b/src/r_data/renderinfo.cpp index bf59ec0c0a..88526d8244 100644 --- a/src/r_data/renderinfo.cpp +++ b/src/r_data/renderinfo.cpp @@ -758,7 +758,7 @@ void FixHoles() // Add the new data. This doesn't care about convexity. It is never directly used to generate a primitive. for (auto &segloop : segloops) { - Printf("Adding dummy subsector for sector %d\n", segloop[0]->Subsector->render_sector->Index()); + DPrintf(DMSG_NOTIFY, "Adding dummy subsector for sector %d\n", segloop[0]->Subsector->render_sector->Index()); subsector_t &sub = level.subsectors[newssstart++]; memset(&sub, 0, sizeof(sub)); @@ -770,7 +770,7 @@ void FixHoles() for (auto otherseg : segloop) { - Printf(" Adding seg from (%2.3f, %2.3f) -> (%2.3f, %2.3f)\n", otherseg->v2->fX(), otherseg->v2->fY(), otherseg->v1->fX(), otherseg->v1->fY()); + DPrintf(DMSG_NOTIFY, " Adding seg from (%2.3f, %2.3f) -> (%2.3f, %2.3f)\n", otherseg->v2->fX(), otherseg->v2->fY(), otherseg->v1->fX(), otherseg->v1->fY()); seg_t &seg = level.segs[newsegstart++]; memset(&seg, 0, sizeof(seg)); seg.v1 = otherseg->v2; From 9661c3b53c3c01f905a8a6fadb7182954b2216a0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 7 Nov 2018 00:53:44 +0100 Subject: [PATCH 32/39] - moved hw_Sections to r_data, because this is an essential component of the dynamic light system now so it is needed for all renderers. --- src/CMakeLists.txt | 2 +- src/g_levellocals.h | 2 +- src/{hwrenderer/data/hw_sections.cpp => r_data/r_sections.cpp} | 3 +-- src/{hwrenderer/data/hw_sections.h => r_data/r_sections.h} | 0 4 files changed, 3 insertions(+), 4 deletions(-) rename src/{hwrenderer/data/hw_sections.cpp => r_data/r_sections.cpp} (99%) rename src/{hwrenderer/data/hw_sections.h => r_data/r_sections.h} (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da9b92dda1..ffc8aa4415 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1050,7 +1050,6 @@ set (PCH_SOURCES gl/textures/gl_hwtexture.cpp gl/textures/gl_samplers.cpp hwrenderer/data/hw_vertexbuilder.cpp - hwrenderer/data/hw_sections.cpp hwrenderer/data/flatvertices.cpp hwrenderer/data/hw_viewpointbuffer.cpp hwrenderer/dynlights/hw_aabbtree.cpp @@ -1143,6 +1142,7 @@ set (PCH_SOURCES r_data/renderstyle.cpp r_data/r_interpolate.cpp r_data/r_vanillatrans.cpp + r_data/r_sections.cpp r_data/models/models_md3.cpp r_data/models/models_md2.cpp r_data/models/models_voxel.cpp diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 6a42c12c5d..1e3aed96f7 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -42,7 +42,7 @@ #include "p_blockmap.h" #include "p_local.h" #include "p_destructible.h" -#include "hwrenderer/data/hw_sections.h" +#include "r_data/r_sections.h" struct FLevelLocals { diff --git a/src/hwrenderer/data/hw_sections.cpp b/src/r_data/r_sections.cpp similarity index 99% rename from src/hwrenderer/data/hw_sections.cpp rename to src/r_data/r_sections.cpp index c37b37395c..fcc273d337 100644 --- a/src/hwrenderer/data/hw_sections.cpp +++ b/src/r_data/r_sections.cpp @@ -28,13 +28,12 @@ #include "c_dispatch.h" #include "r_defs.h" #include "g_levellocals.h" -#include "hw_sections.h" +#include "r_sections.h" #include "earcut.hpp" #include "stats.h" #include "p_setup.h" #include "c_dispatch.h" #include "memarena.h" -#include "flatvertices.h" using DoublePoint = std::pair; diff --git a/src/hwrenderer/data/hw_sections.h b/src/r_data/r_sections.h similarity index 100% rename from src/hwrenderer/data/hw_sections.h rename to src/r_data/r_sections.h From bfffe6df3ef041e4673b46aa51588b0199c0ef9d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 7 Nov 2018 19:20:42 +0100 Subject: [PATCH 33/39] - fixed typo with RNG name. --- wadsrc/static/zscript/doom/possessed.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/zscript/doom/possessed.txt b/wadsrc/static/zscript/doom/possessed.txt index 51505c80bc..0e059792e6 100644 --- a/wadsrc/static/zscript/doom/possessed.txt +++ b/wadsrc/static/zscript/doom/possessed.txt @@ -323,7 +323,7 @@ extend class Actor A_PlaySound(AttackSound, CHAN_WEAPON); A_FaceTarget(); double slope = AimLineAttack(angle, MISSILERANGE); - double ang = angle + Random2[SPosAttack]() * (22.5/256); + double ang = angle + Random2[CPosAttack]() * (22.5/256); int damage = Random[CPosAttack](1, 5) * 3; LineAttack(ang, MISSILERANGE, slope, damage, "Hitscan", "Bulletpuff"); } From 4eecaada67f406efac584acb25e629145bb9f415 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 7 Nov 2018 19:27:35 +0100 Subject: [PATCH 34/39] - added copyright header to p_destructible.cpp --- src/p_destructible.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/p_destructible.cpp b/src/p_destructible.cpp index f60c98a005..e0a28f35a6 100755 --- a/src/p_destructible.cpp +++ b/src/p_destructible.cpp @@ -1,3 +1,35 @@ +/* +** +** +**--------------------------------------------------------------------------- +** Copyright 2018 ZZYZX +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ #include "p_spec.h" #include "g_levellocals.h" #include "p_destructible.h" From 9ff7f338fd9bcb288f6beeb6af9ca4ece757587f Mon Sep 17 00:00:00 2001 From: Major Cooke Date: Wed, 7 Nov 2018 14:03:32 -0600 Subject: [PATCH 35/39] Added IsPointInMap(Vector3 p). - Checks if a point is inside the map geometry or not. --- src/g_level.cpp | 44 +++++++++++++++++++++++++++++++++- wadsrc/static/zscript/base.txt | 2 ++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index 05c41614ab..e62b560b6d 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -85,6 +85,7 @@ #include "g_levellocals.h" #include "actorinlines.h" #include "i_time.h" +#include "nodebuild.h" void STAT_StartNewGame(const char *lev); void STAT_ChangeLevel(const char *newl); @@ -2004,10 +2005,51 @@ void FLevelLocals::SetMusicVolume(float f) } //========================================================================== +// IsPointInMap // -// +// Checks to see if a point is inside the void or not. +// Made by dpJudas, modified and implemented by Major Cooke //========================================================================== + +bool IsPointInMap(DVector3 p) +{ + subsector_t *subsector = R_PointInSubsector(FLOAT2FIXED(p.X), FLOAT2FIXED(p.Y)); + if (!subsector) return false; + + for (uint32_t i = 0; i < subsector->numlines; i++) + { + // Skip single sided lines. + seg_t *seg = subsector->firstline + i; + if (seg->backsector != nullptr) continue; + + int sx = (int)seg->v1->fX(); + int sy = (int)seg->v1->fY(); + int dx = (int)seg->v2->fX() - sx; + int dy = (int)seg->v2->fY() - sy; + int res = FNodeBuilder::PointOnSide(sx, sy, (int)p.X, (int)p.Y, dx, dy); + bool pointOnSide = (res > 0); + if (!pointOnSide) return false; + } + + double ceilingZ = subsector->sector->ceilingplane.ZatPoint(p.X, p.Y); + if (p.Z > ceilingZ) return false; + + double floorZ = subsector->sector->floorplane.ZatPoint(p.X, p.Y); + if (p.Z < floorZ) return false; + + return true; +} + +DEFINE_ACTION_FUNCTION(FLevelLocals, IsPointInMap) +{ + PARAM_PROLOGUE; + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(z); + ACTION_RETURN_BOOL(IsPointInMap(DVector3(x,y,z))); +} + template inline T VecDiff(const T& v1, const T& v2) { diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index fa7a8182d5..2576ea9b20 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -670,6 +670,8 @@ struct LevelLocals native native bool IsCrouchingAllowed() const; native bool IsFreelookAllowed() const; + native static clearscope bool IsPointInMap(vector3 p); + native static clearscope vector2 Vec2Diff(vector2 v1, vector2 v2); native static clearscope vector3 Vec3Diff(vector3 v1, vector3 v2); native static clearscope vector3 SphericalCoords(vector3 viewpoint, vector3 targetPos, vector2 viewAngles = (0, 0), bool absolute = false); From ed72843dece85b1ec3852ce4a94ad1f5d44351b1 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Wed, 7 Nov 2018 23:04:42 +0200 Subject: [PATCH 36/39] - fixed undefined behavior when grouping sections The current group should not be accessed by reference because its container can be reallocated during iteration https://forum.zdoom.org/viewtopic.php?t=62487 --- src/r_data/r_sections.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_data/r_sections.cpp b/src/r_data/r_sections.cpp index fcc273d337..9186a13a13 100644 --- a/src/r_data/r_sections.cpp +++ b/src/r_data/r_sections.cpp @@ -565,7 +565,7 @@ public: // Don't use iterators here. These arrays are modified inside. for (unsigned j = 0; j < build.Size(); j++) { - auto ¤t = build[j]; + auto current = build[j]; for (int i = 0; i < (int)workingSet.Size(); i++) { // Are both sections close together? From cdd60b143188ad11a8638d0137d6a76d1057b3fc Mon Sep 17 00:00:00 2001 From: Major Cooke Date: Thu, 8 Nov 2018 07:26:42 -0600 Subject: [PATCH 37/39] Changed IsPointInMap to use P_PointOnDivlineSide. --- src/g_level.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index e62b560b6d..c51508f042 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -85,7 +85,7 @@ #include "g_levellocals.h" #include "actorinlines.h" #include "i_time.h" -#include "nodebuild.h" +#include "p_maputl.h" void STAT_StartNewGame(const char *lev); void STAT_ChangeLevel(const char *newl); @@ -2022,14 +2022,11 @@ bool IsPointInMap(DVector3 p) // Skip single sided lines. seg_t *seg = subsector->firstline + i; if (seg->backsector != nullptr) continue; - - int sx = (int)seg->v1->fX(); - int sy = (int)seg->v1->fY(); - int dx = (int)seg->v2->fX() - sx; - int dy = (int)seg->v2->fY() - sy; - int res = FNodeBuilder::PointOnSide(sx, sy, (int)p.X, (int)p.Y, dx, dy); - bool pointOnSide = (res > 0); - if (!pointOnSide) return false; + + divline_t dline; + P_MakeDivline(seg->linedef, &dline); + bool pol = P_PointOnDivlineSide(p.XY(), &dline) < 1; + if (!pol) return false; } double ceilingZ = subsector->sector->ceilingplane.ZatPoint(p.X, p.Y); From f6af50fc748c9dcb88d80d6cf7d1968892056402 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 8 Nov 2018 20:39:44 +0100 Subject: [PATCH 38/39] - restored portal code that shouldn't have been deleted. --- src/hwrenderer/scene/hw_bsp.cpp | 15 +++++++++++++++ src/hwrenderer/scene/hw_drawinfo.cpp | 19 +++++++++++++++++++ src/hwrenderer/scene/hw_drawinfo.h | 1 + src/hwrenderer/scene/hw_renderhacks.cpp | 18 ------------------ 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/hwrenderer/scene/hw_bsp.cpp b/src/hwrenderer/scene/hw_bsp.cpp index 075a93d8f3..31a3694ee9 100644 --- a/src/hwrenderer/scene/hw_bsp.cpp +++ b/src/hwrenderer/scene/hw_bsp.cpp @@ -702,6 +702,21 @@ void HWDrawInfo::DoSubsector(subsector_t * sub) ss_renderflags[sub->Index()] = (sub->numlines > 2) ? SSRF_PROCESSED|SSRF_RENDERALL : SSRF_PROCESSED; if (sub->hacked & 1) AddHackedSubsector(sub); + + // This is for portal coverage. + FSectorPortalGroup *portal; + + portal = fakesector->GetPortalGroup(sector_t::ceiling); + if (portal != nullptr) + { + AddSubsectorToPortal(portal, sub); + } + + portal = fakesector->GetPortalGroup(sector_t::floor); + if (portal != nullptr) + { + AddSubsectorToPortal(portal, sub); + } } } } diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index 40bdb8a7a2..803b70c121 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -655,3 +655,22 @@ void HWDrawInfo::ProcessScene(bool toscreen, const std::functionmPortalState, new HWSectorStackPortal(ptg)); + Portals.Push(portal); + } + auto ptl = static_cast(static_cast(portal)->mScene); + ptl->AddSubsector(sub); +} + diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index 33074b0fd4..6fc0e212f9 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -300,6 +300,7 @@ public: void DrawPlayerSprites(bool hudModelStep, FRenderState &state); void ProcessLowerMinisegs(TArray &lowersegs); + void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub); void AddWall(GLWall *w); void AddMirrorSurface(GLWall *w); diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index 1558b4bba4..5592a03ab3 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -1393,22 +1393,4 @@ void HWDrawInfo::ProcessSectorStacks(area_t in_area) CeilingStacks.Clear(); } -//========================================================================== -// -// -// -//========================================================================== - -void HWDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *ptg, subsector_t *sub) -{ - auto portal = FindPortal(ptg); - if (!portal) - { - portal = new HWScenePortal(screen->mPortalState, new HWSectorStackPortal(ptg)); - Portals.Push(portal); - } - auto ptl = static_cast(static_cast(portal)->mScene); - ptl->AddSubsector(sub); -} - #endif From 0caabbe355bcb412ea730a48d8e2947c6685dfeb Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 9 Nov 2018 19:06:54 +0100 Subject: [PATCH 39/39] - clear spechit before leaving P_CheckPosition. Otherwise this may contain residual data from the last call. One can only hope that this doesn't cause other side effects - this entire code is one horrendous mess of bad ideas. --- src/p_map.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index e092ef80ff..148e1fd180 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1809,6 +1809,10 @@ bool P_CheckPosition(AActor *thing, const DVector2 &pos, FCheckPosition &tm, boo tm.abovemidtex = false; validcount++; + // Remove all old entries before returning. + spechit.Clear(); + portalhit.Clear(); + if ((thing->flags & MF_NOCLIP) && !(thing->flags & MF_SKULLFLY)) return true; @@ -1886,13 +1890,15 @@ bool P_CheckPosition(AActor *thing, const DVector2 &pos, FCheckPosition &tm, boo // being considered for collision with the player. validcount++; + // Clear out any residual garbage left behind by PIT_CheckThing induced recursions etc. + spechit.Clear(); + portalhit.Clear(); + thing->BlockingMobj = NULL; thing->Height = realHeight; if (actorsonly || (thing->flags & MF_NOCLIP)) return (thing->BlockingMobj = thingblocker) == NULL; - spechit.Clear(); - portalhit.Clear(); FMultiBlockLinesIterator it(pcheck, pos.X, pos.Y, thing->Z(), thing->Height, thing->radius, newsec); FMultiBlockLinesIterator::CheckResult lcres;