2014-03-15 16:59:03 +00:00
|
|
|
// Emacs style mode select -*- C++ -*-
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Copyright (C) 1998-2001 by DooM Legacy Team.
|
|
|
|
//
|
|
|
|
// 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 2
|
|
|
|
// of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/// \file
|
|
|
|
/// \brief special trick routines to make some SW tricks look OK with
|
|
|
|
/// HW rendering. This includes:
|
|
|
|
/// - deepwatereffect (e.g. tnt/map02)
|
|
|
|
/// - invisible staircase (e.g. eternal/map02)
|
|
|
|
/// - floating ceilings (e.g. eternal/map03)
|
|
|
|
///
|
|
|
|
/// It is not guaranteed that it looks identical to the SW mode,
|
|
|
|
/// but it looks in most of the cases far better than having
|
|
|
|
/// holes in the architecture, HOM, etc.
|
|
|
|
///
|
|
|
|
/// It fixes as well missing textures, which are replaced by either
|
|
|
|
/// a default texture or the midtexture.
|
|
|
|
///
|
|
|
|
/// words of notice:
|
|
|
|
/// pseudosectors, as mentioned in this file, are sectors where both
|
|
|
|
/// sidedefs point to the same sector. This expression is also used
|
|
|
|
/// for sectors which are enclosed by another sector but have no
|
|
|
|
/// correct sidedefs at all
|
|
|
|
///
|
|
|
|
/// if a vertex is inside a poly is determined by the angles between
|
|
|
|
/// this vertex and all angles on the linedefs (imagine walking along
|
|
|
|
/// a circle always facing a certain point inside/outside the circle;
|
|
|
|
/// if inside, angle have taken all values [0..pi), otherwise the
|
|
|
|
/// range was < pi/2
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
#include "../doomdef.h"
|
|
|
|
#include "../doomstat.h"
|
|
|
|
|
|
|
|
#ifdef HWRENDER
|
|
|
|
#include "hw_glob.h"
|
|
|
|
#include "hw_dll.h"
|
|
|
|
#include "../r_local.h"
|
|
|
|
#include "../i_system.h"
|
|
|
|
|
|
|
|
//
|
|
|
|
// add a line to a sectors list of lines
|
|
|
|
//
|
|
|
|
static void addLineToChain(sector_t *sector, line_t *line)
|
|
|
|
{
|
|
|
|
linechain_t *thisElem = NULL, *nextElem;
|
|
|
|
|
|
|
|
if (!sector)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nextElem = sector->sectorLines;
|
|
|
|
|
|
|
|
while (nextElem) // walk through chain
|
|
|
|
{
|
|
|
|
thisElem = nextElem;
|
|
|
|
nextElem = thisElem->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add a new element into the chain
|
|
|
|
if (thisElem)
|
|
|
|
{
|
|
|
|
thisElem->next = malloc(sizeof (linechain_t));
|
|
|
|
if (thisElem->next)
|
|
|
|
{
|
|
|
|
thisElem->next->line = line;
|
|
|
|
thisElem->next->next = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
I_Error("Out of memory in addLineToChain(.)\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else // first element in chain
|
|
|
|
{
|
|
|
|
sector->sectorLines = malloc(sizeof (linechain_t));
|
|
|
|
if (sector->sectorLines)
|
|
|
|
{
|
|
|
|
sector->sectorLines->line = line;
|
|
|
|
sector->sectorLines->next = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
I_Error("Out of memory in addLineToChain(.)\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// We dont want a memory hole, do we?;-)
|
|
|
|
//
|
|
|
|
static void releaseLineChains(void)
|
|
|
|
{
|
|
|
|
linechain_t *thisElem, *nextElem;
|
|
|
|
sector_t *sector;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < numsectors; i++)
|
|
|
|
{
|
|
|
|
sector = §ors[i];
|
|
|
|
nextElem = sector->sectorLines;
|
|
|
|
|
|
|
|
while (nextElem)
|
|
|
|
{
|
|
|
|
thisElem = nextElem;
|
|
|
|
nextElem = thisElem->next;
|
|
|
|
free(thisElem);
|
|
|
|
}
|
|
|
|
|
|
|
|
sector->sectorLines = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// check if a pseudo sector is valid by checking all its linedefs
|
|
|
|
//
|
|
|
|
static boolean isPSectorValid(sector_t *sector)
|
|
|
|
{
|
|
|
|
linechain_t *thisElem, *nextElem;
|
|
|
|
|
|
|
|
if (!sector->pseudoSector) // check only pseudosectors, others dont care
|
|
|
|
{
|
|
|
|
#ifdef PARANOIA
|
|
|
|
CONS_Debug(DBG_RENDER, "Alert! non-pseudosector fed to isPSectorClosed()\n");
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
nextElem = sector->sectorLines;
|
|
|
|
|
|
|
|
while (nextElem)
|
|
|
|
{
|
|
|
|
thisElem = nextElem;
|
|
|
|
nextElem = thisElem->next;
|
|
|
|
if (thisElem->line->frontsector != thisElem->line->backsector)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// angles are always phiMax-phiMin [0...2\pi)
|
|
|
|
//
|
|
|
|
FUNCMATH static double phiDiff(double phiMin, double phiMax)
|
|
|
|
{
|
|
|
|
double result;
|
|
|
|
|
|
|
|
result = phiMax-phiMin;
|
|
|
|
|
|
|
|
if (result < 0.0l)
|
|
|
|
result += 2.0l*M_PIl;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// sort phi's so that enclosed angle < \pi
|
|
|
|
//
|
|
|
|
static void sortPhi(double phi1, double phi2, double *phiMin, double *phiMax)
|
|
|
|
{
|
|
|
|
if (phiDiff(phi1, phi2) < M_PIl)
|
|
|
|
{
|
|
|
|
*phiMin = phi1;
|
|
|
|
*phiMax = phi2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*phiMin = phi2;
|
|
|
|
*phiMax = phi1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// return if angle(phi1, phi2) is bigger than \pi
|
|
|
|
// if so, the vertex lies inside the poly
|
|
|
|
//
|
|
|
|
FUNCMATH static boolean biggerThanPi(double phi1, double phi2)
|
|
|
|
{
|
|
|
|
if (phiDiff(phi1, phi2) > M_PIl)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DELTAPHI (M_PIl/100.0l) // some small phi << \pi
|
|
|
|
|
|
|
|
//
|
|
|
|
// calculate bounds for minimum angle
|
|
|
|
//
|
|
|
|
static void phiBounds(double phi1, double phi2, double *phiMin, double *phiMax)
|
|
|
|
{
|
|
|
|
double phi1Tmp, phi2Tmp;
|
|
|
|
double psi1, psi2, psi3, psi4, psi5, psi6, psi7; // for optimization
|
|
|
|
|
|
|
|
sortPhi(phi1, phi2, &phi1Tmp, &phi2Tmp);
|
|
|
|
phi1 = phi1Tmp;
|
|
|
|
phi2 = phi2Tmp;
|
|
|
|
|
|
|
|
// check start condition
|
|
|
|
if (*phiMin > M_PIl || *phiMax > M_PIl)
|
|
|
|
{
|
|
|
|
*phiMin = phi1;
|
|
|
|
*phiMax = phi2;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 6 cases:
|
|
|
|
// new angles inbetween phiMin, phiMax -> forget it
|
|
|
|
// new angles enclose phiMin -> set phiMin
|
|
|
|
// new angles enclose phiMax -> set phiMax
|
|
|
|
// new angles completely outside phiMin, phiMax -> leave largest area free
|
|
|
|
// new angles close the range completely!
|
|
|
|
// new angles enlarges range on both sides
|
|
|
|
|
|
|
|
psi1 = phiDiff(*phiMin, phi1);
|
|
|
|
psi2 = phiDiff(*phiMin, phi2);
|
|
|
|
psi3 = phiDiff(*phiMax, phi1);
|
|
|
|
psi4 = phiDiff(*phiMax, phi2);
|
|
|
|
psi5 = phiDiff(*phiMin, *phiMax);
|
|
|
|
psi6 = (double)(2.0l*M_PIl - psi5); // phiDiff(*phiMax, *phiMin);
|
|
|
|
psi7 = (double)(2.0l*M_PIl - psi2); // phiDiff(phi2, *phiMin);
|
|
|
|
|
|
|
|
// case 1 & 5!
|
|
|
|
if ((psi1 <= psi5) && (psi2 <= psi5))
|
|
|
|
{
|
|
|
|
if (psi1 <= psi2) // case 1
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else // case 5
|
|
|
|
{
|
|
|
|
// create some artificial interval here not to get into numerical trouble
|
|
|
|
// in fact we know now the sector is completely enclosed -> base for computational optimization
|
|
|
|
*phiMax = 0.0l;
|
|
|
|
*phiMin = DELTAPHI;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// case 2
|
|
|
|
if ((psi1 >= psi5) && (psi2 <= psi5))
|
|
|
|
{
|
|
|
|
*phiMin = phi1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// case 3
|
|
|
|
if ((psi3 >= psi6) && (psi4 <= psi6))
|
|
|
|
{
|
|
|
|
*phiMax = phi2;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// case 4 & 6
|
|
|
|
#ifdef PARANOIA
|
|
|
|
if ((psi3 <= psi6) && (psi4 <= psi6)) // FIXME: isn't this case implicitly true anyway??
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
if (psi3 <= psi4) //case 4
|
|
|
|
{
|
|
|
|
if (psi3 >= psi7)
|
|
|
|
{
|
|
|
|
*phiMin = phi1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*phiMax = phi2;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else // case 6
|
|
|
|
{
|
|
|
|
*phiMin = phi1;
|
|
|
|
*phiMax = phi2;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef PARANOIA
|
|
|
|
CONS_Debug(DBG_RENDER, "phiMin = %f, phiMax = %f, phi1 = %f, phi2 = %f\n", *phiMin, *phiMax, phi1, phi2);
|
|
|
|
I_Error("phiBounds() out of range!\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Check if a vertex lies inside a sector
|
|
|
|
// This works for "well-behaved" convex polygons
|
|
|
|
// If we need it mathematically correct, we need to sort the
|
|
|
|
// linedefs first so we have them in a row, then walk along the linedefs,
|
|
|
|
// but this is a bit overdone
|
|
|
|
//
|
|
|
|
static inline boolean isVertexInside(vertex_t *vertex, sector_t *sector)
|
|
|
|
{
|
|
|
|
double xa, ya, xe, ye;
|
|
|
|
linechain_t *chain;
|
|
|
|
double phiMin, phiMax;
|
|
|
|
double phi1, phi2;
|
|
|
|
|
|
|
|
chain = sector->sectorLines;
|
|
|
|
phiMin = phiMax = 10.0l*M_PIl; // some value > \pi
|
|
|
|
|
|
|
|
while (chain)
|
|
|
|
{
|
|
|
|
// start and end vertex
|
|
|
|
xa = (double)chain->line->v1->x - (double)vertex->x;
|
|
|
|
ya = (double)chain->line->v1->y - (double)vertex->y;
|
|
|
|
xe = (double)chain->line->v2->x - (double)vertex->x;
|
|
|
|
ye = (double)chain->line->v2->y - (double)vertex->y;
|
|
|
|
|
|
|
|
// angle phi of connection between the vertices and the x-axis
|
|
|
|
phi1 = atan2(ya, xa);
|
|
|
|
phi2 = atan2(ye, xe);
|
|
|
|
|
|
|
|
// if we have just started, we can have to create start bounds for phi
|
|
|
|
|
|
|
|
phiBounds(phi1, phi2, &phiMin, &phiMax);
|
|
|
|
chain = chain->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return biggerThanPi(phiMin, phiMax);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define MAXSTACK 256 // Not more than 256 polys in each other?
|
|
|
|
//
|
|
|
|
// generate a list of sectors which enclose the given sector
|
|
|
|
//
|
|
|
|
static void generateStacklist(sector_t *thisSector)
|
|
|
|
{
|
|
|
|
size_t i, stackCnt = 0;
|
|
|
|
sector_t *locStacklist[MAXSTACK];
|
|
|
|
sector_t *checkSector;
|
|
|
|
|
|
|
|
for (i = 0; i < numsectors; i++)
|
|
|
|
{
|
|
|
|
checkSector = §ors[i];
|
|
|
|
|
|
|
|
if (checkSector == thisSector) // dont check self
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// buggy sector?
|
|
|
|
if (!thisSector->sectorLines)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// check if an arbitrary vertex of thisSector lies inside the checkSector
|
|
|
|
if (isVertexInside(thisSector->sectorLines->line->v1, checkSector))
|
|
|
|
{
|
|
|
|
// if so, the thisSector lies inside the checkSector
|
|
|
|
locStacklist[stackCnt] = checkSector;
|
|
|
|
stackCnt++;
|
|
|
|
|
|
|
|
if (MAXSTACK-1 == stackCnt) // beware of the SIGSEGV! and consider terminating NULL!
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
thisSector->stackList = malloc(sizeof (sector_t *) * (stackCnt+1));
|
|
|
|
if (NULL == thisSector->stackList)
|
|
|
|
{
|
|
|
|
I_Error("Out of memory error in generateStacklist()");
|
|
|
|
}
|
|
|
|
|
|
|
|
locStacklist[stackCnt] = NULL; // terminating NULL
|
|
|
|
|
|
|
|
memcpy(thisSector->stackList, locStacklist, sizeof (sector_t *) * (stackCnt+1));
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Bubble sort the stacklist with rising lineoutlengths
|
|
|
|
//
|
|
|
|
static void sortStacklist(sector_t *sector)
|
|
|
|
{
|
|
|
|
sector_t **list;
|
|
|
|
sector_t *sec1, *sec2;
|
|
|
|
boolean finished;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
list = sector->stackList;
|
|
|
|
finished = false;
|
|
|
|
|
|
|
|
if (!*list)
|
|
|
|
return; // nothing to sort
|
|
|
|
|
|
|
|
while (!finished)
|
|
|
|
{
|
|
|
|
i = 0;
|
|
|
|
finished = true;
|
|
|
|
|
|
|
|
while (NULL != *(list+i+1))
|
|
|
|
{
|
|
|
|
sec1 = *(list+i);
|
|
|
|
sec2 = *(list+i+1);
|
|
|
|
|
|
|
|
if (sec1->lineoutLength > sec2->lineoutLength)
|
|
|
|
{
|
|
|
|
*(list+i) = sec2;
|
|
|
|
*(list+i+1) = sec1;
|
|
|
|
finished = false;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// length of a line in euclidian sense
|
|
|
|
//
|
|
|
|
static double lineLength(line_t *line)
|
|
|
|
{
|
|
|
|
double dx, dy, length;
|
|
|
|
|
|
|
|
dx = (double) line->v1->x - (double) line->v2->x;
|
|
|
|
dy = (double) line->v1->y - (double) line->v2->y;
|
|
|
|
|
|
|
|
length = hypot(dx, dy);
|
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// length of the sector lineout
|
|
|
|
//
|
|
|
|
static double calcLineoutLength(sector_t *sector)
|
|
|
|
{
|
|
|
|
linechain_t *chain;
|
|
|
|
double length = 0.0L;
|
|
|
|
chain = sector->sectorLines;
|
|
|
|
|
|
|
|
while (NULL != chain) // sum up lengths of all lines
|
|
|
|
{
|
|
|
|
length += lineLength(chain->line);
|
|
|
|
chain = chain->next;
|
|
|
|
}
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Calculate length of the sectors lineout
|
|
|
|
//
|
|
|
|
static void calcLineouts(sector_t *sector)
|
|
|
|
{
|
|
|
|
size_t secCount = 0;
|
|
|
|
sector_t *encSector = *(sector->stackList);
|
|
|
|
|
|
|
|
while (NULL != encSector)
|
|
|
|
{
|
|
|
|
if (encSector->lineoutLength < 0.0L) // if length has not yet been calculated
|
|
|
|
{
|
|
|
|
encSector->lineoutLength = calcLineoutLength(encSector);
|
|
|
|
}
|
|
|
|
|
|
|
|
secCount++;
|
|
|
|
encSector = *((sector->stackList) + secCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Free Stacklists of all sectors
|
|
|
|
//
|
|
|
|
static void freeStacklists(void)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < numsectors; i++)
|
|
|
|
{
|
|
|
|
if (sectors[i].stackList)
|
|
|
|
{
|
|
|
|
free(sectors[i].stackList);
|
|
|
|
sectors[i].stackList = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// if more than half of the toptextures are missing
|
|
|
|
//
|
|
|
|
static boolean areToptexturesMissing(sector_t *thisSector)
|
|
|
|
{
|
|
|
|
linechain_t *thisElem, *nextElem = thisSector->sectorLines;
|
|
|
|
sector_t *frontSector, *backSector;
|
|
|
|
size_t nomiss = 0;
|
|
|
|
side_t *sidel, *sider;
|
|
|
|
|
|
|
|
while (nextElem) // walk through chain
|
|
|
|
{
|
|
|
|
thisElem = nextElem;
|
|
|
|
nextElem = thisElem->next;
|
|
|
|
|
|
|
|
frontSector = thisElem->line->frontsector;
|
|
|
|
backSector = thisElem->line->backsector;
|
|
|
|
|
|
|
|
if (frontSector == backSector) // skip damn renderer tricks here
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!frontSector || !backSector)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sider = &sides[thisElem->line->sidenum[0]];
|
|
|
|
sidel = &sides[thisElem->line->sidenum[1]];
|
|
|
|
|
|
|
|
if (backSector->ceilingheight < frontSector->ceilingheight)
|
|
|
|
{
|
|
|
|
if (sider->toptexture != 0)
|
|
|
|
{
|
|
|
|
nomiss++;
|
|
|
|
break; // we can stop here if decision criterium is ==0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (backSector->ceilingheight > frontSector->ceilingheight)
|
|
|
|
{
|
|
|
|
if (sidel->toptexture != 0)
|
|
|
|
{
|
|
|
|
nomiss++;
|
|
|
|
break; // we can stop here if decision criterium is ==0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nomiss == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// are more textures missing than present?
|
|
|
|
//
|
|
|
|
static boolean areBottomtexturesMissing(sector_t *thisSector)
|
|
|
|
{
|
|
|
|
linechain_t *thisElem, *nextElem = thisSector->sectorLines;
|
|
|
|
sector_t *frontSector, *backSector;
|
|
|
|
size_t nomiss = 0;
|
|
|
|
side_t *sidel, *sider;
|
|
|
|
|
|
|
|
while (nextElem) // walk through chain
|
|
|
|
{
|
|
|
|
thisElem = nextElem;
|
|
|
|
nextElem = thisElem->next;
|
|
|
|
|
|
|
|
frontSector = thisElem->line->frontsector;
|
|
|
|
backSector = thisElem->line->backsector;
|
|
|
|
|
|
|
|
if (frontSector == backSector) // skip damn renderer tricks here
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (frontSector == NULL || backSector == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sider = &sides[thisElem->line->sidenum[0]];
|
|
|
|
sidel = &sides[thisElem->line->sidenum[1]];
|
|
|
|
|
|
|
|
if (backSector->floorheight > frontSector->floorheight)
|
|
|
|
{
|
|
|
|
if (sider->bottomtexture != 0)
|
|
|
|
{
|
|
|
|
nomiss++;
|
|
|
|
break; // we can stop here if decision criterium is ==0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (backSector->floorheight < frontSector->floorheight)
|
|
|
|
{
|
|
|
|
if (sidel->bottomtexture != 0)
|
|
|
|
{
|
|
|
|
nomiss++;
|
|
|
|
break; // we can stop here if decision criterium is ==0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// return missing >= nomiss;
|
|
|
|
return nomiss == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// check if no adjacent sector has same ceiling height
|
|
|
|
//
|
|
|
|
static boolean isCeilingFloating(sector_t *thisSector)
|
|
|
|
{
|
|
|
|
sector_t *adjSector, *refSector = NULL, *frontSector, *backSector;
|
|
|
|
boolean floating = true;
|
|
|
|
linechain_t *thisElem, *nextElem;
|
|
|
|
|
|
|
|
if (!thisSector)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
nextElem = thisSector->sectorLines;
|
|
|
|
|
|
|
|
while (NULL != nextElem) // walk through chain
|
|
|
|
{
|
|
|
|
thisElem = nextElem;
|
|
|
|
nextElem = thisElem->next;
|
|
|
|
|
|
|
|
frontSector = thisElem->line->frontsector;
|
|
|
|
backSector = thisElem->line->backsector;
|
|
|
|
|
|
|
|
if (frontSector == thisSector)
|
|
|
|
adjSector = backSector;
|
|
|
|
else
|
|
|
|
adjSector = frontSector;
|
|
|
|
|
|
|
|
if (!adjSector) // assume floating sectors have surrounding sectors
|
|
|
|
{
|
|
|
|
floating = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!refSector)
|
|
|
|
{
|
|
|
|
refSector = adjSector;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if adjacent sector has same height or more than one adjacent sector exists -> stop
|
|
|
|
if (thisSector->ceilingheight == adjSector->ceilingheight ||
|
|
|
|
refSector != adjSector)
|
|
|
|
{
|
|
|
|
floating = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now check for walltextures
|
|
|
|
if (floating)
|
|
|
|
{
|
|
|
|
if (!areToptexturesMissing(thisSector))
|
|
|
|
{
|
|
|
|
floating = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return floating;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// check if no adjacent sector has same ceiling height
|
|
|
|
// FIXME: throw that together with isCeilingFloating??
|
|
|
|
//
|
|
|
|
static boolean isFloorFloating(sector_t *thisSector)
|
|
|
|
{
|
|
|
|
sector_t *adjSector, *refSector = NULL, *frontSector, *backSector;
|
|
|
|
boolean floating = true;
|
|
|
|
linechain_t *thisElem, *nextElem;
|
|
|
|
|
|
|
|
if (!thisSector)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
nextElem = thisSector->sectorLines;
|
|
|
|
|
|
|
|
while (nextElem) // walk through chain
|
|
|
|
{
|
|
|
|
thisElem = nextElem;
|
|
|
|
nextElem = thisElem->next;
|
|
|
|
|
|
|
|
frontSector = thisElem->line->frontsector;
|
|
|
|
backSector = thisElem->line->backsector;
|
|
|
|
|
|
|
|
if (frontSector == thisSector)
|
|
|
|
adjSector = backSector;
|
|
|
|
else
|
|
|
|
adjSector = frontSector;
|
|
|
|
|
|
|
|
if (NULL == adjSector) // assume floating sectors have surrounding sectors
|
|
|
|
{
|
|
|
|
floating = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL == refSector)
|
|
|
|
{
|
|
|
|
refSector = adjSector;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if adjacent sector has same height or more than one adjacent sector exists -> stop
|
|
|
|
if (thisSector->floorheight == adjSector->floorheight ||
|
|
|
|
refSector != adjSector)
|
|
|
|
{
|
|
|
|
floating = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now check for walltextures
|
|
|
|
if (floating)
|
|
|
|
{
|
|
|
|
if (!areBottomtexturesMissing(thisSector))
|
|
|
|
{
|
|
|
|
floating = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return floating;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// estimate ceilingheight according to height of adjacent sector
|
|
|
|
//
|
|
|
|
static fixed_t estimateCeilHeight(sector_t *thisSector)
|
|
|
|
{
|
|
|
|
sector_t *adjSector;
|
|
|
|
|
|
|
|
if (!thisSector ||
|
|
|
|
!thisSector->sectorLines ||
|
|
|
|
!thisSector->sectorLines->line)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
adjSector = thisSector->sectorLines->line->frontsector;
|
|
|
|
if (adjSector == thisSector)
|
|
|
|
adjSector = thisSector->sectorLines->line->backsector;
|
|
|
|
|
|
|
|
if (!adjSector)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return adjSector->ceilingheight;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// estimate ceilingheight according to height of adjacent sector
|
|
|
|
//
|
|
|
|
static fixed_t estimateFloorHeight(sector_t *thisSector)
|
|
|
|
{
|
|
|
|
sector_t *adjSector;
|
|
|
|
|
|
|
|
if (!thisSector ||
|
|
|
|
!thisSector->sectorLines ||
|
|
|
|
!thisSector->sectorLines->line)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
adjSector = thisSector->sectorLines->line->frontsector;
|
|
|
|
if (adjSector == thisSector)
|
|
|
|
adjSector = thisSector->sectorLines->line->backsector;
|
|
|
|
|
|
|
|
if (NULL == adjSector)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return adjSector->floorheight;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CORRECT_FLOAT_EXPERIMENTAL
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Some levels have missing sidedefs, which produces HOM, so lets try to compensate for that
|
|
|
|
// and some levels have deep water trick, invisible staircases etc.
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// FIXME: put some nice default texture in legacy.dat and use it
|
|
|
|
void HWR_CorrectSWTricks(void)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
size_t k;
|
|
|
|
line_t *ld;
|
|
|
|
side_t *sidel = NULL, *sider;
|
|
|
|
sector_t *secl, *secr;
|
|
|
|
sector_t **sectorList;
|
|
|
|
sector_t *outSector;
|
|
|
|
|
|
|
|
if ((0 == cv_grcorrecttricks.value))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// determine lines for sectors
|
|
|
|
for (i = 0; i < numlines; i++)
|
|
|
|
{
|
|
|
|
ld = &lines[i];
|
|
|
|
secr = ld->frontsector;
|
|
|
|
secl = ld->backsector;
|
|
|
|
|
|
|
|
if (secr == secl)
|
|
|
|
{
|
|
|
|
secr->pseudoSector = true; // special renderer trick?
|
|
|
|
addLineToChain(secr, ld);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
addLineToChain(secr, ld);
|
|
|
|
addLineToChain(secl, ld);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// preprocessing
|
|
|
|
for (i = 0; i < numsectors; i++)
|
|
|
|
{
|
|
|
|
sector_t *checkSector;
|
|
|
|
|
|
|
|
checkSector = §ors[i];
|
|
|
|
|
|
|
|
// identify real pseudosectors first
|
|
|
|
if (checkSector->pseudoSector)
|
|
|
|
{
|
|
|
|
if (!isPSectorValid(checkSector)) // drop invalid pseudo sectors
|
|
|
|
{
|
|
|
|
checkSector->pseudoSector = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// determine enclosing sectors for pseudosectors ... used later
|
|
|
|
if (checkSector->pseudoSector)
|
|
|
|
{
|
|
|
|
generateStacklist(checkSector);
|
|
|
|
calcLineouts(checkSector);
|
|
|
|
sortStacklist(checkSector);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set virtual floor heights for pseudo sectors
|
|
|
|
// required for deep water effect e.g.
|
|
|
|
for (i = 0; i < numsectors; i++)
|
|
|
|
{
|
|
|
|
if (sectors[i].pseudoSector)
|
|
|
|
{
|
|
|
|
sectorList = sectors[i].stackList;
|
|
|
|
k = 0;
|
|
|
|
while (*(sectorList+k))
|
|
|
|
{
|
|
|
|
outSector = *(sectorList+k);
|
|
|
|
if (!outSector->pseudoSector)
|
|
|
|
{
|
|
|
|
sectors[i].virtualFloorheight = outSector->floorheight;
|
|
|
|
sectors[i].virtualCeilingheight = outSector->ceilingheight;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
k++;
|
|
|
|
}
|
|
|
|
if (*(sectorList+k) == NULL) // sorry, did not work :(
|
|
|
|
{
|
|
|
|
sectors[i].virtualFloorheight = sectors[i].floorheight;
|
|
|
|
sectors[i].virtualCeilingheight = sectors[i].ceilingheight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef CORRECT_FLOAT_EXPERIMENTAL
|
|
|
|
// correct ceiling/floor heights of totally floating sectors
|
|
|
|
for (i = 0; i < numsectors; i++)
|
|
|
|
{
|
|
|
|
sector_t *floatSector;
|
|
|
|
|
|
|
|
floatSector = §ors[i];
|
|
|
|
|
|
|
|
// correct height of floating sectors
|
|
|
|
if (isCeilingFloating(floatSector))
|
|
|
|
{
|
|
|
|
fixed_t corrheight;
|
|
|
|
|
|
|
|
corrheight = estimateCeilHeight(floatSector);
|
|
|
|
floatSector->virtualCeilingheight = corrheight;
|
|
|
|
floatSector->virtualCeiling = true;
|
|
|
|
}
|
|
|
|
if (isFloorFloating(floatSector))
|
|
|
|
{
|
|
|
|
fixed_t corrheight;
|
|
|
|
|
|
|
|
corrheight = estimateFloorHeight(floatSector);
|
|
|
|
floatSector->virtualFloorheight = corrheight;
|
|
|
|
floatSector->virtualFloor = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// now for the missing textures
|
|
|
|
for (i = 0; i < numlines; i++)
|
|
|
|
{
|
|
|
|
ld = &lines[i];
|
|
|
|
sider = &sides[ld->sidenum[0]];
|
|
|
|
if (ld->sidenum[1] != 0xffff)
|
|
|
|
sidel = &sides[ld->sidenum[1]];
|
|
|
|
|
|
|
|
secr = ld->frontsector;
|
|
|
|
secl = ld->backsector;
|
|
|
|
|
|
|
|
if (secr == secl) // special renderer trick
|
|
|
|
continue; // we cant correct missing textures here
|
|
|
|
|
|
|
|
if (secl) // only if there is a backsector
|
|
|
|
{
|
|
|
|
if (secr->pseudoSector || secl->pseudoSector)
|
|
|
|
continue;
|
|
|
|
if (!secr->virtualFloor && !secl->virtualFloor)
|
|
|
|
{
|
|
|
|
if (secl->floorheight > secr->floorheight)
|
|
|
|
{
|
|
|
|
// now check if r-sidedef is correct
|
|
|
|
if (sider->bottomtexture == 0)
|
|
|
|
{
|
|
|
|
if (sider->midtexture == 0)
|
2014-03-24 17:29:14 +00:00
|
|
|
sider->bottomtexture = 0; // Tails // More redwall sky shenanigans
|
2014-03-15 16:59:03 +00:00
|
|
|
else
|
|
|
|
sider->bottomtexture = sider->midtexture;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (secl->floorheight < secr->floorheight)
|
|
|
|
{
|
|
|
|
// now check if l-sidedef is correct
|
|
|
|
if (sidel->bottomtexture == 0)
|
|
|
|
{
|
|
|
|
if (sidel->midtexture == 0)
|
2014-03-24 17:29:14 +00:00
|
|
|
sidel->bottomtexture = 0; // Tails // More redwall sky shenanigans
|
2014-03-15 16:59:03 +00:00
|
|
|
else
|
|
|
|
sidel->bottomtexture = sidel->midtexture;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!secr->virtualCeiling && !secl->virtualCeiling)
|
|
|
|
{
|
|
|
|
if (secl->ceilingheight < secr->ceilingheight)
|
|
|
|
{
|
|
|
|
// now check if r-sidedef is correct
|
|
|
|
if (sider->toptexture == 0)
|
|
|
|
{
|
|
|
|
if (sider->midtexture == 0)
|
|
|
|
sider->toptexture = 0; // Tails // When this was REDWALL it was causing issues in the sky sometimes
|
|
|
|
else
|
|
|
|
sider->toptexture = sider->midtexture;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (secl->ceilingheight > secr->ceilingheight)
|
|
|
|
{
|
|
|
|
// now check if l-sidedef is correct
|
|
|
|
if (sidel->toptexture == 0)
|
|
|
|
{
|
|
|
|
if (sidel->midtexture == 0)
|
|
|
|
sidel->toptexture = 0; // Tails // When this was REDWALL it was causing issues in the sky sometimes
|
|
|
|
else
|
|
|
|
sidel->toptexture = sidel->midtexture;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // if (NULL != secl)
|
|
|
|
} // for (i = 0; i < numlines; i++)
|
|
|
|
|
|
|
|
// release all linechains
|
|
|
|
releaseLineChains();
|
|
|
|
freeStacklists();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // HWRENDER
|