qzdoom/src/hwrenderer/scene/hw_drawinfo.cpp
Christoph Oelckers f2e593f8bf - disabled the hack for fixing the original design of the portal in KDiZD's Z1M1.
This portal got fixed in a later re-release of KDiZD and no other portal needs this runtime fix to my knowledge.
The main problem here is that this runtime fix requires some manipulation of the render data that does not work anymore.

Should other maps need this fix as well they are probably best served with a compatibility entry.
2018-11-06 21:41:16 +01:00

657 lines
19 KiB
C++

//
//---------------------------------------------------------------------------
//
// 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);
//==========================================================================
//
//
//
//==========================================================================
class FDrawInfoList
{
public:
TDeletingArray<HWDrawInfo *> 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->DrawScene = nullptr;
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();
if (parent) di->DrawScene = parent->DrawScene;
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()
{
otherFloorPlanes.Clear();
otherCeilingPlanes.Clear();
floodFloorSegs.Clear();
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();
section_renderflags.Resize(level.sections.allSections.Size());
ss_renderflags.Resize(level.subsectors.Size());
no_renderflags.Resize(level.subsectors.Size());
memset(&section_renderflags[0], 0, level.sections.allSections.Size() * sizeof(section_renderflags[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<int>(gl_bandedswlight) | (static_cast<int>(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
screen->mVertexData->Map();
screen->mLights->Map();
RenderBSP(level.HeadNode());
// 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
PrepareUnhandledMissingTextures();
DispatchRenderHacks();
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.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<HWPortal *>(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(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, const std::function<void(HWDrawInfo *,int)> &drawScene)
{
screen->mPortalState->BeginScene();
int mapsection = R_PointInSubsector(Viewpoint.Pos)->mapsection;
CurrentMapSections.Set(mapsection);
DrawScene = drawScene;
DrawScene(this, toscreen ? DM_MAINVIEW : DM_OFFSCREEN);
}