2021-03-18 13:49:36 +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
2021-12-29 09:29:22 +00:00
// the Free Software Foundation, either version 2 of the License, or
2021-03-18 13:49:36 +00:00
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
* * gl_scene . cpp
* * manages the rendering of the player ' s view
* *
*/
# include "gi.h"
# include "build.h"
# include "v_draw.h"
//#include "a_dynlight.h"
# include "v_video.h"
# include "m_png.h"
# include "i_time.h"
# include "hw_dynlightdata.h"
# include "hw_clock.h"
# include "flatvertices.h"
# include "hw_renderstate.h"
# include "hw_lightbuffer.h"
# include "hw_cvars.h"
# include "hw_viewpointbuffer.h"
# include "hw_clipper.h"
//#include "hwrenderer/scene/hw_portal.h"
# include "hw_vrmodes.h"
# include "hw_drawstructs.h"
# include "hw_drawlist.h"
# include "hw_drawinfo.h"
# include "gamecvars.h"
2021-03-20 22:01:16 +00:00
# include "render.h"
2021-03-28 17:22:51 +00:00
# include "gamestruct.h"
2021-04-21 20:07:49 +00:00
# include "gamehud.h"
2021-03-18 13:49:36 +00:00
EXTERN_CVAR ( Bool , cl_capfps )
2021-03-18 16:18:03 +00:00
PalEntry GlobalMapFog ;
2021-04-03 20:51:31 +00:00
float GlobalFogDensity = 350.f ;
2021-03-21 13:48:35 +00:00
TArray < PortalDesc > allPortals ;
2022-01-08 11:09:04 +00:00
void Draw2D ( F2DDrawer * drawer , FRenderState & state ) ;
2021-03-18 16:18:03 +00:00
2022-08-03 13:50:27 +00:00
CVARD ( Bool , hw_hightile , true , CVAR_ARCHIVE | CVAR_GLOBALCONFIG , " enable/disable hightile texture rendering " )
bool hw_int_useindexedcolortextures ;
CUSTOM_CVARD ( Bool , hw_useindexedcolortextures , false , CVAR_ARCHIVE | CVAR_GLOBALCONFIG , " enable/disable indexed color texture rendering " )
{
if ( screen ) screen - > SetTextureFilterMode ( ) ;
}
CVARD ( Bool , hw_models , false , CVAR_ARCHIVE | CVAR_GLOBALCONFIG , " enable/disable model rendering " )
2021-03-18 16:18:03 +00:00
2021-03-18 13:49:36 +00:00
#if 0
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 + + ;
if ( light - > shadowmapped & & light - > IsActive ( ) & & lightindex < 1024 )
{
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 ) ;
}
}
# endif
//-----------------------------------------------------------------------------
//
// Renders one viewpoint in a scene
//
//-----------------------------------------------------------------------------
void RenderViewpoint ( FRenderViewpoint & mainvp , IntRect * bounds , float fov , float ratio , float fovratio , bool mainview , bool toscreen )
{
auto & RenderState = * screen - > RenderState ( ) ;
/*
if ( mainview & & toscreen )
{
screen - > SetAABBTree ( camera - > Level - > aabbTree ) ;
screen - > mShadowMap . SetCollectLights ( [ = ] {
CollectLights ( camera - > Level ) ;
} ) ;
screen - > UpdateShadowMap ( ) ;
}
*/
// Render (potentially) multiple views for stereo 3d
// 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 ) ;
const int eyeCount = vrmode - > mEyeCount ;
screen - > FirstEye ( ) ;
2021-12-03 17:05:54 +00:00
hw_int_useindexedcolortextures = eyeCount > 1 ? false : * hw_useindexedcolortextures ;
2021-04-05 11:04:51 +00:00
2021-03-18 13:49:36 +00:00
for ( int eye_ix = 0 ; eye_ix < eyeCount ; + + eye_ix )
{
const auto & eye = vrmode - > mEyes [ eye_ix ] ;
screen - > SetViewportRects ( bounds ) ;
if ( mainview ) // Bind the scene frame buffer and turn on draw buffers used by ssao
{
bool useSSAO = ( gl_ssao ! = 0 ) ;
screen - > SetSceneRenderTarget ( useSSAO ) ;
RenderState . SetPassType ( useSSAO ? GBUFFER_PASS : NORMAL_PASS ) ;
RenderState . EnableDrawBuffers ( RenderState . GetPassDrawBufferCount ( ) , true ) ;
}
auto di = HWDrawInfo : : StartDrawInfo ( nullptr , mainvp , nullptr ) ;
2022-01-10 20:19:06 +00:00
di - > SetVisibility ( ) ;
2021-03-18 13:49:36 +00:00
auto & vp = di - > Viewpoint ;
vp = mainvp ;
di - > Set3DViewport ( RenderState ) ;
2022-01-08 11:09:04 +00:00
float flash = 8.f / ( r_scenebrightness + 8.f ) ;
2022-08-26 16:28:22 +00:00
di - > Viewpoint . FieldOfView = FAngle : : fromDeg ( fov ) ; // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint)
2021-03-18 13:49:36 +00:00
// Stereo mode specific perspective projection
di - > VPUniforms . mProjectionMatrix = eye . GetProjection ( fov , ratio , fovratio ) ;
2023-02-06 08:55:19 +00:00
2021-03-18 13:49:36 +00:00
// Stereo mode specific viewpoint adjustment
2023-02-06 08:55:19 +00:00
if ( eye . mShiftFactor ! = 0 )
{
vp . Pos + = eye . GetViewShift ( vp . HWAngles . Yaw . Degrees ( ) ) ;
sectortype * sect = & sector [ vp . SectCount ] ;
updatesector ( DVector2 ( vp . Pos . X , - vp . Pos . Y ) , & sect ) ;
vp . SectCount = sectindex ( sect ) ;
}
2021-03-18 13:49:36 +00:00
2023-02-06 08:55:19 +00:00
di - > SetupView ( RenderState , vp . Pos . X , vp . Pos . Y , vp . Pos . Z , false , false ) ;
2021-03-18 13:49:36 +00:00
di - > ProcessScene ( toscreen ) ;
if ( mainview )
{
PostProcess . Clock ( ) ;
if ( toscreen ) di - > EndDrawScene ( RenderState ) ; // do not call this for camera textures.
if ( RenderState . GetPassType ( ) = = GBUFFER_PASS ) // Turn off ssao draw buffers
{
RenderState . SetPassType ( NORMAL_PASS ) ;
RenderState . EnableDrawBuffers ( 1 ) ;
}
2022-01-08 11:09:04 +00:00
screen - > PostProcessScene ( false , CM_DEFAULT , flash , [ ] ( ) {
2022-01-08 13:16:18 +00:00
hw_int_useindexedcolortextures = false ;
2022-01-13 23:15:33 +00:00
PostProcess . Unclock ( ) ;
2022-01-08 11:09:04 +00:00
Draw2D ( & twodpsp , * screen - > RenderState ( ) ) ; // draws the weapon sprites
2022-01-13 23:15:33 +00:00
PostProcess . Clock ( ) ;
2022-01-08 11:09:04 +00:00
} ) ;
2021-03-18 13:49:36 +00:00
PostProcess . Unclock ( ) ;
}
di - > EndDrawInfo ( ) ;
if ( eyeCount - eye_ix > 1 )
screen - > NextEye ( eyeCount ) ;
}
2021-04-05 11:04:51 +00:00
hw_int_useindexedcolortextures = false ;
2021-03-18 13:49:36 +00:00
}
//===========================================================================
//
// Set up the view point.
//
//===========================================================================
2022-12-05 05:09:43 +00:00
FRenderViewpoint SetupViewpoint ( DCoreActor * cam , const DVector3 & position , int sectnum , const DRotator & angles , float fov = - 1 )
2021-03-18 13:49:36 +00:00
{
FRenderViewpoint r_viewpoint { } ;
2021-12-22 22:27:32 +00:00
r_viewpoint . CameraActor = cam ;
2021-03-24 22:11:06 +00:00
r_viewpoint . SectNums = nullptr ;
r_viewpoint . SectCount = sectnum ;
2022-09-06 11:35:50 +00:00
r_viewpoint . Pos = { position . X , - position . Y , - position . Z } ;
2023-10-29 10:12:41 +00:00
r_viewpoint . HWAngles . Yaw = FAngle : : fromDeg ( - 90. + angles . Yaw . Degrees ( ) ) ;
2022-12-05 05:09:43 +00:00
r_viewpoint . HWAngles . Pitch = FAngle : : fromDeg ( ClampViewPitch ( angles . Pitch ) . Degrees ( ) ) ;
2023-10-29 10:12:41 +00:00
r_viewpoint . HWAngles . Roll = FAngle : : fromDeg ( angles . Roll . Degrees ( ) ) ;
r_viewpoint . FieldOfView = FAngle : : fromDeg ( fov > 0 ? fov : r_fov ) ;
2022-12-05 05:09:43 +00:00
r_viewpoint . RotAngle = angles . Yaw . BAMs ( ) ;
2021-03-27 22:12:41 +00:00
double FocalTangent = tan ( r_viewpoint . FieldOfView . Radians ( ) / 2 ) ;
2022-08-26 16:28:22 +00:00
DAngle an = DAngle : : fromDeg ( 270. - r_viewpoint . HWAngles . Yaw . Degrees ( ) ) ;
2021-03-27 22:12:41 +00:00
r_viewpoint . TanSin = FocalTangent * an . Sin ( ) ;
r_viewpoint . TanCos = FocalTangent * an . Cos ( ) ;
2021-03-28 08:49:34 +00:00
r_viewpoint . ViewVector = an . ToVector ( ) ;
2021-03-18 13:49:36 +00:00
return r_viewpoint ;
}
void DoWriteSavePic ( FileWriter * file , uint8_t * scr , int width , int height , bool upsidedown )
{
2021-04-21 20:07:49 +00:00
int pixelsize = 3 ;
2021-03-18 13:49:36 +00:00
int pitch = width * pixelsize ;
if ( upsidedown )
{
scr + = ( ( height - 1 ) * width * pixelsize ) ;
pitch * = - 1 ;
}
M_CreatePNG ( file , scr , nullptr , SS_RGB , width , height , pitch , vid_gamma ) ;
}
//===========================================================================
//
// Render the view to a savegame picture
//
2021-04-21 20:07:49 +00:00
// Currently a bit messy because the game side still needs to be able to
// handle Polymost.
//
2021-03-18 13:49:36 +00:00
//===========================================================================
2021-04-21 20:07:49 +00:00
bool writingsavepic ;
FileWriter * savefile ;
int savewidth , saveheight ;
2021-03-18 13:49:36 +00:00
2021-04-21 20:07:49 +00:00
void WriteSavePic ( FileWriter * file , int width , int height )
{
writingsavepic = true ;
savefile = file ;
savewidth = width ;
saveheight = height ;
2021-11-14 14:03:50 +00:00
/*bool didit =*/ gi - > GenerateSavePic ( ) ;
2021-04-21 20:07:49 +00:00
writingsavepic = false ;
}
void RenderToSavePic ( FRenderViewpoint & vp , FileWriter * file , int width , int height )
2021-03-18 13:49:36 +00:00
{
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 ) ;
RenderState . SetVertexBuffer ( screen - > mVertexData ) ;
screen - > mVertexData - > Reset ( ) ;
screen - > mLights - > Clear ( ) ;
screen - > mViewpoints - > Clear ( ) ;
2021-04-21 20:07:49 +00:00
twodpsp . Clear ( ) ;
2022-08-26 16:28:22 +00:00
RenderViewpoint ( vp , & bounds , vp . FieldOfView . Degrees ( ) , 1.333f , 1.333f , true , false ) ;
2021-04-21 20:07:49 +00:00
2021-03-18 13:49:36 +00:00
int numpixels = width * height ;
2023-10-02 20:41:36 +00:00
TArray < uint8_t > scr ( numpixels * 3 , true ) ;
screen - > CopyScreenToBuffer ( width , height , scr . Data ( ) ) ;
2021-03-18 13:49:36 +00:00
2023-10-02 20:41:36 +00:00
DoWriteSavePic ( file , scr . Data ( ) , width , height , screen - > FlipSavePic ( ) ) ;
2021-03-18 13:49:36 +00:00
// Switch back the screen render buffers
screen - > SetViewportRects ( nullptr ) ;
screen - > SetSaveBuffers ( false ) ;
}
//===========================================================================
//
// Renders the main view
//
//===========================================================================
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 )
state . firstFrame = screen - > FrameTime ;
}
2021-03-28 17:22:51 +00:00
void animatecamsprite ( double s ) ;
2022-01-30 11:27:35 +00:00
2022-12-05 05:09:43 +00:00
void render_drawrooms ( DCoreActor * playersprite , const DVector3 & position , sectortype * sect , const DRotator & angles , double interpfrac , float fov )
2021-03-18 13:49:36 +00:00
{
2022-09-24 16:27:02 +00:00
updatesector ( position . XY ( ) , & sect ) ;
2022-09-25 16:28:40 +00:00
if ( sect = = nullptr ) return ;
2021-03-20 22:01:16 +00:00
2021-03-18 13:49:36 +00:00
iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0 ;
checkBenchActive ( ) ;
// reset statistics counters
ResetProfilingData ( ) ;
// Get this before everything else
2022-12-05 05:09:43 +00:00
FRenderViewpoint r_viewpoint = SetupViewpoint ( playersprite , position , sectindex ( sect ) , angles , fov ) ;
2022-09-07 09:05:28 +00:00
r_viewpoint . TicFrac = ! cl_capfps ? interpfrac : 1. ;
2021-03-18 13:49:36 +00:00
screen - > mLights - > Clear ( ) ;
screen - > mViewpoints - > Clear ( ) ;
2021-03-28 17:22:51 +00:00
screen - > mVertexData - > Reset ( ) ;
2021-03-18 13:49:36 +00:00
2021-04-21 20:07:49 +00:00
if ( writingsavepic ) // hack alert! The save code should not go through render_drawrooms, but we can only clean up the game side when Polymost is gone for good.
{
RenderToSavePic ( r_viewpoint , savefile , savewidth , saveheight ) ;
return ;
}
2021-03-18 13:49:36 +00:00
// Shader start time does not need to be handled per level. Just use the one from the camera to render from.
2021-03-28 17:22:51 +00:00
auto RenderState = screen - > RenderState ( ) ;
2021-03-18 13:49:36 +00:00
CheckTimer ( * RenderState , 0 /*ShaderStartTime*/ ) ;
2021-03-28 17:22:51 +00:00
// prepare all camera textures that have been used in the last frame.
gi - > UpdateCameras ( r_viewpoint . TicFrac ) ;
RenderState - > SetVertexBuffer ( screen - > mVertexData ) ;
2021-03-18 13:49:36 +00:00
// now render the main view
float fovratio ;
2021-04-05 08:34:03 +00:00
float ratio = ActiveRatio ( screen - > GetWidth ( ) , screen - > GetHeight ( ) ) ;
2021-03-18 13:49:36 +00:00
if ( ratio > = 1.33f )
{
fovratio = 1.33f ;
}
else
{
fovratio = ratio ;
}
screen - > ImageTransitionScene ( true ) ; // Only relevant for Vulkan.
2022-08-26 16:28:22 +00:00
RenderViewpoint ( r_viewpoint , nullptr , r_viewpoint . FieldOfView . Degrees ( ) , ratio , fovratio , true , true ) ;
2021-03-28 17:22:51 +00:00
All . Unclock ( ) ;
}
2022-12-05 05:09:43 +00:00
void render_camtex ( DCoreActor * playersprite , const DVector3 & position , sectortype * sect , const DRotator & angles , FGameTexture * camtex , IntRect & rect , double interpfrac )
2021-03-28 17:22:51 +00:00
{
2022-09-06 11:35:50 +00:00
updatesector ( position , & sect ) ;
2021-12-01 23:26:02 +00:00
if ( ! sect ) return ;
2021-03-28 17:22:51 +00:00
screen - > RenderState ( ) - > SetVertexBuffer ( screen - > mVertexData ) ;
// now render the main view
float ratio = camtex - > GetDisplayWidth ( ) / camtex - > GetDisplayHeight ( ) ;
2022-12-05 05:09:43 +00:00
FRenderViewpoint r_viewpoint = SetupViewpoint ( playersprite , position , sectindex ( sect ) , angles ) ;
2022-09-07 09:05:28 +00:00
r_viewpoint . TicFrac = ! cl_capfps ? interpfrac : 1. ;
2021-03-28 17:22:51 +00:00
2022-08-26 16:28:22 +00:00
RenderViewpoint ( r_viewpoint , & rect , r_viewpoint . FieldOfView . Degrees ( ) , ratio , ratio , false , false ) ;
2021-03-18 13:49:36 +00:00
All . Unclock ( ) ;
}
2021-03-24 17:42:00 +00:00
FSerializer & Serialize ( FSerializer & arc , const char * key , PortalDesc & obj , PortalDesc * defval )
{
if ( arc . BeginObject ( key ) )
{
arc ( " type " , obj . type )
2022-09-16 17:56:46 +00:00
( " d " , obj . delta )
2021-03-24 17:42:00 +00:00
( " targets " , obj . targets )
. EndObject ( ) ;
}
return arc ;
}