vkdoom_m/src/gl/scene/gl_portal.cpp
Christoph Oelckers c3d5b960ee - refactored the entire Stereo3D system to get rid of the class hierarchy and all its ugly implications.
Ultimately all this needs is a small data table describing the geometric properties of each mode and a single Present function that calls the mode specific variants.
Code size got reduced from 50kb to less than 20kb with proper separation of the generic parts from the OpenGL parts.
2018-06-24 17:16:07 +02:00

511 lines
15 KiB
C++

//
//---------------------------------------------------------------------------
//
// Copyright(C) 2004-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/
//
//--------------------------------------------------------------------------
//
/*
** gl_portal.cpp
** Generalized portal maintenance classes for skyboxes, horizons etc.
**
*/
#include "gl_load/gl_system.h"
#include "p_local.h"
#include "c_dispatch.h"
#include "doomstat.h"
#include "a_sharedglobal.h"
#include "r_sky.h"
#include "p_maputl.h"
#include "d_player.h"
#include "g_levellocals.h"
#include "gl_load/gl_interface.h"
#include "gl/renderer/gl_lightdata.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_renderstate.h"
#include "gl/data/gl_vertexbuffer.h"
#include "hwrenderer/scene/hw_clipper.h"
#include "gl/scene/gl_portal.h"
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//
// General portal handling code
//
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
EXTERN_CVAR(Bool, gl_portals)
EXTERN_CVAR(Bool, gl_noquery)
EXTERN_CVAR(Int, r_mirror_recursions)
//==========================================================================
//
//
//
//==========================================================================
void GLPortal::ClearScreen(HWDrawInfo *di)
{
bool multi = !!glIsEnabled(GL_MULTISAMPLE);
di->VPUniforms.mViewMatrix.loadIdentity();
di->VPUniforms.mProjectionMatrix.ortho(0, SCREENWIDTH, SCREENHEIGHT, 0, -1.0f, 1.0f);
di->ApplyVPUniforms();
gl_RenderState.SetColor(0, 0, 0);
gl_RenderState.Apply();
glDisable(GL_MULTISAMPLE);
glDisable(GL_DEPTH_TEST);
glDrawArrays(GL_TRIANGLE_STRIP, FFlatVertexBuffer::FULLSCREEN_INDEX, 4);
glEnable(GL_DEPTH_TEST);
if (multi) glEnable(GL_MULTISAMPLE);
}
//-----------------------------------------------------------------------------
//
// DrawPortalStencil
//
//-----------------------------------------------------------------------------
void GLPortal::DrawPortalStencil()
{
if (mPrimIndices.Size() == 0)
{
mPrimIndices.Resize(2 * lines.Size());
for (unsigned int i = 0; i < lines.Size(); i++)
{
mPrimIndices[i * 2] = lines[i].vertindex;
mPrimIndices[i * 2 + 1] = lines[i].vertcount;
}
}
gl_RenderState.Apply();
for (unsigned int i = 0; i < mPrimIndices.Size(); i += 2)
{
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_FAN, mPrimIndices[i], mPrimIndices[i + 1]);
}
if (NeedCap() && lines.Size() > 1)
{
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_FAN, FFlatVertexBuffer::STENCILTOP_INDEX, 4);
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_FAN, FFlatVertexBuffer::STENCILBOTTOM_INDEX, 4);
}
}
//-----------------------------------------------------------------------------
//
// Start
//
//-----------------------------------------------------------------------------
bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDrawInfo **pDi)
{
*pDi = nullptr;
rendered_portals++;
Clocker c(PortalAll);
if (usestencil)
{
if (!gl_portals)
{
return false;
}
// Create stencil
glStencilFunc(GL_EQUAL, mState->recursion, ~0); // create stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment stencil of valid pixels
{
glColorMask(0,0,0,0); // don't write to the graphics buffer
gl_RenderState.SetEffect(EFF_STENCIL);
gl_RenderState.EnableTexture(false);
gl_RenderState.ResetColor();
glDepthFunc(GL_LESS);
gl_RenderState.Apply();
if (NeedDepthBuffer())
{
glDepthMask(false); // don't write to Z-buffer!
if (!NeedDepthBuffer()) doquery = false; // too much overhead and nothing to gain.
else if (gl_noquery) doquery = false;
// Use occlusion query to avoid rendering portals that aren't visible
if (doquery) glBeginQuery(GL_SAMPLES_PASSED, GLRenderer->PortalQueryObject);
DrawPortalStencil();
if (doquery) glEndQuery(GL_SAMPLES_PASSED);
// Clear Z-buffer
glStencilFunc(GL_EQUAL, mState->recursion + 1, ~0); // draw sky into stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil
glDepthMask(true); // enable z-buffer again
glDepthRange(1, 1);
glDepthFunc(GL_ALWAYS);
DrawPortalStencil();
// set normal drawing mode
gl_RenderState.EnableTexture(true);
glDepthFunc(GL_LESS);
glColorMask(1, 1, 1, 1);
gl_RenderState.SetEffect(EFF_NONE);
glDepthRange(0, 1);
GLuint sampleCount = 1;
if (doquery) glGetQueryObjectuiv(GLRenderer->PortalQueryObject, GL_QUERY_RESULT, &sampleCount);
if (sampleCount == 0) // not visible
{
// restore default stencil op.
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, mState->recursion, ~0); // draw sky into stencil
return false;
}
}
else
{
// No z-buffer is needed therefore we can skip all the complicated stuff that is involved
// No occlusion queries will be done here. For these portals the overhead is far greater
// than the benefit.
// Note: We must draw the stencil with z-write enabled here because there is no second pass!
glDepthMask(true);
DrawPortalStencil();
glStencilFunc(GL_EQUAL, mState->recursion + 1, ~0); // draw sky into stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil
gl_RenderState.EnableTexture(true);
glColorMask(1,1,1,1);
gl_RenderState.SetEffect(EFF_NONE);
glDisable(GL_DEPTH_TEST);
glDepthMask(false); // don't write to Z-buffer!
}
}
mState->recursion++;
}
else
{
if (!NeedDepthBuffer())
{
glDepthMask(false);
glDisable(GL_DEPTH_TEST);
}
}
*pDi = FDrawInfo::StartDrawInfo(outer_di->Viewpoint, &outer_di->VPUniforms);
(*pDi)->mCurrentPortal = this;
// save viewpoint
savedvisibility = outer_di->Viewpoint.camera ? outer_di->Viewpoint.camera->renderflags & RF_MAYBEINVISIBLE : ActorRenderFlags::FromInt(0);
return true;
}
//-----------------------------------------------------------------------------
//
// End
//
//-----------------------------------------------------------------------------
void GLPortal::End(HWDrawInfo *di, bool usestencil)
{
bool needdepth = NeedDepthBuffer();
Clocker c(PortalAll);
di = static_cast<FDrawInfo*>(di)->EndDrawInfo();
di->ApplyVPUniforms();
if (usestencil)
{
auto &vp = di->Viewpoint;
// Restore the old view
if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility;
glColorMask(0, 0, 0, 0); // no graphics
gl_RenderState.SetEffect(EFF_NONE);
gl_RenderState.ResetColor();
gl_RenderState.EnableTexture(false);
gl_RenderState.Apply();
if (needdepth)
{
// first step: reset the depth buffer to max. depth
glDepthRange(1, 1); // always
glDepthFunc(GL_ALWAYS); // write the farthest depth value
DrawPortalStencil();
}
else
{
glEnable(GL_DEPTH_TEST);
}
// second step: restore the depth buffer to the previous values and reset the stencil
glDepthFunc(GL_LEQUAL);
glDepthRange(0, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
glStencilFunc(GL_EQUAL, mState->recursion, ~0); // draw sky into stencil
DrawPortalStencil();
glDepthFunc(GL_LESS);
gl_RenderState.EnableTexture(true);
gl_RenderState.SetEffect(EFF_NONE);
glColorMask(1, 1, 1, 1);
mState->recursion--;
// restore old stencil op.
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, mState->recursion, ~0); // draw sky into stencil
}
else
{
if (needdepth)
{
glClear(GL_DEPTH_BUFFER_BIT);
}
else
{
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
}
auto &vp = di->Viewpoint;
// Restore the old view
if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility;
di->SetupView(vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(mState->MirrorFlag & 1), !!(mState->PlaneMirrorFlag & 1));
// This draws a valid z-buffer into the stencil's contents to ensure it
// doesn't get overwritten by the level's geometry.
gl_RenderState.ResetColor();
glDepthFunc(GL_LEQUAL);
glDepthRange(0, 1);
glColorMask(0, 0, 0, 1); // mark portal in alpha channel but don't touch color
gl_RenderState.SetEffect(EFF_STENCIL);
gl_RenderState.EnableTexture(false);
gl_RenderState.BlendFunc(GL_ONE, GL_ZERO);
gl_RenderState.BlendEquation(GL_FUNC_ADD);
gl_RenderState.Apply();
DrawPortalStencil();
gl_RenderState.SetEffect(EFF_NONE);
gl_RenderState.EnableTexture(true);
glColorMask(1, 1, 1, 1); // mark portal in alpha channel but don't touch color
glDepthFunc(GL_LESS);
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//
// Horizon Portal
//
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
GLHorizonPortal::GLHorizonPortal(FPortalSceneState *s, GLHorizonInfo * pt, FRenderViewpoint &vp, bool local)
: GLPortal(s, local)
{
origin = pt;
// create the vertex data for this horizon portal.
GLSectorPlane * sp = &origin->plane;
const float vx = vp.Pos.X;
const float vy = vp.Pos.Y;
const float vz = vp.Pos.Z;
const float z = sp->Texheight;
const float tz = (z - vz);
// Draw to some far away boundary
// This is not drawn as larger strips because it causes visual glitches.
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
for (float x = -32768 + vx; x<32768 + vx; x += 4096)
{
for (float y = -32768 + vy; y<32768 + vy; y += 4096)
{
ptr->Set(x, z, y, x / 64, -y / 64);
ptr++;
ptr->Set(x + 4096, z, y, x / 64 + 64, -y / 64);
ptr++;
ptr->Set(x, z, y + 4096, x / 64, -y / 64 - 64);
ptr++;
ptr->Set(x + 4096, z, y + 4096, x / 64 + 64, -y / 64 - 64);
ptr++;
}
}
// fill the gap between the polygon and the true horizon
// Since I can't draw into infinity there can always be a
// small gap
ptr->Set(-32768 + vx, z, -32768 + vy, 512.f, 0);
ptr++;
ptr->Set(-32768 + vx, vz, -32768 + vy, 512.f, tz);
ptr++;
ptr->Set(-32768 + vx, z, 32768 + vy, -512.f, 0);
ptr++;
ptr->Set(-32768 + vx, vz, 32768 + vy, -512.f, tz);
ptr++;
ptr->Set(32768 + vx, z, 32768 + vy, 512.f, 0);
ptr++;
ptr->Set(32768 + vx, vz, 32768 + vy, 512.f, tz);
ptr++;
ptr->Set(32768 + vx, z, -32768 + vy, -512.f, 0);
ptr++;
ptr->Set(32768 + vx, vz, -32768 + vy, -512.f, tz);
ptr++;
ptr->Set(-32768 + vx, z, -32768 + vy, 512.f, 0);
ptr++;
ptr->Set(-32768 + vx, vz, -32768 + vy, 512.f, tz);
ptr++;
vcount = GLRenderer->mVBO->GetCount(ptr, &voffset) - 10;
}
//-----------------------------------------------------------------------------
//
// GLHorizonPortal::DrawContents
//
//-----------------------------------------------------------------------------
void GLHorizonPortal::DrawContents(HWDrawInfo *hwdi)
{
auto di = static_cast<FDrawInfo *>(hwdi);
Clocker c(PortalAll);
FMaterial * gltexture;
PalEntry color;
player_t * player=&players[consoleplayer];
GLSectorPlane * sp = &origin->plane;
auto &vp = di->Viewpoint;
gltexture=FMaterial::ValidateTexture(sp->texture, false, true);
if (!gltexture)
{
ClearScreen(di);
return;
}
di->SetCameraPos(vp.Pos);
if (gltexture && gltexture->tex->isFullbright())
{
// glowing textures are always drawn full bright without color
di->SetColor(255, 0, origin->colormap, 1.f);
di->SetFog(255, 0, &origin->colormap, false);
}
else
{
int rel = getExtraLight();
di->SetColor(origin->lightlevel, rel, origin->colormap, 1.0f);
di->SetFog(origin->lightlevel, rel, &origin->colormap, false);
}
gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false);
gl_RenderState.SetObjectColor(origin->specialcolor);
gl_RenderState.SetPlaneTextureRotation(sp, gltexture);
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
gl_RenderState.BlendFunc(GL_ONE,GL_ZERO);
gl_RenderState.Apply();
for (unsigned i = 0; i < vcount; i += 4)
{
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, voffset + i, 4);
}
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, voffset + vcount, 10);
gl_RenderState.EnableTextureMatrix(false);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//
// Eternity-style horizon portal
//
// To the rest of the engine these masquerade as a skybox portal
// Internally they need to draw two horizon or sky portals
// and will use the respective classes to achieve that.
//
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void GLEEHorizonPortal::DrawContents(HWDrawInfo *di)
{
auto &vp = di->Viewpoint;
sector_t *sector = portal->mOrigin;
if (sector->GetTexture(sector_t::floor) == skyflatnum ||
sector->GetTexture(sector_t::ceiling) == skyflatnum)
{
GLSkyInfo skyinfo;
skyinfo.init(sector->sky, 0);
GLSkyPortal sky(mState, &skyinfo, true);
sky.DrawContents(di);
}
if (sector->GetTexture(sector_t::ceiling) != skyflatnum)
{
GLHorizonInfo horz;
horz.plane.GetFromSector(sector, sector_t::ceiling);
horz.lightlevel = hw_ClampLight(sector->GetCeilingLight());
horz.colormap = sector->Colormap;
horz.specialcolor = 0xffffffff;
if (portal->mType == PORTS_PLANE)
{
horz.plane.Texheight = vp.Pos.Z + fabs(horz.plane.Texheight);
}
GLHorizonPortal ceil(mState, &horz, di->Viewpoint, true);
ceil.DrawContents(di);
}
if (sector->GetTexture(sector_t::floor) != skyflatnum)
{
GLHorizonInfo horz;
horz.plane.GetFromSector(sector, sector_t::floor);
horz.lightlevel = hw_ClampLight(sector->GetFloorLight());
horz.colormap = sector->Colormap;
horz.specialcolor = 0xffffffff;
if (portal->mType == PORTS_PLANE)
{
horz.plane.Texheight = vp.Pos.Z - fabs(horz.plane.Texheight);
}
GLHorizonPortal floor(mState, &horz, di->Viewpoint, true);
floor.DrawContents(di);
}
}
const char *GLSkyPortal::GetName() { return "Sky"; }
const char *GLHorizonPortal::GetName() { return "Horizon"; }
const char *GLEEHorizonPortal::GetName() { return "EEHorizon"; }