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-04-02 20:52:46 +00:00
// the Free Software Foundation, either version 3 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 ;
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-04-05 11:04:51 +00:00
hw_int_useindexedcolortextures = eyeCount > 1 ? false : * hw_useindexedcolortextures ;
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 ) ;
auto & vp = di - > Viewpoint ;
vp = mainvp ;
di - > Set3DViewport ( RenderState ) ;
float flash = 1.f ;
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)
// Stereo mode specific perspective projection
di - > VPUniforms . mProjectionMatrix = eye . GetProjection ( fov , ratio , fovratio ) ;
// Stereo mode specific viewpoint adjustment
vp . Pos + = eye . GetViewShift ( vp . HWAngles . Yaw . Degrees ) ;
di - > SetupView ( RenderState , vp . Pos . X , vp . Pos . Y , vp . Pos . Z , false , false ) ;
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 ) ;
}
screen - > PostProcessScene ( false , CM_DEFAULT , flash , [ & ] ( ) { } ) ;
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.
//
//===========================================================================
2021-04-11 05:43:11 +00:00
FRenderViewpoint SetupViewpoint ( spritetype * cam , const vec3_t & position , int sectnum , binangle angle , fixedhoriz horizon , binangle rollang )
2021-03-18 13:49:36 +00:00
{
FRenderViewpoint r_viewpoint { } ;
2021-03-20 22:01:16 +00:00
r_viewpoint . CameraSprite = cam ;
2021-03-24 22:11:06 +00:00
r_viewpoint . SectNums = nullptr ;
r_viewpoint . SectCount = sectnum ;
2021-03-18 13:49:36 +00:00
r_viewpoint . Pos = { position . x / 16.f , position . y / - 16.f , position . z / - 256.f } ;
2021-03-25 08:06:01 +00:00
r_viewpoint . HWAngles . Yaw = - 90.f + angle . asdeg ( ) ;
r_viewpoint . HWAngles . Pitch = - horizon . aspitch ( ) ;
r_viewpoint . HWAngles . Roll = - rollang . asdeg ( ) ;
2021-03-18 13:49:36 +00:00
r_viewpoint . FieldOfView = ( float ) r_fov ;
2021-03-25 08:06:01 +00:00
r_viewpoint . RotAngle = angle . asbam ( ) ;
2021-03-27 22:12:41 +00:00
double FocalTangent = tan ( r_viewpoint . FieldOfView . Radians ( ) / 2 ) ;
DAngle an = 270. - r_viewpoint . HWAngles . Yaw . Degrees ;
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 ;
void PM_WriteSavePic ( FileWriter * file , int width , int height ) ;
EXTERN_CVAR ( Bool , testnewrenderer ) ;
2021-03-18 13:49:36 +00:00
2021-04-21 20:07:49 +00:00
void WriteSavePic ( FileWriter * file , int width , int height )
{
if ( ! testnewrenderer )
{
PM_WriteSavePic ( file , width , height ) ;
return ;
}
int oldx = xdim ;
int oldy = ydim ;
auto oldwindowxy1 = windowxy1 ;
auto oldwindowxy2 = windowxy2 ;
xdim = width ;
ydim = height ;
videoSetViewableArea ( 0 , 0 , width - 1 , height - 1 ) ;
writingsavepic = true ;
savefile = file ;
savewidth = width ;
saveheight = height ;
bool didit = gi - > GenerateSavePic ( ) ;
writingsavepic = false ;
xdim = oldx ;
ydim = oldy ;
videoSetViewableArea ( oldwindowxy1 . x , oldwindowxy1 . y , oldwindowxy2 . x , oldwindowxy2 . y ) ;
}
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 ( ) ;
RenderViewpoint ( vp , & bounds , vp . FieldOfView . Degrees , 1.333f , 1.333f , true , false ) ;
2021-03-18 13:49:36 +00:00
int numpixels = width * height ;
uint8_t * scr = ( uint8_t * ) M_Malloc ( numpixels * 3 ) ;
screen - > CopyScreenToBuffer ( width , height , scr ) ;
2021-04-21 20:07:49 +00:00
DoWriteSavePic ( file , scr , width , height , screen - > FlipSavePic ( ) ) ;
2021-03-18 13:49:36 +00:00
M_Free ( scr ) ;
// 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 ) ;
2021-04-14 19:17:32 +00:00
void render_drawrooms ( spritetype * playersprite , const vec3_t & position , int sectnum , binangle angle , fixedhoriz horizon , binangle rollang , double smoothratio )
2021-03-18 13:49:36 +00:00
{
2021-03-20 22:01:16 +00:00
checkRotatedWalls ( ) ;
if ( gl_fogmode = = 1 ) gl_fogmode = 2 ; // still needed?
2021-03-21 11:22:50 +00:00
int16_t sect = sectnum ;
updatesector ( position . x , position . y , & sect ) ;
if ( sect > = 0 ) sectnum = sect ;
if ( sectnum < 0 ) 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
2021-03-28 17:22:51 +00:00
FRenderViewpoint r_viewpoint = SetupViewpoint ( playersprite , position , sectnum , angle , horizon , rollang ) ;
2021-03-18 13:49:36 +00:00
if ( cl_capfps ) r_viewpoint . TicFrac = 1. ;
2021-04-14 19:17:32 +00:00
else r_viewpoint . TicFrac = smoothratio * ( 1 / 65536. ) ;
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.
2021-03-28 17:22:51 +00:00
RenderViewpoint ( r_viewpoint , nullptr , r_viewpoint . FieldOfView . Degrees , ratio , fovratio , true , true ) ;
All . Unclock ( ) ;
}
2021-04-11 05:43:11 +00:00
void render_camtex ( spritetype * playersprite , const vec3_t & position , int sectnum , binangle angle , fixedhoriz horizon , binangle rollang , FGameTexture * camtex , IntRect & rect , double smoothratio )
2021-03-28 17:22:51 +00:00
{
int16_t sect = sectnum ;
updatesector ( position . x , position . y , & sect ) ;
if ( sect > = 0 ) sectnum = sect ;
if ( sectnum < 0 ) return ;
screen - > RenderState ( ) - > SetVertexBuffer ( screen - > mVertexData ) ;
// now render the main view
float ratio = camtex - > GetDisplayWidth ( ) / camtex - > GetDisplayHeight ( ) ;
FRenderViewpoint r_viewpoint = SetupViewpoint ( playersprite , position , sectnum , angle , horizon , rollang ) ;
if ( cl_capfps ) r_viewpoint . TicFrac = smoothratio ;
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 )
( " dx " , obj . dx )
( " dy " , obj . dy )
( " dz " , obj . dz )
( " targets " , obj . targets )
. EndObject ( ) ;
}
return arc ;
}