diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 56b75a7ec6..3c43f2ba47 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -979,6 +979,7 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE p_xlat.cpp parsecontext.cpp po_man.cpp + portal.cpp r_swrenderer.cpp r_utility.cpp r_3dfloors.cpp diff --git a/src/actionspecials.h b/src/actionspecials.h index b706c0e4d8..a9813c24f3 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -138,6 +138,9 @@ DEFINE_SPECIAL(Thing_SpawnFacing, 139, 2, 4, 4) DEFINE_SPECIAL(Sector_ChangeSound, 140, 2, 2, 2) DEFINE_SPECIAL(Teleport_NoStop, 154, 2, 3, 3) +// portal specials +DEFINE_SPECIAL(Line_SetVisualPortal, 155, -1, -1, 3) +DEFINE_SPECIAL(Line_SetPortal, 156, -1, -1, 3) // GZDoom/Vavoom specials // Although ZDoom doesn't support them it's better to have them defined so that // WADs using them somewhere can at least be started without aborting due diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 8b0c3853c5..e53b8dec6d 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -75,6 +75,7 @@ #include "actorptrselect.h" #include "farchive.h" #include "decallib.h" +#include "portal.h" #include "p_terrain.h" #include "version.h" #include "p_effect.h" @@ -8298,6 +8299,9 @@ scriptwait: line->args[4] = STACK(1); DPrintf("Set special on line %d (id %d) to %d(%d,%d,%d,%d,%d)\n", linenum, STACK(7), specnum, arg0, STACK(4), STACK(3), STACK(2), STACK(1)); + + // [ZZ] re-link with portals (in case this was something related to portal specials) + P_CheckPortal(line); } sp -= 7; } diff --git a/src/p_buildmap.cpp b/src/p_buildmap.cpp index a7a26e9c82..e72d87d06f 100644 --- a/src/p_buildmap.cpp +++ b/src/p_buildmap.cpp @@ -17,6 +17,7 @@ #include "g_level.h" #include "r_data/colormaps.h" #include "gi.h" +#include "portal.h" // MACROS ------------------------------------------------------------------ @@ -629,6 +630,11 @@ static void LoadWalls (walltype *walls, int numwalls, sectortype *bsec) } } + // [ZZ] set initial line portal link + // (even though this is rather hard to happen... build doesn't have portals in our sense and it's walls don't get translated into anything like this) + for (int i = 0; i < numlines; i++) + P_CheckPortal(&lines[i]); + // Finish setting sector properties that depend on walls for (i = 0; i < numsectors; ++i, ++bsec) { diff --git a/src/p_setup.cpp b/src/p_setup.cpp index f5de28c8aa..82e4a88ba8 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -68,6 +68,7 @@ #include "po_man.h" #include "r_renderer.h" #include "r_data/colormaps.h" +#include "portal.h" #include "fragglescript/t_fs.h" @@ -1959,6 +1960,11 @@ void P_SetLineID (int i, line_t *ld) case Static_Init: if (ld->args[1] == Init_SectorLink) setid = ld->args[0]; break; + + case Line_SetPortal: + case Line_SetVisualPortal: + setid = ld->args[1]; // 0 = target id, 1 = this id, 2 = plane anchor + break; } if (setid != -1) { @@ -2059,6 +2065,9 @@ void P_FinishLoadingLineDef(line_t *ld, int alpha) ld->special = 0; break; } + + // [ZZ] check initial portal link + P_CheckPortal(ld); } // killough 4/4/98: delay using sidedefs until they are loaded void P_FinishLoadingLineDefs () diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 9cfcbc0c25..366a56dcf6 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -47,6 +47,7 @@ #include "r_data/colormaps.h" #include "w_wad.h" #include "p_tags.h" +#include "portal.h" #include "p_terrain.h" //=========================================================================== diff --git a/src/portal.cpp b/src/portal.cpp new file mode 100644 index 0000000000..7e8405d570 --- /dev/null +++ b/src/portal.cpp @@ -0,0 +1,368 @@ +#include "portal.h" +#include "p_local.h" +#include "p_lnspec.h" +#include "r_bsp.h" +#include "r_segs.h" +#include "c_cvars.h" +#include "m_bbox.h" +#include "p_tags.h" + +// simulation recurions maximum +CVAR(Int, sv_portal_recursions, 4, CVAR_ARCHIVE|CVAR_SERVERINFO) + +PortalDrawseg* CurrentPortal = NULL; +int CurrentPortalUniq = 0; +bool CurrentPortalInSkybox = false; + +// [ZZ] lots of floats here to avoid overflowing a lot +bool R_IntersectLines(fixed_t o1x, fixed_t o1y, fixed_t p1x, fixed_t p1y, + fixed_t o2x, fixed_t o2y, fixed_t p2x, fixed_t p2y, + fixed_t& rx, fixed_t& ry) +{ + float xx = FIXED2FLOAT(o2x) - FIXED2FLOAT(o1x); + float xy = FIXED2FLOAT(o2y) - FIXED2FLOAT(o1y); + + float d1x = FIXED2FLOAT(p1x) - FIXED2FLOAT(o1x); + float d1y = FIXED2FLOAT(p1y) - FIXED2FLOAT(o1y); + + if (d1x > d1y) + { + d1y = d1y / d1x * 32767.0f; + d1x = 32767.0; + } + else + { + d1x = d1x / d1y * 32767.0f; + d1y = 32767.0; + } + + float d2x = FIXED2FLOAT(p2x) - FIXED2FLOAT(o2x); + float d2y = FIXED2FLOAT(p2y) - FIXED2FLOAT(o2y); + + float cross = d1x*d2y - d1y*d2x; + if (fabs(cross) < 1e-8) + return false; + + float t1 = (xx * d2y - xy * d2x)/cross; + rx = o1x + FLOAT2FIXED(d1x * t1); + ry = o1y + FLOAT2FIXED(d1y * t1); + return true; +} + +inline int P_PointOnLineSideExplicit (fixed_t x, fixed_t y, fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) +{ + return DMulScale32 (y-y1, x2-x1, x1-x, y2-y1) > 0; +} + +bool P_ClipLineToPortal(line_t* line, line_t* portal, fixed_t viewx, fixed_t viewy, bool partial, bool samebehind) +{ + // check if this line is between portal and the viewer. clip away if it is. + bool behind1 = !!P_PointOnLineSide(line->v1->x, line->v1->y, portal); + bool behind2 = !!P_PointOnLineSide(line->v2->x, line->v2->y, portal); + + // [ZZ] update 16.12.2014: if a vertex equals to one of portal's vertices, it's treated as being behind the portal. + // this is required in order to clip away diagonal lines around the portal (example: 1-sided triangle shape with a mirror on it's side) + if ((line->v1->x == portal->v1->x && line->v1->y == portal->v1->y) || + (line->v1->x == portal->v2->x && line->v1->y == portal->v2->y)) + behind1 = samebehind; + if ((line->v2->x == portal->v1->x && line->v2->y == portal->v1->y) || + (line->v2->x == portal->v2->x && line->v2->y == portal->v2->y)) + behind2 = samebehind; + + if (behind1 && behind2) + { + // line is behind the portal plane. now check if it's in front of two view plane borders (i.e. if it will get in the way of rendering) + fixed_t dummyx, dummyy; + bool infront1 = R_IntersectLines(line->v1->x, line->v1->y, line->v2->x, line->v2->y, viewx, viewy, portal->v1->x, portal->v1->y, dummyx, dummyy); + bool infront2 = R_IntersectLines(line->v1->x, line->v1->y, line->v2->x, line->v2->y, viewx, viewy, portal->v2->x, portal->v2->y, dummyx, dummyy); + if (infront1 && infront2) + return true; + } + + return false; +} + +bool P_CheckPortal(line_t* line) +{ + if (line->special == Line_Mirror) + { + line->portal = true; + line->portal_mirror = true; + line->portal_passive = true; + line->portal_dst = line; + } + else if (line->special == Line_SetPortal || + line->special == Line_SetVisualPortal) + { + // portal destination is special argument #0 + line_t* dst = NULL; + + if (line->args[0] > 0) + { + int linenum = -1; + + for (int i = 0; i < numlines; i++) + { + if (&lines[i] == line) + continue; + if (tagManager.LineHasID(&lines[i], line->args[0])) + { + dst = &lines[i]; + break; + } + } + } + + if (dst) + { + line->portal = true; + line->portal_mirror = false; + line->portal_passive = (line->special == Line_SetVisualPortal); + line->portal_dst = dst; + } + else + { + line->portal = false; + line->portal_mirror = false; + line->portal_passive = false; + line->portal_dst = NULL; + } + } + else + { + line->portal = false; + line->portal_mirror = false; + line->portal_passive = false; + line->portal_dst = NULL; + } + + return (line->portal); +} + +void P_TranslatePortalXY(line_t* src, line_t* dst, fixed_t& x, fixed_t& y) +{ + if (!src || !dst) + return; + + fixed_t nposx, nposy; // offsets from line + + // Get the angle between the two linedefs, for rotating + // orientation and velocity. Rotate 180 degrees, and flip + // the position across the exit linedef, if reversed. + angle_t angle = + R_PointToAngle2(0, 0, dst->dx, dst->dy) - + R_PointToAngle2(0, 0, src->dx, src->dy); + + angle += ANGLE_180; + + // Sine, cosine of angle adjustment + fixed_t s = finesine[angle>>ANGLETOFINESHIFT]; + fixed_t c = finecosine[angle>>ANGLETOFINESHIFT]; + + fixed_t tx, ty; + + nposx = x - src->v1->x; + nposy = y - src->v1->y; + + // Rotate position along normal to match exit linedef + tx = FixedMul(nposx, c) - FixedMul(nposy, s); + ty = FixedMul(nposy, c) + FixedMul(nposx, s); + + tx += dst->v2->x; + ty += dst->v2->y; + + x = tx; + y = ty; +} + +void P_TranslatePortalVXVY(line_t* src, line_t* dst, fixed_t& vx, fixed_t& vy) +{ + angle_t angle = + R_PointToAngle2(0, 0, dst->dx, dst->dy) - + R_PointToAngle2(0, 0, src->dx, src->dy); + + angle += ANGLE_180; + + // Sine, cosine of angle adjustment + fixed_t s = finesine[angle>>ANGLETOFINESHIFT]; + fixed_t c = finecosine[angle>>ANGLETOFINESHIFT]; + + fixed_t orig_velx = vx; + fixed_t orig_vely = vy; + vx = FixedMul(orig_velx, c) - FixedMul(orig_vely, s); + vy = FixedMul(orig_vely, c) + FixedMul(orig_velx, s); +} + +void P_TranslatePortalAngle(line_t* src, line_t* dst, angle_t& angle) +{ + if (!src || !dst) + return; + + // Get the angle between the two linedefs, for rotating + // orientation and velocity. Rotate 180 degrees, and flip + // the position across the exit linedef, if reversed. + angle_t xangle = + R_PointToAngle2(0, 0, dst->dx, dst->dy) - + R_PointToAngle2(0, 0, src->dx, src->dy); + + xangle += ANGLE_180; + angle += xangle; +} + +void P_TranslatePortalZ(line_t* src, line_t* dst, fixed_t& z) +{ + // args[2] = 0 - no teleport + // args[2] = 1 - adjust by floor difference + // args[2] = 2 - adjust by ceiling difference + + if (src->args[2] == 1) + { + z = z - src->frontsector->floorplane.ZatPoint(src->v1->x, src->v1->y) + dst->frontsector->floorplane.ZatPoint(dst->v2->x, dst->v2->y); + } + else if (src->args[2] == 2) + { + z = z - src->frontsector->ceilingplane.ZatPoint(src->v1->x, src->v1->y) + dst->frontsector->ceilingplane.ZatPoint(dst->v2->x, dst->v2->y); + } +} + +// calculate shortest distance from a point (x,y) to a linedef +fixed_t P_PointLineDistance(line_t* line, fixed_t x, fixed_t y) +{ + angle_t angle = R_PointToAngle2(0, 0, line->dx, line->dy); + angle += ANGLE_180; + + fixed_t dx = line->v1->x - x; + fixed_t dy = line->v1->y - y; + + fixed_t s = finesine[angle>>ANGLETOFINESHIFT]; + fixed_t c = finecosine[angle>>ANGLETOFINESHIFT]; + + fixed_t d2x = FixedMul(dx, c) - FixedMul(dy, s); + + return abs(d2x); +} + +void P_NormalizeVXVY(fixed_t& vx, fixed_t& vy) +{ + float _vx = FIXED2FLOAT(vx); + float _vy = FIXED2FLOAT(vy); + float len = sqrt(_vx*_vx+_vy*_vy); + vx = FLOAT2FIXED(_vx/len); + vy = FLOAT2FIXED(_vy/len); +} + +// portal tracer code +PortalTracer::PortalTracer(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy) +{ + this->startx = startx; + this->starty = starty; + this->endx = endx; + this->endy = endy; + intx = endx; + inty = endy; + intxIn = intx; + intyIn = inty; + z = 0; + angle = 0; + depth = 0; + frac = 0; + in = NULL; + out = NULL; + vx = 0; + vy = 0; +} + +bool PortalTracer::TraceStep() +{ + if (depth > sv_portal_recursions) + return false; + + this->in = NULL; + this->out = NULL; + this->vx = 0; + this->vy = 0; + + int oDepth = depth; + + fixed_t dirx = endx-startx; + fixed_t diry = endy-starty; + P_NormalizeVXVY(dirx, diry); + + dirx = 0; + diry = 0; + + FPathTraverse it(startx-dirx, starty-diry, endx+dirx, endy+diry, PT_ADDLINES | PT_COMPATIBLE); + + intercept_t *in; + while ((in = it.Next())) + { + line_t* li; + + if (in->isaline) + { + li = in->d.line; + + if (li->portal && !li->portal_passive) + { + if (P_PointOnLineSide(startx-dirx, starty-diry, li)) + continue; // we're at the back side of this line + + line_t* out = li->portal_dst; + + this->in = li; + this->out = out; + + // we only know that we crossed it, but we also need to know WHERE we crossed it + fixed_t vx = it.Trace().dx; + fixed_t vy = it.Trace().dy; + + fixed_t x = it.Trace().x + FixedMul(vx, in->frac); + fixed_t y = it.Trace().y + FixedMul(vy, in->frac); + + P_NormalizeVXVY(vx, vy); + + this->vx = vx; + this->vy = vy; + + // teleport our trace + + if (!out->backsector) + { + intx = x + vx; + inty = y + vy; + } + else + { + intx = x - vx; + inty = y - vy; + } + + //P_TranslateCoordinatesAndAngle(li, out, startx, starty, noangle); + //P_TranslateCoordinatesAndAngle(li, out, endx, endy, angle); + //if (hdeltaZ) + // P_TranslateZ(li, out, deltaZ); + //P_TranslateCoordinatesAndAngle(li, out, vx, vy, noangle); + + P_TranslatePortalXY(li, out, startx, starty); + P_TranslatePortalVXVY(li, out, this->vx, this->vy); + intxIn = intx; + intyIn = inty; + P_TranslatePortalXY(li, out, intx, inty); + P_TranslatePortalXY(li, out, endx, endy); + P_TranslatePortalAngle(li, out, angle); + P_TranslatePortalZ(li, out, z); + frac += in->frac; + depth++; + break; // breaks to outer loop + } + + if (!(li->flags & ML_TWOSIDED) || (li->flags & ML_BLOCKEVERYTHING)) + return false; // stop tracing, 2D blocking line + } + } + + //Printf("returning %d; vx = %.2f; vy = %.2f\n", (oDepth != depth), FIXED2FLOAT(this->vx), FIXED2FLOAT(this->vy)); + + return (oDepth != depth); // if a portal has been found, return false +} + diff --git a/src/portal.h b/src/portal.h new file mode 100644 index 0000000000..103e3072b4 --- /dev/null +++ b/src/portal.h @@ -0,0 +1,74 @@ +#ifndef _PORTALS_H_ +#define _PORTALS_H_ + +#include "basictypes.h" +#include "v_video.h" +#include "r_defs.h" +#include "actor.h" +#include "p_local.h" +#include "m_bbox.h" + +/* portal structure, this is used in r_ code in order to store drawsegs with portals (and mirrors) */ +struct PortalDrawseg +{ + line_t* src; // source line (the one drawn) this doesn't change over render loops + line_t* dst; // destination line (the one that the portal is linked with, equals 'src' for mirrors) + + int x1; // drawseg x1 + int x2; // drawseg x2 + + int len; + TArray ceilingclip; + TArray floorclip; + + bool mirror; // true if this is a mirror (src should equal dst) +}; + +extern PortalDrawseg* CurrentPortal; +extern int CurrentPortalUniq; +extern bool CurrentPortalInSkybox; + +/* code ported from prototype */ +bool P_ClipLineToPortal(line_t* line, line_t* portal, fixed_t viewx, fixed_t viewy, bool partial = true, bool samebehind = true); +bool P_CheckPortal(line_t* line); +void P_TranslatePortalXY(line_t* src, line_t* dst, fixed_t& x, fixed_t& y); +void P_TranslatePortalVXVY(line_t* src, line_t* dst, fixed_t& vx, fixed_t& vy); +void P_TranslatePortalAngle(line_t* src, line_t* dst, angle_t& angle); +void P_TranslatePortalZ(line_t* src, line_t* dst, fixed_t& z); +void P_NormalizeVXVY(fixed_t& vx, fixed_t& vy); + +// basically, this is a teleporting tracer function, +// which can be used by itself (to calculate portal-aware offsets, say, for projectiles) +// or to teleport normal tracers (like hitscan, railgun, autoaim tracers) +class PortalTracer +{ +public: + PortalTracer(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy); + + // trace to next portal + bool TraceStep(); + // trace to last available portal on the path + void TraceAll() { while (TraceStep()) continue; } + + int depth; + fixed_t startx; + fixed_t starty; + fixed_t intx; + fixed_t inty; + fixed_t intxIn; + fixed_t intyIn; + fixed_t endx; + fixed_t endy; + angle_t angle; + fixed_t z; + fixed_t frac; + line_t* in; + line_t* out; + fixed_t vx; + fixed_t vy; +}; + +/* new code */ +fixed_t P_PointLineDistance(line_t* line, fixed_t x, fixed_t y); + +#endif \ No newline at end of file diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index 8ea5c963fc..e9802e5b1a 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -56,6 +56,7 @@ #include "r_sky.h" #include "po_man.h" #include "r_data/colormaps.h" +#include "portal.h" seg_t* curline; side_t* sidedef; @@ -98,8 +99,8 @@ static BYTE FakeSide; int WindowLeft, WindowRight; WORD MirrorFlags; -seg_t *ActiveWallMirror; -TArray WallMirrors; +TArray WallPortals; + static subsector_t *InSubsector; @@ -553,6 +554,11 @@ void R_AddLine (seg_t *line) return; } + // reject lines that aren't seen from the portal (if any) + // [ZZ] 10.01.2016: lines inside a skybox shouldn't be clipped, although this imposes some limitations on portals in skyboxes. + if (!CurrentPortalInSkybox && CurrentPortal && P_ClipLineToPortal(line->linedef, CurrentPortal->dst, viewx, viewy)) + return; + vertex_t *v1, *v2; v1 = line->linedef->v1; @@ -584,7 +590,7 @@ void R_AddLine (seg_t *line) rw_havehigh = rw_havelow = false; // Single sided line? - if (backsector == NULL) + if (backsector == NULL || (line->linedef->portal && line->sidedef == line->linedef->sidedef[0])) { solid = true; } diff --git a/src/r_bsp.h b/src/r_bsp.h index acd519c62d..3b2a399e33 100644 --- a/src/r_bsp.h +++ b/src/r_bsp.h @@ -25,6 +25,7 @@ #include "tarray.h" #include +#include "portal.h" // The 3072 below is just an arbitrary value picked to avoid // drawing lines the player is too close to that would overflow @@ -86,6 +87,7 @@ struct drawseg_t // backups ptrdiff_t bkup; // sprtopclip backup, for mid and fake textures FWallTmapVals tmapvals; + int CurrentPortalUniq; // [ZZ] to identify the portal that this drawseg is in. used for sprite clipping. }; @@ -104,9 +106,8 @@ extern size_t FirstInterestingDrawseg; extern int WindowLeft, WindowRight; extern WORD MirrorFlags; -extern seg_t* ActiveWallMirror; -extern TArray WallMirrors; +extern TArray WallPortals; typedef void (*drawfunc_t) (int start, int stop); diff --git a/src/r_defs.h b/src/r_defs.h index 4f0b5ed01c..1099bef5d8 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -988,10 +988,14 @@ struct line_t sector_t *frontsector, *backsector; int validcount; // if == validcount, already checked int locknumber; // [Dusk] lock number for special + line_t *portal_dst; + bool portal; + bool portal_mirror; + bool portal_passive; bool isLinePortal() const { - return false; + return portal; } }; diff --git a/src/r_main.cpp b/src/r_main.cpp index 4cb78311f4..4c2b89c0a2 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -57,6 +57,7 @@ #include "v_font.h" #include "r_data/colormaps.h" #include "farchive.h" +#include "portal.h" // MACROS ------------------------------------------------------------------ @@ -608,52 +609,133 @@ void R_SetupFreelook() //========================================================================== // -// R_EnterMirror +// R_EnterPortal // // [RH] Draw the reflection inside a mirror +// [ZZ] Merged with portal code, originally called R_EnterMirror // //========================================================================== -void R_EnterMirror (drawseg_t *ds, int depth) +CVAR(Int, r_portal_recursions, 4, CVAR_ARCHIVE) +CVAR(Bool, r_highlight_portals, false, CVAR_ARCHIVE) + +void R_HighlightPortal (PortalDrawseg* pds) { + // [ZZ] NO OVERFLOW CHECKS HERE + // I believe it won't break. if it does, blame me. :( + + BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 255, 0, 0, 0, 255); + + BYTE* pixels = RenderTarget->GetBuffer(); + // top edge + for (int x = pds->x1; x < pds->x2; x++) + { + if (x < 0 || x >= RenderTarget->GetWidth()) + continue; + + int p = x - pds->x1; + int Ytop = pds->ceilingclip[p]; + int Ybottom = pds->floorclip[p]; + + if (x == pds->x1 || x == pds->x2-1) + { + RenderTarget->DrawLine(x, Ytop, x, Ybottom+1, color, 0); + continue; + } + + int YtopPrev = pds->ceilingclip[p-1]; + int YbottomPrev = pds->floorclip[p-1]; + + if (abs(Ytop-YtopPrev) > 1) + RenderTarget->DrawLine(x, YtopPrev, x, Ytop, color, 0); + else *(pixels + Ytop * RenderTarget->GetPitch() + x) = color; + + if (abs(Ybottom-YbottomPrev) > 1) + RenderTarget->DrawLine(x, YbottomPrev, x, Ybottom, color, 0); + else *(pixels + Ybottom * RenderTarget->GetPitch() + x) = color; + } +} + +void R_EnterPortal (PortalDrawseg* pds, int depth) +{ + // [ZZ] check depth. fill portal with black if it's exceeding the visual recursion limit, and continue like nothing happened. + if (depth >= r_portal_recursions) + { + BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 0, 0, 0, 0, 255); + int spacing = RenderTarget->GetPitch(); + for (int x = pds->x1; x < pds->x2; x++) + { + if (x < 0 || x >= RenderTarget->GetWidth()) + continue; + + int Ytop = pds->ceilingclip[x-pds->x1]; + int Ybottom = pds->floorclip[x-pds->x1]; + + BYTE *dest = RenderTarget->GetBuffer() + x + Ytop * spacing; + + for (int y = Ytop; y <= Ybottom; y++) + { + *dest = color; + dest += spacing; + } + } + + if (r_highlight_portals) + R_HighlightPortal(pds); + + return; + } + angle_t startang = viewangle; fixed_t startx = viewx; fixed_t starty = viewy; + fixed_t startz = viewz; - CurrentMirror++; + CurrentPortalUniq++; - unsigned int mirrorsAtStart = WallMirrors.Size (); + unsigned int portalsAtStart = WallPortals.Size (); - vertex_t *v1 = ds->curline->v1; + if (pds->mirror) + { + //vertex_t *v1 = ds->curline->v1; + vertex_t *v1 = pds->src->v1; + + // Reflect the current view behind the mirror. + if (pds->src->dx == 0) + { // vertical mirror + viewx = v1->x - startx + v1->x; + } + else if (pds->src->dy == 0) + { // horizontal mirror + viewy = v1->y - starty + v1->y; + } + else + { // any mirror--use floats to avoid integer overflow + vertex_t *v2 = pds->src->v2; + + float dx = FIXED2FLOAT(v2->x - v1->x); + float dy = FIXED2FLOAT(v2->y - v1->y); + float x1 = FIXED2FLOAT(v1->x); + float y1 = FIXED2FLOAT(v1->y); + float x = FIXED2FLOAT(startx); + float y = FIXED2FLOAT(starty); + + // the above two cases catch len == 0 + float r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); + + viewx = FLOAT2FIXED((x1 + r * dx)*2 - x); + viewy = FLOAT2FIXED((y1 + r * dy)*2 - y); + } + viewangle = 2*R_PointToAngle2 (pds->src->v1->x, pds->src->v1->y, + pds->src->v2->x, pds->src->v2->y) - startang; - // Reflect the current view behind the mirror. - if (ds->curline->linedef->dx == 0) - { // vertical mirror - viewx = v1->x - startx + v1->x; - } - else if (ds->curline->linedef->dy == 0) - { // horizontal mirror - viewy = v1->y - starty + v1->y; } else - { // any mirror--use floats to avoid integer overflow - vertex_t *v2 = ds->curline->v2; - - float dx = FIXED2FLOAT(v2->x - v1->x); - float dy = FIXED2FLOAT(v2->y - v1->y); - float x1 = FIXED2FLOAT(v1->x); - float y1 = FIXED2FLOAT(v1->y); - float x = FIXED2FLOAT(startx); - float y = FIXED2FLOAT(starty); - - // the above two cases catch len == 0 - float r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); - - viewx = FLOAT2FIXED((x1 + r * dx)*2 - x); - viewy = FLOAT2FIXED((y1 + r * dy)*2 - y); + { + P_TranslatePortalXY(pds->src, pds->dst, viewx, viewy); + P_TranslatePortalZ(pds->src, pds->dst, viewz); + P_TranslatePortalAngle(pds->src, pds->dst, viewangle); } - viewangle = 2*R_PointToAngle2 (ds->curline->v1->x, ds->curline->v1->y, - ds->curline->v2->x, ds->curline->v2->y) - startang; viewsin = finesine[viewangle>>ANGLETOFINESHIFT]; viewcos = finecosine[viewangle>>ANGLETOFINESHIFT]; @@ -664,38 +746,73 @@ void R_EnterMirror (drawseg_t *ds, int depth) R_CopyStackedViewParameters(); validcount++; - ActiveWallMirror = ds->curline; + PortalDrawseg* prevpds = CurrentPortal; + CurrentPortal = pds; R_ClearPlanes (false); - R_ClearClipSegs (ds->x1, ds->x2); + R_ClearClipSegs (pds->x1, pds->x2); - memcpy (ceilingclip + ds->x1, openings + ds->sprtopclip, (ds->x2 - ds->x1)*sizeof(*ceilingclip)); - memcpy (floorclip + ds->x1, openings + ds->sprbottomclip, (ds->x2 - ds->x1)*sizeof(*floorclip)); + WindowLeft = pds->x1; + WindowRight = pds->x2; + + // RF_XFLIP should be removed before calling the root function + int prevmf = MirrorFlags; + if (pds->mirror) + { + if (MirrorFlags & RF_XFLIP) + MirrorFlags &= ~RF_XFLIP; + else MirrorFlags |= RF_XFLIP; + } - WindowLeft = ds->x1; - WindowRight = ds->x2; - MirrorFlags = (depth + 1) & 1; + // some portals have height differences, account for this here + R_3D_EnterSkybox(); // push 3D floor height map + CurrentPortalInSkybox = false; // first portal in a skybox should set this variable to false for proper clipping in skyboxes. + + // first pass, set clipping + memcpy (ceilingclip + pds->x1, &pds->ceilingclip[0], pds->len*sizeof(*ceilingclip)); + memcpy (floorclip + pds->x1, &pds->floorclip[0], pds->len*sizeof(*floorclip)); R_RenderBSPNode (nodes + numnodes - 1); R_3D_ResetClip(); // reset clips (floor/ceiling) + PlaneCycles.Clock(); R_DrawPlanes (); R_DrawSkyBoxes (); + PlaneCycles.Unclock(); - // Allow up to 4 recursions through a mirror - if (depth < 4) + fixed_t vzp = viewz; + + int prevuniq = CurrentPortalUniq; + // depth check is in another place right now + unsigned int portalsAtEnd = WallPortals.Size (); + for (; portalsAtStart < portalsAtEnd; portalsAtStart++) { - unsigned int mirrorsAtEnd = WallMirrors.Size (); - - for (; mirrorsAtStart < mirrorsAtEnd; mirrorsAtStart++) - { - R_EnterMirror (drawsegs + WallMirrors[mirrorsAtStart], depth + 1); - } + R_EnterPortal (&WallPortals[portalsAtStart], depth + 1); } + int prevuniq2 = CurrentPortalUniq; + CurrentPortalUniq = prevuniq; + NetUpdate(); + + MaskedCycles.Clock(); // [ZZ] count sprites in portals/mirrors along with normal ones. + R_DrawMasked (); // this is required since with portals there often will be cases when more than 80% of the view is inside a portal. + MaskedCycles.Unclock(); + + NetUpdate(); + + R_3D_LeaveSkybox(); // pop 3D floor height map + CurrentPortalUniq = prevuniq2; + + // draw a red line around a portal if it's being highlighted + if (r_highlight_portals) + R_HighlightPortal(pds); + + CurrentPortal = prevpds; + MirrorFlags = prevmf; viewangle = startang; viewx = startx; viewy = starty; + viewz = startz; } //========================================================================== @@ -779,7 +896,8 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) WindowLeft = 0; WindowRight = viewwidth; MirrorFlags = 0; - ActiveWallMirror = NULL; + CurrentPortal = NULL; + CurrentPortalUniq = 0; r_dontmaplines = dontmaplines; @@ -813,12 +931,16 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) PlaneCycles.Unclock(); // [RH] Walk through mirrors - size_t lastmirror = WallMirrors.Size (); - for (unsigned int i = 0; i < lastmirror; i++) + // [ZZ] Merged with portals + size_t lastportal = WallPortals.Size(); + for (unsigned int i = 0; i < lastportal; i++) { - R_EnterMirror (drawsegs + WallMirrors[i], 0); + R_EnterPortal(&WallPortals[i], 0); } + CurrentPortal = NULL; + CurrentPortalUniq = 0; + NetUpdate (); MaskedCycles.Clock(); @@ -827,7 +949,7 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) NetUpdate (); } - WallMirrors.Clear (); + WallPortals.Clear (); interpolator.RestoreInterpolations (); R_SetupBuffer (); diff --git a/src/r_plane.cpp b/src/r_plane.cpp index 8238adec27..3e0f2444e7 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -58,6 +58,7 @@ #include "r_3dfloors.h" #include "v_palette.h" #include "r_data/colormaps.h" +#include "portal.h" #ifdef _MSC_VER #pragma warning(disable:4244) @@ -686,9 +687,12 @@ visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightl yscale == check->yscale && angle == check->angle && sky == check->sky && - CurrentMirror == check->CurrentMirror && + CurrentPortalUniq == check->CurrentPortalUniq && MirrorFlags == check->MirrorFlags && - CurrentSkybox == check->CurrentSkybox + CurrentSkybox == check->CurrentSkybox && + viewx == check->viewx && + viewy == check->viewy && + viewz == check->viewz ) { return check; @@ -718,7 +722,7 @@ visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightl check->viewangle = stacked_angle; check->Alpha = alpha; check->Additive = additive; - check->CurrentMirror = CurrentMirror; + check->CurrentPortalUniq = CurrentPortalUniq; check->MirrorFlags = MirrorFlags; check->CurrentSkybox = CurrentSkybox; @@ -807,7 +811,7 @@ visplane_t *R_CheckPlane (visplane_t *pl, int start, int stop) new_pl->sky = pl->sky; new_pl->Alpha = pl->Alpha; new_pl->Additive = pl->Additive; - new_pl->CurrentMirror = pl->CurrentMirror; + new_pl->CurrentPortalUniq = pl->CurrentPortalUniq; new_pl->MirrorFlags = pl->MirrorFlags; new_pl->CurrentSkybox = pl->CurrentSkybox; pl = new_pl; @@ -1043,7 +1047,7 @@ int R_DrawPlanes () for (pl = visplanes[i]; pl; pl = pl->next) { // kg3D - draw only correct planes - if(pl->CurrentMirror != CurrentMirror || pl->CurrentSkybox != CurrentSkybox) + if(pl->CurrentPortalUniq != CurrentPortalUniq || pl->CurrentSkybox != CurrentSkybox) continue; // kg3D - draw only real planes now if(pl->sky >= 0) { @@ -1063,22 +1067,31 @@ void R_DrawHeightPlanes(fixed_t height) ds_color = 3; + fixed_t oViewX = viewx, oViewY = viewy, oViewZ = viewz; + angle_t oViewAngle = viewangle; + for (i = 0; i < MAXVISPLANES; i++) { for (pl = visplanes[i]; pl; pl = pl->next) { // kg3D - draw only correct planes - if(pl->CurrentSkybox != CurrentSkybox) + if(pl->CurrentSkybox != CurrentSkybox || pl->CurrentPortalUniq != CurrentPortalUniq) continue; if(pl->sky < 0 && pl->height.Zat0() == height) { viewx = pl->viewx; viewy = pl->viewy; + viewz = pl->viewz; viewangle = pl->viewangle; MirrorFlags = pl->MirrorFlags; R_DrawSinglePlane (pl, pl->sky & 0x7FFFFFFF, pl->Additive, true); } } } + + viewx = oViewX; + viewy = oViewY; + viewz = oViewZ; + viewangle = oViewAngle; } @@ -1180,6 +1193,7 @@ void R_DrawSkyBoxes () return; R_3D_EnterSkybox(); + CurrentPortalInSkybox = true; int savedextralight = extralight; fixed_t savedx = viewx; @@ -1270,6 +1284,7 @@ void R_DrawSkyBoxes () // Create a drawseg to clip sprites to the sky plane R_CheckDrawSegs (); + ds_p->CurrentPortalUniq = CurrentPortalUniq; ds_p->siz1 = INT_MAX; ds_p->siz2 = INT_MAX; ds_p->sz1 = 0; @@ -1281,6 +1296,7 @@ void R_DrawSkyBoxes () ds_p->sprtopclip = R_NewOpening (pl->right - pl->left); ds_p->maskedtexturecol = ds_p->swall = -1; ds_p->bFogBoundary = false; + ds_p->curline = NULL; ds_p->fake = 0; memcpy (openings + ds_p->sprbottomclip, floorclip + pl->left, (pl->right - pl->left)*sizeof(short)); memcpy (openings + ds_p->sprtopclip, ceilingclip + pl->left, (pl->right - pl->left)*sizeof(short)); @@ -1354,6 +1370,7 @@ void R_DrawSkyBoxes () viewangle = savedangle; R_SetViewAngle (); + CurrentPortalInSkybox = false; R_3D_LeaveSkybox(); if(fakeActive) return; diff --git a/src/r_plane.h b/src/r_plane.h index cb1448dcca..b186d2c2c9 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -58,7 +58,7 @@ struct visplane_s // kg3D - keep track of mirror and skybox owner int CurrentSkybox; - int CurrentMirror; // mirror counter, counts all of them + int CurrentPortalUniq; // mirror counter, counts all of them int MirrorFlags; // this is not related to CurrentMirror unsigned short *bottom; // [RH] bottom and top arrays are dynamically diff --git a/src/r_segs.cpp b/src/r_segs.cpp index bca838cbec..1f09dbf184 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -52,6 +52,7 @@ #include "r_3dfloors.h" #include "v_palette.h" #include "r_data/colormaps.h" +#include "portal.h" #define WALLYREPEAT 8 @@ -103,7 +104,7 @@ extern fixed_t rw_frontfz1, rw_frontfz2; int rw_ceilstat, rw_floorstat; bool rw_mustmarkfloor, rw_mustmarkceiling; bool rw_prepped; -bool rw_markmirror; +bool rw_markportal; bool rw_havehigh; bool rw_havelow; @@ -1989,7 +1990,7 @@ void R_NewWall (bool needlights) { fixed_t rowoffset, yrepeat; - rw_markmirror = false; + rw_markportal = false; sidedef = curline->sidedef; linedef = curline->linedef; @@ -1999,65 +2000,64 @@ void R_NewWall (bool needlights) midtexture = toptexture = bottomtexture = 0; - if (backsector == NULL) + if (sidedef == linedef->sidedef[0] && + linedef->portal && + (!linedef->portal_mirror || r_drawmirrors)) // [ZZ] compatibility with r_drawmirrors cvar that existed way before portals + { + markfloor = markceiling = true; // act like an one-sided wall here (todo: check how does this work with transparency) + rw_markportal = true; + } + else if (backsector == NULL) { // single sided line // a single sided line is terminal, so it must mark ends markfloor = markceiling = true; - // [RH] Render mirrors later, but mark them now. - if (linedef->special != Line_Mirror || !r_drawmirrors) + // [RH] Horizon lines do not need to be textured + if (linedef->special != Line_Horizon) { - // [RH] Horizon lines do not need to be textured - if (linedef->special != Line_Horizon) - { - midtexture = TexMan(sidedef->GetTexture(side_t::mid), true); - rw_offset_mid = sidedef->GetTextureXOffset(side_t::mid); - rowoffset = sidedef->GetTextureYOffset(side_t::mid); - rw_midtexturescalex = sidedef->GetTextureXScale(side_t::mid); - rw_midtexturescaley = sidedef->GetTextureYScale(side_t::mid); - yrepeat = FixedMul(midtexture->yScale, rw_midtexturescaley); - if (yrepeat >= 0) - { // normal orientation - if (linedef->flags & ML_DONTPEGBOTTOM) - { // bottom of texture at bottom - rw_midtexturemid = MulScale16(frontsector->GetPlaneTexZ(sector_t::floor) - viewz, yrepeat) + (midtexture->GetHeight() << FRACBITS); - } - else - { // top of texture at top - rw_midtexturemid = MulScale16(frontsector->GetPlaneTexZ(sector_t::ceiling) - viewz, yrepeat); - if (rowoffset < 0 && midtexture != NULL) - { - rowoffset += midtexture->GetHeight() << FRACBITS; - } - } + midtexture = TexMan(sidedef->GetTexture(side_t::mid), true); + rw_offset_mid = sidedef->GetTextureXOffset(side_t::mid); + rowoffset = sidedef->GetTextureYOffset(side_t::mid); + rw_midtexturescalex = sidedef->GetTextureXScale(side_t::mid); + rw_midtexturescaley = sidedef->GetTextureYScale(side_t::mid); + yrepeat = FixedMul(midtexture->yScale, rw_midtexturescaley); + if (yrepeat >= 0) + { // normal orientation + if (linedef->flags & ML_DONTPEGBOTTOM) + { // bottom of texture at bottom + rw_midtexturemid = MulScale16(frontsector->GetPlaneTexZ(sector_t::floor) - viewz, yrepeat) + (midtexture->GetHeight() << FRACBITS); } else - { // upside down - rowoffset = -rowoffset; - if (linedef->flags & ML_DONTPEGBOTTOM) - { // top of texture at bottom - rw_midtexturemid = MulScale16(frontsector->GetPlaneTexZ(sector_t::floor) - viewz, yrepeat); + { // top of texture at top + rw_midtexturemid = MulScale16(frontsector->GetPlaneTexZ(sector_t::ceiling) - viewz, yrepeat); + if (rowoffset < 0 && midtexture != NULL) + { + rowoffset += midtexture->GetHeight() << FRACBITS; } - else - { // bottom of texture at top - rw_midtexturemid = MulScale16(frontsector->GetPlaneTexZ(sector_t::ceiling) - viewz, yrepeat) + (midtexture->GetHeight() << FRACBITS); - } - } - if (midtexture->bWorldPanning) - { - rw_midtexturemid += MulScale16(rowoffset, yrepeat); - } - else - { - // rowoffset is added outside the multiply so that it positions the texture - // by texels instead of world units. - rw_midtexturemid += rowoffset; } } - } - else - { - rw_markmirror = true; + else + { // upside down + rowoffset = -rowoffset; + if (linedef->flags & ML_DONTPEGBOTTOM) + { // top of texture at bottom + rw_midtexturemid = MulScale16(frontsector->GetPlaneTexZ(sector_t::floor) - viewz, yrepeat); + } + else + { // bottom of texture at top + rw_midtexturemid = MulScale16(frontsector->GetPlaneTexZ(sector_t::ceiling) - viewz, yrepeat) + (midtexture->GetHeight() << FRACBITS); + } + } + if (midtexture->bWorldPanning) + { + rw_midtexturemid += MulScale16(rowoffset, yrepeat); + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + rw_midtexturemid += rowoffset; + } } } else @@ -2380,6 +2380,7 @@ void R_StoreWallRange (int start, int stop) rw_offset = sidedef->GetTextureXOffset(side_t::mid); rw_light = rw_lightleft + rw_lightstep * (start - WallC.sx1); + ds_p->CurrentPortalUniq = CurrentPortalUniq; ds_p->sx1 = WallC.sx1; ds_p->sx2 = WallC.sx2; ds_p->sz1 = WallC.sz1; @@ -2403,10 +2404,8 @@ void R_StoreWallRange (int start, int stop) // killough 1/6/98, 2/1/98: remove limit on openings ds_p->sprtopclip = ds_p->sprbottomclip = ds_p->maskedtexturecol = ds_p->bkup = ds_p->swall = -1; - if (rw_markmirror) + if (rw_markportal) { - size_t drawsegnum = ds_p - drawsegs; - WallMirrors.Push (drawsegnum); ds_p->silhouette = SIL_BOTH; } else if (backsector == NULL) @@ -2617,9 +2616,42 @@ void R_StoreWallRange (int start, int stop) } // [RH] Draw any decals bound to the seg - for (DBaseDecal *decal = curline->sidedef->AttachedDecals; decal != NULL; decal = decal->WallNext) + // [ZZ] Only if not an active mirror + if (!rw_markportal) { - R_RenderDecal (curline->sidedef, decal, ds_p, 0); + for (DBaseDecal *decal = curline->sidedef->AttachedDecals; decal != NULL; decal = decal->WallNext) + { + R_RenderDecal (curline->sidedef, decal, ds_p, 0); + } + } + + if (rw_markportal) + { + PortalDrawseg pds; + pds.src = curline->linedef; + pds.dst = curline->linedef->portal_dst; + pds.x1 = ds_p->x1; + pds.x2 = ds_p->x2; + pds.len = pds.x2 - pds.x1; + pds.ceilingclip.Resize(pds.len); + memcpy(&pds.ceilingclip[0], openings + ds_p->sprtopclip, pds.len*sizeof(*openings)); + pds.floorclip.Resize(pds.len); + memcpy(&pds.floorclip[0], openings + ds_p->sprbottomclip, pds.len*sizeof(*openings)); + + for (int i = 0; i < pds.x2-pds.x1; i++) + { + if (pds.ceilingclip[i] < 0) + pds.ceilingclip[i] = 0; + if (pds.ceilingclip[i] >= RenderTarget->GetHeight()) + pds.ceilingclip[i] = RenderTarget->GetHeight()-1; + if (pds.floorclip[i] < 0) + pds.floorclip[i] = 0; + if (pds.floorclip[i] >= RenderTarget->GetHeight()) + pds.floorclip[i] = RenderTarget->GetHeight()-1; + } + + pds.mirror = curline->linedef->portal_mirror; + WallPortals.Push(pds); } ds_p++; diff --git a/src/r_things.cpp b/src/r_things.cpp index 8b22631353..003e3a7582 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -319,6 +319,49 @@ nextpost: } } +// [ZZ] +// R_ClipSpriteColumnWithPortals +// +static inline bool R_ClipSpriteColumnWithPortals (fixed_t x, fixed_t y, vissprite_t* spr) +{ + // [ZZ] 10.01.2016: don't clip sprites from the root of a skybox. + if (CurrentPortalInSkybox) + return false; + + for (drawseg_t* seg = ds_p; seg-- > firstdrawseg; ) // copied code from killough below + { + // ignore segs from other portals + if (seg->CurrentPortalUniq != CurrentPortalUniq) + continue; + + // I don't know what makes this happen (some old top-down portal code or possibly skybox code? something adds null lines...) + // crashes at the first frame of the first map of Action2.wad + if (!seg->curline) continue; + + line_t* line = seg->curline->linedef; + // divline? wtf, anyway, divlines aren't supposed to be drawn. But I definitely saw NULL linedefs in drawsegs. + if (!line) continue; + + // check if this line will clip sprites to itself + if (!line->portal) + continue; + + // don't clip sprites with portal's back side (it's transparent) + if (seg->curline->sidedef != line->sidedef[0]) + continue; + + // don't clip if the sprite is in front of the portal + if (!P_PointOnLineSide(x, y, line)) + continue; + + // now if current column is covered by this drawseg, we clip it away + if ((dc_x >= seg->x1) && (dc_x < seg->x2)) + return true; + } + + return false; +} + // // R_DrawVisSprite // mfloorclip and mceilingclip should also be set. @@ -332,6 +375,7 @@ void R_DrawVisSprite (vissprite_t *vis) int x2, stop4; fixed_t xiscale; ESPSResult mode; + bool ispsprite = (!vis->sector && !vis->gx && !vis->gy && !vis->gz); if (vis->xscale == 0 || vis->yscale == 0) { // scaled to 0; can't see @@ -393,7 +437,8 @@ void R_DrawVisSprite (vissprite_t *vis) while ((dc_x < stop4) && (dc_x & 3)) { pixels = tex->GetColumn (frac >> FRACBITS, &spans); - R_DrawMaskedColumn (pixels, spans); + if (ispsprite || !R_ClipSpriteColumnWithPortals(vis->gx, vis->gy, vis)) + R_DrawMaskedColumn (pixels, spans); dc_x++; frac += xiscale; } @@ -404,7 +449,8 @@ void R_DrawVisSprite (vissprite_t *vis) for (int zz = 4; zz; --zz) { pixels = tex->GetColumn (frac >> FRACBITS, &spans); - R_DrawMaskedColumnHoriz (pixels, spans); + if (ispsprite || !R_ClipSpriteColumnWithPortals(vis->gx, vis->gy, vis)) + R_DrawMaskedColumnHoriz (pixels, spans); dc_x++; frac += xiscale; } @@ -414,7 +460,8 @@ void R_DrawVisSprite (vissprite_t *vis) while (dc_x < x2) { pixels = tex->GetColumn (frac >> FRACBITS, &spans); - R_DrawMaskedColumn (pixels, spans); + if (ispsprite || !R_ClipSpriteColumnWithPortals(vis->gx, vis->gy, vis)) + R_DrawMaskedColumn (pixels, spans); dc_x++; frac += xiscale; } @@ -524,7 +571,8 @@ void R_DrawWallSprite(vissprite_t *spr) { // calculate lighting dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, shade) << COLORMAPSHIFT); } - R_WallSpriteColumn(R_DrawMaskedColumn); + if (!R_ClipSpriteColumnWithPortals(spr->gx, spr->gy, spr)) + R_WallSpriteColumn(R_DrawMaskedColumn); dc_x++; } @@ -537,7 +585,8 @@ void R_DrawWallSprite(vissprite_t *spr) rt_initcols(); for (int zz = 4; zz; --zz) { - R_WallSpriteColumn(R_DrawMaskedColumnHoriz); + if (!R_ClipSpriteColumnWithPortals(spr->gx, spr->gy, spr)) + R_WallSpriteColumn(R_DrawMaskedColumnHoriz); dc_x++; } rt_draw4cols(dc_x - 4); @@ -549,7 +598,8 @@ void R_DrawWallSprite(vissprite_t *spr) { // calculate lighting dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, shade) << COLORMAPSHIFT); } - R_WallSpriteColumn(R_DrawMaskedColumn); + if (!R_ClipSpriteColumnWithPortals(spr->gx, spr->gy, spr)) + R_WallSpriteColumn(R_DrawMaskedColumn); dc_x++; } } @@ -687,6 +737,11 @@ void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor return; } + // [ZZ] Or less definitely not visible (hue) + // [ZZ] 10.01.2016: don't try to clip stuff inside a skybox against the current portal. + if (!CurrentPortalInSkybox && CurrentPortal && !!P_PointOnLineSide(thing->X(), thing->Y(), CurrentPortal->dst)) + return; + // [RH] Interpolate the sprite's position to make it look smooth fixedvec3 pos = thing->InterpolatedPosition(r_TicFrac); fx = pos.x; @@ -912,6 +967,7 @@ void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor // store information in a vissprite vis = R_NewVisSprite(); + vis->CurrentPortalUniq = CurrentPortalUniq; vis->xscale = xscale; vis->yscale = Scale(InvZtoScale, yscale, tz << 4); vis->idepth = (unsigned)DivScale32(1, tz) >> 1; // tz is 20.12, so idepth ought to be 12.20, but signed math makes it 13.19 @@ -939,6 +995,7 @@ void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor { vis = R_NewVisSprite(); + vis->CurrentPortalUniq = CurrentPortalUniq; vis->xscale = xscale; vis->yscale = yscale; vis->x1 = WindowLeft; @@ -1101,6 +1158,7 @@ static void R_ProjectWallSprite(AActor *thing, fixed_t fx, fixed_t fy, fixed_t f gzb = fz + yscale * scaled_bo; vis = R_NewVisSprite(); + vis->CurrentPortalUniq = CurrentPortalUniq; vis->x1 = wallc.sx1 < WindowLeft ? WindowLeft : wallc.sx1; vis->x2 = wallc.sx2 >= WindowRight ? WindowRight : wallc.sx2; vis->yscale = yscale; @@ -1801,7 +1859,6 @@ void R_SortVisSprites (bool (*compare)(vissprite_t *, vissprite_t *), size_t fir std::stable_sort(&spritesorter[0], &spritesorter[vsprcount], compare); } - // // R_DrawSprite // @@ -2096,8 +2153,13 @@ void R_DrawSprite (vissprite_t *spr) for (ds = ds_p; ds-- > firstdrawseg; ) // new -- killough { + // [ZZ] portal handling here + //if (ds->CurrentPortalUniq != spr->CurrentPortalUniq) + // continue; + // [ZZ] WARNING: uncommenting the two above lines, totally breaks sprite clipping + // kg3D - no clipping on fake segs - if(ds->fake) continue; + if (ds->fake) continue; // determine if the drawseg obscures the sprite if (ds->x1 >= x2 || ds->x2 <= x1 || (!(ds->silhouette & SIL_BOTH) && ds->maskedtexturecol == -1 && @@ -2128,7 +2190,8 @@ void R_DrawSprite (vissprite_t *spr) ds->curline->v1->x - spr->gx, ds->curline->v2->y - ds->curline->v1->y) <= 0)) { // seg is behind sprite, so draw the mid texture if it has one - if (ds->maskedtexturecol != -1 || ds->bFogBoundary) + if (ds->CurrentPortalUniq == CurrentPortalUniq && // [ZZ] instead, portal uniq check is made here + (ds->maskedtexturecol != -1 || ds->bFogBoundary)) R_RenderMaskedSegRange (ds, r1, r2); continue; } @@ -2232,6 +2295,8 @@ void R_DrawMaskedSingle (bool renew) for (i = vsprcount; i > 0; i--) { + if (spritesorter[i-1]->CurrentPortalUniq != CurrentPortalUniq) + continue; // probably another time R_DrawSprite (spritesorter[i-1]); } @@ -2249,6 +2314,9 @@ void R_DrawMaskedSingle (bool renew) } for (ds = ds_p; ds-- > firstdrawseg; ) // new -- killough { + // [ZZ] the same as above + if (ds->CurrentPortalUniq != CurrentPortalUniq) + continue; // kg3D - no fake segs if (ds->fake) continue; if (ds->maskedtexturecol != -1 || ds->bFogBoundary) @@ -2328,6 +2396,10 @@ void R_ProjectParticle (particle_t *particle, const sector_t *sector, int shade, sector_t* heightsec = NULL; BYTE* map; + // [ZZ] Particle not visible through the portal plane + if (CurrentPortal && !!P_PointOnLineSide(particle->x, particle->y, CurrentPortal->dst)) + return; + // transform the origin point tr_x = particle->x - viewx; tr_y = particle->y - viewy; @@ -2431,6 +2503,7 @@ void R_ProjectParticle (particle_t *particle, const sector_t *sector, int shade, // store information in a vissprite vis = R_NewVisSprite (); + vis->CurrentPortalUniq = CurrentPortalUniq; vis->heightsec = heightsec; vis->xscale = xscale; // vis->yscale = FixedMul (xscale, InvZtoScale); @@ -2491,7 +2564,9 @@ static void R_DrawMaskedSegsBehindParticle (const vissprite_t *vis) } if (Scale (ds->siz2 - ds->siz1, (x2 + x1)/2 - ds->sx1, ds->sx2 - ds->sx1) + ds->siz1 < vis->idepth) { - R_RenderMaskedSegRange (ds, MAX(ds->x1, x1), MIN(ds->x2, x2)); + // [ZZ] only draw stuff that's inside the same portal as the particle, other portals will care for themselves + if (ds->CurrentPortalUniq == vis->CurrentPortalUniq) + R_RenderMaskedSegRange (ds, MAX(ds->x1, x1), MIN(ds->x2, x2)); } } } @@ -2522,6 +2597,8 @@ void R_DrawParticle (vissprite_t *vis) fg = fg2rgb[color]; } + /* + spacing = RenderTarget->GetPitch() - countbase; dest = ylookup[yl] + x1 + dc_destorg; @@ -2535,7 +2612,28 @@ void R_DrawParticle (vissprite_t *vis) *dest++ = RGB32k.All[bg & (bg>>15)]; } while (--count); dest += spacing; - } while (--ycount); + } while (--ycount);*/ + + // original was row-wise + // width = countbase + // height = ycount + + spacing = RenderTarget->GetPitch(); + + for (int x = x1; x < (x1+countbase); x++) + { + dc_x = x; + if (R_ClipSpriteColumnWithPortals(vis->gx, vis->gy, vis)) + continue; + dest = ylookup[yl] + x + dc_destorg; + for (int y = 0; y < ycount; y++) + { + DWORD bg = bg2rgb[*dest]; + bg = (fg+bg) | 0x1f07c1f; + *dest = RGB32k.All[bg & (bg>>15)]; + dest += spacing; + } + } } extern fixed_t baseyaspectmul; diff --git a/src/r_things.h b/src/r_things.h index 3ce1a4d4dd..732ef95e3e 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -80,6 +80,7 @@ struct vissprite_t short renderflags; DWORD Translation; // [RH] for color translation visstyle_t Style; + int CurrentPortalUniq; // [ZZ] to identify the portal that this thing is in. used for clipping. }; struct particle_t; diff --git a/zdoom.vcproj b/zdoom.vcproj index e000f153df..b16c13fa82 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -6944,6 +6944,18 @@ > + + + + + +