mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-03-16 16:01:06 +00:00
Merge branch 'master' of https://github.com/coelckers/gzdoom
This commit is contained in:
commit
b1cf428c06
22 changed files with 635 additions and 527 deletions
|
@ -963,6 +963,7 @@ set (PCH_SOURCES
|
|||
p_map.cpp
|
||||
p_maputl.cpp
|
||||
p_mobj.cpp
|
||||
p_openmap.cpp
|
||||
p_pillar.cpp
|
||||
p_plats.cpp
|
||||
p_pspr.cpp
|
||||
|
|
|
@ -195,33 +195,6 @@ bool DirEntryExists(const char *pathname, bool *isdir)
|
|||
return res;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DefaultExtension -- char array version
|
||||
//
|
||||
// Appends the extension to a pathname if it does not already have one.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DefaultExtension (char *path, const char *extension)
|
||||
{
|
||||
char *src;
|
||||
//
|
||||
// if path doesn't have a .EXT, append extension
|
||||
// (extension should include the .)
|
||||
//
|
||||
src = path + strlen(path) - 1;
|
||||
|
||||
while (src != path && !IsSeperator(*src))
|
||||
{
|
||||
if (*src == '.')
|
||||
return; // it has an extension
|
||||
src--;
|
||||
}
|
||||
|
||||
strcat (path, extension);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DefaultExtension -- FString version
|
||||
|
|
|
@ -27,7 +27,6 @@ extern FString progdir;
|
|||
void FixPathSeperator (char *path);
|
||||
static void inline FixPathSeperator (FString &path) { path.ReplaceChars('\\', '/'); }
|
||||
|
||||
void DefaultExtension (char *path, const char *extension);
|
||||
void DefaultExtension (FString &path, const char *extension);
|
||||
|
||||
FString ExtractFilePath (const char *path);
|
||||
|
|
|
@ -46,7 +46,54 @@
|
|||
#include "r_data/r_sections.h"
|
||||
#include "r_data/r_canvastexture.h"
|
||||
|
||||
struct FLevelLocals
|
||||
|
||||
struct FLevelData
|
||||
{
|
||||
TArray<vertex_t> vertexes;
|
||||
TArray<sector_t> sectors;
|
||||
TArray<line_t*> linebuffer; // contains the line lists for the sectors.
|
||||
TArray<line_t> lines;
|
||||
TArray<side_t> sides;
|
||||
TArray<seg_t> segs;
|
||||
TArray<subsector_t> subsectors;
|
||||
TArray<node_t> nodes;
|
||||
TArray<subsector_t> gamesubsectors;
|
||||
TArray<node_t> gamenodes;
|
||||
node_t *headgamenode;
|
||||
TArray<uint8_t> rejectmatrix;
|
||||
TArray<zone_t> Zones;
|
||||
|
||||
TArray<FSectorPortal> sectorPortals;
|
||||
TArray<FLinePortal> linePortals;
|
||||
|
||||
// Portal information.
|
||||
FDisplacementTable Displacements;
|
||||
FPortalBlockmap PortalBlockmap;
|
||||
TArray<FLinePortal*> linkedPortals; // only the linked portals, this is used to speed up looking for them in P_CollectConnectedGroups.
|
||||
TArray<FSectorPortalGroup *> portalGroups;
|
||||
TArray<FLinePortalSpan> linePortalSpans;
|
||||
FSectionContainer sections;
|
||||
FCanvasTextureInfo canvasTextureInfo;
|
||||
|
||||
// [ZZ] Destructible geometry information
|
||||
TMap<int, FHealthGroup> healthGroups;
|
||||
|
||||
FBlockmap blockmap;
|
||||
|
||||
// These are copies of the loaded map data that get used by the savegame code to skip unaltered fields
|
||||
// Without such a mechanism the savegame format would become too slow and large because more than 80-90% are normally still unaltered.
|
||||
TArray<sector_t> loadsectors;
|
||||
TArray<line_t> loadlines;
|
||||
TArray<side_t> loadsides;
|
||||
|
||||
// Maintain single and multi player starting spots.
|
||||
TArray<FPlayerStart> deathmatchstarts;
|
||||
FPlayerStart playerstarts[MAXPLAYERS];
|
||||
TArray<FPlayerStart> AllPlayerStarts;
|
||||
|
||||
};
|
||||
|
||||
struct FLevelLocals : public FLevelData
|
||||
{
|
||||
void Tick();
|
||||
void Mark();
|
||||
|
@ -76,57 +123,12 @@ struct FLevelLocals
|
|||
|
||||
uint64_t ShaderStartTime = 0; // tell the shader system when we started the level (forces a timer restart)
|
||||
|
||||
TArray<vertex_t> vertexes;
|
||||
TArray<sector_t> sectors;
|
||||
TArray<line_t*> linebuffer; // contains the line lists for the sectors.
|
||||
TArray<line_t> lines;
|
||||
TArray<side_t> sides;
|
||||
TArray<seg_t> segs;
|
||||
TArray<subsector_t> subsectors;
|
||||
TArray<node_t> nodes;
|
||||
TArray<subsector_t> gamesubsectors;
|
||||
TArray<node_t> gamenodes;
|
||||
node_t *headgamenode;
|
||||
TArray<uint8_t> rejectmatrix;
|
||||
|
||||
static const int BODYQUESIZE = 32;
|
||||
TObjPtr<AActor*> bodyque[BODYQUESIZE];
|
||||
int bodyqueslot;
|
||||
|
||||
|
||||
TArray<FSectorPortal> sectorPortals;
|
||||
TArray<FLinePortal> linePortals;
|
||||
|
||||
// Portal information.
|
||||
FDisplacementTable Displacements;
|
||||
FPortalBlockmap PortalBlockmap;
|
||||
TArray<FLinePortal*> linkedPortals; // only the linked portals, this is used to speed up looking for them in P_CollectConnectedGroups.
|
||||
TArray<FSectorPortalGroup *> portalGroups;
|
||||
TArray<FLinePortalSpan> linePortalSpans;
|
||||
FSectionContainer sections;
|
||||
FCanvasTextureInfo canvasTextureInfo;
|
||||
|
||||
int NumMapSections;
|
||||
|
||||
TArray<zone_t> Zones;
|
||||
|
||||
// [ZZ] Destructible geometry information
|
||||
TMap<int, FHealthGroup> healthGroups;
|
||||
|
||||
FBlockmap blockmap;
|
||||
|
||||
// These are copies of the loaded map data that get used by the savegame code to skip unaltered fields
|
||||
// Without such a mechanism the savegame format would become too slow and large because more than 80-90% are normally still unaltered.
|
||||
TArray<sector_t> loadsectors;
|
||||
TArray<line_t> loadlines;
|
||||
TArray<side_t> loadsides;
|
||||
|
||||
// Maintain single and multi player starting spots.
|
||||
TArray<FPlayerStart> deathmatchstarts;
|
||||
FPlayerStart playerstarts[MAXPLAYERS];
|
||||
TArray<FPlayerStart> AllPlayerStarts;
|
||||
|
||||
|
||||
uint32_t flags;
|
||||
uint32_t flags2;
|
||||
uint32_t flags3;
|
||||
|
@ -224,6 +226,8 @@ struct FLevelLocals
|
|||
}
|
||||
};
|
||||
|
||||
#ifndef NO_DEFINE_LEVEL
|
||||
|
||||
extern FLevelLocals level;
|
||||
|
||||
inline int vertex_t::Index() const
|
||||
|
@ -350,3 +354,4 @@ inline bool line_t::hitSkyWall(AActor* mo) const
|
|||
backsector->GetTexture(sector_t::ceiling) == skyflatnum &&
|
||||
mo->Z() >= backsector->ceilingplane.ZatPoint(mo->PosRelative(this));
|
||||
}
|
||||
#endif
|
413
src/p_openmap.cpp
Normal file
413
src/p_openmap.cpp
Normal file
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
** p_openmap.cpp
|
||||
**
|
||||
** creates the data structures needed to load a map from the resource files.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2005-2018 Christoph Oelckers
|
||||
** Copyright 2005-2016 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "p_setup.h"
|
||||
#include "i_system.h"
|
||||
#include "cmdlib.h"
|
||||
#include "w_wad.h"
|
||||
#include "md5.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
inline bool P_IsBuildMap(MapData *map)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// GetMapIndex
|
||||
//
|
||||
// Gets the type of map lump or -1 if invalid or -2 if required and not found.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
struct checkstruct
|
||||
{
|
||||
const char lumpname[9];
|
||||
bool required;
|
||||
};
|
||||
|
||||
static int GetMapIndex(const char *mapname, int lastindex, const char *lumpname, bool needrequired)
|
||||
{
|
||||
static const checkstruct check[] =
|
||||
{
|
||||
{"", true},
|
||||
{"THINGS", true},
|
||||
{"LINEDEFS", true},
|
||||
{"SIDEDEFS", true},
|
||||
{"VERTEXES", true},
|
||||
{"SEGS", false},
|
||||
{"SSECTORS", false},
|
||||
{"NODES", false},
|
||||
{"SECTORS", true},
|
||||
{"REJECT", false},
|
||||
{"BLOCKMAP", false},
|
||||
{"BEHAVIOR", false},
|
||||
//{"SCRIPTS", false},
|
||||
};
|
||||
|
||||
if (lumpname==NULL) lumpname="";
|
||||
|
||||
for(size_t i=lastindex+1;i<countof(check);i++)
|
||||
{
|
||||
if (!strnicmp(lumpname, check[i].lumpname, 8))
|
||||
return (int)i;
|
||||
|
||||
if (check[i].required)
|
||||
{
|
||||
if (needrequired)
|
||||
{
|
||||
I_Error("'%s' not found in %s\n", check[i].lumpname, mapname);
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
return -1; // End of map reached
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Opens a map for reading
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
MapData *P_OpenMapData(const char * mapname, bool justcheck)
|
||||
{
|
||||
MapData * map = new MapData;
|
||||
FileReader * wadReader = nullptr;
|
||||
bool externalfile = !strnicmp(mapname, "file:", 5);
|
||||
|
||||
if (externalfile)
|
||||
{
|
||||
mapname += 5;
|
||||
if (!FileExists(mapname))
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
map->resource = FResourceFile::OpenResourceFile(mapname, true);
|
||||
wadReader = map->resource->GetReader();
|
||||
}
|
||||
else
|
||||
{
|
||||
FString fmt;
|
||||
int lump_wad;
|
||||
int lump_map;
|
||||
int lump_name = -1;
|
||||
|
||||
// Check for both *.wad and *.map in order to load Build maps
|
||||
// as well. The higher one will take precedence.
|
||||
// Names with more than 8 characters will only be checked as .wad and .map.
|
||||
if (strlen(mapname) <= 8) lump_name = Wads.CheckNumForName(mapname);
|
||||
fmt.Format("maps/%s.wad", mapname);
|
||||
lump_wad = Wads.CheckNumForFullName(fmt);
|
||||
fmt.Format("maps/%s.map", mapname);
|
||||
lump_map = Wads.CheckNumForFullName(fmt);
|
||||
|
||||
if (lump_name > lump_wad && lump_name > lump_map && lump_name != -1)
|
||||
{
|
||||
int lumpfile = Wads.GetLumpFile(lump_name);
|
||||
int nextfile = Wads.GetLumpFile(lump_name+1);
|
||||
|
||||
map->lumpnum = lump_name;
|
||||
|
||||
if (lumpfile != nextfile)
|
||||
{
|
||||
// The following lump is from a different file so whatever this is,
|
||||
// it is not a multi-lump Doom level so let's assume it is a Build map.
|
||||
map->MapLumps[0].Reader = Wads.ReopenLumpReader(lump_name);
|
||||
if (!P_IsBuildMap(map))
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// This case can only happen if the lump is inside a real WAD file.
|
||||
// As such any special handling for other types of lumps is skipped.
|
||||
map->MapLumps[0].Reader = Wads.ReopenLumpReader(lump_name);
|
||||
strncpy(map->MapLumps[0].Name, Wads.GetLumpFullName(lump_name), 8);
|
||||
map->Encrypted = Wads.IsEncryptedFile(lump_name);
|
||||
map->InWad = true;
|
||||
|
||||
if (map->Encrypted)
|
||||
{ // If it's encrypted, then it's a Blood file, presumably a map.
|
||||
if (!P_IsBuildMap(map))
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
|
||||
if (stricmp(Wads.GetLumpFullName(lump_name + 1), "TEXTMAP") != 0)
|
||||
{
|
||||
for(int i = 1;; i++)
|
||||
{
|
||||
// Since levels must be stored in WADs they can't really have full
|
||||
// names and for any valid level lump this always returns the short name.
|
||||
const char * lumpname = Wads.GetLumpFullName(lump_name + i);
|
||||
try
|
||||
{
|
||||
index = GetMapIndex(mapname, index, lumpname, !justcheck);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
delete map;
|
||||
throw;
|
||||
}
|
||||
if (index == -2)
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
if (index == ML_BEHAVIOR) map->HasBehavior = true;
|
||||
|
||||
// The next lump is not part of this map anymore
|
||||
if (index < 0) break;
|
||||
|
||||
map->MapLumps[index].Reader = Wads.ReopenLumpReader(lump_name + i);
|
||||
strncpy(map->MapLumps[index].Name, lumpname, 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
map->isText = true;
|
||||
map->MapLumps[1].Reader = Wads.ReopenLumpReader(lump_name + 1);
|
||||
for(int i = 2;; i++)
|
||||
{
|
||||
const char * lumpname = Wads.GetLumpFullName(lump_name + i);
|
||||
|
||||
if (lumpname == NULL)
|
||||
{
|
||||
I_Error("Invalid map definition for %s", mapname);
|
||||
}
|
||||
else if (!stricmp(lumpname, "ZNODES"))
|
||||
{
|
||||
index = ML_GLZNODES;
|
||||
}
|
||||
else if (!stricmp(lumpname, "BLOCKMAP"))
|
||||
{
|
||||
// there is no real point in creating a blockmap but let's use it anyway
|
||||
index = ML_BLOCKMAP;
|
||||
}
|
||||
else if (!stricmp(lumpname, "REJECT"))
|
||||
{
|
||||
index = ML_REJECT;
|
||||
}
|
||||
else if (!stricmp(lumpname, "DIALOGUE"))
|
||||
{
|
||||
index = ML_CONVERSATION;
|
||||
}
|
||||
else if (!stricmp(lumpname, "BEHAVIOR"))
|
||||
{
|
||||
index = ML_BEHAVIOR;
|
||||
map->HasBehavior = true;
|
||||
}
|
||||
else if (!stricmp(lumpname, "ENDMAP"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else continue;
|
||||
map->MapLumps[index].Reader = Wads.ReopenLumpReader(lump_name + i);
|
||||
strncpy(map->MapLumps[index].Name, lumpname, 8);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lump_map > lump_wad)
|
||||
{
|
||||
lump_wad = lump_map;
|
||||
}
|
||||
if (lump_wad == -1)
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
map->lumpnum = lump_wad;
|
||||
auto reader = Wads.ReopenLumpReader(lump_wad);
|
||||
map->resource = FResourceFile::OpenResourceFile(Wads.GetLumpFullName(lump_wad), reader, true);
|
||||
wadReader = map->resource->GetReader();
|
||||
}
|
||||
}
|
||||
uint32_t id;
|
||||
|
||||
// Although we're using the resource system, we still want to be sure we're
|
||||
// reading from a wad file.
|
||||
wadReader->Seek(0, FileReader::SeekSet);
|
||||
wadReader->Read(&id, sizeof(id));
|
||||
|
||||
if (id == IWAD_ID || id == PWAD_ID)
|
||||
{
|
||||
char maplabel[9]="";
|
||||
int index=0;
|
||||
|
||||
map->MapLumps[0].Reader = map->resource->GetLump(0)->NewReader();
|
||||
strncpy(map->MapLumps[0].Name, map->resource->GetLump(0)->Name, 8);
|
||||
|
||||
for(uint32_t i = 1; i < map->resource->LumpCount(); i++)
|
||||
{
|
||||
const char* lumpname = map->resource->GetLump(i)->Name;
|
||||
|
||||
if (i == 1 && !strnicmp(lumpname, "TEXTMAP", 8))
|
||||
{
|
||||
map->isText = true;
|
||||
map->MapLumps[ML_TEXTMAP].Reader = map->resource->GetLump(i)->NewReader();
|
||||
strncpy(map->MapLumps[ML_TEXTMAP].Name, lumpname, 8);
|
||||
for(int i = 2;; i++)
|
||||
{
|
||||
lumpname = map->resource->GetLump(i)->Name;
|
||||
if (!strnicmp(lumpname, "ZNODES",8))
|
||||
{
|
||||
index = ML_GLZNODES;
|
||||
}
|
||||
else if (!strnicmp(lumpname, "BLOCKMAP",8))
|
||||
{
|
||||
// there is no real point in creating a blockmap but let's use it anyway
|
||||
index = ML_BLOCKMAP;
|
||||
}
|
||||
else if (!strnicmp(lumpname, "REJECT",8))
|
||||
{
|
||||
index = ML_REJECT;
|
||||
}
|
||||
else if (!strnicmp(lumpname, "DIALOGUE",8))
|
||||
{
|
||||
index = ML_CONVERSATION;
|
||||
}
|
||||
else if (!strnicmp(lumpname, "BEHAVIOR",8))
|
||||
{
|
||||
index = ML_BEHAVIOR;
|
||||
map->HasBehavior = true;
|
||||
}
|
||||
else if (!strnicmp(lumpname, "ENDMAP",8))
|
||||
{
|
||||
return map;
|
||||
}
|
||||
else continue;
|
||||
map->MapLumps[index].Reader = map->resource->GetLump(i)->NewReader();
|
||||
strncpy(map->MapLumps[index].Name, lumpname, 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (i>0)
|
||||
{
|
||||
try
|
||||
{
|
||||
index = GetMapIndex(maplabel, index, lumpname, !justcheck);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
delete map;
|
||||
throw;
|
||||
}
|
||||
if (index == -2)
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
if (index == ML_BEHAVIOR) map->HasBehavior = true;
|
||||
|
||||
// The next lump is not part of this map anymore
|
||||
if (index < 0) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(maplabel, lumpname, 8);
|
||||
maplabel[8]=0;
|
||||
}
|
||||
|
||||
map->MapLumps[index].Reader = map->resource->GetLump(i)->NewReader();
|
||||
strncpy(map->MapLumps[index].Name, lumpname, 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a Build map and not subject to WAD consistency checks.
|
||||
//map->MapLumps[0].Size = wadReader->GetLength();
|
||||
if (!P_IsBuildMap(map))
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
bool P_CheckMapData(const char *mapname)
|
||||
{
|
||||
MapData *mapd = P_OpenMapData(mapname, true);
|
||||
if (mapd == NULL) return false;
|
||||
delete mapd;
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// MapData :: GetChecksum
|
||||
//
|
||||
// Hashes a map based on its header, THINGS, LINEDEFS, SIDEDEFS, SECTORS,
|
||||
// and BEHAVIOR lumps. Node-builder generated lumps are not included.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void MapData::GetChecksum(uint8_t cksum[16])
|
||||
{
|
||||
MD5Context md5;
|
||||
|
||||
if (isText)
|
||||
{
|
||||
md5.Update(Reader(ML_TEXTMAP), Size(ML_TEXTMAP));
|
||||
}
|
||||
else
|
||||
{
|
||||
md5.Update(Reader(ML_LABEL), Size(ML_LABEL));
|
||||
md5.Update(Reader(ML_THINGS), Size(ML_THINGS));
|
||||
md5.Update(Reader(ML_LINEDEFS), Size(ML_LINEDEFS));
|
||||
md5.Update(Reader(ML_SIDEDEFS), Size(ML_SIDEDEFS));
|
||||
md5.Update(Reader(ML_SECTORS), Size(ML_SECTORS));
|
||||
}
|
||||
if (HasBehavior)
|
||||
{
|
||||
md5.Update(Reader(ML_BEHAVIOR), Size(ML_BEHAVIOR));
|
||||
}
|
||||
md5.Final(cksum);
|
||||
}
|
384
src/p_setup.cpp
384
src/p_setup.cpp
|
@ -136,11 +136,6 @@ CVAR (Bool, showloadtimes, false, 0);
|
|||
|
||||
static void P_Shutdown ();
|
||||
|
||||
inline bool P_IsBuildMap(MapData *map)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool P_LoadBuildMap(uint8_t *mapdata, size_t len, FMapThing **things, int *numthings)
|
||||
{
|
||||
return false;
|
||||
|
@ -168,385 +163,6 @@ bool ForceNodeBuild;
|
|||
|
||||
static void P_AllocateSideDefs (MapData *map, int count);
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// GetMapIndex
|
||||
//
|
||||
// Gets the type of map lump or -1 if invalid or -2 if required and not found.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
struct checkstruct
|
||||
{
|
||||
const char lumpname[9];
|
||||
bool required;
|
||||
};
|
||||
|
||||
static int GetMapIndex(const char *mapname, int lastindex, const char *lumpname, bool needrequired)
|
||||
{
|
||||
static const checkstruct check[] =
|
||||
{
|
||||
{"", true},
|
||||
{"THINGS", true},
|
||||
{"LINEDEFS", true},
|
||||
{"SIDEDEFS", true},
|
||||
{"VERTEXES", true},
|
||||
{"SEGS", false},
|
||||
{"SSECTORS", false},
|
||||
{"NODES", false},
|
||||
{"SECTORS", true},
|
||||
{"REJECT", false},
|
||||
{"BLOCKMAP", false},
|
||||
{"BEHAVIOR", false},
|
||||
//{"SCRIPTS", false},
|
||||
};
|
||||
|
||||
if (lumpname==NULL) lumpname="";
|
||||
|
||||
for(size_t i=lastindex+1;i<countof(check);i++)
|
||||
{
|
||||
if (!strnicmp(lumpname, check[i].lumpname, 8))
|
||||
return (int)i;
|
||||
|
||||
if (check[i].required)
|
||||
{
|
||||
if (needrequired)
|
||||
{
|
||||
I_Error("'%s' not found in %s\n", check[i].lumpname, mapname);
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
return -1; // End of map reached
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Opens a map for reading
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
MapData *P_OpenMapData(const char * mapname, bool justcheck)
|
||||
{
|
||||
MapData * map = new MapData;
|
||||
FileReader * wadReader = nullptr;
|
||||
bool externalfile = !strnicmp(mapname, "file:", 5);
|
||||
|
||||
if (externalfile)
|
||||
{
|
||||
mapname += 5;
|
||||
if (!FileExists(mapname))
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
map->resource = FResourceFile::OpenResourceFile(mapname, true);
|
||||
wadReader = map->resource->GetReader();
|
||||
}
|
||||
else
|
||||
{
|
||||
FString fmt;
|
||||
int lump_wad;
|
||||
int lump_map;
|
||||
int lump_name = -1;
|
||||
|
||||
// Check for both *.wad and *.map in order to load Build maps
|
||||
// as well. The higher one will take precedence.
|
||||
// Names with more than 8 characters will only be checked as .wad and .map.
|
||||
if (strlen(mapname) <= 8) lump_name = Wads.CheckNumForName(mapname);
|
||||
fmt.Format("maps/%s.wad", mapname);
|
||||
lump_wad = Wads.CheckNumForFullName(fmt);
|
||||
fmt.Format("maps/%s.map", mapname);
|
||||
lump_map = Wads.CheckNumForFullName(fmt);
|
||||
|
||||
if (lump_name > lump_wad && lump_name > lump_map && lump_name != -1)
|
||||
{
|
||||
int lumpfile = Wads.GetLumpFile(lump_name);
|
||||
int nextfile = Wads.GetLumpFile(lump_name+1);
|
||||
|
||||
map->lumpnum = lump_name;
|
||||
|
||||
if (lumpfile != nextfile)
|
||||
{
|
||||
// The following lump is from a different file so whatever this is,
|
||||
// it is not a multi-lump Doom level so let's assume it is a Build map.
|
||||
map->MapLumps[0].Reader = Wads.ReopenLumpReader(lump_name);
|
||||
if (!P_IsBuildMap(map))
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// This case can only happen if the lump is inside a real WAD file.
|
||||
// As such any special handling for other types of lumps is skipped.
|
||||
map->MapLumps[0].Reader = Wads.ReopenLumpReader(lump_name);
|
||||
strncpy(map->MapLumps[0].Name, Wads.GetLumpFullName(lump_name), 8);
|
||||
map->Encrypted = Wads.IsEncryptedFile(lump_name);
|
||||
map->InWad = true;
|
||||
|
||||
if (map->Encrypted)
|
||||
{ // If it's encrypted, then it's a Blood file, presumably a map.
|
||||
if (!P_IsBuildMap(map))
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
|
||||
if (stricmp(Wads.GetLumpFullName(lump_name + 1), "TEXTMAP") != 0)
|
||||
{
|
||||
for(int i = 1;; i++)
|
||||
{
|
||||
// Since levels must be stored in WADs they can't really have full
|
||||
// names and for any valid level lump this always returns the short name.
|
||||
const char * lumpname = Wads.GetLumpFullName(lump_name + i);
|
||||
try
|
||||
{
|
||||
index = GetMapIndex(mapname, index, lumpname, !justcheck);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
delete map;
|
||||
throw;
|
||||
}
|
||||
if (index == -2)
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
if (index == ML_BEHAVIOR) map->HasBehavior = true;
|
||||
|
||||
// The next lump is not part of this map anymore
|
||||
if (index < 0) break;
|
||||
|
||||
map->MapLumps[index].Reader = Wads.ReopenLumpReader(lump_name + i);
|
||||
strncpy(map->MapLumps[index].Name, lumpname, 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
map->isText = true;
|
||||
map->MapLumps[1].Reader = Wads.ReopenLumpReader(lump_name + 1);
|
||||
for(int i = 2;; i++)
|
||||
{
|
||||
const char * lumpname = Wads.GetLumpFullName(lump_name + i);
|
||||
|
||||
if (lumpname == NULL)
|
||||
{
|
||||
I_Error("Invalid map definition for %s", mapname);
|
||||
}
|
||||
else if (!stricmp(lumpname, "ZNODES"))
|
||||
{
|
||||
index = ML_GLZNODES;
|
||||
}
|
||||
else if (!stricmp(lumpname, "BLOCKMAP"))
|
||||
{
|
||||
// there is no real point in creating a blockmap but let's use it anyway
|
||||
index = ML_BLOCKMAP;
|
||||
}
|
||||
else if (!stricmp(lumpname, "REJECT"))
|
||||
{
|
||||
index = ML_REJECT;
|
||||
}
|
||||
else if (!stricmp(lumpname, "DIALOGUE"))
|
||||
{
|
||||
index = ML_CONVERSATION;
|
||||
}
|
||||
else if (!stricmp(lumpname, "BEHAVIOR"))
|
||||
{
|
||||
index = ML_BEHAVIOR;
|
||||
map->HasBehavior = true;
|
||||
}
|
||||
else if (!stricmp(lumpname, "ENDMAP"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else continue;
|
||||
map->MapLumps[index].Reader = Wads.ReopenLumpReader(lump_name + i);
|
||||
strncpy(map->MapLumps[index].Name, lumpname, 8);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lump_map > lump_wad)
|
||||
{
|
||||
lump_wad = lump_map;
|
||||
}
|
||||
if (lump_wad == -1)
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
map->lumpnum = lump_wad;
|
||||
auto reader = Wads.ReopenLumpReader(lump_wad);
|
||||
map->resource = FResourceFile::OpenResourceFile(Wads.GetLumpFullName(lump_wad), reader, true);
|
||||
wadReader = map->resource->GetReader();
|
||||
}
|
||||
}
|
||||
uint32_t id;
|
||||
|
||||
// Although we're using the resource system, we still want to be sure we're
|
||||
// reading from a wad file.
|
||||
wadReader->Seek(0, FileReader::SeekSet);
|
||||
wadReader->Read(&id, sizeof(id));
|
||||
|
||||
if (id == IWAD_ID || id == PWAD_ID)
|
||||
{
|
||||
char maplabel[9]="";
|
||||
int index=0;
|
||||
|
||||
map->MapLumps[0].Reader = map->resource->GetLump(0)->NewReader();
|
||||
strncpy(map->MapLumps[0].Name, map->resource->GetLump(0)->Name, 8);
|
||||
|
||||
for(uint32_t i = 1; i < map->resource->LumpCount(); i++)
|
||||
{
|
||||
const char* lumpname = map->resource->GetLump(i)->Name;
|
||||
|
||||
if (i == 1 && !strnicmp(lumpname, "TEXTMAP", 8))
|
||||
{
|
||||
map->isText = true;
|
||||
map->MapLumps[ML_TEXTMAP].Reader = map->resource->GetLump(i)->NewReader();
|
||||
strncpy(map->MapLumps[ML_TEXTMAP].Name, lumpname, 8);
|
||||
for(int i = 2;; i++)
|
||||
{
|
||||
lumpname = map->resource->GetLump(i)->Name;
|
||||
if (!strnicmp(lumpname, "ZNODES",8))
|
||||
{
|
||||
index = ML_GLZNODES;
|
||||
}
|
||||
else if (!strnicmp(lumpname, "BLOCKMAP",8))
|
||||
{
|
||||
// there is no real point in creating a blockmap but let's use it anyway
|
||||
index = ML_BLOCKMAP;
|
||||
}
|
||||
else if (!strnicmp(lumpname, "REJECT",8))
|
||||
{
|
||||
index = ML_REJECT;
|
||||
}
|
||||
else if (!strnicmp(lumpname, "DIALOGUE",8))
|
||||
{
|
||||
index = ML_CONVERSATION;
|
||||
}
|
||||
else if (!strnicmp(lumpname, "BEHAVIOR",8))
|
||||
{
|
||||
index = ML_BEHAVIOR;
|
||||
map->HasBehavior = true;
|
||||
}
|
||||
else if (!strnicmp(lumpname, "ENDMAP",8))
|
||||
{
|
||||
return map;
|
||||
}
|
||||
else continue;
|
||||
map->MapLumps[index].Reader = map->resource->GetLump(i)->NewReader();
|
||||
strncpy(map->MapLumps[index].Name, lumpname, 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (i>0)
|
||||
{
|
||||
try
|
||||
{
|
||||
index = GetMapIndex(maplabel, index, lumpname, !justcheck);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
delete map;
|
||||
throw;
|
||||
}
|
||||
if (index == -2)
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
if (index == ML_BEHAVIOR) map->HasBehavior = true;
|
||||
|
||||
// The next lump is not part of this map anymore
|
||||
if (index < 0) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(maplabel, lumpname, 8);
|
||||
maplabel[8]=0;
|
||||
}
|
||||
|
||||
map->MapLumps[index].Reader = map->resource->GetLump(i)->NewReader();
|
||||
strncpy(map->MapLumps[index].Name, lumpname, 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a Build map and not subject to WAD consistency checks.
|
||||
//map->MapLumps[0].Size = wadReader->GetLength();
|
||||
if (!P_IsBuildMap(map))
|
||||
{
|
||||
delete map;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
bool P_CheckMapData(const char *mapname)
|
||||
{
|
||||
MapData *mapd = P_OpenMapData(mapname, true);
|
||||
if (mapd == NULL) return false;
|
||||
delete mapd;
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// MapData :: GetChecksum
|
||||
//
|
||||
// Hashes a map based on its header, THINGS, LINEDEFS, SIDEDEFS, SECTORS,
|
||||
// and BEHAVIOR lumps. Node-builder generated lumps are not included.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void MapData::GetChecksum(uint8_t cksum[16])
|
||||
{
|
||||
MD5Context md5;
|
||||
|
||||
if (isText)
|
||||
{
|
||||
md5.Update(Reader(ML_TEXTMAP), Size(ML_TEXTMAP));
|
||||
}
|
||||
else
|
||||
{
|
||||
md5.Update(Reader(ML_LABEL), Size(ML_LABEL));
|
||||
md5.Update(Reader(ML_THINGS), Size(ML_THINGS));
|
||||
md5.Update(Reader(ML_LINEDEFS), Size(ML_LINEDEFS));
|
||||
md5.Update(Reader(ML_SIDEDEFS), Size(ML_SIDEDEFS));
|
||||
md5.Update(Reader(ML_SECTORS), Size(ML_SECTORS));
|
||||
}
|
||||
if (HasBehavior)
|
||||
{
|
||||
md5.Update(Reader(ML_BEHAVIOR), Size(ML_BEHAVIOR));
|
||||
}
|
||||
md5.Final(cksum);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FLevelLocals, GetChecksum)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals);
|
||||
char md5string[33];
|
||||
|
||||
for(int j = 0; j < 16; ++j)
|
||||
{
|
||||
sprintf(md5string + j * 2, "%02x", level.md5[j]);
|
||||
}
|
||||
|
||||
ACTION_RETURN_STRING((const char*)md5string);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Sets a sidedef's texture and prints a message if it's not present.
|
||||
|
|
|
@ -68,17 +68,16 @@ void PolyTriangleDrawer::SetViewport(const DrawerCommandQueuePtr &queue, int x,
|
|||
isBgraRenderTarget = dest_bgra;
|
||||
|
||||
int offsetx = clamp(x, 0, dest_width);
|
||||
int offsety = clamp(y, 0, dest_height);
|
||||
int pixelsize = dest_bgra ? 4 : 1;
|
||||
|
||||
int viewport_x = x - offsetx;
|
||||
int viewport_y = y - offsety;
|
||||
int viewport_y = y;
|
||||
int viewport_width = width;
|
||||
int viewport_height = height;
|
||||
|
||||
dest += (offsetx + offsety * dest_pitch) * pixelsize;
|
||||
dest += offsetx * pixelsize;
|
||||
dest_width = clamp(viewport_x + viewport_width, 0, dest_width - offsetx);
|
||||
dest_height = clamp(viewport_y + viewport_height, 0, dest_height - offsety);
|
||||
dest_height = clamp(viewport_y + viewport_height, 0, dest_height);
|
||||
|
||||
queue->Push<PolySetViewportCommand>(viewport_x, viewport_y, viewport_width, viewport_height, dest, dest_width, dest_height, dest_pitch, dest_bgra);
|
||||
}
|
||||
|
@ -127,13 +126,11 @@ void PolyTriangleThreadData::ClearStencil(uint8_t value)
|
|||
int height = buffer->Height();
|
||||
uint8_t *data = buffer->Values();
|
||||
|
||||
int start_y = numa_node * height / num_numa_nodes;
|
||||
int end_y = (numa_node + 1) * height / num_numa_nodes;
|
||||
int core_skip = (num_cores - (start_y - core) % num_cores) % num_cores;
|
||||
start_y += core_skip;
|
||||
int skip = skipped_by_thread(0);
|
||||
int count = count_for_thread(0, height);
|
||||
|
||||
data += start_y * width;
|
||||
for (int y = start_y; y < end_y; y += num_cores)
|
||||
data += skip * width;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
memset(data, value, width);
|
||||
data += num_cores * width;
|
||||
|
@ -151,8 +148,6 @@ void PolyTriangleThreadData::SetViewport(int x, int y, int width, int height, ui
|
|||
dest_height = new_dest_height;
|
||||
dest_pitch = new_dest_pitch;
|
||||
dest_bgra = new_dest_bgra;
|
||||
numa_start_y = numa_node * screen->GetHeight() / num_numa_nodes;
|
||||
numa_end_y = (numa_node + 1) * screen->GetHeight() / num_numa_nodes;
|
||||
ccw = true;
|
||||
weaponScene = false;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,11 @@ public:
|
|||
int numa_start_y;
|
||||
int numa_end_y;
|
||||
|
||||
// The number of lines to skip to reach the first line to be rendered by this thread
|
||||
bool line_skipped_by_thread(int line)
|
||||
{
|
||||
return line < numa_start_y || line >= numa_end_y || line % num_cores != core;
|
||||
}
|
||||
|
||||
int skipped_by_thread(int first_line)
|
||||
{
|
||||
int clip_first_line = MAX(first_line, numa_start_y);
|
||||
|
@ -79,6 +83,13 @@ public:
|
|||
return clip_first_line + core_skip - first_line;
|
||||
}
|
||||
|
||||
int count_for_thread(int first_line, int count)
|
||||
{
|
||||
count = MIN(count, numa_end_y - first_line);
|
||||
int c = (count - skipped_by_thread(first_line) + num_cores - 1) / num_cores;
|
||||
return MAX(c, 0);
|
||||
}
|
||||
|
||||
// Varyings
|
||||
float worldposX[MAXWIDTH];
|
||||
float worldposY[MAXWIDTH];
|
||||
|
@ -97,6 +108,8 @@ public:
|
|||
uint8_t *dest = nullptr;
|
||||
bool weaponScene = false;
|
||||
|
||||
int viewport_y = 0;
|
||||
|
||||
private:
|
||||
ShadedTriVertex ShadeVertex(const PolyDrawArgs &drawargs, const void *vertices, int index);
|
||||
void DrawShadedTriangle(const ShadedTriVertex *vertices, bool ccw, TriDrawTriangleArgs *args);
|
||||
|
@ -105,7 +118,6 @@ private:
|
|||
static int ClipEdge(const ShadedTriVertex *verts, ShadedTriVertex *clippedvert);
|
||||
|
||||
int viewport_x = 0;
|
||||
int viewport_y = 0;
|
||||
int viewport_width = 0;
|
||||
int viewport_height = 0;
|
||||
bool ccw = true;
|
||||
|
|
|
@ -60,73 +60,84 @@ void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadDat
|
|||
ShadedTriVertex *sortedVertices[3];
|
||||
SortVertices(args, sortedVertices);
|
||||
|
||||
int clipleft = 0;
|
||||
int cliptop = MAX(thread->viewport_y, thread->numa_start_y);
|
||||
int clipright = thread->dest_width;
|
||||
int cliptop = thread->numa_start_y;
|
||||
int clipbottom = MIN(thread->dest_height, thread->numa_end_y);
|
||||
|
||||
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);
|
||||
topY = MAX(topY, cliptop);
|
||||
midY = MIN(midY, clipbottom);
|
||||
bottomY = MIN(bottomY, clipbottom);
|
||||
|
||||
if (topY >= bottomY)
|
||||
return;
|
||||
|
||||
topY += thread->skipped_by_thread(topY);
|
||||
int num_cores = thread->num_cores;
|
||||
|
||||
// Find start/end X positions for each line covered by the triangle:
|
||||
|
||||
int16_t edges[MAXHEIGHT * 2];
|
||||
|
||||
int y = topY;
|
||||
|
||||
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;
|
||||
float longPos = sortedVertices[0]->x + longStep * (y + 0.5f - sortedVertices[0]->y) + 0.5f;
|
||||
longStep *= num_cores;
|
||||
|
||||
if (topY < midY)
|
||||
if (y < 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;
|
||||
float shortPos = sortedVertices[0]->x + shortStep * (y + 0.5f - sortedVertices[0]->y) + 0.5f;
|
||||
shortStep *= num_cores;
|
||||
|
||||
for (int y = topY; y < midY; y++)
|
||||
while (y < midY)
|
||||
{
|
||||
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);
|
||||
x0 = clamp(x0, clipleft, clipright);
|
||||
x1 = clamp(x1, clipleft, clipright);
|
||||
|
||||
edges[y << 1] = x0;
|
||||
edges[(y << 1) + 1] = x1;
|
||||
|
||||
shortPos += shortStep;
|
||||
longPos += longStep;
|
||||
y += num_cores;
|
||||
}
|
||||
}
|
||||
|
||||
if (midY < bottomY)
|
||||
if (y < 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;
|
||||
float shortPos = sortedVertices[1]->x + shortStep * (y + 0.5f - sortedVertices[1]->y) + 0.5f;
|
||||
shortStep *= num_cores;
|
||||
|
||||
for (int y = midY; y < bottomY; y++)
|
||||
while (y < bottomY)
|
||||
{
|
||||
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);
|
||||
x0 = clamp(x0, clipleft, clipright);
|
||||
x1 = clamp(x1, clipleft, clipright);
|
||||
|
||||
edges[y << 1] = x0;
|
||||
edges[(y << 1) + 1] = x1;
|
||||
|
||||
shortPos += shortStep;
|
||||
longPos += longStep;
|
||||
y += num_cores;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,8 +194,14 @@ void DrawTriangle(const TriDrawTriangleArgs *args, PolyTriangleThreadData *threa
|
|||
if (OptT::Flags & SWTRI_WriteStencil)
|
||||
stencilWriteValue = args->uniforms->StencilWriteValue();
|
||||
|
||||
float weaponWOffset;
|
||||
if ((OptT::Flags & SWTRI_DepthTest) || (OptT::Flags & SWTRI_WriteDepth))
|
||||
{
|
||||
weaponWOffset = thread->weaponScene ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
int num_cores = thread->num_cores;
|
||||
for (int y = topY + thread->skipped_by_thread(topY); y < bottomY; y += num_cores)
|
||||
for (int y = topY; y < bottomY; y += num_cores)
|
||||
{
|
||||
int x = edges[y << 1];
|
||||
int xend = edges[(y << 1) + 1];
|
||||
|
@ -198,7 +215,7 @@ void DrawTriangle(const TriDrawTriangleArgs *args, PolyTriangleThreadData *threa
|
|||
|
||||
float startX = x + (0.5f - v1X);
|
||||
float startY = y + (0.5f - v1Y);
|
||||
posXW = v1W + stepXW * startX + args->gradientY.W * startY + (thread->weaponScene ? 1.0f : 0.0f);
|
||||
posXW = v1W + stepXW * startX + args->gradientY.W * startY + weaponWOffset;
|
||||
}
|
||||
|
||||
#ifndef NO_SSE
|
||||
|
|
|
@ -156,6 +156,8 @@ asmjit::CCFunc *JitCompiler::Codegen()
|
|||
LineInfo[j] = info;
|
||||
}
|
||||
|
||||
std::stable_sort(LineInfo.begin(), LineInfo.end(), [](const JitLineInfo &a, const JitLineInfo &b) { return a.InstructionIndex < b.InstructionIndex; });
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
|
@ -444,17 +446,15 @@ void JitCompiler::EmitNullPointerThrow(int index, EVMAbortException reason)
|
|||
cc.je(label);
|
||||
}
|
||||
|
||||
void JitCompiler::ThrowException(VMScriptFunction *func, VMOP *line, int reason)
|
||||
void JitCompiler::ThrowException(int reason)
|
||||
{
|
||||
ThrowAbortException(func, line, (EVMAbortException)reason, nullptr);
|
||||
ThrowAbortException((EVMAbortException)reason, nullptr);
|
||||
}
|
||||
|
||||
void JitCompiler::EmitThrowException(EVMAbortException reason)
|
||||
{
|
||||
auto call = CreateCall<void, VMScriptFunction *, VMOP *, int>(&JitCompiler::ThrowException);
|
||||
call->setArg(0, asmjit::imm_ptr(sfunc));
|
||||
call->setArg(1, asmjit::imm_ptr(pc));
|
||||
call->setArg(2, asmjit::imm(reason));
|
||||
auto call = CreateCall<void, int>(&JitCompiler::ThrowException);
|
||||
call->setArg(0, asmjit::imm(reason));
|
||||
}
|
||||
|
||||
asmjit::Label JitCompiler::EmitThrowExceptionLabel(EVMAbortException reason)
|
||||
|
@ -464,6 +464,12 @@ asmjit::Label JitCompiler::EmitThrowExceptionLabel(EVMAbortException reason)
|
|||
cc.bind(label);
|
||||
EmitThrowException(reason);
|
||||
cc.setCursor(cursor);
|
||||
|
||||
JitLineInfo info;
|
||||
info.Label = label;
|
||||
info.LineNumber = sfunc->PCToLine(pc);
|
||||
LineInfo.Push(info);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
|
|
|
@ -330,15 +330,18 @@ void JitCompiler::EmitBOUND()
|
|||
auto cursor = cc.getCursor();
|
||||
auto label = cc.newLabel();
|
||||
cc.bind(label);
|
||||
auto call = CreateCall<void, VMScriptFunction *, VMOP *, int, int>(&JitCompiler::ThrowArrayOutOfBounds);
|
||||
call->setArg(0, asmjit::imm_ptr(sfunc));
|
||||
call->setArg(1, asmjit::imm_ptr(pc));
|
||||
call->setArg(2, regD[A]);
|
||||
call->setArg(3, asmjit::imm(BC));
|
||||
auto call = CreateCall<void, int, int>(&JitCompiler::ThrowArrayOutOfBounds);
|
||||
call->setArg(0, regD[A]);
|
||||
call->setArg(1, asmjit::imm(BC));
|
||||
cc.setCursor(cursor);
|
||||
|
||||
cc.cmp(regD[A], (int)BC);
|
||||
cc.jae(label);
|
||||
|
||||
JitLineInfo info;
|
||||
info.Label = label;
|
||||
info.LineNumber = sfunc->PCToLine(pc);
|
||||
LineInfo.Push(info);
|
||||
}
|
||||
|
||||
void JitCompiler::EmitBOUND_K()
|
||||
|
@ -346,15 +349,18 @@ void JitCompiler::EmitBOUND_K()
|
|||
auto cursor = cc.getCursor();
|
||||
auto label = cc.newLabel();
|
||||
cc.bind(label);
|
||||
auto call = CreateCall<void, VMScriptFunction *, VMOP *, int, int>(&JitCompiler::ThrowArrayOutOfBounds);
|
||||
call->setArg(0, asmjit::imm_ptr(sfunc));
|
||||
call->setArg(1, asmjit::imm_ptr(pc));
|
||||
call->setArg(2, regD[A]);
|
||||
call->setArg(3, asmjit::imm(konstd[BC]));
|
||||
auto call = CreateCall<void, int, int>(&JitCompiler::ThrowArrayOutOfBounds);
|
||||
call->setArg(0, regD[A]);
|
||||
call->setArg(1, asmjit::imm(konstd[BC]));
|
||||
cc.setCursor(cursor);
|
||||
|
||||
cc.cmp(regD[A], (int)konstd[BC]);
|
||||
cc.jae(label);
|
||||
|
||||
JitLineInfo info;
|
||||
info.Label = label;
|
||||
info.LineNumber = sfunc->PCToLine(pc);
|
||||
LineInfo.Push(info);
|
||||
}
|
||||
|
||||
void JitCompiler::EmitBOUND_R()
|
||||
|
@ -362,18 +368,21 @@ void JitCompiler::EmitBOUND_R()
|
|||
auto cursor = cc.getCursor();
|
||||
auto label = cc.newLabel();
|
||||
cc.bind(label);
|
||||
auto call = CreateCall<void, VMScriptFunction *, VMOP *, int, int>(&JitCompiler::ThrowArrayOutOfBounds);
|
||||
call->setArg(0, asmjit::imm_ptr(sfunc));
|
||||
call->setArg(1, asmjit::imm_ptr(pc));
|
||||
call->setArg(2, regD[A]);
|
||||
call->setArg(3, regD[B]);
|
||||
auto call = CreateCall<void, int, int>(&JitCompiler::ThrowArrayOutOfBounds);
|
||||
call->setArg(0, regD[A]);
|
||||
call->setArg(1, regD[B]);
|
||||
cc.setCursor(cursor);
|
||||
|
||||
cc.cmp(regD[A], regD[B]);
|
||||
cc.jae(label);
|
||||
|
||||
JitLineInfo info;
|
||||
info.Label = label;
|
||||
info.LineNumber = sfunc->PCToLine(pc);
|
||||
LineInfo.Push(info);
|
||||
}
|
||||
|
||||
void JitCompiler::ThrowArrayOutOfBounds(VMScriptFunction *func, VMOP *line, int index, int size)
|
||||
void JitCompiler::ThrowArrayOutOfBounds(int index, int size)
|
||||
{
|
||||
if (index >= size)
|
||||
{
|
||||
|
|
|
@ -219,8 +219,8 @@ private:
|
|||
void EmitThrowException(EVMAbortException reason);
|
||||
asmjit::Label EmitThrowExceptionLabel(EVMAbortException reason);
|
||||
|
||||
static void ThrowArrayOutOfBounds(VMScriptFunction *func, VMOP *line, int index, int size);
|
||||
static void ThrowException(VMScriptFunction *func, VMOP *line, int reason);
|
||||
static void ThrowArrayOutOfBounds(int index, int size);
|
||||
static void ThrowException(int reason);
|
||||
|
||||
asmjit::X86Gp CheckRegD(int r0, int r1);
|
||||
asmjit::X86Xmm CheckRegF(int r0, int r1);
|
||||
|
|
|
@ -2506,6 +2506,19 @@ DEFINE_ACTION_FUNCTION_NATIVE(FLevelLocals, GetUDMFString, ZGetUDMFString)
|
|||
ACTION_RETURN_STRING(GetUDMFString(type, index, key));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FLevelLocals, GetChecksum)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals);
|
||||
char md5string[33];
|
||||
|
||||
for (int j = 0; j < 16; ++j)
|
||||
{
|
||||
sprintf(md5string + j * 2, "%02x", level.md5[j]);
|
||||
}
|
||||
|
||||
ACTION_RETURN_STRING((const char*)md5string);
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
//
|
||||
//
|
||||
|
|
|
@ -142,6 +142,11 @@ void DrawerThreads::WorkerMain(DrawerThread *thread)
|
|||
thread->current_queue++;
|
||||
thread->numa_start_y = thread->numa_node * screen->GetHeight() / thread->num_numa_nodes;
|
||||
thread->numa_end_y = (thread->numa_node + 1) * screen->GetHeight() / thread->num_numa_nodes;
|
||||
if (thread->poly)
|
||||
{
|
||||
thread->poly->numa_start_y = thread->numa_start_y;
|
||||
thread->poly->numa_end_y = thread->numa_end_y;
|
||||
}
|
||||
start_lock.unlock();
|
||||
|
||||
// Do the work:
|
||||
|
|
18
src/tarray.h
18
src/tarray.h
|
@ -497,6 +497,14 @@ public:
|
|||
Array = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Swap(TArray<T, TT> &other)
|
||||
{
|
||||
std::swap(Array, other.Array);
|
||||
std::swap(Count, other.Count);
|
||||
std::swap(Most, other.Most);
|
||||
}
|
||||
|
||||
private:
|
||||
T *Array;
|
||||
unsigned int Count;
|
||||
|
@ -760,6 +768,7 @@ struct FMap
|
|||
hash_t NumUsed;
|
||||
};
|
||||
|
||||
|
||||
template<class KT, class VT, class MapType> class TMapIterator;
|
||||
template<class KT, class VT, class MapType> class TMapConstIterator;
|
||||
|
||||
|
@ -947,6 +956,14 @@ public:
|
|||
DelKey(key);
|
||||
}
|
||||
|
||||
void Swap(MyType &other)
|
||||
{
|
||||
std::swap(Nodes, other.Nodes);
|
||||
std::swap(LastFree, other.LastFree);
|
||||
std::swap(Size, other.Size);
|
||||
std::swap(NumUsed, other.NumUsed);
|
||||
}
|
||||
|
||||
protected:
|
||||
struct IPair // This must be the same as Pair above, but with a
|
||||
{ // non-const Key.
|
||||
|
@ -1518,4 +1535,3 @@ private:
|
|||
T *Array;
|
||||
unsigned int Count;
|
||||
};
|
||||
|
||||
|
|
|
@ -89,8 +89,30 @@ struct BuildInfo
|
|||
int LeftOffset[2] = {};
|
||||
int TopOffset[2] = {};
|
||||
FImageTexture *tex = nullptr;
|
||||
|
||||
void swap(BuildInfo &other)
|
||||
{
|
||||
Name.Swap(other.Name);
|
||||
Parts.Swap(other.Parts);
|
||||
Inits.Swap(other.Inits);
|
||||
std::swap(Width, other.Width);
|
||||
std::swap(Height, other.Height);
|
||||
std::swap(Scale, other.Scale);
|
||||
std::swap(bWorldPanning, other.bWorldPanning);
|
||||
std::swap(DefinitionLump, other.DefinitionLump);
|
||||
std::swap(bComplex, bComplex);
|
||||
std::swap(textual, other.textual);
|
||||
std::swap(bNoDecals, other.bNoDecals);
|
||||
std::swap(LeftOffset[0], other.LeftOffset[0]);
|
||||
std::swap(LeftOffset[1], other.LeftOffset[1]);
|
||||
std::swap(TopOffset[0], other.TopOffset[0]);
|
||||
std::swap(TopOffset[1], other.TopOffset[1]);
|
||||
std::swap(tex, other.tex);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class FMultipatchTextureBuilder
|
||||
{
|
||||
FTextureManager &TexMan;
|
||||
|
|
|
@ -922,11 +922,21 @@ void FMultipatchTextureBuilder::ResolveAllPatches()
|
|||
}
|
||||
// Now try to resolve the images. We only can do this at the end when all multipatch textures are set up.
|
||||
int i = 0;
|
||||
|
||||
// reverse the list so that the Delete operation in the loop below deletes at the end.
|
||||
// For normal sized lists this is of no real concern, but Total Chaos has over 250000 textures where this becomes a performance issue.
|
||||
for (unsigned i = 0; i < BuiltTextures.Size() / 2; i++)
|
||||
{
|
||||
// std::swap is VERY inefficient here...
|
||||
BuiltTextures[i].swap(BuiltTextures[BuiltTextures.Size() - 1 - i]);
|
||||
}
|
||||
|
||||
|
||||
while (BuiltTextures.Size() > 0)
|
||||
{
|
||||
bool donesomething = false;
|
||||
|
||||
for (unsigned i = 0; i < BuiltTextures.Size(); i++)
|
||||
for (int i = BuiltTextures.Size()-1; i>= 0; i--)
|
||||
{
|
||||
auto &buildinfo = BuiltTextures[i];
|
||||
bool hasEmpty = false;
|
||||
|
@ -969,7 +979,6 @@ void FMultipatchTextureBuilder::ResolveAllPatches()
|
|||
}
|
||||
|
||||
BuiltTextures.Delete(i);
|
||||
i--;
|
||||
donesomething = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ FTexture::FTexture (const char *name, int lumpnum)
|
|||
:
|
||||
Scale(1,1), SourceLump(lumpnum),
|
||||
UseType(ETextureType::Any), bNoDecals(false), bNoRemap0(false), bWorldPanning(false),
|
||||
bMasked(true), bAlphaTexture(false), bHasCanvas(false), bWarped(0), bComplex(false), bMultiPatch(false), bKeepAround(false), bFullNameTexture(false),
|
||||
bMasked(true), bAlphaTexture(false), bHasCanvas(false), bWarped(0), bComplex(false), bMultiPatch(false), bFullNameTexture(false),
|
||||
Rotations(0xFFFF), SkyOffset(0), Width(0), Height(0)
|
||||
{
|
||||
bBrightmapChecked = false;
|
||||
|
|
|
@ -469,16 +469,9 @@ void FTextureManager::ReplaceTexture (FTextureID picnum, FTexture *newtexture, b
|
|||
newtexture->Name = oldtexture->Name;
|
||||
newtexture->UseType = oldtexture->UseType;
|
||||
Textures[index].Texture = newtexture;
|
||||
|
||||
newtexture->id = oldtexture->id;
|
||||
if (free && !oldtexture->bKeepAround)
|
||||
{
|
||||
delete oldtexture;
|
||||
}
|
||||
else
|
||||
{
|
||||
oldtexture->id.SetInvalid();
|
||||
}
|
||||
oldtexture->Name = "";
|
||||
AddTexture(oldtexture);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -407,7 +407,6 @@ protected:
|
|||
// fully composited before subjected to any kind of postprocessing instead of
|
||||
// doing it per patch.
|
||||
uint8_t bMultiPatch:2; // This is a multipatch texture (we really could use real type info for textures...)
|
||||
uint8_t bKeepAround:1; // This texture was used as part of a multi-patch texture. Do not free it.
|
||||
uint8_t bFullNameTexture : 1;
|
||||
uint8_t bBrightmapChecked : 1; // Set to 1 if brightmap has been checked
|
||||
uint8_t bGlowing : 1; // Texture glow color
|
||||
|
|
|
@ -148,6 +148,11 @@ public:
|
|||
char *LockBuffer(); // Obtain write access to the character buffer
|
||||
void UnlockBuffer(); // Allow shared access to the character buffer
|
||||
|
||||
void Swap(FString &other)
|
||||
{
|
||||
std::swap(Chars, other.Chars);
|
||||
}
|
||||
|
||||
operator const char *() const { return Chars; }
|
||||
|
||||
const char *GetChars() const { return Chars; }
|
||||
|
|
|
@ -66,7 +66,7 @@ class Ammo : Inventory
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
Class<Ammo> GetParentAmmo ()
|
||||
virtual Class<Ammo> GetParentAmmo ()
|
||||
{
|
||||
class<Object> type = GetClass();
|
||||
|
||||
|
|
Loading…
Reference in a new issue