diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index e72c62a2f..6da475ef4 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1072,6 +1072,8 @@ set (PCH_SOURCES core/gi.cpp core/rendering/scene/hw_clipper.cpp + core/rendering/scene/hw_walls.cpp + core/rendering/render.cpp core/console/c_notifybuffer.cpp core/console/d_event.cpp diff --git a/source/build/include/build.h b/source/build/include/build.h index 0ef345f11..f38528bab 100644 --- a/source/build/include/build.h +++ b/source/build/include/build.h @@ -875,6 +875,19 @@ enum EHitBits void updateModelInterpolation(); +int32_t renderAddTsprite(int16_t z, int16_t sectnum); + +inline void tileUpdatePicnum(int* const tileptr, int const obj, int stat) +{ + auto& tile = *tileptr; + + if (picanm[tile].sf & PICANM_ANIMTYPE_MASK) + tile += animateoffs(tile, obj); + + if (((obj & 16384) == 16384) && (stat & CSTAT_WALL_ROTATE_90) && RotTile(tile).newtile != -1) + tile = RotTile(tile).newtile; +} + #include "iterators.h" diff --git a/source/build/include/buildtypes.h b/source/build/include/buildtypes.h index 1ac86d561..757627400 100644 --- a/source/build/include/buildtypes.h +++ b/source/build/include/buildtypes.h @@ -20,6 +20,17 @@ // bits 12-15: reserved //////////////////// Version 7 map format //////////////////// +enum +{ + CSTAT_SECTOR_SKY = 1, + CSTAT_SECTOR_SLOPE = 2, + CSTAT_SECTOR_SWAPXY = 4, + CSTAT_SECTOR_TEXHALF = 8, + CSTAT_SECTOR_XFLIP = 16, + CSTAT_SECTOR_YFLIP = 32, + CSTAT_SECTOR_ALIGN = 64, + CSTAT_SECTOR_METHOD = 384 +}; //40 bytes struct sectortype @@ -91,6 +102,7 @@ struct walltype int16_t hitag; int16_t extra; float xpan_, ypan_; + angle_t clipangle; int xpan() const { return int(xpan_); } int ypan() const { return int(ypan_); } diff --git a/source/build/src/clip.cpp b/source/build/src/clip.cpp index 6173db182..f1c02c3c3 100644 --- a/source/build/src/clip.cpp +++ b/source/build/src/clip.cpp @@ -1419,7 +1419,7 @@ int32_t hitscan(const vec3_t *sv, int16_t sectnum, int32_t vx, int32_t vy, int32 { if (picanm[tilenum].sf&PICANM_TEXHITSCAN_BIT) { - tileUpdatePicnum(&tilenum, 0); + tileUpdatePicnum(&tilenum, 0, 0); if (tileLoad(tilenum)) { diff --git a/source/build/src/engine.cpp b/source/build/src/engine.cpp index da4781fb0..eddcf5f30 100644 --- a/source/build/src/engine.cpp +++ b/source/build/src/engine.cpp @@ -29,6 +29,7 @@ #include "inputstate.h" #include "printf.h" #include "gamecontrol.h" +#include "render.h" #ifdef USE_OPENGL # include "mdsprite.h" @@ -38,6 +39,7 @@ #include "gl_renderer.h" #endif +float rollang; int32_t r_rortexture = 0; int32_t r_rortexturerange = 0; @@ -909,9 +911,32 @@ void set_globalang(fixed_t const ang) // drawrooms // EXTERN_CVAR(Int, gl_fogmode) +CVAR(Bool, testnewrenderer, true, 0) + int32_t renderDrawRoomsQ16(int32_t daposx, int32_t daposy, int32_t daposz, fixed_t daang, fixed_t dahoriz, int16_t dacursectnum) { + for (int i = 0; i < numwalls; ++i) + { + if (wall[i].cstat & CSTAT_WALL_ROTATE_90) + { + auto& w = wall[i]; + auto& tile = RotTile(w.picnum + animateoffs(w.picnum, 16384)); + + if (tile.newtile == -1 && tile.owner == -1) + { + auto owner = w.picnum + animateoffs(w.picnum, 16384); + + tile.newtile = TileFiles.tileCreateRotated(owner); + assert(tile.newtile != -1); + + RotTile(tile.newtile).owner = w.picnum + animateoffs(w.picnum, 16384); + + } + } + } + + int32_t i; if (gl_fogmode == 1) gl_fogmode = 2; // only radial fog works with Build's screwed up coordinate system. @@ -931,26 +956,6 @@ int32_t renderDrawRoomsQ16(int32_t daposx, int32_t daposy, int32_t daposz, i = xdimen-1; - for (int i = 0; i < numwalls; ++i) - { - if (wall[i].cstat & CSTAT_WALL_ROTATE_90) - { - auto &w = wall[i]; - auto &tile = RotTile(w.picnum+animateoffs(w.picnum,16384)); - - if (tile.newtile == -1 && tile.owner == -1) - { - auto owner = w.picnum + animateoffs(w.picnum, 16384); - - tile.newtile = TileFiles.tileCreateRotated(owner); - assert(tile.newtile != -1); - - RotTile(tile.newtile).owner = w.picnum+animateoffs(w.picnum,16384); - - } - } - } - // Update starting sector number (common to classic and Polymost). // ADJUST_GLOBALCURSECTNUM. if (globalcursectnum >= MAXSECTORS) @@ -967,7 +972,15 @@ int32_t renderDrawRoomsQ16(int32_t daposx, int32_t daposy, int32_t daposz, return 0; } - Polymost::polymost_drawrooms(); + if (!testnewrenderer) + { + Polymost::polymost_drawrooms(); + } + else + { + vec3_t pos = { daposx, daposy, daposz }; + render_drawrooms(pos, globalcursectnum, daang, dahoriz, rollang, r_fov, false, false); + } return inpreparemirror; } @@ -1560,9 +1573,11 @@ void renderDrawMapView(int32_t dax, int32_t day, int32_t zoome, int16_t ang) globalfloorpal = globalpal = sec->floorpal; - globalpicnum = sec->floorpicnum; - if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0; - tileUpdatePicnum(&globalpicnum, s); + int _globalpicnum = sec->floorpicnum; + if ((unsigned)_globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0; + tileUpdatePicnum(&_globalpicnum, s, 0); + globalpicnum = _globalpicnum; + setgotpic(globalpicnum); if ((tileWidth(globalpicnum) <= 0) || (tileHeight(globalpicnum) <= 0)) continue; @@ -1679,8 +1694,12 @@ void renderDrawMapView(int32_t dax, int32_t day, int32_t zoome, int16_t ang) globalpicnum = spr->picnum; globalpal = spr->pal; // GL needs this, software doesn't - if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0; - tileUpdatePicnum(&globalpicnum, s); + + int _globalpicnum = sec->floorpicnum; + if ((unsigned)_globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0; + tileUpdatePicnum(&_globalpicnum, s, 0); + globalpicnum = _globalpicnum; + setgotpic(globalpicnum); if ((tileWidth(globalpicnum) <= 0) || (tileHeight(globalpicnum) <= 0)) continue; @@ -2981,6 +3000,7 @@ void alignflorslope(int16_t dasect, int32_t x, int32_t y, int32_t z) void renderSetRollAngle(float rolla) { Polymost::gtang = rolla * BAngRadian; + rollang = rolla * (BAngRadian * 180 / pi::pif()); } #endif diff --git a/source/build/src/engine_priv.h b/source/build/src/engine_priv.h index 76c21f8b1..1baa6e87e 100644 --- a/source/build/src/engine_priv.h +++ b/source/build/src/engine_priv.h @@ -8,6 +8,8 @@ #pragma once +#include "cmdlib.h" + #ifndef ENGINE_PRIV_H #define ENGINE_PRIV_H @@ -96,10 +98,6 @@ static FORCE_INLINE int32_t getpalookup(int32_t davis, int32_t dashade) static FORCE_INLINE int32_t getpalookupsh(int32_t davis) { return getpalookup(davis, globalshade) << 8; } -////// yax'y stuff ////// -int32_t renderAddTsprite(int16_t z, int16_t sectnum); - - static FORCE_INLINE void setgotpic(int32_t tilenume) { gotpic[tilenume>>3] |= pow2char[tilenume&7]; @@ -117,16 +115,6 @@ static FORCE_INLINE void set_globalpos(int32_t const x, int32_t const y, int32_t globalposz = z, fglobalposz = (float)z; } -template static FORCE_INLINE void tileUpdatePicnum(T * const tileptr, int const obj) -{ - auto &tile = *tileptr; - - if (picanm[tile].sf & PICANM_ANIMTYPE_MASK) - tile += animateoffs(tile, obj); - - if (((obj & 16384) == 16384) && (globalorientation & CSTAT_WALL_ROTATE_90) && RotTile(tile).newtile != -1) - tile = RotTile(tile).newtile; -} // x1, y1: in/out // rest x/y: out @@ -196,22 +184,12 @@ static inline void get_floorspr_points(T const * const spr, int32_t px, int32_t inline int widthBits(int num) { - int w = tileWidth(num); - int j = 15; - - while ((j > 1) && ((1 << j) > w)) - j--; - return j; + return sizeToBits(tileWidth(num)); } inline int heightBits(int num) { - int w = tileHeight(num); - int j = 15; - - while ((j > 1) && ((1 << j) > w)) - j--; - return j; + return sizeToBits(tileHeight(num)); } diff --git a/source/build/src/polymost.cpp b/source/build/src/polymost.cpp index 5f879d4f5..7732cb9f5 100644 --- a/source/build/src/polymost.cpp +++ b/source/build/src/polymost.cpp @@ -114,13 +114,24 @@ static inline float polymost_invsqrt_approximation(float x) return 1.f / sqrtf(x); } -float sectorVisibility(int sectnum) +static float sectorVisibility(int sectnum) { // Beware of wraparound madness... int v = sector[sectnum].visibility; return v? ((uint8_t)(v + 16)) / 16.f : 1.f; } +template static FORCE_INLINE void tileUpdatePicnum(T* const tileptr, int const obj) +{ + auto& tile = *tileptr; + + if (picanm[tile].sf & PICANM_ANIMTYPE_MASK) + tile += animateoffs(tile, obj); + + if (((obj & 16384) == 16384) && (globalorientation & CSTAT_WALL_ROTATE_90) && RotTile(tile).newtile != -1) + tile = RotTile(tile).newtile; +} + //-------------------------------------------------------------------------------------------------- //Use this for both initialization and uninitialization of OpenGL. diff --git a/source/common/utility/cmdlib.h b/source/common/utility/cmdlib.h index 3792030ae..b998cf99d 100644 --- a/source/common/utility/cmdlib.h +++ b/source/common/utility/cmdlib.h @@ -103,4 +103,14 @@ inline void fillshort(void* buff, size_t count, uint16_t clear) template inline constexpr T Sgn(const T& val) { return (val > 0) - (val < 0); } +inline int sizeToBits(int w) +{ + int j = 15; + + while ((j > 1) && ((1 << j) > w)) + j--; + return j; +} + + #endif diff --git a/source/core/rendering/render.cpp b/source/core/rendering/render.cpp new file mode 100644 index 000000000..b58937359 --- /dev/null +++ b/source/core/rendering/render.cpp @@ -0,0 +1,688 @@ +// renderer draft. This code is not for release! +#include "glbackend/glbackend.h" +#include "build.h" +#include "hw_vrmodes.h" +#include "v_draw.h" +#include "gamecvars.h" +#include "binaryangle.h" +#include "automap.h" +#include "hw_clipper.h" +#include "hw_drawstructs.h" +#include "hw_clock.h" +#include "render.h" +#include "printf.h" +#include "v_video.h" +#include "flatvertices.h" + +angle_t FrustumAngle(float ratio, float fov, float pitch) +{ + float tilt = fabs(pitch); + + // If the pitch is larger than this you can look all around at a FOV of 90° + if (tilt > 46.0f) return 0xffffffff; + + // ok, this is a gross hack that barely works... + // but at least it doesn't overestimate too much... + // todo: integrate roll into the calculation + double floatangle = 2.0 + (45.0 + ((tilt / 1.9))) * fov * 48.0 / AspectMultiplier(ratio) / 90.0; + angle_t a1 = DAngle(floatangle).BAMs(); + if (a1 >= ANGLE_180) return 0xffffffff; + return a1; +} + +//========================================================================== +// +// note that these return values in renderer coordinate space with inverted sign! +// +//========================================================================== + +float CeilingAtPoint(sectortype* sec, float dax, float day) +{ + if (!(sec->ceilingstat & CSTAT_SECTOR_SLOPE)) + return float(sec->ceilingz); + + auto wal = &wall[sec->wallptr]; + auto wal2 = &wall[wal->point2]; + + vec2_t d = { wal2->x - wal->x, wal2->y - wal->y }; + + int i = ksqrt(uhypsq(d.x, d.y)) << 5; + if (i == 0) return sec->ceilingz; + + float const j = (d.x * (day - wal->y) - d.y * (dax - wal->x)) * (1.f / 8.f); + return -float(sec->ceilingz) + (sec->ceilingheinum * j) / i; +} + +float FloorAtPoint(usectorptr_t sec, float dax, float day) +{ + if (!(sec->floorstat & CSTAT_SECTOR_SLOPE)) + return float(sec->floorz); + + auto wal = &wall[sec->wallptr]; + auto wal2 = &wall[wal->point2]; + + vec2_t d = { wal2->x - wal->x, wal2->y - wal->y }; + + int i = ksqrt(uhypsq(d.x, d.y)) << 5; + if (i == 0) return sec->floorz; + + float const j = (d.x * (day - wal->y) - d.y * (dax - wal->x)) * (1.f / 8.f); + return -float(sec->floorz) + (sec->floorheinum * j) / i; +} + +void PlanesAtPoint(usectorptr_t sec, 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) + { + auto wal = &wall[sec->wallptr]; + auto wal2 = &wall[wal->point2]; + + vec2_t d = { wal2->x - wal->x, wal2->y - wal->y }; + + int i = ksqrt(uhypsq(d.x, d.y)) << 5; + if (i != 0) + { + float const j = (d.x * (day - wal->y) - d.y * (dax - wal->x)) * (1.f / 8.f); + if (sec->ceilingstat & CSTAT_SECTOR_SLOPE) ceilz += (sec->ceilingheinum * j) / i; + if (sec->floorstat & CSTAT_SECTOR_SLOPE) florz += (sec->floorheinum * j) / i; + } + } + // Scale to render coordinates. + *pceilz = ceilz * -(1.f / 256.f); + *pflorz = florz * -(1.f / 256.f); +} + + +#define NS namespace Newrender { // auto-format blocking #define. +NS + +struct FBunch +{ + int sectnum; + int startline; + int endline; + angle_t startangle; // in pseudo angles for the clipper + angle_t endangle; +}; + +const float aspect_factor = (1.28f / 1.333333f); // this factor gets applied by the original code - must check if needed or just an artifact of the weird math. + +// ---------------------------------------------------------------------------- +// +// Bunches are groups of continuous lines +// This array stores the amount of points per bunch, +// the view angles for each point and the line index for the starting line +// +// ---------------------------------------------------------------------------- + +class BunchDrawer +{ +public: + Clipper &clipper; + int LastBunch; + int StartTime; + TArray Bunches; + TArray CompareData; + double viewx, viewy; + FixedBitArray gotsector; + + //========================================================================== + // + // + // + //========================================================================== +public: + BunchDrawer(Clipper& c, vec2_t& view) : clipper(c) + { + viewx = view.x * (1/ 16.f); + viewy = view.y * -(1/ 16.f); + StartScene(); + clipper.SetViewpoint(DVector2(viewx, viewy)); + for (int i = 0; i < numwalls; i++) + { + // Precalculate the clip angles to avoid doing this repeatedly during level traversal. + // Reverse the orientation so that startangle and endangle are properly ordered. + wall[i].clipangle = 0 - clipper.PointToPseudoAngle(wall[i].x * (1 / 16.f), wall[i].y * (-1 / 16.f)); + } + } + + //========================================================================== + // + // + // + //========================================================================== +private: + void StartScene() + { + LastBunch = 0; + StartTime = I_msTime(); + Bunches.Clear(); + CompareData.Clear(); + gotsector.Zero(); + } + + //========================================================================== + // + // + // + //========================================================================== + + void StartBunch(int sectnum, int linenum, angle_t startan, angle_t endan) + { + FBunch* bunch = &Bunches[LastBunch = Bunches.Reserve(1)]; + + bunch->sectnum = sectnum; + bunch->startline = bunch->endline = linenum; + bunch->startangle = startan; + bunch->endangle = endan; + } + + //========================================================================== + // + // + // + //========================================================================== + + void AddLineToBunch(int line, int newan) + { + Bunches[LastBunch].endline++; + Bunches[LastBunch].endangle = newan; + } + + //========================================================================== + // + // + // + //========================================================================== + + void DeleteBunch(int index) + { + Bunches[index] = Bunches.Last(); + Bunches.Pop(); + } + + bool CheckClip(walltype* wal) + { + auto pt2 = &wall[wal->point2]; + sectortype* backsector = §or[wal->nextsector]; + sectortype* frontsector = §or[wall[wal->nextwall].nextsector]; + float bs_floorheight1; + float bs_floorheight2; + float bs_ceilingheight1; + float bs_ceilingheight2; + float fs_floorheight1; + float fs_floorheight2; + float fs_ceilingheight1; + float fs_ceilingheight2; + + // Mirrors and horizons always block the view + //if (linedef->special==Line_Mirror || linedef->special==Line_Horizon) return true; + + PlanesAtPoint(frontsector, wal->x, wal->y, &fs_ceilingheight1, &fs_floorheight1); + PlanesAtPoint(frontsector, pt2->x, pt2->y, &fs_ceilingheight2, &fs_floorheight2); + + PlanesAtPoint(backsector, wal->x, wal->y, &bs_ceilingheight1, &bs_floorheight1); + PlanesAtPoint(backsector, pt2->x, pt2->y, &bs_ceilingheight2, &bs_floorheight2); + + // now check for closed sectors! No idea if we really need the sky checks. We'll see. + if (bs_ceilingheight1 <= fs_floorheight1 && bs_ceilingheight2 <= fs_floorheight2) + { + // backsector's ceiling is below frontsector's floor. + if (frontsector->ceilingstat & backsector->ceilingstat & CSTAT_SECTOR_SKY) return false; + return true; + } + + if (fs_ceilingheight1 <= bs_floorheight1 && fs_ceilingheight2 <= bs_floorheight2) + { + // backsector's floor is above frontsector's ceiling + if (frontsector->floorstat & backsector->floorstat & CSTAT_SECTOR_SKY) return false; + return true; + } + + if (bs_ceilingheight1 <= bs_floorheight1 && bs_ceilingheight2 <= bs_floorheight2) + { + // backsector is closed + if (frontsector->ceilingstat & backsector->ceilingstat & CSTAT_SECTOR_SKY) return false; + if (frontsector->floorstat & backsector->floorstat & CSTAT_SECTOR_SKY) return false; + return true; + } + + return false; + } + + //========================================================================== + // + // ClipLine + // Clips the given segment + // + //========================================================================== + + enum + { + CL_Skip = 0, + CL_Draw = 1, + CL_Pass = 2, + }; + + + int ClipLine(int line) + { + angle_t startAngle, endAngle; + auto wal = &wall[line]; + + startAngle = wal->clipangle; + endAngle = wall[wal->point2].clipangle; + + // Back side, i.e. backface culling - read: endAngle >= startAngle! + if (startAngle - endAngle < ANGLE_180) + { + return CL_Skip; + } + + if (!clipper.SafeCheckRange(startAngle, endAngle)) + { + return CL_Skip; + } + + if (wal->nextwall == -1 || (wal->cstat & CSTAT_WALL_1WAY) || CheckClip(wal)) + { + // one-sided + clipper.SafeAddClipRange(startAngle, endAngle); + return CL_Draw; + } + else + { + return CL_Draw | CL_Pass; + } + } + + //========================================================================== + // + // + // + //========================================================================== + + void ProcessBunch(int bnch) + { + FBunch* bunch = &Bunches[bnch]; + + ClipWall.Clock(); + for (int i = bunch->startline; i <= bunch->endline; i++) + { + int clipped = ClipLine(i); + + if (clipped & CL_Draw) + { + show2dwall.Set(i); + + //if (gl_render_walls) + { + SetupWall.Clock(); + + HWWall hwwall; + //Printf("Rendering wall %d\n", i); + hwwall.Process(nullptr, &wall[i], §or[bunch->sectnum], wall[i].nextsector<0? nullptr : §or[wall[i].nextsector]); + rendered_lines++; + + SetupWall.Unclock(); + } + } + + if (clipped & CL_Pass) + { + ClipWall.Unclock(); + ProcessSector(wall[i].nextsector); + ClipWall.Clock(); + } + } + ClipWall.Unclock(); + } + + //========================================================================== + // + // + // + //========================================================================== + + int WallInFront(int wall1, int wall2) + { + double x1s = WallStartX(wall1); + double y1s = WallStartY(wall1); + double x1e = WallEndX(wall1); + double y1e = WallEndY(wall1); + double x2s = WallStartX(wall2); + double y2s = WallStartY(wall2); + double x2e = WallEndX(wall2); + double y2e = WallEndY(wall2); + + double dx = x1e - x1s; + double dy = y1e - y1s; + + double t1 = PointOnLineSide(x2s, y2s, x1s, y1s, dx, dy); + double t2 = PointOnLineSide(x2e, y2e, x1s, y1s, dx, dy); + if (t1 == 0) + { + if (t2 == 0) return(-1); + t1 = t2; + } + if (t2 == 0) t2 = t1; + + if ((t1 * t2) >= 0) + { + t2 = PointOnLineSide(viewx, viewy, x1s, y1s, dx, dy); + return((t2 * t1) < 0); + } + + dx = x2e - x2s; + dy = y2e - y2s; + t1 = PointOnLineSide(x1s, y1s, x2s, y2s, dx, dy); + t2 = PointOnLineSide(x1e, y1e, x2s, y2s, dx, dy); + if (t1 == 0) + { + if (t2 == 0) return(-1); + t1 = t2; + } + if (t2 == 0) t2 = t1; + if ((t1 * t2) >= 0) + { + t2 = PointOnLineSide(viewx, viewy, x2s, y2s, dx, dy); + return((t2 * t1) >= 0); + } + return(-2); + } + + //========================================================================== + // + // This is a bit more complicated than it looks because angles can wrap + // around so we can only compare angle differences. + // + // Rules: + // 1. Any bunch can span at most 180°. + // 2. 2 bunches can never overlap at both ends + // 3. if there is an overlap one of the 2 starting points must be in the + // overlapping area. + // + //========================================================================== + + int BunchInFront(FBunch* b1, FBunch* b2) + { + angle_t anglecheck, endang; + + if (b2->startangle - b1->startangle < b1->endangle - b1->startangle) + { + // we have an overlap at b2->startangle + anglecheck = b2->startangle - b1->startangle; + + // Find the wall in b1 that overlaps b2->startangle + for (int i = b1->startline; i <= b1->endline; i++) + { + endang = wall[wall[i].point2].clipangle - b1->startangle; + if (endang > anglecheck) + { + // found a line + int ret = WallInFront(b2->startline, i); + return ret; + } + } + } + else if (b1->startangle - b2->startangle < b2->endangle - b2->startangle) + { + // we have an overlap at b1->startangle + anglecheck = b1->startangle - b2->startangle; + + // Find the wall in b2 that overlaps b1->startangle + for (int i = b2->startline; i <= b2->endline; i++) + { + endang = wall[wall[i].point2].clipangle - b2->startangle; + if (endang > anglecheck) + { + // found a line + int ret = WallInFront(i, b1->startline); + return ret; + } + } + } + // we have no overlap + return -1; + } + + //========================================================================== + // + // + // + //========================================================================== + + int FindClosestBunch() + { + int closest = 0; //Almost works, but not quite :( + + CompareData.Clear(); + for (unsigned i = 1; i < Bunches.Size(); i++) + { + switch (BunchInFront(&Bunches[i], &Bunches[closest])) + { + case 0: // i is in front + closest = i; + continue; + + case 1: // i is behind + continue; + + default: // can't determine + CompareData.Push(i); // mark for later comparison + continue; + } + } + + // we need to do a second pass to see how the marked bunches relate to the currently closest one. + for (unsigned i = 0; i < CompareData.Size(); i++) + { + switch (BunchInFront(&Bunches[CompareData[i]], &Bunches[closest])) + { + case 0: // is in front + closest = CompareData[i]; + CompareData[i] = CompareData.Last(); + CompareData.Pop(); + i = 0; // we need to recheck everything that's still marked. + continue; + + case 1: // is behind + CompareData[i] = CompareData.Last(); + CompareData.Pop(); + i--; + continue; + + default: + continue; + + } + } + return closest; + } + + //========================================================================== + // + // + // + //========================================================================== + + void ProcessSector(int sectnum) + { + if (gotsector[sectnum]) return; + gotsector.Set(sectnum); + + Bsp.Clock(); + + auto sect = §or[sectnum]; + bool inbunch; + angle_t startangle; + + //if (sect->validcount == StartTime) return; + //sect->validcount = StartTime; + +#if 0//ndef BUILD_TEST + DoSector(sectnum, false); +#endif + + //Todo: process subsectors + inbunch = false; + for (int i = 0; i < sect->wallnum; i++) + { + auto thiswall = &wall[sect->wallptr + i]; + +#ifdef _DEBUG + // For displaying positions in debugger + DVector2 start = { WallStartX(thiswall), WallStartY(thiswall) }; + DVector2 end = { WallStartX(thiswall->point2), WallStartY(thiswall->point2) }; +#endif + angle_t ang1 = thiswall->clipangle; + angle_t ang2 = wall[thiswall->point2].clipangle; + + if (ang1 - ang2 < ANGLE_180) + { + // Backside + inbunch = false; + } + else if (!clipper.SafeCheckRange(ang1, ang2)) + { + // is it visible? + inbunch = false; + } + else if (!inbunch || ang2 - startangle >= ANGLE_180) + { + // don't let a bunch span more than 180° to avoid problems. + // This limitation ensures that the combined range of 2 + // bunches will always be less than 360° which simplifies + // the distance comparison code because it prevents a + // situation where 2 bunches may overlap at both ends. + + startangle = ang1; + StartBunch(sectnum, sect->wallptr + i, ang1, ang2); + inbunch = true; + } + else + { + AddLineToBunch(sect->wallptr + i, ang2); + } + if (thiswall->point2 != sect->wallptr + i + 1) inbunch = false; + } + Bsp.Unclock(); + } + + //========================================================================== + // + // + // + //========================================================================== + +public: + void RenderScene(int viewsector) + { + ProcessSector(viewsector); + while (Bunches.Size() > 0) + { + int closest = FindClosestBunch(); + ProcessBunch(closest); + DeleteBunch(closest); + } + } +}; + + +//----------------------------------------------------------------------------- +// +// R_FrustumAngle +// +//----------------------------------------------------------------------------- + +static void SetProjection(const FRotator& rotation, FAngle fov) +{ + auto vrmode = VRMode::GetVRMode(false); + const int eyeCount = vrmode->mEyeCount; + const auto& eye = vrmode->mEyes[0]; + + int width = (windowxy2.x - windowxy1.x + 1); + int height = (windowxy2.y - windowxy1.y + 1); + float ratio = ActiveRatio(width, height, nullptr); + float fovratio; + + if (ratio >= 1.3f) + { + fovratio = 1.333333f; + } + else + { + fovratio = ratio; + } + ratio *= aspect_factor; // this factor gets applied by the original code - must check if needed or just an artifact of the weird math. + fovratio *= aspect_factor; + auto rotmat = eye.GetProjection(fov.Degrees, ratio, fovratio); + renderSetProjectionMatrix(rotmat.get()); +} + +static void SetViewMatrix(const FRotator& angles, float vx, float vy, float vz, bool mirror, bool planemirror) +{ + float mult = mirror ? -1.f : 1.f; + float planemult = planemirror ? -aspect_factor : aspect_factor;// Level->info->pixelstretch : Level->info->pixelstretch; + VSMatrix mViewMatrix; + + mViewMatrix.loadIdentity(); + mViewMatrix.rotate(angles.Roll.Degrees, 0.0f, 0.0f, 1.0f); + mViewMatrix.rotate(angles.Pitch.Degrees, 1.0f, 0.0f, 0.0f); + mViewMatrix.rotate(angles.Yaw.Degrees, 0.0f, mult, 0.0f); + mViewMatrix.translate(vx * mult, -vz * planemult, -vy); + mViewMatrix.scale(-mult, planemult, 1); + renderSetViewMatrix(mViewMatrix.get()); + + +} + + + + +} + +using namespace Newrender; + +void render_drawrooms(vec3_t& position, int sectnum, fixed_t q16angle, fixed_t q16horizon, float rollang, float fov, bool mirror, bool planemirror) +{ + GLInterface.ClearDepth(); + GLInterface.EnableBlend(false); + GLInterface.EnableAlphaTest(false); + GLInterface.EnableDepthTest(true); + GLInterface.SetDepthFunc(DF_LEqual); + GLInterface.SetRenderStyle(LegacyRenderStyles[STYLE_Translucent]); + + + FRotator rotation; + rotation.Yaw = -90.f + q16ang(q16angle).asdeg(); + rotation.Pitch = -HorizToPitch(q16horizon); + rotation.Roll = rollang; + SetProjection(rotation, fov); + SetViewMatrix(rotation, position.x / 16.f, -position.y / 16.f, -position.z / 256.f, mirror, planemirror); + + renderSetViewpoint(position.x / 16.f, -position.y / 16.f, -position.z / 256.f); + renderSetVisibility((1.f / (65536.f * 65536.f)) * g_visibility / r_ambientlight);// MulScale(g_visibility, MulScale(xdimenscale * viewingrangerecip * fviewingrange / r_ambientlight); + renderBeginScene(); + + Clipper clipper; + // fixme: This does not consider the roll angle yet. Pitch disabled to get consistent values during testing. + auto fa = FrustumAngle(16.f / 9, r_fov, 0);// rotation.Pitch.Degrees); + + angle_t rotang = q16ang(q16angle).asbam(); + clipper.SafeAddClipRangeRealAngles(rotang + fa, rotang - fa); + + BunchDrawer drawer(clipper, position.vec2); + + drawer.RenderScene(sectnum); + + renderFinishScene(); + + GLInterface.SetDepthFunc(DF_LEqual); +} + + + diff --git a/source/core/rendering/render.h b/source/core/rendering/render.h new file mode 100644 index 000000000..192b55f98 --- /dev/null +++ b/source/core/rendering/render.h @@ -0,0 +1,68 @@ +#pragma once +#include "build.h" + + +// 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! + +inline double WallStartX(int wallnum) +{ + return wall[wallnum].x * (1 / 16.); +} + +inline double WallStartY(int wallnum) +{ + return wall[wallnum].y * (1 / -16.); +} + +inline double WallEndX(int wallnum) +{ + return wall[wall[wallnum].point2].x * (1 / 16.); +} + +inline double WallEndY(int wallnum) +{ + return wall[wall[wallnum].point2].y * (1 / -16.); +} + +inline double WallStartX(const walltype* wallnum) +{ + return wallnum->x * (1 / 16.); +} + +inline double WallStartY(const walltype* wallnum) +{ + return wallnum->y * (1 / -16.); +} + +inline double WallEndX(const walltype* wallnum) +{ + return wall[wallnum->point2].x * (1 / 16.); +} + +inline double WallEndY(const walltype* wallnum) +{ + return wall[wallnum->point2].y * (1 / -16.); +} + +inline double SpriteX(int wallnum) +{ + return sprite[wallnum].x * (1 / 16.); +} + +inline double SpriteY(int wallnum) +{ + return sprite[wallnum].y * (1 / -16.); +} + +inline double PointOnLineSide(double x, double y, double linex, double liney, double deltax, double deltay) +{ + return (x - linex) * deltay - (y - liney) * deltax; +} + +float CeilingAtPoint(sectortype* sec, float dax, float day); +float FloorAtPoint(usectorptr_t sec, float dax, float day); +void PlanesAtPoint(usectorptr_t sec, float dax, float day, float* ceilz, float* florz); + +void render_drawrooms(vec3_t& position, int sectnum, fixed_t q16angle, fixed_t q16horizon, float rollang, float fov, bool mirror, bool planemirror); + diff --git a/source/core/rendering/scene/hw_clipper.h b/source/core/rendering/scene/hw_clipper.h index 172bf5aac..29c7e322b 100644 --- a/source/core/rendering/scene/hw_clipper.h +++ b/source/core/rendering/scene/hw_clipper.h @@ -32,7 +32,6 @@ class Clipper DVector2 viewpoint; bool blocked = false; - static angle_t AngleToPseudo(angle_t ang); bool IsRangeVisible(angle_t startangle, angle_t endangle); void RemoveRange(ClipNode * cn); void AddClipRange(angle_t startangle, angle_t endangle); @@ -44,6 +43,7 @@ public: Clipper(); void Clear(); + static angle_t AngleToPseudo(angle_t ang); void Free(ClipNode *node) { diff --git a/source/core/rendering/scene/hw_drawstructs.h b/source/core/rendering/scene/hw_drawstructs.h new file mode 100644 index 000000000..e5cecfe11 --- /dev/null +++ b/source/core/rendering/scene/hw_drawstructs.h @@ -0,0 +1,392 @@ +#pragma once +//========================================================================== +// +// One wall segment in the draw list +// +//========================================================================== +#include "renderstyle.h" +#include "textures.h" +#include "fcolormap.h" +#include "build.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +struct HWHorizonInfo; +struct HWSkyInfo; +class FMaterial; +struct FTexCoordInfo; +struct FSectorPortalGroup; +struct FFlatVertex; +struct FLinePortalSpan; +struct FDynLightData; +class VSMatrix; +struct FSpriteModelFrame; +class FRenderState; +struct HWDecal; + +struct HWSectorPlane +{ + FTextureID texture; + //secplane_t plane; + float Texheight; + float Angle; + FVector2 Offs; + FVector2 Scale; + + void GetFromSector(sectortype* sec, int ceiling) + { + /* + Offs.X = (float)sec->GetXOffset(ceiling); + Offs.Y = (float)sec->GetYOffset(ceiling); + Scale.X = (float)sec->GetXScale(ceiling); + Scale.Y = (float)sec->GetYScale(ceiling); + Angle = (float)sec->GetAngle(ceiling).Degrees; + texture = sec->GetTexture(ceiling); + plane = sec->GetSecPlane(ceiling); + Texheight = (float)((ceiling == sector_t::ceiling) ? plane.fD() : -plane.fD()); + */ + } +}; + +enum HWRenderStyle +{ + STYLEHW_Normal, // default + STYLEHW_Solid, // drawn solid (needs special treatment for sprites) + STYLEHW_NoAlphaTest, // disable alpha test +}; + +enum WallTypes +{ + RENDERWALL_NONE, + RENDERWALL_TOP, + RENDERWALL_M1S, + RENDERWALL_M2S, + RENDERWALL_BOTTOM, + RENDERWALL_FOGBOUNDARY, + RENDERWALL_MIRRORSURFACE, + RENDERWALL_M2SNF, + RENDERWALL_COLOR, + // Insert new types at the end! +}; + +enum PortalTypes +{ + PORTALTYPE_SKY, + PORTALTYPE_HORIZON, + PORTALTYPE_SKYBOX, + PORTALTYPE_SECTORSTACK, + PORTALTYPE_PLANEMIRROR, + PORTALTYPE_MIRROR, + PORTALTYPE_LINETOLINE, +}; + +//========================================================================== +// +// One sector plane, still in fixed point +// +//========================================================================== + +struct HWSeg +{ + float x1,x2; + float y1,y2; + float fracleft, fracright; + + FVector3 Normal() const + { + // we do not use the vector math inlines here because they are not optimized for speed but accuracy in the playsim and this is called quite frequently. + float x = y2 - y1; + float y = x1 - x2; + float ilength = 1.f / sqrtf(x*x + y*y); + return FVector3(x * ilength, 0, y * ilength); + } +}; + +struct texcoord +{ + float u,v; +}; + +struct HWDrawInfo; + +class HWWall +{ +public: + static const char passflag[]; + + enum + { + HWF_CLAMPX=1, + HWF_CLAMPY=2, + HWF_SKYHACK=4, + HWF_NOSPLIT=64, + HWF_TRANSLUCENT = 128, + HWF_NOSLICE = 256 + }; + + enum + { + RWF_BLANK = 0, + RWF_TEXTURED = 1, // actually not being used anymore because with buffers it's even less efficient not writing the texture coordinates - but leave it here + RWF_NORENDER = 8, + }; + + enum + { + LOLFT, + UPLFT, + UPRGT, + LORGT, + }; + + friend struct HWDrawList; + friend class HWPortal; + + FGameTexture *texture; + + HWSeg glseg; + float ztop[2],zbottom[2]; + texcoord tcs[4]; + float alpha; + + ERenderStyle RenderStyle; + + float ViewDistance; + float visibility; + short shade, palette, floorpal; + + uint16_t flags; + uint8_t type; + + int dynlightindex; + + /* + union + { + // it's either one of them but never more! + FSectorPortal *secportal; // sector portal (formerly skybox) + HWSkyInfo * sky; // for normal sky + HWHorizonInfo * horizon; // for horizon information + FSectorPortalGroup * portal; // stacked sector portals + secplane_t * planemirror; // for plane mirrors + FLinePortalSpan *lineportal; // line-to-line portals + }; + */ + + + + // these are not the same as ytop and ybottom!!! + float zceil[2]; + float zfloor[2]; + + unsigned int vertindex; + unsigned int vertcount; + +public: + walltype * seg; + sectortype *frontsector, *backsector; +//private: + + void PutWall(HWDrawInfo *di, bool translucent); + void PutPortal(HWDrawInfo *di, int ptype, int plane); + void CheckTexturePosition(); + + void SetupLights(HWDrawInfo *di, FDynLightData &lightdata); + + void MakeVertices(HWDrawInfo *di, bool nosplit); + + void SkyPlane(HWDrawInfo *di, sectortype *sector, int plane, bool allowmirror); + void SkyLine(HWDrawInfo *di, sectortype *sec, walltype *line); + void SkyNormal(HWDrawInfo* di, sectortype* fs, FVector2& v1, FVector2& v2) {} + void SkyTop(HWDrawInfo *di, walltype * seg,sectortype * fs,sectortype * bs, FVector2& v1, FVector2& v2) {} + void SkyBottom(HWDrawInfo *di, walltype * seg,sectortype * fs,sectortype * bs, FVector2& v1, FVector2& v2) {} + + bool DoHorizon(HWDrawInfo *di, walltype * seg,sectortype * fs, DVector2& v1, DVector2& v2); + + bool SetWallCoordinates(walltype* seg, float topleft, float topright, float bottomleft, float bottomright); + + void DoTexture(HWDrawInfo* di, walltype* wal, walltype* refwall, float yref, float topleft, float topright, float bottomleft, float bottomright); + void DoOneSidedTexture(HWDrawInfo *di, walltype * seg, sectortype* frontsector, sectortype* backsector, float topleft,float topright, float bottomleft,float bottomright); + void DoUpperTexture(HWDrawInfo* di, walltype* seg, sectortype* frontsector, sectortype* backsector, float topleft, float topright, float bottomleft, float bottomright); + void DoLowerTexture(HWDrawInfo* di, walltype* seg, sectortype* frontsector, sectortype* backsector, float topleft, float topright, float bottomleft, float bottomright); + + void DoMidTexture(HWDrawInfo *di, walltype * seg, + sectortype * front, sectortype * back, + float fch1, float fch2, float ffh1, float ffh2, + float bch1, float bch2, float bfh1, float bfh2); + + //void ProcessDecal(HWDrawInfo *di, DBaseDecal *decal, const FVector3 &normal); + //void ProcessDecals(HWDrawInfo *di); + + int CreateVertices(FFlatVertex *&ptr, bool nosplit); + + //int CountVertices(); + + void RenderWall(HWDrawInfo *di, FRenderState &state, int textured); + void RenderFogBoundary(HWDrawInfo *di, FRenderState &state); + void RenderMirrorSurface(HWDrawInfo *di, FRenderState &state); + void RenderTexturedWall(HWDrawInfo *di, FRenderState &state, int rflags); + void RenderTranslucentWall(HWDrawInfo *di, FRenderState &state); + +public: + void Process(HWDrawInfo* di, walltype* seg, sectortype* frontsector, sectortype* backsector); + + float PointOnSide(float x,float y) + { + return -((y-glseg.y1)*(glseg.x2-glseg.x1)-(x-glseg.x1)*(glseg.y2-glseg.y1)); + } + + void DrawWall(HWDrawInfo *di, FRenderState &state, bool translucent); + +}; + +//========================================================================== +// +// One flat plane in the draw list +// +//========================================================================== + +class HWFlat +{ +public: + sectortype * sector; + FGameTexture *texture; + + float z; // the z position of the flat (only valid for non-sloped planes) + FColormap Colormap; // light and fog + PalEntry FlatColor; + PalEntry AddColor; + ERenderStyle renderstyle; + + float alpha; + HWSectorPlane plane; + int lightlevel; + bool stack; + bool ceiling; + uint8_t renderflags; + uint8_t hacktype; + int iboindex; + //int vboheight; + + int dynlightindex; + + void CreateSkyboxVertices(FFlatVertex *buffer); + //void SetupLights(HWDrawInfo *di, FLightNode *head, FDynLightData &lightdata, int portalgroup); + + void PutFlat(HWDrawInfo *di, bool fog = false); + void Process(HWDrawInfo *di, sectortype * model, int whichplane, bool notexture); + void ProcessSector(HWDrawInfo *di, sectortype * frontsector, int which = 7 /*SSRF_RENDERALL*/); // cannot use constant due to circular dependencies. + + void DrawSubsectors(HWDrawInfo *di, FRenderState &state); + void DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent); + + void DrawOtherPlanes(HWDrawInfo *di, FRenderState &state); + void DrawFloodPlanes(HWDrawInfo *di, FRenderState &state); +}; + +//========================================================================== +// +// One sprite in the draw list +// +//========================================================================== + + +class HWSprite +{ +public: + int lightlevel; + uint8_t foglevel; + uint8_t hw_styleflags; + bool fullbright; + bool polyoffset; + FColormap Colormap; + FSpriteModelFrame * modelframe; + FRenderStyle RenderStyle; + int OverrideShader; + + int translation; + int index; + float depth; + int vertexindex; + + float topclip; + float bottomclip; + + float x,y,z; // needed for sorting! + + float ul,ur; + float vt,vb; + float x1,y1,z1; + float x2,y2,z2; + float trans; + int dynlightindex; + + FGameTexture *texture; + spritetype * actor; + //TArray *lightlist; + DRotator Angles; + + + void SplitSprite(HWDrawInfo *di, sectortype * frontsector, bool translucent); + void PerformSpriteClipAdjustment(AActor *thing, const DVector2 &thingpos, float spriteheight); + bool CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp); + +public: + + void CreateVertices(HWDrawInfo *di); + void PutSprite(HWDrawInfo *di, bool translucent); + void Process(HWDrawInfo *di, AActor* thing,sectortype * sector, int thruportal = false); + + void DrawSprite(HWDrawInfo *di, FRenderState &state, bool translucent); +}; + + + + +struct DecalVertex +{ + float x, y, z; + float u, v; +}; + +/* +struct HWDecal +{ + FGameTexture *texture; + TArray *lightlist; + DBaseDecal *decal; + DecalVertex dv[4]; + float zcenter; + unsigned int vertindex; + + FRenderStyle renderstyle; + int lightlevel; + int rellight; + float alpha; + FColormap Colormap; + int dynlightindex; + sectortype *frontsector; + FVector3 Normal; + + void DrawDecal(HWDrawInfo *di, FRenderState &state); + +}; +*/ + + +inline float Dist2(float x1,float y1,float x2,float y2) +{ + return sqrtf((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); +} + +bool hw_SetPlaneTextureRotation(const HWSectorPlane * secplane, FGameTexture * gltexture, VSMatrix &mat); +void hw_GetDynModelLight(AActor *self, FDynLightData &modellightdata); + +extern const float LARGE_VALUE; + +struct FDynLightData; +struct FDynamicLight; +bool GetLight(FDynLightData& dld, int group, Plane& p, FDynamicLight* light, bool checkside); +void AddLightToList(FDynLightData &dld, int group, FDynamicLight* light, bool forceAttenuate); diff --git a/source/core/rendering/scene/hw_walls.cpp b/source/core/rendering/scene/hw_walls.cpp new file mode 100644 index 000000000..98e4fc4be --- /dev/null +++ b/source/core/rendering/scene/hw_walls.cpp @@ -0,0 +1,1098 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2000-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include "texturemanager.h" +#include "hw_dynlightdata.h" +#include "hw_material.h" +#include "hw_cvars.h" +#include "hw_clock.h" +//#include "hw_lighting.h" +//#include "hwrenderer/scene/hw_drawinfo.h" +//#include "hwrenderer/scene/hw_drawstructs.h" +//#include "hwrenderer/scene/hw_portal.h" +#include "hw_lightbuffer.h" +#include "hw_renderstate.h" +#include "hw_skydome.h" +#include "hw_drawstructs.h" +#include "render.h" +#include "cmdlib.h" + +#include "v_video.h" +#include "flatvertices.h" +#include "glbackend/glbackend.h" + + +//========================================================================== +// +// General purpose wall rendering function +// everything goes through here +// +//========================================================================== + +void HWWall::RenderWall(HWDrawInfo *di, FRenderState &state, int textured) +{ + assert(vertcount > 0); + state.SetLightIndex(dynlightindex); + state.Draw(DT_TriangleFan, vertindex, vertcount); + vertexcount += vertcount; +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWWall::RenderFogBoundary(HWDrawInfo *di, FRenderState &state) +{ +#if 0 + if (gl_fogmode && !di->isFullbrightScene()) + { + int rel = rellight + getExtraLight(); + state.EnableDrawBufferAttachments(false); + di->SetFog(state, lightlevel, rel, false, &Colormap, false); + state.SetEffect(EFF_FOGBOUNDARY); + state.AlphaFunc(Alpha_GEqual, 0.f); + state.SetDepthBias(-1, -128); + RenderWall(di, state, HWWall::RWF_BLANK); + state.ClearDepthBias(); + state.SetEffect(EFF_NONE); + state.EnableDrawBufferAttachments(true); + } +#endif +} + + +//========================================================================== +// +// +// +//========================================================================== +void HWWall::RenderMirrorSurface(HWDrawInfo *di, FRenderState &state) +{ +#if 0 + if (!TexMan.mirrorTexture.isValid()) return; + + state.SetDepthFunc(DF_LEqual); + + // we use texture coordinates and texture matrix to pass the normal stuff to the shader so that the default vertex buffer format can be used as is. + state.EnableTextureMatrix(true); + + // Use sphere mapping for this + state.SetEffect(EFF_SPHEREMAP); + di->SetColor(state, lightlevel, 0, di->isFullbrightScene(), Colormap, 0.1f); + di->SetFog(state, lightlevel, 0, di->isFullbrightScene(), &Colormap, true); + state.SetRenderStyle(STYLE_Add); + state.AlphaFunc(Alpha_Greater, 0); + + auto tex = TexMan.GetGameTexture(TexMan.mirrorTexture, false); + state.SetMaterial(tex, UF_None, 0, CLAMP_NONE, 0, -1); // do not upscale the mirror texture. + + flags &= ~HWWall::HWF_GLOW; + RenderWall(di, state, HWWall::RWF_BLANK); + + state.EnableTextureMatrix(false); + state.SetEffect(EFF_NONE); + state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold); + + state.SetDepthFunc(DF_Less); + + // This is drawn in the translucent pass which is done after the decal pass + // As a result the decals have to be drawn here, right after the wall they are on, + // because the depth buffer won't get set by translucent items. + if (seg->sidedef->AttachedDecals) + { + DrawDecalsForMirror(di, state, di->Decals[1]); + } + state.SetRenderStyle(STYLE_Translucent); +#endif +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWWall::RenderTexturedWall(HWDrawInfo *di, FRenderState &state, int rflags) +{ +#if 0 + int tmode = state.GetTextureMode(); + + state.SetMaterial(texture, UF_Texture, 0, flags & 3, 0, -1); + + + int32_t size = xs_CRoundToInt(texture->GetDisplayHeight()); + int32_t size2; + for (size2 = 1; size2 < size; size2 += size2) {} + if (size == size2) + state.SetNpotEmulation(0.f, 0.f); + else + { + float xOffset = 1.f / texture->GetDisplayWidth(); + state.SetNpotEmulation((1.f * size2) / size, xOffset); + } + + + float absalpha = fabsf(alpha); + if (type != RENDERWALL_M2SNF) di->SetFog(state, lightlevel, rel, di->isFullbrightScene(), &Colormap, RenderStyle == STYLE_Add); + di->SetColor(state, lightlevel, rel, di->isFullbrightScene(), Colormap, absalpha); + RenderWall(di, state, rflags); + + state.SetNpotEmulation(0.f, 0.f); + state.SetObjectColor(0xffffffff); + state.SetObjectColor2(0); + state.SetAddColor(0); + state.SetTextureMode(tmode); + state.EnableGlow(false); + state.EnableGradient(false); + state.ApplyTextureManipulation(nullptr); +#endif +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWWall::RenderTranslucentWall(HWDrawInfo *di, FRenderState &state) +{ +#if 0 + state.SetRenderStyle(RenderStyle); + if (!texture->GetTranslucency()) state.AlphaFunc(Alpha_GEqual, gl_mask_threshold); + else state.AlphaFunc(Alpha_GEqual, 0.f); + RenderTexturedWall(di, state, HWWall::RWF_TEXTURED); + state.SetRenderStyle(STYLE_Translucent); +#endif +} + +//========================================================================== +// +// +// +//========================================================================== +void HWWall::DrawWall(HWDrawInfo *di, FRenderState &state, bool translucent) +{ + /* + if (screen->BuffersArePersistent()) + { + if (di->Level->HasDynamicLights && !di->isFullbrightScene() && texture != nullptr) + { + SetupLights(di, lightdata); + } + MakeVertices(di, !!(flags & HWWall::HWF_TRANSLUCENT)); + } + */ + + state.SetNormal(glseg.Normal()); + if (!translucent) + { + RenderTexturedWall(di, state, HWWall::RWF_TEXTURED); + } + else + { + switch (type) + { + case RENDERWALL_MIRRORSURFACE: + RenderMirrorSurface(di, state); + break; + + case RENDERWALL_FOGBOUNDARY: + RenderFogBoundary(di, state); + break; + + default: + RenderTranslucentWall(di, state); + break; + } + } +} + +//========================================================================== +// +// Collect lights for shader +// +//========================================================================== +#if 0 +void HWWall::SetupLights(HWDrawInfo *di, FDynLightData &lightdata) +{ + lightdata.Clear(); + + if (RenderStyle == STYLE_Add && !di->Level->lightadditivesurfaces) return; // no lights on additively blended surfaces. + + // check for wall types which cannot have dynamic lights on them (portal types never get here so they don't need to be checked.) + switch (type) + { + case RENDERWALL_FOGBOUNDARY: + case RENDERWALL_MIRRORSURFACE: + case RENDERWALL_COLOR: + return; + } + + float vtx[]={glseg.x1,zbottom[0],glseg.y1, glseg.x1,ztop[0],glseg.y1, glseg.x2,ztop[1],glseg.y2, glseg.x2,zbottom[1],glseg.y2}; + Plane p; + + + auto normal = glseg.Normal(); + p.Set(normal, -normal.X * glseg.x1 - normal.Z * glseg.y1); + + FLightNode *node; + if (seg->sidedef == NULL) + { + node = NULL; + } + else if (!(seg->sidedef->Flags & WALLF_POLYOBJ)) + { + node = seg->sidedef->lighthead; + } + else if (sub) + { + // Polobject segs cannot be checked per sidedef so use the subsector instead. + node = sub->section->lighthead; + } + else node = NULL; + + // Iterate through all dynamic lights which touch this wall and render them + while (node) + { + if (node->lightsource->IsActive()) + { + iter_dlight++; + + DVector3 posrel = node->lightsource->PosRelative(seg->frontsector->PortalGroup); + float x = posrel.X; + float y = posrel.Y; + float z = posrel.Z; + float dist = fabsf(p.DistToPoint(x, z, y)); + float radius = node->lightsource->GetRadius(); + float scale = 1.0f / ((2.f * radius) - dist); + FVector3 fn, pos; + + if (radius > 0.f && dist < radius) + { + FVector3 nearPt, up, right; + + pos = { x, z, y }; + fn = p.Normal(); + + fn.GetRightUp(right, up); + + FVector3 tmpVec = fn * dist; + nearPt = pos + tmpVec; + + FVector3 t1; + int outcnt[4]={0,0,0,0}; + texcoord tcs[4]; + + // do a quick check whether the light touches this polygon + for(int i=0;i<4;i++) + { + t1 = FVector3(&vtx[i*3]); + FVector3 nearToVert = t1 - nearPt; + tcs[i].u = ((nearToVert | right) * scale) + 0.5f; + tcs[i].v = ((nearToVert | up) * scale) + 0.5f; + + if (tcs[i].u<0) outcnt[0]++; + if (tcs[i].u>1) outcnt[1]++; + if (tcs[i].v<0) outcnt[2]++; + if (tcs[i].v>1) outcnt[3]++; + + } + if (outcnt[0]!=4 && outcnt[1]!=4 && outcnt[2]!=4 && outcnt[3]!=4) + { + draw_dlight += GetLight(lightdata, seg->frontsector->PortalGroup, p, node->lightsource, true); + } + } + } + node = node->nextLight; + } + dynlightindex = screen->mLights->UploadLights(lightdata); +} +#endif + +//========================================================================== +// +// +// +//========================================================================== +void HWWall::PutWall(HWDrawInfo *di, bool translucent) +{ + if (translucent || (texture && texture->GetTranslucency() && type == RENDERWALL_M2S)) + { + flags |= HWF_TRANSLUCENT; + //ViewDistance = (di->Viewpoint.Pos - (seg->linedef->v1->fPos() + seg->linedef->Delta() / 2)).XY().LengthSquared(); + } + + /* + if (!screen->BuffersArePersistent()) + { + if (di->Level->HasDynamicLights && !di->isFullbrightScene() && texture != nullptr) + { + SetupLights(di, lightdata); + } + MakeVertices(di, translucent); + } + + bool hasDecals = type != RENDERWALL_M2S && seg->sidedef->AttachedDecals; + if (hasDecals) + { + // If we want to use the light infos for the decal we cannot delay the creation until the render pass. + if (screen->BuffersArePersistent()) + { + if (di->Level->HasDynamicLights && !di->isFullbrightScene() && texture != nullptr) + { + SetupLights(di, lightdata); + } + } + ProcessDecals(di); + } + */ + +#if 0 + di->AddWall(this); +#else + // route it through the GLInterface for now for easier testing + int palid = TRANSLATION(Translation_Remap + curbasepal, globalpal); + GLInterface.SetFade(globalfloorpal); + GLInterface.SetTexture(texture, TRANSLATION(Translation_Remap + curbasepal, palette), 0); + + GLInterface.SetShade(shade, numshades); + + int h = texture->GetTexelHeight(); + int h2 = 1 << sizeToBits(h); + if (h != h2) + { + float xOffset = 1.f / texture->GetTexelWidth(); + GLInterface.SetNpotEmulation(float(h2) / h, xOffset); + } + else + { + GLInterface.SetNpotEmulation(0.f, 0.f); + } + GLInterface.SetTextureMode(TM_OPAQUE); + GLInterface.SetVisibility(visibility); + + // The shade rgb from the tint is ignored here. + float pc[4]; + pc[0] = (float)globalr * (1.f / 255.f); + pc[1] = (float)globalg * (1.f / 255.f); + pc[2] = (float)globalb * (1.f / 255.f); + pc[3] = alpha; + GLInterface.SetColor(pc[0], pc[1], pc[2], pc[3]); + + auto data = screen->mVertexData->AllocVertices(4); + auto vt = data.first; + vt[0].SetTexCoord(tcs[LOLFT].u, tcs[LOLFT].v); + vt[0].SetVertex(glseg.x1, zbottom[0], glseg.y1); + vt[1].SetTexCoord(tcs[UPLFT].u, tcs[UPLFT].v); + vt[1].SetVertex(glseg.x1, ztop[0], glseg.y1); + vt[2].SetTexCoord(tcs[UPRGT].u, tcs[UPRGT].v); + vt[2].SetVertex(glseg.x2, ztop[1], glseg.y2); + vt[3].SetTexCoord(tcs[LORGT].u, tcs[LORGT].v); + vt[3].SetVertex(glseg.x2, zbottom[1], glseg.y2); + + GLInterface.Draw(DT_TriangleFan, data.second, 4); + + GLInterface.SetNpotEmulation(0.f, 0.f); + GLInterface.SetTextureMode(TM_NORMAL); + +#endif + // make sure that following parts of the same linedef do not get this one's vertex and lighting info. + vertcount = 0; + dynlightindex = -1; + flags &= ~HWF_TRANSLUCENT; +} + +//========================================================================== +// +// will be done later. +// +//========================================================================== + +void HWWall::PutPortal(HWDrawInfo *di, int ptype, int plane) +{ +#if 0 + HWPortal * portal = nullptr; + + MakeVertices(di, false); + switch (ptype) + { + // portals don't go into the draw list. + // Instead they are added to the portal manager + case PORTALTYPE_HORIZON: + horizon = portalState.UniqueHorizons.Get(horizon); + portal = di->FindPortal(horizon); + if (!portal) + { + portal = new HWHorizonPortal(&portalState, horizon, di->Viewpoint); + di->Portals.Push(portal); + } + portal->AddLine(this); + break; + + case PORTALTYPE_SKYBOX: + portal = di->FindPortal(secportal); + if (!portal) + { + // either a regular skybox or an Eternity-style horizon + if (secportal->mType != PORTS_SKYVIEWPOINT) portal = new HWEEHorizonPortal(&portalState, secportal); + else + { + portal = new HWSkyboxPortal(&portalState, secportal); + di->Portals.Push(portal); + } + } + portal->AddLine(this); + break; + + case PORTALTYPE_SECTORSTACK: + portal = di->FindPortal(this->portal); + if (!portal) + { + portal = new HWSectorStackPortal(&portalState, this->portal); + di->Portals.Push(portal); + } + portal->AddLine(this); + break; + + case PORTALTYPE_PLANEMIRROR: + if (portalState.PlaneMirrorMode * planemirror->fC() <= 0) + { + planemirror = portalState.UniquePlaneMirrors.Get(planemirror); + portal = di->FindPortal(planemirror); + if (!portal) + { + portal = new HWPlaneMirrorPortal(&portalState, planemirror); + di->Portals.Push(portal); + } + portal->AddLine(this); + } + break; + + case PORTALTYPE_MIRROR: + portal = di->FindPortal(seg->linedef); + if (!portal) + { + portal = new HWMirrorPortal(&portalState, seg->linedef); + di->Portals.Push(portal); + } + portal->AddLine(this); + if (gl_mirror_envmap) + { + // draw a reflective layer over the mirror + di->AddMirrorSurface(this); + } + break; + + case PORTALTYPE_LINETOLINE: + if (!lineportal) + return; + portal = di->FindPortal(lineportal); + if (!portal) + { + line_t *otherside = lineportal->lines[0]->mDestination; + if (otherside != nullptr && otherside->portalindex < di->Level->linePortals.Size()) + { + di->ProcessActorsInPortal(otherside->getPortal()->mGroup, di->in_area); + } + portal = new HWLineToLinePortal(&portalState, lineportal); + di->Portals.Push(portal); + } + portal->AddLine(this); + break; + + case PORTALTYPE_SKY: + sky = portalState.UniqueSkies.Get(sky); + portal = di->FindPortal(sky); + if (!portal) + { + portal = new HWSkyPortal(screen->mSkyData, &portalState, sky); + di->Portals.Push(portal); + } + portal->AddLine(this); + break; + } + vertcount = 0; + + if (plane != -1 && portal) + { + portal->planesused |= (1<GetPlaneTexZ(sector_t::ceiling); + zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor); + + auto vpz = di->Viewpoint.Pos.Z; + if (vpz < fs->GetPlaneTexZ(sector_t::ceiling)) + { + if (vpz > fs->GetPlaneTexZ(sector_t::floor)) + zbottom[1] = zbottom[0] = vpz; + + if (fs->GetTexture(sector_t::ceiling) == skyflatnum) + { + SkyPlane(di, fs, sector_t::ceiling, false); + } + else + { + hi.plane.GetFromSector(fs, sector_t::ceiling); + hi.lightlevel = hw_ClampLight(fs->GetCeilingLight()); + hi.colormap = fs->Colormap; + hi.specialcolor = fs->SpecialColors[sector_t::ceiling]; + + if (di->isFullbrightScene()) hi.colormap.Clear(); + horizon = &hi; + PutPortal(di, PORTALTYPE_HORIZON, -1); + } + ztop[1] = ztop[0] = zbottom[0]; + } + + if (vpz > fs->GetPlaneTexZ(sector_t::floor)) + { + zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor); + if (fs->GetTexture(sector_t::floor) == skyflatnum) + { + SkyPlane(di, fs, sector_t::floor, false); + } + else + { + hi.plane.GetFromSector(fs, sector_t::floor); + hi.lightlevel = hw_ClampLight(fs->GetFloorLight()); + hi.colormap = fs->Colormap; + hi.specialcolor = fs->SpecialColors[sector_t::floor]; + + horizon = &hi; + PutPortal(di, PORTALTYPE_HORIZON, -1); + } + } +#endif + return true; +} + +static float sectorVisibility(sectortype *sec) +{ + // Beware of wraparound madness... + int v = sec->visibility; + return v ? ((uint8_t)(v + 16)) / 16.f : 1.f; +} + +//========================================================================== +// +// 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); +} + +//========================================================================== +// +// +// +//========================================================================== + +bool HWWall::SetWallCoordinates(walltype * seg, float topleft, float topright, float bottomleft, float bottomright) +{ + // + // + // set up coordinates for the left side of the polygon + // + // check left side for intersections + if (topleft >= bottomleft) + { + // normal case + ztop[0] = topleft; + zbottom[0] = bottomleft; + } + else + { + // ceiling below floor - clip to the visible part of the wall + float dch = topright - topleft; + float dfh = bottomright - bottomleft; + float inter_x = (bottomleft - topleft) / (dch - dfh); + float inter_y = topleft + inter_x * dch; + + glseg.x1 = glseg.x1 + inter_x * (glseg.x2 - glseg.x1); + glseg.y1 = glseg.y1 + inter_x * (glseg.y2 - glseg.y1); + glseg.fracleft = inter_x; + + zbottom[0] = ztop[0] = inter_y; + } + + // + // + // set up coordinates for the right side of the polygon + // + // check left side for intersections + if (topright >= bottomright) + { + // normal case + ztop[1] = topright; + zbottom[1] = bottomright; + } + else + { + // ceiling below floor - clip to the visible part of the wall + float dch = topright - topleft; + float dfh = bottomright - bottomleft; + float inter_x = (bottomleft - topleft) / (dch - dfh); + + float inter_y = topleft + inter_x * dch; + + glseg.x2 = glseg.x1 + inter_x * (glseg.x2 - glseg.x1); + glseg.y2 = glseg.y1 + inter_x * (glseg.y2 - glseg.y1); + glseg.fracright = inter_x; + + zbottom[1] = ztop[1] = inter_y; + } + + return true; +} + +//========================================================================== +// +// Do some tweaks with the texture coordinates to reduce visual glitches +// +//========================================================================== + +void HWWall::CheckTexturePosition() +{ + float sub; + + if (texture->isHardwareCanvas()) return; + + // 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[UPRGT].v) + { + sub = float(xs_FloorToInt(tcs[UPLFT].v)); + } + else + { + sub = float(xs_FloorToInt(tcs[UPRGT].v)); + } + tcs[UPLFT].v -= sub; + tcs[UPRGT].v -= sub; + tcs[LOLFT].v -= sub; + tcs[LORGT].v -= sub; + + if ((tcs[UPLFT].v == 0.f && tcs[UPRGT].v == 0.f && tcs[LOLFT].v <= 1.f && tcs[LORGT].v <= 1.f) || + (tcs[UPLFT].v >= 0.f && tcs[UPRGT].v >= 0.f && tcs[LOLFT].v == 1.f && tcs[LORGT].v == 1.f)) + { + flags |= HWF_CLAMPY; + } + } + else + { + if (tcs[LOLFT].v < tcs[LORGT].v) + { + sub = float(xs_FloorToInt(tcs[LOLFT].v)); + } + else + { + sub = float(xs_FloorToInt(tcs[LORGT].v)); + } + tcs[UPLFT].v -= sub; + tcs[UPRGT].v -= sub; + tcs[LOLFT].v -= sub; + tcs[LORGT].v -= sub; + + if ((tcs[LOLFT].v == 0.f && tcs[LORGT].v == 0.f && tcs[UPLFT].v <= 1.f && tcs[UPRGT].v <= 1.f) || + (tcs[LOLFT].v >= 0.f && tcs[LORGT].v >= 0.f && tcs[UPLFT].v == 1.f && tcs[UPRGT].v == 1.f)) + { + flags |= HWF_CLAMPY; + } + } +} + + +//========================================================================== +// +// Common part of wall drawers +// +//========================================================================== + +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); + float leftdist = xflipped ? 1.f - glseg.fracleft : glseg.fracleft; + float rightdist = xflipped ? 1.f - glseg.fracright : glseg.fracright; + + 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; + + auto setv = [=](float hl, float hr, float frac) -> float + { + float h = hl + (hr - hl) * frac; + h = ((float)(refheight - (h * 256)) / ((th * 2048.0f) / (float)(wal->yrepeat))) + ypanning; + if (wal->cstat & CSTAT_WALL_YFLIP) h = 1.f - h; + return h; + }; + + tcs[UPLFT].v = setv(topleft, topright, glseg.fracleft); + 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(); + bool trans = type == RENDERWALL_M2S && (wal->cstat & CSTAT_WALL_TRANSLUCENT); + if (trans) + { + SetRenderStyleFromBlend(!!(wal->cstat & CSTAT_WALL_TRANSLUCENT), 0, !!(wal->cstat & CSTAT_WALL_TRANS_FLIP)); + alpha = GetAlphaFromBlend((wal->cstat & CSTAT_WALL_TRANS_FLIP) ? DAMETH_TRANS2 : DAMETH_TRANS1, 0); + } + PutWall(di, trans); + glseg = glsave; +} + +//========================================================================== +// +// Handle one sided walls +// +//========================================================================== + +void HWWall::DoOneSidedTexture(HWDrawInfo* di, walltype* wal, sectortype* frontsector, sectortype* backsector, + float topleft, float topright, float bottomleft, float bottomright) +{ + // get the alignment reference position. + int refheight; + + if ((wal->cstat & CSTAT_WALL_1WAY) && backsector) + { + if ((!(wal->cstat & CSTAT_WALL_BOTTOM_SWAP) && (wal->cstat & CSTAT_WALL_1WAY)) || + ((wal->cstat & CSTAT_WALL_BOTTOM_SWAP) && (wall[wal->nextwall].cstat & CSTAT_WALL_ALIGN_BOTTOM))) + refheight = frontsector->ceilingz; + else + refheight = backsector->floorz; + } + else + { + refheight = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->floorz : frontsector->ceilingz; + } + + type = RENDERWALL_M1S; + DoTexture(di, wal, wal, refheight, topleft, topright, bottomleft, bottomright); +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWWall::DoUpperTexture(HWDrawInfo* di, walltype* wal, sectortype* frontsector, sectortype* backsector, + float topleft, float topright, float bottomleft, float bottomright) +{ + // get the alignment reference position. + int refheight = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->floorz : frontsector->ceilingz; + + type = RENDERWALL_BOTTOM; + DoTexture(di, wal, wal, refheight, topleft, topright, bottomleft, bottomright); +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWWall::DoLowerTexture(HWDrawInfo* di, walltype* wal, sectortype* frontsector, sectortype* backsector, + float topleft, float topright, float bottomleft, float bottomright) +{ + // get the alignment reference position. + int refheight; + auto refwall = (wall->cstat & CSTAT_WALL_BOTTOM_SWAP) ? &wall[wal->nextwall] : wal; + + if ((wal->cstat & CSTAT_WALL_1WAY) && backsector) + { + if ((!(wal->cstat & CSTAT_WALL_BOTTOM_SWAP) && (wal->cstat & CSTAT_WALL_1WAY)) || + ((wal->cstat & CSTAT_WALL_BOTTOM_SWAP) && (wall[wal->nextwall].cstat & CSTAT_WALL_ALIGN_BOTTOM))) + refheight = frontsector->ceilingz; + else + refheight = backsector->floorz; + } + else + { + refheight = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->floorz : frontsector->ceilingz; + } + + shade = refwall->shade; + palette = refwall->pal; + type = RENDERWALL_BOTTOM; + DoTexture(di, wal, refwall, refheight, topleft, topright, bottomleft, bottomright); +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWWall::DoMidTexture(HWDrawInfo* di, walltype* wal, + sectortype* front, sectortype* back, + float fch1, float fch2, float ffh1, float ffh2, + float bch1, float bch2, float bfh1, float bfh2) +{ + float topleft,bottomleft,topright,bottomright; + float refheight = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->ceilingz : backsector->ceilingz; + + topleft = std::min(bch1,fch1); + topright = std::min(bch2,fch2); + bottomleft = std::max(bfh1,ffh1); + bottomright = std::max(bfh2,ffh2); + if (topleft<=bottomleft && topright<=bottomright) return; + type = seg->cstat & CSTAT_WALL_1WAY ? RENDERWALL_M1S : RENDERWALL_M2S; + + // todo: transparency. + + DoTexture(di, wal, wal, refheight, topleft, topright, bottomleft, bottomright); + RenderStyle = STYLE_Normal; + alpha = 1.f; +} + + +//========================================================================== +// +// +// +//========================================================================== +void HWWall::Process(HWDrawInfo *di, walltype *wal, sectortype* frontsector, sectortype* backsector) +{ + auto backwall = wall->nextwall >= 0 && wall->nextwall < numwalls? &wall[wal->nextwall] : nullptr; + auto p2wall = &wall[wal->point2]; + + float fch1; + float ffh1; + float fch2; + float ffh2; + + FVector2 v1(WallStartX(wal), WallStartY(wal)); + FVector2 v2(WallEndX(wal), WallEndY(wal)); + + PlanesAtPoint(frontsector, wal->x, wal->y, &fch1, &ffh1); + PlanesAtPoint(frontsector, p2wall->x, p2wall->y, &fch2, &ffh2); + +#ifdef _DEBUG + if (wal - wall == 14454) + { + int a = 0; + } + if (fabs(v1.X) == 400 && fabs(v1.Y) == 3968) + { + int a = 0; + } +#endif + + // note: we always have a valid sidedef and linedef reference when getting here. + + this->seg = wal; + this->frontsector = frontsector; + this->backsector = backsector; + vertindex = 0; + vertcount = 0; + + //vertexes[0] = v1; + //vertexes[1] = v2; + + glseg.x1 = v1.X; + glseg.y1 = v1.Y; + glseg.x2 = v2.X; + glseg.y2 = v2.Y; + glseg.fracleft = 0; + glseg.fracright = 1; + flags = 0; + dynlightindex = -1; + shade = wal->shade; + palette = wal->pal; + floorpal = frontsector->floorpal; + visibility = sectorVisibility(frontsector); + + alpha = 1.0f; + RenderStyle = STYLE_Normal; + texture = NULL; + + /* + if (wal->linedef->special == Line_Horizon) + { + SkyNormal(di, frontsector, v1, v2); + DoHorizon(di, wal, frontsector, v1, v2); + return; + } + */ + + bool isportal = false;// wal->linedef->isVisualPortal() && wal->sidedef == wal->linedef->sidedef[0]; + + if (!backsector || !backwall) + { + // sector's sky + SkyNormal(di, frontsector, v1, v2); + + /* + if (isportal) + { + lineportal = wal->linedef->getPortal()->mGroup; + ztop[0] = zceil[0]; + ztop[1] = zceil[1]; + zbottom[0] = zfloor[0]; + zbottom[1] = zfloor[1]; + PutPortal(di, PORTALTYPE_LINETOLINE, -1); + } + else + */ + { + // normal texture + + int tilenum = (wal->cstat & CSTAT_WALL_1WAY) ? wal->overpicnum : wal->picnum; + tileUpdatePicnum(&tilenum, int(wal-wall) + 16384, wall->cstat); + texture = tileGetTexture(tilenum); + if (texture && texture->isValid()) + { + DoOneSidedTexture(di, wal, frontsector, backsector, fch1, fch2, ffh1, ffh2); + } + } + } + else // two sided + { + float bfh1; + float bfh2; + float bch1; + float bch2; + PlanesAtPoint(backsector, wal->x, wal->y, &bch1, &bfh1); + PlanesAtPoint(backsector, p2wall->x, p2wall->y, &bch2, &bfh2); + + float zalign = 0.f; + + SkyTop(di, wal, frontsector, backsector, v1, v2); + SkyBottom(di, wal, frontsector, backsector, v1, v2); + + // upper texture + if (!(frontsector->ceilingstat & backsector->ceilingstat & CSTAT_SECTOR_SKY)) + { + float bch1a = bch1; + float bch2a = bch2; + if (ffh1 > bch1 && ffh2 > bch2) + { + // the back sector's floor obstructs part of this wall + bch2a = ffh2; + bch1a = ffh1; + } + + if (bch1a < fch1 || bch2a < fch2) + { + int tilenum = wal->picnum; + tileUpdatePicnum(&tilenum, int(wal - wall) + 16384, wall->cstat); + texture = tileGetTexture(tilenum); + if (texture && texture->isValid()) + { + DoUpperTexture(di, wal, frontsector, backsector, fch1, fch2, bch1a, bch2a); + } + } + } + + /* + if (isportal) + { + lineportal = wal->linedef->getPortal()->mGroup; + ztop[0] = bch1; + ztop[1] = bch2; + zbottom[0] = bfh1; + zbottom[1] = bfh2; + PutPortal(di, PORTALTYPE_LINETOLINE, -1); + } + else*/ + + if (wall->cstat & (CSTAT_WALL_MASKED | CSTAT_WALL_1WAY)) + { + int tilenum = wal->overpicnum; + tileUpdatePicnum(&tilenum, int(wal - wall) + 16384, wall->cstat); + texture = tileGetTexture(tilenum); + if (texture && texture->isValid()) + { + DoMidTexture(di, wal, frontsector, backsector, fch1, fch2, ffh1, ffh2, bch1, bch2, bfh1, bfh2); + } + } + + // lower texture + if (!(frontsector->floorstat & backsector->floorstat & CSTAT_SECTOR_SKY)) + { + if (fch1 < bfh1 && fch2 < bfh2) + { + // the back sector's ceiling obstructs part of this wall. + bfh1 = fch1; + bfh2 = fch2; + } + + if (bfh1 > ffh1 || bfh2 > ffh2) + { + auto w = (wal->cstat & CSTAT_WALL_BOTTOM_SWAP) ? backwall : wal; + int tilenum = w->picnum; + tileUpdatePicnum(&tilenum, int(wal - wall) + 16384, w->cstat); + texture = tileGetTexture(tilenum); + if (texture && texture->isValid()) + { + DoLowerTexture(di, wal, frontsector, backsector, bfh1, bfh2, ffh1, ffh2); + } + } + } + } +} + diff --git a/source/glbackend/glbackend.cpp b/source/glbackend/glbackend.cpp index 051297a8b..bf3667eee 100644 --- a/source/glbackend/glbackend.cpp +++ b/source/glbackend/glbackend.cpp @@ -389,6 +389,11 @@ void renderSetVisibility(float vis) vp.mGlobVis = vis; } +void renderSetViewpoint(float x, float y, float z) +{ + vp.mCameraPos = {x, y, z, 0}; +} + void renderBeginScene() { assert(BufferLock == 0); diff --git a/source/glbackend/glbackend.h b/source/glbackend/glbackend.h index 1a74145e7..44a41cbdd 100644 --- a/source/glbackend/glbackend.h +++ b/source/glbackend/glbackend.h @@ -345,6 +345,7 @@ extern F2DDrawer twodpsp; void renderSetProjectionMatrix(const float* p); void renderSetViewMatrix(const float* p); void renderSetVisibility(float v); +void renderSetViewpoint(float x, float y, float z); void renderBeginScene(); void renderFinishScene(); void DrawRateStuff();