gzdoom/src/maploader/slopes.cpp

600 lines
15 KiB
C++
Raw Normal View History

2016-03-01 15:47:10 +00:00
/*
** p_slopes.cpp
** Slope creation
**
**---------------------------------------------------------------------------
** Copyright 1998-2008 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 "doomtype.h"
#include "p_local.h"
#include "cmdlib.h"
#include "p_lnspec.h"
#include "p_maputl.h"
#include "p_spec.h"
#include "g_levellocals.h"
#include "actor.h"
#include "p_setup.h"
#include "maploader.h"
2016-03-01 15:47:10 +00:00
//===========================================================================
//
// P_SpawnSlopeMakers
//
//===========================================================================
void MapLoader::SlopeLineToPoint (int lineid, const DVector3 &pos, bool slopeCeil)
2016-03-01 15:47:10 +00:00
{
int linenum;
FLineIdIterator itr(lineid);
while ((linenum = itr.Next()) >= 0)
{
const line_t *line = &Level->lines[linenum];
2016-03-01 15:47:10 +00:00
sector_t *sec;
secplane_t *plane;
if (P_PointOnLineSidePrecise (pos, line) == 0)
2016-03-01 15:47:10 +00:00
{
sec = line->frontsector;
}
else
{
sec = line->backsector;
}
if (sec == NULL)
{
continue;
}
if (slopeCeil)
{
plane = &sec->ceilingplane;
}
else
{
plane = &sec->floorplane;
}
DVector3 p, v1, v2, cross;
2016-03-01 15:47:10 +00:00
p[0] = line->v1->fX();
p[1] = line->v1->fY();
p[2] = plane->ZatPoint (line->v1);
v1[0] = line->Delta().X;
v1[1] = line->Delta().Y;
v1[2] = plane->ZatPoint (line->v2) - p[2];
v2[0] = pos.X - p[0];
v2[1] = pos.Y - p[1];
v2[2] = pos.Z - p[2];
2016-03-01 15:47:10 +00:00
cross = v1 ^ v2;
double len = cross.Length();
if (len == 0)
{
Printf ("Slope thing at (%f,%f) lies directly on its target line.\n", pos.X, pos.Y);
2016-03-01 15:47:10 +00:00
return;
}
cross /= len;
// Fix backward normals
if ((cross.Z < 0 && !slopeCeil) || (cross.Z > 0 && slopeCeil))
{
cross = -cross;
}
double dist = -cross[0] * pos.X - cross[1] * pos.Y - cross[2] * pos.Z;
plane->set(cross[0], cross[1], cross[2], dist);
2016-03-01 15:47:10 +00:00
}
}
//===========================================================================
//
// P_CopyPlane
//
//===========================================================================
void MapLoader::CopyPlane (int tag, sector_t *dest, bool copyCeil)
2016-03-01 15:47:10 +00:00
{
sector_t *source;
int secnum;
secnum = P_FindFirstSectorFromTag (tag);
if (secnum == -1)
{
return;
}
source = &Level->sectors[secnum];
2016-03-01 15:47:10 +00:00
if (copyCeil)
{
dest->ceilingplane = source->ceilingplane;
2016-03-01 15:47:10 +00:00
}
else
{
dest->floorplane = source->floorplane;
2016-03-01 15:47:10 +00:00
}
}
void MapLoader::CopyPlane (int tag, const DVector2 &pos, bool copyCeil)
2016-03-01 15:47:10 +00:00
{
sector_t *dest = P_PointInSector (pos);
CopyPlane(tag, dest, copyCeil);
2016-03-01 15:47:10 +00:00
}
//===========================================================================
//
// P_SetSlope
//
//===========================================================================
void MapLoader::SetSlope (secplane_t *plane, bool setCeil, int xyangi, int zangi, const DVector3 &pos)
2016-03-01 15:47:10 +00:00
{
DAngle xyang;
DAngle zang;
2016-03-01 15:47:10 +00:00
if (zangi >= 180)
{
zang = 179.;
2016-03-01 15:47:10 +00:00
}
else if (zangi <= 0)
{
zang = 1.;
2016-03-01 15:47:10 +00:00
}
else
{
zang = (double)zangi;
2016-03-01 15:47:10 +00:00
}
if (setCeil)
{
zang += 180.;
2016-03-01 15:47:10 +00:00
}
xyang = (double)xyangi;
2016-03-01 15:47:10 +00:00
DVector3 norm;
2016-03-01 15:47:10 +00:00
if (ib_compatflags & BCOMPATF_SETSLOPEOVERFLOW)
{
// We have to consider an integer multiplication overflow here.
norm[0] = FixedToFloat(FloatToFixed(zang.Cos()) * FloatToFixed(xyang.Cos())) / 65536.;
norm[1] = FixedToFloat(FloatToFixed(zang.Cos()) * FloatToFixed(xyang.Sin())) / 65536.;
2016-03-01 15:47:10 +00:00
}
else
{
norm[0] = zang.Cos() * xyang.Cos();
norm[1] = zang.Cos() * xyang.Sin();
2016-03-01 15:47:10 +00:00
}
norm[2] = zang.Sin();
2016-03-01 15:47:10 +00:00
norm.MakeUnit();
double dist = -norm[0] * pos.X - norm[1] * pos.Y - norm[2] * pos.Z;
plane->set(norm[0], norm[1], norm[2], dist);
2016-03-01 15:47:10 +00:00
}
//===========================================================================
//
// P_VavoomSlope
//
//===========================================================================
void MapLoader::VavoomSlope(sector_t * sec, int id, const DVector3 &pos, int which)
2016-03-01 15:47:10 +00:00
{
for(auto l : sec->Lines)
2016-03-01 15:47:10 +00:00
{
if (l->args[0]==id)
{
DVector3 v1, v2, cross;
2016-03-01 15:47:10 +00:00
secplane_t *srcplane = (which == 0) ? &sec->floorplane : &sec->ceilingplane;
double srcheight = (which == 0) ? sec->GetPlaneTexZ(sector_t::floor) : sec->GetPlaneTexZ(sector_t::ceiling);
2016-03-01 15:47:10 +00:00
v1[0] = pos.X - l->v2->fX();
v1[1] = pos.Y - l->v2->fY();
v1[2] = pos.Z - srcheight;
2016-03-01 15:47:10 +00:00
v2[0] = pos.X - l->v1->fX();
v2[1] = pos.Y - l->v1->fY();
v2[2] = pos.Z - srcheight;
2016-03-01 15:47:10 +00:00
cross = v1 ^ v2;
double len = cross.Length();
if (len == 0)
{
Printf ("Slope thing at (%f,%f) lies directly on its target line.\n", pos.X, pos.Y);
2016-03-01 15:47:10 +00:00
return;
}
cross /= len;
// Fix backward normals
if ((cross.Z < 0 && which == 0) || (cross.Z > 0 && which == 1))
{
cross = -cross;
}
double dist = -cross[0] * pos.X - cross[1] * pos.Y - cross[2] * pos.Z;
srcplane->set(cross[0], cross[1], cross[2], dist);
2016-03-01 15:47:10 +00:00
return;
}
}
}
//==========================================================================
//
// P_SetSlopesFromVertexHeights
//
//==========================================================================
void MapLoader::SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable)
2016-03-01 15:47:10 +00:00
{
TMap<int, double> vt_heights[2];
2016-03-01 15:47:10 +00:00
FMapThing *mt;
bool vt_found = false;
for (mt = firstmt; mt < lastmt; ++mt)
{
if (mt->info != NULL && mt->info->Type == NULL)
{
if (mt->info->Special == SMT_VertexFloorZ || mt->info->Special == SMT_VertexCeilingZ)
{
for (unsigned i = 0; i < Level->vertexes.Size(); i++)
2016-03-01 15:47:10 +00:00
{
if (Level->vertexes[i].fX() == mt->pos.X && Level->vertexes[i].fY() == mt->pos.Y)
2016-03-01 15:47:10 +00:00
{
if (mt->info->Special == SMT_VertexFloorZ)
{
vt_heights[0][i] = mt->pos.Z;
2016-03-01 15:47:10 +00:00
}
else
{
vt_heights[1][i] = mt->pos.Z;
2016-03-01 15:47:10 +00:00
}
vt_found = true;
}
}
mt->EdNum = 0;
}
}
}
for(unsigned i = 0; i < vertexdatas.Size(); i++)
2016-03-01 15:47:10 +00:00
{
int ii = oldvertextable == NULL ? i : oldvertextable[i];
if (vertexdatas[i].flags & VERTEXFLAG_ZCeilingEnabled)
{
vt_heights[1][ii] = vertexdatas[i].zCeiling;
vt_found = true;
}
if (vertexdatas[i].flags & VERTEXFLAG_ZFloorEnabled)
{
vt_heights[0][ii] = vertexdatas[i].zFloor;
vt_found = true;
}
}
// If vertexdata_t is ever extended for non-slope usage, this will obviously have to be deferred or removed.
vertexdatas.Clear();
vertexdatas.ShrinkToFit();
2016-03-01 15:47:10 +00:00
if (vt_found)
{
for (auto &sec : Level->sectors)
2016-03-01 15:47:10 +00:00
{
if (sec.Lines.Size() != 3) continue; // only works with triangular sectors
2016-03-01 15:47:10 +00:00
DVector3 vt1, vt2, vt3;
DVector3 vec1, vec2;
2016-03-01 15:47:10 +00:00
int vi1, vi2, vi3;
vi1 = Index(sec.Lines[0]->v1);
vi2 = Index(sec.Lines[0]->v2);
vi3 = (sec.Lines[1]->v1 == sec.Lines[0]->v1 || sec.Lines[1]->v1 == sec.Lines[0]->v2)?
Index(sec.Lines[1]->v2) : Index(sec.Lines[1]->v1);
2016-03-01 15:47:10 +00:00
vt1 = DVector3(Level->vertexes[vi1].fPos(), 0);
vt2 = DVector3(Level->vertexes[vi2].fPos(), 0);
vt3 = DVector3(Level->vertexes[vi3].fPos(), 0);
2016-03-01 15:47:10 +00:00
for(int j=0; j<2; j++)
{
double *h1 = vt_heights[j].CheckKey(vi1);
double *h2 = vt_heights[j].CheckKey(vi2);
double *h3 = vt_heights[j].CheckKey(vi3);
if (h1 == NULL && h2 == NULL && h3 == NULL) continue;
2016-03-01 15:47:10 +00:00
vt1.Z = h1? *h1 : j==0? sec.GetPlaneTexZ(sector_t::floor) : sec.GetPlaneTexZ(sector_t::ceiling);
vt2.Z = h2? *h2 : j==0? sec.GetPlaneTexZ(sector_t::floor) : sec.GetPlaneTexZ(sector_t::ceiling);
vt3.Z = h3? *h3 : j==0? sec.GetPlaneTexZ(sector_t::floor) : sec.GetPlaneTexZ(sector_t::ceiling);
2016-03-01 15:47:10 +00:00
if (P_PointOnLineSidePrecise(Level->vertexes[vi3].fX(), Level->vertexes[vi3].fY(), sec.Lines[0]) == 0)
2016-03-01 15:47:10 +00:00
{
vec1 = vt2 - vt3;
vec2 = vt1 - vt3;
}
else
{
vec1 = vt1 - vt3;
vec2 = vt2 - vt3;
}
DVector3 cross = vec1 ^ vec2;
2016-03-01 15:47:10 +00:00
double len = cross.Length();
if (len == 0)
{
// Only happens when all vertices in this sector are on the same line.
// Let's just ignore this case.
continue;
}
cross /= len;
// Fix backward normals
if ((cross.Z < 0 && j == 0) || (cross.Z > 0 && j == 1))
{
cross = -cross;
}
secplane_t *plane = j==0? &sec.floorplane : &sec.ceilingplane;
2016-03-01 15:47:10 +00:00
double dist = -cross[0] * Level->vertexes[vi3].fX() - cross[1] * Level->vertexes[vi3].fY() - cross[2] * vt3.Z;
plane->set(cross[0], cross[1], cross[2], dist);
2016-03-01 15:47:10 +00:00
}
}
}
}
//===========================================================================
//
// P_SpawnSlopeMakers
//
//===========================================================================
void MapLoader::SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable)
2016-03-01 15:47:10 +00:00
{
FMapThing *mt;
for (mt = firstmt; mt < lastmt; ++mt)
{
if (mt->info != NULL && mt->info->Type == NULL &&
(mt->info->Special >= SMT_SlopeFloorPointLine && mt->info->Special <= SMT_VavoomCeiling))
{
DVector3 pos = mt->pos;
2016-03-01 15:47:10 +00:00
secplane_t *refplane;
sector_t *sec;
bool ceiling;
sec = P_PointInSector (mt->pos);
2016-03-01 15:47:10 +00:00
if (mt->info->Special == SMT_SlopeCeilingPointLine || mt->info->Special == SMT_VavoomCeiling || mt->info->Special == SMT_SetCeilingSlope)
{
refplane = &sec->ceilingplane;
ceiling = true;
}
else
{
refplane = &sec->floorplane;
ceiling = false;
}
pos.Z = refplane->ZatPoint (mt->pos) + mt->pos.Z;
2016-03-01 15:47:10 +00:00
if (mt->info->Special <= SMT_SlopeCeilingPointLine)
{ // SlopeFloorPointLine and SlopCeilingPointLine
SlopeLineToPoint (mt->args[0], pos, ceiling);
2016-03-01 15:47:10 +00:00
}
else if (mt->info->Special <= SMT_SetCeilingSlope)
{ // SetFloorSlope and SetCeilingSlope
SetSlope (refplane, ceiling, mt->angle, mt->args[0], pos);
2016-03-01 15:47:10 +00:00
}
else
{ // VavoomFloor and VavoomCeiling (these do not perform any sector height adjustment - z is absolute)
VavoomSlope(sec, mt->thingid, mt->pos, ceiling);
2016-03-01 15:47:10 +00:00
}
mt->EdNum = 0;
}
}
for (mt = firstmt; mt < lastmt; ++mt)
{
if (mt->info != NULL && mt->info->Type == NULL &&
(mt->info->Special == SMT_CopyFloorPlane || mt->info->Special == SMT_CopyCeilingPlane))
{
CopyPlane (mt->args[0], mt->pos, mt->info->Special == SMT_CopyCeilingPlane);
2016-03-01 15:47:10 +00:00
mt->EdNum = 0;
}
}
SetSlopesFromVertexHeights(firstmt, lastmt, oldvertextable);
2016-03-01 15:47:10 +00:00
}
//===========================================================================
//
// [RH] Set slopes for sectors, based on line specials
//
// P_AlignPlane
//
// Aligns the floor or ceiling of a sector to the corresponding plane
// on the other side of the reference line. (By definition, line must be
// two-sided.)
//
// If (which & 1), sets floor.
// If (which & 2), sets ceiling.
//
//===========================================================================
void MapLoader::AlignPlane(sector_t *sec, line_t *line, int which)
2016-03-01 15:47:10 +00:00
{
sector_t *refsec;
double bestdist;
vertex_t *refvert = sec->Lines[0]->v1; // Shut up, GCC
2016-03-01 15:47:10 +00:00
if (line->backsector == NULL)
return;
// Find furthest vertex from the reference line. It, along with the two ends
// of the line, will define the plane.
bestdist = 0;
for (auto ln : sec->Lines)
2016-03-01 15:47:10 +00:00
{
for (int i = 0; i < 2; i++)
{
double dist;
vertex_t *vert;
2016-03-01 15:47:10 +00:00
vert = i == 0 ? ln->v1 : ln->v2;
dist = fabs((line->v1->fY() - vert->fY()) * line->Delta().X -
(line->v1->fX() - vert->fX()) * line->Delta().Y);
2016-03-01 15:47:10 +00:00
if (dist > bestdist)
{
bestdist = dist;
refvert = vert;
}
2016-03-01 15:47:10 +00:00
}
}
refsec = line->frontsector == sec ? line->backsector : line->frontsector;
DVector3 p, v1, v2, cross;
2016-03-01 15:47:10 +00:00
secplane_t *srcplane;
double srcheight, destheight;
2016-03-01 15:47:10 +00:00
srcplane = (which == 0) ? &sec->floorplane : &sec->ceilingplane;
srcheight = (which == 0) ? sec->GetPlaneTexZ(sector_t::floor) : sec->GetPlaneTexZ(sector_t::ceiling);
destheight = (which == 0) ? refsec->GetPlaneTexZ(sector_t::floor) : refsec->GetPlaneTexZ(sector_t::ceiling);
2016-03-01 15:47:10 +00:00
p[0] = line->v1->fX();
p[1] = line->v1->fY();
p[2] = destheight;
v1[0] = line->Delta().X;
v1[1] = line->Delta().Y;
2016-03-01 15:47:10 +00:00
v1[2] = 0;
v2[0] = refvert->fX() - line->v1->fX();
v2[1] = refvert->fY() - line->v1->fY();
v2[2] = srcheight - destheight;
2016-03-01 15:47:10 +00:00
cross = (v1 ^ v2).Unit();
// Fix backward normals
if ((cross.Z < 0 && which == 0) || (cross.Z > 0 && which == 1))
{
cross = -cross;
}
double dist = -cross[0] * line->v1->fX() - cross[1] * line->v1->fY() - cross[2] * destheight;
srcplane->set(cross[0], cross[1], cross[2], dist);
2016-03-01 15:47:10 +00:00
}
//===========================================================================
//
// P_SetSlopes
//
//===========================================================================
void MapLoader::SetSlopes ()
2016-03-01 15:47:10 +00:00
{
int s;
2016-03-01 15:47:10 +00:00
for (auto &line : Level->lines)
2016-03-01 15:47:10 +00:00
{
if (line.special == Plane_Align)
2016-03-01 15:47:10 +00:00
{
line.special = 0;
if (line.backsector != nullptr)
2016-03-01 15:47:10 +00:00
{
// args[0] is for floor, args[1] is for ceiling
//
// As a special case, if args[1] is 0,
// then args[0], bits 2-3 are for ceiling.
for (s = 0; s < 2; s++)
{
int bits = line.args[s] & 3;
2016-03-01 15:47:10 +00:00
if (s == 1 && bits == 0)
bits = (line.args[0] >> 2) & 3;
2016-03-01 15:47:10 +00:00
if (bits == 1) // align front side to back
AlignPlane (line.frontsector, &line, s);
2016-03-01 15:47:10 +00:00
else if (bits == 2) // align back side to front
AlignPlane (line.backsector, &line, s);
2016-03-01 15:47:10 +00:00
}
}
}
}
}
//===========================================================================
//
// P_CopySlopes
//
//===========================================================================
void MapLoader::CopySlopes()
2016-03-01 15:47:10 +00:00
{
for (auto &line : Level->lines)
2016-03-01 15:47:10 +00:00
{
if (line.special == Plane_Copy)
2016-03-01 15:47:10 +00:00
{
// The args are used for the tags of sectors to copy:
// args[0]: front floor
// args[1]: front ceiling
// args[2]: back floor
// args[3]: back ceiling
// args[4]: copy slopes from one side of the line to the other.
line.special = 0;
for (int s = 0; s < (line.backsector ? 4 : 2); s++)
2016-03-01 15:47:10 +00:00
{
if (line.args[s])
CopyPlane(line.args[s],
(s & 2 ? line.backsector : line.frontsector), s & 1);
2016-03-01 15:47:10 +00:00
}
if (line.backsector != NULL)
2016-03-01 15:47:10 +00:00
{
if ((line.args[4] & 3) == 1)
2016-03-01 15:47:10 +00:00
{
line.backsector->floorplane = line.frontsector->floorplane;
2016-03-01 15:47:10 +00:00
}
else if ((line.args[4] & 3) == 2)
2016-03-01 15:47:10 +00:00
{
line.frontsector->floorplane = line.backsector->floorplane;
2016-03-01 15:47:10 +00:00
}
if ((line.args[4] & 12) == 4)
2016-03-01 15:47:10 +00:00
{
line.backsector->ceilingplane = line.frontsector->ceilingplane;
2016-03-01 15:47:10 +00:00
}
else if ((line.args[4] & 12) == 8)
2016-03-01 15:47:10 +00:00
{
line.frontsector->ceilingplane = line.backsector->ceilingplane;
2016-03-01 15:47:10 +00:00
}
}
}
}
}