From 2df45598d8f816aa9e504fd31ba72256b503830b Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 16 Dec 2014 18:08:48 +0200 Subject: [PATCH 01/21] Merged mirrors with portals; Render part of mirrors done, render part of portals needs further testing --- src/actionspecials.h | 5 +- src/p_acs.cpp | 4 ++ src/p_buildmap.cpp | 6 ++ src/p_setup.cpp | 6 ++ src/p_udmf.cpp | 6 ++ src/r_bsp.cpp | 15 +++- src/r_bsp.h | 5 +- src/r_defs.h | 4 ++ src/r_main.cpp | 163 ++++++++++++++++++++++++++++++------------- src/r_plane.cpp | 10 +-- src/r_plane.h | 2 +- src/r_segs.cpp | 130 +++++++++++++++++++--------------- src/r_things.cpp | 110 ++++++++++++++++++++++++++--- src/r_things.h | 1 + zdoom.vcproj | 12 ++++ 15 files changed, 352 insertions(+), 127 deletions(-) diff --git a/src/actionspecials.h b/src/actionspecials.h index 752ef77eb..879f16a11 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -140,7 +140,7 @@ DEFINE_SPECIAL(Teleport_NoStop, 154, 2, 3, 3) // 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 // to an error message. -DEFINE_SPECIAL(SetGlobalFogParameter, 157, 2, 2, 2) +DEFINE_SPECIAL(SetGlobalFogParameter, 157, 2, 2, 2) // vavoom? interferes with Line_SetVisualPortal DEFINE_SPECIAL(FS_Execute, 158, 1, 4, 4) DEFINE_SPECIAL(Sector_SetPlaneReflection, 159, 3, 3, 3) DEFINE_SPECIAL(Sector_Set3DFloor, 160, -1, -1, 5) @@ -236,4 +236,7 @@ DEFINE_SPECIAL(Ceiling_LowerToLowest, 253, 2, 2, 2) DEFINE_SPECIAL(Ceiling_LowerToFloor, 254, 2, 2, 2) DEFINE_SPECIAL(Ceiling_CrushRaiseAndStaySilA, 255, 4, 5, 5) +DEFINE_SPECIAL(Line_SetVisualPortal, 155, -1, -1, 3) +DEFINE_SPECIAL(Line_SetPortal, 156, -1, -1, 3) + #undef DEFINE_SPECIAL diff --git a/src/p_acs.cpp b/src/p_acs.cpp index bc4cbdb75..e0eb524f1 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 "g_shared/a_pickups.h" @@ -7902,6 +7903,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 2ee2ae83a..d9f2857ba 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 ------------------------------------------------------------------ @@ -628,6 +629,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 6b5d9403f..8076e19a9 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" @@ -2161,6 +2162,11 @@ void P_LoadLineDefs (MapData * map) if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; if (level.flags2 & LEVEL2_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE; } + + // [ZZ] check initial portal link + for (int i = 0; i < numlines; i++) + P_CheckPortal(&lines[i]); + delete[] mldf; } diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 473cdcd81..cb9ed19f1 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -46,6 +46,7 @@ #include "r_state.h" #include "r_data/colormaps.h" #include "w_wad.h" +#include "portal.h" //=========================================================================== // @@ -1704,6 +1705,11 @@ public: P_AdjustLine(&lines[line]); P_FinishLoadingLineDef(&lines[line], tempalpha[0]); } + + // [ZZ] check initial portal link + for (int i = 0; i < numlines; i++) + P_CheckPortal(&lines[i]); + assert(side <= numsides); if (side < numsides) { diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index c56255888..e35c95f45 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,10 @@ void R_AddLine (seg_t *line) return; } + // reject lines that aren't seen from the portal (if any) + if (CurrentPortal && P_ClipLineToPortal(line->linedef, CurrentPortal->dst, viewx, viewy)) + return; + vertex_t *v1, *v2; v1 = line->linedef->v1; @@ -583,6 +588,8 @@ void R_AddLine (seg_t *line) rw_mustmarkfloor = rw_mustmarkceiling = false; rw_havehigh = rw_havelow = false; + bool is_portal = (line->linedef && line->linedef->special == Line_Mirror); + // Single sided line? if (backsector == NULL) { @@ -649,7 +656,9 @@ void R_AddLine (seg_t *line) // Window. solid = false; } - else if (backsector->lightlevel != frontsector->lightlevel + else if (is_portal + + || backsector->lightlevel != frontsector->lightlevel || backsector->GetTexture(sector_t::floor) != frontsector->GetTexture(sector_t::floor) || backsector->GetTexture(sector_t::ceiling) != frontsector->GetTexture(sector_t::ceiling) || curline->sidedef->GetTexture(side_t::mid).isValid() diff --git a/src/r_bsp.h b/src/r_bsp.h index 1b5af9805..59a275398 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 @@ -88,6 +89,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. }; @@ -106,9 +108,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 dd2136508..8f94db3f2 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -899,6 +899,10 @@ struct line_t sector_t *frontsector, *backsector; int validcount; // if == validcount, already checked int locknumber; // [Dusk] lock number for special + bool portal; + bool portal_mirror; + bool portal_passive; + line_t *portal_dst; }; // phares 3/14/98 diff --git a/src/r_main.cpp b/src/r_main.cpp index bc3c4c7c0..c6af51533 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 ------------------------------------------------------------------ @@ -610,52 +611,91 @@ 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) +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 > 4) + { + 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++) + { + int Ytop = pds->ceilingclip[x-pds->x1]; + int Ybottom = pds->floorclip[x-pds->x1]; + + BYTE *dest = RenderTarget->GetBuffer() + x + Ytop * spacing; + + if (Ytop < 0) Ytop = 0; + if (Ybottom >= RenderTarget->GetHeight()) + Ybottom = RenderTarget->GetHeight()-1; + + for (int y = Ytop; y <= Ybottom; y++) + { + *dest = color; + dest += spacing; + } + } + + return; + } + angle_t startang = viewangle; fixed_t startx = viewx; fixed_t starty = viewy; - CurrentMirror++; + int prevuniq = CurrentPortalUniq; + 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]; @@ -666,35 +706,57 @@ 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 + 1); + R_ClearClipSegs (pds->x1, pds->x2 + 1); // todo: check if this "+1" is actually needed - memcpy (ceilingclip + ds->x1, openings + ds->sprtopclip, (ds->x2 - ds->x1 + 1)*sizeof(*ceilingclip)); - memcpy (floorclip + ds->x1, openings + ds->sprbottomclip, (ds->x2 - ds->x1 + 1)*sizeof(*floorclip)); + // some portals have height differences, account for this here + R_3D_EnterSkybox(); // push 3D floor height map - WindowLeft = ds->x1; - WindowRight = ds->x2; - MirrorFlags = (depth + 1) & 1; + memcpy (ceilingclip + pds->x1, &pds->ceilingclip[0], pds->ceilingclip.Size()*sizeof(*ceilingclip)); + memcpy (floorclip + pds->x1, &pds->floorclip[0], pds->floorclip.Size()*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; + } 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) + // 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); } + 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 + CurrentPortal = prevpds; + MirrorFlags = prevmf; + CurrentPortalUniq = prevuniq; viewangle = startang; viewx = startx; viewy = starty; @@ -781,7 +843,7 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) WindowLeft = 0; WindowRight = viewwidth - 1; MirrorFlags = 0; - ActiveWallMirror = NULL; + CurrentPortal = NULL; r_dontmaplines = dontmaplines; @@ -815,10 +877,11 @@ 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); } NetUpdate (); @@ -829,7 +892,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 6572c1f0b..dc13e95a3 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) @@ -687,7 +688,7 @@ 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 ) @@ -719,7 +720,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; @@ -808,7 +809,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; @@ -1044,7 +1045,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) { @@ -1270,6 +1271,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; diff --git a/src/r_plane.h b/src/r_plane.h index 231fa3ad4..5fd9b8eeb 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 15f736c93..9e20a3535 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; @@ -1948,7 +1949,7 @@ void R_NewWall (bool needlights) { fixed_t rowoffset, yrepeat; - rw_markmirror = false; + rw_markportal = false; sidedef = curline->sidedef; linedef = curline->linedef; @@ -1958,65 +1959,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 @@ -2339,6 +2339,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; @@ -2362,10 +2363,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) @@ -2576,9 +2575,28 @@ 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.ceilingclip.Resize((pds.x2-pds.x1)+1); + memcpy(&pds.ceilingclip[0], openings + ds_p->sprtopclip, pds.ceilingclip.Size()*sizeof(*openings)); + pds.floorclip.Resize((pds.x2-pds.x1)+1); + memcpy(&pds.floorclip[0], openings + ds_p->sprbottomclip, pds.floorclip.Size()*sizeof(*openings)); + pds.mirror = curline->linedef->portal_mirror; + WallPortals.Push(pds); } ds_p++; diff --git a/src/r_things.cpp b/src/r_things.cpp index c0475ee75..96afaa216 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -316,6 +316,44 @@ nextpost: } } +// [ZZ] +// R_ClipSpriteColumnWithPortals +// +bool R_ClipSpriteColumnWithPortals (fixed_t x, fixed_t y, vissprite_t* spr) +{ + size_t numdrawsegs = ds_p-firstdrawseg; + for (int i = (int)numdrawsegs-1; i >= 0; i--) + { + drawseg_t* seg = &firstdrawseg[i]; + + // ignore segs from other portals + if (seg->CurrentPortalUniq != CurrentPortalUniq) + 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. @@ -329,6 +367,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); dc_colormap = vis->Style.colormap; @@ -372,7 +411,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; } @@ -383,7 +423,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; } @@ -393,7 +434,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; } @@ -503,7 +545,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++; } @@ -516,7 +559,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); @@ -528,7 +572,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++; } } @@ -668,6 +713,10 @@ void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor return; } + // [ZZ] Or less definitely not visible (hue) + if (CurrentPortal && !!P_PointOnLineSide(thing->x, thing->y, CurrentPortal->dst)) + return; + // [RH] Interpolate the sprite's position to make it look smooth fx = thing->PrevX + FixedMul (r_TicFrac, thing->x - thing->PrevX); fy = thing->PrevY + FixedMul (r_TicFrac, thing->y - thing->PrevY); @@ -885,6 +934,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 @@ -912,6 +962,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; @@ -1073,6 +1124,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-1; vis->yscale = yscale; @@ -1767,7 +1819,6 @@ void R_SortVisSprites (bool (*compare)(vissprite_t *, vissprite_t *), size_t fir std::stable_sort(&spritesorter[0], &spritesorter[vsprcount], compare); } - // // R_DrawSprite // @@ -2062,8 +2113,11 @@ void R_DrawSprite (vissprite_t *spr) for (ds = ds_p; ds-- > firstdrawseg; ) // new -- killough { + // [ZZ] portal handling here + if (ds->CurrentPortalUniq != spr->CurrentPortalUniq) + continue; // 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 && @@ -2198,6 +2252,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]); } @@ -2215,6 +2271,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) @@ -2294,6 +2353,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; @@ -2397,6 +2460,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); @@ -2449,8 +2513,11 @@ static void R_DrawMaskedSegsBehindParticle (const vissprite_t *vis) for (unsigned int p = InterestingDrawsegs.Size(); p-- > FirstInterestingDrawseg; ) { drawseg_t *ds = &drawsegs[InterestingDrawsegs[p]]; + // [ZZ] only draw stuff that's inside the same portal as the particle, other portals will care for themselves + if (ds->CurrentPortalUniq != vis->CurrentPortalUniq) + continue; // kg3D - no fake segs - if(ds->fake) continue; + if (ds->fake) continue; if (ds->x1 >= x2 || ds->x2 < x1) { continue; @@ -2488,6 +2555,8 @@ void R_DrawParticle (vissprite_t *vis) fg = fg2rgb[color]; } + /* + spacing = RenderTarget->GetPitch() - countbase; dest = ylookup[yl] + x1 + dc_destorg; @@ -2501,7 +2570,28 @@ void R_DrawParticle (vissprite_t *vis) *dest++ = RGB32k[0][0][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[0][0][bg & (bg>>15)]; + dest += spacing; + } + } } extern fixed_t baseyaspectmul; diff --git a/src/r_things.h b/src/r_things.h index 3ce1a4d4d..732ef95e3 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 cabc6c84f..15895c0a3 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -6632,6 +6632,18 @@ > + + + + + + From cac266b5dfbe329be5161a8b259bcf1e8b2a5536 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 16 Dec 2014 18:13:20 +0200 Subject: [PATCH 02/21] Put line ID from args[1] for Hexen maps --- src/p_setup.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 8076e19a9..361832323 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1956,6 +1956,11 @@ void P_SetLineID (line_t *ld) case Static_Init: if (ld->args[1] == Init_SectorLink) ld->id = ld->args[0]; break; + + case Line_SetPortal: + case Line_SetVisualPortal: + ld->id = ld->args[1]; // 0 = target id, 1 = this id, 2 = plane anchor + break; } } } From e04a3c094af5c324674bf657fc8806b511feb54f Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 16 Dec 2014 18:46:26 +0200 Subject: [PATCH 03/21] Fixed portal detection in R_AddLine --- src/r_bsp.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index e35c95f45..4d4399810 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -588,8 +588,6 @@ void R_AddLine (seg_t *line) rw_mustmarkfloor = rw_mustmarkceiling = false; rw_havehigh = rw_havelow = false; - bool is_portal = (line->linedef && line->linedef->special == Line_Mirror); - // Single sided line? if (backsector == NULL) { @@ -656,7 +654,7 @@ void R_AddLine (seg_t *line) // Window. solid = false; } - else if (is_portal + else if (line->linedef->portal // [ZZ] portals are always drawn, even if there's exactly same sector on both sides || backsector->lightlevel != frontsector->lightlevel || backsector->GetTexture(sector_t::floor) != frontsector->GetTexture(sector_t::floor) From 5d3c2f17506813c3ff0b4d51ff763f7b655c42f9 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 16 Dec 2014 19:48:06 +0200 Subject: [PATCH 04/21] Made a CVar to limit recursions; Made a CVar to highlight portal borders; Fixed slight logical bug in unique portal identifiers --- src/r_main.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/src/r_main.cpp b/src/r_main.cpp index c6af51533..94a0cce10 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -618,15 +618,63 @@ void R_SetupFreelook() // //========================================================================== +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); + RenderTarget->DrawLine(pds->x1, pds->ceilingclip[0], pds->x1, pds->floorclip[0], color, 0); + RenderTarget->DrawLine(pds->x2, pds->ceilingclip[pds->ceilingclip.Size()-1], pds->x2, pds->floorclip[pds->floorclip.Size()-1], color, 0); + + BYTE* pixels = RenderTarget->GetBuffer(); + // top edge + for (int x = pds->x1+1; 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]; + + int YtopPrev = pds->ceilingclip[p-1]; + int YbottomPrev = pds->floorclip[p-1]; + + if (Ytop < 0) Ytop = 0; + if (Ybottom >= RenderTarget->GetHeight()) + Ybottom = RenderTarget->GetHeight()-1; + + if (YtopPrev < 0) YtopPrev = 0; + if (YbottomPrev >= RenderTarget->GetHeight()) + YbottomPrev = RenderTarget->GetHeight()-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 > 4) + 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]; @@ -643,6 +691,9 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) } } + if (r_highlight_portals) + R_HighlightPortal(pds); + return; } @@ -650,7 +701,6 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) fixed_t startx = viewx; fixed_t starty = viewy; - int prevuniq = CurrentPortalUniq; CurrentPortalUniq++; unsigned int portalsAtStart = WallPortals.Size (); @@ -738,12 +788,14 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) R_DrawSkyBoxes (); PlaneCycles.Unclock(); + int prevuniq = CurrentPortalUniq; // depth check is in another place right now unsigned int portalsAtEnd = WallPortals.Size (); for (; portalsAtStart < portalsAtEnd; portalsAtStart++) { R_EnterPortal (&WallPortals[portalsAtStart], depth + 1); } + CurrentPortalUniq = prevuniq; NetUpdate(); @@ -753,10 +805,13 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) NetUpdate(); + // draw a red line around a portal if it's being highlighted + if (r_highlight_portals) + R_HighlightPortal(pds); + R_3D_LeaveSkybox(); // pop 3D floor height map CurrentPortal = prevpds; MirrorFlags = prevmf; - CurrentPortalUniq = prevuniq; viewangle = startang; viewx = startx; viewy = starty; From 322ecae93f1b5788bd48f66b18d009059165283f Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 16 Dec 2014 21:31:21 +0200 Subject: [PATCH 05/21] Fixed sprite clipping with drawsegs (removed unnecessary portal check) --- src/r_main.cpp | 53 +++++++++++++++++++++++++----------------------- src/r_plane.cpp | 16 +++++++++++++-- src/r_segs.cpp | 22 ++++++++++++++++---- src/r_things.cpp | 6 ++++-- 4 files changed, 64 insertions(+), 33 deletions(-) diff --git a/src/r_main.cpp b/src/r_main.cpp index 94a0cce10..7314696be 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -627,32 +627,27 @@ void R_HighlightPortal (PortalDrawseg* pds) // I believe it won't break. if it does, blame me. :( BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 255, 0, 0, 0, 255); - RenderTarget->DrawLine(pds->x1, pds->ceilingclip[0], pds->x1, pds->floorclip[0], color, 0); - RenderTarget->DrawLine(pds->x2, pds->ceilingclip[pds->ceilingclip.Size()-1], pds->x2, pds->floorclip[pds->floorclip.Size()-1], color, 0); BYTE* pixels = RenderTarget->GetBuffer(); // top edge - for (int x = pds->x1+1; x < pds->x2; x++) + 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) + { + RenderTarget->DrawLine(x, Ytop, x, Ybottom, color, 0); + continue; + } + int YtopPrev = pds->ceilingclip[p-1]; int YbottomPrev = pds->floorclip[p-1]; - if (Ytop < 0) Ytop = 0; - if (Ybottom >= RenderTarget->GetHeight()) - Ybottom = RenderTarget->GetHeight()-1; - - if (YtopPrev < 0) YtopPrev = 0; - if (YbottomPrev >= RenderTarget->GetHeight()) - YbottomPrev = RenderTarget->GetHeight()-1; - if (abs(Ytop-YtopPrev) > 1) RenderTarget->DrawLine(x, YtopPrev, x, Ytop, color, 0); else *(pixels + Ytop * RenderTarget->GetPitch() + x) = color; @@ -680,10 +675,6 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) BYTE *dest = RenderTarget->GetBuffer() + x + Ytop * spacing; - if (Ytop < 0) Ytop = 0; - if (Ybottom >= RenderTarget->GetHeight()) - Ybottom = RenderTarget->GetHeight()-1; - for (int y = Ytop; y <= Ybottom; y++) { *dest = color; @@ -700,6 +691,7 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) angle_t startang = viewangle; fixed_t startx = viewx; fixed_t starty = viewy; + fixed_t startz = viewz; CurrentPortalUniq++; @@ -760,17 +752,11 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) CurrentPortal = pds; R_ClearPlanes (false); - R_ClearClipSegs (pds->x1, pds->x2 + 1); // todo: check if this "+1" is actually needed - - // some portals have height differences, account for this here - R_3D_EnterSkybox(); // push 3D floor height map - - memcpy (ceilingclip + pds->x1, &pds->ceilingclip[0], pds->ceilingclip.Size()*sizeof(*ceilingclip)); - memcpy (floorclip + pds->x1, &pds->floorclip[0], pds->floorclip.Size()*sizeof(*floorclip)); + R_ClearClipSegs (pds->x1, pds->x2+1); // todo: check if this "+1" is actually needed WindowLeft = pds->x1; WindowRight = pds->x2; - + // RF_XFLIP should be removed before calling the root function int prevmf = MirrorFlags; if (pds->mirror) @@ -780,6 +766,13 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) else MirrorFlags |= RF_XFLIP; } + // some portals have height differences, account for this here + R_3D_EnterSkybox(); // push 3D floor height map + + // 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) @@ -788,6 +781,8 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) R_DrawSkyBoxes (); PlaneCycles.Unclock(); + fixed_t vzp = viewz; + int prevuniq = CurrentPortalUniq; // depth check is in another place right now unsigned int portalsAtEnd = WallPortals.Size (); @@ -795,6 +790,7 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) { R_EnterPortal (&WallPortals[portalsAtStart], depth + 1); } + int prevuniq2 = CurrentPortalUniq; CurrentPortalUniq = prevuniq; NetUpdate(); @@ -805,16 +801,19 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) 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); - R_3D_LeaveSkybox(); // pop 3D floor height map CurrentPortal = prevpds; MirrorFlags = prevmf; viewangle = startang; viewx = startx; viewy = starty; + viewz = startz; } //========================================================================== @@ -899,6 +898,7 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) WindowRight = viewwidth - 1; MirrorFlags = 0; CurrentPortal = NULL; + CurrentPortalUniq = 0; r_dontmaplines = dontmaplines; @@ -939,6 +939,9 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) R_EnterPortal(&WallPortals[i], 0); } + CurrentPortal = NULL; + CurrentPortalUniq = 0; + NetUpdate (); MaskedCycles.Clock(); diff --git a/src/r_plane.cpp b/src/r_plane.cpp index dc13e95a3..111954bcd 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -690,7 +690,10 @@ visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightl sky == check->sky && CurrentPortalUniq == check->CurrentPortalUniq && MirrorFlags == check->MirrorFlags && - CurrentSkybox == check->CurrentSkybox + CurrentSkybox == check->CurrentSkybox && + viewx == check->viewx && + viewy == check->viewy && + viewz == check->viewz ) { return check; @@ -1065,22 +1068,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; } diff --git a/src/r_segs.cpp b/src/r_segs.cpp index 9e20a3535..9009240bc 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -2591,10 +2591,24 @@ void R_StoreWallRange (int start, int stop) pds.dst = curline->linedef->portal_dst; pds.x1 = ds_p->x1; pds.x2 = ds_p->x2; - pds.ceilingclip.Resize((pds.x2-pds.x1)+1); - memcpy(&pds.ceilingclip[0], openings + ds_p->sprtopclip, pds.ceilingclip.Size()*sizeof(*openings)); - pds.floorclip.Resize((pds.x2-pds.x1)+1); - memcpy(&pds.floorclip[0], openings + ds_p->sprbottomclip, pds.floorclip.Size()*sizeof(*openings)); + pds.len = (pds.x2 - pds.x1) + 1; + 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)+1; 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); } diff --git a/src/r_things.cpp b/src/r_things.cpp index 96afaa216..9cfedf068 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -2114,8 +2114,10 @@ void R_DrawSprite (vissprite_t *spr) for (ds = ds_p; ds-- > firstdrawseg; ) // new -- killough { // [ZZ] portal handling here - if (ds->CurrentPortalUniq != spr->CurrentPortalUniq) - continue; + //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; // determine if the drawseg obscures the sprite From 45c97c3ac55f3e6432b84d75539c455e4308c33c Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 16 Dec 2014 22:54:49 +0200 Subject: [PATCH 06/21] Fixed trivial bug with two-sided portals not clipping stuff correctly --- src/r_bsp.cpp | 6 ++---- src/r_main.cpp | 2 +- src/r_things.cpp | 10 +++++----- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index 4d4399810..0fc33c042 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -589,7 +589,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; } @@ -654,9 +654,7 @@ void R_AddLine (seg_t *line) // Window. solid = false; } - else if (line->linedef->portal // [ZZ] portals are always drawn, even if there's exactly same sector on both sides - - || backsector->lightlevel != frontsector->lightlevel + else if (backsector->lightlevel != frontsector->lightlevel || backsector->GetTexture(sector_t::floor) != frontsector->GetTexture(sector_t::floor) || backsector->GetTexture(sector_t::ceiling) != frontsector->GetTexture(sector_t::ceiling) || curline->sidedef->GetTexture(side_t::mid).isValid() diff --git a/src/r_main.cpp b/src/r_main.cpp index 7314696be..7c15cefb0 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -641,7 +641,7 @@ void R_HighlightPortal (PortalDrawseg* pds) if (x == pds->x1 || x == pds->x2) { - RenderTarget->DrawLine(x, Ytop, x, Ybottom, color, 0); + RenderTarget->DrawLine(x, Ytop, x, Ybottom+1, color, 0); continue; } diff --git a/src/r_things.cpp b/src/r_things.cpp index 9cfedf068..92cd84155 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -2150,7 +2150,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; } @@ -2515,9 +2516,6 @@ static void R_DrawMaskedSegsBehindParticle (const vissprite_t *vis) for (unsigned int p = InterestingDrawsegs.Size(); p-- > FirstInterestingDrawseg; ) { drawseg_t *ds = &drawsegs[InterestingDrawsegs[p]]; - // [ZZ] only draw stuff that's inside the same portal as the particle, other portals will care for themselves - if (ds->CurrentPortalUniq != vis->CurrentPortalUniq) - continue; // kg3D - no fake segs if (ds->fake) continue; if (ds->x1 >= x2 || ds->x2 < x1) @@ -2526,7 +2524,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-1)); + // [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-1)); } } } From cd39c4b8a6b80e17a78e2e1699096fdb169e0a09 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 16 Dec 2014 18:08:48 +0200 Subject: [PATCH 07/21] Merged mirrors with portals; Render part of mirrors done, render part of portals needs further testing --- src/actionspecials.h | 5 +- src/p_acs.cpp | 4 ++ src/p_buildmap.cpp | 6 ++ src/p_setup.cpp | 6 ++ src/p_udmf.cpp | 6 ++ src/r_bsp.cpp | 15 +++- src/r_bsp.h | 5 +- src/r_defs.h | 4 ++ src/r_main.cpp | 163 ++++++++++++++++++++++++++++++------------- src/r_plane.cpp | 10 +-- src/r_plane.h | 2 +- src/r_segs.cpp | 130 +++++++++++++++++++--------------- src/r_things.cpp | 110 ++++++++++++++++++++++++++--- src/r_things.h | 1 + zdoom.vcproj | 12 ++++ 15 files changed, 352 insertions(+), 127 deletions(-) diff --git a/src/actionspecials.h b/src/actionspecials.h index 752ef77eb..879f16a11 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -140,7 +140,7 @@ DEFINE_SPECIAL(Teleport_NoStop, 154, 2, 3, 3) // 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 // to an error message. -DEFINE_SPECIAL(SetGlobalFogParameter, 157, 2, 2, 2) +DEFINE_SPECIAL(SetGlobalFogParameter, 157, 2, 2, 2) // vavoom? interferes with Line_SetVisualPortal DEFINE_SPECIAL(FS_Execute, 158, 1, 4, 4) DEFINE_SPECIAL(Sector_SetPlaneReflection, 159, 3, 3, 3) DEFINE_SPECIAL(Sector_Set3DFloor, 160, -1, -1, 5) @@ -236,4 +236,7 @@ DEFINE_SPECIAL(Ceiling_LowerToLowest, 253, 2, 2, 2) DEFINE_SPECIAL(Ceiling_LowerToFloor, 254, 2, 2, 2) DEFINE_SPECIAL(Ceiling_CrushRaiseAndStaySilA, 255, 4, 5, 5) +DEFINE_SPECIAL(Line_SetVisualPortal, 155, -1, -1, 3) +DEFINE_SPECIAL(Line_SetPortal, 156, -1, -1, 3) + #undef DEFINE_SPECIAL diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 2cc9f958f..69045ea5f 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 "g_shared/a_pickups.h" @@ -8068,6 +8069,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 46c2b9f4c..49e834764 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 ------------------------------------------------------------------ @@ -628,6 +629,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 fb6b34b53..137de2b8c 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" @@ -2169,6 +2170,11 @@ void P_LoadLineDefs (MapData * map) if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; if (level.flags2 & LEVEL2_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE; } + + // [ZZ] check initial portal link + for (int i = 0; i < numlines; i++) + P_CheckPortal(&lines[i]); + delete[] mldf; } diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 0434d8bce..e10cc33f4 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" //=========================================================================== // @@ -1741,6 +1742,11 @@ public: P_AdjustLine(&lines[line]); P_FinishLoadingLineDef(&lines[line], tempalpha[0]); } + + // [ZZ] check initial portal link + for (int i = 0; i < numlines; i++) + P_CheckPortal(&lines[i]); + assert(side <= numsides); if (side < numsides) { diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index bf0ac910f..def34ea9b 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,10 @@ void R_AddLine (seg_t *line) return; } + // reject lines that aren't seen from the portal (if any) + if (CurrentPortal && P_ClipLineToPortal(line->linedef, CurrentPortal->dst, viewx, viewy)) + return; + vertex_t *v1, *v2; v1 = line->linedef->v1; @@ -583,6 +588,8 @@ void R_AddLine (seg_t *line) rw_mustmarkfloor = rw_mustmarkceiling = false; rw_havehigh = rw_havelow = false; + bool is_portal = (line->linedef && line->linedef->special == Line_Mirror); + // Single sided line? if (backsector == NULL) { @@ -649,7 +656,9 @@ void R_AddLine (seg_t *line) // Window. solid = false; } - else if (backsector->lightlevel != frontsector->lightlevel + else if (is_portal + + || backsector->lightlevel != frontsector->lightlevel || backsector->GetTexture(sector_t::floor) != frontsector->GetTexture(sector_t::floor) || backsector->GetTexture(sector_t::ceiling) != frontsector->GetTexture(sector_t::ceiling) || curline->sidedef->GetTexture(side_t::mid).isValid() diff --git a/src/r_bsp.h b/src/r_bsp.h index acd519c62..3b2a399e3 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 afda92089..bbc7a9198 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -894,6 +894,10 @@ struct line_t sector_t *frontsector, *backsector; int validcount; // if == validcount, already checked int locknumber; // [Dusk] lock number for special + bool portal; + bool portal_mirror; + bool portal_passive; + line_t *portal_dst; }; // phares 3/14/98 diff --git a/src/r_main.cpp b/src/r_main.cpp index 5da0b8992..5165d8371 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,91 @@ 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) +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 > 4) + { + 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++) + { + int Ytop = pds->ceilingclip[x-pds->x1]; + int Ybottom = pds->floorclip[x-pds->x1]; + + BYTE *dest = RenderTarget->GetBuffer() + x + Ytop * spacing; + + if (Ytop < 0) Ytop = 0; + if (Ybottom >= RenderTarget->GetHeight()) + Ybottom = RenderTarget->GetHeight()-1; + + for (int y = Ytop; y <= Ybottom; y++) + { + *dest = color; + dest += spacing; + } + } + + return; + } + angle_t startang = viewangle; fixed_t startx = viewx; fixed_t starty = viewy; - CurrentMirror++; + int prevuniq = CurrentPortalUniq; + 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,35 +704,57 @@ 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 + 1); + R_ClearClipSegs (pds->x1, pds->x2 + 1); // todo: check if this "+1" is actually needed - memcpy (ceilingclip + ds->x1, openings + ds->sprtopclip, (ds->x2 - ds->x1 + 1)*sizeof(*ceilingclip)); - memcpy (floorclip + ds->x1, openings + ds->sprbottomclip, (ds->x2 - ds->x1 + 1)*sizeof(*floorclip)); + // some portals have height differences, account for this here + R_3D_EnterSkybox(); // push 3D floor height map - WindowLeft = ds->x1; - WindowRight = ds->x2; - MirrorFlags = (depth + 1) & 1; + memcpy (ceilingclip + pds->x1, &pds->ceilingclip[0], pds->ceilingclip.Size()*sizeof(*ceilingclip)); + memcpy (floorclip + pds->x1, &pds->floorclip[0], pds->floorclip.Size()*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; + } 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) + // 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); } + 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 + CurrentPortal = prevpds; + MirrorFlags = prevmf; + CurrentPortalUniq = prevuniq; viewangle = startang; viewx = startx; viewy = starty; @@ -779,7 +841,7 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) WindowLeft = 0; WindowRight = viewwidth - 1; MirrorFlags = 0; - ActiveWallMirror = NULL; + CurrentPortal = NULL; r_dontmaplines = dontmaplines; @@ -813,10 +875,11 @@ 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); } NetUpdate (); @@ -827,7 +890,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 8cab34230..a19a2f677 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,7 +687,7 @@ 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 ) @@ -718,7 +719,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 +808,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 +1044,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) { @@ -1269,6 +1270,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; diff --git a/src/r_plane.h b/src/r_plane.h index 231fa3ad4..5fd9b8eeb 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 cf00abc45..b97a6d4d1 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; @@ -1948,7 +1949,7 @@ void R_NewWall (bool needlights) { fixed_t rowoffset, yrepeat; - rw_markmirror = false; + rw_markportal = false; sidedef = curline->sidedef; linedef = curline->linedef; @@ -1958,65 +1959,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 @@ -2339,6 +2339,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; @@ -2362,10 +2363,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) @@ -2576,9 +2575,28 @@ 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.ceilingclip.Resize((pds.x2-pds.x1)+1); + memcpy(&pds.ceilingclip[0], openings + ds_p->sprtopclip, pds.ceilingclip.Size()*sizeof(*openings)); + pds.floorclip.Resize((pds.x2-pds.x1)+1); + memcpy(&pds.floorclip[0], openings + ds_p->sprbottomclip, pds.floorclip.Size()*sizeof(*openings)); + pds.mirror = curline->linedef->portal_mirror; + WallPortals.Push(pds); } ds_p++; diff --git a/src/r_things.cpp b/src/r_things.cpp index 8e53fb6d1..353961d81 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -317,6 +317,44 @@ nextpost: } } +// [ZZ] +// R_ClipSpriteColumnWithPortals +// +bool R_ClipSpriteColumnWithPortals (fixed_t x, fixed_t y, vissprite_t* spr) +{ + size_t numdrawsegs = ds_p-firstdrawseg; + for (int i = (int)numdrawsegs-1; i >= 0; i--) + { + drawseg_t* seg = &firstdrawseg[i]; + + // ignore segs from other portals + if (seg->CurrentPortalUniq != CurrentPortalUniq) + 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. @@ -330,6 +368,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); dc_colormap = vis->Style.colormap; @@ -373,7 +412,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; } @@ -384,7 +424,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; } @@ -394,7 +435,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; } @@ -504,7 +546,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++; } @@ -517,7 +560,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); @@ -529,7 +573,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++; } } @@ -669,6 +714,10 @@ void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor return; } + // [ZZ] Or less definitely not visible (hue) + if (CurrentPortal && !!P_PointOnLineSide(thing->x, thing->y, CurrentPortal->dst)) + return; + // [RH] Interpolate the sprite's position to make it look smooth fx = thing->PrevX + FixedMul (r_TicFrac, thing->x - thing->PrevX); fy = thing->PrevY + FixedMul (r_TicFrac, thing->y - thing->PrevY); @@ -886,6 +935,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 @@ -913,6 +963,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; @@ -1074,6 +1125,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-1; vis->yscale = yscale; @@ -1774,7 +1826,6 @@ void R_SortVisSprites (bool (*compare)(vissprite_t *, vissprite_t *), size_t fir std::stable_sort(&spritesorter[0], &spritesorter[vsprcount], compare); } - // // R_DrawSprite // @@ -2069,8 +2120,11 @@ void R_DrawSprite (vissprite_t *spr) for (ds = ds_p; ds-- > firstdrawseg; ) // new -- killough { + // [ZZ] portal handling here + if (ds->CurrentPortalUniq != spr->CurrentPortalUniq) + continue; // 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 && @@ -2205,6 +2259,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]); } @@ -2222,6 +2278,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) @@ -2301,6 +2360,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; @@ -2404,6 +2467,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); @@ -2456,8 +2520,11 @@ static void R_DrawMaskedSegsBehindParticle (const vissprite_t *vis) for (unsigned int p = InterestingDrawsegs.Size(); p-- > FirstInterestingDrawseg; ) { drawseg_t *ds = &drawsegs[InterestingDrawsegs[p]]; + // [ZZ] only draw stuff that's inside the same portal as the particle, other portals will care for themselves + if (ds->CurrentPortalUniq != vis->CurrentPortalUniq) + continue; // kg3D - no fake segs - if(ds->fake) continue; + if (ds->fake) continue; if (ds->x1 >= x2 || ds->x2 < x1) { continue; @@ -2495,6 +2562,8 @@ void R_DrawParticle (vissprite_t *vis) fg = fg2rgb[color]; } + /* + spacing = RenderTarget->GetPitch() - countbase; dest = ylookup[yl] + x1 + dc_destorg; @@ -2508,7 +2577,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[0][0][bg & (bg>>15)]; + dest += spacing; + } + } } extern fixed_t baseyaspectmul; diff --git a/src/r_things.h b/src/r_things.h index 3ce1a4d4d..732ef95e3 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 d802fc61c..d57b2d4d8 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -6704,6 +6704,18 @@ > + + + + + + From 78ea00be59a2d78bb66f5f1e4885f0aa9ad0a045 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 16 Dec 2014 18:13:20 +0200 Subject: [PATCH 08/21] Put line ID from args[1] for Hexen maps --- src/p_setup.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 137de2b8c..51cbdf76e 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1961,6 +1961,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: + ld->id = ld->args[1]; // 0 = target id, 1 = this id, 2 = plane anchor + break; } if (setid != -1) { From b066b0891a4bf2ca91ed104f253d19d8eac05992 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 16 Dec 2014 18:46:26 +0200 Subject: [PATCH 09/21] Fixed portal detection in R_AddLine --- src/r_bsp.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index def34ea9b..4174ab629 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -588,8 +588,6 @@ void R_AddLine (seg_t *line) rw_mustmarkfloor = rw_mustmarkceiling = false; rw_havehigh = rw_havelow = false; - bool is_portal = (line->linedef && line->linedef->special == Line_Mirror); - // Single sided line? if (backsector == NULL) { @@ -656,7 +654,7 @@ void R_AddLine (seg_t *line) // Window. solid = false; } - else if (is_portal + else if (line->linedef->portal // [ZZ] portals are always drawn, even if there's exactly same sector on both sides || backsector->lightlevel != frontsector->lightlevel || backsector->GetTexture(sector_t::floor) != frontsector->GetTexture(sector_t::floor) From f07d43943a60b1fcf10d1af6c14d40be2ff63222 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 16 Dec 2014 19:48:06 +0200 Subject: [PATCH 10/21] Made a CVar to limit recursions; Made a CVar to highlight portal borders; Fixed slight logical bug in unique portal identifiers --- src/r_main.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/src/r_main.cpp b/src/r_main.cpp index 5165d8371..55cde3d9b 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -616,15 +616,63 @@ void R_SetupFreelook() // //========================================================================== +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); + RenderTarget->DrawLine(pds->x1, pds->ceilingclip[0], pds->x1, pds->floorclip[0], color, 0); + RenderTarget->DrawLine(pds->x2, pds->ceilingclip[pds->ceilingclip.Size()-1], pds->x2, pds->floorclip[pds->floorclip.Size()-1], color, 0); + + BYTE* pixels = RenderTarget->GetBuffer(); + // top edge + for (int x = pds->x1+1; 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]; + + int YtopPrev = pds->ceilingclip[p-1]; + int YbottomPrev = pds->floorclip[p-1]; + + if (Ytop < 0) Ytop = 0; + if (Ybottom >= RenderTarget->GetHeight()) + Ybottom = RenderTarget->GetHeight()-1; + + if (YtopPrev < 0) YtopPrev = 0; + if (YbottomPrev >= RenderTarget->GetHeight()) + YbottomPrev = RenderTarget->GetHeight()-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 > 4) + 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]; @@ -641,6 +689,9 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) } } + if (r_highlight_portals) + R_HighlightPortal(pds); + return; } @@ -648,7 +699,6 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) fixed_t startx = viewx; fixed_t starty = viewy; - int prevuniq = CurrentPortalUniq; CurrentPortalUniq++; unsigned int portalsAtStart = WallPortals.Size (); @@ -736,12 +786,14 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) R_DrawSkyBoxes (); PlaneCycles.Unclock(); + int prevuniq = CurrentPortalUniq; // depth check is in another place right now unsigned int portalsAtEnd = WallPortals.Size (); for (; portalsAtStart < portalsAtEnd; portalsAtStart++) { R_EnterPortal (&WallPortals[portalsAtStart], depth + 1); } + CurrentPortalUniq = prevuniq; NetUpdate(); @@ -751,10 +803,13 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) NetUpdate(); + // draw a red line around a portal if it's being highlighted + if (r_highlight_portals) + R_HighlightPortal(pds); + R_3D_LeaveSkybox(); // pop 3D floor height map CurrentPortal = prevpds; MirrorFlags = prevmf; - CurrentPortalUniq = prevuniq; viewangle = startang; viewx = startx; viewy = starty; From 29880026a6dfdc06184d2c505fed36650bd24a6e Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 16 Dec 2014 21:31:21 +0200 Subject: [PATCH 11/21] Fixed sprite clipping with drawsegs (removed unnecessary portal check) --- src/r_main.cpp | 53 +++++++++++++++++++++++++----------------------- src/r_plane.cpp | 16 +++++++++++++-- src/r_segs.cpp | 22 ++++++++++++++++---- src/r_things.cpp | 6 ++++-- 4 files changed, 64 insertions(+), 33 deletions(-) diff --git a/src/r_main.cpp b/src/r_main.cpp index 55cde3d9b..676b59ac3 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -625,32 +625,27 @@ void R_HighlightPortal (PortalDrawseg* pds) // I believe it won't break. if it does, blame me. :( BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 255, 0, 0, 0, 255); - RenderTarget->DrawLine(pds->x1, pds->ceilingclip[0], pds->x1, pds->floorclip[0], color, 0); - RenderTarget->DrawLine(pds->x2, pds->ceilingclip[pds->ceilingclip.Size()-1], pds->x2, pds->floorclip[pds->floorclip.Size()-1], color, 0); BYTE* pixels = RenderTarget->GetBuffer(); // top edge - for (int x = pds->x1+1; x < pds->x2; x++) + 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) + { + RenderTarget->DrawLine(x, Ytop, x, Ybottom, color, 0); + continue; + } + int YtopPrev = pds->ceilingclip[p-1]; int YbottomPrev = pds->floorclip[p-1]; - if (Ytop < 0) Ytop = 0; - if (Ybottom >= RenderTarget->GetHeight()) - Ybottom = RenderTarget->GetHeight()-1; - - if (YtopPrev < 0) YtopPrev = 0; - if (YbottomPrev >= RenderTarget->GetHeight()) - YbottomPrev = RenderTarget->GetHeight()-1; - if (abs(Ytop-YtopPrev) > 1) RenderTarget->DrawLine(x, YtopPrev, x, Ytop, color, 0); else *(pixels + Ytop * RenderTarget->GetPitch() + x) = color; @@ -678,10 +673,6 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) BYTE *dest = RenderTarget->GetBuffer() + x + Ytop * spacing; - if (Ytop < 0) Ytop = 0; - if (Ybottom >= RenderTarget->GetHeight()) - Ybottom = RenderTarget->GetHeight()-1; - for (int y = Ytop; y <= Ybottom; y++) { *dest = color; @@ -698,6 +689,7 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) angle_t startang = viewangle; fixed_t startx = viewx; fixed_t starty = viewy; + fixed_t startz = viewz; CurrentPortalUniq++; @@ -758,17 +750,11 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) CurrentPortal = pds; R_ClearPlanes (false); - R_ClearClipSegs (pds->x1, pds->x2 + 1); // todo: check if this "+1" is actually needed - - // some portals have height differences, account for this here - R_3D_EnterSkybox(); // push 3D floor height map - - memcpy (ceilingclip + pds->x1, &pds->ceilingclip[0], pds->ceilingclip.Size()*sizeof(*ceilingclip)); - memcpy (floorclip + pds->x1, &pds->floorclip[0], pds->floorclip.Size()*sizeof(*floorclip)); + R_ClearClipSegs (pds->x1, pds->x2+1); // todo: check if this "+1" is actually needed WindowLeft = pds->x1; WindowRight = pds->x2; - + // RF_XFLIP should be removed before calling the root function int prevmf = MirrorFlags; if (pds->mirror) @@ -778,6 +764,13 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) else MirrorFlags |= RF_XFLIP; } + // some portals have height differences, account for this here + R_3D_EnterSkybox(); // push 3D floor height map + + // 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) @@ -786,6 +779,8 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) R_DrawSkyBoxes (); PlaneCycles.Unclock(); + fixed_t vzp = viewz; + int prevuniq = CurrentPortalUniq; // depth check is in another place right now unsigned int portalsAtEnd = WallPortals.Size (); @@ -793,6 +788,7 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) { R_EnterPortal (&WallPortals[portalsAtStart], depth + 1); } + int prevuniq2 = CurrentPortalUniq; CurrentPortalUniq = prevuniq; NetUpdate(); @@ -803,16 +799,19 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) 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); - R_3D_LeaveSkybox(); // pop 3D floor height map CurrentPortal = prevpds; MirrorFlags = prevmf; viewangle = startang; viewx = startx; viewy = starty; + viewz = startz; } //========================================================================== @@ -897,6 +896,7 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) WindowRight = viewwidth - 1; MirrorFlags = 0; CurrentPortal = NULL; + CurrentPortalUniq = 0; r_dontmaplines = dontmaplines; @@ -937,6 +937,9 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) R_EnterPortal(&WallPortals[i], 0); } + CurrentPortal = NULL; + CurrentPortalUniq = 0; + NetUpdate (); MaskedCycles.Clock(); diff --git a/src/r_plane.cpp b/src/r_plane.cpp index a19a2f677..ea2678e96 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -689,7 +689,10 @@ visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightl sky == check->sky && CurrentPortalUniq == check->CurrentPortalUniq && MirrorFlags == check->MirrorFlags && - CurrentSkybox == check->CurrentSkybox + CurrentSkybox == check->CurrentSkybox && + viewx == check->viewx && + viewy == check->viewy && + viewz == check->viewz ) { return check; @@ -1064,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; } diff --git a/src/r_segs.cpp b/src/r_segs.cpp index b97a6d4d1..d4eb657c3 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -2591,10 +2591,24 @@ void R_StoreWallRange (int start, int stop) pds.dst = curline->linedef->portal_dst; pds.x1 = ds_p->x1; pds.x2 = ds_p->x2; - pds.ceilingclip.Resize((pds.x2-pds.x1)+1); - memcpy(&pds.ceilingclip[0], openings + ds_p->sprtopclip, pds.ceilingclip.Size()*sizeof(*openings)); - pds.floorclip.Resize((pds.x2-pds.x1)+1); - memcpy(&pds.floorclip[0], openings + ds_p->sprbottomclip, pds.floorclip.Size()*sizeof(*openings)); + pds.len = (pds.x2 - pds.x1) + 1; + 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)+1; 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); } diff --git a/src/r_things.cpp b/src/r_things.cpp index 353961d81..03539eeb6 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -2121,8 +2121,10 @@ void R_DrawSprite (vissprite_t *spr) for (ds = ds_p; ds-- > firstdrawseg; ) // new -- killough { // [ZZ] portal handling here - if (ds->CurrentPortalUniq != spr->CurrentPortalUniq) - continue; + //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; // determine if the drawseg obscures the sprite From b3764f4ee298a0ac91eb91a52730fd88b02cd62b Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Tue, 16 Dec 2014 22:54:49 +0200 Subject: [PATCH 12/21] Fixed trivial bug with two-sided portals not clipping stuff correctly --- src/r_bsp.cpp | 6 ++---- src/r_main.cpp | 2 +- src/r_things.cpp | 10 +++++----- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index 4174ab629..5605272e7 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -589,7 +589,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; } @@ -654,9 +654,7 @@ void R_AddLine (seg_t *line) // Window. solid = false; } - else if (line->linedef->portal // [ZZ] portals are always drawn, even if there's exactly same sector on both sides - - || backsector->lightlevel != frontsector->lightlevel + else if (backsector->lightlevel != frontsector->lightlevel || backsector->GetTexture(sector_t::floor) != frontsector->GetTexture(sector_t::floor) || backsector->GetTexture(sector_t::ceiling) != frontsector->GetTexture(sector_t::ceiling) || curline->sidedef->GetTexture(side_t::mid).isValid() diff --git a/src/r_main.cpp b/src/r_main.cpp index 676b59ac3..ef942734e 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -639,7 +639,7 @@ void R_HighlightPortal (PortalDrawseg* pds) if (x == pds->x1 || x == pds->x2) { - RenderTarget->DrawLine(x, Ytop, x, Ybottom, color, 0); + RenderTarget->DrawLine(x, Ytop, x, Ybottom+1, color, 0); continue; } diff --git a/src/r_things.cpp b/src/r_things.cpp index 03539eeb6..2aa97f7f0 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -2157,7 +2157,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; } @@ -2522,9 +2523,6 @@ static void R_DrawMaskedSegsBehindParticle (const vissprite_t *vis) for (unsigned int p = InterestingDrawsegs.Size(); p-- > FirstInterestingDrawseg; ) { drawseg_t *ds = &drawsegs[InterestingDrawsegs[p]]; - // [ZZ] only draw stuff that's inside the same portal as the particle, other portals will care for themselves - if (ds->CurrentPortalUniq != vis->CurrentPortalUniq) - continue; // kg3D - no fake segs if (ds->fake) continue; if (ds->x1 >= x2 || ds->x2 < x1) @@ -2533,7 +2531,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-1)); + // [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-1)); } } } From 31cad858d930552f8b531d0c5d0dd9f7751993b8 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 7 Jun 2015 20:54:36 +0300 Subject: [PATCH 13/21] Fixed odd merge bugs --- src/CMakeLists.txt | 1 + src/portal.cpp | 390 +++++++++++++++++++++++++++++++++++++++++++++ src/portal.h | 73 +++++++++ 3 files changed, 464 insertions(+) create mode 100644 src/portal.cpp create mode 100644 src/portal.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index af1f04f49..f0bbdebcc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -951,6 +951,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/portal.cpp b/src/portal.cpp new file mode 100644 index 000000000..0a05e83ae --- /dev/null +++ b/src/portal.cpp @@ -0,0 +1,390 @@ +#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" + +// simulation recurions maximum +CVAR(Int, sv_portal_recursions, 4, CVAR_ARCHIVE|CVAR_SERVERINFO) + +PortalDrawseg* CurrentPortal = NULL; +int CurrentPortalUniq = 0; + +// [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; +} + +bool P_ClipLineToPortal(line_t* line, line_t* portal, fixed_t viewx, fixed_t viewy, bool partial, bool samebehind) +{ + // we do this only if simple check doesn't clip away the line + 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) + return true; + + if ((behind1 || behind2) && partial) + { + fixed_t v1x = line->v1->x; + fixed_t v1y = line->v1->y; + fixed_t v2x = line->v2->x; + fixed_t v2y = line->v2->y; + + fixed_t check1X = viewx; + fixed_t check1Y = viewy; + + fixed_t check2X = portal->v2->x; + fixed_t check2Y = portal->v2->y; + + fixed_t in1x = v1x; + fixed_t in1y = v1y; + + bool i1 = R_IntersectLines(check1X, check1Y, check2X, check2Y, v1x, v1y, v2x, v2y, in1x, in1y); + + check2X = portal->v1->x; + check2Y = portal->v1->y; + + fixed_t in2x = v2x; + fixed_t in2y = v2y; + + bool i2 = R_IntersectLines(check1X, check1Y, check2X, check2Y, v1x, v1y, v2x, v2y, in2x, in2y); + + //if (log) Printf("intersection = %d [at %.2f, %.2f]; %d [at %.2f, %.2f]\n", i1, FIXED2FLOAT(in1x), FIXED2FLOAT(in1y), i2, FIXED2FLOAT(in2x), FIXED2FLOAT(in2y)); + + // now, to cull the shitty line that obstructs the view, we check if any of intersected points are on behind of the portal :) + behind1 = !!P_PointOnLineSide(in1x, in1y, portal); + behind2 = !!P_PointOnLineSide(in2x, in2y, portal); + + if (behind1 && behind2) + 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 (lines[i].id == 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 000000000..5897622fd --- /dev/null +++ b/src/portal.h @@ -0,0 +1,73 @@ +#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; + +/* 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 From d0df4c1744a8b30c147e82b82e1f5a5f27cefc2d Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 7 Jun 2015 21:27:20 +0300 Subject: [PATCH 14/21] Upgraded the portal code to work with the new tagManager system. Was uncompilable --- src/p_setup.cpp | 2 +- src/portal.cpp | 3 ++- src/r_things.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 51cbdf76e..fc31db411 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1964,7 +1964,7 @@ void P_SetLineID (int i, line_t *ld) case Line_SetPortal: case Line_SetVisualPortal: - ld->id = ld->args[1]; // 0 = target id, 1 = this id, 2 = plane anchor + setid = ld->args[1]; // 0 = target id, 1 = this id, 2 = plane anchor break; } if (setid != -1) diff --git a/src/portal.cpp b/src/portal.cpp index 0a05e83ae..055603d8c 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -5,6 +5,7 @@ #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) @@ -127,7 +128,7 @@ bool P_CheckPortal(line_t* line) { if (&lines[i] == line) continue; - if (lines[i].id == line->args[0]) + if (tagManager.LineHasID(&lines[i], line->args[0])) { dst = &lines[i]; break; diff --git a/src/r_things.cpp b/src/r_things.cpp index 2aa97f7f0..d5c0489b3 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -2597,7 +2597,7 @@ void R_DrawParticle (vissprite_t *vis) { DWORD bg = bg2rgb[*dest]; bg = (fg+bg) | 0x1f07c1f; - *dest = RGB32k[0][0][bg & (bg>>15)]; + *dest = RGB32k.All[bg & (bg>>15)]; dest += spacing; } } From 7bfce1d1763f49719d8b4183aa5f710b16cbf348 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Thu, 8 Oct 2015 21:16:30 +0300 Subject: [PATCH 15/21] Fixed crash in R_ClipSpriteColumnWithPortals if seg->curline is null --- src/r_things.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/r_things.cpp b/src/r_things.cpp index d5c0489b3..cbcfbc295 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -331,6 +331,10 @@ bool R_ClipSpriteColumnWithPortals (fixed_t x, fixed_t y, vissprite_t* spr) 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; From e669e3cd082a43acd66eefd178f0c05686eaedc0 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 9 Oct 2015 16:03:59 +0300 Subject: [PATCH 16/21] Cosmetic fixes to actionspecials.h --- src/actionspecials.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/actionspecials.h b/src/actionspecials.h index 879f16a11..2a0ada28f 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -136,11 +136,14 @@ 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 // to an error message. -DEFINE_SPECIAL(SetGlobalFogParameter, 157, 2, 2, 2) // vavoom? interferes with Line_SetVisualPortal +DEFINE_SPECIAL(SetGlobalFogParameter, 157, 2, 2, 2) DEFINE_SPECIAL(FS_Execute, 158, 1, 4, 4) DEFINE_SPECIAL(Sector_SetPlaneReflection, 159, 3, 3, 3) DEFINE_SPECIAL(Sector_Set3DFloor, 160, -1, -1, 5) @@ -236,7 +239,4 @@ DEFINE_SPECIAL(Ceiling_LowerToLowest, 253, 2, 2, 2) DEFINE_SPECIAL(Ceiling_LowerToFloor, 254, 2, 2, 2) DEFINE_SPECIAL(Ceiling_CrushRaiseAndStaySilA, 255, 4, 5, 5) -DEFINE_SPECIAL(Line_SetVisualPortal, 155, -1, -1, 3) -DEFINE_SPECIAL(Line_SetPortal, 156, -1, -1, 3) - #undef DEFINE_SPECIAL From 75d21d6dd81671960158f2c04fc237da373ba906 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Thu, 12 Nov 2015 01:17:03 +0200 Subject: [PATCH 17/21] ZDoom wasn't setting drawseg_t::curline when making "fake" drawseg for skyboxes, which interfered with portal sprite clipping routine randomly. --- src/r_plane.cpp | 1 + src/r_things.cpp | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/r_plane.cpp b/src/r_plane.cpp index ea2678e96..394a77993 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -1294,6 +1294,7 @@ void R_DrawSkyBoxes () ds_p->sprtopclip = R_NewOpening (pl->maxx - pl->minx + 1); ds_p->maskedtexturecol = ds_p->swall = -1; ds_p->bFogBoundary = false; + ds_p->curline = NULL; memcpy (openings + ds_p->sprbottomclip, floorclip + pl->minx, (pl->maxx - pl->minx + 1)*sizeof(short)); memcpy (openings + ds_p->sprtopclip, ceilingclip + pl->minx, (pl->maxx - pl->minx + 1)*sizeof(short)); diff --git a/src/r_things.cpp b/src/r_things.cpp index cbcfbc295..3a6773c70 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -322,11 +322,8 @@ nextpost: // bool R_ClipSpriteColumnWithPortals (fixed_t x, fixed_t y, vissprite_t* spr) { - size_t numdrawsegs = ds_p-firstdrawseg; - for (int i = (int)numdrawsegs-1; i >= 0; i--) + for (drawseg_t* seg = ds_p; seg-- > firstdrawseg; ) // copied code from killough below { - drawseg_t* seg = &firstdrawseg[i]; - // ignore segs from other portals if (seg->CurrentPortalUniq != CurrentPortalUniq) continue; From 4d518a220c281d1d408a3e7b1dd5029a499e8c0b Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 10 Jan 2016 05:22:33 +0200 Subject: [PATCH 18/21] Fixed skybox clipping: lines in a skybox shouldn't be clipped against current portal --- src/portal.cpp | 47 ++++++++++++----------------------------------- src/portal.h | 1 + src/r_bsp.cpp | 3 ++- src/r_main.cpp | 2 +- src/r_plane.cpp | 2 ++ src/r_things.cpp | 2 +- 6 files changed, 19 insertions(+), 38 deletions(-) diff --git a/src/portal.cpp b/src/portal.cpp index 055603d8c..1e3bf30a8 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -12,6 +12,7 @@ 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, @@ -48,9 +49,14 @@ bool R_IntersectLines(fixed_t o1x, fixed_t o1y, fixed_t p1x, fixed_t p1y, 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) { - // we do this only if simple check doesn't clip away the line + // 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); @@ -64,41 +70,12 @@ bool P_ClipLineToPortal(line_t* line, line_t* portal, fixed_t viewx, fixed_t vie behind2 = samebehind; if (behind1 && behind2) - return true; - - if ((behind1 || behind2) && partial) { - fixed_t v1x = line->v1->x; - fixed_t v1y = line->v1->y; - fixed_t v2x = line->v2->x; - fixed_t v2y = line->v2->y; - - fixed_t check1X = viewx; - fixed_t check1Y = viewy; - - fixed_t check2X = portal->v2->x; - fixed_t check2Y = portal->v2->y; - - fixed_t in1x = v1x; - fixed_t in1y = v1y; - - bool i1 = R_IntersectLines(check1X, check1Y, check2X, check2Y, v1x, v1y, v2x, v2y, in1x, in1y); - - check2X = portal->v1->x; - check2Y = portal->v1->y; - - fixed_t in2x = v2x; - fixed_t in2y = v2y; - - bool i2 = R_IntersectLines(check1X, check1Y, check2X, check2Y, v1x, v1y, v2x, v2y, in2x, in2y); - - //if (log) Printf("intersection = %d [at %.2f, %.2f]; %d [at %.2f, %.2f]\n", i1, FIXED2FLOAT(in1x), FIXED2FLOAT(in1y), i2, FIXED2FLOAT(in2x), FIXED2FLOAT(in2y)); - - // now, to cull the shitty line that obstructs the view, we check if any of intersected points are on behind of the portal :) - behind1 = !!P_PointOnLineSide(in1x, in1y, portal); - behind2 = !!P_PointOnLineSide(in2x, in2y, portal); - - 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; } diff --git a/src/portal.h b/src/portal.h index 5897622fd..103e3072b 100644 --- a/src/portal.h +++ b/src/portal.h @@ -26,6 +26,7 @@ struct PortalDrawseg 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); diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index 5605272e7..8a7e5c560 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -555,7 +555,8 @@ void R_AddLine (seg_t *line) } // reject lines that aren't seen from the portal (if any) - if (CurrentPortal && P_ClipLineToPortal(line->linedef, CurrentPortal->dst, viewx, viewy)) + // [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; diff --git a/src/r_main.cpp b/src/r_main.cpp index ef942734e..c0fe5819c 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -659,7 +659,7 @@ void R_HighlightPortal (PortalDrawseg* pds) 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) + if (depth >= r_portal_recursions) { BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 0, 0, 0, 0, 255); int spacing = RenderTarget->GetPitch(); diff --git a/src/r_plane.cpp b/src/r_plane.cpp index 394a77993..ed013b821 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -1193,6 +1193,7 @@ void R_DrawSkyBoxes () return; R_3D_EnterSkybox(); + CurrentPortalInSkybox = true; int savedextralight = extralight; fixed_t savedx = viewx; @@ -1367,6 +1368,7 @@ void R_DrawSkyBoxes () viewangle = savedangle; R_SetViewAngle (); + CurrentPortalInSkybox = false; R_3D_LeaveSkybox(); if(fakeActive) return; diff --git a/src/r_things.cpp b/src/r_things.cpp index 3a6773c70..9c3d5fe42 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -320,7 +320,7 @@ nextpost: // [ZZ] // R_ClipSpriteColumnWithPortals // -bool R_ClipSpriteColumnWithPortals (fixed_t x, fixed_t y, vissprite_t* spr) +static inline bool R_ClipSpriteColumnWithPortals (fixed_t x, fixed_t y, vissprite_t* spr) { for (drawseg_t* seg = ds_p; seg-- > firstdrawseg; ) // copied code from killough below { From 605fce9008112b3b6a59b4a4d0ccf8d1110972d7 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 10 Jan 2016 05:45:26 +0200 Subject: [PATCH 19/21] Fixed skybox clipping: things in a skybox shouldn't be clipped against current portal. Fixed: portal inside of a skybox sets CurrentPortalInSkybox to false. --- src/portal.cpp | 730 +++++++++++++++++++++++------------------------ src/r_main.cpp | 1 + src/r_things.cpp | 3 +- 3 files changed, 368 insertions(+), 366 deletions(-) diff --git a/src/portal.cpp b/src/portal.cpp index 1e3bf30a8..7e8405d57 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -1,368 +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; -} - +#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 -} - +} + +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/r_main.cpp b/src/r_main.cpp index c0fe5819c..9af2e00b4 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -766,6 +766,7 @@ void R_EnterPortal (PortalDrawseg* pds, int depth) // 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)); diff --git a/src/r_things.cpp b/src/r_things.cpp index 9c3d5fe42..cb4c1f00b 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -716,7 +716,8 @@ void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor } // [ZZ] Or less definitely not visible (hue) - if (CurrentPortal && !!P_PointOnLineSide(thing->x, thing->y, CurrentPortal->dst)) + // [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 From 4bbec2a7fe7df584a22d1002677c7eb7fc2171f6 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 10 Jan 2016 05:48:06 +0200 Subject: [PATCH 20/21] Fixed skybox clipping: sprite columns shouldn't be clipped against the current portal if rendered on root level of a skybox. --- src/r_things.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/r_things.cpp b/src/r_things.cpp index cb4c1f00b..12d808075 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -322,6 +322,10 @@ nextpost: // 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 From b6cdcddf30c4498415d9bfe036eae10045135fbf Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 18 Jan 2016 10:30:56 -0500 Subject: [PATCH 21/21] - Fixed: Static mirrors in hexen format maps were broken. --- src/p_setup.cpp | 8 +++----- src/p_udmf.cpp | 5 ----- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index f92e1b9d2..fc929540d 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -2068,6 +2068,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 () @@ -2177,11 +2180,6 @@ void P_LoadLineDefs (MapData * map) if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; if (level.flags2 & LEVEL2_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE; } - - // [ZZ] check initial portal link - for (int i = 0; i < numlines; i++) - P_CheckPortal(&lines[i]); - delete[] mldf; } diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 5b6c0b60f..366a56dcf 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -1792,11 +1792,6 @@ public: P_AdjustLine(&lines[line]); P_FinishLoadingLineDef(&lines[line], tempalpha[0]); } - - // [ZZ] check initial portal link - for (int i = 0; i < numlines; i++) - P_CheckPortal(&lines[i]); - assert(side <= numsides); if (side < numsides) {