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 "gi.h"
|
2020-04-25 19:08:07 +00:00
|
|
|
#include "a_dynlight.h"
|
2013-06-23 07:49:34 +00:00
|
|
|
#include "m_png.h"
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "r_data/r_interpolate.h"
|
|
|
|
#include "r_utility.h"
|
|
|
|
#include "d_player.h"
|
2020-04-25 15:58:26 +00:00
|
|
|
#include "i_time.h"
|
|
|
|
#include "swrenderer/r_swscene.h"
|
2020-04-25 11:18:57 +00:00
|
|
|
#include "swrenderer/r_renderer.h"
|
2020-04-26 10:30:36 +00:00
|
|
|
#include "hw_dynlightdata.h"
|
2020-04-29 15:19:17 +00:00
|
|
|
#include "hw_clock.h"
|
2020-04-26 10:30:36 +00:00
|
|
|
#include "flatvertices.h"
|
2020-04-26 22:03:23 +00:00
|
|
|
#include "v_palette.h"
|
2020-04-27 21:53:26 +00:00
|
|
|
#include "d_main.h"
|
2013-06-23 07:49:34 +00:00
|
|
|
|
2020-04-26 10:30:36 +00:00
|
|
|
#include "hw_lightbuffer.h"
|
2020-04-26 09:38:38 +00:00
|
|
|
#include "hw_cvars.h"
|
2018-10-28 12:26:47 +00:00
|
|
|
#include "hwrenderer/data/hw_viewpointbuffer.h"
|
2020-04-25 11:18:57 +00:00
|
|
|
#include "hwrenderer/scene/hw_fakeflat.h"
|
2018-04-23 20:18:13 +00:00
|
|
|
#include "hwrenderer/scene/hw_clipper.h"
|
2018-06-23 11:25:23 +00:00
|
|
|
#include "hwrenderer/scene/hw_portal.h"
|
2020-04-26 08:26:29 +00:00
|
|
|
#include "hw_vrmodes.h"
|
2013-06-23 07:49:34 +00:00
|
|
|
|
2020-04-25 15:58:26 +00:00
|
|
|
EXTERN_CVAR(Bool, cl_capfps)
|
|
|
|
extern bool NoInterpolateView;
|
|
|
|
|
|
|
|
static SWSceneDrawer *swdrawer;
|
|
|
|
|
|
|
|
void CleanSWDrawer()
|
|
|
|
{
|
|
|
|
if (swdrawer) delete swdrawer;
|
|
|
|
swdrawer = nullptr;
|
|
|
|
}
|
2018-10-29 12:56:17 +00:00
|
|
|
|
2020-04-26 17:58:17 +00:00
|
|
|
#include "g_levellocals.h"
|
|
|
|
#include "a_dynlight.h"
|
|
|
|
|
|
|
|
|
|
|
|
void CollectLights(FLevelLocals* Level)
|
|
|
|
{
|
|
|
|
IShadowMap* sm = &screen->mShadowMap;
|
|
|
|
int lightindex = 0;
|
|
|
|
|
|
|
|
// Todo: this should go through the blockmap in a spiral pattern around the player so that closer lights are preferred.
|
|
|
|
for (auto light = Level->lights; light; light = light->next)
|
|
|
|
{
|
|
|
|
IShadowMap::LightsProcessed++;
|
2020-06-12 13:07:13 +00:00
|
|
|
if (light->shadowmapped && light->IsActive() && lightindex < 1024)
|
2020-04-26 17:58:17 +00:00
|
|
|
{
|
|
|
|
IShadowMap::LightsShadowmapped++;
|
|
|
|
|
|
|
|
light->mShadowmapIndex = lightindex;
|
|
|
|
sm->SetLight(lightindex, (float)light->X(), (float)light->Y(), (float)light->Z(), light->GetRadius());
|
|
|
|
lightindex++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
light->mShadowmapIndex = 1024;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; lightindex < 1024; lightindex++)
|
|
|
|
{
|
|
|
|
sm->SetLight(lightindex, 0, 0, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-23 07:49:34 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Renders one viewpoint in a scene
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2020-04-25 08:51:45 +00:00
|
|
|
sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen)
|
2018-06-20 08:47:03 +00:00
|
|
|
{
|
2020-04-25 08:51:45 +00:00
|
|
|
auto& RenderState = *screen->RenderState();
|
|
|
|
|
|
|
|
R_SetupFrame(mainvp, r_viewwindow, camera);
|
2014-12-31 11:04:55 +00:00
|
|
|
|
2021-07-13 09:54:25 +00:00
|
|
|
if (mainview && toscreen && !(camera->Level->flags3 & LEVEL3_NOSHADOWMAP) && gl_light_shadowmap && (screen->hwcaps & RFL_SHADER_STORAGE_BUFFER))
|
2020-04-26 16:54:43 +00:00
|
|
|
{
|
|
|
|
screen->SetAABBTree(camera->Level->aabbTree);
|
2020-04-26 17:58:17 +00:00
|
|
|
screen->mShadowMap.SetCollectLights([=] {
|
|
|
|
CollectLights(camera->Level);
|
|
|
|
});
|
2020-05-04 20:15:18 +00:00
|
|
|
screen->UpdateShadowMap();
|
2020-04-26 16:54:43 +00:00
|
|
|
}
|
2021-07-13 09:54:25 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// null all references to the level if we do not need a shadowmap. This will shortcut all internal calculations without further checks.
|
|
|
|
screen->SetAABBTree(nullptr);
|
|
|
|
screen->mShadowMap.SetCollectLights(nullptr);
|
|
|
|
}
|
2019-01-04 14:51:59 +00:00
|
|
|
|
2019-01-29 19:09:06 +00:00
|
|
|
// Update the attenuation flag of all light defaults for each viewpoint.
|
|
|
|
// This function will only do something if the setting differs.
|
|
|
|
FLightDefaults::SetAttenuationForLevel(!!(camera->Level->flags3 & LEVEL3_ATTENUATE));
|
|
|
|
|
2020-04-25 08:51:45 +00:00
|
|
|
// Render (potentially) multiple views for stereo 3d
|
2018-06-24 11:39:14 +00:00
|
|
|
// Fixme. The view offsetting should be done with a static table and not require setup of the entire render state for the mode.
|
|
|
|
auto vrmode = VRMode::GetVRMode(mainview && toscreen);
|
2019-03-03 17:35:38 +00:00
|
|
|
const int eyeCount = vrmode->mEyeCount;
|
2020-04-25 08:51:45 +00:00
|
|
|
screen->FirstEye();
|
2019-03-03 17:35:38 +00:00
|
|
|
for (int eye_ix = 0; eye_ix < eyeCount; ++eye_ix)
|
2015-10-31 00:51:35 +00:00
|
|
|
{
|
2020-04-25 08:51:45 +00:00
|
|
|
const auto& eye = vrmode->mEyes[eye_ix];
|
2018-06-03 11:59:40 +00:00
|
|
|
screen->SetViewportRects(bounds);
|
2018-06-30 13:24:13 +00:00
|
|
|
|
|
|
|
if (mainview) // Bind the scene frame buffer and turn on draw buffers used by ssao
|
|
|
|
{
|
|
|
|
bool useSSAO = (gl_ssao != 0);
|
2020-04-25 08:51:45 +00:00
|
|
|
screen->SetSceneRenderTarget(useSSAO);
|
|
|
|
RenderState.SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS);
|
|
|
|
RenderState.EnableDrawBuffers(RenderState.GetPassDrawBufferCount(), true);
|
2018-06-30 13:24:13 +00:00
|
|
|
}
|
|
|
|
|
2019-01-25 00:26:16 +00:00
|
|
|
auto di = HWDrawInfo::StartDrawInfo(mainvp.ViewLevel, nullptr, mainvp, nullptr);
|
2020-04-25 08:51:45 +00:00
|
|
|
auto& vp = di->Viewpoint;
|
2018-10-29 09:21:52 +00:00
|
|
|
|
2020-04-25 08:51:45 +00:00
|
|
|
di->Set3DViewport(RenderState);
|
2018-05-21 20:04:29 +00:00
|
|
|
di->SetViewArea();
|
2020-04-25 08:51:45 +00:00
|
|
|
auto cm = di->SetFullbrightFlags(mainview ? vp.camera->player : nullptr);
|
2020-06-07 07:16:56 +00:00
|
|
|
float flash = 1.f;
|
2018-06-19 21:16:22 +00:00
|
|
|
di->Viewpoint.FieldOfView = fov; // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint)
|
2018-05-21 20:04:29 +00:00
|
|
|
|
2015-10-31 00:51:35 +00:00
|
|
|
// Stereo mode specific perspective projection
|
2018-06-24 11:39:14 +00:00
|
|
|
di->VPUniforms.mProjectionMatrix = eye.GetProjection(fov, ratio, fovratio);
|
|
|
|
// Stereo mode specific viewpoint adjustment
|
|
|
|
vp.Pos += eye.GetViewShift(vp.HWAngles.Yaw.Degrees);
|
2020-04-25 08:51:45 +00:00
|
|
|
di->SetupView(RenderState, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false);
|
2018-06-22 19:44:53 +00:00
|
|
|
|
2020-04-24 19:47:18 +00:00
|
|
|
di->ProcessScene(toscreen);
|
2018-04-29 09:00:34 +00:00
|
|
|
|
2018-06-16 07:37:01 +00:00
|
|
|
if (mainview)
|
2016-07-29 19:31:20 +00:00
|
|
|
{
|
2018-09-02 09:34:18 +00:00
|
|
|
PostProcess.Clock();
|
2020-04-25 08:51:45 +00:00
|
|
|
if (toscreen) di->EndDrawScene(mainvp.sector, RenderState); // do not call this for camera textures.
|
2018-06-30 13:24:13 +00:00
|
|
|
|
2020-04-25 08:51:45 +00:00
|
|
|
if (RenderState.GetPassType() == GBUFFER_PASS) // Turn off ssao draw buffers
|
2018-06-30 13:24:13 +00:00
|
|
|
{
|
2020-04-25 08:51:45 +00:00
|
|
|
RenderState.SetPassType(NORMAL_PASS);
|
|
|
|
RenderState.EnableDrawBuffers(1);
|
2018-06-30 13:24:13 +00:00
|
|
|
}
|
|
|
|
|
2020-06-07 07:16:56 +00:00
|
|
|
screen->PostProcessScene(false, cm, flash, [&]() { di->DrawEndScene2D(mainvp.sector, RenderState); });
|
2018-09-02 09:34:18 +00:00
|
|
|
PostProcess.Unclock();
|
2016-07-29 19:31:20 +00:00
|
|
|
}
|
2018-06-19 21:16:22 +00:00
|
|
|
di->EndDrawInfo();
|
2019-03-03 17:35:38 +00:00
|
|
|
if (eyeCount - eye_ix > 1)
|
2020-04-25 08:51:45 +00:00
|
|
|
screen->NextEye(eyeCount);
|
2015-10-31 00:51:35 +00:00
|
|
|
}
|
2013-06-23 07:49:34 +00:00
|
|
|
|
2018-06-20 08:47:03 +00:00
|
|
|
return mainvp.sector;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
2020-04-25 11:18:57 +00:00
|
|
|
|
|
|
|
void DoWriteSavePic(FileWriter* file, ESSType ssformat, uint8_t* scr, int width, int height, sector_t* viewsector, bool upsidedown)
|
|
|
|
{
|
|
|
|
PalEntry palette[256];
|
|
|
|
PalEntry modulateColor;
|
|
|
|
auto blend = V_CalcBlend(viewsector, &modulateColor);
|
|
|
|
int pixelsize = 1;
|
|
|
|
// Apply the screen blend, because the renderer does not provide this.
|
|
|
|
if (ssformat == SS_RGB)
|
|
|
|
{
|
|
|
|
int numbytes = width * height * 3;
|
|
|
|
pixelsize = 3;
|
|
|
|
if (modulateColor != 0xffffffff)
|
|
|
|
{
|
|
|
|
float r = modulateColor.r / 255.f;
|
|
|
|
float g = modulateColor.g / 255.f;
|
|
|
|
float b = modulateColor.b / 255.f;
|
|
|
|
for (int i = 0; i < numbytes; i += 3)
|
|
|
|
{
|
|
|
|
scr[i] = uint8_t(scr[i] * r);
|
|
|
|
scr[i + 1] = uint8_t(scr[i + 1] * g);
|
|
|
|
scr[i + 2] = uint8_t(scr[i + 2] * b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
float iblendfac = 1.f - blend.W;
|
|
|
|
blend.X *= blend.W;
|
|
|
|
blend.Y *= blend.W;
|
|
|
|
blend.Z *= blend.W;
|
|
|
|
for (int i = 0; i < numbytes; i += 3)
|
|
|
|
{
|
|
|
|
scr[i] = uint8_t(scr[i] * iblendfac + blend.X);
|
|
|
|
scr[i + 1] = uint8_t(scr[i + 1] * iblendfac + blend.Y);
|
|
|
|
scr[i + 2] = uint8_t(scr[i + 2] * iblendfac + blend.Z);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Apply the screen blend to the palette. The colormap related parts get skipped here because these are already part of the image.
|
|
|
|
DoBlending(GPalette.BaseColors, palette, 256, uint8_t(blend.X), uint8_t(blend.Y), uint8_t(blend.Z), uint8_t(blend.W * 255));
|
|
|
|
}
|
|
|
|
|
|
|
|
int pitch = width * pixelsize;
|
|
|
|
if (upsidedown)
|
|
|
|
{
|
|
|
|
scr += ((height - 1) * width * pixelsize);
|
|
|
|
pitch *= -1;
|
|
|
|
}
|
|
|
|
|
2020-04-26 08:26:29 +00:00
|
|
|
M_CreatePNG(file, scr, ssformat == SS_PAL ? palette : nullptr, ssformat, width, height, pitch, vid_gamma);
|
2020-04-25 11:18:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Render the view to a savegame picture
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void WriteSavePic(player_t* player, FileWriter* file, int width, int height)
|
|
|
|
{
|
|
|
|
if (!V_IsHardwareRenderer())
|
|
|
|
{
|
|
|
|
SWRenderer->WriteSavePic(player, file, width, height);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IntRect bounds;
|
|
|
|
bounds.left = 0;
|
|
|
|
bounds.top = 0;
|
|
|
|
bounds.width = width;
|
|
|
|
bounds.height = height;
|
|
|
|
auto& RenderState = *screen->RenderState();
|
|
|
|
|
|
|
|
// we must be sure the GPU finished reading from the buffer before we fill it with new data.
|
|
|
|
screen->WaitForCommands(false);
|
|
|
|
|
|
|
|
// Switch to render buffers dimensioned for the savepic
|
|
|
|
screen->SetSaveBuffers(true);
|
|
|
|
screen->ImageTransitionScene(true);
|
|
|
|
|
|
|
|
hw_ClearFakeFlat();
|
|
|
|
RenderState.SetVertexBuffer(screen->mVertexData);
|
|
|
|
screen->mVertexData->Reset();
|
|
|
|
screen->mLights->Clear();
|
|
|
|
screen->mViewpoints->Clear();
|
|
|
|
|
|
|
|
// This shouldn't overwrite the global viewpoint even for a short time.
|
|
|
|
FRenderViewpoint savevp;
|
|
|
|
sector_t* viewsector = RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false);
|
|
|
|
RenderState.EnableStencil(false);
|
|
|
|
RenderState.SetNoSoftLightLevel();
|
|
|
|
|
|
|
|
int numpixels = width * height;
|
|
|
|
uint8_t* scr = (uint8_t*)M_Malloc(numpixels * 3);
|
|
|
|
screen->CopyScreenToBuffer(width, height, scr);
|
|
|
|
|
|
|
|
DoWriteSavePic(file, SS_RGB, scr, width, height, viewsector, screen->FlipSavePic());
|
|
|
|
M_Free(scr);
|
|
|
|
|
|
|
|
// Switch back the screen render buffers
|
|
|
|
screen->SetViewportRects(nullptr);
|
|
|
|
screen->SetSaveBuffers(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-25 15:58:26 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Renders the main view
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2020-04-25 21:29:38 +00:00
|
|
|
static void CheckTimer(FRenderState &state, uint64_t ShaderStartTime)
|
|
|
|
{
|
|
|
|
// if firstFrame is not yet initialized, initialize it to current time
|
|
|
|
// if we're going to overflow a float (after ~4.6 hours, or 24 bits), re-init to regain precision
|
|
|
|
if ((state.firstFrame == 0) || (screen->FrameTime - state.firstFrame >= 1 << 24) || ShaderStartTime >= state.firstFrame)
|
2021-05-18 18:37:05 +00:00
|
|
|
state.firstFrame = screen->FrameTime - 1;
|
2020-04-25 21:29:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-04-25 15:58:26 +00:00
|
|
|
sector_t* RenderView(player_t* player)
|
|
|
|
{
|
|
|
|
auto RenderState = screen->RenderState();
|
|
|
|
RenderState->SetVertexBuffer(screen->mVertexData);
|
|
|
|
screen->mVertexData->Reset();
|
|
|
|
|
|
|
|
sector_t* retsec;
|
|
|
|
if (!V_IsHardwareRenderer())
|
|
|
|
{
|
|
|
|
screen->SetActiveRenderTarget(); // only relevant for Vulkan
|
|
|
|
|
|
|
|
if (!swdrawer) swdrawer = new SWSceneDrawer;
|
|
|
|
retsec = swdrawer->RenderView(player);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hw_ClearFakeFlat();
|
|
|
|
|
|
|
|
iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0;
|
|
|
|
|
|
|
|
checkBenchActive();
|
|
|
|
|
|
|
|
// reset statistics counters
|
|
|
|
ResetProfilingData();
|
|
|
|
|
|
|
|
// Get this before everything else
|
|
|
|
if (cl_capfps || r_NoInterpolate) r_viewpoint.TicFrac = 1.;
|
|
|
|
else r_viewpoint.TicFrac = I_GetTimeFrac();
|
|
|
|
|
|
|
|
screen->mLights->Clear();
|
|
|
|
screen->mViewpoints->Clear();
|
|
|
|
|
|
|
|
// NoInterpolateView should have no bearing on camera textures, but needs to be preserved for the main view below.
|
|
|
|
bool saved_niv = NoInterpolateView;
|
|
|
|
NoInterpolateView = false;
|
|
|
|
|
|
|
|
// Shader start time does not need to be handled per level. Just use the one from the camera to render from.
|
|
|
|
if (player->camera)
|
2020-04-25 21:29:38 +00:00
|
|
|
CheckTimer(*RenderState, player->camera->Level->ShaderStartTime);
|
2020-04-25 15:58:26 +00:00
|
|
|
// prepare all camera textures that have been used in the last frame.
|
|
|
|
// This must be done for all levels, not just the primary one!
|
|
|
|
for (auto Level : AllLevels())
|
|
|
|
{
|
|
|
|
Level->canvasTextureInfo.UpdateAll([&](AActor* camera, FCanvasTexture* camtex, double fov)
|
|
|
|
{
|
|
|
|
screen->RenderTextureView(camtex, [=](IntRect& bounds)
|
|
|
|
{
|
|
|
|
FRenderViewpoint texvp;
|
2021-03-18 22:29:19 +00:00
|
|
|
float ratio = camtex->aspectRatio / Level->info->pixelstretch;
|
2020-04-25 15:58:26 +00:00
|
|
|
RenderViewpoint(texvp, camera, &bounds, fov, ratio, ratio, false, false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
NoInterpolateView = saved_niv;
|
|
|
|
|
|
|
|
// now render the main view
|
|
|
|
float fovratio;
|
|
|
|
float ratio = r_viewwindow.WidescreenRatio;
|
|
|
|
if (r_viewwindow.WidescreenRatio >= 1.3f)
|
|
|
|
{
|
|
|
|
fovratio = 1.333333f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fovratio = ratio;
|
|
|
|
}
|
|
|
|
|
|
|
|
screen->ImageTransitionScene(true); // Only relevant for Vulkan.
|
|
|
|
|
|
|
|
retsec = RenderViewpoint(r_viewpoint, player->camera, NULL, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true);
|
|
|
|
}
|
|
|
|
All.Unclock();
|
|
|
|
return retsec;
|
|
|
|
}
|
|
|
|
|