From 6389b6b9148947e9b856238e785c496376f17249 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 19 Nov 2017 23:04:15 +0100 Subject: [PATCH 01/10] - UMAPINFO parser, including some convenience additions to FScanner. Not tested yet! --- .gitignore | 2 + src/CMakeLists.txt | 1 + src/g_level.h | 10 +- src/g_mapinfo.cpp | 25 +- src/intermission/intermission_parse.cpp | 21 +- src/sc_man.cpp | 70 ++++ src/sc_man.h | 9 + src/umapinfo.cpp | 431 ++++++++++++++++++++++++ 8 files changed, 556 insertions(+), 13 deletions(-) create mode 100644 src/umapinfo.cpp diff --git a/.gitignore b/.gitignore index e071ea2e8..80c2238ae 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,5 @@ /src/r_drawersasm.obj /src/r_drawersasm.o .vs +/src/gl/unused +/mapfiles_release/*.map diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7ede5128b..96910731c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -958,6 +958,7 @@ set (PCH_SOURCES stats.cpp stringtable.cpp teaminfo.cpp + umapinfo.cpp v_blend.cpp v_collection.cpp v_draw.cpp diff --git a/src/g_level.h b/src/g_level.h index 2df5af946..8b27dc68d 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -253,7 +253,7 @@ struct FSpecialAction { FName Type; // this is initialized before the actors... uint8_t Action; - int Args[5]; // must allow 16 bit tags for 666 & 667! + int Args[5]; }; class DScroller; @@ -307,6 +307,11 @@ struct FExitText FString mText; FString mMusic; FString mBackdrop; + + FExitText(int def = 0, int order = -1, const FString &text = "", const FString &backdrop = "", const FString &music = "") + : mDefined(int16_t(def)), mOrder(int16_t(order)), mText(text), mMusic(music), mBackdrop(backdrop) + { + } }; struct level_info_t @@ -619,5 +624,8 @@ struct FEpisode extern TArray AllEpisodes; +int ParseUMapInfo(int lumpnum); +void CommitUMapinfo(level_info_t *defaultinfo); + #endif //__G_LEVEL_H__ diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 5f22b25cf..cb750f4a1 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -2206,7 +2206,7 @@ void G_ParseMapInfo (FString basemapinfo) parse.ParseMapInfo(baselump, gamedefaults, defaultinfo); } - static const char *mapinfonames[] = { "MAPINFO", "ZMAPINFO", NULL }; + static const char *mapinfonames[] = { "MAPINFO", "ZMAPINFO", "UMAPINFO", NULL }; int nindex; // Parse any extra MAPINFOs. @@ -2222,9 +2222,26 @@ void G_ParseMapInfo (FString basemapinfo) if (altlump >= 0) continue; } - FMapInfoParser parse(nindex == 1? FMapInfoParser::FMT_New : FMapInfoParser::FMT_Unknown); - level_info_t defaultinfo; - parse.ParseMapInfo(lump, gamedefaults, defaultinfo); + else if (nindex == 2) + { + // MAPINFO and ZMAPINFO will override UMAPINFO if in the same WAD. + int wad = Wads.GetLumpFile(lump); + int altlump = Wads.CheckNumForName("ZMAPINFO", ns_global, wad, true); + if (altlump >= 0) continue; + altlump = Wads.CheckNumForName("MAPINFO", ns_global, wad, true); + if (altlump >= 0) continue; + } + if (nindex != 2) + { + CommitUMapinfo(&gamedefaults); // UMPAINFOs are collected until a regular MAPINFO is found so that they properly use the base settings. + FMapInfoParser parse(nindex == 1 ? FMapInfoParser::FMT_New : FMapInfoParser::FMT_Unknown); + level_info_t defaultinfo; + parse.ParseMapInfo(lump, gamedefaults, defaultinfo); + } + else + { + ParseUMapInfo(lump); + } } if (AllEpisodes.Size() == 0) diff --git a/src/intermission/intermission_parse.cpp b/src/intermission/intermission_parse.cpp index 2c3114afe..2841b70f7 100644 --- a/src/intermission/intermission_parse.cpp +++ b/src/intermission/intermission_parse.cpp @@ -724,6 +724,18 @@ FName FMapInfoParser::ParseEndGame() // //========================================================================== +FName MakeEndPic(const char *string) +{ + FString seqname; + seqname << "@EndPic_" << string; + FIntermissionDescriptor *desc = new FIntermissionDescriptor; + FIntermissionAction *action = new FIntermissionAction; + action->mBackground = string; + desc->mActions.Push(action); + ReplaceIntermission(seqname, desc); + return FName(seqname); +} + FName FMapInfoParser::CheckEndSequence() { const char *seqname = NULL; @@ -756,14 +768,7 @@ FName FMapInfoParser::CheckEndSequence() { ParseComma(); sc.MustGetString (); - FString seqname; - seqname << "@EndPic_" << sc.String; - FIntermissionDescriptor *desc = new FIntermissionDescriptor; - FIntermissionAction *action = new FIntermissionAction; - action->mBackground = sc.String; - desc->mActions.Push(action); - ReplaceIntermission(seqname, desc); - return FName(seqname); + return MakeEndPic(sc.String); } else if (sc.Compare("endbunny")) { diff --git a/src/sc_man.cpp b/src/sc_man.cpp index 6482fbc10..2a515d91b 100644 --- a/src/sc_man.cpp +++ b/src/sc_man.cpp @@ -949,6 +949,76 @@ bool FScanner::Compare (const char *text) return (stricmp (text, String) == 0); } + +//========================================================================== +// +// Convenience helpers that parse an entire number including a leading minus or plus sign +// +//========================================================================== + +bool FScanner::ScanValue(bool allowfloat) +{ + bool neg = false; + if (!GetToken()) + { + return false; + } + if (TokenType == '-' || TokenType == '+') + { + neg = TokenType == '-'; + if (!GetToken()) + { + return false; + } + } + if (TokenType != TK_IntConst && (TokenType != TK_FloatConst || !allowfloat)) + { + return false; + } + if (neg) + { + Number = -Number; + Float = -Float; + } + return true; +} + +bool FScanner::CheckValue(bool allowfloat) +{ + auto savedstate = SavePos(); + bool res = ScanValue(allowfloat); + if (!res) RestorePos(savedstate); + return res; +} + +void FScanner::MustGetValue(bool allowfloat) +{ + if (!ScanValue(allowfloat)) ScriptError(allowfloat ? "Numeric constant expected" : "Integer constant expected"); +} + +bool FScanner::CheckBoolToken() +{ + if (CheckToken(TK_True)) + { + Number = 1; + Float = 1; + return true; + } + if (CheckToken(TK_False)) + { + Number = 0; + Float = 0; + return true; + } + return false; +} + +void FScanner::MustGetBoolToken() +{ + if (!CheckBoolToken()) + ScriptError("Expected true or false"); +} + //========================================================================== // // FScanner :: TokenName diff --git a/src/sc_man.h b/src/sc_man.h index 860935b8d..44dd9370a 100644 --- a/src/sc_man.h +++ b/src/sc_man.h @@ -57,6 +57,12 @@ public: bool GetFloat(); void MustGetFloat(); bool CheckFloat(); + + // Token based variant + bool CheckValue(bool allowfloat); + void MustGetValue(bool allowfloat); + bool CheckBoolToken(); + void MustGetBoolToken(); void UnGet(); @@ -107,6 +113,9 @@ protected: bool StateOptions; bool Escape; VersionInfo ParseVersion = { 0, 0, 0 }; // no ZScript extensions by default + + + bool ScanValue(bool allowfloat); }; enum diff --git a/src/umapinfo.cpp b/src/umapinfo.cpp new file mode 100644 index 000000000..c8d416740 --- /dev/null +++ b/src/umapinfo.cpp @@ -0,0 +1,431 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2017 Christoph Oelckers +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include "w_wad.h" +#include "g_level.h" +#include "sc_man.h" +#include "r_defs.h" +#include "p_setup.h" +#include "gi.h" + +FName MakeEndPic(const char *string); + +struct BossAction +{ + int type; + int special; + int tag; +}; + +struct UMapEntry +{ + FString MapName; + FString LevelName; + FString InterText; + FString InterTextSecret; + TArray BossActions; + + char levelpic[9]; + char nextmap[9]; + char nextsecret[9]; + char music[9]; + char skytexture[9]; + char endpic[9]; + char exitpic[9]; + char enterpic[9]; + char interbackdrop[9] = "FLOOR4_8"; + char intermusic[9]; + int partime; + int nointermission; +}; + +static TArray Maps; + + +// ----------------------------------------------- +// +// Parses a set of string and concatenates them +// +// ----------------------------------------------- + +static FString ParseMultiString(FScanner &scanner, int error) +{ + FString build; + + if (scanner.CheckToken(TK_Identifier)) + { + if (!stricmp(scanner.String, "clear")) + { + return "-"; + } + else + { + scanner.ScriptError("Either 'clear' or string constant expected"); + } + } + + do + { + scanner.MustGetToken(TK_StringConst); + if (build.Len() > 0) build += "\n"; + build += scanner.String; + } + while (scanner.CheckToken(',')); + return build; +} + +// ----------------------------------------------- +// +// Parses a lump name. The buffer must be at least 9 characters. +// +// ----------------------------------------------- + +static int ParseLumpName(FScanner &scanner, char *buffer) +{ + scanner.MustGetToken(TK_StringConst); + if (strlen(scanner.String) > 8) + { + scanner.ScriptError("String too long. Maximum size is 8 characters."); + return 0; + } + uppercopy(buffer, scanner.String); + return 1; +} + +// ----------------------------------------------- +// +// Parses a standard property that is already known +// These do not get stored in the property list +// but in dedicated struct member variables. +// +// ----------------------------------------------- + +static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape) +{ + // find the next line with content. + // this line is no property. + + scanner.MustGetToken(TK_Identifier); + FString pname = scanner.String; + scanner.MustGetToken('='); + + if (!pname.CompareNoCase("levelname")) + { + scanner.MustGetToken(TK_StringConst); + mape->LevelName, scanner.String; + } + else if (!pname.CompareNoCase("next")) + { + ParseLumpName(scanner, mape->nextmap); + } + else if (!pname.CompareNoCase("nextsecret")) + { + ParseLumpName(scanner, mape->nextsecret); + } + else if (!pname.CompareNoCase("levelpic")) + { + ParseLumpName(scanner, mape->levelpic); + } + else if (!pname.CompareNoCase("skytexture")) + { + ParseLumpName(scanner, mape->skytexture); + } + else if (!pname.CompareNoCase("music")) + { + ParseLumpName(scanner, mape->music); + } + else if (!pname.CompareNoCase("endpic")) + { + ParseLumpName(scanner, mape->endpic); + } + else if (!pname.CompareNoCase("endcast")) + { + scanner.MustGetBoolToken(); + if (scanner.Number) strcpy(mape->endpic, "$CAST"); + else strcpy(mape->endpic, "-"); + } + else if (!pname.CompareNoCase("endbunny")) + { + scanner.MustGetBoolToken(); + if (scanner.Number) strcpy(mape->endpic, "$BUNNY"); + else strcpy(mape->endpic, "-"); + } + else if (!pname.CompareNoCase("endgame")) + { + scanner.MustGetBoolToken(); + if (scanner.Number) strcpy(mape->endpic, "!"); + else strcpy(mape->endpic, "-"); + } + else if (!pname.CompareNoCase("exitpic")) + { + ParseLumpName(scanner, mape->exitpic); + } + else if (!pname.CompareNoCase("enterpic")) + { + ParseLumpName(scanner, mape->enterpic); + } + else if (!pname.CompareNoCase("nointermission")) + { + scanner.MustGetBoolToken(); + mape->nointermission = scanner.Number; + } + else if (!pname.CompareNoCase("partime")) + { + scanner.MustGetValue(false); + mape->partime = TICRATE * scanner.Number; + } + else if (!pname.CompareNoCase("intertext")) + { + mape->InterText = ParseMultiString(scanner, 1); + if (mape->InterText.IsEmpty()) return 0; + } + else if (!pname.CompareNoCase("intertextsecret")) + { + mape->InterTextSecret = ParseMultiString(scanner, 1); + if (mape->InterTextSecret.IsEmpty()) return 0; + } + else if (!pname.CompareNoCase("interbackdrop")) + { + ParseLumpName(scanner, mape->interbackdrop); + } + else if (!pname.CompareNoCase("intermusic")) + { + ParseLumpName(scanner, mape->intermusic); + } + else if (!pname.CompareNoCase("episode")) + { + FString Episode = ParseMultiString(scanner, 1); + if (Episode.IsEmpty()) return 0; + //M_AddEpisode(mape->mapname, lname); + } + else if (!pname.CompareNoCase("bossaction")) + { + scanner.MustGetToken(TK_Identifier); + int classnum, special, tag; + if (!stricmp(scanner.String, "clear")) + { + // mark level free of boss actions + classnum = special = tag = -1; + mape->BossActions.Clear(); + } + else + { + FName type = scanner.String; + scanner.MustGetToken(','); + scanner.MustGetValue(false); + int special = scanner.Number; + scanner.MustGetToken(','); + scanner.MustGetValue(false); + int tag = scanner.Number; + // allow no 0-tag specials here, unless a level exit. + if (tag != 0 || special == 11 || special == 51 || special == 52 || special == 124) + { + FSpecialAction & bossact = mape->BossActions[mape->BossActions.Reserve(1)]; + line_t line; + maplinedef_t mld; + mld.special = special; + mld.tag = tag; + P_TranslateLineDef(&line, &mld); + bossact = { type, (uint8_t)line.special, {line.args[0], line.args[1],line.args[2],line.args[3],line.args[4]} }; + } + } + } + else + { + // Skip over all unknown properties. + do + { + if (!scanner.CheckFloat()) + { + scanner.MustGetAnyToken(); + if (scanner.TokenType != TK_Identifier && scanner.TokenType != TK_StringConst && scanner.TokenType != TK_True && scanner.TokenType != TK_False) + { + scanner.ScriptError("Identifier or value expecte3d"); + } + } + + } while (scanner.CheckToken(',')); + } + return 1; +} + +// ----------------------------------------------- +// +// Parses a complete map entry +// +// ----------------------------------------------- + +static int ParseMapEntry(FScanner &scanner, UMapEntry *val) +{ + scanner.MustGetToken(TK_Identifier); + + val->MapName, scanner.String; + scanner.MustGetToken('{'); + while(!scanner.CheckToken('}')) + { + ParseStandardProperty(scanner, val); + } + return 1; +} + +// ----------------------------------------------- +// +// Parses a complete UMAPINFO lump +// +// ----------------------------------------------- + +int ParseUMapInfo(int lumpnum) +{ + FScanner scanner(lumpnum); + unsigned int i; + + while (scanner.CheckToken(TK_Identifier)) + { + if (!scanner.Compare("map")) + { + scanner.ScriptError("'MAP' expected"); + } + UMapEntry parsed; + ParseMapEntry(scanner, &parsed); + + // Endpic overrides level exits. + if (parsed.endpic[0]) + { + parsed.nextmap[0] = parsed.nextsecret[0] = 0; + if (parsed.endpic[0] == '!') parsed.endpic[0] = 0; + } + /* + else if (!parsed.nextmap[0] && !parsed.endpic[0]) + { + if (!parsed.MapName.CompareNoCase("MAP30")) uppercopy(parsed.endpic, "$CAST"); + else if (!parsed.MapName.CompareNoCase("E1M8")) uppercopy(parsed.endpic, gameinfo.creditPages.Last()); + else if (!parsed.MapName.CompareNoCase("E2M8")) uppercopy(parsed.endpic, "VICTORY"); + else if (!parsed.MapName.CompareNoCase("E3M8")) uppercopy(parsed.endpic, "$BUNNY"); + else if (!parsed.MapName.CompareNoCase("E4M8")) uppercopy(parsed.endpic, "ENDPIC"); + else if (gameinfo.gametype == GAME_Chex && !parsed.MapName.CompareNoCase("E1M5")) uppercopy(parsed.endpic, "CREDIT"); + else + { + parsed.nextmap[0] = 0; // keep previous setting + } + } + */ + + // Does this property already exist? If yes, replace it. + for(i = 0; i < Maps.Size(); i++) + { + if (!parsed.MapName.Compare(Maps[i].MapName)) + { + Maps[i] = parsed; + return 1; + } + } + // Not found so create a new one. + Maps.Push(parsed); + + } + return 1; +} + + +// This will get called if after an UMAPINFO lump a regular (Z)MAPINFO is found or when MAPINFO parsing is complete. +void CommitUMapinfo(level_info_t *defaultinfo) +{ + for (auto &map : Maps) + { + auto levelinfo = FindLevelInfo(map.MapName); + if (levelinfo == nullptr) + { + *levelinfo = *defaultinfo; + if (map.MapName.IsNotEmpty()) levelinfo->MapName = map.MapName; + if (map.MapName.IsNotEmpty()) levelinfo->LevelName = map.LevelName; + if (map.levelpic[0]) levelinfo->PName = map.levelpic; + if (map.nextmap[0]) levelinfo->NextMap = map.nextmap; + else if (map.endpic[0]) + { + FName name; + + if (!stricmp(map.endpic, "$CAST")) + { + name = "INTER_CAST"; + } + else if (!stricmp(map.endpic, "$BUNNY")) + { + name = "INTER_BUNNY"; + } + else + { + name = MakeEndPic(map.endpic); + } + if (name != NAME_None) + { + levelinfo->NextMap.Format("enDSeQ%04x", int(name)); + } + } + + if (map.nextsecret[0]) levelinfo->NextSecretMap = map.nextsecret; + if (map.music[0]) + { + levelinfo->Music = map.music; + levelinfo->musicorder = 0; + } + if (map.skytexture[0]) + { + levelinfo->SkyPic1 = map.skytexture; + levelinfo->skyspeed1 = 0; + levelinfo->SkyPic2 = ""; + levelinfo->skyspeed2 = 0; + } + if (map.partime > 0) levelinfo->partime = map.partime; + if (map.enterpic[0]) levelinfo->EnterPic = map.enterpic; + if (map.exitpic[0]) levelinfo->ExitPic = map.exitpic; + if (map.intermusic[0]) + { + levelinfo->InterMusic = map.intermusic; + levelinfo->intermusicorder = 0; + } + if (map.BossActions.Size() > 0) levelinfo->specialactions = std::move(map.BossActions); + + const int exflags = FExitText::DEF_TEXT | FExitText::DEF_BACKDROP | FExitText::DEF_MUSIC; + if (map.InterText.IsNotEmpty()) + { + if (map.InterText.Compare("-") != 0) + levelinfo->ExitMapTexts[NAME_Normal] = { exflags, 0, map.InterText, map.interbackdrop, map.intermusic[0]? map.intermusic : gameinfo.intermissionMusic }; + else + levelinfo->ExitMapTexts[NAME_Normal] = { 0, 0 }; + } + if (map.InterTextSecret.IsNotEmpty()) + { + if (map.InterTextSecret.Compare("-") != 0) + levelinfo->ExitMapTexts[NAME_Secret] = { exflags, 0, map.InterTextSecret, map.interbackdrop, map.intermusic[0] ? map.intermusic : gameinfo.intermissionMusic }; + else + levelinfo->ExitMapTexts[NAME_Secret] = { 0, 0 }; + } + if (map.nointermission) levelinfo->flags |= LEVEL_NOINTERMISSION; + } + } + + + // All done. If we get here again, start fresh. + Maps.Clear(); + Maps.ShrinkToFit(); +} \ No newline at end of file From 495fc79fe93b0ea6a92e8889dbe14e426449ce17 Mon Sep 17 00:00:00 2001 From: Rachael Alexanderson Date: Tue, 21 Nov 2017 05:26:46 -0500 Subject: [PATCH 02/10] - added dpJudas's drawer fix for the upscaled fuzz drawing --- src/swrenderer/drawers/r_draw.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/swrenderer/drawers/r_draw.cpp b/src/swrenderer/drawers/r_draw.cpp index c894e46ce..793cf2ae4 100644 --- a/src/swrenderer/drawers/r_draw.cpp +++ b/src/swrenderer/drawers/r_draw.cpp @@ -195,7 +195,7 @@ namespace swrenderer { if (r_fuzzscale) { - int next_random = 0; + static int next_random = 0; fuzzpos = (fuzzpos + fuzz_random_x_offset[next_random] * FUZZTABLE / 100) % FUZZTABLE; From 0ef8105e6b6adbf7649fa599ab00d861f017e6fb Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 21 Nov 2017 18:57:44 +0100 Subject: [PATCH 03/10] - Add missing r_draw.cpp in CMakeLists.txt --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7ede5128b..f623de5a4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -702,6 +702,7 @@ set ( SWRENDER_SOURCES swrenderer/r_swrenderer.cpp swrenderer/r_memory.cpp swrenderer/r_renderthread.cpp + swrenderer/drawers/r_draw.cpp swrenderer/drawers/r_draw_pal.cpp swrenderer/drawers/r_draw_rgba.cpp swrenderer/drawers/r_thread.cpp From 4e4f94db2f431e1d384b19cdadbc143dc744b83e Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 22 Nov 2017 06:32:55 +0100 Subject: [PATCH 04/10] - Implement newer scaled fuzz in softpoly's truecolor non-sse drawer --- src/polyrenderer/drawers/poly_drawer32.h | 42 +++++++++++++----------- src/polyrenderer/poly_renderer.cpp | 2 ++ src/swrenderer/drawers/r_draw.cpp | 4 +-- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/polyrenderer/drawers/poly_drawer32.h b/src/polyrenderer/drawers/poly_drawer32.h index 579488ea3..f3d05ae74 100644 --- a/src/polyrenderer/drawers/poly_drawer32.h +++ b/src/polyrenderer/drawers/poly_drawer32.h @@ -119,7 +119,7 @@ namespace TriScreenDrawerModes } template - FORCEINLINE unsigned int SampleShade32(int32_t u, int32_t v, const uint32_t *texPixels, int texWidth, int texHeight, int &fuzzpos) + FORCEINLINE unsigned int SampleShade32(int32_t u, int32_t v, const uint32_t *texPixels, int texWidth, int texHeight, int x, int y) { if (SamplerT::Mode == (int)Samplers::Shaded) { @@ -140,12 +140,23 @@ namespace TriScreenDrawerModes } else if (SamplerT::Mode == (int)Samplers::Fuzz) { + using namespace swrenderer; + uint32_t texelX = ((((uint32_t)u << 8) >> 16) * texWidth) >> 16; uint32_t texelY = ((((uint32_t)v << 8) >> 16) * texHeight) >> 16; unsigned int sampleshadeout = APART(texPixels[texelX * texHeight + texelY]); sampleshadeout += sampleshadeout >> 7; // 255 -> 256 - sampleshadeout = (sampleshadeout * fuzzcolormap[fuzzpos++]) >> 5; - if (fuzzpos >= FUZZTABLE) fuzzpos = 0; + + fixed_t fuzzscale = (200 << FRACBITS) / viewheight; + + int scaled_x = (x * fuzzscale) >> FRACBITS; + int fuzz_x = fuzz_random_x_offset[scaled_x % FUZZ_RANDOM_X_SIZE] + fuzzpos; + + fixed_t fuzzcount = FUZZTABLE << FRACBITS; + fixed_t fuzz = ((fuzz_x << FRACBITS) + y * fuzzscale) % fuzzcount; + unsigned int alpha = fuzzoffset[fuzz >> FRACBITS]; + + sampleshadeout = (sampleshadeout * alpha) >> 5; return sampleshadeout; } else @@ -373,8 +384,6 @@ private: uint32_t srcalpha = args->uniforms->SrcAlpha(); uint32_t destalpha = args->uniforms->DestAlpha(); - int fuzzpos = (ScreenTriangle::FuzzStart + destX * 123 + destY) % FUZZTABLE; - auto lights = args->uniforms->Lights(); auto num_lights = args->uniforms->NumLights(); FVector3 worldnormal = args->uniforms->Normal(); @@ -505,7 +514,7 @@ private: // Sample fgcolor if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[ix]; unsigned int ifgcolor = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - unsigned int ifgshade = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + unsigned int ifgshade = SampleShade32(posU, posV, texPixels, texWidth, texHeight, destX + ix, destY + y); posU += stepU; posV += stepV; @@ -608,7 +617,7 @@ private: // Sample fgcolor if (SamplerT::Mode == (int)Samplers::FogBoundary && (mask0 & (1 << 31))) color = dest[x]; unsigned int ifgcolor = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - unsigned int ifgshade = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + unsigned int ifgshade = SampleShade32(posU, posV, texPixels, texWidth, texHeight, destX + x, destY + y); posU += stepU; posV += stepV; @@ -711,7 +720,7 @@ private: // Sample fgcolor if (SamplerT::Mode == (int)Samplers::FogBoundary && (mask1 & (1 << 31))) color = dest[x]; unsigned int ifgcolor = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - unsigned int ifgshade = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + unsigned int ifgshade = SampleShade32(posU, posV, texPixels, texWidth, texHeight, destX + x, destY + 4 + y); posU += stepU; posV += stepV; @@ -773,7 +782,11 @@ public: { using namespace TriScreenDrawerModes; - if (args->SimpleShade()) + if (SamplerT::Mode == (int)Samplers::Fuzz) + { + Loop(destOrg, destWidth, destHeight, destPitch, args, thread); + } + else if (args->SimpleShade()) { Loop(destOrg, destWidth, destHeight, destPitch, args, thread); } @@ -869,18 +882,9 @@ private: int count = x1 - x0; - int fuzzpos = (ScreenTriangle::FuzzStart + x0 * 123 + y0) % FUZZTABLE; - uint32_t posV = startV; for (int y = y0; y < y1; y++, posV += stepV) { - int coreBlock = y / 8; - if (coreBlock % thread->num_cores != thread->core) - { - fuzzpos = (fuzzpos + count) % FUZZTABLE; - continue; - } - uint32_t *dest = ((uint32_t*)destOrg) + y * destPitch + x0; uint32_t posU = startU; @@ -896,7 +900,7 @@ private: // Sample fgcolor if (SamplerT::Mode == (int)Samplers::FogBoundary) color = *dest; unsigned int ifgcolor = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - unsigned int ifgshade = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + unsigned int ifgshade = SampleShade32(posU, posV, texPixels, texWidth, texHeight, x0 + i, y); posU += stepU; // Shade and blend diff --git a/src/polyrenderer/poly_renderer.cpp b/src/polyrenderer/poly_renderer.cpp index c3389bba5..a0aa12c32 100644 --- a/src/polyrenderer/poly_renderer.cpp +++ b/src/polyrenderer/poly_renderer.cpp @@ -119,6 +119,8 @@ void PolyRenderer::RenderActorView(AActor *actor, bool dontmaplines) P_FindParticleSubsectors(); PO_LinkToSubsectors(); + swrenderer::R_UpdateFuzzPosFrameStart(); + if (APART(R_OldBlend)) NormalLight.Maps = realcolormaps.Maps; else NormalLight.Maps = realcolormaps.Maps + NUMCOLORMAPS * 256 * R_OldBlend; diff --git a/src/swrenderer/drawers/r_draw.cpp b/src/swrenderer/drawers/r_draw.cpp index 793cf2ae4..bc7eb731d 100644 --- a/src/swrenderer/drawers/r_draw.cpp +++ b/src/swrenderer/drawers/r_draw.cpp @@ -193,7 +193,7 @@ namespace swrenderer void R_UpdateFuzzPosFrameStart() { - if (r_fuzzscale) + if (r_fuzzscale || r_polyrenderer) { static int next_random = 0; @@ -207,7 +207,7 @@ namespace swrenderer void R_UpdateFuzzPos(const SpriteDrawerArgs &args) { - if (!r_fuzzscale) + if (!r_fuzzscale && !r_polyrenderer) { int yl = MAX(args.FuzzY1(), 1); int yh = MIN(args.FuzzY2(), fuzzviewheight); From f04fca75f710a2e82027911f17f6df22a424d9d8 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 22 Nov 2017 18:46:45 +0100 Subject: [PATCH 05/10] - Add scaled fuzz to softpoly sse2 drawer --- src/polyrenderer/drawers/poly_drawer32_sse2.h | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/polyrenderer/drawers/poly_drawer32_sse2.h b/src/polyrenderer/drawers/poly_drawer32_sse2.h index 8c245eec9..01c614434 100644 --- a/src/polyrenderer/drawers/poly_drawer32_sse2.h +++ b/src/polyrenderer/drawers/poly_drawer32_sse2.h @@ -107,7 +107,7 @@ namespace TriScreenDrawerModes } template - FORCEINLINE unsigned int VECTORCALL SampleShade32(int32_t u, int32_t v, const uint32_t *texPixels, int texWidth, int texHeight, int &fuzzpos) + FORCEINLINE unsigned int VECTORCALL SampleShade32(int32_t u, int32_t v, const uint32_t *texPixels, int texWidth, int texHeight, int x, int y) { if (SamplerT::Mode == (int)Samplers::Shaded) { @@ -128,12 +128,23 @@ namespace TriScreenDrawerModes } else if (SamplerT::Mode == (int)Samplers::Fuzz) { + using namespace swrenderer; + uint32_t texelX = ((((uint32_t)u << 8) >> 16) * texWidth) >> 16; uint32_t texelY = ((((uint32_t)v << 8) >> 16) * texHeight) >> 16; unsigned int sampleshadeout = APART(texPixels[texelX * texHeight + texelY]); sampleshadeout += sampleshadeout >> 7; // 255 -> 256 - sampleshadeout = (sampleshadeout * fuzzcolormap[fuzzpos++]) >> 5; - if (fuzzpos >= FUZZTABLE) fuzzpos = 0; + + fixed_t fuzzscale = (200 << FRACBITS) / viewheight; + + int scaled_x = (x * fuzzscale) >> FRACBITS; + int fuzz_x = fuzz_random_x_offset[scaled_x % FUZZ_RANDOM_X_SIZE] + fuzzpos; + + fixed_t fuzzcount = FUZZTABLE << FRACBITS; + fixed_t fuzz = ((fuzz_x << FRACBITS) + y * fuzzscale) % fuzzcount; + unsigned int alpha = fuzzoffset[fuzz >> FRACBITS]; + + sampleshadeout = (sampleshadeout * alpha) >> 5; return sampleshadeout; } else @@ -392,8 +403,6 @@ private: uint32_t srcalpha = args->uniforms->SrcAlpha(); uint32_t destalpha = args->uniforms->DestAlpha(); - int fuzzpos = (ScreenTriangle::FuzzStart + destX * 123 + destY) % FUZZTABLE; - auto lights = args->uniforms->Lights(); auto num_lights = args->uniforms->NumLights(); __m128 worldnormal = _mm_setr_ps(args->uniforms->Normal().X, args->uniforms->Normal().Y, args->uniforms->Normal().Z, 0.0f); @@ -518,13 +527,13 @@ private: unsigned int ifgcolor[2], ifgshade[2]; if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[ix * 2]; ifgcolor[0] = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - ifgshade[0] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + ifgshade[0] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, destX + ix * 2, destY + y); posU += stepU; posV += stepV; if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[ix * 2 + 1]; ifgcolor[1] = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - ifgshade[1] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + ifgshade[1] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, destX + ix * 2 + 1, destY + y); posU += stepU; posV += stepV; @@ -626,13 +635,13 @@ private: unsigned int ifgcolor[2], ifgshade[2]; if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[x * 2]; ifgcolor[0] = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - ifgshade[0] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + ifgshade[0] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, destX + x * 2, destY + y); posU += stepU; posV += stepV; if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[x * 2 + 1]; ifgcolor[1] = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - ifgshade[1] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + ifgshade[1] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, destX + x * 2 + 1, destY + y); posU += stepU; posV += stepV; @@ -736,13 +745,13 @@ private: unsigned int ifgcolor[2], ifgshade[2]; if (SamplerT::Mode == (int)Samplers::FogBoundary && (mask1 & (1 << 31))) color = dest[x * 2]; ifgcolor[0] = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - ifgshade[0] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + ifgshade[0] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, destX + x * 2, destY + 4 + y); posU += stepU; posV += stepV; if (SamplerT::Mode == (int)Samplers::FogBoundary && (mask1 & (1 << 30))) color = dest[x * 2 + 1]; ifgcolor[1] = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - ifgshade[1] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + ifgshade[1] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, destX + x * 2 + 1, destY + 4 + y); posU += stepU; posV += stepV; @@ -889,18 +898,9 @@ private: int count = x1 - x0; int sseCount = count / 2; - int fuzzpos = (ScreenTriangle::FuzzStart + x0 * 123 + y0) % FUZZTABLE; - uint32_t posV = startV; for (int y = y0; y < y1; y++, posV += stepV) { - int coreBlock = y / 8; - if (coreBlock % thread->num_cores != thread->core) - { - fuzzpos = (fuzzpos + count) % FUZZTABLE; - continue; - } - uint32_t *dest = ((uint32_t*)destOrg) + y * destPitch + x0; uint32_t posU = startU; @@ -917,12 +917,12 @@ private: unsigned int ifgcolor[2], ifgshade[2]; if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[0]; ifgcolor[0] = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - ifgshade[0] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + ifgshade[0] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, x0 + i * 2, y); posU += stepU; if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[1]; ifgcolor[1] = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - ifgshade[1] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + ifgshade[1] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, x0 + i * 2 + 1, y); posU += stepU; // Shade and blend @@ -948,7 +948,7 @@ private: unsigned int ifgcolor[2], ifgshade[2]; if (SamplerT::Mode == (int)Samplers::FogBoundary) color = *dest; ifgcolor[0] = Sample32(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation); - ifgshade[0] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + ifgshade[0] = SampleShade32(posU, posV, texPixels, texWidth, texHeight, x0 + sseCount * 2, y); ifgcolor[1] = 0; ifgshade[1] = 0; posU += stepU; From 9052ee6bc80c4ab647d5ac8c1e1f2886872f848f Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 22 Nov 2017 18:54:49 +0100 Subject: [PATCH 06/10] - Added scaled fuzz to softpoly pal drawer --- src/polyrenderer/drawers/poly_drawer8.h | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/polyrenderer/drawers/poly_drawer8.h b/src/polyrenderer/drawers/poly_drawer8.h index f78d2a31c..d5ec5bade 100644 --- a/src/polyrenderer/drawers/poly_drawer8.h +++ b/src/polyrenderer/drawers/poly_drawer8.h @@ -79,7 +79,7 @@ namespace TriScreenDrawerModes } template - FORCEINLINE unsigned int SampleShade8(int32_t u, int32_t v, const uint8_t *texPixels, int texWidth, int texHeight, int &fuzzpos) + FORCEINLINE unsigned int SampleShade8(int32_t u, int32_t v, const uint8_t *texPixels, int texWidth, int texHeight, int x, int y) { if (SamplerT::Mode == (int)Samplers::Shaded) { @@ -97,11 +97,22 @@ namespace TriScreenDrawerModes } else if (SamplerT::Mode == (int)Samplers::Fuzz) { + using namespace swrenderer; + uint32_t texelX = ((((uint32_t)u << 8) >> 16) * texWidth) >> 16; uint32_t texelY = ((((uint32_t)v << 8) >> 16) * texHeight) >> 16; unsigned int sampleshadeout = (texPixels[texelX * texHeight + texelY] != 0) ? 256 : 0; - sampleshadeout = (sampleshadeout * fuzzcolormap[fuzzpos++]) >> 5; - if (fuzzpos >= FUZZTABLE) fuzzpos = 0; + + fixed_t fuzzscale = (200 << FRACBITS) / viewheight; + + int scaled_x = (x * fuzzscale) >> FRACBITS; + int fuzz_x = fuzz_random_x_offset[scaled_x % FUZZ_RANDOM_X_SIZE] + fuzzpos; + + fixed_t fuzzcount = FUZZTABLE << FRACBITS; + fixed_t fuzz = ((fuzz_x << FRACBITS) + y * fuzzscale) % fuzzcount; + unsigned int alpha = fuzzoffset[fuzz >> FRACBITS]; + + sampleshadeout = (sampleshadeout * alpha) >> 5; return sampleshadeout; } else @@ -228,8 +239,6 @@ public: uint32_t srcalpha = args->uniforms->SrcAlpha(); uint32_t destalpha = args->uniforms->DestAlpha(); - int fuzzpos = (ScreenTriangle::FuzzStart + destX * 123 + destY) % FUZZTABLE; - // Calculate gradients const ShadedTriVertex &v1 = *args->v1; ScreenTriangleStepVariables gradientX = args->gradientX; @@ -292,7 +301,7 @@ public: uint8_t bgcolor = dest[ix]; if (SamplerT::Mode == (int)Samplers::FogBoundary) color = bgcolor; uint8_t fgcolor = Sample8(posU, posV, texPixels, texWidth, texHeight, color, translation); - uint32_t fgshade = SampleShade8(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + uint32_t fgshade = SampleShade8(posU, posV, texPixels, texWidth, texHeight, destX + ix, destY + y); if (SamplerT::Mode == (int)Samplers::Fuzz) lightshade = 256; dest[ix] = ShadeAndBlend8(fgcolor, bgcolor, fgshade, lightshade, colormaps, srcalpha, destalpha); posU += stepU; @@ -342,7 +351,7 @@ public: uint8_t bgcolor = dest[x]; if (SamplerT::Mode == (int)Samplers::FogBoundary) color = bgcolor; uint8_t fgcolor = Sample8(posU, posV, texPixels, texWidth, texHeight, color, translation); - uint32_t fgshade = SampleShade8(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + uint32_t fgshade = SampleShade8(posU, posV, texPixels, texWidth, texHeight, destX + x, destY + y); if (SamplerT::Mode == (int)Samplers::Fuzz) lightshade = 256; dest[x] = ShadeAndBlend8(fgcolor, bgcolor, fgshade, lightshade, colormaps, srcalpha, destalpha); } @@ -394,7 +403,7 @@ public: uint8_t bgcolor = dest[x]; if (SamplerT::Mode == (int)Samplers::FogBoundary) color = bgcolor; uint8_t fgcolor = Sample8(posU, posV, texPixels, texWidth, texHeight, color, translation); - uint32_t fgshade = SampleShade8(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + uint32_t fgshade = SampleShade8(posU, posV, texPixels, texWidth, texHeight, destX + x, destY + 4 + y); if (SamplerT::Mode == (int)Samplers::Fuzz) lightshade = 256; dest[x] = ShadeAndBlend8(fgcolor, bgcolor, fgshade, lightshade, colormaps, srcalpha, destalpha); } @@ -458,18 +467,9 @@ public: int count = x1 - x0; - int fuzzpos = (ScreenTriangle::FuzzStart + x0 * 123 + y0) % FUZZTABLE; - uint32_t posV = startV; for (int y = y0; y < y1; y++, posV += stepV) { - int coreBlock = y / 8; - if (coreBlock % thread->num_cores != thread->core) - { - fuzzpos = (fuzzpos + count) % FUZZTABLE; - continue; - } - uint8_t *dest = ((uint8_t*)destOrg) + y * destPitch + x0; uint32_t posU = startU; @@ -478,7 +478,7 @@ public: uint8_t bgcolor = *dest; if (SamplerT::Mode == (int)Samplers::FogBoundary) color = bgcolor; uint8_t fgcolor = Sample8(posU, posV, texPixels, texWidth, texHeight, color, translation); - uint32_t fgshade = SampleShade8(posU, posV, texPixels, texWidth, texHeight, fuzzpos); + uint32_t fgshade = SampleShade8(posU, posV, texPixels, texWidth, texHeight, x0 + i, y); *dest = ShadeAndBlend8(fgcolor, bgcolor, fgshade, lightshade, colormaps, srcalpha, destalpha); posU += stepU; From e7e9d1a94206dd8ee0edcebdc6bebffc46e2301f Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 22 Nov 2017 19:02:25 +0100 Subject: [PATCH 07/10] - Fixed bug introduced by removing a little bit too much from the rect drawers --- src/polyrenderer/drawers/poly_drawer32.h | 6 ++++++ src/polyrenderer/drawers/poly_drawer32_sse2.h | 6 ++++++ src/polyrenderer/drawers/poly_drawer8.h | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/polyrenderer/drawers/poly_drawer32.h b/src/polyrenderer/drawers/poly_drawer32.h index f3d05ae74..1acb3de1b 100644 --- a/src/polyrenderer/drawers/poly_drawer32.h +++ b/src/polyrenderer/drawers/poly_drawer32.h @@ -885,6 +885,12 @@ private: uint32_t posV = startV; for (int y = y0; y < y1; y++, posV += stepV) { + int coreBlock = y / 8; + if (coreBlock % thread->num_cores != thread->core) + { + continue; + } + uint32_t *dest = ((uint32_t*)destOrg) + y * destPitch + x0; uint32_t posU = startU; diff --git a/src/polyrenderer/drawers/poly_drawer32_sse2.h b/src/polyrenderer/drawers/poly_drawer32_sse2.h index 01c614434..3826d063d 100644 --- a/src/polyrenderer/drawers/poly_drawer32_sse2.h +++ b/src/polyrenderer/drawers/poly_drawer32_sse2.h @@ -901,6 +901,12 @@ private: uint32_t posV = startV; for (int y = y0; y < y1; y++, posV += stepV) { + int coreBlock = y / 8; + if (coreBlock % thread->num_cores != thread->core) + { + continue; + } + uint32_t *dest = ((uint32_t*)destOrg) + y * destPitch + x0; uint32_t posU = startU; diff --git a/src/polyrenderer/drawers/poly_drawer8.h b/src/polyrenderer/drawers/poly_drawer8.h index d5ec5bade..fb7169186 100644 --- a/src/polyrenderer/drawers/poly_drawer8.h +++ b/src/polyrenderer/drawers/poly_drawer8.h @@ -470,6 +470,12 @@ public: uint32_t posV = startV; for (int y = y0; y < y1; y++, posV += stepV) { + int coreBlock = y / 8; + if (coreBlock % thread->num_cores != thread->core) + { + continue; + } + uint8_t *dest = ((uint8_t*)destOrg) + y * destPitch + x0; uint32_t posU = startU; From 978fdfb27337d5b4130ebc317f19243648beda15 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 22 Nov 2017 21:42:48 +0100 Subject: [PATCH 08/10] - tested and fixed UMAPINFO parser. Although this looks like it's working with the test file I used it still needs some stress testing! --- src/g_mapinfo.cpp | 1 + src/umapinfo.cpp | 218 ++++++++++++++++++++++++++++------------------ 2 files changed, 133 insertions(+), 86 deletions(-) diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index cb750f4a1..0481c1784 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -2243,6 +2243,7 @@ void G_ParseMapInfo (FString basemapinfo) ParseUMapInfo(lump); } } + CommitUMapinfo(&gamedefaults); // commit remaining UMPAINFOs. if (AllEpisodes.Size() == 0) { diff --git a/src/umapinfo.cpp b/src/umapinfo.cpp index c8d416740..b6b435c40 100644 --- a/src/umapinfo.cpp +++ b/src/umapinfo.cpp @@ -45,18 +45,18 @@ struct UMapEntry FString InterTextSecret; TArray BossActions; - char levelpic[9]; - char nextmap[9]; - char nextsecret[9]; - char music[9]; - char skytexture[9]; - char endpic[9]; - char exitpic[9]; - char enterpic[9]; + char levelpic[9] = ""; + char nextmap[9] = ""; + char nextsecret[9] = ""; + char music[9] = ""; + char skytexture[9] = ""; + char endpic[9] = ""; + char exitpic[9] = ""; + char enterpic[9] = ""; char interbackdrop[9] = "FLOOR4_8"; - char intermusic[9]; - int partime; - int nointermission; + char intermusic[9] = ""; + int partime = 0; + int nointermission = 0; }; static TArray Maps; @@ -132,7 +132,7 @@ static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape) if (!pname.CompareNoCase("levelname")) { scanner.MustGetToken(TK_StringConst); - mape->LevelName, scanner.String; + mape->LevelName = scanner.String; } else if (!pname.CompareNoCase("next")) { @@ -216,7 +216,43 @@ static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape) { FString Episode = ParseMultiString(scanner, 1); if (Episode.IsEmpty()) return 0; - //M_AddEpisode(mape->mapname, lname); + if (Episode.Compare("-") == 0) + { + // clear the given episode + for (unsigned i = 0; i < AllEpisodes.Size(); i++) + { + if (AllEpisodes[i].mEpisodeMap.CompareNoCase(mape->MapName) == 0) + { + AllEpisodes.Delete(i); + break; + } + } + } + else + { + auto split = Episode.Split("\n"); + // add the given episode + FEpisode epi; + + epi.mEpisodeName = split[1]; + epi.mEpisodeMap = mape->MapName; + epi.mPicName = split[0]; + epi.mShortcut = split[2][0]; + + unsigned i; + for (i = 0; i < AllEpisodes.Size(); i++) + { + if (AllEpisodes[i].mEpisodeMap.CompareNoCase(mape->MapName) == 0) + { + AllEpisodes[i] = std::move(epi); + break; + } + } + if (i == AllEpisodes.Size()) + { + AllEpisodes.Push(epi); + } + } } else if (!pname.CompareNoCase("bossaction")) { @@ -255,12 +291,12 @@ static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape) // Skip over all unknown properties. do { - if (!scanner.CheckFloat()) + if (!scanner.CheckValue(true)) { scanner.MustGetAnyToken(); if (scanner.TokenType != TK_Identifier && scanner.TokenType != TK_StringConst && scanner.TokenType != TK_True && scanner.TokenType != TK_False) { - scanner.ScriptError("Identifier or value expecte3d"); + scanner.ScriptError("Identifier or value expected"); } } @@ -279,7 +315,7 @@ static int ParseMapEntry(FScanner &scanner, UMapEntry *val) { scanner.MustGetToken(TK_Identifier); - val->MapName, scanner.String; + val->MapName = scanner.String; scanner.MustGetToken('{'); while(!scanner.CheckToken('}')) { @@ -299,12 +335,10 @@ int ParseUMapInfo(int lumpnum) FScanner scanner(lumpnum); unsigned int i; - while (scanner.CheckToken(TK_Identifier)) + while (scanner.GetToken()) { - if (!scanner.Compare("map")) - { - scanner.ScriptError("'MAP' expected"); - } + scanner.TokenMustBe(TK_Map); + UMapEntry parsed; ParseMapEntry(scanner, &parsed); @@ -355,73 +389,85 @@ void CommitUMapinfo(level_info_t *defaultinfo) auto levelinfo = FindLevelInfo(map.MapName); if (levelinfo == nullptr) { + // Map did not exist yet. + auto levelindex = wadlevelinfos.Reserve(1); + levelinfo = &wadlevelinfos[levelindex]; *levelinfo = *defaultinfo; - if (map.MapName.IsNotEmpty()) levelinfo->MapName = map.MapName; - if (map.MapName.IsNotEmpty()) levelinfo->LevelName = map.LevelName; - if (map.levelpic[0]) levelinfo->PName = map.levelpic; - if (map.nextmap[0]) levelinfo->NextMap = map.nextmap; - else if (map.endpic[0]) - { - FName name; - - if (!stricmp(map.endpic, "$CAST")) - { - name = "INTER_CAST"; - } - else if (!stricmp(map.endpic, "$BUNNY")) - { - name = "INTER_BUNNY"; - } - else - { - name = MakeEndPic(map.endpic); - } - if (name != NAME_None) - { - levelinfo->NextMap.Format("enDSeQ%04x", int(name)); - } - } - - if (map.nextsecret[0]) levelinfo->NextSecretMap = map.nextsecret; - if (map.music[0]) - { - levelinfo->Music = map.music; - levelinfo->musicorder = 0; - } - if (map.skytexture[0]) - { - levelinfo->SkyPic1 = map.skytexture; - levelinfo->skyspeed1 = 0; - levelinfo->SkyPic2 = ""; - levelinfo->skyspeed2 = 0; - } - if (map.partime > 0) levelinfo->partime = map.partime; - if (map.enterpic[0]) levelinfo->EnterPic = map.enterpic; - if (map.exitpic[0]) levelinfo->ExitPic = map.exitpic; - if (map.intermusic[0]) - { - levelinfo->InterMusic = map.intermusic; - levelinfo->intermusicorder = 0; - } - if (map.BossActions.Size() > 0) levelinfo->specialactions = std::move(map.BossActions); - - const int exflags = FExitText::DEF_TEXT | FExitText::DEF_BACKDROP | FExitText::DEF_MUSIC; - if (map.InterText.IsNotEmpty()) - { - if (map.InterText.Compare("-") != 0) - levelinfo->ExitMapTexts[NAME_Normal] = { exflags, 0, map.InterText, map.interbackdrop, map.intermusic[0]? map.intermusic : gameinfo.intermissionMusic }; - else - levelinfo->ExitMapTexts[NAME_Normal] = { 0, 0 }; - } - if (map.InterTextSecret.IsNotEmpty()) - { - if (map.InterTextSecret.Compare("-") != 0) - levelinfo->ExitMapTexts[NAME_Secret] = { exflags, 0, map.InterTextSecret, map.interbackdrop, map.intermusic[0] ? map.intermusic : gameinfo.intermissionMusic }; - else - levelinfo->ExitMapTexts[NAME_Secret] = { 0, 0 }; - } - if (map.nointermission) levelinfo->flags |= LEVEL_NOINTERMISSION; } + if (map.MapName.IsNotEmpty()) levelinfo->MapName = map.MapName; + if (map.LevelName.IsNotEmpty()) + { + levelinfo->LevelName = map.LevelName; + levelinfo->PName = ""; // clear the map name patch to force the string version to be shown - unless explicitly overridden right next. + } + if (map.levelpic[0]) levelinfo->PName = map.levelpic; + if (map.nextmap[0]) levelinfo->NextMap = map.nextmap; + else if (map.endpic[0]) + { + FName name; + + if (!stricmp(map.endpic, "$CAST")) + { + name = "INTER_CAST"; + } + else if (!stricmp(map.endpic, "$BUNNY")) + { + name = "INTER_BUNNY"; + } + else + { + name = MakeEndPic(map.endpic); + } + if (name != NAME_None) + { + levelinfo->NextMap.Format("enDSeQ%04x", int(name)); + } + } + + if (map.nextsecret[0]) levelinfo->NextSecretMap = map.nextsecret; + if (map.music[0]) + { + levelinfo->Music = map.music; + levelinfo->musicorder = 0; + } + if (map.skytexture[0]) + { + levelinfo->SkyPic1 = map.skytexture; + levelinfo->skyspeed1 = 0; + levelinfo->SkyPic2 = ""; + levelinfo->skyspeed2 = 0; + } + if (map.partime > 0) levelinfo->partime = map.partime; + if (map.enterpic[0]) levelinfo->EnterPic = map.enterpic; + if (map.exitpic[0]) levelinfo->ExitPic = map.exitpic; + if (map.intermusic[0]) + { + levelinfo->InterMusic = map.intermusic; + levelinfo->intermusicorder = 0; + } + if (map.BossActions.Size() > 0) + { + // Setting a boss action will deactivate the flag based monster actions. + levelinfo->specialactions = std::move(map.BossActions); + levelinfo->flags &= ~(LEVEL_BRUISERSPECIAL | LEVEL_CYBORGSPECIAL | LEVEL_SPIDERSPECIAL | LEVEL_MAP07SPECIAL | LEVEL_MINOTAURSPECIAL | LEVEL_HEADSPECIAL | LEVEL_SORCERER2SPECIAL | LEVEL_SPECACTIONSMASK | LEVEL_SPECKILLMONSTERS); + } + + const int exflags = FExitText::DEF_TEXT | FExitText::DEF_BACKDROP | FExitText::DEF_MUSIC; + if (map.InterText.IsNotEmpty()) + { + if (map.InterText.Compare("-") != 0) + levelinfo->ExitMapTexts[NAME_Normal] = { exflags, 0, map.InterText, map.interbackdrop, map.intermusic[0]? map.intermusic : gameinfo.intermissionMusic }; + else + levelinfo->ExitMapTexts[NAME_Normal] = { 0, 0 }; + } + if (map.InterTextSecret.IsNotEmpty()) + { + if (map.InterTextSecret.Compare("-") != 0) + levelinfo->ExitMapTexts[NAME_Secret] = { exflags, 0, map.InterTextSecret, map.interbackdrop, map.intermusic[0] ? map.intermusic : gameinfo.intermissionMusic }; + else + levelinfo->ExitMapTexts[NAME_Secret] = { 0, 0 }; + } + if (map.nointermission) levelinfo->flags |= LEVEL_NOINTERMISSION; } From f1e7df542f7edbd8bb27d98ff62b55fb6a5abc19 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Fri, 24 Nov 2017 00:38:52 +0100 Subject: [PATCH 09/10] - Create model renderer abstraction to share more model drawing between renderers --- src/gl/data/gl_vertexbuffer.h | 29 +- src/gl/models/gl_models.cpp | 584 ++++++++++++++++++-------------- src/gl/models/gl_models.h | 68 +++- src/gl/models/gl_models_md2.cpp | 23 +- src/gl/models/gl_models_md3.cpp | 23 +- src/gl/models/gl_voxels.cpp | 18 +- src/gl/textures/gl_texture.cpp | 3 +- 7 files changed, 436 insertions(+), 312 deletions(-) diff --git a/src/gl/data/gl_vertexbuffer.h b/src/gl/data/gl_vertexbuffer.h index b1bc60f0d..8f4b22550 100644 --- a/src/gl/data/gl_vertexbuffer.h +++ b/src/gl/data/gl_vertexbuffer.h @@ -293,8 +293,23 @@ struct FModelVertex } }; +class FModelRenderer; -class FModelVertexBuffer : public FVertexBuffer +class IModelVertexBuffer +{ +public: + virtual ~IModelVertexBuffer() { } + + virtual FModelVertex *LockVertexBuffer(unsigned int size) = 0; + virtual void UnlockVertexBuffer() = 0; + + virtual unsigned int *LockIndexBuffer(unsigned int size) = 0; + virtual void UnlockIndexBuffer() = 0; + + virtual void SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size) = 0; +}; + +class FModelVertexBuffer : public FVertexBuffer, public IModelVertexBuffer { int mIndexFrame[2]; FModelVertex *vbo_ptr; @@ -305,14 +320,14 @@ public: FModelVertexBuffer(bool needindex, bool singleframe); ~FModelVertexBuffer(); - FModelVertex *LockVertexBuffer(unsigned int size); - void UnlockVertexBuffer(); + FModelVertex *LockVertexBuffer(unsigned int size) override; + void UnlockVertexBuffer() override; - unsigned int *LockIndexBuffer(unsigned int size); - void UnlockIndexBuffer(); + unsigned int *LockIndexBuffer(unsigned int size) override; + void UnlockIndexBuffer() override; - unsigned int SetupFrame(unsigned int frame1, unsigned int frame2, unsigned int size); - void BindVBO(); + void SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size) override; + void BindVBO() override; }; #define VMO ((FModelVertex*)NULL) diff --git a/src/gl/models/gl_models.cpp b/src/gl/models/gl_models.cpp index fcad2a95b..24ad00845 100644 --- a/src/gl/models/gl_models.cpp +++ b/src/gl/models/gl_models.cpp @@ -52,11 +52,6 @@ #include "gl/renderer/gl_renderstate.h" #include "gl/shaders/gl_shader.h" -static inline float GetTimeFloat() -{ - return (float)I_MSTime() * (float)TICRATE / 1000.0f; -} - CVAR(Bool, gl_interpolate_model_frames, true, CVAR_ARCHIVE) CVAR(Bool, gl_light_models, true, CVAR_ARCHIVE) EXTERN_CVAR(Int, gl_fogmode) @@ -66,6 +61,332 @@ extern TDeletingArray VoxelDefs; DeletingModelArray Models; +void FModelRenderer::RenderModel(float x, float y, float z, FSpriteModelFrame *smf, AActor *actor) +{ + // Setup transformation. + + int translation = 0; + if (!(smf->flags & MDL_IGNORETRANSLATION)) + translation = actor->Translation; + + // y scale for a sprite means height, i.e. z in the world! + float scaleFactorX = actor->Scale.X * smf->xscale; + float scaleFactorY = actor->Scale.X * smf->yscale; + float scaleFactorZ = actor->Scale.Y * smf->zscale; + float pitch = 0; + float roll = 0; + float rotateOffset = 0; + float angle = actor->Angles.Yaw.Degrees; + + // [BB] Workaround for the missing pitch information. + if ((smf->flags & MDL_PITCHFROMMOMENTUM)) + { + const double x = actor->Vel.X; + const double y = actor->Vel.Y; + const double z = actor->Vel.Z; + + if (actor->Vel.LengthSquared() > EQUAL_EPSILON) + { + // [BB] Calculate the pitch using spherical coordinates. + if (z || x || y) pitch = float(atan(z / sqrt(x*x + y*y)) / M_PI * 180); + + // Correcting pitch if model is moving backwards + if (fabs(x) > EQUAL_EPSILON || fabs(y) > EQUAL_EPSILON) + { + if ((x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180)) / sqrt(x * x + y * y) < 0) pitch *= -1; + } + else pitch = fabs(pitch); + } + } + + if (smf->flags & MDL_ROTATING) + { + const float time = smf->rotationSpeed*GetTimeFloat() / 200.f; + rotateOffset = float((time - xs_FloorToInt(time)) *360.f); + } + + // Added MDL_USEACTORPITCH and MDL_USEACTORROLL flags processing. + // If both flags MDL_USEACTORPITCH and MDL_PITCHFROMMOMENTUM are set, the pitch sums up the actor pitch and the velocity vector pitch. + if (smf->flags & MDL_USEACTORPITCH) + { + double d = actor->Angles.Pitch.Degrees; + if (smf->flags & MDL_BADROTATION) pitch += d; + else pitch -= d; + } + if (smf->flags & MDL_USEACTORROLL) roll += actor->Angles.Roll.Degrees; + + VSMatrix objectToWorldMatrix; + objectToWorldMatrix.loadIdentity(); + + // Model space => World space + objectToWorldMatrix.translate(x, z, y); + + // [Nash] take SpriteRotation into account + angle += actor->SpriteRotation.Degrees; + + if (actor->renderflags & RF_INTERPOLATEANGLES) + { + // [Nash] use interpolated angles + DRotator Angles = actor->InterpolatedAngles(r_viewpoint.TicFrac); + angle = Angles.Yaw.Degrees; + } + + // Applying model transformations: + // 1) Applying actor angle, pitch and roll to the model + objectToWorldMatrix.rotate(-angle, 0, 1, 0); + objectToWorldMatrix.rotate(pitch, 0, 0, 1); + objectToWorldMatrix.rotate(-roll, 1, 0, 0); + + // 2) Applying Doomsday like rotation of the weapon pickup models + // The rotation angle is based on the elapsed time. + + if (smf->flags & MDL_ROTATING) + { + objectToWorldMatrix.translate(smf->rotationCenterX, smf->rotationCenterY, smf->rotationCenterZ); + objectToWorldMatrix.rotate(rotateOffset, smf->xrotate, smf->yrotate, smf->zrotate); + objectToWorldMatrix.translate(-smf->rotationCenterX, -smf->rotationCenterY, -smf->rotationCenterZ); + } + + // 3) Scaling model. + objectToWorldMatrix.scale(scaleFactorX, scaleFactorZ, scaleFactorY); + + // 4) Aplying model offsets (model offsets do not depend on model scalings). + objectToWorldMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); + + // 5) Applying model rotations. + objectToWorldMatrix.rotate(-smf->angleoffset, 0, 1, 0); + objectToWorldMatrix.rotate(smf->pitchoffset, 0, 0, 1); + objectToWorldMatrix.rotate(-smf->rolloffset, 1, 0, 0); + + // consider the pixel stretching. For non-voxels this must be factored out here + float stretch = (smf->modelIDs[0] != -1 ? Models[smf->modelIDs[0]]->getAspectFactor() : 1.f) / level.info->pixelstretch; + objectToWorldMatrix.scale(1, stretch, 1); + + BeginDrawModel(actor, smf, objectToWorldMatrix); + RenderFrameModels(smf, actor->state, actor->tics, actor->GetClass(), nullptr, translation); + EndDrawModel(actor, smf); +} + +void FModelRenderer::RenderHUDModel(DPSprite *psp, float ofsX, float ofsY) +{ + AActor * playermo = players[consoleplayer].camera; + FSpriteModelFrame *smf = gl_FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->GetState()->sprite, psp->GetState()->GetFrame(), false); + + // [BB] No model found for this sprite, so we can't render anything. + if (smf == nullptr) + return; + + // The model position and orientation has to be drawn independently from the position of the player, + // but we need to position it correctly in the world for light to work properly. + VSMatrix objectToWorldMatrix = GetViewToWorldMatrix(); + + // Scaling model (y scale for a sprite means height, i.e. z in the world!). + objectToWorldMatrix.scale(smf->xscale, smf->zscale, smf->yscale); + + // Aplying model offsets (model offsets do not depend on model scalings). + objectToWorldMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); + + // [BB] Weapon bob, very similar to the normal Doom weapon bob. + objectToWorldMatrix.rotate(ofsX / 4, 0, 1, 0); + objectToWorldMatrix.rotate((ofsY - WEAPONTOP) / -4., 1, 0, 0); + + // [BB] For some reason the jDoom models need to be rotated. + objectToWorldMatrix.rotate(90.f, 0, 1, 0); + + // Applying angleoffset, pitchoffset, rolloffset. + objectToWorldMatrix.rotate(-smf->angleoffset, 0, 1, 0); + objectToWorldMatrix.rotate(smf->pitchoffset, 0, 0, 1); + objectToWorldMatrix.rotate(-smf->rolloffset, 1, 0, 0); + + BeginDrawHUDModel(playermo, objectToWorldMatrix); + RenderFrameModels(smf, psp->GetState(), psp->GetTics(), playermo->player->ReadyWeapon->GetClass(), nullptr, 0); + EndDrawHUDModel(playermo); +} + +void FModelRenderer::RenderFrameModels(const FSpriteModelFrame *smf, + const FState *curState, + const int curTics, + const PClass *ti, + Matrix3x4 *normaltransform, + int translation) +{ + // [BB] Frame interpolation: Find the FSpriteModelFrame smfNext which follows after smf in the animation + // and the scalar value inter ( element of [0,1) ), both necessary to determine the interpolated frame. + FSpriteModelFrame * smfNext = nullptr; + double inter = 0.; + if (gl_interpolate_model_frames && !(smf->flags & MDL_NOINTERPOLATION)) + { + FState *nextState = curState->GetNextState(); + if (curState != nextState && nextState) + { + // [BB] To interpolate at more than 35 fps we take tic fractions into account. + float ticFraction = 0.; + // [BB] In case the tic counter is frozen we have to leave ticFraction at zero. + if (ConsoleState == c_up && menuactive != MENU_On && !(level.flags2 & LEVEL2_FROZEN)) + { + float time = GetTimeFloat(); + ticFraction = (time - static_cast(time)); + } + inter = static_cast(curState->Tics - curTics - ticFraction) / static_cast(curState->Tics); + + // [BB] For some actors (e.g. ZPoisonShroom) spr->actor->tics can be bigger than curState->Tics. + // In this case inter is negative and we need to set it to zero. + if (inter < 0.) + inter = 0.; + else + { + // [BB] Workaround for actors that use the same frame twice in a row. + // Most of the standard Doom monsters do this in their see state. + if ((smf->flags & MDL_INTERPOLATEDOUBLEDFRAMES)) + { + const FState *prevState = curState - 1; + if ((curState->sprite == prevState->sprite) && (curState->Frame == prevState->Frame)) + { + inter /= 2.; + inter += 0.5; + } + if ((curState->sprite == nextState->sprite) && (curState->Frame == nextState->Frame)) + { + inter /= 2.; + nextState = nextState->GetNextState(); + } + } + if (inter != 0.0) + smfNext = gl_FindModelFrame(ti, nextState->sprite, nextState->Frame, false); + } + } + } + + for (int i = 0; imodelIDs[i] != -1) + { + FModel * mdl = Models[smf->modelIDs[i]]; + FTexture *tex = smf->skinIDs[i].isValid() ? TexMan(smf->skinIDs[i]) : nullptr; + mdl->BuildVertexBuffer(this); + SetVertexBuffer(mdl->mVBuf); + + mdl->PushSpriteMDLFrame(smf, i); + + if (smfNext && smf->modelframes[i] != smfNext->modelframes[i]) + mdl->RenderFrame(this, tex, smf->modelframes[i], smfNext->modelframes[i], inter, translation); + else + mdl->RenderFrame(this, tex, smf->modelframes[i], smf->modelframes[i], 0.f, translation); + + ResetVertexBuffer(); + } + } +} + +///////////////////////////////////////////////////////////////////////////// + +extern int modellightindex; + +VSMatrix FGLModelRenderer::GetViewToWorldMatrix() +{ + VSMatrix objectToWorldMatrix; + gl_RenderState.mViewMatrix.inverseMatrix(objectToWorldMatrix); + return objectToWorldMatrix; +} + +void FGLModelRenderer::BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix) +{ + glDepthFunc(GL_LEQUAL); + gl_RenderState.EnableTexture(true); + // [BB] In case the model should be rendered translucent, do back face culling. + // This solves a few of the problems caused by the lack of depth sorting. + // [Nash] Don't do back face culling if explicitly specified in MODELDEF + // TO-DO: Implement proper depth sorting. + if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal]) && !(smf->flags & MDL_DONTCULLBACKFACES)) + { + glEnable(GL_CULL_FACE); + glFrontFace(GL_CW); + } + + gl_RenderState.mModelMatrix = objectToWorldMatrix; + gl_RenderState.EnableModelMatrix(true); +} + +void FGLModelRenderer::EndDrawModel(AActor *actor, FSpriteModelFrame *smf) +{ + gl_RenderState.EnableModelMatrix(false); + + glDepthFunc(GL_LESS); + if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal]) && !(smf->flags & MDL_DONTCULLBACKFACES)) + glDisable(GL_CULL_FACE); +} + +void FGLModelRenderer::BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix) +{ + glDepthFunc(GL_LEQUAL); + + // [BB] In case the model should be rendered translucent, do back face culling. + // This solves a few of the problems caused by the lack of depth sorting. + // TO-DO: Implement proper depth sorting. + if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal])) + { + glEnable(GL_CULL_FACE); + glFrontFace(GL_CCW); + } + + gl_RenderState.mModelMatrix = objectToWorldMatrix; + gl_RenderState.EnableModelMatrix(true); +} + +void FGLModelRenderer::EndDrawHUDModel(AActor *actor) +{ + gl_RenderState.EnableModelMatrix(false); + + glDepthFunc(GL_LESS); + if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal])) + glDisable(GL_CULL_FACE); +} + +IModelVertexBuffer *FGLModelRenderer::CreateVertexBuffer(bool needindex, bool singleframe) +{ + return new FModelVertexBuffer(needindex, singleframe); +} + +void FGLModelRenderer::SetVertexBuffer(IModelVertexBuffer *buffer) +{ + gl_RenderState.SetVertexBuffer((FModelVertexBuffer*)buffer); +} + +void FGLModelRenderer::ResetVertexBuffer() +{ + gl_RenderState.SetVertexBuffer(GLRenderer->mVBO); +} + +void FGLModelRenderer::SetInterpolation(double inter) +{ + gl_RenderState.SetInterpolationFactor((float)inter); +} + +void FGLModelRenderer::SetMaterial(FTexture *skin, int clampmode, int translation) +{ + FMaterial * tex = FMaterial::ValidateTexture(skin, false); + gl_RenderState.SetMaterial(tex, clampmode, translation, -1, false); + + gl_RenderState.Apply(); + if (modellightindex != -1) gl_RenderState.ApplyLightIndex(modellightindex); +} + +void FGLModelRenderer::DrawArrays(int primitiveType, int start, int count) +{ + glDrawArrays(primitiveType, start, count); +} + +void FGLModelRenderer::DrawElements(int primitiveType, int numIndices, int elementType, size_t offset) +{ + glDrawElements(primitiveType, numIndices, elementType, (void*)(intptr_t)offset); +} + +float FGLModelRenderer::GetTimeFloat() +{ + return (float)I_MSTime() * (float)TICRATE / 1000.0f; +} + +///////////////////////////////////////////////////////////////////////////// void gl_LoadModels() { @@ -238,7 +559,7 @@ void FModelVertexBuffer::UnlockIndexBuffer() //=========================================================================== static TArray iBuffer; -unsigned int FModelVertexBuffer::SetupFrame(unsigned int frame1, unsigned int frame2, unsigned int size) +void FModelVertexBuffer::SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size) { glBindBuffer(GL_ARRAY_BUFFER, vbo_id); if (vbo_id > 0) @@ -276,7 +597,6 @@ unsigned int FModelVertexBuffer::SetupFrame(unsigned int frame1, unsigned int fr iBuffer[i].z = vbo_ptr[frame1 + i].z * (1.f - frac) + vbo_ptr[frame2 + i].z * frac; } } - return frame1; } //=========================================================================== @@ -868,207 +1188,12 @@ FSpriteModelFrame * gl_FindModelFrame(const PClass * ti, int sprite, int frame, // //=========================================================================== -void gl_RenderFrameModels( const FSpriteModelFrame *smf, - const FState *curState, - const int curTics, - const PClass *ti, - Matrix3x4 *normaltransform, - int translation) -{ - // [BB] Frame interpolation: Find the FSpriteModelFrame smfNext which follows after smf in the animation - // and the scalar value inter ( element of [0,1) ), both necessary to determine the interpolated frame. - FSpriteModelFrame * smfNext = nullptr; - double inter = 0.; - if( gl_interpolate_model_frames && !(smf->flags & MDL_NOINTERPOLATION) ) - { - FState *nextState = curState->GetNextState( ); - if( curState != nextState && nextState ) - { - // [BB] To interpolate at more than 35 fps we take tic fractions into account. - float ticFraction = 0.; - // [BB] In case the tic counter is frozen we have to leave ticFraction at zero. - if ( ConsoleState == c_up && menuactive != MENU_On && !(level.flags2 & LEVEL2_FROZEN) ) - { - float time = GetTimeFloat(); - ticFraction = (time - static_cast(time)); - } - inter = static_cast(curState->Tics - curTics - ticFraction)/static_cast(curState->Tics); - - // [BB] For some actors (e.g. ZPoisonShroom) spr->actor->tics can be bigger than curState->Tics. - // In this case inter is negative and we need to set it to zero. - if ( inter < 0. ) - inter = 0.; - else - { - // [BB] Workaround for actors that use the same frame twice in a row. - // Most of the standard Doom monsters do this in their see state. - if ( (smf->flags & MDL_INTERPOLATEDOUBLEDFRAMES) ) - { - const FState *prevState = curState - 1; - if ( (curState->sprite == prevState->sprite) && ( curState->Frame == prevState->Frame) ) - { - inter /= 2.; - inter += 0.5; - } - if ( (curState->sprite == nextState->sprite) && ( curState->Frame == nextState->Frame) ) - { - inter /= 2.; - nextState = nextState->GetNextState( ); - } - } - if ( inter != 0.0 ) - smfNext = gl_FindModelFrame(ti, nextState->sprite, nextState->Frame, false); - } - } - } - - for(int i=0; imodelIDs[i] != -1) - { - FModel * mdl = Models[smf->modelIDs[i]]; - FTexture *tex = smf->skinIDs[i].isValid()? TexMan(smf->skinIDs[i]) : nullptr; - mdl->BuildVertexBuffer(); - gl_RenderState.SetVertexBuffer(mdl->mVBuf); - - mdl->PushSpriteMDLFrame(smf, i); - - if ( smfNext && smf->modelframes[i] != smfNext->modelframes[i] ) - mdl->RenderFrame(tex, smf->modelframes[i], smfNext->modelframes[i], inter, translation); - else - mdl->RenderFrame(tex, smf->modelframes[i], smf->modelframes[i], 0.f, translation); - - gl_RenderState.SetVertexBuffer(GLRenderer->mVBO); - } - } -} - void gl_RenderModel(GLSprite * spr) { - FSpriteModelFrame * smf = spr->modelframe; - - - // Setup transformation. - glDepthFunc(GL_LEQUAL); - gl_RenderState.EnableTexture(true); - // [BB] In case the model should be rendered translucent, do back face culling. - // This solves a few of the problems caused by the lack of depth sorting. - // [Nash] Don't do back face culling if explicitly specified in MODELDEF - // TO-DO: Implement proper depth sorting. - if (!(spr->actor->RenderStyle == LegacyRenderStyles[STYLE_Normal]) && !(smf->flags & MDL_DONTCULLBACKFACES)) - { - glEnable(GL_CULL_FACE); - glFrontFace(GL_CW); - } - - int translation = 0; - if ( !(smf->flags & MDL_IGNORETRANSLATION) ) - translation = spr->actor->Translation; - - - // y scale for a sprite means height, i.e. z in the world! - float scaleFactorX = spr->actor->Scale.X * smf->xscale; - float scaleFactorY = spr->actor->Scale.X * smf->yscale; - float scaleFactorZ = spr->actor->Scale.Y * smf->zscale; - float pitch = 0; - float roll = 0; - float rotateOffset = 0; - float angle = spr->actor->Angles.Yaw.Degrees; - - // [BB] Workaround for the missing pitch information. - if ( (smf->flags & MDL_PITCHFROMMOMENTUM) ) - { - const double x = spr->actor->Vel.X; - const double y = spr->actor->Vel.Y; - const double z = spr->actor->Vel.Z; - - if (spr->actor->Vel.LengthSquared() > EQUAL_EPSILON) - { - // [BB] Calculate the pitch using spherical coordinates. - if (z || x || y) pitch = float(atan(z / sqrt(x*x + y*y)) / M_PI * 180); - - // Correcting pitch if model is moving backwards - if (fabs(x) > EQUAL_EPSILON || fabs(y) > EQUAL_EPSILON) - { - if ((x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180)) / sqrt(x * x + y * y) < 0) pitch *= -1; - } - else pitch = fabs(pitch); - } - } - - if( smf->flags & MDL_ROTATING ) - { - const float time = smf->rotationSpeed*GetTimeFloat()/200.f; - rotateOffset = float((time - xs_FloorToInt(time)) *360.f ); - } - - // Added MDL_USEACTORPITCH and MDL_USEACTORROLL flags processing. - // If both flags MDL_USEACTORPITCH and MDL_PITCHFROMMOMENTUM are set, the pitch sums up the actor pitch and the velocity vector pitch. - if (smf->flags & MDL_USEACTORPITCH) - { - double d = spr->actor->Angles.Pitch.Degrees; - if (smf->flags & MDL_BADROTATION) pitch += d; - else pitch -= d; - } - if(smf->flags & MDL_USEACTORROLL) roll += spr->actor->Angles.Roll.Degrees; - - gl_RenderState.mModelMatrix.loadIdentity(); - - // Model space => World space - gl_RenderState.mModelMatrix.translate(spr->x, spr->z, spr->y ); - - // [Nash] take SpriteRotation into account - angle += spr->actor->SpriteRotation.Degrees; - - if (spr->actor->renderflags & RF_INTERPOLATEANGLES) - { - // [Nash] use interpolated angles - DRotator Angles = spr->actor->InterpolatedAngles(r_viewpoint.TicFrac); - angle = Angles.Yaw.Degrees; - } - - // Applying model transformations: - // 1) Applying actor angle, pitch and roll to the model - gl_RenderState.mModelMatrix.rotate(-angle, 0, 1, 0); - gl_RenderState.mModelMatrix.rotate(pitch, 0, 0, 1); - gl_RenderState.mModelMatrix.rotate(-roll, 1, 0, 0); - - // 2) Applying Doomsday like rotation of the weapon pickup models - // The rotation angle is based on the elapsed time. - - if( smf->flags & MDL_ROTATING ) - { - gl_RenderState.mModelMatrix.translate(smf->rotationCenterX, smf->rotationCenterY, smf->rotationCenterZ); - gl_RenderState.mModelMatrix.rotate(rotateOffset, smf->xrotate, smf->yrotate, smf->zrotate); - gl_RenderState.mModelMatrix.translate(-smf->rotationCenterX, -smf->rotationCenterY, -smf->rotationCenterZ); - } - - // 3) Scaling model. - gl_RenderState.mModelMatrix.scale(scaleFactorX, scaleFactorZ, scaleFactorY); - - // 4) Aplying model offsets (model offsets do not depend on model scalings). - gl_RenderState.mModelMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); - - // 5) Applying model rotations. - gl_RenderState.mModelMatrix.rotate(-smf->angleoffset, 0, 1, 0); - gl_RenderState.mModelMatrix.rotate(smf->pitchoffset, 0, 0, 1); - gl_RenderState.mModelMatrix.rotate(-smf->rolloffset, 1, 0, 0); - - // consider the pixel stretching. For non-voxels this must be factored out here - float stretch = (smf->modelIDs[0] != -1 ? Models[smf->modelIDs[0]]->getAspectFactor() : 1.f) / level.info->pixelstretch; - gl_RenderState.mModelMatrix.scale(1, stretch, 1); - - - gl_RenderState.EnableModelMatrix(true); - gl_RenderFrameModels( smf, spr->actor->state, spr->actor->tics, spr->actor->GetClass(), nullptr, translation ); - gl_RenderState.EnableModelMatrix(false); - - glDepthFunc(GL_LESS); - if (!( spr->actor->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) - glDisable(GL_CULL_FACE); + FGLModelRenderer renderer; + renderer.RenderModel(spr->x, spr->y, spr->z, spr->modelframe, spr->actor); } - //=========================================================================== // // gl_RenderHUDModel @@ -1077,55 +1202,8 @@ void gl_RenderModel(GLSprite * spr) void gl_RenderHUDModel(DPSprite *psp, float ofsX, float ofsY) { - AActor * playermo=players[consoleplayer].camera; - FSpriteModelFrame *smf = gl_FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->GetState()->sprite, psp->GetState()->GetFrame(), false); - - // [BB] No model found for this sprite, so we can't render anything. - if ( smf == nullptr ) - return; - - glDepthFunc(GL_LEQUAL); - - // [BB] In case the model should be rendered translucent, do back face culling. - // This solves a few of the problems caused by the lack of depth sorting. - // TO-DO: Implement proper depth sorting. - if (!( playermo->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) - { - glEnable(GL_CULL_FACE); - glFrontFace(GL_CCW); - } - - // The model position and orientation has to be drawn independently from the position of the player, - // but we need to position it correctly in the world for light to work properly. - VSMatrix objectToWorldMatrix; - gl_RenderState.mViewMatrix.inverseMatrix(objectToWorldMatrix); - - // Scaling model (y scale for a sprite means height, i.e. z in the world!). - objectToWorldMatrix.scale(smf->xscale, smf->zscale, smf->yscale); - - // Aplying model offsets (model offsets do not depend on model scalings). - objectToWorldMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); - - // [BB] Weapon bob, very similar to the normal Doom weapon bob. - objectToWorldMatrix.rotate(ofsX/4, 0, 1, 0); - objectToWorldMatrix.rotate((ofsY-WEAPONTOP)/-4., 1, 0, 0); - - // [BB] For some reason the jDoom models need to be rotated. - objectToWorldMatrix.rotate(90.f, 0, 1, 0); - - // Applying angleoffset, pitchoffset, rolloffset. - objectToWorldMatrix.rotate(-smf->angleoffset, 0, 1, 0); - objectToWorldMatrix.rotate(smf->pitchoffset, 0, 0, 1); - objectToWorldMatrix.rotate(-smf->rolloffset, 1, 0, 0); - - gl_RenderState.mModelMatrix = objectToWorldMatrix; - gl_RenderState.EnableModelMatrix(true); - gl_RenderFrameModels( smf, psp->GetState(), psp->GetTics(), playermo->player->ReadyWeapon->GetClass(), nullptr, 0 ); - gl_RenderState.EnableModelMatrix(false); - - glDepthFunc(GL_LESS); - if (!( playermo->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) - glDisable(GL_CULL_FACE); + FGLModelRenderer renderer; + renderer.RenderHUDModel(psp, ofsX, ofsY); } //=========================================================================== diff --git a/src/gl/models/gl_models.h b/src/gl/models/gl_models.h index a1ed4014d..84e331e31 100644 --- a/src/gl/models/gl_models.h +++ b/src/gl/models/gl_models.h @@ -45,6 +45,56 @@ FTextureID LoadSkin(const char * path, const char * fn); // [JM] Necessary forward declaration typedef struct FSpriteModelFrame FSpriteModelFrame; +class FModelRenderer +{ +public: + virtual ~FModelRenderer() { } + + void RenderModel(float x, float y, float z, FSpriteModelFrame *modelframe, AActor *actor); + void RenderHUDModel(DPSprite *psp, float ofsx, float ofsy); + + virtual void BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix) = 0; + virtual void EndDrawModel(AActor *actor, FSpriteModelFrame *smf) = 0; + + virtual IModelVertexBuffer *CreateVertexBuffer(bool needindex, bool singleframe) = 0; + + virtual void SetVertexBuffer(IModelVertexBuffer *buffer) = 0; + virtual void ResetVertexBuffer() = 0; + + virtual VSMatrix GetViewToWorldMatrix() = 0; + + virtual void BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix) = 0; + virtual void EndDrawHUDModel(AActor *actor) = 0; + + virtual void SetInterpolation(double interpolation) = 0; + virtual void SetMaterial(FTexture *skin, int clampmode, int translation) = 0; + virtual void DrawArrays(int primitiveType, int start, int count) = 0; + virtual void DrawElements(int primitiveType, int numIndices, int elementType, size_t offset) = 0; + + virtual float GetTimeFloat() = 0; + +private: + void RenderFrameModels(const FSpriteModelFrame *smf, const FState *curState, const int curTics, const PClass *ti, Matrix3x4 *normaltransform, int translation); +}; + +class FGLModelRenderer : public FModelRenderer +{ +public: + void BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix) override; + void EndDrawModel(AActor *actor, FSpriteModelFrame *smf) override; + IModelVertexBuffer *CreateVertexBuffer(bool needindex, bool singleframe) override; + void SetVertexBuffer(IModelVertexBuffer *buffer) override; + void ResetVertexBuffer() override; + VSMatrix GetViewToWorldMatrix() override; + void BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix) override; + void EndDrawHUDModel(AActor *actor) override; + void SetInterpolation(double interpolation) override; + void SetMaterial(FTexture *skin, int clampmode, int translation) override; + void DrawArrays(int primitiveType, int start, int count) override; + void DrawElements(int primitiveType, int numIndices, int elementType, size_t offset) override; + float GetTimeFloat() override; +}; + class FModel { public: @@ -57,8 +107,8 @@ public: virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) = 0; virtual int FindFrame(const char * name) = 0; - virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0) = 0; - virtual void BuildVertexBuffer() = 0; + virtual void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0) = 0; + virtual void BuildVertexBuffer(FModelRenderer *renderer) = 0; virtual void AddSkins(uint8_t *hitlist) = 0; void DestroyVertexBuffer() { @@ -71,7 +121,7 @@ public: int curMDLIndex; void PushSpriteMDLFrame(const FSpriteModelFrame *smf, int index) { curSpriteMDLFrame = smf; curMDLIndex = index; }; - FModelVertexBuffer *mVBuf; + IModelVertexBuffer *mVBuf; FString mFileName; }; @@ -183,12 +233,12 @@ public: virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length); virtual int FindFrame(const char * name); - virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0); + virtual void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0); virtual void LoadGeometry(); virtual void AddSkins(uint8_t *hitlist); void UnloadGeometry(); - void BuildVertexBuffer(); + void BuildVertexBuffer(FModelRenderer *renderer); }; @@ -289,9 +339,9 @@ public: virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length); virtual int FindFrame(const char * name); - virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0); + virtual void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0); void LoadGeometry(); - void BuildVertexBuffer(); + void BuildVertexBuffer(FModelRenderer *renderer); virtual void AddSkins(uint8_t *hitlist); }; @@ -344,10 +394,10 @@ public: bool Load(const char * fn, int lumpnum, const char * buffer, int length); void Initialize(); virtual int FindFrame(const char * name); - virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0); + virtual void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0); virtual void AddSkins(uint8_t *hitlist); FTextureID GetPaletteTexture() const { return mPalette; } - void BuildVertexBuffer(); + void BuildVertexBuffer(FModelRenderer *renderer); float getAspectFactor(); }; diff --git a/src/gl/models/gl_models_md2.cpp b/src/gl/models/gl_models_md2.cpp index 7f7930ac9..b666c2e1c 100644 --- a/src/gl/models/gl_models_md2.cpp +++ b/src/gl/models/gl_models_md2.cpp @@ -40,8 +40,6 @@ #include "gl/shaders/gl_shader.h" #include "gl/data/gl_vertexbuffer.h" -extern int modellightindex; - static float avertexnormals[NUMVERTEXNORMALS][3] = { #include "tab_anorms.h" }; @@ -286,7 +284,7 @@ FDMDModel::~FDMDModel() // //=========================================================================== -void FDMDModel::BuildVertexBuffer() +void FDMDModel::BuildVertexBuffer(FModelRenderer *renderer) { if (mVBuf == NULL) { @@ -295,7 +293,7 @@ void FDMDModel::BuildVertexBuffer() int VertexBufferSize = info.numFrames * lodInfo[0].numTriangles * 3; unsigned int vindex = 0; - mVBuf = new FModelVertexBuffer(false, info.numFrames == 1); + mVBuf = renderer->CreateVertexBuffer(false, info.numFrames == 1); FModelVertex *vertptr = mVBuf->LockVertexBuffer(VertexBufferSize); for (int i = 0; i < info.numFrames; i++) @@ -364,7 +362,7 @@ int FDMDModel::FindFrame(const char * name) // //=========================================================================== -void FDMDModel::RenderFrame(FTexture * skin, int frameno, int frameno2, double inter, int translation) +void FDMDModel::RenderFrame(FModelRenderer *renderer, FTexture * skin, int frameno, int frameno2, double inter, int translation) { if (frameno >= info.numFrames || frameno2 >= info.numFrames) return; @@ -375,16 +373,11 @@ void FDMDModel::RenderFrame(FTexture * skin, int frameno, int frameno2, double i if (!skin) return; } - FMaterial * tex = FMaterial::ValidateTexture(skin, false); - - gl_RenderState.SetMaterial(tex, CLAMP_NONE, translation, -1, false); - gl_RenderState.SetInterpolationFactor((float)inter); - - gl_RenderState.Apply(); - if (modellightindex != -1) gl_RenderState.ApplyLightIndex(modellightindex); - mVBuf->SetupFrame(frames[frameno].vindex, frames[frameno2].vindex, lodInfo[0].numTriangles * 3); - glDrawArrays(GL_TRIANGLES, 0, lodInfo[0].numTriangles * 3); - gl_RenderState.SetInterpolationFactor(0.f); + renderer->SetInterpolation(inter); + renderer->SetMaterial(skin, CLAMP_NONE, translation); + mVBuf->SetupFrame(renderer, frames[frameno].vindex, frames[frameno2].vindex, lodInfo[0].numTriangles * 3); + renderer->DrawArrays(GL_TRIANGLES, 0, lodInfo[0].numTriangles * 3); + renderer->SetInterpolation(0.f); } diff --git a/src/gl/models/gl_models_md3.cpp b/src/gl/models/gl_models_md3.cpp index 9573b83c1..a1954ce5c 100644 --- a/src/gl/models/gl_models_md3.cpp +++ b/src/gl/models/gl_models_md3.cpp @@ -35,8 +35,6 @@ #define MAX_QPATH 64 -extern int modellightindex; - //=========================================================================== // // decode the lat/lng normal to a 3 float normal @@ -240,7 +238,7 @@ void FMD3Model::LoadGeometry() // //=========================================================================== -void FMD3Model::BuildVertexBuffer() +void FMD3Model::BuildVertexBuffer(FModelRenderer *renderer) { if (mVBuf == nullptr) { @@ -256,7 +254,7 @@ void FMD3Model::BuildVertexBuffer() ibufsize += 3 * surf->numTriangles; } - mVBuf = new FModelVertexBuffer(true, numFrames == 1); + mVBuf = renderer->CreateVertexBuffer(true, numFrames == 1); FModelVertex *vertptr = mVBuf->LockVertexBuffer(vbufsize); unsigned int *indxptr = mVBuf->LockIndexBuffer(ibufsize); @@ -343,11 +341,11 @@ int FMD3Model::FindFrame(const char * name) // //=========================================================================== -void FMD3Model::RenderFrame(FTexture * skin, int frameno, int frameno2, double inter, int translation) +void FMD3Model::RenderFrame(FModelRenderer *renderer, FTexture * skin, int frameno, int frameno2, double inter, int translation) { if (frameno>=numFrames || frameno2>=numFrames) return; - gl_RenderState.SetInterpolationFactor((float)inter); + renderer->SetInterpolation(inter); for(int i=0;iSetupFrame(surf->vindex + frameno * surf->numVertices, surf->vindex + frameno2 * surf->numVertices, surf->numVertices); - glDrawElements(GL_TRIANGLES, surf->numTriangles * 3, GL_UNSIGNED_INT, (void*)(intptr_t)(surf->iindex * sizeof(unsigned int))); + renderer->SetMaterial(surfaceSkin, CLAMP_NONE, translation); + mVBuf->SetupFrame(renderer, surf->vindex + frameno * surf->numVertices, surf->vindex + frameno2 * surf->numVertices, surf->numVertices); + renderer->DrawElements(GL_TRIANGLES, surf->numTriangles * 3, GL_UNSIGNED_INT, surf->iindex * sizeof(unsigned int)); } - gl_RenderState.SetInterpolationFactor(0.f); + renderer->SetInterpolation(0.f); } //=========================================================================== diff --git a/src/gl/models/gl_voxels.cpp b/src/gl/models/gl_voxels.cpp index 0b97e87f8..839218f2e 100644 --- a/src/gl/models/gl_voxels.cpp +++ b/src/gl/models/gl_voxels.cpp @@ -50,8 +50,6 @@ #include "gl/utility/gl_convert.h" #include "gl/renderer/gl_renderstate.h" -extern int modellightindex; - //=========================================================================== // // Creates a 16x16 texture from the palette so that we can @@ -364,13 +362,13 @@ void FVoxelModel::Initialize() // //=========================================================================== -void FVoxelModel::BuildVertexBuffer() +void FVoxelModel::BuildVertexBuffer(FModelRenderer *renderer) { if (mVBuf == NULL) { Initialize(); - mVBuf = new FModelVertexBuffer(true, true); + mVBuf = renderer->CreateVertexBuffer(true, true); FModelVertex *vertptr = mVBuf->LockVertexBuffer(mVertices.Size()); unsigned int *indxptr = mVBuf->LockIndexBuffer(mIndices.Size()); @@ -440,14 +438,10 @@ float FVoxelModel::getAspectFactor() // //=========================================================================== -void FVoxelModel::RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation) +void FVoxelModel::RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation) { - FMaterial * tex = FMaterial::ValidateTexture(skin, false); - gl_RenderState.SetMaterial(tex, CLAMP_NOFILTER, translation, -1, false); - - gl_RenderState.Apply(); - if (modellightindex != -1) gl_RenderState.ApplyLightIndex(modellightindex); - mVBuf->SetupFrame(0, 0, 0); - glDrawElements(GL_TRIANGLES, mNumIndices, GL_UNSIGNED_INT, (void*)(intptr_t)0); + renderer->SetMaterial(skin, CLAMP_NOFILTER, translation); + mVBuf->SetupFrame(renderer, 0, 0, 0); + renderer->DrawElements(GL_TRIANGLES, mNumIndices, GL_UNSIGNED_INT, 0); } diff --git a/src/gl/textures/gl_texture.cpp b/src/gl/textures/gl_texture.cpp index 51425d40d..ddfd7ecc9 100644 --- a/src/gl/textures/gl_texture.cpp +++ b/src/gl/textures/gl_texture.cpp @@ -890,10 +890,11 @@ void gl_PrecacheTexture(uint8_t *texhitlist, TMap &actorhitl } // cache all used models + FGLModelRenderer renderer; for (unsigned i = 0; i < Models.Size(); i++) { if (modellist[i]) - Models[i]->BuildVertexBuffer(); + Models[i]->BuildVertexBuffer(&renderer); } } From 00d7dd0c64c340e7b03c1908b8ba35bfa80c2e24 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Fri, 24 Nov 2017 00:39:10 +0100 Subject: [PATCH 10/10] - Implement model rendering in softpoly --- src/CMakeLists.txt | 1 + src/polyrenderer/drawers/poly_draw_args.cpp | 10 + src/polyrenderer/drawers/poly_draw_args.h | 3 + src/polyrenderer/drawers/poly_triangle.cpp | 63 ++++- src/polyrenderer/drawers/poly_triangle.h | 1 + src/polyrenderer/poly_all.cpp | 1 + src/polyrenderer/poly_renderer.cpp | 4 +- src/polyrenderer/poly_renderer.h | 4 +- src/polyrenderer/scene/poly_model.cpp | 255 +++++++++++++++++++ src/polyrenderer/scene/poly_model.h | 82 ++++++ src/polyrenderer/scene/poly_playersprite.cpp | 11 + src/polyrenderer/scene/poly_playersprite.h | 1 + src/polyrenderer/scene/poly_sprite.cpp | 12 + 13 files changed, 444 insertions(+), 4 deletions(-) create mode 100644 src/polyrenderer/scene/poly_model.cpp create mode 100644 src/polyrenderer/scene/poly_model.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 30981fbf0..6c266eb86 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -756,6 +756,7 @@ set( POLYRENDER_SOURCES polyrenderer/scene/poly_wall.cpp polyrenderer/scene/poly_wallsprite.cpp polyrenderer/scene/poly_sprite.cpp + polyrenderer/scene/poly_model.cpp polyrenderer/scene/poly_sky.cpp polyrenderer/scene/poly_light.cpp polyrenderer/drawers/poly_buffer.cpp diff --git a/src/polyrenderer/drawers/poly_draw_args.cpp b/src/polyrenderer/drawers/poly_draw_args.cpp index 0df28f9aa..5fb7f0288 100644 --- a/src/polyrenderer/drawers/poly_draw_args.cpp +++ b/src/polyrenderer/drawers/poly_draw_args.cpp @@ -130,6 +130,16 @@ void PolyDrawArgs::DrawArray(PolyRenderThread *thread, const TriVertex *vertices { mVertices = vertices; mVertexCount = vcount; + mElements = nullptr; + mDrawMode = mode; + thread->DrawQueue->Push(*this, PolyTriangleDrawer::is_mirror()); +} + +void PolyDrawArgs::DrawElements(PolyRenderThread *thread, const TriVertex *vertices, const unsigned int *elements, int count, PolyDrawMode mode) +{ + mVertices = vertices; + mElements = elements; + mVertexCount = count; mDrawMode = mode; thread->DrawQueue->Push(*this, PolyTriangleDrawer::is_mirror()); } diff --git a/src/polyrenderer/drawers/poly_draw_args.h b/src/polyrenderer/drawers/poly_draw_args.h index 34ef56eb2..31780d77a 100644 --- a/src/polyrenderer/drawers/poly_draw_args.h +++ b/src/polyrenderer/drawers/poly_draw_args.h @@ -83,12 +83,14 @@ public: void SetLights(PolyLight *lights, int numLights) { mLights = lights; mNumLights = numLights; } void SetDynLightColor(uint32_t color) { mDynLightColor = color; } void DrawArray(PolyRenderThread *thread, const TriVertex *vertices, int vcount, PolyDrawMode mode = PolyDrawMode::Triangles); + void DrawElements(PolyRenderThread *thread, const TriVertex *vertices, const unsigned int *elements, int count, PolyDrawMode mode = PolyDrawMode::Triangles); const TriMatrix *ObjectToClip() const { return mObjectToClip; } const PolyClipPlane &ClipPlane(int index) const { return mClipPlane[index]; } const TriVertex *Vertices() const { return mVertices; } int VertexCount() const { return mVertexCount; } + const unsigned int *Elements() const { return mElements; } PolyDrawMode DrawMode() const { return mDrawMode; } bool FaceCullCCW() const { return mFaceCullCCW; } @@ -139,6 +141,7 @@ private: const TriMatrix *mObjectToClip = nullptr; const TriVertex *mVertices = nullptr; int mVertexCount = 0; + const unsigned int *mElements = nullptr; PolyDrawMode mDrawMode = PolyDrawMode::Triangles; bool mFaceCullCCW = false; bool mDepthTest = false; diff --git a/src/polyrenderer/drawers/poly_triangle.cpp b/src/polyrenderer/drawers/poly_triangle.cpp index beb283498..386c4c630 100644 --- a/src/polyrenderer/drawers/poly_triangle.cpp +++ b/src/polyrenderer/drawers/poly_triangle.cpp @@ -84,6 +84,64 @@ bool PolyTriangleDrawer::is_mirror() return mirror; } +void PolyTriangleDrawer::draw_elements(const PolyDrawArgs &drawargs, WorkerThreadData *thread) +{ + if (drawargs.VertexCount() < 3) + return; + + TriDrawTriangleArgs args; + args.dest = dest; + args.pitch = dest_pitch; + args.clipright = dest_width; + args.clipbottom = dest_height; + args.uniforms = &drawargs; + args.destBgra = dest_bgra; + args.stencilPitch = PolyStencilBuffer::Instance()->BlockWidth(); + args.stencilValues = PolyStencilBuffer::Instance()->Values(); + args.stencilMasks = PolyStencilBuffer::Instance()->Masks(); + args.zbuffer = PolyZBuffer::Instance()->Values(); + + bool ccw = drawargs.FaceCullCCW(); + const TriVertex *vinput = drawargs.Vertices(); + const unsigned int *elements = drawargs.Elements(); + int vcount = drawargs.VertexCount(); + + ShadedTriVertex vert[3]; + if (drawargs.DrawMode() == PolyDrawMode::Triangles) + { + for (int i = 0; i < vcount / 3; i++) + { + for (int j = 0; j < 3; j++) + vert[j] = shade_vertex(drawargs, vinput[*(elements++)]); + draw_shaded_triangle(vert, ccw, &args, thread); + } + } + else if (drawargs.DrawMode() == PolyDrawMode::TriangleFan) + { + vert[0] = shade_vertex(drawargs, vinput[*(elements++)]); + vert[1] = shade_vertex(drawargs, vinput[*(elements++)]); + for (int i = 2; i < vcount; i++) + { + vert[2] = shade_vertex(drawargs, vinput[*(elements++)]); + draw_shaded_triangle(vert, ccw, &args, thread); + vert[1] = vert[2]; + } + } + else // TriangleDrawMode::TriangleStrip + { + vert[0] = shade_vertex(drawargs, vinput[*(elements++)]); + vert[1] = shade_vertex(drawargs, vinput[*(elements++)]); + for (int i = 2; i < vcount; i++) + { + vert[2] = shade_vertex(drawargs, vinput[*(elements++)]); + draw_shaded_triangle(vert, ccw, &args, thread); + vert[0] = vert[1]; + vert[1] = vert[2]; + ccw = !ccw; + } + } +} + void PolyTriangleDrawer::draw_arrays(const PolyDrawArgs &drawargs, WorkerThreadData *thread) { if (drawargs.VertexCount() < 3) @@ -472,7 +530,10 @@ void DrawPolyTrianglesCommand::Execute(DrawerThread *thread) thread_data.core = thread->core; thread_data.num_cores = thread->num_cores; - PolyTriangleDrawer::draw_arrays(args, &thread_data); + if (!args.Elements()) + PolyTriangleDrawer::draw_arrays(args, &thread_data); + else + PolyTriangleDrawer::draw_elements(args, &thread_data); } ///////////////////////////////////////////////////////////////////////////// diff --git a/src/polyrenderer/drawers/poly_triangle.h b/src/polyrenderer/drawers/poly_triangle.h index 4992f6972..eea3b8531 100644 --- a/src/polyrenderer/drawers/poly_triangle.h +++ b/src/polyrenderer/drawers/poly_triangle.h @@ -40,6 +40,7 @@ public: private: static ShadedTriVertex shade_vertex(const PolyDrawArgs &drawargs, const TriVertex &v); + static void draw_elements(const PolyDrawArgs &args, WorkerThreadData *thread); static void draw_arrays(const PolyDrawArgs &args, WorkerThreadData *thread); static void draw_shaded_triangle(const ShadedTriVertex *vertices, bool ccw, TriDrawTriangleArgs *args, WorkerThreadData *thread); static bool is_degenerate(const ShadedTriVertex *vertices); diff --git a/src/polyrenderer/poly_all.cpp b/src/polyrenderer/poly_all.cpp index 62d63c2d3..216dd88e1 100644 --- a/src/polyrenderer/poly_all.cpp +++ b/src/polyrenderer/poly_all.cpp @@ -14,6 +14,7 @@ #include "scene/poly_scene.cpp" #include "scene/poly_sky.cpp" #include "scene/poly_sprite.cpp" +#include "scene/poly_model.cpp" #include "scene/poly_wall.cpp" #include "scene/poly_wallsprite.cpp" #include "scene/poly_light.cpp" diff --git a/src/polyrenderer/poly_renderer.cpp b/src/polyrenderer/poly_renderer.cpp index a0aa12c32..1b00412d1 100644 --- a/src/polyrenderer/poly_renderer.cpp +++ b/src/polyrenderer/poly_renderer.cpp @@ -208,12 +208,12 @@ void PolyRenderer::SetupPerspectiveMatrix() float fovratio = (Viewwindow.WidescreenRatio >= 1.3f) ? 1.333333f : ratio; float fovy = (float)(2 * DAngle::ToDegrees(atan(tan(Viewpoint.FieldOfView.Radians() / 2) / fovratio)).Degrees); - TriMatrix worldToView = + WorldToView = TriMatrix::rotate(adjustedPitch, 1.0f, 0.0f, 0.0f) * TriMatrix::rotate(adjustedViewAngle, 0.0f, -1.0f, 0.0f) * TriMatrix::scale(1.0f, level.info->pixelstretch, 1.0f) * TriMatrix::swapYZ() * TriMatrix::translate((float)-Viewpoint.Pos.X, (float)-Viewpoint.Pos.Y, (float)-Viewpoint.Pos.Z); - WorldToClip = TriMatrix::perspective(fovy, ratio, 5.0f, 65535.0f) * worldToView; + WorldToClip = TriMatrix::perspective(fovy, ratio, 5.0f, 65535.0f) * WorldToView; } diff --git a/src/polyrenderer/poly_renderer.h b/src/polyrenderer/poly_renderer.h index 8e5011782..2ef60a0ea 100644 --- a/src/polyrenderer/poly_renderer.h +++ b/src/polyrenderer/poly_renderer.h @@ -61,13 +61,15 @@ public: FRenderViewpoint Viewpoint; PolyLightVisibility Light; + TriMatrix WorldToView; + TriMatrix WorldToClip; + private: void RenderActorView(AActor *actor, bool dontmaplines); void ClearBuffers(); void SetSceneViewport(); void SetupPerspectiveMatrix(); - TriMatrix WorldToClip; RenderPolyScene MainPortal; PolySkyDome Skydome; RenderPolyPlayerSprites PlayerSprites; diff --git a/src/polyrenderer/scene/poly_model.cpp b/src/polyrenderer/scene/poly_model.cpp new file mode 100644 index 000000000..88064ce97 --- /dev/null +++ b/src/polyrenderer/scene/poly_model.cpp @@ -0,0 +1,255 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_model.h" +#include "polyrenderer/poly_renderer.h" +#include "polyrenderer/scene/poly_light.h" +#include "polyrenderer/poly_renderthread.h" +#include "r_data/r_vanillatrans.h" +#include "actorinlines.h" + +void PolyRenderModel(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, uint32_t stencilValue, float x, float y, float z, FSpriteModelFrame *smf, AActor *actor) +{ + PolyModelRenderer renderer(thread, worldToClip, clipPlane, stencilValue); + renderer.RenderModel(x, y, z, smf, actor); +} + +void PolyRenderHUDModel(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, uint32_t stencilValue, DPSprite *psp, float ofsx, float ofsy) +{ + PolyModelRenderer renderer(thread, worldToClip, clipPlane, stencilValue); + renderer.RenderHUDModel(psp, ofsx, ofsy); +} + +///////////////////////////////////////////////////////////////////////////// + +void PolyModelRenderer::BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix) +{ + ModelActor = actor; + const_cast(objectToWorldMatrix).copy(ObjectToWorld.matrix); +} + +void PolyModelRenderer::EndDrawModel(AActor *actor, FSpriteModelFrame *smf) +{ + ModelActor = nullptr; +} + +IModelVertexBuffer *PolyModelRenderer::CreateVertexBuffer(bool needindex, bool singleframe) +{ + return new PolyModelVertexBuffer(needindex, singleframe); +} + +void PolyModelRenderer::SetVertexBuffer(IModelVertexBuffer *buffer) +{ +} + +void PolyModelRenderer::ResetVertexBuffer() +{ +} + +VSMatrix PolyModelRenderer::GetViewToWorldMatrix() +{ + TriMatrix swapYZ = TriMatrix::null(); + swapYZ.matrix[0 + 0 * 4] = 1.0f; + swapYZ.matrix[1 + 2 * 4] = 1.0f; + swapYZ.matrix[2 + 1 * 4] = 1.0f; + swapYZ.matrix[3 + 3 * 4] = 1.0f; + + VSMatrix worldToView; + worldToView.loadMatrix((PolyRenderer::Instance()->WorldToView * swapYZ).matrix); + + VSMatrix objectToWorld; + worldToView.inverseMatrix(objectToWorld); + return objectToWorld; +} + +void PolyModelRenderer::BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix) +{ + ModelActor = actor; + const_cast(objectToWorldMatrix).copy(ObjectToWorld.matrix); +} + +void PolyModelRenderer::EndDrawHUDModel(AActor *actor) +{ + ModelActor = nullptr; +} + +void PolyModelRenderer::SetInterpolation(double interpolation) +{ + InterpolationFactor = (float)interpolation; +} + +void PolyModelRenderer::SetMaterial(FTexture *skin, int clampmode, int translation) +{ + SkinTexture = skin; +} + +void PolyModelRenderer::DrawArrays(int primitiveType, int start, int count) +{ + const auto &viewpoint = PolyRenderer::Instance()->Viewpoint; + + bool foggy = false; + int actualextralight = foggy ? 0 : viewpoint.extralight << 4; + sector_t *sector = ModelActor->Sector; + + bool fullbrightSprite = ((ModelActor->renderflags & RF_FULLBRIGHT) || (ModelActor->flags5 & MF5_BRIGHT)); + int lightlevel = fullbrightSprite ? 255 : ModelActor->Sector->lightlevel + actualextralight; + + TriMatrix swapYZ = TriMatrix::null(); + swapYZ.matrix[0 + 0 * 4] = 1.0f; + swapYZ.matrix[1 + 2 * 4] = 1.0f; + swapYZ.matrix[2 + 1 * 4] = 1.0f; + swapYZ.matrix[3 + 3 * 4] = 1.0f; + + TriMatrix *transform = Thread->FrameMemory->NewObject(); + *transform = WorldToClip * swapYZ * ObjectToWorld; + + PolyDrawArgs args; + args.SetLight(GetColorTable(sector->Colormap, sector->SpecialColors[sector_t::sprites], true), lightlevel, PolyRenderer::Instance()->Light.SpriteGlobVis(foggy), fullbrightSprite); + args.SetTransform(transform); + args.SetFaceCullCCW(true); + args.SetStencilTestValue(StencilValue); + args.SetClipPlane(0, PolyClipPlane()); + args.SetStyle(TriBlendMode::TextureOpaque); + args.SetTexture(SkinTexture); + args.SetDepthTest(true); + args.SetWriteDepth(true); + args.SetWriteStencil(false); + args.DrawArray(Thread, VertexBuffer + start, count); +} + +void PolyModelRenderer::DrawElements(int primitiveType, int numIndices, int elementType, size_t offset) +{ + const auto &viewpoint = PolyRenderer::Instance()->Viewpoint; + + bool foggy = false; + int actualextralight = foggy ? 0 : viewpoint.extralight << 4; + sector_t *sector = ModelActor->Sector; + + bool fullbrightSprite = ((ModelActor->renderflags & RF_FULLBRIGHT) || (ModelActor->flags5 & MF5_BRIGHT)); + int lightlevel = fullbrightSprite ? 255 : ModelActor->Sector->lightlevel + actualextralight; + + TriMatrix swapYZ = TriMatrix::null(); + swapYZ.matrix[0 + 0 * 4] = 1.0f; + swapYZ.matrix[1 + 2 * 4] = 1.0f; + swapYZ.matrix[2 + 1 * 4] = 1.0f; + swapYZ.matrix[3 + 3 * 4] = 1.0f; + + TriMatrix *transform = Thread->FrameMemory->NewObject(); + *transform = WorldToClip * swapYZ * ObjectToWorld; + + PolyDrawArgs args; + args.SetLight(GetColorTable(sector->Colormap, sector->SpecialColors[sector_t::sprites], true), lightlevel, PolyRenderer::Instance()->Light.SpriteGlobVis(foggy), fullbrightSprite); + args.SetTransform(transform); + args.SetFaceCullCCW(true); + args.SetStencilTestValue(StencilValue); + args.SetClipPlane(0, PolyClipPlane()); + args.SetStyle(TriBlendMode::TextureOpaque); + args.SetTexture(SkinTexture); + args.SetDepthTest(true); + args.SetWriteDepth(true); + args.SetWriteStencil(false); + args.DrawElements(Thread, VertexBuffer, IndexBuffer + offset / sizeof(unsigned int), numIndices); +} + +float PolyModelRenderer::GetTimeFloat() +{ + return 0.0f; // (float)gl_frameMS * (float)TICRATE / 1000.0f; +} + +///////////////////////////////////////////////////////////////////////////// + +PolyModelVertexBuffer::PolyModelVertexBuffer(bool needindex, bool singleframe) +{ +} + +PolyModelVertexBuffer::~PolyModelVertexBuffer() +{ +} + +FModelVertex *PolyModelVertexBuffer::LockVertexBuffer(unsigned int size) +{ + mVertexBuffer.Resize(size); + return &mVertexBuffer[0]; +} + +void PolyModelVertexBuffer::UnlockVertexBuffer() +{ +} + +unsigned int *PolyModelVertexBuffer::LockIndexBuffer(unsigned int size) +{ + mIndexBuffer.Resize(size); + return &mIndexBuffer[0]; +} + +void PolyModelVertexBuffer::UnlockIndexBuffer() +{ +} + +void PolyModelVertexBuffer::SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size) +{ + PolyModelRenderer *polyrenderer = (PolyModelRenderer *)renderer; + + if (true)//if (frame1 == frame2 || size == 0 || polyrenderer->InterpolationFactor == 0.f) + { + TriVertex *vertices = polyrenderer->Thread->FrameMemory->AllocMemory(size); + + for (unsigned int i = 0; i < size; i++) + { + vertices[i] = + { + mVertexBuffer[frame1 + i].x, + mVertexBuffer[frame1 + i].y, + mVertexBuffer[frame1 + i].z, + 1.0f, + mVertexBuffer[frame1 + i].u, + mVertexBuffer[frame1 + i].v + }; + } + + polyrenderer->VertexBuffer = vertices; + polyrenderer->IndexBuffer = &mIndexBuffer[0]; + } + else + { + TriVertex *vertices = polyrenderer->Thread->FrameMemory->AllocMemory(size); + + float frac = polyrenderer->InterpolationFactor; + for (unsigned int i = 0; i < size; i++) + { + vertices[i].x = mVertexBuffer[frame1 + i].x * (1.0f - frac) + mVertexBuffer[frame2 + i].x * frac; + vertices[i].y = mVertexBuffer[frame1 + i].y * (1.0f - frac) + mVertexBuffer[frame2 + i].y * frac; + vertices[i].z = mVertexBuffer[frame1 + i].z * (1.0f - frac) + mVertexBuffer[frame2 + i].z * frac; + vertices[i].w = 1.0f; + vertices[i].u = mVertexBuffer[frame1 + i].u; + vertices[i].v = mVertexBuffer[frame1 + i].v; + } + + polyrenderer->VertexBuffer = vertices; + polyrenderer->IndexBuffer = &mIndexBuffer[0]; + } +} diff --git a/src/polyrenderer/scene/poly_model.h b/src/polyrenderer/scene/poly_model.h new file mode 100644 index 000000000..8585be44c --- /dev/null +++ b/src/polyrenderer/scene/poly_model.h @@ -0,0 +1,82 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" +#include "gl/data/gl_matrix.h" +#include "gl/models/gl_models.h" + +void PolyRenderModel(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, uint32_t stencilValue, float x, float y, float z, FSpriteModelFrame *smf, AActor *actor); +void PolyRenderHUDModel(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, uint32_t stencilValue, DPSprite *psp, float ofsx, float ofsy); + +class PolyModelRenderer : public FModelRenderer +{ +public: + PolyModelRenderer(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, uint32_t stencilValue) : Thread(thread), WorldToClip(worldToClip), ClipPlane(clipPlane), StencilValue(stencilValue) { } + + void BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix) override; + void EndDrawModel(AActor *actor, FSpriteModelFrame *smf) override; + IModelVertexBuffer *CreateVertexBuffer(bool needindex, bool singleframe) override; + void SetVertexBuffer(IModelVertexBuffer *buffer) override; + void ResetVertexBuffer() override; + VSMatrix GetViewToWorldMatrix() override; + void BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix) override; + void EndDrawHUDModel(AActor *actor) override; + void SetInterpolation(double interpolation) override; + void SetMaterial(FTexture *skin, int clampmode, int translation) override; + void DrawArrays(int primitiveType, int start, int count) override; + void DrawElements(int primitiveType, int numIndices, int elementType, size_t offset) override; + float GetTimeFloat() override; + + PolyRenderThread *Thread = nullptr; + const TriMatrix &WorldToClip; + const PolyClipPlane &ClipPlane; + uint32_t StencilValue = 0; + + AActor *ModelActor = nullptr; + TriMatrix ObjectToWorld; + FTexture *SkinTexture = nullptr; + unsigned int *IndexBuffer = nullptr; + TriVertex *VertexBuffer = nullptr; + float InterpolationFactor = 0.0; +}; + +class PolyModelVertexBuffer : public IModelVertexBuffer +{ +public: + PolyModelVertexBuffer(bool needindex, bool singleframe); + ~PolyModelVertexBuffer(); + + FModelVertex *LockVertexBuffer(unsigned int size) override; + void UnlockVertexBuffer() override; + + unsigned int *LockIndexBuffer(unsigned int size) override; + void UnlockIndexBuffer() override; + + void SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size) override; + +private: + int mIndexFrame[2]; + TArray mVertexBuffer; + TArray mIndexBuffer; +}; diff --git a/src/polyrenderer/scene/poly_playersprite.cpp b/src/polyrenderer/scene/poly_playersprite.cpp index 8d467ef0a..017396ffb 100644 --- a/src/polyrenderer/scene/poly_playersprite.cpp +++ b/src/polyrenderer/scene/poly_playersprite.cpp @@ -29,6 +29,7 @@ #include "polyrenderer/poly_renderer.h" #include "d_player.h" #include "polyrenderer/scene/poly_light.h" +#include "polyrenderer/scene/poly_model.h" EXTERN_CVAR(Bool, r_drawplayersprites) EXTERN_CVAR(Bool, r_deathcamera) @@ -39,6 +40,10 @@ void RenderPolyPlayerSprites::Render(PolyRenderThread *thread) { // This code cannot be moved directly to RenderRemainingSprites because the engine // draws the canvas textures between this call and the final call to RenderRemainingSprites.. + // + // We also can't move it because the model render code relies on it + + renderHUDModel = gl_IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player); const auto &viewpoint = PolyRenderer::Instance()->Viewpoint; @@ -244,6 +249,12 @@ void RenderPolyPlayerSprites::RenderSprite(PolyRenderThread *thread, DPSprite *p sy += wy; } + if (renderHUDModel) + { + PolyRenderHUDModel(thread, PolyRenderer::Instance()->WorldToClip, PolyClipPlane(), 1, pspr, (float)sx, (float)sy); + return; + } + double yaspectMul = 1.2 * ((double)SCREENHEIGHT / SCREENWIDTH) * r_viewwindow.WidescreenRatio; double pspritexscale = viewwindow.centerxwide / 160.0; diff --git a/src/polyrenderer/scene/poly_playersprite.h b/src/polyrenderer/scene/poly_playersprite.h index b07a86715..8e3e9aa7a 100644 --- a/src/polyrenderer/scene/poly_playersprite.h +++ b/src/polyrenderer/scene/poly_playersprite.h @@ -102,4 +102,5 @@ private: TArray AcceleratedSprites; sector_t tempsec; + bool renderHUDModel = false; }; diff --git a/src/polyrenderer/scene/poly_sprite.cpp b/src/polyrenderer/scene/poly_sprite.cpp index 8bc1c5054..9ea184c35 100644 --- a/src/polyrenderer/scene/poly_sprite.cpp +++ b/src/polyrenderer/scene/poly_sprite.cpp @@ -29,6 +29,7 @@ #include "polyrenderer/poly_renderer.h" #include "polyrenderer/scene/poly_light.h" #include "polyrenderer/poly_renderthread.h" +#include "polyrenderer/scene/poly_model.h" #include "r_data/r_vanillatrans.h" #include "actorinlines.h" @@ -73,6 +74,17 @@ bool RenderPolySprite::GetLine(AActor *thing, DVector2 &left, DVector2 &right) void RenderPolySprite::Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, AActor *thing, subsector_t *sub, uint32_t stencilValue, float t1, float t2) { + int spritenum = thing->sprite; + bool isPicnumOverride = thing->picnum.isValid(); + FSpriteModelFrame *modelframe = isPicnumOverride ? nullptr : gl_FindModelFrame(thing->GetClass(), spritenum, thing->frame, !!(thing->flags & MF_DROPPED)); + if (modelframe) + { + const auto &viewpoint = PolyRenderer::Instance()->Viewpoint; + DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac); + PolyRenderModel(thread, worldToClip, clipPlane, stencilValue, (float)pos.X, (float)pos.Y, (float)pos.Z, modelframe, thing); + return; + } + DVector2 line[2]; if (!GetLine(thing, line[0], line[1])) return;