vkdoom_m/src/gl/scene/gl_flats.cpp
Christoph Oelckers 243e12bd8f - split gl_flats.cpp
2018-04-28 13:24:45 +02:00

406 lines
12 KiB
C++

//
//---------------------------------------------------------------------------
//
// Copyright(C) 2000-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_flat.cpp
** Flat rendering
**
*/
#include "gl/system/gl_system.h"
#include "a_sharedglobal.h"
#include "r_defs.h"
#include "r_sky.h"
#include "r_utility.h"
#include "doomstat.h"
#include "d_player.h"
#include "g_levellocals.h"
#include "actorinlines.h"
#include "p_lnspec.h"
#include "hwrenderer/dynlights/hw_dynlightdata.h"
#include "gl/system/gl_interface.h"
#include "hwrenderer/utility/hw_cvars.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_lightdata.h"
#include "gl/renderer/gl_renderstate.h"
#include "gl/data/gl_vertexbuffer.h"
#include "gl/dynlights/gl_lightbuffer.h"
#include "gl/scene/gl_drawinfo.h"
#include "gl/scene/gl_scenedrawer.h"
#include "gl/renderer/gl_quaddrawer.h"
//==========================================================================
//
// Flats
//
//==========================================================================
extern FDynLightData lightdata;
void FDrawInfo::SetupSubsectorLights(GLFlat *flat, int pass, subsector_t * sub, int *dli)
{
if (dli != NULL && *dli != -1)
{
gl_RenderState.ApplyLightIndex(GLRenderer->mLights->GetIndex(*dli));
(*dli)++;
return;
}
if (flat->SetupSubsectorLights(pass, sub))
{
int d = GLRenderer->mLights->UploadLights(lightdata);
if (pass == GLPASS_LIGHTSONLY)
{
GLRenderer->mLights->StoreIndex(d);
}
else
{
gl_RenderState.ApplyLightIndex(d);
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void FDrawInfo::DrawSubsector(GLFlat *flat, subsector_t * sub)
{
if (gl.buffermethod != BM_DEFERRED)
{
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
for (unsigned int k = 0; k < sub->numlines; k++)
{
vertex_t *vt = sub->firstline[k].v1;
ptr->x = vt->fX();
ptr->z = flat->plane.plane.ZatPoint(vt) + flat->dz;
ptr->y = vt->fY();
ptr->u = vt->fX() / 64.f;
ptr->v = -vt->fY() / 64.f;
ptr++;
}
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_FAN);
}
else
{
// if we cannot access the buffer, use the quad drawer as fallback by splitting the subsector into quads.
// Trying to get this into the vertex buffer in the processing pass is too costly and this is only used for render hacks.
FQuadDrawer qd;
unsigned int vi[4];
vi[0] = 0;
for (unsigned int i = 0; i < sub->numlines - 2; i += 2)
{
for (unsigned int j = 1; j < 4; j++)
{
vi[j] = MIN(i + j, sub->numlines - 1);
}
for (unsigned int x = 0; x < 4; x++)
{
vertex_t *vt = sub->firstline[vi[x]].v1;
qd.Set(x, vt->fX(), flat->plane.plane.ZatPoint(vt) + flat->dz, vt->fY(), vt->fX() / 64.f, -vt->fY() / 64.f);
}
qd.Render(GL_TRIANGLE_FAN);
}
}
flatvertices += sub->numlines;
flatprimitives++;
}
//==========================================================================
//
// this is only used by LM_DEFERRED
//
//==========================================================================
void FDrawInfo::ProcessLights(GLFlat *flat, bool istrans)
{
flat->dynlightindex = GLRenderer->mLights->GetIndexPtr();
// Draw the subsectors belonging to this sector
for (int i=0; i< flat->sector->subsectorcount; i++)
{
subsector_t * sub = flat->sector->subsectors[i];
if (gl_drawinfo->ss_renderflags[sub->Index()]& flat->renderflags || istrans)
{
SetupSubsectorLights(flat, GLPASS_LIGHTSONLY, sub, nullptr);
}
}
// Draw the subsectors assigned to it due to missing textures
if (!(flat->renderflags&SSRF_RENDER3DPLANES))
{
gl_subsectorrendernode * node = (flat->renderflags&SSRF_RENDERFLOOR)?
gl_drawinfo->GetOtherFloorPlanes(flat->sector->sectornum) :
gl_drawinfo->GetOtherCeilingPlanes(flat->sector->sectornum);
while (node)
{
SetupSubsectorLights(flat, GLPASS_LIGHTSONLY, node->sub, nullptr);
node = node->next;
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void FDrawInfo::DrawSubsectors(GLFlat *flat, int pass, bool processlights, bool istrans)
{
int dli = flat->dynlightindex;
gl_RenderState.Apply();
if (flat->vboindex >= 0)
{
int index = flat->vboindex;
for (int i=0; i<flat->sector->subsectorcount; i++)
{
subsector_t * sub = flat->sector->subsectors[i];
if (gl_drawinfo->ss_renderflags[sub->Index()]& flat->renderflags || istrans)
{
if (processlights) SetupSubsectorLights(flat, GLPASS_ALL, sub, &dli);
drawcalls.Clock();
glDrawArrays(GL_TRIANGLE_FAN, index, sub->numlines);
drawcalls.Unclock();
flatvertices += sub->numlines;
flatprimitives++;
}
index += sub->numlines;
}
}
else
{
// Draw the subsectors belonging to this sector
// (can this case even happen?)
for (int i=0; i<flat->sector->subsectorcount; i++)
{
subsector_t * sub = flat->sector->subsectors[i];
if (gl_drawinfo->ss_renderflags[sub->Index()]& flat->renderflags || istrans)
{
if (processlights) SetupSubsectorLights(flat, GLPASS_ALL, sub, &dli);
DrawSubsector(flat, sub);
}
}
}
// Draw the subsectors assigned to it due to missing textures
if (!(flat->renderflags&SSRF_RENDER3DPLANES))
{
gl_subsectorrendernode * node = (flat->renderflags&SSRF_RENDERFLOOR)?
gl_drawinfo->GetOtherFloorPlanes(flat->sector->sectornum) :
gl_drawinfo->GetOtherCeilingPlanes(flat->sector->sectornum);
while (node)
{
if (processlights) SetupSubsectorLights(flat, GLPASS_ALL, node->sub, &dli);
DrawSubsector(flat, node->sub);
node = node->next;
}
}
}
//==========================================================================
//
// special handling for skyboxes which need texture clamping.
// This will find the bounding rectangle of the sector and just
// draw one single polygon filling that rectangle with a clamped
// texture.
//
//==========================================================================
void FDrawInfo::DrawSkyboxSector(GLFlat *flat, int pass, bool processlights)
{
float minx = FLT_MAX, miny = FLT_MAX;
float maxx = -FLT_MAX, maxy = -FLT_MAX;
for (auto ln : flat->sector->Lines)
{
float x = ln->v1->fX();
float y = ln->v1->fY();
if (x < minx) minx = x;
if (y < miny) miny = y;
if (x > maxx) maxx = x;
if (y > maxy) maxy = y;
x = ln->v2->fX();
y = ln->v2->fY();
if (x < minx) minx = x;
if (y < miny) miny = y;
if (x > maxx) maxx = x;
if (y > maxy) maxy = y;
}
float z = flat->plane.plane.ZatPoint(0., 0.) + flat->dz;
static float uvals[] = { 0, 0, 1, 1 };
static float vvals[] = { 1, 0, 0, 1 };
int rot = -xs_FloorToInt(flat->plane.Angle / 90.f);
FQuadDrawer qd;
qd.Set(0, minx, z, miny, uvals[rot & 3], vvals[rot & 3]);
qd.Set(1, minx, z, maxy, uvals[(rot + 1) & 3], vvals[(rot + 1) & 3]);
qd.Set(2, maxx, z, maxy, uvals[(rot + 2) & 3], vvals[(rot + 2) & 3]);
qd.Set(3, maxx, z, miny, uvals[(rot + 3) & 3], vvals[(rot + 3) & 3]);
qd.Render(GL_TRIANGLE_FAN);
flatvertices += 4;
flatprimitives++;
}
//==========================================================================
//
//
//
//==========================================================================
void FDrawInfo::DrawFlat(GLFlat *flat, int pass, bool trans) // trans only has meaning for GLPASS_LIGHTSONLY
{
int rel = getExtraLight();
auto &plane = flat->plane;
gl_RenderState.SetNormal(plane.plane.Normal().X, plane.plane.Normal().Z, plane.plane.Normal().Y);
switch (pass)
{
case GLPASS_PLAIN: // Single-pass rendering
case GLPASS_ALL: // Same, but also creates the dynlight data.
mDrawer->SetColor(flat->lightlevel, rel, flat->Colormap,1.0f);
mDrawer->SetFog(flat->lightlevel, rel, &flat->Colormap, false);
if (!flat->gltexture->tex->isFullbright())
gl_RenderState.SetObjectColor(flat->FlatColor | 0xff000000);
if (flat->sector->special != GLSector_Skybox)
{
gl_RenderState.SetMaterial(flat->gltexture, CLAMP_NONE, 0, -1, false);
gl_RenderState.SetPlaneTextureRotation(&plane, flat->gltexture);
DrawSubsectors(flat, pass, (pass == GLPASS_ALL || flat->dynlightindex > -1), false);
gl_RenderState.EnableTextureMatrix(false);
}
else
{
gl_RenderState.SetMaterial(flat->gltexture, CLAMP_XY, 0, -1, false);
DrawSkyboxSector(flat, pass, (pass == GLPASS_ALL || flat->dynlightindex > -1));
}
gl_RenderState.SetObjectColor(0xffffffff);
break;
case GLPASS_LIGHTSONLY:
if (!trans || flat->gltexture)
{
ProcessLights(flat, trans);
}
break;
case GLPASS_TRANSLUCENT:
if (flat->renderstyle==STYLE_Add) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE);
mDrawer->SetColor(flat->lightlevel, rel, flat->Colormap, flat->alpha);
mDrawer->SetFog(flat->lightlevel, rel, &flat->Colormap, false);
if (!flat->gltexture || !flat->gltexture->tex->isFullbright())
gl_RenderState.SetObjectColor(flat->FlatColor | 0xff000000);
if (!flat->gltexture)
{
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
gl_RenderState.EnableTexture(false);
DrawSubsectors(flat, pass, false, true);
gl_RenderState.EnableTexture(true);
}
else
{
if (!flat->gltexture->tex->GetTranslucency()) gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold);
else gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
gl_RenderState.SetMaterial(flat->gltexture, CLAMP_NONE, 0, -1, false);
gl_RenderState.SetPlaneTextureRotation(&plane, flat->gltexture);
DrawSubsectors(flat, pass, !gl.legacyMode && (gl.lightmethod == LM_DIRECT || flat->dynlightindex > -1), true);
gl_RenderState.EnableTextureMatrix(false);
}
if (flat->renderstyle==STYLE_Add) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl_RenderState.SetObjectColor(0xffffffff);
break;
case GLPASS_LIGHTTEX:
case GLPASS_LIGHTTEX_ADDITIVE:
case GLPASS_LIGHTTEX_FOGGY:
DrawLightsCompat(flat, pass);
break;
case GLPASS_TEXONLY:
gl_RenderState.SetMaterial(flat->gltexture, CLAMP_NONE, 0, -1, false);
gl_RenderState.SetPlaneTextureRotation(&plane, flat->gltexture);
DrawSubsectors(flat, pass, false, false);
gl_RenderState.EnableTextureMatrix(false);
break;
}
}
//==========================================================================
//
// FDrawInfo::AddFlat
//
// Checks texture, lighting and translucency settings and puts this
// plane in the appropriate render list.
//
//==========================================================================
void FDrawInfo::AddFlat(GLFlat *flat, bool fog)
{
int list;
if (gl.legacyMode)
{
if (PutFlatCompat(flat, fog)) return;
}
if (flat->renderstyle != STYLE_Translucent || flat->alpha < 1.f - FLT_EPSILON || fog || flat->gltexture == nullptr)
{
// translucent 3D floors go into the regular translucent list, translucent portals go into the translucent border list.
list = (flat->renderflags&SSRF_RENDER3DPLANES) ? GLDL_TRANSLUCENT : GLDL_TRANSLUCENTBORDER;
}
else if (flat->gltexture->tex->GetTranslucency())
{
if (flat->stack)
{
list = GLDL_TRANSLUCENTBORDER;
}
else if ((flat->renderflags&SSRF_RENDER3DPLANES) && !flat->plane.plane.isSlope())
{
list = GLDL_TRANSLUCENT;
}
else
{
list = GLDL_PLAINFLATS;
}
}
else
{
bool masked = flat->gltexture->isMasked() && ((flat->renderflags&SSRF_RENDER3DPLANES) || flat->stack);
list = masked ? GLDL_MASKEDFLATS : GLDL_PLAINFLATS;
}
auto newflat = gl_drawinfo->drawlists[list].NewFlat();
*newflat = *flat;
}