// //--------------------------------------------------------------------------- // // 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 "a_sharedglobal.h" #include "r_utility.h" #include "r_sky.h" #include "d_player.h" #include "g_levellocals.h" #include "hw_fakeflat.h" #include "hw_portal.h" #include "hw_renderstate.h" #include "hw_drawinfo.h" #include "po_man.h" #include "r_data/models/models.h" #include "hwrenderer/utility/hw_clock.h" #include "hwrenderer/utility/hw_cvars.h" #include "hwrenderer/data/hw_viewpointbuffer.h" #include "hwrenderer/data/flatvertices.h" #include "hwrenderer/dynlights/hw_lightbuffer.h" #include "hwrenderer/utility/hw_vrmodes.h" #include "hw_clipper.h" EXTERN_CVAR(Float, r_visibility) CVAR(Bool, gl_bandedswlight, false, CVAR_ARCHIVE) CVAR(Bool, gl_sort_textures, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back); //========================================================================== // // // //========================================================================== template inline void DeleteLinkedList(T *node) { while (node) { auto n = node; node = node->next; delete n; } } 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 screen->CreateDrawInfo(); } 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; 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.SetDefaults(); 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() { for (auto node : otherfloorplanes) DeleteLinkedList(node); otherfloorplanes.Clear(); for (auto node : otherceilingplanes) DeleteLinkedList(node); otherceilingplanes.Clear(); for (auto node : floodfloorsegs) DeleteLinkedList(node); floodfloorsegs.Clear(); for (auto node : floodceilingsegs) DeleteLinkedList(node); floodceilingsegs.Clear(); // clear all the lists that might not have been cleared already MissingUpperTextures.Clear(); MissingLowerTextures.Clear(); MissingUpperSegs.Clear(); MissingLowerSegs.Clear(); SubsectorHacks.Clear(); CeilingStacks.Clear(); FloorStacks.Clear(); HandledSubsectors.Clear(); spriteindex = 0; CurrentMapSections.Resize(level.NumMapSections); CurrentMapSections.Zero(); sectorrenderflags.Resize(level.sectors.Size()); ss_renderflags.Resize(level.subsectors.Size()); no_renderflags.Resize(level.subsectors.Size()); memset(§orrenderflags[0], 0, level.sectors.Size() * sizeof(sectorrenderflags[0])); memset(&ss_renderflags[0], 0, level.subsectors.Size() * sizeof(ss_renderflags[0])); memset(&no_renderflags[0], 0, level.nodes.Size() * sizeof(no_renderflags[0])); Decals[0].Clear(); Decals[1].Clear(); mClipPortal = nullptr; mCurrentPortal = nullptr; } //========================================================================== // // // //========================================================================== void HWDrawInfo::UpdateCurrentMapSection() { const int mapsection = R_PointInSubsector(Viewpoint.Pos)->mapsection; CurrentMapSections.Set(mapsection); } //----------------------------------------------------------------------------- // // Sets the area the camera is in // //----------------------------------------------------------------------------- void HWDrawInfo::SetViewArea() { auto &vp = Viewpoint; // The render_sector is better suited to represent the current position in GL vp.sector = R_PointInSubsector(vp.Pos)->render_sector; // Get the heightsec state from the render sector, not the current one! if (vp.sector->GetHeightSec()) { in_area = vp.Pos.Z <= vp.sector->heightsec->floorplane.ZatPoint(vp.Pos) ? area_below : (vp.Pos.Z > vp.sector->heightsec->ceilingplane.ZatPoint(vp.Pos) && !(vp.sector->heightsec->MoreFlags&SECMF_FAKEFLOORONLY)) ? area_above : area_normal; } else { in_area = level.HasHeightSecs ? area_default : area_normal; // depends on exposed lower sectors, if map contains heightsecs. } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- int HWDrawInfo::SetFullbrightFlags(player_t *player) { FullbrightFlags = 0; // check for special colormaps player_t * cplayer = player? player->camera->player : nullptr; if (cplayer) { int cm = CM_DEFAULT; if (cplayer->extralight == INT_MIN) { cm = CM_FIRSTSPECIALCOLORMAP + INVERSECOLORMAP; Viewpoint.extralight = 0; FullbrightFlags = Fullbright; // This does never set stealth vision. } else if (cplayer->fixedcolormap != NOFIXEDCOLORMAP) { cm = CM_FIRSTSPECIALCOLORMAP + cplayer->fixedcolormap; FullbrightFlags = Fullbright; if (gl_enhanced_nv_stealth > 2) FullbrightFlags |= StealthVision; } else if (cplayer->fixedlightlevel != -1) { auto torchtype = PClass::FindActor(NAME_PowerTorch); auto litetype = PClass::FindActor(NAME_PowerLightAmp); for (AInventory * in = cplayer->mo->Inventory; in; in = in->Inventory) { //PalEntry color = in->CallGetBlend(); // Need special handling for light amplifiers if (in->IsKindOf(torchtype)) { FullbrightFlags = Fullbright; if (gl_enhanced_nv_stealth > 1) FullbrightFlags |= StealthVision; } else if (in->IsKindOf(litetype)) { FullbrightFlags = Fullbright; if (gl_enhanced_nightvision) FullbrightFlags |= Nightvision; if (gl_enhanced_nv_stealth > 0) FullbrightFlags |= StealthVision; } } } return cm; } else { return CM_DEFAULT; } } //----------------------------------------------------------------------------- // // R_FrustumAngle // //----------------------------------------------------------------------------- angle_t HWDrawInfo::FrustumAngle() { 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(r_viewwindow.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 ? -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); 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; } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void HWViewpointUniforms::SetDefaults() { mProjectionMatrix.loadIdentity(); mViewMatrix.loadIdentity(); mNormalViewMatrix.loadIdentity(); mViewHeight = viewheight; mGlobVis = (float)R_GetGlobVis(r_viewwindow, r_visibility) / 32.f; mPalLightLevels = static_cast(gl_bandedswlight) | (static_cast(gl_fogmode) << 8); mClipLine.X = -10000000.0f; mShadowmapFilter = gl_shadowmap_filter; } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- GLDecal *HWDrawInfo::AddDecal(bool onmirror) { auto decal = (GLDecal*)RenderDataAllocator.Alloc(sizeof(GLDecal)); Decals[onmirror ? 1 : 0].Push(decal); return decal; } //----------------------------------------------------------------------------- // // CreateScene // // creates the draw lists for the current scene // //----------------------------------------------------------------------------- void HWDrawInfo::CreateScene() { const auto &vp = Viewpoint; angle_t a1 = FrustumAngle(); mClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + a1, vp.Angles.Yaw.BAMs() - a1); // reset the portal manager screen->mPortalState->StartFrame(); PO_LinkToSubsectors(); ProcessAll.Clock(); // clip the scene and fill the drawlists Bsp.Clock(); screen->mVertexData->Map(); screen->mLights->Map(); // Give the DrawInfo the viewpoint in fixed point because that's what the nodes are. viewx = FLOAT2FIXED(vp.Pos.X); viewy = FLOAT2FIXED(vp.Pos.Y); validcount++; // used for processing sidedefs only once by the renderer. RenderBSPNode(level.HeadNode()); PreparePlayerSprites(vp.sector, in_area); // Process all the sprites on the current portal's back side which touch the portal. if (mCurrentPortal != nullptr) mCurrentPortal->RenderAttached(this); Bsp.Unclock(); // 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. HandleMissingTextures(in_area); // Missing upper/lower textures HandleHackedSubsectors(); // open sector hacks for deep water ProcessSectorStacks(in_area); // merge visplanes of sector stacks PrepareUnhandledMissingTextures(); 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); screen->mLights->BindBase(state); // not needed for OpenGL but necessary for Vulkan command buffers to do it here! 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(); screen->mVertexData->Bind(state); screen->mViewpoints->Bind(state, vpIndex); gp->RemoveStencil(this, state, usestencil); } //----------------------------------------------------------------------------- // // Draws player sprites and color blend // //----------------------------------------------------------------------------- void HWDrawInfo::EndDrawScene(sector_t * viewsector, FRenderState &state) { state.EnableFog(false); // [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); } 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(sector_t * 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.SetSoftLightLevel(-1); // 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); } //----------------------------------------------------------------------------- // // R_RenderView - renders one view - either the screen or a camera texture // //----------------------------------------------------------------------------- void HWDrawInfo::ProcessScene(bool toscreen) { screen->mPortalState->BeginScene(); int mapsection = R_PointInSubsector(Viewpoint.Pos)->mapsection; CurrentMapSections.Set(mapsection); DrawScene(toscreen ? DM_MAINVIEW : DM_OFFSCREEN); }