Merge commit '88706e8e1320b782702e3da7c1293b3791aa97d7' into whaven

This commit is contained in:
Christoph Oelckers 2021-03-20 01:45:34 +01:00
commit 8f07ccd295
22 changed files with 449 additions and 282 deletions

View file

@ -1117,6 +1117,7 @@ set (PCH_SOURCES
core/savegamehelp.cpp
core/quotes.cpp
core/screenshot.cpp
core/sectorgeometry.cpp
core/raze_music.cpp
core/raze_sound.cpp
core/palette.cpp

View file

@ -622,7 +622,6 @@ static FORCE_INLINE int32_t E_SpriteIsValid(const int32_t i)
void alignceilslope(int16_t dasect, int32_t x, int32_t y, int32_t z);
void alignflorslope(int16_t dasect, int32_t x, int32_t y, int32_t z);
int32_t sectorofwall(int16_t wallNum);
void setslope(int32_t sectnum, int32_t cf, int16_t slope);
int32_t lintersect(int32_t originX, int32_t originY, int32_t originZ,
@ -862,7 +861,6 @@ void markTileForPrecache(int tilenum, int palnum);
void precacheMarkedTiles();
extern int32_t(*animateoffs_replace)(int const tilenum, int fakevar);
extern int32_t(*getpalookup_replace)(int32_t davis, int32_t dashade);
extern void(*initspritelists_replace)(void);
extern int32_t(*insertsprite_replace)(int16_t sectnum, int16_t statnum);
extern int32_t(*deletesprite_replace)(int16_t spritenum);

View file

@ -51,6 +51,7 @@ struct sectortype
int16_t hitag;
int16_t extra;
uint8_t dirty;
float ceilingxpan_, ceilingypan_, floorxpan_, floorypan_;
int ceilingxpan() const { return int(ceilingxpan_); }
@ -91,7 +92,7 @@ struct walltype
};
vec2_t pos;
};
int16_t point2, nextwall, nextsector;
int16_t point2, nextwall, sector, nextsector;
uint16_t cstat;
int16_t picnum, overpicnum;
int8_t shade;

View file

@ -508,7 +508,7 @@ int32_t clipmove(vec3_t * const pos, int16_t * const sectnum, int32_t xvect, int
{
clipyou = 1;
}
else if (editstatus == 0)
else
{
clipmove_tweak_pos(pos, diff.x, diff.y, p1.x, p1.y, p2.x, p2.y, &v.x, &v.y);
clipyou = cliptestsector(dasect, wal->nextsector, flordist, ceildist, v, pos->z);
@ -990,11 +990,8 @@ void getzrange(const vec3_t *pos, int16_t sectnum,
if (wall[j].cstat&dawalclipmask) continue; // XXX?
auto const sec = (usectorptr_t)&sector[k];
if (editstatus == 0)
{
if (((sec->ceilingstat&1) == 0) && (pos->z <= sec->ceilingz+(3<<8))) continue;
if (((sec->floorstat&1) == 0) && (pos->z >= sec->floorz-(3<<8))) continue;
}
if (((sec->ceilingstat&1) == 0) && (pos->z <= sec->ceilingz+(3<<8))) continue;
if (((sec->floorstat&1) == 0) && (pos->z >= sec->floorz-(3<<8))) continue;
if (bitmap_test(clipsectormap, k) == 0)
addclipsect(k);

View file

@ -73,7 +73,6 @@ int32_t globalflags;
static int8_t tempbuf[MAXWALLS];
// referenced from asm
int32_t reciptable[2048];
intptr_t asm1, asm2;
int32_t globalx1, globaly2, globalx3, globaly3;
@ -87,7 +86,6 @@ int32_t showfirstwall=0;
int32_t showheightindicators=1;
int32_t circlewall=-1;
int16_t editstatus = 0;
static fixed_t global100horiz; // (-100..300)-scale horiz (the one passed to drawrooms)
static FString printcoords(void)
@ -118,8 +116,6 @@ ADD_STAT(printcoords)
return printcoords();
}
int32_t(*getpalookup_replace)(int32_t davis, int32_t dashade) = NULL;
// adapted from build.c
static void getclosestpointonwall_internal(vec2_t const p, int32_t const dawall, vec2_t *const closest)
{
@ -149,7 +145,6 @@ static void getclosestpointonwall_internal(vec2_t const p, int32_t const dawall,
}
int32_t xb1[MAXWALLSB]; // Polymost uses this as a temp array
static int32_t xb2[MAXWALLSB];
int32_t rx1[MAXWALLSB], ry1[MAXWALLSB];
int16_t bunchp2[MAXWALLSB], thesector[MAXWALLSB];
@ -158,7 +153,7 @@ int16_t bunchfirst[MAXWALLSB], bunchlast[MAXWALLSB];
static vec3_t spritesxyz[MAXSPRITESONSCREEN+1];
int32_t xdimen = -1, xdimenrecip, halfxdimen, xdimenscale, xdimscale;
int32_t xdimen = -1, xdimenscale, xdimscale;
float fxdimen = -1.f;
int32_t ydimen;
@ -191,18 +186,6 @@ static_assert(MAXWALLSB < INT16_MAX);
int16_t numscans, numbunches;
static int16_t numhits;
int16_t searchit;
int16_t searchsector, searchwall, searchstat; //search output
// SEARCHBOTTOMWALL:
// When aiming at a the bottom part of a 2-sided wall whose bottom part
// is swapped (.cstat&2), searchbottomwall equals that wall's .nextwall. In all
// other cases (when aiming at a wall), searchbottomwall equals searchwall.
//
// SEARCHISBOTTOM:
// When aiming at a 2-sided wall, 1 if aiming at the bottom part, 0 else
int16_t searchbottomwall, searchisbottom;
bool inpreparemirror = 0;
static int32_t mirrorsx1, mirrorsy1, mirrorsx2, mirrorsy2;
@ -319,9 +302,6 @@ static int32_t engineLoadTables(void)
{
int32_t i;
for (i=0; i<2048; i++)
reciptable[i] = DivScale(2048, i+2048, 30);
for (i=0; i<=512; i++)
sintable[i] = bsinf(i);
for (i=513; i<1024; i++)
@ -799,8 +779,6 @@ int32_t engineInit(void)
paletteloaded = 0;
searchit = 0; searchstat = -1;
g_visibility = 512;
parallaxvisibility = 512;
@ -2607,6 +2585,7 @@ void dragpoint(int16_t pointhighlight, int32_t dax, int32_t day, uint8_t flags)
while (1)
{
sector[wall[w].sector].dirty = 255;
wall[w].x = dax;
wall[w].y = day;
walbitmap[w>>3] |= pow2char[w&7];
@ -2985,8 +2964,7 @@ void videoSetViewableArea(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
windowxy2.x = x2;
windowxy2.y = y2;
xdimen = (x2-x1)+1; halfxdimen = (xdimen>>1);
xdimenrecip = DivScale(1L,xdimen, 32);
xdimen = (x2-x1)+1;
ydimen = (y2-y1)+1;
fxdimen = (float) xdimen;
@ -3124,36 +3102,6 @@ void renderCompleteMirror(void)
}
//
// sectorofwall
//
static int32_t sectorofwall_internal(int16_t wallNum)
{
native_t gap = numsectors>>1, sectNum = gap;
while (gap > 1)
{
gap >>= 1;
native_t const n = !!(sector[sectNum].wallptr < wallNum);
sectNum += (n ^ (n - 1)) * gap;
}
while (sector[sectNum].wallptr > wallNum) sectNum--;
while (sector[sectNum].wallptr + sector[sectNum].wallnum <= wallNum) sectNum++;
return sectNum;
}
int32_t sectorofwall(int16_t wallNum)
{
if ((unsigned)wallNum < (unsigned)numwalls)
{
native_t const w = wall[wallNum].nextwall;
return ((unsigned)w < MAXWALLS) ? wall[w].nextsector : sectorofwall_internal(wallNum);
}
return -1;
}
int32_t getceilzofslopeptr(usectorptr_t sec, int32_t dax, int32_t day)
{
if (!(sec->ceilingstat&2))

View file

@ -39,7 +39,7 @@ extern int16_t thesector[MAXWALLSB], thewall[MAXWALLSB];
extern int16_t bunchfirst[MAXWALLSB], bunchlast[MAXWALLSB];
extern int16_t maskwall[MAXWALLSB], maskwallcnt;
extern tspriteptr_t tspriteptr[MAXSPRITESONSCREEN + 1];
extern int32_t xdimen, xdimenrecip, halfxdimen, xdimenscale, xdimscale, ydimen;
extern int32_t xdimen, xdimenscale, xdimscale, ydimen;
extern float fxdimen;
extern int32_t globalposx, globalposy, globalposz;
extern fixed_t qglobalhoriz, qglobalang;
@ -53,12 +53,6 @@ extern int16_t globalpicnum;
extern int32_t globalorientation;
extern int16_t editstatus;
extern int16_t searchit;
extern int16_t searchsector, searchwall, searchstat;
extern int16_t searchbottomwall, searchisbottom;
extern int16_t sectorborder[256];
extern int32_t hitallsprites;
@ -67,7 +61,6 @@ extern int32_t rx1[MAXWALLSB], ry1[MAXWALLSB];
extern int16_t bunchp2[MAXWALLSB];
extern int16_t numscans, numbunches;
extern int32_t rxi[8], ryi[8];
extern int32_t reciptable[2048];
// int32_t wallmost(int16_t *mostbuf, int32_t w, int32_t sectnum, char dastat);
@ -84,18 +77,6 @@ static FORCE_INLINE int32_t bad_tspr(tspriteptr_t tspr)
return (tspr->owner < 0 || (unsigned)tspr->picnum >= MAXTILES);
}
//
// getpalookup (internal)
//
static FORCE_INLINE int32_t getpalookup(int32_t davis, int32_t dashade)
{
if (getpalookup_replace)
return getpalookup_replace(davis, dashade);
return min(max(dashade + (davis >> 8), 0), numshades - 1);
}
static FORCE_INLINE int32_t getpalookupsh(int32_t davis) { return getpalookup(davis, globalshade) << 8; }
static FORCE_INLINE void setgotpic(int32_t tilenume)
{
gotpic[tilenume>>3] |= pow2char[tilenume&7];

View file

@ -915,16 +915,6 @@ skip: ;
#endif
}
// Moved in from pragmas.h
static inline int32_t krecipasm(int32_t i)
{
// Ken did this
union { int32_t i; float f; } x;
x.f = (float)i;
i = x.i;
return ((reciptable[(i >> 12) & 2047] >> (((i - 0x3f800000) >> 23) & 31)) ^ (i >> 31));
}
// variables that are set to ceiling- or floor-members, depending
// on which one is processed right now
static int32_t global_cf_z;
@ -956,14 +946,14 @@ static void polymost_internal_nonparallaxed(vec2f_t n0, vec2f_t n1, float ryp0,
wall[wall[sec->wallptr].point2].y - wall[sec->wallptr].y };
float r;
int length = ksqrt(uhypsq(xy.x, xy.y));
if (globalorientation & 2)
{
int i = krecipasm(ksqrt(uhypsq(xy.x,xy.y)));
r = i * (1.f/1073741824.f);
r = 1.f / length;
}
else
{
int i = ksqrt(uhypsq(xy.x,xy.y)); if (i == 0) i = 1024; else i = 1048576 / i;
int i; if (length == 0) i = 1024; else i = 1048576 / length; // consider integer truncation
r = i * (1.f/1048576.f);
}

View file

@ -175,7 +175,7 @@ bool spriteIsModelOrVoxel(const spritetype * tspr)
//
//==========================================================================
void PlanesAtPoint(usectorptr_t sec, float dax, float day, float* pceilz, float* pflorz)
void PlanesAtPoint(const sectortype* sec, float dax, float day, float* pceilz, float* pflorz)
{
float ceilz = float(sec->ceilingz);
float florz = float(sec->floorz);
@ -200,25 +200,3 @@ void PlanesAtPoint(usectorptr_t sec, float dax, float day, float* pceilz, float*
if (pceilz) *pceilz = ceilz * -(1.f / 256.f);
if (pflorz) *pflorz = florz * -(1.f / 256.f);
}
// variant that allows to pass precalculated info for the first line in. For cases where multiple points in a sector need to be checked.
void PlanesAtPoint(usectorptr_t sec, PlaneParam *pp, float dax, float day, float* pceilz, float* pflorz)
{
float ceilz = float(sec->ceilingz);
float florz = float(sec->floorz);
if (((sec->ceilingstat | sec->floorstat) & CSTAT_SECTOR_SLOPE) == CSTAT_SECTOR_SLOPE)
{
if (pp->length != 0)
{
auto wal = &wall[sec->wallptr];
float const j = (pp->dx * (day - wal->y) - pp->dy * (dax - wal->x)) * (1.f / 8.f);
if (sec->ceilingstat & CSTAT_SECTOR_SLOPE) ceilz += (sec->ceilingheinum * j) / pp->length;
if (sec->floorstat & CSTAT_SECTOR_SLOPE) florz += (sec->floorheinum * j) / pp->length;
}
}
// Scale to render coordinates.
if (pceilz) *pceilz = ceilz * -(1.f / 256.f);
if (pflorz) *pflorz = florz * -(1.f / 256.f);
}

View file

@ -8,15 +8,8 @@ extern int cameradist, cameraclock;
bool calcChaseCamPos(int* px, int* py, int* pz, spritetype* pspr, short *psectnum, binangle ang, fixedhoriz horiz, double const smoothratio);
bool spriteIsModelOrVoxel(const spritetype* tspr);
void PlanesAtPoint(usectorptr_t sec, float dax, float day, float* ceilz, float* florz);
struct PlaneParam
{
float dx, dy;
int length;
};
void PlanesAtPoint(usectorptr_t sec, PlaneParam* pp, float dax, float day, float* ceilz, float* florz);
void PlanesAtPoint(const sectortype* sec, float dax, float day, float* ceilz, float* florz);
void setWallSectors();
// y is negated so that the orientation is the same as in GZDoom, in order to use its utilities.
// The render code should NOT use Build coordinates for anything!
@ -76,3 +69,10 @@ inline double PointOnLineSide(double x, double y, double linex, double liney, do
return (x - linex) * deltay - (y - liney) * deltax;
}
inline int sectorofwall(int wallNum)
{
if ((unsigned)wallNum < (unsigned)numwalls) return wall[wallNum].sector;
return -1;
}

View file

@ -72,8 +72,8 @@ void Set(int index, int type, double val)
case Interp_Sect_CeilingPanX: sector[index].ceilingxpan_ = float(val); break;
case Interp_Sect_CeilingPanY: sector[index].ceilingypan_ = float(val); break;
case Interp_Wall_X: wall[index].x = xs_CRoundToInt(val); break;
case Interp_Wall_Y: wall[index].y = xs_CRoundToInt(val); break;
case Interp_Wall_X: wall[index].x = xs_CRoundToInt(val); sector[wall[index].sector].dirty = 255; break;
case Interp_Wall_Y: wall[index].y = xs_CRoundToInt(val); sector[wall[index].sector].dirty = 255; break;
case Interp_Wall_PanX: wall[index].xpan_ = float(val); break;
case Interp_Wall_PanY: wall[index].ypan_ = float(val); break;

View file

@ -41,6 +41,8 @@
#include "inputstate.h"
#include "md4.h"
#include "gamecontrol.h"
#include "gamefuncs.h"
#include "sectorgeometry.h"
static void ReadSectorV7(FileReader& fr, sectortype& sect)
@ -444,6 +446,7 @@ void engineLoadBoard(const char* filename, int flags, vec3_t* pos, int16_t* ang,
unsigned char md4[16];
md4once(buffer.Data(), buffer.Size(), md4);
G_LoadMapHack(filename, md4);
setWallSectors();
memcpy(wallbackup, wall, sizeof(wallbackup));
memcpy(sectorbackup, sector, sizeof(sectorbackup));
@ -468,4 +471,18 @@ void loadMapBackup(const char* filename)
engineLoadBoard(filename, 0, &pos, &scratch, &scratch);
initspritelists();
}
}
// Sets the sector reference for each wall. We need this for the triangulation cache.
void setWallSectors()
{
for (int i = 0; i < numsectors; i++)
{
sector[i].dirty = 255;
for (int w = 0; w < sector[i].wallnum; w++)
{
wall[sector[i].wallptr + w].sector = i;
}
}
sectorGeometry.SetSize(numsectors);
}

View file

@ -185,7 +185,7 @@ FRenderViewpoint SetupView(vec3_t& position, int sectnum, fixed_t q16angle, fixe
r_viewpoint.Pos = { position.x / 16.f, position.y / -16.f, position.z / -256.f };
r_viewpoint.HWAngles.Yaw = -90.f + q16ang(q16angle).asdeg();
r_viewpoint.HWAngles.Pitch = -HorizToPitch(q16horizon);
r_viewpoint.HWAngles.Roll = rollang;
r_viewpoint.HWAngles.Roll = -rollang;
r_viewpoint.FieldOfView = (float)r_fov;
r_viewpoint.RotAngle = q16ang(q16angle).asbam();
return r_viewpoint;

View file

@ -35,8 +35,7 @@
#include "hw_lightbuffer.h"
#include "hw_drawstructs.h"
#include "hw_renderstate.h"
#include "texturemanager.h"
#include "earcut.hpp"
#include "sectorgeometry.h"
#ifdef _DEBUG
CVAR(Int, gl_breaksec, -1, 0)
@ -92,115 +91,26 @@ void HWFlat::SetupLights(HWDrawInfo *di, FLightNode * node, FDynLightData &light
//==========================================================================
//
// CalcPlane fixme - this should be stored in the sector, not be recalculated each frame.
//
//==========================================================================
static FVector3 CalcNormal(sectortype* sector, int plane)
{
FVector3 pt[3];
auto wal = &wall[sector->wallptr];
auto wal2 = &wall[wal->point2];
pt[0] = { (float)WallStartX(wal), (float)WallStartY(wal), 0 };
pt[1] = { (float)WallEndX(wal), (float)WallEndY(wal), 0 };
PlanesAtPoint(sector, wal->x, wal->y, plane ? &pt[0].Z : nullptr, plane? nullptr : &pt[0].Z);
PlanesAtPoint(sector, wal2->x, wal2->y, plane ? &pt[1].Z : nullptr, plane ? nullptr : &pt[1].Z);
if (pt[0].X == pt[1].X)
{
if (pt[0].Y == pt[1].Y) return { 0.f, 0.f, plane ? -1.f : 1.f };
pt[2].X = pt[0].X + 4;
pt[2].Y = pt[0].Y;
}
else
{
pt[2].X = pt[0].X;
pt[2].Y = pt[0].Y + 4;
}
PlanesAtPoint(sector, pt[2].X * 16, pt[2].Y * 16, plane ? &pt[2].Z : nullptr, plane ? nullptr : &pt[2].Z);
auto normal = (pt[2] - pt[0]) ^ (pt[1] - pt[0]);
if ((pt[2].Z < 0 && !plane) || (pt[2].Z > 0 && plane)) return -pt[2];
return pt[2];
}
//==========================================================================
//
// this should be buffered later.
//
//==========================================================================
void HWFlat::MakeVertices()
{
int numvertices = sec->wallnum;
TArray<FVector3> points(numvertices, true);
using Point = std::pair<float, float>;
std::vector<std::vector<Point>> polygon;
std::vector<Point>* curPoly;
polygon.resize(1);
curPoly = &polygon.back();
for (int i = 0; i < numvertices; i++)
{
auto wal = &wall[sec->wallptr + i];
float X = WallStartX(wal);
float Y = WallStartY(wal);
curPoly->push_back(std::make_pair(X, Y));
if (wal->point2 != sec->wallptr+i+1 && i < numvertices - 1)
{
polygon.resize(polygon.size() + 1);
curPoly = &polygon.back();
}
}
// Now make sure that the outer boundary is the first polygon by picking a point that's as much to the outside as possible.
int outer = 0;
float minx = FLT_MAX;
float miny = FLT_MAX;
for (size_t a = 0; a < polygon.size(); a++)
{
for (auto& pt : polygon[a])
{
if (pt.first < minx || (pt.first == minx && pt.second < miny))
{
minx = pt.first;
miny = pt.second;
outer = a;
}
}
}
if (outer != 0) std::swap(polygon[0], polygon[outer]);
auto indices = mapbox::earcut(polygon);
int p = 0;
for (size_t a = 0; a < polygon.size(); a++)
{
for (auto& pt : polygon[a])
{
float planez;
PlanesAtPoint(sec, (pt.first * 16), (pt.second * -16), plane ? &planez : nullptr, !plane ? &planez : nullptr);
FVector3 point = { pt.first, pt.second, planez };
points[p++] = point;
}
}
auto ret = screen->mVertexData->AllocVertices(indices.size());
auto mesh = sectorGeometry.get(sec - sector, plane);
if (!mesh) return;
auto ret = screen->mVertexData->AllocVertices(mesh->vertices.Size());
auto vp = ret.first;
for (auto i : indices)
for (unsigned i = 0; i < mesh->vertices.Size(); i++)
{
auto& pt = points[i];
auto& pt = mesh->vertices[i];
auto& uv = mesh->texcoords[i];
vp->SetVertex(pt.X, pt.Z, pt.Y);
vp->SetTexCoord(pt.X / 64.f, pt.Y / 64.f); // todo: align
vp->SetTexCoord(uv.X, uv.Y);
vp++;
}
vertindex = ret.second;
vertcount = indices.size();
vertcount = mesh->vertices.Size();
}
//==========================================================================
@ -222,7 +132,8 @@ void HWFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent)
}
#endif
state.SetNormal(CalcNormal(sector, plane));
auto mesh = sectorGeometry.get(sec - sector, plane);
state.SetNormal(mesh->normal);
// Fog must be done before the texture so that the texture selector can override it.
bool foggy = (GlobalMapFog || (fade & 0xffffff));
@ -275,6 +186,7 @@ void HWFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent)
void HWFlat::PutFlat(HWDrawInfo *di, int whichplane)
{
plane = whichplane;
if (!screen->BuffersArePersistent()) // should be made static buffer content later (when the logic is working)
{
#if 0
@ -285,7 +197,6 @@ void HWFlat::PutFlat(HWDrawInfo *di, int whichplane)
#endif
MakeVertices();
}
plane = whichplane;
di->AddFlat(this);
rendered_flats++;
}

View file

@ -615,34 +615,6 @@ bool HWWall::DoHorizon(HWDrawInfo* di, walltype* seg, sectortype* fs, DVector2&
return true;
}
//==========================================================================
//
// Build math sucks. This would be easier if NPOT was handled properly.
// Panning is calculated in NPOT dimensions only, the next largest one
// to the texture height applies, so the panning needs to be scaled accordingly.
//
//==========================================================================
static float GetYPanning(float curypanning, FGameTexture* tex/*, bool aligned*/)
{
// get next largest POT size.
int th = tex->GetTexelHeight();
int pow2size = 1 << sizeToBits(th);
if (pow2size < th) pow2size *= 2;
/* crap for lack of NPOT emulation. Should not be needed anymore
if (aligned)
{
float yoffs = (pow2size - th) * (255.0f / pow2size);
if (curypanning > 256 - yoffs)
curypanning -= yoffs;
}
*/
// scale the panning factor and scale to texture coordinates.
return pow2size * curypanning / (256.0f * th);
}
//==========================================================================
//
//
@ -721,7 +693,7 @@ void HWWall::CheckTexturePosition()
// clamp texture coordinates to a reasonable range.
// Extremely large values can cause visual problems
if (tcs[UPLFT].v > tcs[LOLFT].v || tcs[UPRGT].v > tcs[LORGT].v)
if (tcs[UPLFT].v < tcs[LOLFT].v || tcs[UPRGT].v < tcs[LORGT].v)
{
if (tcs[UPLFT].v < tcs[UPRGT].v)
{
@ -775,7 +747,6 @@ void HWWall::CheckTexturePosition()
void HWWall::DoTexture(HWDrawInfo* di, walltype* wal, walltype* refwall, float refheight, float topleft, float topright, float bottomleft, float bottomright)
{
auto glsave = glseg;
float ypanning = wal->ypan_ ? GetYPanning(wal->ypan_, texture) : 0;
SetWallCoordinates(wal, topleft, topright, bottomleft, bottomright);
bool xflipped = (wal->cstat & CSTAT_WALL_XFLIP);
@ -784,8 +755,12 @@ void HWWall::DoTexture(HWDrawInfo* di, walltype* wal, walltype* refwall, float r
float tw = texture->GetTexelWidth();
float th = texture->GetTexelHeight();
tcs[LOLFT].u = tcs[UPLFT].u = ((leftdist * 8.f * wal->xrepeat) + wal->xpan_) / tw;
tcs[LORGT].u = tcs[UPRGT].u = ((rightdist * 8.f * wal->xrepeat) + wal->xpan_) / tw;
int pow2size = 1 << sizeToBits(th);
if (pow2size < th) pow2size *= 2;
float ypanning = refwall->ypan_ ? pow2size * refwall->ypan_ / (256.0f * th) : 0;
tcs[LOLFT].u = tcs[UPLFT].u = ((leftdist * 8.f * wal->xrepeat) + refwall->xpan_) / tw;
tcs[LORGT].u = tcs[UPRGT].u = ((rightdist * 8.f * wal->xrepeat) + refwall->xpan_) / tw;
auto setv = [=](float hl, float hr, float frac) -> float
{
@ -799,7 +774,7 @@ void HWWall::DoTexture(HWDrawInfo* di, walltype* wal, walltype* refwall, float r
tcs[LOLFT].v = setv(bottomleft, bottomright, glseg.fracleft);
tcs[UPRGT].v = setv(topleft, topright, glseg.fracright);
tcs[LORGT].v = setv(bottomleft, bottomright, glseg.fracright);
CheckTexturePosition();
if (th == pow2size) CheckTexturePosition(); // for NPOT textures this adjustment can break things.
bool trans = type == RENDERWALL_M2S && (wal->cstat & CSTAT_WALL_TRANSLUCENT);
if (trans)
{
@ -807,6 +782,7 @@ void HWWall::DoTexture(HWDrawInfo* di, walltype* wal, walltype* refwall, float r
alpha = GetAlphaFromBlend((wal->cstat & CSTAT_WALL_TRANS_FLIP) ? DAMETH_TRANS2 : DAMETH_TRANS1, 0);
}
PutWall(di, trans);
flags = 0;
glseg = glsave;
}
@ -851,7 +827,7 @@ void HWWall::DoUpperTexture(HWDrawInfo* di, walltype* wal, sectortype* frontsect
// get the alignment reference position.
int refheight = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->ceilingz : backsector->ceilingz;
type = RENDERWALL_BOTTOM;
type = RENDERWALL_TOP;
DoTexture(di, wal, wal, refheight, topleft, topright, bottomleft, bottomright);
}
@ -867,7 +843,7 @@ void HWWall::DoLowerTexture(HWDrawInfo* di, walltype* wal, sectortype* frontsect
// get the alignment reference position.
int refheight;
auto refwall = (wal->cstat & CSTAT_WALL_BOTTOM_SWAP) ? &wall[wal->nextwall] : wal;
refheight = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->ceilingz : backsector->floorz;
refheight = (refwall->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->ceilingz : backsector->floorz;
shade = refwall->shade;
palette = refwall->pal;
@ -942,7 +918,7 @@ void HWWall::Process(HWDrawInfo *di, walltype *wal, sectortype* frontsector, sec
PlanesAtPoint(frontsector, p2wall->x, p2wall->y, &fch2, &ffh2);
#ifdef _DEBUG
if (wal - wall == 3810)
if (wal - wall == 7591)
{
int a = 0;
}

View file

@ -56,6 +56,7 @@
#include "gamestate.h"
#include "razemenu.h"
#include "interpolate.h"
#include "gamefuncs.h"
sectortype sectorbackup[MAXSECTORS];
@ -670,6 +671,10 @@ void SerializeMap(FSerializer& arc)
if (prevspritestat[i] == -2) prevspritestat[i] = i - 1;
if (prevspritesect[i] == -2) prevspritesect[i] = i - 1;
}
if (arc.isReading())
{
setWallSectors();
}
}
//=============================================================================

View file

@ -0,0 +1,325 @@
/*
** sectorgeometry.cpp
**
** caches the triangle meshes used for rendering sector planes.
**
**---------------------------------------------------------------------------
** Copyright 2021 Christoph Oelckers
** 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 "sectorgeometry.h"
#include "build.h"
#include "gamefuncs.h"
#include "texturemanager.h"
#include "earcut.hpp"
SectorGeometry sectorGeometry;
//==========================================================================
//
// CalcPlane fixme - this should be stored in the sector, not be recalculated each frame.
//
//==========================================================================
static FVector3 CalcNormal(sectortype* sector, int plane)
{
FVector3 pt[3];
auto wal = &wall[sector->wallptr];
auto wal2 = &wall[wal->point2];
pt[0] = { (float)WallStartX(wal), (float)WallStartY(wal), 0 };
pt[1] = { (float)WallEndX(wal), (float)WallEndY(wal), 0 };
PlanesAtPoint(sector, wal->x, wal->y, plane ? &pt[0].Z : nullptr, plane? nullptr : &pt[0].Z);
PlanesAtPoint(sector, wal2->x, wal2->y, plane ? &pt[1].Z : nullptr, plane ? nullptr : &pt[1].Z);
if (pt[0].X == pt[1].X)
{
if (pt[0].Y == pt[1].Y) return { 0.f, 0.f, plane ? -1.f : 1.f };
pt[2].X = pt[0].X + 4;
pt[2].Y = pt[0].Y;
}
else
{
pt[2].X = pt[0].X;
pt[2].Y = pt[0].Y + 4;
}
PlanesAtPoint(sector, pt[2].X * 16, pt[2].Y * 16, plane ? &pt[2].Z : nullptr, plane ? nullptr : &pt[2].Z);
auto normal = (pt[2] - pt[0]) ^ (pt[1] - pt[0]);
if ((pt[2].Z < 0 && !plane) || (pt[2].Z > 0 && plane)) return -pt[2];
return pt[2];
}
//==========================================================================
//
// The math used here to calculate texture positioning was derived from
// Polymer but required several fixes for correctness.
//
//==========================================================================
class UVCalculator
{
sectortype* sect;
int myplane;
int stat;
float z1;
int ix1;
int iy1;
int ix2;
int iy2;
float sinalign, cosalign;
FGameTexture* tex;
float xpanning, ypanning;
float xscaled, yscaled;
public:
// Moved in from pragmas.h
UVCalculator(sectortype* sec, int plane, FGameTexture* tx)
{
float xpan, ypan;
sect = sec;
tex = tx;
myplane = plane;
auto firstwall = &wall[sec->wallptr];
ix1 = firstwall->x;
iy1 = firstwall->y;
ix2 = wall[firstwall->point2].x;
iy2 = wall[firstwall->point2].y;
if (plane == 0)
{
stat = sec->floorstat;
xpan = sec->floorxpan_;
ypan = sec->floorypan_;
PlanesAtPoint(sec, ix1, iy1, nullptr, &z1);
}
else
{
stat = sec->ceilingstat;
xpan = sec->ceilingxpan_;
ypan = sec->ceilingypan_;
PlanesAtPoint(sec, ix1, iy1, &z1, nullptr);
}
DVector2 dv = { double(ix2 - ix1), -double(iy2 - iy1) };
auto vang = dv.Angle() - 90.;
cosalign = vang.Cos();
sinalign = vang.Sin();
int pow2width = 1 << sizeToBits(tx->GetTexelWidth());
if (pow2width < tx->GetTexelWidth()) pow2width *= 2;
int pow2height = 1 << sizeToBits(tx->GetTexelHeight());
if (pow2height < tx->GetTexelHeight()) pow2height *= 2;
xpanning = pow2width * xpan / (256.f * tx->GetTexelWidth());
ypanning = pow2height * ypan / (256.f * tx->GetTexelHeight());
float scalefactor = (stat & CSTAT_SECTOR_TEXHALF) ? 8.0f : 16.0f;
if ((stat & (CSTAT_SECTOR_SLOPE | CSTAT_SECTOR_ALIGN)) == (CSTAT_SECTOR_ALIGN))
{
// This is necessary to adjust for some imprecisions in the math.
// To calculate the inverse Build performs an integer division with significant loss of precision
// that can cause the texture to be shifted by multiple pixels.
// The code below calculates the amount of this deviation so that it can be added back to the formula.
int len = ksqrt(uhypsq(ix2 - ix1, iy2 - iy1));
if (len != 0)
{
int i = 1048576 / len;
scalefactor *= 1048576.f / (i * len);
}
}
xscaled = scalefactor * tx->GetTexelWidth();
yscaled = scalefactor * tx->GetTexelHeight();
}
FVector2 GetUV(int x, int y, float z)
{
float tv, tu;
if (stat & CSTAT_SECTOR_ALIGN)
{
float dx = (float)(x - ix1);
float dy = (float)(y - iy1);
tu = -(dx * sinalign + dy * cosalign);
tv = (dx * cosalign - dy * sinalign);
if (stat & CSTAT_SECTOR_SLOPE)
{
float dz = (z - z1) * 16;
float newtv = sqrt(tv * tv + dz * dz);
tv = tv < 0 ? -newtv : newtv;
}
}
else
{
tu = x;
tv = -y;
}
if (stat & CSTAT_SECTOR_SWAPXY)
std::swap(tu, tv);
if (stat & CSTAT_SECTOR_XFLIP) tu = -tu;
if (stat & CSTAT_SECTOR_YFLIP) tv = -tv;
return { tu / xscaled + xpanning, tv / yscaled + ypanning };
}
};
//==========================================================================
//
//
//
//==========================================================================
void SectorGeometry::MakeVertices(unsigned int secnum, int plane)
{
auto sec = &sector[secnum];
int numvertices = sec->wallnum;
TArray<FVector3> points(numvertices, true);
using Point = std::pair<float, float>;
std::vector<std::vector<Point>> polygon;
std::vector<Point>* curPoly;
polygon.resize(1);
curPoly = &polygon.back();
for (int i = 0; i < numvertices; i++)
{
auto wal = &wall[sec->wallptr + i];
float X = WallStartX(wal);
float Y = WallStartY(wal);
curPoly->push_back(std::make_pair(X, Y));
if (wal->point2 != sec->wallptr+i+1 && i < numvertices - 1)
{
polygon.resize(polygon.size() + 1);
curPoly = &polygon.back();
}
}
// Now make sure that the outer boundary is the first polygon by picking a point that's as much to the outside as possible.
int outer = 0;
float minx = FLT_MAX;
float miny = FLT_MAX;
for (size_t a = 0; a < polygon.size(); a++)
{
for (auto& pt : polygon[a])
{
if (pt.first < minx || (pt.first == minx && pt.second < miny))
{
minx = pt.first;
miny = pt.second;
outer = a;
}
}
}
if (outer != 0) std::swap(polygon[0], polygon[outer]);
auto indices = mapbox::earcut(polygon);
int p = 0;
for (size_t a = 0; a < polygon.size(); a++)
{
for (auto& pt : polygon[a])
{
float planez;
PlanesAtPoint(sec, (pt.first * 16), (pt.second * -16), plane ? &planez : nullptr, !plane ? &planez : nullptr);
FVector3 point = { pt.first, pt.second, planez };
points[p++] = point;
}
}
auto& entry = data[secnum].planes[plane];
entry.vertices.Resize(indices.size());
entry.texcoords.Resize(indices.size());
entry.normal = CalcNormal(sec, plane);
auto texture = tileGetTexture(plane ? sec->ceilingpicnum : sec->floorpicnum);
UVCalculator uvcalc(sec, plane, texture);
for(unsigned i = 0; i < entry.vertices.Size(); i++)
{
auto& pt = points[indices[i]];
entry.vertices[i] = pt;
entry.texcoords[i] = uvcalc.GetUV(int(pt.X * 16), int(pt.Y * -16), pt.Z);
}
}
//==========================================================================
//
//
//
//==========================================================================
void SectorGeometry::ValidateSector(unsigned int secnum, int plane)
{
auto sec = &sector[secnum];
auto compare = &data[secnum].compare;
if (plane == 0)
{
if (sec->floorheinum == compare->floorheinum &&
sec->floorpicnum == compare->floorpicnum &&
((sec->floorstat ^ compare->floorstat) & (CSTAT_SECTOR_ALIGN | CSTAT_SECTOR_YFLIP | CSTAT_SECTOR_XFLIP | CSTAT_SECTOR_TEXHALF | CSTAT_SECTOR_SWAPXY)) == 0 &&
sec->floorxpan_ == compare->floorxpan_ &&
sec->floorypan_ == compare->floorypan_ &&
sec->floorz == compare->floorz &&
!(sec->dirty & 1) && data[secnum].planes[plane].vertices.Size() ) return;
sec->dirty &= ~1;
}
else
{
if (sec->ceilingheinum == compare->ceilingheinum &&
sec->ceilingpicnum == compare->ceilingpicnum &&
((sec->ceilingstat ^ compare->ceilingstat) & (CSTAT_SECTOR_ALIGN | CSTAT_SECTOR_YFLIP | CSTAT_SECTOR_XFLIP | CSTAT_SECTOR_TEXHALF | CSTAT_SECTOR_SWAPXY)) == 0 &&
sec->ceilingxpan_ == compare->ceilingxpan_ &&
sec->ceilingypan_ == compare->ceilingypan_ &&
sec->ceilingz == compare->ceilingz &&
!(sec->dirty & 2) && data[secnum].planes[plane].vertices.Size()) return;
sec->dirty &= ~2;
}
*compare = *sec;
MakeVertices(secnum, plane);
}

View file

@ -0,0 +1,44 @@
#pragma once
#include "tarray.h"
#include "vectors.h"
#include "build.h"
struct SectorGeometryPlane
{
TArray<FVector3> vertices;
TArray<FVector2> texcoords;
FVector3 normal{};
};
struct SectorGeometryData
{
SectorGeometryPlane planes[2];
sectortype compare{};
};
class SectorGeometry
{
TArray<SectorGeometryData> data;
void ValidateSector(unsigned sectnum, int plane);
void MakeVertices(unsigned sectnum, int plane);
public:
SectorGeometryPlane* get(unsigned sectnum, int plane)
{
if (sectnum >= data.Size()) return nullptr;
ValidateSector(sectnum, plane);
return &data[sectnum].planes[plane];
}
void SetSize(unsigned sectcount)
{
data.Clear(); // delete old content
data.Resize(sectcount);
}
};
extern SectorGeometry sectorGeometry;

View file

@ -31,6 +31,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "md4.h"
#include "automap.h"
#include "raze_sound.h"
#include "gamefuncs.h"
#include "blood.h"
@ -1058,6 +1059,7 @@ void dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, shor
}
}
setWallSectors();
memcpy(wallbackup, wall, sizeof(wallbackup));
memcpy(sectorbackup, sector, sizeof(sectorbackup));
}

View file

@ -41,7 +41,6 @@ void sub_5571C(char mode);
void sub_557C4(int x, int y, int interpolation);
void DrawMirrors(int x, int y, int z, fixed_t a, fixed_t horiz, int smooth, int viewPlayer);
int qanimateoffs(int a1, int a2);
int32_t qgetpalookup(int32_t a1, int32_t a2);
void HookReplaceFunctions();
struct QAV;

View file

@ -62,14 +62,6 @@ int qanimateoffs(int a1, int a2)
return offset;
}
int32_t qgetpalookup(int32_t a1, int32_t a2)
{
if (gFogMode)
return ClipHigh(a1 >> 8, 15) * 16 + ClipRange(a2, 0, 15);
else
return ClipRange((a1 >> 8) + a2, 0, 63);
}
void qinitspritelists();
int32_t qinsertsprite(int16_t nSector, int16_t nStat);
int32_t qdeletesprite(int16_t nSprite);
@ -79,7 +71,6 @@ int32_t qchangespritestat(int16_t nSprite, int16_t nStatus);
void HookReplaceFunctions(void)
{
animateoffs_replace = qanimateoffs;
getpalookup_replace = qgetpalookup;
initspritelists_replace = qinitspritelists;
insertsprite_replace = qinsertsprite;
deletesprite_replace = qdeletesprite;

View file

@ -794,6 +794,7 @@ void PathSound(int nSector, int nSound)
void DragPoint(int nWall, int x, int y)
{
sector[wall[nWall].sector].dirty = 255;
viewInterpolateWall(nWall, &wall[nWall]);
wall[nWall].x = x;
wall[nWall].y = y;
@ -805,6 +806,7 @@ void DragPoint(int nWall, int x, int y)
if (wall[vb].nextwall >= 0)
{
vb = wall[wall[vb].nextwall].point2;
sector[wall[vb].sector].dirty = 255;
viewInterpolateWall(vb, &wall[vb]);
wall[vb].x = x;
wall[vb].y = y;
@ -817,6 +819,7 @@ void DragPoint(int nWall, int x, int y)
if (wall[lastwall(vb)].nextwall >= 0)
{
vb = wall[lastwall(vb)].nextwall;
sector[wall[vb].sector].dirty = 255;
viewInterpolateWall(vb, &wall[vb]);
wall[vb].x = x;
wall[vb].y = y;

View file

@ -977,7 +977,7 @@ void DoWall(char bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor, s
else SetGameVarID((int)lVar2, wall[iWall].overpicnum, sActor, sPlayer);
break;
case WALL_SHADE:
if (bSet) wall[iWall].x = lValue;
if (bSet) wall[iWall].shade = lValue;
else SetGameVarID((int)lVar2, wall[iWall].shade, sActor, sPlayer);
break;
case WALL_PAL:
@ -1009,8 +1009,8 @@ void DoWall(char bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor, s
else SetGameVarID((int)lVar2, wall[iWall].hitag, sActor, sPlayer);
break;
case WALL_EXTRA:
if (bSet) wall[iWall].x = lValue;
else SetGameVarID((int)lVar2, wall[iWall].x, sActor, sPlayer);
if (bSet) wall[iWall].extra = lValue;
else SetGameVarID((int)lVar2, wall[iWall].extra, sActor, sPlayer);
break;
default:
break;