2016-09-14 18:01:13 +00:00
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// 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/
|
|
|
|
|
//
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
//
|
2013-06-23 07:49:34 +00:00
|
|
|
|
/*
|
|
|
|
|
** gl_scene.cpp
|
|
|
|
|
** manages the rendering of the player's view
|
|
|
|
|
**
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "gl/system/gl_system.h"
|
|
|
|
|
#include "gi.h"
|
|
|
|
|
#include "m_png.h"
|
|
|
|
|
#include "m_random.h"
|
|
|
|
|
#include "st_stuff.h"
|
|
|
|
|
#include "dobject.h"
|
|
|
|
|
#include "doomstat.h"
|
|
|
|
|
#include "g_level.h"
|
|
|
|
|
#include "r_data/r_interpolate.h"
|
|
|
|
|
#include "r_utility.h"
|
|
|
|
|
#include "d_player.h"
|
|
|
|
|
#include "p_effect.h"
|
|
|
|
|
#include "sbar.h"
|
|
|
|
|
#include "po_man.h"
|
|
|
|
|
#include "r_utility.h"
|
|
|
|
|
#include "p_local.h"
|
|
|
|
|
#include "gl/gl_functions.h"
|
2016-09-23 23:47:44 +00:00
|
|
|
|
#include "serializer.h"
|
2017-01-08 17:45:30 +00:00
|
|
|
|
#include "g_levellocals.h"
|
2017-01-22 00:33:53 +00:00
|
|
|
|
#include "events.h"
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2014-08-01 18:59:39 +00:00
|
|
|
|
#include "gl/dynlights/gl_lightbuffer.h"
|
2013-09-03 16:29:39 +00:00
|
|
|
|
#include "gl/system/gl_interface.h"
|
2013-06-23 07:49:34 +00:00
|
|
|
|
#include "gl/system/gl_framebuffer.h"
|
|
|
|
|
#include "gl/system/gl_cvars.h"
|
|
|
|
|
#include "gl/renderer/gl_lightdata.h"
|
|
|
|
|
#include "gl/renderer/gl_renderstate.h"
|
2016-07-26 19:27:02 +00:00
|
|
|
|
#include "gl/renderer/gl_renderbuffers.h"
|
2013-06-23 07:49:34 +00:00
|
|
|
|
#include "gl/data/gl_data.h"
|
|
|
|
|
#include "gl/data/gl_vertexbuffer.h"
|
|
|
|
|
#include "gl/dynlights/gl_dynlight.h"
|
|
|
|
|
#include "gl/models/gl_models.h"
|
|
|
|
|
#include "gl/scene/gl_clipper.h"
|
|
|
|
|
#include "gl/scene/gl_drawinfo.h"
|
|
|
|
|
#include "gl/scene/gl_portal.h"
|
|
|
|
|
#include "gl/shaders/gl_shader.h"
|
2015-10-31 00:51:35 +00:00
|
|
|
|
#include "gl/stereo3d/gl_stereo3d.h"
|
|
|
|
|
#include "gl/stereo3d/scoped_view_shifter.h"
|
2016-05-02 23:00:52 +00:00
|
|
|
|
#include "gl/textures/gl_translate.h"
|
2013-06-23 07:49:34 +00:00
|
|
|
|
#include "gl/textures/gl_material.h"
|
2016-05-01 22:13:00 +00:00
|
|
|
|
#include "gl/textures/gl_skyboxtexture.h"
|
2013-06-23 07:49:34 +00:00
|
|
|
|
#include "gl/utility/gl_clock.h"
|
|
|
|
|
#include "gl/utility/gl_convert.h"
|
|
|
|
|
#include "gl/utility/gl_templates.h"
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// CVARs
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
CVAR(Bool, gl_texture, true, 0)
|
|
|
|
|
CVAR(Bool, gl_no_skyclear, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
|
CVAR(Float, gl_mask_threshold, 0.5f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
|
CVAR(Float, gl_mask_sprite_threshold, 0.5f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
2014-08-30 13:34:14 +00:00
|
|
|
|
CVAR(Bool, gl_sort_textures, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
EXTERN_CVAR (Bool, cl_capfps)
|
|
|
|
|
EXTERN_CVAR (Bool, r_deathcamera)
|
2016-11-11 10:08:47 +00:00
|
|
|
|
EXTERN_CVAR (Float, underwater_fade_scalar)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern int viewpitch;
|
2016-03-05 11:46:47 +00:00
|
|
|
|
extern bool NoInterpolateView;
|
2016-06-18 10:14:20 +00:00
|
|
|
|
extern bool r_showviewer;
|
2016-03-05 11:46:47 +00:00
|
|
|
|
|
2017-01-05 13:45:15 +00:00
|
|
|
|
int gl_fixedcolormap;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
area_t in_area;
|
|
|
|
|
TArray<BYTE> currentmapsection;
|
2016-12-25 21:56:18 +00:00
|
|
|
|
int camtexcount;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
void gl_ParseDefs();
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// R_FrustumAngle
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
angle_t FGLRenderer::FrustumAngle()
|
|
|
|
|
{
|
2016-03-21 01:57:02 +00:00
|
|
|
|
float tilt= fabs(mAngles.Pitch.Degrees);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
// If the pitch is larger than this you can look all around at a FOV of 90<39>
|
|
|
|
|
if (tilt>46.0f) return 0xffffffff;
|
|
|
|
|
|
|
|
|
|
// ok, this is a gross hack that barely works...
|
|
|
|
|
// but at least it doesn't overestimate too much...
|
2016-09-12 18:29:26 +00:00
|
|
|
|
double floatangle=2.0+(45.0+((tilt/1.9)))*mCurrentFoV*48.0/AspectMultiplier(WidescreenRatio)/90.0;
|
2016-03-30 18:01:44 +00:00
|
|
|
|
angle_t a1 = DAngle(floatangle).BAMs();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
if (a1>=ANGLE_180) return 0xffffffff;
|
|
|
|
|
return a1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Sets the area the camera is in
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void FGLRenderer::SetViewArea()
|
|
|
|
|
{
|
|
|
|
|
// The render_sector is better suited to represent the current position in GL
|
2016-04-02 21:17:16 +00:00
|
|
|
|
viewsector = R_PointInSubsector(ViewPos)->render_sector;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
// Get the heightsec state from the render sector, not the current one!
|
|
|
|
|
if (viewsector->heightsec && !(viewsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC))
|
|
|
|
|
{
|
2016-04-02 21:17:16 +00:00
|
|
|
|
in_area = ViewPos.Z <= viewsector->heightsec->floorplane.ZatPoint(ViewPos) ? area_below :
|
|
|
|
|
(ViewPos.Z > viewsector->heightsec->ceilingplane.ZatPoint(ViewPos) &&
|
|
|
|
|
!(viewsector->heightsec->MoreFlags&SECF_FAKEFLOORONLY)) ? area_above : area_normal;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-04-02 21:17:16 +00:00
|
|
|
|
in_area = area_default; // depends on exposed lower sectors
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// resets the 3D viewport
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2016-07-26 19:27:02 +00:00
|
|
|
|
void FGLRenderer::Reset3DViewport()
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2016-08-12 05:28:29 +00:00
|
|
|
|
glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// sets 3D viewport and initial state
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2016-07-31 14:23:21 +00:00
|
|
|
|
void FGLRenderer::Set3DViewport(bool mainview)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2016-08-28 16:07:44 +00:00
|
|
|
|
if (mainview && mBuffers->Setup(mScreenViewport.width, mScreenViewport.height, mSceneViewport.width, mSceneViewport.height))
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2016-09-24 17:26:25 +00:00
|
|
|
|
bool useSSAO = (gl_ssao != 0);
|
|
|
|
|
mBuffers->BindSceneFB(useSSAO);
|
|
|
|
|
gl_RenderState.SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS);
|
2016-10-21 22:09:06 +00:00
|
|
|
|
gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount());
|
2016-09-21 00:04:56 +00:00
|
|
|
|
gl_RenderState.Apply();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
2016-07-26 19:27:02 +00:00
|
|
|
|
|
2016-08-17 15:37:13 +00:00
|
|
|
|
// 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.
|
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
2016-08-21 23:52:51 +00:00
|
|
|
|
glClearColor(mSceneClearColor[0], mSceneClearColor[1], mSceneClearColor[2], 1.0f);
|
2016-08-17 15:37:13 +00:00
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
|
|
|
|
2016-08-12 05:28:29 +00:00
|
|
|
|
const auto &bounds = mSceneViewport;
|
2016-07-31 11:23:49 +00:00
|
|
|
|
glViewport(bounds.left, bounds.top, bounds.width, bounds.height);
|
|
|
|
|
glScissor(bounds.left, bounds.top, bounds.width, bounds.height);
|
|
|
|
|
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glEnable(GL_MULTISAMPLE);
|
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
|
glEnable(GL_STENCIL_TEST);
|
|
|
|
|
glStencilFunc(GL_ALWAYS,0,~0); // default stencil
|
|
|
|
|
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Setup the camera position
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2016-04-02 21:17:16 +00:00
|
|
|
|
void FGLRenderer::SetViewAngle(DAngle viewangle)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2016-04-02 21:17:16 +00:00
|
|
|
|
mAngles.Yaw = float(270.0-viewangle.Degrees);
|
|
|
|
|
DVector2 v = ViewAngle.ToVector();
|
|
|
|
|
mViewVector.X = v.X;
|
|
|
|
|
mViewVector.Y = v.Y;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
R_SetViewAngle();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// SetProjection
|
|
|
|
|
// sets projection matrix
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void FGLRenderer::SetProjection(float fov, float ratio, float fovratio)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
float fovy = 2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovratio));
|
2016-08-29 11:10:22 +00:00
|
|
|
|
gl_RenderState.mProjectionMatrix.perspective(fovy, ratio, GetZNear(), GetZFar());
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-31 00:51:35 +00:00
|
|
|
|
// raw matrix input from stereo 3d modes
|
2015-10-31 11:46:36 +00:00
|
|
|
|
void FGLRenderer::SetProjection(VSMatrix matrix)
|
2015-10-31 00:51:35 +00:00
|
|
|
|
{
|
2015-10-31 11:46:36 +00:00
|
|
|
|
gl_RenderState.mProjectionMatrix.loadIdentity();
|
|
|
|
|
gl_RenderState.mProjectionMatrix.multMatrix(matrix);
|
2015-10-31 00:51:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-23 07:49:34 +00:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Setup the modelview matrix
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2016-04-02 21:17:16 +00:00
|
|
|
|
void FGLRenderer::SetViewMatrix(float vx, float vy, float vz, bool mirror, bool planemirror)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
|
|
|
|
float mult = mirror? -1:1;
|
2014-12-29 22:52:20 +00:00
|
|
|
|
float planemult = planemirror? -glset.pixelstretch : glset.pixelstretch;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2014-07-13 20:37:34 +00:00
|
|
|
|
gl_RenderState.mViewMatrix.loadIdentity();
|
2016-03-21 01:57:02 +00:00
|
|
|
|
gl_RenderState.mViewMatrix.rotate(GLRenderer->mAngles.Roll.Degrees, 0.0f, 0.0f, 1.0f);
|
|
|
|
|
gl_RenderState.mViewMatrix.rotate(GLRenderer->mAngles.Pitch.Degrees, 1.0f, 0.0f, 0.0f);
|
|
|
|
|
gl_RenderState.mViewMatrix.rotate(GLRenderer->mAngles.Yaw.Degrees, 0.0f, mult, 0.0f);
|
2016-04-02 21:17:16 +00:00
|
|
|
|
gl_RenderState.mViewMatrix.translate(vx * mult, -vz * planemult , -vy);
|
2014-07-13 20:37:34 +00:00
|
|
|
|
gl_RenderState.mViewMatrix.scale(-mult, planemult, 1);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// SetupView
|
|
|
|
|
// Setup the view rotation matrix for the given viewpoint
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2016-04-02 21:17:16 +00:00
|
|
|
|
void FGLRenderer::SetupView(float vx, float vy, float vz, DAngle va, bool mirror, bool planemirror)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2016-04-02 21:17:16 +00:00
|
|
|
|
SetViewAngle(va);
|
|
|
|
|
SetViewMatrix(vx, vy, vz, mirror, planemirror);
|
2014-07-13 20:37:34 +00:00
|
|
|
|
gl_RenderState.ApplyMatrices();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// CreateScene
|
|
|
|
|
//
|
|
|
|
|
// creates the draw lists for the current scene
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void FGLRenderer::CreateScene()
|
|
|
|
|
{
|
|
|
|
|
// reset the portal manager
|
|
|
|
|
GLPortal::StartFrame();
|
|
|
|
|
PO_LinkToSubsectors();
|
|
|
|
|
|
|
|
|
|
ProcessAll.Clock();
|
|
|
|
|
|
|
|
|
|
// clip the scene and fill the drawlists
|
|
|
|
|
for(unsigned i=0;i<portals.Size(); i++) portals[i]->glportal = NULL;
|
|
|
|
|
gl_spriteindex=0;
|
|
|
|
|
Bsp.Clock();
|
2016-08-29 08:43:03 +00:00
|
|
|
|
GLRenderer->mVBO->Map();
|
2016-04-02 21:17:16 +00:00
|
|
|
|
R_SetView();
|
2016-04-17 08:39:29 +00:00
|
|
|
|
validcount++; // used for processing sidedefs only once by the renderer.
|
2013-06-23 07:49:34 +00:00
|
|
|
|
gl_RenderBSPNode (nodes + numnodes - 1);
|
2016-04-30 14:57:53 +00:00
|
|
|
|
if (GLRenderer->mCurrentPortal != NULL) GLRenderer->mCurrentPortal->RenderAttached();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
Bsp.Unclock();
|
|
|
|
|
|
|
|
|
|
// And now the crappy hacks that have to be done to avoid rendering anomalies:
|
|
|
|
|
|
|
|
|
|
gl_drawinfo->HandleMissingTextures(); // Missing upper/lower textures
|
|
|
|
|
gl_drawinfo->HandleHackedSubsectors(); // open sector hacks for deep water
|
|
|
|
|
gl_drawinfo->ProcessSectorStacks(); // merge visplanes of sector stacks
|
2016-08-29 08:43:03 +00:00
|
|
|
|
GLRenderer->mVBO->Unmap();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
ProcessAll.Unclock();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// RenderScene
|
|
|
|
|
//
|
|
|
|
|
// Draws the current draw lists for the non GLSL renderer
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void FGLRenderer::RenderScene(int recursion)
|
|
|
|
|
{
|
|
|
|
|
RenderAll.Clock();
|
|
|
|
|
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glDepthMask(true);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
if (!gl_no_skyclear) GLPortal::RenderFirstSkyPortal(recursion);
|
|
|
|
|
|
2016-04-02 21:17:16 +00:00
|
|
|
|
gl_RenderState.SetCameraPos(ViewPos.X, ViewPos.Y, ViewPos.Z);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
gl_RenderState.EnableFog(true);
|
|
|
|
|
gl_RenderState.BlendFunc(GL_ONE,GL_ZERO);
|
|
|
|
|
|
2014-08-30 13:34:14 +00:00
|
|
|
|
if (gl_sort_textures)
|
|
|
|
|
{
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_PLAINWALLS].SortWalls();
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_PLAINFLATS].SortFlats();
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_MASKEDWALLS].SortWalls();
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_MASKEDFLATS].SortFlats();
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_MASKEDWALLSOFS].SortWalls();
|
|
|
|
|
}
|
2014-08-19 12:18:21 +00:00
|
|
|
|
|
|
|
|
|
// if we don't have a persistently mapped buffer, we have to process all the dynamic lights up front,
|
|
|
|
|
// so that we don't have to do repeated map/unmap calls on the buffer.
|
2016-04-26 11:50:05 +00:00
|
|
|
|
bool haslights = mLightCount > 0 && gl_fixedcolormap == CM_DEFAULT && gl_lights;
|
|
|
|
|
if (gl.lightmethod == LM_DEFERRED && haslights)
|
2014-08-19 12:18:21 +00:00
|
|
|
|
{
|
|
|
|
|
GLRenderer->mLights->Begin();
|
2014-08-30 12:33:06 +00:00
|
|
|
|
gl_drawinfo->drawlists[GLDL_PLAINWALLS].DrawWalls(GLPASS_LIGHTSONLY);
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_PLAINFLATS].DrawFlats(GLPASS_LIGHTSONLY);
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_MASKEDWALLS].DrawWalls(GLPASS_LIGHTSONLY);
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_MASKEDFLATS].DrawFlats(GLPASS_LIGHTSONLY);
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_MASKEDWALLSOFS].DrawWalls(GLPASS_LIGHTSONLY);
|
2014-08-19 12:18:21 +00:00
|
|
|
|
gl_drawinfo->drawlists[GLDL_TRANSLUCENTBORDER].Draw(GLPASS_LIGHTSONLY);
|
2014-11-09 11:10:33 +00:00
|
|
|
|
gl_drawinfo->drawlists[GLDL_TRANSLUCENT].Draw(GLPASS_LIGHTSONLY, true);
|
2014-08-19 12:18:21 +00:00
|
|
|
|
GLRenderer->mLights->Finish();
|
|
|
|
|
}
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
// Part 1: solid geometry. This is set up so that there are no transparent parts
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glDepthFunc(GL_LESS);
|
2014-07-14 19:14:43 +00:00
|
|
|
|
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
|
2014-08-19 12:18:21 +00:00
|
|
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
int pass;
|
|
|
|
|
|
2016-04-26 11:50:05 +00:00
|
|
|
|
if (!haslights || gl.lightmethod == LM_DEFERRED)
|
|
|
|
|
{
|
|
|
|
|
pass = GLPASS_PLAIN;
|
|
|
|
|
}
|
|
|
|
|
else if (gl.lightmethod == LM_DIRECT)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
|
|
|
|
pass = GLPASS_ALL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-04-27 22:58:44 +00:00
|
|
|
|
// process everything that needs to handle textured dynamic lights.
|
|
|
|
|
if (haslights) RenderMultipassStuff();
|
2016-04-26 11:50:05 +00:00
|
|
|
|
|
2016-04-27 22:58:44 +00:00
|
|
|
|
// The remaining lists which are unaffected by dynamic lights are just processed as normal.
|
2014-07-15 18:49:21 +00:00
|
|
|
|
pass = GLPASS_PLAIN;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl_RenderState.EnableTexture(gl_texture);
|
2014-07-15 18:49:21 +00:00
|
|
|
|
gl_RenderState.EnableBrightmap(true);
|
2014-08-30 12:33:06 +00:00
|
|
|
|
gl_drawinfo->drawlists[GLDL_PLAINWALLS].DrawWalls(pass);
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_PLAINFLATS].DrawFlats(pass);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
|
2014-07-15 18:49:21 +00:00
|
|
|
|
// Part 2: masked geometry. This is set up so that only pixels with alpha>gl_mask_threshold will show
|
2013-06-23 07:49:34 +00:00
|
|
|
|
if (!gl_texture)
|
|
|
|
|
{
|
|
|
|
|
gl_RenderState.EnableTexture(true);
|
|
|
|
|
gl_RenderState.SetTextureMode(TM_MASK);
|
|
|
|
|
}
|
2014-07-14 19:14:43 +00:00
|
|
|
|
gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold);
|
2014-08-30 12:33:06 +00:00
|
|
|
|
gl_drawinfo->drawlists[GLDL_MASKEDWALLS].DrawWalls(pass);
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_MASKEDFLATS].DrawFlats(pass);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2014-08-19 12:18:21 +00:00
|
|
|
|
// Part 3: masked geometry with polygon offset. This list is empty most of the time so only waste time on it when in use.
|
2014-08-30 11:04:41 +00:00
|
|
|
|
if (gl_drawinfo->drawlists[GLDL_MASKEDWALLSOFS].Size() > 0)
|
2014-07-15 19:16:59 +00:00
|
|
|
|
{
|
|
|
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
|
|
|
glPolygonOffset(-1.0f, -128.0f);
|
2014-08-30 12:33:06 +00:00
|
|
|
|
gl_drawinfo->drawlists[GLDL_MASKEDWALLSOFS].DrawWalls(pass);
|
2014-07-15 19:16:59 +00:00
|
|
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
|
|
|
glPolygonOffset(0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_MODELS].Draw(pass);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
2014-08-19 12:18:21 +00:00
|
|
|
|
// Part 4: Draw decals (not a real pass)
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glDepthFunc(GL_LEQUAL);
|
|
|
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
|
|
|
glPolygonOffset(-1.0f, -128.0f);
|
|
|
|
|
glDepthMask(false);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2014-08-30 12:33:06 +00:00
|
|
|
|
// this is the only geometry type on which decals can possibly appear
|
2014-08-30 13:34:14 +00:00
|
|
|
|
gl_drawinfo->drawlists[GLDL_PLAINWALLS].DrawDecals();
|
2016-09-01 09:52:52 +00:00
|
|
|
|
if (gl.legacyMode)
|
2016-04-26 11:50:05 +00:00
|
|
|
|
{
|
|
|
|
|
// also process the render lists with walls and dynamic lights
|
2016-08-24 07:27:14 +00:00
|
|
|
|
gl_drawinfo->dldrawlists[GLLDL_WALLS_PLAIN].DrawDecals();
|
|
|
|
|
gl_drawinfo->dldrawlists[GLLDL_WALLS_FOG].DrawDecals();
|
2016-04-26 11:50:05 +00:00
|
|
|
|
}
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
gl_RenderState.SetTextureMode(TM_MODULATE);
|
|
|
|
|
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glDepthMask(true);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Push bleeding floor/ceiling textures back a little in the z-buffer
|
|
|
|
|
// so they don't interfere with overlapping mid textures.
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glPolygonOffset(1.0f, 128.0f);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2014-08-19 12:18:21 +00:00
|
|
|
|
// Part 5: flood all the gaps with the back sector's flat texture
|
|
|
|
|
// This will always be drawn like GLDL_PLAIN, depending on the fog settings
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glDepthMask(false); // don't write to Z-buffer!
|
2013-08-18 13:41:52 +00:00
|
|
|
|
gl_RenderState.EnableFog(true);
|
2014-07-14 19:14:43 +00:00
|
|
|
|
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
|
2013-08-18 13:41:52 +00:00
|
|
|
|
gl_RenderState.BlendFunc(GL_ONE,GL_ZERO);
|
|
|
|
|
gl_drawinfo->DrawUnhandledMissingTextures();
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glDepthMask(true);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glPolygonOffset(0.0f, 0.0f);
|
|
|
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
RenderAll.Unclock();
|
2014-08-30 12:33:06 +00:00
|
|
|
|
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// RenderTranslucent
|
|
|
|
|
//
|
|
|
|
|
// Draws the current draw lists for the non GLSL renderer
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void FGLRenderer::RenderTranslucent()
|
|
|
|
|
{
|
|
|
|
|
RenderAll.Clock();
|
|
|
|
|
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glDepthMask(false);
|
2016-04-02 21:17:16 +00:00
|
|
|
|
gl_RenderState.SetCameraPos(ViewPos.X, ViewPos.Y, ViewPos.Z);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
// final pass: translucent stuff
|
2014-07-14 19:14:43 +00:00
|
|
|
|
gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_sprite_threshold);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
|
|
gl_RenderState.EnableBrightmap(true);
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_TRANSLUCENTBORDER].Draw(GLPASS_TRANSLUCENT);
|
|
|
|
|
gl_drawinfo->drawlists[GLDL_TRANSLUCENT].DrawSorted();
|
|
|
|
|
gl_RenderState.EnableBrightmap(false);
|
|
|
|
|
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glDepthMask(true);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2014-07-14 19:14:43 +00:00
|
|
|
|
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.5f);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
RenderAll.Unclock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// gl_drawscene - this function renders the scene from the current
|
|
|
|
|
// viewpoint, including mirrors and skyboxes and other portals
|
|
|
|
|
// It is assumed that the GLPortal::EndFrame returns with the
|
|
|
|
|
// stencil, z-buffer and the projection matrix intact!
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2016-06-18 10:14:20 +00:00
|
|
|
|
void FGLRenderer::DrawScene(int drawmode)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
|
|
|
|
static int recursion=0;
|
2016-09-24 18:09:40 +00:00
|
|
|
|
static int ssao_portals_available = 0;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2016-10-22 19:35:49 +00:00
|
|
|
|
bool applySSAO = false;
|
2016-10-21 22:09:06 +00:00
|
|
|
|
if (drawmode == DM_MAINVIEW)
|
2016-10-22 19:35:49 +00:00
|
|
|
|
{
|
|
|
|
|
ssao_portals_available = gl_ssao_portals;
|
|
|
|
|
applySSAO = true;
|
|
|
|
|
}
|
2016-10-21 22:09:06 +00:00
|
|
|
|
else if (drawmode == DM_OFFSCREEN)
|
2016-10-22 19:35:49 +00:00
|
|
|
|
{
|
2016-10-21 22:09:06 +00:00
|
|
|
|
ssao_portals_available = 0;
|
2016-10-22 19:35:49 +00:00
|
|
|
|
}
|
|
|
|
|
else if (ssao_portals_available > 0)
|
|
|
|
|
{
|
|
|
|
|
applySSAO = true;
|
|
|
|
|
ssao_portals_available--;
|
|
|
|
|
}
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2016-06-18 10:14:20 +00:00
|
|
|
|
if (camera != nullptr)
|
|
|
|
|
{
|
|
|
|
|
ActorRenderFlags savedflags = camera->renderflags;
|
|
|
|
|
CreateScene();
|
|
|
|
|
camera->renderflags = savedflags;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
CreateScene();
|
|
|
|
|
}
|
2016-04-28 23:48:06 +00:00
|
|
|
|
GLRenderer->mClipPortal = NULL; // this must be reset before any portal recursion takes place.
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
RenderScene(recursion);
|
|
|
|
|
|
2016-10-22 19:35:49 +00:00
|
|
|
|
if (applySSAO && gl_RenderState.GetPassType() == GBUFFER_PASS)
|
2016-09-21 00:04:56 +00:00
|
|
|
|
{
|
2016-10-21 22:09:06 +00:00
|
|
|
|
gl_RenderState.EnableDrawBuffers(1);
|
2016-09-21 00:04:56 +00:00
|
|
|
|
AmbientOccludeScene();
|
2016-09-22 01:49:19 +00:00
|
|
|
|
mBuffers->BindSceneFB(true);
|
2016-10-21 22:09:06 +00:00
|
|
|
|
gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount());
|
2016-09-21 00:04:56 +00:00
|
|
|
|
gl_RenderState.Apply();
|
2016-09-22 01:49:19 +00:00
|
|
|
|
gl_RenderState.ApplyMatrices();
|
2016-09-21 00:04:56 +00:00
|
|
|
|
}
|
2016-09-03 02:29:50 +00:00
|
|
|
|
|
2013-06-23 07:49:34 +00:00
|
|
|
|
// Handle all portals after rendering the opaque objects but before
|
|
|
|
|
// doing all translucent stuff
|
|
|
|
|
recursion++;
|
|
|
|
|
GLPortal::EndFrame();
|
|
|
|
|
recursion--;
|
|
|
|
|
RenderTranslucent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-04-26 18:02:57 +00:00
|
|
|
|
void gl_FillScreen()
|
2014-05-11 12:46:37 +00:00
|
|
|
|
{
|
2014-07-14 19:14:43 +00:00
|
|
|
|
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
|
2014-05-11 12:46:37 +00:00
|
|
|
|
gl_RenderState.EnableTexture(false);
|
2014-05-11 20:57:42 +00:00
|
|
|
|
gl_RenderState.Apply();
|
2016-08-08 12:24:48 +00:00
|
|
|
|
// The fullscreen quad is stored at index 4 in the main vertex buffer.
|
2016-08-29 08:43:03 +00:00
|
|
|
|
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, FFlatVertexBuffer::FULLSCREEN_INDEX, 4);
|
2014-05-11 12:46:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-23 07:49:34 +00:00
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// Draws a blend over the entire view
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
void FGLRenderer::DrawBlend(sector_t * viewsector)
|
|
|
|
|
{
|
|
|
|
|
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 (!gl_fixedcolormap)
|
|
|
|
|
{
|
|
|
|
|
if (!viewsector->e->XFloor.ffloors.Size())
|
|
|
|
|
{
|
|
|
|
|
if (viewsector->heightsec && !(viewsector->MoreFlags&SECF_IGNOREHEIGHTSEC))
|
|
|
|
|
{
|
2014-05-11 12:46:37 +00:00
|
|
|
|
switch (in_area)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
|
|
|
|
default:
|
2014-05-11 12:46:37 +00:00
|
|
|
|
case area_normal: blendv = viewsector->heightsec->midmap; break;
|
|
|
|
|
case area_above: blendv = viewsector->heightsec->topmap; break;
|
|
|
|
|
case area_below: blendv = viewsector->heightsec->bottommap; break;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TArray<lightlist_t> & lightlist = viewsector->e->XFloor.lightlist;
|
|
|
|
|
|
2014-05-11 12:46:37 +00:00
|
|
|
|
for (unsigned int i = 0; i < lightlist.Size(); i++)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2016-04-02 21:17:16 +00:00
|
|
|
|
double lightbottom;
|
2014-05-11 12:46:37 +00:00
|
|
|
|
if (i < lightlist.Size() - 1)
|
2016-04-02 21:17:16 +00:00
|
|
|
|
lightbottom = lightlist[i + 1].plane.ZatPoint(ViewPos);
|
2014-05-11 12:46:37 +00:00
|
|
|
|
else
|
2016-04-02 21:17:16 +00:00
|
|
|
|
lightbottom = viewsector->floorplane.ZatPoint(ViewPos);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2016-04-02 21:17:16 +00:00
|
|
|
|
if (lightbottom < ViewPos.Z && (!lightlist[i].caster || !(lightlist[i].caster->flags&FF_FADEWALLS)))
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
|
|
|
|
// 3d floor 'fog' is rendered as a blending value
|
2014-05-11 12:46:37 +00:00
|
|
|
|
blendv = lightlist[i].blend;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
// If this is the same as the sector's it doesn't apply!
|
2014-05-11 12:46:37 +00:00
|
|
|
|
if (blendv == viewsector->ColorMap->Fade) blendv = 0;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
// a little hack to make this work for Legacy maps.
|
2014-05-11 12:46:37 +00:00
|
|
|
|
if (blendv.a == 0 && blendv != 0) blendv.a = 128;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-11 12:46:37 +00:00
|
|
|
|
if (blendv.a == 0)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2014-05-11 12:46:37 +00:00
|
|
|
|
blendv = R_BlendForColormap(blendv);
|
|
|
|
|
if (blendv.a == 255)
|
|
|
|
|
{
|
|
|
|
|
// The calculated average is too dark so brighten it according to the palettes's overall brightness
|
|
|
|
|
int maxcol = MAX<int>(MAX<int>(framebuffer->palette_brightness, blendv.r), MAX<int>(blendv.g, blendv.b));
|
|
|
|
|
blendv.r = blendv.r * 255 / maxcol;
|
|
|
|
|
blendv.g = blendv.g * 255 / maxcol;
|
|
|
|
|
blendv.b = blendv.b * 255 / maxcol;
|
|
|
|
|
}
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-05-11 12:46:37 +00:00
|
|
|
|
if (blendv.a == 255)
|
|
|
|
|
{
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2014-05-11 12:46:37 +00:00
|
|
|
|
extra_red = blendv.r / 255.0f;
|
|
|
|
|
extra_green = blendv.g / 255.0f;
|
|
|
|
|
extra_blue = blendv.b / 255.0f;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2014-05-11 12:46:37 +00:00
|
|
|
|
// If this is a multiplicative blend do it separately and add the additive ones on top of it.
|
|
|
|
|
blendv = 0;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2014-05-11 12:46:37 +00:00
|
|
|
|
// black multiplicative blends are ignored
|
|
|
|
|
if (extra_red || extra_green || extra_blue)
|
|
|
|
|
{
|
|
|
|
|
gl_RenderState.BlendFunc(GL_DST_COLOR, GL_ZERO);
|
2014-05-11 20:57:42 +00:00
|
|
|
|
gl_RenderState.SetColor(extra_red, extra_green, extra_blue, 1.0f);
|
2016-04-26 18:02:57 +00:00
|
|
|
|
gl_FillScreen();
|
2014-05-11 12:46:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (blendv.a)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2016-11-11 10:08:47 +00:00
|
|
|
|
// [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);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-05-11 12:46:37 +00:00
|
|
|
|
|
2013-06-23 07:49:34 +00:00
|
|
|
|
// This mostly duplicates the code in shared_sbar.cpp
|
|
|
|
|
// When I was writing this the original was called too late so that I
|
|
|
|
|
// couldn't get the blend in time. However, since then I made some changes
|
|
|
|
|
// here that would get lost if I switched back so I won't do it.
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-22 08:54:16 +00:00
|
|
|
|
gl_RenderState.SetTextureMode(TM_MODULATE);
|
2016-09-01 15:14:51 +00:00
|
|
|
|
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
if (blend[3]>0.0f)
|
|
|
|
|
{
|
2014-05-11 20:57:42 +00:00
|
|
|
|
gl_RenderState.SetColor(blend[0], blend[1], blend[2], blend[3]);
|
2016-04-26 18:02:57 +00:00
|
|
|
|
gl_FillScreen();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
2016-09-01 15:38:17 +00:00
|
|
|
|
gl_RenderState.ResetColor();
|
|
|
|
|
gl_RenderState.EnableTexture(true);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Draws player sprites and color blend
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FGLRenderer::EndDrawScene(sector_t * viewsector)
|
|
|
|
|
{
|
2014-07-26 20:26:17 +00:00
|
|
|
|
gl_RenderState.EnableFog(false);
|
|
|
|
|
|
2013-06-23 07:49:34 +00:00
|
|
|
|
// [BB] HUD models need to be rendered here. Make sure that
|
|
|
|
|
// DrawPlayerSprites is only called once. Either to draw
|
|
|
|
|
// HUD models or to draw the weapon sprites.
|
|
|
|
|
const bool renderHUDModel = gl_IsHUDModelForPlayerAvailable( players[consoleplayer].camera->player );
|
|
|
|
|
if ( renderHUDModel )
|
|
|
|
|
{
|
|
|
|
|
// [BB] The HUD model should be drawn over everything else already drawn.
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
DrawPlayerSprites (viewsector, true);
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glDisable(GL_STENCIL_TEST);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
framebuffer->Begin2D(false);
|
|
|
|
|
|
2016-07-26 19:27:02 +00:00
|
|
|
|
Reset3DViewport();
|
2016-09-20 00:06:52 +00:00
|
|
|
|
|
2013-06-23 07:49:34 +00:00
|
|
|
|
// [BB] Only draw the sprites if we didn't render a HUD model before.
|
|
|
|
|
if ( renderHUDModel == false )
|
|
|
|
|
{
|
|
|
|
|
DrawPlayerSprites (viewsector, false);
|
|
|
|
|
}
|
2016-09-01 09:52:52 +00:00
|
|
|
|
if (gl.legacyMode)
|
2016-04-26 18:02:57 +00:00
|
|
|
|
{
|
|
|
|
|
gl_RenderState.DrawColormapOverlay();
|
2016-09-01 15:14:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl_RenderState.SetFixedColormap(CM_DEFAULT);
|
|
|
|
|
gl_RenderState.SetSoftLightLevel(-1);
|
|
|
|
|
DrawTargeterSprites();
|
2016-09-02 22:43:18 +00:00
|
|
|
|
if (!FGLRenderBuffers::IsEnabled())
|
2016-09-01 15:14:51 +00:00
|
|
|
|
{
|
|
|
|
|
DrawBlend(viewsector);
|
2016-04-26 18:02:57 +00:00
|
|
|
|
}
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
// Restore standard rendering state
|
|
|
|
|
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
2014-05-11 20:57:42 +00:00
|
|
|
|
gl_RenderState.ResetColor();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
gl_RenderState.EnableTexture(true);
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// R_RenderView - renders one view - either the screen or a camera texture
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void FGLRenderer::ProcessScene(bool toscreen)
|
|
|
|
|
{
|
|
|
|
|
FDrawInfo::StartDrawInfo();
|
|
|
|
|
iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0;
|
|
|
|
|
GLPortal::BeginScene();
|
|
|
|
|
|
2016-04-02 21:17:16 +00:00
|
|
|
|
int mapsection = R_PointInSubsector(ViewPos)->mapsection;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
memset(¤tmapsection[0], 0, currentmapsection.Size());
|
|
|
|
|
currentmapsection[mapsection>>3] |= 1 << (mapsection & 7);
|
2016-06-18 10:14:20 +00:00
|
|
|
|
DrawScene(toscreen ? DM_MAINVIEW : DM_OFFSCREEN);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
FDrawInfo::EndDrawInfo();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// gl_SetFixedColormap
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void FGLRenderer::SetFixedColormap (player_t *player)
|
|
|
|
|
{
|
|
|
|
|
gl_fixedcolormap=CM_DEFAULT;
|
|
|
|
|
|
|
|
|
|
// check for special colormaps
|
|
|
|
|
player_t * cplayer = player->camera->player;
|
|
|
|
|
if (cplayer)
|
|
|
|
|
{
|
2013-07-22 15:30:08 +00:00
|
|
|
|
if (cplayer->extralight == INT_MIN)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
|
|
|
|
gl_fixedcolormap=CM_FIRSTSPECIALCOLORMAP + INVERSECOLORMAP;
|
|
|
|
|
extralight=0;
|
|
|
|
|
}
|
|
|
|
|
else if (cplayer->fixedcolormap != NOFIXEDCOLORMAP)
|
|
|
|
|
{
|
|
|
|
|
gl_fixedcolormap = CM_FIRSTSPECIALCOLORMAP + cplayer->fixedcolormap;
|
|
|
|
|
}
|
|
|
|
|
else if (cplayer->fixedlightlevel != -1)
|
|
|
|
|
{
|
2017-01-15 22:21:38 +00:00
|
|
|
|
auto torchtype = PClass::FindActor(NAME_PowerTorch);
|
|
|
|
|
auto litetype = PClass::FindActor(NAME_PowerLightAmp);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
for(AInventory * in = cplayer->mo->Inventory; in; in = in->Inventory)
|
|
|
|
|
{
|
2017-01-19 22:42:12 +00:00
|
|
|
|
PalEntry color = in->CallGetBlend ();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
// Need special handling for light amplifiers
|
2017-01-15 22:21:38 +00:00
|
|
|
|
if (in->IsKindOf(torchtype))
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
|
|
|
|
gl_fixedcolormap = cplayer->fixedlightlevel + CM_TORCH;
|
|
|
|
|
}
|
2017-01-15 22:21:38 +00:00
|
|
|
|
else if (in->IsKindOf(litetype))
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
|
|
|
|
gl_fixedcolormap = CM_LITE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-05-11 19:47:54 +00:00
|
|
|
|
gl_RenderState.SetFixedColormap(gl_fixedcolormap);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Renders one viewpoint in a scene
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen)
|
|
|
|
|
{
|
2016-09-05 07:07:34 +00:00
|
|
|
|
sector_t * lviewsector;
|
2016-08-21 23:52:51 +00:00
|
|
|
|
mSceneClearColor[0] = 0.0f;
|
|
|
|
|
mSceneClearColor[1] = 0.0f;
|
|
|
|
|
mSceneClearColor[2] = 0.0f;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
R_SetupFrame (camera);
|
|
|
|
|
SetViewArea();
|
2014-12-31 11:04:55 +00:00
|
|
|
|
|
|
|
|
|
// We have to scale the pitch to account for the pixel stretching, because the playsim doesn't know about this and treats it as 1:1.
|
2016-04-28 15:55:58 +00:00
|
|
|
|
double radPitch = ViewPitch.Normalized180().Radians();
|
2014-12-31 11:04:55 +00:00
|
|
|
|
double angx = cos(radPitch);
|
|
|
|
|
double angy = sin(radPitch) * glset.pixelstretch;
|
|
|
|
|
double alen = sqrt(angx*angx + angy*angy);
|
|
|
|
|
|
|
|
|
|
mAngles.Pitch = (float)RAD2DEG(asin(angy / alen));
|
2016-04-25 19:12:16 +00:00
|
|
|
|
mAngles.Roll.Degrees = ViewRoll.Degrees;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
// Scroll the sky
|
|
|
|
|
mSky1Pos = (float)fmod(gl_frameMS * level.skyspeed1, 1024.f) * 90.f/256.f;
|
|
|
|
|
mSky2Pos = (float)fmod(gl_frameMS * level.skyspeed2, 1024.f) * 90.f/256.f;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (camera->player && camera->player-players==consoleplayer &&
|
|
|
|
|
((camera->player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0)) && camera==camera->player->mo)
|
|
|
|
|
{
|
|
|
|
|
mViewActor=NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mViewActor=camera;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-03 23:45:44 +00:00
|
|
|
|
// 'viewsector' will not survive the rendering so it cannot be used anymore below.
|
2016-09-05 07:07:34 +00:00
|
|
|
|
lviewsector = viewsector;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2015-10-31 00:51:35 +00:00
|
|
|
|
// Render (potentially) multiple views for stereo 3d
|
|
|
|
|
float viewShift[3];
|
2016-09-03 14:54:17 +00:00
|
|
|
|
const s3d::Stereo3DMode& stereo3dMode = mainview && toscreen? s3d::Stereo3DMode::getCurrentMode() : s3d::Stereo3DMode::getMonoMode();
|
2015-10-31 00:51:35 +00:00
|
|
|
|
stereo3dMode.SetUp();
|
2015-12-31 14:36:37 +00:00
|
|
|
|
for (int eye_ix = 0; eye_ix < stereo3dMode.eye_count(); ++eye_ix)
|
2015-10-31 00:51:35 +00:00
|
|
|
|
{
|
2015-12-31 14:36:37 +00:00
|
|
|
|
const s3d::EyePose * eye = stereo3dMode.getEyePose(eye_ix);
|
|
|
|
|
eye->SetUp();
|
2016-07-26 19:27:02 +00:00
|
|
|
|
SetOutputViewport(bounds);
|
2016-07-31 14:23:21 +00:00
|
|
|
|
Set3DViewport(mainview);
|
2016-07-28 22:36:43 +00:00
|
|
|
|
mDrawingScene2D = true;
|
2015-10-31 00:51:35 +00:00
|
|
|
|
mCurrentFoV = fov;
|
|
|
|
|
// Stereo mode specific perspective projection
|
2015-12-31 14:36:37 +00:00
|
|
|
|
SetProjection( eye->GetProjection(fov, ratio, fovratio) );
|
2015-10-31 01:29:36 +00:00
|
|
|
|
// SetProjection(fov, ratio, fovratio); // switch to perspective mode and set up clipper
|
2016-04-02 21:17:16 +00:00
|
|
|
|
SetViewAngle(ViewAngle);
|
2016-05-01 15:43:30 +00:00
|
|
|
|
// Stereo mode specific viewpoint adjustment - temporarily shifts global ViewPos
|
2016-03-21 01:57:02 +00:00
|
|
|
|
eye->GetViewShift(GLRenderer->mAngles.Yaw.Degrees, viewShift);
|
2015-10-31 00:51:35 +00:00
|
|
|
|
s3d::ScopedViewShifter viewShifter(viewShift);
|
2016-04-02 21:17:16 +00:00
|
|
|
|
SetViewMatrix(ViewPos.X, ViewPos.Y, ViewPos.Z, false, false);
|
2015-10-31 00:51:35 +00:00
|
|
|
|
gl_RenderState.ApplyMatrices();
|
|
|
|
|
|
|
|
|
|
clipper.Clear();
|
|
|
|
|
angle_t a1 = FrustumAngle();
|
2016-04-02 21:17:16 +00:00
|
|
|
|
clipper.SafeAddClipRangeRealAngles(ViewAngle.BAMs() + a1, ViewAngle.BAMs() - a1);
|
2015-10-31 00:51:35 +00:00
|
|
|
|
|
|
|
|
|
ProcessScene(toscreen);
|
2016-09-20 00:06:52 +00:00
|
|
|
|
if (mainview && toscreen) EndDrawScene(lviewsector); // do not call this for camera textures.
|
2016-09-01 09:52:52 +00:00
|
|
|
|
if (mainview && FGLRenderBuffers::IsEnabled())
|
2016-07-29 19:31:20 +00:00
|
|
|
|
{
|
2016-08-29 11:10:22 +00:00
|
|
|
|
PostProcessScene();
|
2016-09-04 01:21:47 +00:00
|
|
|
|
|
|
|
|
|
// This should be done after postprocessing, not before.
|
|
|
|
|
mBuffers->BindCurrentFB();
|
|
|
|
|
glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
|
2017-02-05 09:26:17 +00:00
|
|
|
|
|
|
|
|
|
if (!toscreen)
|
|
|
|
|
{
|
|
|
|
|
gl_RenderState.mViewMatrix.loadIdentity();
|
|
|
|
|
gl_RenderState.mProjectionMatrix.ortho(mScreenViewport.left, mScreenViewport.width, mScreenViewport.height, mScreenViewport.top, -1.0f, 1.0f);
|
|
|
|
|
gl_RenderState.ApplyMatrices();
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-05 07:07:34 +00:00
|
|
|
|
DrawBlend(lviewsector);
|
2016-07-29 19:31:20 +00:00
|
|
|
|
}
|
2016-07-28 22:36:43 +00:00
|
|
|
|
mDrawingScene2D = false;
|
2016-09-08 19:24:25 +00:00
|
|
|
|
if (!stereo3dMode.IsMono() && FGLRenderBuffers::IsEnabled())
|
2016-09-07 19:52:43 +00:00
|
|
|
|
mBuffers->BlitToEyeTexture(eye_ix);
|
2015-12-31 14:36:37 +00:00
|
|
|
|
eye->TearDown();
|
2015-10-31 00:51:35 +00:00
|
|
|
|
}
|
|
|
|
|
stereo3dMode.TearDown();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
gl_frameCount++; // This counter must be increased right before the interpolations are restored.
|
|
|
|
|
interpolator.RestoreInterpolations ();
|
2016-09-05 07:07:34 +00:00
|
|
|
|
return lviewsector;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// renders the view
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void FGLRenderer::RenderView (player_t* player)
|
|
|
|
|
{
|
|
|
|
|
OpenGLFrameBuffer* GLTarget = static_cast<OpenGLFrameBuffer*>(screen);
|
|
|
|
|
AActor *&LastCamera = GLTarget->LastCamera;
|
|
|
|
|
|
2014-08-30 12:33:06 +00:00
|
|
|
|
checkBenchActive();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
if (player->camera != LastCamera)
|
|
|
|
|
{
|
|
|
|
|
// If the camera changed don't interpolate
|
|
|
|
|
// Otherwise there will be some not so nice effects.
|
|
|
|
|
R_ResetViewInterpolation();
|
|
|
|
|
LastCamera=player->camera;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-10 19:47:07 +00:00
|
|
|
|
gl_RenderState.SetVertexBuffer(mVBO);
|
2014-05-10 23:23:27 +00:00
|
|
|
|
GLRenderer->mVBO->Reset();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
// reset statistics counters
|
|
|
|
|
ResetProfilingData();
|
|
|
|
|
|
|
|
|
|
// Get this before everything else
|
2016-03-30 18:01:44 +00:00
|
|
|
|
if (cl_capfps || r_NoInterpolate) r_TicFracF = 1.;
|
|
|
|
|
else r_TicFracF = I_GetTimeFrac (&r_FrameTime);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
gl_frameMS = I_MSTime();
|
|
|
|
|
|
|
|
|
|
P_FindParticleSubsectors ();
|
|
|
|
|
|
2016-09-01 09:52:52 +00:00
|
|
|
|
if (!gl.legacyMode) GLRenderer->mLights->Clear();
|
2014-09-21 19:01:11 +00:00
|
|
|
|
|
2016-03-05 11:46:47 +00: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;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
// prepare all camera textures that have been used in the last frame
|
|
|
|
|
FCanvasTextureInfo::UpdateAll();
|
2016-03-05 12:09:49 +00:00
|
|
|
|
NoInterpolateView = saved_niv;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// now render the main view
|
|
|
|
|
float fovratio;
|
2016-09-12 18:29:26 +00:00
|
|
|
|
float ratio = WidescreenRatio;
|
|
|
|
|
if (WidescreenRatio >= 1.3f)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2014-12-29 21:42:19 +00:00
|
|
|
|
fovratio = 1.333333f;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fovratio = ratio;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetFixedColormap (player);
|
|
|
|
|
|
|
|
|
|
// Check if there's some lights. If not some code can be skipped.
|
|
|
|
|
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
|
|
|
|
|
GLRenderer->mLightCount = ((it.Next()) != NULL);
|
|
|
|
|
|
2016-04-28 15:55:58 +00:00
|
|
|
|
sector_t * viewsector = RenderViewpoint(player->camera, NULL, FieldOfView.Degrees, ratio, fovratio, true, true);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
All.Unclock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
// Render the view to a savegame picture
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
2016-09-23 23:47:44 +00:00
|
|
|
|
void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
|
|
|
|
GL_IRECT bounds;
|
|
|
|
|
|
|
|
|
|
bounds.left=0;
|
|
|
|
|
bounds.top=0;
|
|
|
|
|
bounds.width=width;
|
|
|
|
|
bounds.height=height;
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glFlush();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
SetFixedColormap(player);
|
2014-05-10 23:23:27 +00:00
|
|
|
|
gl_RenderState.SetVertexBuffer(mVBO);
|
|
|
|
|
GLRenderer->mVBO->Reset();
|
2016-09-01 09:52:52 +00:00
|
|
|
|
if (!gl.legacyMode) GLRenderer->mLights->Clear();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
// Check if there's some lights. If not some code can be skipped.
|
|
|
|
|
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
|
|
|
|
|
GLRenderer->mLightCount = ((it.Next()) != NULL);
|
|
|
|
|
|
|
|
|
|
sector_t *viewsector = RenderViewpoint(players[consoleplayer].camera, &bounds,
|
2016-04-28 15:55:58 +00:00
|
|
|
|
FieldOfView.Degrees, 1.6f, 1.6f, true, false);
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glDisable(GL_STENCIL_TEST);
|
2014-05-11 19:47:54 +00:00
|
|
|
|
gl_RenderState.SetFixedColormap(CM_DEFAULT);
|
2014-05-12 12:45:41 +00:00
|
|
|
|
gl_RenderState.SetSoftLightLevel(-1);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
screen->Begin2D(false);
|
2016-09-02 22:43:18 +00:00
|
|
|
|
if (!FGLRenderBuffers::IsEnabled())
|
|
|
|
|
{
|
|
|
|
|
DrawBlend(viewsector);
|
|
|
|
|
}
|
2016-07-31 14:56:41 +00:00
|
|
|
|
CopyToBackbuffer(&bounds, false);
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glFlush();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
byte * scr = (byte *)M_Malloc(width * height * 3);
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glReadPixels(0,0,width, height,GL_RGB,GL_UNSIGNED_BYTE,scr);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
M_CreatePNG (file, scr + ((height-1) * width * 3), NULL, SS_RGB, width, height, -width*3);
|
|
|
|
|
M_Free(scr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
struct FGLInterface : public FRenderer
|
|
|
|
|
{
|
2016-09-22 07:06:21 +00:00
|
|
|
|
bool UsesColormap() const override;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
void PrecacheTexture(FTexture *tex, int cache);
|
2016-05-02 23:00:52 +00:00
|
|
|
|
void PrecacheSprite(FTexture *tex, SpriteHits &hits);
|
2016-09-22 07:06:21 +00:00
|
|
|
|
void Precache(BYTE *texhitlist, TMap<PClassActor*, bool> &actorhitlist) override;
|
|
|
|
|
void RenderView(player_t *player) override;
|
2016-09-23 23:47:44 +00:00
|
|
|
|
void WriteSavePic (player_t *player, FileWriter *file, int width, int height) override;
|
2016-09-22 07:06:21 +00:00
|
|
|
|
void StateChanged(AActor *actor) override;
|
2016-09-23 23:47:44 +00:00
|
|
|
|
void StartSerialize(FSerializer &arc) override;
|
|
|
|
|
void EndSerialize(FSerializer &arc) override;
|
2016-09-22 07:06:21 +00:00
|
|
|
|
void RenderTextureView (FCanvasTexture *self, AActor *viewpoint, int fov) override;
|
|
|
|
|
sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) override;
|
|
|
|
|
void SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog) override;
|
|
|
|
|
void PreprocessLevel() override;
|
|
|
|
|
void CleanLevelData() override;
|
|
|
|
|
bool RequireGLNodes() override;
|
|
|
|
|
|
|
|
|
|
int GetMaxViewPitch(bool down) override;
|
|
|
|
|
void ClearBuffer(int color) override;
|
|
|
|
|
void Init() override;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
// The GL renderer has no use for colormaps so let's
|
|
|
|
|
// not create them and save us some time.
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
bool FGLInterface::UsesColormap() const
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// DFrameBuffer :: PrecacheTexture
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
|
|
void FGLInterface::PrecacheTexture(FTexture *tex, int cache)
|
|
|
|
|
{
|
2016-05-03 16:14:16 +00:00
|
|
|
|
if (cache & (FTextureManager::HIT_Wall | FTextureManager::HIT_Flat | FTextureManager::HIT_Sky))
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2016-05-03 16:14:16 +00:00
|
|
|
|
FMaterial * gltex = FMaterial::ValidateTexture(tex, false);
|
|
|
|
|
if (gltex) gltex->Precache();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
2017-02-10 13:18:00 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// make sure that software pixel buffers do not stick around for unneeded textures.
|
|
|
|
|
tex->Unload();
|
|
|
|
|
}
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-02 23:00:52 +00:00
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// DFrameBuffer :: PrecacheSprite
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
|
|
void FGLInterface::PrecacheSprite(FTexture *tex, SpriteHits &hits)
|
|
|
|
|
{
|
2016-05-03 16:14:16 +00:00
|
|
|
|
FMaterial * gltex = FMaterial::ValidateTexture(tex, true);
|
|
|
|
|
if (gltex) gltex->PrecacheList(hits);
|
2016-05-02 23:00:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// DFrameBuffer :: Precache
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
2016-05-01 22:13:00 +00:00
|
|
|
|
void FGLInterface::Precache(BYTE *texhitlist, TMap<PClassActor*, bool> &actorhitlist)
|
|
|
|
|
{
|
2016-05-02 23:00:52 +00:00
|
|
|
|
SpriteHits *spritelist = new SpriteHits[sprites.Size()];
|
|
|
|
|
SpriteHits **spritehitlist = new SpriteHits*[TexMan.NumTextures()];
|
2016-05-01 22:13:00 +00:00
|
|
|
|
TMap<PClassActor*, bool>::Iterator it(actorhitlist);
|
|
|
|
|
TMap<PClassActor*, bool>::Pair *pair;
|
2016-05-03 16:14:16 +00:00
|
|
|
|
BYTE *modellist = new BYTE[Models.Size()];
|
|
|
|
|
memset(modellist, 0, Models.Size());
|
2016-05-02 23:00:52 +00:00
|
|
|
|
memset(spritehitlist, 0, sizeof(SpriteHits**) * TexMan.NumTextures());
|
2016-05-01 22:13:00 +00:00
|
|
|
|
|
|
|
|
|
// this isn't done by the main code so it needs to be done here first:
|
|
|
|
|
// check skybox textures and mark the separate faces as used
|
|
|
|
|
for (int i = 0; i<TexMan.NumTextures(); i++)
|
|
|
|
|
{
|
|
|
|
|
// HIT_Wall must be checked for MBF-style sky transfers.
|
|
|
|
|
if (texhitlist[i] & (FTextureManager::HIT_Sky | FTextureManager::HIT_Wall))
|
|
|
|
|
{
|
|
|
|
|
FTexture *tex = TexMan.ByIndex(i);
|
|
|
|
|
if (tex->gl_info.bSkybox)
|
|
|
|
|
{
|
|
|
|
|
FSkyBox *sb = static_cast<FSkyBox*>(tex);
|
|
|
|
|
for (int i = 0; i<6; i++)
|
|
|
|
|
{
|
|
|
|
|
if (sb->faces[i])
|
|
|
|
|
{
|
|
|
|
|
int index = sb->faces[i]->id.GetIndex();
|
|
|
|
|
texhitlist[index] |= FTextureManager::HIT_Flat;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 16:14:16 +00:00
|
|
|
|
// Check all used actors.
|
|
|
|
|
// 1. mark all sprites associated with its states
|
|
|
|
|
// 2. mark all model data and skins associated with its states
|
2016-05-01 22:13:00 +00:00
|
|
|
|
while (it.NextPair(pair))
|
|
|
|
|
{
|
|
|
|
|
PClassActor *cls = pair->Key;
|
2016-05-02 23:00:52 +00:00
|
|
|
|
int gltrans = GLTranslationPalette::GetInternalTranslation(GetDefaultByType(cls)->Translation);
|
2016-05-01 22:13:00 +00:00
|
|
|
|
|
|
|
|
|
for (int i = 0; i < cls->NumOwnedStates; i++)
|
|
|
|
|
{
|
2016-05-02 23:00:52 +00:00
|
|
|
|
spritelist[cls->OwnedStates[i].sprite].Insert(gltrans, true);
|
2016-05-03 16:14:16 +00:00
|
|
|
|
FSpriteModelFrame * smf = gl_FindModelFrame(cls, cls->OwnedStates[i].sprite, cls->OwnedStates[i].Frame, false);
|
|
|
|
|
if (smf != NULL)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < MAX_MODELS_PER_FRAME; i++)
|
|
|
|
|
{
|
|
|
|
|
if (smf->skinIDs[i].isValid())
|
|
|
|
|
{
|
|
|
|
|
texhitlist[smf->skinIDs[i].GetIndex()] |= FTexture::TEX_Flat;
|
|
|
|
|
}
|
|
|
|
|
else if (smf->modelIDs[i] != -1)
|
|
|
|
|
{
|
2016-07-16 04:15:14 +00:00
|
|
|
|
Models[smf->modelIDs[i]]->PushSpriteMDLFrame(smf, i);
|
2016-05-03 16:14:16 +00:00
|
|
|
|
Models[smf->modelIDs[i]]->AddSkins(texhitlist);
|
|
|
|
|
}
|
|
|
|
|
if (smf->modelIDs[i] != -1)
|
|
|
|
|
{
|
|
|
|
|
modellist[smf->modelIDs[i]] = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-01 22:13:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 16:14:16 +00:00
|
|
|
|
// mark all sprite textures belonging to the marked sprites.
|
2016-05-01 22:13:00 +00:00
|
|
|
|
for (int i = (int)(sprites.Size() - 1); i >= 0; i--)
|
|
|
|
|
{
|
2016-05-02 23:00:52 +00:00
|
|
|
|
if (spritelist[i].CountUsed())
|
2016-05-01 22:13:00 +00:00
|
|
|
|
{
|
|
|
|
|
int j, k;
|
|
|
|
|
for (j = 0; j < sprites[i].numframes; j++)
|
|
|
|
|
{
|
|
|
|
|
const spriteframe_t *frame = &SpriteFrames[sprites[i].spriteframes + j];
|
|
|
|
|
|
|
|
|
|
for (k = 0; k < 16; k++)
|
|
|
|
|
{
|
|
|
|
|
FTextureID pic = frame->Texture[k];
|
|
|
|
|
if (pic.isValid())
|
|
|
|
|
{
|
2016-05-02 23:00:52 +00:00
|
|
|
|
spritehitlist[pic.GetIndex()] = &spritelist[i];
|
2016-05-01 22:13:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 16:14:16 +00:00
|
|
|
|
// delete everything unused before creating any new resources to avoid memory usage peaks.
|
|
|
|
|
|
|
|
|
|
// delete unused models
|
|
|
|
|
for (unsigned i = 0; i < Models.Size(); i++)
|
|
|
|
|
{
|
|
|
|
|
if (!modellist[i]) Models[i]->DestroyVertexBuffer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// delete unused textures
|
2016-05-01 22:13:00 +00:00
|
|
|
|
int cnt = TexMan.NumTextures();
|
|
|
|
|
for (int i = cnt - 1; i >= 0; i--)
|
|
|
|
|
{
|
2016-05-03 16:14:16 +00:00
|
|
|
|
FTexture *tex = TexMan.ByIndex(i);
|
|
|
|
|
if (tex != nullptr)
|
|
|
|
|
{
|
|
|
|
|
if (!texhitlist[i])
|
|
|
|
|
{
|
|
|
|
|
if (tex->gl_info.Material[0]) tex->gl_info.Material[0]->Clean(true);
|
|
|
|
|
}
|
|
|
|
|
if (spritehitlist[i] == nullptr || (*spritehitlist[i]).CountUsed() == 0)
|
|
|
|
|
{
|
|
|
|
|
if (tex->gl_info.Material[1]) tex->gl_info.Material[1]->Clean(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gl_precache)
|
|
|
|
|
{
|
|
|
|
|
// cache all used textures
|
|
|
|
|
for (int i = cnt - 1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
FTexture *tex = TexMan.ByIndex(i);
|
|
|
|
|
if (tex != nullptr)
|
|
|
|
|
{
|
|
|
|
|
PrecacheTexture(tex, texhitlist[i]);
|
|
|
|
|
if (spritehitlist[i] != nullptr && (*spritehitlist[i]).CountUsed() > 0)
|
|
|
|
|
{
|
|
|
|
|
PrecacheSprite(tex, *spritehitlist[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// cache all used models
|
|
|
|
|
for (unsigned i = 0; i < Models.Size(); i++)
|
|
|
|
|
{
|
|
|
|
|
if (modellist[i])
|
|
|
|
|
Models[i]->BuildVertexBuffer();
|
|
|
|
|
}
|
2016-05-01 22:13:00 +00:00
|
|
|
|
}
|
2016-05-03 16:14:16 +00:00
|
|
|
|
|
2016-05-02 23:00:52 +00:00
|
|
|
|
delete[] spritehitlist;
|
|
|
|
|
delete[] spritelist;
|
2016-05-03 16:14:16 +00:00
|
|
|
|
delete[] modellist;
|
2016-05-01 22:13:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-06-23 07:49:34 +00:00
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
// DFrameBuffer :: StateChanged
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
|
|
void FGLInterface::StateChanged(AActor *actor)
|
|
|
|
|
{
|
|
|
|
|
gl_SetActorLights(actor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
// notify the renderer that serialization of the curent level is about to start/end
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
2016-09-23 23:47:44 +00:00
|
|
|
|
void FGLInterface::StartSerialize(FSerializer &arc)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2016-09-23 23:47:44 +00:00
|
|
|
|
if (arc.BeginObject("glinfo"))
|
|
|
|
|
{
|
|
|
|
|
arc("fogdensity", fogdensity)
|
|
|
|
|
("outsidefogdensity", outsidefogdensity)
|
|
|
|
|
("skyfog", skyfog)
|
|
|
|
|
.EndObject();
|
|
|
|
|
}
|
2014-11-16 08:08:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-23 23:47:44 +00:00
|
|
|
|
void FGLInterface::EndSerialize(FSerializer &arc)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2016-10-16 07:12:43 +00:00
|
|
|
|
if (arc.isReading())
|
|
|
|
|
{
|
|
|
|
|
gl_RecreateAllAttachedLights();
|
|
|
|
|
gl_InitPortals();
|
|
|
|
|
}
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
// Get max. view angle (renderer specific information so it goes here now)
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
EXTERN_CVAR(Float, maxviewpitch)
|
|
|
|
|
|
|
|
|
|
int FGLInterface::GetMaxViewPitch(bool down)
|
|
|
|
|
{
|
|
|
|
|
return int(maxviewpitch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
void FGLInterface::ClearBuffer(int color)
|
|
|
|
|
{
|
|
|
|
|
PalEntry pe = GPalette.BaseColors[color];
|
2016-08-21 23:52:51 +00:00
|
|
|
|
GLRenderer->mSceneClearColor[0] = pe.r / 255.f;
|
|
|
|
|
GLRenderer->mSceneClearColor[1] = pe.g / 255.f;
|
|
|
|
|
GLRenderer->mSceneClearColor[2] = pe.b / 255.f;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
// Render the view to a savegame picture
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
2016-09-23 23:47:44 +00:00
|
|
|
|
void FGLInterface::WriteSavePic (player_t *player, FileWriter *file, int width, int height)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
|
|
|
|
GLRenderer->WriteSavePic(player, file, width, height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
void FGLInterface::RenderView(player_t *player)
|
|
|
|
|
{
|
|
|
|
|
GLRenderer->RenderView(player);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
void FGLInterface::Init()
|
|
|
|
|
{
|
|
|
|
|
gl_ParseDefs();
|
2014-06-19 13:22:00 +00:00
|
|
|
|
gl_InitData();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
// Camera texture rendering
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
CVAR(Bool, gl_usefb, false , CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
|
extern TexFilter_s TexFilter[];
|
|
|
|
|
|
|
|
|
|
void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, int FOV)
|
|
|
|
|
{
|
2014-08-22 21:50:38 +00:00
|
|
|
|
FMaterial * gltex = FMaterial::ValidateTexture(tex, false);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2014-08-22 21:50:38 +00:00
|
|
|
|
int width = gltex->TextureWidth();
|
|
|
|
|
int height = gltex->TextureHeight();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
gl_fixedcolormap=CM_DEFAULT;
|
2014-05-11 19:47:54 +00:00
|
|
|
|
gl_RenderState.SetFixedColormap(CM_DEFAULT);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
2016-09-04 12:16:05 +00:00
|
|
|
|
if (gl.legacyMode)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2016-09-04 12:16:05 +00:00
|
|
|
|
// In legacy mode, fail if the requested texture is too large.
|
|
|
|
|
if (gltex->GetWidth() > screen->GetWidth() || gltex->GetHeight() > screen->GetHeight()) return;
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glFlush();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-09-04 12:16:05 +00:00
|
|
|
|
GLRenderer->StartOffscreen();
|
|
|
|
|
gltex->BindToFrameBuffer();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GL_IRECT bounds;
|
|
|
|
|
bounds.left=bounds.top=0;
|
2014-08-22 21:50:38 +00:00
|
|
|
|
bounds.width=FHardwareTexture::GetTexDimension(gltex->GetWidth());
|
|
|
|
|
bounds.height=FHardwareTexture::GetTexDimension(gltex->GetHeight());
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
GLRenderer->RenderViewpoint(Viewpoint, &bounds, FOV, (float)width/height, (float)width/height, false, false);
|
|
|
|
|
|
2016-09-04 12:16:05 +00:00
|
|
|
|
if (gl.legacyMode)
|
2013-06-23 07:49:34 +00:00
|
|
|
|
{
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glFlush();
|
2014-09-09 10:00:42 +00:00
|
|
|
|
gl_RenderState.SetMaterial(gltex, 0, 0, -1, false);
|
2013-09-03 12:05:41 +00:00
|
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, bounds.width, bounds.height);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GLRenderer->EndOffscreen();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tex->SetUpdated();
|
2016-12-25 21:56:18 +00:00
|
|
|
|
camtexcount++;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
|
|
sector_t *FGLInterface::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back)
|
|
|
|
|
{
|
|
|
|
|
if (floorlightlevel != NULL)
|
|
|
|
|
{
|
|
|
|
|
*floorlightlevel = sec->GetFloorLight ();
|
|
|
|
|
}
|
|
|
|
|
if (ceilinglightlevel != NULL)
|
|
|
|
|
{
|
|
|
|
|
*ceilinglightlevel = sec->GetCeilingLight ();
|
|
|
|
|
}
|
|
|
|
|
return gl_FakeFlat(sec, tempsec, back);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
void FGLInterface::SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog)
|
|
|
|
|
{
|
|
|
|
|
gl_SetFogParams(_fogdensity, _outsidefogcolor, _outsidefogdensity, _skyfog);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FGLInterface::PreprocessLevel()
|
|
|
|
|
{
|
|
|
|
|
gl_PreprocessLevel();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FGLInterface::CleanLevelData()
|
|
|
|
|
{
|
|
|
|
|
gl_CleanLevelData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FGLInterface::RequireGLNodes()
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
FRenderer *gl_CreateInterface()
|
|
|
|
|
{
|
|
|
|
|
return new FGLInterface;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|