# Conflicts:
#	src/version.h
This commit is contained in:
Rachael Alexanderson 2017-09-20 09:05:03 -04:00
commit da42c9adf5
593 changed files with 11976 additions and 1645 deletions

View file

@ -20,10 +20,10 @@ environment:
CONFIGURATION: Release
TOOLSET: v141_xp
APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2017"
- GENERATOR: "Visual Studio 15 2017 Win64"
CONFIGURATION: Release
TOOLSET: v141_xp
APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2017"
# - GENERATOR: "Visual Studio 15 2017 Win64"
# CONFIGURATION: Release
# TOOLSET: v141_xp
# APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2017"
- GENERATOR: "Visual Studio 14 2015 Win64"
CONFIGURATION: Debug
TOOLSET: v140

View file

@ -56,15 +56,15 @@ matrix:
- os: linux
compiler: clang
env:
- CLANG_VERSION=4.0
- CLANG_VERSION=5.0
- CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=MinSizeRel -DDYN_OPENAL=NO -DDYN_SNDFILE=NO -DDYN_MPG123=NO -DDYN_FLUIDSYNTH=NO"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-4.0
- llvm-toolchain-trusty-5.0
packages:
- clang-4.0
- clang-5.0
- libstdc++-5-dev
- libsdl2-dev
- libgme-dev

View file

@ -15,8 +15,15 @@ include( CreateLaunchers )
include( FindPackageHandleStandardArgs )
# Produce a warning if XP support will be missing.
if( MSVC14 AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v140_xp" )
message( WARNING "This project supports Windows XP (including XP x64), but you must set the optional toolset to v140_xp manually to have it in your build! Use -T \"v140_xp\" from the command prompt." )
if( MSVC )
list( APPEND WINXP_TOOLSETS v140_xp v141_xp)
list( FIND WINXP_TOOLSETS "${CMAKE_GENERATOR_TOOLSET}" HAVE_WINXP_SUPPORT)
if( HAVE_WINXP_SUPPORT EQUAL -1 )
string( REPLACE ";" " or " WINXP_TOOLSETS_STR "${WINXP_TOOLSETS}" )
message( WARNING "This project supports Windows XP but you must set the optional toolset to ${WINXP_TOOLSETS_STR} manually to have it in your build!\n"
"Assign toolset's name to CMAKE_GENERATOR_TOOLSET variable or use -T <toolset> from the command prompt." )
endif()
endif()
# Support cross compiling
@ -323,6 +330,7 @@ add_subdirectory( gdtoa )
add_subdirectory( wadsrc )
add_subdirectory( wadsrc_bm )
add_subdirectory( wadsrc_lights )
add_subdirectory( wadsrc_extra )
add_subdirectory( src )
if( NOT CMAKE_CROSSCOMPILING )

View file

@ -342,7 +342,7 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
set (USE_ARMV8 0 CACHE BOOL "Use ARMv8 instructions - Raspberry Pi 3")
if (USE_ARMV8)
set( CMAKE_CXX_FLAGS "-mcpu=cortex-a53 -mfpu=neon-fp-armv8 -mtune=cortex-a53 -funsafe-math-optimizations -mhard-float -DNO_SSE ${CMAKE_CXX_FLAGS}" )
set( CMAKE_CXX_FLAGS "-mcpu=cortex-a53 -mfpu=neon-fp-armv8 -mtune=cortex-a53 -mhard-float -DNO_SSE ${CMAKE_CXX_FLAGS}" )
else ()
set( CMAKE_CXX_FLAGS "-mfpu=neon -DNO_SSE ${CMAKE_CXX_FLAGS}" )
endif ()
@ -744,6 +744,7 @@ set ( SWRENDER_SOURCES
set( POLYRENDER_SOURCES
polyrenderer/poly_renderer.cpp
polyrenderer/poly_renderthread.cpp
polyrenderer/scene/poly_scene.cpp
polyrenderer/scene/poly_portal.cpp
polyrenderer/scene/poly_cull.cpp

View file

@ -1086,6 +1086,7 @@ public:
double projectilepassheight; // height for clipping projectile movement against this actor
double CameraHeight; // Height of camera when used as such
double CameraFOV;
double RadiusDamageFactor; // Radius damage factor
double SelfDamageFactor;

View file

@ -184,6 +184,25 @@ bool FileExists (const char *filename)
return stat(filename, &buff) == 0 && !(buff.st_mode & S_IFDIR);
}
//==========================================================================
//
// DirExists
//
// Returns true if the given path exists and is a directory.
//
//==========================================================================
bool DirExists(const char *filename)
{
struct stat buff;
// [RH] Empty filenames are never there
if (filename == NULL || *filename == 0)
return false;
return stat(filename, &buff) == 0 && (buff.st_mode & S_IFDIR);
}
//==========================================================================
//
// DirEntryExists

View file

@ -20,6 +20,7 @@
int Q_filelength (FILE *f);
bool FileExists (const char *filename);
bool DirExists(const char *filename);
bool DirEntryExists (const char *pathname);
extern FString progdir;

View file

@ -2431,12 +2431,12 @@ int D_LoadDehLumps(DehLumpSource source)
{
const int filenum = Wads.GetLumpFile(lumpnum);
if (FromIWAD == source && filenum > FWadCollection::IWAD_FILENUM)
if (FromIWAD == source && filenum > Wads.GetIwadNum())
{
// No more DEHACKED lumps in IWAD
break;
}
else if (FromPWADs == source && filenum <= FWadCollection::IWAD_FILENUM)
else if (FromPWADs == source && filenum <= Wads.GetIwadNum())
{
// Skip DEHACKED lumps from IWAD
continue;

View file

@ -48,80 +48,37 @@
#include "gameconfigfile.h"
#include "resourcefiles/resourcefile.h"
#include "version.h"
#include "doomerrors.h"
#include "v_text.h"
CVAR (Bool, queryiwad, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
CVAR (String, defaultiwad, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
//==========================================================================
//
// Clear check list
//
//==========================================================================
void FIWadManager::ClearChecks()
{
mLumpsFound.Resize(mIWads.Size());
for(unsigned i=0;i<mLumpsFound.Size(); i++)
{
mLumpsFound[i] = 0;
}
}
//==========================================================================
//
// Check one lump
//
//==========================================================================
void FIWadManager::CheckLumpName(const char *name)
{
for(unsigned i=0; i< mIWads.Size(); i++)
{
for(unsigned j=0; j < mIWads[i].Lumps.Size(); j++)
{
if (!mIWads[i].Lumps[j].CompareNoCase(name))
{
mLumpsFound[i] |= (1<<j);
}
}
}
}
//==========================================================================
//
// Returns check result
//
//==========================================================================
int FIWadManager::GetIWadInfo()
{
for(unsigned i=0; i< mIWads.Size(); i++)
{
if (mLumpsFound[i] == (1 << mIWads[i].Lumps.Size()) - 1)
{
return i;
}
}
return -1;
}
//==========================================================================
//
// Parses IWAD definitions
//
//==========================================================================
void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize)
void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize, FIWADInfo *result)
{
FScanner sc;
int numblocks = 0;
sc.OpenMem("IWADINFO", data, datasize);
while (sc.GetString())
{
if (sc.Compare("IWAD"))
{
FIWADInfo *iwad = &mIWads[mIWads.Reserve(1)];
numblocks++;
if (result && numblocks > 1)
{
sc.ScriptMessage("Multiple IWAD records ignored");
// Skip the rest.
break;
}
FIWADInfo *iwad = result ? result : &mIWadInfos[mIWadInfos.Reserve(1)];
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
@ -138,6 +95,17 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize)
sc.MustGetString();
iwad->Autoname = sc.String;
}
else if (sc.Compare("IWadname"))
{
sc.MustGetStringName("=");
sc.MustGetString();
iwad->IWadname = sc.String;
if (sc.CheckString(","))
{
sc.MustGetNumber();
iwad->prio = sc.Number;
}
}
else if (sc.Compare("Config"))
{
sc.MustGetStringName("=");
@ -225,7 +193,7 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize)
iwad->MapInfo = "mapinfo/mindefaults.txt";
}
}
else if (sc.Compare("NAMES"))
else if (result == nullptr && sc.Compare("NAMES"))
{
sc.MustGetStringName("{");
mIWadNames.Push(FString());
@ -233,20 +201,22 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize)
{
sc.MustGetString();
FString wadname = sc.String;
#if defined(_WIN32) || defined(__APPLE__) // Turns out Mac OS X is case insensitive.
mIWadNames.Push(wadname);
#else
// check for lowercase, uppercased first letter and full uppercase on Linux etc.
wadname.ToLower();
mIWadNames.Push(wadname);
wadname.LockBuffer()[0] = toupper(wadname[0]);
wadname.UnlockBuffer();
mIWadNames.Push(wadname);
wadname.ToUpper();
mIWadNames.Push(wadname);
#endif
}
}
else if (result == nullptr && sc.Compare("ORDER"))
{
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
sc.MustGetString();
mOrderNames.Push(sc.String);
}
}
else
{
sc.ScriptError("Unknown keyword '%s'", sc.String);
}
}
}
@ -256,7 +226,7 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize)
//
//==========================================================================
void FIWadManager::ParseIWadInfos(const char *fn)
FIWadManager::FIWadManager(const char *fn)
{
FResourceFile *resfile = FResourceFile::OpenResourceFile(fn, NULL, true);
if (resfile != NULL)
@ -275,7 +245,7 @@ void FIWadManager::ParseIWadInfos(const char *fn)
}
delete resfile;
}
if (mIWadNames.Size() == 0 || mIWads.Size() == 0)
if (mIWadNames.Size() == 0 || mIWadInfos.Size() == 0)
{
I_FatalError("No IWAD definitions found");
}
@ -293,9 +263,25 @@ int FIWadManager::ScanIWAD (const char *iwad)
{
FResourceFile *iwadfile = FResourceFile::OpenResourceFile(iwad, NULL, true);
mLumpsFound.Resize(mIWadInfos.Size());
auto CheckLumpName = [=](const char *name)
{
for (unsigned i = 0; i< mIWadInfos.Size(); i++)
{
for (unsigned j = 0; j < mIWadInfos[i].Lumps.Size(); j++)
{
if (!mIWadInfos[i].Lumps[j].CompareNoCase(name))
{
mLumpsFound[i] |= (1 << j);
}
}
}
};
if (iwadfile != NULL)
{
ClearChecks();
memset(&mLumpsFound[0], 0, mLumpsFound.Size() * sizeof(mLumpsFound[0]));
for(uint32_t ii = 0; ii < iwadfile->LumpCount(); ii++)
{
FResourceLump *lump = iwadfile->GetLump(ii);
@ -312,52 +298,171 @@ int FIWadManager::ScanIWAD (const char *iwad)
}
delete iwadfile;
}
return GetIWadInfo();
for (unsigned i = 0; i< mIWadInfos.Size(); i++)
{
if (mLumpsFound[i] == (1 << mIWadInfos[i].Lumps.Size()) - 1)
{
DPrintf(DMSG_NOTIFY, "Identified %s as %s\n", iwad, mIWadInfos[i].Name.GetChars());
return i;
}
}
return -1;
}
//==========================================================================
//
// CheckIWAD
// Look for IWAD definition lump
//
// Tries to find an IWAD from a set of known IWAD names, and checks the
// contents of each one found to determine which game it belongs to.
// Returns the number of new wads found in this pass (does not count wads
// found from a previous call).
//
//==========================================================================
int FIWadManager::CheckIWAD (const char *doomwaddir, WadStuff *wads)
int FIWadManager::CheckIWADInfo(const char *fn)
{
const char *slash;
int numfound;
numfound = 0;
slash = (doomwaddir[0] && doomwaddir[strlen (doomwaddir)-1] != '/') ? "/" : "";
// Search for a pre-defined IWAD
for (unsigned i=0; i< mIWadNames.Size(); i++)
FResourceFile *resfile = FResourceFile::OpenResourceFile(fn, NULL, true);
if (resfile != NULL)
{
if (mIWadNames[i].IsNotEmpty() && wads[i].Path.IsEmpty())
uint32_t cnt = resfile->LumpCount();
for (int i = cnt - 1; i >= 0; i--)
{
FString iwad;
iwad.Format ("%s%s%s", doomwaddir, slash, mIWadNames[i].GetChars());
FixPathSeperator (iwad);
if (FileExists (iwad))
FResourceLump *lmp = resfile->GetLump(i);
if (lmp->Namespace == ns_global && !stricmp(lmp->Name, "IWADINFO"))
{
wads[i].Type = ScanIWAD (iwad);
if (wads[i].Type != -1)
// Found one!
try
{
wads[i].Path = iwad;
wads[i].Name = mIWads[wads[i].Type].Name;
numfound++;
FIWADInfo result;
ParseIWadInfo(resfile->Filename, (const char*)lmp->CacheLump(), lmp->LumpSize, &result);
delete resfile;
for (auto &wadinf : mIWadInfos)
{
if (wadinf.Name == result.Name)
{
return -1; // do not show the same one twice.
}
}
return mIWadInfos.Push(result);
}
catch (CRecoverableError &err)
{
delete resfile;
Printf(TEXTCOLOR_RED "%s: %s\nFile has been removed from the list of IWADs\n", fn, err.GetMessage());
return -1;
}
break;
}
}
delete resfile;
Printf(TEXTCOLOR_RED "%s: Unable to find IWADINFO\nFile has been removed from the list of IWADs\n", fn);
return -1;
}
Printf(TEXTCOLOR_RED "%s: Unable to open as resource file.\nFile has been removed from the list of IWADs\n", fn);
return -1;
}
//==========================================================================
//
// CollectSearchPaths
//
// collect all paths in a local array for easier management
//
//==========================================================================
void FIWadManager::CollectSearchPaths()
{
if (GameConfig->SetSection("IWADSearch.Directories"))
{
const char *key;
const char *value;
while (GameConfig->NextInSection(key, value))
{
if (stricmp(key, "Path") == 0)
{
FString nice = NicePath(value);
if (nice.Len() > 0) mSearchPaths.Push(nice);
}
}
}
mSearchPaths.Append(I_GetGogPaths());
mSearchPaths.Append(I_GetSteamPath());
return numfound;
// Unify and remove trailing slashes
for (auto &str : mSearchPaths)
{
FixPathSeperator(str);
if (str[str.Len() - 1] == '/') str.Truncate(str.Len() - 1);
}
}
//==========================================================================
//
// AddIWADCandidates
//
// scans the given directory and adds all potential IWAD candidates
//
//==========================================================================
void FIWadManager::AddIWADCandidates(const char *dir)
{
void *handle;
findstate_t findstate;
FStringf slasheddir("%s/", dir);
FString findmask = slasheddir + "*.*";
if ((handle = I_FindFirst(findmask, &findstate)) != (void *)-1)
{
do
{
if (!(I_FindAttr(&findstate) & FA_DIREC))
{
auto FindName = I_FindName(&findstate);
auto p = strrchr(FindName, '.');
if (p != nullptr)
{
// special IWAD extension.
if (!stricmp(p, ".iwad") || !stricmp(p, ".ipk3") || !stricmp(p, "ipk7"))
{
mFoundWads.Push(FFoundWadInfo{ slasheddir + FindName, "", -1 });
}
}
for (auto &name : mIWadNames)
{
if (!stricmp(name, FindName))
{
mFoundWads.Push(FFoundWadInfo{ slasheddir + FindName, "", -1 });
}
}
}
} while (I_FindNext(handle, &findstate) == 0);
I_FindClose(handle);
}
}
//==========================================================================
//
// ValidateIWADs
//
// validate all found candidates and eliminate the bogus ones.
//
//==========================================================================
void FIWadManager::ValidateIWADs()
{
TArray<int> returns;
unsigned originalsize = mIWadInfos.Size();
for (auto &p : mFoundWads)
{
int index;
auto x = strrchr(p.mFullPath, '.');
if (x != nullptr && (!stricmp(x, ".iwad") || !stricmp(x, ".ipk3") || !stricmp(x, "ipk7")))
{
index = CheckIWADInfo(p.mFullPath);
}
else
{
index = ScanIWAD(p.mFullPath);
}
p.mInfoIndex = index;
}
}
//==========================================================================
@ -383,131 +488,138 @@ int FIWadManager::CheckIWAD (const char *doomwaddir, WadStuff *wads)
static bool havepicked = false;
int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad, const char *zdoom_wad)
int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad, const char *zdoom_wad, const char *optional_wad)
{
TArray<WadStuff> wads;
TArray<size_t> foundwads;
const char *iwadparm = Args->CheckValue ("-iwad");
int pickwad;
size_t numwads;
size_t i;
bool iwadparmfound = false;
FString custwad;
wads.Resize(mIWadNames.Size());
foundwads.Resize(mIWads.Size());
memset(&foundwads[0], 0, foundwads.Size() * sizeof(foundwads[0]));
CollectSearchPaths();
if (iwadparm == NULL && iwad != NULL && *iwad != 0)
// Collect all IWADs in the search path
for (auto &dir : mSearchPaths)
{
iwadparm = iwad;
AddIWADCandidates(dir);
}
unsigned numFoundWads = mFoundWads.Size();
if (iwadparm)
{
// Check if the given IWAD has an absolute path, in which case the search path will be ignored.
custwad = iwadparm;
FixPathSeperator (custwad);
if (CheckIWAD (custwad, &wads[0]))
{ // -iwad parameter was a directory
iwadparm = NULL;
FixPathSeperator(custwad);
DefaultExtension(custwad, ".wad");
bool isAbsolute = (custwad[0] == '/');
#ifdef WINDOWS
isAbsolute |= (custwad.Len() >= 2 && custwad[1] == ':');
#endif
if (isAbsolute)
{
if (FileExists(custwad)) mFoundWads.Push({ custwad, "", -1 });
}
else
{
DefaultExtension (custwad, ".wad");
iwadparm = custwad;
mIWadNames[0] = custwad;
CheckIWAD ("", &wads[0]);
}
}
if (iwadparm == NULL || wads[0].Path.IsEmpty() || mIWads[wads[0].Type].Required.IsNotEmpty())
{
if (GameConfig->SetSection ("IWADSearch.Directories"))
{
const char *key;
const char *value;
while (GameConfig->NextInSection (key, value))
for (auto &dir : mSearchPaths)
{
if (stricmp (key, "Path") == 0)
FStringf fullpath("%s/%s", dir.GetChars(), custwad.GetChars());
if (FileExists(fullpath))
{
FString nice = NicePath(value);
FixPathSeperator(nice);
CheckIWAD(nice, &wads[0]);
mFoundWads.Push({ fullpath, "", -1 });
}
}
}
TArray<FString> gog_paths = I_GetGogPaths();
for (i = 0; i < gog_paths.Size(); ++i)
{
CheckIWAD (gog_paths[i], &wads[0]);
}
TArray<FString> steam_path = I_GetSteamPath();
for (i = 0; i < steam_path.Size(); ++i)
{
CheckIWAD (steam_path[i], &wads[0]);
}
}
// -iwad not found or not specified. Revert back to standard behavior.
if (mFoundWads.Size() == numFoundWads) iwadparm = nullptr;
if (iwadparm != NULL && !wads[0].Path.IsEmpty())
{
iwadparmfound = true;
}
// Now check if what got collected actually is an IWAD.
ValidateIWADs();
for (i = numwads = 0; i < mIWadNames.Size(); i++)
// Check for required dependencies.
for (unsigned i = 0; i < mFoundWads.Size(); i++)
{
if (!wads[i].Path.IsEmpty())
auto infndx = mFoundWads[i].mInfoIndex;
if (infndx >= 0)
{
if (i != numwads)
auto &wadinfo = mIWadInfos[infndx];
if (wadinfo.Required.IsNotEmpty())
{
wads[numwads] = wads[i];
}
foundwads[wads[numwads].Type] = numwads + 1;
numwads++;
}
}
for (unsigned i=0; i<mIWads.Size(); i++)
{
if (mIWads[i].Required.IsNotEmpty() && foundwads[i])
{
bool found = false;
// needs to be loaded with another IWAD (HexenDK)
for (unsigned j=0; j<mIWads.Size(); j++)
{
if (!mIWads[i].Required.Compare(mIWads[j].Name))
bool found = false;
// needs to be loaded with another IWAD (HexenDK)
for (unsigned j = 0; j < mFoundWads.Size(); j++)
{
if (foundwads[j])
auto inf2ndx = mFoundWads[j].mInfoIndex;
if (inf2ndx >= 0)
{
found = true;
mIWads[i].preload = j;
}
break;
}
}
// The required WAD is not there so this one can't be used and must be deleted from the list
if (!found)
{
size_t kill = foundwads[i];
for (size_t j = kill; j < numwads; ++j)
{
wads[j - 1] = wads[j];
}
numwads--;
foundwads[i] = 0;
for (unsigned j = 0; j < foundwads.Size(); ++j)
{
if (foundwads[j] > kill)
{
foundwads[j]--;
if (!mIWadInfos[infndx].Required.Compare(mIWadInfos[inf2ndx].Name))
{
mFoundWads[i].mRequiredPath = mFoundWads[j].mFullPath;
break;
}
}
}
// The required dependency was not found. Skip this IWAD.
if (mFoundWads[i].mRequiredPath.IsEmpty()) mFoundWads[i].mInfoIndex = -1;
}
}
}
TArray<FFoundWadInfo> picks;
if (numFoundWads < mFoundWads.Size())
{
// We have a -iwad parameter. Pick the first usable IWAD we found through that.
for (unsigned i = numFoundWads; i < mFoundWads.Size(); i++)
{
if (mFoundWads[i].mInfoIndex > 0)
{
picks.Push(mFoundWads[i]);
break;
}
}
}
else if (iwad != nullptr && *iwad != 0)
{
int pickedprio = -1;
// scan the list of found IWADs for a matching one for the current PWAD.
for (auto &found : mFoundWads)
{
if (found.mInfoIndex >= 0 && mIWadInfos[found.mInfoIndex].IWadname.CompareNoCase(iwad) == 0 && mIWadInfos[found.mInfoIndex].prio > pickedprio)
{
picks.Clear();
picks.Push(found);
pickedprio = mIWadInfos[found.mInfoIndex].prio;
}
}
}
if (picks.Size() == 0)
{
// Now sort what we found and discard all duplicates.
for (unsigned i = 0; i < mOrderNames.Size(); i++)
{
bool picked = false;
for (int j = 0; j < (int)mFoundWads.Size(); j++)
{
if (mFoundWads[j].mInfoIndex >= 0)
{
if (mIWadInfos[mFoundWads[j].mInfoIndex].Name.Compare(mOrderNames[i]) == 0)
{
if (!picked)
{
picked = true;
picks.Push(mFoundWads[j]);
}
mFoundWads.Delete(j--);
}
}
}
}
// What's left is IWADs with their own IWADINFO. Copy those in order of discovery.
for (auto &entry : mFoundWads)
{
if (entry.mInfoIndex >= 0) picks.Push(entry);
}
}
if (numwads == 0)
// If we still haven't found a suitable IWAD let's error out.
if (picks.Size() == 0)
{
I_FatalError ("Cannot find a game IWAD (doom.wad, doom2.wad, heretic.wad, etc.).\n"
"Did you install " GAMENAME " properly? You can do either of the following:\n"
@ -526,38 +638,48 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
"iwads to the list beneath [IWADSearch.Directories]");
#endif
}
int pick = 0;
pickwad = 0;
if (!iwadparmfound && numwads > 1)
// We got more than one so present the IWAD selection box.
if (picks.Size() > 1)
{
int defiwad = 0;
// Locate the user's prefered IWAD, if it was found.
if (defaultiwad[0] != '\0')
{
for (i = 0; i < numwads; ++i)
for (unsigned i = 0; i < picks.Size(); ++i)
{
FString basename = ExtractFileBase(wads[i].Path);
FString &basename = mIWadInfos[picks[i].mInfoIndex].Name;
if (stricmp(basename, defaultiwad) == 0)
{
defiwad = (int)i;
pick = i;
break;
}
}
}
if (!havepicked) // just use the first IWAD if the restart doesn't have a -iwad parameter. We cannot open the picker in fullscreen mode.
if (picks.Size() > 1)
{
pickwad = I_PickIWad(&wads[0], (int)numwads, queryiwad, defiwad);
if (pickwad >= 0)
if (!havepicked)
{
// The newly selected IWAD becomes the new default
FString basename = ExtractFileBase(wads[pickwad].Path);
defaultiwad = basename;
TArray<WadStuff> wads;
for (auto & found : picks)
{
WadStuff stuff;
stuff.Name = mIWadInfos[found.mInfoIndex].Name;
stuff.Path = ExtractFileBase(found.mFullPath);
wads.Push(stuff);
}
pick = I_PickIWad(&wads[0], (int)wads.Size(), queryiwad, pick);
if (pick >= 0)
{
// The newly selected IWAD becomes the new default
defaultiwad = mIWadInfos[picks[pick].mInfoIndex].Name;
}
else
{
exit(0);
}
havepicked = true;
}
if (pickwad < 0)
exit(0);
havepicked = true;
}
}
@ -565,15 +687,23 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
wadfiles.Clear();
D_AddFile (wadfiles, zdoom_wad);
if (mIWads[wads[pickwad].Type].preload >= 0)
{
D_AddFile (wadfiles, wads[foundwads[mIWads[wads[pickwad].Type].preload]-1].Path);
}
D_AddFile (wadfiles, wads[pickwad].Path);
// [SP] Load non-free assets if available. This must be done before the IWAD.
if (D_AddFile(wadfiles, optional_wad))
Wads.SetIwadNum(2);
else
Wads.SetIwadNum(1);
for (unsigned i=0; i < mIWads[wads[pickwad].Type].Load.Size(); i++)
if (picks[pick].mRequiredPath.IsNotEmpty())
{
long lastslash = wads[pickwad].Path.LastIndexOf ('/');
D_AddFile (wadfiles, picks[pick].mRequiredPath);
}
D_AddFile (wadfiles, picks[pick].mFullPath);
auto info = mIWadInfos[picks[pick].mInfoIndex];
// Load additional resources from the same directory as the IWAD itself.
for (unsigned i=0; i < info.Load.Size(); i++)
{
long lastslash = picks[pick].mFullPath.LastIndexOf ('/');
FString path;
if (lastslash == -1)
@ -582,13 +712,13 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
}
else
{
path = FString (wads[pickwad].Path.GetChars(), lastslash + 1);
path = FString (picks[pick].mFullPath.GetChars(), lastslash + 1);
}
path += mIWads[wads[pickwad].Type].Load[i];
path += info.Load[i];
D_AddFile (wadfiles, path);
}
return wads[pickwad].Type;
return picks[pick].mInfoIndex;
}
@ -598,11 +728,11 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
//
//==========================================================================
const FIWADInfo *FIWadManager::FindIWAD(TArray<FString> &wadfiles, const char *iwad, const char *basewad)
const FIWADInfo *FIWadManager::FindIWAD(TArray<FString> &wadfiles, const char *iwad, const char *basewad, const char *optionalwad)
{
int iwadType = IdentifyVersion(wadfiles, iwad, basewad);
int iwadType = IdentifyVersion(wadfiles, iwad, basewad, optionalwad);
//gameiwad = iwadType;
const FIWADInfo *iwad_info = &mIWads[iwadType];
const FIWADInfo *iwad_info = &mIWadInfos[iwadType];
if (DoomStartupInfo.Name.IsEmpty()) DoomStartupInfo.Name = iwad_info->Name;
if (DoomStartupInfo.BkColor == 0 && DoomStartupInfo.FgColor == 0)
{
@ -611,4 +741,4 @@ const FIWADInfo *FIWadManager::FindIWAD(TArray<FString> &wadfiles, const char *i
}
I_SetIWADInfo();
return iwad_info;
}
}

View file

@ -399,19 +399,19 @@ CUSTOM_CVAR (Int, dmflags, 0, CVAR_SERVERINFO)
// If nofov is set, force everybody to the arbitrator's FOV.
if ((self & DF_NO_FOV) && consoleplayer == Net_Arbitrator)
{
uint8_t fov;
float fov;
Net_WriteByte (DEM_FOV);
// If the game is started with DF_NO_FOV set, the arbitrator's
// DesiredFOV will not be set when this callback is run, so
// be sure not to transmit a 0 FOV.
fov = (uint8_t)players[consoleplayer].DesiredFOV;
fov = players[consoleplayer].DesiredFOV;
if (fov == 0)
{
fov = 90;
}
Net_WriteByte (fov);
Net_WriteFloat (fov);
}
}
@ -684,8 +684,15 @@ void D_Display ()
if (viewactive)
{
R_SetFOV (r_viewpoint, players[consoleplayer].camera && players[consoleplayer].camera->player ?
players[consoleplayer].camera->player->FOV : 90.f);
DAngle fov = 90.f;
AActor *cam = players[consoleplayer].camera;
if (cam)
{
if (cam->player)
fov = cam->player->FOV;
else fov = cam->CameraFOV;
}
R_SetFOV(r_viewpoint, fov);
}
// [RH] change the screen mode if needed
@ -981,6 +988,7 @@ void D_ErrorCleanup ()
G_CheckDemoStatus ();
Net_ClearBuffers ();
G_NewInit ();
M_ClearMenus ();
singletics = false;
playeringame[0] = 1;
players[0].playerstate = PST_LIVE;
@ -1984,9 +1992,9 @@ static FString CheckGameInfo(TArray<FString> & pwads)
static void SetMapxxFlag()
{
int lump_name = Wads.CheckNumForName("MAP01", ns_global, FWadCollection::IWAD_FILENUM);
int lump_wad = Wads.CheckNumForFullName("maps/map01.wad", FWadCollection::IWAD_FILENUM);
int lump_map = Wads.CheckNumForFullName("maps/map01.map", FWadCollection::IWAD_FILENUM);
int lump_name = Wads.CheckNumForName("MAP01", ns_global, Wads.GetIwadNum());
int lump_wad = Wads.CheckNumForFullName("maps/map01.wad", Wads.GetIwadNum());
int lump_map = Wads.CheckNumForFullName("maps/map01.map", Wads.GetIwadNum());
if (lump_name >= 0 || lump_wad >= 0 || lump_map >= 0) gameinfo.flags |= GI_MAPxx;
}
@ -2331,6 +2339,8 @@ void D_DoomMain (void)
}
}
if (!batchrun) Printf(PRINT_LOG, "%s version %s\n", GAMENAME, GetVersionString());
D_DoomInit();
// [RH] Make sure zdoom.pk3 is always loaded,
@ -2342,8 +2352,9 @@ void D_DoomMain (void)
}
FString basewad = wad;
iwad_man = new FIWadManager;
iwad_man->ParseIWadInfos(basewad);
FString optionalwad = BaseFileSearch(OPTIONALWAD, NULL, true);
iwad_man = new FIWadManager(basewad);
// Now that we have the IWADINFO, initialize the autoload ini sections.
GameConfig->DoAutoloadSetup(iwad_man);
@ -2373,10 +2384,9 @@ void D_DoomMain (void)
if (iwad_man == NULL)
{
iwad_man = new FIWadManager;
iwad_man->ParseIWadInfos(basewad);
iwad_man = new FIWadManager(basewad);
}
const FIWADInfo *iwad_info = iwad_man->FindIWAD(allwads, iwad, basewad);
const FIWADInfo *iwad_info = iwad_man->FindIWAD(allwads, iwad, basewad, optionalwad);
gameinfo.gametype = iwad_info->gametype;
gameinfo.flags = iwad_info->flags;
gameinfo.ConfigName = iwad_info->Configname;

View file

@ -70,29 +70,38 @@ extern uint32_t r_renderercaps;
struct WadStuff
{
WadStuff() : Type(0) {}
FString Path;
FString Name;
int Type;
};
struct FIWADInfo
{
FString Name; // Title banner text for this IWAD
FString Autoname; // Name of autoload ini section for this IWAD
FString IWadname; // Default name this game would use - this is for IWAD detection in GAMEINFO.
int prio = 0; // selection priority for given IWAD name.
FString Configname; // Name of config section for this IWAD
FString Required; // Requires another IWAD
uint32_t FgColor; // Foreground color for title banner
uint32_t BkColor; // Background color for title banner
EGameType gametype; // which game are we playing?
uint32_t FgColor = 0; // Foreground color for title banner
uint32_t BkColor = 0xc0c0c0; // Background color for title banner
EGameType gametype = GAME_Doom; // which game are we playing?
FString MapInfo; // Base mapinfo to load
TArray<FString> Load; // Wads to be loaded with this one.
TArray<FString> Lumps; // Lump names for identification
int flags;
int preload;
int flags = 0;
};
FIWADInfo() { flags = 0; preload = -1; FgColor = 0; BkColor= 0xc0c0c0; gametype = GAME_Doom; }
struct FFoundWadInfo
{
FString mFullPath;
FString mRequiredPath;
int mInfoIndex = -1; // must be an index because of reallocation
FFoundWadInfo() {}
FFoundWadInfo(const FString &s1, const FString &s2, int index)
: mFullPath(s1), mRequiredPath(s2), mInfoIndex(index)
{
}
};
struct FStartupInfo
@ -123,28 +132,31 @@ extern FStartupInfo DoomStartupInfo;
class FIWadManager
{
TArray<FIWADInfo> mIWads;
TArray<FIWADInfo> mIWadInfos;
TArray<FString> mIWadNames;
TArray<FString> mSearchPaths;
TArray<FString> mOrderNames;
TArray<FFoundWadInfo> mFoundWads;
TArray<int> mLumpsFound;
void ParseIWadInfo(const char *fn, const char *data, int datasize);
void ClearChecks();
void CheckLumpName(const char *name);
int GetIWadInfo();
void ParseIWadInfo(const char *fn, const char *data, int datasize, FIWADInfo *result = nullptr);
int ScanIWAD (const char *iwad);
int CheckIWAD (const char *doomwaddir, WadStuff *wads);
int IdentifyVersion (TArray<FString> &wadfiles, const char *iwad, const char *zdoom_wad);
int CheckIWADInfo(const char *iwad);
int IdentifyVersion (TArray<FString> &wadfiles, const char *iwad, const char *zdoom_wad, const char *optional_wad);
void CollectSearchPaths();
void AddIWADCandidates(const char *dir);
void ValidateIWADs();
public:
void ParseIWadInfos(const char *fn);
const FIWADInfo *FindIWAD(TArray<FString> &wadfiles, const char *iwad, const char *basewad);
FIWadManager(const char *fn);
const FIWADInfo *FindIWAD(TArray<FString> &wadfiles, const char *iwad, const char *basewad, const char *optionalwad);
const FString *GetAutoname(unsigned int num) const
{
if (num < mIWads.Size()) return &mIWads[num].Autoname;
if (num < mIWadInfos.Size()) return &mIWadInfos[num].Autoname;
else return NULL;
}
int GetIWadFlags(unsigned int num) const
{
if (num < mIWads.Size()) return mIWads[num].flags;
if (num < mIWadInfos.Size()) return mIWadInfos[num].flags;
else return false;
}

View file

@ -2049,7 +2049,7 @@ FDynamicBuffer::~FDynamicBuffer ()
{
if (m_Data)
{
free (m_Data);
M_Free (m_Data);
m_Data = NULL;
}
m_Len = m_BufferLen = 0;
@ -2465,7 +2465,7 @@ void Net_DoCommand (int type, uint8_t **stream, int player)
case DEM_FOV:
{
float newfov = (float)ReadByte (stream);
float newfov = ReadFloat (stream);
if (newfov != players[consoleplayer].DesiredFOV)
{
@ -2485,7 +2485,7 @@ void Net_DoCommand (int type, uint8_t **stream, int player)
break;
case DEM_MYFOV:
players[player].DesiredFOV = (float)ReadByte (stream);
players[player].DesiredFOV = ReadFloat (stream);
break;
case DEM_RUNSCRIPT:
@ -2775,6 +2775,8 @@ void Net_SkipCommand (int type, uint8_t **stream)
break;
case DEM_INVUSE:
case DEM_FOV:
case DEM_MYFOV:
skip = 4;
break;
@ -2784,8 +2786,6 @@ void Net_SkipCommand (int type, uint8_t **stream)
case DEM_GENERICCHEAT:
case DEM_DROPPLAYER:
case DEM_FOV:
case DEM_MYFOV:
case DEM_ADDCONTROLLER:
case DEM_DELCONTROLLER:
skip = 1;

View file

@ -116,8 +116,8 @@ enum EDemoCommand
DEM_UNDONE5, // 24
DEM_UNDONE6, // 25
DEM_SUMMON, // 26 String: Thing to fabricate
DEM_FOV, // 27 Byte: New FOV for all players
DEM_MYFOV, // 28 Byte: New FOV for this player
DEM_FOV, // 27 Float: New FOV for all players
DEM_MYFOV, // 28 Float: New FOV for this player
DEM_CHANGEMAP2, // 29 Byte: Position in new map, String: name of new map
DEM_UNDONE7, // 30
DEM_UNDONE8, // 31

View file

@ -562,6 +562,10 @@ void FDecalLib::ParseDecal (FScanner &sc)
case DECAL_ANIMATOR:
sc.MustGetString ();
newdecal.Animator = FindAnimator (sc.String);
if (newdecal.Animator == nullptr)
{
sc.ScriptMessage("Unable to find animator %s", sc.String);
}
break;
case DECAL_LOWERDECAL:

View file

@ -2910,11 +2910,14 @@ void FParser::SF_MoveCamera(void)
DAngle targetangle = floatvalue(t_argv[4]);
DAngle anglespeed = floatvalue(t_argv[5]);
DAngle diffangle = deltaangle(cam->Angles.Yaw, targetangle);
if (movespeed > 0 && anglespeed == 0.)
{
if (!finished) targetangle = diffangle * movespeed / movelen;
if (!finished)
{
const DAngle diffangle = targetangle - cam->Angles.Yaw;
targetangle = cam->Angles.Yaw + diffangle * movespeed / movelen;
}
}
else
{
@ -2924,6 +2927,7 @@ void FParser::SF_MoveCamera(void)
cam->radius = 1 / 8192.;
cam->Height = 1 / 8192.;
cam->SetOrigin(movepos, true);
cam->SetAngle(targetangle, false);
t_return.value.i = 1;
}
else

View file

@ -2195,7 +2195,7 @@ static void PutSaveWads (FSerializer &arc)
const char *name;
// Name of IWAD
name = Wads.GetWadName (FWadCollection::IWAD_FILENUM);
name = Wads.GetWadName (Wads.GetIwadNum());
arc.AddString("Game WAD", name);
// Name of wad the map resides in

View file

@ -376,6 +376,7 @@ void FMapInfoParser::ParseGameInfo()
GAMEINFOKEY_BOOL(drawreadthis, "drawreadthis")
GAMEINFOKEY_BOOL(swapmenu, "swapmenu")
GAMEINFOKEY_BOOL(dontcrunchcorpses, "dontcrunchcorpses")
GAMEINFOKEY_BOOL(correctprintbold, "correctprintbold")
GAMEINFOKEY_BOOL(intermissioncounter, "intermissioncounter")
GAMEINFOKEY_BOOL(nightmarefast, "nightmarefast")
GAMEINFOKEY_COLOR(dimcolor, "dimcolor")

View file

@ -114,6 +114,7 @@ struct gameinfo_t
bool nightmarefast;
bool swapmenu;
bool dontcrunchcorpses;
bool correctprintbold;
TArray<FName> creditPages;
TArray<FName> finalePages;
TArray<FName> infoPages;

View file

@ -88,13 +88,13 @@ void AdjustSpriteOffsets()
for (int i = 0; i < numtex; i++)
{
if (Wads.GetLumpFile(i) > 1) break; // we are past the IWAD
if (Wads.GetLumpNamespace(i) == ns_sprites && Wads.GetLumpFile(i) == FWadCollection::IWAD_FILENUM)
if (Wads.GetLumpNamespace(i) == ns_sprites && Wads.GetLumpFile(i) == Wads.GetIwadNum())
{
char str[9];
Wads.GetLumpName(str, i);
str[8] = 0;
FTextureID texid = TexMan.CheckForTexture(str, FTexture::TEX_Sprite, 0);
if (texid.isValid() && Wads.GetLumpFile(TexMan[texid]->SourceLump) > FWadCollection::IWAD_FILENUM)
if (texid.isValid() && Wads.GetLumpFile(TexMan[texid]->SourceLump) > Wads.GetIwadNum())
{
// This texture has been replaced by some PWAD.
memcpy(&sprid, str, 4);
@ -137,9 +137,9 @@ void AdjustSpriteOffsets()
if (lumpnum >= 0 && lumpnum < Wads.GetNumLumps())
{
int wadno = Wads.GetLumpFile(lumpnum);
if ((iwadonly && wadno==FWadCollection::IWAD_FILENUM) || (!iwadonly && wadno == ofslumpno))
if ((iwadonly && wadno==Wads.GetIwadNum()) || (!iwadonly && wadno == ofslumpno))
{
if (wadno == FWadCollection::IWAD_FILENUM && !forced && iwadonly)
if (wadno == Wads.GetIwadNum() && !forced && iwadonly)
{
memcpy(&sprid, &tex->Name[0], 4);
if (donotprocess.CheckKey(sprid)) continue; // do not alter sprites that only get partially replaced.

View file

@ -82,12 +82,10 @@ ADD_STAT(shadowmap)
return out;
}
CUSTOM_CVAR(Int, gl_shadowmap_quality, 128, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CUSTOM_CVAR(Int, gl_shadowmap_quality, 512, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
switch (self)
{
case 32:
case 64:
case 128:
case 256:
case 512:

View file

@ -987,7 +987,7 @@ struct FGLInterface : public FRenderer
void WriteSavePic (player_t *player, FileWriter *file, int width, int height) override;
void StartSerialize(FSerializer &arc) override;
void EndSerialize(FSerializer &arc) override;
void RenderTextureView (FCanvasTexture *self, AActor *viewpoint, int fov) override;
void RenderTextureView (FCanvasTexture *self, AActor *viewpoint, double fov) override;
void PreprocessLevel() override;
void CleanLevelData() override;
bool RequireGLNodes() override;
@ -1098,7 +1098,7 @@ void FGLInterface::Init()
CVAR(Bool, gl_usefb, false , CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
extern TexFilter_s TexFilter[];
void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, int FOV)
void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, double FOV)
{
FMaterial * gltex = FMaterial::ValidateTexture(tex, false);

View file

@ -321,6 +321,12 @@ void GLSprite::Draw(int pass)
}
else if (modelframe == nullptr)
{
int tm, sb, db, be;
// This still needs to set the texture mode. As blend mode it will always use GL_ONE/GL_ZERO
gl_GetRenderStyle(RenderStyle, false, false, &tm, &sb, &db, &be);
gl_RenderState.SetTextureMode(tm);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -128.0f);
}
@ -685,7 +691,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal)
// check renderrequired vs ~r_rendercaps, if anything matches we don't support that feature,
// check renderhidden vs r_rendercaps, if anything matches we do support that feature and should hide it.
if (!r_debug_disable_vis_filter && (!!(thing->RenderRequired & ~r_renderercaps)) ||
if ((!r_debug_disable_vis_filter && !!(thing->RenderRequired & ~r_renderercaps)) ||
(!!(thing->RenderHidden & r_renderercaps)))
return;
@ -1301,4 +1307,4 @@ void GLSceneDrawer::RenderActorsInPortal(FGLLinePortal *glport)
}
}
}
}
}

View file

@ -34,6 +34,7 @@
#include "gl/gl_functions.h"
#include "g_level.h"
#include "g_levellocals.h"
#include "actorinlines.h"
#include "gl/system/gl_cvars.h"
#include "gl/renderer/gl_renderer.h"
@ -179,6 +180,13 @@ void BSPWalkCircle(float x, float y, float radiusSquared, const Callback &callba
void gl_SetDynModelLight(AActor *self, bool hudmodel)
{
// Legacy and deferred render paths gets the old flat model light
if (gl.lightmethod != LM_DIRECT)
{
gl_SetDynSpriteLight(self, nullptr);
return;
}
modellightdata.Clear();
if (self)

View file

@ -43,6 +43,7 @@ static TArray<FString> m_Extensions;
RenderContext gl;
EXTERN_CVAR(Bool, gl_legacy_mode)
extern int currentrenderer;
//==========================================================================
//
@ -207,7 +208,8 @@ void gl_LoadExtensions()
// The minimum requirement for the modern render path is GL 3.3.
// Although some GL 3.1 or 3.2 solutions may theoretically work they are usually too broken or too slow.
if (gl_version < 3.3f)
// unless, of course, we're simply using this as a software backend...
if ((gl_version < 3.3f && (currentrenderer==1)) || gl_version < 3.0f)
{
gl.legacyMode = true;
gl.lightmethod = LM_LEGACY;

View file

@ -93,7 +93,6 @@ EXTERN_CVAR(Float, Gamma)
EXTERN_CVAR(Bool, vid_vsync)
EXTERN_CVAR(Float, transsouls)
EXTERN_CVAR(Int, vid_refreshrate)
EXTERN_CVAR(Bool, gl_legacy_mode)
#ifdef WIN32
extern cycle_t BlitCycles;
@ -199,15 +198,6 @@ OpenGLSWFrameBuffer::OpenGLSWFrameBuffer(void *hMonitor, int width, int height,
const char *glversion = (const char*)glGetString(GL_VERSION);
bool isGLES = (glversion && strlen(glversion) > 10 && memcmp(glversion, "OpenGL ES ", 10) == 0);
UCVarValue value;
// GL 3.0 is mostly broken on MESA drivers which really are the only relevant case here that doesn't fulfill the requirements based on version number alone.
#ifdef _WIN32
value.Bool = !ogl_IsVersionGEQ(3, 0);
#else
value.Bool = !ogl_IsVersionGEQ(3, 1);
#endif
gl_legacy_mode.ForceSet (value, CVAR_Bool);
if (!isGLES && ogl_IsVersionGEQ(3, 0) == 0)
{
Printf("OpenGL acceleration requires at least OpenGL 3.0. No Acceleration will be used.\n");

View file

@ -614,7 +614,7 @@ void gl_ParseBrightmap(FScanner &sc, int deflump)
if (lumpnum != -1)
{
if (iwad && Wads.GetLumpFile(lumpnum) <= FWadCollection::IWAD_FILENUM) useme = true;
if (iwad && Wads.GetLumpFile(lumpnum) <= Wads.GetIwadNum()) useme = true;
if (thiswad && Wads.GetLumpFile(lumpnum) == deflump) useme = true;
}
if (!useme) return;

View file

@ -213,7 +213,7 @@ void FSavegameManager::ReadSaveStrings()
// old, incompatible savegame. List as not usable.
oldVer = true;
}
else if (iwad.CompareNoCase(Wads.GetWadName(FWadCollection::IWAD_FILENUM)) == 0)
else if (iwad.CompareNoCase(Wads.GetWadName(Wads.GetIwadNum())) == 0)
{
missing = !G_CheckSaveGameWads(arc, false);
}

View file

@ -65,7 +65,7 @@
CVAR (Float, mouse_sensitivity, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, show_messages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE)
CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR (Int, m_showinputgrid, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, m_blockcontrollers, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)

View file

@ -1014,7 +1014,7 @@ void M_ParseMenuDefs()
atterm( DeinitMenus);
DeinitMenus();
int IWADMenu = Wads.CheckNumForName("MENUDEF", ns_global, FWadCollection::IWAD_FILENUM);
int IWADMenu = Wads.CheckNumForName("MENUDEF", ns_global, Wads.GetIwadNum());
while ((lump = Wads.FindLump ("MENUDEF", &lastlump)) != -1)
{
@ -1386,6 +1386,7 @@ static void InitMusicMenus()
DMenuDescriptor **gusmenu = MenuDescriptors.CheckKey("GusConfigMenu");
DMenuDescriptor **timiditymenu = MenuDescriptors.CheckKey("TimidityExeMenu");
DMenuDescriptor **wildmidimenu = MenuDescriptors.CheckKey("WildMidiConfigMenu");
DMenuDescriptor **timiditycfgmenu = MenuDescriptors.CheckKey("TimidityConfigMenu");
DMenuDescriptor **fluidmenu = MenuDescriptors.CheckKey("FluidPatchsetMenu");
const char *key, *value;
@ -1426,6 +1427,11 @@ static void InitMusicMenus()
auto it = CreateOptionMenuItemCommand(key, FStringf("wildmidi_config %s", NicePath(value).GetChars()), true);
static_cast<DOptionMenuDescriptor*>(*wildmidimenu)->mItems.Push(it);
}
if (timiditycfgmenu != nullptr)
{
auto it = CreateOptionMenuItemCommand(key, FStringf("timidity_config \"%s\"", NicePath(value).GetChars()), true);
static_cast<DOptionMenuDescriptor*>(*timiditycfgmenu)->mItems.Push(it);
}
}
}
}
@ -1437,6 +1443,8 @@ static void InitMusicMenus()
if (it != nullptr) d->mItems.Delete(d->mItems.Find(it));
it = d->GetItem("WildMidiConfigMenu");
if (it != nullptr) d->mItems.Delete(d->mItems.Find(it));
it = d->GetItem("TimidityConfigMenu");
if (it != nullptr) d->mItems.Delete(d->mItems.Find(it));
}
#ifdef _WIN32 // Different Timidity paths only make sense if they can be stored in arbitrary paths with local configs (i.e. not if things are done the Linux way)
if (GameConfig->SetSection("TimidityExes"))

View file

@ -876,8 +876,7 @@ void P_Spawn3DFloors (void)
line.args[4]=0;
}
}
if (line.args[0] != 0)
P_Set3DFloor(&line, line.args[1]&~8, line.args[2], line.args[3]);
P_Set3DFloor(&line, line.args[1]&~8, line.args[2], line.args[3]);
break;
default:

View file

@ -1843,7 +1843,9 @@ static bool DoUseInv (AActor *actor, PClassActor *info)
AInventory *item = actor->FindInventory (info);
if (item != NULL)
{
if (actor->player == NULL)
player_t* const player = actor->player;
if (nullptr == player)
{
return actor->UseInventory(item);
}
@ -1853,10 +1855,10 @@ static bool DoUseInv (AActor *actor, PClassActor *info)
bool res;
// Bypass CF_TOTALLYFROZEN
cheats = actor->player->cheats;
actor->player->cheats &= ~CF_TOTALLYFROZEN;
cheats = player->cheats;
player->cheats &= ~CF_TOTALLYFROZEN;
res = actor->UseInventory(item);
actor->player->cheats |= (cheats & CF_TOTALLYFROZEN);
player->cheats |= (cheats & CF_TOTALLYFROZEN);
return res;
}
}
@ -3437,7 +3439,7 @@ void FBehavior::StartTypedScripts (uint16_t type, AActor *activator, bool always
{
DLevelScript *runningScript = P_GetScriptGoing (activator, NULL, ptr->Number,
ptr, this, &arg1, 1, always ? ACS_ALWAYS : 0);
if (runNow)
if (nullptr != runningScript && runNow)
{
runningScript->RunScript ();
}
@ -8765,7 +8767,10 @@ scriptwait:
if (pcd == PCD_ENDPRINTBOLD || screen == NULL ||
screen->CheckLocalView (consoleplayer))
{
C_MidPrint (activefont, work);
if (pcd == PCD_ENDPRINTBOLD && (gameinfo.correctprintbold || (level.flags2 & LEVEL2_HEXENHACK)))
C_MidPrintBold(activefont, work);
else
C_MidPrint (activefont, work);
}
STRINGBUILDER_FINISH(work);
}

View file

@ -979,6 +979,15 @@ DEFINE_ACTION_FUNCTION(AActor, A_StopSound)
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_SoundVolume)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT(channel);
PARAM_FLOAT(volume);
S_ChangeSoundVolume(self, channel, static_cast<float>(volume));
return 0;
}
//==========================================================================
//
// These come from a time when DECORATE constants did not exist yet and
@ -5904,9 +5913,9 @@ static void DoKill(AActor *killtarget, AActor *inflictor, AActor *source, FName
{
int dmgFlags = DMG_NO_ARMOR | DMG_NO_FACTOR;
if (KILS_FOILINVUL)
if (flags & KILS_FOILINVUL)
dmgFlags |= DMG_FOILINVUL;
if (KILS_FOILBUDDHA)
if (flags & KILS_FOILBUDDHA)
dmgFlags |= DMG_FOILBUDDHA;
if ((killtarget->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES))

View file

@ -36,6 +36,7 @@ struct FCheckPosition
TMap<AActor*, bool> LastRipped;
bool DoRipping;
bool portalstep;
int portalgroup;
int PushTime;

View file

@ -161,6 +161,7 @@ DEFINE_FIELD_X(FCheckPosition, FCheckPosition, ceilingline);
DEFINE_FIELD_X(FCheckPosition, FCheckPosition, stepthing);
DEFINE_FIELD_X(FCheckPosition, FCheckPosition, DoRipping);
DEFINE_FIELD_X(FCheckPosition, FCheckPosition, portalstep);
DEFINE_FIELD_X(FCheckPosition, FCheckPosition, portalgroup);
DEFINE_FIELD_X(FCheckPosition, FCheckPosition, PushTime);
//==========================================================================
@ -288,6 +289,7 @@ static bool PIT_FindFloorCeiling(FMultiBlockLinesIterator &mit, FMultiBlockLines
}
}
// If we are stepping through a portal the line's opening must be checked, regardless of the NOFLOOR flag
if (!(flags & FFCF_NOFLOOR))
{
if (open.bottom > tmf.floorz)
@ -905,6 +907,7 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec
tm.floorpic = cres.line->sidedef[0]->GetTexture(side_t::mid);
tm.floorterrain = 0;
tm.portalstep = true;
tm.portalgroup = cres.line->frontsector->GetOppositePortalGroup(sector_t::ceiling);
return true;
}
}
@ -999,6 +1002,7 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec
{
// Actor is stepping through a portal.
tm.portalstep = true;
tm.portalgroup = tm.thing->Sector->GetOppositePortalGroup(sector_t::ceiling);
return true;
}
}
@ -1051,7 +1055,8 @@ bool PIT_CheckLine(FMultiBlockLinesIterator &mit, FMultiBlockLinesIterator::Chec
}
}
if (!(cres.portalflags & FFCF_NOFLOOR))
// If we are stepping through a portal the line's opening must be checked, regardless of the NOFLOOR flag
if (!(cres.portalflags & FFCF_NOFLOOR) || (tm.portalstep && open.bottomsec->PortalGroup == tm.portalgroup))
{
if (open.bottom > tm.floorz)
{
@ -6699,7 +6704,8 @@ bool P_ActivateThingSpecial(AActor * thing, AActor * trigger, bool death)
false, thing->args[0], thing->args[1], thing->args[2], thing->args[3], thing->args[4]);
// Clears the special if it was run on thing's death or if flag is set.
if (death || (thing->activationtype & THINGSPEC_ClearSpecial && res)) thing->special = 0;
// Note that Hexen originally did not clear the special which some original maps depend on (e.g. the bell in HEXDD.)
if ((death && !(level.flags2 & LEVEL2_HEXENHACK)) || (thing->activationtype & THINGSPEC_ClearSpecial && res)) thing->special = 0;
}
// Returns the result

View file

@ -346,6 +346,7 @@ DEFINE_FIELD(AActor, Conversation)
DEFINE_FIELD(AActor, DecalGenerator)
DEFINE_FIELD(AActor, fountaincolor)
DEFINE_FIELD(AActor, CameraHeight)
DEFINE_FIELD(AActor, CameraFOV)
DEFINE_FIELD(AActor, RadiusDamageFactor)
DEFINE_FIELD(AActor, SelfDamageFactor)
DEFINE_FIELD(AActor, StealthAlpha)
@ -520,6 +521,7 @@ void AActor::Serialize(FSerializer &arc)
A("spriterotation", SpriteRotation)
("alternative", alternative)
A("cameraheight", CameraHeight)
A("camerafov", CameraFOV)
A("tag", Tag)
A("visiblestartangle",VisibleStartAngle)
A("visibleendangle",VisibleEndAngle)
@ -4036,6 +4038,14 @@ void AActor::CheckPortalTransition(bool islinked)
if (islinked && moved) LinkToWorld(&ctx);
}
DEFINE_ACTION_FUNCTION(AActor, CheckPortalTransition)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_BOOL_DEF(linked);
self->CheckPortalTransition(linked);
return 0;
}
//
// P_MobjThinker
//
@ -4069,10 +4079,6 @@ void AActor::Tick ()
return;
}
// This is necessary to properly interpolate movement outside this function
// like from an ActorMover
ClearInterpolation();
if (flags5 & MF5_NOINTERACTION)
{
// only do the minimally necessary things here to save time:
@ -4107,18 +4113,22 @@ void AActor::Tick ()
}
else
{
AInventory * item = Inventory;
// Handle powerup effects here so that the order is controlled
// by the order in the inventory, not the order in the thinker table
while (item != NULL && item->Owner == this)
if (!player || !(player->cheats & CF_PREDICTING))
{
IFVIRTUALPTR(item, AInventory, DoEffect)
// Handle powerup effects here so that the order is controlled
// by the order in the inventory, not the order in the thinker table
AInventory *item = Inventory;
while (item != NULL && item->Owner == this)
{
VMValue params[1] = { item };
VMCall(func, params, 1, nullptr, 0);
IFVIRTUALPTR(item, AInventory, DoEffect)
{
VMValue params[1] = { item };
VMCall(func, params, 1, nullptr, 0);
}
item = item->Inventory;
}
item = item->Inventory;
}
if (flags & MF_UNMORPHED)
@ -6741,6 +6751,7 @@ bool P_CheckMissileSpawn (AActor* th, double maxdist)
newpos = th->Vec3Offset(newpos);
th->SetXYZ(newpos);
th->Sector = P_PointInSector(th->Pos());
FCheckPosition tm(!!(th->flags2 & MF2_RIP));

View file

@ -236,7 +236,7 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in)
FLinePortal *lport = li->getPortal();
if (open.range == 0 && open.portalflags == 0 && (lport == NULL || lport->mType != PORTT_LINKED)) // quick test for totally closed doors (must be delayed if portal checks are needed, though)
if (open.range == 0 && open.portalflags == 0 && (lport == nullptr || lport->mType != PORTT_LINKED)) // quick test for totally closed doors (must be delayed if portal checks are needed, though)
return false; // stop
if (in->frac == 0)
@ -284,7 +284,7 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in)
portals.Push({ in->frac, topslope, bottomslope, sector_t::floor, backsec->GetOppositePortalGroup(sector_t::floor) });
}
}
if (lport)
if (lport != nullptr && lport->mDestination != nullptr)
{
portals.Push({ in->frac, topslope, bottomslope, portaldir, lport->mDestination->frontsector->PortalGroup });
return false;

View file

@ -596,12 +596,22 @@ void P_GiveSecret(AActor *actor, bool printmessage, bool playsound, int sectornu
DEFINE_ACTION_FUNCTION(AActor, GiveSecret)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_BOOL(printmessage);
PARAM_BOOL(playsound);
PARAM_BOOL_DEF(printmessage);
PARAM_BOOL_DEF(playsound);
P_GiveSecret(self, printmessage, playsound, -1);
return 0;
}
DEFINE_ACTION_FUNCTION(FLevelLocals, GiveSecret)
{
PARAM_PROLOGUE;
PARAM_OBJECT(activator, AActor);
PARAM_BOOL_DEF(printmessage);
PARAM_BOOL_DEF(playsound);
P_GiveSecret(activator, printmessage, playsound, -1);
return 0;
}
//============================================================================
//
// P_PlayerOnSpecialFlat

View file

@ -154,7 +154,7 @@ void FStateLabels::Destroy ()
if (Labels[i].Children != NULL)
{
Labels[i].Children->Destroy();
free(Labels[i].Children); // These are malloc'd, not new'd!
M_Free(Labels[i].Children); // These are malloc'd, not new'd!
Labels[i].Children = NULL;
}
}
@ -336,7 +336,7 @@ static bool VerifyJumpTarget(PClassActor *cls, FState *CallingState, int index)
FState *FStateLabelStorage::GetState(int pos, PClassActor *cls, bool exact)
{
if (pos > 0x10000000)
if (pos >= 0x10000000)
{
return cls? cls->FindState(ENamedName(pos - 0x10000000)) : nullptr;
}
@ -1142,4 +1142,4 @@ DEFINE_ACTION_FUNCTION(FState, ValidateSpriteFrame)
{
PARAM_SELF_STRUCT_PROLOGUE(FState);
ACTION_RETURN_BOOL(self->Frame < sprites[self->sprite].numframes);
}
}

View file

@ -40,6 +40,7 @@
#include "p_spec.h"
#include "g_levellocals.h"
#include "events.h"
#include "actorinlines.h"
extern gamestate_t wipegamestate;
@ -119,6 +120,15 @@ void P_Ticker (void)
P_ResetSightCounters (false);
R_ClearInterpolationPath();
// Reset all actor interpolations for all actors before the current thinking turn so that indirect actor movement gets properly interpolated.
TThinkerIterator<AActor> it;
AActor *ac;
while ((ac = it.Next()))
{
ac->ClearInterpolation();
}
// Since things will be moving, it's okay to interpolate them in the renderer.
r_NoInterpolate = false;

View file

@ -614,7 +614,7 @@ void player_t::SetFOV(float fov)
{
Net_WriteByte(DEM_MYFOV);
}
Net_WriteByte((uint8_t)clamp<float>(fov, 5.f, 179.f));
Net_WriteFloat(clamp<float>(fov, 5.f, 179.f));
}
}
@ -962,7 +962,7 @@ void APlayerPawn::BeginPlay ()
int wadnorm = Wads.GetLumpFile(spritenorm);
int wadcrouch = Wads.GetLumpFile(spritenorm);
if (wadnorm > FWadCollection::IWAD_FILENUM && wadcrouch <= FWadCollection::IWAD_FILENUM)
if (wadnorm > Wads.GetIwadNum() && wadcrouch <= Wads.GetIwadNum())
{
// Question: Add an option / disable crouching or do what?
crouchsprite = 0;

View file

@ -1,5 +1,5 @@
/*
** Triangle drawers
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -38,13 +38,13 @@
/////////////////////////////////////////////////////////////////////////////
PolySubsectorGBuffer *PolySubsectorGBuffer::Instance()
PolyZBuffer *PolyZBuffer::Instance()
{
static PolySubsectorGBuffer buffer;
static PolyZBuffer buffer;
return &buffer;
}
void PolySubsectorGBuffer::Resize(int newwidth, int newheight)
void PolyZBuffer::Resize(int newwidth, int newheight)
{
width = newwidth;
height = newheight;

View file

@ -1,5 +1,5 @@
/*
** Frame buffers
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -26,21 +26,21 @@
struct TriVertex;
class PolySubsectorGBuffer
class PolyZBuffer
{
public:
static PolySubsectorGBuffer *Instance();
static PolyZBuffer *Instance();
void Resize(int newwidth, int newheight);
int Width() const { return width; }
int Height() const { return height; }
int BlockWidth() const { return (width + 7) / 8; }
int BlockHeight() const { return (height + 7) / 8; }
uint32_t *Values() { return values.data(); }
float *Values() { return values.data(); }
private:
int width;
int height;
std::vector<uint32_t> values;
std::vector<float> values;
};
class PolyStencilBuffer

View file

@ -1,5 +1,5 @@
/*
** Triangle drawers
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -36,14 +36,7 @@
#include "swrenderer/r_swcolormaps.h"
#include "poly_draw_args.h"
#include "swrenderer/viewport/r_viewport.h"
void PolyDrawArgs::SetClipPlane(const PolyClipPlane &plane)
{
mClipPlane[0] = plane.A;
mClipPlane[1] = plane.B;
mClipPlane[2] = plane.C;
mClipPlane[3] = plane.D;
}
#include "polyrenderer/poly_renderthread.h"
void PolyDrawArgs::SetTexture(const uint8_t *texels, int width, int height)
{
@ -133,12 +126,12 @@ void PolyDrawArgs::SetColor(uint32_t bgra, uint8_t palindex)
}
}
void PolyDrawArgs::DrawArray(const TriVertex *vertices, int vcount, PolyDrawMode mode)
void PolyDrawArgs::DrawArray(PolyRenderThread *thread, const TriVertex *vertices, int vcount, PolyDrawMode mode)
{
mVertices = vertices;
mVertexCount = vcount;
mDrawMode = mode;
PolyRenderer::Instance()->DrawQueue->Push<DrawPolyTrianglesCommand>(*this, PolyTriangleDrawer::is_mirror());
thread->DrawQueue->Push<DrawPolyTrianglesCommand>(*this, PolyTriangleDrawer::is_mirror());
}
void PolyDrawArgs::SetStyle(const FRenderStyle &renderstyle, double alpha, uint32_t fillcolor, uint32_t translationID, FTexture *tex, bool fullbright)
@ -281,7 +274,7 @@ void RectDrawArgs::SetColor(uint32_t bgra, uint8_t palindex)
}
}
void RectDrawArgs::Draw(double x0, double x1, double y0, double y1, double u0, double u1, double v0, double v1)
void RectDrawArgs::Draw(PolyRenderThread *thread, double x0, double x1, double y0, double y1, double u0, double u1, double v0, double v1)
{
mX0 = (float)x0;
mX1 = (float)x1;
@ -291,7 +284,7 @@ void RectDrawArgs::Draw(double x0, double x1, double y0, double y1, double u0, d
mU1 = (float)u1;
mV0 = (float)v0;
mV1 = (float)v1;
PolyRenderer::Instance()->DrawQueue->Push<DrawRectCommand>(*this);
thread->DrawQueue->Push<DrawRectCommand>(*this);
}
void RectDrawArgs::SetStyle(const FRenderStyle &renderstyle, double alpha, uint32_t fillcolor, uint32_t translationID, FTexture *tex, bool fullbright)

View file

@ -1,5 +1,5 @@
/*
** Triangle drawers
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -26,6 +26,7 @@
#include "r_data/colormaps.h"
#include "screen_triangle.h"
class PolyRenderThread;
class FTexture;
struct TriMatrix;
@ -48,26 +49,25 @@ public:
class PolyDrawArgs
{
public:
void SetClipPlane(const PolyClipPlane &plane);
void SetClipPlane(int index, const PolyClipPlane &plane) { mClipPlane[index] = plane; }
void SetTexture(const uint8_t *texels, int width, int height);
void SetTexture(FTexture *texture);
void SetTexture(FTexture *texture, uint32_t translationID, bool forcePal = false);
void SetLight(FSWColormap *basecolormap, uint32_t lightlevel, double globVis, bool fixed);
void SetSubsectorDepth(uint32_t subsectorDepth) { mSubsectorDepth = subsectorDepth; }
void SetSubsectorDepthTest(bool enable) { mSubsectorTest = enable; }
void SetDepthTest(bool enable) { mDepthTest = enable; }
void SetStencilTestValue(uint8_t stencilTestValue) { mStencilTestValue = stencilTestValue; }
void SetWriteColor(bool enable) { mWriteColor = enable; }
void SetWriteStencil(bool enable, uint8_t stencilWriteValue = 0) { mWriteStencil = enable; mStencilWriteValue = stencilWriteValue; }
void SetWriteSubsectorDepth(bool enable) { mWriteSubsector = enable; }
void SetWriteDepth(bool enable) { mWriteDepth = enable; }
void SetFaceCullCCW(bool counterclockwise) { mFaceCullCCW = counterclockwise; }
void SetStyle(TriBlendMode blendmode, double srcalpha = 1.0, double destalpha = 1.0) { mBlendMode = blendmode; mSrcAlpha = (uint32_t)(srcalpha * 256.0 + 0.5); mDestAlpha = (uint32_t)(destalpha * 256.0 + 0.5); }
void SetStyle(const FRenderStyle &renderstyle, double alpha, uint32_t fillcolor, uint32_t translationID, FTexture *texture, bool fullbright);
void SetTransform(const TriMatrix *objectToClip) { mObjectToClip = objectToClip; }
void SetColor(uint32_t bgra, uint8_t palindex);
void DrawArray(const TriVertex *vertices, int vcount, PolyDrawMode mode = PolyDrawMode::Triangles);
void DrawArray(PolyRenderThread *thread, const TriVertex *vertices, int vcount, PolyDrawMode mode = PolyDrawMode::Triangles);
const TriMatrix *ObjectToClip() const { return mObjectToClip; }
const float *ClipPlane() const { return mClipPlane; }
const PolyClipPlane &ClipPlane(int index) const { return mClipPlane[index]; }
const TriVertex *Vertices() const { return mVertices; }
int VertexCount() const { return mVertexCount; }
@ -85,9 +85,8 @@ public:
uint8_t StencilTestValue() const { return mStencilTestValue; }
uint8_t StencilWriteValue() const { return mStencilWriteValue; }
bool SubsectorTest() const { return mSubsectorTest; }
bool WriteSubsector() const { return mWriteSubsector; }
uint32_t SubsectorDepth() const { return mSubsectorDepth; }
bool DepthTest() const { return mDepthTest; }
bool WriteDepth() const { return mWriteDepth; }
TriBlendMode BlendMode() const { return mBlendMode; }
uint32_t Color() const { return mColor; }
@ -117,10 +116,10 @@ private:
int mVertexCount = 0;
PolyDrawMode mDrawMode = PolyDrawMode::Triangles;
bool mFaceCullCCW = false;
bool mSubsectorTest = false;
bool mDepthTest = false;
bool mWriteStencil = true;
bool mWriteColor = true;
bool mWriteSubsector = true;
bool mWriteDepth = true;
const uint8_t *mTexturePixels = nullptr;
int mTextureWidth = 0;
int mTextureHeight = 0;
@ -128,10 +127,9 @@ private:
uint8_t mStencilTestValue = 0;
uint8_t mStencilWriteValue = 0;
const uint8_t *mColormaps = nullptr;
float mClipPlane[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
PolyClipPlane mClipPlane[3];
TriBlendMode mBlendMode = TriBlendMode::FillOpaque;
uint32_t mLight = 0;
uint32_t mSubsectorDepth = 0;
uint32_t mColor = 0;
uint32_t mSrcAlpha = 0;
uint32_t mDestAlpha = 0;
@ -160,7 +158,7 @@ public:
void SetStyle(TriBlendMode blendmode, double srcalpha = 1.0, double destalpha = 1.0) { mBlendMode = blendmode; mSrcAlpha = (uint32_t)(srcalpha * 256.0 + 0.5); mDestAlpha = (uint32_t)(destalpha * 256.0 + 0.5); }
void SetStyle(const FRenderStyle &renderstyle, double alpha, uint32_t fillcolor, uint32_t translationID, FTexture *texture, bool fullbright);
void SetColor(uint32_t bgra, uint8_t palindex);
void Draw(double x0, double x1, double y0, double y1, double u0, double u1, double v0, double v1);
void Draw(PolyRenderThread *thread, double x0, double x1, double y0, double y1, double u0, double u1, double v0, double v1);
const uint8_t *TexturePixels() const { return mTexturePixels; }
int TextureWidth() const { return mTextureWidth; }

View file

@ -1,5 +1,5 @@
/*
** Projected triangle drawer
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -42,7 +42,7 @@ namespace TriScreenDrawerModes
FORCEINLINE unsigned int Sample32(int32_t u, int32_t v, const uint32_t *texPixels, int texWidth, int texHeight, uint32_t oneU, uint32_t oneV, uint32_t color, const uint32_t *translation)
{
uint32_t texel;
if (SamplerT::Mode == (int)Samplers::Shaded || SamplerT::Mode == (int)Samplers::Stencil || SamplerT::Mode == (int)Samplers::Fill || SamplerT::Mode == (int)Samplers::Fuzz)
if (SamplerT::Mode == (int)Samplers::Shaded || SamplerT::Mode == (int)Samplers::Stencil || SamplerT::Mode == (int)Samplers::Fill || SamplerT::Mode == (int)Samplers::Fuzz || SamplerT::Mode == (int)Samplers::FogBoundary)
{
return color;
}
@ -426,6 +426,7 @@ private:
bgcolor = 0;
// Sample fgcolor
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[ix];
unsigned int ifgcolor = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
unsigned int ifgshade = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
posU += stepU;
@ -508,6 +509,7 @@ private:
bgcolor = 0;
// Sample fgcolor
if (SamplerT::Mode == (int)Samplers::FogBoundary && (mask0 & (1 << 31))) color = dest[x];
unsigned int ifgcolor = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
unsigned int ifgshade = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
posU += stepU;
@ -590,6 +592,7 @@ private:
bgcolor = 0;
// Sample fgcolor
if (SamplerT::Mode == (int)Samplers::FogBoundary && (mask1 & (1 << 31))) color = dest[x];
unsigned int ifgcolor = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
unsigned int ifgshade = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
posU += stepU;
@ -765,6 +768,7 @@ private:
bgcolor = 0;
// Sample fgcolor
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = *dest;
unsigned int ifgcolor = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
unsigned int ifgshade = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
posU += stepU;

View file

@ -1,5 +1,5 @@
/*
** Projected triangle drawer
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -30,7 +30,7 @@ namespace TriScreenDrawerModes
FORCEINLINE unsigned int VECTORCALL Sample32(int32_t u, int32_t v, const uint32_t *texPixels, int texWidth, int texHeight, uint32_t oneU, uint32_t oneV, uint32_t color, const uint32_t *translation)
{
uint32_t texel;
if (SamplerT::Mode == (int)Samplers::Shaded || SamplerT::Mode == (int)Samplers::Stencil || SamplerT::Mode == (int)Samplers::Fill || SamplerT::Mode == (int)Samplers::Fuzz)
if (SamplerT::Mode == (int)Samplers::Shaded || SamplerT::Mode == (int)Samplers::Stencil || SamplerT::Mode == (int)Samplers::Fill || SamplerT::Mode == (int)Samplers::Fuzz || SamplerT::Mode == (int)Samplers::FogBoundary)
{
return color;
}
@ -430,11 +430,13 @@ private:
// Sample fgcolor
unsigned int ifgcolor[2], ifgshade[2];
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[ix * 2];
ifgcolor[0] = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
ifgshade[0] = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
posU += stepU;
posV += stepV;
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[ix * 2 + 1];
ifgcolor[1] = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
ifgshade[1] = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
posU += stepU;
@ -517,11 +519,13 @@ private:
// Sample fgcolor
unsigned int ifgcolor[2], ifgshade[2];
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[x * 2];
ifgcolor[0] = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
ifgshade[0] = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
posU += stepU;
posV += stepV;
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[x * 2 + 1];
ifgcolor[1] = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
ifgshade[1] = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
posU += stepU;
@ -606,11 +610,13 @@ private:
// Sample fgcolor
unsigned int ifgcolor[2], ifgshade[2];
if (SamplerT::Mode == (int)Samplers::FogBoundary && (mask1 & (1 << 31))) color = dest[x * 2];
ifgcolor[0] = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
ifgshade[0] = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
posU += stepU;
posV += stepV;
if (SamplerT::Mode == (int)Samplers::FogBoundary && (mask1 & (1 << 30))) color = dest[x * 2 + 1];
ifgcolor[1] = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
ifgshade[1] = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
posU += stepU;
@ -780,10 +786,12 @@ private:
// Sample fgcolor
unsigned int ifgcolor[2], ifgshade[2];
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[0];
ifgcolor[0] = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
ifgshade[0] = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
posU += stepU;
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = dest[1];
ifgcolor[1] = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
ifgshade[1] = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
posU += stepU;
@ -809,6 +817,7 @@ private:
// Sample fgcolor
unsigned int ifgcolor[2], ifgshade[2];
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = *dest;
ifgcolor[0] = Sample32<SamplerT, FilterModeT>(posU, posV, texPixels, texWidth, texHeight, oneU, oneV, color, translation);
ifgshade[0] = SampleShade32<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
ifgcolor[1] = 0;

View file

@ -1,5 +1,5 @@
/*
** Projected triangle drawer
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -30,7 +30,7 @@ namespace TriScreenDrawerModes
FORCEINLINE unsigned int Sample8(int32_t u, int32_t v, const uint8_t *texPixels, int texWidth, int texHeight, uint32_t color, const uint8_t *translation)
{
uint8_t texel;
if (SamplerT::Mode == (int)Samplers::Shaded || SamplerT::Mode == (int)Samplers::Stencil || SamplerT::Mode == (int)Samplers::Fill || SamplerT::Mode == (int)Samplers::Fuzz)
if (SamplerT::Mode == (int)Samplers::Shaded || SamplerT::Mode == (int)Samplers::Stencil || SamplerT::Mode == (int)Samplers::Fill || SamplerT::Mode == (int)Samplers::Fuzz || SamplerT::Mode == (int)Samplers::FogBoundary)
{
return color;
}
@ -290,6 +290,7 @@ public:
{
int lightshade = lightpos >> 8;
uint8_t bgcolor = dest[ix];
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = bgcolor;
uint8_t fgcolor = Sample8<SamplerT>(posU, posV, texPixels, texWidth, texHeight, color, translation);
uint32_t fgshade = SampleShade8<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
if (SamplerT::Mode == (int)Samplers::Fuzz) lightshade = 256;
@ -339,6 +340,7 @@ public:
{
int lightshade = lightpos >> 8;
uint8_t bgcolor = dest[x];
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = bgcolor;
uint8_t fgcolor = Sample8<SamplerT>(posU, posV, texPixels, texWidth, texHeight, color, translation);
uint32_t fgshade = SampleShade8<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
if (SamplerT::Mode == (int)Samplers::Fuzz) lightshade = 256;
@ -390,6 +392,7 @@ public:
{
int lightshade = lightpos >> 8;
uint8_t bgcolor = dest[x];
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = bgcolor;
uint8_t fgcolor = Sample8<SamplerT>(posU, posV, texPixels, texWidth, texHeight, color, translation);
uint32_t fgshade = SampleShade8<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
if (SamplerT::Mode == (int)Samplers::Fuzz) lightshade = 256;
@ -473,6 +476,7 @@ public:
for (int i = 0; i < count; i++)
{
uint8_t bgcolor = *dest;
if (SamplerT::Mode == (int)Samplers::FogBoundary) color = bgcolor;
uint8_t fgcolor = Sample8<SamplerT>(posU, posV, texPixels, texWidth, texHeight, color, translation);
uint32_t fgshade = SampleShade8<SamplerT>(posU, posV, texPixels, texWidth, texHeight, fuzzpos);
*dest = ShadeAndBlend8<BlendT>(fgcolor, bgcolor, fgshade, lightshade, colormaps, srcalpha, destalpha);

View file

@ -1,5 +1,5 @@
/*
** Triangle drawers
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -99,7 +99,7 @@ void PolyTriangleDrawer::draw_arrays(const PolyDrawArgs &drawargs, WorkerThreadD
args.stencilPitch = PolyStencilBuffer::Instance()->BlockWidth();
args.stencilValues = PolyStencilBuffer::Instance()->Values();
args.stencilMasks = PolyStencilBuffer::Instance()->Masks();
args.subsectorGBuffer = PolySubsectorGBuffer::Instance()->Values();
args.zbuffer = PolyZBuffer::Instance()->Values();
bool ccw = drawargs.FaceCullCCW();
const TriVertex *vinput = drawargs.Vertices();
@ -111,28 +111,28 @@ void PolyTriangleDrawer::draw_arrays(const PolyDrawArgs &drawargs, WorkerThreadD
for (int i = 0; i < vcount / 3; i++)
{
for (int j = 0; j < 3; j++)
vert[j] = shade_vertex(*drawargs.ObjectToClip(), drawargs.ClipPlane(), *(vinput++));
vert[j] = shade_vertex(drawargs, *(vinput++));
draw_shaded_triangle(vert, ccw, &args, thread);
}
}
else if (drawargs.DrawMode() == PolyDrawMode::TriangleFan)
{
vert[0] = shade_vertex(*drawargs.ObjectToClip(), drawargs.ClipPlane(), *(vinput++));
vert[1] = shade_vertex(*drawargs.ObjectToClip(), drawargs.ClipPlane(), *(vinput++));
vert[0] = shade_vertex(drawargs, *(vinput++));
vert[1] = shade_vertex(drawargs, *(vinput++));
for (int i = 2; i < vcount; i++)
{
vert[2] = shade_vertex(*drawargs.ObjectToClip(), drawargs.ClipPlane(), *(vinput++));
vert[2] = shade_vertex(drawargs, *(vinput++));
draw_shaded_triangle(vert, ccw, &args, thread);
vert[1] = vert[2];
}
}
else // TriangleDrawMode::TriangleStrip
{
vert[0] = shade_vertex(*drawargs.ObjectToClip(), drawargs.ClipPlane(), *(vinput++));
vert[1] = shade_vertex(*drawargs.ObjectToClip(), drawargs.ClipPlane(), *(vinput++));
vert[0] = shade_vertex(drawargs, *(vinput++));
vert[1] = shade_vertex(drawargs, *(vinput++));
for (int i = 2; i < vcount; i++)
{
vert[2] = shade_vertex(*drawargs.ObjectToClip(), drawargs.ClipPlane(), *(vinput++));
vert[2] = shade_vertex(drawargs, *(vinput++));
draw_shaded_triangle(vert, ccw, &args, thread);
vert[0] = vert[1];
vert[1] = vert[2];
@ -141,13 +141,20 @@ void PolyTriangleDrawer::draw_arrays(const PolyDrawArgs &drawargs, WorkerThreadD
}
}
ShadedTriVertex PolyTriangleDrawer::shade_vertex(const TriMatrix &objectToClip, const float *clipPlane, const TriVertex &v)
ShadedTriVertex PolyTriangleDrawer::shade_vertex(const PolyDrawArgs &drawargs, const TriVertex &v)
{
// Apply transform to get clip coordinates:
ShadedTriVertex sv = objectToClip * v;
const TriMatrix &objectToClip = *drawargs.ObjectToClip();
// Calculate gl_ClipDistance[0]
sv.clipDistance0 = v.x * clipPlane[0] + v.y * clipPlane[1] + v.z * clipPlane[2] + v.w * clipPlane[3];
// Apply transform to get clip coordinates:
ShadedTriVertex sv;
sv.position = objectToClip * v;
// Calculate gl_ClipDistance[i]
for (int i = 0; i < 3; i++)
{
const auto &clipPlane = drawargs.ClipPlane(i);
sv.clipDistance[i] = v.x * clipPlane.A + v.y * clipPlane.B + v.z * clipPlane.C + v.w * clipPlane.D;
}
return sv;
}
@ -155,12 +162,12 @@ ShadedTriVertex PolyTriangleDrawer::shade_vertex(const TriMatrix &objectToClip,
bool PolyTriangleDrawer::is_degenerate(const ShadedTriVertex *vert)
{
// A degenerate triangle has a zero cross product for two of its sides.
float ax = vert[1].x - vert[0].x;
float ay = vert[1].y - vert[0].y;
float az = vert[1].w - vert[0].w;
float bx = vert[2].x - vert[0].x;
float by = vert[2].y - vert[0].y;
float bz = vert[2].w - vert[0].w;
float ax = vert[1].position.x - vert[0].position.x;
float ay = vert[1].position.y - vert[0].position.y;
float az = vert[1].position.w - vert[0].position.w;
float bx = vert[2].position.x - vert[0].position.x;
float by = vert[2].position.y - vert[0].position.y;
float bz = vert[2].position.w - vert[0].position.w;
float crossx = ay * bz - az * by;
float crossy = az * bx - ax * bz;
float crossz = ax * by - ay * bx;
@ -274,22 +281,25 @@ int PolyTriangleDrawer::clipedge(const ShadedTriVertex *verts, TriVertex *clippe
// -v.w <= v.z <= v.w
// halfspace clip distances
static const int numclipdistances = 7;
static const int numclipdistances = 9;
#ifdef NO_SSE
float clipdistance[numclipdistances * 3];
bool needsclipping = false;
float *clipd = clipdistance;
for (int i = 0; i < 3; i++)
{
const auto &v = verts[i];
const auto &v = verts[i].position;
clipd[0] = v.x + v.w;
clipd[1] = v.w - v.x;
clipd[2] = v.y + v.w;
clipd[3] = v.w - v.y;
clipd[4] = v.z + v.w;
clipd[5] = v.w - v.z;
clipd[6] = v.clipDistance0;
needsclipping = needsclipping || clipd[0] < 0.0f || clipd[1] < 0.0f || clipd[2] < 0.0f || clipd[3] < 0.0f || clipd[4] < 0.0f || clipd[5] < 0.0f || clipd[6] < 0.0f;
clipd[6] = verts[i].clipDistance[0];
clipd[7] = verts[i].clipDistance[1];
clipd[8] = verts[i].clipDistance[2];
for (int j = 0; j < 9; j++)
needsclipping = needsclipping || clipd[i];
clipd += numclipdistances;
}
@ -298,14 +308,14 @@ int PolyTriangleDrawer::clipedge(const ShadedTriVertex *verts, TriVertex *clippe
{
for (int i = 0; i < 3; i++)
{
memcpy(clippedvert + i, verts + i, sizeof(TriVertex));
memcpy(clippedvert + i, &verts[i].position, sizeof(TriVertex));
}
return 3;
}
#else
__m128 mx = _mm_loadu_ps(&verts[0].x);
__m128 my = _mm_loadu_ps(&verts[1].x);
__m128 mz = _mm_loadu_ps(&verts[2].x);
__m128 mx = _mm_loadu_ps(&verts[0].position.x);
__m128 my = _mm_loadu_ps(&verts[1].position.x);
__m128 mz = _mm_loadu_ps(&verts[2].position.x);
__m128 mw = _mm_setzero_ps();
_MM_TRANSPOSE4_PS(mx, my, mz, mw);
__m128 clipd0 = _mm_add_ps(mx, mw);
@ -314,7 +324,9 @@ int PolyTriangleDrawer::clipedge(const ShadedTriVertex *verts, TriVertex *clippe
__m128 clipd3 = _mm_sub_ps(mw, my);
__m128 clipd4 = _mm_add_ps(mz, mw);
__m128 clipd5 = _mm_sub_ps(mw, mz);
__m128 clipd6 = _mm_setr_ps(verts[0].clipDistance0, verts[1].clipDistance0, verts[2].clipDistance0, 0.0f);
__m128 clipd6 = _mm_setr_ps(verts[0].clipDistance[0], verts[1].clipDistance[0], verts[2].clipDistance[0], 0.0f);
__m128 clipd7 = _mm_setr_ps(verts[0].clipDistance[1], verts[1].clipDistance[1], verts[2].clipDistance[1], 0.0f);
__m128 clipd8 = _mm_setr_ps(verts[0].clipDistance[2], verts[1].clipDistance[2], verts[2].clipDistance[2], 0.0f);
__m128 mneedsclipping = _mm_cmplt_ps(clipd0, _mm_setzero_ps());
mneedsclipping = _mm_or_ps(mneedsclipping, _mm_cmplt_ps(clipd1, _mm_setzero_ps()));
mneedsclipping = _mm_or_ps(mneedsclipping, _mm_cmplt_ps(clipd2, _mm_setzero_ps()));
@ -322,11 +334,13 @@ int PolyTriangleDrawer::clipedge(const ShadedTriVertex *verts, TriVertex *clippe
mneedsclipping = _mm_or_ps(mneedsclipping, _mm_cmplt_ps(clipd4, _mm_setzero_ps()));
mneedsclipping = _mm_or_ps(mneedsclipping, _mm_cmplt_ps(clipd5, _mm_setzero_ps()));
mneedsclipping = _mm_or_ps(mneedsclipping, _mm_cmplt_ps(clipd6, _mm_setzero_ps()));
mneedsclipping = _mm_or_ps(mneedsclipping, _mm_cmplt_ps(clipd7, _mm_setzero_ps()));
mneedsclipping = _mm_or_ps(mneedsclipping, _mm_cmplt_ps(clipd8, _mm_setzero_ps()));
if (_mm_movemask_ps(mneedsclipping) == 0)
{
for (int i = 0; i < 3; i++)
{
memcpy(clippedvert + i, verts + i, sizeof(TriVertex));
memcpy(clippedvert + i, &verts[i].position, sizeof(TriVertex));
}
return 3;
}
@ -338,6 +352,8 @@ int PolyTriangleDrawer::clipedge(const ShadedTriVertex *verts, TriVertex *clippe
_mm_storeu_ps(clipdistance + 16, clipd4);
_mm_storeu_ps(clipdistance + 20, clipd5);
_mm_storeu_ps(clipdistance + 24, clipd6);
_mm_storeu_ps(clipdistance + 28, clipd7);
_mm_storeu_ps(clipdistance + 32, clipd8);
#endif
// use barycentric weights while clipping vertices
@ -417,12 +433,12 @@ int PolyTriangleDrawer::clipedge(const ShadedTriVertex *verts, TriVertex *clippe
for (int w = 0; w < 3; w++)
{
float weight = input[i * 3 + w];
v.x += verts[w].x * weight;
v.y += verts[w].y * weight;
v.z += verts[w].z * weight;
v.w += verts[w].w * weight;
v.u += verts[w].u * weight;
v.v += verts[w].v * weight;
v.x += verts[w].position.x * weight;
v.y += verts[w].position.y * weight;
v.z += verts[w].position.z * weight;
v.w += verts[w].position.w * weight;
v.u += verts[w].position.u * weight;
v.v += verts[w].position.v * weight;
}
}
return inputverts;

View file

@ -1,5 +1,5 @@
/*
** Triangle drawers
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -29,9 +29,10 @@
#include "polyrenderer/drawers/poly_buffer.h"
#include "polyrenderer/drawers/poly_draw_args.h"
struct ShadedTriVertex : public TriVertex
struct ShadedTriVertex
{
float clipDistance0;
TriVertex position;
float clipDistance[3];
};
typedef void(*PolyDrawFuncPtr)(const TriDrawTriangleArgs *, WorkerThreadData *);
@ -44,7 +45,7 @@ public:
static bool is_mirror();
private:
static ShadedTriVertex shade_vertex(const TriMatrix &objectToClip, const float *clipPlane, const TriVertex &v);
static ShadedTriVertex shade_vertex(const PolyDrawArgs &drawargs, const TriVertex &v);
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);

View file

@ -1,5 +1,5 @@
/*
** Triangle drawers
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -47,10 +47,22 @@
class TriangleBlock
{
public:
TriangleBlock(const TriDrawTriangleArgs *args);
void Loop(const TriDrawTriangleArgs *args, WorkerThreadData *thread);
TriangleBlock(const TriDrawTriangleArgs *args, WorkerThreadData *thread);
void Render();
private:
void RenderSubdivide(int x0, int y0, int x1, int y1);
enum class CoverageModes { Full, Partial };
struct CoverageFull { static const int Mode = (int)CoverageModes::Full; };
struct CoveragePartial { static const int Mode = (int)CoverageModes::Partial; };
template<typename CoverageMode>
void RenderBlock(int x0, int y0, int x1, int y1);
const TriDrawTriangleArgs *args;
WorkerThreadData *thread;
// Block size, standard 8x8 (must be power of two)
static const int q = 8;
@ -76,10 +88,9 @@ private:
int clipright;
int clipbottom;
// Subsector buffer
uint32_t * RESTRICT subsectorGBuffer;
uint32_t subsectorDepth;
int32_t subsectorPitch;
// Depth buffer
float * RESTRICT zbuffer;
int32_t zbufferPitch;
// Triangle bounding block
int minx, miny;
@ -110,16 +121,24 @@ private:
__m128i mDY31;
#endif
enum class CoverageResult
{
full,
partial,
none
};
CoverageResult AreaCoverageTest(int x0, int y0, int x1, int y1);
void CoverageTest();
void StencilEqualTest();
void StencilGreaterEqualTest();
void SubsectorTest();
void DepthTest(const TriDrawTriangleArgs *args);
void ClipTest();
void StencilWrite();
void SubsectorWrite();
void DepthWrite(const TriDrawTriangleArgs *args);
};
TriangleBlock::TriangleBlock(const TriDrawTriangleArgs *args)
TriangleBlock::TriangleBlock(const TriDrawTriangleArgs *args, WorkerThreadData *thread) : args(args), thread(thread)
{
const TriVertex &v1 = *args->v1;
const TriVertex &v2 = *args->v2;
@ -134,9 +153,8 @@ TriangleBlock::TriangleBlock(const TriDrawTriangleArgs *args)
stencilTestValue = args->uniforms->StencilTestValue();
stencilWriteValue = args->uniforms->StencilWriteValue();
subsectorGBuffer = args->subsectorGBuffer;
subsectorDepth = args->uniforms->SubsectorDepth();
subsectorPitch = args->stencilPitch;
zbuffer = args->zbuffer;
zbufferPitch = args->stencilPitch;
// 28.4 fixed-point coordinates
#ifdef NO_SSE
@ -191,9 +209,11 @@ TriangleBlock::TriangleBlock(const TriDrawTriangleArgs *args)
return;
}
// Start in corner of 8x8 block
// Start and end in corner of 8x8 block
minx &= ~(q - 1);
miny &= ~(q - 1);
maxx |= q - 1;
maxy |= q - 1;
// Half-edge constants
C1 = DY12 * X1 - DX12 * Y1;
@ -227,40 +247,94 @@ TriangleBlock::TriangleBlock(const TriDrawTriangleArgs *args)
#endif
}
void TriangleBlock::Loop(const TriDrawTriangleArgs *args, WorkerThreadData *thread)
void TriangleBlock::Render()
{
RenderSubdivide(minx / q, miny / q, (maxx + 1) / q, (maxy + 1) / q);
}
void TriangleBlock::RenderSubdivide(int x0, int y0, int x1, int y1)
{
CoverageResult result = AreaCoverageTest(x0 * q, y0 * q, x1 * q, y1 * q);
if (result == CoverageResult::full)
{
RenderBlock<CoverageFull>(x0 * q, y0 * q, x1 * q, y1 * q);
}
else if (result == CoverageResult::partial)
{
bool doneX = x1 - x0 <= 8;
bool doneY = y1 - y0 <= 8;
if (doneX && doneY)
{
RenderBlock<CoveragePartial>(x0 * q, y0 * q, x1 * q, y1 * q);
}
else
{
int midx = (x0 + x1) >> 1;
int midy = (y0 + y1) >> 1;
if (doneX)
{
RenderSubdivide(x0, y0, x1, midy);
RenderSubdivide(x0, midy, x1, y1);
}
else if (doneY)
{
RenderSubdivide(x0, y0, midx, y1);
RenderSubdivide(midx, y0, x1, y1);
}
else
{
RenderSubdivide(x0, y0, midx, midy);
RenderSubdivide(midx, y0, x1, midy);
RenderSubdivide(x0, midy, midx, y1);
RenderSubdivide(midx, midy, x1, y1);
}
}
}
}
template<typename CoverageModeT>
void TriangleBlock::RenderBlock(int x0, int y0, int x1, int y1)
{
// First block line for this thread
int core = thread->core;
int num_cores = thread->num_cores;
int core_skip = (num_cores - ((miny / q) - core) % num_cores) % num_cores;
int start_miny = miny + core_skip * q;
int core_skip = (num_cores - ((y0 / q) - core) % num_cores) % num_cores;
int start_miny = y0 + core_skip * q;
bool subsectorTest = args->uniforms->SubsectorTest();
bool depthTest = args->uniforms->DepthTest();
bool writeColor = args->uniforms->WriteColor();
bool writeStencil = args->uniforms->WriteStencil();
bool writeSubsector = args->uniforms->WriteSubsector();
bool writeDepth = args->uniforms->WriteDepth();
int bmode = (int)args->uniforms->BlendMode();
auto drawFunc = args->destBgra ? ScreenTriangle::TriDrawers32[bmode] : ScreenTriangle::TriDrawers8[bmode];
// Loop through blocks
for (int y = start_miny; y < maxy; y += q * num_cores)
for (int y = start_miny; y < y1; y += q * num_cores)
{
for (int x = minx; x < maxx; x += q)
for (int x = x0; x < x1; x += q)
{
X = x;
Y = y;
CoverageTest();
if (Mask0 == 0 && Mask1 == 0)
continue;
if (CoverageModeT::Mode == (int)CoverageModes::Full)
{
Mask0 = 0xffffffff;
Mask1 = 0xffffffff;
}
else
{
CoverageTest();
if (Mask0 == 0 && Mask1 == 0)
continue;
}
ClipTest();
if (Mask0 == 0 && Mask1 == 0)
continue;
// To do: make the stencil test use its own flag for comparison mode instead of abusing the subsector test..
if (!subsectorTest)
// To do: make the stencil test use its own flag for comparison mode instead of abusing the depth test..
if (!depthTest)
{
StencilEqualTest();
if (Mask0 == 0 && Mask1 == 0)
@ -272,7 +346,7 @@ void TriangleBlock::Loop(const TriDrawTriangleArgs *args, WorkerThreadData *thre
if (Mask0 == 0 && Mask1 == 0)
continue;
SubsectorTest();
DepthTest(args);
if (Mask0 == 0 && Mask1 == 0)
continue;
}
@ -281,34 +355,54 @@ void TriangleBlock::Loop(const TriDrawTriangleArgs *args, WorkerThreadData *thre
drawFunc(X, Y, Mask0, Mask1, args);
if (writeStencil)
StencilWrite();
if (writeSubsector)
SubsectorWrite();
if (writeDepth)
DepthWrite(args);
}
}
}
#ifdef NO_SSE
void TriangleBlock::SubsectorTest()
void TriangleBlock::DepthTest(const TriDrawTriangleArgs *args)
{
int block = (X >> 3) + (Y >> 3) * subsectorPitch;
uint32_t *subsector = subsectorGBuffer + block * 64;
int block = (X >> 3) + (Y >> 3) * zbufferPitch;
float *depth = zbuffer + block * 64;
const TriVertex &v1 = *args->v1;
float stepXW = args->gradientX.W;
float stepYW = args->gradientY.W;
float posYW = v1.w + stepXW * (X - v1.x) + stepYW * (Y - v1.y);
uint32_t mask0 = 0;
uint32_t mask1 = 0;
for (int i = 0; i < 32; i++)
for (int iy = 0; iy < 4; iy++)
{
bool covered = *subsector >= subsectorDepth;
mask0 <<= 1;
mask0 |= (uint32_t)covered;
subsector++;
float posXW = posYW;
for (int ix = 0; ix < 8; ix++)
{
bool covered = *depth <= posXW;
mask0 <<= 1;
mask0 |= (uint32_t)covered;
depth++;
posXW += stepXW;
}
posYW += stepYW;
}
for (int i = 0; i < 32; i++)
for (int iy = 0; iy < 4; iy++)
{
bool covered = *subsector >= subsectorDepth;
mask1 <<= 1;
mask1 |= (uint32_t)covered;
subsector++;
float posXW = posYW;
for (int ix = 0; ix < 8; ix++)
{
bool covered = *depth <= posXW;
mask1 <<= 1;
mask1 |= (uint32_t)covered;
depth++;
posXW += stepXW;
}
posYW += stepYW;
}
Mask0 = Mask0 & mask0;
@ -317,26 +411,50 @@ void TriangleBlock::SubsectorTest()
#else
void TriangleBlock::SubsectorTest()
void TriangleBlock::DepthTest(const TriDrawTriangleArgs *args)
{
int block = (X >> 3) + (Y >> 3) * subsectorPitch;
uint32_t *subsector = subsectorGBuffer + block * 64;
int block = (X >> 3) + (Y >> 3) * zbufferPitch;
float *depth = zbuffer + block * 64;
const TriVertex &v1 = *args->v1;
float stepXW = args->gradientX.W;
float stepYW = args->gradientY.W;
float posYW = v1.w + stepXW * (X - v1.x) + stepYW * (Y - v1.y);
__m128 mposYW = _mm_setr_ps(posYW, posYW + stepXW, posYW + stepXW + stepXW, posYW + stepXW + stepXW + stepXW);
__m128 mstepXW = _mm_set1_ps(stepXW * 4.0f);
__m128 mstepYW = _mm_set1_ps(stepYW);
uint32_t mask0 = 0;
uint32_t mask1 = 0;
__m128i msubsectorDepth = _mm_set1_epi32(subsectorDepth);
__m128i mnotxor = _mm_set1_epi32(0xffffffff);
for (int iy = 0; iy < 8; iy++)
for (int iy = 0; iy < 4; iy++)
{
mask0 <<= 4;
mask0 |= _mm_movemask_ps(_mm_castsi128_ps(_mm_shuffle_epi32(_mm_xor_si128(_mm_cmplt_epi32(_mm_loadu_si128((const __m128i *)subsector), msubsectorDepth), mnotxor), _MM_SHUFFLE(0, 1, 2, 3))));
subsector += 4;
__m128 mposXW = mposYW;
for (int ix = 0; ix < 2; ix++)
{
__m128 covered = _mm_cmplt_ps(_mm_loadu_ps(depth), mposXW);
mask0 <<= 4;
mask0 |= _mm_movemask_ps(_mm_shuffle_ps(covered, covered, _MM_SHUFFLE(0, 1, 2, 3)));
depth += 4;
mposXW = _mm_add_ps(mposXW, mstepXW);
}
mposYW = _mm_add_ps(mposYW, mstepYW);
}
for (int iy = 0; iy < 8; iy++)
for (int iy = 0; iy < 4; iy++)
{
mask1 <<= 4;
mask1 |= _mm_movemask_ps(_mm_castsi128_ps(_mm_shuffle_epi32(_mm_xor_si128(_mm_cmplt_epi32(_mm_loadu_si128((const __m128i *)subsector), msubsectorDepth), mnotxor), _MM_SHUFFLE(0, 1, 2, 3))));
subsector += 4;
__m128 mposXW = mposYW;
for (int ix = 0; ix < 2; ix++)
{
__m128 covered = _mm_cmplt_ps(_mm_loadu_ps(depth), mposXW);
mask1 <<= 4;
mask1 |= _mm_movemask_ps(_mm_shuffle_ps(covered, covered, _MM_SHUFFLE(0, 1, 2, 3)));
depth += 4;
mposXW = _mm_add_ps(mposXW, mstepXW);
}
mposYW = _mm_add_ps(mposYW, mstepYW);
}
Mask0 = Mask0 & mask0;
@ -535,6 +653,47 @@ void TriangleBlock::StencilGreaterEqualTest()
}
}
TriangleBlock::CoverageResult TriangleBlock::AreaCoverageTest(int x0, int y0, int x1, int y1)
{
// Corners of block
x0 = x0 << 4;
x1 = (x1 - 1) << 4;
y0 = y0 << 4;
y1 = (y1 - 1) << 4;
// Evaluate half-space functions
bool a00 = C1 + DX12 * y0 - DY12 * x0 > 0;
bool a10 = C1 + DX12 * y0 - DY12 * x1 > 0;
bool a01 = C1 + DX12 * y1 - DY12 * x0 > 0;
bool a11 = C1 + DX12 * y1 - DY12 * x1 > 0;
int a = (a00 << 0) | (a10 << 1) | (a01 << 2) | (a11 << 3);
bool b00 = C2 + DX23 * y0 - DY23 * x0 > 0;
bool b10 = C2 + DX23 * y0 - DY23 * x1 > 0;
bool b01 = C2 + DX23 * y1 - DY23 * x0 > 0;
bool b11 = C2 + DX23 * y1 - DY23 * x1 > 0;
int b = (b00 << 0) | (b10 << 1) | (b01 << 2) | (b11 << 3);
bool c00 = C3 + DX31 * y0 - DY31 * x0 > 0;
bool c10 = C3 + DX31 * y0 - DY31 * x1 > 0;
bool c01 = C3 + DX31 * y1 - DY31 * x0 > 0;
bool c11 = C3 + DX31 * y1 - DY31 * x1 > 0;
int c = (c00 << 0) | (c10 << 1) | (c01 << 2) | (c11 << 3);
if (a == 0 || b == 0 || c == 0) // Skip block when outside an edge
{
return CoverageResult::none;
}
else if (a == 0xf && b == 0xf && c == 0xf) // Accept whole block when totally covered
{
return CoverageResult::full;
}
else // Partially covered block
{
return CoverageResult::partial;
}
}
#ifdef NO_SSE
void TriangleBlock::CoverageTest()
@ -798,65 +957,91 @@ void TriangleBlock::StencilWrite()
#ifdef NO_SSE
void TriangleBlock::SubsectorWrite()
void TriangleBlock::DepthWrite(const TriDrawTriangleArgs *args)
{
int block = (X >> 3) + (Y >> 3) * subsectorPitch;
uint32_t *subsector = subsectorGBuffer + block * 64;
int block = (X >> 3) + (Y >> 3) * zbufferPitch;
float *depth = zbuffer + block * 64;
const TriVertex &v1 = *args->v1;
float stepXW = args->gradientX.W;
float stepYW = args->gradientY.W;
float posYW = v1.w + stepXW * (X - v1.x) + stepYW * (Y - v1.y);
if (Mask0 == 0xffffffff && Mask1 == 0xffffffff)
{
for (int i = 0; i < 64; i++)
for (int iy = 0; iy < 8; iy++)
{
*(subsector++) = subsectorDepth;
float posXW = posYW;
for (int ix = 0; ix < 8; ix++)
{
*(depth++) = posXW;
posXW += stepXW;
}
posYW += stepYW;
}
}
else
{
uint32_t mask0 = Mask0;
uint32_t mask1 = Mask1;
for (int i = 0; i < 32; i++)
for (int iy = 0; iy < 4; iy++)
{
if (mask0 & (1 << 31))
*subsector = subsectorDepth;
mask0 <<= 1;
subsector++;
float posXW = posYW;
for (int ix = 0; ix < 8; ix++)
{
if (mask0 & (1 << 31))
*depth = posXW;
posXW += stepXW;
mask0 <<= 1;
depth++;
}
posYW += stepYW;
}
for (int i = 0; i < 32; i++)
for (int iy = 0; iy < 4; iy++)
{
if (mask1 & (1 << 31))
*subsector = subsectorDepth;
mask1 <<= 1;
subsector++;
float posXW = posYW;
for (int ix = 0; ix < 8; ix++)
{
if (mask1 & (1 << 31))
*depth = posXW;
posXW += stepXW;
mask1 <<= 1;
depth++;
}
posYW += stepYW;
}
}
}
#else
void TriangleBlock::SubsectorWrite()
void TriangleBlock::DepthWrite(const TriDrawTriangleArgs *args)
{
int block = (X >> 3) + (Y >> 3) * subsectorPitch;
uint32_t *subsector = subsectorGBuffer + block * 64;
__m128i msubsectorDepth = _mm_set1_epi32(subsectorDepth);
int block = (X >> 3) + (Y >> 3) * zbufferPitch;
float *depth = zbuffer + block * 64;
const TriVertex &v1 = *args->v1;
float stepXW = args->gradientX.W;
float stepYW = args->gradientY.W;
float posYW = v1.w + stepXW * (X - v1.x) + stepYW * (Y - v1.y);
__m128 mposYW = _mm_setr_ps(posYW, posYW + stepXW, posYW + stepXW + stepXW, posYW + stepXW + stepXW + stepXW);
__m128 mstepXW = _mm_set1_ps(stepXW * 4.0f);
__m128 mstepYW = _mm_set1_ps(stepYW);
if (Mask0 == 0xffffffff && Mask1 == 0xffffffff)
{
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth); subsector += 4;
_mm_storeu_si128((__m128i*)subsector, msubsectorDepth);
for (int iy = 0; iy < 8; iy++)
{
__m128 mposXW = mposYW;
_mm_storeu_ps(depth, mposXW); depth += 4; mposXW = _mm_add_ps(mposXW, mstepXW);
_mm_storeu_ps(depth, mposXW); depth += 4;
mposYW = _mm_add_ps(mposYW, mstepYW);
}
}
else
{
@ -866,34 +1051,251 @@ void TriangleBlock::SubsectorWrite()
__m128i mmask0 = _mm_set1_epi32(Mask0);
__m128i mmask1 = _mm_set1_epi32(Mask1);
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask0, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask0 = _mm_slli_epi32(mmask0, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask0, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask0 = _mm_slli_epi32(mmask0, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask0, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask0 = _mm_slli_epi32(mmask0, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask0, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask0 = _mm_slli_epi32(mmask0, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask0, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask0 = _mm_slli_epi32(mmask0, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask0, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask0 = _mm_slli_epi32(mmask0, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask0, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask0 = _mm_slli_epi32(mmask0, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask0, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); subsector += 4;
for (int iy = 0; iy < 4; iy++)
{
__m128 mposXW = mposYW;
_mm_maskmoveu_si128(_mm_castps_si128(mposXW), _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask0, topfour), _mm_setzero_si128()), mxormask), (char*)depth); mmask0 = _mm_slli_epi32(mmask0, 4); depth += 4; mposXW = _mm_add_ps(mposXW, mstepXW);
_mm_maskmoveu_si128(_mm_castps_si128(mposXW), _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask0, topfour), _mm_setzero_si128()), mxormask), (char*)depth); mmask0 = _mm_slli_epi32(mmask0, 4); depth += 4;
mposYW = _mm_add_ps(mposYW, mstepYW);
}
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask1, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask1 = _mm_slli_epi32(mmask1, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask1, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask1 = _mm_slli_epi32(mmask1, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask1, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask1 = _mm_slli_epi32(mmask1, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask1, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask1 = _mm_slli_epi32(mmask1, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask1, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask1 = _mm_slli_epi32(mmask1, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask1, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask1 = _mm_slli_epi32(mmask1, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask1, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); mmask1 = _mm_slli_epi32(mmask1, 4); subsector += 4;
_mm_maskmoveu_si128(msubsectorDepth, _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask1, topfour), _mm_setzero_si128()), mxormask), (char*)subsector); subsector += 4;
for (int iy = 0; iy < 4; iy++)
{
__m128 mposXW = mposYW;
_mm_maskmoveu_si128(_mm_castps_si128(mposXW), _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask1, topfour), _mm_setzero_si128()), mxormask), (char*)depth); mmask1 = _mm_slli_epi32(mmask1, 4); depth += 4; mposXW = _mm_add_ps(mposXW, mstepXW);
_mm_maskmoveu_si128(_mm_castps_si128(mposXW), _mm_xor_si128(_mm_cmpeq_epi32(_mm_and_si128(mmask1, topfour), _mm_setzero_si128()), mxormask), (char*)depth); mmask1 = _mm_slli_epi32(mmask1, 4); depth += 4;
mposYW = _mm_add_ps(mposYW, mstepYW);
}
}
}
#endif
#if 1
void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, WorkerThreadData *thread)
{
TriangleBlock block(args);
block.Loop(args, thread);
TriangleBlock block(args, thread);
block.Render();
}
#else
static void SortVertices(const TriDrawTriangleArgs *args, TriVertex **sortedVertices)
{
sortedVertices[0] = args->v1;
sortedVertices[1] = args->v2;
sortedVertices[2] = args->v3;
if (sortedVertices[1]->y < sortedVertices[0]->y)
std::swap(sortedVertices[0], sortedVertices[1]);
if (sortedVertices[2]->y < sortedVertices[0]->y)
std::swap(sortedVertices[0], sortedVertices[2]);
if (sortedVertices[2]->y < sortedVertices[1]->y)
std::swap(sortedVertices[1], sortedVertices[2]);
}
void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, WorkerThreadData *thread)
{
// Sort vertices by Y position
TriVertex *sortedVertices[3];
SortVertices(args, sortedVertices);
int clipright = args->clipright;
int clipbottom = args->clipbottom;
// Ranges that different triangles edges are active
int topY = (int)(sortedVertices[0]->y + 0.5f);
int midY = (int)(sortedVertices[1]->y + 0.5f);
int bottomY = (int)(sortedVertices[2]->y + 0.5f);
topY = MAX(topY, 0);
midY = clamp(midY, 0, clipbottom);
bottomY = MIN(bottomY, clipbottom);
if (topY >= bottomY)
return;
// Find start/end X positions for each line covered by the triangle:
int leftEdge[MAXHEIGHT];
int rightEdge[MAXHEIGHT];
float longDX = sortedVertices[2]->x - sortedVertices[0]->x;
float longDY = sortedVertices[2]->y - sortedVertices[0]->y;
float longStep = longDX / longDY;
float longPos = sortedVertices[0]->x + longStep * (topY + 0.5f - sortedVertices[0]->y) + 0.5f;
if (topY < midY)
{
float shortDX = sortedVertices[1]->x - sortedVertices[0]->x;
float shortDY = sortedVertices[1]->y - sortedVertices[0]->y;
float shortStep = shortDX / shortDY;
float shortPos = sortedVertices[0]->x + shortStep * (topY + 0.5f - sortedVertices[0]->y) + 0.5f;
for (int y = topY; y < midY; y++)
{
int x0 = (int)shortPos;
int x1 = (int)longPos;
if (x1 < x0) std::swap(x0, x1);
x0 = clamp(x0, 0, clipright);
x1 = clamp(x1, 0, clipright);
leftEdge[y] = x0;
rightEdge[y] = x1;
shortPos += shortStep;
longPos += longStep;
}
}
if (midY < bottomY)
{
float shortDX = sortedVertices[2]->x - sortedVertices[1]->x;
float shortDY = sortedVertices[2]->y - sortedVertices[1]->y;
float shortStep = shortDX / shortDY;
float shortPos = sortedVertices[1]->x + shortStep * (midY + 0.5f - sortedVertices[1]->y) + 0.5f;
for (int y = midY; y < bottomY; y++)
{
int x0 = (int)shortPos;
int x1 = (int)longPos;
if (x1 < x0) std::swap(x0, x1);
x0 = clamp(x0, 0, clipright);
x1 = clamp(x1, 0, clipright);
leftEdge[y] = x0;
rightEdge[y] = x1;
shortPos += shortStep;
longPos += longStep;
}
}
// Make variables local so the compiler can optimize without worrying about pointer aliasing
bool depthTest = args->uniforms->DepthTest();
bool writeColor = args->uniforms->WriteColor();
bool writeStencil = args->uniforms->WriteStencil();
bool writeDepth = args->uniforms->WriteDepth();
uint8_t stencilTestValue = args->uniforms->StencilTestValue();
uint8_t stencilWriteValue = args->uniforms->StencilWriteValue();
int bmode = (int)args->uniforms->BlendMode();
auto drawFunc = args->destBgra ? ScreenTriangle::TriDrawers32[bmode] : ScreenTriangle::TriDrawers8[bmode];
uint8_t *dest = args->dest;
uint8_t *stencilbuffer = args->stencilValues;
uint32_t *stencilMasks = args->stencilMasks;
float *zbuffer = args->zbuffer;
int pitch = args->pitch;
int stencilpitch = args->stencilPitch * 8;
int color = ((int)(ptrdiff_t)args->uniforms->TexturePixels()) >> 2;
float v1X = args->v1->x;
float v1Y = args->v1->y;
float v1W = args->v1->w;
float v1U = args->v1->u * v1W;
float v1V = args->v1->v * v1W;
float stepXW = args->gradientX.W;
float stepXU = args->gradientX.U;
float stepXV = args->gradientX.V;
float stepYW = args->gradientY.W;
float stepYU = args->gradientY.U;
float stepYV = args->gradientY.V;
int texWidth = args->uniforms->TextureWidth();
int texHeight = args->uniforms->TextureHeight();
const uint8_t *texPixels = args->uniforms->TexturePixels();
auto colormaps = args->uniforms->BaseColormap();
bool is_fixed_light = args->uniforms->FixedLight();
uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff;
uint32_t light = args->uniforms->Light();
float shade = 2.0f - (light + 12.0f) / 128.0f;
float globVis = args->uniforms->GlobVis() * (1.0f / 32.0f);
light += light >> 7; // 255 -> 256
// Draw the triangle:
int num_cores = thread->num_cores;
for (int y = topY + thread->skipped_by_thread(topY); y < bottomY; y += num_cores)
{
int x0 = leftEdge[y];
int x1 = rightEdge[y];
uint8_t *destLine = dest + pitch * y;
uint8_t *stencilLine = stencilbuffer + stencilpitch * y;
float *zbufferLine = zbuffer + stencilpitch * y;
if ((stencilMasks[y] & 0xffffff00) == 0xffffff00) // First time we draw a line we have to clear the stencil buffer
{
memset(stencilLine, stencilMasks[y] & 0xff, stencilpitch);
stencilMasks[y] = 0;
}
float posXW = v1W + stepXW * (x0 + (0.5f - v1X)) + stepYW * (y + (0.5f - v1Y));
float posXU = v1U + stepXU * (x0 + (0.5f - v1X)) + stepYU * (y + (0.5f - v1Y));
float posXV = v1V + stepXV * (x0 + (0.5f - v1X)) + stepYV * (y + (0.5f - v1Y));
int x = x0;
while (x < x1)
{
bool processPixel = true;
if (!depthTest) // To do: make the stencil test use its own flag for comparison mode instead of abusing the depth test..
{
processPixel = stencilTestValue == stencilLine[x];
}
else
{
processPixel = stencilTestValue >= stencilLine[x] && zbufferLine[x] <= posXW;
}
if (processPixel) // Pixel is visible (passed stencil and depth tests)
{
if (writeColor)
{
if (texPixels)
{
float rcpW = 0x01000000 / posXW;
int32_t u = (int32_t)(posXU * rcpW);
int32_t v = (int32_t)(posXV * rcpW);
uint32_t texelX = ((((uint32_t)u << 8) >> 16) * texWidth) >> 16;
uint32_t texelY = ((((uint32_t)v << 8) >> 16) * texHeight) >> 16;
uint8_t fgcolor = texPixels[texelX * texHeight + texelY];
fixed_t lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * posXW), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT);
lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask);
int lightshade = lightpos >> 8;
lightshade = ((256 - lightshade) * NUMCOLORMAPS) & 0xffffff00;
uint8_t shadedfg = colormaps[lightshade + fgcolor];
if (fgcolor != 0)
destLine[x] = shadedfg;
}
else
{
destLine[x] = color;
}
}
if (writeStencil)
stencilLine[x] = stencilWriteValue;
if (writeDepth)
zbufferLine[x] = posXW;
}
posXW += stepXW;
posXU += stepXU;
posXV += stepXV;
x++;
}
}
}
#endif
void(*ScreenTriangle::TriDrawers8[])(int, int, uint32_t, uint32_t, const TriDrawTriangleArgs *) =
{
&TriScreenDrawer8<TriScreenDrawerModes::OpaqueBlend, TriScreenDrawerModes::TextureSampler>::Execute, // TextureOpaque
@ -918,7 +1320,8 @@ void(*ScreenTriangle::TriDrawers8[])(int, int, uint32_t, uint32_t, const TriDraw
&TriScreenDrawer8<TriScreenDrawerModes::RevSubClampBlend, TriScreenDrawerModes::FillSampler>::Execute, // FillRevSub
&TriScreenDrawer8<TriScreenDrawerModes::AddSrcColorBlend, TriScreenDrawerModes::FillSampler>::Execute, // FillAddSrcColor
&TriScreenDrawer8<TriScreenDrawerModes::OpaqueBlend, TriScreenDrawerModes::SkycapSampler>::Execute, // Skycap
&TriScreenDrawer8<TriScreenDrawerModes::ShadedBlend, TriScreenDrawerModes::FuzzSampler>::Execute // Fuzz
&TriScreenDrawer8<TriScreenDrawerModes::ShadedBlend, TriScreenDrawerModes::FuzzSampler>::Execute, // Fuzz
&TriScreenDrawer8<TriScreenDrawerModes::OpaqueBlend, TriScreenDrawerModes::FogBoundarySampler>::Execute, // FogBoundary
};
void(*ScreenTriangle::TriDrawers32[])(int, int, uint32_t, uint32_t, const TriDrawTriangleArgs *) =
@ -945,7 +1348,8 @@ void(*ScreenTriangle::TriDrawers32[])(int, int, uint32_t, uint32_t, const TriDra
&TriScreenDrawer32<TriScreenDrawerModes::RevSubClampBlend, TriScreenDrawerModes::FillSampler>::Execute, // FillRevSub
&TriScreenDrawer32<TriScreenDrawerModes::AddSrcColorBlend, TriScreenDrawerModes::FillSampler>::Execute, // FillAddSrcColor
&TriScreenDrawer32<TriScreenDrawerModes::OpaqueBlend, TriScreenDrawerModes::SkycapSampler>::Execute, // Skycap
&TriScreenDrawer32<TriScreenDrawerModes::ShadedBlend, TriScreenDrawerModes::FuzzSampler>::Execute // Fuzz
&TriScreenDrawer32<TriScreenDrawerModes::ShadedBlend, TriScreenDrawerModes::FuzzSampler>::Execute, // Fuzz
&TriScreenDrawer32<TriScreenDrawerModes::OpaqueBlend, TriScreenDrawerModes::FogBoundarySampler>::Execute // FogBoundary
};
void(*ScreenTriangle::RectDrawers8[])(const void *, int, int, int, const RectDrawArgs *, WorkerThreadData *) =
@ -972,7 +1376,8 @@ void(*ScreenTriangle::RectDrawers8[])(const void *, int, int, int, const RectDra
&RectScreenDrawer8<TriScreenDrawerModes::RevSubClampBlend, TriScreenDrawerModes::FillSampler>::Execute, // FillRevSub
&RectScreenDrawer8<TriScreenDrawerModes::AddSrcColorBlend, TriScreenDrawerModes::FillSampler>::Execute, // FillAddSrcColor
&RectScreenDrawer8<TriScreenDrawerModes::OpaqueBlend, TriScreenDrawerModes::SkycapSampler>::Execute, // Skycap
&RectScreenDrawer8<TriScreenDrawerModes::ShadedBlend, TriScreenDrawerModes::FuzzSampler>::Execute // Fuzz
&RectScreenDrawer8<TriScreenDrawerModes::ShadedBlend, TriScreenDrawerModes::FuzzSampler>::Execute, // Fuzz
&RectScreenDrawer8<TriScreenDrawerModes::OpaqueBlend, TriScreenDrawerModes::FogBoundarySampler>::Execute // FogBoundary
};
void(*ScreenTriangle::RectDrawers32[])(const void *, int, int, int, const RectDrawArgs *, WorkerThreadData *) =
@ -999,7 +1404,8 @@ void(*ScreenTriangle::RectDrawers32[])(const void *, int, int, int, const RectDr
&RectScreenDrawer32<TriScreenDrawerModes::RevSubClampBlend, TriScreenDrawerModes::FillSampler>::Execute, // FillRevSub
&RectScreenDrawer32<TriScreenDrawerModes::AddSrcColorBlend, TriScreenDrawerModes::FillSampler>::Execute, // FillAddSrcColor
&RectScreenDrawer32<TriScreenDrawerModes::OpaqueBlend, TriScreenDrawerModes::SkycapSampler>::Execute, // Skycap
&RectScreenDrawer32<TriScreenDrawerModes::ShadedBlend, TriScreenDrawerModes::FuzzSampler>::Execute // Fuzz
&RectScreenDrawer32<TriScreenDrawerModes::ShadedBlend, TriScreenDrawerModes::FuzzSampler>::Execute, // Fuzz
&RectScreenDrawer32<TriScreenDrawerModes::OpaqueBlend, TriScreenDrawerModes::FogBoundarySampler>::Execute, // FogBoundary
};
int ScreenTriangle::FuzzStart = 0;

View file

@ -1,5 +1,5 @@
/*
** Projected triangle drawer
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -32,6 +32,13 @@ struct WorkerThreadData
{
int32_t core;
int32_t num_cores;
// The number of lines to skip to reach the first line to be rendered by this thread
int skipped_by_thread(int first_line)
{
int core_skip = (num_cores - (first_line - core) % num_cores) % num_cores;
return core_skip;
}
};
struct TriVertex
@ -60,7 +67,7 @@ struct TriDrawTriangleArgs
uint8_t *stencilValues;
uint32_t *stencilMasks;
int32_t stencilPitch;
uint32_t *subsectorGBuffer;
float *zbuffer;
const PolyDrawArgs *uniforms;
bool destBgra;
ScreenTriangleStepVariables gradientX;
@ -120,7 +127,8 @@ enum class TriBlendMode
FillRevSub,
FillAddSrcColor,
Skycap,
Fuzz
Fuzz,
FogBoundary
};
class ScreenTriangle
@ -157,7 +165,7 @@ namespace TriScreenDrawerModes
struct SimpleShade { static const int Mode = (int)ShadeMode::Simple; };
struct AdvancedShade { static const int Mode = (int)ShadeMode::Advanced; };
enum class Samplers { Texture, Fill, Shaded, Stencil, Translated, Skycap, Fuzz };
enum class Samplers { Texture, Fill, Shaded, Stencil, Translated, Skycap, Fuzz, FogBoundary };
struct TextureSampler { static const int Mode = (int)Samplers::Texture; };
struct FillSampler { static const int Mode = (int)Samplers::Fill; };
struct ShadedSampler { static const int Mode = (int)Samplers::Shaded; };
@ -165,6 +173,7 @@ namespace TriScreenDrawerModes
struct TranslatedSampler { static const int Mode = (int)Samplers::Translated; };
struct SkycapSampler { static const int Mode = (int)Samplers::Skycap; };
struct FuzzSampler { static const int Mode = (int)Samplers::Fuzz; };
struct FogBoundarySampler { static const int Mode = (int)Samplers::FogBoundary; };
static const int fuzzcolormap[FUZZTABLE] =
{

View file

@ -1,5 +1,5 @@
/*
** Triangle drawers
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -173,14 +173,14 @@ TriMatrix TriMatrix::operator*(const TriMatrix &mult) const
return result;
}
ShadedTriVertex TriMatrix::operator*(TriVertex v) const
TriVertex TriMatrix::operator*(TriVertex v) const
{
#ifdef NO_SSE
float vx = matrix[0 * 4 + 0] * v.x + matrix[1 * 4 + 0] * v.y + matrix[2 * 4 + 0] * v.z + matrix[3 * 4 + 0] * v.w;
float vy = matrix[0 * 4 + 1] * v.x + matrix[1 * 4 + 1] * v.y + matrix[2 * 4 + 1] * v.z + matrix[3 * 4 + 1] * v.w;
float vz = matrix[0 * 4 + 2] * v.x + matrix[1 * 4 + 2] * v.y + matrix[2 * 4 + 2] * v.z + matrix[3 * 4 + 2] * v.w;
float vw = matrix[0 * 4 + 3] * v.x + matrix[1 * 4 + 3] * v.y + matrix[2 * 4 + 3] * v.z + matrix[3 * 4 + 3] * v.w;
ShadedTriVertex sv;
TriVertex sv;
sv.x = vx;
sv.y = vy;
sv.z = vz;
@ -196,7 +196,7 @@ ShadedTriVertex TriMatrix::operator*(TriVertex v) const
m2 = _mm_mul_ps(m2, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(2, 2, 2, 2)));
m3 = _mm_mul_ps(m3, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(3, 3, 3, 3)));
mv = _mm_add_ps(_mm_add_ps(_mm_add_ps(m0, m1), m2), m3);
ShadedTriVertex sv;
TriVertex sv;
_mm_storeu_ps(&sv.x, mv);
#endif
sv.u = v.u;

View file

@ -1,5 +1,5 @@
/*
** Triangle drawers
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -23,7 +23,6 @@
#pragma once
struct TriVertex;
struct ShadedTriVertex;
struct FRenderViewpoint;
struct TriMatrix
@ -40,7 +39,7 @@ struct TriMatrix
//static TriMatrix worldToView(const FRenderViewpoint &viewpoint); // Software renderer world to view space transform
//static TriMatrix viewToClip(double focalTangent, double centerY, double invZtoScale); // Software renderer shearing projection
ShadedTriVertex operator*(TriVertex v) const;
TriVertex operator*(TriVertex v) const;
TriMatrix operator*(const TriMatrix &m) const;
float matrix[16];

View file

@ -1,4 +1,5 @@
#include "poly_renderer.cpp"
#include "poly_renderthread.cpp"
#include "drawers/poly_buffer.cpp"
#include "drawers/poly_draw_args.cpp"
#include "drawers/poly_triangle.cpp"

View file

@ -32,6 +32,7 @@
#include "po_man.h"
#include "st_stuff.h"
#include "g_levellocals.h"
#include "p_effect.h"
#include "polyrenderer/scene/poly_light.h"
#include "swrenderer/scene/r_scene.h"
#include "swrenderer/drawers/r_draw_rgba.h"
@ -53,7 +54,6 @@ PolyRenderer *PolyRenderer::Instance()
PolyRenderer::PolyRenderer()
{
DrawQueue = std::make_shared<DrawerCommandQueue>(&FrameMemory);
}
void PolyRenderer::RenderView(player_t *player)
@ -74,12 +74,11 @@ void PolyRenderer::RenderView(player_t *player)
CameraLight *cameraLight = CameraLight::Instance();
if (cameraLight->ShaderColormap() && RenderTarget->IsBgra() && !(r_shadercolormaps && screen->Accel2D))
{
DrawQueue->Push<ApplySpecialColormapRGBACommand>(cameraLight->ShaderColormap(), screen);
Threads.MainThread()->DrawQueue->Push<ApplySpecialColormapRGBACommand>(cameraLight->ShaderColormap(), screen);
}
DrawerThreads::Execute(DrawQueue);
Threads.MainThread()->FlushDrawQueue();
DrawerThreads::WaitForWorkers();
DrawQueue->Clear();
}
void PolyRenderer::RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines)
@ -97,7 +96,7 @@ void PolyRenderer::RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int
canvas->Lock(true);
RenderActorView(actor, dontmaplines);
DrawerThreads::Execute(DrawQueue);
Threads.MainThread()->FlushDrawQueue();
DrawerThreads::WaitForWorkers();
canvas->Unlock();
@ -140,9 +139,9 @@ void PolyRenderer::RenderActorView(AActor *actor, bool dontmaplines)
SetupPerspectiveMatrix();
MainPortal.SetViewpoint(WorldToClip, PolyClipPlane(0.0f, 0.0f, 0.0f, 1.0f), GetNextStencilValue());
MainPortal.Render(0);
Skydome.Render(WorldToClip);
Skydome.Render(Threads.MainThread(), WorldToClip);
MainPortal.RenderTranslucent(0);
PlayerSprites.Render();
PlayerSprites.Render(Threads.MainThread());
Viewpoint.camera->renderflags = savedflags;
interpolator.RestoreInterpolations ();
@ -157,9 +156,9 @@ void PolyRenderer::RenderRemainingPlayerSprites()
void PolyRenderer::ClearBuffers()
{
FrameMemory.Clear();
Threads.Clear();
PolyStencilBuffer::Instance()->Clear(RenderTarget->GetWidth(), RenderTarget->GetHeight(), 0);
PolySubsectorGBuffer::Instance()->Resize(RenderTarget->GetPitch(), RenderTarget->GetHeight());
PolyZBuffer::Instance()->Resize(RenderTarget->GetPitch(), RenderTarget->GetHeight());
NextStencilValue = 0;
}

View file

@ -33,6 +33,7 @@
#include "scene/poly_sky.h"
#include "scene/poly_light.h"
#include "swrenderer/r_memory.h"
#include "poly_renderthread.h"
class AActor;
class DCanvas;
@ -54,8 +55,7 @@ public:
bool DontMapLines = false;
RenderMemory FrameMemory;
DrawerCommandQueuePtr DrawQueue;
PolyRenderThreads Threads;
DCanvas *RenderTarget = nullptr;
FViewWindow Viewwindow;
FRenderViewpoint Viewpoint;

View file

@ -0,0 +1,264 @@
/*
** 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 <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "m_bbox.h"
#include "i_system.h"
#include "p_lnspec.h"
#include "p_setup.h"
#include "a_sharedglobal.h"
#include "g_level.h"
#include "p_effect.h"
#include "doomstat.h"
#include "r_state.h"
#include "v_palette.h"
#include "r_sky.h"
#include "po_man.h"
#include "r_data/colormaps.h"
#include "poly_renderthread.h"
#include "poly_renderer.h"
#include <mutex>
#ifdef WIN32
void PeekThreadedErrorPane();
#endif
EXTERN_CVAR(Bool, r_scene_multithreaded);
PolyRenderThread::PolyRenderThread(int threadIndex) : MainThread(threadIndex == 0), ThreadIndex(threadIndex)
{
FrameMemory.reset(new RenderMemory());
DrawQueue = std::make_shared<DrawerCommandQueue>(FrameMemory.get());
}
PolyRenderThread::~PolyRenderThread()
{
}
void PolyRenderThread::FlushDrawQueue()
{
DrawerThreads::Execute(DrawQueue);
UsedDrawQueues.push_back(DrawQueue);
DrawQueue.reset();
if (!FreeDrawQueues.empty())
{
DrawQueue = FreeDrawQueues.back();
FreeDrawQueues.pop_back();
}
else
{
DrawQueue = std::make_shared<DrawerCommandQueue>(FrameMemory.get());
}
}
void PolyRenderThread::PrepareTexture(FTexture *texture)
{
if (texture == nullptr)
return;
// Textures may not have loaded/refreshed yet. The shared code doing
// this is not thread safe. By calling GetPixels in a mutex lock we
// make sure that only one thread is loading a texture at any given
// time.
//
// It is critical that this function is called before any direct
// calls to GetPixels for this to work.
static std::mutex loadmutex;
std::unique_lock<std::mutex> lock(loadmutex);
texture->GetPixels();
const FTexture::Span *spans;
texture->GetColumn(0, &spans);
if (PolyRenderer::Instance()->RenderTarget->IsBgra())
{
texture->GetPixelsBgra();
texture->GetColumnBgra(0, &spans);
}
}
void PolyRenderThread::PreparePolyObject(subsector_t *sub)
{
static std::mutex polyobjmutex;
std::unique_lock<std::mutex> lock(polyobjmutex);
if (sub->BSP == nullptr || sub->BSP->bDirty)
{
sub->BuildPolyBSP();
}
}
/////////////////////////////////////////////////////////////////////////////
PolyRenderThreads::PolyRenderThreads()
{
std::unique_ptr<PolyRenderThread> thread(new PolyRenderThread(0));
Threads.push_back(std::move(thread));
}
PolyRenderThreads::~PolyRenderThreads()
{
StopThreads();
}
void PolyRenderThreads::Clear()
{
for (auto &thread : Threads)
{
thread->FrameMemory->Clear();
thread->DrawQueue->Clear();
while (!thread->UsedDrawQueues.empty())
{
auto queue = thread->UsedDrawQueues.back();
thread->UsedDrawQueues.pop_back();
queue->Clear();
thread->FreeDrawQueues.push_back(queue);
}
}
}
void PolyRenderThreads::RenderThreadSlices(int totalcount, std::function<void(PolyRenderThread *)> workerCallback, std::function<void(PolyRenderThread *)> collectCallback)
{
WorkerCallback = workerCallback;
int numThreads = std::thread::hardware_concurrency();
if (numThreads == 0)
numThreads = 4;
if (!r_scene_multithreaded || !r_multithreaded)
numThreads = 1;
if (numThreads != (int)Threads.size())
{
StopThreads();
StartThreads(numThreads);
}
// Setup threads:
std::unique_lock<std::mutex> start_lock(start_mutex);
for (int i = 0; i < numThreads; i++)
{
Threads[i]->Start = totalcount * i / numThreads;
Threads[i]->End = totalcount * (i + 1) / numThreads;
}
run_id++;
start_lock.unlock();
// Notify threads to run
if (Threads.size() > 1)
{
start_condition.notify_all();
}
// Do the main thread ourselves:
RenderThreadSlice(MainThread());
// Wait for everyone to finish:
if (Threads.size() > 1)
{
using namespace std::chrono_literals;
std::unique_lock<std::mutex> end_lock(end_mutex);
finished_threads++;
if (!end_condition.wait_for(end_lock, 5s, [&]() { return finished_threads == Threads.size(); }))
{
#ifdef WIN32
PeekThreadedErrorPane();
#endif
// Invoke the crash reporter so that we can capture the call stack of whatever the hung worker thread is doing
int *threadCrashed = nullptr;
*threadCrashed = 0xdeadbeef;
}
finished_threads = 0;
}
for (int i = 0; i < numThreads; i++)
{
Threads[i]->FlushDrawQueue();
}
WorkerCallback = {};
for (int i = 1; i < numThreads; i++)
{
collectCallback(Threads[i].get());
}
}
void PolyRenderThreads::RenderThreadSlice(PolyRenderThread *thread)
{
WorkerCallback(thread);
}
void PolyRenderThreads::StartThreads(size_t numThreads)
{
while (Threads.size() < (size_t)numThreads)
{
std::unique_ptr<PolyRenderThread> thread(new PolyRenderThread((int)Threads.size()));
auto renderthread = thread.get();
int start_run_id = run_id;
thread->thread = std::thread([=]()
{
int last_run_id = start_run_id;
while (true)
{
// Wait until we are signalled to run:
std::unique_lock<std::mutex> start_lock(start_mutex);
start_condition.wait(start_lock, [&]() { return run_id != last_run_id || shutdown_flag; });
if (shutdown_flag)
break;
last_run_id = run_id;
start_lock.unlock();
RenderThreadSlice(renderthread);
// Notify main thread that we finished:
std::unique_lock<std::mutex> end_lock(end_mutex);
finished_threads++;
end_lock.unlock();
end_condition.notify_all();
}
});
Threads.push_back(std::move(thread));
}
}
void PolyRenderThreads::StopThreads()
{
std::unique_lock<std::mutex> lock(start_mutex);
shutdown_flag = true;
lock.unlock();
start_condition.notify_all();
while (Threads.size() > 1)
{
Threads.back()->thread.join();
Threads.pop_back();
}
lock.lock();
shutdown_flag = false;
}

View file

@ -0,0 +1,91 @@
/*
** 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 <memory>
#include <thread>
#include "swrenderer/r_memory.h"
class DrawerCommandQueue;
typedef std::shared_ptr<DrawerCommandQueue> DrawerCommandQueuePtr;
class RenderMemory;
class PolyRenderThread
{
public:
PolyRenderThread(int threadIndex);
~PolyRenderThread();
void FlushDrawQueue();
int Start = 0;
int End = 0;
bool MainThread = false;
int ThreadIndex = 0;
std::unique_ptr<RenderMemory> FrameMemory;
DrawerCommandQueuePtr DrawQueue;
// Make sure texture can accessed safely
void PrepareTexture(FTexture *texture);
// Setup poly object in a threadsafe manner
void PreparePolyObject(subsector_t *sub);
private:
std::thread thread;
std::vector<DrawerCommandQueuePtr> UsedDrawQueues;
std::vector<DrawerCommandQueuePtr> FreeDrawQueues;
friend class PolyRenderThreads;
};
class PolyRenderThreads
{
public:
PolyRenderThreads();
~PolyRenderThreads();
void Clear();
void RenderThreadSlices(int totalcount, std::function<void(PolyRenderThread *)> workerCallback, std::function<void(PolyRenderThread *)> collectCallback);
PolyRenderThread *MainThread() { return Threads.front().get(); }
int NumThreads() const { return (int)Threads.size(); }
private:
void RenderThreadSlice(PolyRenderThread *thread);
void StartThreads(size_t numThreads);
void StopThreads();
std::function<void(PolyRenderThread *)> WorkerCallback;
std::vector<std::unique_ptr<PolyRenderThread>> Threads;
std::mutex start_mutex;
std::condition_variable start_condition;
bool shutdown_flag = false;
int run_id = 0;
std::mutex end_mutex;
std::condition_variable end_condition;
size_t finished_threads = 0;
};

View file

@ -1,5 +1,5 @@
/*
** Potential visible set (PVS) handling
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -30,7 +30,35 @@
void PolyCull::CullScene(const TriMatrix &worldToClip, const PolyClipPlane &portalClipPlane)
{
ClearSolidSegments();
MarkViewFrustum();
if (level.LevelName != lastLevelName) // Is this the best way to detect a level change?
{
lastLevelName = level.LevelName;
SubsectorDepths.clear();
SubsectorDepths.resize(level.subsectors.Size(), 0xffffffff);
SectorSeen.clear();
SectorSeen.resize(level.sectors.Size());
}
else
{
for (const auto &sub : PvsSectors)
SubsectorDepths[sub->Index()] = 0xffffffff;
SubsectorDepths.resize(level.subsectors.Size(), 0xffffffff);
for (const auto &sector : SeenSectors)
SectorSeen[sector->Index()] = false;
SectorSeen.resize(level.sectors.Size());
}
PvsSectors.clear();
SeenSectors.clear();
NextPvsLineStart = 0;
PvsLineStart.clear();
PvsLineVisible.resize(level.segs.Size());
PortalClipPlane = portalClipPlane;
// Cull front to back
@ -97,35 +125,54 @@ void PolyCull::CullSubsector(subsector_t *sub)
FirstSkyHeight = false;
}
uint32_t subsectorDepth = (uint32_t)PvsSectors.size();
// Mark that we need to render this
PvsSectors.push_back(sub);
PvsLineStart.push_back(NextPvsLineStart);
DVector3 viewpos = PolyRenderer::Instance()->Viewpoint.Pos;
// Update culling info for further bsp clipping
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
if ((line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) && line->backsector == nullptr)
// Skip lines not facing viewer
DVector2 pt1 = line->v1->fPos() - viewpos;
DVector2 pt2 = line->v2->fPos() - viewpos;
if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0)
{
// Skip lines not facing viewer
DVector2 pt1 = line->v1->fPos() - PolyRenderer::Instance()->Viewpoint.Pos;
DVector2 pt2 = line->v2->fPos() - PolyRenderer::Instance()->Viewpoint.Pos;
if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0)
continue;
// Skip line if entirely behind portal clipping plane
if ((PortalClipPlane.A * line->v1->fX() + PortalClipPlane.B * line->v1->fY() + PortalClipPlane.D <= 0.0) ||
(PortalClipPlane.A * line->v2->fX() + PortalClipPlane.B * line->v2->fY() + PortalClipPlane.D <= 0.0))
{
continue;
}
angle_t angle1, angle2;
if (GetAnglesForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), angle1, angle2))
{
MarkSegmentCulled(angle1, angle2);
}
PvsLineVisible[NextPvsLineStart++] = false;
continue;
}
// Skip line if entirely behind portal clipping plane
if ((PortalClipPlane.A * line->v1->fX() + PortalClipPlane.B * line->v1->fY() + PortalClipPlane.D <= 0.0) ||
(PortalClipPlane.A * line->v2->fX() + PortalClipPlane.B * line->v2->fY() + PortalClipPlane.D <= 0.0))
{
PvsLineVisible[NextPvsLineStart++] = false;
continue;
}
angle_t angle1, angle2;
bool lineVisible = GetAnglesForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), angle1, angle2);
if (lineVisible && line->backsector == nullptr)
{
MarkSegmentCulled(angle1, angle2);
}
// Mark if this line was visible
PvsLineVisible[NextPvsLineStart++] = lineVisible;
}
if (!SectorSeen[sub->sector->Index()])
{
SectorSeen[sub->sector->Index()] = true;
SeenSectors.push_back(sub->sector);
}
SubsectorDepths[sub->Index()] = subsectorDepth;
}
void PolyCull::ClearSolidSegments()

View file

@ -1,5 +1,5 @@
/*
** Potential visible set (PVS) handling
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -23,23 +23,27 @@
#pragma once
#include "polyrenderer/drawers/poly_triangle.h"
#include <set>
#include <unordered_map>
class PolyCull
{
public:
void ClearSolidSegments();
void CullScene(const TriMatrix &worldToClip, const PolyClipPlane &portalClipPlane);
bool GetAnglesForLine(double x1, double y1, double x2, double y2, angle_t &angle1, angle_t &angle2) const;
void MarkSegmentCulled(angle_t angle1, angle_t angle2);
bool IsSegmentCulled(angle_t angle1, angle_t angle2) const;
void InvertSegments();
void MarkViewFrustum();
bool IsLineSegVisible(uint32_t subsectorDepth, uint32_t lineIndex)
{
return PvsLineVisible[PvsLineStart[subsectorDepth] + lineIndex];
}
std::vector<subsector_t *> PvsSectors;
double MaxCeilingHeight = 0.0;
double MinFloorHeight = 0.0;
std::vector<sector_t *> SeenSectors;
std::vector<bool> SectorSeen;
std::vector<uint32_t> SubsectorDepths;
static angle_t PointToPseudoAngle(double x, double y);
private:
@ -49,6 +53,13 @@ private:
angle_t Start, End;
};
void ClearSolidSegments();
void MarkViewFrustum();
bool GetAnglesForLine(double x1, double y1, double x2, double y2, angle_t &angle1, angle_t &angle2) const;
bool IsSegmentCulled(angle_t angle1, angle_t angle2) const;
void InvertSegments();
void CullNode(void *node);
void CullSubsector(subsector_t *sub);
int PointOnSide(const DVector2 &pos, const node_t *node);
@ -57,6 +68,10 @@ private:
// Returns true if some part of the bbox might be visible.
bool CheckBBox(float *bspcoord);
void MarkSegmentCulled(angle_t angle1, angle_t angle2);
FString lastLevelName;
std::vector<SolidSegment> SolidSegments;
std::vector<SolidSegment> TempInvertSolidSegments;
const int SolidCullScale = 3000;
@ -64,5 +79,9 @@ private:
PolyClipPlane PortalClipPlane;
std::vector<uint32_t> PvsLineStart;
std::vector<bool> PvsLineVisible;
uint32_t NextPvsLineStart = 0;
static angle_t AngleToPseudo(angle_t ang);
};

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a decal
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -28,10 +28,11 @@
#include "poly_decal.h"
#include "polyrenderer/poly_renderer.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/poly_renderthread.h"
#include "a_sharedglobal.h"
#include "swrenderer/scene/r_scene.h"
void RenderPolyDecal::RenderWallDecals(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, const seg_t *line, uint32_t subsectorDepth, uint32_t stencilValue)
void RenderPolyDecal::RenderWallDecals(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, const seg_t *line, uint32_t stencilValue)
{
if (line->linedef == nullptr && line->sidedef == nullptr)
return;
@ -39,11 +40,11 @@ void RenderPolyDecal::RenderWallDecals(const TriMatrix &worldToClip, const PolyC
for (DBaseDecal *decal = line->sidedef->AttachedDecals; decal != nullptr; decal = decal->WallNext)
{
RenderPolyDecal render;
render.Render(worldToClip, clipPlane, decal, line, subsectorDepth, stencilValue);
render.Render(thread, worldToClip, clipPlane, decal, line, stencilValue);
}
}
void RenderPolyDecal::Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, DBaseDecal *decal, const seg_t *line, uint32_t subsectorDepth, uint32_t stencilValue)
void RenderPolyDecal::Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, DBaseDecal *decal, const seg_t *line, uint32_t stencilValue)
{
if (decal->RenderFlags & RF_INVISIBLE || !viewactive || !decal->PicNum.isValid())
return;
@ -52,103 +53,233 @@ void RenderPolyDecal::Render(const TriMatrix &worldToClip, const PolyClipPlane &
if (tex == nullptr || tex->UseType == FTexture::TEX_Null)
return;
double edge_right = tex->GetWidth();
double edge_left = tex->LeftOffset;
edge_right = (edge_right - edge_left) * decal->ScaleX;
edge_left *= decal->ScaleX;
sector_t *front, *back;
GetDecalSectors(decal, line, &front, &back);
// Calculate unclipped position and UV coordinates
double edge_left = tex->LeftOffset * decal->ScaleX;
double edge_right = (tex->GetWidth() - tex->LeftOffset) * decal->ScaleX;
DVector2 angvec = (line->v2->fPos() - line->v1->fPos()).Unit();
DVector2 normal = { angvec.Y, -angvec.X };
double dcx, dcy;
decal->GetXY(line->sidedef, dcx, dcy);
DVector2 decal_pos = { dcx, dcy };
DVector2 angvec = (line->v2->fPos() - line->v1->fPos()).Unit();
DVector2 decal_pos = DVector2(dcx, dcy) + normal;
DVector2 decal_left = decal_pos - edge_left * angvec;
DVector2 decal_right = decal_pos + edge_right * angvec;
// Determine actor z
double zpos = decal->Z;
sector_t *back = (line->backsector != nullptr) ? line->backsector : line->frontsector;
bool flipTextureX = (decal->RenderFlags & RF_XFLIP) == RF_XFLIP;
double u_left = flipTextureX ? 1.0 : 0.0;
double u_right = flipTextureX ? 1.0 - tex->Scale.X : tex->Scale.X;
double u_unit = (u_right - u_left) / (edge_left + edge_right);
// for 3d-floor segments use the model sector as reference
sector_t *front;
if ((decal->RenderFlags&RF_CLIPMASK) == RF_CLIPMID) front = decal->Sector;
else front = line->frontsector;
double zpos = GetDecalZ(decal, line, front, back);
double spriteHeight = decal->ScaleY / tex->Scale.Y * tex->GetHeight();
double ztop = zpos + spriteHeight - spriteHeight * 0.5;
double zbottom = zpos - spriteHeight * 0.5;
switch (decal->RenderFlags & RF_RELMASK)
double v_top = 0.0;
double v_bottom = tex->Scale.Y;
double v_unit = (v_bottom - v_top) / (zbottom - ztop);
// Clip decal to wall part
double walltopz, wallbottomz;
GetWallZ(decal, line, front, back, walltopz, wallbottomz);
double clip_left_v1 = (decal_left - line->v1->fPos()) | angvec;
double clip_right_v1 = (decal_right - line->v1->fPos()) | angvec;
double clip_left_v2 = (decal_left - line->v2->fPos()) | angvec;
double clip_right_v2 = (decal_right - line->v2->fPos()) | angvec;
if ((clip_left_v1 <= 0.0 && clip_right_v1 <= 0.0) || (clip_left_v2 >= 0.0 && clip_right_v2 >= 0.0))
return;
if (clip_left_v1 < 0.0)
{
default:
zpos = decal->Z;
break;
case RF_RELUPPER:
if (line->linedef->flags & ML_DONTPEGTOP)
zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling);
else
zpos = decal->Z + back->GetPlaneTexZ(sector_t::ceiling);
break;
case RF_RELLOWER:
if (line->linedef->flags & ML_DONTPEGBOTTOM)
zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling);
else
zpos = decal->Z + back->GetPlaneTexZ(sector_t::floor);
break;
case RF_RELMID:
if (line->linedef->flags & ML_DONTPEGBOTTOM)
zpos = decal->Z + front->GetPlaneTexZ(sector_t::floor);
else
zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling);
decal_left -= angvec * clip_left_v1;
u_left -= u_unit * clip_left_v1;
}
if (clip_right_v1 < 0.0)
{
decal_right -= angvec * clip_right_v1;
u_right -= u_unit * clip_right_v1;
}
if (clip_left_v2 > 0.0)
{
decal_left -= angvec * clip_left_v2;
u_left -= u_unit * clip_left_v2;
}
if (clip_right_v2 > 0.0)
{
decal_right -= angvec * clip_right_v2;
u_right -= u_unit * clip_right_v2;
}
DVector2 spriteScale = { decal->ScaleX, decal->ScaleY };
double thingxscalemul = spriteScale.X / tex->Scale.X;
double thingyscalemul = spriteScale.Y / tex->Scale.Y;
double spriteHeight = thingyscalemul * tex->GetHeight();
double clip_top_floor = ztop - wallbottomz;
double clip_bottom_floor = zbottom - wallbottomz;
double clip_top_ceiling = ztop - walltopz;
double clip_bottom_ceiling = zbottom - walltopz;
bool flipTextureX = (decal->RenderFlags & RF_XFLIP) == RF_XFLIP;
if ((clip_top_floor <= 0.0 && clip_bottom_floor <= 0.0) || (clip_top_ceiling >= 0.0 && clip_bottom_ceiling >= 0.0))
return;
DVector2 points[2] = { decal_left, decal_right };
if (clip_top_floor < 0.0)
{
ztop -= clip_top_floor;
v_top -= v_unit * clip_top_floor;
}
if (clip_bottom_floor < 0.0)
{
zbottom -= clip_bottom_floor;
v_bottom -= v_unit * clip_bottom_floor;
}
if (clip_top_ceiling > 0.0)
{
ztop -= clip_top_ceiling;
v_top -= v_unit * clip_top_ceiling;
}
if (clip_bottom_ceiling > 0.0)
{
zbottom -= clip_bottom_ceiling;
v_bottom -= v_unit * clip_bottom_ceiling;
}
TriVertex *vertices = PolyRenderer::Instance()->FrameMemory.AllocMemory<TriVertex>(4);
// Generate vertices for the decal
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(4);
vertices[0].x = (float)decal_left.X;
vertices[0].y = (float)decal_left.Y;
vertices[0].z = (float)ztop;
vertices[0].w = 1.0f;
vertices[0].u = (float)u_left;
vertices[0].v = (float)v_top;
vertices[1].x = (float)decal_right.X;
vertices[1].y = (float)decal_right.Y;
vertices[1].z = (float)ztop;
vertices[1].w = 1.0f;
vertices[1].u = (float)u_right;
vertices[1].v = (float)v_top;
vertices[2].x = (float)decal_right.X;
vertices[2].y = (float)decal_right.Y;
vertices[2].z = (float)zbottom;
vertices[2].w = 1.0f;
vertices[2].u = (float)u_right;
vertices[2].v = (float)v_bottom;
vertices[3].x = (float)decal_left.X;
vertices[3].y = (float)decal_left.Y;
vertices[3].z = (float)zbottom;
vertices[3].w = 1.0f;
vertices[3].u = (float)u_left;
vertices[3].v = (float)v_bottom;
// Light calculations
bool foggy = false;
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
std::pair<float, float> offsets[4] =
{
{ 0.0f, 1.0f },
{ 1.0f, 1.0f },
{ 1.0f, 0.0f },
{ 0.0f, 0.0f },
};
for (int i = 0; i < 4; i++)
{
auto &p = (i == 0 || i == 3) ? points[0] : points[1];
vertices[i].x = (float)p.X;
vertices[i].y = (float)p.Y;
vertices[i].z = (float)(zpos + spriteHeight * offsets[i].second - spriteHeight * 0.5);
vertices[i].w = 1.0f;
vertices[i].u = (float)(offsets[i].first * tex->Scale.X);
vertices[i].v = (float)((1.0f - offsets[i].second) * tex->Scale.Y);
if (flipTextureX)
vertices[i].u = 1.0f - vertices[i].u;
}
bool fullbrightSprite = (decal->RenderFlags & RF_FULLBRIGHT) == RF_FULLBRIGHT;
int lightlevel = fullbrightSprite ? 255 : front->lightlevel + actualextralight;
PolyDrawArgs args;
args.SetLight(GetColorTable(front->Colormap), lightlevel, PolyRenderer::Instance()->Light.WallGlobVis(foggy), fullbrightSprite);
args.SetSubsectorDepth(subsectorDepth);
args.SetColor(0xff000000 | decal->AlphaColor, decal->AlphaColor >> 24);
args.SetStyle(decal->RenderStyle, decal->Alpha, decal->AlphaColor, decal->Translation, tex, false);
args.SetTransform(&worldToClip);
args.SetFaceCullCCW(true);
args.SetStencilTestValue(stencilValue);
args.SetWriteStencil(true, stencilValue);
args.SetClipPlane(clipPlane);
args.SetSubsectorDepthTest(true);
args.SetClipPlane(0, clipPlane);
args.SetDepthTest(true);
args.SetWriteStencil(false);
args.SetWriteSubsectorDepth(false);
args.DrawArray(vertices, 4, PolyDrawMode::TriangleFan);
args.SetWriteDepth(false);
args.DrawArray(thread, vertices, 4, PolyDrawMode::TriangleFan);
}
void RenderPolyDecal::GetDecalSectors(DBaseDecal *decal, const seg_t *line, sector_t **front, sector_t **back)
{
// for 3d-floor segments use the model sector as reference
if ((decal->RenderFlags&RF_CLIPMASK) == RF_CLIPMID)
*front = decal->Sector;
else
*front = line->frontsector;
*back = (line->backsector != nullptr) ? line->backsector : line->frontsector;
}
double RenderPolyDecal::GetDecalZ(DBaseDecal *decal, const seg_t *line, sector_t *front, sector_t *back)
{
switch (decal->RenderFlags & RF_RELMASK)
{
default:
return decal->Z;
case RF_RELUPPER:
if (line->linedef->flags & ML_DONTPEGTOP)
return decal->Z + front->GetPlaneTexZ(sector_t::ceiling);
else
return decal->Z + back->GetPlaneTexZ(sector_t::ceiling);
case RF_RELLOWER:
if (line->linedef->flags & ML_DONTPEGBOTTOM)
return decal->Z + front->GetPlaneTexZ(sector_t::ceiling);
else
return decal->Z + back->GetPlaneTexZ(sector_t::floor);
break;
case RF_RELMID:
if (line->linedef->flags & ML_DONTPEGBOTTOM)
return decal->Z + front->GetPlaneTexZ(sector_t::floor);
else
return decal->Z + front->GetPlaneTexZ(sector_t::ceiling);
}
}
void RenderPolyDecal::GetWallZ(DBaseDecal *decal, const seg_t *line, sector_t *front, sector_t *back, double &walltopz, double &wallbottomz)
{
double frontceilz1 = front->ceilingplane.ZatPoint(line->v1);
double frontfloorz1 = front->floorplane.ZatPoint(line->v1);
double frontceilz2 = front->ceilingplane.ZatPoint(line->v2);
double frontfloorz2 = front->floorplane.ZatPoint(line->v2);
if (back == nullptr)
{
walltopz = MAX(frontceilz1, frontceilz2);
wallbottomz = MIN(frontfloorz1, frontfloorz2);
}
else
{
double backceilz1 = back->ceilingplane.ZatPoint(line->v1);
double backfloorz1 = back->floorplane.ZatPoint(line->v1);
double backceilz2 = back->ceilingplane.ZatPoint(line->v2);
double backfloorz2 = back->floorplane.ZatPoint(line->v2);
double topceilz1 = frontceilz1;
double topceilz2 = frontceilz2;
double topfloorz1 = MAX(MIN(backceilz1, frontceilz1), frontfloorz1);
double topfloorz2 = MAX(MIN(backceilz2, frontceilz2), frontfloorz2);
double bottomceilz1 = MIN(MAX(frontfloorz1, backfloorz1), frontceilz1);
double bottomceilz2 = MIN(MAX(frontfloorz2, backfloorz2), frontceilz2);
double bottomfloorz1 = frontfloorz1;
double bottomfloorz2 = frontfloorz2;
double middleceilz1 = topfloorz1;
double middleceilz2 = topfloorz2;
double middlefloorz1 = MIN(bottomceilz1, middleceilz1);
double middlefloorz2 = MIN(bottomceilz2, middleceilz2);
switch (decal->RenderFlags & RF_RELMASK)
{
default:
walltopz = MAX(frontceilz1, frontceilz2);
wallbottomz = MIN(frontfloorz1, frontfloorz2);
break;
case RF_RELUPPER:
walltopz = MAX(topceilz1, topceilz2);
wallbottomz = MIN(topfloorz1, topfloorz2);
break;
case RF_RELLOWER:
walltopz = MAX(bottomceilz1, bottomceilz2);
wallbottomz = MIN(bottomfloorz1, bottomfloorz2);
break;
case RF_RELMID:
walltopz = MAX(middleceilz1, middleceilz2);
wallbottomz = MIN(middlefloorz1, middlefloorz2);
}
}
}

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a decal
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -27,8 +27,12 @@
class RenderPolyDecal
{
public:
static void RenderWallDecals(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, const seg_t *line, uint32_t subsectorDepth, uint32_t stencilValue);
static void RenderWallDecals(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, const seg_t *line, uint32_t stencilValue);
private:
void Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, DBaseDecal *decal, const seg_t *line, uint32_t subsectorDepth, uint32_t stencilValue);
void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, DBaseDecal *decal, const seg_t *line, uint32_t stencilValue);
void GetDecalSectors(DBaseDecal *decal, const seg_t *line, sector_t **front, sector_t **back);
double GetDecalZ(DBaseDecal *decal, const seg_t *line, sector_t *front, sector_t *back);
void GetWallZ(DBaseDecal *decal, const seg_t *line, sector_t *front, sector_t *back, double &walltopz, double &wallbottomz);
};

View file

@ -1,5 +1,5 @@
/*
** Light calculations
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied

View file

@ -1,5 +1,5 @@
/*
** Light calculations
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied

View file

@ -1,5 +1,5 @@
/*
** Particle drawing
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -28,10 +28,11 @@
#include "poly_particle.h"
#include "polyrenderer/poly_renderer.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/poly_renderthread.h"
EXTERN_CVAR(Int, gl_particles_style)
void RenderPolyParticle::Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, particle_t *particle, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue)
void RenderPolyParticle::Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, particle_t *particle, subsector_t *sub, uint32_t stencilValue)
{
DVector3 pos = particle->Pos;
double psize = particle->size / 8.0;
@ -45,7 +46,7 @@ void RenderPolyParticle::Render(const TriMatrix &worldToClip, const PolyClipPlan
{ pos.X + viewpoint.Sin * psize, pos.Y - viewpoint.Cos * psize }
};
TriVertex *vertices = PolyRenderer::Instance()->FrameMemory.AllocMemory<TriVertex>(4);
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(4);
bool foggy = false;
int actualextralight = foggy ? 0 : viewpoint.extralight << 4;
@ -75,18 +76,17 @@ void RenderPolyParticle::Render(const TriMatrix &worldToClip, const PolyClipPlan
PolyDrawArgs args;
args.SetLight(GetColorTable(sub->sector->Colormap), lightlevel, PolyRenderer::Instance()->Light.ParticleGlobVis(foggy), fullbrightSprite);
args.SetSubsectorDepth(subsectorDepth);
args.SetSubsectorDepthTest(true);
args.SetDepthTest(true);
args.SetColor(particle->color | 0xff000000, particle->color >> 24);
args.SetStyle(TriBlendMode::Shaded, particle->alpha, 1.0 - particle->alpha);
args.SetTransform(&worldToClip);
args.SetFaceCullCCW(true);
args.SetStencilTestValue(stencilValue);
args.SetWriteStencil(false);
args.SetWriteSubsectorDepth(false);
args.SetClipPlane(clipPlane);
args.SetWriteDepth(false);
args.SetClipPlane(0, clipPlane);
args.SetTexture(GetParticleTexture(), ParticleTextureSize, ParticleTextureSize);
args.DrawArray(vertices, 4, PolyDrawMode::TriangleFan);
args.DrawArray(thread, vertices, 4, PolyDrawMode::TriangleFan);
}
uint8_t *RenderPolyParticle::GetParticleTexture()

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a particle
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -28,7 +28,7 @@
class RenderPolyParticle
{
public:
void Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, particle_t *particle, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue);
void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, particle_t *particle, subsector_t *sub, uint32_t stencilValue);
private:
static uint8_t *GetParticleTexture();
@ -39,3 +39,19 @@ private:
ParticleTextureSize = 64
};
};
class PolyTranslucentParticle : public PolyTranslucentObject
{
public:
PolyTranslucentParticle(particle_t *particle, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue) : PolyTranslucentObject(subsectorDepth, 0.0), particle(particle), sub(sub), StencilValue(stencilValue) { }
void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &portalPlane) override
{
RenderPolyParticle spr;
spr.Render(thread, worldToClip, portalPlane, particle, sub, StencilValue + 1);
}
particle_t *particle = nullptr;
subsector_t *sub = nullptr;
uint32_t StencilValue = 0;
};

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a plane (ceiling, floor)
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -30,122 +30,24 @@
#include "polyrenderer/poly_renderer.h"
#include "r_sky.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/poly_renderthread.h"
#include "p_lnspec.h"
EXTERN_CVAR(Int, r_3dfloors)
void RenderPolyPlane::RenderPlanes(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, PolyCull &cull, subsector_t *sub, uint32_t ceilingSubsectorDepth, uint32_t floorSubsectorDepth, uint32_t stencilValue, double skyCeilingHeight, double skyFloorHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals)
void RenderPolyPlane::RenderPlanes(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, subsector_t *sub, uint32_t stencilValue, double skyCeilingHeight, double skyFloorHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals)
{
RenderPolyPlane plane;
if (r_3dfloors)
{
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
auto frontsector = sub->sector;
auto &ffloors = frontsector->e->XFloor.ffloors;
// 3D floor floors
for (int i = 0; i < (int)ffloors.Size(); i++)
{
F3DFloor *fakeFloor = ffloors[i];
if (!(fakeFloor->flags & FF_EXISTS)) continue;
if (!fakeFloor->model) continue;
if (fakeFloor->bottom.plane->isSlope()) continue;
//if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES)))
// R_3D_AddHeight(fakeFloor->top.plane, frontsector);
if (!(fakeFloor->flags & FF_RENDERPLANES)) continue;
if (fakeFloor->alpha == 0) continue;
if (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR) continue;
//fakeFloor->alpha
double fakeHeight = fakeFloor->top.plane->ZatPoint(frontsector->centerspot);
if (fakeHeight < viewpoint.Pos.Z && fakeHeight > frontsector->floorplane.ZatPoint(frontsector->centerspot))
{
plane.Render3DFloor(worldToClip, clipPlane, sub, floorSubsectorDepth, stencilValue, false, fakeFloor);
}
}
// 3D floor ceilings
for (int i = 0; i < (int)ffloors.Size(); i++)
{
F3DFloor *fakeFloor = ffloors[i];
if (!(fakeFloor->flags & FF_EXISTS)) continue;
if (!fakeFloor->model) continue;
if (fakeFloor->top.plane->isSlope()) continue;
//if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES)))
// R_3D_AddHeight(fakeFloor->bottom.plane, frontsector);
if (!(fakeFloor->flags & FF_RENDERPLANES)) continue;
if (fakeFloor->alpha == 0) continue;
if (!(fakeFloor->flags & FF_THISINSIDE) && (fakeFloor->flags & (FF_SWIMMABLE | FF_INVERTSECTOR)) == (FF_SWIMMABLE | FF_INVERTSECTOR)) continue;
//fakeFloor->alpha
double fakeHeight = fakeFloor->bottom.plane->ZatPoint(frontsector->centerspot);
if (fakeHeight > viewpoint.Pos.Z && fakeHeight < frontsector->ceilingplane.ZatPoint(frontsector->centerspot))
{
plane.Render3DFloor(worldToClip, clipPlane, sub, ceilingSubsectorDepth, stencilValue, true, fakeFloor);
}
}
}
plane.Render(worldToClip, clipPlane, cull, sub, ceilingSubsectorDepth, stencilValue, true, skyCeilingHeight, sectorPortals);
plane.Render(worldToClip, clipPlane, cull, sub, floorSubsectorDepth, stencilValue, false, skyFloorHeight, sectorPortals);
}
void RenderPolyPlane::Render3DFloor(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, bool ceiling, F3DFloor *fakeFloor)
{
FTextureID picnum = ceiling ? *fakeFloor->bottom.texture : *fakeFloor->top.texture;
FTexture *tex = TexMan(picnum);
if (tex->UseType == FTexture::TEX_Null)
if (sub->sector->CenterFloor() == sub->sector->CenterCeiling())
return;
PolyCameraLight *cameraLight = PolyCameraLight::Instance();
int lightlevel = 255;
bool foggy = false;
if (cameraLight->FixedLightLevel() < 0 && sub->sector->e->XFloor.lightlist.Size())
{
lightlist_t *light = P_GetPlaneLight(sub->sector, &sub->sector->ceilingplane, false);
//basecolormap = light->extra_colormap;
lightlevel = *light->p_lightlevel;
}
UVTransform xform(ceiling ? fakeFloor->top.model->planes[sector_t::ceiling].xform : fakeFloor->top.model->planes[sector_t::floor].xform, tex);
TriVertex *vertices = PolyRenderer::Instance()->FrameMemory.AllocMemory<TriVertex>(sub->numlines);
if (ceiling)
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
vertices[sub->numlines - 1 - i] = PlaneVertex(line->v1, fakeFloor->bottom.plane->ZatPoint(line->v1), xform);
}
}
else
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
vertices[i] = PlaneVertex(line->v1, fakeFloor->top.plane->ZatPoint(line->v1), xform);
}
}
PolyDrawArgs args;
args.SetLight(GetColorTable(sub->sector->Colormap), lightlevel, PolyRenderer::Instance()->Light.WallGlobVis(foggy), false);
args.SetSubsectorDepth(subsectorDepth);
args.SetTransform(&worldToClip);
args.SetStyle(TriBlendMode::TextureOpaque);
args.SetFaceCullCCW(true);
args.SetStencilTestValue(stencilValue);
args.SetWriteStencil(true, stencilValue + 1);
args.SetTexture(tex);
args.SetClipPlane(clipPlane);
args.DrawArray(vertices, sub->numlines, PolyDrawMode::TriangleFan);
RenderPolyPlane plane;
plane.Render(thread, worldToClip, clipPlane, sub, stencilValue, true, skyCeilingHeight, sectorPortals);
plane.Render(thread, worldToClip, clipPlane, sub, stencilValue, false, skyFloorHeight, sectorPortals);
}
void RenderPolyPlane::Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, PolyCull &cull, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, bool ceiling, double skyHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals)
void RenderPolyPlane::Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, subsector_t *sub, uint32_t stencilValue, bool ceiling, double skyHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals)
{
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
bool foggy = false;
sector_t *fakesector = sub->sector->heightsec;
if (fakesector && (fakesector == sub->sector || (fakesector->MoreFlags & SECF_IGNOREHEIGHTSEC) == SECF_IGNOREHEIGHTSEC))
@ -279,48 +181,88 @@ void RenderPolyPlane::Render(const TriMatrix &worldToClip, const PolyClipPlane &
#endif
}
UVTransform transform(ceiling ? frontsector->planes[sector_t::ceiling].xform : frontsector->planes[sector_t::floor].xform, tex);
PolyPlaneUVTransform transform(ceiling ? frontsector->planes[sector_t::ceiling].xform : frontsector->planes[sector_t::floor].xform, tex);
TriVertex *vertices = PolyRenderer::Instance()->FrameMemory.AllocMemory<TriVertex>(sub->numlines);
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(sub->numlines);
if (ceiling)
{
for (uint32_t i = 0; i < sub->numlines; i++)
if (!isSky)
{
seg_t *line = &sub->firstline[i];
vertices[sub->numlines - 1 - i] = PlaneVertex(line->v1, isSky ? skyHeight : frontsector->ceilingplane.ZatPoint(line->v1), transform);
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
vertices[sub->numlines - 1 - i] = transform.GetVertex(line->v1, frontsector->ceilingplane.ZatPoint(line->v1));
}
}
else
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
vertices[sub->numlines - 1 - i] = transform.GetVertex(line->v1, skyHeight);
}
}
}
else
{
for (uint32_t i = 0; i < sub->numlines; i++)
if (!isSky)
{
seg_t *line = &sub->firstline[i];
vertices[i] = PlaneVertex(line->v1, isSky ? skyHeight : frontsector->floorplane.ZatPoint(line->v1), transform);
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
vertices[i] = transform.GetVertex(line->v1, frontsector->floorplane.ZatPoint(line->v1));
}
}
else
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
vertices[i] = transform.GetVertex(line->v1, skyHeight);
}
}
}
bool foggy = level.fadeto || frontsector->Colormap.FadeColor || (level.flags & LEVEL_HASFADETABLE);
int lightlevel = ceiling ? frontsector->GetCeilingLight() : frontsector->GetFloorLight();
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
lightlevel = clamp(lightlevel + actualextralight, 0, 255);
PolyCameraLight *cameraLight = PolyCameraLight::Instance();
FDynamicColormap *basecolormap = GetColorTable(frontsector->Colormap, frontsector->SpecialColors[ceiling ? sector_t::ceiling : sector_t::floor]);
if (cameraLight->FixedLightLevel() < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size())
{
lightlist_t *light = P_GetPlaneLight(frontsector, ceiling ? &frontsector->ceilingplane : &frontsector->floorplane, false);
basecolormap = GetColorTable(light->extra_colormap, frontsector->SpecialColors[ceiling ? sector_t::ceiling : sector_t::floor]);
if (light->p_lightlevel != &frontsector->lightlevel) // If this is the real ceiling, don't discard plane lighting R_FakeFlat() accounted for.
{
lightlevel = *light->p_lightlevel;
}
}
PolyDrawArgs args;
args.SetLight(GetColorTable(frontsector->Colormap, frontsector->SpecialColors[ceiling]), frontsector->lightlevel, PolyRenderer::Instance()->Light.WallGlobVis(foggy), false);
args.SetSubsectorDepth(isSky ? RenderPolyScene::SkySubsectorDepth : subsectorDepth);
args.SetLight(basecolormap, lightlevel, PolyRenderer::Instance()->Light.WallGlobVis(foggy), false);
//args.SetSubsectorDepth(isSky ? RenderPolyScene::SkySubsectorDepth : subsectorDepth);
args.SetTransform(&worldToClip);
args.SetFaceCullCCW(ccw);
args.SetStencilTestValue(stencilValue);
args.SetWriteStencil(true, stencilValue + 1);
args.SetClipPlane(clipPlane);
args.SetClipPlane(0, clipPlane);
if (!isSky)
{
args.SetTexture(tex);
args.SetStyle(TriBlendMode::TextureOpaque);
args.DrawArray(vertices, sub->numlines, PolyDrawMode::TriangleFan);
args.DrawArray(thread, vertices, sub->numlines, PolyDrawMode::TriangleFan);
}
else
{
if (portal)
{
args.SetWriteStencil(true, polyportal->StencilValue);
polyportal->Shape.push_back({ vertices, (int)sub->numlines, ccw, subsectorDepth });
polyportal->Shape.push_back({ vertices, (int)sub->numlines, ccw });
}
else
{
@ -328,31 +270,40 @@ void RenderPolyPlane::Render(const TriMatrix &worldToClip, const PolyClipPlane &
}
args.SetWriteColor(false);
args.SetWriteSubsectorDepth(false);
args.DrawArray(vertices, sub->numlines, PolyDrawMode::TriangleFan);
args.SetWriteDepth(false);
args.DrawArray(thread, vertices, sub->numlines, PolyDrawMode::TriangleFan);
for (uint32_t i = 0; i < sub->numlines; i++)
RenderSkyWalls(thread, args, sub, frontsector, portal, polyportal, ceiling, skyHeight, transform);
}
}
void RenderPolyPlane::RenderSkyWalls(PolyRenderThread *thread, PolyDrawArgs &args, subsector_t *sub, sector_t *frontsector, FSectorPortal *portal, PolyDrawSectorPortal *polyportal, bool ceiling, double skyHeight, const PolyPlaneUVTransform &transform)
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
double skyBottomz1 = frontsector->ceilingplane.ZatPoint(line->v1);
double skyBottomz2 = frontsector->ceilingplane.ZatPoint(line->v2);
if (line->backsector)
{
TriVertex *wallvert = PolyRenderer::Instance()->FrameMemory.AllocMemory<TriVertex>(4);
sector_t *backsector = line->backsector;
seg_t *line = &sub->firstline[i];
double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1);
double backfloorz1 = backsector->floorplane.ZatPoint(line->v1);
double backceilz2 = backsector->ceilingplane.ZatPoint(line->v2);
double backfloorz2 = backsector->floorplane.ZatPoint(line->v2);
double skyBottomz1 = frontsector->ceilingplane.ZatPoint(line->v1);
double skyBottomz2 = frontsector->ceilingplane.ZatPoint(line->v2);
if (line->backsector)
bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum;
bool closedSector = backceilz1 == backfloorz1 && backceilz2 == backfloorz2;
if (ceiling && bothSkyCeiling && closedSector)
{
sector_t *backsector = (line->backsector != line->frontsector) ? line->backsector : line->frontsector;
double frontceilz1 = frontsector->ceilingplane.ZatPoint(line->v1);
double frontfloorz1 = frontsector->floorplane.ZatPoint(line->v1);
double frontceilz2 = frontsector->ceilingplane.ZatPoint(line->v2);
double frontfloorz2 = frontsector->floorplane.ZatPoint(line->v2);
double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1);
double backfloorz1 = backsector->floorplane.ZatPoint(line->v1);
double backceilz2 = backsector->ceilingplane.ZatPoint(line->v2);
double backfloorz2 = backsector->floorplane.ZatPoint(line->v2);
double topceilz1 = frontceilz1;
double topceilz2 = frontceilz2;
double topfloorz1 = MIN(backceilz1, frontceilz1);
@ -364,58 +315,50 @@ void RenderPolyPlane::Render(const TriMatrix &worldToClip, const PolyClipPlane &
double middlefloorz1 = MIN(bottomceilz1, middleceilz1);
double middlefloorz2 = MIN(bottomceilz2, middleceilz2);
bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum;
bool closedSector = backceilz1 == backfloorz1 && backceilz2 == backfloorz2;
if (ceiling && bothSkyCeiling && closedSector)
{
skyBottomz1 = middlefloorz1;
skyBottomz2 = middlefloorz2;
}
else if (bothSkyCeiling)
{
continue;
}
skyBottomz1 = middlefloorz1;
skyBottomz2 = middlefloorz2;
}
if (ceiling)
else if (bothSkyCeiling)
{
wallvert[0] = PlaneVertex(line->v1, skyHeight, transform);
wallvert[1] = PlaneVertex(line->v2, skyHeight, transform);
wallvert[2] = PlaneVertex(line->v2, skyBottomz2, transform);
wallvert[3] = PlaneVertex(line->v1, skyBottomz1, transform);
}
else
{
wallvert[0] = PlaneVertex(line->v1, frontsector->floorplane.ZatPoint(line->v1), transform);
wallvert[1] = PlaneVertex(line->v2, frontsector->floorplane.ZatPoint(line->v2), transform);
wallvert[2] = PlaneVertex(line->v2, skyHeight, transform);
wallvert[3] = PlaneVertex(line->v1, skyHeight, transform);
continue;
}
}
else if (portal && line->linedef && line->linedef->special == Line_Horizon)
{
// Not entirely correct as this closes the line horizon rather than allowing the floor to continue to infinity
skyBottomz1 = frontsector->floorplane.ZatPoint(line->v1);
skyBottomz2 = frontsector->floorplane.ZatPoint(line->v2);
}
args.DrawArray(wallvert, 4, PolyDrawMode::TriangleFan);
if (portal)
{
polyportal->Shape.push_back({ wallvert, 4, ccw, subsectorDepth });
}
TriVertex *wallvert = thread->FrameMemory->AllocMemory<TriVertex>(4);
if (ceiling)
{
wallvert[0] = transform.GetVertex(line->v1, skyHeight);
wallvert[1] = transform.GetVertex(line->v2, skyHeight);
wallvert[2] = transform.GetVertex(line->v2, skyBottomz2);
wallvert[3] = transform.GetVertex(line->v1, skyBottomz1);
}
else
{
wallvert[0] = transform.GetVertex(line->v1, frontsector->floorplane.ZatPoint(line->v1));
wallvert[1] = transform.GetVertex(line->v2, frontsector->floorplane.ZatPoint(line->v2));
wallvert[2] = transform.GetVertex(line->v2, skyHeight);
wallvert[3] = transform.GetVertex(line->v1, skyHeight);
}
args.DrawArray(thread, wallvert, 4, PolyDrawMode::TriangleFan);
if (portal)
{
polyportal->Shape.push_back({ wallvert, 4, args.FaceCullCCW() });
}
}
}
TriVertex RenderPolyPlane::PlaneVertex(vertex_t *v1, double height, const UVTransform &transform)
{
TriVertex v;
v.x = (float)v1->fPos().X;
v.y = (float)v1->fPos().Y;
v.z = (float)height;
v.w = 1.0f;
v.u = transform.GetU(v.x, v.y);
v.v = transform.GetV(v.x, v.y);
return v;
}
/////////////////////////////////////////////////////////////////////////////
RenderPolyPlane::UVTransform::UVTransform(const FTransform &transform, FTexture *tex)
PolyPlaneUVTransform::PolyPlaneUVTransform(const FTransform &transform, FTexture *tex)
{
if (tex)
{
@ -440,12 +383,160 @@ RenderPolyPlane::UVTransform::UVTransform(const FTransform &transform, FTexture
}
}
float RenderPolyPlane::UVTransform::GetU(float x, float y) const
/////////////////////////////////////////////////////////////////////////////
void Render3DFloorPlane::RenderPlanes(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, subsector_t *sub, uint32_t stencilValue, uint32_t subsectorDepth, std::vector<PolyTranslucentObject *> &translucentObjects)
{
return (xOffs + x * cosine - y * sine) * xscale;
if (!r_3dfloors || sub->sector->CenterFloor() == sub->sector->CenterCeiling())
return;
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
auto frontsector = sub->sector;
auto &ffloors = frontsector->e->XFloor.ffloors;
// 3D floor floors
for (int i = 0; i < (int)ffloors.Size(); i++)
{
F3DFloor *fakeFloor = ffloors[i];
if (!(fakeFloor->flags & FF_EXISTS)) continue;
if (!fakeFloor->model) continue;
//if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES)))
// R_3D_AddHeight(fakeFloor->top.plane, frontsector);
if (!(fakeFloor->flags & FF_RENDERPLANES)) continue;
if (fakeFloor->alpha == 0) continue;
if (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR) continue;
//fakeFloor->alpha
double fakeHeight = fakeFloor->top.plane->ZatPoint(frontsector->centerspot);
if (fakeFloor->bottom.plane->isSlope() || (fakeHeight < viewpoint.Pos.Z && fakeHeight > frontsector->floorplane.ZatPoint(frontsector->centerspot)))
{
Render3DFloorPlane plane;
plane.sub = sub;
plane.stencilValue = stencilValue;
plane.ceiling = false;
plane.fakeFloor = fakeFloor;
plane.Additive = !!(fakeFloor->flags & FF_ADDITIVETRANS);
if (!plane.Additive && fakeFloor->alpha == 255)
{
plane.Masked = false;
plane.Alpha = 1.0;
}
else
{
plane.Masked = true;
plane.Alpha = fakeFloor->alpha / 255.0;
}
if (!plane.Masked)
plane.Render(thread, worldToClip, clipPlane);
else
translucentObjects.push_back(thread->FrameMemory->NewObject<PolyTranslucent3DFloorPlane>(plane, subsectorDepth));
}
}
// 3D floor ceilings
for (int i = 0; i < (int)ffloors.Size(); i++)
{
F3DFloor *fakeFloor = ffloors[i];
if (!(fakeFloor->flags & FF_EXISTS)) continue;
if (!fakeFloor->model) continue;
//if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES)))
// R_3D_AddHeight(fakeFloor->bottom.plane, frontsector);
if (!(fakeFloor->flags & FF_RENDERPLANES)) continue;
if (fakeFloor->alpha == 0) continue;
if (!(fakeFloor->flags & FF_THISINSIDE) && (fakeFloor->flags & (FF_SWIMMABLE | FF_INVERTSECTOR)) == (FF_SWIMMABLE | FF_INVERTSECTOR)) continue;
//fakeFloor->alpha
double fakeHeight = fakeFloor->bottom.plane->ZatPoint(frontsector->centerspot);
if (fakeFloor->bottom.plane->isSlope() || (fakeHeight > viewpoint.Pos.Z && fakeHeight < frontsector->ceilingplane.ZatPoint(frontsector->centerspot)))
{
Render3DFloorPlane plane;
plane.sub = sub;
plane.stencilValue = stencilValue;
plane.ceiling = true;
plane.fakeFloor = fakeFloor;
plane.Additive = !!(fakeFloor->flags & FF_ADDITIVETRANS);
if (!plane.Additive && fakeFloor->alpha == 255)
{
plane.Masked = false;
plane.Alpha = 1.0;
}
else
{
plane.Masked = true;
plane.Alpha = fakeFloor->alpha / 255.0;
}
if (!plane.Masked)
plane.Render(thread, worldToClip, clipPlane);
else
translucentObjects.push_back(thread->FrameMemory->NewObject<PolyTranslucent3DFloorPlane>(plane, subsectorDepth));
}
}
}
float RenderPolyPlane::UVTransform::GetV(float x, float y) const
void Render3DFloorPlane::Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane)
{
return (yOffs - x * sine - y * cosine) * yscale;
FTextureID picnum = ceiling ? *fakeFloor->bottom.texture : *fakeFloor->top.texture;
FTexture *tex = TexMan(picnum);
if (tex->UseType == FTexture::TEX_Null)
return;
PolyCameraLight *cameraLight = PolyCameraLight::Instance();
int lightlevel = 255;
bool foggy = false;
if (cameraLight->FixedLightLevel() < 0 && sub->sector->e->XFloor.lightlist.Size())
{
lightlist_t *light = P_GetPlaneLight(sub->sector, &sub->sector->ceilingplane, false);
//basecolormap = light->extra_colormap;
lightlevel = *light->p_lightlevel;
}
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
lightlevel = clamp(lightlevel + actualextralight, 0, 255);
PolyPlaneUVTransform xform(ceiling ? fakeFloor->top.model->planes[sector_t::ceiling].xform : fakeFloor->top.model->planes[sector_t::floor].xform, tex);
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(sub->numlines);
if (ceiling)
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
vertices[sub->numlines - 1 - i] = xform.GetVertex(line->v1, fakeFloor->bottom.plane->ZatPoint(line->v1));
}
}
else
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
vertices[i] = xform.GetVertex(line->v1, fakeFloor->top.plane->ZatPoint(line->v1));
}
}
PolyDrawArgs args;
args.SetLight(GetColorTable(sub->sector->Colormap), lightlevel, PolyRenderer::Instance()->Light.WallGlobVis(foggy), false);
args.SetTransform(&worldToClip);
if (!Masked)
{
args.SetStyle(TriBlendMode::TextureOpaque);
}
else
{
double srcalpha = MIN(Alpha, 1.0);
double destalpha = Additive ? 1.0 : 1.0 - srcalpha;
args.SetStyle(TriBlendMode::TextureAdd, srcalpha, destalpha);
args.SetDepthTest(true);
args.SetWriteDepth(true);
args.SetWriteStencil(false);
}
args.SetFaceCullCCW(true);
args.SetStencilTestValue(stencilValue);
args.SetWriteStencil(true, stencilValue + 1);
args.SetTexture(tex);
args.SetClipPlane(0, clipPlane);
args.DrawArray(thread, vertices, sub->numlines, PolyDrawMode::TriangleFan);
}

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a plane (ceiling, floor)
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -25,29 +25,70 @@
#include "polyrenderer/drawers/poly_triangle.h"
class PolyDrawSectorPortal;
class PolyCull;
class PolyPlaneUVTransform
{
public:
PolyPlaneUVTransform(const FTransform &transform, FTexture *tex);
TriVertex GetVertex(vertex_t *v1, double height) const
{
TriVertex v;
v.x = (float)v1->fX();
v.y = (float)v1->fY();
v.z = (float)height;
v.w = 1.0f;
v.u = GetU(v.x, v.y);
v.v = GetV(v.x, v.y);
return v;
}
private:
float GetU(float x, float y) const { return (xOffs + x * cosine - y * sine) * xscale; }
float GetV(float x, float y) const { return (yOffs - x * sine - y * cosine) * yscale; }
float xscale;
float yscale;
float cosine;
float sine;
float xOffs, yOffs;
};
class RenderPolyPlane
{
public:
static void RenderPlanes(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, PolyCull &cull, subsector_t *sub, uint32_t ceilingSubsectorDepth, uint32_t floorSubsectorDepth, uint32_t stencilValue, double skyCeilingHeight, double skyFloorHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals);
static void RenderPlanes(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, subsector_t *sub, uint32_t stencilValue, double skyCeilingHeight, double skyFloorHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals);
private:
struct UVTransform
{
UVTransform(const FTransform &transform, FTexture *tex);
float GetU(float x, float y) const;
float GetV(float x, float y) const;
float xscale;
float yscale;
float cosine;
float sine;
float xOffs, yOffs;
};
void Render3DFloor(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, bool ceiling, F3DFloor *fakefloor);
void Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, PolyCull &cull, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, bool ceiling, double skyHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals);
TriVertex PlaneVertex(vertex_t *v1, double height, const UVTransform &transform);
void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, subsector_t *sub, uint32_t stencilValue, bool ceiling, double skyHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals);
void RenderSkyWalls(PolyRenderThread *thread, PolyDrawArgs &args, subsector_t *sub, sector_t *frontsector, FSectorPortal *portal, PolyDrawSectorPortal *polyportal, bool ceiling, double skyHeight, const PolyPlaneUVTransform &transform);
};
class Render3DFloorPlane
{
public:
static void RenderPlanes(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, subsector_t *sub, uint32_t stencilValue, uint32_t subsectorDepth, std::vector<PolyTranslucentObject *> &translucentObjects);
void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane);
subsector_t *sub = nullptr;
uint32_t stencilValue = 0;
bool ceiling = false;
F3DFloor *fakeFloor = nullptr;
bool Masked = false;
bool Additive = false;
double Alpha = 1.0;
};
class PolyTranslucent3DFloorPlane : public PolyTranslucentObject
{
public:
PolyTranslucent3DFloorPlane(Render3DFloorPlane plane, uint32_t subsectorDepth) : PolyTranslucentObject(subsectorDepth, 1e7), plane(plane) { }
void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &portalPlane) override
{
plane.Render(thread, worldToClip, portalPlane);
}
Render3DFloorPlane plane;
};

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a player sprite
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -35,7 +35,7 @@ EXTERN_CVAR(Bool, r_deathcamera)
EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor)
EXTERN_CVAR(Bool, r_shadercolormaps)
void RenderPolyPlayerSprites::Render()
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..
@ -143,7 +143,7 @@ void RenderPolyPlayerSprites::Render()
if ((psp->GetID() != PSP_TARGETCENTER || CrosshairImage == nullptr) && psp->GetCaller() != nullptr)
{
RenderSprite(psp, viewpoint.camera, bobx, boby, wx, wy, viewpoint.TicFrac, spriteshade, basecolormap, foggy);
RenderSprite(thread, psp, viewpoint.camera, bobx, boby, wx, wy, viewpoint.TicFrac, spriteshade, basecolormap, foggy);
}
psp = psp->GetNext();
@ -180,7 +180,7 @@ void RenderPolyPlayerSprites::RenderRemainingSprites()
AcceleratedSprites.Clear();
}
void RenderPolyPlayerSprites::RenderSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap, bool foggy)
void RenderPolyPlayerSprites::RenderSprite(PolyRenderThread *thread, DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap, bool foggy)
{
double tx;
int x1;
@ -242,7 +242,7 @@ void RenderPolyPlayerSprites::RenderSprite(DPSprite *pspr, AActor *owner, float
sy += wy;
}
double yaspectMul = 1.2;// 320.0 * SCREENHEIGHT / (r_Yaspect * SCREENWIDTH);
double yaspectMul = 1.2 * ((double)SCREENHEIGHT / SCREENWIDTH) * r_viewwindow.WidescreenRatio;
double pspritexscale = viewwindow.centerxwide / 160.0;
double pspriteyscale = pspritexscale * yaspectMul;
@ -454,7 +454,7 @@ void RenderPolyPlayerSprites::RenderSprite(DPSprite *pspr, AActor *owner, float
}
}
vis.Render();
vis.Render(thread);
}
fixed_t RenderPolyPlayerSprites::LightLevelToShade(int lightlevel, bool foggy)
@ -475,7 +475,7 @@ fixed_t RenderPolyPlayerSprites::LightLevelToShade(int lightlevel, bool foggy)
/////////////////////////////////////////////////////////////////////////
void PolyNoAccelPlayerSprite::Render()
void PolyNoAccelPlayerSprite::Render(PolyRenderThread *thread)
{
if (xscale == 0 || fabs(yscale) < (1.0f / 32000.0f))
{ // scaled to 0; can't see
@ -498,7 +498,7 @@ void PolyNoAccelPlayerSprite::Render()
y1 = centerY - texturemid * yscale;
y2 = y1 + pic->GetHeight() * yscale;
}
args.Draw(x1, x2, y1, y2, 0.0f, 1.0f, 0.0f, 1.0f);
args.Draw(thread, x1, x2, y1, y2, 0.0f, 1.0f, 0.0f, 1.0f);
}
/////////////////////////////////////////////////////////////////////////////

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a player sprite
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -61,7 +61,7 @@ public:
short renderflags = 0;
void Render();
void Render(PolyRenderThread *thread);
};
class PolyHWAccelPlayerSprite
@ -90,11 +90,11 @@ public:
class RenderPolyPlayerSprites
{
public:
void Render();
void Render(PolyRenderThread *thread);
void RenderRemainingSprites();
private:
void RenderSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap, bool foggy);
void RenderSprite(PolyRenderThread *thread, DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap, bool foggy);
static fixed_t LightLevelToShade(int lightlevel, bool foggy);
enum { BASEXCENTER = 160 };

View file

@ -68,7 +68,7 @@ void PolyDrawSectorPortal::Render(int portalDepth)
PolyClipPlane portalPlane(0.0f, 0.0f, 0.0f, 1.0f);
RenderPortal.SetViewpoint(worldToClip, portalPlane, StencilValue);
RenderPortal.SetPortalSegments(Segments);
//RenderPortal.SetPortalSegments(Segments);
RenderPortal.Render(portalDepth);
RestoreGlobals();
@ -202,7 +202,7 @@ void PolyDrawLinePortal::Render(int portalDepth)
RenderPortal.LastPortalLine = clipLine;
RenderPortal.SetViewpoint(worldToClip, portalPlane, StencilValue);
RenderPortal.SetPortalSegments(Segments);
//RenderPortal.SetPortalSegments(Segments);
RenderPortal.Render(portalDepth);
RestoreGlobals();

View file

@ -26,11 +26,10 @@
struct PolyPortalVertexRange
{
PolyPortalVertexRange(const TriVertex *vertices, int count, bool ccw, uint32_t subsectorDepth) : Vertices(vertices), Count(count), Ccw(ccw), SubsectorDepth(subsectorDepth) { }
PolyPortalVertexRange(const TriVertex *vertices, int count, bool ccw) : Vertices(vertices), Count(count), Ccw(ccw) { }
const TriVertex *Vertices;
int Count;
bool Ccw;
uint32_t SubsectorDepth;
};
class PolyPortalSegment
@ -51,7 +50,7 @@ public:
FSectorPortal *Portal = nullptr;
uint32_t StencilValue = 0;
std::vector<PolyPortalVertexRange> Shape;
std::vector<PolyPortalSegment> Segments;
//std::vector<PolyPortalSegment> Segments;
private:
void SaveGlobals();
@ -81,7 +80,7 @@ public:
line_t *Mirror = nullptr;
uint32_t StencilValue = 0;
std::vector<PolyPortalVertexRange> Shape;
std::vector<PolyPortalSegment> Segments;
//std::vector<PolyPortalSegment> Segments;
private:
void SaveGlobals();

View file

@ -26,9 +26,14 @@
#include "p_maputl.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "polyrenderer/scene/poly_scene.h"
#include "polyrenderer/poly_renderer.h"
#include "polyrenderer/scene/poly_scene.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/scene/poly_wall.h"
#include "polyrenderer/scene/poly_wallsprite.h"
#include "polyrenderer/scene/poly_plane.h"
#include "polyrenderer/scene/poly_particle.h"
#include "polyrenderer/scene/poly_sprite.h"
EXTERN_CVAR(Int, r_portal_recursions)
@ -49,100 +54,48 @@ void RenderPolyScene::SetViewpoint(const TriMatrix &worldToClip, const PolyClipP
PortalPlane = portalPlane;
}
void RenderPolyScene::SetPortalSegments(const std::vector<PolyPortalSegment> &segments)
{
if (!segments.empty())
{
Cull.ClearSolidSegments();
for (const auto &segment : segments)
{
Cull.MarkSegmentCulled(segment.Start, segment.End);
}
Cull.InvertSegments();
PortalSegmentsAdded = true;
}
else
{
PortalSegmentsAdded = false;
}
}
void RenderPolyScene::Render(int portalDepth)
{
ClearBuffers();
if (!PortalSegmentsAdded)
Cull.ClearSolidSegments();
Cull.MarkViewFrustum();
PolyRenderThread *thread = PolyRenderer::Instance()->Threads.MainThread();
SectorPortals.clear();
LinePortals.clear();
Cull.CullScene(WorldToClip, PortalPlane);
Cull.ClearSolidSegments();
RenderSectors();
RenderPortals(portalDepth);
}
void RenderPolyScene::ClearBuffers()
{
SeenSectors.clear();
SubsectorDepths.clear();
TranslucentObjects.clear();
SectorPortals.clear();
LinePortals.clear();
NextSubsectorDepth = 0;
}
void RenderPolyScene::RenderSectors()
{
int count = (int)Cull.PvsSectors.size();
PolyRenderThread *mainthread = PolyRenderer::Instance()->Threads.MainThread();
int totalcount = (int)Cull.PvsSectors.size();
auto subsectors = Cull.PvsSectors.data();
int nextCeilingZChange = 0;
int nextFloorZChange = 0;
uint32_t ceilingSubsectorDepth = 0;
uint32_t floorSubsectorDepth = 0;
TranslucentObjects.resize(PolyRenderer::Instance()->Threads.NumThreads());
for (int i = 0; i < count; i++)
PolyRenderer::Instance()->Threads.RenderThreadSlices(totalcount, [&](PolyRenderThread *thread)
{
// The software renderer only updates the clipping if the sector height changes.
// Find the subsector depths for when that happens.
if (i == nextCeilingZChange)
{
double z = subsectors[i]->sector->ceilingplane.Zat0();
nextCeilingZChange++;
while (nextCeilingZChange < count)
{
double nextZ = subsectors[nextCeilingZChange]->sector->ceilingplane.Zat0();
if (nextZ > z)
break;
z = nextZ;
nextCeilingZChange++;
}
ceilingSubsectorDepth = NextSubsectorDepth + nextCeilingZChange - i - 1;
}
if (i == nextFloorZChange)
{
double z = subsectors[i]->sector->floorplane.Zat0();
nextFloorZChange++;
while (nextFloorZChange < count)
{
double nextZ = subsectors[nextFloorZChange]->sector->floorplane.Zat0();
if (nextZ < z)
break;
z = nextZ;
nextFloorZChange++;
}
floorSubsectorDepth = NextSubsectorDepth + nextFloorZChange - i - 1;
}
TranslucentObjects[thread->ThreadIndex].clear();
RenderSubsector(subsectors[i], ceilingSubsectorDepth, floorSubsectorDepth);
}
int start = thread->Start;
int end = thread->End;
for (int i = start; i < end; i++)
{
RenderSubsector(thread, subsectors[i], i);
}
}, [&](PolyRenderThread *thread)
{
const auto &objects = TranslucentObjects[thread->ThreadIndex];
TranslucentObjects[0].insert(TranslucentObjects[0].end(), objects.begin(), objects.end());
});
}
void RenderPolyScene::RenderSubsector(subsector_t *sub, uint32_t ceilingSubsectorDepth, uint32_t floorSubsectorDepth)
void RenderPolyScene::RenderSubsector(PolyRenderThread *thread, subsector_t *sub, uint32_t subsectorDepth)
{
sector_t *frontsector = sub->sector;
frontsector->MoreFlags |= SECF_DRAWN;
uint32_t subsectorDepth = NextSubsectorDepth++;
bool mainBSP = sub->polys == nullptr;
if (sub->polys)
@ -161,43 +114,43 @@ void RenderPolyScene::RenderSubsector(subsector_t *sub, uint32_t ceilingSubsecto
if (sub->BSP->Nodes.Size() == 0)
{
RenderPolySubsector(&sub->BSP->Subsectors[0], subsectorDepth, frontsector);
RenderPolySubsector(thread, &sub->BSP->Subsectors[0], subsectorDepth, frontsector);
}
else
{
RenderPolyNode(&sub->BSP->Nodes.Last(), subsectorDepth, frontsector);
RenderPolyNode(thread, &sub->BSP->Nodes.Last(), subsectorDepth, frontsector);
}
Render3DFloorPlane::RenderPlanes(thread, WorldToClip, PortalPlane, sub, StencilValue, subsectorDepth, TranslucentObjects[thread->ThreadIndex]);
RenderPolyPlane::RenderPlanes(thread, WorldToClip, PortalPlane, sub, StencilValue, Cull.MaxCeilingHeight, Cull.MinFloorHeight, SectorPortals);
}
else
{
Render3DFloorPlane::RenderPlanes(thread, WorldToClip, PortalPlane, sub, StencilValue, subsectorDepth, TranslucentObjects[thread->ThreadIndex]);
RenderPolyPlane::RenderPlanes(thread, WorldToClip, PortalPlane, sub, StencilValue, Cull.MaxCeilingHeight, Cull.MinFloorHeight, SectorPortals);
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
RenderLine(sub, line, frontsector, subsectorDepth);
if (Cull.IsLineSegVisible(subsectorDepth, i))
{
seg_t *line = &sub->firstline[i];
RenderLine(thread, sub, line, frontsector, subsectorDepth);
}
}
}
if (sub->sector->CenterFloor() != sub->sector->CenterCeiling())
{
RenderPolyPlane::RenderPlanes(WorldToClip, PortalPlane, Cull, sub, ceilingSubsectorDepth, floorSubsectorDepth, StencilValue, Cull.MaxCeilingHeight, Cull.MinFloorHeight, SectorPortals);
}
if (mainBSP)
{
RenderMemory &memory = PolyRenderer::Instance()->FrameMemory;
int subsectorIndex = sub->Index();
for (int i = ParticlesInSubsec[subsectorIndex]; i != NO_PARTICLE; i = Particles[i].snext)
{
particle_t *particle = Particles + i;
TranslucentObjects.push_back(memory.NewObject<PolyTranslucentObject>(particle, sub, subsectorDepth));
TranslucentObjects[thread->ThreadIndex].push_back(thread->FrameMemory->NewObject<PolyTranslucentParticle>(particle, sub, subsectorDepth, StencilValue));
}
}
SeenSectors.insert(sub->sector);
SubsectorDepths[sub] = subsectorDepth;
}
void RenderPolyScene::RenderPolyNode(void *node, uint32_t subsectorDepth, sector_t *frontsector)
void RenderPolyScene::RenderPolyNode(PolyRenderThread *thread, void *node, uint32_t subsectorDepth, sector_t *frontsector)
{
while (!((size_t)node & 1)) // Keep going until found a subsector
{
@ -207,7 +160,7 @@ void RenderPolyScene::RenderPolyNode(void *node, uint32_t subsectorDepth, sector
int side = PointOnSide(PolyRenderer::Instance()->Viewpoint.Pos, bsp);
// Recursively divide front space (toward the viewer).
RenderPolyNode(bsp->children[side], subsectorDepth, frontsector);
RenderPolyNode(thread, bsp->children[side], subsectorDepth, frontsector);
// Possibly divide back space (away from the viewer).
side ^= 1;
@ -220,10 +173,10 @@ void RenderPolyScene::RenderPolyNode(void *node, uint32_t subsectorDepth, sector
}
subsector_t *sub = (subsector_t *)((uint8_t *)node - 1);
RenderPolySubsector(sub, subsectorDepth, frontsector);
RenderPolySubsector(thread, sub, subsectorDepth, frontsector);
}
void RenderPolyScene::RenderPolySubsector(subsector_t *sub, uint32_t subsectorDepth, sector_t *frontsector)
void RenderPolyScene::RenderPolySubsector(PolyRenderThread *thread, subsector_t *sub, uint32_t subsectorDepth, sector_t *frontsector)
{
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
@ -238,11 +191,6 @@ void RenderPolyScene::RenderPolySubsector(subsector_t *sub, uint32_t subsectorDe
if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0)
continue;
// Cull wall if not visible
angle_t angle1, angle2;
if (!Cull.GetAnglesForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), angle1, angle2))
continue;
// Tell automap we saw this
if (!PolyRenderer::Instance()->DontMapLines && line->linedef)
{
@ -250,11 +198,7 @@ void RenderPolyScene::RenderPolySubsector(subsector_t *sub, uint32_t subsectorDe
sub->flags |= SSECF_DRAWN;
}
// Render wall, and update culling info if its an occlusion blocker
if (RenderPolyWall::RenderLine(WorldToClip, PortalPlane, Cull, line, frontsector, subsectorDepth, StencilValue, TranslucentObjects, LinePortals, LastPortalLine))
{
Cull.MarkSegmentCulled(angle1, angle2);
}
RenderPolyWall::RenderLine(thread, WorldToClip, PortalPlane, line, frontsector, subsectorDepth, StencilValue, TranslucentObjects[thread->ThreadIndex], LinePortals, LastPortalLine);
}
}
}
@ -264,22 +208,21 @@ int RenderPolyScene::PointOnSide(const DVector2 &pos, const node_t *node)
return DMulScale32(FLOAT2FIXED(pos.Y) - node->y, node->dx, node->x - FLOAT2FIXED(pos.X), node->dy) > 0;
}
void RenderPolyScene::RenderSprite(AActor *thing, double sortDistance, const DVector2 &left, const DVector2 &right)
void RenderPolyScene::RenderSprite(PolyRenderThread *thread, AActor *thing, double sortDistance, const DVector2 &left, const DVector2 &right)
{
if (level.nodes.Size() == 0)
{
subsector_t *sub = &level.subsectors[0];
auto it = SubsectorDepths.find(sub);
if (it != SubsectorDepths.end())
TranslucentObjects.push_back(PolyRenderer::Instance()->FrameMemory.NewObject<PolyTranslucentObject>(thing, sub, it->second, sortDistance, 0.0f, 1.0f));
if (Cull.SubsectorDepths[sub->Index()] != 0xffffffff)
TranslucentObjects[thread->ThreadIndex].push_back(thread->FrameMemory->NewObject<PolyTranslucentThing>(thing, sub, Cull.SubsectorDepths[sub->Index()], sortDistance, 0.0f, 1.0f, StencilValue));
}
else
{
RenderSprite(thing, sortDistance, left, right, 0.0, 1.0, level.HeadNode());
RenderSprite(thread, thing, sortDistance, left, right, 0.0, 1.0, level.HeadNode());
}
}
void RenderPolyScene::RenderSprite(AActor *thing, double sortDistance, DVector2 left, DVector2 right, double t1, double t2, void *node)
void RenderPolyScene::RenderSprite(PolyRenderThread *thread, AActor *thing, double sortDistance, DVector2 left, DVector2 right, double t1, double t2, void *node)
{
while (!((size_t)node & 1)) // Keep going until found a subsector
{
@ -301,7 +244,7 @@ void RenderPolyScene::RenderSprite(AActor *thing, double sortDistance, DVector2
DVector2 mid = left * (1.0 - t) + right * t;
double tmid = t1 * (1.0 - t) + t2 * t;
RenderSprite(thing, sortDistance, mid, right, tmid, t2, bsp->children[sideRight]);
RenderSprite(thread, thing, sortDistance, mid, right, tmid, t2, bsp->children[sideRight]);
right = mid;
t2 = tmid;
}
@ -310,26 +253,12 @@ void RenderPolyScene::RenderSprite(AActor *thing, double sortDistance, DVector2
subsector_t *sub = (subsector_t *)((uint8_t *)node - 1);
auto it = SubsectorDepths.find(sub);
if (it != SubsectorDepths.end())
TranslucentObjects.push_back(PolyRenderer::Instance()->FrameMemory.NewObject<PolyTranslucentObject>(thing, sub, it->second, sortDistance, (float)t1, (float)t2));
if (Cull.SubsectorDepths[sub->Index()] != 0xffffffff)
TranslucentObjects[thread->ThreadIndex].push_back(thread->FrameMemory->NewObject<PolyTranslucentThing>(thing, sub, Cull.SubsectorDepths[sub->Index()], sortDistance, (float)t1, (float)t2, StencilValue));
}
void RenderPolyScene::RenderLine(subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth)
void RenderPolyScene::RenderLine(PolyRenderThread *thread, subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth)
{
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
// Reject lines not facing viewer
DVector2 pt1 = line->v1->fPos() - viewpoint.Pos;
DVector2 pt2 = line->v2->fPos() - viewpoint.Pos;
if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0)
return;
// Cull wall if not visible
angle_t angle1, angle2;
if (!Cull.GetAnglesForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), angle1, angle2))
return;
// Tell automap we saw this
if (!PolyRenderer::Instance()->DontMapLines && line->linedef)
{
@ -338,27 +267,23 @@ void RenderPolyScene::RenderLine(subsector_t *sub, seg_t *line, sector_t *fronts
}
// Render 3D floor sides
if (line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size())
if (line->sidedef && line->backsector && line->backsector->e && line->backsector->e->XFloor.ffloors.Size())
{
for (unsigned int i = 0; i < line->backsector->e->XFloor.ffloors.Size(); i++)
{
F3DFloor *fakeFloor = line->backsector->e->XFloor.ffloors[i];
if (!(fakeFloor->flags & FF_EXISTS)) continue;
if (!(fakeFloor->flags & FF_RENDERPLANES)) continue;
if (!fakeFloor->model) continue;
RenderPolyWall::Render3DFloorLine(WorldToClip, PortalPlane, Cull, line, frontsector, subsectorDepth, StencilValue, fakeFloor, TranslucentObjects);
RenderPolyWall::Render3DFloorLine(thread, WorldToClip, PortalPlane, line, frontsector, subsectorDepth, StencilValue, fakeFloor, TranslucentObjects[thread->ThreadIndex]);
}
}
// Render wall, and update culling info if its an occlusion blocker
if (RenderPolyWall::RenderLine(WorldToClip, PortalPlane, Cull, line, frontsector, subsectorDepth, StencilValue, TranslucentObjects, LinePortals, LastPortalLine))
{
Cull.MarkSegmentCulled(angle1, angle2);
}
RenderPolyWall::RenderLine(thread, WorldToClip, PortalPlane, line, frontsector, subsectorDepth, StencilValue, TranslucentObjects[thread->ThreadIndex], LinePortals, LastPortalLine);
}
void RenderPolyScene::RenderPortals(int portalDepth)
{
PolyRenderThread *thread = PolyRenderer::Instance()->Threads.MainThread();
bool foggy = false;
if (portalDepth < r_portal_recursions)
{
@ -374,7 +299,7 @@ void RenderPolyScene::RenderPortals(int portalDepth)
args.SetTransform(&WorldToClip);
args.SetLight(&NormalLight, 255, PolyRenderer::Instance()->Light.WallGlobVis(foggy), true);
args.SetColor(0, 0);
args.SetClipPlane(PortalPlane);
args.SetClipPlane(0, PortalPlane);
args.SetStyle(TriBlendMode::FillOpaque);
for (auto &portal : SectorPortals)
@ -384,8 +309,7 @@ void RenderPolyScene::RenderPortals(int portalDepth)
for (const auto &verts : portal->Shape)
{
args.SetFaceCullCCW(verts.Ccw);
args.SetSubsectorDepth(verts.SubsectorDepth);
args.DrawArray(verts.Vertices, verts.Count, PolyDrawMode::TriangleFan);
args.DrawArray(thread, verts.Vertices, verts.Count, PolyDrawMode::TriangleFan);
}
}
@ -396,8 +320,7 @@ void RenderPolyScene::RenderPortals(int portalDepth)
for (const auto &verts : portal->Shape)
{
args.SetFaceCullCCW(verts.Ccw);
args.SetSubsectorDepth(verts.SubsectorDepth);
args.DrawArray(verts.Vertices, verts.Count, PolyDrawMode::TriangleFan);
args.DrawArray(thread, verts.Vertices, verts.Count, PolyDrawMode::TriangleFan);
}
}
}
@ -405,6 +328,8 @@ void RenderPolyScene::RenderPortals(int portalDepth)
void RenderPolyScene::RenderTranslucent(int portalDepth)
{
PolyRenderThread *thread = PolyRenderer::Instance()->Threads.MainThread();
if (portalDepth < r_portal_recursions)
{
for (auto it = SectorPortals.rbegin(); it != SectorPortals.rend(); ++it)
@ -416,13 +341,12 @@ void RenderPolyScene::RenderTranslucent(int portalDepth)
args.SetTransform(&WorldToClip);
args.SetStencilTestValue(portal->StencilValue + 1);
args.SetWriteStencil(true, StencilValue + 1);
args.SetClipPlane(PortalPlane);
args.SetClipPlane(0, PortalPlane);
for (const auto &verts : portal->Shape)
{
args.SetFaceCullCCW(verts.Ccw);
args.SetSubsectorDepth(verts.SubsectorDepth);
args.SetWriteColor(false);
args.DrawArray(verts.Vertices, verts.Count, PolyDrawMode::TriangleFan);
args.DrawArray(thread, verts.Vertices, verts.Count, PolyDrawMode::TriangleFan);
}
}
@ -435,19 +359,18 @@ void RenderPolyScene::RenderTranslucent(int portalDepth)
args.SetTransform(&WorldToClip);
args.SetStencilTestValue(portal->StencilValue + 1);
args.SetWriteStencil(true, StencilValue + 1);
args.SetClipPlane(PortalPlane);
args.SetClipPlane(0, PortalPlane);
for (const auto &verts : portal->Shape)
{
args.SetFaceCullCCW(verts.Ccw);
args.SetSubsectorDepth(verts.SubsectorDepth);
args.SetWriteColor(false);
args.DrawArray(verts.Vertices, verts.Count, PolyDrawMode::TriangleFan);
args.DrawArray(thread, verts.Vertices, verts.Count, PolyDrawMode::TriangleFan);
}
}
}
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
for (sector_t *sector : SeenSectors)
for (sector_t *sector : Cull.SeenSectors)
{
for (AActor *thing = sector->thinglist; thing != nullptr; thing = thing->snext)
{
@ -455,33 +378,18 @@ void RenderPolyScene::RenderTranslucent(int portalDepth)
if (!RenderPolySprite::GetLine(thing, left, right))
continue;
double distanceSquared = (thing->Pos() - viewpoint.Pos).LengthSquared();
RenderSprite(thing, distanceSquared, left, right);
RenderSprite(thread, thing, distanceSquared, left, right);
}
}
std::stable_sort(TranslucentObjects.begin(), TranslucentObjects.end(), [](auto a, auto b) { return *a < *b; });
std::stable_sort(TranslucentObjects[0].begin(), TranslucentObjects[0].end(), [](auto a, auto b) { return *a < *b; });
for (auto it = TranslucentObjects.rbegin(); it != TranslucentObjects.rend(); ++it)
for (auto it = TranslucentObjects[0].rbegin(); it != TranslucentObjects[0].rend(); ++it)
{
PolyTranslucentObject *obj = *it;
if (obj->particle)
{
RenderPolyParticle spr;
spr.Render(WorldToClip, PortalPlane, obj->particle, obj->sub, obj->subsectorDepth, StencilValue + 1);
}
else if (!obj->thing)
{
obj->wall.Render(WorldToClip, PortalPlane, Cull);
}
else if ((obj->thing->renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE)
{
RenderPolyWallSprite wallspr;
wallspr.Render(WorldToClip, PortalPlane, obj->thing, obj->sub, obj->subsectorDepth, StencilValue + 1);
}
else
{
RenderPolySprite spr;
spr.Render(WorldToClip, PortalPlane, obj->thing, obj->sub, obj->subsectorDepth, StencilValue + 1, obj->SpriteLeft, obj->SpriteRight);
}
obj->Render(thread, WorldToClip, PortalPlane);
obj->~PolyTranslucentObject();
}
TranslucentObjects[0].clear();
}

View file

@ -29,38 +29,24 @@
#include "doomdata.h"
#include "r_utility.h"
#include "polyrenderer/drawers/poly_triangle.h"
#include "poly_wall.h"
#include "poly_sprite.h"
#include "poly_wallsprite.h"
#include "poly_playersprite.h"
#include "poly_particle.h"
#include "poly_plane.h"
#include "poly_cull.h"
#include <set>
#include <unordered_map>
class PolyTranslucentObject
{
public:
PolyTranslucentObject(particle_t *particle, subsector_t *sub, uint32_t subsectorDepth) : particle(particle), sub(sub), subsectorDepth(subsectorDepth) { }
PolyTranslucentObject(AActor *thing, subsector_t *sub, uint32_t subsectorDepth, double dist, float t1, float t2) : thing(thing), sub(sub), subsectorDepth(subsectorDepth), DistanceSquared(dist), SpriteLeft(t1), SpriteRight(t2) { }
PolyTranslucentObject(RenderPolyWall wall) : wall(wall), subsectorDepth(wall.SubsectorDepth), DistanceSquared(1.e6) { }
PolyTranslucentObject(uint32_t subsectorDepth = 0, double distanceSquared = 0.0) : subsectorDepth(subsectorDepth), DistanceSquared(distanceSquared) { }
virtual ~PolyTranslucentObject() { }
virtual void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &portalPlane) = 0;
bool operator<(const PolyTranslucentObject &other) const
{
return subsectorDepth != other.subsectorDepth ? subsectorDepth < other.subsectorDepth : DistanceSquared < other.DistanceSquared;
}
particle_t *particle = nullptr;
AActor *thing = nullptr;
subsector_t *sub = nullptr;
RenderPolyWall wall;
uint32_t subsectorDepth = 0;
double DistanceSquared = 0.0;
float SpriteLeft = 0.0f, SpriteRight = 1.0f;
uint32_t subsectorDepth;
double DistanceSquared;
};
class PolyDrawSectorPortal;
@ -74,7 +60,6 @@ public:
RenderPolyScene();
~RenderPolyScene();
void SetViewpoint(const TriMatrix &worldToClip, const PolyClipPlane &portalPlane, uint32_t stencilValue);
void SetPortalSegments(const std::vector<PolyPortalSegment> &segments);
void Render(int portalDepth);
void RenderTranslucent(int portalDepth);
@ -83,28 +68,24 @@ public:
line_t *LastPortalLine = nullptr;
private:
void ClearBuffers();
void RenderPortals(int portalDepth);
void RenderSectors();
void RenderSubsector(subsector_t *sub, uint32_t ceilingSubsectorDepth, uint32_t floorSubsectorDepth);
void RenderLine(subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth);
void RenderSprite(AActor *thing, double sortDistance, const DVector2 &left, const DVector2 &right);
void RenderSprite(AActor *thing, double sortDistance, DVector2 left, DVector2 right, double t1, double t2, void *node);
void RenderSubsector(PolyRenderThread *thread, subsector_t *sub, uint32_t subsectorDepth);
void RenderLine(PolyRenderThread *thread, subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth);
void RenderSprite(PolyRenderThread *thread, AActor *thing, double sortDistance, const DVector2 &left, const DVector2 &right);
void RenderSprite(PolyRenderThread *thread, AActor *thing, double sortDistance, DVector2 left, DVector2 right, double t1, double t2, void *node);
void RenderPolySubsector(subsector_t *sub, uint32_t subsectorDepth, sector_t *frontsector);
void RenderPolyNode(void *node, uint32_t subsectorDepth, sector_t *frontsector);
void RenderPolySubsector(PolyRenderThread *thread, subsector_t *sub, uint32_t subsectorDepth, sector_t *frontsector);
void RenderPolyNode(PolyRenderThread *thread, void *node, uint32_t subsectorDepth, sector_t *frontsector);
static int PointOnSide(const DVector2 &pos, const node_t *node);
TriMatrix WorldToClip;
PolyClipPlane PortalPlane;
uint32_t StencilValue = 0;
PolyCull Cull;
uint32_t NextSubsectorDepth = 0;
std::set<sector_t *> SeenSectors;
std::unordered_map<subsector_t *, uint32_t> SubsectorDepths;
std::vector<PolyTranslucentObject *> TranslucentObjects;
bool PortalSegmentsAdded = false;
std::vector<std::vector<PolyTranslucentObject *>> TranslucentObjects;
std::vector<std::unique_ptr<PolyDrawSectorPortal>> SectorPortals;
std::vector<std::unique_ptr<PolyDrawLinePortal>> LinePortals;
bool PortalSegmentsAdded = false;
};

View file

@ -35,7 +35,7 @@ PolySkyDome::PolySkyDome()
CreateDome();
}
void PolySkyDome::Render(const TriMatrix &worldToClip)
void PolySkyDome::Render(PolyRenderThread *thread, const TriMatrix &worldToClip)
{
#ifdef USE_GL_DOME_MATH
TriMatrix modelMatrix = GLSkyMath();
@ -83,14 +83,13 @@ void PolySkyDome::Render(const TriMatrix &worldToClip)
PolyDrawArgs args;
args.SetLight(&NormalLight, 255, PolyRenderer::Instance()->Light.WallGlobVis(false), true);
args.SetSubsectorDepth(RenderPolyScene::SkySubsectorDepth);
args.SetTransform(&objectToClip);
args.SetStencilTestValue(255);
args.SetWriteStencil(true, 1);
args.SetClipPlane(PolyClipPlane(0.0f, 0.0f, 0.0f, 1.0f));
args.SetClipPlane(0, PolyClipPlane(0.0f, 0.0f, 0.0f, 1.0f));
RenderCapColorRow(args, mCurrentSetup.frontskytex, 0, false);
RenderCapColorRow(args, mCurrentSetup.frontskytex, rc, true);
RenderCapColorRow(thread, args, mCurrentSetup.frontskytex, 0, false);
RenderCapColorRow(thread, args, mCurrentSetup.frontskytex, rc, true);
args.SetTexture(mCurrentSetup.frontskytex);
@ -101,20 +100,20 @@ void PolySkyDome::Render(const TriMatrix &worldToClip)
for (int i = 1; i <= mRows; i++)
{
RenderRow(args, i, topcapcolor, topcapindex);
RenderRow(args, rc + i, bottomcapcolor, bottomcapindex);
RenderRow(thread, args, i, topcapcolor, topcapindex);
RenderRow(thread, args, rc + i, bottomcapcolor, bottomcapindex);
}
}
void PolySkyDome::RenderRow(PolyDrawArgs &args, int row, uint32_t capcolor, uint8_t capcolorindex)
void PolySkyDome::RenderRow(PolyRenderThread *thread, PolyDrawArgs &args, int row, uint32_t capcolor, uint8_t capcolorindex)
{
args.SetFaceCullCCW(false);
args.SetColor(capcolor, capcolorindex);
args.SetStyle(TriBlendMode::Skycap);
args.DrawArray(&mVertices[mPrimStart[row]], mPrimStart[row + 1] - mPrimStart[row], PolyDrawMode::TriangleStrip);
args.DrawArray(thread, &mVertices[mPrimStart[row]], mPrimStart[row + 1] - mPrimStart[row], PolyDrawMode::TriangleStrip);
}
void PolySkyDome::RenderCapColorRow(PolyDrawArgs &args, FTexture *skytex, int row, bool bottomCap)
void PolySkyDome::RenderCapColorRow(PolyRenderThread *thread, PolyDrawArgs &args, FTexture *skytex, int row, bool bottomCap)
{
uint32_t solid = skytex->GetSkyCapColor(bottomCap);
uint8_t palsolid = RGB32k.RGB[(RPART(solid) >> 3)][(GPART(solid) >> 3)][(BPART(solid) >> 3)];
@ -122,7 +121,7 @@ void PolySkyDome::RenderCapColorRow(PolyDrawArgs &args, FTexture *skytex, int ro
args.SetFaceCullCCW(bottomCap);
args.SetColor(solid, palsolid);
args.SetStyle(TriBlendMode::FillOpaque);
args.DrawArray(&mVertices[mPrimStart[row]], mPrimStart[row + 1] - mPrimStart[row], PolyDrawMode::TriangleFan);
args.DrawArray(thread, &mVertices[mPrimStart[row]], mPrimStart[row + 1] - mPrimStart[row], PolyDrawMode::TriangleFan);
}
void PolySkyDome::CreateDome()

View file

@ -46,7 +46,7 @@ class PolySkyDome
{
public:
PolySkyDome();
void Render(const TriMatrix &worldToClip);
void Render(PolyRenderThread *thread, const TriMatrix &worldToClip);
private:
TArray<FVector2> mInitialUV;
@ -58,8 +58,8 @@ private:
void SkyVertex(int r, int c, bool yflip);
void CreateSkyHemisphere(bool zflip);
void CreateDome();
void RenderRow(PolyDrawArgs &args, int row, uint32_t capcolor, uint8_t capcolorindex);
void RenderCapColorRow(PolyDrawArgs &args, FTexture *skytex, int row, bool bottomCap);
void RenderRow(PolyRenderThread *thread, PolyDrawArgs &args, int row, uint32_t capcolor, uint8_t capcolorindex);
void RenderCapColorRow(PolyRenderThread *thread, PolyDrawArgs &args, FTexture *skytex, int row, bool bottomCap);
TriVertex SetVertexXYZ(float xx, float yy, float zz, float uu = 0, float vv = 0);

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a sprite
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -28,11 +28,16 @@
#include "poly_sprite.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"
EXTERN_CVAR(Float, transsouls)
EXTERN_CVAR(Int, r_drawfuzz)
EXTERN_CVAR (Bool, r_debug_disable_vis_filter)
EXTERN_CVAR(Int, gl_spriteclip)
EXTERN_CVAR(Float, gl_sclipthreshold)
EXTERN_CVAR(Float, gl_sclipfactor)
extern uint32_t r_renderercaps;
bool RenderPolySprite::GetLine(AActor *thing, DVector2 &left, DVector2 &right)
@ -67,15 +72,24 @@ bool RenderPolySprite::GetLine(AActor *thing, DVector2 &left, DVector2 &right)
return true;
}
void RenderPolySprite::Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, float t1, float t2)
void RenderPolySprite::Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, AActor *thing, subsector_t *sub, uint32_t stencilValue, float t1, float t2)
{
DVector2 line[2];
if (!GetLine(thing, line[0], line[1]))
return;
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac);
pos.Z += thing->GetBobOffset(viewpoint.TicFrac);
DVector3 thingpos = thing->InterpolatedPosition(viewpoint.TicFrac);
DVector3 pos = thingpos;
uint32_t spritetype = (thing->renderflags & RF_SPRITETYPEMASK);
if (spritetype == RF_FACESPRITE)
pos.Z -= thing->Floorclip;
if (thing->flags2 & MF2_FLOATBOB)
pos.Z += thing->GetBobOffset(viewpoint.TicFrac);
bool flipTextureX = false;
FTexture *tex = GetSpriteTexture(thing, flipTextureX);
@ -85,26 +99,25 @@ void RenderPolySprite::Render(const TriMatrix &worldToClip, const PolyClipPlane
DVector2 spriteScale = thing->Scale;
double thingxscalemul = spriteScale.X / tex->Scale.X;
double thingyscalemul = spriteScale.Y / tex->Scale.Y;
double spriteHalfWidth = thingxscalemul * tex->GetWidth() * 0.5;
double spriteHeight = thingyscalemul * tex->GetHeight();
if (flipTextureX)
pos.X -= (tex->GetWidth() - tex->LeftOffset) * thingxscalemul;
else
pos.X -= tex->LeftOffset * thingxscalemul;
//pos.Z -= tex->TopOffset * thingyscalemul;
pos.Z -= (tex->GetHeight() - tex->TopOffset) * thingyscalemul + thing->Floorclip;
double spriteHalfWidth = thingxscalemul * tex->GetWidth() * 0.5;
double spriteHeight = thingyscalemul * tex->GetHeight();
pos.X += spriteHalfWidth;
pos.Z -= (tex->GetHeight() - tex->TopOffset) * thingyscalemul;
pos.Z = PerformSpriteClipAdjustment(thing, thingpos, spriteHeight, pos.Z);
//double depth = 1.0;
//visstyle_t visstyle = GetSpriteVisStyle(thing, depth);
// Rumor has it that AlterWeaponSprite needs to be called with visstyle passed in somewhere around here..
//R_SetColorMapLight(visstyle.BaseColormap, 0, visstyle.ColormapNum << FRACBITS);
TriVertex *vertices = PolyRenderer::Instance()->FrameMemory.AllocMemory<TriVertex>(4);
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(4);
bool foggy = false;
int actualextralight = foggy ? 0 : viewpoint.extralight << 4;
@ -142,20 +155,122 @@ void RenderPolySprite::Render(const TriMatrix &worldToClip, const PolyClipPlane
PolyDrawArgs args;
args.SetLight(GetColorTable(sub->sector->Colormap, sub->sector->SpecialColors[sector_t::sprites], true), lightlevel, PolyRenderer::Instance()->Light.SpriteGlobVis(foggy), fullbrightSprite);
args.SetSubsectorDepth(subsectorDepth);
args.SetTransform(&worldToClip);
args.SetFaceCullCCW(true);
args.SetStencilTestValue(stencilValue);
args.SetWriteStencil(true, stencilValue);
args.SetClipPlane(clipPlane);
args.SetClipPlane(0, clipPlane);
if ((thing->renderflags & RF_ZDOOMTRANS) && r_UseVanillaTransparency)
args.SetStyle(LegacyRenderStyles[STYLE_Normal], 1.0f, thing->fillcolor, thing->Translation, tex, fullbrightSprite);
else
args.SetStyle(thing->RenderStyle, thing->Alpha, thing->fillcolor, thing->Translation, tex, fullbrightSprite);
args.SetSubsectorDepthTest(true);
args.SetWriteSubsectorDepth(false);
args.SetDepthTest(true);
args.SetWriteDepth(false);
args.SetWriteStencil(false);
args.DrawArray(vertices, 4, PolyDrawMode::TriangleFan);
args.DrawArray(thread, vertices, 4, PolyDrawMode::TriangleFan);
}
double RenderPolySprite::GetSpriteFloorZ(AActor *thing, const DVector2 &thingpos)
{
extsector_t::xfloor &x = thing->Sector->e->XFloor;
for (unsigned int i = 0; i < x.ffloors.Size(); i++)
{
F3DFloor *ff = x.ffloors[i];
double floorh = ff->top.plane->ZatPoint(thingpos);
if (floorh == thing->floorz)
return floorh;
}
if (thing->Sector->heightsec && !(thing->Sector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC))
{
if (thing->flags2&MF2_ONMOBJ && thing->floorz == thing->Sector->heightsec->floorplane.ZatPoint(thingpos))
{
return thing->floorz;
}
}
return thing->Sector->floorplane.ZatPoint(thing) - thing->Floorclip;
}
double RenderPolySprite::GetSpriteCeilingZ(AActor *thing, const DVector2 &thingpos)
{
extsector_t::xfloor &x = thing->Sector->e->XFloor;
for (unsigned int i = 0; i < x.ffloors.Size(); i++)
{
F3DFloor *ff = x.ffloors[i];
double ceilingh = ff->bottom.plane->ZatPoint(thingpos);
if (ceilingh == thing->ceilingz)
return ceilingh;
}
if (thing->Sector->heightsec && !(thing->Sector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC))
{
if (thing->flags2&MF2_ONMOBJ && thing->ceilingz == thing->Sector->heightsec->ceilingplane.ZatPoint(thingpos))
{
return thing->ceilingz;
}
}
return thing->Sector->ceilingplane.ZatPoint(thingpos);
}
double RenderPolySprite::PerformSpriteClipAdjustment(AActor *thing, const DVector2 &thingpos, double spriteheight, double z2)
{
int spriteclip = 2; // gl_spriteclip, but use 'always' mode for now
double z1 = z2 + spriteheight;
// Tests show that this doesn't look good for many decorations and corpses
uint32_t spritetype = (thing->renderflags & RF_SPRITETYPEMASK);
if (!(spriteheight > 0 && spriteclip > 0 && spritetype == RF_FACESPRITE))
return z2;
bool clipthing = (thing->player || thing->flags3&MF3_ISMONSTER || thing->IsKindOf(RUNTIME_CLASS(AInventory))) && (thing->flags&MF_ICECORPSE || !(thing->flags&MF_CORPSE));
bool smarterclip = !clipthing && spriteclip == 3;
if (clipthing || spriteclip > 1)
{
double diffb = MIN(z2 - GetSpriteFloorZ(thing, thingpos), 0.0);
// Adjust sprites clipping into ceiling and adjust clipping adjustment for tall graphics
if (smarterclip)
{
// Reduce slightly clipping adjustment of corpses
if (thing->flags & MF_CORPSE || spriteheight > fabs(diffb))
{
double ratio = clamp<double>((fabs(diffb) * (double)gl_sclipfactor / (spriteheight + 1)), 0.5, 1.0);
diffb *= ratio;
}
if (!diffb)
{
double difft = MAX(z1 - GetSpriteCeilingZ(thing, thingpos), 0.0);
if (difft >= (double)gl_sclipthreshold)
{
// dumb copy of the above.
if (!(thing->flags3&MF3_ISMONSTER) || (thing->flags&MF_NOGRAVITY) || (thing->flags&MF_CORPSE) || difft > (double)gl_sclipthreshold)
{
difft = 0;
}
}
if (spriteheight > fabs(difft))
{
double ratio = clamp<double>((fabs(difft) * (double)gl_sclipfactor / (spriteheight + 1)), 0.5, 1.0);
difft *= ratio;
}
z2 -= difft;
}
}
if (diffb <= (0 - (double)gl_sclipthreshold)) // such a large displacement can't be correct!
{
// for living monsters standing on the floor allow a little more.
if (!(thing->flags3&MF3_ISMONSTER) || (thing->flags&MF_NOGRAVITY) || (thing->flags&MF_CORPSE) || diffb < (-1.8*(double)gl_sclipthreshold))
{
diffb = 0;
}
}
z2 -= diffb;
}
return z2;
}
bool RenderPolySprite::IsThingCulled(AActor *thing)
@ -180,7 +295,7 @@ bool RenderPolySprite::IsThingCulled(AActor *thing)
// check renderrequired vs ~r_rendercaps, if anything matches we don't support that feature,
// check renderhidden vs r_rendercaps, if anything matches we do support that feature and should hide it.
if (!r_debug_disable_vis_filter && (!!(thing->RenderRequired & ~r_renderercaps)) ||
if ((!r_debug_disable_vis_filter && !!(thing->RenderRequired & ~r_renderercaps)) ||
(!!(thing->RenderHidden & r_renderercaps)))
return true;

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a sprite
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -27,12 +27,40 @@
class RenderPolySprite
{
public:
void Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, float t1, float t2);
void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, AActor *thing, subsector_t *sub, uint32_t stencilValue, float t1, float t2);
static bool GetLine(AActor *thing, DVector2 &left, DVector2 &right);
static bool IsThingCulled(AActor *thing);
static FTexture *GetSpriteTexture(AActor *thing, /*out*/ bool &flipX);
private:
//visstyle_t GetSpriteVisStyle(AActor *thing, double z);
static double PerformSpriteClipAdjustment(AActor *thing, const DVector2 &thingpos, double spriteheight, double z);
static double GetSpriteFloorZ(AActor *thing, const DVector2 &thingpos);
static double GetSpriteCeilingZ(AActor *thing, const DVector2 &thingpos);
};
class PolyTranslucentThing : public PolyTranslucentObject
{
public:
PolyTranslucentThing(AActor *thing, subsector_t *sub, uint32_t subsectorDepth, double dist, float t1, float t2, uint32_t stencilValue) : PolyTranslucentObject(subsectorDepth, dist), thing(thing), sub(sub), SpriteLeft(t1), SpriteRight(t2), StencilValue(stencilValue) { }
void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &portalPlane) override
{
if ((thing->renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE)
{
RenderPolyWallSprite wallspr;
wallspr.Render(thread, worldToClip, portalPlane, thing, sub, StencilValue + 1);
}
else
{
RenderPolySprite spr;
spr.Render(thread, worldToClip, portalPlane, thing, sub, StencilValue + 1, SpriteLeft, SpriteRight);
}
}
AActor *thing = nullptr;
subsector_t *sub = nullptr;
float SpriteLeft = 0.0f;
float SpriteRight = 1.0f;
uint32_t StencilValue = 0;
};

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a wall
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -33,11 +33,13 @@
#include "polyrenderer/poly_renderer.h"
#include "r_sky.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/poly_renderthread.h"
#include "g_levellocals.h"
EXTERN_CVAR(Bool, r_drawmirrors)
EXTERN_CVAR(Bool, r_fogboundary)
bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector<PolyTranslucentObject*> &translucentWallsOutput, std::vector<std::unique_ptr<PolyDrawLinePortal>> &linePortals, line_t *lastPortalLine)
bool RenderPolyWall::RenderLine(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector<PolyTranslucentObject*> &translucentWallsOutput, std::vector<std::unique_ptr<PolyDrawLinePortal>> &linePortals, line_t *lastPortalLine)
{
double frontceilz1 = frontsector->ceilingplane.ZatPoint(line->v1);
double frontfloorz1 = frontsector->floorplane.ZatPoint(line->v1);
@ -88,6 +90,7 @@ bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, const PolyClipPlan
wall.LineSeg = line;
wall.Line = line->linedef;
wall.Side = line->sidedef;
wall.LineSegLine = line->linedef;
wall.Colormap = GetColorTable(frontsector->Colormap, frontsector->SpecialColors[sector_t::walltop]);
wall.Masked = false;
wall.SubsectorDepth = subsectorDepth;
@ -100,15 +103,16 @@ bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, const PolyClipPlan
wall.SetCoords(line->v1->fPos(), line->v2->fPos(), frontceilz1, frontfloorz1, frontceilz2, frontfloorz2);
wall.TopTexZ = topTexZ;
wall.BottomTexZ = bottomTexZ;
wall.Texpart = side_t::mid;
wall.Wallpart = side_t::mid;
wall.Texture = GetTexture(wall.Line, wall.Side, side_t::mid);
wall.Polyportal = polyportal;
wall.Render(worldToClip, clipPlane, cull);
wall.Render(thread, worldToClip, clipPlane);
return true;
}
}
else
{
sector_t *backsector = (line->backsector != line->frontsector) ? line->backsector : line->frontsector;
sector_t *backsector = line->backsector;
double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1);
double backfloorz1 = backsector->floorplane.ZatPoint(line->v1);
@ -136,8 +140,9 @@ bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, const PolyClipPlan
wall.SetCoords(line->v1->fPos(), line->v2->fPos(), topceilz1, topfloorz1, topceilz2, topfloorz2);
wall.TopTexZ = topTexZ;
wall.BottomTexZ = MIN(MIN(backceilz1, frontceilz1), MIN(backceilz2, frontceilz2));
wall.Texpart = side_t::top;
wall.Render(worldToClip, clipPlane, cull);
wall.Wallpart = side_t::top;
wall.Texture = GetTexture(wall.Line, wall.Side, side_t::top);
wall.Render(thread, worldToClip, clipPlane);
}
if ((bottomfloorz1 < bottomceilz1 || bottomfloorz2 < bottomceilz2) && line->sidedef && !bothSkyFloor)
@ -147,8 +152,9 @@ bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, const PolyClipPlan
wall.BottomTexZ = bottomTexZ;
wall.UnpeggedCeil1 = topceilz1;
wall.UnpeggedCeil2 = topceilz2;
wall.Texpart = side_t::bottom;
wall.Render(worldToClip, clipPlane, cull);
wall.Wallpart = side_t::bottom;
wall.Texture = GetTexture(wall.Line, wall.Side, side_t::bottom);
wall.Render(thread, worldToClip, clipPlane);
}
if (line->sidedef)
@ -156,47 +162,86 @@ bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, const PolyClipPlan
wall.SetCoords(line->v1->fPos(), line->v2->fPos(), middleceilz1, middlefloorz1, middleceilz2, middlefloorz2);
wall.TopTexZ = MAX(middleceilz1, middleceilz2);
wall.BottomTexZ = MIN(middlefloorz1, middlefloorz2);
wall.Texpart = side_t::mid;
wall.Wallpart = side_t::mid;
wall.Texture = GetTexture(wall.Line, wall.Side, side_t::mid);
wall.Masked = true;
wall.Additive = !!(wall.Line->flags & ML_ADDTRANS);
wall.Alpha = wall.Line->alpha;
wall.FogBoundary = IsFogBoundary(frontsector, backsector);
FTexture *midtex = TexMan(line->sidedef->GetTexture(side_t::mid), true);
if (midtex && midtex->UseType != FTexture::TEX_Null)
translucentWallsOutput.push_back(PolyRenderer::Instance()->FrameMemory.NewObject<PolyTranslucentObject>(wall));
if ((midtex && midtex->UseType != FTexture::TEX_Null) || wall.FogBoundary)
translucentWallsOutput.push_back(thread->FrameMemory->NewObject<PolyTranslucentWall>(wall));
if (polyportal)
{
wall.Polyportal = polyportal;
wall.Render(worldToClip, clipPlane, cull);
wall.Render(thread, worldToClip, clipPlane);
}
}
}
return polyportal != nullptr;
}
void RenderPolyWall::Render3DFloorLine(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector<PolyTranslucentObject*> &translucentWallsOutput)
bool RenderPolyWall::IsFogBoundary(sector_t *front, sector_t *back)
{
return r_fogboundary && PolyCameraLight::Instance()->FixedColormap() == nullptr && front->Colormap.FadeColor &&
front->Colormap.FadeColor != back->Colormap.FadeColor &&
(front->GetTexture(sector_t::ceiling) != skyflatnum || back->GetTexture(sector_t::ceiling) != skyflatnum);
}
void RenderPolyWall::Render3DFloorLine(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector<PolyTranslucentObject*> &translucentWallsOutput)
{
if (!(fakeFloor->flags & FF_EXISTS)) return;
if (!(fakeFloor->flags & FF_RENDERPLANES)) return;
if (fakeFloor->flags & FF_SWIMMABLE) return;
if (!fakeFloor->model) return;
if (fakeFloor->alpha == 0) return;
double frontceilz1 = fakeFloor->top.plane->ZatPoint(line->v1);
double frontfloorz1 = fakeFloor->bottom.plane->ZatPoint(line->v1);
double frontceilz2 = fakeFloor->top.plane->ZatPoint(line->v2);
double frontfloorz2 = fakeFloor->bottom.plane->ZatPoint(line->v2);
double topTexZ = frontsector->GetPlaneTexZ(sector_t::ceiling);
double bottomTexZ = frontsector->GetPlaneTexZ(sector_t::floor);
double topTexZ = fakeFloor->model->GetPlaneTexZ(sector_t::ceiling);
double bottomTexZ = fakeFloor->model->GetPlaneTexZ(sector_t::floor);
if (frontceilz1 <= frontfloorz1 || frontceilz2 <= frontfloorz2)
return;
RenderPolyWall wall;
wall.LineSeg = line;
wall.LineSegLine = line->linedef;
wall.Line = fakeFloor->master;
wall.Side = fakeFloor->master->sidedef[0];
wall.Colormap = GetColorTable(frontsector->Colormap, frontsector->SpecialColors[sector_t::walltop]);
wall.Masked = false;
wall.Additive = !!(fakeFloor->flags & FF_ADDITIVETRANS);
if (!wall.Additive && fakeFloor->alpha == 255)
{
wall.Masked = false;
wall.Alpha = 1.0;
}
else
{
wall.Masked = true;
wall.Alpha = fakeFloor->alpha / 255.0;
}
wall.SubsectorDepth = subsectorDepth;
wall.StencilValue = stencilValue;
wall.SetCoords(line->v1->fPos(), line->v2->fPos(), frontceilz1, frontfloorz1, frontceilz2, frontfloorz2);
wall.TopTexZ = topTexZ;
wall.BottomTexZ = bottomTexZ;
wall.UnpeggedCeil1 = frontceilz1;
wall.UnpeggedCeil2 = frontceilz2;
wall.Texpart = side_t::mid;
wall.Render(worldToClip, clipPlane, cull);
wall.Wallpart = side_t::mid;
if (fakeFloor->flags & FF_UPPERTEXTURE)
wall.Texture = GetTexture(line->linedef, line->sidedef, side_t::top);
else if (fakeFloor->flags & FF_LOWERTEXTURE)
wall.Texture = GetTexture(line->linedef, line->sidedef, side_t::bottom);
else
wall.Texture = GetTexture(wall.Line, wall.Side, side_t::mid);
if (!wall.Masked)
wall.Render(thread, worldToClip, clipPlane);
else
translucentWallsOutput.push_back(thread->FrameMemory->NewObject<PolyTranslucentWall>(wall));
}
void RenderPolyWall::SetCoords(const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2)
@ -209,14 +254,13 @@ void RenderPolyWall::SetCoords(const DVector2 &v1, const DVector2 &v2, double ce
this->floor2 = floor2;
}
void RenderPolyWall::Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, PolyCull &cull)
void RenderPolyWall::Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane)
{
bool foggy = false;
FTexture *tex = GetTexture();
if (!tex && !Polyportal)
if (!Texture && !Polyportal && !FogBoundary)
return;
TriVertex *vertices = PolyRenderer::Instance()->FrameMemory.AllocMemory<TriVertex>(4);
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(4);
vertices[0].x = (float)v1.X;
vertices[0].y = (float)v1.Y;
@ -238,11 +282,11 @@ void RenderPolyWall::Render(const TriMatrix &worldToClip, const PolyClipPlane &c
vertices[3].z = (float)floor1;
vertices[3].w = 1.0f;
if (tex)
if (Texture)
{
PolyWallTextureCoordsU texcoordsU(tex, LineSeg, Line, Side, Texpart);
PolyWallTextureCoordsV texcoordsVLeft(tex, Line, Side, Texpart, ceil1, floor1, UnpeggedCeil1, TopTexZ, BottomTexZ);
PolyWallTextureCoordsV texcoordsVRght(tex, Line, Side, Texpart, ceil2, floor2, UnpeggedCeil2, TopTexZ, BottomTexZ);
PolyWallTextureCoordsU texcoordsU(Texture, LineSeg, LineSegLine, Side, Wallpart);
PolyWallTextureCoordsV texcoordsVLeft(Texture, Line, Side, Wallpart, ceil1, floor1, UnpeggedCeil1, TopTexZ, BottomTexZ);
PolyWallTextureCoordsV texcoordsVRght(Texture, Line, Side, Wallpart, ceil2, floor2, UnpeggedCeil2, TopTexZ, BottomTexZ);
vertices[0].u = (float)texcoordsU.u1;
vertices[0].v = (float)texcoordsVLeft.v1;
vertices[1].u = (float)texcoordsU.u2;
@ -264,47 +308,115 @@ void RenderPolyWall::Render(const TriMatrix &worldToClip, const PolyClipPlane &c
// Masked walls clamp to the 0-1 range (no texture repeat)
if (Masked)
{
ClampHeight(vertices[0], vertices[3]);
ClampHeight(vertices[1], vertices[2]);
bool wrap = (Line->flags & ML_WRAP_MIDTEX) || (Side->Flags & WALLF_WRAP_MIDTEX);
if (!wrap)
{
ClampHeight(vertices[0], vertices[3]);
ClampHeight(vertices[1], vertices[2]);
}
}
PolyDrawArgs args;
args.SetLight(GetColorTable(Line->frontsector->Colormap, Line->frontsector->SpecialColors[sector_t::walltop]), GetLightLevel(), PolyRenderer::Instance()->Light.WallGlobVis(foggy), false);
args.SetSubsectorDepth(SubsectorDepth);
args.SetTransform(&worldToClip);
args.SetFaceCullCCW(true);
args.SetStencilTestValue(StencilValue);
args.SetWriteStencil(true, StencilValue + 1);
if (tex && !Polyportal)
args.SetTexture(tex);
args.SetClipPlane(clipPlane);
if (Texture && !Polyportal)
args.SetTexture(Texture);
args.SetClipPlane(0, clipPlane);
if (FogBoundary)
{
args.SetStyle(TriBlendMode::FogBoundary);
args.SetColor(0xffffffff, 254);
args.SetDepthTest(true);
args.SetWriteDepth(true);
args.SetWriteStencil(false);
args.DrawArray(thread, vertices, 4, PolyDrawMode::TriangleFan);
if (!Texture)
return;
}
if (Polyportal)
{
args.SetWriteStencil(true, Polyportal->StencilValue);
args.SetWriteColor(false);
args.SetWriteSubsectorDepth(false);
args.DrawArray(vertices, 4, PolyDrawMode::TriangleFan);
Polyportal->Shape.push_back({ vertices, 4, true, SubsectorDepth });
args.SetWriteDepth(false);
args.DrawArray(thread, vertices, 4, PolyDrawMode::TriangleFan);
Polyportal->Shape.push_back({ vertices, 4, true });
}
else if (!Masked)
{
args.SetStyle(TriBlendMode::TextureOpaque);
args.DrawArray(vertices, 4, PolyDrawMode::TriangleFan);
DrawStripes(thread, args, vertices);
}
else
{
bool addtrans = !!(Line->flags & ML_ADDTRANS);
double srcalpha = MIN(Line->alpha, 1.0);
double destalpha = addtrans ? 1.0 : 1.0 - srcalpha;
double srcalpha = MIN(Alpha, 1.0);
double destalpha = Additive ? 1.0 : 1.0 - srcalpha;
args.SetStyle(TriBlendMode::TextureAdd, srcalpha, destalpha);
args.SetSubsectorDepthTest(true);
args.SetWriteSubsectorDepth(true);
args.SetDepthTest(true);
args.SetWriteDepth(true);
args.SetWriteStencil(false);
args.DrawArray(vertices, 4, PolyDrawMode::TriangleFan);
DrawStripes(thread, args, vertices);
}
RenderPolyDecal::RenderWallDecals(worldToClip, clipPlane, LineSeg, SubsectorDepth, StencilValue);
RenderPolyDecal::RenderWallDecals(thread, worldToClip, clipPlane, LineSeg, StencilValue);
}
void RenderPolyWall::DrawStripes(PolyRenderThread *thread, PolyDrawArgs &args, TriVertex *vertices)
{
const auto &lightlist = Line->frontsector->e->XFloor.lightlist;
if (lightlist.Size() > 0)
{
PolyClipPlane topPlane;
for (unsigned int i = 0; i < lightlist.Size(); i++)
{
lightlist_t *lit = &lightlist[i];
DVector3 normal = lit->plane.Normal();
double d = lit->plane.fD();
if (normal.Z < 0.0)
{
normal = -normal;
d = -d;
}
PolyClipPlane bottomPlane = { (float)normal.X, (float)normal.Y, (float)normal.Z, (float)d };
args.SetClipPlane(1, topPlane);
args.SetClipPlane(2, bottomPlane);
args.DrawArray(thread, vertices, 4, PolyDrawMode::TriangleFan);
FDynamicColormap *basecolormap = GetColorTable(lit->extra_colormap, Line->frontsector->SpecialColors[sector_t::walltop]);
bool foggy = false;
int lightlevel;
PolyCameraLight *cameraLight = PolyCameraLight::Instance();
if (cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap())
{
lightlevel = 255;
}
else
{
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
lightlevel = clamp(Side->GetLightLevel(foggy, *lit->p_lightlevel) + actualextralight, 0, 255);
}
args.SetLight(basecolormap, lightlevel, PolyRenderer::Instance()->Light.WallGlobVis(foggy), false);
topPlane = { (float)-normal.X, (float)-normal.Y, (float)-normal.Z, (float)-d };
}
args.SetClipPlane(1, topPlane);
args.SetClipPlane(2, PolyClipPlane());
args.DrawArray(thread, vertices, 4, PolyDrawMode::TriangleFan);
}
else
{
args.DrawArray(thread, vertices, 4, PolyDrawMode::TriangleFan);
}
}
void RenderPolyWall::ClampHeight(TriVertex &v1, TriVertex &v2)
@ -327,27 +439,27 @@ void RenderPolyWall::ClampHeight(TriVertex &v1, TriVertex &v2)
v2.v = texv1 * inv_t2 + texv2 * t2;
}
FTexture *RenderPolyWall::GetTexture()
FTexture *RenderPolyWall::GetTexture(const line_t *line, const side_t *side, side_t::ETexpart texpart)
{
FTexture *tex = TexMan(Side->GetTexture(Texpart), true);
FTexture *tex = TexMan(side->GetTexture(texpart), true);
if (tex == nullptr || tex->UseType == FTexture::TEX_Null)
{
// Mapping error. Doom floodfills this with a plane.
// This code doesn't do that, but at least it uses the "right" texture..
if (Line && Line->backsector && Line->sidedef[0] == Side)
if (line && line->backsector && line->sidedef[0] == side)
{
if (Texpart == side_t::top)
tex = TexMan(Line->backsector->GetTexture(sector_t::ceiling), true);
else if (Texpart == side_t::bottom)
tex = TexMan(Line->backsector->GetTexture(sector_t::floor), true);
if (texpart == side_t::top)
tex = TexMan(line->backsector->GetTexture(sector_t::ceiling), true);
else if (texpart == side_t::bottom)
tex = TexMan(line->backsector->GetTexture(sector_t::floor), true);
}
if (Line && Line->backsector && Line->sidedef[1] == Side)
if (line && line->backsector && line->sidedef[1] == side)
{
if (Texpart == side_t::top)
tex = TexMan(Line->frontsector->GetTexture(sector_t::ceiling), true);
else if (Texpart == side_t::bottom)
tex = TexMan(Line->frontsector->GetTexture(sector_t::floor), true);
if (texpart == side_t::top)
tex = TexMan(line->frontsector->GetTexture(sector_t::ceiling), true);
else if (texpart == side_t::bottom)
tex = TexMan(line->frontsector->GetTexture(sector_t::floor), true);
}
if (tex == nullptr || tex->UseType == FTexture::TEX_Null)
@ -373,59 +485,62 @@ int RenderPolyWall::GetLightLevel()
/////////////////////////////////////////////////////////////////////////////
PolyWallTextureCoordsU::PolyWallTextureCoordsU(FTexture *tex, const seg_t *lineseg, const line_t *line, const side_t *side, side_t::ETexpart texpart)
PolyWallTextureCoordsU::PolyWallTextureCoordsU(FTexture *tex, const seg_t *lineseg, const line_t *line, const side_t *side, side_t::ETexpart wallpart)
{
// Calculate the U texture coordinate for the line
double lineu1 = side->GetTextureXOffset(wallpart);
double lineu2 = side->GetTextureXOffset(wallpart) + line->sidedef[0]->TexelLength * side->GetTextureXScale(wallpart);
lineu1 *= tex->Scale.X / tex->GetWidth();
lineu2 *= tex->Scale.X / tex->GetWidth();
// Calculate where we are on the lineseg
double t1, t2;
double deltaX = line->v2->fX() - line->v1->fX();
double deltaY = line->v2->fY() - line->v1->fY();
if (fabs(deltaX) > fabs(deltaY))
if (fabs(line->delta.X) > fabs(line->delta.Y))
{
t1 = (lineseg->v1->fX() - line->v1->fX()) / deltaX;
t2 = (lineseg->v2->fX() - line->v1->fX()) / deltaX;
t1 = (lineseg->v1->fX() - line->v1->fX()) / line->delta.X;
t2 = (lineseg->v2->fX() - line->v1->fX()) / line->delta.X;
}
else
{
t1 = (lineseg->v1->fY() - line->v1->fY()) / deltaY;
t2 = (lineseg->v2->fY() - line->v1->fY()) / deltaY;
t1 = (lineseg->v1->fY() - line->v1->fY()) / line->delta.Y;
t2 = (lineseg->v2->fY() - line->v1->fY()) / line->delta.Y;
}
int texWidth = tex->GetWidth();
double uscale = side->GetTextureXScale(texpart) * tex->Scale.X;
u1 = t1 * side->TexelLength + side->GetTextureXOffset(texpart);
u2 = t2 * side->TexelLength + side->GetTextureXOffset(texpart);
u1 *= uscale;
u2 *= uscale;
u1 /= texWidth;
u2 /= texWidth;
// Check if lineseg is the backside of the line
if (t2 < t1)
{
std::swap(lineu1, lineu2);
}
// Calculate texture coordinates for the lineseg
u1 = (1.0 - t1) * lineu1 + t1 * lineu2;
u2 = (1.0 - t2) * lineu1 + t2 * lineu2;
}
/////////////////////////////////////////////////////////////////////////////
PolyWallTextureCoordsV::PolyWallTextureCoordsV(FTexture *tex, const line_t *line, const side_t *side, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil, double topTexZ, double bottomTexZ)
PolyWallTextureCoordsV::PolyWallTextureCoordsV(FTexture *tex, const line_t *line, const side_t *side, side_t::ETexpart wallpart, double topz, double bottomz, double unpeggedceil, double topTexZ, double bottomTexZ)
{
double vscale = side->GetTextureYScale(texpart) * tex->Scale.Y;
double yoffset = side->GetTextureYOffset(texpart);
double yoffset = side->GetTextureYOffset(wallpart);
if (tex->bWorldPanning)
yoffset *= vscale;
yoffset *= side->GetTextureYScale(wallpart) * tex->Scale.Y;
switch (texpart)
switch (wallpart)
{
default:
case side_t::mid:
CalcVMidPart(tex, line, side, topTexZ, bottomTexZ, vscale, yoffset);
CalcVMidPart(tex, line, side, topTexZ, bottomTexZ, yoffset);
break;
case side_t::top:
CalcVTopPart(tex, line, side, topTexZ, bottomTexZ, vscale, yoffset);
CalcVTopPart(tex, line, side, topTexZ, bottomTexZ, yoffset);
break;
case side_t::bottom:
CalcVBottomPart(tex, line, side, topTexZ, bottomTexZ, unpeggedceil, vscale, yoffset);
CalcVBottomPart(tex, line, side, topTexZ, bottomTexZ, unpeggedceil, yoffset);
break;
}
int texHeight = tex->GetHeight();
v1 /= texHeight;
v2 /= texHeight;
v1 *= tex->Scale.Y / tex->GetHeight();
v2 *= tex->Scale.Y / tex->GetHeight();
double texZHeight = (bottomTexZ - topTexZ);
if (texZHeight > 0.0f || texZHeight < -0.0f)
@ -439,60 +554,53 @@ PolyWallTextureCoordsV::PolyWallTextureCoordsV(FTexture *tex, const line_t *line
}
}
void PolyWallTextureCoordsV::CalcVTopPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double vscale, double yoffset)
void PolyWallTextureCoordsV::CalcVTopPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double yoffset)
{
bool pegged = (line->flags & ML_DONTPEGTOP) == 0;
if (pegged) // bottom to top
{
int texHeight = tex->GetHeight();
v1 = -yoffset;
v2 = v1 + (topz - bottomz);
v1 *= vscale;
v2 *= vscale;
double texHeight = tex->GetHeight() / tex->Scale.Y;
v1 = (topz - bottomz) * side->GetTextureYScale(side_t::top) - yoffset;
v2 = -yoffset;
v1 = texHeight - v1;
v2 = texHeight - v2;
std::swap(v1, v2);
}
else // top to bottom
{
v1 = yoffset;
v2 = v1 + (topz - bottomz);
v1 *= vscale;
v2 *= vscale;
v2 = (topz - bottomz) * side->GetTextureYScale(side_t::top) + yoffset;
}
}
void PolyWallTextureCoordsV::CalcVMidPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double vscale, double yoffset)
{
bool pegged = (line->flags & ML_DONTPEGBOTTOM) == 0;
if (pegged) // top to bottom
{
v1 = yoffset * vscale;
v2 = (yoffset + (topz - bottomz)) * vscale;
}
else // bottom to top
{
int texHeight = tex->GetHeight();
v1 = texHeight - (-yoffset + (topz - bottomz)) * vscale;
v2 = texHeight + yoffset * vscale;
}
}
void PolyWallTextureCoordsV::CalcVBottomPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double unpeggedceil, double vscale, double yoffset)
void PolyWallTextureCoordsV::CalcVMidPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double yoffset)
{
bool pegged = (line->flags & ML_DONTPEGBOTTOM) == 0;
if (pegged) // top to bottom
{
v1 = yoffset;
v2 = v1 + (topz - bottomz);
v1 *= vscale;
v2 *= vscale;
v2 = (topz - bottomz) * side->GetTextureYScale(side_t::mid) + yoffset;
}
else // bottom to top
{
double texHeight = tex->GetHeight() / tex->Scale.Y;
v1 = yoffset - (topz - bottomz) * side->GetTextureYScale(side_t::mid);
v2 = yoffset;
v1 = texHeight + v1;
v2 = texHeight + v2;
}
}
void PolyWallTextureCoordsV::CalcVBottomPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double unpeggedceil, double yoffset)
{
bool pegged = (line->flags & ML_DONTPEGBOTTOM) == 0;
if (pegged) // top to bottom
{
v1 = yoffset;
v2 = yoffset + (topz - bottomz) * side->GetTextureYScale(side_t::bottom);
}
else
{
v1 = yoffset + (unpeggedceil - topz);
v2 = v1 + (topz - bottomz);
v1 *= vscale;
v2 *= vscale;
v1 = yoffset + (unpeggedceil - topz) * side->GetTextureYScale(side_t::bottom);
v2 = yoffset + (unpeggedceil - bottomz) * side->GetTextureYScale(side_t::bottom);
}
}

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a wall
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -31,11 +31,11 @@ class PolyCull;
class RenderPolyWall
{
public:
static bool RenderLine(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector<PolyTranslucentObject*> &translucentWallsOutput, std::vector<std::unique_ptr<PolyDrawLinePortal>> &linePortals, line_t *lastPortalLine);
static void Render3DFloorLine(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector<PolyTranslucentObject*> &translucentWallsOutput);
static bool RenderLine(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector<PolyTranslucentObject*> &translucentWallsOutput, std::vector<std::unique_ptr<PolyDrawLinePortal>> &linePortals, line_t *lastPortalLine);
static void Render3DFloorLine(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector<PolyTranslucentObject*> &translucentWallsOutput);
void SetCoords(const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2);
void Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, PolyCull &cull);
void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane);
DVector2 v1;
DVector2 v2;
@ -45,29 +45,37 @@ public:
double floor2 = 0.0;
const seg_t *LineSeg = nullptr;
const line_t *LineSegLine = nullptr;
const line_t *Line = nullptr;
const side_t *Side = nullptr;
side_t::ETexpart Texpart = side_t::mid;
FTexture *Texture = nullptr;
side_t::ETexpart Wallpart = side_t::mid;
double TopTexZ = 0.0;
double BottomTexZ = 0.0;
double UnpeggedCeil1 = 0.0;
double UnpeggedCeil2 = 0.0;
FSWColormap *Colormap = nullptr;
bool Masked = false;
bool Additive = false;
double Alpha = 1.0;
bool FogBoundary = false;
uint32_t SubsectorDepth = 0;
uint32_t StencilValue = 0;
PolyDrawLinePortal *Polyportal = nullptr;
private:
void ClampHeight(TriVertex &v1, TriVertex &v2);
FTexture *GetTexture();
int GetLightLevel();
void DrawStripes(PolyRenderThread *thread, PolyDrawArgs &args, TriVertex *vertices);
static bool IsFogBoundary(sector_t *front, sector_t *back);
static FTexture *GetTexture(const line_t *Line, const side_t *Side, side_t::ETexpart texpart);
};
class PolyWallTextureCoordsU
{
public:
PolyWallTextureCoordsU(FTexture *tex, const seg_t *lineseg, const line_t *line, const side_t *side, side_t::ETexpart texpart);
PolyWallTextureCoordsU(FTexture *tex, const seg_t *lineseg, const line_t *linesegline, const side_t *side, side_t::ETexpart wallpart);
double u1, u2;
};
@ -75,12 +83,25 @@ public:
class PolyWallTextureCoordsV
{
public:
PolyWallTextureCoordsV(FTexture *tex, const line_t *line, const side_t *side, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil, double topTexZ, double bottomTexZ);
PolyWallTextureCoordsV(FTexture *tex, const line_t *line, const side_t *side, side_t::ETexpart wallpart, double topz, double bottomz, double unpeggedceil, double topTexZ, double bottomTexZ);
double v1, v2;
private:
void CalcVTopPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double vscale, double yoffset);
void CalcVMidPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double vscale, double yoffset);
void CalcVBottomPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double unpeggedceil, double vscale, double yoffset);
void CalcVTopPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double yoffset);
void CalcVMidPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double yoffset);
void CalcVBottomPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double unpeggedceil, double yoffset);
};
class PolyTranslucentWall : public PolyTranslucentObject
{
public:
PolyTranslucentWall(RenderPolyWall wall) : PolyTranslucentObject(wall.SubsectorDepth, 1e6), wall(wall) { }
void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &portalPlane) override
{
wall.Render(thread, worldToClip, portalPlane);
}
RenderPolyWall wall;
};

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a sprite
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -28,8 +28,9 @@
#include "poly_wallsprite.h"
#include "polyrenderer/poly_renderer.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/poly_renderthread.h"
void RenderPolyWallSprite::Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue)
void RenderPolyWallSprite::Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, AActor *thing, subsector_t *sub, uint32_t stencilValue)
{
if (RenderPolySprite::IsThingCulled(thing))
return;
@ -69,7 +70,7 @@ void RenderPolyWallSprite::Render(const TriMatrix &worldToClip, const PolyClipPl
DVector2 points[2] = { left, right };
TriVertex *vertices = PolyRenderer::Instance()->FrameMemory.AllocMemory<TriVertex>(4);
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(4);
bool foggy = false;
int actualextralight = foggy ? 0 : viewpoint.extralight << 4;
@ -105,10 +106,10 @@ void RenderPolyWallSprite::Render(const TriMatrix &worldToClip, const PolyClipPl
args.SetFaceCullCCW(true);
args.SetStencilTestValue(stencilValue);
args.SetTexture(tex);
args.SetClipPlane(clipPlane);
args.SetSubsectorDepthTest(true);
args.SetWriteSubsectorDepth(false);
args.SetClipPlane(0, clipPlane);
args.SetDepthTest(true);
args.SetWriteDepth(false);
args.SetWriteStencil(false);
args.SetStyle(TriBlendMode::TextureMasked);
args.DrawArray(vertices, 4, PolyDrawMode::TriangleFan);
args.DrawArray(thread, vertices, 4, PolyDrawMode::TriangleFan);
}

View file

@ -1,5 +1,5 @@
/*
** Handling drawing a wall sprite
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
@ -27,5 +27,5 @@
class RenderPolyWallSprite
{
public:
void Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue);
void Render(PolyRenderThread *thread, const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, AActor *thing, subsector_t *sub, uint32_t stencilValue);
};

View file

@ -149,9 +149,16 @@ bool I_SetCursor(FTexture *);
struct findstate_t
{
private:
int count;
struct dirent **namelist;
int current;
friend void *I_FindFirst(const char *filespec, findstate_t *fileinfo);
friend int I_FindNext(void *handle, findstate_t *fileinfo);
friend const char *I_FindName(findstate_t *fileinfo);
friend int I_FindAttr(findstate_t *fileinfo);
friend int I_FindClose(void *handle);
};
void *I_FindFirst (const char *filespec, findstate_t *fileinfo);
@ -159,7 +166,10 @@ int I_FindNext (void *handle, findstate_t *fileinfo);
int I_FindClose (void *handle);
int I_FindAttr (findstate_t *fileinfo);
#define I_FindName(a) ((a)->namelist[(a)->current]->d_name)
inline const char *I_FindName(findstate_t *fileinfo)
{
return (fileinfo->namelist[fileinfo->current]->d_name);
}
#define FA_RDONLY 1
#define FA_HIDDEN 2

View file

@ -395,11 +395,11 @@ static NSString* GetArchitectureString()
#endif
}
static void RestartWithParameters(const char* iwadPath, NSString* parameters)
static void RestartWithParameters(const WadStuff& wad, NSString* parameters)
{
assert(nil != parameters);
defaultiwad = ExtractFileBase(iwadPath);
defaultiwad = wad.Name;
GameConfig->DoGameSetup("Doom");
M_SaveDefaults(NULL);
@ -427,7 +427,7 @@ static void RestartWithParameters(const char* iwadPath, NSString* parameters)
[arguments addObject:@"-wad_picker_restart"];
[arguments addObject:@"-iwad"];
[arguments addObject:[NSString stringWithUTF8String:iwadPath]];
[arguments addObject:[NSString stringWithUTF8String:wad.Path]];
for (int i = 1, count = Args->NumArgs(); i < count; ++i)
{
@ -477,7 +477,7 @@ int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad
{
if (0 != [parametersToAppend length])
{
RestartWithParameters(wads[ret].Path, parametersToAppend);
RestartWithParameters(wads[ret], parametersToAppend);
}
}

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -49,7 +49,7 @@ struct FRenderer
virtual void OnModeSet () {}
virtual void SetClearColor(int color) = 0;
virtual void Init() = 0;
virtual void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) = 0;
virtual void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, double fov) = 0;
virtual void PreprocessLevel() {}
virtual void CleanLevelData() {}
virtual bool RequireGLNodes() { return false; }

View file

@ -1014,7 +1014,7 @@ void R_SetupFrame (FRenderViewpoint &viewpoint, FViewWindow &viewwindow, AActor
//
//==========================================================================
void FCanvasTextureInfo::Add (AActor *viewpoint, FTextureID picnum, int fov)
void FCanvasTextureInfo::Add (AActor *viewpoint, FTextureID picnum, double fov)
{
FCanvasTextureInfo *probe;
FCanvasTexture *texture;
@ -1062,7 +1062,7 @@ DEFINE_ACTION_FUNCTION(_TexMan, SetCameraToTexture)
PARAM_PROLOGUE;
PARAM_OBJECT(viewpoint, AActor);
PARAM_STRING(texturename); // [ZZ] there is no point in having this as FTextureID because it's easier to refer to a cameratexture by name and it isn't executed too often to cache it.
PARAM_INT(fov);
PARAM_FLOAT(fov);
FTextureID textureid = TexMan.CheckForTexture(texturename, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable);
FCanvasTextureInfo::Add(viewpoint, textureid, fov);
return 0;
@ -1150,7 +1150,7 @@ void FCanvasTextureInfo::Serialize(FSerializer &arc)
if (arc.BeginArray("canvastextures"))
{
AActor *viewpoint = nullptr;
int fov;
double fov;
FTextureID picnum;
while (arc.BeginObject(nullptr))
{

View file

@ -136,9 +136,9 @@ struct FCanvasTextureInfo
TObjPtr<AActor*> Viewpoint;
FCanvasTexture *Texture;
FTextureID PicNum;
int FOV;
double FOV;
static void Add (AActor *viewpoint, FTextureID picnum, int fov);
static void Add (AActor *viewpoint, FTextureID picnum, double fov);
static void UpdateAll ();
static void EmptyList ();
static void Serialize(FSerializer &arc);

View file

@ -25,63 +25,70 @@
#include "c_dispatch.h"
#include "c_cvars.h"
CUSTOM_CVAR (Int, vid_scalemode, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
#define NUMSCALEMODES 5
namespace
{
if (self < 0 || self > 6)
struct v_ScaleTable
{
self = 0;
bool isValid;
bool isLinear;
uint32_t(*GetScaledWidth)(uint32_t Width);
uint32_t(*GetScaledHeight)(uint32_t Height);
bool isScaled43;
};
v_ScaleTable vScaleTable[NUMSCALEMODES] =
{
// isValid, isLinear, GetScaledWidth(), GetScaledHeight(), isScaled43
{ true, false, [](uint32_t Width)->uint32_t { return Width; }, [](uint32_t Height)->uint32_t { return Height; }, false }, // 0 - Native
{ true, true, [](uint32_t Width)->uint32_t { return Width; }, [](uint32_t Height)->uint32_t { return Height; }, false }, // 1 - Native (Linear)
{ true, false, [](uint32_t Width)->uint32_t { return 320; }, [](uint32_t Height)->uint32_t { return 200; }, true }, // 2 - 320x200
{ true, false, [](uint32_t Width)->uint32_t { return 640; }, [](uint32_t Height)->uint32_t { return 400; }, true }, // 3 - 640x400
{ true, true, [](uint32_t Width)->uint32_t { return 1280; }, [](uint32_t Height)->uint32_t { return 800; }, true }, // 4 - 1280x800
};
bool isOutOfBounds(int x)
{
return (x < 0 || x >= NUMSCALEMODES || vScaleTable[x].isValid == false);
}
}
CUSTOM_CVAR(Float, vid_scalefactor, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self <= 0.0 || self > 2.0)
self = 1.0;
}
CUSTOM_CVAR(Int, vid_scalemode, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (isOutOfBounds(self))
self = 0;
}
bool ViewportLinearScale()
{
switch(vid_scalemode)
{
default: return false;
case 4:
case 5:
case 6: return true;
}
if (isOutOfBounds(vid_scalemode))
vid_scalemode = 0;
// vid_scalefactor > 1 == forced linear scale
return (vid_scalefactor > 1.0) ? true : vScaleTable[vid_scalemode].isLinear;
}
int ViewportScaledWidth(int width)
{
switch (vid_scalemode)
{
default:
case 0: return width;
case 1: return 320;
case 2: return 640;
case 3: return (int)roundf(width * 0.5f);
case 4: return (int)roundf(width * 0.75f);
case 5: return width * 2;
case 6: return 1280;
}
if (isOutOfBounds(vid_scalemode))
vid_scalemode = 0;
return vScaleTable[vid_scalemode].GetScaledWidth((int)((float)width * vid_scalefactor));
}
int ViewportScaledHeight(int height)
{
switch (vid_scalemode)
{
default:
case 0: return height;
case 1: return 200;
case 2: return 400;
case 3: return (int)roundf(height * 0.5f);
case 4: return (int)roundf(height * 0.75f);
case 5: return height * 2;
case 6: return 800;
}
if (isOutOfBounds(vid_scalemode))
vid_scalemode = 0;
return vScaleTable[vid_scalemode].GetScaledHeight((int)((float)height * vid_scalefactor));
}
bool ViewportIsScaled43()
{
switch (vid_scalemode)
{
default: return false;
case 1:
case 2:
case 6: return true;
}
if (isOutOfBounds(vid_scalemode))
vid_scalemode = 0;
return vScaleTable[vid_scalemode].isScaled43;
}

View file

@ -202,7 +202,7 @@ FxLocalVariableDeclaration *FCompileContext::FindLocalVariable(FName name)
static PContainerType *FindContainerType(FName name, FCompileContext &ctx)
{
auto sym = ctx.Class->Symbols.FindSymbol(name, true);
auto sym = ctx.Class != nullptr? ctx.Class->Symbols.FindSymbol(name, true) : nullptr;
if (sym == nullptr) sym = ctx.CurGlobals->Symbols.FindSymbol(name, true);
if (sym && sym->IsKindOf(RUNTIME_CLASS(PSymbolType)))
{
@ -11101,7 +11101,7 @@ ExpEmit FxRuntimeStateIndex::Emit(VMFunctionBuilder *build)
//
//==========================================================================
FxMultiNameState::FxMultiNameState(const char *_statestring, const FScriptPosition &pos)
FxMultiNameState::FxMultiNameState(const char *_statestring, const FScriptPosition &pos, PClassActor *checkclass)
:FxExpression(EFX_MultiNameState, pos)
{
FName scopename;
@ -11119,7 +11119,7 @@ FxMultiNameState::FxMultiNameState(const char *_statestring, const FScriptPositi
}
names = MakeStateNameList(statestring);
names.Insert(0, scopename);
scope = nullptr;
scope = checkclass;
}
//==========================================================================
@ -11135,8 +11135,8 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
int symlabel;
auto vclass = PType::toClass(ctx.Class);
assert(vclass != nullptr);
auto clstype = ValidateActor(vclass->Descriptor);
//assert(vclass != nullptr);
auto clstype = vclass == nullptr? nullptr : ValidateActor(vclass->Descriptor);
if (names[0] == NAME_None)
{

View file

@ -2089,7 +2089,7 @@ class FxMultiNameState : public FxExpression
TArray<FName> names;
public:
FxMultiNameState(const char *statestring, const FScriptPosition &pos);
FxMultiNameState(const char *statestring, const FScriptPosition &pos, PClassActor *checkclass = nullptr);
FxExpression *Resolve(FCompileContext&);
};

View file

@ -1280,3 +1280,16 @@ DEFINE_ACTION_FUNCTION(FStringStruct, ToLower)
self->ToLower();
return 0;
}
DEFINE_ACTION_FUNCTION(FStringStruct, ToInt)
{
PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_INT_DEF(base);
ACTION_RETURN_INT(self->ToLong(base));
}
DEFINE_ACTION_FUNCTION(FStringStruct, ToDouble)
{
PARAM_SELF_STRUCT_PROLOGUE(FString);
ACTION_RETURN_FLOAT(self->ToDouble());
}

View file

@ -2247,6 +2247,7 @@ bool PStruct::ReadValue(FSerializer &ar, const char *key, void *addr) const
PField *PStruct::AddField(FName name, PType *type, uint32_t flags)
{
assert(type->Size > 0);
return Symbols.AddField(name, type, flags, Size, &Align);
}

View file

@ -1298,6 +1298,11 @@ bool ZCCCompiler::CompileFields(PContainerType *type, TArray<ZCC_VarDeclarator *
auto name = field->Names;
do
{
if (fieldtype->Size == 0 && !(varflags & VARF_Native)) // Size not known yet.
{
return false;
}
if (AddTreeNode(name->Name, name, TreeNodes, !forstruct))
{
auto thisfieldtype = fieldtype;

View file

@ -34,6 +34,8 @@
#include "i_midi_win32.h"
#include <string>
#include <vector>
#include "i_musicinterns.h"
#include "c_cvars.h"
@ -46,6 +48,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <wordexp.h>
#include <glob.h>
#include <signal.h>
@ -81,14 +84,16 @@ protected:
HANDLE ReadWavePipe;
HANDLE WriteWavePipe;
HANDLE ChildProcess;
FString CommandLine;
size_t LoopPos;
bool Validated;
bool ValidateTimidity();
#else // _WIN32
int WavePipe[2];
pid_t ChildProcess;
#endif
FString CommandLine;
size_t LoopPos;
FString ExeName;
bool Looping;
static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata);
#ifdef _WIN32
@ -118,6 +123,7 @@ CUSTOM_CVAR(String, timidity_exe, "timidity", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
}
CVAR (String, timidity_extargs, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // extra args to pass to Timidity
CVAR (String, timidity_config, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (String, timidity_chorus, "0", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (String, timidity_reverb, "0", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, timidity_stereo, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
@ -137,9 +143,9 @@ CUSTOM_CVAR (Float, timidity_mastervolume, 1.0f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CUSTOM_CVAR (Int, timidity_pipe, 90, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{ // pipe size in ms
if (timidity_pipe < 0)
{ // a negative size makes no sense
timidity_pipe = 0;
if (self < 20)
{ // Don't allow pipes less than 20ms
self = 20;
}
}
@ -162,20 +168,30 @@ TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args)
#ifdef _WIN32
ReadWavePipe(INVALID_HANDLE_VALUE), WriteWavePipe(INVALID_HANDLE_VALUE),
ChildProcess(INVALID_HANDLE_VALUE),
Validated(false)
Validated(false),
#else
ChildProcess(-1)
ChildProcess(-1),
#endif
Looping(false)
{
#ifndef _WIN32
WavePipe[0] = WavePipe[1] = -1;
#endif
if (args == NULL || *args == 0) args = timidity_exe;
ExeName = args;
#ifdef _WIN32
CommandLine.Format("%s %s -EFchorus=%s -EFreverb=%s -s%d ",
args, *timidity_extargs,
*timidity_chorus, *timidity_reverb, *timidity_frequency);
if (**timidity_config != '\0')
{
CommandLine += "-c \"";
CommandLine += timidity_config;
CommandLine += "\" ";
}
#endif
if (DiskName == NULL)
{
@ -229,14 +245,17 @@ bool TimidityPPMIDIDevice::Preprocess(MIDIStreamer *song, bool looping)
bool success;
FILE *f;
if (CommandLine.IsEmpty())
if (ExeName.IsEmpty())
{
return false;
}
// Tell TiMidity++ whether it should loop or not
#ifdef _WIN32
CommandLine.LockBuffer()[LoopPos] = looping ? 'l' : ' ';
CommandLine.UnlockBuffer();
#endif
Looping = looping;
// Write MIDI song to temporary file
song->CreateSMF(midi, looping ? 0 : 1);
@ -298,46 +317,31 @@ int TimidityPPMIDIDevice::Open(MidiCallback callback, void *userdata)
#endif
{
Printf(PRINT_BOLD, "Could not create a data pipe for TiMidity++.\n");
pipeSize = 0;
return 1;
}
else
Stream = GSnd->CreateStream(FillStream, pipeSize,
(timidity_stereo ? 0 : SoundStream::Mono) |
(timidity_8bit ? SoundStream::Bits8 : 0),
timidity_frequency, this);
if (Stream == NULL)
{
Stream = GSnd->CreateStream(FillStream, pipeSize,
(timidity_stereo ? 0 : SoundStream::Mono) |
(timidity_8bit ? SoundStream::Bits8 : 0),
timidity_frequency, this);
if (Stream == NULL)
{
Printf(PRINT_BOLD, "Could not create music stream.\n");
pipeSize = 0;
Printf(PRINT_BOLD, "Could not create music stream.\n");
#ifdef _WIN32
CloseHandle(WriteWavePipe);
CloseHandle(ReadWavePipe);
ReadWavePipe = WriteWavePipe = INVALID_HANDLE_VALUE;
CloseHandle(WriteWavePipe);
CloseHandle(ReadWavePipe);
ReadWavePipe = WriteWavePipe = INVALID_HANDLE_VALUE;
#else
close(WavePipe[1]);
close(WavePipe[0]);
WavePipe[0] = WavePipe[1] = -1;
close(WavePipe[1]);
close(WavePipe[0]);
WavePipe[0] = WavePipe[1] = -1;
#endif
}
}
if (pipeSize == 0)
{
Printf(PRINT_BOLD, "If your soundcard cannot play more than one\n"
"wave at a time, you will hear no music.\n");
}
else
{
CommandLine += "-o - -Ors";
return 1;
}
}
if (pipeSize == 0)
{
CommandLine += "-Od";
}
#ifdef _WIN32
CommandLine += "-o - -Ors";
CommandLine += timidity_stereo ? 'S' : 'M';
CommandLine += timidity_8bit ? '8' : '1';
if (timidity_byteswap)
@ -349,6 +353,7 @@ int TimidityPPMIDIDevice::Open(MidiCallback callback, void *userdata)
CommandLine += " -idl ";
CommandLine += DiskName.GetName();
#endif
return 0;
}
@ -456,6 +461,7 @@ bool TimidityPPMIDIDevice::ValidateTimidity()
bool TimidityPPMIDIDevice::LaunchTimidity ()
{
#ifdef _WIN32
if (CommandLine.IsEmpty())
{
return false;
@ -463,7 +469,6 @@ bool TimidityPPMIDIDevice::LaunchTimidity ()
DPrintf (DMSG_NOTIFY, "cmd: \x1cG%s\n", CommandLine.GetChars());
#ifdef _WIN32
STARTUPINFO startup = { sizeof(startup), };
PROCESS_INFORMATION procInfo;
@ -509,6 +514,11 @@ bool TimidityPPMIDIDevice::LaunchTimidity ()
}
return false;
#else
if (ExeName.IsEmpty())
{
return false;
}
if (WavePipe[0] != -1 && WavePipe[1] == -1 && Stream != NULL)
{
// Timidity was previously launched, so the write end of the pipe
@ -523,78 +533,55 @@ bool TimidityPPMIDIDevice::LaunchTimidity ()
}
int forkres;
wordexp_t words;
glob_t glb;
// Get timidity executable path
int spaceIdx = 0;
int spaceInExePathCount = -1;
FString TimidityExe;
do
const char *exename = "timidity"; // Fallback default
glob(ExeName.GetChars(), 0, NULL, &glb);
if(glb.gl_pathc != 0)
exename = glb.gl_pathv[0];
// Get user-defined extra args
wordexp(timidity_extargs, &words, WRDE_NOCMD);
std::string chorusarg = std::string("-EFchorus=") + *timidity_chorus;
std::string reverbarg = std::string("-EFreverb=") + *timidity_reverb;
std::string sratearg = std::string("-s") + std::to_string(*timidity_frequency);
std::string outfilearg = "-o"; // An extra "-" is added later
std::string outmodearg = "-Or";
outmodearg += timidity_8bit ? "u8" : "s1";
outmodearg += timidity_stereo ? "S" : "M";
if(timidity_byteswap) outmodearg += "x";
std::string ifacearg = "-id";
if(Looping) ifacearg += "l";
std::vector<const char*> arglist;
arglist.push_back(exename);
for(size_t i = 0;i < words.we_wordc;i++)
arglist.push_back(words.we_wordv[i]);
if(**timidity_config != '\0')
{
spaceIdx = CommandLine.IndexOf(' ', spaceIdx);
TimidityExe = CommandLine.Left(spaceIdx);
glob(TimidityExe.GetChars(), 0, NULL, &glb);
spaceIdx += 1;
spaceInExePathCount += 1;
} while (spaceIdx != 0 && glb.gl_pathc == 0);
if (spaceIdx == 0)
{
TimidityExe = FString("timidity"); // Maybe it's in your PATH?
spaceInExePathCount = 0;
arglist.push_back("-c");
arglist.push_back(timidity_config);
}
globfree(&glb);
arglist.push_back(chorusarg.c_str());
arglist.push_back(reverbarg.c_str());
arglist.push_back(sratearg.c_str());
arglist.push_back(outfilearg.c_str());
arglist.push_back("-");
arglist.push_back(outmodearg.c_str());
arglist.push_back(ifacearg.c_str());
arglist.push_back(DiskName.GetName());
int strCount = 1;
for (spaceIdx = 0; spaceIdx < CommandLine.Len(); spaceIdx++)
{
if (CommandLine[spaceIdx] == ' ')
{
++strCount;
if (CommandLine[spaceIdx+1] == ' ')
{
--strCount;
}
}
}
strCount -= spaceInExePathCount;
char** TimidityArgs = new char*[strCount + 1];
TimidityArgs[strCount] = NULL;
spaceIdx = CommandLine.IndexOf(' ');
int curSpace = spaceIdx, i = 1;
TimidityArgs[0] = new char[TimidityExe.Len() + 1];
TimidityArgs[0][TimidityExe.Len()] = 0;
strcpy(TimidityArgs[0], TimidityExe.GetChars());
int argLen;
while (curSpace != -1)
{
curSpace = CommandLine.IndexOf(' ', spaceIdx);
if (curSpace != spaceIdx)
{
argLen = curSpace - spaceIdx + 1;
if (argLen < 0)
{
argLen = CommandLine.Len() - curSpace;
}
TimidityArgs[i] = new char[argLen];
TimidityArgs[i][argLen-1] = 0;
strcpy(TimidityArgs[i], CommandLine.Mid(spaceIdx, curSpace - spaceIdx).GetChars());
i += 1;
}
spaceIdx = curSpace + 1;
}
DPrintf(DMSG_NOTIFY, "Timidity EXE: \x1cG%s\n", TimidityExe.GetChars());
for (i = 0; i < strCount; i++)
{
DPrintf(DMSG_NOTIFY, "arg %d: \x1cG%s\n", i, TimidityArgs[i]);
}
DPrintf(DMSG_NOTIFY, "Timidity EXE: \x1cG%s\n", exename);
int i = 1;
std::for_each(arglist.begin()+1, arglist.end(),
[&i](const char *arg)
{ DPrintf(DMSG_NOTIFY, "arg %d: \x1cG%s\n", i++, arg); }
);
arglist.push_back(nullptr);
forkres = fork ();
if (forkres == 0)
{
close (WavePipe[0]);
@ -603,7 +590,7 @@ bool TimidityPPMIDIDevice::LaunchTimidity ()
// freopen ("/dev/null", "w", stderr);
close (WavePipe[1]);
execvp (TimidityExe.GetChars(), TimidityArgs);
execvp (exename, const_cast<char*const*>(arglist.data()));
fprintf(stderr,"execvp failed: %s\n", strerror(errno));
_exit (0); // if execvp succeeds, we never get here
}
@ -624,6 +611,7 @@ bool TimidityPPMIDIDevice::LaunchTimidity ()
}*/
}
wordfree(&words);
globfree (&glb);
return ChildProcess != -1;
#endif // _WIN32
@ -666,7 +654,6 @@ bool TimidityPPMIDIDevice::FillStream(SoundStream *stream, void *buff, int len,
}
}
#else
ssize_t got;
fd_set rfds;
struct timeval tv;
@ -691,11 +678,26 @@ bool TimidityPPMIDIDevice::FillStream(SoundStream *stream, void *buff, int len,
}
// fprintf(stderr,"something\n");
got = read(song->WavePipe[0], (uint8_t *)buff, len);
if (got < len)
{
memset((uint8_t *)buff+got, 0, len-got);
}
ssize_t got = 0;
do {
ssize_t r = read(song->WavePipe[0], (uint8_t*)buff+got, len-got);
if(r < 0)
{
if(errno == EWOULDBLOCK || errno == EAGAIN)
{
FD_ZERO(&rfds);
FD_SET(song->WavePipe[0], &rfds);
tv.tv_sec = 0;
tv.tv_usec = 50;
select(1, &rfds, NULL, NULL, &tv);
continue;
}
break;
}
got += r;
} while(got < len);
if(got < len)
memset((uint8_t*)buff+got, 0, len-got);
#endif
return true;
}
@ -755,8 +757,7 @@ int TimidityPPMIDIDevice::Resume()
{
if (LaunchTimidity())
{
// Assume success if not mixing with the sound system
if (Stream == NULL || Stream->Play(true, timidity_mastervolume))
if (Stream != NULL && Stream->Play(true, timidity_mastervolume))
{
Started = true;
return 0;

View file

@ -61,6 +61,9 @@ enum
STAT_MAPMARKER, // Map marker actors
STAT_DLIGHT,
STAT_USER = 70,
STAT_USER_MAX = 90,
STAT_DEFAULT = 100, // Thinkers go here unless specified otherwise.
STAT_SECTOREFFECT, // All sector effects that cause floor and ceiling movement
STAT_ACTORMOVER, // actor movers

View file

@ -1398,9 +1398,9 @@ namespace swrenderer
{
uint32_t val = colormap[source[frac >> FRACBITS]] << 2;
int r = (palette[*dest].r * (255-val) + palette[color].r * val) >> 10;
int g = (palette[*dest].g * (255-val) + palette[color].g * val) >> 10;
int b = (palette[*dest].b * (255-val) + palette[color].b * val) >> 10;
int r = (palette[*dest].r * (256-val) + palette[color].r * val) >> 10;
int g = (palette[*dest].g * (256-val) + palette[color].g * val) >> 10;
int b = (palette[*dest].b * (256-val) + palette[color].b * val) >> 10;
*dest = RGB256k.RGB[clamp(r,0,63)][clamp(g,0,63)][clamp(b,0,63)];
dest += pitch;
@ -1441,9 +1441,9 @@ namespace swrenderer
{
uint32_t val = colormap[source[frac >> FRACBITS]] << 2;
int r = (palette[*dest].r * (255) + palette[color].r * val) >> 10;
int g = (palette[*dest].g * (255) + palette[color].g * val) >> 10;
int b = (palette[*dest].b * (255) + palette[color].b * val) >> 10;
int r = (palette[*dest].r * (256) + palette[color].r * val) >> 10;
int g = (palette[*dest].g * (256) + palette[color].g * val) >> 10;
int b = (palette[*dest].b * (256) + palette[color].b * val) >> 10;
*dest = RGB256k.RGB[clamp(r,0,63)][clamp(g,0,63)][clamp(b,0,63)];
dest += pitch;
@ -2151,8 +2151,8 @@ namespace swrenderer
}
else
{
uint8_t srcwidth = _srcwidth;
uint8_t srcheight = _srcheight;
uint32_t srcwidth = _srcwidth;
uint32_t srcheight = _srcheight;
do
{
@ -2222,8 +2222,8 @@ namespace swrenderer
}
else
{
uint8_t srcwidth = _srcwidth;
uint8_t srcheight = _srcheight;
uint32_t srcwidth = _srcwidth;
uint32_t srcheight = _srcheight;
do
{
@ -2298,8 +2298,8 @@ namespace swrenderer
}
else
{
uint8_t srcwidth = _srcwidth;
uint8_t srcheight = _srcheight;
uint32_t srcwidth = _srcwidth;
uint32_t srcheight = _srcheight;
do
{
@ -2338,8 +2338,8 @@ namespace swrenderer
}
else
{
uint8_t srcwidth = _srcwidth;
uint8_t srcheight = _srcheight;
uint32_t srcwidth = _srcwidth;
uint32_t srcheight = _srcheight;
do
{
@ -2421,8 +2421,8 @@ namespace swrenderer
}
else
{
uint8_t srcwidth = _srcwidth;
uint8_t srcheight = _srcheight;
uint32_t srcwidth = _srcwidth;
uint32_t srcheight = _srcheight;
do
{
@ -2474,8 +2474,8 @@ namespace swrenderer
}
else
{
uint8_t srcwidth = _srcwidth;
uint8_t srcheight = _srcheight;
uint32_t srcwidth = _srcwidth;
uint32_t srcheight = _srcheight;
do
{
@ -2559,8 +2559,8 @@ namespace swrenderer
}
else
{
uint8_t srcwidth = _srcwidth;
uint8_t srcheight = _srcheight;
uint32_t srcwidth = _srcwidth;
uint32_t srcheight = _srcheight;
do
{
@ -2603,8 +2603,8 @@ namespace swrenderer
}
else
{
uint8_t srcwidth = _srcwidth;
uint8_t srcheight = _srcheight;
uint32_t srcwidth = _srcwidth;
uint32_t srcheight = _srcheight;
do
{
@ -2689,8 +2689,8 @@ namespace swrenderer
}
else
{
uint8_t srcwidth = _srcwidth;
uint8_t srcheight = _srcheight;
uint32_t srcwidth = _srcwidth;
uint32_t srcheight = _srcheight;
do
{
@ -2746,8 +2746,8 @@ namespace swrenderer
}
else
{
uint8_t srcwidth = _srcwidth;
uint8_t srcheight = _srcheight;
uint32_t srcwidth = _srcwidth;
uint32_t srcheight = _srcheight;
do
{

View file

@ -76,7 +76,7 @@ namespace swrenderer
while (node)
{
if (!(node->lightsource->flags2&MF2_DORMANT))
if (!(node->lightsource->flags2&MF2_DORMANT) && (height.PointOnSide(node->lightsource->Pos()) > 0))
{
bool found = false;
VisiblePlaneLight *light_node = lights;

View file

@ -272,7 +272,7 @@ void FSoftwareRenderer::SetClearColor(int color)
mScene.SetClearColor(color);
}
void FSoftwareRenderer::RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov)
void FSoftwareRenderer::RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, double fov)
{
auto renderTarget = r_polyrenderer ? PolyRenderer::Instance()->RenderTarget : mScene.MainThread()->Viewport->RenderTarget;
auto &cameraViewpoint = r_polyrenderer ? PolyRenderer::Instance()->Viewpoint : mScene.MainThread()->Viewport->viewpoint;
@ -290,7 +290,7 @@ void FSoftwareRenderer::RenderTextureView (FCanvasTexture *tex, AActor *viewpoin
CameraLight savedCameraLight = *CameraLight::Instance();
DAngle savedfov = cameraViewpoint.FieldOfView;
R_SetFOV (cameraViewpoint, (double)fov);
R_SetFOV (cameraViewpoint, fov);
if (r_polyrenderer)
PolyRenderer::Instance()->RenderViewToCanvas(viewpoint, Canvas, 0, 0, tex->GetWidth(), tex->GetHeight(), tex->bFirstUpdate);
@ -382,12 +382,16 @@ void FSoftwareRenderer::CleanLevelData()
uint32_t FSoftwareRenderer::GetCaps()
{
ActorRenderFeatureFlags FlagSet = RFF_UNCLIPPEDTEX;
ActorRenderFeatureFlags FlagSet = 0;
if (r_polyrenderer)
FlagSet |= RFF_POLYGONAL | RFF_TILTPITCH;
else if (r_drawvoxels)
FlagSet |= RFF_VOXELS;
FlagSet |= RFF_POLYGONAL | RFF_TILTPITCH | RFF_SLOPE3DFLOORS;
else
{
FlagSet |= RFF_UNCLIPPEDTEX;
if (r_drawvoxels)
FlagSet |= RFF_VOXELS;
}
if (screen && screen->IsBgra())
FlagSet |= RFF_TRUECOLOR;

Some files were not shown because too many files have changed in this diff Show more