diff --git a/src/common/2d/v_2ddrawer.cpp b/src/common/2d/v_2ddrawer.cpp index d774d12b4..46d02a2af 100644 --- a/src/common/2d/v_2ddrawer.cpp +++ b/src/common/2d/v_2ddrawer.cpp @@ -189,8 +189,9 @@ DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, PushTriangle, Shape2D_PushTriangle) // //========================================================================== -int F2DDrawer::AddCommand(const RenderCommand *data) +int F2DDrawer::AddCommand(RenderCommand *data) { + data->mScreenFade = screenFade; if (mData.Size() > 0 && data->isCompatible(mData.Last())) { // Merge with the last command. @@ -738,12 +739,12 @@ void F2DDrawer::AddPoly(FGameTexture *texture, FVector2 *points, int npoints, // //========================================================================== -void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, unsigned int* ind, size_t idxcount, int translation, PalEntry color, FRenderStyle style, int clipx1, int clipy1, int clipx2, int clipy2) +void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, const unsigned int* ind, size_t idxcount, int translation, PalEntry color, FRenderStyle style, int clipx1, int clipy1, int clipx2, int clipy2) { RenderCommand dg; int method = 0; - if (!img->isValid()) return; + if (!img || !img->isValid()) return; dg.mType = DrawTypeTriangles; if (clipx1 > 0 || clipy1 > 0 || clipx2 < GetWidth() - 1 || clipy2 < GetHeight() - 1) @@ -769,14 +770,28 @@ void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, unsigne Set(ptr, vt[i].X, vt[i].Y, 0.f, vt[i].Z, vt[i].W, color); ptr++; } - dg.mIndexIndex = mIndices.Size(); - mIndices.Reserve(idxcount); - for (size_t i = 0; i < idxcount; i++) + + if (idxcount > 0) { - mIndices[dg.mIndexIndex + i] = ind[i] + dg.mVertIndex; + mIndices.Reserve(idxcount); + for (size_t i = 0; i < idxcount; i++) + { + mIndices[dg.mIndexIndex + i] = ind[i] + dg.mVertIndex; + } + dg.mIndexCount = (int)idxcount; + } + else + { + // If we have no index buffer, treat this as an unindexed list of triangles. + mIndices.Reserve(vtcount); + for (size_t i = 0; i < vtcount; i++) + { + mIndices[dg.mIndexIndex + i] = i + dg.mVertIndex; + } + dg.mIndexCount = (int)vtcount; + } - dg.mIndexCount = (int)idxcount; AddCommand(&dg); } @@ -941,6 +956,7 @@ void F2DDrawer::AddColorOnlyQuad(int x1, int y1, int w, int h, PalEntry color, F { // Only needed by Raze's fullscreen blends because they are being calculated late when half of the 2D content has already been submitted, // This ensures they are below the HUD, not above it. + dg.mScreenFade = screenFade; mData.Insert(0, dg); } } diff --git a/src/common/2d/v_2ddrawer.h b/src/common/2d/v_2ddrawer.h index d1c2a161e..feac2e4d7 100644 --- a/src/common/2d/v_2ddrawer.h +++ b/src/common/2d/v_2ddrawer.h @@ -118,6 +118,7 @@ public: ETexMode mDrawMode; uint8_t mLightLevel; uint8_t mFlags; + float mScreenFade; bool useTransform; DMatrix3x3 transform; @@ -149,6 +150,7 @@ public: mLightLevel == other.mLightLevel && mColor1.d == other.mColor1.d && useTransform == other.useTransform && + mScreenFade == other.mScreenFade && ( !useTransform || ( @@ -172,7 +174,7 @@ public: int fullscreenautoaspect = 3; int cliptop = -1, clipleft = -1, clipwidth = -1, clipheight = -1; - int AddCommand(const RenderCommand *data); + int AddCommand(RenderCommand *data); void AddIndices(int firstvert, int count, ...); private: void AddIndices(int firstvert, TArray &v); @@ -187,7 +189,7 @@ public: void AddPoly(FGameTexture *texture, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, DAngle rotation, const FColormap &colormap, PalEntry flatcolor, double lightlevel, uint32_t *indices, size_t indexcount); - void AddPoly(FGameTexture* img, FVector4 *vt, size_t vtcount, unsigned int *ind, size_t idxcount, int translation, PalEntry color, FRenderStyle style, int clipx1, int clipy1, int clipx2, int clipy2); + void AddPoly(FGameTexture* img, FVector4 *vt, size_t vtcount, const unsigned int *ind, size_t idxcount, int translation, PalEntry color, FRenderStyle style, int clipx1, int clipy1, int clipx2, int clipy2); void FillPolygon(int* rx1, int* ry1, int* xb1, int32_t npoints, int picnum, int palette, int shade, int props, const FVector2& xtex, const FVector2& ytex, const FVector2& otex, int clipx1, int clipy1, int clipx2, int clipy2); void AddFlatFill(int left, int top, int right, int bottom, FGameTexture *src, int local_origin = false, double flatscale = 1.0, PalEntry color = 0xffffffff, ERenderStyle rs = STYLE_Normal); diff --git a/src/common/audio/music/music.cpp b/src/common/audio/music/music.cpp index 1437ea83f..c1c8290f6 100644 --- a/src/common/audio/music/music.cpp +++ b/src/common/audio/music/music.cpp @@ -82,8 +82,11 @@ static MusicCallbacks mus_cb = { nullptr, DefaultOpenMusic }; // PUBLIC DATA DEFINITIONS ------------------------------------------------- EXTERN_CVAR(Int, snd_mididevice) +EXTERN_CVAR(Float, mod_dumb_mastervolume) +EXTERN_CVAR(Float, fluid_gain) -CVAR(Bool, mus_calcgain, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // changing this will only take effect for the next song. + +CVAR(Bool, mus_calcgain, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // changing this will only take effect for the next song. CVAR(Bool, mus_usereplaygain, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // changing this will only take effect for the next song. CUSTOM_CVAR(Float, mus_gainoffset, 0.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // for customizing the base volume { @@ -107,13 +110,18 @@ void S_SetMusicCallbacks(MusicCallbacks* cb) //========================================================================== static std::unique_ptr musicStream; +static TArray customStreams; SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels, StreamCallback cb, void *userdata) { int flags = 0; if (numchannels < 2) flags |= SoundStream::Mono; auto stream = GSnd->CreateStream(cb, int(size), flags, samplerate, userdata); - if (stream) stream->Play(true, 1); + if (stream) + { + stream->Play(true, 1); + customStreams.Push(stream); + } return stream; } @@ -122,11 +130,19 @@ void S_StopCustomStream(SoundStream *stream) if (stream) { stream->Stop(); + auto f = customStreams.Find(stream); + if (f < customStreams.Size()) customStreams.Delete(f); delete stream; } - } +void S_PauseAllCustomStreams(bool on) +{ + for (auto s : customStreams) + { + s->SetPaused(on); + } +} static TArray convert; static bool FillStream(SoundStream* stream, void* buff, int len, void* userdata) @@ -214,6 +230,9 @@ static bool S_StartMusicPlaying(ZMusic_MusicStream song, bool loop, float rel_vo I_SetRelativeVolume(saved_relative_volume * factor); } ZMusic_Stop(song); + // make sure the volume modifiers update properly in case replay gain settings have changed. + fluid_gain.Callback(); + mod_dumb_mastervolume.Callback(); if (!ZMusic_Start(song, subsong, loop)) { return false; @@ -488,6 +507,8 @@ static void CheckReplayGain(const char *musicname, EMidiDevice playertype, const { mus_playing.replayGain = 0.f; mus_playing.replayGainFactor = dBToAmplitude(mus_gainoffset); + fluid_gain.Callback(); + mod_dumb_mastervolume.Callback(); if (!mus_usereplaygain) return; FileReader reader = mus_cb.OpenMusic(musicname); diff --git a/src/common/audio/music/music_config.cpp b/src/common/audio/music/music_config.cpp index 77975532a..86eef267e 100644 --- a/src/common/audio/music/music_config.cpp +++ b/src/common/audio/music/music_config.cpp @@ -41,6 +41,7 @@ #include "version.h" #include +EXTERN_CVAR(Bool, mus_usereplaygain) //========================================================================== // // ADL Midi device @@ -122,7 +123,16 @@ CUSTOM_CVAR(String, fluid_patchset, GAMENAMELOWERCASE, CVAR_ARCHIVE | CVAR_GLOBA CUSTOM_CVAR(Float, fluid_gain, 0.5, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL) { - FORWARD_CVAR(fluid_gain); + if (!mus_usereplaygain) + { + FORWARD_CVAR(fluid_gain); + } + else + { + // Replay gain will disable the user setting for consistency. + float newval; + ChangeMusicSetting(zmusic_fluid_gain, mus_playing.handle, 0.5f, & newval); + } } CUSTOM_CVAR(Bool, fluid_reverb, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL) @@ -493,6 +503,14 @@ CUSTOM_CVAR(Int, mod_autochip_scan_threshold, 12, CVAR_ARCHIVE | CVAR_GLOBAL CUSTOM_CVAR(Float, mod_dumb_mastervolume, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL) { - FORWARD_CVAR(mod_dumb_mastervolume); + if (!mus_usereplaygain) + { + FORWARD_CVAR(mod_dumb_mastervolume); + } + else + { + float newval; + ChangeMusicSetting(zmusic_mod_dumb_mastervolume, mus_playing.handle, 0.5f, &newval); + } } diff --git a/src/common/audio/music/s_music.h b/src/common/audio/music/s_music.h index ef852273c..a76189fc1 100644 --- a/src/common/audio/music/s_music.h +++ b/src/common/audio/music/s_music.h @@ -14,6 +14,7 @@ class SoundStream; typedef bool(*StreamCallback)(SoundStream* stream, void* buff, int len, void* userdata); SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels, StreamCallback cb, void *userdata); void S_StopCustomStream(SoundStream* stream); +void S_PauseAllCustomStreams(bool on); struct MusicCallbacks { diff --git a/src/common/audio/sound/i_soundinternal.h b/src/common/audio/sound/i_soundinternal.h index be3486440..1ef4178d4 100644 --- a/src/common/audio/sound/i_soundinternal.h +++ b/src/common/audio/sound/i_soundinternal.h @@ -31,6 +31,7 @@ enum EChanFlag CHANF_OVERLAP = 8192, // [MK] Does not stop any sounds in the channel and instead plays over them. CHANF_LOCAL = 16384, // only plays locally for the calling actor CHANF_TRANSIENT = 32768, // Do not record in savegames - used for sounds that get restarted outside the sound system (e.g. ambients in SW and Blood) + CHANF_FORCE = 65536, // Start, even if sound is paused. }; typedef TFlags EChanFlags; diff --git a/src/common/audio/sound/s_sound.cpp b/src/common/audio/sound/s_sound.cpp index 4521ebd90..7fdbd74c6 100644 --- a/src/common/audio/sound/s_sound.cpp +++ b/src/common/audio/sound/s_sound.cpp @@ -528,7 +528,7 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source, // sound is paused and a non-looped sound is being started. // Such a sound would play right after unpausing which wouldn't sound right. - if (!(chanflags & CHANF_LOOP) && !(chanflags & (CHANF_UI|CHANF_NOPAUSE)) && SoundPaused) + if (!(chanflags & CHANF_LOOP) && !(chanflags & (CHANF_UI|CHANF_NOPAUSE|CHANF_FORCE)) && SoundPaused) { return NULL; } diff --git a/src/common/console/c_bind.cpp b/src/common/console/c_bind.cpp index 1aad2d255..fa94d1e72 100644 --- a/src/common/console/c_bind.cpp +++ b/src/common/console/c_bind.cpp @@ -696,6 +696,12 @@ void ReadBindings(int lump, bool override) dest = &AutomapBindings; sc.MustGetString(); } + else if (sc.Compare("unbind")) + { + sc.MustGetString(); + dest->UnbindKey(sc.String); + continue; + } key = GetConfigKeyFromName(sc.String); sc.MustGetString(); dest->SetBind(key, sc.String, override); diff --git a/src/common/engine/d_event.cpp b/src/common/engine/d_event.cpp index b42915c7b..3a779a5fc 100644 --- a/src/common/engine/d_event.cpp +++ b/src/common/engine/d_event.cpp @@ -76,12 +76,15 @@ void D_ProcessEvents (void) continue; if (ev->type == EV_DeviceChange) UpdateJoystickMenu(I_UpdateDeviceList()); - if (gamestate == GS_INTRO) - continue; - if (C_Responder (ev)) - continue; // console ate the event - if (M_Responder (ev)) - continue; // menu ate the event + + if (gamestate != GS_INTRO) // GS_INTRO blocks the UI. + { + if (C_Responder(ev)) + continue; // console ate the event + if (M_Responder(ev)) + continue; // menu ate the event + } + G_Responder (ev); } } diff --git a/src/common/engine/sc_man.cpp b/src/common/engine/sc_man.cpp index fa9a2f6c4..f24f25d92 100644 --- a/src/common/engine/sc_man.cpp +++ b/src/common/engine/sc_man.cpp @@ -1287,6 +1287,42 @@ void FScanner::AddSymbol(const char* name, double value) symbols.Insert(name, sym); } +//========================================================================== +// +// +// +//========================================================================== + +int FScanner::StartBraces(FScanner::SavedPos* braceend) +{ + if (CheckString("{")) + { + auto here = SavePos(); + SkipToEndOfBlock(); + *braceend = SavePos(); + RestorePos(here); + return 0; + } + else + { + ScriptError("'{' expected"); + return -1; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +bool FScanner::FoundEndBrace(FScanner::SavedPos& braceend) +{ + auto here = SavePos(); + return here.SavedScriptPtr >= braceend.SavedScriptPtr; +} + + //========================================================================== // // a class that remembers a parser position diff --git a/src/common/engine/sc_man.h b/src/common/engine/sc_man.h index 6c37806a8..64690d24b 100644 --- a/src/common/engine/sc_man.h +++ b/src/common/engine/sc_man.h @@ -94,6 +94,8 @@ public: inline void AddSymbol(const char* name, uint32_t value) { return AddSymbol(name, uint64_t(value)); } void AddSymbol(const char* name, double value); void SkipToEndOfBlock(); + int StartBraces(FScanner::SavedPos* braceend); + bool FoundEndBrace(FScanner::SavedPos& braceend); static FString TokenName(int token, const char *string=NULL); @@ -113,7 +115,30 @@ public: void MustGetNumber(bool evaluate = false); bool CheckNumber(bool evaluate = false); + bool GetNumber(int& var, bool evaluate = false) + { + if (!GetNumber(evaluate)) return false; + var = Number; + return true; + } + + bool GetString(FString& var) + { + if (!GetString()) return false; + var = String; + return true; + } + bool GetFloat(bool evaluate = false); + + bool GetFloat(double& var, bool evaluate = false) + { + if (!GetFloat(evaluate)) return false; + var = Float; + return true; + } + + void MustGetFloat(bool evaluate = false); bool CheckFloat(bool evaluate = false); diff --git a/src/common/engine/startupinfo.h b/src/common/engine/startupinfo.h index f47fa351f..c19d99537 100644 --- a/src/common/engine/startupinfo.h +++ b/src/common/engine/startupinfo.h @@ -13,7 +13,6 @@ struct FStartupInfo int LoadLights = -1; int LoadBrightmaps = -1; int LoadWidescreen = -1; - int modern = 0; enum { DefaultStartup, diff --git a/src/common/filesystem/file_7z.cpp b/src/common/filesystem/file_7z.cpp index 8d0d64bb2..f207702d0 100644 --- a/src/common/filesystem/file_7z.cpp +++ b/src/common/filesystem/file_7z.cpp @@ -293,7 +293,7 @@ bool F7ZFile::Open(bool quiet, LumpFilterInfo *filter) lump_p->Owner = this; lump_p->Flags = LUMPF_FULLPATH|LUMPF_COMPRESSED; lump_p->Position = i; - lump_p->CheckEmbedded(); + lump_p->CheckEmbedded(filter); lump_p++; } // Resize the lump record array to its actual size diff --git a/src/common/filesystem/file_directory.cpp b/src/common/filesystem/file_directory.cpp index 7654764cb..8c7b57876 100644 --- a/src/common/filesystem/file_directory.cpp +++ b/src/common/filesystem/file_directory.cpp @@ -154,13 +154,28 @@ int FDirectory::AddDirectory(const char *dirpath) } else { - if (strstr(fi, ".orig") || strstr(fi, ".bak")) + if (strstr(fi, ".orig") || strstr(fi, ".bak") || strstr(fi, ".cache")) { // We shouldn't add backup files to the file system continue; } size_t size = 0; FString fn = FString(dirpath) + fi; + + // The next one is courtesy of EDuke32. :( + // Putting cache files in the application directory is very bad style. + // Unfortunately, having a garbage file named "texture" present will cause serious problems down the line. + if (!stricmp(fi, "textures")) + { + FILE* f = fopen(fn, "rb"); + if (f) + { + char check[3]{}; + fread(check, 1, 3, f); + if (!memcmp(check, "LZ4", 3)) continue; + } + } + if (GetFileInfo(fn, &size, nullptr)) { AddEntry(fn, (int)size); @@ -209,7 +224,7 @@ void FDirectory::AddEntry(const char *fullpath, int size) lump_p->LumpSize = size; lump_p->Owner = this; lump_p->Flags = 0; - lump_p->CheckEmbedded(); + lump_p->CheckEmbedded(nullptr); } diff --git a/src/common/filesystem/file_pak.cpp b/src/common/filesystem/file_pak.cpp index 5ff9ff886..13aafa985 100644 --- a/src/common/filesystem/file_pak.cpp +++ b/src/common/filesystem/file_pak.cpp @@ -109,7 +109,7 @@ bool FPakFile::Open(bool quiet, LumpFilterInfo* filter) Lumps[i].Owner = this; Lumps[i].Position = LittleLong(fileinfo[i].filepos); Lumps[i].LumpSize = LittleLong(fileinfo[i].filelen); - Lumps[i].CheckEmbedded(); + Lumps[i].CheckEmbedded(filter); } GenerateHash(); PostProcessArchive(&Lumps[0], sizeof(Lumps[0]), filter); diff --git a/src/common/filesystem/file_zip.cpp b/src/common/filesystem/file_zip.cpp index 00de98cdc..f33b60ba4 100644 --- a/src/common/filesystem/file_zip.cpp +++ b/src/common/filesystem/file_zip.cpp @@ -233,12 +233,14 @@ bool FZipFile::Open(bool quiet, LumpFilterInfo* filter) } name.ToLower(); + if (name.IndexOf("__macosx") == 0) + continue; // skip Apple garbage. At this stage only the root folder matters, if (i == 0) { // check for special names, if one of these gets found this must be treated as a normal zip. bool isspecial = name.IndexOf("/") < 0 || (filter && filter->reservedFolders.Find(name) < filter->reservedFolders.Size()); if (isspecial) break; - name0 = name; + name0 = name.Left(name.LastIndexOf("/")+1); } else { @@ -252,7 +254,7 @@ bool FZipFile::Open(bool quiet, LumpFilterInfo* filter) // at least one of the more common definition lumps must be present. for (auto &p : filter->requiredPrefixes) { - if (name.IndexOf(name0 + p) == 0) + if (name.IndexOf(name0 + p) == 0 || name.LastIndexOf(p) == name.Len() - strlen(p)) { foundspeciallump = true; break; @@ -272,7 +274,6 @@ bool FZipFile::Open(bool quiet, LumpFilterInfo* filter) int len = LittleShort(zip_fh->NameLength); FString name(dirptr + sizeof(FZipCentralDirectoryInfo), len); - if (name0.IsNotEmpty()) name = name.Mid(name0.Len()); dirptr += sizeof(FZipCentralDirectoryInfo) + LittleShort(zip_fh->NameLength) + LittleShort(zip_fh->ExtraLength) + @@ -284,7 +285,14 @@ bool FZipFile::Open(bool quiet, LumpFilterInfo* filter) if (!quiet) Printf(TEXTCOLOR_RED "\n%s: Central directory corrupted.", FileName.GetChars()); return false; } - + + if (name.IndexOf("__macosx") == 0 || name.IndexOf("__MACOSX") == 0) + { + skipped++; + continue; // Weed out Apple's resource fork garbage right here because it interferes with safe operation. + } + if (name0.IsNotEmpty()) name = name.Mid(name0.Len()); + // skip Directories if (name.IsEmpty() || (name.Back() == '/' && LittleLong(zip_fh->UncompressedSize) == 0)) { @@ -329,7 +337,7 @@ bool FZipFile::Open(bool quiet, LumpFilterInfo* filter) lump_p->CRC32 = zip_fh->CRC32; lump_p->CompressedSize = LittleLong(zip_fh->CompressedSize); lump_p->Position = LittleLong(zip_fh->LocalHeaderOffset); - lump_p->CheckEmbedded(); + lump_p->CheckEmbedded(filter); lump_p++; } diff --git a/src/common/filesystem/filesystem.cpp b/src/common/filesystem/filesystem.cpp index 0ea4ac7a6..dae98edf5 100644 --- a/src/common/filesystem/filesystem.cpp +++ b/src/common/filesystem/filesystem.cpp @@ -76,14 +76,14 @@ struct FileSystem::LumpRecord shortName.String[8] = 0; longName = ""; Namespace = lump->GetNamespace(); - resourceId = 0; + resourceId = -1; } else if ((lump->Flags & LUMPF_EMBEDDED) || !lump->getName() || !*lump->getName()) { shortName.qword = 0; longName = ""; Namespace = ns_hidden; - resourceId = 0; + resourceId = -1; } else { diff --git a/src/common/filesystem/resourcefile.cpp b/src/common/filesystem/resourcefile.cpp index d718d74ec..5fc484b23 100644 --- a/src/common/filesystem/resourcefile.cpp +++ b/src/common/filesystem/resourcefile.cpp @@ -122,7 +122,7 @@ static bool IsWadInFolder(const FResourceFile* const archive, const char* const return 0 == filePath.CompareNoCase(resPath); } -void FResourceLump::CheckEmbedded() +void FResourceLump::CheckEmbedded(LumpFilterInfo* lfi) { // Checks for embedded archives const char *c = strstr(FullName, ".wad"); @@ -130,22 +130,13 @@ void FResourceLump::CheckEmbedded() { Flags |= LUMPF_EMBEDDED; } - /* later - else + else if (lfi) for (auto& fstr : lfi->embeddings) { - if (c==NULL) c = strstr(Name, ".zip"); - if (c==NULL) c = strstr(Name, ".pk3"); - if (c==NULL) c = strstr(Name, ".7z"); - if (c==NULL) c = strstr(Name, ".pak"); - if (c && strlen(c) <= 4) + if (!stricmp(FullName, fstr)) { - // Mark all embedded archives in any directory Flags |= LUMPF_EMBEDDED; - memset(Name, 0, 8); } } - */ - } diff --git a/src/common/filesystem/resourcefile.h b/src/common/filesystem/resourcefile.h index bfd20e816..5e9276c76 100644 --- a/src/common/filesystem/resourcefile.h +++ b/src/common/filesystem/resourcefile.h @@ -15,6 +15,7 @@ struct LumpFilterInfo // The following are for checking if the root directory of a zip can be removed. TArray reservedFolders; TArray requiredPrefixes; + TArray embeddings; std::function postprocessFunc; }; @@ -111,7 +112,7 @@ public: virtual int GetIndexNum() const { return -1; } virtual int GetNamespace() const { return 0; } void LumpNameSetup(FString iname); - void CheckEmbedded(); + void CheckEmbedded(LumpFilterInfo* lfi); virtual FCompressedBuffer GetRawData(); void *Lock(); // validates the cache and increases the refcount. diff --git a/src/common/menu/savegamemanager.cpp b/src/common/menu/savegamemanager.cpp index 935b6dedd..22dd8c6a3 100644 --- a/src/common/menu/savegamemanager.cpp +++ b/src/common/menu/savegamemanager.cpp @@ -232,6 +232,7 @@ DEFINE_ACTION_FUNCTION(FSavegameManager, LoadSavegame) void FSavegameManagerBase::DoSave(int Selected, const char *savegamestring) { + RemoveNewSaveNode(); if (Selected != 0) { auto node = SaveGames[Selected]; diff --git a/src/common/rendering/hwrenderer/data/flatvertices.cpp b/src/common/rendering/hwrenderer/data/flatvertices.cpp index 47ae19ec3..5bc37bcb7 100644 --- a/src/common/rendering/hwrenderer/data/flatvertices.cpp +++ b/src/common/rendering/hwrenderer/data/flatvertices.cpp @@ -80,6 +80,8 @@ FFlatVertexBuffer::FFlatVertexBuffer(int width, int height) mVertexBuffer = screen->CreateVertexBuffer(); mIndexBuffer = screen->CreateIndexBuffer(); + int data[4] = {}; + mIndexBuffer->SetData(4, data); // On Vulkan this may not be empty, so set some dummy defaults to avoid crashes. unsigned int bytesize = BUFFER_SIZE * sizeof(FFlatVertex); mVertexBuffer->SetData(bytesize, nullptr, false); diff --git a/src/common/rendering/hwrenderer/data/flatvertices.h b/src/common/rendering/hwrenderer/data/flatvertices.h index 04feb8388..d28339ee7 100644 --- a/src/common/rendering/hwrenderer/data/flatvertices.h +++ b/src/common/rendering/hwrenderer/data/flatvertices.h @@ -9,7 +9,6 @@ class FRenderState; struct secplane_t; -struct subsector_t; struct FFlatVertex { diff --git a/src/common/rendering/hwrenderer/data/hw_aabbtree.h b/src/common/rendering/hwrenderer/data/hw_aabbtree.h index 389a991ab..2bd0e1506 100644 --- a/src/common/rendering/hwrenderer/data/hw_aabbtree.h +++ b/src/common/rendering/hwrenderer/data/hw_aabbtree.h @@ -4,8 +4,6 @@ #include "tarray.h" #include "vectors.h" -struct FLevelLocals; - namespace hwrenderer { diff --git a/src/common/rendering/hwrenderer/data/hw_renderstate.h b/src/common/rendering/hwrenderer/data/hw_renderstate.h index 9cf1015d2..54aab25f8 100644 --- a/src/common/rendering/hwrenderer/data/hw_renderstate.h +++ b/src/common/rendering/hwrenderer/data/hw_renderstate.h @@ -200,9 +200,8 @@ struct StreamData FVector4 uSplitBottomPlane; FVector4 uDetailParms; -#ifdef NPOT_EMULATION - FVector2 uNpotEmulation; -#endif + FVector4 uNpotEmulation; + FVector4 padding1, padding2, padding3; }; class FRenderState @@ -295,7 +294,7 @@ public: mStreamData.uDynLightColor = { 0.0f, 0.0f, 0.0f, 1.0f }; mStreamData.uDetailParms = { 0.0f, 0.0f, 0.0f, 0.0f }; #ifdef NPOT_EMULATION - mStreamData.uNpotEmulation = { 0,0 }; + mStreamData.uNpotEmulation = { 0,0,0,0 }; #endif mModelMatrix.loadIdentity(); mTextureMatrix.loadIdentity(); @@ -490,7 +489,7 @@ public: void SetNpotEmulation(float factor, float offset) { #ifdef NPOT_EMULATION - mStreamData.uNpotEmulation = { offset, factor }; + mStreamData.uNpotEmulation = { offset, factor, 0, 0 }; #endif } diff --git a/src/common/rendering/hwrenderer/data/hw_skydome.cpp b/src/common/rendering/hwrenderer/data/hw_skydome.cpp index 52bdc4d36..5d5ee8000 100644 --- a/src/common/rendering/hwrenderer/data/hw_skydome.cpp +++ b/src/common/rendering/hwrenderer/data/hw_skydome.cpp @@ -62,13 +62,7 @@ #include "hw_renderstate.h" #include "v_video.h" #include "hwrenderer/data/buffers.h" - -// 57 world units roughly represent one sky texel for the glTranslate call. -enum -{ - skyoffsetfactor = 57 -}; - +#include "version.h" //----------------------------------------------------------------------------- // @@ -150,7 +144,7 @@ FSkyVertexBuffer::~FSkyVertexBuffer() // //----------------------------------------------------------------------------- -void FSkyVertexBuffer::SkyVertex(int r, int c, bool zflip) +void FSkyVertexBuffer::SkyVertexDoom(int r, int c, bool zflip) { static const FAngle maxSideAngle = 60.f; static const float scale = 10000.; @@ -187,6 +181,37 @@ void FSkyVertexBuffer::SkyVertex(int r, int c, bool zflip) mVertices.Push(vert); } +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void FSkyVertexBuffer::SkyVertexBuild(int r, int c, bool zflip) +{ + static const FAngle maxSideAngle = 60.f; + static const float scale = 10000.; + + FAngle topAngle = (c / (float)mColumns * 360.f); + FVector2 pos = topAngle.ToVector(scale); + float z = (!zflip) ? (mRows - r) * 4000.f : -(mRows - r) * 4000.f; + + FSkyVertex vert; + + vert.color = r == 0 ? 0xffffff : 0xffffffff; + + // And the texture coordinates. + if (zflip) r = mRows * 2 - r; + vert.u = 0.5f + (-c / (float)mColumns); + vert.v = (r / (float)(2*mRows)); + + // And finally the vertex. + vert.x = pos.X; + vert.y = z - 1.f; + vert.z = pos.Y; + + mVertices.Push(vert); +} //----------------------------------------------------------------------------- // @@ -194,27 +219,58 @@ void FSkyVertexBuffer::SkyVertex(int r, int c, bool zflip) // //----------------------------------------------------------------------------- -void FSkyVertexBuffer::CreateSkyHemisphere(int hemi) +void FSkyVertexBuffer::CreateSkyHemisphereDoom(int hemi) { int r, c; bool zflip = !!(hemi & SKYHEMI_LOWER); - mPrimStart.Push(mVertices.Size()); + mPrimStartDoom.Push(mVertices.Size()); for (c = 0; c < mColumns; c++) { - SkyVertex(1, c, zflip); + SkyVertexDoom(1, c, zflip); } // The total number of triangles per hemisphere can be calculated // as follows: rows * columns * 2 + 2 (for the top cap). for (r = 0; r < mRows; r++) { - mPrimStart.Push(mVertices.Size()); + mPrimStartDoom.Push(mVertices.Size()); for (c = 0; c <= mColumns; c++) { - SkyVertex(r + zflip, c, zflip); - SkyVertex(r + 1 - zflip, c, zflip); + SkyVertexDoom(r + zflip, c, zflip); + SkyVertexDoom(r + 1 - zflip, c, zflip); + } + } +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void FSkyVertexBuffer::CreateSkyHemisphereBuild(int hemi) +{ + int r, c; + bool zflip = !!(hemi & SKYHEMI_LOWER); + + mPrimStartBuild.Push(mVertices.Size()); + + for (c = 0; c < mColumns; c++) + { + SkyVertexBuild(1, c, zflip); + } + + // The total number of triangles per hemisphere can be calculated + // as follows: rows * columns * 2 + 2 (for the top cap). + for (r = 0; r < mRows; r++) + { + mPrimStartBuild.Push(mVertices.Size()); + for (c = 0; c <= mColumns; c++) + { + SkyVertexBuild(r + zflip, c, zflip); + SkyVertexBuild(r + 1 - zflip, c, zflip); } } } @@ -248,9 +304,13 @@ void FSkyVertexBuffer::CreateDome() mColumns = 128; mRows = 4; - CreateSkyHemisphere(SKYHEMI_UPPER); - CreateSkyHemisphere(SKYHEMI_LOWER); - mPrimStart.Push(mVertices.Size()); + CreateSkyHemisphereDoom(SKYHEMI_UPPER); + CreateSkyHemisphereDoom(SKYHEMI_LOWER); + mPrimStartDoom.Push(mVertices.Size()); + + CreateSkyHemisphereBuild(SKYHEMI_UPPER); + CreateSkyHemisphereBuild(SKYHEMI_LOWER); + mPrimStartBuild.Push(mVertices.Size()); mSideStart = mVertices.Size(); mFaceStart[0] = mSideStart + 10; @@ -324,7 +384,7 @@ void FSkyVertexBuffer::CreateDome() // //----------------------------------------------------------------------------- -void FSkyVertexBuffer::SetupMatrices(FGameTexture *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelMatrix, VSMatrix &textureMatrix, bool tiled) +void FSkyVertexBuffer::SetupMatrices(FGameTexture *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelMatrix, VSMatrix &textureMatrix, bool tiled, float xscale, float yscale) { float texw = tex->GetDisplayWidth(); float texh = tex->GetDisplayHeight(); @@ -332,37 +392,46 @@ void FSkyVertexBuffer::SetupMatrices(FGameTexture *tex, float x_offset, float y_ modelMatrix.loadIdentity(); modelMatrix.rotate(-180.0f + x_offset, 0.f, 1.f, 0.f); - float xscale = texw < 1024.f ? floorf(1024.f / float(texw)) : 1.f; - float yscale = 1.f; + if (xscale == 0) xscale = texw < 1024.f ? floorf(1024.f / float(texw)) : 1.f; auto texskyoffset = tex->GetSkyOffset() + skyoffset; - if (texh <= 128 && tiled) + if (yscale == 0) { - modelMatrix.translate(0.f, (-40 + texskyoffset)*skyoffsetfactor, 0.f); - modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f); - yscale = 240.f / texh; - } - else if (texh < 128) - { - // smaller sky textures must be tiled. We restrict it to 128 sky pixels, though - modelMatrix.translate(0.f, -1250.f, 0.f); - modelMatrix.scale(1.f, 128 / 230.f, 1.f); - yscale = float(128 / texh); // intentionally left as integer. - } - else if (texh < 200) - { - modelMatrix.translate(0.f, -1250.f, 0.f); - modelMatrix.scale(1.f, texh / 230.f, 1.f); - } - else if (texh <= 240) - { - modelMatrix.translate(0.f, (200 - texh + texskyoffset)*skyoffsetfactor, 0.f); - modelMatrix.scale(1.f, 1.f + ((texh - 200.f) / 200.f) * 1.17f, 1.f); + if (texh <= 128 && tiled) + { + modelMatrix.translate(0.f, (-40 + texskyoffset) * skyoffsetfactor, 0.f); + modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f); + yscale = 240.f / texh; + } + else if (texh < 128) + { + // smaller sky textures must be tiled. We restrict it to 128 sky pixels, though + modelMatrix.translate(0.f, -1250.f, 0.f); + modelMatrix.scale(1.f, 128 / 230.f, 1.f); + yscale = float(128 / texh); // intentionally left as integer. + } + else if (texh < 200) + { + modelMatrix.translate(0.f, -1250.f, 0.f); + modelMatrix.scale(1.f, texh / 230.f, 1.f); + yscale = 1.f; + } + else if (texh <= 240) + { + modelMatrix.translate(0.f, (200 - texh + texskyoffset) * skyoffsetfactor, 0.f); + modelMatrix.scale(1.f, 1.f + ((texh - 200.f) / 200.f) * 1.17f, 1.f); + yscale = 1.f; + } + else + { + modelMatrix.translate(0.f, (-40 + texskyoffset) * skyoffsetfactor, 0.f); + modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f); + yscale = 240.f / texh; + } } else { - modelMatrix.translate(0.f, (-40 + texskyoffset)*skyoffsetfactor, 0.f); - modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f); - yscale = 240.f / texh; + modelMatrix.translate(0.f, (-40 + texskyoffset) * skyoffsetfactor, 0.f); + modelMatrix.scale(1.f, 0.8f * 1.17f, 1.f); } textureMatrix.loadIdentity(); textureMatrix.scale(mirror ? -xscale : xscale, yscale, 1.f); @@ -375,7 +444,7 @@ void FSkyVertexBuffer::SetupMatrices(FGameTexture *tex, float x_offset, float y_ // //----------------------------------------------------------------------------- -void FSkyVertexBuffer::RenderRow(FRenderState& state, EDrawType prim, int row, bool apply) +void FSkyVertexBuffer::RenderRow(FRenderState& state, EDrawType prim, int row, TArray& mPrimStart, bool apply) { state.Draw(prim, mPrimStart[row], mPrimStart[row + 1] - mPrimStart[row]); } @@ -386,36 +455,35 @@ void FSkyVertexBuffer::RenderRow(FRenderState& state, EDrawType prim, int row, b // //----------------------------------------------------------------------------- -void FSkyVertexBuffer::RenderDome(FRenderState& state, FGameTexture* tex, float x_offset, float y_offset, bool mirror, int mode, bool tiled) +void FSkyVertexBuffer::RenderDome(FRenderState& state, FGameTexture* tex, int mode, bool which) { - if (tex) + auto& primStart = which ? mPrimStartBuild : mPrimStartDoom; + if (tex && tex->isValid()) { state.SetMaterial(tex, UF_Texture, 0, CLAMP_NONE, 0, -1); state.EnableModelMatrix(true); state.EnableTextureMatrix(true); - - SetupMatrices(tex, x_offset, y_offset, mirror, mode, state.mModelMatrix, state.mTextureMatrix, tiled); } int rc = mRows + 1; // The caps only get drawn for the main layer but not for the overlay. - if (mode == FSkyVertexBuffer::SKYMODE_MAINLAYER && tex != NULL) + if (mode == FSkyVertexBuffer::SKYMODE_MAINLAYER && tex != nullptr) { auto& col = R_GetSkyCapColor(tex); state.SetObjectColor(col.first); state.EnableTexture(false); - RenderRow(state, DT_TriangleFan, 0); + RenderRow(state, DT_TriangleFan, 0, primStart); state.SetObjectColor(col.second); - RenderRow(state, DT_TriangleFan, rc); + RenderRow(state, DT_TriangleFan, rc, primStart); state.EnableTexture(true); } state.SetObjectColor(0xffffffff); for (int i = 1; i <= mRows; i++) { - RenderRow(state, DT_TriangleStrip, i, i == 1); - RenderRow(state, DT_TriangleStrip, rc + i, false); + RenderRow(state, DT_TriangleStrip, i, primStart, i == 1); + RenderRow(state, DT_TriangleStrip, rc + i, primStart, false); } state.EnableTextureMatrix(false); @@ -423,13 +491,29 @@ void FSkyVertexBuffer::RenderDome(FRenderState& state, FGameTexture* tex, float } +//----------------------------------------------------------------------------- +// +// This is only for Doom-style skies. +// +//----------------------------------------------------------------------------- + +void FSkyVertexBuffer::RenderDome(FRenderState& state, FGameTexture* tex, float x_offset, float y_offset, bool mirror, int mode, bool tiled, float xscale, float yscale) +{ + if (tex) + { + SetupMatrices(tex, x_offset, y_offset, mirror, mode, state.mModelMatrix, state.mTextureMatrix, tiled, xscale, yscale); + } + RenderDome(state, tex, mode, false); +} + + //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- -void FSkyVertexBuffer::RenderBox(FRenderState& state, FTextureID texno, FSkyBox* tex, float x_offset, bool sky2, float stretch, const FVector3& skyrotatevector, const FVector3& skyrotatevector2) +void FSkyVertexBuffer::RenderBox(FRenderState& state, FSkyBox* tex, float x_offset, bool sky2, float stretch, const FVector3& skyrotatevector, const FVector3& skyrotatevector2) { int faces; diff --git a/src/common/rendering/hwrenderer/data/hw_skydome.h b/src/common/rendering/hwrenderer/data/hw_skydome.h index 7cbd37948..2e10869bb 100644 --- a/src/common/rendering/hwrenderer/data/hw_skydome.h +++ b/src/common/rendering/hwrenderer/data/hw_skydome.h @@ -11,6 +11,12 @@ class IVertexBuffer; struct HWSkyPortal; struct HWDrawInfo; +// 57 world units roughly represent one sky texel for the glTranslate call. +enum +{ + skyoffsetfactor = 57 +}; + struct FSkyVertex { float x, y, z, u, v; @@ -55,7 +61,8 @@ public: IVertexBuffer *mVertexBuffer; TArray mVertices; - TArray mPrimStart; + TArray mPrimStartDoom; + TArray mPrimStartBuild; int mRows, mColumns; @@ -63,15 +70,17 @@ public: int mFaceStart[7]; int mSideStart; - void SkyVertex(int r, int c, bool yflip); - void CreateSkyHemisphere(int hemi); + void SkyVertexDoom(int r, int c, bool yflip); + void SkyVertexBuild(int r, int c, bool yflip); + void CreateSkyHemisphereDoom(int hemi); + void CreateSkyHemisphereBuild(int hemi); void CreateDome(); public: FSkyVertexBuffer(); ~FSkyVertexBuffer(); - void SetupMatrices(FGameTexture *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelmatrix, VSMatrix &textureMatrix, bool tiled); + void SetupMatrices(FGameTexture *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelmatrix, VSMatrix &textureMatrix, bool tiled, float xscale = 0, float vertscale = 0); std::pair GetBufferObjects() const { return std::make_pair(mVertexBuffer, nullptr); @@ -83,8 +92,9 @@ public: else return mSideStart; } - void RenderRow(FRenderState& state, EDrawType prim, int row, bool apply = true); - void RenderDome(FRenderState& state, FGameTexture* tex, float x_offset, float y_offset, bool mirror, int mode, bool tiled); - void RenderBox(FRenderState& state, FTextureID texno, FSkyBox* tex, float x_offset, bool sky2, float stretch, const FVector3& skyrotatevector, const FVector3& skyrotatevector2); + void RenderRow(FRenderState& state, EDrawType prim, int row, TArray& mPrimStart, bool apply = true); + void RenderDome(FRenderState& state, FGameTexture* tex, int mode, bool which); + void RenderDome(FRenderState& state, FGameTexture* tex, float x_offset, float y_offset, bool mirror, int mode, bool tiled, float xscale = 0, float yscale = 0); + void RenderBox(FRenderState& state, FSkyBox* tex, float x_offset, bool sky2, float stretch, const FVector3& skyrotatevector, const FVector3& skyrotatevector2); }; diff --git a/src/common/rendering/hwrenderer/hw_draw2d.cpp b/src/common/rendering/hwrenderer/hw_draw2d.cpp index 36fd8f369..cf3399432 100644 --- a/src/common/rendering/hwrenderer/hw_draw2d.cpp +++ b/src/common/rendering/hwrenderer/hw_draw2d.cpp @@ -85,7 +85,6 @@ void Draw2D(F2DDrawer *drawer, FRenderState &state) vb.UploadData(&vertices[0], vertices.Size(), &indices[0], indices.Size()); state.SetVertexBuffer(&vb); state.EnableFog(false); - state.SetScreenFade(drawer->screenFade); for(auto &cmd : commands) { @@ -94,6 +93,7 @@ void Draw2D(F2DDrawer *drawer, FRenderState &state) state.SetRenderStyle(cmd.mRenderStyle); state.EnableBrightmap(!(cmd.mRenderStyle.Flags & STYLEF_ColorIsFixed)); state.EnableFog(2); // Special 2D mode 'fog'. + state.SetScreenFade(cmd.mScreenFade); state.SetTextureMode(cmd.mDrawMode); diff --git a/src/common/rendering/v_video.h b/src/common/rendering/v_video.h index ae4b318d0..c9124e05f 100644 --- a/src/common/rendering/v_video.h +++ b/src/common/rendering/v_video.h @@ -45,7 +45,6 @@ #include "hw_shadowmap.h" -struct sector_t; struct FPortalSceneState; class FSkyVertexBuffer; class IIndexBuffer; @@ -147,7 +146,7 @@ public: IntRect mScreenViewport; IntRect mSceneViewport; IntRect mOutputLetterbox; - float mSceneClearColor[4]; + float mSceneClearColor[4]{ 0,0,0,255 }; public: DFrameBuffer (int width=1, int height=1); diff --git a/src/common/rendering/vulkan/shaders/vk_shader.cpp b/src/common/rendering/vulkan/shaders/vk_shader.cpp index 0456061fb..84f7d3d5e 100644 --- a/src/common/rendering/vulkan/shaders/vk_shader.cpp +++ b/src/common/rendering/vulkan/shaders/vk_shader.cpp @@ -163,9 +163,8 @@ static const char *shaderBindings = R"( vec4 uSplitBottomPlane; vec4 uDetailParms; - #ifdef NPOT_EMULATION - vec2 uNpotEmulation; - #endif + vec4 uNpotEmulation; + vec4 padding1, padding2, padding3; }; layout(set = 0, binding = 3, std140) uniform StreamUBO { @@ -297,6 +296,9 @@ std::unique_ptr VkShaderManager::LoadFragShader(FString shadername code << defines; code << "\n$placeholder$"; // here the code can later add more needed #defines. code << "\n#define MAX_STREAM_DATA " << std::to_string(MAX_STREAM_DATA).c_str() << "\n"; +#ifdef NPOT_EMULATION + code << "#define NPOT_EMULATION\n"; +#endif code << shaderBindings; FString placeholder = "\n"; diff --git a/src/common/rendering/vulkan/system/vk_framebuffer.cpp b/src/common/rendering/vulkan/system/vk_framebuffer.cpp index 32d36447f..9b83b6e26 100644 --- a/src/common/rendering/vulkan/system/vk_framebuffer.cpp +++ b/src/common/rendering/vulkan/system/vk_framebuffer.cpp @@ -390,7 +390,7 @@ void VulkanFrameBuffer::PrecacheMaterial(FMaterial *mat, int translation) IHardwareTexture *VulkanFrameBuffer::CreateHardwareTexture(int numchannels) { - return new VkHardwareTexture(); + return new VkHardwareTexture(numchannels); } FMaterial* VulkanFrameBuffer::CreateMaterial(FGameTexture* tex, int scaleflags) diff --git a/src/common/rendering/vulkan/textures/vk_hwtexture.cpp b/src/common/rendering/vulkan/textures/vk_hwtexture.cpp index 25bd66d71..6178549ab 100644 --- a/src/common/rendering/vulkan/textures/vk_hwtexture.cpp +++ b/src/common/rendering/vulkan/textures/vk_hwtexture.cpp @@ -37,8 +37,9 @@ VkHardwareTexture *VkHardwareTexture::First = nullptr; -VkHardwareTexture::VkHardwareTexture() +VkHardwareTexture::VkHardwareTexture(int numchannels) { + mTexelsize = numchannels; Next = First; First = this; if (Next) Next->Prev = this; @@ -126,7 +127,8 @@ void VkHardwareTexture::CreateImage(FTexture *tex, int translation, int flags) if (!tex->isHardwareCanvas()) { FTextureBuffer texbuffer = tex->CreateTexBuffer(translation, flags | CTF_ProcessData); - CreateTexture(texbuffer.mWidth, texbuffer.mHeight, 4, VK_FORMAT_B8G8R8A8_UNORM, texbuffer.mBuffer); + bool indexed = flags & CTF_Indexed; + CreateTexture(texbuffer.mWidth, texbuffer.mHeight,indexed? 1 : 4, indexed? VK_FORMAT_R8_UNORM : VK_FORMAT_B8G8R8A8_UNORM, texbuffer.mBuffer, !indexed); } else { @@ -156,7 +158,7 @@ void VkHardwareTexture::CreateImage(FTexture *tex, int translation, int flags) } } -void VkHardwareTexture::CreateTexture(int w, int h, int pixelsize, VkFormat format, const void *pixels) +void VkHardwareTexture::CreateTexture(int w, int h, int pixelsize, VkFormat format, const void *pixels, bool mipmap) { if (w <= 0 || h <= 0) throw CVulkanError("Trying to create zero size texture"); @@ -177,7 +179,7 @@ void VkHardwareTexture::CreateTexture(int w, int h, int pixelsize, VkFormat form ImageBuilder imgbuilder; imgbuilder.setFormat(format); - imgbuilder.setSize(w, h, GetMipLevels(w, h)); + imgbuilder.setSize(w, h, !mipmap ? 1 : GetMipLevels(w, h)); imgbuilder.setUsage(VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); mImage.Image = imgbuilder.create(fb->device); mImage.Image->SetDebugName("VkHardwareTexture.mImage"); @@ -203,7 +205,7 @@ void VkHardwareTexture::CreateTexture(int w, int h, int pixelsize, VkFormat form fb->FrameDeleteList.Buffers.push_back(std::move(stagingBuffer)); - mImage.GenerateMipmaps(cmdbuffer); + if (mipmap) mImage.GenerateMipmaps(cmdbuffer); } int VkHardwareTexture::GetMipLevels(int w, int h) @@ -268,6 +270,7 @@ uint8_t *VkHardwareTexture::MapBuffer() unsigned int VkHardwareTexture::CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, const char *name) { + CreateTexture(w, h, mTexelsize, mTexelsize == 4 ? VK_FORMAT_B8G8R8A8_UNORM : VK_FORMAT_R8_UNORM, buffer, mipmap); return 0; } @@ -371,10 +374,6 @@ VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state) int clampmode = state.mClampMode; int translation = state.mTranslation; - auto remap = translation <= 0 ? nullptr : GPalette.TranslationToTable(translation); - if (remap) - translation = remap->Index; - clampmode = base->GetClampMode(clampmode); for (auto& set : mDescriptorSets) @@ -395,10 +394,23 @@ VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state) MaterialLayerInfo *layer; auto systex = static_cast(GetLayer(0, state.mTranslation, &layer)); update.addCombinedImageSampler(descriptor.get(), 0, systex->GetImage(layer->layerTexture, state.mTranslation, layer->scaleFlags)->View.get(), sampler, systex->mImage.Layout); - for (int i = 1; i < numLayers; i++) + + if (!(layer->scaleFlags & CTF_Indexed)) { - auto systex = static_cast(GetLayer(i, 0, &layer)); - update.addCombinedImageSampler(descriptor.get(), i, systex->GetImage(layer->layerTexture, 0, layer->scaleFlags)->View.get(), sampler, systex->mImage.Layout); + for (int i = 1; i < numLayers; i++) + { + auto systex = static_cast(GetLayer(i, 0, &layer)); + update.addCombinedImageSampler(descriptor.get(), i, systex->GetImage(layer->layerTexture, 0, layer->scaleFlags)->View.get(), sampler, systex->mImage.Layout); + } + } + else + { + for (int i = 1; i < 3; i++) + { + auto systex = static_cast(GetLayer(i, translation, &layer)); + update.addCombinedImageSampler(descriptor.get(), i, systex->GetImage(layer->layerTexture, 0, layer->scaleFlags)->View.get(), sampler, systex->mImage.Layout); + } + numLayers = 3; } auto dummyImage = fb->GetRenderPassManager()->GetNullTextureView(); diff --git a/src/common/rendering/vulkan/textures/vk_hwtexture.h b/src/common/rendering/vulkan/textures/vk_hwtexture.h index 872f58aef..22958c494 100644 --- a/src/common/rendering/vulkan/textures/vk_hwtexture.h +++ b/src/common/rendering/vulkan/textures/vk_hwtexture.h @@ -24,7 +24,7 @@ class VkHardwareTexture : public IHardwareTexture { friend class VkMaterial; public: - VkHardwareTexture(); + VkHardwareTexture(int numchannels); ~VkHardwareTexture(); static void ResetAll(); @@ -45,7 +45,7 @@ public: private: void CreateImage(FTexture *tex, int translation, int flags); - void CreateTexture(int w, int h, int pixelsize, VkFormat format, const void *pixels); + void CreateTexture(int w, int h, int pixelsize, VkFormat format, const void *pixels, bool mipmap); static int GetMipLevels(int w, int h); static VkHardwareTexture *First; diff --git a/src/common/statusbar/base_sbar.cpp b/src/common/statusbar/base_sbar.cpp index b0e917872..ae1a375c3 100644 --- a/src/common/statusbar/base_sbar.cpp +++ b/src/common/statusbar/base_sbar.cpp @@ -605,13 +605,13 @@ void DStatusBarCore::DrawGraphic(FGameTexture* tex, double x, double y, int flag // //============================================================================ -void DStatusBarCore::DrawRotated(FTextureID texture, double x, double y, double angle, int flags, double Alpha, double scaleX, double scaleY, PalEntry color, int translation, ERenderStyle style) +void DStatusBarCore::DrawRotated(FTextureID texture, double x, double y, int flags, double angle, double Alpha, double scaleX, double scaleY, PalEntry color, int translation, ERenderStyle style) { if (!texture.isValid()) return; FGameTexture* tex = TexMan.GetGameTexture(texture, !(flags & DI_DONTANIMATE)); - DrawRotated(tex, x, y, angle, flags, Alpha, scaleX, scaleY, color, translation, style); + DrawRotated(tex, x, y, flags, angle, Alpha, scaleX, scaleY, color, translation, style); } void DStatusBarCore::DrawRotated(FGameTexture* tex, double x, double y, int flags, double angle, double Alpha, double scaleX, double scaleY, PalEntry color, int translation, ERenderStyle style) diff --git a/src/common/statusbar/base_sbar.h b/src/common/statusbar/base_sbar.h index 02c66ba26..7f892b4e4 100644 --- a/src/common/statusbar/base_sbar.h +++ b/src/common/statusbar/base_sbar.h @@ -186,7 +186,7 @@ public: void StatusbarToRealCoords(double& x, double& y, double& w, double& h) const; void DrawGraphic(FGameTexture* texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent, double clipwidth = -1.0); void DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent, double clipwidth = -1.0); - void DrawRotated(FTextureID texture, double x, double y, double angle, int flags, double Alpha, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent); + void DrawRotated(FTextureID texture, double x, double y, int flags, double angle, double Alpha, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent); void DrawRotated(FGameTexture* tex, double x, double y, int flags, double angle, double Alpha, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent); void DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt); void TransformRect(double& x, double& y, double& w, double& h, int flags = 0); diff --git a/src/common/textures/animtexture.cpp b/src/common/textures/animtexture.cpp index 6a96365a8..8f8add3f7 100644 --- a/src/common/textures/animtexture.cpp +++ b/src/common/textures/animtexture.cpp @@ -48,7 +48,6 @@ void AnimTexture::SetFrameSize(int format, int width, int height) FTexture::SetSize(width, height); Image.Resize(width * height * (format == Paletted ? 1 : 3)); memset(Image.Data(), 0, Image.Size()); - CleanHardwareTextures(); } void AnimTexture::SetFrame(const uint8_t* palette, const void* data_) @@ -81,7 +80,6 @@ void AnimTexture::SetFrame(const uint8_t* palette, const void* data_) } else memcpy(Image.Data(), data_, Width * Height * (pixelformat == Paletted ? 1 : 3)); } - CleanHardwareTextures(); } //=========================================================================== @@ -156,10 +154,13 @@ void AnimTextures::SetSize(int format, int width, int height) static_cast(tex[1]->GetTexture())->SetFrameSize(format, width, height); tex[0]->SetSize(width, height); tex[1]->SetSize(width, height); + tex[0]->CleanHardwareData(); + tex[1]->CleanHardwareData(); } void AnimTextures::SetFrame(const uint8_t* palette, const void* data) { active ^= 1; static_cast(tex[active]->GetTexture())->SetFrame(palette, data); + tex[active]->CleanHardwareData(); } diff --git a/src/common/textures/hw_material.cpp b/src/common/textures/hw_material.cpp index f8d379cd4..a157cbc07 100644 --- a/src/common/textures/hw_material.cpp +++ b/src/common/textures/hw_material.cpp @@ -30,6 +30,7 @@ #include "v_video.h" static IHardwareTexture* (*layercallback)(int layer, int translation); +TArray usershaders; void FMaterial::SetLayerCallback(IHardwareTexture* (*cb)(int layer, int translation)) { diff --git a/src/common/thirdparty/earcut.hpp b/src/common/thirdparty/earcut.hpp index d6a2c9798..2c83e33bf 100644 --- a/src/common/thirdparty/earcut.hpp +++ b/src/common/thirdparty/earcut.hpp @@ -82,6 +82,7 @@ private: template Node* eliminateHoles(const Polygon& points, Node* outerNode); void eliminateHole(Node* hole, Node* outerNode); Node* findHoleBridge(Node* hole, Node* outerNode); + bool sectorContainsSector(const Node* m, const Node* p); void indexCurve(Node* start); Node* sortLinked(Node* list); int32_t zOrder(const double x_, const double y_); @@ -91,6 +92,8 @@ private: 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 onSegment(const Node* p, const Node* q, const Node* r); + int sign(double val); bool intersectsPolygon(const Node* a, const Node* b); bool locallyInside(const Node* a, const Node* b); bool middleInside(const Node* a, const Node* b); @@ -116,16 +119,18 @@ private: template T* construct(Args&&... args) { if (currentIndex >= blockSize) { - currentBlock = alloc.allocate(blockSize); + currentBlock = alloc_traits::allocate(alloc, blockSize); allocations.emplace_back(currentBlock); currentIndex = 0; } T* object = ¤tBlock[currentIndex++]; - alloc.construct(object, std::forward(args)...); + alloc_traits::construct(alloc, object, std::forward(args)...); return object; } void reset(std::size_t newBlockSize) { - for (auto allocation : allocations) alloc.deallocate(allocation, blockSize); + for (auto allocation : allocations) { + alloc_traits::deallocate(alloc, allocation, blockSize); + } allocations.clear(); blockSize = std::max(1, newBlockSize); currentBlock = nullptr; @@ -138,6 +143,7 @@ private: std::size_t blockSize = 1; std::vector allocations; Alloc alloc; + typedef typename std::allocator_traits alloc_traits; }; ObjectPool nodes; }; @@ -165,7 +171,7 @@ void Earcut::operator()(const Polygon& points) { indices.reserve(len + points[0].size()); Node* outerNode = linkedList(points[0], true); - if (!outerNode) return; + if (!outerNode || outerNode->prev == outerNode->next) return; if (points.size() > 1) outerNode = eliminateHoles(points, outerNode); @@ -244,8 +250,7 @@ Earcut::filterPoints(Node* start, Node* end) { do { again = false; - if (!p->steiner && (equals(p, p->next) /*|| area(p->prev, p, p->next) == 0*/)) - { + if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) { removeNode(p); p = end = p->prev; @@ -304,7 +309,7 @@ void Earcut::earcutLinked(Node* ear, int pass) { // if this didn't work, try curing all small self-intersections locally else if (pass == 1) { - ear = cureLocalIntersections(ear); + ear = cureLocalIntersections(filterPoints(ear)); earcutLinked(ear, 2); // as a last resort, try splitting the remaining polygon into two @@ -401,7 +406,7 @@ Earcut::cureLocalIntersections(Node* start) { p = p->next; } while (p != start); - return p; + return filterPoints(p); } // try splitting polygon into two and triangulate them independently @@ -464,6 +469,9 @@ void Earcut::eliminateHole(Node* hole, Node* outerNode) { outerNode = findHoleBridge(hole, outerNode); if (outerNode) { Node* b = splitPolygon(outerNode, hole); + + // filter out colinear points around cuts + filterPoints(outerNode, outerNode->next); filterPoints(b, b->next); } } @@ -497,7 +505,7 @@ Earcut::findHoleBridge(Node* hole, Node* outerNode) { if (!m) return 0; - if (hx == qx) return m->prev; + if (hx == qx) return m; // hole touches outer segment; pick leftmost endpoint // look for points inside the triangle of hole Vertex, segment intersection and endpoint; // if there are no points found, we have a valid connection; @@ -507,28 +515,35 @@ Earcut::findHoleBridge(Node* hole, Node* outerNode) { double tanMin = std::numeric_limits::infinity(); double tanCur = 0; - p = m->next; + p = m; double mx = m->x; double my = m->y; - while (p != stop) { + do { 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)) { + if (locallyInside(p, hole) && + (tanCur < tanMin || (tanCur == tanMin && (p->x > m->x || sectorContainsSector(m, p))))) { m = p; tanMin = tanCur; } } p = p->next; - } + } while (p != stop); return m; } +// whether sector in vertex m contains sector in vertex p in the same coordinates +template +bool Earcut::sectorContainsSector(const Node* m, const Node* p) { + return area(m->prev, m, p->prev) < 0 && area(p->next, m, m->next) < 0; +} + // interlink polygon nodes in z-order template void Earcut::indexCurve(Node* start) { @@ -644,7 +659,8 @@ Earcut::getLeftmost(Node* start) { Node* p = start; Node* leftmost = start; do { - if (p->x < leftmost->x) leftmost = p; + if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y)) + leftmost = p; p = p->next; } while (p != start); @@ -662,8 +678,10 @@ bool Earcut::pointInTriangle(double ax, double ay, double bx, double by, doub // 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); + return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && // dones't intersect other edges + ((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible + (area(a->prev, a, b->prev) != 0.0 || area(a, b->prev, b) != 0.0)) || // does not create opposite-facing sectors + (equals(a, b) && area(a->prev, a, a->next) > 0 && area(b->prev, b, b->next) > 0)); // special zero-length case } // signed area of a triangle @@ -681,10 +699,33 @@ bool Earcut::equals(const Node* p1, const Node* p2) { // 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); + int o1 = sign(area(p1, q1, p2)); + int o2 = sign(area(p1, q1, q2)); + int o3 = sign(area(p2, q2, p1)); + int o4 = sign(area(p2, q2, q1)); + + if (o1 != o2 && o3 != o4) return true; // general case + + if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 + if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 + if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 + if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 + + return false; +} + +// for collinear points p, q, r, check if point q lies on segment pr +template +bool Earcut::onSegment(const Node* p, const Node* q, const Node* r) { + return q->x <= std::max(p->x, r->x) && + q->x >= std::min(p->x, r->x) && + q->y <= std::max(p->y, r->y) && + q->y >= std::min(p->y, r->y); +} + +template +int Earcut::sign(double val) { + return (0.0 < val) - (val < 0.0); } // check if a polygon diagonal intersects any polygon segments diff --git a/src/common/utility/basics.h b/src/common/utility/basics.h index 34450469a..703a84c30 100644 --- a/src/common/utility/basics.h +++ b/src/common/utility/basics.h @@ -2,6 +2,7 @@ #include #include +#include #define MAXWIDTH 12000 #define MAXHEIGHT 5000 @@ -101,3 +102,6 @@ enum EStateUseFlags SUF_WEAPON = 4, SUF_ITEM = 8, }; + +using std::min; +using std::max; diff --git a/src/common/utility/cmdlib.h b/src/common/utility/cmdlib.h index 3792030ae..b998cf99d 100644 --- a/src/common/utility/cmdlib.h +++ b/src/common/utility/cmdlib.h @@ -103,4 +103,14 @@ inline void fillshort(void* buff, size_t count, uint16_t clear) template inline constexpr T Sgn(const T& val) { return (val > 0) - (val < 0); } +inline int sizeToBits(int w) +{ + int j = 15; + + while ((j > 1) && ((1 << j) > w)) + j--; + return j; +} + + #endif diff --git a/src/common/utility/matrix.h b/src/common/utility/matrix.h index 1c46c2397..81d7be6f2 100644 --- a/src/common/utility/matrix.h +++ b/src/common/utility/matrix.h @@ -33,9 +33,7 @@ class VSMatrix { public: - VSMatrix() - { - } + VSMatrix() = default; VSMatrix(int) { diff --git a/src/r_data/gldefs.cpp b/src/r_data/gldefs.cpp index 44467244d..345bad544 100644 --- a/src/r_data/gldefs.cpp +++ b/src/r_data/gldefs.cpp @@ -54,7 +54,6 @@ void AddLightAssociation(const char *actor, const char *frame, const char *light void InitializeActorLights(TArray &LightAssociations); void ParseColorization(FScanner& sc); -TArray usershaders; extern TDeletingArray LightDefaults; extern int AttenuationIsSet; diff --git a/src/rendering/hwrenderer/doom_aabbtree.h b/src/rendering/hwrenderer/doom_aabbtree.h index f41612b44..37afb8cac 100644 --- a/src/rendering/hwrenderer/doom_aabbtree.h +++ b/src/rendering/hwrenderer/doom_aabbtree.h @@ -1,6 +1,8 @@ #pragma once #include "hw_aabbtree.h" +struct FLevelLocals; + // Axis aligned bounding box tree used for ray testing treelines. class DoomLevelAABBTree : public hwrenderer::LevelAABBTree { diff --git a/src/rendering/hwrenderer/scene/hw_skyportal.cpp b/src/rendering/hwrenderer/scene/hw_skyportal.cpp index f02ce5a29..7000cd8b9 100644 --- a/src/rendering/hwrenderer/scene/hw_skyportal.cpp +++ b/src/rendering/hwrenderer/scene/hw_skyportal.cpp @@ -62,7 +62,7 @@ void HWSkyPortal::DrawContents(HWDrawInfo *di, FRenderState &state) auto skybox = origin->texture[0] ? dynamic_cast(origin->texture[0]->GetTexture()) : nullptr; if (skybox) { - vertexBuffer->RenderBox(state, origin->skytexno1, skybox, origin->x_offset[0], origin->sky2, di->Level->info->pixelstretch, di->Level->info->skyrotatevector, di->Level->info->skyrotatevector2); + vertexBuffer->RenderBox(state, skybox, origin->x_offset[0], origin->sky2, di->Level->info->pixelstretch, di->Level->info->skyrotatevector, di->Level->info->skyrotatevector2); } else {