2016-09-14 20:01:13 +02:00
|
|
|
//
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Copyright(C) 2005-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/
|
|
|
|
//
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
//
|
2013-06-23 09:49:34 +02:00
|
|
|
/*
|
|
|
|
** gl1_renderer.cpp
|
|
|
|
** Renderer interface
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "gl/system/gl_system.h"
|
|
|
|
#include "files.h"
|
|
|
|
#include "v_video.h"
|
|
|
|
#include "m_png.h"
|
|
|
|
#include "w_wad.h"
|
2016-07-26 21:27:02 +02:00
|
|
|
#include "doomstat.h"
|
2018-04-04 00:21:25 +02:00
|
|
|
#include "i_time.h"
|
|
|
|
#include "p_effect.h"
|
|
|
|
#include "d_player.h"
|
|
|
|
#include "a_dynlight.h"
|
2013-06-23 09:49:34 +02:00
|
|
|
|
2013-09-03 18:29:39 +02:00
|
|
|
#include "gl/system/gl_interface.h"
|
2013-06-23 09:49:34 +02:00
|
|
|
#include "gl/system/gl_framebuffer.h"
|
2018-04-25 20:33:55 +02:00
|
|
|
#include "hwrenderer/utility/hw_cvars.h"
|
2016-08-17 23:18:47 +02:00
|
|
|
#include "gl/system/gl_debug.h"
|
2013-06-23 09:49:34 +02:00
|
|
|
#include "gl/renderer/gl_renderer.h"
|
|
|
|
#include "gl/renderer/gl_lightdata.h"
|
|
|
|
#include "gl/renderer/gl_renderstate.h"
|
2016-07-26 21:27:02 +02:00
|
|
|
#include "gl/renderer/gl_renderbuffers.h"
|
2013-06-23 09:49:34 +02:00
|
|
|
#include "gl/data/gl_vertexbuffer.h"
|
|
|
|
#include "gl/scene/gl_drawinfo.h"
|
2018-04-04 00:21:25 +02:00
|
|
|
#include "gl/scene/gl_scenedrawer.h"
|
2018-04-05 23:08:09 +02:00
|
|
|
#include "gl/scene/gl_swscene.h"
|
2016-08-29 13:10:22 +02:00
|
|
|
#include "gl/shaders/gl_ambientshader.h"
|
2016-07-27 21:50:30 +02:00
|
|
|
#include "gl/shaders/gl_bloomshader.h"
|
|
|
|
#include "gl/shaders/gl_blurshader.h"
|
2016-07-29 00:36:43 +02:00
|
|
|
#include "gl/shaders/gl_tonemapshader.h"
|
2016-09-01 17:14:51 +02:00
|
|
|
#include "gl/shaders/gl_colormapshader.h"
|
2016-08-02 17:32:21 +02:00
|
|
|
#include "gl/shaders/gl_lensshader.h"
|
2016-09-25 12:25:01 +03:00
|
|
|
#include "gl/shaders/gl_fxaashader.h"
|
2016-07-26 21:27:02 +02:00
|
|
|
#include "gl/shaders/gl_presentshader.h"
|
2016-10-02 12:24:37 -04:00
|
|
|
#include "gl/shaders/gl_present3dRowshader.h"
|
2017-03-01 03:33:53 +01:00
|
|
|
#include "gl/shaders/gl_shadowmapshader.h"
|
2018-04-02 12:28:20 +02:00
|
|
|
#include "gl/shaders/gl_postprocessshaderinstance.h"
|
2016-09-11 13:42:41 -04:00
|
|
|
#include "gl/stereo3d/gl_stereo3d.h"
|
2014-08-22 23:50:38 +02:00
|
|
|
#include "gl/textures/gl_samplers.h"
|
2014-08-01 20:59:39 +02:00
|
|
|
#include "gl/dynlights/gl_lightbuffer.h"
|
2017-07-27 03:08:42 -04:00
|
|
|
#include "r_videoscale.h"
|
2013-06-23 09:49:34 +02:00
|
|
|
|
2016-07-26 21:27:02 +02:00
|
|
|
EXTERN_CVAR(Int, screenblocks)
|
2018-04-04 00:21:25 +02:00
|
|
|
EXTERN_CVAR(Bool, cl_capfps)
|
2018-04-05 23:08:09 +02:00
|
|
|
EXTERN_CVAR(Float, underwater_fade_scalar)
|
2016-07-26 21:27:02 +02:00
|
|
|
|
2016-12-28 16:08:32 +02:00
|
|
|
CVAR(Bool, gl_scale_viewport, true, CVAR_ARCHIVE);
|
2018-04-04 00:21:25 +02:00
|
|
|
extern bool NoInterpolateView;
|
2016-08-12 07:28:29 +02:00
|
|
|
|
2013-06-23 09:49:34 +02:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Renderer interface
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Initialize
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2013-08-18 15:41:52 +02:00
|
|
|
FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb)
|
|
|
|
{
|
|
|
|
framebuffer = fb;
|
2016-09-01 17:14:51 +02:00
|
|
|
mClipPortal = nullptr;
|
|
|
|
mCurrentPortal = nullptr;
|
2013-08-18 15:41:52 +02:00
|
|
|
mMirrorCount = 0;
|
|
|
|
mPlaneMirrorCount = 0;
|
|
|
|
mLightCount = 0;
|
2016-03-21 02:57:02 +01:00
|
|
|
mAngles = FRotator(0.f, 0.f, 0.f);
|
2013-08-18 15:41:52 +02:00
|
|
|
mViewVector = FVector2(0,0);
|
2016-09-01 17:14:51 +02:00
|
|
|
mVBO = nullptr;
|
|
|
|
mSkyVBO = nullptr;
|
|
|
|
mShaderManager = nullptr;
|
|
|
|
mLights = nullptr;
|
2016-08-25 19:58:50 +02:00
|
|
|
mTonemapPalette = nullptr;
|
2016-09-01 17:14:51 +02:00
|
|
|
mBuffers = nullptr;
|
|
|
|
mPresentShader = nullptr;
|
2016-10-09 13:05:50 -04:00
|
|
|
mPresent3dCheckerShader = nullptr;
|
|
|
|
mPresent3dColumnShader = nullptr;
|
2016-10-02 12:24:37 -04:00
|
|
|
mPresent3dRowShader = nullptr;
|
2016-09-01 17:14:51 +02:00
|
|
|
mBloomExtractShader = nullptr;
|
|
|
|
mBloomCombineShader = nullptr;
|
2016-09-18 15:57:22 +02:00
|
|
|
mExposureExtractShader = nullptr;
|
|
|
|
mExposureAverageShader = nullptr;
|
|
|
|
mExposureCombineShader = nullptr;
|
2016-09-01 17:14:51 +02:00
|
|
|
mBlurShader = nullptr;
|
|
|
|
mTonemapShader = nullptr;
|
|
|
|
mTonemapPalette = nullptr;
|
|
|
|
mColormapShader = nullptr;
|
|
|
|
mLensShader = nullptr;
|
2016-09-02 05:45:00 +02:00
|
|
|
mLinearDepthShader = nullptr;
|
|
|
|
mDepthBlurShader = nullptr;
|
|
|
|
mSSAOShader = nullptr;
|
|
|
|
mSSAOCombineShader = nullptr;
|
2016-09-25 12:25:01 +03:00
|
|
|
mFXAAShader = nullptr;
|
|
|
|
mFXAALumaShader = nullptr;
|
2017-03-01 03:33:53 +01:00
|
|
|
mShadowMapShader = nullptr;
|
2017-07-06 05:36:01 +02:00
|
|
|
mCustomPostProcessShaders = nullptr;
|
2013-08-18 15:41:52 +02:00
|
|
|
}
|
|
|
|
|
2014-10-24 11:43:25 +02:00
|
|
|
void gl_LoadModels();
|
2015-04-12 19:42:03 +02:00
|
|
|
void gl_FlushModels();
|
2014-10-24 11:43:25 +02:00
|
|
|
|
2016-08-08 14:24:48 +02:00
|
|
|
void FGLRenderer::Initialize(int width, int height)
|
2013-06-23 09:49:34 +02:00
|
|
|
{
|
2016-07-26 21:27:02 +02:00
|
|
|
mBuffers = new FGLRenderBuffers();
|
2016-08-29 13:10:22 +02:00
|
|
|
mLinearDepthShader = new FLinearDepthShader();
|
2016-09-02 05:45:00 +02:00
|
|
|
mDepthBlurShader = new FDepthBlurShader();
|
2016-08-29 13:10:22 +02:00
|
|
|
mSSAOShader = new FSSAOShader();
|
2016-09-02 05:45:00 +02:00
|
|
|
mSSAOCombineShader = new FSSAOCombineShader();
|
2016-07-27 21:50:30 +02:00
|
|
|
mBloomExtractShader = new FBloomExtractShader();
|
|
|
|
mBloomCombineShader = new FBloomCombineShader();
|
2016-09-18 15:57:22 +02:00
|
|
|
mExposureExtractShader = new FExposureExtractShader();
|
|
|
|
mExposureAverageShader = new FExposureAverageShader();
|
|
|
|
mExposureCombineShader = new FExposureCombineShader();
|
2016-07-27 21:50:30 +02:00
|
|
|
mBlurShader = new FBlurShader();
|
2016-07-29 00:36:43 +02:00
|
|
|
mTonemapShader = new FTonemapShader();
|
2016-09-01 17:14:51 +02:00
|
|
|
mColormapShader = new FColormapShader();
|
2016-08-23 09:18:18 +02:00
|
|
|
mTonemapPalette = nullptr;
|
2016-08-02 17:32:21 +02:00
|
|
|
mLensShader = new FLensShader();
|
2016-09-25 12:25:01 +03:00
|
|
|
mFXAAShader = new FFXAAShader;
|
|
|
|
mFXAALumaShader = new FFXAALumaShader;
|
2016-07-26 21:27:02 +02:00
|
|
|
mPresentShader = new FPresentShader();
|
2016-10-09 13:05:50 -04:00
|
|
|
mPresent3dCheckerShader = new FPresent3DCheckerShader();
|
|
|
|
mPresent3dColumnShader = new FPresent3DColumnShader();
|
2016-10-02 12:24:37 -04:00
|
|
|
mPresent3dRowShader = new FPresent3DRowShader();
|
2017-03-01 03:33:53 +01:00
|
|
|
mShadowMapShader = new FShadowMapShader();
|
2017-07-06 05:36:01 +02:00
|
|
|
mCustomPostProcessShaders = new FCustomPostProcessShaders();
|
2016-07-26 21:27:02 +02:00
|
|
|
|
2018-04-08 12:11:51 +02:00
|
|
|
if (gl.legacyMode)
|
|
|
|
{
|
|
|
|
legacyShaders = new LegacyShaderContainer;
|
|
|
|
}
|
|
|
|
|
2016-09-01 11:52:52 +02:00
|
|
|
// needed for the core profile, because someone decided it was a good idea to remove the default VAO.
|
|
|
|
if (!gl.legacyMode)
|
2016-04-26 16:26:34 +02:00
|
|
|
{
|
|
|
|
glGenVertexArrays(1, &mVAOID);
|
|
|
|
glBindVertexArray(mVAOID);
|
2016-08-17 23:18:47 +02:00
|
|
|
FGLDebug::LabelObject(GL_VERTEX_ARRAY, mVAOID, "FGLRenderer.mVAOID");
|
2016-04-26 16:26:34 +02:00
|
|
|
}
|
|
|
|
else mVAOID = 0;
|
|
|
|
|
2016-08-08 14:24:48 +02:00
|
|
|
mVBO = new FFlatVertexBuffer(width, height);
|
2014-06-14 01:24:28 +02:00
|
|
|
mSkyVBO = new FSkyVertexBuffer;
|
2016-09-01 11:52:52 +02:00
|
|
|
if (!gl.legacyMode) mLights = new FLightBuffer();
|
2016-04-26 13:50:05 +02:00
|
|
|
else mLights = NULL;
|
2014-05-21 00:36:04 +02:00
|
|
|
gl_RenderState.SetVertexBuffer(mVBO);
|
2013-06-23 09:49:34 +02:00
|
|
|
mFBID = 0;
|
2016-04-30 16:29:22 +03:00
|
|
|
mOldFBID = 0;
|
2016-04-26 16:26:34 +02:00
|
|
|
|
2013-06-23 09:49:34 +02:00
|
|
|
SetupLevel();
|
|
|
|
mShaderManager = new FShaderManager;
|
2014-08-22 23:50:38 +02:00
|
|
|
mSamplerManager = new FSamplerManager;
|
2014-10-24 11:43:25 +02:00
|
|
|
gl_LoadModels();
|
2017-05-30 13:01:51 +03:00
|
|
|
|
|
|
|
GLPortal::Initialize();
|
2013-06-23 09:49:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
FGLRenderer::~FGLRenderer()
|
|
|
|
{
|
2017-05-30 13:01:51 +03:00
|
|
|
GLPortal::Shutdown();
|
|
|
|
|
2015-04-12 19:42:03 +02:00
|
|
|
gl_FlushModels();
|
2017-03-12 16:56:00 +01:00
|
|
|
AActor::DeleteAllAttachedLights();
|
2013-06-23 09:49:34 +02:00
|
|
|
FMaterial::FlushAll();
|
2018-04-08 12:11:51 +02:00
|
|
|
if (legacyShaders) delete legacyShaders;
|
2013-06-23 09:49:34 +02:00
|
|
|
if (mShaderManager != NULL) delete mShaderManager;
|
2014-08-22 23:50:38 +02:00
|
|
|
if (mSamplerManager != NULL) delete mSamplerManager;
|
2013-06-23 09:49:34 +02:00
|
|
|
if (mVBO != NULL) delete mVBO;
|
2014-06-14 01:24:28 +02:00
|
|
|
if (mSkyVBO != NULL) delete mSkyVBO;
|
2014-08-01 20:59:39 +02:00
|
|
|
if (mLights != NULL) delete mLights;
|
2013-09-03 18:29:39 +02:00
|
|
|
if (mFBID != 0) glDeleteFramebuffers(1, &mFBID);
|
2016-04-26 16:26:34 +02:00
|
|
|
if (mVAOID != 0)
|
|
|
|
{
|
|
|
|
glBindVertexArray(0);
|
|
|
|
glDeleteVertexArrays(1, &mVAOID);
|
|
|
|
}
|
2018-04-05 23:08:09 +02:00
|
|
|
if (swdrawer) delete swdrawer;
|
2016-07-26 21:27:02 +02:00
|
|
|
if (mBuffers) delete mBuffers;
|
|
|
|
if (mPresentShader) delete mPresentShader;
|
2016-08-29 13:10:22 +02:00
|
|
|
if (mLinearDepthShader) delete mLinearDepthShader;
|
2016-09-02 05:45:00 +02:00
|
|
|
if (mDepthBlurShader) delete mDepthBlurShader;
|
2016-08-29 13:10:22 +02:00
|
|
|
if (mSSAOShader) delete mSSAOShader;
|
2016-09-02 05:45:00 +02:00
|
|
|
if (mSSAOCombineShader) delete mSSAOCombineShader;
|
2016-10-09 13:05:50 -04:00
|
|
|
if (mPresent3dCheckerShader) delete mPresent3dCheckerShader;
|
|
|
|
if (mPresent3dColumnShader) delete mPresent3dColumnShader;
|
2016-10-02 12:24:37 -04:00
|
|
|
if (mPresent3dRowShader) delete mPresent3dRowShader;
|
2016-07-27 21:50:30 +02:00
|
|
|
if (mBloomExtractShader) delete mBloomExtractShader;
|
|
|
|
if (mBloomCombineShader) delete mBloomCombineShader;
|
2016-09-18 15:57:22 +02:00
|
|
|
if (mExposureExtractShader) delete mExposureExtractShader;
|
|
|
|
if (mExposureAverageShader) delete mExposureAverageShader;
|
|
|
|
if (mExposureCombineShader) delete mExposureCombineShader;
|
2016-07-27 21:50:30 +02:00
|
|
|
if (mBlurShader) delete mBlurShader;
|
2016-07-29 00:36:43 +02:00
|
|
|
if (mTonemapShader) delete mTonemapShader;
|
2016-08-23 09:18:18 +02:00
|
|
|
if (mTonemapPalette) delete mTonemapPalette;
|
2016-09-01 17:14:51 +02:00
|
|
|
if (mColormapShader) delete mColormapShader;
|
2016-08-02 17:32:21 +02:00
|
|
|
if (mLensShader) delete mLensShader;
|
2017-03-01 03:33:53 +01:00
|
|
|
if (mShadowMapShader) delete mShadowMapShader;
|
2017-07-06 05:36:01 +02:00
|
|
|
delete mCustomPostProcessShaders;
|
2016-09-25 12:25:01 +03:00
|
|
|
delete mFXAAShader;
|
|
|
|
delete mFXAALumaShader;
|
2016-07-26 21:27:02 +02:00
|
|
|
}
|
2016-04-26 16:26:34 +02:00
|
|
|
|
2016-07-26 21:27:02 +02:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// Calculates the viewport values needed for 2D and 3D operations
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FGLRenderer::SetOutputViewport(GL_IRECT *bounds)
|
|
|
|
{
|
|
|
|
if (bounds)
|
|
|
|
{
|
2016-08-12 07:28:29 +02:00
|
|
|
mSceneViewport = *bounds;
|
2016-07-31 13:23:49 +02:00
|
|
|
mScreenViewport = *bounds;
|
2016-08-12 07:28:29 +02:00
|
|
|
mOutputLetterbox = *bounds;
|
2016-07-26 21:27:02 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Special handling so the view with a visible status bar displays properly
|
2016-08-12 07:28:29 +02:00
|
|
|
int height, width;
|
2016-07-26 21:27:02 +02:00
|
|
|
if (screenblocks >= 10)
|
|
|
|
{
|
|
|
|
height = framebuffer->GetHeight();
|
|
|
|
width = framebuffer->GetWidth();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
height = (screenblocks*framebuffer->GetHeight() / 10) & ~7;
|
|
|
|
width = (screenblocks*framebuffer->GetWidth() / 10);
|
|
|
|
}
|
|
|
|
|
2016-08-12 07:28:29 +02:00
|
|
|
// Back buffer letterbox for the final output
|
|
|
|
int clientWidth = framebuffer->GetClientWidth();
|
|
|
|
int clientHeight = framebuffer->GetClientHeight();
|
2016-09-04 03:15:50 +02:00
|
|
|
if (clientWidth == 0 || clientHeight == 0)
|
|
|
|
{
|
|
|
|
// When window is minimized there may not be any client area.
|
|
|
|
// Pretend to the rest of the render code that we just have a very small window.
|
|
|
|
clientWidth = 160;
|
|
|
|
clientHeight = 120;
|
|
|
|
}
|
2016-08-12 07:28:29 +02:00
|
|
|
int screenWidth = framebuffer->GetWidth();
|
|
|
|
int screenHeight = framebuffer->GetHeight();
|
2017-07-23 17:18:25 +02:00
|
|
|
float scaleX, scaleY;
|
2017-07-27 03:05:01 -04:00
|
|
|
if (ViewportIsScaled43())
|
2017-07-23 17:18:25 +02:00
|
|
|
{
|
|
|
|
scaleX = MIN(clientWidth / (float)screenWidth, clientHeight / (screenHeight * 1.2f));
|
|
|
|
scaleY = scaleX * 1.2f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
scaleX = MIN(clientWidth / (float)screenWidth, clientHeight / (float)screenHeight);
|
|
|
|
scaleY = scaleX;
|
|
|
|
}
|
|
|
|
mOutputLetterbox.width = (int)round(screenWidth * scaleX);
|
|
|
|
mOutputLetterbox.height = (int)round(screenHeight * scaleY);
|
2016-08-12 07:28:29 +02:00
|
|
|
mOutputLetterbox.left = (clientWidth - mOutputLetterbox.width) / 2;
|
|
|
|
mOutputLetterbox.top = (clientHeight - mOutputLetterbox.height) / 2;
|
2016-07-26 21:27:02 +02:00
|
|
|
|
2016-07-31 13:23:49 +02:00
|
|
|
// The entire renderable area, including the 2D HUD
|
|
|
|
mScreenViewport.left = 0;
|
|
|
|
mScreenViewport.top = 0;
|
2016-08-12 07:28:29 +02:00
|
|
|
mScreenViewport.width = screenWidth;
|
|
|
|
mScreenViewport.height = screenHeight;
|
|
|
|
|
|
|
|
// Viewport for the 3D scene
|
|
|
|
mSceneViewport.left = viewwindowx;
|
|
|
|
mSceneViewport.top = screenHeight - (height + viewwindowy - ((height - viewheight) / 2));
|
|
|
|
mSceneViewport.width = viewwidth;
|
|
|
|
mSceneViewport.height = height;
|
|
|
|
|
|
|
|
// Scale viewports to fit letterbox
|
2017-12-10 04:22:13 -05:00
|
|
|
bool notScaled = ((mScreenViewport.width == ViewportScaledWidth(mScreenViewport.width, mScreenViewport.height)) &&
|
|
|
|
(mScreenViewport.width == ViewportScaledHeight(mScreenViewport.width, mScreenViewport.height)) &&
|
|
|
|
!ViewportIsScaled43());
|
|
|
|
if ((gl_scale_viewport && !framebuffer->IsFullscreen() && notScaled) || !FGLRenderBuffers::IsEnabled())
|
2016-08-12 07:28:29 +02:00
|
|
|
{
|
|
|
|
mScreenViewport.width = mOutputLetterbox.width;
|
|
|
|
mScreenViewport.height = mOutputLetterbox.height;
|
2017-07-23 17:18:25 +02:00
|
|
|
mSceneViewport.left = (int)round(mSceneViewport.left * scaleX);
|
|
|
|
mSceneViewport.top = (int)round(mSceneViewport.top * scaleY);
|
|
|
|
mSceneViewport.width = (int)round(mSceneViewport.width * scaleX);
|
|
|
|
mSceneViewport.height = (int)round(mSceneViewport.height * scaleY);
|
2016-08-12 07:28:29 +02:00
|
|
|
|
|
|
|
// Without render buffers we have to render directly to the letterbox
|
|
|
|
if (!FGLRenderBuffers::IsEnabled())
|
|
|
|
{
|
|
|
|
mScreenViewport.left += mOutputLetterbox.left;
|
|
|
|
mScreenViewport.top += mOutputLetterbox.top;
|
|
|
|
mSceneViewport.left += mOutputLetterbox.left;
|
|
|
|
mSceneViewport.top += mOutputLetterbox.top;
|
|
|
|
}
|
|
|
|
}
|
2016-09-11 13:42:41 -04:00
|
|
|
|
|
|
|
s3d::Stereo3DMode::getCurrentMode().AdjustViewports();
|
2016-08-12 07:28:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Calculates the OpenGL window coordinates for a zdoom screen position
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
int FGLRenderer::ScreenToWindowX(int x)
|
|
|
|
{
|
|
|
|
return mScreenViewport.left + (int)round(x * mScreenViewport.width / (float)framebuffer->GetWidth());
|
|
|
|
}
|
|
|
|
|
|
|
|
int FGLRenderer::ScreenToWindowY(int y)
|
|
|
|
{
|
|
|
|
return mScreenViewport.top + mScreenViewport.height - (int)round(y * mScreenViewport.height / (float)framebuffer->GetHeight());
|
2013-06-23 09:49:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2018-04-08 08:03:46 +02:00
|
|
|
void FGLRenderer::ResetSWScene()
|
|
|
|
{
|
|
|
|
// force recreation of the SW scene drawer to ensure it gets a new set of resources.
|
|
|
|
if (swdrawer != nullptr) delete swdrawer;
|
|
|
|
swdrawer = nullptr;
|
|
|
|
}
|
|
|
|
|
2013-06-23 09:49:34 +02:00
|
|
|
void FGLRenderer::SetupLevel()
|
|
|
|
{
|
|
|
|
mVBO->CreateVBO();
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void FGLRenderer::FlushTextures()
|
|
|
|
{
|
|
|
|
FMaterial::FlushAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
bool FGLRenderer::StartOffscreen()
|
|
|
|
{
|
2016-08-17 23:18:47 +02:00
|
|
|
bool firstBind = (mFBID == 0);
|
|
|
|
if (mFBID == 0)
|
|
|
|
glGenFramebuffers(1, &mFBID);
|
2016-04-30 17:09:57 +02:00
|
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mOldFBID);
|
2014-06-21 15:50:32 +02:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, mFBID);
|
2016-08-17 23:18:47 +02:00
|
|
|
if (firstBind)
|
|
|
|
FGLDebug::LabelObject(GL_FRAMEBUFFER, mFBID, "OffscreenFB");
|
2014-06-21 15:50:32 +02:00
|
|
|
return true;
|
2013-06-23 09:49:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void FGLRenderer::EndOffscreen()
|
|
|
|
{
|
2016-04-30 16:29:22 +03:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, mOldFBID);
|
2013-06-23 09:49:34 +02:00
|
|
|
}
|
|
|
|
|
2018-04-04 00:21:25 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// renders the view
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void FGLRenderer::RenderView(player_t* player)
|
|
|
|
{
|
|
|
|
gl_RenderState.SetVertexBuffer(mVBO);
|
|
|
|
mVBO->Reset();
|
|
|
|
|
2018-04-07 23:30:28 +02:00
|
|
|
if (!V_IsHardwareRenderer())
|
2018-04-05 23:08:09 +02:00
|
|
|
{
|
|
|
|
if (swdrawer == nullptr) swdrawer = new SWSceneDrawer;
|
|
|
|
swdrawer->RenderView(player);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
checkBenchActive();
|
2018-04-04 00:21:25 +02:00
|
|
|
|
2018-04-05 23:08:09 +02:00
|
|
|
// reset statistics counters
|
|
|
|
ResetProfilingData();
|
2018-04-04 00:21:25 +02:00
|
|
|
|
2018-04-05 23:08:09 +02:00
|
|
|
// Get this before everything else
|
|
|
|
if (cl_capfps || r_NoInterpolate) r_viewpoint.TicFrac = 1.;
|
|
|
|
else r_viewpoint.TicFrac = I_GetTimeFrac();
|
2018-04-04 00:21:25 +02:00
|
|
|
|
2018-04-05 23:08:09 +02:00
|
|
|
P_FindParticleSubsectors();
|
2018-04-04 00:21:25 +02:00
|
|
|
|
2018-04-05 23:08:09 +02:00
|
|
|
if (!gl.legacyMode) mLights->Clear();
|
2018-04-04 00:21:25 +02:00
|
|
|
|
2018-04-05 23:08:09 +02:00
|
|
|
// NoInterpolateView should have no bearing on camera textures, but needs to be preserved for the main view below.
|
|
|
|
bool saved_niv = NoInterpolateView;
|
|
|
|
NoInterpolateView = false;
|
|
|
|
// prepare all camera textures that have been used in the last frame
|
|
|
|
FCanvasTextureInfo::UpdateAll();
|
|
|
|
NoInterpolateView = saved_niv;
|
2018-04-04 00:21:25 +02:00
|
|
|
|
|
|
|
|
2018-04-05 23:08:09 +02:00
|
|
|
// now render the main view
|
|
|
|
float fovratio;
|
|
|
|
float ratio = r_viewwindow.WidescreenRatio;
|
|
|
|
if (r_viewwindow.WidescreenRatio >= 1.3f)
|
|
|
|
{
|
|
|
|
fovratio = 1.333333f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fovratio = ratio;
|
|
|
|
}
|
|
|
|
// Check if there's some lights. If not some code can be skipped.
|
|
|
|
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
|
|
|
|
mLightCount = ((it.Next()) != NULL);
|
2018-04-04 00:21:25 +02:00
|
|
|
|
2018-04-05 23:08:09 +02:00
|
|
|
GLSceneDrawer drawer;
|
2018-04-04 00:21:25 +02:00
|
|
|
|
2018-04-05 23:08:09 +02:00
|
|
|
drawer.SetFixedColormap(player);
|
|
|
|
|
|
|
|
mShadowMap.Update();
|
|
|
|
sector_t * viewsector = drawer.RenderViewpoint(player->camera, NULL, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true);
|
|
|
|
}
|
2018-04-04 00:21:25 +02:00
|
|
|
|
|
|
|
All.Unclock();
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Camera texture rendering
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void FGLRenderer::RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, double FOV)
|
|
|
|
{
|
|
|
|
FMaterial * gltex = FMaterial::ValidateTexture(tex, false);
|
|
|
|
|
|
|
|
int width = gltex->TextureWidth();
|
|
|
|
int height = gltex->TextureHeight();
|
|
|
|
|
|
|
|
if (gl.legacyMode)
|
|
|
|
{
|
|
|
|
// In legacy mode, fail if the requested texture is too large.
|
|
|
|
if (gltex->GetWidth() > screen->GetWidth() || gltex->GetHeight() > screen->GetHeight()) return;
|
|
|
|
glFlush();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
StartOffscreen();
|
|
|
|
gltex->BindToFrameBuffer();
|
|
|
|
}
|
|
|
|
|
|
|
|
GL_IRECT bounds;
|
|
|
|
bounds.left = bounds.top = 0;
|
|
|
|
bounds.width = FHardwareTexture::GetTexDimension(gltex->GetWidth());
|
|
|
|
bounds.height = FHardwareTexture::GetTexDimension(gltex->GetHeight());
|
|
|
|
|
|
|
|
GLSceneDrawer drawer;
|
|
|
|
drawer.FixedColormap = CM_DEFAULT;
|
|
|
|
gl_RenderState.SetFixedColormap(CM_DEFAULT);
|
|
|
|
drawer.RenderViewpoint(Viewpoint, &bounds, FOV, (float)width / height, (float)width / height, false, false);
|
|
|
|
|
|
|
|
if (gl.legacyMode)
|
|
|
|
{
|
|
|
|
glFlush();
|
|
|
|
gl_RenderState.SetMaterial(gltex, 0, 0, -1, false);
|
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, bounds.width, bounds.height);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EndOffscreen();
|
|
|
|
}
|
|
|
|
|
|
|
|
tex->SetUpdated();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGLRenderer::WriteSavePic(player_t *player, FileWriter *file, int width, int height)
|
|
|
|
{
|
|
|
|
// Todo: This needs to call the software renderer and process the returned image, if so desired.
|
|
|
|
// This also needs to take out parts of the scene drawer so they can be shared between renderers.
|
|
|
|
GLSceneDrawer drawer;
|
|
|
|
drawer.WriteSavePic(player, file, width, height);
|
|
|
|
}
|
|
|
|
|
2018-04-29 20:15:19 +02:00
|
|
|
void FGLRenderer::BeginFrame()
|
|
|
|
{
|
|
|
|
buffersActive = GLRenderer->mBuffers->Setup(GLRenderer->mScreenViewport.width, GLRenderer->mScreenViewport.height, GLRenderer->mSceneViewport.width, GLRenderer->mSceneViewport.height);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-04 00:21:25 +02:00
|
|
|
|
2018-04-05 23:08:09 +02:00
|
|
|
void gl_FillScreen()
|
|
|
|
{
|
|
|
|
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
|
|
|
|
gl_RenderState.EnableTexture(false);
|
|
|
|
gl_RenderState.Apply();
|
|
|
|
// The fullscreen quad is stored at index 4 in the main vertex buffer.
|
|
|
|
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, FFlatVertexBuffer::FULLSCREEN_INDEX, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// Draws a blend over the entire view
|
|
|
|
//
|
|
|
|
//==========================================================================
|
2018-04-07 10:53:20 +02:00
|
|
|
void FGLRenderer::DrawBlend(sector_t * viewsector, bool FixedColormap, bool docolormap, bool in2d)
|
2018-04-05 23:08:09 +02:00
|
|
|
{
|
|
|
|
float blend[4] = { 0,0,0,0 };
|
|
|
|
PalEntry blendv = 0;
|
|
|
|
float extra_red;
|
|
|
|
float extra_green;
|
|
|
|
float extra_blue;
|
|
|
|
player_t *player = NULL;
|
|
|
|
|
|
|
|
if (players[consoleplayer].camera != NULL)
|
|
|
|
{
|
|
|
|
player = players[consoleplayer].camera->player;
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't draw sector based blends when an invulnerability colormap is active
|
|
|
|
if (!FixedColormap)
|
|
|
|
{
|
|
|
|
if (!viewsector->e->XFloor.ffloors.Size())
|
|
|
|
{
|
2018-05-01 09:47:09 +02:00
|
|
|
if (viewsector->GetHeightSec())
|
2018-04-05 23:08:09 +02:00
|
|
|
{
|
|
|
|
auto s = viewsector->heightsec;
|
|
|
|
blendv = s->floorplane.PointOnSide(r_viewpoint.Pos) < 0? s->bottommap : s->ceilingplane.PointOnSide(r_viewpoint.Pos) < 0 ? s->topmap : s->midmap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TArray<lightlist_t> & lightlist = viewsector->e->XFloor.lightlist;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < lightlist.Size(); i++)
|
|
|
|
{
|
|
|
|
double lightbottom;
|
|
|
|
if (i < lightlist.Size() - 1)
|
|
|
|
lightbottom = lightlist[i + 1].plane.ZatPoint(r_viewpoint.Pos);
|
|
|
|
else
|
|
|
|
lightbottom = viewsector->floorplane.ZatPoint(r_viewpoint.Pos);
|
|
|
|
|
|
|
|
if (lightbottom < r_viewpoint.Pos.Z && (!lightlist[i].caster || !(lightlist[i].caster->flags&FF_FADEWALLS)))
|
|
|
|
{
|
|
|
|
// 3d floor 'fog' is rendered as a blending value
|
|
|
|
blendv = lightlist[i].blend;
|
|
|
|
// If this is the same as the sector's it doesn't apply!
|
|
|
|
if (blendv == viewsector->Colormap.FadeColor) blendv = 0;
|
|
|
|
// a little hack to make this work for Legacy maps.
|
|
|
|
if (blendv.a == 0 && blendv != 0) blendv.a = 128;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blendv.a == 0 && docolormap)
|
|
|
|
{
|
|
|
|
blendv = R_BlendForColormap(blendv);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blendv.a == 255)
|
|
|
|
{
|
|
|
|
|
|
|
|
extra_red = blendv.r / 255.0f;
|
|
|
|
extra_green = blendv.g / 255.0f;
|
|
|
|
extra_blue = blendv.b / 255.0f;
|
|
|
|
|
|
|
|
// If this is a multiplicative blend do it separately and add the additive ones on top of it.
|
|
|
|
|
|
|
|
// black multiplicative blends are ignored
|
|
|
|
if (extra_red || extra_green || extra_blue)
|
|
|
|
{
|
2018-04-07 10:53:20 +02:00
|
|
|
if (!in2d)
|
|
|
|
{
|
|
|
|
gl_RenderState.BlendFunc(GL_DST_COLOR, GL_ZERO);
|
|
|
|
gl_RenderState.SetColor(extra_red, extra_green, extra_blue, 1.0f);
|
|
|
|
gl_FillScreen();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
screen->Dim(blendv, 1, 0, 0, screen->GetWidth(), screen->GetHeight(), &LegacyRenderStyles[STYLE_Multiply]);
|
|
|
|
}
|
2018-04-05 23:08:09 +02:00
|
|
|
}
|
2018-04-07 10:53:20 +02:00
|
|
|
blendv = 0;
|
2018-04-05 23:08:09 +02:00
|
|
|
}
|
|
|
|
else if (blendv.a)
|
|
|
|
{
|
|
|
|
// [Nash] allow user to set blend intensity
|
|
|
|
int cnt = blendv.a;
|
|
|
|
cnt = (int)(cnt * underwater_fade_scalar);
|
|
|
|
|
|
|
|
V_AddBlend(blendv.r / 255.f, blendv.g / 255.f, blendv.b / 255.f, cnt / 255.0f, blend);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (player)
|
|
|
|
{
|
|
|
|
V_AddPlayerBlend(player, blend, 0.5, 175);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (players[consoleplayer].camera != NULL)
|
|
|
|
{
|
|
|
|
// except for fadeto effects
|
|
|
|
player_t *player = (players[consoleplayer].camera->player != NULL) ? players[consoleplayer].camera->player : &players[consoleplayer];
|
|
|
|
V_AddBlend(player->BlendR, player->BlendG, player->BlendB, player->BlendA, blend);
|
|
|
|
}
|
|
|
|
|
2018-04-07 10:53:20 +02:00
|
|
|
if (!in2d)
|
|
|
|
{
|
|
|
|
gl_RenderState.SetTextureMode(TM_MODULATE);
|
|
|
|
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
if (blend[3] > 0.0f)
|
|
|
|
{
|
|
|
|
gl_RenderState.SetColor(blend[0], blend[1], blend[2], blend[3]);
|
|
|
|
gl_FillScreen();
|
|
|
|
}
|
|
|
|
gl_RenderState.ResetColor();
|
|
|
|
gl_RenderState.EnableTexture(true);
|
|
|
|
}
|
|
|
|
else
|
2018-04-05 23:08:09 +02:00
|
|
|
{
|
2018-04-07 10:53:20 +02:00
|
|
|
screen->Dim(PalEntry(255, blend[0] * 255, blend[1] * 255, blend[2] * 255), blend[3], 0, 0, screen->GetWidth(), screen->GetHeight());
|
2018-04-05 23:08:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-28 21:38:00 +02:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Vertex buffer for 2D drawer
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2018-03-29 11:58:28 +02:00
|
|
|
class F2DVertexBuffer : public FSimpleVertexBuffer
|
2018-03-28 21:38:00 +02:00
|
|
|
{
|
|
|
|
uint32_t ibo_id;
|
|
|
|
|
2018-03-29 11:58:28 +02:00
|
|
|
// Make sure we can build upon FSimpleVertexBuffer.
|
2018-04-08 11:18:20 +03:00
|
|
|
static_assert(offsetof(FSimpleVertex, x) == offsetof(F2DDrawer::TwoDVertex, x), "x not aligned");
|
|
|
|
static_assert(offsetof(FSimpleVertex, u) == offsetof(F2DDrawer::TwoDVertex, u), "u not aligned");
|
|
|
|
static_assert(offsetof(FSimpleVertex, color) == offsetof(F2DDrawer::TwoDVertex, color0), "color not aligned");
|
2018-03-29 11:58:28 +02:00
|
|
|
|
2018-03-28 21:38:00 +02:00
|
|
|
public:
|
|
|
|
|
|
|
|
F2DVertexBuffer()
|
|
|
|
{
|
|
|
|
glGenBuffers(1, &ibo_id);
|
|
|
|
}
|
|
|
|
~F2DVertexBuffer()
|
|
|
|
{
|
|
|
|
if (ibo_id != 0)
|
|
|
|
{
|
|
|
|
glDeleteBuffers(1, &ibo_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void UploadData(F2DDrawer::TwoDVertex *vertices, int vertcount, int *indices, int indexcount)
|
|
|
|
{
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, vertcount * sizeof(vertices[0]), vertices, GL_STREAM_DRAW);
|
|
|
|
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id);
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexcount * sizeof(indices[0]), indices, GL_STREAM_DRAW);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BindVBO() override
|
|
|
|
{
|
2018-03-29 11:58:28 +02:00
|
|
|
FSimpleVertexBuffer::BindVBO();
|
2018-03-28 23:42:06 +02:00
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id);
|
2018-03-28 21:38:00 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Draws the 2D stuff. This is the version for OpenGL 3 and later.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2018-03-30 18:14:42 +02:00
|
|
|
void LegacyColorOverlay(F2DDrawer *drawer, F2DDrawer::RenderCommand & cmd);
|
|
|
|
int LegacyDesaturation(F2DDrawer::RenderCommand &cmd);
|
2018-04-29 20:15:19 +02:00
|
|
|
CVAR(Bool, gl_aalines, false, CVAR_ARCHIVE)
|
2018-03-30 18:14:42 +02:00
|
|
|
|
2018-03-28 21:38:00 +02:00
|
|
|
void FGLRenderer::Draw2D(F2DDrawer *drawer)
|
|
|
|
{
|
2018-04-29 20:15:19 +02:00
|
|
|
if (buffersActive)
|
|
|
|
{
|
|
|
|
mBuffers->BindCurrentFB();
|
|
|
|
}
|
|
|
|
glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
|
|
|
|
|
|
|
|
gl_RenderState.mViewMatrix.loadIdentity();
|
|
|
|
gl_RenderState.mProjectionMatrix.ortho(0, screen->GetWidth(), screen->GetHeight(), 0, -1.0f, 1.0f);
|
|
|
|
gl_RenderState.ApplyMatrices();
|
|
|
|
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
|
|
|
|
// Korshun: ENABLE AUTOMAP ANTIALIASING!!!
|
|
|
|
if (gl_aalines)
|
|
|
|
glEnable(GL_LINE_SMOOTH);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glDisable(GL_MULTISAMPLE);
|
|
|
|
glDisable(GL_LINE_SMOOTH);
|
|
|
|
glLineWidth(1.0);
|
|
|
|
}
|
2018-03-30 18:14:42 +02:00
|
|
|
|
|
|
|
|
2018-03-28 21:38:00 +02:00
|
|
|
auto &vertices = drawer->mVertices;
|
|
|
|
auto &indices = drawer->mIndices;
|
|
|
|
auto &commands = drawer->mData;
|
|
|
|
|
|
|
|
if (commands.Size() == 0) return;
|
|
|
|
|
|
|
|
for (auto &v : vertices)
|
|
|
|
{
|
|
|
|
// Change from BGRA to RGBA
|
|
|
|
std::swap(v.color0.r, v.color0.b);
|
|
|
|
}
|
|
|
|
auto vb = new F2DVertexBuffer;
|
|
|
|
vb->UploadData(&vertices[0], vertices.Size(), &indices[0], indices.Size());
|
|
|
|
gl_RenderState.SetVertexBuffer(vb);
|
2018-03-30 18:14:42 +02:00
|
|
|
gl_RenderState.SetFixedColormap(CM_DEFAULT);
|
2018-04-29 20:15:19 +02:00
|
|
|
gl_RenderState.EnableFog(false);
|
2018-03-28 21:38:00 +02:00
|
|
|
|
|
|
|
for(auto &cmd : commands)
|
|
|
|
{
|
2018-03-30 18:14:42 +02:00
|
|
|
|
|
|
|
int gltrans = -1;
|
2018-03-28 21:38:00 +02:00
|
|
|
int tm, sb, db, be;
|
|
|
|
// The texture mode being returned here cannot be used, because the higher level code
|
|
|
|
// already manipulated the data so that some cases will not be handled correctly.
|
|
|
|
// Since we already get a proper mode from the calling code this doesn't really matter.
|
|
|
|
gl_GetRenderStyle(cmd.mRenderStyle, false, false, &tm, &sb, &db, &be);
|
2018-03-29 11:58:28 +02:00
|
|
|
gl_RenderState.BlendEquation(be);
|
2018-03-28 21:38:00 +02:00
|
|
|
gl_RenderState.BlendFunc(sb, db);
|
2018-04-30 21:28:06 +02:00
|
|
|
gl_RenderState.EnableBrightmap(!(cmd.mRenderStyle.Flags & STYLEF_ColorIsFixed));
|
2018-03-28 21:38:00 +02:00
|
|
|
|
|
|
|
// Rather than adding remapping code, let's enforce that the constants here are equal.
|
2018-04-08 12:53:41 +03:00
|
|
|
static_assert(int(F2DDrawer::DTM_Normal) == int(TM_MODULATE), "DTM_Normal != TM_MODULATE");
|
|
|
|
static_assert(int(F2DDrawer::DTM_Opaque) == int(TM_OPAQUE), "DTM_Opaque != TM_OPAQUE");
|
|
|
|
static_assert(int(F2DDrawer::DTM_Invert) == int(TM_INVERSE), "DTM_Invert != TM_INVERSE");
|
|
|
|
static_assert(int(F2DDrawer::DTM_InvertOpaque) == int(TM_INVERTOPAQUE), "DTM_InvertOpaque != TM_INVERTOPAQUE");
|
|
|
|
static_assert(int(F2DDrawer::DTM_Stencil) == int(TM_MASK), "DTM_Stencil != TM_MASK");
|
|
|
|
static_assert(int(F2DDrawer::DTM_AlphaTexture) == int(TM_REDTOALPHA), "DTM_AlphaTexture != TM_REDTOALPHA");
|
2018-03-28 21:38:00 +02:00
|
|
|
gl_RenderState.SetTextureMode(cmd.mDrawMode);
|
|
|
|
if (cmd.mFlags & F2DDrawer::DTF_Scissor)
|
|
|
|
{
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
// scissor test doesn't use the current viewport for the coordinates, so use real screen coordinates
|
2018-03-29 11:58:28 +02:00
|
|
|
// Note that the origin here is the lower left corner!
|
|
|
|
auto sciX = ScreenToWindowX(cmd.mScissor[0]);
|
|
|
|
auto sciY = ScreenToWindowY(cmd.mScissor[3]);
|
|
|
|
auto sciW = ScreenToWindowX(cmd.mScissor[2]) - sciX;
|
|
|
|
auto sciH = ScreenToWindowY(cmd.mScissor[1]) - sciY;
|
|
|
|
glScissor(sciX, sciY, sciW, sciH);
|
2018-03-28 21:38:00 +02:00
|
|
|
}
|
|
|
|
else glDisable(GL_SCISSOR_TEST);
|
|
|
|
|
2018-03-30 18:14:42 +02:00
|
|
|
if (cmd.mSpecialColormap != nullptr)
|
2018-03-28 21:38:00 +02:00
|
|
|
{
|
2018-03-30 18:14:42 +02:00
|
|
|
auto index = cmd.mSpecialColormap - &SpecialColormaps[0];
|
|
|
|
if (index < 0 || (unsigned)index >= SpecialColormaps.Size()) index = 0; // if it isn't in the table FBitmap cannot use it. Shouldn't happen anyway.
|
2018-04-08 13:04:44 +02:00
|
|
|
if (!gl.legacyMode || cmd.mTexture->UseType == ETextureType::SWCanvas)
|
2018-03-30 18:14:42 +02:00
|
|
|
{
|
2018-03-30 21:14:49 +02:00
|
|
|
gl_RenderState.SetFixedColormap(CM_FIRSTSPECIALCOLORMAPFORCED + int(index));
|
2018-03-30 18:14:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// map the special colormap to a translation for the legacy renderer.
|
|
|
|
// This only gets used on the software renderer's weapon sprite.
|
|
|
|
gltrans = STRange_Specialcolormap + index;
|
|
|
|
}
|
2018-03-28 21:38:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-03-30 18:14:42 +02:00
|
|
|
if (!gl.legacyMode)
|
2018-03-28 22:04:28 +02:00
|
|
|
{
|
2018-03-30 18:14:42 +02:00
|
|
|
gl_RenderState.Set2DOverlayColor(cmd.mColor1);
|
|
|
|
gl_RenderState.SetFixedColormap(CM_PLAIN2D);
|
2018-03-28 22:04:28 +02:00
|
|
|
}
|
2018-03-30 18:14:42 +02:00
|
|
|
else if (cmd.mDesaturate > 0)
|
2018-03-28 22:04:28 +02:00
|
|
|
{
|
2018-03-30 18:14:42 +02:00
|
|
|
gltrans = LegacyDesaturation(cmd);
|
2018-03-28 22:04:28 +02:00
|
|
|
}
|
2018-03-28 21:38:00 +02:00
|
|
|
}
|
2018-03-30 18:14:42 +02:00
|
|
|
|
2018-03-28 23:42:06 +02:00
|
|
|
gl_RenderState.SetColor(1, 1, 1, 1, cmd.mDesaturate);
|
2018-03-28 21:38:00 +02:00
|
|
|
|
|
|
|
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
|
|
|
|
|
2018-03-30 18:14:42 +02:00
|
|
|
if (cmd.mTexture != nullptr)
|
2018-03-28 21:38:00 +02:00
|
|
|
{
|
2018-03-30 18:14:42 +02:00
|
|
|
auto mat = FMaterial::ValidateTexture(cmd.mTexture, false);
|
|
|
|
if (mat == nullptr) continue;
|
|
|
|
|
2018-04-08 12:11:51 +02:00
|
|
|
// This requires very special handling
|
|
|
|
if (gl.legacyMode && cmd.mTexture->UseType == ETextureType::SWCanvas)
|
|
|
|
{
|
|
|
|
gl_RenderState.SetTextureMode(TM_SWCANVAS);
|
|
|
|
}
|
|
|
|
|
2018-04-01 00:59:49 +02:00
|
|
|
if (gltrans == -1 && cmd.mTranslation != nullptr) gltrans = cmd.mTranslation->GetUniqueIndex();
|
2018-03-30 18:14:42 +02:00
|
|
|
gl_RenderState.SetMaterial(mat, cmd.mFlags & F2DDrawer::DTF_Wrap ? CLAMP_NONE : CLAMP_XY_NOMIP, -gltrans, -1, cmd.mDrawMode == F2DDrawer::DTM_AlphaTexture);
|
|
|
|
gl_RenderState.EnableTexture(true);
|
|
|
|
|
|
|
|
// Canvas textures are stored upside down
|
|
|
|
if (cmd.mTexture->bHasCanvas)
|
2018-03-28 21:38:00 +02:00
|
|
|
{
|
2018-03-30 18:14:42 +02:00
|
|
|
gl_RenderState.mTextureMatrix.loadIdentity();
|
|
|
|
gl_RenderState.mTextureMatrix.scale(1.f, -1.f, 1.f);
|
|
|
|
gl_RenderState.mTextureMatrix.translate(0.f, 1.f, 0.0f);
|
|
|
|
gl_RenderState.EnableTextureMatrix(true);
|
2018-03-28 21:38:00 +02:00
|
|
|
}
|
2018-03-30 18:14:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gl_RenderState.EnableTexture(false);
|
|
|
|
}
|
|
|
|
gl_RenderState.Apply();
|
|
|
|
|
|
|
|
switch (cmd.mType)
|
|
|
|
{
|
|
|
|
case F2DDrawer::DrawTypeTriangles:
|
|
|
|
glDrawElements(GL_TRIANGLES, cmd.mIndexCount, GL_UNSIGNED_INT, (const void *)(cmd.mIndexIndex * sizeof(unsigned int)));
|
|
|
|
if (gl.legacyMode && cmd.mColor1 != 0)
|
2018-03-28 21:38:00 +02:00
|
|
|
{
|
2018-03-30 18:14:42 +02:00
|
|
|
// Draw the overlay as a separate operation.
|
|
|
|
LegacyColorOverlay(drawer, cmd);
|
2018-03-28 21:38:00 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case F2DDrawer::DrawTypeLines:
|
|
|
|
glDrawArrays(GL_LINES, cmd.mVertIndex, cmd.mVertCount);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case F2DDrawer::DrawTypePoints:
|
|
|
|
glDrawArrays(GL_POINTS, cmd.mVertIndex, cmd.mVertCount);
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
2018-04-05 23:08:09 +02:00
|
|
|
gl_RenderState.SetEffect(EFF_NONE);
|
2018-03-30 18:14:42 +02:00
|
|
|
gl_RenderState.EnableTextureMatrix(false);
|
2018-03-28 21:38:00 +02:00
|
|
|
}
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
2018-03-29 11:58:28 +02:00
|
|
|
|
|
|
|
gl_RenderState.SetVertexBuffer(GLRenderer->mVBO);
|
2018-03-28 23:42:06 +02:00
|
|
|
gl_RenderState.EnableTexture(true);
|
2018-04-30 21:28:06 +02:00
|
|
|
gl_RenderState.EnableBrightmap(true);
|
2018-03-29 00:38:31 +02:00
|
|
|
gl_RenderState.SetTextureMode(TM_MODULATE);
|
2018-03-30 18:14:42 +02:00
|
|
|
gl_RenderState.SetFixedColormap(CM_DEFAULT);
|
2018-03-29 00:38:31 +02:00
|
|
|
gl_RenderState.ResetColor();
|
2018-03-29 11:58:28 +02:00
|
|
|
gl_RenderState.Apply();
|
2018-03-28 21:38:00 +02:00
|
|
|
delete vb;
|
2018-03-29 11:58:28 +02:00
|
|
|
}
|