vkdoom_m/src/gl/scene/gl_drawinfo.cpp
2018-10-27 22:55:33 +02:00

344 lines
8.9 KiB
C++

//
//---------------------------------------------------------------------------
//
// 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/
//
//--------------------------------------------------------------------------
//
/*
** gl_drawinfo.cpp
** Implements the draw info structure which contains most of the
** data in a scene and the draw lists - including a very thorough BSP
** style sorting algorithm for translucent objects.
**
*/
#include "gl_load/gl_system.h"
#include "r_sky.h"
#include "r_utility.h"
#include "doomstat.h"
#include "g_levellocals.h"
#include "tarray.h"
#include "hwrenderer/scene/hw_drawstructs.h"
#include "hwrenderer/data/flatvertices.h"
#include "hwrenderer/utility/hw_clock.h"
#include "gl/scene/gl_drawinfo.h"
#include "hwrenderer/scene/hw_clipper.h"
#include "gl/renderer/gl_renderstate.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/data/gl_viewpointbuffer.h"
#include "gl/dynlights/gl_lightbuffer.h"
#include "gl/models/gl_models.h"
class FDrawInfoList
{
public:
TDeletingArray<FDrawInfo *> mList;
FDrawInfo * GetNew();
void Release(FDrawInfo *);
};
static FDrawInfo * gl_drawinfo;
FDrawInfoList di_list;
//==========================================================================
//
//
//
//==========================================================================
void FDrawInfo::DrawSorted(int listindex)
{
HWDrawList *dl = &drawlists[listindex];
if (dl->drawitems.Size()==0) return;
if (!dl->sorted)
{
GLRenderer->mVBO->Map();
dl->Sort(this);
GLRenderer->mVBO->Unmap();
}
gl_RenderState.ClearClipSplit();
if (!(gl.flags & RFL_NO_CLIP_PLANES))
{
glEnable(GL_CLIP_DISTANCE1);
glEnable(GL_CLIP_DISTANCE2);
}
dl->DrawSorted(this, gl_RenderState, dl->sorted);
if (!(gl.flags & RFL_NO_CLIP_PLANES))
{
glDisable(GL_CLIP_DISTANCE1);
glDisable(GL_CLIP_DISTANCE2);
}
gl_RenderState.ClearClipSplit();
}
//==========================================================================
//
// 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.
//
//==========================================================================
FDrawInfo *FDrawInfoList::GetNew()
{
if (mList.Size() > 0)
{
FDrawInfo *di;
mList.Pop(di);
return di;
}
return new FDrawInfo;
}
void FDrawInfoList::Release(FDrawInfo * di)
{
di->ClearBuffers();
mList.Push(di);
}
//==========================================================================
//
// Sets up a new drawinfo struct
//
//==========================================================================
// OpenGL has no use for multiple clippers so use the same one for all DrawInfos.
static Clipper staticClipper;
FDrawInfo *FDrawInfo::StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms)
{
FDrawInfo *di=di_list.GetNew();
di->mVBO = GLRenderer->mVBO;
di->mClipper = &staticClipper;
di->Viewpoint = parentvp;
if (uniforms)
{
di->VPUniforms = *uniforms;
// The clip planes will never be inherited from the parent drawinfo.
di->VPUniforms.mClipLine.X = -1000001.f;
di->VPUniforms.mClipHeight = 0;
}
else di->VPUniforms.SetDefaults();
di->mClipper->SetViewpoint(di->Viewpoint);
staticClipper.Clear();
di->StartScene();
return di;
}
void FDrawInfo::StartScene()
{
ClearBuffers();
outer = gl_drawinfo;
gl_drawinfo = this;
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;
}
//==========================================================================
//
//
//
//==========================================================================
FDrawInfo *FDrawInfo::EndDrawInfo()
{
assert(this == gl_drawinfo);
for(int i=0;i<GLDL_TYPES;i++) drawlists[i].Reset();
gl_drawinfo=static_cast<FDrawInfo*>(outer);
di_list.Release(this);
if (gl_drawinfo == nullptr)
ResetRenderDataAllocator();
return gl_drawinfo;
}
// Same here for the dependency on the portal.
void FDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *ptg, subsector_t *sub)
{
auto portal = FindPortal(ptg);
if (!portal)
{
portal = new HWScenePortal(screen->mPortalState, new HWSectorStackPortal(ptg));
Portals.Push(portal);
}
auto ptl = static_cast<HWSectorStackPortal*>(static_cast<HWScenePortal*>(portal)->mScene);
ptl->AddSubsector(sub);
}
std::pair<FFlatVertex *, unsigned int> FDrawInfo::AllocVertices(unsigned int count)
{
unsigned int index = -1;
auto p = GLRenderer->mVBO->Alloc(count, &index);
return std::make_pair(p, index);
}
int FDrawInfo::UploadLights(FDynLightData &data)
{
return GLRenderer->mLights->UploadLights(data);
}
bool FDrawInfo::SetDepthClamp(bool on)
{
return gl_RenderState.SetDepthClamp(on);
}
//==========================================================================
//
//
//
//==========================================================================
static int dt2gl[] = { GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP };
void FDrawInfo::Draw(EDrawType dt, FRenderState &state, int index, int count, bool apply)
{
assert(&state == &gl_RenderState);
if (apply)
{
gl_RenderState.Apply();
}
drawcalls.Clock();
glDrawArrays(dt2gl[dt], index, count);
drawcalls.Unclock();
}
void FDrawInfo::DrawIndexed(EDrawType dt, FRenderState &state, int index, int count, bool apply)
{
assert(&state == &gl_RenderState);
if (apply)
{
gl_RenderState.Apply();
}
drawcalls.Clock();
glDrawElements(dt2gl[dt], count, GL_UNSIGNED_INT, (void*)(intptr_t)(index * sizeof(uint32_t)));
drawcalls.Unclock();
}
void FDrawInfo::DrawModel(GLSprite *spr, FRenderState &state)
{
FGLModelRenderer renderer(this, state, spr->dynlightindex);
renderer.RenderModel(spr->x, spr->y, spr->z, spr->modelframe, spr->actor, Viewpoint.TicFrac);
}
void FDrawInfo::DrawHUDModel(HUDSprite *huds, FRenderState &state)
{
FGLModelRenderer renderer(this, state, huds->lightindex);
renderer.RenderHUDModel(huds->weapon, huds->mx, huds->my);
}
void FDrawInfo::RenderPortal(HWPortal *p, bool usestencil)
{
auto gp = static_cast<HWPortal *>(p);
gp->SetupStencil(this, gl_RenderState, usestencil);
auto new_di = StartDrawInfo(Viewpoint, &VPUniforms);
new_di->mCurrentPortal = gp;
gl_RenderState.SetLightIndex(-1);
gp->DrawContents(new_di, gl_RenderState);
new_di->EndDrawInfo();
GLRenderer->mVBO->Bind(gl_RenderState);
GLRenderer->mViewpoints->Bind(vpIndex);
gp->RemoveStencil(this, gl_RenderState, usestencil);
}
void FDrawInfo::SetDepthMask(bool on)
{
glDepthMask(on);
}
void FDrawInfo::SetDepthFunc(int func)
{
static int df2gl[] = { GL_LESS, GL_LEQUAL, GL_ALWAYS };
glDepthFunc(df2gl[func]);
}
void FDrawInfo::SetDepthRange(float min, float max)
{
glDepthRange(min, max);
}
void FDrawInfo::EnableDrawBufferAttachments(bool on)
{
gl_RenderState.EnableDrawBuffers(on? gl_RenderState.GetPassDrawBufferCount() : 1);
}
void FDrawInfo::SetStencil(int offs, int op, int flags)
{
static int op2gl[] = { GL_KEEP, GL_INCR, GL_DECR };
glStencilFunc(GL_EQUAL, screen->stencilValue + offs, ~0); // draw sky into stencil
glStencilOp(GL_KEEP, GL_KEEP, op2gl[op]); // this stage doesn't modify the stencil
bool cmon = !(flags & SF_ColorMaskOff);
glColorMask(cmon, cmon, cmon, cmon); // don't write to the graphics buffer
glDepthMask(!(flags & SF_DepthMaskOff));
if (flags & SF_DepthTestOff)
glDisable(GL_DEPTH_TEST);
else
glEnable(GL_DEPTH_TEST);
if (flags & SF_DepthClear)
glClear(GL_DEPTH_BUFFER_BIT);
}
void FDrawInfo::SetCulling(int mode)
{
if (mode != Cull_None)
{
glEnable(GL_CULL_FACE);
glFrontFace(mode == Cull_CCW ? GL_CCW : GL_CW);
}
else
{
glDisable(GL_CULL_FACE);
}
}
//==========================================================================
//
//
//
//==========================================================================
void FDrawInfo::ClearScreen()
{
bool multi = !!glIsEnabled(GL_MULTISAMPLE);
GLRenderer->mViewpoints->Set2D(SCREENWIDTH, SCREENHEIGHT);
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);
}