From cc3ac9ea0536ec201e964156d910d896d2cdd77c Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Fri, 25 Nov 2016 23:44:55 +0100 Subject: [PATCH] Line portal rendering --- src/r_poly_portal.cpp | 136 ++++++++++++++++++++++++++++++++++++++++-- src/r_poly_portal.h | 20 +++++-- src/r_poly_wall.cpp | 79 +++++++++++++++++++++++- src/r_poly_wall.h | 3 +- 4 files changed, 228 insertions(+), 10 deletions(-) diff --git a/src/r_poly_portal.cpp b/src/r_poly_portal.cpp index d6a67a317..e4ce5c547 100644 --- a/src/r_poly_portal.cpp +++ b/src/r_poly_portal.cpp @@ -23,6 +23,7 @@ #include #include "templates.h" #include "doomdef.h" +#include "p_maputl.h" #include "sbar.h" #include "r_data/r_translate.h" #include "r_poly_portal.h" @@ -32,6 +33,8 @@ CVAR(Bool, r_debug_cull, 0, 0) EXTERN_CVAR(Int, r_portal_recursions) +extern bool r_showviewer; + ///////////////////////////////////////////////////////////////////////////// RenderPolyPortal::RenderPolyPortal() @@ -185,7 +188,7 @@ void RenderPolyPortal::RenderLine(subsector_t *sub, seg_t *line, sector_t *front } // Render wall, and update culling info if its an occlusion blocker - if (RenderPolyWall::RenderLine(WorldToClip, line, frontsector, subsectorDepth, StencilValue, SubsectorTranslucentWalls)) + if (RenderPolyWall::RenderLine(WorldToClip, line, frontsector, subsectorDepth, StencilValue, SubsectorTranslucentWalls, LinePortals)) { if (hasSegmentRange) Cull.MarkSegmentCulled(sx1, sx2); @@ -362,19 +365,144 @@ void PolyDrawSectorPortal::RestoreGlobals() ///////////////////////////////////////////////////////////////////////////// -PolyDrawLinePortal::PolyDrawLinePortal(line_t *src, line_t *dest, bool mirror) : Src(src), Dest(dest) +PolyDrawLinePortal::PolyDrawLinePortal(FLinePortal *portal) : Portal(portal) +{ + StencilValue = RenderPolyScene::Instance()->GetNextStencilValue(); +} + +PolyDrawLinePortal::PolyDrawLinePortal(line_t *mirror) : Mirror(mirror) { - // To do: do what R_EnterPortal and PortalDrawseg does - StencilValue = RenderPolyScene::Instance()->GetNextStencilValue(); } void PolyDrawLinePortal::Render(int portalDepth) { + SaveGlobals(); + + // To do: get this information from RenderPolyScene instead of duplicating the code.. + double radPitch = ViewPitch.Normalized180().Radians(); + double angx = cos(radPitch); + double angy = sin(radPitch) * glset.pixelstretch; + double alen = sqrt(angx*angx + angy*angy); + float adjustedPitch = (float)asin(angy / alen); + float adjustedViewAngle = (float)(ViewAngle - 90).Radians(); + float ratio = WidescreenRatio; + float fovratio = (WidescreenRatio >= 1.3f) ? 1.333333f : ratio; + float fovy = (float)(2 * DAngle::ToDegrees(atan(tan(FieldOfView.Radians() / 2) / fovratio)).Degrees); + TriMatrix worldToView = + TriMatrix::rotate(adjustedPitch, 1.0f, 0.0f, 0.0f) * + TriMatrix::rotate(adjustedViewAngle, 0.0f, -1.0f, 0.0f) * + TriMatrix::scale(1.0f, glset.pixelstretch, 1.0f) * + TriMatrix::swapYZ() * + TriMatrix::translate((float)-ViewPos.X, (float)-ViewPos.Y, (float)-ViewPos.Z); + TriMatrix worldToClip = TriMatrix::perspective(fovy, ratio, 5.0f, 65535.0f) * worldToView; + + RenderPortal.SetViewpoint(worldToClip, StencilValue); RenderPortal.Render(portalDepth); + + RestoreGlobals(); } void PolyDrawLinePortal::RenderTranslucent(int portalDepth) { + SaveGlobals(); RenderPortal.RenderTranslucent(portalDepth); + RestoreGlobals(); +} + +void PolyDrawLinePortal::SaveGlobals() +{ + savedextralight = extralight; + savedpos = ViewPos; + savedangle = ViewAngle; + savedcamera = camera; + savedsector = viewsector; + savedvisibility = camera ? camera->renderflags & RF_INVISIBLE : ActorRenderFlags::FromInt(0); + savedViewPath[0] = ViewPath[0]; + savedViewPath[1] = ViewPath[1]; + + if (Mirror) + { + DAngle startang = ViewAngle; + DVector3 startpos = ViewPos; + + vertex_t *v1 = Mirror->v1; + + // Reflect the current view behind the mirror. + if (Mirror->Delta().X == 0) + { // vertical mirror + ViewPos.X = v1->fX() - startpos.X + v1->fX(); + } + else if (Mirror->Delta().Y == 0) + { // horizontal mirror + ViewPos.Y = v1->fY() - startpos.Y + v1->fY(); + } + else + { // any mirror + vertex_t *v2 = Mirror->v2; + + double dx = v2->fX() - v1->fX(); + double dy = v2->fY() - v1->fY(); + double x1 = v1->fX(); + double y1 = v1->fY(); + double x = startpos.X; + double y = startpos.Y; + + // the above two cases catch len == 0 + double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); + + ViewPos.X = (x1 + r * dx) * 2 - x; + ViewPos.Y = (y1 + r * dy) * 2 - y; + } + ViewAngle = Mirror->Delta().Angle() * 2 - startang; + } + else + { + auto src = Portal->mOrigin; + auto dst = Portal->mDestination; + + P_TranslatePortalXY(src, ViewPos.X, ViewPos.Y); + P_TranslatePortalZ(src, ViewPos.Z); + P_TranslatePortalAngle(src, ViewAngle); + P_TranslatePortalXY(src, ViewPath[0].X, ViewPath[0].Y); + P_TranslatePortalXY(src, ViewPath[1].X, ViewPath[1].Y); + + if (!r_showviewer && camera && P_PointOnLineSidePrecise(ViewPath[0], dst) != P_PointOnLineSidePrecise(ViewPath[1], dst)) + { + double distp = (ViewPath[0] - ViewPath[1]).Length(); + if (distp > EQUAL_EPSILON) + { + double dist1 = (ViewPos - ViewPath[0]).Length(); + double dist2 = (ViewPos - ViewPath[1]).Length(); + + if (dist1 + dist2 < distp + 1) + { + camera->renderflags |= RF_INVISIBLE; + } + } + } + + /*if (Portal->mirror) + { + if (MirrorFlags & RF_XFLIP) MirrorFlags &= ~RF_XFLIP; + else MirrorFlags |= RF_XFLIP; + }*/ + } + + camera = nullptr; + //viewsector = Portal->mDestination; + R_SetViewAngle(); +} + +void PolyDrawLinePortal::RestoreGlobals() +{ + if (!savedvisibility && camera) camera->renderflags &= ~RF_INVISIBLE; + camera = savedcamera; + viewsector = savedsector; + ViewPos = savedpos; + extralight = savedextralight; + ViewAngle = savedangle; + ViewPath[0] = savedViewPath[0]; + ViewPath[1] = savedViewPath[1]; + R_SetViewAngle(); } diff --git a/src/r_poly_portal.h b/src/r_poly_portal.h index a53a185e3..e47277f32 100644 --- a/src/r_poly_portal.h +++ b/src/r_poly_portal.h @@ -127,7 +127,7 @@ public: void Render(int portalDepth); void RenderTranslucent(int portalDepth); - FSectorPortal *Portal; + FSectorPortal *Portal = nullptr; uint32_t StencilValue = 0; std::vector Shape; @@ -149,16 +149,28 @@ private: class PolyDrawLinePortal { public: - PolyDrawLinePortal(line_t *src, line_t *dest, bool mirror); + PolyDrawLinePortal(FLinePortal *portal); + PolyDrawLinePortal(line_t *mirror); void Render(int portalDepth); void RenderTranslucent(int portalDepth); + FLinePortal *Portal = nullptr; + line_t *Mirror = nullptr; uint32_t StencilValue = 0; std::vector Shape; private: - line_t *Src; - line_t *Dest; + void SaveGlobals(); + void RestoreGlobals(); + RenderPolyPortal RenderPortal; + + int savedextralight; + DVector3 savedpos; + DAngle savedangle; + AActor *savedcamera; + sector_t *savedsector; + ActorRenderFlags savedvisibility; + DVector3 savedViewPath[2]; }; diff --git a/src/r_poly_wall.cpp b/src/r_poly_wall.cpp index 8f3fb05d7..7484fd493 100644 --- a/src/r_poly_wall.cpp +++ b/src/r_poly_wall.cpp @@ -23,6 +23,9 @@ #include #include "templates.h" #include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" +#include "p_lnspec.h" #include "sbar.h" #include "r_data/r_translate.h" #include "r_poly_wall.h" @@ -30,8 +33,82 @@ #include "r_poly.h" #include "r_sky.h" // for skyflatnum -bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector &translucentWallsOutput) +EXTERN_CVAR(Bool, r_drawmirrors) + +bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector &translucentWallsOutput, std::vector> &linePortals) { + PolyDrawLinePortal *polyportal = nullptr; + if (line->backsector == nullptr && line->sidedef == line->linedef->sidedef[0] && (line->linedef->special == Line_Mirror && r_drawmirrors)) + { + linePortals.push_back(std::make_unique(line->linedef)); + polyportal = linePortals.back().get(); + } + else if (line->linedef && line->linedef->isVisualPortal()) + { + FLinePortal *portal = line->linedef->getPortal(); + for (auto &p : linePortals) + { + if (p->Portal == portal) // To do: what other criterias do we need to check for? + { + polyportal = p.get(); + break; + } + } + if (!polyportal) + { + linePortals.push_back(std::make_unique(portal)); + polyportal = linePortals.back().get(); + } + } + + if (polyportal) + { + double ceil1 = frontsector->ceilingplane.ZatPoint(line->v1); + double floor1 = frontsector->floorplane.ZatPoint(line->v1); + double ceil2 = frontsector->ceilingplane.ZatPoint(line->v2); + double floor2 = frontsector->floorplane.ZatPoint(line->v2); + DVector2 v1 = line->v1->fPos(); + DVector2 v2 = line->v2->fPos(); + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return true; + + vertices[0].x = (float)v1.X; + vertices[0].y = (float)v1.Y; + vertices[0].z = (float)ceil1; + vertices[0].w = 1.0f; + + vertices[1].x = (float)v2.X; + vertices[1].y = (float)v2.Y; + vertices[1].z = (float)ceil2; + vertices[1].w = 1.0f; + + vertices[2].x = (float)v2.X; + vertices[2].y = (float)v2.Y; + vertices[2].z = (float)floor2; + vertices[2].w = 1.0f; + + vertices[3].x = (float)v1.X; + vertices[3].y = (float)v1.Y; + vertices[3].z = (float)floor1; + vertices[3].w = 1.0f; + + PolyDrawArgs args; + args.uniforms.flags = 0; + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = polyportal->StencilValue; + PolyTriangleDrawer::draw(args, TriDrawVariant::Stencil, TriBlendMode::Copy); + + polyportal->Shape.push_back({ vertices, 4, true, subsectorDepth }); + return true; + } + RenderPolyWall wall; wall.LineSeg = line; wall.Line = line->linedef; diff --git a/src/r_poly_wall.h b/src/r_poly_wall.h index 3d2f89f84..31c236dd4 100644 --- a/src/r_poly_wall.h +++ b/src/r_poly_wall.h @@ -25,11 +25,12 @@ #include "r_poly_triangle.h" class PolyTranslucentObject; +class PolyDrawLinePortal; class RenderPolyWall { public: - static bool RenderLine(const TriMatrix &worldToClip, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector &translucentWallsOutput); + static bool RenderLine(const TriMatrix &worldToClip, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector &translucentWallsOutput, std::vector> &linePortals); static void Render3DFloorLine(const TriMatrix &worldToClip, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector &translucentWallsOutput); void SetCoords(const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2);