diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 59fc9b734..236daa0df 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1075,6 +1075,8 @@ set (PCH_SOURCES core/rendering/scene/hw_walls.cpp core/rendering/render.cpp core/rendering/scene/hw_drawlistadd.cpp + core/rendering/scene/hw_drawlist.cpp + core/rendering/scene/hw_drawinfo.cpp core/console/c_notifybuffer.cpp core/console/d_event.cpp diff --git a/source/common/rendering/hwrenderer/data/hw_aabbtree.h b/source/common/rendering/hwrenderer/data/hw_aabbtree.h index 389a991ab..2bd0e1506 100644 --- a/source/common/rendering/hwrenderer/data/hw_aabbtree.h +++ b/source/common/rendering/hwrenderer/data/hw_aabbtree.h @@ -4,8 +4,6 @@ #include "tarray.h" #include "vectors.h" -struct FLevelLocals; - namespace hwrenderer { diff --git a/source/core/rendering/scene/hw_drawinfo.cpp b/source/core/rendering/scene/hw_drawinfo.cpp new file mode 100644 index 000000000..8410626b5 --- /dev/null +++ b/source/core/rendering/scene/hw_drawinfo.cpp @@ -0,0 +1,601 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2000-2018 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_drawinfo.cpp +** Basic scene draw info management class +** +*/ + +#include "hw_portal.h" +#include "build.h" +#include "hw_renderstate.h" +#include "hw_drawinfo.h" +//#include "models.h" +#include "hw_clock.h" +#include "hw_cvars.h" +#include "hw_viewpointbuffer.h" +#include "flatvertices.h" +#include "hw_lightbuffer.h" +#include "hw_vrmodes.h" +#include "hw_clipper.h" +#include "v_draw.h" + +EXTERN_CVAR(Float, r_visibility) +CVAR(Bool, gl_bandedswlight, false, CVAR_ARCHIVE) +CVAR(Bool, gl_sort_textures, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, gl_no_skyclear, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, gl_enhanced_nv_stealth, 3, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +CVAR(Bool, gl_texture, true, 0) +CVAR(Float, gl_mask_threshold, 0.5f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Float, gl_mask_sprite_threshold, 0.5f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +//========================================================================== +// +// +// +//========================================================================== + +class FDrawInfoList +{ +public: + TDeletingArray mList; + + HWDrawInfo * GetNew(); + void Release(HWDrawInfo *); +}; + + +FDrawInfoList di_list; + +//========================================================================== +// +// Try to reuse the lists as often as possible as they contain resources that +// are expensive to create and delete. +// +// Note: If multithreading gets used, this class needs synchronization. +// +//========================================================================== + +HWDrawInfo *FDrawInfoList::GetNew() +{ + if (mList.Size() > 0) + { + HWDrawInfo *di; + mList.Pop(di); + return di; + } + return new HWDrawInfo(); +} + +void FDrawInfoList::Release(HWDrawInfo * di) +{ + di->ClearBuffers(); + mList.Push(di); +} + +//========================================================================== +// +// Sets up a new drawinfo struct +// +//========================================================================== + +HWDrawInfo *HWDrawInfo::StartDrawInfo(HWDrawInfo *parent, FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms) +{ + HWDrawInfo *di = di_list.GetNew(); + di->StartScene(parentvp, uniforms); + return di; +} + + +//========================================================================== +// +// +// +//========================================================================== + +static Clipper staticClipper; // Since all scenes are processed sequentially we only need one clipper. +static HWDrawInfo * gl_drawinfo; // This is a linked list of all active DrawInfos and needed to free the memory arena after the last one goes out of scope. + +void HWDrawInfo::StartScene(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms) +{ + staticClipper.Clear(); + mClipper = &staticClipper; + + Viewpoint = parentvp; + //lightmode = Level->lightMode; + if (uniforms) + { + VPUniforms = *uniforms; + // The clip planes will never be inherited from the parent drawinfo. + VPUniforms.mClipLine.X = -1000001.f; + VPUniforms.mClipHeight = 0; + } + else + { + VPUniforms.mProjectionMatrix.loadIdentity(); + VPUniforms.mViewMatrix.loadIdentity(); + VPUniforms.mNormalViewMatrix.loadIdentity(); + //VPUniforms.mViewHeight = viewheight; + VPUniforms.mGlobVis = 1 / 64.f; + VPUniforms.mPalLightLevels = numshades | (static_cast(gl_fogmode) << 8) | (5 << 16); + + VPUniforms.mClipLine.X = -10000000.0f; + VPUniforms.mShadowmapFilter = gl_shadowmap_filter; + } + mClipper->SetViewpoint(Viewpoint); + + ClearBuffers(); + + for (int i = 0; i < GLDL_TYPES; i++) drawlists[i].Reset(); + hudsprites.Clear(); + vpIndex = 0; + + // Fullbright information needs to be propagated from the main view. + if (outer != nullptr) FullbrightFlags = outer->FullbrightFlags; + else FullbrightFlags = 0; + + outer = gl_drawinfo; + gl_drawinfo = this; + +} + +//========================================================================== +// +// +// +//========================================================================== + +HWDrawInfo *HWDrawInfo::EndDrawInfo() +{ + assert(this == gl_drawinfo); + for (int i = 0; i < GLDL_TYPES; i++) drawlists[i].Reset(); + gl_drawinfo = outer; + di_list.Release(this); + if (gl_drawinfo == nullptr) + ResetRenderDataAllocator(); + return gl_drawinfo; +} + + +//========================================================================== +// +// +// +//========================================================================== + +void HWDrawInfo::ClearBuffers() +{ + HandledSubsectors.Clear(); + spriteindex = 0; + + sector_renderflags.Resize(numsectors); + memset(§or_renderflags[0], 0, numsectors * sizeof(sector_renderflags[0])); + + Decals[0].Clear(); + Decals[1].Clear(); + + mClipPortal = nullptr; + mCurrentPortal = nullptr; +} + +//----------------------------------------------------------------------------- +// +// R_FrustumAngle +// +//----------------------------------------------------------------------------- + +angle_t HWDrawInfo::FrustumAngle() +{ + float WidescreenRatio = 1.6666f; // fixme - this is a placeholder. + float tilt = fabs(Viewpoint.HWAngles.Pitch.Degrees); + + // If the pitch is larger than this you can look all around at a FOV of 90° + if (tilt > 46.0f) return 0xffffffff; + + // ok, this is a gross hack that barely works... + // but at least it doesn't overestimate too much... + double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*Viewpoint.FieldOfView.Degrees*48.0 / AspectMultiplier(WidescreenRatio) / 90.0; + angle_t a1 = DAngle(floatangle).BAMs(); + if (a1 >= ANGLE_180) return 0xffffffff; + return a1; +} + +//----------------------------------------------------------------------------- +// +// Setup the modelview matrix +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror) +{ + float mult = mirror ? -1.f : 1.f; + float planemult = planemirror ? -1 : 1;// Level->info->pixelstretch : Level->info->pixelstretch; + + VPUniforms.mViewMatrix.loadIdentity(); + VPUniforms.mViewMatrix.rotate(angles.Roll.Degrees, 0.0f, 0.0f, 1.0f); + VPUniforms.mViewMatrix.rotate(angles.Pitch.Degrees, 1.0f, 0.0f, 0.0f); + VPUniforms.mViewMatrix.rotate(angles.Yaw.Degrees, 0.0f, mult, 0.0f); + VPUniforms.mViewMatrix.translate(vx * mult, -vz * planemult, -vy); + VPUniforms.mViewMatrix.scale(-mult, planemult, 1); +} + + +//----------------------------------------------------------------------------- +// +// SetupView +// Setup the view rotation matrix for the given viewpoint +// +//----------------------------------------------------------------------------- +void HWDrawInfo::SetupView(FRenderState &state, float vx, float vy, float vz, bool mirror, bool planemirror) +{ + auto &vp = Viewpoint; + //vp.SetViewAngle(r_viewwindow); // todo: need to pass in. + SetViewMatrix(vp.HWAngles, vx, vy, vz, mirror, planemirror); + SetCameraPos(vp.Pos); + VPUniforms.CalcDependencies(); + vpIndex = screen->mViewpoints->SetViewpoint(state, &VPUniforms); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +HWPortal * HWDrawInfo::FindPortal(const void * src) +{ + int i = Portals.Size() - 1; + + while (i >= 0 && Portals[i] && Portals[i]->GetSource() != src) i--; + return i >= 0 ? Portals[i] : nullptr; +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +HWDecal* HWDrawInfo::AddDecal(bool onmirror) +{ +#if 0 + auto decal = (HWDecal*)RenderDataAllocator.Alloc(sizeof(HWDecal)); + Decals[onmirror ? 1 : 0].Push(decal); + return decal; +#else + return nullptr; +#endif +} + +//----------------------------------------------------------------------------- +// +// CreateScene +// +// creates the draw lists for the current scene +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::CreateScene(bool drawpsprites) +{ + const auto &vp = Viewpoint; + angle_t a1 = FrustumAngle(); + mClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + a1, vp.Angles.Yaw.BAMs() - a1); + + // reset the portal manager + portalState.StartFrame(); + + ProcessAll.Clock(); + + // clip the scene and fill the drawlists + screen->mVertexData->Map(); + screen->mLights->Map(); + + //RenderBSP(Level->HeadNode(), drawpsprites); + + // And now the crappy hacks that have to be done to avoid rendering anomalies. + // These cannot be multithreaded when the time comes because all these depend + // on the global 'validcount' variable. + + screen->mLights->Unmap(); + screen->mVertexData->Unmap(); + + ProcessAll.Unclock(); + +} + +//----------------------------------------------------------------------------- +// +// RenderScene +// +// Draws the current draw lists for the non GLSL renderer +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::RenderScene(FRenderState &state) +{ + const auto &vp = Viewpoint; + RenderAll.Clock(); + + state.SetDepthMask(true); + + state.EnableFog(true); + state.SetRenderStyle(STYLE_Source); + + if (gl_sort_textures) + { + drawlists[GLDL_PLAINWALLS].SortWalls(); + drawlists[GLDL_PLAINFLATS].SortFlats(); + drawlists[GLDL_MASKEDWALLS].SortWalls(); + drawlists[GLDL_MASKEDFLATS].SortFlats(); + drawlists[GLDL_MASKEDWALLSOFS].SortWalls(); + } + + // Part 1: solid geometry. This is set up so that there are no transparent parts + state.SetDepthFunc(DF_Less); + state.AlphaFunc(Alpha_GEqual, 0.f); + state.ClearDepthBias(); + + state.EnableTexture(gl_texture); + state.EnableBrightmap(true); + drawlists[GLDL_PLAINWALLS].DrawWalls(this, state, false); + drawlists[GLDL_PLAINFLATS].DrawFlats(this, state, false); + + + // Part 2: masked geometry. This is set up so that only pixels with alpha>gl_mask_threshold will show + state.AlphaFunc(Alpha_GEqual, gl_mask_threshold); + drawlists[GLDL_MASKEDWALLS].DrawWalls(this, state, false); + drawlists[GLDL_MASKEDFLATS].DrawFlats(this, state, false); + + // Part 3: masked geometry with polygon offset. This list is empty most of the time so only waste time on it when in use. + if (drawlists[GLDL_MASKEDWALLSOFS].Size() > 0) + { + state.SetDepthBias(-1, -128); + drawlists[GLDL_MASKEDWALLSOFS].DrawWalls(this, state, false); + state.ClearDepthBias(); + } + + drawlists[GLDL_MODELS].Draw(this, state, false); + + state.SetRenderStyle(STYLE_Translucent); + + // Part 4: Draw decals (not a real pass) + state.SetDepthFunc(DF_LEqual); + DrawDecals(state, Decals[0]); + + RenderAll.Unclock(); +} + +//----------------------------------------------------------------------------- +// +// RenderTranslucent +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::RenderTranslucent(FRenderState &state) +{ + RenderAll.Clock(); + + // final pass: translucent stuff + state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold); + state.SetRenderStyle(STYLE_Translucent); + + state.EnableBrightmap(true); + drawlists[GLDL_TRANSLUCENTBORDER].Draw(this, state, true); + state.SetDepthMask(false); + + drawlists[GLDL_TRANSLUCENT].DrawSorted(this, state); + state.EnableBrightmap(false); + + + state.AlphaFunc(Alpha_GEqual, 0.5f); + state.SetDepthMask(true); + + RenderAll.Unclock(); +} + + +//----------------------------------------------------------------------------- +// +// RenderTranslucent +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::RenderPortal(HWPortal *p, FRenderState &state, bool usestencil) +{ + auto gp = static_cast(p); + gp->SetupStencil(this, state, usestencil); + auto new_di = StartDrawInfo(this, Viewpoint, &VPUniforms); + new_di->mCurrentPortal = gp; + state.SetLightIndex(-1); + gp->DrawContents(new_di, state); + new_di->EndDrawInfo(); + state.SetVertexBuffer(screen->mVertexData); + screen->mViewpoints->Bind(state, vpIndex); + gp->RemoveStencil(this, state, usestencil); + +} + +//----------------------------------------------------------------------------- +// +// Draws player sprites and color blend +// +//----------------------------------------------------------------------------- + + +void HWDrawInfo::EndDrawScene(sectortype * viewsector, FRenderState &state) +{ + state.EnableFog(false); + +#if 0 + // [BB] HUD models need to be rendered here. + const bool renderHUDModel = IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player); + if (renderHUDModel) + { + // [BB] The HUD model should be drawn over everything else already drawn. + state.Clear(CT_Depth); + DrawPlayerSprites(true, state); + } +#endif + + state.EnableStencil(false); + state.SetViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height); + + // Restore standard rendering state + state.SetRenderStyle(STYLE_Translucent); + state.ResetColor(); + state.EnableTexture(true); + state.SetScissor(0, 0, -1, -1); +} + +void HWDrawInfo::DrawEndScene2D(sectortype * viewsector, FRenderState &state) +{ + //const bool renderHUDModel = IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player); + auto vrmode = VRMode::GetVRMode(true); + + HWViewpointUniforms vp = VPUniforms; + vp.mViewMatrix.loadIdentity(); + vp.mProjectionMatrix = vrmode->GetHUDSpriteProjection(); + screen->mViewpoints->SetViewpoint(state, &vp); + state.EnableDepthTest(false); + state.EnableMultisampling(false); + + //DrawPlayerSprites(false, state); + + state.SetNoSoftLightLevel(); + + // Restore standard rendering state + state.SetRenderStyle(STYLE_Translucent); + state.ResetColor(); + state.EnableTexture(true); + state.SetScissor(0, 0, -1, -1); +} + +//----------------------------------------------------------------------------- +// +// sets 3D viewport and initial state +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::Set3DViewport(FRenderState &state) +{ + // Always clear all buffers with scissor test disabled. + // This is faster on newer hardware because it allows the GPU to skip + // reading from slower memory where the full buffers are stored. + state.SetScissor(0, 0, -1, -1); + state.Clear(CT_Color | CT_Depth | CT_Stencil); + + const auto &bounds = screen->mSceneViewport; + state.SetViewport(bounds.left, bounds.top, bounds.width, bounds.height); + state.SetScissor(bounds.left, bounds.top, bounds.width, bounds.height); + state.EnableMultisampling(true); + state.EnableDepthTest(true); + state.EnableStencil(true); + state.SetStencil(0, SOP_Keep, SF_AllOn); +} + +//----------------------------------------------------------------------------- +// +// gl_drawscene - this function renders the scene from the current +// viewpoint, including mirrors and skyboxes and other portals +// It is assumed that the HWPortal::EndFrame returns with the +// stencil, z-buffer and the projection matrix intact! +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::DrawScene(int drawmode) +{ + static int recursion = 0; + static int ssao_portals_available = 0; + const auto& vp = Viewpoint; + + bool applySSAO = false; + if (drawmode == DM_MAINVIEW) + { + ssao_portals_available = gl_ssao_portals; + applySSAO = true; + } + else if (drawmode == DM_OFFSCREEN) + { + ssao_portals_available = 0; + } + else if (drawmode == DM_PORTAL && ssao_portals_available > 0) + { + applySSAO = (mCurrentPortal->AllowSSAO()/* || Level->flags3&LEVEL3_SKYBOXAO*/); + ssao_portals_available--; + } + + CreateScene(false); + auto& RenderState = *screen->RenderState(); + + RenderState.SetDepthMask(true); + if (!gl_no_skyclear) portalState.RenderFirstSkyPortal(recursion, this, RenderState); + + RenderScene(RenderState); + + if (applySSAO && RenderState.GetPassType() == GBUFFER_PASS) + { + screen->AmbientOccludeScene(VPUniforms.mProjectionMatrix.get()[5]); + screen->mViewpoints->Bind(RenderState, vpIndex); + } + + // Handle all portals after rendering the opaque objects but before + // doing all translucent stuff + recursion++; + portalState.EndFrame(this, RenderState); + recursion--; + RenderTranslucent(RenderState); +} + + +//----------------------------------------------------------------------------- +// +// R_RenderView - renders one view - either the screen or a camera texture +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::ProcessScene(bool toscreen) +{ + portalState.BeginScene(); + DrawScene(toscreen ? DM_MAINVIEW : DM_OFFSCREEN); + +} + +//========================================================================== +// +// +// +//========================================================================== +/* +void HWDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *ptg, subsector_t *sub) +{ + auto portal = FindPortal(ptg); + if (!portal) + { + portal = new HWSectorStackPortal(&portalState, ptg); + Portals.Push(portal); + } + auto ptl = static_cast(portal); + ptl->AddSubsector(sub); +} +*/ + diff --git a/source/core/rendering/scene/hw_drawinfo.h b/source/core/rendering/scene/hw_drawinfo.h index e10258c03..236d807f3 100644 --- a/source/core/rendering/scene/hw_drawinfo.h +++ b/source/core/rendering/scene/hw_drawinfo.h @@ -2,10 +2,12 @@ #include #include +#include "build.h" #include "vectors.h" #include "hw_viewpointuniforms.h" #include "v_video.h" #include "hw_drawlist.h" +#include "r_viewpoint.h" enum EDrawMode { @@ -16,7 +18,6 @@ enum EDrawMode }; struct FSectorPortalGroup; -struct FLinePortalSpan; struct FFlatVertex; class HWWall; class HWFlat; @@ -82,7 +83,6 @@ struct HWDrawInfo int vpIndex; //ELightMode lightmode; - FLevelLocals *Level; HWDrawInfo * outer = nullptr; int FullbrightFlags; std::atomic spriteindex; @@ -101,7 +101,6 @@ struct HWDrawInfo TArray sector_renderflags; // This is needed by the BSP traverser. - BitArray CurrentMapSections; // this cannot be a single number, because a group of portals with the same displacement may link different sections. fixed_t viewx, viewy; // since the nodes are still fixed point, keeping the view position also fixed point for node traversal is faster. bool multithread; @@ -151,7 +150,7 @@ public: HWPortal * FindPortal(const void * src); - //static HWDrawInfo *StartDrawInfo(FLevelLocals *lev, HWDrawInfo *parent, FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms); + //static HWDrawInfo *StartDrawInfo(HWDrawInfo *parent, FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms); //void StartScene(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms); void ClearBuffers(); HWDrawInfo *EndDrawInfo(); @@ -169,7 +168,6 @@ public: //void GetDynSpriteLight(AActor *self, float x, float y, float z, FLightNode *node, int portalgroup, float *out); //void GetDynSpriteLight(AActor *thing, particle_t *particle, float *out); - void UpdateCurrentMapSection(); void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror); void SetupView(FRenderState &state, float vx, float vy, float vz, bool mirror, bool planemirror); angle_t FrustumAngle(); diff --git a/source/core/rendering/scene/hw_drawlist.cpp b/source/core/rendering/scene/hw_drawlist.cpp new file mode 100644 index 000000000..13adce468 --- /dev/null +++ b/source/core/rendering/scene/hw_drawlist.cpp @@ -0,0 +1,941 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2002-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** hw_drawlist.cpp +** The main container type for draw items. +** +*/ + +#include "hw_drawstructs.h" +#include "hw_drawlist.h" +#include "flatvertices.h" +#include "hw_clock.h" +#include "hw_renderstate.h" +#include "hw_drawinfo.h" + +FMemArena RenderDataAllocator(1024*1024); // Use large blocks to reduce allocation time. + +void ResetRenderDataAllocator() +{ + RenderDataAllocator.FreeAll(); +} + +//========================================================================== +// +// +// +//========================================================================== +class StaticSortNodeArray : public TDeletingArray +{ + unsigned usecount; +public: + unsigned Size() { return usecount; } + void Clear() { usecount=0; } + void Release(int start) { usecount=start; } + SortNode * GetNew(); +}; + + +SortNode * StaticSortNodeArray::GetNew() +{ + if (usecount==TArray::Size()) + { + Push(new SortNode); + } + return operator[](usecount++); +} + + +static StaticSortNodeArray SortNodes; + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::Reset() +{ + if (sorted) SortNodes.Release(SortNodeStart); + sorted=NULL; + walls.Clear(); + flats.Clear(); + sprites.Clear(); + drawitems.Clear(); +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::UnlinkFromChain() +{ + if (parent) parent->next=next; + if (next) next->parent=parent; + parent=next=NULL; +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::Link(SortNode * hook) +{ + if (hook) + { + parent=hook->parent; + hook->parent=this; + } + next=hook; + if (parent) parent->next=this; +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::AddToEqual(SortNode *child) +{ + child->UnlinkFromChain(); + child->equal=equal; + equal=child; +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::AddToLeft(SortNode * child) +{ + child->UnlinkFromChain(); + child->Link(left); + left=child; +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::AddToRight(SortNode * child) +{ + child->UnlinkFromChain(); + child->Link(right); + right=child; +} + + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::MakeSortList() +{ + SortNode * p, * n, * c; + unsigned i; + + SortNodeStart=SortNodes.Size(); + p=NULL; + n=SortNodes.GetNew(); + for(i=0;iitemindex=(int)i; + n->left=n->equal=n->right=NULL; + n->parent=p; + p=n; + if (i!=drawitems.Size()-1) + { + c=SortNodes.GetNew(); + n->next=c; + n=c; + } + else + { + n->next=NULL; + } + } +} + + +//========================================================================== +// +// +// +//========================================================================== +SortNode * HWDrawList::FindSortPlane(SortNode * head) +{ + while (head->next && drawitems[head->itemindex].rendertype!=DrawType_FLAT) + head=head->next; + if (drawitems[head->itemindex].rendertype==DrawType_FLAT) return head; + return NULL; +} + + +//========================================================================== +// +// +// +//========================================================================== +SortNode * HWDrawList::FindSortWall(SortNode * head) +{ + float farthest = -FLT_MAX; + float nearest = FLT_MAX; + SortNode * best = NULL; + SortNode * node = head; + float bestdist = FLT_MAX; + + while (node) + { + HWDrawItem * it = &drawitems[node->itemindex]; + if (it->rendertype == DrawType_WALL) + { + float d = walls[it->index]->ViewDistance; + if (d > farthest) farthest = d; + if (d < nearest) nearest = d; + } + node = node->next; + } + if (farthest == INT_MIN) return NULL; + node = head; + farthest = (farthest + nearest) / 2; + while (node) + { + HWDrawItem * it = &drawitems[node->itemindex]; + if (it->rendertype == DrawType_WALL) + { + float di = fabsf(walls[it->index]->ViewDistance - farthest); + if (!best || di < bestdist) + { + best = node; + bestdist = di; + } + } + node = node->next; + } + return best; +} + +//========================================================================== +// +// Note: sloped planes are a huge problem... +// +//========================================================================== +void HWDrawList::SortPlaneIntoPlane(SortNode * head,SortNode * sort) +{ + HWFlat * fh= flats[drawitems[head->itemindex].index]; + HWFlat * fs= flats[drawitems[sort->itemindex].index]; + + if (fh->z==fs->z) + head->AddToEqual(sort); + else if ( (fh->zz && fh->ceiling) || (fh->z>fs->z && !fh->ceiling)) + head->AddToLeft(sort); + else + head->AddToRight(sort); +} + + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::SortWallIntoPlane(SortNode * head, SortNode * sort) +{ + HWFlat * fh = flats[drawitems[head->itemindex].index]; + HWWall * ws = walls[drawitems[sort->itemindex].index]; + + bool ceiling = fh->z > SortZ; + + if ((ws->ztop[0] > fh->z || ws->ztop[1] > fh->z) && (ws->zbottom[0] < fh->z || ws->zbottom[1] < fh->z)) + { + // We have to split this wall! + + HWWall *w = NewWall(); + *w = *ws; + + // Splitting is done in the shader with clip planes, if available + if (screen->hwcaps & RFL_NO_CLIP_PLANES) + { + ws->vertcount = 0; // invalidate current vertices. + float newtexv = ws->tcs[HWWall::UPLFT].v + ((ws->tcs[HWWall::LOLFT].v - ws->tcs[HWWall::UPLFT].v) / (ws->zbottom[0] - ws->ztop[0])) * (fh->z - ws->ztop[0]); + + // I make the very big assumption here that translucent walls in sloped sectors + // and 3D-floors never coexist in the same level - If that were the case this + // code would become extremely more complicated. + if (!ceiling) + { + ws->ztop[1] = w->zbottom[1] = ws->ztop[0] = w->zbottom[0] = fh->z; + ws->tcs[HWWall::UPRGT].v = w->tcs[HWWall::LORGT].v = ws->tcs[HWWall::UPLFT].v = w->tcs[HWWall::LOLFT].v = newtexv; + } + else + { + w->ztop[1] = ws->zbottom[1] = w->ztop[0] = ws->zbottom[0] = fh->z; + w->tcs[HWWall::UPLFT].v = ws->tcs[HWWall::LOLFT].v = w->tcs[HWWall::UPRGT].v = ws->tcs[HWWall::LORGT].v = newtexv; + } + } + + SortNode * sort2 = SortNodes.GetNew(); + memset(sort2, 0, sizeof(SortNode)); + sort2->itemindex = drawitems.Size() - 1; + + head->AddToLeft(sort); + head->AddToRight(sort2); + } + else if ((ws->zbottom[0] < fh->z && !ceiling) || (ws->ztop[0] > fh->z && ceiling)) // completely on the left side + { + head->AddToLeft(sort); + } + else + { + head->AddToRight(sort); + } + +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::SortSpriteIntoPlane(SortNode * head, SortNode * sort) +{ + HWFlat * fh = flats[drawitems[head->itemindex].index]; + HWSprite * ss = sprites[drawitems[sort->itemindex].index]; + + bool ceiling = fh->z > SortZ; + + auto hiz = ss->z1 > ss->z2 ? ss->z1 : ss->z2; + auto loz = ss->z1 < ss->z2 ? ss->z1 : ss->z2; + + if ((hiz > fh->z && loz < fh->z) || ss->modelframe) + { + // We have to split this sprite + HWSprite *s = NewSprite(); + *s = *ss; + + // Splitting is done in the shader with clip planes, if available. + // The fallback here only really works for non-y-billboarded sprites. + if (screen->hwcaps & RFL_NO_CLIP_PLANES) + { + float newtexv = ss->vt + ((ss->vb - ss->vt) / (ss->z2 - ss->z1))*(fh->z - ss->z1); + + if (!ceiling) + { + ss->z1 = s->z2 = fh->z; + ss->vt = s->vb = newtexv; + } + else + { + s->z1 = ss->z2 = fh->z; + s->vt = ss->vb = newtexv; + } + } + + SortNode * sort2 = SortNodes.GetNew(); + memset(sort2, 0, sizeof(SortNode)); + sort2->itemindex = drawitems.Size() - 1; + + head->AddToLeft(sort); + head->AddToRight(sort2); + } + else if ((ss->z2z && !ceiling) || (ss->z1>fh->z && ceiling)) // completely on the left side + { + head->AddToLeft(sort); + } + else + { + head->AddToRight(sort); + } +} + +//========================================================================== +// +// +// +//========================================================================== +#define MIN_EQ (0.0005f) + +// Lines start-end and fdiv must intersect. +inline double CalcIntersectionVertex(HWWall *w1, HWWall * w2) +{ + float ax = w1->glseg.x1, ay = w1->glseg.y1; + float bx = w1->glseg.x2, by = w1->glseg.y2; + float cx = w2->glseg.x1, cy = w2->glseg.y1; + float dx = w2->glseg.x2, dy = w2->glseg.y2; + return ((ay - cy)*(dx - cx) - (ax - cx)*(dy - cy)) / ((bx - ax)*(dy - cy) - (by - ay)*(dx - cx)); +} + +void HWDrawList::SortWallIntoWall(HWDrawInfo *di, SortNode * head,SortNode * sort) +{ + HWWall * wh= walls[drawitems[head->itemindex].index]; + HWWall * ws= walls[drawitems[sort->itemindex].index]; + float v1=wh->PointOnSide(ws->glseg.x1,ws->glseg.y1); + float v2=wh->PointOnSide(ws->glseg.x2,ws->glseg.y2); + + if (fabs(v1)type==RENDERWALL_FOGBOUNDARY && wh->type!=RENDERWALL_FOGBOUNDARY) + { + head->AddToRight(sort); + } + else if (ws->type!=RENDERWALL_FOGBOUNDARY && wh->type==RENDERWALL_FOGBOUNDARY) + { + head->AddToLeft(sort); + } + else + { + head->AddToEqual(sort); + } + } + else if (v1AddToLeft(sort); + } + else if (v1>-MIN_EQ && v2>-MIN_EQ) + { + head->AddToRight(sort); + } + else + { + double r = CalcIntersectionVertex(ws, wh); + + float ix=(float)(ws->glseg.x1+r*(ws->glseg.x2-ws->glseg.x1)); + float iy=(float)(ws->glseg.y1+r*(ws->glseg.y2-ws->glseg.y1)); + float iu=(float)(ws->tcs[HWWall::UPLFT].u + r * (ws->tcs[HWWall::UPRGT].u - ws->tcs[HWWall::UPLFT].u)); + float izt=(float)(ws->ztop[0]+r*(ws->ztop[1]-ws->ztop[0])); + float izb=(float)(ws->zbottom[0]+r*(ws->zbottom[1]-ws->zbottom[0])); + + ws->vertcount = 0; // invalidate current vertices. + HWWall *w= NewWall(); + *w = *ws; + + w->glseg.x1=ws->glseg.x2=ix; + w->glseg.y1=ws->glseg.y2=iy; + w->ztop[0]=ws->ztop[1]=izt; + w->zbottom[0]=ws->zbottom[1]=izb; + w->tcs[HWWall::LOLFT].u = w->tcs[HWWall::UPLFT].u = ws->tcs[HWWall::LORGT].u = ws->tcs[HWWall::UPRGT].u = iu; + ws->MakeVertices(di, false); + w->MakeVertices(di, false); + + SortNode * sort2=SortNodes.GetNew(); + memset(sort2,0,sizeof(SortNode)); + sort2->itemindex=drawitems.Size()-1; + + if (v1>0) + { + head->AddToLeft(sort2); + head->AddToRight(sort); + } + else + { + head->AddToLeft(sort); + head->AddToRight(sort2); + } + } +} + + +//========================================================================== +// +// +// +//========================================================================== +EXTERN_CVAR(Int, gl_billboard_mode) +EXTERN_CVAR(Bool, gl_billboard_faces_camera) +EXTERN_CVAR(Bool, gl_billboard_particles) + +inline double CalcIntersectionVertex(HWSprite *s, HWWall * w2) +{ + float ax = s->x1, ay = s->y1; + float bx = s->x2, by = s->y2; + float cx = w2->glseg.x1, cy = w2->glseg.y1; + float dx = w2->glseg.x2, dy = w2->glseg.y2; + return ((ay - cy)*(dx - cx) - (ax - cx)*(dy - cy)) / ((bx - ax)*(dy - cy) - (by - ay)*(dx - cx)); +} + +void HWDrawList::SortSpriteIntoWall(HWDrawInfo *di, SortNode * head,SortNode * sort) +{ + HWWall *wh= walls[drawitems[head->itemindex].index]; + HWSprite * ss= sprites[drawitems[sort->itemindex].index]; + + float v1 = wh->PointOnSide(ss->x1, ss->y1); + float v2 = wh->PointOnSide(ss->x2, ss->y2); + + if (fabs(v1)type==RENDERWALL_FOGBOUNDARY) + { + head->AddToLeft(sort); + } + else + { + head->AddToEqual(sort); + } + } + else if (v1AddToLeft(sort); + } + else if (v1>-MIN_EQ && v2>-MIN_EQ) + { + head->AddToRight(sort); + } + else + { + const bool drawWithXYBillboard = false;// + + const bool drawBillboardFacingCamera = gl_billboard_faces_camera; + // [Nash] has +ROLLSPRITE + const bool rotated = false;// + + // cannot sort them at the moment. This requires more complex splitting. + /* + if (drawWithXYBillboard || drawBillboardFacingCamera || rotated) + { + float v1 = wh->PointOnSide(ss->x, ss->y); + if (v1 < 0) + { + head->AddToLeft(sort); + } + else + { + head->AddToRight(sort); + } + return; + } + */ + double r=CalcIntersectionVertex(ss, wh); + + float ix=(float)(ss->x1 + r * (ss->x2-ss->x1)); + float iy=(float)(ss->y1 + r * (ss->y2-ss->y1)); + float iu=(float)(ss->ul + r * (ss->ur-ss->ul)); + + HWSprite *s = NewSprite(); + *s = *ss; + + s->x1=ss->x2=ix; + s->y1=ss->y2=iy; + s->ul=ss->ur=iu; + + SortNode * sort2=SortNodes.GetNew(); + memset(sort2,0,sizeof(SortNode)); + sort2->itemindex=drawitems.Size()-1; + + if (v1>0) + { + head->AddToLeft(sort2); + head->AddToRight(sort); + } + else + { + head->AddToLeft(sort); + head->AddToRight(sort2); + } + if (screen->BuffersArePersistent()) + { + s->vertexindex = ss->vertexindex = -1; + } + else + { + s->CreateVertices(di); + ss->CreateVertices(di); + } + + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +inline int HWDrawList::CompareSprites(SortNode * a,SortNode * b) +{ + HWSprite * s1= sprites[drawitems[a->itemindex].index]; + HWSprite * s2= sprites[drawitems[b->itemindex].index]; + + if (s1->depth < s2->depth) return 1; + if (s1->depth > s2->depth) return -1; + return reverseSort? s2->index-s1->index : s1->index-s2->index; +} + +//========================================================================== +// +// +// +//========================================================================== +SortNode * HWDrawList::SortSpriteList(SortNode * head) +{ + SortNode * n; + int count; + unsigned i; + + static TArray sortspritelist; + + SortNode * parent=head->parent; + + sortspritelist.Clear(); + for(count=0,n=head;n;n=n->next) sortspritelist.Push(n); + std::stable_sort(sortspritelist.begin(), sortspritelist.end(), [=](SortNode *a, SortNode *b) + { + return CompareSprites(a, b) < 0; + }); + + for(i=0;inext=NULL; + if (parent) parent->equal=sortspritelist[i]; + parent=sortspritelist[i]; + } + return sortspritelist[0]; +} + +//========================================================================== +// +// +// +//========================================================================== +SortNode * HWDrawList::DoSort(HWDrawInfo *di, SortNode * head) +{ + SortNode * node, * sn, * next; + + sn=FindSortPlane(head); + if (sn) + { + if (sn==head) head=head->next; + sn->UnlinkFromChain(); + node=head; + head=sn; + while (node) + { + next=node->next; + switch(drawitems[node->itemindex].rendertype) + { + case DrawType_FLAT: + SortPlaneIntoPlane(head,node); + break; + + case DrawType_WALL: + SortWallIntoPlane(head,node); + break; + + case DrawType_SPRITE: + SortSpriteIntoPlane(head,node); + break; + } + node=next; + } + } + else + { + sn=FindSortWall(head); + if (sn) + { + if (sn==head) head=head->next; + sn->UnlinkFromChain(); + node=head; + head=sn; + while (node) + { + next=node->next; + switch(drawitems[node->itemindex].rendertype) + { + case DrawType_WALL: + SortWallIntoWall(di, head,node); + break; + + case DrawType_SPRITE: + SortSpriteIntoWall(di, head, node); + break; + + case DrawType_FLAT: break; + } + node=next; + } + } + else + { + return SortSpriteList(head); + } + } + if (head->left) head->left=DoSort(di, head->left); + if (head->right) head->right=DoSort(di, head->right); + return sn; +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::Sort(HWDrawInfo *di) +{ + reverseSort = false; + SortZ = di->Viewpoint.Pos.Z; + MakeSortList(); + sorted = DoSort(di, SortNodes[SortNodeStart]); +} + +//========================================================================== +// +// Sorting the drawitems first by texture and then by light level +// +//========================================================================== + +void HWDrawList::SortWalls() +{ + if (drawitems.Size() > 1) + { + std::sort(drawitems.begin(), drawitems.end(), [=](const HWDrawItem &a, const HWDrawItem &b) -> bool + { + HWWall * w1 = walls[a.index]; + HWWall * w2 = walls[b.index]; + + if (w1->texture != w2->texture) return w1->texture < w2->texture; + return (w1->flags & 3) < (w2->flags & 3); + + }); + } +} + +void HWDrawList::SortFlats() +{ + if (drawitems.Size() > 1) + { + std::sort(drawitems.begin(), drawitems.end(), [=](const HWDrawItem &a, const HWDrawItem &b) + { + HWFlat * w1 = flats[a.index]; + HWFlat* w2 = flats[b.index]; + return w1->texture < w2->texture; + }); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +HWWall *HWDrawList::NewWall() +{ + auto wall = (HWWall*)RenderDataAllocator.Alloc(sizeof(HWWall)); + drawitems.Push(HWDrawItem(DrawType_WALL, walls.Push(wall))); + return wall; +} + +//========================================================================== +// +// +// +//========================================================================== +HWFlat *HWDrawList::NewFlat() +{ + auto flat = (HWFlat*)RenderDataAllocator.Alloc(sizeof(HWFlat)); + drawitems.Push(HWDrawItem(DrawType_FLAT,flats.Push(flat))); + return flat; +} + +//========================================================================== +// +// +// +//========================================================================== +HWSprite *HWDrawList::NewSprite() +{ + auto sprite = (HWSprite*)RenderDataAllocator.Alloc(sizeof(HWSprite)); + drawitems.Push(HWDrawItem(DrawType_SPRITE, sprites.Push(sprite))); + return sprite; +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::DoDraw(HWDrawInfo *di, FRenderState &state, bool translucent, int i) +{ + switch(drawitems[i].rendertype) + { + case DrawType_FLAT: + { + HWFlat * f= flats[drawitems[i].index]; + RenderFlat.Clock(); + f->DrawFlat(di, state, translucent); + RenderFlat.Unclock(); + } + break; + + case DrawType_WALL: + { + HWWall * w= walls[drawitems[i].index]; + RenderWall.Clock(); + w->DrawWall(di, state, translucent); + RenderWall.Unclock(); + } + break; + + case DrawType_SPRITE: + { + HWSprite * s= sprites[drawitems[i].index]; + RenderSprite.Clock(); + s->DrawSprite(di, state, translucent); + RenderSprite.Unclock(); + } + break; + } +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::Draw(HWDrawInfo *di, FRenderState &state, bool translucent) +{ + for (unsigned i = 0; i < drawitems.Size(); i++) + { + DoDraw(di, state, translucent, i); + } +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::DrawWalls(HWDrawInfo *di, FRenderState &state, bool translucent) +{ + RenderWall.Clock(); + for (auto &item : drawitems) + { + walls[item.index]->DrawWall(di, state, translucent); + } + RenderWall.Unclock(); +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::DrawFlats(HWDrawInfo *di, FRenderState &state, bool translucent) +{ + RenderFlat.Clock(); + for (unsigned i = 0; iDrawFlat(di, state, translucent); + } + RenderFlat.Unclock(); +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::DrawSorted(HWDrawInfo *di, FRenderState &state, SortNode * head) +{ + float clipsplit[2]; + int relation = 0; + float z = 0.f; + + state.GetClipSplit(clipsplit); + + if (drawitems[head->itemindex].rendertype == DrawType_FLAT) + { + z = flats[drawitems[head->itemindex].index]->z; + relation = z > di->Viewpoint.Pos.Z ? 1 : -1; + } + + + // left is further away, i.e. for stuff above viewz its z coordinate higher, for stuff below viewz its z coordinate is lower + if (head->left) + { + if (relation == -1) + { + state.SetClipSplit(clipsplit[0], z); // render below: set flat as top clip plane + } + else if (relation == 1) + { + state.SetClipSplit(z, clipsplit[1]); // render above: set flat as bottom clip plane + } + DrawSorted(di, state, head->left); + state.SetClipSplit(clipsplit); + } + DoDraw(di, state, true, head->itemindex); + if (head->equal) + { + SortNode * ehead = head->equal; + while (ehead) + { + DoDraw(di, state, true, ehead->itemindex); + ehead = ehead->equal; + } + } + // right is closer, i.e. for stuff above viewz its z coordinate is lower, for stuff below viewz its z coordinate is higher + if (head->right) + { + if (relation == 1) + { + state.SetClipSplit(clipsplit[0], z); // render below: set flat as top clip plane + } + else if (relation == -1) + { + state.SetClipSplit(z, clipsplit[1]); // render above: set flat as bottom clip plane + } + DrawSorted(di, state, head->right); + state.SetClipSplit(clipsplit); + } +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::DrawSorted(HWDrawInfo *di, FRenderState &state) +{ + if (drawitems.Size() == 0) return; + + if (!sorted) + { + screen->mVertexData->Map(); + Sort(di); + screen->mVertexData->Unmap(); + } + state.ClearClipSplit(); + state.EnableClipDistance(1, true); + state.EnableClipDistance(2, true); + DrawSorted(di, state, sorted); + state.EnableClipDistance(1, false); + state.EnableClipDistance(2, false); + state.ClearClipSplit(); +} + diff --git a/source/core/rendering/scene/hw_drawstructs.h b/source/core/rendering/scene/hw_drawstructs.h index e5cecfe11..02a2ced5e 100644 --- a/source/core/rendering/scene/hw_drawstructs.h +++ b/source/core/rendering/scene/hw_drawstructs.h @@ -19,7 +19,6 @@ class FMaterial; struct FTexCoordInfo; struct FSectorPortalGroup; struct FFlatVertex; -struct FLinePortalSpan; struct FDynLightData; class VSMatrix; struct FSpriteModelFrame; @@ -171,7 +170,7 @@ public: HWHorizonInfo * horizon; // for horizon information FSectorPortalGroup * portal; // stacked sector portals secplane_t * planemirror; // for plane mirrors - FLinePortalSpan *lineportal; // line-to-line portals + walltype *lineportal; // line-to-line portals }; */ diff --git a/source/core/rendering/scene/hw_portal.h b/source/core/rendering/scene/hw_portal.h new file mode 100644 index 000000000..375715604 --- /dev/null +++ b/source/core/rendering/scene/hw_portal.h @@ -0,0 +1,406 @@ +#pragma once + +#include "tarray.h" +#include "hw_drawinfo.h" +#include "hw_drawstructs.h" +#include "hw_renderstate.h" +#include "hw_material.h" + +class FSkyBox; + +struct HWSkyInfo +{ + float x_offset[2]; + float y_offset; // doubleskies don't have a y-offset + FGameTexture * texture[2]; + FTextureID skytexno1; + bool mirrored; + bool doublesky; + bool sky2; + PalEntry fadecolor; + + bool operator==(const HWSkyInfo & inf) + { + return !memcmp(this, &inf, sizeof(*this)); + } + bool operator!=(const HWSkyInfo & inf) + { + return !!memcmp(this, &inf, sizeof(*this)); + } + void init(HWDrawInfo *di, int sky1, PalEntry fadecolor); +}; + +struct HWHorizonInfo +{ + HWSectorPlane plane; + int lightlevel; + FColormap colormap; + PalEntry specialcolor; +}; + +struct BoundingRect +{ + double left, top, right, bottom; + + BoundingRect() = default; + BoundingRect(bool) + { + setEmpty(); + } + + void setEmpty() + { + left = top = FLT_MAX; + bottom = right = -FLT_MAX; + } + + bool contains(const BoundingRect& other) const + { + return left <= other.left && top <= other.top && right >= other.right && bottom >= other.bottom; + } + + bool contains(double x, double y) const + { + return left <= x && top <= y && right >= x && bottom >= y; + } + + template + bool contains(const T& vec) const + { + return left <= vec.X && top <= vec.Y && right >= vec.X && bottom >= vec.Y; + } + + bool intersects(const BoundingRect& other) const + { + return !(other.left > right || + other.right < left || + other.top > bottom || + other.bottom < top); + } + + void Union(const BoundingRect& other) + { + if (other.left < left) left = other.left; + if (other.right > right) right = other.right; + if (other.top < top) top = other.top; + if (other.bottom > bottom) bottom = other.bottom; + } + + double distanceTo(const BoundingRect& other) const + { + if (intersects(other)) return 0; + return std::max(std::min(fabs(left - other.right), fabs(right - other.left)), + std::min(fabs(top - other.bottom), fabs(bottom - other.top))); + } + + void addVertex(double x, double y) + { + if (x < left) left = x; + if (x > right) right = x; + if (y < top) top = y; + if (y > bottom) bottom = y; + } + + bool operator == (const BoundingRect& other) const + { + return left == other.left && top == other.top && right == other.right && bottom == other.bottom; + } + +}; + +struct secplane_t +{ + double a, b, c, d, ic; +}; + +struct FPortalSceneState; + +class HWPortal +{ + friend struct FPortalSceneState; + + enum + { + STP_Stencil, + STP_DepthClear, + STP_DepthRestore, + STP_AllInOne + }; + + TArray mPrimIndices; + unsigned int mTopCap = ~0u, mBottomCap = ~0u; + + void DrawPortalStencil(FRenderState &state, int pass); + +public: + FPortalSceneState * mState; + TArray lines; + BoundingRect boundingBox; + int planesused = 0; + + HWPortal(FPortalSceneState *s, bool local = false) : mState(s), boundingBox(false) + { + } + virtual ~HWPortal() {} + virtual int ClipSeg(walltype *seg, const DVector3 &viewpos) { return PClip_Inside; } + virtual int ClipSubsector(sectortype *sub) { return PClip_Inside; } + virtual int ClipPoint(const DVector2 &pos) { return PClip_Inside; } + virtual walltype *ClipLine() { return nullptr; } + virtual void * GetSource() const = 0; // GetSource MUST be implemented! + virtual const char *GetName() = 0; + virtual bool AllowSSAO() { return true; } + virtual bool IsSky() { return false; } + virtual bool NeedCap() { return true; } + virtual bool NeedDepthBuffer() { return true; } + virtual void DrawContents(HWDrawInfo *di, FRenderState &state) = 0; + virtual void RenderAttached(HWDrawInfo *di) {} + void SetupStencil(HWDrawInfo *di, FRenderState &state, bool usestencil); + void RemoveStencil(HWDrawInfo *di, FRenderState &state, bool usestencil); + + void AddLine(HWWall * l) + { + lines.Push(*l); + boundingBox.addVertex(l->glseg.x1, l->glseg.y1); + boundingBox.addVertex(l->glseg.x2, l->glseg.y2); + } + + +}; + + +struct FPortalSceneState +{ + int MirrorFlag = 0; + int PlaneMirrorFlag = 0; + int renderdepth = 0; + + int PlaneMirrorMode = 0; + bool inskybox = 0; + + UniqueList UniqueSkies; + UniqueList UniqueHorizons; + UniqueList UniquePlaneMirrors; + + int skyboxrecursion = 0; + + void BeginScene() + { + UniqueSkies.Clear(); + UniqueHorizons.Clear(); + UniquePlaneMirrors.Clear(); + } + + bool isMirrored() const + { + return !!((MirrorFlag ^ PlaneMirrorFlag) & 1); + } + + void StartFrame(); + bool RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di, FRenderState &state); + void EndFrame(HWDrawInfo *outer_di, FRenderState &state); + void RenderPortal(HWPortal *p, FRenderState &state, bool usestencil, HWDrawInfo *outer_di); +}; + +extern FPortalSceneState portalState; + + +class HWScenePortalBase : public HWPortal +{ +protected: + HWScenePortalBase(FPortalSceneState *state) : HWPortal(state, false) + { + + } +public: + void ClearClipper(HWDrawInfo *di, Clipper *clipper); + virtual bool NeedDepthBuffer() { return true; } + virtual void DrawContents(HWDrawInfo *di, FRenderState &state) + { + if (Setup(di, state, di->mClipper)) + { + di->DrawScene(DM_PORTAL); + Shutdown(di, state); + } + else state.ClearScreen(); + } + virtual bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) = 0; + virtual void Shutdown(HWDrawInfo *di, FRenderState &rstate) {} +}; + +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. + walltype* line; + vec2_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(FPortalSceneState *state, walltype *line) : HWScenePortalBase(state) + { + this->line = line; + v1 = &line->pos; + v2 = &wall[line->point2].pos; + CalcDelta(); + } + + void CalcDelta() + { + //delta = v2->fPos() - v1->fPos(); + } + + int ClipSeg(walltype *seg, const DVector3 &viewpos) override; + int ClipSubsector(sectortype *sub) override; + int ClipPoint(const DVector2 &pos); + bool NeedCap() override { return false; } +}; + +struct HWMirrorPortal : public HWLinePortal +{ + +protected: + bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di, FRenderState &rstate) override; + void * GetSource() const override { return line; } + const char *GetName() override; + +public: + + HWMirrorPortal(FPortalSceneState *state, walltype * line) + : HWLinePortal(state, line) + { + } +}; + + +struct HWLineToLinePortal : public HWLinePortal +{ +protected: + bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override; + virtual void * GetSource() const override { return line; } + virtual const char *GetName() override; + virtual walltype *ClipLine() override { return line; } + virtual void RenderAttached(HWDrawInfo *di) override; + +public: + + HWLineToLinePortal(FPortalSceneState *state, walltype *ll) + : HWLinePortal(state, ll) + { + } +}; + + +struct HWSkyboxPortal : public HWScenePortalBase +{ + bool oldclamp; + int old_pm; + spritetype * portal; + +protected: + bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di, FRenderState &rstate) override; + virtual void * GetSource() const { return portal; } + virtual bool IsSky() { return true; } + virtual const char *GetName(); + virtual bool AllowSSAO() override; + +public: + + + HWSkyboxPortal(FPortalSceneState *state, spritetype * pt) : HWScenePortalBase(state) + { + portal = pt; + } + +}; + + +struct HWSectorStackPortal : public HWScenePortalBase +{ + TArray sectors; +protected: + bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di, FRenderState &rstate) 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(FPortalSceneState *state, FSectorPortalGroup *pt) : HWScenePortalBase(state) + { + origin = pt; + } + void SetupCoverage(HWDrawInfo *di); + void AddSector(sectortype *sub) + { + sectors.Push(sub); + } + +}; + +struct HWPlaneMirrorPortal : public HWScenePortalBase +{ + int old_pm; +protected: + bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di, FRenderState &rstate) override; + virtual void * GetSource() const { return origin; } + virtual const char *GetName(); + secplane_t * origin; + +public: + + HWPlaneMirrorPortal(FPortalSceneState *state, secplane_t * pt) : HWScenePortalBase(state) + { + origin = pt; + } + +}; + + +struct HWHorizonPortal : public HWPortal +{ + HWHorizonInfo * origin; + unsigned int voffset; + unsigned int vcount; + friend struct HWEEHorizonPortal; + +protected: + virtual void DrawContents(HWDrawInfo *di, FRenderState &state); + virtual void * GetSource() const { return origin; } + virtual bool NeedDepthBuffer() { return false; } + virtual bool NeedCap() { return false; } + virtual const char *GetName(); + +public: + + HWHorizonPortal(FPortalSceneState *state, HWHorizonInfo * pt, FRenderViewpoint &vp, bool local = false); +}; + +struct HWSkyPortal : public HWPortal +{ + HWSkyInfo * origin; + FSkyVertexBuffer *vertexBuffer; + friend struct HWEEHorizonPortal; + +protected: + virtual void DrawContents(HWDrawInfo *di, FRenderState &state); + virtual void * GetSource() const { return origin; } + virtual bool IsSky() { return true; } + virtual bool NeedDepthBuffer() { return false; } + virtual const char *GetName(); + +public: + + + HWSkyPortal(FSkyVertexBuffer *vertexbuffer, FPortalSceneState *state, HWSkyInfo * pt, bool local = false) + : HWPortal(state, local) + { + origin = pt; + vertexBuffer = vertexbuffer; + } + +};