From 6ebec37baf316369706aee0ac456aabbb702feb4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 23 Jun 2018 20:57:02 +0200 Subject: [PATCH] - moved all portal code that sets up a scene to be rendered into API independent code and let it be handled by a common wrapper class. --- src/gl/scene/gl_drawinfo.cpp | 38 +-- src/gl/scene/gl_drawinfo.h | 16 +- src/gl/scene/gl_portal.cpp | 481 --------------------------- src/gl/scene/gl_portal.h | 166 +-------- src/gl/scene/gl_scene.cpp | 1 - src/gl/scene/gl_walls_draw.cpp | 14 +- src/hwrenderer/scene/hw_bsp.cpp | 4 +- src/hwrenderer/scene/hw_drawinfo.cpp | 4 +- src/hwrenderer/scene/hw_drawinfo.h | 10 +- src/hwrenderer/scene/hw_portal.cpp | 480 ++++++++++++++++++++++++++ src/hwrenderer/scene/hw_portal.h | 197 ++++++++++- src/portal.h | 5 - src/r_data/portalgroups.cpp | 1 - 13 files changed, 728 insertions(+), 689 deletions(-) diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index de3cd91ee06..1aa3eac5770 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -175,23 +175,6 @@ void FDrawInfoList::Release(FDrawInfo * di) mList.Push(di); } -//========================================================================== -// -// -// -//========================================================================== - -FDrawInfo::FDrawInfo() -{ - next = NULL; -} - -FDrawInfo::~FDrawInfo() -{ - ClearBuffers(); -} - - //========================================================================== // // Sets up a new drawinfo struct @@ -225,7 +208,7 @@ void FDrawInfo::StartScene() { ClearBuffers(); - next = gl_drawinfo; + outer = gl_drawinfo; gl_drawinfo = this; for (int i = 0; i < GLDL_TYPES; i++) drawlists[i].Reset(); decals[0].Clear(); @@ -233,7 +216,7 @@ void FDrawInfo::StartScene() hudsprites.Clear(); // Fullbright information needs to be propagated from the main view. - if (next != nullptr) FullbrightFlags = next->FullbrightFlags; + if (outer != nullptr) FullbrightFlags = outer->FullbrightFlags; else FullbrightFlags = 0; } @@ -247,7 +230,7 @@ FDrawInfo *FDrawInfo::EndDrawInfo() { assert(this == gl_drawinfo); for(int i=0;i(outer); di_list.Release(this); if (gl_drawinfo == nullptr) ResetRenderDataAllocator(); @@ -499,9 +482,15 @@ void FDrawInfo::FloodLowerGap(seg_t * seg) } // Same here for the dependency on the portal. -void FDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub) +void FDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *ptg, subsector_t *sub) { - portal->GetRenderState()->AddSubsector(sub); + auto portal = GLRenderer->mPortalState.FindPortal(ptg); + if (!portal) + { + portal = new GLScenePortal(&GLRenderer->mPortalState, new HWSectorStackPortal(ptg)); + } + auto ptl = static_cast(static_cast(portal)->mScene); + ptl->AddSubsector(sub); } std::pair FDrawInfo::AllocVertices(unsigned int count) @@ -523,5 +512,10 @@ int FDrawInfo::UploadLights(FDynLightData &data) return GLRenderer->mLights->UploadLights(data); } +bool FDrawInfo::SetDepthClamp(bool on) +{ + return gl_RenderState.SetDepthClamp(on); +} + diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h index ec0a6ed85e1..e41e718a238 100644 --- a/src/gl/scene/gl_drawinfo.h +++ b/src/gl/scene/gl_drawinfo.h @@ -35,14 +35,10 @@ enum Drawpasses struct FDrawInfo : public HWDrawInfo { - FDrawInfo * next; HWDrawList drawlists[GLDL_TYPES]; TArray hudsprites; // These may just be stored by value. TArray decals[2]; // the second slot is for mirrors which get rendered in a separate pass. - FDrawInfo(); - ~FDrawInfo(); - void ApplyVPUniforms() override; void AddWall(GLWall *wall) override; @@ -56,15 +52,6 @@ struct FDrawInfo : public HWDrawInfo std::pair AllocVertices(unsigned int count) override; int UploadLights(FDynLightData &data) override; - // Legacy GL only. - bool PutWallCompat(GLWall *wall, int passflag); - bool PutFlatCompat(GLFlat *flat, bool fog); - void RenderFogBoundaryCompat(GLWall *wall); - void RenderLightsCompat(GLWall *wall, int pass); - void DrawSubsectorLights(GLFlat *flat, subsector_t * sub, int pass); - void DrawLightsCompat(GLFlat *flat, int pass); - - void DrawDecal(GLDecal *gldecal); void DrawDecals(); void DrawDecalsForMirror(GLWall *wall); @@ -111,7 +98,8 @@ struct FDrawInfo : public HWDrawInfo void ProcessScene(bool toscreen = false); void EndDrawScene(sector_t * viewsector); void DrawEndScene2D(sector_t * viewsector); - + bool SetDepthClamp(bool on) override; + static FDrawInfo *StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms); FDrawInfo *EndDrawInfo(); diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 201d7bf2fa4..a4bb2b16b16 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -222,34 +222,6 @@ bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDraw } -inline void GLPortal::ClearClipper(HWDrawInfo *di) -{ - auto outer_di = static_cast(di)->next; - DAngle angleOffset = deltaangle(outer_di->Viewpoint.Angles.Yaw, di->Viewpoint.Angles.Yaw); - - di->mClipper->Clear(); - - // Set the clipper to the minimal visible area - di->mClipper->SafeAddClipRange(0,0xffffffff); - for (unsigned int i = 0; i < lines.Size(); i++) - { - DAngle startAngle = (DVector2(lines[i].glseg.x2, lines[i].glseg.y2) - outer_di->Viewpoint.Pos).Angle() + angleOffset; - DAngle endAngle = (DVector2(lines[i].glseg.x1, lines[i].glseg.y1) - outer_di->Viewpoint.Pos).Angle() + angleOffset; - - if (deltaangle(endAngle, startAngle) < 0) - { - di->mClipper->SafeRemoveClipRangeRealAngles(startAngle.BAMs(), endAngle.BAMs()); - } - } - - // and finally clip it to the visible area - angle_t a1 = di->FrustumAngle(); - if (a1 < ANGLE_180) di->mClipper->SafeAddClipRangeRealAngles(di->Viewpoint.Angles.Yaw.BAMs() + a1, di->Viewpoint.Angles.Yaw.BAMs() - a1); - - // lock the parts that have just been clipped out. - di->mClipper->SetSilhouette(); -} - //----------------------------------------------------------------------------- // // End @@ -346,454 +318,12 @@ void GLPortal::End(HWDrawInfo *di, bool usestencil) } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// -// -// Skybox Portal -// -// -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// -// GLSkyboxPortal::DrawContents -// -//----------------------------------------------------------------------------- - -void GLSkyboxPortal::DrawContents(HWDrawInfo *di) -{ - int old_pm = mState->PlaneMirrorMode; - - if (mState->skyboxrecursion >= 3) - { - ClearScreen(di); - return; - } - auto &vp = di->Viewpoint; - - mState->skyboxrecursion++; - AActor *origin = portal->mSkybox; - portal->mFlags |= PORTSF_INSKYBOX; - vp.extralight = 0; - - mState->PlaneMirrorMode = 0; - - bool oldclamp = gl_RenderState.SetDepthClamp(false); - vp.Pos = origin->InterpolatedPosition(vp.TicFrac); - vp.ActorPos = origin->Pos(); - vp.Angles.Yaw += (origin->PrevAngles.Yaw + deltaangle(origin->PrevAngles.Yaw, origin->Angles.Yaw) * vp.TicFrac); - - // Don't let the viewpoint be too close to a floor or ceiling - double floorh = origin->Sector->floorplane.ZatPoint(origin->Pos()); - double ceilh = origin->Sector->ceilingplane.ZatPoint(origin->Pos()); - if (vp.Pos.Z < floorh + 4) vp.Pos.Z = floorh + 4; - if (vp.Pos.Z > ceilh - 4) vp.Pos.Z = ceilh - 4; - - vp.ViewActor = origin; - - mState->inskybox = true; - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag & 1), !!(mState->PlaneMirrorFlag & 1)); - di->SetViewArea(); - ClearClipper(di); - - di->UpdateCurrentMapSection(); - - static_cast(di)->DrawScene(DM_PORTAL); - portal->mFlags &= ~PORTSF_INSKYBOX; - mState->inskybox = false; - gl_RenderState.SetDepthClamp(oldclamp); - mState->skyboxrecursion--; - - mState->PlaneMirrorMode = old_pm; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// -// -// Sector stack Portal -// -// -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - - -//========================================================================== -// -// Fixme: This needs abstraction. -// -//========================================================================== - -GLSectorStackPortal *FSectorPortalGroup::GetRenderState() -{ - if (glportal == nullptr) glportal = new GLSectorStackPortal(&GLRenderer->mPortalState, this); - return glportal; -} - - - -GLSectorStackPortal::~GLSectorStackPortal() -{ - if (origin != nullptr && origin->glportal == this) - { - origin->glportal = nullptr; - } -} - -//----------------------------------------------------------------------------- -// -// GLSectorStackPortal::SetupCoverage -// -//----------------------------------------------------------------------------- - -static uint8_t SetCoverage(HWDrawInfo *di, void *node) -{ - if (level.nodes.Size() == 0) - { - return 0; - } - if (!((size_t)node & 1)) // Keep going until found a subsector - { - node_t *bsp = (node_t *)node; - uint8_t coverage = SetCoverage(di, bsp->children[0]) | SetCoverage(di, bsp->children[1]); - di->no_renderflags[bsp->Index()] = coverage; - return coverage; - } - else - { - subsector_t *sub = (subsector_t *)((uint8_t *)node - 1); - return di->ss_renderflags[sub->Index()] & SSRF_SEEN; - } -} - -void GLSectorStackPortal::SetupCoverage(HWDrawInfo *di) -{ - for(unsigned i=0; iplane; - for(int j=0;jportalcoverage[plane].sscount; j++) - { - subsector_t *dsub = &::level.subsectors[sub->portalcoverage[plane].subsectors[j]]; - di->CurrentMapSections.Set(dsub->mapsection); - di->ss_renderflags[dsub->Index()] |= SSRF_SEEN; - } - } - SetCoverage(di, ::level.HeadNode()); -} - -//----------------------------------------------------------------------------- -// -// GLSectorStackPortal::DrawContents -// -//----------------------------------------------------------------------------- -void GLSectorStackPortal::DrawContents(HWDrawInfo *di) -{ - FSectorPortalGroup *portal = origin; - auto &vp = di->Viewpoint; - - vp.Pos += origin->mDisplacement; - vp.ActorPos += origin->mDisplacement; - vp.ViewActor = nullptr; - - // avoid recursions! - if (origin->plane != -1) screen->instack[origin->plane]++; - - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag&1), !!(mState->PlaneMirrorFlag&1)); - SetupCoverage(di); - ClearClipper(di); - - // If the viewpoint is not within the portal, we need to invalidate the entire clip area. - // The portal will re-validate the necessary parts when its subsectors get traversed. - subsector_t *sub = R_PointInSubsector(vp.Pos); - if (!(di->ss_renderflags[sub->Index()] & SSRF_SEEN)) - { - di->mClipper->SafeAddClipRange(0, ANGLE_MAX); - di->mClipper->SetBlocked(true); - } - - static_cast(di)->DrawScene(DM_PORTAL); - - if (origin->plane != -1) screen->instack[origin->plane]--; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// -// -// Plane Mirror Portal -// -// -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// -// GLPlaneMirrorPortal::DrawContents -// -//----------------------------------------------------------------------------- - -void GLPlaneMirrorPortal::DrawContents(HWDrawInfo *di) -{ - if (mState->renderdepth > r_mirror_recursions) - { - ClearScreen(di); - return; - } - // A plane mirror needs to flip the portal exclusion logic because inside the mirror, up is down and down is up. - std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); - - auto &vp = di->Viewpoint; - int old_pm = mState->PlaneMirrorMode; - - // the player is always visible in a mirror. - vp.showviewer = true; - - double planez = origin->ZatPoint(vp.Pos); - vp.Pos.Z = 2 * planez - vp.Pos.Z; - vp.ViewActor = nullptr; - mState->PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; - - mState->PlaneMirrorFlag++; - di->SetClipHeight(planez, mState->PlaneMirrorMode < 0 ? -1.f : 1.f); - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag & 1), !!(mState->PlaneMirrorFlag & 1)); - ClearClipper(di); - - di->UpdateCurrentMapSection(); - - static_cast(di)->DrawScene(DM_PORTAL); - mState->PlaneMirrorFlag--; - mState->PlaneMirrorMode = old_pm; - std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); -} - -//----------------------------------------------------------------------------- -// -// Common code for line to line and mirror portals -// -//----------------------------------------------------------------------------- - -int GLLinePortal::ClipSeg(seg_t *seg, const DVector3 &viewpos) -{ - line_t *linedef = seg->linedef; - if (!linedef) - { - return PClip_Inside; // should be handled properly. - } - return P_ClipLineToPortal(linedef, line(), viewpos) ? PClip_InFront : PClip_Inside; -} - -int GLLinePortal::ClipSubsector(subsector_t *sub) -{ - // this seg is completely behind the mirror - for(unsigned int i=0;inumlines;i++) - { - if (P_PointOnLineSidePrecise(sub->firstline[i].v1->fPos(), line()) == 0) return PClip_Inside; - } - return PClip_InFront; -} - -int GLLinePortal::ClipPoint(const DVector2 &pos) -{ - if (P_PointOnLineSidePrecise(pos, line())) - { - return PClip_InFront; - } - return PClip_Inside; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// -// -// Mirror Portal -// -// -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// -// R_EnterMirror -// -//----------------------------------------------------------------------------- -void GLMirrorPortal::DrawContents(HWDrawInfo *di) -{ - if (mState->renderdepth>r_mirror_recursions) - { - ClearScreen(di); - return; - } - - auto &vp = di->Viewpoint; - di->UpdateCurrentMapSection(); - - di->mClipPortal = this; - DAngle StartAngle = vp.Angles.Yaw; - DVector3 StartPos = vp.Pos; - - vertex_t *v1 = linedef->v1; - vertex_t *v2 = linedef->v2; - - // the player is always visible in a mirror. - vp.showviewer = true; - // Reflect the current view behind the mirror. - if (linedef->Delta().X == 0) - { - // vertical mirror - vp.Pos.X = 2 * v1->fX() - StartPos.X; - - // Compensation for reendering inaccuracies - if (StartPos.X < v1->fX()) vp.Pos.X -= 0.1; - else vp.Pos.X += 0.1; - } - else if (linedef->Delta().Y == 0) - { - // horizontal mirror - vp.Pos.Y = 2*v1->fY() - StartPos.Y; - - // Compensation for reendering inaccuracies - if (StartPos.YfY()) vp.Pos.Y -= 0.1; - else vp.Pos.Y += 0.1; - } - else - { - // any mirror--use floats to avoid integer overflow. - // Use doubles to avoid losing precision which is very important here. - - 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); - - vp.Pos.X = (x1 + r * dx)*2 - x; - vp.Pos.Y = (y1 + r * dy)*2 - y; - - // Compensation for reendering inaccuracies - FVector2 v(-dx, dy); - v.MakeUnit(); - - vp.Pos.X+= v[1] * mState->renderdepth / 2; - vp.Pos.Y+= v[0] * mState->renderdepth / 2; - } - vp.Angles.Yaw = linedef->Delta().Angle() * 2. - StartAngle; - - vp.ViewActor = nullptr; - - mState->MirrorFlag++; - di->SetClipLine(linedef); - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag&1), !!(mState->PlaneMirrorFlag&1)); - - di->mClipper->Clear(); - - angle_t af = di->FrustumAngle(); - if (afmClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs()+af, vp.Angles.Yaw.BAMs()-af); - - di->mClipper->SafeAddClipRange(linedef->v1, linedef->v2); - - static_cast(di)->DrawScene(DM_PORTAL); - - mState->MirrorFlag--; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// -// -// Line to line Portal -// -// -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// -// -// -//----------------------------------------------------------------------------- -void GLLineToLinePortal::DrawContents(HWDrawInfo *di) -{ - // TODO: Handle recursion more intelligently - if (mState->renderdepth>r_mirror_recursions) - { - ClearScreen(di); - return; - } - auto &vp = di->Viewpoint; - di->mClipPortal = this; - - line_t *origin = glport->lines[0]->mOrigin; - P_TranslatePortalXY(origin, vp.Pos.X, vp.Pos.Y); - P_TranslatePortalXY(origin, vp.ActorPos.X, vp.ActorPos.Y); - P_TranslatePortalAngle(origin, vp.Angles.Yaw); - P_TranslatePortalZ(origin, vp.Pos.Z); - P_TranslatePortalXY(origin, vp.Path[0].X, vp.Path[0].Y); - P_TranslatePortalXY(origin, vp.Path[1].X, vp.Path[1].Y); - if (!vp.showviewer && vp.camera != nullptr && P_PointOnLineSidePrecise(vp.Path[0], glport->lines[0]->mDestination) != P_PointOnLineSidePrecise(vp.Path[1], glport->lines[0]->mDestination)) - { - double distp = (vp.Path[0] - vp.Path[1]).Length(); - if (distp > EQUAL_EPSILON) - { - double dist1 = (vp.Pos - vp.Path[0]).Length(); - double dist2 = (vp.Pos - vp.Path[1]).Length(); - - if (dist1 + dist2 < distp + 1) - { - vp.camera->renderflags |= RF_MAYBEINVISIBLE; - } - } - } - - - for (unsigned i = 0; i < lines.Size(); i++) - { - line_t *line = lines[i].seg->linedef->getPortalDestination(); - subsector_t *sub; - if (line->sidedef[0]->Flags & WALLF_POLYOBJ) - sub = R_PointInSubsector(line->v1->fixX(), line->v1->fixY()); - else sub = line->frontsector->subsectors[0]; - di->CurrentMapSections.Set(sub->mapsection); - } - - vp.ViewActor = nullptr; - di->SetClipLine(glport->lines[0]->mDestination); - di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag&1), !!(mState->PlaneMirrorFlag&1)); - - ClearClipper(di); - static_cast(di)->DrawScene(DM_PORTAL); -} - -void GLLineToLinePortal::RenderAttached(HWDrawInfo *di) -{ - di->ProcessActorsInPortal(glport, di->in_area); -} - //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // // // Horizon Portal // -// This simply draws the area in medium sized squares. Drawing it as a whole -// polygon creates visible inaccuracies. -// -// Originally I tried to minimize the amount of data to be drawn but there -// are 2 problems with it: -// -// 1. Setting this up completely negates any performance gains. -// 2. It doesn't work with a 360° field of view (as when you are looking up.) -// -// -// So the brute force mechanism is just as good. -// // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -978,17 +508,6 @@ void GLEEHorizonPortal::DrawContents(HWDrawInfo *di) } const char *GLSkyPortal::GetName() { return "Sky"; } -const char *GLSkyboxPortal::GetName() { return "Skybox"; } -const char *GLSectorStackPortal::GetName() { return "Sectorstack"; } -const char *GLPlaneMirrorPortal::GetName() { return "Planemirror"; } -const char *GLMirrorPortal::GetName() { return "Mirror"; } -const char *GLLineToLinePortal::GetName() { return "LineToLine"; } const char *GLHorizonPortal::GetName() { return "Horizon"; } const char *GLEEHorizonPortal::GetName() { return "EEHorizon"; } -// This needs to remain on the renderer side until the portal interface can be abstracted. -void FSectorPortalGroup::AddSubsector(subsector_t *sub) -{ - GLSectorStackPortal *glportal = GetRenderState(); - glportal->AddSubsector(sub); -} diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h index cda52886e5e..890eb79095f 100644 --- a/src/gl/scene/gl_portal.h +++ b/src/gl/scene/gl_portal.h @@ -66,115 +66,30 @@ class GLPortal : public IPortal void ClearScreen(HWDrawInfo *di); }; -struct GLLinePortal : public GLPortal +class GLScenePortal : public GLPortal { - // this must be the same as at the start of line_t, so that we can pass in this structure directly to P_ClipLineToPortal. - vertex_t *v1, *v2; // vertices, from v1 to v2 - DVector2 delta; // precalculated v2 - v1 for side checking - - angle_t angv1, angv2; // for quick comparisons with a line or subsector - - GLLinePortal(FPortalSceneState *state, line_t *line) : GLPortal(state) +public: + HWScenePortalBase *mScene; + GLScenePortal(FPortalSceneState *state, HWScenePortalBase *handler) : GLPortal(state) { - v1 = line->v1; - v2 = line->v2; - CalcDelta(); + mScene = handler; + handler->SetOwner(this); } - - GLLinePortal(FPortalSceneState *state, FLinePortalSpan *line) : GLPortal(state) - { - if (line->lines[0]->mType != PORTT_LINKED || line->v1 == nullptr) - { - // For non-linked portals we must check the actual linedef. - line_t *lline = line->lines[0]->mDestination; - v1 = lline->v1; - v2 = lline->v2; - } - else + ~GLScenePortal() { delete mScene; } + virtual void * GetSource() const { return mScene->GetSource(); } + virtual const char *GetName() { return mScene->GetName(); } + virtual bool IsSky() { return false; } + virtual bool NeedCap() { return true; } + virtual bool NeedDepthBuffer() { return true; } + virtual void DrawContents(HWDrawInfo *di) + { + if (mScene->Setup(di, di->mClipper)) { - // For linked portals we can check the merged span. - v1 = line->v1; - v2 = line->v2; + static_cast(di)->DrawScene(DM_PORTAL); + mScene->Shutdown(di); } - CalcDelta(); - } - - void CalcDelta() - { - delta = v2->fPos() - v1->fPos(); - } - - line_t *line() - { - vertex_t **pv = &v1; - return reinterpret_cast(pv); - } - - virtual int ClipSeg(seg_t *seg, const DVector3 &viewpos); - virtual int ClipSubsector(subsector_t *sub); - virtual int ClipPoint(const DVector2 &pos); - virtual bool NeedCap() { return false; } -}; - - -struct GLMirrorPortal : public GLLinePortal -{ - // mirror portals always consist of single linedefs! - line_t * linedef; - -protected: - virtual void DrawContents(HWDrawInfo *di); - virtual void * GetSource() const { return linedef; } - virtual const char *GetName(); - -public: - - GLMirrorPortal(FPortalSceneState *state, line_t * line) - : GLLinePortal(state, line) - { - linedef=line; - } -}; - - -struct GLLineToLinePortal : public GLLinePortal -{ - FLinePortalSpan *glport; -protected: - virtual void DrawContents(HWDrawInfo *di); - virtual void * GetSource() const { return glport; } - virtual const char *GetName(); - virtual line_t *ClipLine() { return line(); } - virtual void RenderAttached(HWDrawInfo *di); - -public: - - GLLineToLinePortal(FPortalSceneState *state, FLinePortalSpan *ll) - : GLLinePortal(state, ll) - { - glport = ll; - } -}; - - -struct GLSkyboxPortal : public GLPortal -{ - FSectorPortal * portal; - -protected: - virtual void DrawContents(HWDrawInfo *di); - virtual void * GetSource() const { return portal; } - virtual bool IsSky() { return true; } - virtual const char *GetName(); - -public: - - - GLSkyboxPortal(FPortalSceneState *state, FSectorPortal * pt) : GLPortal(state) - { - portal=pt; } - + virtual void RenderAttached(HWDrawInfo *di) { return mScene->RenderAttached(di); } }; @@ -201,51 +116,6 @@ struct GLSkyPortal : public GLPortal }; - - -struct GLSectorStackPortal : public GLPortal -{ - TArray subsectors; -protected: - virtual ~GLSectorStackPortal(); - virtual void DrawContents(HWDrawInfo *di); - virtual void * GetSource() const { return origin; } - virtual bool IsSky() { return true; } // although this isn't a real sky it can be handled as one. - virtual const char *GetName(); - FSectorPortalGroup *origin; - -public: - - GLSectorStackPortal(FPortalSceneState *state, FSectorPortalGroup *pt) : GLPortal(state) - { - origin=pt; - } - void SetupCoverage(HWDrawInfo *di); - void AddSubsector(subsector_t *sub) - { - subsectors.Push(sub); - } - -}; - -struct GLPlaneMirrorPortal : public GLPortal -{ -protected: - virtual void DrawContents(HWDrawInfo *di); - virtual void * GetSource() const { return origin; } - virtual const char *GetName(); - secplane_t * origin; - -public: - - GLPlaneMirrorPortal(FPortalSceneState *state, secplane_t * pt) : GLPortal(state) - { - origin=pt; - } - -}; - - struct GLHorizonPortal : public GLPortal { GLHorizonInfo * origin; diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 5d0bd725319..c5a5009227c 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -113,7 +113,6 @@ void FDrawInfo::CreateScene() ProcessAll.Clock(); // clip the scene and fill the drawlists - for(auto p : level.portalGroups) p->glportal = nullptr; Bsp.Clock(); GLRenderer->mVBO->Map(); GLRenderer->mLights->Begin(); diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 5a2a841555e..cf48f1348ed 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -358,13 +358,17 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) { // either a regular skybox or an Eternity-style horizon if (wall->secportal->mType != PORTS_SKYVIEWPOINT) portal = new GLEEHorizonPortal(&pstate, wall->secportal); - else portal = new GLSkyboxPortal(&pstate, wall->secportal); + else portal = new GLScenePortal(&pstate, new HWSkyboxPortal(wall->secportal)); } portal->AddLine(wall); break; case PORTALTYPE_SECTORSTACK: - portal = wall->portal->GetRenderState(); + portal = pstate.FindPortal(wall->portal); + if (!portal) + { + portal = new GLScenePortal(&pstate, new HWSectorStackPortal(wall->portal)); + } portal->AddLine(wall); break; @@ -374,14 +378,14 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) //@sync-portal wall->planemirror = pstate.UniquePlaneMirrors.Get(wall->planemirror); portal = pstate.FindPortal(wall->planemirror); - if (!portal) portal = new GLPlaneMirrorPortal(&pstate, wall->planemirror); + if (!portal) portal = new GLScenePortal(&pstate, new HWPlaneMirrorPortal(wall->planemirror)); portal->AddLine(wall); } break; case PORTALTYPE_MIRROR: portal = pstate.FindPortal(wall->seg->linedef); - if (!portal) portal = new GLMirrorPortal(&pstate, wall->seg->linedef); + if (!portal) portal = new GLScenePortal(&pstate, new HWMirrorPortal(wall->seg->linedef)); portal->AddLine(wall); if (gl_mirror_envmap) { @@ -399,7 +403,7 @@ void FDrawInfo::AddPortal(GLWall *wall, int ptype) { ProcessActorsInPortal(otherside->getPortal()->mGroup, in_area); } - portal = new GLLineToLinePortal(&pstate, wall->lineportal); + portal = new GLScenePortal(&pstate, new HWLineToLinePortal(wall->lineportal)); } portal->AddLine(wall); break; diff --git a/src/hwrenderer/scene/hw_bsp.cpp b/src/hwrenderer/scene/hw_bsp.cpp index 18884dab01a..edbe04dc3c1 100644 --- a/src/hwrenderer/scene/hw_bsp.cpp +++ b/src/hwrenderer/scene/hw_bsp.cpp @@ -538,13 +538,13 @@ void HWDrawInfo::DoSubsector(subsector_t * sub) portal = fakesector->GetPortalGroup(sector_t::ceiling); if (portal != nullptr) { - portal->AddSubsector(sub); + AddSubsectorToPortal(portal, sub); } portal = fakesector->GetPortalGroup(sector_t::floor); if (portal != nullptr) { - portal->AddSubsector(sub); + AddSubsectorToPortal(portal, sub); } } } diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index 69fa2ab98f0..697715f61cc 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -223,7 +223,7 @@ angle_t HWDrawInfo::FrustumAngle() void HWDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror) { - float mult = mirror ? -1 : 1; + float mult = mirror ? -1.f : 1.f; float planemult = planemirror ? -level.info->pixelstretch : level.info->pixelstretch; VPUniforms.mViewMatrix.loadIdentity(); @@ -250,7 +250,6 @@ void HWDrawInfo::SetupView(float vx, float vy, float vz, bool mirror, bool plane ApplyVPUniforms(); } - //----------------------------------------------------------------------------- // // @@ -268,3 +267,4 @@ void HWViewpointUniforms::SetDefaults() mClipLine.X = -10000000.0f; } + diff --git a/src/hwrenderer/scene/hw_drawinfo.h b/src/hwrenderer/scene/hw_drawinfo.h index cf34a477851..4c8a303c5ac 100644 --- a/src/hwrenderer/scene/hw_drawinfo.h +++ b/src/hwrenderer/scene/hw_drawinfo.h @@ -23,6 +23,7 @@ class Clipper; class IPortal; class FFlatVertexGenerator; class IRenderQueue; +class HWScenePortalBase; //========================================================================== // @@ -58,7 +59,10 @@ enum EPortalClip struct HWDrawInfo { - virtual ~HWDrawInfo() {} + virtual ~HWDrawInfo() + { + ClearBuffers(); + } struct wallseg { @@ -96,9 +100,10 @@ struct HWDrawInfo bool isNightvision() const { return !!(FullbrightFlags & Nightvision); } bool isStealthVision() const { return !!(FullbrightFlags & StealthVision); } + HWDrawInfo * outer = nullptr; int FullbrightFlags; std::atomic spriteindex; - IPortal *mClipPortal; + HWScenePortalBase *mClipPortal; IPortal *mCurrentPortal; //FRotator mAngles; IShadowMap *mShadowMap; @@ -240,6 +245,7 @@ struct HWDrawInfo virtual int UploadLights(FDynLightData &data) = 0; virtual void ApplyVPUniforms() = 0; + virtual bool SetDepthClamp(bool on) = 0; virtual GLDecal *AddDecal(bool onmirror) = 0; virtual std::pair AllocVertices(unsigned int count) = 0; diff --git a/src/hwrenderer/scene/hw_portal.cpp b/src/hwrenderer/scene/hw_portal.cpp index 9c6ca5caa75..248a9c94c18 100644 --- a/src/hwrenderer/scene/hw_portal.cpp +++ b/src/hwrenderer/scene/hw_portal.cpp @@ -26,7 +26,14 @@ */ #include "c_dispatch.h" +#include "portal.h" +#include "p_maputl.h" #include "hw_portal.h" +#include "hw_clipper.h" +#include "actor.h" +#include "g_levellocals.h" + +EXTERN_CVAR(Int, r_mirror_recursions) IPortal * FPortalSceneState::FindPortal(const void * src) { @@ -155,3 +162,476 @@ bool FPortalSceneState::RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di return false; } +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void HWScenePortalBase::ClearClipper(HWDrawInfo *di) +{ + auto outer_di = di->outer; + DAngle angleOffset = deltaangle(outer_di->Viewpoint.Angles.Yaw, di->Viewpoint.Angles.Yaw); + + di->mClipper->Clear(); + + auto &lines = mOwner->lines; + // Set the clipper to the minimal visible area + di->mClipper->SafeAddClipRange(0, 0xffffffff); + for (unsigned int i = 0; i < lines.Size(); i++) + { + DAngle startAngle = (DVector2(lines[i].glseg.x2, lines[i].glseg.y2) - outer_di->Viewpoint.Pos).Angle() + angleOffset; + DAngle endAngle = (DVector2(lines[i].glseg.x1, lines[i].glseg.y1) - outer_di->Viewpoint.Pos).Angle() + angleOffset; + + if (deltaangle(endAngle, startAngle) < 0) + { + di->mClipper->SafeRemoveClipRangeRealAngles(startAngle.BAMs(), endAngle.BAMs()); + } + } + + // and finally clip it to the visible area + angle_t a1 = di->FrustumAngle(); + if (a1 < ANGLE_180) di->mClipper->SafeAddClipRangeRealAngles(di->Viewpoint.Angles.Yaw.BAMs() + a1, di->Viewpoint.Angles.Yaw.BAMs() - a1); + + // lock the parts that have just been clipped out. + di->mClipper->SetSilhouette(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Common code for line to line and mirror portals +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +int HWLinePortal::ClipSeg(seg_t *seg, const DVector3 &viewpos) +{ + line_t *linedef = seg->linedef; + if (!linedef) + { + return PClip_Inside; // should be handled properly. + } + return P_ClipLineToPortal(linedef, line(), viewpos) ? PClip_InFront : PClip_Inside; +} + +int HWLinePortal::ClipSubsector(subsector_t *sub) +{ + // this seg is completely behind the mirror + for (unsigned int i = 0; inumlines; i++) + { + if (P_PointOnLineSidePrecise(sub->firstline[i].v1->fPos(), line()) == 0) return PClip_Inside; + } + return PClip_InFront; +} + +int HWLinePortal::ClipPoint(const DVector2 &pos) +{ + if (P_PointOnLineSidePrecise(pos, line())) + { + return PClip_InFront; + } + return PClip_Inside; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Mirror Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +bool HWMirrorPortal::Setup(HWDrawInfo *di, Clipper *clipper) +{ + auto state = mOwner->mState; + if (state->renderdepth > r_mirror_recursions) + { + return false; + } + + auto &vp = di->Viewpoint; + di->UpdateCurrentMapSection(); + + di->mClipPortal = this; + DAngle StartAngle = vp.Angles.Yaw; + DVector3 StartPos = vp.Pos; + + vertex_t *v1 = linedef->v1; + vertex_t *v2 = linedef->v2; + + // the player is always visible in a mirror. + vp.showviewer = true; + // Reflect the current view behind the mirror. + if (linedef->Delta().X == 0) + { + // vertical mirror + vp.Pos.X = 2 * v1->fX() - StartPos.X; + + // Compensation for reendering inaccuracies + if (StartPos.X < v1->fX()) vp.Pos.X -= 0.1; + else vp.Pos.X += 0.1; + } + else if (linedef->Delta().Y == 0) + { + // horizontal mirror + vp.Pos.Y = 2 * v1->fY() - StartPos.Y; + + // Compensation for reendering inaccuracies + if (StartPos.Y < v1->fY()) vp.Pos.Y -= 0.1; + else vp.Pos.Y += 0.1; + } + else + { + // any mirror--use floats to avoid integer overflow. + // Use doubles to avoid losing precision which is very important here. + + 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); + + vp.Pos.X = (x1 + r * dx) * 2 - x; + vp.Pos.Y = (y1 + r * dy) * 2 - y; + + // Compensation for reendering inaccuracies + FVector2 v(-dx, dy); + v.MakeUnit(); + + vp.Pos.X += v[1] * state->renderdepth / 2; + vp.Pos.Y += v[0] * state->renderdepth / 2; + } + vp.Angles.Yaw = linedef->Delta().Angle() * 2. - StartAngle; + + vp.ViewActor = nullptr; + + state->MirrorFlag++; + di->SetClipLine(linedef); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + + clipper->Clear(); + + angle_t af = di->FrustumAngle(); + if (af < ANGLE_180) clipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + af, vp.Angles.Yaw.BAMs() - af); + + clipper->SafeAddClipRange(linedef->v1, linedef->v2); + return true; +} + +void HWMirrorPortal::Shutdown(HWDrawInfo *di) +{ + mOwner->mState->MirrorFlag--; +} + +const char *HWMirrorPortal::GetName() { return "Mirror"; } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Line to line Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- +bool HWLineToLinePortal::Setup(HWDrawInfo *di, Clipper *clipper) +{ + // TODO: Handle recursion more intelligently + auto &state = mOwner->mState; + if (state->renderdepth>r_mirror_recursions) + { + return false; + } + auto &vp = di->Viewpoint; + di->mClipPortal = this; + + line_t *origin = glport->lines[0]->mOrigin; + P_TranslatePortalXY(origin, vp.Pos.X, vp.Pos.Y); + P_TranslatePortalXY(origin, vp.ActorPos.X, vp.ActorPos.Y); + P_TranslatePortalAngle(origin, vp.Angles.Yaw); + P_TranslatePortalZ(origin, vp.Pos.Z); + P_TranslatePortalXY(origin, vp.Path[0].X, vp.Path[0].Y); + P_TranslatePortalXY(origin, vp.Path[1].X, vp.Path[1].Y); + if (!vp.showviewer && vp.camera != nullptr && P_PointOnLineSidePrecise(vp.Path[0], glport->lines[0]->mDestination) != P_PointOnLineSidePrecise(vp.Path[1], glport->lines[0]->mDestination)) + { + double distp = (vp.Path[0] - vp.Path[1]).Length(); + if (distp > EQUAL_EPSILON) + { + double dist1 = (vp.Pos - vp.Path[0]).Length(); + double dist2 = (vp.Pos - vp.Path[1]).Length(); + + if (dist1 + dist2 < distp + 1) + { + vp.camera->renderflags |= RF_MAYBEINVISIBLE; + } + } + } + + auto &lines = mOwner->lines; + + for (unsigned i = 0; i < lines.Size(); i++) + { + line_t *line = lines[i].seg->linedef->getPortalDestination(); + subsector_t *sub; + if (line->sidedef[0]->Flags & WALLF_POLYOBJ) + sub = R_PointInSubsector(line->v1->fixX(), line->v1->fixY()); + else sub = line->frontsector->subsectors[0]; + di->CurrentMapSections.Set(sub->mapsection); + } + + vp.ViewActor = nullptr; + di->SetClipLine(glport->lines[0]->mDestination); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + + ClearClipper(di); + return true; +} + +void HWLineToLinePortal::RenderAttached(HWDrawInfo *di) +{ + di->ProcessActorsInPortal(glport, di->in_area); +} + +const char *HWLineToLinePortal::GetName() { return "LineToLine"; } + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Skybox Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// GLSkyboxPortal::DrawContents +// +//----------------------------------------------------------------------------- + +bool HWSkyboxPortal::Setup(HWDrawInfo *di, Clipper *clipper) +{ + auto state = mOwner->mState; + old_pm = state->PlaneMirrorMode; + + if (mOwner->mState->skyboxrecursion >= 3) + { + return false; + } + auto &vp = di->Viewpoint; + + state->skyboxrecursion++; + state->PlaneMirrorMode = 0; + state->inskybox = true; + + AActor *origin = portal->mSkybox; + portal->mFlags |= PORTSF_INSKYBOX; + vp.extralight = 0; + + + oldclamp = di->SetDepthClamp(false); + vp.Pos = origin->InterpolatedPosition(vp.TicFrac); + vp.ActorPos = origin->Pos(); + vp.Angles.Yaw += (origin->PrevAngles.Yaw + deltaangle(origin->PrevAngles.Yaw, origin->Angles.Yaw) * vp.TicFrac); + + // Don't let the viewpoint be too close to a floor or ceiling + double floorh = origin->Sector->floorplane.ZatPoint(origin->Pos()); + double ceilh = origin->Sector->ceilingplane.ZatPoint(origin->Pos()); + if (vp.Pos.Z < floorh + 4) vp.Pos.Z = floorh + 4; + if (vp.Pos.Z > ceilh - 4) vp.Pos.Z = ceilh - 4; + + vp.ViewActor = origin; + + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + di->SetViewArea(); + ClearClipper(di); + di->UpdateCurrentMapSection(); + return true; +} + + +void HWSkyboxPortal::Shutdown(HWDrawInfo *di) +{ + auto state = mOwner->mState; + portal->mFlags &= ~PORTSF_INSKYBOX; + di->SetDepthClamp(oldclamp); + state->inskybox = false; + state->skyboxrecursion--; + state->PlaneMirrorMode = old_pm; +} + +const char *HWSkyboxPortal::GetName() { return "Skybox"; } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Sector stack Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// GLSectorStackPortal::SetupCoverage +// +//----------------------------------------------------------------------------- + +static uint8_t SetCoverage(HWDrawInfo *di, void *node) +{ + if (level.nodes.Size() == 0) + { + return 0; + } + if (!((size_t)node & 1)) // Keep going until found a subsector + { + node_t *bsp = (node_t *)node; + uint8_t coverage = SetCoverage(di, bsp->children[0]) | SetCoverage(di, bsp->children[1]); + di->no_renderflags[bsp->Index()] = coverage; + return coverage; + } + else + { + subsector_t *sub = (subsector_t *)((uint8_t *)node - 1); + return di->ss_renderflags[sub->Index()] & SSRF_SEEN; + } +} + +void HWSectorStackPortal::SetupCoverage(HWDrawInfo *di) +{ + for (unsigned i = 0; iplane; + for (int j = 0; jportalcoverage[plane].sscount; j++) + { + subsector_t *dsub = &::level.subsectors[sub->portalcoverage[plane].subsectors[j]]; + di->CurrentMapSections.Set(dsub->mapsection); + di->ss_renderflags[dsub->Index()] |= SSRF_SEEN; + } + } + SetCoverage(di, ::level.HeadNode()); +} + +//----------------------------------------------------------------------------- +// +// GLSectorStackPortal::DrawContents +// +//----------------------------------------------------------------------------- +bool HWSectorStackPortal::Setup(HWDrawInfo *di, Clipper *clipper) +{ + auto state = mOwner->mState; + FSectorPortalGroup *portal = origin; + auto &vp = di->Viewpoint; + + vp.Pos += origin->mDisplacement; + vp.ActorPos += origin->mDisplacement; + vp.ViewActor = nullptr; + + // avoid recursions! + if (origin->plane != -1) screen->instack[origin->plane]++; + + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + SetupCoverage(di); + ClearClipper(di); + + // If the viewpoint is not within the portal, we need to invalidate the entire clip area. + // The portal will re-validate the necessary parts when its subsectors get traversed. + subsector_t *sub = R_PointInSubsector(vp.Pos); + if (!(di->ss_renderflags[sub->Index()] & SSRF_SEEN)) + { + di->mClipper->SafeAddClipRange(0, ANGLE_MAX); + di->mClipper->SetBlocked(true); + } + return true; +} + + +void HWSectorStackPortal::Shutdown(HWDrawInfo *di) +{ + if (origin->plane != -1) screen->instack[origin->plane]--; +} + +const char *HWSectorStackPortal::GetName() { return "Sectorstack"; } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Plane Mirror Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// GLPlaneMirrorPortal::DrawContents +// +//----------------------------------------------------------------------------- + +bool HWPlaneMirrorPortal::Setup(HWDrawInfo *di, Clipper *clipper) +{ + auto state = mOwner->mState; + if (state->renderdepth > r_mirror_recursions) + { + return false; + } + // A plane mirror needs to flip the portal exclusion logic because inside the mirror, up is down and down is up. + std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); + + auto &vp = di->Viewpoint; + int old_pm = state->PlaneMirrorMode; + + // the player is always visible in a mirror. + vp.showviewer = true; + + double planez = origin->ZatPoint(vp.Pos); + vp.Pos.Z = 2 * planez - vp.Pos.Z; + vp.ViewActor = nullptr; + state->PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; + + state->PlaneMirrorFlag++; + di->SetClipHeight(planez, state->PlaneMirrorMode < 0 ? -1.f : 1.f); + di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + ClearClipper(di); + + di->UpdateCurrentMapSection(); + return true; +} + +void HWPlaneMirrorPortal::Shutdown(HWDrawInfo *di) +{ + auto state = mOwner->mState; + state->PlaneMirrorFlag--; + state->PlaneMirrorMode = old_pm; + std::swap(screen->instack[sector_t::floor], screen->instack[sector_t::ceiling]); +} + +const char *HWPlaneMirrorPortal::GetName() { return "Planemirror"; } diff --git a/src/hwrenderer/scene/hw_portal.h b/src/hwrenderer/scene/hw_portal.h index da23db55269..39b45153091 100644 --- a/src/hwrenderer/scene/hw_portal.h +++ b/src/hwrenderer/scene/hw_portal.h @@ -1,5 +1,6 @@ #pragma once +#include "portal.h" #include "hw_drawinfo.h" #include "hw_drawstructs.h" #include "hwrenderer/textures/hw_material.h" @@ -39,16 +40,12 @@ struct FPortalSceneState; class IPortal { friend struct FPortalSceneState; -protected: +public: FPortalSceneState * mState; TArray lines; -public: + IPortal(FPortalSceneState *s, bool local); virtual ~IPortal() {} - virtual int ClipSeg(seg_t *seg, const DVector3 &viewpos) { return PClip_Inside; } - virtual int ClipSubsector(subsector_t *sub) { return PClip_Inside; } - virtual int ClipPoint(const DVector2 &pos) { return PClip_Inside; } - virtual line_t *ClipLine() { return nullptr; } virtual void * GetSource() const = 0; // GetSource MUST be implemented! virtual const char *GetName() = 0; virtual bool IsSky() { return false; } @@ -127,3 +124,191 @@ inline IPortal::IPortal(FPortalSceneState *s, bool local) : mState(s) { if (!local) s->portals.Push(this); } + + +class HWScenePortalBase +{ +protected: + IPortal *mOwner; +public: + HWScenePortalBase() {} + virtual ~HWScenePortalBase() {} + void SetOwner(IPortal *p) { mOwner = p; } + void ClearClipper(HWDrawInfo *di); + + virtual int ClipSeg(seg_t *seg, const DVector3 &viewpos) { return PClip_Inside; } + virtual int ClipSubsector(subsector_t *sub) { return PClip_Inside; } + virtual int ClipPoint(const DVector2 &pos) { return PClip_Inside; } + virtual line_t *ClipLine() { return nullptr; } + + virtual bool IsSky() { return false; } + virtual bool NeedCap() { return false; } + virtual bool NeedDepthBuffer() { return true; } + virtual void * GetSource() const = 0; // GetSource MUST be implemented! + virtual const char *GetName() = 0; + + virtual bool Setup(HWDrawInfo *di, Clipper *clipper) = 0; + virtual void Shutdown(HWDrawInfo *di) {} + virtual void RenderAttached(HWDrawInfo *di) {} + +}; + +struct HWLinePortal : public HWScenePortalBase +{ + // this must be the same as at the start of line_t, so that we can pass in this structure directly to P_ClipLineToPortal. + vertex_t *v1, *v2; // vertices, from v1 to v2 + DVector2 delta; // precalculated v2 - v1 for side checking + + angle_t angv1, angv2; // for quick comparisons with a line or subsector + + HWLinePortal(line_t *line) + { + v1 = line->v1; + v2 = line->v2; + CalcDelta(); + } + + HWLinePortal(FLinePortalSpan *line) + { + if (line->lines[0]->mType != PORTT_LINKED || line->v1 == nullptr) + { + // For non-linked portals we must check the actual linedef. + line_t *lline = line->lines[0]->mDestination; + v1 = lline->v1; + v2 = lline->v2; + } + else + { + // For linked portals we can check the merged span. + v1 = line->v1; + v2 = line->v2; + } + CalcDelta(); + } + + void CalcDelta() + { + delta = v2->fPos() - v1->fPos(); + } + + line_t *line() + { + vertex_t **pv = &v1; + return reinterpret_cast(pv); + } + + int ClipSeg(seg_t *seg, const DVector3 &viewpos) override; + int ClipSubsector(subsector_t *sub) override; + int ClipPoint(const DVector2 &pos); + bool NeedCap() override { return false; } +}; + +struct HWMirrorPortal : public HWLinePortal +{ + // mirror portals always consist of single linedefs! + line_t * linedef; + +protected: + bool Setup(HWDrawInfo *di, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di) override; + void * GetSource() const override { return linedef; } + const char *GetName() override; + +public: + + HWMirrorPortal(line_t * line) + : HWLinePortal(line) + { + linedef = line; + } +}; + + +struct HWLineToLinePortal : public HWLinePortal +{ + FLinePortalSpan *glport; +protected: + bool Setup(HWDrawInfo *di, Clipper *clipper) override; + virtual void * GetSource() const override { return glport; } + virtual const char *GetName() override; + virtual line_t *ClipLine() override { return line(); } + virtual void RenderAttached(HWDrawInfo *di) override; + +public: + + HWLineToLinePortal(FLinePortalSpan *ll) + : HWLinePortal(ll) + { + glport = ll; + } +}; + + +struct HWSkyboxPortal : public HWScenePortalBase +{ + bool oldclamp; + int old_pm; + FSectorPortal * portal; + +protected: + bool Setup(HWDrawInfo *di, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di) override; + virtual void * GetSource() const { return portal; } + virtual bool IsSky() { return true; } + virtual const char *GetName(); + +public: + + + HWSkyboxPortal(FSectorPortal * pt) + { + portal = pt; + } + +}; + + +struct HWSectorStackPortal : public HWScenePortalBase +{ + TArray subsectors; +protected: + bool Setup(HWDrawInfo *di, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di) override; + virtual void * GetSource() const { return origin; } + virtual bool IsSky() { return true; } // although this isn't a real sky it can be handled as one. + virtual const char *GetName(); + FSectorPortalGroup *origin; + +public: + + HWSectorStackPortal(FSectorPortalGroup *pt) + { + origin = pt; + } + void SetupCoverage(HWDrawInfo *di); + void AddSubsector(subsector_t *sub) + { + subsectors.Push(sub); + } + +}; + +struct HWPlaneMirrorPortal : public HWScenePortalBase +{ + int old_pm; +protected: + bool Setup(HWDrawInfo *di, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di) override; + virtual void * GetSource() const { return origin; } + virtual const char *GetName(); + secplane_t * origin; + +public: + + HWPlaneMirrorPortal(secplane_t * pt) + { + origin = pt; + } + +}; + diff --git a/src/portal.h b/src/portal.h index 343e09b29ea..01b17fc8be0 100644 --- a/src/portal.h +++ b/src/portal.h @@ -254,11 +254,6 @@ struct FSectorPortalGroup { DVector2 mDisplacement; int plane; - GLSectorStackPortal *glportal; // for quick access to the render data. This is only valid during BSP traversal! - - GLSectorStackPortal *GetRenderState(); - - void AddSubsector(subsector_t *sub); }; diff --git a/src/r_data/portalgroups.cpp b/src/r_data/portalgroups.cpp index a65f8b5e02c..f1baac476cf 100644 --- a/src/r_data/portalgroups.cpp +++ b/src/r_data/portalgroups.cpp @@ -363,7 +363,6 @@ static void GroupSectorPortals() FSectorPortalGroup *portal = new FSectorPortalGroup; portal->mDisplacement = pair->Key.mDisplacement; portal->plane = (i == 1 ? sector_t::floor : sector_t::ceiling); /**/ - portal->glportal = NULL; level.portalGroups.Push(portal); for (unsigned j = 0; j < pair->Value.Size(); j++) {