2019-10-17 07:42:11 +00:00
/*
* * glbackend . cpp
* *
* * OpenGL API abstraction
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 2019 Christoph Oelckers
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
2019-10-04 21:29:00 +00:00
# include <memory>
2020-05-30 22:01:00 +00:00
# include <assert.h>
2019-10-06 17:32:35 +00:00
# include "glbackend.h"
2019-10-17 18:29:58 +00:00
# include "textures.h"
# include "palette.h"
2019-11-09 13:05:52 +00:00
# include "gamecontrol.h"
2019-12-28 21:36:47 +00:00
# include "v_2ddrawer.h"
# include "v_video.h"
2020-01-18 12:23:01 +00:00
# include "flatvertices.h"
2020-04-26 21:17:54 +00:00
# include "build.h"
2020-05-30 22:01:00 +00:00
# include "v_draw.h"
# include "v_font.h"
2020-06-04 16:46:44 +00:00
# include "hw_viewpointuniforms.h"
2020-06-04 18:14:48 +00:00
# include "hw_viewpointbuffer.h"
2020-06-11 22:25:52 +00:00
# include "hw_renderstate.h"
2020-06-11 16:40:53 +00:00
# include "hw_cvars.h"
2019-10-04 21:29:00 +00:00
2020-04-28 20:55:37 +00:00
F2DDrawer twodpsp ;
2020-06-04 16:46:44 +00:00
static int BufferLock = 0 ;
2020-04-28 20:55:37 +00:00
2020-01-19 22:27:59 +00:00
TArray < VSMatrix > matrixArray ;
2020-01-03 09:09:38 +00:00
2019-10-24 22:47:40 +00:00
FileReader GetResource ( const char * fn )
{
2020-04-11 21:54:33 +00:00
auto fr = fileSystem . OpenFileReader ( fn ) ;
2019-10-24 22:47:40 +00:00
if ( ! fr . isOpen ( ) )
2019-10-04 21:29:00 +00:00
{
2019-11-07 19:31:16 +00:00
I_Error ( " Fatal: '%s' not found " , fn ) ;
2019-10-04 21:29:00 +00:00
}
2019-10-24 22:47:40 +00:00
return fr ;
2019-10-04 21:29:00 +00:00
}
2019-09-23 21:33:59 +00:00
2019-09-16 17:35:04 +00:00
GLInstance GLInterface ;
2019-10-06 19:15:53 +00:00
GLInstance : : GLInstance ( )
: palmanager ( this )
{
2020-01-19 13:08:48 +00:00
VSMatrix mat ( 0 ) ;
matrixArray . Push ( mat ) ;
2019-10-06 19:15:53 +00:00
}
2020-04-11 21:54:33 +00:00
//void ImGui_Init_Backend();
//ImGuiContext* im_ctx;
2019-11-07 19:31:16 +00:00
TArray < uint8_t > ttf ;
2020-06-15 18:09:49 +00:00
IHardwareTexture * setpalettelayer ( int layer , int translation )
{
if ( layer = = 1 )
return GLInterface . palmanager . GetPalette ( GetTranslationType ( translation ) - Translation_Remap ) ;
else if ( layer = = 2 )
return GLInterface . palmanager . GetLookup ( GetTranslationIndex ( translation ) ) ;
else return nullptr ;
}
2019-11-07 19:31:16 +00:00
void GLInstance : : Init ( int ydim )
2019-09-16 20:56:48 +00:00
{
2020-06-15 18:09:49 +00:00
FMaterial : : SetLayerCallback ( setpalettelayer ) ;
2019-10-05 10:28:08 +00:00
new ( & renderState ) PolymostRenderState ; // reset to defaults.
}
2019-10-04 16:12:03 +00:00
void GLInstance : : InitGLState ( int fogmode , int multisample )
{
2019-12-28 21:36:47 +00:00
// This is a bad place to call this but without deconstructing the entire render loops in all front ends there is no way to have a well defined spot for this stuff.
// Before doing that the backend needs to work in some fashion, so we have to make sure everything is set up when the first render call is performed.
screen - > BeginFrame ( ) ;
}
2019-09-16 20:56:48 +00:00
void GLInstance : : Deinit ( )
{
2020-05-24 22:31:05 +00:00
palmanager . DeleteAll ( ) ;
2019-10-06 22:07:45 +00:00
lastPalswapIndex = - 1 ;
2019-09-16 20:56:48 +00:00
}
2020-01-02 22:15:16 +00:00
2020-01-18 21:41:08 +00:00
void GLInstance : : Draw ( EDrawType type , size_t start , size_t count )
{
2020-06-04 16:46:44 +00:00
assert ( BufferLock > 0 ) ;
2020-04-11 22:21:35 +00:00
applyMapFog ( ) ;
2020-01-18 21:41:08 +00:00
renderState . vindex = start ;
renderState . vcount = count ;
renderState . primtype = type ;
rendercommands . Push ( renderState ) ;
2020-04-11 22:21:35 +00:00
clearMapFog ( ) ;
2020-01-18 21:41:08 +00:00
renderState . StateFlags & = ~ ( STF_CLEARCOLOR | STF_CLEARDEPTH | STF_VIEWPORTSET | STF_SCISSORSET ) ;
2020-01-18 12:23:01 +00:00
}
2020-01-18 21:41:08 +00:00
void GLInstance : : DoDraw ( )
{
2020-06-12 20:32:49 +00:00
GLState lastState ;
if ( rendercommands . Size ( ) > 0 )
2020-06-11 16:40:53 +00:00
{
2020-06-12 20:32:49 +00:00
lastState . Flags = ~ rendercommands [ 0 ] . StateFlags ; // Force ALL flags to be considered 'changed'.
lastState . DepthFunc = INT_MIN ; // Something totally invalid.
2020-06-14 18:59:26 +00:00
screen - > RenderState ( ) - > EnableMultisampling ( true ) ;
2020-06-12 20:32:49 +00:00
for ( auto & rs : rendercommands )
{
rs . Apply ( * screen - > RenderState ( ) , lastState ) ;
screen - > RenderState ( ) - > Draw ( rs . primtype , rs . vindex , rs . vcount ) ;
}
renderState . Apply ( * screen - > RenderState ( ) , lastState ) ; // apply any pending change before returning.
rendercommands . Clear ( ) ;
2020-01-03 22:38:50 +00:00
}
2020-01-19 13:08:48 +00:00
matrixArray . Resize ( 1 ) ;
2019-09-16 17:35:04 +00:00
}
2019-09-16 20:56:48 +00:00
2019-10-04 16:12:03 +00:00
2020-01-19 13:08:48 +00:00
int GLInstance : : SetMatrix ( int num , const VSMatrix * mat )
2019-10-04 16:12:03 +00:00
{
2020-01-19 13:08:48 +00:00
int r = renderState . matrixIndex [ num ] ;
2020-01-03 22:38:50 +00:00
renderState . matrixIndex [ num ] = matrixArray . Size ( ) ;
matrixArray . Push ( * mat ) ;
2020-01-19 13:08:48 +00:00
return r ;
2019-10-04 16:12:03 +00:00
}
2020-06-04 16:46:44 +00:00
void GLInstance : : SetIdentityMatrix ( int num )
{
2020-06-12 20:32:49 +00:00
renderState . matrixIndex [ num ] = - 1 ;
2020-06-04 16:46:44 +00:00
}
2019-10-07 20:11:09 +00:00
void GLInstance : : SetPalswap ( int index )
{
2020-06-05 21:18:21 +00:00
renderState . ShadeDiv = lookups . tables [ index ] . ShadeFactor ;
2020-06-15 18:09:49 +00:00
renderState . FogColor = lookups . getFade ( index ) ;
2019-10-07 20:11:09 +00:00
}
2020-06-11 16:40:53 +00:00
void PolymostRenderState : : Apply ( FRenderState & state , GLState & oldState )
{
if ( Flags & RF_ColorOnly )
{
state . EnableTexture ( false ) ;
}
else
{
state . EnableTexture ( true ) ;
state . SetMaterial ( mMaterial . mMaterial , mMaterial . mClampMode , mMaterial . mTranslation , mMaterial . mOverrideShader ) ;
}
/* todo: bind indexed textures */
state . SetColor ( Color [ 0 ] , Color [ 1 ] , Color [ 2 ] , Color [ 3 ] ) ;
if ( StateFlags ! = oldState . Flags )
{
state . EnableDepthTest ( StateFlags & STF_DEPTHTEST ) ;
if ( ( StateFlags ^ oldState . Flags ) & ( STF_STENCILTEST | STF_STENCILWRITE ) )
{
if ( StateFlags & STF_STENCILWRITE )
{
state . EnableStencil ( true ) ;
state . SetEffect ( EFF_STENCIL ) ;
state . SetStencil ( 0 , SOP_Increment , SF_ColorMaskOff ) ;
}
else if ( StateFlags & STF_STENCILTEST )
{
state . EnableStencil ( true ) ;
state . SetEffect ( EFF_NONE ) ;
state . SetStencil ( 1 , SOP_Keep , SF_DepthMaskOff ) ;
}
else
{
state . EnableStencil ( false ) ;
state . SetEffect ( EFF_NONE ) ;
}
}
if ( ( StateFlags ^ oldState . Flags ) & ( STF_CULLCW | STF_CULLCCW ) )
{
int cull = Cull_None ;
if ( StateFlags & STF_CULLCCW ) cull = Cull_CCW ;
else if ( StateFlags & STF_CULLCW ) cull = Cull_CW ;
state . SetCulling ( cull ) ;
}
state . SetColorMask ( StateFlags & STF_COLORMASK ) ;
state . SetDepthMask ( StateFlags & STF_DEPTHMASK ) ;
if ( StateFlags & ( STF_CLEARCOLOR | STF_CLEARDEPTH ) )
{
int clear = 0 ;
2020-06-12 18:31:23 +00:00
if ( StateFlags & STF_CLEARCOLOR ) clear | = CT_Color ;
2020-06-11 16:40:53 +00:00
if ( StateFlags & STF_CLEARDEPTH ) clear | = CT_Depth ;
state . Clear ( clear ) ;
}
if ( StateFlags & STF_VIEWPORTSET )
{
state . SetViewport ( vp_x , vp_y , vp_w , vp_h ) ;
}
if ( StateFlags & STF_SCISSORSET )
{
state . SetScissor ( sc_x , sc_y , sc_w , sc_h ) ;
}
state . SetDepthBias ( mBias . mFactor , mBias . mUnits ) ;
StateFlags & = ~ ( STF_CLEARCOLOR | STF_CLEARDEPTH | STF_VIEWPORTSET | STF_SCISSORSET ) ;
oldState . Flags = StateFlags ;
}
state . SetRenderStyle ( Style ) ;
if ( DepthFunc ! = oldState . DepthFunc )
{
state . SetDepthFunc ( DepthFunc ) ;
oldState . DepthFunc = DepthFunc ;
}
// Disable brightmaps if non-black fog is used.
if ( ! ( Flags & RF_FogDisabled ) & & ShadeDiv > = 1 / 1000.f )
{
state . EnableFog ( FogColor . isBlack ( ) & & ! ( Flags & RF_MapFog ) ? 1 : - 1 ) ;
}
else state . EnableFog ( 0 ) ;
state . SetFog ( ( Flags & RF_MapFog ) ? PalEntry ( 0x999999 ) : FogColor , 21.f ) ; // Fixme: The real density still needs to be implemented. 21 is a reasonable default only.
state . SetSoftLightLevel ( ShadeDiv > = 1 / 1000.f ? 255 - Scale ( Shade , 255 , numshades ) : 255 ) ;
state . SetLightParms ( VisFactor , ShadeDiv / ( numshades - 2 ) ) ;
2020-06-11 21:55:23 +00:00
state . SetTextureMode ( TextureMode ) ;
2020-06-11 16:40:53 +00:00
state . SetNpotEmulation ( NPOTEmulation . Y , NPOTEmulation . X ) ;
state . AlphaFunc ( Alpha_Greater , AlphaTest ? AlphaThreshold : - 1.f ) ;
FVector4 addcol ( 0 , 0 , 0 , 0 ) ;
FVector4 modcol ( fullscreenTint . r / 255.f , fullscreenTint . g / 255.f , fullscreenTint . b / 255.f , 1 ) ;
FVector4 blendcol ( 0 , 0 , 0 , 0 ) ;
int flags = 0 ;
if ( fullscreenTint ! = 0xffffff ) flags | = 16 ;
if ( hictint_flags ! = - 1 )
{
flags | = TextureManipulation : : ActiveBit ;
if ( hictint_flags & TINTF_COLORIZE )
{
modcol . X * = hictint . r / 64.f ;
modcol . Y * = hictint . g / 64.f ;
modcol . Z * = hictint . b / 64.f ;
}
if ( hictint_flags & TINTF_GRAYSCALE )
modcol . W = 1.f ;
if ( hictint_flags & TINTF_INVERT )
flags | = TextureManipulation : : InvertBit ;
if ( hictint_flags & TINTF_BLENDMASK )
{
blendcol = modcol ; // WTF???, but the tinting code really uses the same color for both!
flags | = ( ( ( hictint_flags & TINTF_BLENDMASK ) > > 6 ) + 1 ) & TextureManipulation : : BlendMask ;
}
addcol . W = flags ;
}
state . SetTextureColors ( & modcol . X , & addcol . X , & blendcol . X ) ;
if ( matrixIndex [ Matrix_Model ] ! = - 1 )
{
state . EnableModelMatrix ( true ) ;
state . mModelMatrix = matrixArray [ matrixIndex [ Matrix_Model ] ] ;
}
else state . EnableModelMatrix ( false ) ;
memset ( matrixIndex , - 1 , sizeof ( matrixIndex ) ) ;
}
void DoWriteSavePic ( FileWriter * file , ESSType ssformat , uint8_t * scr , int width , int height , bool upsidedown )
{
int pixelsize = 3 ;
int pitch = width * pixelsize ;
if ( upsidedown )
{
scr + = ( ( height - 1 ) * width * pixelsize ) ;
pitch * = - 1 ;
}
M_CreatePNG ( file , scr , nullptr , ssformat , width , height , pitch , vid_gamma ) ;
}
2020-04-26 21:17:54 +00:00
//===========================================================================
//
// Render the view to a savegame picture
//
//===========================================================================
void WriteSavePic ( FileWriter * file , int width , int height )
{
IntRect bounds ;
bounds . left = 0 ;
bounds . top = 0 ;
bounds . width = width ;
bounds . height = height ;
2020-06-11 16:40:53 +00:00
auto & RenderState = * screen - > RenderState ( ) ;
2020-04-26 21:17:54 +00:00
// we must be sure the GPU finished reading from the buffer before we fill it with new data.
2020-06-11 22:25:52 +00:00
screen - > WaitForCommands ( false ) ;
2020-05-31 08:53:11 +00:00
screen - > mVertexData - > Reset ( ) ;
2020-04-26 21:17:54 +00:00
// Switch to render buffers dimensioned for the savepic
2020-06-11 16:40:53 +00:00
screen - > SetSaveBuffers ( true ) ;
screen - > ImageTransitionScene ( true ) ;
RenderState . SetVertexBuffer ( screen - > mVertexData ) ;
screen - > mVertexData - > Reset ( ) ;
//screen->mLights->Clear();
screen - > mViewpoints - > Clear ( ) ;
2020-04-26 21:17:54 +00:00
int oldx = xdim ;
int oldy = ydim ;
auto oldwindowxy1 = windowxy1 ;
auto oldwindowxy2 = windowxy2 ;
xdim = width ;
ydim = height ;
videoSetViewableArea ( 0 , 0 , width - 1 , height - 1 ) ;
renderSetAspect ( 65536 , 65536 ) ;
bool didit = gi - > GenerateSavePic ( ) ;
xdim = oldx ;
ydim = oldy ;
videoSetViewableArea ( oldwindowxy1 . x , oldwindowxy1 . y , oldwindowxy2 . x , oldwindowxy2 . y ) ;
// The 2D drawers can contain some garbage from the dirty render setup. Get rid of that first.
2020-05-30 22:01:00 +00:00
twod - > Clear ( ) ;
2020-04-26 21:17:54 +00:00
twodpsp . Clear ( ) ;
2020-06-11 16:40:53 +00:00
int numpixels = width * height ;
uint8_t * scr = ( uint8_t * ) M_Malloc ( numpixels * 3 ) ;
screen - > CopyScreenToBuffer ( width , height , scr ) ;
2020-04-26 21:17:54 +00:00
2020-06-11 16:40:53 +00:00
DoWriteSavePic ( file , SS_RGB , scr , width , height , screen - > FlipSavePic ( ) ) ;
M_Free ( scr ) ;
2020-04-26 21:17:54 +00:00
// Switch back the screen render buffers
screen - > SetViewportRects ( nullptr ) ;
2020-06-11 16:40:53 +00:00
screen - > SetSaveBuffers ( false ) ;
2020-04-26 21:17:54 +00:00
}
2020-06-04 16:46:44 +00:00
static HWViewpointUniforms vp ;
void renderSetProjectionMatrix ( const float * p )
{
if ( p )
{
vp . mProjectionMatrix . loadMatrix ( p ) ;
GLInterface . mProjectionM5 = p [ 5 ] ;
}
else vp . mProjectionMatrix . loadIdentity ( ) ;
}
void renderSetViewMatrix ( const float * p )
{
if ( p ) vp . mViewMatrix . loadMatrix ( p ) ;
else vp . mViewMatrix . loadIdentity ( ) ;
}
2020-05-30 22:01:00 +00:00
2020-06-05 17:06:31 +00:00
void renderSetVisibility ( float vis )
{
vp . mGlobVis = vis ;
}
2020-05-30 22:01:00 +00:00
void renderBeginScene ( )
{
if ( videoGetRenderMode ( ) < REND_POLYMOST ) return ;
2020-06-04 16:46:44 +00:00
assert ( BufferLock = = 0 ) ;
2020-06-11 16:40:53 +00:00
vp . mPalLightLevels = numshades | ( static_cast < int > ( gl_fogmode ) < < 8 ) | ( ( int ) 5 < < 16 ) ;
2020-06-11 22:25:52 +00:00
screen - > mViewpoints - > SetViewpoint ( * screen - > RenderState ( ) , & vp ) ;
2020-06-04 16:46:44 +00:00
2020-05-30 22:01:00 +00:00
if ( BufferLock + + = = 0 )
{
screen - > mVertexData - > Map ( ) ;
}
}
void renderFinishScene ( )
{
if ( videoGetRenderMode ( ) < REND_POLYMOST ) return ;
2020-06-04 16:46:44 +00:00
assert ( BufferLock = = 1 ) ;
2020-05-30 22:01:00 +00:00
if ( - - BufferLock = = 0 )
{
screen - > mVertexData - > Unmap ( ) ;
GLInterface . DoDraw ( ) ;
}
}
//==========================================================================
//
// DFrameBuffer :: DrawRateStuff
//
// Draws the fps counter, dot ticker, and palette debug.
//
//==========================================================================
CVAR ( Bool , vid_fps , false , 0 )
void DrawRateStuff ( )
{
// Draws frame time and cumulative fps
if ( vid_fps )
{
FString fpsbuff = gi - > statFPS ( ) ;
int textScale = active_con_scale ( twod ) ;
int rate_x = screen - > GetWidth ( ) / textScale - NewConsoleFont - > StringWidth ( & fpsbuff [ 0 ] ) ;
twod - > AddColorOnlyQuad ( rate_x * textScale , 0 , screen - > GetWidth ( ) , NewConsoleFont - > GetHeight ( ) * textScale , MAKEARGB ( 255 , 0 , 0 , 0 ) ) ;
DrawText ( twod , NewConsoleFont , CR_WHITE , rate_x , 0 , ( char * ) & fpsbuff [ 0 ] ,
DTA_VirtualWidth , screen - > GetWidth ( ) / textScale ,
DTA_VirtualHeight , screen - > GetHeight ( ) / textScale ,
DTA_KeepRatio , true , TAG_DONE ) ;
}
}
2020-06-07 20:06:47 +00:00
int32_t r_scenebrightness = 0 ;
2020-06-11 16:40:53 +00:00
void Draw2D ( F2DDrawer * drawer , FRenderState & state ) ;
2020-04-28 20:55:37 +00:00
void videoShowFrame ( int32_t w )
{
if ( gl_ssao )
{
2020-06-11 16:40:53 +00:00
screen - > AmbientOccludeScene ( GLInterface . GetProjectionM5 ( ) ) ;
// To do: the translucent part of the scene should be drawn here, but the render setup in the games is really too broken to do SSAO.
2020-04-28 20:55:37 +00:00
2020-06-11 16:40:53 +00:00
//glDrawBuffers(1, buffers);
2020-04-28 20:55:37 +00:00
}
2020-06-07 20:06:47 +00:00
float Brightness = 8.f / ( r_scenebrightness + 8.f ) ;
screen - > PostProcessScene ( false , 0 , Brightness , [ ] ( ) {
2020-06-11 16:40:53 +00:00
Draw2D ( & twodpsp , * screen - > RenderState ( ) ) ; // draws the weapon sprites
2020-04-28 20:55:37 +00:00
} ) ;
screen - > Update ( ) ;
2020-06-14 19:13:22 +00:00
screen - > mVertexData - > Reset ( ) ;
2020-04-28 20:55:37 +00:00
// After finishing the frame, reset everything for the next frame. This needs to be done better.
screen - > BeginFrame ( ) ;
2020-06-11 16:40:53 +00:00
bool useSSAO = ( gl_ssao ! = 0 ) ;
screen - > SetSceneRenderTarget ( useSSAO ) ;
2020-04-28 20:55:37 +00:00
twodpsp . Clear ( ) ;
twod - > Clear ( ) ;
}
2020-06-11 16:40:53 +00:00
2020-05-20 20:18:03 +00:00
void markTileForPrecache ( int tilenum , int palnum )
{
}
2020-06-11 16:40:53 +00:00
2020-05-20 20:18:03 +00:00
void precacheMarkedTiles ( )
{
}