From 0f64dbf61ed83ded5a537d292442f051a1565318 Mon Sep 17 00:00:00 2001 From: Emile Belanger Date: Sat, 19 Jun 2021 14:03:40 +0100 Subject: [PATCH] Re-sync with gzdoom/master and add my latest fixes (#3) * - Fixed bug with BishopPuff moving in wrong direction * - pull new widepix fix for hexen sprites * - font system overhaul. This eliminates nearly all palette dependencies, most importantly font translation will now be done on True Color data, making translations on True Color font less destructive. * Force gl_customshader = false, some fixes to shader code. * Delete some unused code * Actually read GL_MAX_TEXTURE_SIZE for max size * - fix for "Return to Phobos (return01.wad) has an ancient bug in map E1M2 where the switch to raise the exit bridge doesn't work in versions of Doom past patch 1.2." * - widepix update * - add missing break statement * - do not calculate translations for empty fonts. FONTDEFS will create some due to lack of game filtering. * - fix DaggerAlert using FindState incorrectly. * Fixed wrong parameter type passed into TryMove here * - fixed: GetColorTranslation did not handle translation-less single pic 'fonts' properly. * - fixed: Actors did not set the position when spawning a dynamic light. It always reused the previous content of the 'Pos' field which was either undefined or an older position where the actor was located when last spawning a light. * - fixed generation of default color range for the option menu font. * - added fallback to the parenthesis glyphs for the braces. * Add 'AddDialogues' in MAPINFO to additively add Strife NPC dialogs without overwriting each other. * Added APROP_SoundClass - Added APROP_SoundClass to GetActorProperty, SetActorProperty, CheckActorProperty * APROP_SoundClass inits a default value and checks for null pointers - Gave default init value to SoundClass as "Player" - Changed SoundClass detection to use the if/else structure - Checked for null pointer in S_FindSkinnedSound when reading the player's sound class * Made S_FindSkinnedSound to use GetSoundClass again - I couldn't simply init SoundClass to NAME_SoundClass, even after converting it to the appropriate type. Probably because NAME_SoundClass hasn't been parsed from decorate yet. Instead, I change it to NAME_SoundClass through GetSoundClass if it's valid and currently "player". - The skin checker code in GetSoundClass now checks if the SoundClass is equal to NAME_SoundClass. This mechanism exists so that way reverting the SoundClass to NAME_SoundClass processes the skin soundclass code. If it's different, the code is not processed. - Just returns sclass. This is never null, so there's no need to check if so. - S_FindSkinnedSound just uses GetSoundClass. This makes sure skins are checked. * Update d_player.h - Deleted some comment fragments I left when trying to get the SoundClass to init to NAME_SoundClass * Sound Class renovations - SoundClass is instantiated to "" by default. Since this property is only used when it is not empty (otherwise GetSoundClass just defaults to player), we can get away with this. - We may want the soundclass to remain the same if we explicitly set it to the same one that is currently used (say, we set SoundClass to "Caleb" so all other skins can use it) - GetActorProperty for APROP_SoundClass just calls GetSoundClass, - CheckActorProperty also just runs GetSoundClass - GetSoundClass is no longer a static method. We needed to access it in other places. - Made renovations to GetSoundClass. First of all, SoundClass is no longer instantiated there. Secondly, skinned sounds are now returned if SoundClass is empty. Thirdly, "sclass" in this method will return the default soundclass of the player pawn or SoundClass, depending on if SoundClass is empty. Finally, sclass will retrieve "player" if it is empty. * APROP_Soundclass update again - Just set init for SoundClass to empty. - Removed code block from SetActorProperty for APROP_Soundclass that does nothing - Lower-cased soundclass in FSerializer - Created a new const char to read the player's soundclass. If the playerpawn returns NAME_None for it's default, then it will set defaultsoundclass to "player". After running the skin code, the function now returns defaultsoundclass or soundclass, depending if soundclass is empty or not. - Renamed GetSoundClass to S_GetSoundClass * - Fixed crash calling ChangeSky() with an invalid texture. * - moved Doom specific font init code out of the backend. * - let dynamic lights call UpdateLocation instead of just setting their position right after being spawned. This ensures that the position is correct and that everything gets set up properly. * - fixed parsing of MAPxx par times in BEX lumps https://forum.zdoom.org/viewtopic.php?t=72458 * - corrected the NUL checks in S_FindSkinnedSound. * Make sprite shadows ignore float bob * - fixed explosive damage radius for clericflame. This was fixed before but must have gotten lost somehow... * - fixed permission validation in OptionMenuItemCommand.DoCommand. This was missing the InMenu check like the other critical menu functions. * - added detection of macOS 12 Monterey * - prevent redundant string copying in Strife conversation parser strifedialogue.cpp:110:22: warning: loop variable 'addd' of type 'const FString' creates a copy from type 'const FString' [-Wrange-loop-analysis] * - Fixed impassable exit line in 007ltsd.wad E4M7 * - fixed initialization of model frames Replaced loop arrays initialization and obvious comments with something more readable, I hope https://forum.zdoom.org/viewtopic.php?t=72523 * - fix missing border flat on heretic shareware * Stop colormap being applied to 2D drawing * - fixed parsing of 2D vectors in OBJ model loader There is no `TVector2<>` constructor that accepts a pointer to float. However, there is such constructor in `TVector3<>`, so `TVector2<>` can be constructed from `float*` implicitly via temporary `TVector3<>` object. * - added `TVector2<>` constructor from `float*` As we seem to do not like explicit constructors, this will make temporary `TVector3<>` object creation much less probable * - fixed bad type in FxFontCast. * - fixed potential crash when sound sequence is destroyed Level can be unset if sound sequence destruction happens after saved game loading failure https://forum.zdoom.org/viewtopic.php?t=72551 * Fix light binding when using pipeline buffer on OpenGL Co-authored-by: Dasperal Co-authored-by: Rachael Alexanderson Co-authored-by: Christoph Oelckers Co-authored-by: Chronos Ouroboros Co-authored-by: nashmuhandes Co-authored-by: Zandrewnum Co-authored-by: Shiny Metagross <30511800+ShinyMetagross@users.noreply.github.com> Co-authored-by: drfrag Co-authored-by: alexey.lysiuk Co-authored-by: emily Co-authored-by: Player701 --- src/common/2d/v_drawtext.cpp | 15 +- src/common/engine/palettecontainer.h | 14 + src/common/fonts/font.cpp | 318 +++------------- src/common/fonts/fontinternals.h | 1 - src/common/fonts/hexfont.cpp | 128 +++---- src/common/fonts/singlelumpfont.cpp | 221 +++-------- src/common/fonts/singlepicfont.cpp | 6 +- src/common/fonts/specialfont.cpp | 111 +++--- src/common/fonts/v_font.cpp | 357 ++++++++++-------- src/common/fonts/v_font.h | 17 +- src/common/models/models_obj.cpp | 7 +- src/common/platform/posix/cocoa/i_main.mm | 17 +- src/common/rendering/gl/gl_renderstate.cpp | 2 +- src/common/scripting/backend/codegen.cpp | 2 +- src/common/scripting/interface/vmnatives.cpp | 8 +- src/common/textures/bitmap.h | 5 + src/common/textures/formats/fontchars.cpp | 98 +---- src/common/textures/formats/fontchars.h | 25 +- src/common/textures/hw_material.cpp | 1 + src/common/textures/hw_texcontainer.h | 12 +- src/common/textures/texture.cpp | 7 +- src/common/utility/utf8.cpp | 2 + src/common/utility/vectors.h | 5 + src/d_main.cpp | 2 + src/gamedata/d_dehacked.cpp | 2 +- src/gamedata/doomfont.h | 147 ++++++++ src/gamedata/gi.cpp | 1 + src/gamedata/gi.h | 1 + src/maploader/strifedialogue.cpp | 18 + src/playsim/a_dynlight.cpp | 1 + src/playsim/d_player.h | 1 + src/playsim/p_acs.cpp | 13 + src/playsim/p_user.cpp | 5 +- src/r_data/models.cpp | 29 +- src/rendering/hwrenderer/hw_entrypoint.cpp | 3 + src/rendering/hwrenderer/scene/hw_sprites.cpp | 3 +- src/rendering/r_sky.cpp | 4 +- src/rendering/swrenderer/plane/r_skyplane.cpp | 2 +- src/sound/s_advsound.cpp | 20 +- src/sound/s_sndseq.cpp | 2 +- src/sound/s_sound.h | 1 + wadsrc/static/mapinfo/heretic.txt | 2 +- wadsrc/static/mapinfo/hereticshareware.txt | 7 + wadsrc/static/zscript/actors/attacks.zs | 2 +- wadsrc/static/zscript/actors/hexen/bishop.zs | 2 +- .../zscript/actors/hexen/clericflame.zs | 2 +- .../zscript/actors/shared/fastprojectile.zs | 2 +- wadsrc/static/zscript/level_compatibility.zs | 15 + wadsrc_extra/static/iwadinfo.txt | 2 +- wadsrc_widescreen/static | 2 +- 50 files changed, 773 insertions(+), 897 deletions(-) create mode 100644 src/gamedata/doomfont.h create mode 100644 wadsrc/static/mapinfo/hereticshareware.txt diff --git a/src/common/2d/v_drawtext.cpp b/src/common/2d/v_drawtext.cpp index 9a778eb13..97d418fd7 100644 --- a/src/common/2d/v_drawtext.cpp +++ b/src/common/2d/v_drawtext.cpp @@ -173,9 +173,8 @@ void DrawChar(F2DDrawer *drawer, FFont* font, int normalcolor, double x, double FGameTexture* pic; int dummy; - bool redirected; - if (NULL != (pic = font->GetChar(character, normalcolor, &dummy, &redirected))) + if (NULL != (pic = font->GetChar(character, normalcolor, &dummy))) { DrawParms parms; Va_List tags; @@ -188,7 +187,7 @@ void DrawChar(F2DDrawer *drawer, FFont* font, int normalcolor, double x, double } bool palettetrans = (normalcolor == CR_UNDEFINED && parms.TranslationId != 0); PalEntry color = 0xffffffff; - if (!palettetrans) parms.TranslationId = redirected ? -1 : font->GetColorTranslation((EColorRange)normalcolor, &color); + if (!palettetrans) parms.TranslationId = font->GetColorTranslation((EColorRange)normalcolor, &color); parms.color = PalEntry((color.a * parms.color.a) / 255, (color.r * parms.color.r) / 255, (color.g * parms.color.g) / 255, (color.b * parms.color.b) / 255); drawer->AddTexture(pic, parms); } @@ -204,9 +203,8 @@ void DrawChar(F2DDrawer *drawer, FFont *font, int normalcolor, double x, double FGameTexture *pic; int dummy; - bool redirected; - if (NULL != (pic = font->GetChar(character, normalcolor, &dummy, &redirected))) + if (NULL != (pic = font->GetChar(character, normalcolor, &dummy))) { DrawParms parms; uint32_t tag = ListGetInt(args); @@ -214,7 +212,7 @@ void DrawChar(F2DDrawer *drawer, FFont *font, int normalcolor, double x, double if (!res) return; bool palettetrans = (normalcolor == CR_UNDEFINED && parms.TranslationId != 0); PalEntry color = 0xffffffff; - if (!palettetrans) parms.TranslationId = redirected ? -1 : font->GetColorTranslation((EColorRange)normalcolor, &color); + if (!palettetrans) parms.TranslationId = font->GetColorTranslation((EColorRange)normalcolor, &color); parms.color = PalEntry((color.a * parms.color.a) / 255, (color.r * parms.color.r) / 255, (color.g * parms.color.g) / 255, (color.b * parms.color.b) / 255); drawer->AddTexture(pic, parms); } @@ -316,11 +314,10 @@ void DrawTextCommon(F2DDrawer *drawer, FFont *font, int normalcolor, double x, d continue; } - bool redirected = false; - if (NULL != (pic = font->GetChar(c, currentcolor, &w, &redirected))) + if (NULL != (pic = font->GetChar(c, currentcolor, &w))) { // if palette translation is used, font colors will be ignored. - if (!palettetrans) parms.TranslationId = redirected? -1 : trans; + if (!palettetrans) parms.TranslationId = trans; SetTextureParms(drawer, &parms, pic, cx, cy); if (parms.cellx) { diff --git a/src/common/engine/palettecontainer.h b/src/common/engine/palettecontainer.h index cac902929..1103f5c34 100644 --- a/src/common/engine/palettecontainer.h +++ b/src/common/engine/palettecontainer.h @@ -70,12 +70,26 @@ inline constexpr uint32_t TRANSLATION(uint8_t a, uint32_t b) { return (a << TRANSLATION_SHIFT) | b; } +inline constexpr uint32_t LuminosityTranslation(int range, uint8_t min, uint8_t max) +{ + // ensure that the value remains positive. + return ( (1 << 30) | ((range&0x3fff) << 16) | (min << 8) | max ); +} + +inline constexpr bool IsLuminosityTranslation(int trans) +{ + return trans > 0 && (trans & (1 << 30)); +} + inline constexpr int GetTranslationType(uint32_t trans) { + assert(!IsLuminosityTranslation(trans)); return (trans & TRANSLATIONTYPE_MASK) >> TRANSLATION_SHIFT; } + inline constexpr int GetTranslationIndex(uint32_t trans) { + assert(!IsLuminosityTranslation(trans)); return (trans & TRANSLATION_MASK); } diff --git a/src/common/fonts/font.cpp b/src/common/fonts/font.cpp index 755ef1129..ff2279e34 100644 --- a/src/common/fonts/font.cpp +++ b/src/common/fonts/font.cpp @@ -83,7 +83,6 @@ FFont::FFont (const char *name, const char *nametemplate, const char *filetempla Next = FirstFont; FirstFont = this; Cursor = '_'; - ActiveColors = 0; SpaceWidth = 0; FontHeight = 0; uint8_t pp = 0; @@ -333,23 +332,13 @@ FFont::FFont (const char *name, const char *nametemplate, const char *filetempla TexMan.AddGameTexture(tex); Chars[i].OriginalPic = tex; - if (!noTranslate) - { - Chars[i].TranslatedPic = MakeGameTexture(new FImageTexture(new FFontChar1(orig->GetImage())), nullptr, ETextureType::FontChar); - Chars[i].TranslatedPic->CopySize(pic, true); - TexMan.AddGameTexture(Chars[i].TranslatedPic); - } - else - { - Chars[i].TranslatedPic = tex; - } - if (sysCallbacks.FontCharCreated) sysCallbacks.FontCharCreated(pic, Chars[i].OriginalPic, Chars[i].TranslatedPic); + if (sysCallbacks.FontCharCreated) sysCallbacks.FontCharCreated(pic, Chars[i].OriginalPic, Chars[i].OriginalPic); - Chars[i].XMove = (int)Chars[i].TranslatedPic->GetDisplayWidth(); + Chars[i].XMove = (int)Chars[i].OriginalPic->GetDisplayWidth(); } else { - Chars[i].TranslatedPic = nullptr; + Chars[i].OriginalPic = nullptr; Chars[i].XMove = INT_MIN; } } @@ -360,7 +349,7 @@ FFont::FFont (const char *name, const char *nametemplate, const char *filetempla { SpaceWidth = spacewidth; } - else if ('N' - FirstChar >= 0 && 'N' - FirstChar < count && Chars['N' - FirstChar].TranslatedPic != nullptr) + else if ('N' - FirstChar >= 0 && 'N' - FirstChar < count && Chars['N' - FirstChar].OriginalPic != nullptr) { SpaceWidth = (Chars['N' - FirstChar].XMove + 1) / 2; } @@ -444,11 +433,7 @@ void FFont::ReadSheetFont(TArray &folderdata, int width, int height Chars[i].OriginalPic = (*lump)->GetUseType() == ETextureType::FontChar? (*lump) : MakeGameTexture(pic, nullptr, ETextureType::FontChar); Chars[i].OriginalPic->SetUseType(ETextureType::FontChar); Chars[i].OriginalPic->CopySize(*lump, true); - Chars[i].TranslatedPic = MakeGameTexture(new FImageTexture(new FFontChar1(pic->GetImage())), nullptr, ETextureType::FontChar); - Chars[i].TranslatedPic->CopySize(*lump, true); - Chars[i].TranslatedPic->SetUseType(ETextureType::FontChar); if (Chars[i].OriginalPic != *lump) TexMan.AddGameTexture(Chars[i].OriginalPic); - TexMan.AddGameTexture(Chars[i].TranslatedPic); } Chars[i].XMove = width; } @@ -458,7 +443,7 @@ void FFont::ReadSheetFont(TArray &folderdata, int width, int height // Move the Windows-1252 characters to their proper place. for (int i = 0x80; i < 0xa0; i++) { - if (win1252map[i - 0x80] != i && Chars[i - minchar].TranslatedPic != nullptr && Chars[win1252map[i - 0x80] - minchar].TranslatedPic == nullptr) + if (win1252map[i - 0x80] != i && Chars[i - minchar].OriginalPic != nullptr && Chars[win1252map[i - 0x80] - minchar].OriginalPic == nullptr) { std::swap(Chars[i - minchar], Chars[win1252map[i - 0x80] - minchar]); } @@ -509,24 +494,24 @@ void FFont::CheckCase() } if (myislower(chr)) { - if (Chars[i].TranslatedPic != nullptr) lowercount++; + if (Chars[i].OriginalPic != nullptr) lowercount++; } else { - if (Chars[i].TranslatedPic != nullptr) uppercount++; + if (Chars[i].OriginalPic != nullptr) uppercount++; } } if (lowercount == 0) return; // This is an uppercase-only font and we are done. // The ß needs special treatment because it is far more likely to be supplied lowercase only, even in an uppercase font. - if (Chars[0xdf - FirstChar].TranslatedPic != nullptr) + if (Chars[0xdf - FirstChar].OriginalPic != nullptr) { if (LastChar < 0x1e9e) { Chars.Resize(0x1e9f - FirstChar); LastChar = 0x1e9e; } - if (Chars[0x1e9e - FirstChar].TranslatedPic == nullptr) + if (Chars[0x1e9e - FirstChar].OriginalPic == nullptr) { std::swap(Chars[0xdf - FirstChar], Chars[0x1e9e - FirstChar]); lowercount--; @@ -596,75 +581,14 @@ void FFont::RecordAllTextureColors(uint32_t *usedcolors) { for (unsigned int i = 0; i < Chars.Size(); i++) { - if (Chars[i].TranslatedPic) + if (Chars[i].OriginalPic) { - FFontChar1 *pic = static_cast(Chars[i].TranslatedPic->GetTexture()->GetImage()); - if (pic) - { - // The remap must be temporarily reset here because this can be called on an initialized font. - auto sr = pic->ResetSourceRemap(); - RecordTextureColors(pic, usedcolors); - pic->SetSourceRemap(sr); - } + auto pic = Chars[i].OriginalPic->GetTexture()->GetImage(); + if (pic) RecordTextureColors(pic, usedcolors); } } } -//========================================================================== -// -// SetDefaultTranslation -// -// Builds a translation to map the stock font to a mod provided replacement. -// -//========================================================================== - -void FFont::SetDefaultTranslation(uint32_t *othercolors) -{ - uint32_t mycolors[256] = {}; - RecordAllTextureColors(mycolors); - - uint8_t mytranslation[256], othertranslation[256], myreverse[256], otherreverse[256]; - TArray myluminosity, otherluminosity; - - SimpleTranslation(mycolors, mytranslation, myreverse, myluminosity); - SimpleTranslation(othercolors, othertranslation, otherreverse, otherluminosity); - - FRemapTable remap(ActiveColors); - remap.Remap[0] = 0; - remap.Palette[0] = 0; - remap.ForFont = true; - - for (unsigned l = 1; l < myluminosity.Size(); l++) - { - for (unsigned o = 1; o < otherluminosity.Size()-1; o++) // luminosity[0] is for the transparent color - { - if (myluminosity[l] >= otherluminosity[o] && myluminosity[l] <= otherluminosity[o+1]) - { - PalEntry color1 = GPalette.BaseColors[otherreverse[o]]; - PalEntry color2 = GPalette.BaseColors[otherreverse[o+1]]; - double weight = 0; - if (otherluminosity[o] != otherluminosity[o + 1]) - { - weight = (myluminosity[l] - otherluminosity[o]) / (otherluminosity[o + 1] - otherluminosity[o]); - } - int r = int(color1.r + weight * (color2.r - color1.r)); - int g = int(color1.g + weight * (color2.g - color1.g)); - int b = int(color1.b + weight * (color2.b - color1.b)); - - r = clamp(r, 0, 255); - g = clamp(g, 0, 255); - b = clamp(b, 0, 255); - remap.Remap[l] = ColorMatcher.Pick(r, g, b); - remap.Palette[l] = PalEntry(255, r, g, b); - break; - } - } - } - Translations[CR_UNTRANSLATED] = GPalette.StoreTranslation(TRANSLATION_Internal, &remap); - forceremap = true; -} - - //========================================================================== // // compare @@ -688,160 +612,40 @@ static int compare (const void *arg1, const void *arg2) //========================================================================== // -// FFont :: SimpleTranslation -// -// Colorsused, translation, and reverse must all be 256 entry buffers. -// Colorsused must already be filled out. -// Translation be set to remap the source colors to a new range of -// consecutive colors based at 1 (0 is transparent). -// Reverse will be just the opposite of translation: It maps the new color -// range to the original colors. -// *Luminosity will be an array just large enough to hold the brightness -// levels of all the used colors, in consecutive order. It is sorted from -// darkest to lightest and scaled such that the darkest color is 0.0 and -// the brightest color is 1.0. -// The return value is the number of used colors and thus the number of -// entries in *luminosity. +// FFont :: GetLuminosity // //========================================================================== -int FFont::SimpleTranslation (uint32_t *colorsused, uint8_t *translation, uint8_t *reverse, TArray &Luminosity) +int FFont::GetLuminosity (uint32_t *colorsused, TArray &Luminosity, int* minlum, int* maxlum) { double min, max, diver; - int i, j; - memset (translation, 0, 256); - - reverse[0] = 0; - for (i = 1, j = 1; i < 256; i++) - { - if (colorsused[i]) - { - reverse[j++] = i; - } - } - - qsort (reverse+1, j-1, 1, compare); - - Luminosity.Resize(j); + Luminosity.Resize(256); Luminosity[0] = 0.0; // [BL] Prevent uninitalized memory max = 0.0; min = 100000000.0; - for (i = 1; i < j; i++) + for (int i = 1; i < 256; i++) { - translation[reverse[i]] = i; - - Luminosity[i] = RPART(GPalette.BaseColors[reverse[i]]) * 0.299 + - GPART(GPalette.BaseColors[reverse[i]]) * 0.587 + - BPART(GPalette.BaseColors[reverse[i]]) * 0.114; - if (Luminosity[i] > max) - max = Luminosity[i]; - if (Luminosity[i] < min) - min = Luminosity[i]; + if (colorsused[i]) + { + Luminosity[i] = GPalette.BaseColors[i].r * 0.299 + GPalette.BaseColors[i].g * 0.587 + GPalette.BaseColors[i].b * 0.114; + if (Luminosity[i] > max) max = Luminosity[i]; + if (Luminosity[i] < min) min = Luminosity[i]; + } + else Luminosity[i] = -1; // this color is not of interest. } diver = 1.0 / (max - min); - for (i = 1; i < j; i++) + for (int i = 1; i < 256; i++) { - Luminosity[i] = (Luminosity[i] - min) * diver; + if (colorsused[i]) + { + Luminosity[i] = (Luminosity[i] - min) * diver; + } } + if (minlum) *minlum = int(min); + if (maxlum) *maxlum = int(max); - return j; -} - -//========================================================================== -// -// FFont :: BuildTranslations -// -// Build color translations for this font. Luminosity is an array of -// brightness levels. The ActiveColors member must be set to indicate how -// large this array is. Identity is an array that remaps the colors to -// their original values; it is only used for CR_UNTRANSLATED. Ranges -// is an array of TranslationParm structs defining the ranges for every -// possible color, in order. Palette is the colors to use for the -// untranslated version of the font. -// -//========================================================================== - -void FFont::BuildTranslations (const double *luminosity, const uint8_t *identity, - const void *ranges, int total_colors, const PalEntry *palette, std::function post) -{ - int i, j; - const TranslationParm *parmstart = (const TranslationParm *)ranges; - - FRemapTable remap(total_colors); - remap.ForFont = true; - - // Create different translations for different color ranges - Translations.Clear(); - for (i = 0; i < NumTextColors; i++) - { - if (i == CR_UNTRANSLATED) - { - if (identity != nullptr) - { - memcpy(remap.Remap, identity, ActiveColors); - if (palette != nullptr) - { - memcpy(remap.Palette, palette, ActiveColors * sizeof(PalEntry)); - } - else - { - remap.Palette[0] = GPalette.BaseColors[identity[0]] & MAKEARGB(0, 255, 255, 255); - for (j = 1; j < ActiveColors; ++j) - { - remap.Palette[j] = GPalette.BaseColors[identity[j]] | MAKEARGB(255, 0, 0, 0); - } - } - Translations.Push(GPalette.StoreTranslation(TRANSLATION_Internal, &remap)); - } - else - { - Translations.Push(Translations[0]); - } - continue; - } - - assert(parmstart->RangeStart >= 0); - - remap.Remap[0] = 0; - remap.Palette[0] = 0; - remap.ForFont = true; - - for (j = 1; j < ActiveColors; j++) - { - int v = int(luminosity[j] * 256.0); - - // Find the color range that this luminosity value lies within. - const TranslationParm *parms = parmstart - 1; - do - { - parms++; - if (parms->RangeStart <= v && parms->RangeEnd >= v) - break; - } - while (parms[1].RangeStart > parms[0].RangeEnd); - - // Linearly interpolate to find out which color this luminosity level gets. - int rangev = ((v - parms->RangeStart) << 8) / (parms->RangeEnd - parms->RangeStart); - int r = ((parms->Start[0] << 8) + rangev * (parms->End[0] - parms->Start[0])) >> 8; // red - int g = ((parms->Start[1] << 8) + rangev * (parms->End[1] - parms->Start[1])) >> 8; // green - int b = ((parms->Start[2] << 8) + rangev * (parms->End[2] - parms->Start[2])) >> 8; // blue - r = clamp(r, 0, 255); - g = clamp(g, 0, 255); - b = clamp(b, 0, 255); - remap.Remap[j] = ColorMatcher.Pick(r, g, b); - remap.Palette[j] = PalEntry(255,r,g,b); - } - if (post) post(&remap); - Translations.Push(GPalette.StoreTranslation(TRANSLATION_Internal, &remap)); - - // Advance to the next color range. - while (parmstart[1].RangeStart > parmstart[0].RangeEnd) - { - parmstart++; - } - parmstart++; - } + return 256; } //========================================================================== @@ -852,6 +656,10 @@ void FFont::BuildTranslations (const double *luminosity, const uint8_t *identity int FFont::GetColorTranslation (EColorRange range, PalEntry *color) const { + // Single pic fonts don not set up their translation table and must always return 0. + if (Translations.Size() == 0) return 0; + assert(Translations.Size() == NumTextColors); + if (noTranslate) { PalEntry retcolor = PalEntry(255, 255, 255, 255); @@ -862,11 +670,10 @@ int FFont::GetColorTranslation (EColorRange range, PalEntry *color) const } if (color != nullptr) *color = retcolor; } - if (ActiveColors == 0 || range == CR_UNDEFINED) + if (range == CR_UNDEFINED) return -1; else if (range >= NumTextColors) range = CR_UNTRANSLATED; - //if (range == CR_UNTRANSLATED && !translateUntranslated) return nullptr; return Translations[range]; } @@ -889,7 +696,7 @@ int FFont::GetCharCode(int code, bool needpic) const // regular chars turn negative when the 8th bit is set. code &= 255; } - if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr)) + if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].OriginalPic != nullptr)) { return code; } @@ -903,7 +710,7 @@ int FFont::GetCharCode(int code, bool needpic) const if (myislower(code)) { code = upperforlower[code]; - if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr)) + if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].OriginalPic != nullptr)) { return code; } @@ -912,7 +719,7 @@ int FFont::GetCharCode(int code, bool needpic) const while ((newcode = stripaccent(code)) != code) { code = newcode; - if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr)) + if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].OriginalPic != nullptr)) { return code; } @@ -926,7 +733,7 @@ int FFont::GetCharCode(int code, bool needpic) const while ((newcode = stripaccent(code)) != code) { code = newcode; - if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr)) + if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].OriginalPic != nullptr)) { return code; } @@ -944,7 +751,7 @@ int FFont::GetCharCode(int code, bool needpic) const while ((newcode = stripaccent(code)) != code) { code = newcode; - if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].TranslatedPic != nullptr)) + if (code >= FirstChar && code <= LastChar && (!needpic || Chars[code - FirstChar].OriginalPic != nullptr)) { return code; } @@ -961,7 +768,7 @@ int FFont::GetCharCode(int code, bool needpic) const // //========================================================================== -FGameTexture *FFont::GetChar (int code, int translation, int *const width, bool *redirected) const +FGameTexture *FFont::GetChar (int code, int translation, int *const width) const { code = GetCharCode(code, true); int xmove = SpaceWidth; @@ -979,19 +786,8 @@ FGameTexture *FFont::GetChar (int code, int translation, int *const width, bool if (code < 0) return nullptr; - if ((translation == CR_UNTRANSLATED || translation == CR_UNDEFINED) && !forceremap) - { - bool redirect = Chars[code].OriginalPic && Chars[code].OriginalPic != Chars[code].TranslatedPic; - if (redirected) *redirected = redirect; - if (redirect) - { - assert(Chars[code].OriginalPic->GetUseType() == ETextureType::FontChar); - return Chars[code].OriginalPic; - } - } - if (redirected) *redirected = false; - assert(Chars[code].TranslatedPic->GetUseType() == ETextureType::FontChar); - return Chars[code].TranslatedPic; + assert(Chars[code].OriginalPic->GetUseType() == ETextureType::FontChar); + return Chars[code].OriginalPic; } //========================================================================== @@ -1172,31 +968,29 @@ void FFont::LoadTranslations() { unsigned int count = LastChar - FirstChar + 1; uint32_t usedcolors[256] = {}; - uint8_t identity[256]; TArray Luminosity; for (unsigned int i = 0; i < count; i++) { - if (Chars[i].TranslatedPic) + if (Chars[i].OriginalPic) { - FFontChar1 *pic = static_cast(Chars[i].TranslatedPic->GetTexture()->GetImage()); - if (pic) - { - pic->SetSourceRemap(nullptr); // Force the FFontChar1 to return the same pixels as the base texture - RecordTextureColors(pic, usedcolors); - } + auto pic = Chars[i].OriginalPic->GetTexture()->GetImage(); + if (pic) RecordTextureColors(pic, usedcolors); } } - ActiveColors = SimpleTranslation (usedcolors, PatchRemap, identity, Luminosity); + int minlum = 0, maxlum = 0; + GetLuminosity (usedcolors, Luminosity, &minlum, &maxlum); - for (unsigned int i = 0; i < count; i++) + // Here we can set everything to a luminosity translation. + + // Create different translations for different color ranges + Translations.Resize(NumTextColors); + for (int i = 0; i < NumTextColors; i++) { - if(Chars[i].TranslatedPic) - static_cast(Chars[i].TranslatedPic->GetTexture()->GetImage())->SetSourceRemap(PatchRemap); + if (i == CR_UNTRANSLATED) Translations[i] = 0; + else Translations[i] = LuminosityTranslation(i*2 + TranslationType, minlum, maxlum); } - - BuildTranslations (Luminosity.Data(), identity, &TranslationParms[TranslationType][0], ActiveColors, nullptr); } //========================================================================== diff --git a/src/common/fonts/fontinternals.h b/src/common/fonts/fontinternals.h index dc942319e..b74750c57 100644 --- a/src/common/fonts/fontinternals.h +++ b/src/common/fonts/fontinternals.h @@ -3,7 +3,6 @@ #include #include "tarray.h" -// This structure is used by BuildTranslations() to hold color information. struct TranslationParm { short RangeStart; // First level for this range diff --git a/src/common/fonts/hexfont.cpp b/src/common/fonts/hexfont.cpp index 1c23d3bd0..705195dfd 100644 --- a/src/common/fonts/hexfont.cpp +++ b/src/common/fonts/hexfont.cpp @@ -50,6 +50,8 @@ struct HexDataSource TArray glyphdata; unsigned glyphmap[65536] = {}; + PalEntry ConsolePal[18], SmallPal[18]; + //========================================================================== // // parse a HEX font @@ -83,6 +85,17 @@ struct HexDataSource if (codepoint > LastChar) LastChar = codepoint; } } + + ConsolePal[0] = SmallPal[0] = 0; + for (int i = 1; i < 18; i++) + { + double lum = i == 1 ? 0.01 : 0.5 + (i - 2) * (0.5 / 17.); + uint8_t lumb = (uint8_t(lum * 255)); + + ConsolePal[i] = PalEntry(255, lumb, lumb, lumb); + lumb = i * 255 / 17; + SmallPal[i] = PalEntry(255, lumb, lumb, lumb); + } } }; @@ -95,6 +108,7 @@ public: FHexFontChar(uint8_t *sourcedata, int swidth, int width, int height); TArray CreatePalettedPixels(int conversion) override; + int CopyPixels(FBitmap* bmp, int conversion); protected: int SourceWidth; @@ -159,12 +173,23 @@ TArray FHexFontChar::CreatePalettedPixels(int) return Pixels; } +int FHexFontChar::CopyPixels(FBitmap* bmp, int conversion) +{ + if (conversion == luminance) conversion = normal; // luminance images have no use as an RGB source. + PalEntry* palette = hexdata.ConsolePal; + auto ppix = CreatePalettedPixels(conversion); + bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, palette, nullptr); + return 0; + +} + class FHexFontChar2 : public FHexFontChar { public: FHexFontChar2(uint8_t *sourcedata, int swidth, int width, int height); TArray CreatePalettedPixels(int conversion) override; + int CopyPixels(FBitmap* bmp, int conversion); }; @@ -228,6 +253,15 @@ TArray FHexFontChar2::CreatePalettedPixels(int) return Pixels; } +int FHexFontChar2::CopyPixels(FBitmap* bmp, int conversion) +{ + if (conversion == luminance) conversion = normal; // luminance images have no use as an RGB source. + PalEntry* palette = hexdata.SmallPal; + auto ppix = CreatePalettedPixels(conversion); + bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, palette, nullptr); + return 0; +} + class FHexFont : public FFont @@ -267,10 +301,9 @@ public: { auto offset = hexdata.glyphmap[i]; int size = hexdata.glyphdata[offset] / 16; - Chars[i - FirstChar].TranslatedPic = MakeGameTexture(new FImageTexture(new FHexFontChar(&hexdata.glyphdata[offset + 1], size, size * 9, 16)), nullptr, ETextureType::FontChar); - Chars[i - FirstChar].OriginalPic = Chars[i - FirstChar].TranslatedPic; + Chars[i - FirstChar].OriginalPic = MakeGameTexture(new FImageTexture(new FHexFontChar(&hexdata.glyphdata[offset + 1], size, size * 9, 16)), nullptr, ETextureType::FontChar); Chars[i - FirstChar].XMove = size * spacing; - TexMan.AddGameTexture(Chars[i - FirstChar].TranslatedPic); + TexMan.AddGameTexture(Chars[i - FirstChar].OriginalPic); } else Chars[i - FirstChar].XMove = spacing; @@ -285,18 +318,15 @@ public: void LoadTranslations() { - double luminosity[256]; + int minlum = hexdata.ConsolePal[1].r; + int maxlum = hexdata.ConsolePal[17].r; - memset (PatchRemap, 0, 256); - for (int i = 0; i < 18; i++) - { - // Create a gradient similar to the old console font. - PatchRemap[i] = i; - luminosity[i] = i == 1? 0.01 : 0.5 + (i-2) * (0.5 / 17.); + Translations.Resize(NumTextColors); + for (int i = 0; i < NumTextColors; i++) + { + if (i == CR_UNTRANSLATED) Translations[i] = 0; + else Translations[i] = LuminosityTranslation(i * 2 + 1, minlum, maxlum); } - ActiveColors = 18; - - BuildTranslations (luminosity, nullptr, &TranslationParms[1][0], ActiveColors, nullptr); } }; @@ -338,10 +368,9 @@ public: { auto offset = hexdata.glyphmap[i]; int size = hexdata.glyphdata[offset] / 16; - Chars[i - FirstChar].TranslatedPic = MakeGameTexture(new FImageTexture(new FHexFontChar2(&hexdata.glyphdata[offset + 1], size, 2 + size * 8, 18)), nullptr, ETextureType::FontChar); - Chars[i - FirstChar].OriginalPic = Chars[i - FirstChar].TranslatedPic; + Chars[i - FirstChar].OriginalPic = MakeGameTexture(new FImageTexture(new FHexFontChar2(&hexdata.glyphdata[offset + 1], size, 2 + size * 8, 18)), nullptr, ETextureType::FontChar); Chars[i - FirstChar].XMove = size * spacing; - TexMan.AddGameTexture(Chars[i - FirstChar].TranslatedPic); + TexMan.AddGameTexture(Chars[i - FirstChar].OriginalPic); } else Chars[i - FirstChar].XMove = spacing; @@ -356,71 +385,16 @@ public: void LoadTranslations() override { - double luminosity[256]; + int minlum = hexdata.SmallPal[1].r; + int maxlum = hexdata.SmallPal[17].r; - memset(PatchRemap, 0, 256); - for (int i = 0; i < 18; i++) + Translations.Resize(NumTextColors); + for (int i = 0; i < NumTextColors; i++) { - // Create a gradient similar to the old console font. - PatchRemap[i] = i; - luminosity[i] = i / 17.; + if (i == CR_UNTRANSLATED) Translations[i] = 0; + else Translations[i] = LuminosityTranslation(i * 2, minlum, maxlum); } - ActiveColors = 18; - - BuildTranslations(luminosity, nullptr, &TranslationParms[0][0], ActiveColors, nullptr); } - - void SetDefaultTranslation(uint32_t *colors) override - { - double myluminosity[18]; - - myluminosity[0] = 0; - for (int i = 1; i < 18; i++) - { - myluminosity[i] = (i - 1) / 16.; - } - - uint8_t othertranslation[256], otherreverse[256]; - TArray otherluminosity; - - SimpleTranslation(colors, othertranslation, otherreverse, otherluminosity); - - FRemapTable remap(ActiveColors); - remap.Remap[0] = 0; - remap.Palette[0] = 0; - remap.ForFont = true; - - for (unsigned l = 1; l < 18; l++) - { - for (unsigned o = 1; o < otherluminosity.Size() - 1; o++) // luminosity[0] is for the transparent color - { - if (myluminosity[l] >= otherluminosity[o] && myluminosity[l] <= otherluminosity[o + 1]) - { - PalEntry color1 = GPalette.BaseColors[otherreverse[o]]; - PalEntry color2 = GPalette.BaseColors[otherreverse[o + 1]]; - double weight = 0; - if (otherluminosity[o] != otherluminosity[o + 1]) - { - weight = (myluminosity[l] - otherluminosity[o]) / (otherluminosity[o + 1] - otherluminosity[o]); - } - int r = int(color1.r + weight * (color2.r - color1.r)); - int g = int(color1.g + weight * (color2.g - color1.g)); - int b = int(color1.b + weight * (color2.b - color1.b)); - - r = clamp(r, 0, 255); - g = clamp(g, 0, 255); - b = clamp(b, 0, 255); - remap.Remap[l] = ColorMatcher.Pick(r, g, b); - remap.Palette[l] = PalEntry(255, r, g, b); - break; - } - } - } - Translations[CR_UNTRANSLATED] = GPalette.StoreTranslation(TRANSLATION_Internal, &remap); - forceremap = true; - - } - }; diff --git a/src/common/fonts/singlelumpfont.cpp b/src/common/fonts/singlelumpfont.cpp index 3678ee441..dcded6efa 100644 --- a/src/common/fonts/singlelumpfont.cpp +++ b/src/common/fonts/singlelumpfont.cpp @@ -90,26 +90,22 @@ public: void RecordAllTextureColors(uint32_t* usedcolors) override; protected: - void CheckFON1Chars (double *luminosity); - void BuildTranslations2 (); - void FixupPalette (uint8_t *identity, double *luminosity, const uint8_t *palette, - bool rescale, PalEntry *out_palette); + void CheckFON1Chars (); + void FixupPalette (uint8_t *identity, const PalEntry *palette, int* minlum ,int* maxlum); void LoadTranslations (); void LoadFON1 (int lump, const uint8_t *data); void LoadFON2 (int lump, const uint8_t *data); void LoadBMF (int lump, const uint8_t *data); - void CreateFontFromPic (FTextureID picnum); - - static int BMFCompare(const void *a, const void *b); - + enum { FONT1, FONT2, BMFFONT } FontType; - uint8_t PaletteData[768]; + PalEntry Palette[256]; bool RescalePalette; + int ActiveColors = -1; }; @@ -160,29 +156,6 @@ FSingleLumpFont::FSingleLumpFont (const char *name, int lump) : FFont(lump) FirstFont = this; } -//========================================================================== -// -// FSingleLumpFont :: CreateFontFromPic -// -//========================================================================== - -void FSingleLumpFont::CreateFontFromPic (FTextureID picnum) -{ - auto pic = TexMan.GetGameTexture(picnum); - - FontHeight = (int)pic->GetDisplayHeight (); - SpaceWidth = (int)pic->GetDisplayWidth (); - GlobalKerning = 0; - - FirstChar = LastChar = 'A'; - Chars.Resize(1); - Chars[0].TranslatedPic = pic; - Chars[0].OriginalPic = pic; - - // Only one color range. Don't bother with the others. - ActiveColors = 0; -} - //========================================================================== // // FSingleLumpFont :: LoadTranslations @@ -191,28 +164,21 @@ void FSingleLumpFont::CreateFontFromPic (FTextureID picnum) void FSingleLumpFont::LoadTranslations() { - double luminosity[256]; uint8_t identity[256]; - PalEntry local_palette[256]; - bool useidentity = true; - bool usepalette = false; - const void* ranges; unsigned int count = LastChar - FirstChar + 1; + int minlum, maxlum; switch(FontType) { case FONT1: - useidentity = false; - ranges = &TranslationParms[1][0]; - CheckFON1Chars (luminosity); + CheckFON1Chars(); + minlum = 1; + maxlum = 255; break; case BMFFONT: case FONT2: - usepalette = true; - FixupPalette (identity, luminosity, PaletteData, RescalePalette, local_palette); - - ranges = &TranslationParms[0][0]; + FixupPalette (identity, Palette, &minlum, &maxlum); break; default: @@ -223,11 +189,16 @@ void FSingleLumpFont::LoadTranslations() for(unsigned int i = 0;i < count;++i) { - if(Chars[i].TranslatedPic) - static_cast(Chars[i].TranslatedPic->GetTexture()->GetImage())->SetSourceRemap(PatchRemap); + if(Chars[i].OriginalPic) + static_cast(Chars[i].OriginalPic->GetTexture()->GetImage())->SetSourceRemap(Palette); } - BuildTranslations (luminosity, useidentity ? identity : nullptr, ranges, ActiveColors, usepalette ? local_palette : nullptr); + Translations.Resize(NumTextColors); + for (int i = 0; i < NumTextColors; i++) + { + if (i == CR_UNTRANSLATED) Translations[i] = 0; + else Translations[i] = LuminosityTranslation(i * 2 + (FontType == FONT1 ? 1 : 0), minlum, maxlum); + } } //========================================================================== @@ -262,19 +233,20 @@ void FSingleLumpFont::LoadFON1 (int lump, const uint8_t *data) // Move the Windows-1252 characters to their proper place. for (int i = 0x80; i < 0xa0; i++) { - if (win1252map[i-0x80] != i && Chars[i].TranslatedPic != nullptr && Chars[win1252map[i - 0x80]].TranslatedPic == nullptr) + if (win1252map[i-0x80] != i && Chars[i].OriginalPic != nullptr && Chars[win1252map[i - 0x80]].OriginalPic == nullptr) { std::swap(Chars[i], Chars[win1252map[i - 0x80]]); } } + Palette[0] = 0; + for (int i = 1; i < 256; i++) Palette[i] = PalEntry(255, i, i, i); } //========================================================================== // // FSingleLumpFont :: LoadFON2 // -// FON2 is used for everything but the console font. The console font should -// probably use FON2 as well, but oh well. +// FON2 is used for everything but the console font. // //========================================================================== @@ -340,7 +312,11 @@ void FSingleLumpFont::LoadFON2 (int lump, const uint8_t *data) SpaceWidth = totalwidth * 2 / (3 * count); } - memcpy(PaletteData, palette, ActiveColors*3); + Palette[0] = 0; + for (int i = 1; i < ActiveColors; i++) + { + Palette[i] = PalEntry(255, palette[i * 3], palette[i * 3 + 1], palette[i * 3 + 2]); + } data_p = palette + ActiveColors*3; @@ -350,14 +326,12 @@ void FSingleLumpFont::LoadFON2 (int lump, const uint8_t *data) Chars[i].XMove = widths2[i]; if (destSize <= 0) { - Chars[i].TranslatedPic = nullptr; Chars[i].OriginalPic = nullptr; } else { - Chars[i].TranslatedPic = MakeGameTexture(new FImageTexture(new FFontChar2 (lump, int(data_p - data), widths2[i], FontHeight)), nullptr, ETextureType::FontChar); - Chars[i].OriginalPic = Chars[i].TranslatedPic; - TexMan.AddGameTexture(Chars[i].TranslatedPic); + Chars[i].OriginalPic = MakeGameTexture(new FImageTexture(new FFontChar2 (lump, int(data_p - data), widths2[i], FontHeight)), nullptr, ETextureType::FontChar); + TexMan.AddGameTexture(Chars[i].OriginalPic); do { int8_t code = *data_p++; @@ -396,8 +370,6 @@ void FSingleLumpFont::LoadBMF(int lump, const uint8_t *data) int numchars, count, totalwidth, nwidth; int infolen; int i, chari; - uint8_t raw_palette[256*3]; - PalEntry sort_palette[256]; FontType = BMFFONT; FontHeight = data[5]; @@ -439,31 +411,14 @@ void FSingleLumpFont::LoadBMF(int lump, const uint8_t *data) count = LastChar - FirstChar + 1; Chars.Resize(count); // BMF palettes are only six bits per component. Fix that. - for (i = 0; i < ActiveColors*3; ++i) - { - raw_palette[i+3] = (data[17 + i] << 2) | (data[17 + i] >> 4); - } - ActiveColors++; - - // Sort the palette by increasing brightness for (i = 0; i < ActiveColors; ++i) { - PalEntry *pal = &sort_palette[i]; - pal->a = i; // Use alpha part to point back to original entry - pal->r = raw_palette[i*3 + 0]; - pal->g = raw_palette[i*3 + 1]; - pal->b = raw_palette[i*3 + 2]; + int r = (data[17 + i * 3] << 2) | (data[17 + i * 3] >> 4); + int g = (data[18 + i * 3] << 2) | (data[18 + i * 3] >> 4); + int b = (data[19 + i * 3] << 2) | (data[19 + i * 3] >> 4); + Palette[i + 1] = PalEntry(255, r, g, b); // entry 0 (transparent) is not stored in the font file. } - qsort(sort_palette + 1, ActiveColors - 1, sizeof(PalEntry), BMFCompare); - - // Create the PatchRemap table from the sorted "alpha" values. - PatchRemap[0] = 0; - for (i = 1; i < ActiveColors; ++i) - { - PatchRemap[sort_palette[i].a] = i; - } - - memcpy(PaletteData, raw_palette, 768); + ActiveColors++; // Now scan through the characters again, creating glyphs for each one. for (i = chari = 0; i < numchars; ++i, chari += 6 + chardata[chari+1] * chardata[chari+2]) @@ -489,7 +444,6 @@ void FSingleLumpFont::LoadBMF(int lump, const uint8_t *data) -(int8_t)chardata[chari+3], // x offset -(int8_t)chardata[chari+4] // y offset )), nullptr, ETextureType::FontChar); - Chars[chardata[chari] - FirstChar].TranslatedPic = tex; Chars[chardata[chari] - FirstChar].OriginalPic = tex; TexMan.AddGameTexture(tex); } @@ -510,55 +464,33 @@ void FSingleLumpFont::LoadBMF(int lump, const uint8_t *data) FixXMoves(); } -//========================================================================== -// -// FSingleLumpFont :: BMFCompare STATIC -// -// Helper to sort BMF palettes. -// -//========================================================================== - -int FSingleLumpFont::BMFCompare(const void *a, const void *b) -{ - const PalEntry *pa = (const PalEntry *)a; - const PalEntry *pb = (const PalEntry *)b; - - return (pa->r * 299 + pa->g * 587 + pa->b * 114) - - (pb->r * 299 + pb->g * 587 + pb->b * 114); -} - //========================================================================== // // FSingleLumpFont :: CheckFON1Chars // // Scans a FON1 resource for all the color values it uses and sets up -// some tables like SimpleTranslation. Data points to the RLE data for +// some tables. Data points to the RLE data for // the characters. Also sets up the character textures. // //========================================================================== -void FSingleLumpFont::CheckFON1Chars (double *luminosity) +void FSingleLumpFont::CheckFON1Chars() { FileData memLump = fileSystem.ReadFile(Lump); - const uint8_t* data = (const uint8_t*) memLump.GetMem(); + const uint8_t* data = (const uint8_t*)memLump.GetMem(); + const uint8_t* data_p; - uint8_t used[256], reverse[256]; - const uint8_t *data_p; - int i, j; - - memset (used, 0, 256); data_p = data + 8; - for (i = 0; i < 256; ++i) + for (int i = 0; i < 256; ++i) { int destSize = SpaceWidth * FontHeight; - if(!Chars[i].TranslatedPic) + if (!Chars[i].OriginalPic) { - Chars[i].TranslatedPic = MakeGameTexture(new FImageTexture(new FFontChar2 (Lump, int(data_p - data), SpaceWidth, FontHeight)), nullptr, ETextureType::FontChar); - Chars[i].OriginalPic = Chars[i].TranslatedPic; + Chars[i].OriginalPic = MakeGameTexture(new FImageTexture(new FFontChar2(Lump, int(data_p - data), SpaceWidth, FontHeight)), nullptr, ETextureType::FontChar); Chars[i].XMove = SpaceWidth; - TexMan.AddGameTexture(Chars[i].TranslatedPic); + TexMan.AddGameTexture(Chars[i].OriginalPic); } // Advance to next char's data and count the used colors. @@ -567,87 +499,52 @@ void FSingleLumpFont::CheckFON1Chars (double *luminosity) int8_t code = *data_p++; if (code >= 0) { - destSize -= code+1; + destSize -= code + 1; while (code-- >= 0) { - used[*data_p++] = 1; + data_p++; } } else if (code != -128) { - used[*data_p++] = 1; + data_p++; destSize -= 1 - code; } } while (destSize > 0); } - - memset (PatchRemap, 0, 256); - reverse[0] = 0; - for (i = 1, j = 1; i < 256; ++i) - { - if (used[i]) - { - reverse[j++] = i; - } - } - for (i = 1; i < j; ++i) - { - PatchRemap[reverse[i]] = i; - luminosity[i] = (reverse[i] - 1) / 254.0; - } - ActiveColors = j; + ActiveColors = 256; } //========================================================================== // // FSingleLumpFont :: FixupPalette // -// Finds the best matches for the colors used by a FON2 font and sets up -// some tables like SimpleTranslation. +// Finds the best matches for the colors used by a FON2 font and finds thr +// used luminosity range // //========================================================================== -void FSingleLumpFont::FixupPalette (uint8_t *identity, double *luminosity, const uint8_t *palette, bool rescale, PalEntry *out_palette) +void FSingleLumpFont::FixupPalette (uint8_t *identity, const PalEntry *palette, int *pminlum, int *pmaxlum) { - int i; double maxlum = 0.0; double minlum = 100000000.0; - double diver; identity[0] = 0; palette += 3; // Skip the transparent color - for (i = 1; i < ActiveColors; ++i, palette += 3) + for (int i = 1; i < ActiveColors; ++i, palette ++) { - int r = palette[0]; - int g = palette[1]; - int b = palette[2]; + int r = palette->r; + int g = palette->g; + int b = palette->b; double lum = r*0.299 + g*0.587 + b*0.114; identity[i] = ColorMatcher.Pick(r, g, b); - luminosity[i] = lum; - out_palette[i].r = r; - out_palette[i].g = g; - out_palette[i].b = b; - out_palette[i].a = 255; - if (lum > maxlum) - maxlum = lum; - if (lum < minlum) - minlum = lum; + if (lum > maxlum) maxlum = lum; + if (lum < minlum) minlum = lum; } - out_palette[0] = 0; - if (rescale) - { - diver = 1.0 / (maxlum - minlum); - } - else - { - diver = 1.0 / 255.0; - } - for (i = 1; i < ActiveColors; ++i) - { - luminosity[i] = (luminosity[i] - minlum) * diver; - } + if (pminlum) *pminlum = int(minlum); + if (pmaxlum) *pmaxlum = int(maxlum); } //========================================================================== @@ -661,13 +558,11 @@ void FSingleLumpFont::FixupPalette (uint8_t *identity, double *luminosity, const void FSingleLumpFont::RecordAllTextureColors(uint32_t* usedcolors) { - double luminosity[256]; uint8_t identity[256]; - PalEntry local_palette[256]; if (FontType == BMFFONT || FontType == FONT2) { - FixupPalette(identity, luminosity, PaletteData, RescalePalette, local_palette); + FixupPalette(identity, Palette, nullptr, nullptr); for (int i = 0; i < 256; i++) { if (identity[i] != 0) usedcolors[identity[i]]++; diff --git a/src/common/fonts/singlepicfont.cpp b/src/common/fonts/singlepicfont.cpp index aefede946..c8d5614ae 100644 --- a/src/common/fonts/singlepicfont.cpp +++ b/src/common/fonts/singlepicfont.cpp @@ -45,7 +45,7 @@ public: FSinglePicFont(const char *picname); // FFont interface - FGameTexture *GetChar(int code, int translation, int *const width, bool *redirected = nullptr) const override; + FGameTexture *GetChar(int code, int translation, int *const width) const override; int GetCharWidth (int code) const override; protected: @@ -79,7 +79,6 @@ FSinglePicFont::FSinglePicFont(const char *picname) : SpaceWidth = (int)pic->GetDisplayWidth(); GlobalKerning = 0; FirstChar = LastChar = 'A'; - ActiveColors = 0; PicNum = picnum; Next = FirstFont; @@ -94,10 +93,9 @@ FSinglePicFont::FSinglePicFont(const char *picname) : // //========================================================================== -FGameTexture *FSinglePicFont::GetChar (int code, int translation, int *const width, bool *redirected) const +FGameTexture *FSinglePicFont::GetChar (int code, int translation, int *const width) const { *width = SpaceWidth; - if (redirected) *redirected = false; if (code == 'a' || code == 'A') { return TexMan.GetGameTexture(PicNum, true); diff --git a/src/common/fonts/specialfont.cpp b/src/common/fonts/specialfont.cpp index 637cbf866..22d43187b 100644 --- a/src/common/fonts/specialfont.cpp +++ b/src/common/fonts/specialfont.cpp @@ -108,26 +108,18 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FGameTexture Chars[i].OriginalPic = MakeGameTexture(pic->GetTexture(), nullptr, ETextureType::FontChar); Chars[i].OriginalPic->CopySize(pic, true); TexMan.AddGameTexture(Chars[i].OriginalPic); - - if (!noTranslate) - { - Chars[i].TranslatedPic = MakeGameTexture(new FImageTexture(new FFontChar1 (charlumps[i]->GetTexture()->GetImage())), nullptr, ETextureType::FontChar); - Chars[i].TranslatedPic->CopySize(charlumps[i], true); - TexMan.AddGameTexture(Chars[i].TranslatedPic); - } - else Chars[i].TranslatedPic = Chars[i].OriginalPic; - Chars[i].XMove = (int)Chars[i].TranslatedPic->GetDisplayWidth(); - if (sysCallbacks.FontCharCreated) sysCallbacks.FontCharCreated(pic, Chars[i].OriginalPic, Chars[i].TranslatedPic); + Chars[i].XMove = (int)Chars[i].OriginalPic->GetDisplayWidth(); + if (sysCallbacks.FontCharCreated) sysCallbacks.FontCharCreated(pic, Chars[i].OriginalPic, Chars[i].OriginalPic); } else { - Chars[i].TranslatedPic = nullptr; + Chars[i].OriginalPic = nullptr; Chars[i].XMove = INT_MIN; } } // Special fonts normally don't have all characters so be careful here! - if ('N'-first >= 0 && 'N'-first < count && Chars['N' - first].TranslatedPic != nullptr) + if ('N'-first >= 0 && 'N'-first < count && Chars['N' - first].OriginalPic != nullptr) { SpaceWidth = (Chars['N' - first].XMove + 1) / 2; } @@ -137,11 +129,6 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FGameTexture } FixXMoves(); - - if (noTranslate) - { - ActiveColors = 0; - } } //========================================================================== @@ -152,65 +139,67 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FGameTexture void FSpecialFont::LoadTranslations() { - int count = LastChar - FirstChar + 1; - uint32_t usedcolors[256] = {}; - uint8_t identity[256]; - TArray Luminosity; - int TotalColors; - int i; + FFont::LoadTranslations(); - for (i = 0; i < count; i++) + bool empty = true; + for (auto& c : Chars) { - if (Chars[i].TranslatedPic) + if (c.OriginalPic != nullptr) { - FFontChar1 *pic = static_cast(Chars[i].TranslatedPic->GetTexture()->GetImage()); - if (pic) - { - pic->SetSourceRemap(nullptr); // Force the FFontChar1 to return the same pixels as the base texture - RecordTextureColors(pic, usedcolors); - } + empty = false; + break; } } + if (empty) return; // Font has no characters. + bool needsnotrans = false; // exclude the non-translated colors from the translation calculation - for (i = 0; i < 256; i++) - if (notranslate[i]) - usedcolors[i] = false; - - TotalColors = ActiveColors = SimpleTranslation (usedcolors, PatchRemap, identity, Luminosity); - - // Map all untranslated colors into the table of used colors - for (i = 0; i < 256; i++) - { + for (int i = 0; i < 256; i++) if (notranslate[i]) { - PatchRemap[i] = TotalColors; - identity[TotalColors] = i; - TotalColors++; + needsnotrans = true; + break; } - } - for (i = 0; i < count; i++) + // If we have no non-translateable colors, we can use the base data as-is. + if (!needsnotrans) { - if(Chars[i].TranslatedPic) - static_cast(Chars[i].TranslatedPic->GetTexture()->GetImage())->SetSourceRemap(PatchRemap); + return; } - BuildTranslations(Luminosity.Data(), identity, &TranslationParms[0][0], TotalColors, nullptr, [=](FRemapTable* remap) - { - // add the untranslated colors to the Ranges tables - if (ActiveColors < TotalColors) - { - for (int j = ActiveColors; j < TotalColors; ++j) - { - remap->Remap[j] = identity[j]; - remap->Palette[j] = GPalette.BaseColors[identity[j]]; - remap->Palette[j].a = 0xff; - } - } - }); + // we only need to add special handling if there's colors that should not be translated. + // Obviously 'notranslate' should only be used on data that uses the base palette, otherwise results are undefined! + for (auto &trans : Translations) + { + if (!IsLuminosityTranslation(trans)) continue; // this should only happen for CR_UNTRANSLATED. - ActiveColors = TotalColors; + FRemapTable remap(256); + remap.ForFont = true; + + uint8_t workpal[1024]; + for (int i = 0; i < 256; i++) + { + workpal[i * 4 + 0] = GPalette.BaseColors[i].b; + workpal[i * 4 + 1] = GPalette.BaseColors[i].g; + workpal[i * 4 + 2] = GPalette.BaseColors[i].r; + workpal[i * 4 + 3] = GPalette.BaseColors[i].a; + } + V_ApplyLuminosityTranslation(trans, workpal, 256); + for (int i = 0; i < 256; i++) + { + if (!notranslate[i]) + { + remap.Palette[i] = PalEntry(workpal[i * 4 + 3], workpal[i * 4 + 2], workpal[i * 4 + 1], workpal[i * 4 + 0]); + remap.Remap[i] = ColorMatcher.Pick(remap.Palette[i]); + } + else + { + remap.Palette[i] = GPalette.BaseColors[i]; + remap.Remap[i] = i; + } + } + trans = GPalette.StoreTranslation(TRANSLATION_Internal, &remap); + } } FFont *CreateSpecialFont (const char *name, int first, int count, FGameTexture **lumplist, const bool *notranslate, int lump, bool donttranslate) diff --git a/src/common/fonts/v_font.cpp b/src/common/fonts/v_font.cpp index 7f54e7bf1..37dd1f46f 100644 --- a/src/common/fonts/v_font.cpp +++ b/src/common/fonts/v_font.cpp @@ -60,16 +60,6 @@ #define DEFAULT_LOG_COLOR PalEntry(223,223,223) -// -// Globally visible constants. -// -#define HU_FONTSTART uint8_t('!') // the first font characters -#define HU_FONTEND uint8_t('\377') // the last font characters - -// Calculate # of glyphs in font. -#define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) - - // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -158,7 +148,7 @@ FFont *V_GetFont(const char *name, const char *fontlumpname) } if (folderdata.Size() > 0) { - font = new FFont(name, nullptr, name, HU_FONTSTART, HU_FONTSIZE, 1, -1); + font = new FFont(name, nullptr, name, 0, 0, 1, -1); if (translationsLoaded) font->LoadTranslations(); return font; } @@ -586,6 +576,179 @@ EColorRange V_FindFontColor (FName name) return CR_UNTRANSLATED; } +//========================================================================== +// +// CreateLuminosityTranslationRanges +// +// Create universal remap ranges for hardware rendering. +// +//========================================================================== +static PalEntry* paletteptr; + +static void CreateLuminosityTranslationRanges() +{ + paletteptr = (PalEntry*)ImageArena.Alloc(256 * ((NumTextColors * 2)) * sizeof(PalEntry)); + for (int l = 0; l < 2; l++) + { + auto parmstart = &TranslationParms[l][0]; + // Put the data into the image arena where it gets deleted with the rest of the texture data. + for (int p = 0; p < NumTextColors; p++) + { + // Intended storage order is Range 1, variant 1 - Range 1, variant 2, Range 2, variant 1, and so on. + // The storage of the ranges forces us to go through this differently... + PalEntry* palette = paletteptr + p * 512 + l * 256; + for (int v = 0; v < 256; v++) + { + palette[v].b = palette[v].g = palette[v].r = (uint8_t)v; + } + if (p != CR_UNTRANSLATED) // This table skips the untranslated entry. Do I need to say that the stored data format is garbage? >) + { + for (int v = 0; v < 256; v++) + { + // Find the color range that this luminosity value lies within. + const TranslationParm* parms = parmstart - 1; + do + { + parms++; + if (parms->RangeStart <= v && parms->RangeEnd >= v) + break; + } while (parms[1].RangeStart > parms[0].RangeEnd); + + // Linearly interpolate to find out which color this luminosity level gets. + int rangev = ((v - parms->RangeStart) << 8) / (parms->RangeEnd - parms->RangeStart); + int r = ((parms->Start[0] << 8) + rangev * (parms->End[0] - parms->Start[0])) >> 8; // red + int g = ((parms->Start[1] << 8) + rangev * (parms->End[1] - parms->Start[1])) >> 8; // green + int b = ((parms->Start[2] << 8) + rangev * (parms->End[2] - parms->Start[2])) >> 8; // blue + palette[v].r = (uint8_t)clamp(r, 0, 255); + palette[v].g = (uint8_t)clamp(g, 0, 255); + palette[v].b = (uint8_t)clamp(b, 0, 255); + } + // Advance to the next color range. + while (parmstart[1].RangeStart > parmstart[0].RangeEnd) + { + parmstart++; + } + parmstart++; + } + } + } +} + +//========================================================================== +// +// V_ApplyLuminosityTranslation +// +// Applies the translation to a bitmap for texture generation. +// +//========================================================================== + +void V_ApplyLuminosityTranslation(int translation, uint8_t* pixel, int size) +{ + int colorrange = (translation >> 16) & 0x3fff; + if (colorrange >= NumTextColors * 2) return; + int lum_min = (translation >> 8) & 0xff; + int lum_max = translation & 0xff; + int lum_range = (lum_max - lum_min + 1); + PalEntry* remap = paletteptr + colorrange * 256; + + for (int i = 0; i < size; i++, pixel += 4) + { + // we must also process the transparent pixels here to ensure proper filtering on the characters' edges. + int gray = PalEntry(255, pixel[2], pixel[1], pixel[0]).Luminance(); + int lumadjust = (gray - lum_min) * 255 / lum_range; + int index = clamp(lumadjust, 0, 255); + PalEntry newcol = remap[index]; + // extend the range if we find colors outside what initial analysis provided. + if (gray < lum_min) + { + newcol.r = newcol.r * gray / lum_min; + newcol.g = newcol.g * gray / lum_min; + newcol.b = newcol.b * gray / lum_min; + } + else if (gray > lum_max) + { + newcol.r = clamp(newcol.r * gray / lum_max, 0, 255); + newcol.g = clamp(newcol.g * gray / lum_max, 0, 255); + newcol.b = clamp(newcol.b * gray / lum_max, 0, 255); + } + pixel[0] = newcol.b; + pixel[1] = newcol.g; + pixel[2] = newcol.r; + } +} + +//========================================================================== +// +// SetDefaultTranslation +// +// Builds a translation to map the stock font to a mod provided replacement. +// This probably won't work that well if the original font is extremely colorful. +// +//========================================================================== + +static void CalcDefaultTranslation(FFont* base, int index) +{ + uint32_t othercolors[256] = {}; + base->RecordAllTextureColors(othercolors); + + TArray otherluminosity; + base->GetLuminosity(othercolors, otherluminosity); + + PalEntry *remap = &paletteptr[index * 256]; + memset(remap, 0, 1024); + + for (unsigned i = 0; i < 256; i++) + { + auto lum = otherluminosity[i]; + if (lum >= 0 && lum <= 1) + { + int index = int(lum * 255); + remap[index] = GPalette.BaseColors[i]; + remap[index].a = 255; + } + } + + // todo: fill the gaps. + //remap[0] = 0; + int lowindex = 0; + while (lowindex < 255 && remap[lowindex].a == 0) lowindex++; + lowindex++; + int highindex = lowindex + 1; + + while (lowindex < 255) + { + while (highindex <= 255 && remap[highindex].a == 0) highindex++; + if (lowindex == 0) + { + for (int i = 0; i < highindex; i++) remap[i] = remap[highindex]; + lowindex = highindex++; + } + else if (highindex > 256) + { + for (int i = lowindex + 1; i < highindex; i++) remap[i] = remap[lowindex]; + break; + } + else + { + for (int i = lowindex + 1; i < highindex; i++) + { + PalEntry color1 = remap[lowindex]; + PalEntry color2 = remap[highindex]; + double weight = (i - lowindex) / double(highindex - lowindex); + int r = int(color1.r + weight * (color2.r - color1.r)); + int g = int(color1.g + weight * (color2.g - color1.g)); + int b = int(color1.b + weight * (color2.b - color1.b)); + r = clamp(r, 0, 255); + g = clamp(g, 0, 255); + b = clamp(b, 0, 255); + remap[i] = PalEntry(255, r, g, b); + } + lowindex = highindex++; + } + } + +} + //========================================================================== // // V_LogColorFromColorRange @@ -676,6 +839,7 @@ EColorRange V_ParseFontColor (const uint8_t *&color_value, int normalcolor, int void V_InitFonts() { + CreateLuminosityTranslationRanges(); V_InitCustomFonts(); FFont *CreateHexLumpFont(const char *fontname, int lump); @@ -686,140 +850,7 @@ void V_InitFonts() NewConsoleFont = CreateHexLumpFont("NewConsoleFont", lump); NewSmallFont = CreateHexLumpFont2("NewSmallFont", lump); CurrentConsoleFont = NewConsoleFont; - - // load the heads-up font - if (!(SmallFont = V_GetFont("SmallFont", "SMALLFNT"))) - { - if (fileSystem.CheckNumForName("FONTA_S") >= 0) - { - int wadfile = -1; - auto a = fileSystem.CheckNumForName("FONTA33", ns_graphics); - if (a != -1) wadfile = fileSystem.GetFileContainer(a); - if (wadfile > fileSystem.GetIwadNum()) - { - // The font has been replaced, so we need to create a copy of the original as well. - SmallFont = new FFont("SmallFont", "FONTA%02u", nullptr, HU_FONTSTART, HU_FONTSIZE, 1, -1); - SmallFont->SetCursor('['); - } - else - { - SmallFont = new FFont("SmallFont", "FONTA%02u", "defsmallfont", HU_FONTSTART, HU_FONTSIZE, 1, -1); - SmallFont->SetCursor('['); - } - } - else if (fileSystem.CheckNumForName("STCFN033", ns_graphics) >= 0) - { - int wadfile = -1; - auto a = fileSystem.CheckNumForName("STCFN065", ns_graphics); - if (a != -1) wadfile = fileSystem.GetFileContainer(a); - if (wadfile > fileSystem.GetIwadNum()) - { - // The font has been replaced, so we need to create a copy of the original as well. - SmallFont = new FFont("SmallFont", "STCFN%.3d", nullptr, HU_FONTSTART, HU_FONTSIZE, HU_FONTSTART, -1, -1, false, false, true); - } - else - { - SmallFont = new FFont("SmallFont", "STCFN%.3d", "defsmallfont", HU_FONTSTART, HU_FONTSIZE, HU_FONTSTART, -1, -1, false, false, true); - } - } - } - - // Create the original small font as a fallback for incomplete definitions. - if (fileSystem.CheckNumForName("FONTA_S") >= 0) - { - OriginalSmallFont = new FFont("OriginalSmallFont", "FONTA%02u", "defsmallfont", HU_FONTSTART, HU_FONTSIZE, 1, -1, -1, false, true); - OriginalSmallFont->SetCursor('['); - } - else if (fileSystem.CheckNumForName("STCFN033", ns_graphics) >= 0) - { - OriginalSmallFont = new FFont("OriginalSmallFont", "STCFN%.3d", "defsmallfont", HU_FONTSTART, HU_FONTSIZE, HU_FONTSTART, -1, -1, false, true); - } - - - if (!(SmallFont2 = V_GetFont("SmallFont2"))) // Only used by Strife - { - if (fileSystem.CheckNumForName("STBFN033", ns_graphics) >= 0) - { - SmallFont2 = new FFont("SmallFont2", "STBFN%.3d", "defsmallfont2", HU_FONTSTART, HU_FONTSIZE, HU_FONTSTART, -1); - } - } - - //This must be read before BigFont so that it can be properly substituted. - BigUpper = V_GetFont("BigUpper"); - - if (!(BigFont = V_GetFont("BigFont"))) - { - if (fileSystem.CheckNumForName("FONTB_S") >= 0) - { - BigFont = new FFont("BigFont", "FONTB%02u", "defbigfont", HU_FONTSTART, HU_FONTSIZE, 1, -1); - } - } - - if (!BigFont) - { - // Load the generic fallback if no BigFont is found. - BigFont = V_GetFont("BigFont", "ZBIGFONT"); - } - - if (fileSystem.CheckNumForName("FONTB_S") >= 0) - { - OriginalBigFont = new FFont("OriginalBigFont", "FONTB%02u", "defbigfont", HU_FONTSTART, HU_FONTSIZE, 1, -1, -1, false, true); - } - else - { - OriginalBigFont = new FFont("OriginalBigFont", nullptr, "bigfont", HU_FONTSTART, HU_FONTSIZE, 1, -1, -1, false, true); - } - - // let PWAD BIGFONTs override the stock BIGUPPER font. (This check needs to be made smarter.) - if (BigUpper && BigFont->Type != FFont::Folder && BigUpper->Type == FFont::Folder) - { - delete BigUpper; - BigUpper = BigFont; - } - - if (BigUpper == nullptr) - { - BigUpper = BigFont; - } - if (!(ConFont = V_GetFont("ConsoleFont", "CONFONT"))) - { - ConFont = SmallFont; - } - if (!(IntermissionFont = FFont::FindFont("IntermissionFont"))) - { - if (TexMan.CheckForTexture("WINUM0", ETextureType::MiscPatch).isValid()) - { - IntermissionFont = FFont::FindFont("IntermissionFont_Doom"); - } - if (IntermissionFont == nullptr) - { - IntermissionFont = BigFont; - } - } - // This can only happen if gzdoom.pk3 is corrupted. ConFont should always be present. - if (ConFont == nullptr) - { - I_FatalError("Console font not found."); - } - // SmallFont and SmallFont2 have no default provided by the engine. BigFont only has in non-Raven games. - if (OriginalSmallFont == nullptr) - { - OriginalSmallFont = ConFont; - } - if (SmallFont == nullptr) - { - SmallFont = OriginalSmallFont; - } - if (SmallFont2 == nullptr) - { - SmallFont2 = SmallFont; - } - if (BigFont == nullptr) - { - BigFont = OriginalBigFont; - } - AlternativeSmallFont = OriginalSmallFont; - AlternativeBigFont = OriginalBigFont; + ConFont = V_GetFont("ConsoleFont", "CONFONT"); } void V_LoadTranslations() @@ -827,24 +858,44 @@ void V_LoadTranslations() for (auto font = FFont::FirstFont; font; font = font->Next) { if (!font->noTranslate) font->LoadTranslations(); - else font->ActiveColors = 0; } + if (BigFont) { - uint32_t colors[256] = {}; - BigFont->RecordAllTextureColors(colors); - if (OriginalBigFont != nullptr) OriginalBigFont->SetDefaultTranslation(colors); + CalcDefaultTranslation(BigFont, CR_UNTRANSLATED * 2 + 1); + if (OriginalBigFont != nullptr && OriginalBigFont != BigFont) + { + int sometrans = OriginalBigFont->Translations[0]; + sometrans &= ~(0x3fff << 16); + sometrans |= (CR_UNTRANSLATED * 2 + 1) << 16; + OriginalBigFont->Translations[CR_UNTRANSLATED] = sometrans; + OriginalBigFont->forceremap = true; + } } if (SmallFont) { - uint32_t colors[256] = {}; - SmallFont->RecordAllTextureColors(colors); - if (OriginalSmallFont != nullptr) OriginalSmallFont->SetDefaultTranslation(colors); - NewSmallFont->SetDefaultTranslation(colors); + CalcDefaultTranslation(SmallFont, CR_UNTRANSLATED * 2); + if (OriginalSmallFont != nullptr && OriginalSmallFont != SmallFont) + { + int sometrans = OriginalSmallFont->Translations[0]; + sometrans &= ~(0x3fff << 16); + sometrans |= (CR_UNTRANSLATED * 2) << 16; + OriginalSmallFont->Translations[CR_UNTRANSLATED] = sometrans; + OriginalSmallFont->forceremap = true; + } + if (NewSmallFont != nullptr) + { + int sometrans = NewSmallFont->Translations[0]; + sometrans &= ~(0x3fff << 16); + sometrans |= (CR_UNTRANSLATED * 2) << 16; + NewSmallFont->Translations[CR_UNTRANSLATED] = sometrans; + NewSmallFont->forceremap = true; + } } translationsLoaded = true; } + void V_ClearFonts() { while (FFont::FirstFont != nullptr) diff --git a/src/common/fonts/v_font.h b/src/common/fonts/v_font.h index 37bf049a3..99f8afb9f 100644 --- a/src/common/fonts/v_font.h +++ b/src/common/fonts/v_font.h @@ -97,7 +97,7 @@ public: FFont (const char *fontname, const char *nametemplate, const char *filetemplate, int first, int count, int base, int fdlump, int spacewidth=-1, bool notranslate = false, bool iwadonly = false, bool doomtemplate = false, GlyphSet *baseGlpyphs = nullptr); virtual ~FFont (); - virtual FGameTexture *GetChar (int code, int translation, int *const width, bool *redirected = nullptr) const; + virtual FGameTexture *GetChar (int code, int translation, int *const width) const; virtual int GetCharWidth (int code) const; int GetColorTranslation (EColorRange range, PalEntry *color = nullptr) const; int GetLump() const { return Lump; } @@ -128,22 +128,19 @@ public: void SetKerning(int c) { GlobalKerning = c; } bool NoTranslate() const { return noTranslate; } virtual void RecordAllTextureColors(uint32_t *usedcolors); - virtual void SetDefaultTranslation(uint32_t *colors); void CheckCase(); int GetDisplacement() const { return Displacement; } + static int GetLuminosity(uint32_t* colorsused, TArray& Luminosity, int* minlum = nullptr, int* maxlum = nullptr); + EFontType GetType() const { return Type; } + protected: FFont (int lump); - void BuildTranslations (const double *luminosity, const uint8_t *identity, - const void *ranges, int total_colors, const PalEntry *palette, std::function post = nullptr); void FixXMoves(); - static int SimpleTranslation (uint32_t *colorsused, uint8_t *translation, - uint8_t *identity, TArray &Luminosity); - void ReadSheetFont(TArray &folderdata, int width, int height, const DVector2 &Scale); EFontType Type = EFontType::Unknown; @@ -161,12 +158,10 @@ protected: bool forceremap = false; struct CharData { - FGameTexture *TranslatedPic = nullptr; // Texture for use with font translations. - FGameTexture *OriginalPic = nullptr; // Texture for use with CR_UNTRANSLATED or font colorization. + FGameTexture *OriginalPic = nullptr; int XMove = INT_MIN; }; TArray Chars; - int ActiveColors = -1; TArray Translations; uint8_t PatchRemap[256]; @@ -192,6 +187,8 @@ EColorRange V_ParseFontColor (const uint8_t *&color_value, int normalcolor, int FFont *V_GetFont(const char *fontname, const char *fontlumpname = nullptr); void V_InitFontColors(); char* CleanseString(char* str); +void V_ApplyLuminosityTranslation(int translation, uint8_t* pixel, int size); void V_LoadTranslations(); +class FBitmap; diff --git a/src/common/models/models_obj.cpp b/src/common/models/models_obj.cpp index 1fa6d3181..1af4fab7e 100644 --- a/src/common/models/models_obj.cpp +++ b/src/common/models/models_obj.cpp @@ -240,13 +240,12 @@ bool FOBJModel::Load(const char* fn, int lumpnum, const char* buffer, int length */ template void FOBJModel::ParseVector(TArray &array) { - float coord[L]; - for (size_t axis = 0; axis < L; axis++) + T vec; + for (unsigned axis = 0; axis < L; axis++) { sc.MustGetFloat(); - coord[axis] = (float)sc.Float; + vec[axis] = (float)sc.Float; } - T vec(coord); array.Push(vec); } diff --git a/src/common/platform/posix/cocoa/i_main.mm b/src/common/platform/posix/cocoa/i_main.mm index 7159d31ea..732648e5b 100644 --- a/src/common/platform/posix/cocoa/i_main.mm +++ b/src/common/platform/posix/cocoa/i_main.mm @@ -101,8 +101,9 @@ void I_DetectOS() const char* name = "Unknown version"; - if (10 == version.majorVersion) + switch (version.majorVersion) { + case 10: switch (version.minorVersion) { case 9: name = "OS X Mavericks"; break; @@ -114,13 +115,13 @@ void I_DetectOS() case 15: name = "macOS Catalina"; break; case 16: name = "macOS Big Sur"; break; } - } - else if (11 == version.majorVersion) - { - switch (version.minorVersion) - { - case 0: name = "macOS Big Sur"; break; - } + break; + case 11: + name = "macOS Big Sur"; + break; + case 12: + name = "macOS Monterey"; + break; } char release[16] = "unknown"; diff --git a/src/common/rendering/gl/gl_renderstate.cpp b/src/common/rendering/gl/gl_renderstate.cpp index 64e8cc4d1..b8272e58d 100644 --- a/src/common/rendering/gl/gl_renderstate.cpp +++ b/src/common/rendering/gl/gl_renderstate.cpp @@ -204,7 +204,7 @@ bool FGLRenderState::ApplyShader() size_t start, size; index = screen->mLights->GetBinding(index, &start, &size); - if (start != mLastMappedLightIndex) + if (start != mLastMappedLightIndex || screen->mPipelineNbr > 1) // If multiple buffers always bind { mLastMappedLightIndex = start; static_cast(screen->mLights->GetBuffer())->BindRange(nullptr, start, size); diff --git a/src/common/scripting/backend/codegen.cpp b/src/common/scripting/backend/codegen.cpp index 9eb887827..6a551f1c6 100644 --- a/src/common/scripting/backend/codegen.cpp +++ b/src/common/scripting/backend/codegen.cpp @@ -1449,7 +1449,7 @@ FxFontCast::FxFontCast(FxExpression *x) : FxExpression(EFX_FontCast, x->ScriptPosition) { basex = x; - ValueType = TypeSound; + ValueType = TypeFont; } //========================================================================== diff --git a/src/common/scripting/interface/vmnatives.cpp b/src/common/scripting/interface/vmnatives.cpp index 6276ac906..d7d4519e6 100644 --- a/src/common/scripting/interface/vmnatives.cpp +++ b/src/common/scripting/interface/vmnatives.cpp @@ -964,10 +964,16 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, UnbindACommand) // This is only accessible to the special menu item to run CCMDs. DEFINE_ACTION_FUNCTION(DOptionMenuItemCommand, DoCommand) { - if (CurrentMenu == nullptr) return 0; PARAM_PROLOGUE; PARAM_STRING(cmd); PARAM_BOOL(unsafe); + + // Only menus are allowed to execute CCMDs. + if (DMenu::InMenu == 0) + { + I_FatalError("Attempt to execute CCMD '%s' outside of menu code", cmd.GetChars()); + } + UnsafeExecutionScope scope(unsafe); C_DoCommand(cmd); return 0; diff --git a/src/common/textures/bitmap.h b/src/common/textures/bitmap.h index 5ee574b7f..36d520771 100644 --- a/src/common/textures/bitmap.h +++ b/src/common/textures/bitmap.h @@ -198,6 +198,11 @@ public: return Pitch; } + int GetBufferSize() const + { + return Pitch * Height; + } + const uint8_t *GetPixels() const { return data; diff --git a/src/common/textures/formats/fontchars.cpp b/src/common/textures/formats/fontchars.cpp index 107080e35..44a72ca6e 100644 --- a/src/common/textures/formats/fontchars.cpp +++ b/src/common/textures/formats/fontchars.cpp @@ -41,48 +41,6 @@ #include "fontchars.h" #include "engineerrors.h" -//========================================================================== -// -// FFontChar1 :: FFontChar1 -// -// Used by fonts made from textures. -// -//========================================================================== - -FFontChar1::FFontChar1 (FImageSource *sourcelump) -: BaseTexture(sourcelump), SourceRemap (nullptr) -{ - // now copy all the properties from the base texture - assert(BaseTexture != nullptr); - CopySize(*BaseTexture); - bUseGamePalette = false; -} - -//========================================================================== -// -// FFontChar1 :: GetPixels -// -// Render style is not relevant for fonts. This must not use it! -// -//========================================================================== - -TArray FFontChar1::CreatePalettedPixels (int) -{ - // Make the texture as normal, then remap it so that all the colors - // are at the low end of the palette - // Why? It only creates unnecessary work! - auto Pixels = BaseTexture->GetPalettedPixels(normal); - - if (SourceRemap) - { - for (int x = 0; x < Width*Height; ++x) - { - Pixels[x] = SourceRemap[Pixels[x]]; - } - } - return Pixels; -} - //========================================================================== // // FFontChar2 :: FFontChar2 @@ -91,8 +49,8 @@ TArray FFontChar1::CreatePalettedPixels (int) // //========================================================================== -FFontChar2::FFontChar2 (int sourcelump, int sourcepos, int width, int height, int leftofs, int topofs) -: SourceLump (sourcelump), SourcePos (sourcepos), SourceRemap(nullptr) +FFontChar2::FFontChar2(int sourcelump, int sourcepos, int width, int height, int leftofs, int topofs) + : SourceLump(sourcelump), SourcePos(sourcepos) { Width = width; Height = height; @@ -100,17 +58,6 @@ FFontChar2::FFontChar2 (int sourcelump, int sourcepos, int width, int height, in TopOffset = topofs; } -//========================================================================== -// -// FFontChar2 :: SetSourceRemap -// -//========================================================================== - -void FFontChar2::SetSourceRemap(const uint8_t *sourceremap) -{ - SourceRemap = sourceremap; -} - //========================================================================== // // FFontChar2 :: Get8BitPixels @@ -121,7 +68,7 @@ void FFontChar2::SetSourceRemap(const uint8_t *sourceremap) TArray FFontChar2::CreatePalettedPixels(int) { - auto lump = fileSystem.OpenFileReader (SourceLump); + auto lump = fileSystem.OpenFileReader(SourceLump); int destSize = Width * Height; uint8_t max = 255; bool rle = true; @@ -129,23 +76,23 @@ TArray FFontChar2::CreatePalettedPixels(int) // This is to "fix" bad fonts { uint8_t buff[16]; - lump.Read (buff, 4); + lump.Read(buff, 4); if (buff[3] == '2') { - lump.Read (buff, 7); + lump.Read(buff, 7); max = buff[6]; - lump.Seek (SourcePos - 11, FileReader::SeekCur); + lump.Seek(SourcePos - 11, FileReader::SeekCur); } else if (buff[3] == 0x1A) { lump.Read(buff, 13); max = buff[12] - 1; - lump.Seek (SourcePos - 17, FileReader::SeekCur); + lump.Seek(SourcePos - 17, FileReader::SeekCur); rle = false; } else { - lump.Seek (SourcePos - 4, FileReader::SeekCur); + lump.Seek(SourcePos - 4, FileReader::SeekCur); } } @@ -153,7 +100,7 @@ TArray FFontChar2::CreatePalettedPixels(int) int runlen = 0, setlen = 0; uint8_t setval = 0; // Shut up, GCC! - uint8_t *dest_p = Pixels.Data(); + uint8_t* dest_p = Pixels.Data(); int dest_adv = Height; int dest_rew = destSize - 1; @@ -166,11 +113,7 @@ TArray FFontChar2::CreatePalettedPixels(int) if (runlen != 0) { uint8_t color = lump.ReadUInt8(); - color = MIN (color, max); - if (SourceRemap != nullptr) - { - color = SourceRemap[color]; - } + color = MIN(color, max); *dest_p = color; dest_p += dest_adv; x--; @@ -194,11 +137,7 @@ TArray FFontChar2::CreatePalettedPixels(int) { uint8_t color = lump.ReadUInt8(); setlen = (-code) + 1; - setval = MIN (color, max); - if (SourceRemap != nullptr) - { - setval = SourceRemap[setval]; - } + setval = MIN(color, max); } } } @@ -216,10 +155,6 @@ TArray FFontChar2::CreatePalettedPixels(int) { color = max; } - if (SourceRemap != nullptr) - { - color = SourceRemap[color]; - } *dest_p = color; dest_p += dest_adv; } @@ -230,10 +165,17 @@ TArray FFontChar2::CreatePalettedPixels(int) if (destSize < 0) { char name[9]; - fileSystem.GetFileShortName (name, SourceLump); + fileSystem.GetFileShortName(name, SourceLump); name[8] = 0; - I_FatalError ("The font %s is corrupt", name); + I_FatalError("The font %s is corrupt", name); } return Pixels; } +int FFontChar2::CopyPixels(FBitmap* bmp, int conversion) +{ + if (conversion == luminance) conversion = normal; // luminance images have no use as an RGB source. + auto ppix = CreatePalettedPixels(conversion); + bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, SourceRemap, nullptr); + return 0; +} diff --git a/src/common/textures/formats/fontchars.h b/src/common/textures/formats/fontchars.h index 341fb9445..79baf4a2b 100644 --- a/src/common/textures/formats/fontchars.h +++ b/src/common/textures/formats/fontchars.h @@ -1,21 +1,5 @@ -// This is a font character that loads a texture and recolors it. -class FFontChar1 : public FImageSource -{ -public: - FFontChar1 (FImageSource *sourcelump); - TArray CreatePalettedPixels(int conversion) override; - void SetSourceRemap(const uint8_t *sourceremap) { SourceRemap = sourceremap; } - const uint8_t *ResetSourceRemap() { auto p = SourceRemap; SourceRemap = nullptr; return p; } - FImageSource *GetBase() const { return BaseTexture; } - -protected: - - FImageSource *BaseTexture; - const uint8_t *SourceRemap; -}; - // This is a font character that reads RLE compressed data. class FFontChar2 : public FImageSource { @@ -23,10 +7,15 @@ public: FFontChar2 (int sourcelump, int sourcepos, int width, int height, int leftofs=0, int topofs=0); TArray CreatePalettedPixels(int conversion) override; - void SetSourceRemap(const uint8_t *sourceremap); + int CopyPixels(FBitmap* bmp, int conversion); + + void SetSourceRemap(const PalEntry* sourceremap) + { + SourceRemap = sourceremap; + } protected: int SourceLump; int SourcePos; - const uint8_t *SourceRemap; + const PalEntry *SourceRemap; }; diff --git a/src/common/textures/hw_material.cpp b/src/common/textures/hw_material.cpp index cb6f3a54b..3f149d728 100644 --- a/src/common/textures/hw_material.cpp +++ b/src/common/textures/hw_material.cpp @@ -29,6 +29,7 @@ #include "c_cvars.h" #include "v_video.h" + CVAR(Bool, gl_customshader, true, 0); diff --git a/src/common/textures/hw_texcontainer.h b/src/common/textures/hw_texcontainer.h index c36eea38a..91afa526f 100644 --- a/src/common/textures/hw_texcontainer.h +++ b/src/common/textures/hw_texcontainer.h @@ -65,8 +65,16 @@ private: // This is needed for allowing the client to allocate slots that aren't matched to a palette, e.g. Build's indexed variants. if (translation >= 0) { - auto remap = GPalette.TranslationToTable(translation); - translation = remap == nullptr ? 0 : remap->Index; + if (!IsLuminosityTranslation(translation)) + { + auto remap = GPalette.TranslationToTable(translation); + translation = remap == nullptr ? 0 : remap->Index; + } + else + { + // only needs to preserve the color range plus an identifier for marking this a luminosity translation. + translation = ((translation >> 16) & 0x3fff) | 0xff0000; + } } else translation &= ~0x7fffffff; diff --git a/src/common/textures/texture.cpp b/src/common/textures/texture.cpp index 503657f00..6d24f1154 100644 --- a/src/common/textures/texture.cpp +++ b/src/common/textures/texture.cpp @@ -321,6 +321,7 @@ bool FTexture::ProcessData(unsigned char* buffer, int w, int h, bool ispatch) // Initializes the buffer for the texture data // //=========================================================================== +void V_ApplyLuminosityTranslation(int translation, uint8_t *buffer, int size); FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags) { @@ -356,7 +357,7 @@ FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags) buffer = new unsigned char[W * (H + 1) * 4]; memset(buffer, 0, W * (H + 1) * 4); - auto remap = translation <= 0 ? nullptr : GPalette.TranslationToTable(translation); + auto remap = translation <= 0 || IsLuminosityTranslation(translation) ? nullptr : GPalette.TranslationToTable(translation); if (remap && remap->Inactive) remap = nullptr; if (remap) translation = remap->Index; FBitmap bmp(buffer, W * 4, W, H); @@ -364,6 +365,10 @@ FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags) int trans; auto Pixels = GetBgraBitmap(remap ? remap->Palette : nullptr, &trans); bmp.Blit(exx, exx, Pixels); + if (IsLuminosityTranslation(translation)) + { + V_ApplyLuminosityTranslation(translation, buffer, W * H); + } if (remap == nullptr) { diff --git a/src/common/utility/utf8.cpp b/src/common/utility/utf8.cpp index 2341d575c..b0b225ae5 100644 --- a/src/common/utility/utf8.cpp +++ b/src/common/utility/utf8.cpp @@ -354,6 +354,8 @@ int getAlternative(int code) default: return code; + case '{': return '('; + case '}': return ')'; case 0x17f: return 's'; // The 'long s' can be safely remapped to the regular variant, not that this gets used in any real text... case 0x218: return 0x15e; // Romanian S with comma below may get remapped to S with cedilla. case 0x219: return 0x15f; diff --git a/src/common/utility/vectors.h b/src/common/utility/vectors.h index e8c39fc28..2118a22fa 100644 --- a/src/common/utility/vectors.h +++ b/src/common/utility/vectors.h @@ -82,6 +82,11 @@ struct TVector2 { } + TVector2(vec_t *o) + : X(o[0]), Y(o[1]) + { + } + void Zero() { Y = X = 0; diff --git a/src/d_main.cpp b/src/d_main.cpp index 6d5bf3063..007c1adce 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -117,6 +117,7 @@ #include "texturemanager.h" #include "hw_clock.h" #include "hwrenderer/scene/hw_drawinfo.h" +#include "doomfont.h" #ifdef __unix__ #include "i_system.h" // for SHARE_DIR @@ -3409,6 +3410,7 @@ static int D_DoomMain_Internal (void) StartScreen->Progress(); V_InitFonts(); + InitDoomFonts(); V_LoadTranslations(); UpdateGenericUI(false); diff --git a/src/gamedata/d_dehacked.cpp b/src/gamedata/d_dehacked.cpp index 50876323e..f2d48939b 100644 --- a/src/gamedata/d_dehacked.cpp +++ b/src/gamedata/d_dehacked.cpp @@ -2033,7 +2033,7 @@ static int PatchPars (int dummy) { while ('\0' != *str) { - if (isspace((unsigned char)*str)) + if (*str != '\r' && isspace((unsigned char)*str)) { return str; } diff --git a/src/gamedata/doomfont.h b/src/gamedata/doomfont.h new file mode 100644 index 000000000..c960a9b35 --- /dev/null +++ b/src/gamedata/doomfont.h @@ -0,0 +1,147 @@ +// +// Globally visible constants. +// +#define HU_FONTSTART uint8_t('!') // the first font characters +#define HU_FONTEND uint8_t('\377') // the last font characters + +// Calculate # of glyphs in font. +#define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) + + + +void InitDoomFonts() +{ + // load the heads-up font + if (!(SmallFont = V_GetFont("SmallFont", "SMALLFNT"))) + { + if (fileSystem.CheckNumForName("FONTA_S") >= 0) + { + int wadfile = -1; + auto a = fileSystem.CheckNumForName("FONTA33", ns_graphics); + if (a != -1) wadfile = fileSystem.GetFileContainer(a); + if (wadfile > fileSystem.GetIwadNum()) + { + // The font has been replaced, so we need to create a copy of the original as well. + SmallFont = new FFont("SmallFont", "FONTA%02u", nullptr, HU_FONTSTART, HU_FONTSIZE, 1, -1); + SmallFont->SetCursor('['); + } + else + { + SmallFont = new FFont("SmallFont", "FONTA%02u", "defsmallfont", HU_FONTSTART, HU_FONTSIZE, 1, -1); + SmallFont->SetCursor('['); + } + } + else if (fileSystem.CheckNumForName("STCFN033", ns_graphics) >= 0) + { + int wadfile = -1; + auto a = fileSystem.CheckNumForName("STCFN065", ns_graphics); + if (a != -1) wadfile = fileSystem.GetFileContainer(a); + if (wadfile > fileSystem.GetIwadNum()) + { + // The font has been replaced, so we need to create a copy of the original as well. + SmallFont = new FFont("SmallFont", "STCFN%.3d", nullptr, HU_FONTSTART, HU_FONTSIZE, HU_FONTSTART, -1, -1, false, false, true); + } + else + { + SmallFont = new FFont("SmallFont", "STCFN%.3d", "defsmallfont", HU_FONTSTART, HU_FONTSIZE, HU_FONTSTART, -1, -1, false, false, true); + } + } + } + + // Create the original small font as a fallback for incomplete definitions. + if (fileSystem.CheckNumForName("FONTA_S") >= 0) + { + OriginalSmallFont = new FFont("OriginalSmallFont", "FONTA%02u", "defsmallfont", HU_FONTSTART, HU_FONTSIZE, 1, -1, -1, false, true); + OriginalSmallFont->SetCursor('['); + } + else if (fileSystem.CheckNumForName("STCFN033", ns_graphics) >= 0) + { + OriginalSmallFont = new FFont("OriginalSmallFont", "STCFN%.3d", "defsmallfont", HU_FONTSTART, HU_FONTSIZE, HU_FONTSTART, -1, -1, false, true); + } + + + if (!(SmallFont2 = V_GetFont("SmallFont2"))) // Only used by Strife + { + if (fileSystem.CheckNumForName("STBFN033", ns_graphics) >= 0) + { + SmallFont2 = new FFont("SmallFont2", "STBFN%.3d", "defsmallfont2", HU_FONTSTART, HU_FONTSIZE, HU_FONTSTART, -1); + } + } + + //This must be read before BigFont so that it can be properly substituted. + BigUpper = V_GetFont("BigUpper"); + + if (!(BigFont = V_GetFont("BigFont"))) + { + if (fileSystem.CheckNumForName("FONTB_S") >= 0) + { + BigFont = new FFont("BigFont", "FONTB%02u", "defbigfont", HU_FONTSTART, HU_FONTSIZE, 1, -1); + } + } + + if (!BigFont) + { + // Load the generic fallback if no BigFont is found. + BigFont = V_GetFont("BigFont", "ZBIGFONT"); + } + + if (fileSystem.CheckNumForName("FONTB_S") >= 0) + { + OriginalBigFont = new FFont("OriginalBigFont", "FONTB%02u", "defbigfont", HU_FONTSTART, HU_FONTSIZE, 1, -1, -1, false, true); + } + else + { + OriginalBigFont = new FFont("OriginalBigFont", nullptr, "bigfont", HU_FONTSTART, HU_FONTSIZE, 1, -1, -1, false, true); + } + + // let PWAD BIGFONTs override the stock BIGUPPER font. (This check needs to be made smarter.) + if (BigUpper && BigFont->GetType() != FFont::Folder && BigUpper->GetType() == FFont::Folder) + { + delete BigUpper; + BigUpper = BigFont; + } + + if (BigUpper == nullptr) + { + BigUpper = BigFont; + } + if (!ConFont) + { + ConFont = SmallFont; + } + if (!(IntermissionFont = FFont::FindFont("IntermissionFont"))) + { + if (TexMan.CheckForTexture("WINUM0", ETextureType::MiscPatch).isValid()) + { + IntermissionFont = FFont::FindFont("IntermissionFont_Doom"); + } + if (IntermissionFont == nullptr) + { + IntermissionFont = BigFont; + } + } + // This can only happen if gzdoom.pk3 is corrupted. ConFont should always be present. + if (ConFont == nullptr) + { + I_FatalError("Console font not found."); + } + // SmallFont and SmallFont2 have no default provided by the engine. BigFont only has in non-Raven games. + if (OriginalSmallFont == nullptr) + { + OriginalSmallFont = ConFont; + } + if (SmallFont == nullptr) + { + SmallFont = OriginalSmallFont; + } + if (SmallFont2 == nullptr) + { + SmallFont2 = SmallFont; + } + if (BigFont == nullptr) + { + BigFont = OriginalBigFont; + } + AlternativeSmallFont = OriginalSmallFont; + AlternativeBigFont = OriginalBigFont; +} diff --git a/src/gamedata/gi.cpp b/src/gamedata/gi.cpp index 9e5bd537b..4524929ab 100644 --- a/src/gamedata/gi.cpp +++ b/src/gamedata/gi.cpp @@ -438,6 +438,7 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_BOOL(norandomplayerclass, "norandomplayerclass") GAMEINFOKEY_BOOL(forcekillscripts, "forcekillscripts") // [JM] Force kill scripts on thing death. (MF7_NOKILLSCRIPTS overrides.) GAMEINFOKEY_STRING(Dialogue, "dialogue") + GAMEINFOKEY_STRINGARRAY(AddDialogues, "adddialogues", 0, false) GAMEINFOKEY_STRING(statusscreen_single, "statscreen_single") GAMEINFOKEY_STRING(statusscreen_coop, "statscreen_coop") GAMEINFOKEY_STRING(statusscreen_dm, "statscreen_dm") diff --git a/src/gamedata/gi.h b/src/gamedata/gi.h index 0d5f259a8..466c323d5 100644 --- a/src/gamedata/gi.h +++ b/src/gamedata/gi.h @@ -192,6 +192,7 @@ struct gameinfo_t FString mMapArrow, mCheatMapArrow; FString mEasyKey, mCheatKey; FString Dialogue; + TArray AddDialogues; FGIFont mStatscreenMapNameFont; FGIFont mStatscreenFinishedFont; FGIFont mStatscreenEnteringFont; diff --git a/src/maploader/strifedialogue.cpp b/src/maploader/strifedialogue.cpp index 748c3dc7f..247aaa5a9 100644 --- a/src/maploader/strifedialogue.cpp +++ b/src/maploader/strifedialogue.cpp @@ -105,6 +105,20 @@ void MapLoader::LoadStrifeConversations (MapData *map, const char *mapname) } else { + // additive dialogues via MAPINFO + bool addedDialogues = false; + for (const FString &addd : gameinfo.AddDialogues) + { + if (!LoadScriptFile(addd, true, 0)) + { + continue; + } + else + { + addedDialogues = true; + } + } + if (strnicmp (mapname, "MAP", 3) == 0) { char scriptname_b[9] = { 'S','C','R','I','P','T',mapname[3],mapname[4],0 }; @@ -121,6 +135,10 @@ void MapLoader::LoadStrifeConversations (MapData *map, const char *mapname) { if (LoadScriptFile(gameinfo.Dialogue, false, 0)) { + if (addedDialogues) + { + Printf(TEXTCOLOR_RED "Warning! Dialogue was mixed with AddDialogues! Previous AddDialogues have been overwritten\n"); + } return; } } diff --git a/src/playsim/a_dynlight.cpp b/src/playsim/a_dynlight.cpp index 82d4d94ad..4750dddfa 100644 --- a/src/playsim/a_dynlight.cpp +++ b/src/playsim/a_dynlight.cpp @@ -748,6 +748,7 @@ void AActor::AttachLight(unsigned int count, const FLightDefaults *lightdef) AttachedLights.Push(light); } lightdef->ApplyProperties(light); + light->UpdateLocation(); } //========================================================================== diff --git a/src/playsim/d_player.h b/src/playsim/d_player.h index f11763386..3dc3acc16 100644 --- a/src/playsim/d_player.h +++ b/src/playsim/d_player.h @@ -391,6 +391,7 @@ public: float BlendB = 0; float BlendA = 0; + FString SoundClass; FString LogText; // [RH] Log for Strife FString SubtitleText; int SubtitleCounter; diff --git a/src/playsim/p_acs.cpp b/src/playsim/p_acs.cpp index 73d490e1f..674aad0f4 100644 --- a/src/playsim/p_acs.cpp +++ b/src/playsim/p_acs.cpp @@ -4044,6 +4044,7 @@ enum APROP_MaxStepHeight = 44, APROP_MaxDropOffHeight= 45, APROP_DamageType = 46, + APROP_SoundClass = 47, }; // These are needed for ACS's APROP_RenderStyle @@ -4307,6 +4308,16 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) actor->DamageType = Level->Behaviors.LookupString(value); break; + case APROP_SoundClass: + if (actor->IsKindOf(NAME_PlayerPawn)) + { + if (actor->player != nullptr) + { + actor->player->SoundClass = Level->Behaviors.LookupString(value); + } + } + break; + default: // do nothing. break; @@ -4404,6 +4415,7 @@ int DLevelScript::GetActorProperty (int tid, int property) case APROP_MaxStepHeight: return DoubleToACS(actor->MaxStepHeight); case APROP_MaxDropOffHeight: return DoubleToACS(actor->MaxDropOffHeight); case APROP_DamageType: return GlobalACSStrings.AddString(actor->DamageType.GetChars()); + case APROP_SoundClass: return GlobalACSStrings.AddString(S_GetSoundClass(actor)); default: return 0; } @@ -4477,6 +4489,7 @@ int DLevelScript::CheckActorProperty (int tid, int property, int value) case APROP_Species: string = actor->GetSpecies().GetChars(); break; case APROP_NameTag: string = actor->GetTag(); break; case APROP_DamageType: string = actor->DamageType.GetChars(); break; + case APROP_SoundClass: string = S_GetSoundClass(actor); break; } if (string == NULL) string = ""; return (!stricmp(string, Level->Behaviors.LookupString(value))); diff --git a/src/playsim/p_user.cpp b/src/playsim/p_user.cpp index a3c9a81d6..9d976431b 100644 --- a/src/playsim/p_user.cpp +++ b/src/playsim/p_user.cpp @@ -347,6 +347,7 @@ void player_t::CopyFrom(player_t &p, bool copyPSP) ConversationFaceTalker = p.ConversationFaceTalker; MUSINFOactor = p.MUSINFOactor; MUSINFOtics = p.MUSINFOtics; + SoundClass = p.SoundClass; if (copyPSP) { // This needs to transfer ownership completely. @@ -1696,7 +1697,8 @@ void player_t::Serialize(FSerializer &arc) ("settings_controller", settings_controller) ("onground", onground) ("musinfoactor", MUSINFOactor) - ("musinfotics", MUSINFOtics); + ("musinfotics", MUSINFOtics) + ("soundclass", SoundClass); if (arc.isWriting ()) { @@ -1812,6 +1814,7 @@ DEFINE_FIELD_X(PlayerInfo, player_t, original_cmd) DEFINE_FIELD_X(PlayerInfo, player_t, userinfo) DEFINE_FIELD_X(PlayerInfo, player_t, weapons) DEFINE_FIELD_NAMED_X(PlayerInfo, player_t, cmd.ucmd.buttons, buttons) +DEFINE_FIELD_X(PlayerInfo, player_t, SoundClass) DEFINE_FIELD_X(UserCmd, usercmd_t, buttons) DEFINE_FIELD_X(UserCmd, usercmd_t, pitch) diff --git a/src/r_data/models.cpp b/src/r_data/models.cpp index 0564099b6..d970d5239 100644 --- a/src/r_data/models.cpp +++ b/src/r_data/models.cpp @@ -413,25 +413,18 @@ static void ParseModelDefLump(int Lump) { smf.modelsAmount = MIN_MODELS; } - //Allocate TArrays - smf.modelIDs.Alloc(smf.modelsAmount); - smf.skinIDs.Alloc(smf.modelsAmount); - smf.surfaceskinIDs.Alloc(smf.modelsAmount * MD3_MAX_SURFACES); - smf.modelframes.Alloc(smf.modelsAmount); - //Make sure all modelIDs are -1 by default - for (auto& modelID : smf.modelIDs) + + const auto initArray = [](auto& array, const unsigned count, const auto value) { - modelID = -1; - } - //Make sure no TArray elements of type FTextureID are null. These elements will have a textureid (FTextureID.texnum) of 0. - for (auto& skinID : smf.skinIDs) - { - skinID = FTextureID(FNullTextureID()); - } - for (auto& surfaceskinID : smf.surfaceskinIDs) - { - surfaceskinID = FTextureID(FNullTextureID()); - } + array.Alloc(count); + std::fill(array.begin(), array.end(), value); + }; + + initArray(smf.modelIDs, smf.modelsAmount, -1); + initArray(smf.skinIDs, smf.modelsAmount, FNullTextureID()); + initArray(smf.surfaceskinIDs, smf.modelsAmount * MD3_MAX_SURFACES, FNullTextureID()); + initArray(smf.modelframes, smf.modelsAmount, 0); + sc.RestorePos(scPos); sc.MustGetStringName("{"); while (!sc.CheckString("}")) diff --git a/src/rendering/hwrenderer/hw_entrypoint.cpp b/src/rendering/hwrenderer/hw_entrypoint.cpp index 7d2cdbddd..ec46790d9 100644 --- a/src/rendering/hwrenderer/hw_entrypoint.cpp +++ b/src/rendering/hwrenderer/hw_entrypoint.cpp @@ -159,6 +159,9 @@ sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bou di->ProcessScene(toscreen); + // Reset colormap so 2D drawing isn't affected + RenderState.SetSpecialColormap(CM_DEFAULT, 1); + if (mainview) { PostProcess.Clock(); diff --git a/src/rendering/hwrenderer/scene/hw_sprites.cpp b/src/rendering/hwrenderer/scene/hw_sprites.cpp index 0873629bc..8f4842206 100644 --- a/src/rendering/hwrenderer/scene/hw_sprites.cpp +++ b/src/rendering/hwrenderer/scene/hw_sprites.cpp @@ -800,9 +800,8 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t { z = thing->floorz; } - // [RH] Make floatbobbing a renderer-only effect. - if (thing->flags2 & MF2_FLOATBOB) + else if (thing->flags2 & MF2_FLOATBOB) { float fz = thing->GetBobOffset(vp.TicFrac); z += fz; diff --git a/src/rendering/r_sky.cpp b/src/rendering/r_sky.cpp index 96cc6083a..4d9e2a5cf 100644 --- a/src/rendering/r_sky.cpp +++ b/src/rendering/r_sky.cpp @@ -80,14 +80,12 @@ void InitSkyMap(FLevelLocals *Level) Level->skytexture1 = TexMan.GetFrontSkyLayer(Level->skytexture1); } - skytex1 = TexMan.GetGameTexture(Level->skytexture1, false); skytex2 = TexMan.GetGameTexture(Level->skytexture2, false); - if (skytex1 == nullptr) + if (skytex1 == nullptr || skytex2 == nullptr) return; - if ((Level->flags & LEVEL_DOUBLESKY) && skytex1->GetDisplayHeight() != skytex2->GetDisplayHeight()) { Printf(TEXTCOLOR_BOLD "Both sky textures must be the same height." TEXTCOLOR_NORMAL "\n"); diff --git a/src/rendering/swrenderer/plane/r_skyplane.cpp b/src/rendering/swrenderer/plane/r_skyplane.cpp index 48639768a..cc59301ad 100644 --- a/src/rendering/swrenderer/plane/r_skyplane.cpp +++ b/src/rendering/swrenderer/plane/r_skyplane.cpp @@ -74,7 +74,7 @@ namespace swrenderer auto sskytex1 = GetPalettedSWTexture(Level->skytexture1, true, false, true); auto sskytex2 = GetPalettedSWTexture(Level->skytexture2, true, false, true); - if (sskytex1 == nullptr) + if (sskytex1 == nullptr || sskytex2 == nullptr) return; skytexturemid = 0; diff --git a/src/sound/s_advsound.cpp b/src/sound/s_advsound.cpp index c0729bba6..3b7cca0f8 100644 --- a/src/sound/s_advsound.cpp +++ b/src/sound/s_advsound.cpp @@ -1608,19 +1608,20 @@ bool S_AreSoundsEquivalent (AActor *actor, int id1, int id2) // //=========================================================================== -static const char *GetSoundClass(AActor *pp) +const char *S_GetSoundClass(AActor *pp) { auto player = pp->player; + const char *defaultsoundclass = pp->NameVar(NAME_SoundClass) == NAME_None ? "player" : pp->NameVar(NAME_SoundClass).GetChars(); if (player != nullptr && (player->mo == nullptr || !(player->mo->flags4 &MF4_NOSKIN)) && (unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size() && - (unsigned)player->userinfo.GetSkin() < Skins.Size()) + (unsigned)player->userinfo.GetSkin() < Skins.Size() && + player->SoundClass.IsEmpty()) { return Skins[player->userinfo.GetSkin()].Name.GetChars(); } - auto sclass = player? pp->NameVar(NAME_SoundClass) : NAME_None; - - return sclass != NAME_None ? sclass.GetChars() : "player"; + + return player->SoundClass.IsEmpty() ? defaultsoundclass : player->SoundClass.GetChars(); } //========================================================================== @@ -1635,10 +1636,10 @@ int S_FindSkinnedSound (AActor *actor, FSoundID refid) const char *pclass; int gender = 0; - if (actor != nullptr) + if (actor != nullptr && actor->player != nullptr) { - pclass = GetSoundClass (actor); - if (actor->player != nullptr) gender = actor->player->userinfo.GetGender(); + pclass = S_GetSoundClass(actor); + gender = actor->player->userinfo.GetGender(); } else { @@ -1682,7 +1683,7 @@ int S_FindSkinnedSoundEx (AActor *actor, const char *name, const char *extendedn void S_MarkPlayerSounds (AActor *player) { - const char *playerclass = GetSoundClass(player); + const char *playerclass = S_GetSoundClass(player); int classidx = S_FindPlayerClass(playerclass); if (classidx < 0) { @@ -1696,6 +1697,7 @@ void S_MarkPlayerSounds (AActor *player) PlayerSounds[listidx].MarkUsed(); } } + } //========================================================================== diff --git a/src/sound/s_sndseq.cpp b/src/sound/s_sndseq.cpp index 4c7702a88..abe16c3c8 100644 --- a/src/sound/s_sndseq.cpp +++ b/src/sound/s_sndseq.cpp @@ -395,7 +395,7 @@ void DSeqNode::OnDestroy() m_ParentSeqNode->m_ChildSeqNode = nullptr; m_ParentSeqNode = nullptr; } - if (Level->SequenceListHead == this) + if (Level && Level->SequenceListHead == this) { Level->SequenceListHead = m_Next; GC::WriteBarrier(m_Next); diff --git a/src/sound/s_sound.h b/src/sound/s_sound.h index c8bcf7ebc..c4c0655cf 100644 --- a/src/sound/s_sound.h +++ b/src/sound/s_sound.h @@ -70,6 +70,7 @@ bool S_AreSoundsEquivalent (AActor *actor, int id1, int id2); bool S_AreSoundsEquivalent (AActor *actor, const char *name1, const char *name2); int S_LookupPlayerSound (const char *playerclass, int gender, const char *logicalname); int S_LookupPlayerSound (const char *playerclass, int gender, FSoundID refid); +const char *S_GetSoundClass(AActor *pp); int S_FindSkinnedSound (AActor *actor, FSoundID refid); int S_FindSkinnedSoundEx (AActor *actor, const char *logicalname, const char *extendedname); int S_AddSound (const char *logicalname, const char *lumpname, FScanner *sc=NULL); // Add sound by lumpname diff --git a/wadsrc/static/mapinfo/heretic.txt b/wadsrc/static/mapinfo/heretic.txt index bdc68c62d..f889a9718 100644 --- a/wadsrc/static/mapinfo/heretic.txt +++ b/wadsrc/static/mapinfo/heretic.txt @@ -1,4 +1,4 @@ -// MAPINFO for Heretic (Shareware and Retail) +// MAPINFO for Heretic (Retail) include "mapinfo/common.txt" gameinfo diff --git a/wadsrc/static/mapinfo/hereticshareware.txt b/wadsrc/static/mapinfo/hereticshareware.txt new file mode 100644 index 000000000..fe369f36f --- /dev/null +++ b/wadsrc/static/mapinfo/hereticshareware.txt @@ -0,0 +1,7 @@ +// MAPINFO for Heretic (Shareware) +include "mapinfo/heretic.txt" + +gameinfo +{ + borderflat = "FLOOR04" +} \ No newline at end of file diff --git a/wadsrc/static/zscript/actors/attacks.zs b/wadsrc/static/zscript/actors/attacks.zs index 369474c44..102451abd 100644 --- a/wadsrc/static/zscript/actors/attacks.zs +++ b/wadsrc/static/zscript/actors/attacks.zs @@ -146,7 +146,7 @@ extend class Actor bInCombat = true; self.target = target; - let painstate = FindState('Pain', 'Dagger'); + let painstate = FindState('Pain.Dagger'); if (painstate != NULL) { SetState(painstate); diff --git a/wadsrc/static/zscript/actors/hexen/bishop.zs b/wadsrc/static/zscript/actors/hexen/bishop.zs index 4ebd519f5..1b65f3edb 100644 --- a/wadsrc/static/zscript/actors/hexen/bishop.zs +++ b/wadsrc/static/zscript/actors/hexen/bishop.zs @@ -63,7 +63,7 @@ class Bishop : Actor BISH J 5 BRIGHT A_Explode(random[BishopBoom](25,40)); BISH K 5 Bright; BISH LM 4 Bright; - BISH N 4 A_SpawnItemEx("BishopPuff", 0,0,40, 0,0,-0.5); + BISH N 4 A_SpawnItemEx("BishopPuff", 0,0,40, 0,0,0.5); BISH O 4 A_QueueCorpse; BISH P -1; Stop; diff --git a/wadsrc/static/zscript/actors/hexen/clericflame.zs b/wadsrc/static/zscript/actors/hexen/clericflame.zs index 9672d55ee..6630713b9 100644 --- a/wadsrc/static/zscript/actors/hexen/clericflame.zs +++ b/wadsrc/static/zscript/actors/hexen/clericflame.zs @@ -185,7 +185,7 @@ class CircleFlame : Actor Stop; Death: CFCF QR 3 Bright; - CFCF S 3 Bright A_Explode(20, 20, 0); + CFCF S 3 Bright A_Explode(20, 128, 0); CFCF TUVWXYZ 3 Bright; Stop; } diff --git a/wadsrc/static/zscript/actors/shared/fastprojectile.zs b/wadsrc/static/zscript/actors/shared/fastprojectile.zs index befc4252d..88df40a32 100644 --- a/wadsrc/static/zscript/actors/shared/fastprojectile.zs +++ b/wadsrc/static/zscript/actors/shared/fastprojectile.zs @@ -102,7 +102,7 @@ class FastProjectile : Actor tm.ClearLastRipped(); // [RH] Do rip damage each step, like Hexen } - if (!TryMove (Pos.XY + frac.XY, true, NULL, tm)) + if (!TryMove (Pos.XY + frac.XY, true, false, tm)) { // Blocked move if (!bSkyExplode) { diff --git a/wadsrc/static/zscript/level_compatibility.zs b/wadsrc/static/zscript/level_compatibility.zs index 57dcf5d0f..9f29a6bae 100644 --- a/wadsrc/static/zscript/level_compatibility.zs +++ b/wadsrc/static/zscript/level_compatibility.zs @@ -2112,6 +2112,21 @@ class LevelCompatibility : LevelPostProcessor { SetLineFlags(1461, Line.ML_REPEAT_SPECIAL); SetLineFlags(1468, Line.ML_REPEAT_SPECIAL); + break; + } + + case 'E0D747B9EE58A0CB74B9AD54423AC15C': // return01.wad e1m2 + { + // fix broken switch to raise the exit bridge + SetLineSpecial(1248, Floor_RaiseByValue, 39, 8, 512); + break; + } + + case '1C35384B22BD805F51B3B2C9D17D62E4': // 007ltsd.wad E4M7 + { + // Fix impassable exit line + SetLineFlags(6842, 0, Line.ML_BLOCKING); + break; } } } diff --git a/wadsrc_extra/static/iwadinfo.txt b/wadsrc_extra/static/iwadinfo.txt index 29230f5be..a1c419d4e 100644 --- a/wadsrc_extra/static/iwadinfo.txt +++ b/wadsrc_extra/static/iwadinfo.txt @@ -264,7 +264,7 @@ IWad Name = "Heretic Shareware" Game = "Heretic" Config = "Heretic" - Mapinfo = "mapinfo/heretic.txt" + Mapinfo = "mapinfo/hereticshareware.txt" Compatibility = "Shareware" MustContain = "E1M1", "TITLE", "MUS_E1M1" BannerColors = "fc fc 00", "a8 00 00" diff --git a/wadsrc_widescreen/static b/wadsrc_widescreen/static index d458411db..89dac47db 160000 --- a/wadsrc_widescreen/static +++ b/wadsrc_widescreen/static @@ -1 +1 @@ -Subproject commit d458411db4795dfd1420cf1c6456f6d2999b3bad +Subproject commit 89dac47dbf874351e2d5a07a0bb33b7d1175ef28