The main loading block is now completely in MapLoader, except for the compatibility script handler

This commit is contained in:
Christoph Oelckers 2018-11-09 13:45:57 +01:00
parent 01d258910b
commit b7365fb70f
12 changed files with 921 additions and 874 deletions

View file

@ -1074,6 +1074,7 @@ set (PCH_SOURCES
hwrenderer/utility/hw_vrmodes.cpp
maploader/mapdata.cpp
maploader/maploader.cpp
maploader/maploader_misc.cpp
maploader/maploader_glnodes.cpp
maploader/maploader_bsp.cpp
maploader/maploader_blockmap.cpp

View file

@ -96,6 +96,7 @@ struct FLevelLocals
TArray<FSectorPortalGroup *> portalGroups;
TArray<FLinePortalSpan> linePortalSpans;
FSectionContainer sections;
int NumMapSections;

View file

@ -44,6 +44,32 @@ enum
MISSING_TEXTURE_WARN_LIMIT = 20
};
//===========================================================================
//
// SummarizeMissingTextures
//
// Lists textures that were missing more than MISSING_TEXTURE_WARN_LIMIT
// times.
//
//===========================================================================
void MapLoader::SummarizeMissingTextures(const FMissingTextureTracker &missing)
{
FMissingTextureTracker::ConstIterator it(missing);
FMissingTextureTracker::ConstPair *pair;
while (it.NextPair(pair))
{
if (pair->Value.Count > MISSING_TEXTURE_WARN_LIMIT)
{
Printf(TEXTCOLOR_RED "Missing texture '"
TEXTCOLOR_ORANGE "%s" TEXTCOLOR_RED
"' is used %d more times\n",
pair->Key.GetChars(), pair->Value.Count - MISSING_TEXTURE_WARN_LIMIT);
}
}
}
//===========================================================================
//
// P_LoadVertexes
@ -1326,3 +1352,154 @@ void MapLoader::LoadSideDefs2 (MapData *map, FMissingTextureTracker &missingtex)
}
//===========================================================================
//
// P_LoadThings
//
//===========================================================================
static uint16_t MakeSkill(int flags)
{
uint16_t res = 0;
if (flags & 1) res |= 1+2;
if (flags & 2) res |= 4;
if (flags & 4) res |= 8+16;
return res;
}
void MapLoader::LoadThings (MapData * map)
{
int lumplen = map->Size(ML_THINGS);
int numthings = lumplen / sizeof(mapthing_t);
TArray<uint8_t> mtp;
map->Read(ML_THINGS, mtp.Data());
auto mt = (mapthing_t*)mtp.Data();
MapThingsConverted.Resize(numthings);
FMapThing *mti = &MapThingsConverted[0];
// [RH] ZDoom now uses Hexen-style maps as its native format.
// Since this is the only place where Doom-style Things are ever
// referenced, we translate them into a Hexen-style thing.
for (int i=0 ; i < numthings; i++, mt++)
{
// [RH] At this point, monsters unique to Doom II were weeded out
// if the IWAD wasn't for Doom II. P_SpawnMapThing() can now
// handle these and more cases better, so we just pass it
// everything and let it decide what to do with them.
// [RH] Need to translate the spawn flags to Hexen format.
short flags = LittleShort(mt->options);
memset (&mti[i], 0, sizeof(mti[i]));
mti[i].Gravity = 1;
mti[i].Conversation = 0;
mti[i].SkillFilter = MakeSkill(flags);
mti[i].ClassFilter = 0xffff; // Doom map format doesn't have class flags so spawn for all player classes
mti[i].RenderStyle = STYLE_Count;
mti[i].Alpha = -1;
mti[i].Health = 1;
mti[i].FloatbobPhase = -1;
mti[i].pos.X = LittleShort(mt->x);
mti[i].pos.Y = LittleShort(mt->y);
mti[i].angle = LittleShort(mt->angle);
mti[i].EdNum = LittleShort(mt->type);
mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
#ifndef NO_EDATA
if (mti[i].info != NULL && mti[i].info->Special == SMT_EDThing)
{
ProcessEDMapthing(&mti[i], flags);
}
else
#endif
{
flags &= ~MTF_SKILLMASK;
mti[i].flags = (short)((flags & 0xf) | 0x7e0);
if (gameinfo.gametype == GAME_Strife)
{
mti[i].flags &= ~MTF_AMBUSH;
if (flags & STF_SHADOW) mti[i].flags |= MTF_SHADOW;
if (flags & STF_ALTSHADOW) mti[i].flags |= MTF_ALTSHADOW;
if (flags & STF_STANDSTILL) mti[i].flags |= MTF_STANDSTILL;
if (flags & STF_AMBUSH) mti[i].flags |= MTF_AMBUSH;
if (flags & STF_FRIENDLY) mti[i].flags |= MTF_FRIENDLY;
}
else
{
if (flags & BTF_BADEDITORCHECK)
{
flags &= 0x1F;
}
if (flags & BTF_NOTDEATHMATCH) mti[i].flags &= ~MTF_DEATHMATCH;
if (flags & BTF_NOTCOOPERATIVE) mti[i].flags &= ~MTF_COOPERATIVE;
if (flags & BTF_FRIENDLY) mti[i].flags |= MTF_FRIENDLY;
}
if (flags & BTF_NOTSINGLE) mti[i].flags &= ~MTF_SINGLE;
}
}
}
//===========================================================================
//
// [RH]
// P_LoadThings2
//
// Same as P_LoadThings() except it assumes Things are
// saved Hexen-style. Position also controls which single-
// player start spots are spawned by filtering out those
// whose first parameter don't match position.
//
//===========================================================================
void MapLoader::LoadThings2 (MapData * map)
{
int lumplen = map->Size(ML_THINGS);
int numthings = lumplen / sizeof(mapthinghexen_t);
MapThingsConverted.Resize(numthings);
FMapThing *mti = &MapThingsConverted[0];
TArray<uint8_t> mtp;
map->Read(ML_THINGS, mtp.Data());
auto mth = (mapthinghexen_t*)mtp.Data();
for(int i = 0; i< numthings; i++)
{
memset (&mti[i], 0, sizeof(mti[i]));
mti[i].thingid = LittleShort(mth[i].thingid);
mti[i].pos.X = LittleShort(mth[i].x);
mti[i].pos.Y = LittleShort(mth[i].y);
mti[i].pos.Z = LittleShort(mth[i].z);
mti[i].angle = LittleShort(mth[i].angle);
mti[i].EdNum = LittleShort(mth[i].type);
mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
mti[i].flags = LittleShort(mth[i].flags);
mti[i].special = mth[i].special;
for(int j=0;j<5;j++) mti[i].args[j] = mth[i].args[j];
mti[i].SkillFilter = MakeSkill(mti[i].flags);
mti[i].ClassFilter = (mti[i].flags & MTF_CLASS_MASK) >> MTF_CLASS_SHIFT;
mti[i].flags &= ~(MTF_SKILLMASK|MTF_CLASS_MASK);
if (level->flags2 & LEVEL2_HEXENHACK)
{
mti[i].flags &= 0x7ff; // mask out Strife flags if playing an original Hexen map.
}
mti[i].Gravity = 1;
mti[i].RenderStyle = STYLE_Count;
mti[i].Alpha = -1;
mti[i].Health = 1;
mti[i].FloatbobPhase = -1;
mti[i].friendlyseeblocks = -1;
}
}

View file

@ -55,6 +55,12 @@ public:
TArray<node_t> &gamenodes;
TArray<line_t*>linebuffer;
FBlockmap &blockmap;
TArray<uint8_t> &rejectmatrix;
TArray<zone_t> &Zones;
TMap<unsigned,unsigned> MapThingsUserDataIndex; // from mapthing idx -> user data idx
TArray<FUDMFKey> MapThingsUserData;
TArray<FMapThing> MapThingsConverted;
const int *oldvertextable = nullptr;
int firstglvertex = -1;
@ -119,8 +125,8 @@ public:
void CopyPlane (int tag, const DVector2 &pos, bool copyCeil);
void SetSlope (secplane_t *plane, bool setCeil, int xyangi, int zangi, const DVector3 &pos);
void VavoomSlope(sector_t * sec, int id, const DVector3 &pos, int which);
void SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable);
void SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable);
void SetSlopesFromVertexHeights();
void SpawnSlopeMakers ();
void AlignPlane(sector_t *sec, line_t *line, int which);
void SetSlopes ();
void CopySlopes();
@ -138,7 +144,9 @@ public:
gamesubsectors(store.gamesubsectors),
gamenodes(store.gamenodes),
linebuffer(store.linebuffer),
blockmap(store.blockmap)
blockmap(store.blockmap),
rejectmatrix(store.rejectmatrix),
Zones(store.Zones)
{
tagManager = tm;
}
@ -156,8 +164,17 @@ public:
void LoadSideDefs2 (MapData *map, FMissingTextureTracker &missingtex);
void LoopSidedefs (bool firstloop);
void GroupLines(bool buildmap);
void LoadThings (MapData * map);
void LoadThings2 (MapData * map);
bool LoadBsp(MapData *map);
void LoadReject (MapData * map);
void FloodZone (sector_t *sec, int zonenum);
void FloodZones ();
void SummarizeMissingTextures(const FMissingTextureTracker &missing);
void SetRenderSector();
void FixMinisegReferences();
void FixHoles();
void ParseTextMap(MapData *map, FMissingTextureTracker &missingtex);

View file

@ -0,0 +1,580 @@
//-----------------------------------------------------------------------------
//
// Copyright 1993-1996 id Software
// Copyright 1994-1996 Raven Software
// Copyright 1999-2016 Randy Heit
// Copyright 2002-2018 Christoph Oelckers
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//-----------------------------------------------------------------------------
//
// DESCRIPTION:
// Map loader
//
#include "p_setup.h"
#include "maploader.h"
#include "mapdata.h"
#include "files.h"
#include "i_system.h"
#include "g_levellocals.h"
#include "r_sky.h"
#include "p_lnspec.h"
#include "p_tags.h"
#include "v_text.h"
#include "doomstat.h"
#include "edata.h"
#include "w_wad.h"
#include "actor.h"
#include "gi.h"
//===========================================================================
//
//
//
//===========================================================================
void MapLoader::LoadReject (MapData * map)
{
const int neededsize = (sectors.Size() * sectors.Size() + 7) >> 3;
int rejectsize;
if (!map->CheckName(ML_REJECT, "REJECT"))
{
rejectsize = 0;
}
else
{
rejectsize = map->Size(ML_REJECT);
}
if (rejectsize < neededsize)
{
if (rejectsize > 0)
{
Printf ("REJECT is %d byte%s too small.\n", neededsize - rejectsize,
neededsize-rejectsize==1?"":"s");
}
rejectmatrix.Reset();
}
else
{
// Check if the reject has some actual content. If not, free it.
rejectsize = MIN (rejectsize, neededsize);
rejectmatrix.Alloc(rejectsize);
map->Read (ML_REJECT, &rejectmatrix[0], rejectsize);
int qwords = rejectsize / 8;
int i;
if (qwords > 0)
{
const uint64_t *qreject = (const uint64_t *)&rejectmatrix[0];
i = 0;
do
{
if (qreject[i] != 0)
return;
} while (++i < qwords);
}
rejectsize &= 7;
qwords *= 8;
for (i = 0; i < rejectsize; ++i)
{
if (rejectmatrix[qwords + i] != 0)
return;
}
// Reject has no data, so pretend it isn't there.
rejectmatrix.Reset();
}
}
//===========================================================================
//
// Sound enviroment handling
//
//===========================================================================
void MapLoader::FloodZone (sector_t *sec, int zonenum)
{
if (sec->ZoneNumber == zonenum)
return;
sec->ZoneNumber = zonenum;
for (auto check : sec->Lines)
{
sector_t *other;
if (check->sidedef[1] == nullptr || (check->flags & ML_ZONEBOUNDARY))
continue;
if (check->frontsector == sec)
{
assert(check->backsector != nullptr);
other = check->backsector;
}
else
{
assert(check->frontsector != nullptr);
other = check->frontsector;
}
if (other->ZoneNumber != zonenum)
FloodZone (other, zonenum);
}
}
void MapLoader::FloodZones ()
{
int z = 0, i;
ReverbContainer *reverb;
for (auto &sec : sectors)
{
if (sec.ZoneNumber == 0xFFFF)
{
FloodZone (&sec, z++);
}
}
Zones.Resize(z);
reverb = S_FindEnvironment(level->DefaultEnvironment);
if (reverb == NULL)
{
Printf("Sound environment %d, %d not found\n", level->DefaultEnvironment >> 8, level->DefaultEnvironment & 255);
reverb = DefaultEnvironments[0];
}
for (i = 0; i < z; ++i)
{
Zones[i].Environment = reverb;
}
}
//==========================================================================
//
// Keep both the original nodes from the WAD and the GL nodes created here.
// The original set is only being used to get the sector for in-game
// positioning of actors but not for rendering.
//
// This is necessary because ZDBSP is much more sensitive
// to sloppy mapping practices that produce overlapping sectors.
// The crane in P:AR E1M3 is a good example that would be broken if
// this wasn't done.
//
//==========================================================================
//==========================================================================
//
// PointOnLine
//
// Same as the one im the node builder, but not part of a specific class
//
//==========================================================================
static bool PointOnLine (int x, int y, int x1, int y1, int dx, int dy)
{
const double SIDE_EPSILON = 6.5536;
// For most cases, a simple dot product is enough.
double d_dx = double(dx);
double d_dy = double(dy);
double d_x = double(x);
double d_y = double(y);
double d_x1 = double(x1);
double d_y1 = double(y1);
double s_num = (d_y1-d_y)*d_dx - (d_x1-d_x)*d_dy;
if (fabs(s_num) < 17179869184.0) // 4<<32
{
// Either the point is very near the line, or the segment defining
// the line is very short: Do a more expensive test to determine
// just how far from the line the point is.
double l = g_sqrt(d_dx*d_dx+d_dy*d_dy);
double dist = fabs(s_num)/l;
if (dist < SIDE_EPSILON)
{
return true;
}
}
return false;
}
//==========================================================================
//
// SetRenderSector
//
// Sets the render sector for each GL subsector so that the proper flat
// information can be retrieved
//
//==========================================================================
void MapLoader::SetRenderSector()
{
int i;
uint32_t j;
TArray<subsector_t *> undetermined;
subsector_t * ss;
#if 0 // doesn't work as expected :(
// hide all sectors on textured automap that only have hidden lines.
bool *hidesec = new bool[numsectors];
for(i = 0; i < numsectors; i++)
{
hidesec[i] = true;
}
for(i = 0; i < numlines; i++)
{
if (!(lines[i].flags & ML_DONTDRAW))
{
hidesec[lines[i].frontsector - sectors] = false;
if (lines[i].backsector != NULL)
{
hidesec[lines[i].backsector - sectors] = false;
}
}
}
for(i = 0; i < numsectors; i++)
{
if (hidesec[i]) sectors[i].MoreFlags |= SECMF_HIDDEN;
}
delete [] hidesec;
#endif
// Check for incorrect partner seg info so that the following code does not crash.
for (auto &seg : segs)
{
auto p = seg.PartnerSeg;
if (p != nullptr)
{
int partner = p->Index();
if (partner < 0 || partner >= (int)segs.Size() || &segs[partner] != p)
{
seg.PartnerSeg = nullptr;
}
// glbsp creates such incorrect references for Strife.
if (seg.linedef && seg.PartnerSeg != nullptr && !seg.PartnerSeg->linedef)
{
seg.PartnerSeg = seg.PartnerSeg->PartnerSeg = nullptr;
}
}
}
for (auto &seg : segs)
{
if (seg.PartnerSeg != nullptr && seg.PartnerSeg->PartnerSeg != &seg)
{
seg.PartnerSeg = nullptr;
}
}
// look up sector number for each subsector
for (auto &ss : subsectors)
{
// For rendering pick the sector from the first seg that is a sector boundary
// this takes care of self-referencing sectors
seg_t *seg = ss.firstline;
// Check for one-dimensional subsectors. These should be ignored when
// being processed for automap drawing etc.
ss.flags |= SSECF_DEGENERATE;
for(j=2; j<ss.numlines; j++)
{
if (!PointOnLine(seg[j].v1->fixX(), seg[j].v1->fixY(), seg->v1->fixX(), seg->v1->fixY(), seg->v2->fixX() -seg->v1->fixX(), seg->v2->fixY() -seg->v1->fixY()))
{
// Not on the same line
ss.flags &= ~SSECF_DEGENERATE;
break;
}
}
seg = ss.firstline;
for(j=0; j<ss.numlines; j++)
{
if(seg->sidedef && (seg->PartnerSeg == nullptr || (seg->PartnerSeg->sidedef != nullptr && seg->sidedef->sector!=seg->PartnerSeg->sidedef->sector)))
{
ss.render_sector = seg->sidedef->sector;
break;
}
seg++;
}
if(ss.render_sector == NULL)
{
undetermined.Push(&ss);
}
}
// assign a vaild render sector to all subsectors which haven't been processed yet.
while (undetermined.Size())
{
bool deleted=false;
for(i=undetermined.Size()-1;i>=0;i--)
{
ss=undetermined[i];
seg_t * seg = ss->firstline;
for(j=0; j<ss->numlines; j++)
{
if (seg->PartnerSeg != nullptr && seg->PartnerSeg->Subsector)
{
sector_t * backsec = seg->PartnerSeg->Subsector->render_sector;
if (backsec)
{
ss->render_sector = backsec;
undetermined.Delete(i);
deleted = 1;
break;
}
}
seg++;
}
}
// We still got some left but the loop above was unable to assign them.
// This only happens when a subsector is off the map.
// Don't bother and just assign the real sector for rendering
if (!deleted && undetermined.Size())
{
for(i=undetermined.Size()-1;i>=0;i--)
{
ss=undetermined[i];
ss->render_sector=ss->sector;
}
break;
}
}
}
//==========================================================================
//
// FixMinisegReferences
//
// Sometimes it can happen that two matching minisegs do not have their partner set.
// Fix that here.
//
//==========================================================================
void MapLoader::FixMinisegReferences()
{
TArray<seg_t *> bogussegs;
for (unsigned i = 0; i < segs.Size(); i++)
{
if (segs[i].sidedef == nullptr && segs[i].PartnerSeg == nullptr)
{
bogussegs.Push(&segs[i]);
}
}
for (unsigned i = 0; i < bogussegs.Size(); i++)
{
auto seg1 = bogussegs[i];
seg_t *pick = nullptr;
unsigned int picki = -1;
// Try to fix the reference: If there's exactly one other seg in the set which matches as a partner link those two segs together.
for (unsigned j = i + 1; j < bogussegs.Size(); j++)
{
auto seg2 = bogussegs[j];
if (seg1->v1 == seg2->v2 && seg2->v1 == seg1->v2 && seg1->Subsector->render_sector == seg2->Subsector->render_sector)
{
pick = seg2;
picki = j;
break;
}
}
if (pick)
{
DPrintf(DMSG_NOTIFY, "Linking miniseg pair from (%2.3f, %2.3f) -> (%2.3f, %2.3f) in sector %d\n", pick->v2->fX(), pick->v2->fY(), pick->v1->fX(), pick->v1->fY(), pick->frontsector->Index());
pick->PartnerSeg = seg1;
seg1->PartnerSeg = pick;
assert(seg1->v1 == pick->v2 && pick->v1 == seg1->v2);
bogussegs.Delete(picki);
bogussegs.Delete(i);
i--;
}
}
}
//==========================================================================
//
// FixHoles
//
// ZDBSP can leave holes in the node tree on extremely detailed maps.
// To help out the triangulator these are filled with dummy subsectors
// so that it can process the area correctly.
//
//==========================================================================
void MapLoader::FixHoles()
{
TArray<seg_t *> bogussegs;
TArray<TArray<seg_t *>> segloops;
for (unsigned i = 0; i < segs.Size(); i++)
{
if (segs[i].sidedef == nullptr && segs[i].PartnerSeg == nullptr)
{
bogussegs.Push(&segs[i]);
}
}
while (bogussegs.Size() > 0)
{
segloops.Reserve(1);
auto *segloop = &segloops.Last();
seg_t *startseg;
seg_t *checkseg;
while (bogussegs.Size() > 0)
{
bool foundsome = false;
if (segloop->Size() == 0)
{
bogussegs.Pop(startseg);
segloop->Push(startseg);
checkseg = startseg;
}
for (unsigned i = 0; i < bogussegs.Size(); i++)
{
auto seg1 = bogussegs[i];
if (seg1->v1 == checkseg->v2 && seg1->Subsector->render_sector == checkseg->Subsector->render_sector)
{
foundsome = true;
segloop->Push(seg1);
bogussegs.Delete(i);
i--;
checkseg = seg1;
if (seg1->v2 == startseg->v1)
{
// The loop is complete. Start a new one
segloops.Reserve(1);
segloop = &segloops.Last();
}
}
}
if (!foundsome)
{
if ((*segloop)[0]->v1 != segloop->Last()->v2)
{
// There was no connected seg, leaving an unclosed loop.
// Clear this and continue looking.
segloop->Clear();
}
}
}
for (unsigned i = 0; i < segloops.Size(); i++)
{
if (segloops[i].Size() == 0)
{
segloops.Delete(i);
i--;
}
}
// Add dummy entries to the level's seg and subsector arrays
if (segloops.Size() > 0)
{
// cound the number of segs to add.
unsigned segcount = 0;
for (auto &segloop : segloops)
segcount += segloop.Size();
seg_t *oldsegstartptr = &segs[0];
subsector_t *oldssstartptr = &subsectors[0];
unsigned newsegstart = segs.Reserve(segcount);
unsigned newssstart = subsectors.Reserve(segloops.Size());
seg_t *newsegstartptr = &segs[0];
subsector_t *newssstartptr = &subsectors[0];
// Now fix all references to these in the level data.
// Note that the Index() method does not work here due to the reallocation.
for (auto &seg : segs)
{
if (seg.PartnerSeg) seg.PartnerSeg = newsegstartptr + (seg.PartnerSeg - oldsegstartptr);
seg.Subsector = newssstartptr + (seg.Subsector - oldssstartptr);
}
for (auto &sub : subsectors)
{
sub.firstline = newsegstartptr + (sub.firstline - oldsegstartptr);
}
for (auto &node : nodes)
{
// How hideous... :(
for (auto & p : node.children)
{
auto intp = (intptr_t)p;
if (intp & 1)
{
subsector_t *sp = (subsector_t*)(intp - 1);
sp = newssstartptr + (sp - oldssstartptr);
intp = intptr_t(sp) + 1;
p = (void*)intp;
}
}
}
for (auto &segloop : segloops)
{
for (auto &seg : segloop)
{
seg = newsegstartptr + (seg - oldsegstartptr);
}
}
// The seg lists in the sidedefs and the subsector lists in the sectors are not set yet when this gets called.
// Add the new data. This doesn't care about convexity. It is never directly used to generate a primitive.
for (auto &segloop : segloops)
{
DPrintf(DMSG_NOTIFY, "Adding dummy subsector for sector %d\n", segloop[0]->Subsector->render_sector->Index());
subsector_t &sub = subsectors[newssstart++];
memset(&sub, 0, sizeof(sub));
sub.sector = segloop[0]->frontsector;
sub.render_sector = segloop[0]->Subsector->render_sector;
sub.numlines = segloop.Size();
sub.firstline = &segs[newsegstart];
sub.flags = SSECF_HOLE;
for (auto otherseg : segloop)
{
DPrintf(DMSG_NOTIFY, " Adding seg from (%2.3f, %2.3f) -> (%2.3f, %2.3f)\n", otherseg->v2->fX(), otherseg->v2->fY(), otherseg->v1->fX(), otherseg->v1->fY());
seg_t &seg = segs[newsegstart++];
memset(&seg, 0, sizeof(seg));
seg.v1 = otherseg->v2;
seg.v2 = otherseg->v1;
seg.frontsector = seg.backsector = otherseg->backsector = otherseg->frontsector;
seg.PartnerSeg = otherseg;
otherseg->PartnerSeg = &seg;
seg.Subsector = &sub;
}
}
}
}
}

View file

@ -276,14 +276,15 @@ void MapLoader::VavoomSlope(sector_t * sec, int id, const DVector3 &pos, int whi
//
//==========================================================================
void MapLoader::SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable)
void MapLoader::SetSlopesFromVertexHeights()
{
TMap<int, double> vt_heights[2];
FMapThing *mt;
bool vt_found = false;
for (mt = firstmt; mt < lastmt; ++mt)
{
for (unsigned i=0;i<MapThingsConverted.Size(); i++)
{
auto mt = &MapThingsConverted[i];
if (mt->info != nullptr && mt->info->Type == nullptr)
{
if (mt->info->Special == SMT_VertexFloorZ || mt->info->Special == SMT_VertexCeilingZ)
@ -401,12 +402,11 @@ void MapLoader::SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt
//
//===========================================================================
void MapLoader::SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable)
void MapLoader::SpawnSlopeMakers ()
{
FMapThing *mt;
for (mt = firstmt; mt < lastmt; ++mt)
for (unsigned i=0;i<MapThingsConverted.Size(); i++)
{
auto mt = &MapThingsConverted[i];
if (mt->info != nullptr && mt->info->Type == nullptr &&
(mt->info->Special >= SMT_SlopeFloorPointLine && mt->info->Special <= SMT_VavoomCeiling))
{
@ -444,8 +444,9 @@ void MapLoader::SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt, const i
}
}
for (mt = firstmt; mt < lastmt; ++mt)
{
for (unsigned i=0;i<MapThingsConverted.Size(); i++)
{
auto mt = &MapThingsConverted[i];
if (mt->info != nullptr && mt->info->Type == nullptr &&
(mt->info->Special == SMT_CopyFloorPlane || mt->info->Special == SMT_CopyCeilingPlane))
{
@ -454,7 +455,7 @@ void MapLoader::SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt, const i
}
}
SetSlopesFromVertexHeights(firstmt, lastmt, oldvertextable);
SetSlopesFromVertexHeights();
}

View file

@ -1,242 +0,0 @@
//===========================================================================
//
// SpawnMapThing
//
//===========================================================================
CVAR(Bool, dumpspawnedthings, false, 0)
AActor *SpawnMapThing(int index, FMapThing *mt, int position)
{
AActor *spawned = P_SpawnMapThing(mt, position);
if (dumpspawnedthings)
{
Printf("%5d: (%5f, %5f, %5f), doomednum = %5d, flags = %04x, type = %s\n",
index, mt->pos.X, mt->pos.Y, mt->pos.Z, mt->EdNum, mt->flags,
spawned? spawned->GetClass()->TypeName.GetChars() : "(none)");
}
T_AddSpawnedThing(spawned);
return spawned;
}
//===========================================================================
//
// SetMapThingUserData
//
//===========================================================================
static void SetMapThingUserData(AActor *actor, unsigned udi)
{
if (actor == NULL)
{
return;
}
while (MapThingsUserData[udi].Key != NAME_None)
{
FName varname = MapThingsUserData[udi].Key;
PField *var = dyn_cast<PField>(actor->GetClass()->FindSymbol(varname, true));
if (var == NULL || (var->Flags & (VARF_Native|VARF_Private|VARF_Protected|VARF_Static)) || !var->Type->isScalar())
{
DPrintf(DMSG_WARNING, "%s is not a writable user variable in class %s\n", varname.GetChars(),
actor->GetClass()->TypeName.GetChars());
}
else
{ // Set the value of the specified user variable.
void *addr = reinterpret_cast<uint8_t *>(actor) + var->Offset;
if (var->Type == TypeString)
{
var->Type->InitializeValue(addr, &MapThingsUserData[udi].StringVal);
}
else if (var->Type->isFloat())
{
var->Type->SetValue(addr, MapThingsUserData[udi].FloatVal);
}
else if (var->Type->isInt() || var->Type == TypeBool)
{
var->Type->SetValue(addr, MapThingsUserData[udi].IntVal);
}
}
udi++;
}
}
//===========================================================================
//
// P_LoadThings
//
//===========================================================================
uint16_t MakeSkill(int flags)
{
uint16_t res = 0;
if (flags & 1) res |= 1+2;
if (flags & 2) res |= 4;
if (flags & 4) res |= 8+16;
return res;
}
void P_LoadThings (MapData * map)
{
int lumplen = map->Size(ML_THINGS);
int numthings = lumplen / sizeof(mapthing_t);
char *mtp;
mapthing_t *mt;
mtp = new char[lumplen];
map->Read(ML_THINGS, mtp);
mt = (mapthing_t*)mtp;
MapThingsConverted.Resize(numthings);
FMapThing *mti = &MapThingsConverted[0];
// [RH] ZDoom now uses Hexen-style maps as its native format.
// Since this is the only place where Doom-style Things are ever
// referenced, we translate them into a Hexen-style thing.
for (int i=0 ; i < numthings; i++, mt++)
{
// [RH] At this point, monsters unique to Doom II were weeded out
// if the IWAD wasn't for Doom II. P_SpawnMapThing() can now
// handle these and more cases better, so we just pass it
// everything and let it decide what to do with them.
// [RH] Need to translate the spawn flags to Hexen format.
short flags = LittleShort(mt->options);
memset (&mti[i], 0, sizeof(mti[i]));
mti[i].Gravity = 1;
mti[i].Conversation = 0;
mti[i].SkillFilter = MakeSkill(flags);
mti[i].ClassFilter = 0xffff; // Doom map format doesn't have class flags so spawn for all player classes
mti[i].RenderStyle = STYLE_Count;
mti[i].Alpha = -1;
mti[i].Health = 1;
mti[i].FloatbobPhase = -1;
mti[i].pos.X = LittleShort(mt->x);
mti[i].pos.Y = LittleShort(mt->y);
mti[i].angle = LittleShort(mt->angle);
mti[i].EdNum = LittleShort(mt->type);
mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
#ifndef NO_EDATA
if (mti[i].info != NULL && mti[i].info->Special == SMT_EDThing)
{
ProcessEDMapthing(&mti[i], flags);
}
else
#endif
{
flags &= ~MTF_SKILLMASK;
mti[i].flags = (short)((flags & 0xf) | 0x7e0);
if (gameinfo.gametype == GAME_Strife)
{
mti[i].flags &= ~MTF_AMBUSH;
if (flags & STF_SHADOW) mti[i].flags |= MTF_SHADOW;
if (flags & STF_ALTSHADOW) mti[i].flags |= MTF_ALTSHADOW;
if (flags & STF_STANDSTILL) mti[i].flags |= MTF_STANDSTILL;
if (flags & STF_AMBUSH) mti[i].flags |= MTF_AMBUSH;
if (flags & STF_FRIENDLY) mti[i].flags |= MTF_FRIENDLY;
}
else
{
if (flags & BTF_BADEDITORCHECK)
{
flags &= 0x1F;
}
if (flags & BTF_NOTDEATHMATCH) mti[i].flags &= ~MTF_DEATHMATCH;
if (flags & BTF_NOTCOOPERATIVE) mti[i].flags &= ~MTF_COOPERATIVE;
if (flags & BTF_FRIENDLY) mti[i].flags |= MTF_FRIENDLY;
}
if (flags & BTF_NOTSINGLE) mti[i].flags &= ~MTF_SINGLE;
}
}
delete [] mtp;
}
//===========================================================================
//
// [RH]
// P_LoadThings2
//
// Same as P_LoadThings() except it assumes Things are
// saved Hexen-style. Position also controls which single-
// player start spots are spawned by filtering out those
// whose first parameter don't match position.
//
//===========================================================================
void P_LoadThings2 (MapData * map)
{
int lumplen = map->Size(ML_THINGS);
int numthings = lumplen / sizeof(mapthinghexen_t);
char *mtp;
MapThingsConverted.Resize(numthings);
FMapThing *mti = &MapThingsConverted[0];
mtp = new char[lumplen];
map->Read(ML_THINGS, mtp);
mapthinghexen_t *mth = (mapthinghexen_t*)mtp;
for(int i = 0; i< numthings; i++)
{
memset (&mti[i], 0, sizeof(mti[i]));
mti[i].thingid = LittleShort(mth[i].thingid);
mti[i].pos.X = LittleShort(mth[i].x);
mti[i].pos.Y = LittleShort(mth[i].y);
mti[i].pos.Z = LittleShort(mth[i].z);
mti[i].angle = LittleShort(mth[i].angle);
mti[i].EdNum = LittleShort(mth[i].type);
mti[i].info = DoomEdMap.CheckKey(mti[i].EdNum);
mti[i].flags = LittleShort(mth[i].flags);
mti[i].special = mth[i].special;
for(int j=0;j<5;j++) mti[i].args[j] = mth[i].args[j];
mti[i].SkillFilter = MakeSkill(mti[i].flags);
mti[i].ClassFilter = (mti[i].flags & MTF_CLASS_MASK) >> MTF_CLASS_SHIFT;
mti[i].flags &= ~(MTF_SKILLMASK|MTF_CLASS_MASK);
if (level.flags2 & LEVEL2_HEXENHACK)
{
mti[i].flags &= 0x7ff; // mask out Strife flags if playing an original Hexen map.
}
mti[i].Gravity = 1;
mti[i].RenderStyle = STYLE_Count;
mti[i].Alpha = -1;
mti[i].Health = 1;
mti[i].FloatbobPhase = -1;
mti[i].friendlyseeblocks = -1;
}
delete[] mtp;
}
//===========================================================================
//
//
//
//===========================================================================
void P_SpawnThings (int position)
{
int numthings = MapThingsConverted.Size();
for (int i=0; i < numthings; i++)
{
AActor *actor = SpawnMapThing (i, &MapThingsConverted[i], position);
unsigned *udi = MapThingsUserDataIndex.CheckKey((unsigned)i);
if (udi != NULL)
{
SetMapThingUserData(actor, *udi);
}
}
}

View file

@ -1,4 +1,4 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
// Copyright 1993-1996 id Software
// Copyright 1994-1996 Raven Software
@ -98,6 +98,8 @@
#include "events.h"
#include "actorinlines.h"
#include "a_dynlight.h"
#include "fragglescript/t_fs.h"
#include "types.h"
// MACROS ------------------------------------------------------------------
@ -5791,13 +5793,14 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
}
//
// P_SpawnMapThing
// The fields of the mapthing should
// already be in host byte order.
//
// [RH] position is used to weed out unwanted start spots
AActor *P_SpawnMapThing (FMapThing *mthing, int position)
static AActor *P_SpawnMapThing (FMapThing *mthing, int position)
{
PClassActor *i;
int mask;
@ -6193,6 +6196,94 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
return mobj;
}
//===========================================================================
//
// SpawnMapThing
//
//===========================================================================
CVAR(Bool, dumpspawnedthings, false, 0)
static AActor *SpawnMapThing(int index, FMapThing *mt, int position)
{
AActor *spawned = P_SpawnMapThing(mt, position);
if (dumpspawnedthings)
{
Printf("%5d: (%5f, %5f, %5f), doomednum = %5d, flags = %04x, type = %s\n",
index, mt->pos.X, mt->pos.Y, mt->pos.Z, mt->EdNum, mt->flags,
spawned? spawned->GetClass()->TypeName.GetChars() : "(none)");
}
T_AddSpawnedThing(spawned);
return spawned;
}
//===========================================================================
//
// SetMapThingUserData
//
//===========================================================================
static void SetMapThingUserData(AActor *actor, unsigned udi, TArray<FUDMFKey> &MapThingsUserData, TMap<unsigned,unsigned> &MapThingsUserDataIndex)
{
if (actor == NULL)
{
return;
}
while (MapThingsUserData[udi].Key != NAME_None)
{
FName varname = MapThingsUserData[udi].Key;
PField *var = dyn_cast<PField>(actor->GetClass()->FindSymbol(varname, true));
if (var == NULL || (var->Flags & (VARF_Native|VARF_Private|VARF_Protected|VARF_Static)) || !var->Type->isScalar())
{
DPrintf(DMSG_WARNING, "%s is not a writable user variable in class %s\n", varname.GetChars(),
actor->GetClass()->TypeName.GetChars());
}
else
{ // Set the value of the specified user variable.
void *addr = reinterpret_cast<uint8_t *>(actor) + var->Offset;
if (var->Type == TypeString)
{
var->Type->InitializeValue(addr, &MapThingsUserData[udi].StringVal);
}
else if (var->Type->isFloat())
{
var->Type->SetValue(addr, MapThingsUserData[udi].FloatVal);
}
else if (var->Type->isInt() || var->Type == TypeBool)
{
var->Type->SetValue(addr, MapThingsUserData[udi].IntVal);
}
}
udi++;
}
}
//===========================================================================
//
//
//
//===========================================================================
void P_SpawnThings (TArray<FMapThing> &MapThingsConverted, TArray<FUDMFKey> &MapThingsUserData, TMap<unsigned,unsigned> &MapThingsUserDataIndex, int position)
{
int numthings = MapThingsConverted.Size();
for (int i=0; i < numthings; i++)
{
AActor *actor = SpawnMapThing (i, &MapThingsConverted[i], position);
unsigned *udi = MapThingsUserDataIndex.CheckKey((unsigned)i);
if (udi != NULL)
{
SetMapThingUserData(actor, *udi, MapThingsUserData, MapThingsUserDataIndex);
}
}
}
//

View file

@ -116,7 +116,6 @@ void BloodCrypt (void *data, int key, int len);
void P_ClearUDMFKeys();
void InitRenderInfo();
extern AActor *P_SpawnMapThing (FMapThing *mthing, int position);
extern void P_TranslateTeleportThings (void);
@ -140,6 +139,8 @@ inline bool P_LoadBuildMap(uint8_t *mapdata, size_t len, FMapThing **things, int
return false;
}
void P_SpawnThings (TArray<FMapThing> &MapThingsConverted, TArray<FUDMFKey> &MapThingsUserData, TMap<unsigned,unsigned> &MapThingsUserDataIndex, int position);
//
// MAP related Lookup tables.
@ -149,164 +150,8 @@ TArray<vertexdata_t> vertexdatas;
bool hasglnodes;
TArray<FMapThing> MapThingsConverted;
TMap<unsigned,unsigned> MapThingsUserDataIndex; // from mapthing idx -> user data idx
TArray<FUDMFKey> MapThingsUserData;
static void P_AllocateSideDefs (MapData *map, int count);
//===========================================================================
//
// SummarizeMissingTextures
//
// Lists textures that were missing more than MISSING_TEXTURE_WARN_LIMIT
// times.
//
//===========================================================================
static void SummarizeMissingTextures(const FMissingTextureTracker &missing)
{
FMissingTextureTracker::ConstIterator it(missing);
FMissingTextureTracker::ConstPair *pair;
while (it.NextPair(pair))
{
if (pair->Value.Count > MISSING_TEXTURE_WARN_LIMIT)
{
Printf(TEXTCOLOR_RED "Missing texture '"
TEXTCOLOR_ORANGE "%s" TEXTCOLOR_RED
"' is used %d more times\n",
pair->Key.GetChars(), pair->Value.Count - MISSING_TEXTURE_WARN_LIMIT);
}
}
}
//===========================================================================
//
// Sound enviroment handling
//
//===========================================================================
void P_FloodZone (sector_t *sec, int zonenum)
{
if (sec->ZoneNumber == zonenum)
return;
sec->ZoneNumber = zonenum;
for (auto check : sec->Lines)
{
sector_t *other;
if (check->sidedef[1] == NULL || (check->flags & ML_ZONEBOUNDARY))
continue;
if (check->frontsector == sec)
{
assert(check->backsector != NULL);
other = check->backsector;
}
else
{
assert(check->frontsector != NULL);
other = check->frontsector;
}
if (other->ZoneNumber != zonenum)
P_FloodZone (other, zonenum);
}
}
void P_FloodZones ()
{
int z = 0, i;
ReverbContainer *reverb;
for (auto &sec : level.sectors)
{
if (sec.ZoneNumber == 0xFFFF)
{
P_FloodZone (&sec, z++);
}
}
level.Zones.Resize(z);
reverb = S_FindEnvironment(level.DefaultEnvironment);
if (reverb == NULL)
{
Printf("Sound environment %d, %d not found\n", level.DefaultEnvironment >> 8, level.DefaultEnvironment & 255);
reverb = DefaultEnvironments[0];
}
for (i = 0; i < z; ++i)
{
level.Zones[i].Environment = reverb;
}
}
//===========================================================================
//
//
//
//===========================================================================
void P_LoadReject (MapData * map, bool junk)
{
const int neededsize = (level.sectors.Size() * level.sectors.Size() + 7) >> 3;
int rejectsize;
if (!map->CheckName(ML_REJECT, "REJECT"))
{
rejectsize = 0;
}
else
{
rejectsize = junk ? 0 : map->Size(ML_REJECT);
}
if (rejectsize < neededsize)
{
if (rejectsize > 0)
{
Printf ("REJECT is %d byte%s too small.\n", neededsize - rejectsize,
neededsize-rejectsize==1?"":"s");
}
level.rejectmatrix.Reset();
}
else
{
// Check if the reject has some actual content. If not, free it.
rejectsize = MIN (rejectsize, neededsize);
level.rejectmatrix.Alloc(rejectsize);
map->Read (ML_REJECT, &level.rejectmatrix[0], rejectsize);
int qwords = rejectsize / 8;
int i;
if (qwords > 0)
{
const uint64_t *qreject = (const uint64_t *)&level.rejectmatrix[0];
i = 0;
do
{
if (qreject[i] != 0)
return;
} while (++i < qwords);
}
rejectsize &= 7;
qwords *= 8;
for (i = 0; i < rejectsize; ++i)
{
if (level.rejectmatrix[qwords + i] != 0)
return;
}
// Reject has no data, so pretend it isn't there.
level.rejectmatrix.Reset();
}
}
//===========================================================================
//
//
@ -439,9 +284,6 @@ void P_FreeLevelData ()
// [ZZ] delete per-map event handlers
E_Shutdown(true);
MapThingsConverted.Clear();
MapThingsUserDataIndex.Clear();
MapThingsUserData.Clear();
FCanvasTextureInfo::EmptyList();
R_FreePastViewers();
P_ClearUDMFKeys();
@ -569,12 +411,7 @@ void P_FreeExtraLevelData()
void P_SetupLevel (const char *lumpname, int position, bool newGame)
{
cycle_t times[20];
#if 0
FMapThing *buildthings;
int numbuildthings;
#endif
int i;
bool buildmap;
level.ShaderStartTime = I_msTimeFS(); // indicate to the shader system that the level just started
@ -672,10 +509,6 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
}
// ----------------------------------------->
MapLoader maploader(level, &tagManager);
maploader.maptype = level.maptype;
maploader.ForceNodeBuild = gennodes;
// note: most of this ordering is important
@ -713,10 +546,7 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
level.maptype = MAPTYPE_UDMF;
}
FName checksum = CheckCompatibility(map);
if (ib_compatflags & BCOMPATF_REBUILDNODES)
{
maploader.ForceNodeBuild = true;
}
T_LoadScripts(map);
@ -727,6 +557,18 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
P_LoadStrifeConversations (map, lumpname);
// ----------------------------------------->
MapLoader maploader(level, &tagManager);
maploader.maptype = level.maptype;
maploader.ForceNodeBuild = gennodes;
if (ib_compatflags & BCOMPATF_REBUILDNODES)
{
maploader.ForceNodeBuild = true;
}
FMissingTextureTracker missingtex;
@ -745,9 +587,9 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
maploader.FinishLoadingLineDefs ();
if (!map->HasBehavior)
P_LoadThings (map);
maploader.LoadThings (map);
else
P_LoadThings2 (map); // [RH] Load Hexen-style things
maploader.LoadThings2 (map); // [RH] Load Hexen-style things
}
else
{
@ -759,34 +601,33 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
maploader.LoopSidedefs (true);
SummarizeMissingTextures(missingtex);
bool reloop = false;
maploader.SummarizeMissingTextures(missingtex);
maploader.LoadBsp(map);
// set the head node for gameplay purposes. If the separate gamenodes array is not empty, use that, otherwise use the render nodes.
level.headgamenode = level.gamenodes.Size() > 0 ? &level.gamenodes[level.gamenodes.Size() - 1] : level.nodes.Size()? &level.nodes[level.nodes.Size() - 1] : nullptr;
bool reloop = maploader.LoadBsp(map);
maploader.LoadBlockMap (map);
P_LoadReject (map, buildmap);
maploader.LoadReject (map);
maploader.GroupLines (buildmap);
maploader.GroupLines (false);
maploader.SetSlopes();
P_FloodZones ();
maploader.FloodZones ();
P_SetRenderSector();
FixMinisegReferences();
FixHoles();
maploader.SetRenderSector();
maploader.FixMinisegReferences();
maploader.FixHoles();
// [RH] Spawn slope creating things first.
maploader.SpawnSlopeMakers (&MapThingsConverted[0], &MapThingsConverted[MapThingsConverted.Size()], maploader.oldvertextable);
maploader.SpawnSlopeMakers();
maploader.CopySlopes();
//////////////////////// <-------------------------------------
// set the head node for gameplay purposes. If the separate gamenodes array is not empty, use that, otherwise use the render nodes.
level.headgamenode = level.gamenodes.Size() > 0 ? &level.gamenodes[level.gamenodes.Size() - 1] : level.nodes.Size()? &level.nodes[level.nodes.Size() - 1] : nullptr;
// phares 8/10/98: Clear body queue so the corpses from previous games are
// not assumed to be from this one.
bodyqueslot = 0;
@ -799,7 +640,7 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
P_Spawn3DFloors();
times[14].Clock();
P_SpawnThings(position);
P_SpawnThings(maploader.MapThingsConverted, maploader.MapThingsUserData, maploader.MapThingsUserDataIndex, position);
for (i = 0; i < MAXPLAYERS; ++i)
{
@ -953,6 +794,7 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
P_ResetSightCounters (true);
//Printf ("free memory: 0x%x\n", Z_FreeMemory());
/*
if (showloadtimes)
{
Printf ("---Total load times---\n");
@ -982,10 +824,8 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
Printf ("Time%3d:%9.4f ms (%s)\n", i, times[i].TimeMS(), timenames[i]);
}
}
MapThingsConverted.Clear();
MapThingsUserDataIndex.Clear();
MapThingsUserData.Clear();
*/
// Create a backup of the map data so the savegame code can toss out all fields that haven't changed in order to reduce processing time and file size.
// Note that we want binary identity here, so assignment is not sufficient because it won't initialize any padding bytes.
// Note that none of these structures may contain non POD fields anyway.

View file

@ -56,16 +56,10 @@ int P_TranslateSectorSpecial (int);
int GetUDMFInt(int type, int index, FName key);
double GetUDMFFloat(int type, int index, FName key);
void P_SetRenderSector();
void FixMinisegReferences();
void FixHoles();
void ReportUnpairedMinisegs();
extern bool hasglnodes;
extern TMap<unsigned,unsigned> MapThingsUserDataIndex; // from mapthing idx -> user data idx
extern TArray<FUDMFKey> MapThingsUserData;
#endif

View file

@ -793,7 +793,7 @@ public:
FUDMFKey ukey;
ukey.Key = key;
ReadUserKey(ukey);
MapThingsUserData.Push(ukey);
maploader->MapThingsUserData.Push(ukey);
}
break;
}
@ -2132,17 +2132,17 @@ public:
if (sc.Compare("thing"))
{
FMapThing th;
unsigned userdatastart = MapThingsUserData.Size();
unsigned userdatastart = maploader->MapThingsUserData.Size();
ParseThing(&th);
MapThingsConverted.Push(th);
if (userdatastart < MapThingsUserData.Size())
if (userdatastart < maploader->MapThingsUserData.Size())
{ // User data added
MapThingsUserDataIndex[MapThingsConverted.Size()-1] = userdatastart;
maploader->MapThingsUserDataIndex[MapThingsConverted.Size()-1] = userdatastart;
// Mark end of the user data for this map thing
FUDMFKey ukey;
ukey.Key = NAME_None;
ukey = 0;
MapThingsUserData.Push(ukey);
maploader->MapThingsUserData.Push(ukey);
}
}
else if (sc.Compare("linedef"))

View file

@ -567,223 +567,9 @@ void InitRenderInfo()
}
delete[] checkmap;
#if 0
gl_CreateSections();
#endif
}
//==========================================================================
//
// FixMinisegReferences
//
// Sometimes it can happen that two matching minisegs do not have their partner set.
// Fix that here.
//
//==========================================================================
void FixMinisegReferences()
{
TArray<seg_t *> bogussegs;
for (unsigned i = 0; i < level.segs.Size(); i++)
{
if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr)
{
bogussegs.Push(&level.segs[i]);
}
}
for (unsigned i = 0; i < bogussegs.Size(); i++)
{
auto seg1 = bogussegs[i];
seg_t *pick = nullptr;
unsigned int picki = -1;
// Try to fix the reference: If there's exactly one other seg in the set which matches as a partner link those two segs together.
for (unsigned j = i + 1; j < bogussegs.Size(); j++)
{
auto seg2 = bogussegs[j];
if (seg1->v1 == seg2->v2 && seg2->v1 == seg1->v2 && seg1->Subsector->render_sector == seg2->Subsector->render_sector)
{
pick = seg2;
picki = j;
break;
}
}
if (pick)
{
DPrintf(DMSG_NOTIFY, "Linking miniseg pair from (%2.3f, %2.3f) -> (%2.3f, %2.3f) in sector %d\n", pick->v2->fX(), pick->v2->fY(), pick->v1->fX(), pick->v1->fY(), pick->frontsector->Index());
pick->PartnerSeg = seg1;
seg1->PartnerSeg = pick;
assert(seg1->v1 == pick->v2 && pick->v1 == seg1->v2);
bogussegs.Delete(picki);
bogussegs.Delete(i);
i--;
}
}
}
//==========================================================================
//
// FixHoles
//
// ZDBSP can leave holes in the node tree on extremely detailed maps.
// To help out the triangulator these are filled with dummy subsectors
// so that it can process the area correctly.
//
//==========================================================================
void FixHoles()
{
TArray<seg_t *> bogussegs;
TArray<TArray<seg_t *>> segloops;
for (unsigned i = 0; i < level.segs.Size(); i++)
{
if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr)
{
bogussegs.Push(&level.segs[i]);
}
}
while (bogussegs.Size() > 0)
{
segloops.Reserve(1);
auto *segloop = &segloops.Last();
seg_t *startseg;
seg_t *checkseg;
while (bogussegs.Size() > 0)
{
bool foundsome = false;
if (segloop->Size() == 0)
{
bogussegs.Pop(startseg);
segloop->Push(startseg);
checkseg = startseg;
}
for (unsigned i = 0; i < bogussegs.Size(); i++)
{
auto seg1 = bogussegs[i];
if (seg1->v1 == checkseg->v2 && seg1->Subsector->render_sector == checkseg->Subsector->render_sector)
{
foundsome = true;
segloop->Push(seg1);
bogussegs.Delete(i);
i--;
checkseg = seg1;
if (seg1->v2 == startseg->v1)
{
// The loop is complete. Start a new one
segloops.Reserve(1);
segloop = &segloops.Last();
}
}
}
if (!foundsome)
{
if ((*segloop)[0]->v1 != segloop->Last()->v2)
{
// There was no connected seg, leaving an unclosed loop.
// Clear this and continue looking.
segloop->Clear();
}
}
}
for (unsigned i = 0; i < segloops.Size(); i++)
{
if (segloops[i].Size() == 0)
{
segloops.Delete(i);
i--;
}
}
// Add dummy entries to the level's seg and subsector arrays
if (segloops.Size() > 0)
{
// cound the number of segs to add.
unsigned segcount = 0;
for (auto &segloop : segloops)
segcount += segloop.Size();
seg_t *oldsegstartptr = &level.segs[0];
subsector_t *oldssstartptr = &level.subsectors[0];
unsigned newsegstart = level.segs.Reserve(segcount);
unsigned newssstart = level.subsectors.Reserve(segloops.Size());
seg_t *newsegstartptr = &level.segs[0];
subsector_t *newssstartptr = &level.subsectors[0];
// Now fix all references to these in the level data.
// Note that the Index() method does not work here due to the reallocation.
for (auto &seg : level.segs)
{
if (seg.PartnerSeg) seg.PartnerSeg = newsegstartptr + (seg.PartnerSeg - oldsegstartptr);
seg.Subsector = newssstartptr + (seg.Subsector - oldssstartptr);
}
for (auto &sub : level.subsectors)
{
sub.firstline = newsegstartptr + (sub.firstline - oldsegstartptr);
}
for (auto &node : level.nodes)
{
// How hideous... :(
for (auto & p : node.children)
{
auto intp = (intptr_t)p;
if (intp & 1)
{
subsector_t *sp = (subsector_t*)(intp - 1);
sp = newssstartptr + (sp - oldssstartptr);
intp = intptr_t(sp) + 1;
p = (void*)intp;
}
}
}
for (auto &segloop : segloops)
{
for (auto &seg : segloop)
{
seg = newsegstartptr + (seg - oldsegstartptr);
}
}
// The seg lists in the sidedefs and the subsector lists in the sectors are not set yet when this gets called.
// Add the new data. This doesn't care about convexity. It is never directly used to generate a primitive.
for (auto &segloop : segloops)
{
DPrintf(DMSG_NOTIFY, "Adding dummy subsector for sector %d\n", segloop[0]->Subsector->render_sector->Index());
subsector_t &sub = level.subsectors[newssstart++];
memset(&sub, 0, sizeof(sub));
sub.sector = segloop[0]->frontsector;
sub.render_sector = segloop[0]->Subsector->render_sector;
sub.numlines = segloop.Size();
sub.firstline = &level.segs[newsegstart];
sub.flags = SSECF_HOLE;
for (auto otherseg : segloop)
{
DPrintf(DMSG_NOTIFY, " Adding seg from (%2.3f, %2.3f) -> (%2.3f, %2.3f)\n", otherseg->v2->fX(), otherseg->v2->fY(), otherseg->v1->fX(), otherseg->v1->fY());
seg_t &seg = level.segs[newsegstart++];
memset(&seg, 0, sizeof(seg));
seg.v1 = otherseg->v2;
seg.v2 = otherseg->v1;
seg.frontsector = seg.backsector = otherseg->backsector = otherseg->frontsector;
seg.PartnerSeg = otherseg;
otherseg->PartnerSeg = &seg;
seg.Subsector = &sub;
}
}
}
}
}
//==========================================================================
//
@ -837,202 +623,3 @@ CCMD(listbadminisegs)
ReportUnpairedMinisegs();
}
//==========================================================================
//
// Keep both the original nodes from the WAD and the GL nodes created here.
// The original set is only being used to get the sector for in-game
// positioning of actors but not for rendering.
//
// This is necessary because ZDBSP is much more sensitive
// to sloppy mapping practices that produce overlapping sectors.
// The crane in P:AR E1M3 is a good example that would be broken if
// this wasn't done.
//
//==========================================================================
//==========================================================================
//
// PointOnLine
//
// Same as the one im the node builder, but not part of a specific class
//
//==========================================================================
static bool PointOnLine (int x, int y, int x1, int y1, int dx, int dy)
{
const double SIDE_EPSILON = 6.5536;
// For most cases, a simple dot product is enough.
double d_dx = double(dx);
double d_dy = double(dy);
double d_x = double(x);
double d_y = double(y);
double d_x1 = double(x1);
double d_y1 = double(y1);
double s_num = (d_y1-d_y)*d_dx - (d_x1-d_x)*d_dy;
if (fabs(s_num) < 17179869184.0) // 4<<32
{
// Either the point is very near the line, or the segment defining
// the line is very short: Do a more expensive test to determine
// just how far from the line the point is.
double l = g_sqrt(d_dx*d_dx+d_dy*d_dy);
double dist = fabs(s_num)/l;
if (dist < SIDE_EPSILON)
{
return true;
}
}
return false;
}
//==========================================================================
//
// SetRenderSector
//
// Sets the render sector for each GL subsector so that the proper flat
// information can be retrieved
//
//==========================================================================
void P_SetRenderSector()
{
int i;
uint32_t j;
TArray<subsector_t *> undetermined;
subsector_t * ss;
#if 0 // doesn't work as expected :(
// hide all sectors on textured automap that only have hidden lines.
bool *hidesec = new bool[numsectors];
for(i = 0; i < numsectors; i++)
{
hidesec[i] = true;
}
for(i = 0; i < numlines; i++)
{
if (!(lines[i].flags & ML_DONTDRAW))
{
hidesec[lines[i].frontsector - sectors] = false;
if (lines[i].backsector != NULL)
{
hidesec[lines[i].backsector - sectors] = false;
}
}
}
for(i = 0; i < numsectors; i++)
{
if (hidesec[i]) sectors[i].MoreFlags |= SECMF_HIDDEN;
}
delete [] hidesec;
#endif
// Check for incorrect partner seg info so that the following code does not crash.
for (auto &seg : level.segs)
{
auto p = seg.PartnerSeg;
if (p != nullptr)
{
int partner = p->Index();
if (partner < 0 || partner >= (int)level.segs.Size() || &level.segs[partner] != p)
{
seg.PartnerSeg = nullptr;
}
// glbsp creates such incorrect references for Strife.
if (seg.linedef && seg.PartnerSeg != nullptr && !seg.PartnerSeg->linedef)
{
seg.PartnerSeg = seg.PartnerSeg->PartnerSeg = nullptr;
}
}
}
for (auto &seg : level.segs)
{
if (seg.PartnerSeg != nullptr && seg.PartnerSeg->PartnerSeg != &seg)
{
seg.PartnerSeg = nullptr;
}
}
// look up sector number for each subsector
for (auto &ss : level.subsectors)
{
// For rendering pick the sector from the first seg that is a sector boundary
// this takes care of self-referencing sectors
seg_t *seg = ss.firstline;
// Check for one-dimensional subsectors. These should be ignored when
// being processed for automap drawing etc.
ss.flags |= SSECF_DEGENERATE;
for(j=2; j<ss.numlines; j++)
{
if (!PointOnLine(seg[j].v1->fixX(), seg[j].v1->fixY(), seg->v1->fixX(), seg->v1->fixY(), seg->v2->fixX() -seg->v1->fixX(), seg->v2->fixY() -seg->v1->fixY()))
{
// Not on the same line
ss.flags &= ~SSECF_DEGENERATE;
break;
}
}
seg = ss.firstline;
for(j=0; j<ss.numlines; j++)
{
if(seg->sidedef && (seg->PartnerSeg == nullptr || (seg->PartnerSeg->sidedef != nullptr && seg->sidedef->sector!=seg->PartnerSeg->sidedef->sector)))
{
ss.render_sector = seg->sidedef->sector;
break;
}
seg++;
}
if(ss.render_sector == NULL)
{
undetermined.Push(&ss);
}
}
// assign a vaild render sector to all subsectors which haven't been processed yet.
while (undetermined.Size())
{
bool deleted=false;
for(i=undetermined.Size()-1;i>=0;i--)
{
ss=undetermined[i];
seg_t * seg = ss->firstline;
for(j=0; j<ss->numlines; j++)
{
if (seg->PartnerSeg != nullptr && seg->PartnerSeg->Subsector)
{
sector_t * backsec = seg->PartnerSeg->Subsector->render_sector;
if (backsec)
{
ss->render_sector = backsec;
undetermined.Delete(i);
deleted = 1;
break;
}
}
seg++;
}
}
// We still got some left but the loop above was unable to assign them.
// This only happens when a subsector is off the map.
// Don't bother and just assign the real sector for rendering
if (!deleted && undetermined.Size())
{
for(i=undetermined.Size()-1;i>=0;i--)
{
ss=undetermined[i];
ss->render_sector=ss->sector;
}
break;
}
}
}