gzdoom-gles/src/maploader/polyobjects.cpp
Christoph Oelckers 8b18ed4759 - store the index of each map item in the struct itself and return that for the Index function.
If we ever want to refactor the global level data these must not reference the 'level' variable.
The main parts of the map loader cannot use this information, because it can only be created after running the node builder, so it got its own set of index functions instead.
2019-01-06 00:41:46 +01:00

415 lines
11 KiB
C++

//-----------------------------------------------------------------------------
//
// 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/
//
//-----------------------------------------------------------------------------
//
// HEADER FILES ------------------------------------------------------------
#include "doomdef.h"
#include "p_local.h"
#include "m_bbox.h"
#include "a_sharedglobal.h"
#include "p_3dmidtex.h"
#include "p_lnspec.h"
#include "po_man.h"
#include "p_setup.h"
#include "p_blockmap.h"
#include "p_maputl.h"
#include "r_utility.h"
#include "g_levellocals.h"
#include "actorinlines.h"
#include "v_text.h"
#include "maploader/maploader.h"
//==========================================================================
//
// InitBlockMap
//
//==========================================================================
void MapLoader::InitPolyBlockMap ()
{
int bmapwidth = Level->blockmap.bmapwidth;
int bmapheight = Level->blockmap.bmapheight;
Level->PolyBlockMap.Resize(bmapwidth*bmapheight);
memset (Level->PolyBlockMap.Data(), 0, bmapwidth*bmapheight*sizeof(polyblock_t *));
for(auto &poly : Level->Polyobjects)
{
poly.LinkPolyobj();
}
}
//==========================================================================
//
// InitSideLists [RH]
//
// Group sides by vertex and collect side that are known to belong to a
// polyobject so that they can be initialized fast.
//==========================================================================
void MapLoader::InitSideLists ()
{
auto &sides = Level->sides;
for (unsigned i = 0; i < sides.Size(); ++i)
{
if (sides[i].linedef != NULL &&
(sides[i].linedef->special == Polyobj_StartLine ||
sides[i].linedef->special == Polyobj_ExplicitLine))
{
KnownPolySides.Push (i);
}
}
}
//==========================================================================
//
// AddPolyVert
//
// Helper function for IterFindPolySides()
//
//==========================================================================
static void AddPolyVert(TArray<uint32_t> &vnum, uint32_t vert)
{
for (unsigned int i = vnum.Size() - 1; i-- != 0; )
{
if (vnum[i] == vert)
{ // Already in the set. No need to add it.
return;
}
}
vnum.Push(vert);
}
//==========================================================================
//
// IterFindPolySides
//
// Beginning with the first vertex of the starting side, for each vertex
// in vnum, add all the sides that use it as a first vertex to the polyobj,
// and add all their second vertices to vnum. This continues until there
// are no new vertices in vnum.
//
//==========================================================================
void MapLoader::IterFindPolySides (FPolyObj *po, side_t *side)
{
static TArray<uint32_t> vnum;
unsigned int vnumat;
assert(sidetemp.Size() > 0);
vnum.Clear();
vnum.Push(uint32_t(Index(side->V1())));
vnumat = 0;
while (vnum.Size() != vnumat)
{
uint32_t sidenum = sidetemp[vnum[vnumat++]].b.first;
while (sidenum != NO_SIDE)
{
po->Sidedefs.Push(&Level->sides[sidenum]);
AddPolyVert(vnum, uint32_t(Index(Level->sides[sidenum].V2())));
sidenum = sidetemp[sidenum].b.next;
}
}
}
//==========================================================================
//
// SpawnPolyobj
//
//==========================================================================
static int posicmp(const void *a, const void *b)
{
return (*(const side_t **)a)->linedef->args[1] - (*(const side_t **)b)->linedef->args[1];
}
void MapLoader::SpawnPolyobj (int index, int tag, int type)
{
unsigned int ii;
int i;
FPolyObj *po = &Level->Polyobjects[index];
for (ii = 0; ii < KnownPolySides.Size(); ++ii)
{
i = KnownPolySides[ii];
if (i < 0)
{
continue;
}
po->bBlocked = false;
po->bHasPortals = 0;
side_t *sd = &Level->sides[i];
if (sd->linedef->special == Polyobj_StartLine &&
sd->linedef->args[0] == tag)
{
if (po->Sidedefs.Size() > 0)
{
Printf (TEXTCOLOR_RED "SpawnPolyobj: Polyobj %d already spawned.\n", tag);
return;
}
else
{
sd->linedef->special = 0;
sd->linedef->args[0] = 0;
IterFindPolySides(&Level->Polyobjects[index], sd);
po->MirrorNum = sd->linedef->args[1];
po->crush = (type != SMT_PolySpawn) ? 3 : 0;
po->bHurtOnTouch = (type == SMT_PolySpawnHurt);
po->tag = tag;
po->seqType = sd->linedef->args[2];
if (po->seqType < 0 || po->seqType > 63)
{
po->seqType = 0;
}
}
break;
}
}
if (po->Sidedefs.Size() == 0)
{
// didn't find a polyobj through PO_LINE_START
TArray<side_t *> polySideList;
unsigned int psIndexOld;
psIndexOld = po->Sidedefs.Size();
for (ii = 0; ii < KnownPolySides.Size(); ++ii)
{
i = KnownPolySides[ii];
if (i >= 0 &&
Level->sides[i].linedef->special == Polyobj_ExplicitLine &&
Level->sides[i].linedef->args[0] == tag)
{
if (!Level->sides[i].linedef->args[1])
{
Printf(TEXTCOLOR_RED "SpawnPolyobj: Explicit line missing order number in poly %d, linedef %d.\n", tag, Index(Level->sides[i].linedef));
return;
}
else
{
po->Sidedefs.Push(&Level->sides[i]);
}
}
}
qsort(&po->Sidedefs[0], po->Sidedefs.Size(), sizeof(po->Sidedefs[0]), posicmp);
if (po->Sidedefs.Size() > 0)
{
po->crush = (type != SMT_PolySpawn) ? 3 : 0;
po->bHurtOnTouch = (type == SMT_PolySpawnHurt);
po->tag = tag;
po->seqType = po->Sidedefs[0]->linedef->args[3];
po->MirrorNum = po->Sidedefs[0]->linedef->args[2];
}
else
{
Printf(TEXTCOLOR_RED "SpawnPolyobj: Poly %d does not exist\n", tag);
return;
}
}
validcount++;
for(unsigned int i=0; i<po->Sidedefs.Size(); i++)
{
line_t *l = po->Sidedefs[i]->linedef;
if (l->validcount != validcount)
{
FLinePortal *port = l->getPortal();
if (port && (port->mDefFlags & PORTF_PASSABLE))
{
int type = port->mType == PORTT_LINKED ? 2 : 1;
if (po->bHasPortals < type) po->bHasPortals = (uint8_t)type;
}
l->validcount = validcount;
po->Linedefs.Push(l);
vertex_t *v = l->v1;
int j;
for(j = po->Vertices.Size() - 1; j >= 0; j--)
{
if (po->Vertices[j] == v) break;
}
if (j < 0) po->Vertices.Push(v);
v = l->v2;
for(j = po->Vertices.Size() - 1; j >= 0; j--)
{
if (po->Vertices[j] == v) break;
}
if (j < 0) po->Vertices.Push(v);
}
}
po->Sidedefs.ShrinkToFit();
po->Linedefs.ShrinkToFit();
po->Vertices.ShrinkToFit();
}
//==========================================================================
//
// TranslateToStartSpot
//
//==========================================================================
void MapLoader::TranslateToStartSpot (int tag, const DVector2 &origin)
{
FPolyObj *po;
DVector2 delta;
po = PO_GetPolyobj(tag);
if (po == nullptr)
{ // didn't match the tag with a polyobj tag
Printf(TEXTCOLOR_RED "TranslateToStartSpot: Unable to match polyobj tag: %d\n", tag);
return;
}
if (po->Sidedefs.Size() == 0)
{
Printf(TEXTCOLOR_RED "TranslateToStartSpot: Anchor point located without a StartSpot point: %d\n", tag);
return;
}
po->OriginalPts.Resize(po->Sidedefs.Size());
po->PrevPts.Resize(po->Sidedefs.Size());
delta = origin - po->StartSpot.pos;
for (unsigned i = 0; i < po->Sidedefs.Size(); i++)
{
po->Sidedefs[i]->Flags |= WALLF_POLYOBJ;
}
for (unsigned i = 0; i < po->Linedefs.Size(); i++)
{
po->Linedefs[i]->bbox[BOXTOP] -= delta.Y;
po->Linedefs[i]->bbox[BOXBOTTOM] -= delta.Y;
po->Linedefs[i]->bbox[BOXLEFT] -= delta.X;
po->Linedefs[i]->bbox[BOXRIGHT] -= delta.X;
}
for (unsigned i = 0; i < po->Vertices.Size(); i++)
{
po->Vertices[i]->set(po->Vertices[i]->fX() - delta.X, po->Vertices[i]->fY() - delta.Y);
po->OriginalPts[i].pos = po->Vertices[i]->fPos() - po->StartSpot.pos;
}
po->CalcCenter();
// For compatibility purposes
po->CenterSubsector = R_PointInSubsector(po->CenterSpot.pos);
}
//==========================================================================
//
// PO_Init
//
//==========================================================================
void MapLoader::PO_Init (void)
{
int NumPolyobjs = 0;
TArray<FMapThing *> polythings;
for (auto &mthing : MapThingsConverted)
{
if (mthing.EdNum == 0 || mthing.EdNum == -1 || mthing.info == nullptr) continue;
FDoomEdEntry *mentry = mthing.info;
switch (mentry->Special)
{
case SMT_PolyAnchor:
case SMT_PolySpawn:
case SMT_PolySpawnCrush:
case SMT_PolySpawnHurt:
polythings.Push(&mthing);
if (mentry->Special != SMT_PolyAnchor)
NumPolyobjs++;
}
}
int polyIndex;
// [RH] Make this faster
InitSideLists ();
Level->Polyobjects.Resize(NumPolyobjs);
polyIndex = 0; // index polyobj number
// Find the startSpot points, and spawn each polyobj
for (int i=polythings.Size()-1; i >= 0; i--)
{
// 9301 (3001) = no crush, 9302 (3002) = crushing, 9303 = hurting touch
int type = polythings[i]->info->Special;
if (type >= SMT_PolySpawn && type <= SMT_PolySpawnHurt)
{
// Polyobj StartSpot Pt.
Level->Polyobjects[polyIndex].StartSpot.pos = polythings[i]->pos;
SpawnPolyobj(polyIndex, polythings[i]->angle, type);
polyIndex++;
}
}
for (int i = polythings.Size() - 1; i >= 0; i--)
{
int type = polythings[i]->info->Special;
if (type == SMT_PolyAnchor)
{
// Polyobj Anchor Pt.
TranslateToStartSpot (polythings[i]->angle, polythings[i]->pos);
}
}
// check for a startspot without an anchor point
for (auto &poly : Level->Polyobjects)
{
if (poly.OriginalPts.Size() == 0)
{
Printf (TEXTCOLOR_RED "PO_Init: StartSpot located without an Anchor point: %d\n", poly.tag);
}
}
InitPolyBlockMap();
// [RH] Don't need the side lists anymore
KnownPolySides.Reset();
// mark all subsectors which have a seg belonging to a polyobj
// These ones should not be rendered on the textured automap.
for (auto &ss : Level->subsectors)
{
for(uint32_t j=0;j<ss.numlines; j++)
{
if (ss.firstline[j].sidedef != NULL &&
ss.firstline[j].sidedef->Flags & WALLF_POLYOBJ)
{
ss.flags |= SSECF_POLYORG;
break;
}
}
}
// clear all polyobj specials so that they do not obstruct using other lines.
for (auto &line : Level->lines)
{
if (line.special == Polyobj_ExplicitLine || line.special == Polyobj_StartLine)
{
line.special = 0;
}
}
}