2016-10-09 10:50:57 +00:00
/*
* * gl_swframebuffer . cpp
* * Code to let ZDoom use OpenGL as a simple framebuffer
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 1998 - 2011 Randy Heit
* * 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 .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
* * This file does _not_ implement hardware - acclerated 3 D rendering . It is
* * just a means of getting the pixel data to the screen in a more reliable
* * method on modern hardware by copying the entire frame to a texture ,
* * drawing that to the screen , and presenting .
* *
* * That said , it does implement hardware - accelerated 2 D rendering .
*/
# include "gl/system/gl_system.h"
# include "m_swap.h"
# include "v_video.h"
# include "doomstat.h"
# include "m_png.h"
# include "m_crc32.h"
# include "vectors.h"
# include "v_palette.h"
# include "templates.h"
# include "c_dispatch.h"
# include "templates.h"
# include "i_system.h"
# include "i_video.h"
# include "v_pfx.h"
# include "stats.h"
# include "doomerrors.h"
# include "r_data/r_translate.h"
# include "f_wipe.h"
# include "sbar.h"
# include "w_wad.h"
# include "r_data/colormaps.h"
# include "gl/system/gl_interface.h"
# include "gl/system/gl_swframebuffer.h"
# include "gl/data/gl_data.h"
# include "gl/utility/gl_clock.h"
# include "gl/utility/gl_templates.h"
# include "gl/gl_functions.h"
2016-10-10 22:03:46 +00:00
# include "gl_debug.h"
2016-10-09 10:50:57 +00:00
2017-01-12 15:21:46 +00:00
# include "swrenderer/scene/r_light.h"
2017-05-06 20:25:18 +00:00
# ifndef NO_SSE
# include <immintrin.h>
# endif
2016-10-09 10:50:57 +00:00
CVAR ( Int , gl_showpacks , 0 , 0 )
# ifndef WIN32 // Defined in fb_d3d9 for Windows
CVAR ( Bool , vid_hwaalines , true , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
2016-10-16 20:40:08 +00:00
CUSTOM_CVAR ( Bool , vid_hw2d , true , CVAR_NOINITCALL )
{
V_SetBorderNeedRefresh ( ) ;
}
2016-10-09 10:50:57 +00:00
# else
EXTERN_CVAR ( Bool , vid_hwaalines )
2016-10-16 20:40:08 +00:00
EXTERN_CVAR ( Bool , vid_hw2d )
2016-10-09 10:50:57 +00:00
# endif
EXTERN_CVAR ( Bool , fullscreen )
EXTERN_CVAR ( Float , Gamma )
EXTERN_CVAR ( Bool , vid_vsync )
EXTERN_CVAR ( Float , transsouls )
EXTERN_CVAR ( Int , vid_refreshrate )
2017-05-08 17:30:51 +00:00
EXTERN_CVAR ( Bool , gl_legacy_mode )
2016-10-09 10:50:57 +00:00
2016-10-12 06:05:31 +00:00
CVAR ( Int , vid_max_width , 0 , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
CVAR ( Int , vid_max_height , 0 , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
namespace
{
int ClampWidth ( int width ) { return ( vid_max_width = = 0 | | width < vid_max_width ) ? width : vid_max_width ; }
int ClampHeight ( int height ) { return ( vid_max_height = = 0 | | height < vid_max_height ) ? height : vid_max_height ; }
}
2016-10-16 20:40:08 +00:00
# ifdef WIN32
2016-10-09 10:50:57 +00:00
extern cycle_t BlitCycles ;
2016-10-16 20:40:08 +00:00
# endif
2016-10-09 10:50:57 +00:00
2016-10-10 19:03:55 +00:00
void gl_LoadExtensions ( ) ;
2016-10-16 20:40:08 +00:00
void gl_PrintStartupLog ( ) ;
# ifndef WIN32
// This has to be in this file because system headers conflict Doom headers
2016-10-16 21:18:16 +00:00
DFrameBuffer * CreateGLSWFrameBuffer ( int width , int height , bool bgra , bool fullscreen )
2016-10-16 20:40:08 +00:00
{
2016-10-16 21:18:16 +00:00
return new OpenGLSWFrameBuffer ( NULL , width , height , 32 , 60 , fullscreen , bgra ) ;
2016-10-16 20:40:08 +00:00
}
# endif
2016-10-10 19:03:55 +00:00
const char * const OpenGLSWFrameBuffer : : ShaderDefines [ OpenGLSWFrameBuffer : : NUM_SHADERS ] =
2016-10-09 10:50:57 +00:00
{
2016-10-16 20:40:08 +00:00
" #define ENORMALCOLOR " , // NormalColor
" #define ENORMALCOLOR \n #define PALTEX " , // NormalColorPal
" #define ENORMALCOLOR \n #define INVERT " , // NormalColorInv
" #define ENORMALCOLOR \n #define PALTEX \n #define INVERT " , // NormalColorPalInv
2016-10-10 05:39:02 +00:00
2016-10-16 20:40:08 +00:00
" #define EREDTOALPHA " , // RedToAlpha
" #define EREDTOALPHA \n #define INVERT " , // RedToAlphaInv
2016-10-10 05:39:02 +00:00
2016-10-10 19:03:55 +00:00
" #define EVERTEXCOLOR " , // VertexColor
2016-10-10 05:39:02 +00:00
2016-10-16 20:40:08 +00:00
" #define ESPECIALCOLORMAP \n " , // SpecialColormap
" #define ESPECIALCOLORMAP \n #define PALTEX " , // SpecialColorMapPal
2016-10-10 05:39:02 +00:00
2016-10-16 20:40:08 +00:00
" #define EINGAMECOLORMAP " , // InGameColormap
" #define EINGAMECOLORMAP \n #define DESAT " , // InGameColormapDesat
" #define EINGAMECOLORMAP \n #define INVERT " , // InGameColormapInv
" #define EINGAMECOLORMAP \n #define INVERT \n #define DESAT " , // InGameColormapInvDesat
" #define EINGAMECOLORMAP \n #define PALTEX \n " , // InGameColormapPal
" #define EINGAMECOLORMAP \n #define PALTEX \n #define DESAT " , // InGameColormapPalDesat
" #define EINGAMECOLORMAP \n #define PALTEX \n #define INVERT " , // InGameColormapPalInv
" #define EINGAMECOLORMAP \n #define PALTEX \n #define INVERT \n #define DESAT " , // InGameColormapPalInvDesat
2016-10-10 05:39:02 +00:00
2016-10-10 19:03:55 +00:00
" #define EBURNWIPE " , // BurnWipe
" #define EGAMMACORRECTION " , // GammaCorrection
2016-10-09 10:50:57 +00:00
} ;
2016-10-11 13:43:12 +00:00
OpenGLSWFrameBuffer : : OpenGLSWFrameBuffer ( void * hMonitor , int width , int height , int bits , int refreshHz , bool fullscreen , bool bgra ) :
2016-10-12 06:05:31 +00:00
Super ( hMonitor , ClampWidth ( width ) , ClampHeight ( height ) , bits , refreshHz , fullscreen , bgra )
2016-10-09 10:50:57 +00:00
{
VertexBuffer = nullptr ;
IndexBuffer = nullptr ;
FBTexture = nullptr ;
InitialWipeScreen = nullptr ;
ScreenshotTexture = nullptr ;
FinalWipeScreen = nullptr ;
PaletteTexture = nullptr ;
for ( int i = 0 ; i < NUM_SHADERS ; + + i )
{
Shaders [ i ] = nullptr ;
}
2017-01-24 00:43:45 +00:00
2016-10-09 10:50:57 +00:00
BlendingRect . left = 0 ;
BlendingRect . top = 0 ;
2016-10-12 05:34:07 +00:00
BlendingRect . right = Width ;
BlendingRect . bottom = Height ;
2016-10-09 10:50:57 +00:00
In2D = 0 ;
Palettes = nullptr ;
Textures = nullptr ;
Accel2D = true ;
GatheringWipeScreen = false ;
ScreenWipe = nullptr ;
InScene = false ;
QuadExtra = new BufferedTris [ MAX_QUAD_BATCH ] ;
2016-10-10 22:03:46 +00:00
memset ( QuadExtra , 0 , sizeof ( BufferedTris ) * MAX_QUAD_BATCH ) ;
2016-10-09 10:50:57 +00:00
Atlases = nullptr ;
PixelDoubling = 0 ;
Gamma = 1.0 ;
FlashColor0 = 0 ;
FlashColor1 = 0xFFFFFFFF ;
FlashColor = 0 ;
FlashAmount = 0 ;
NeedGammaUpdate = false ;
NeedPalUpdate = false ;
if ( MemBuffer = = nullptr )
{
return ;
}
memcpy ( SourcePalette , GPalette . BaseColors , sizeof ( PalEntry ) * 256 ) ;
2017-01-24 00:43:45 +00:00
// To do: this needs to cooperate with the same static in OpenGLFrameBuffer::InitializeState
static bool first = true ;
if ( first )
{
2017-04-07 00:17:34 +00:00
if ( ogl_LoadFunctions ( ) = = ogl_LOAD_FAILED )
{
Printf ( " OpenGL load failed. No OpenGL acceleration will be used. \n " ) ;
return ;
}
2017-01-24 00:43:45 +00:00
}
2017-04-07 01:03:21 +00:00
const char * glversion = ( const char * ) glGetString ( GL_VERSION ) ;
bool isGLES = ( glversion & & strlen ( glversion ) > 10 & & memcmp ( glversion , " OpenGL ES " , 10 ) = = 0 ) ;
2017-05-08 17:30:51 +00:00
2017-06-05 09:41:54 +00:00
UCVarValue value ;
2017-05-08 17:30:51 +00:00
// GL 3.0 is mostly broken on MESA drivers which really are the only relevant case here that doesn't fulfill the requirements based on version number alone.
# ifdef _WIN32
2017-06-05 09:41:54 +00:00
value . Bool = ! ogl_IsVersionGEQ ( 3 , 0 ) ;
2017-05-08 17:30:51 +00:00
# else
2017-06-05 09:41:54 +00:00
value . Bool = ! ogl_IsVersionGEQ ( 3 , 1 ) ;
2017-05-08 17:30:51 +00:00
# endif
2017-06-05 09:41:54 +00:00
gl_legacy_mode . ForceSet ( value , CVAR_Bool ) ;
2017-04-07 22:19:39 +00:00
if ( ! isGLES & & ogl_IsVersionGEQ ( 3 , 0 ) = = 0 )
2017-04-07 01:03:21 +00:00
{
Printf ( " OpenGL acceleration requires at least OpenGL 3.0. No Acceleration will be used. \n " ) ;
return ;
}
2017-01-24 00:43:45 +00:00
gl_LoadExtensions ( ) ;
2017-04-07 13:45:08 +00:00
if ( gl . legacyMode )
{
Printf ( " Legacy OpenGL path is active. No Acceleration will be used. \n " ) ;
return ;
}
2017-01-24 00:43:45 +00:00
InitializeState ( ) ;
if ( first )
{
gl_PrintStartupLog ( ) ;
first = false ;
}
if ( ! glGetString )
return ;
// SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver.
// If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed!
Super : : SetVSync ( vid_vsync ) ;
Debug = std : : make_shared < FGLDebug > ( ) ;
Debug - > Update ( ) ;
2016-10-10 19:03:55 +00:00
//Windowed = !(static_cast<Win32Video *>(Video)->GoFullscreen(fullscreen));
2016-10-09 10:50:57 +00:00
TrueHeight = height ;
2017-01-24 00:43:45 +00:00
Valid = CreateResources ( ) ;
if ( Valid )
SetInitialState ( ) ;
2016-10-09 10:50:57 +00:00
}
OpenGLSWFrameBuffer : : ~ OpenGLSWFrameBuffer ( )
{
ReleaseResources ( ) ;
delete [ ] QuadExtra ;
}
2016-10-16 20:40:08 +00:00
void * OpenGLSWFrameBuffer : : MapBuffer ( int target , int size )
{
if ( glMapBufferRange )
{
return ( FBVERTEX * ) glMapBufferRange ( target , 0 , size , GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT ) ;
}
else
{
glBufferData ( target , size , nullptr , GL_STREAM_DRAW ) ;
return glMapBuffer ( target , GL_WRITE_ONLY ) ;
}
}
2016-10-11 11:09:32 +00:00
OpenGLSWFrameBuffer : : HWFrameBuffer : : ~ HWFrameBuffer ( )
{
if ( Framebuffer ! = 0 ) glDeleteFramebuffers ( 1 , ( GLuint * ) & Framebuffer ) ;
2017-05-27 00:31:15 +00:00
Texture . reset ( ) ;
2016-10-11 11:09:32 +00:00
}
2016-10-10 19:03:55 +00:00
OpenGLSWFrameBuffer : : HWTexture : : ~ HWTexture ( )
{
if ( Texture ! = 0 ) glDeleteTextures ( 1 , ( GLuint * ) & Texture ) ;
2016-10-11 08:27:18 +00:00
if ( Buffers [ 0 ] ! = 0 ) glDeleteBuffers ( 2 , ( GLuint * ) Buffers ) ;
2016-10-10 19:03:55 +00:00
}
OpenGLSWFrameBuffer : : HWVertexBuffer : : ~ HWVertexBuffer ( )
{
if ( VertexArray ! = 0 ) glDeleteVertexArrays ( 1 , ( GLuint * ) & VertexArray ) ;
if ( Buffer ! = 0 ) glDeleteBuffers ( 1 , ( GLuint * ) & Buffer ) ;
}
OpenGLSWFrameBuffer : : FBVERTEX * OpenGLSWFrameBuffer : : HWVertexBuffer : : Lock ( )
{
glBindBuffer ( GL_ARRAY_BUFFER , Buffer ) ;
2016-10-16 20:40:08 +00:00
return ( FBVERTEX * ) MapBuffer ( GL_ARRAY_BUFFER , Size ) ;
2016-10-10 19:03:55 +00:00
}
void OpenGLSWFrameBuffer : : HWVertexBuffer : : Unlock ( )
{
glUnmapBuffer ( GL_ARRAY_BUFFER ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
}
OpenGLSWFrameBuffer : : HWIndexBuffer : : ~ HWIndexBuffer ( )
{
if ( Buffer ! = 0 ) glDeleteBuffers ( 1 , ( GLuint * ) & Buffer ) ;
}
uint16_t * OpenGLSWFrameBuffer : : HWIndexBuffer : : Lock ( )
{
glGetIntegerv ( GL_ELEMENT_ARRAY_BUFFER_BINDING , & LockedOldBinding ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , Buffer ) ;
2016-10-16 20:40:08 +00:00
return ( uint16_t * ) MapBuffer ( GL_ELEMENT_ARRAY_BUFFER , Size ) ;
2016-10-10 19:03:55 +00:00
}
void OpenGLSWFrameBuffer : : HWIndexBuffer : : Unlock ( )
{
glUnmapBuffer ( GL_ELEMENT_ARRAY_BUFFER ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , LockedOldBinding ) ;
}
OpenGLSWFrameBuffer : : HWPixelShader : : ~ HWPixelShader ( )
{
if ( Program ! = 0 ) glDeleteProgram ( Program ) ;
if ( VertexShader ! = 0 ) glDeleteShader ( VertexShader ) ;
if ( FragmentShader ! = 0 ) glDeleteShader ( FragmentShader ) ;
}
2017-05-27 00:31:15 +00:00
std : : unique_ptr < OpenGLSWFrameBuffer : : HWFrameBuffer > OpenGLSWFrameBuffer : : CreateFrameBuffer ( const FString & name , int width , int height )
2016-10-11 11:09:32 +00:00
{
2017-04-10 23:25:44 +00:00
std : : unique_ptr < HWFrameBuffer > fb ( new HWFrameBuffer ( ) ) ;
2017-01-25 06:18:26 +00:00
GLint format = GL_RGBA16F ;
if ( gl . es ) format = GL_RGB ;
2016-10-11 11:09:32 +00:00
2017-05-27 00:31:15 +00:00
fb - > Texture = CreateTexture ( name , width , height , 1 , format ) ;
if ( ! fb - > Texture )
2016-10-11 11:09:32 +00:00
{
2017-05-27 00:31:15 +00:00
return nullptr ;
2016-10-11 11:09:32 +00:00
}
glGenFramebuffers ( 1 , ( GLuint * ) & fb - > Framebuffer ) ;
GLint oldFramebufferBinding = 0 , oldTextureBinding = 0 ;
glGetIntegerv ( GL_FRAMEBUFFER_BINDING , & oldFramebufferBinding ) ;
glGetIntegerv ( GL_TEXTURE_BINDING_2D , & oldTextureBinding ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , fb - > Framebuffer ) ;
FGLDebug : : LabelObject ( GL_FRAMEBUFFER , fb - > Framebuffer , name ) ;
glBindTexture ( GL_TEXTURE_2D , fb - > Texture - > Texture ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , fb - > Texture - > Texture , 0 ) ;
GLenum result = glCheckFramebufferStatus ( GL_FRAMEBUFFER ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , oldFramebufferBinding ) ;
glBindTexture ( GL_TEXTURE_2D , oldTextureBinding ) ;
if ( result ! = GL_FRAMEBUFFER_COMPLETE )
{
2017-01-25 06:18:26 +00:00
Printf ( " Framebuffer is not complete \n " ) ;
2017-05-27 00:31:15 +00:00
return nullptr ;
2016-10-11 11:09:32 +00:00
}
2017-05-27 04:27:36 +00:00
return fb ;
2016-10-11 11:09:32 +00:00
}
2017-05-27 00:31:15 +00:00
std : : unique_ptr < OpenGLSWFrameBuffer : : HWPixelShader > OpenGLSWFrameBuffer : : CreatePixelShader ( FString vertexsrc , FString fragmentsrc , const FString & defines )
2016-10-10 05:39:02 +00:00
{
2017-04-10 23:25:44 +00:00
std : : unique_ptr < HWPixelShader > shader ( new HWPixelShader ( ) ) ;
2016-10-10 05:39:02 +00:00
shader - > Program = glCreateProgram ( ) ;
2017-05-27 00:31:15 +00:00
if ( shader - > Program = = 0 ) { Printf ( " glCreateProgram failed. Disabling OpenGL hardware acceleration. \n " ) ; return nullptr ; }
2016-10-10 05:39:02 +00:00
shader - > VertexShader = glCreateShader ( GL_VERTEX_SHADER ) ;
2017-05-27 00:31:15 +00:00
if ( shader - > VertexShader = = 0 ) { Printf ( " glCreateShader(GL_VERTEX_SHADER) failed. Disabling OpenGL hardware acceleration. \n " ) ; return nullptr ; }
2016-10-10 05:39:02 +00:00
shader - > FragmentShader = glCreateShader ( GL_FRAGMENT_SHADER ) ;
2017-05-27 00:31:15 +00:00
if ( shader - > FragmentShader = = 0 ) { Printf ( " glCreateShader(GL_FRAGMENT_SHADER) failed. Disabling OpenGL hardware acceleration. \n " ) ; return nullptr ; }
2016-10-16 20:40:08 +00:00
int maxGlslVersion = 330 ;
int shaderVersion = MIN ( ( int ) round ( gl . glslversion * 10 ) * 10 , maxGlslVersion ) ;
FString prefix ;
prefix . AppendFormat ( " #version %d \n %s \n #line 0 \n " , shaderVersion , defines . GetChars ( ) ) ;
2017-01-25 06:18:26 +00:00
//Printf("Shader prefix: %s", prefix.GetChars());
2016-10-16 20:40:08 +00:00
vertexsrc = prefix + vertexsrc ;
fragmentsrc = prefix + fragmentsrc ;
2016-10-10 19:03:55 +00:00
2016-10-10 05:39:02 +00:00
{
2016-10-10 19:03:55 +00:00
int lengths [ 1 ] = { ( int ) vertexsrc . Len ( ) } ;
const char * sources [ 1 ] = { vertexsrc . GetChars ( ) } ;
2016-10-10 05:39:02 +00:00
glShaderSource ( shader - > VertexShader , 1 , sources , lengths ) ;
glCompileShader ( shader - > VertexShader ) ;
}
{
2016-10-10 19:03:55 +00:00
int lengths [ 1 ] = { ( int ) fragmentsrc . Len ( ) } ;
const char * sources [ 1 ] = { fragmentsrc . GetChars ( ) } ;
2016-10-10 05:39:02 +00:00
glShaderSource ( shader - > FragmentShader , 1 , sources , lengths ) ;
glCompileShader ( shader - > FragmentShader ) ;
}
GLint status = 0 ;
2016-10-10 19:03:55 +00:00
int errorShader = shader - > VertexShader ;
2016-10-10 05:39:02 +00:00
glGetShaderiv ( shader - > VertexShader , GL_COMPILE_STATUS , & status ) ;
2016-10-10 19:03:55 +00:00
if ( status ! = GL_FALSE ) { errorShader = shader - > FragmentShader ; glGetShaderiv ( shader - > FragmentShader , GL_COMPILE_STATUS , & status ) ; }
2016-10-10 05:39:02 +00:00
if ( status = = GL_FALSE )
{
2016-10-16 20:40:08 +00:00
static char buffer [ 10000 ] ;
2016-10-10 19:03:55 +00:00
GLsizei length = 0 ;
buffer [ 0 ] = 0 ;
2016-10-16 20:40:08 +00:00
glGetShaderInfoLog ( errorShader , 10000 , & length , buffer ) ;
//Printf("Shader compile failed: %s", buffer);
2016-10-10 19:03:55 +00:00
2017-05-27 00:31:15 +00:00
return nullptr ;
2016-10-10 05:39:02 +00:00
}
glAttachShader ( shader - > Program , shader - > VertexShader ) ;
glAttachShader ( shader - > Program , shader - > FragmentShader ) ;
glBindFragDataLocation ( shader - > Program , 0 , " FragColor " ) ;
2016-10-16 20:40:08 +00:00
glBindAttribLocation ( shader - > Program , 0 , " AttrPosition " ) ;
glBindAttribLocation ( shader - > Program , 1 , " AttrColor0 " ) ;
glBindAttribLocation ( shader - > Program , 2 , " AttrColor1 " ) ;
glBindAttribLocation ( shader - > Program , 3 , " AttrTexCoord0 " ) ;
2016-10-10 05:39:02 +00:00
glLinkProgram ( shader - > Program ) ;
glGetProgramiv ( shader - > Program , GL_LINK_STATUS , & status ) ;
if ( status = = GL_FALSE )
{
2016-10-16 20:40:08 +00:00
static char buffer [ 10000 ] ;
GLsizei length = 0 ;
buffer [ 0 ] = 0 ;
glGetProgramInfoLog ( shader - > Program , 10000 , & length , buffer ) ;
2017-01-25 06:18:26 +00:00
//Printf("Shader link failed: %s", buffer);
2016-10-16 20:40:08 +00:00
2017-05-27 00:31:15 +00:00
return nullptr ;
2016-10-10 05:39:02 +00:00
}
2016-10-10 22:03:46 +00:00
shader - > ConstantLocations [ PSCONST_Desaturation ] = glGetUniformLocation ( shader - > Program , " Desaturation " ) ;
shader - > ConstantLocations [ PSCONST_PaletteMod ] = glGetUniformLocation ( shader - > Program , " PaletteMod " ) ;
shader - > ConstantLocations [ PSCONST_Weights ] = glGetUniformLocation ( shader - > Program , " Weights " ) ;
shader - > ConstantLocations [ PSCONST_Gamma ] = glGetUniformLocation ( shader - > Program , " Gamma " ) ;
2016-10-11 08:27:18 +00:00
shader - > ConstantLocations [ PSCONST_ScreenSize ] = glGetUniformLocation ( shader - > Program , " ScreenSize " ) ;
2016-10-10 22:03:46 +00:00
shader - > ImageLocation = glGetUniformLocation ( shader - > Program , " Image " ) ;
shader - > PaletteLocation = glGetUniformLocation ( shader - > Program , " Palette " ) ;
shader - > NewScreenLocation = glGetUniformLocation ( shader - > Program , " NewScreen " ) ;
shader - > BurnLocation = glGetUniformLocation ( shader - > Program , " Burn " ) ;
2016-10-10 05:39:02 +00:00
2017-05-27 04:27:36 +00:00
return shader ;
2016-10-10 05:39:02 +00:00
}
2017-05-27 00:31:15 +00:00
std : : unique_ptr < OpenGLSWFrameBuffer : : HWVertexBuffer > OpenGLSWFrameBuffer : : CreateVertexBuffer ( int size )
2016-10-10 05:39:02 +00:00
{
2017-04-10 23:25:44 +00:00
std : : unique_ptr < HWVertexBuffer > obj ( new HWVertexBuffer ( ) ) ;
2016-10-10 05:39:02 +00:00
2016-10-11 08:27:18 +00:00
obj - > Size = size ;
2016-10-10 05:39:02 +00:00
GLint oldBinding = 0 ;
glGetIntegerv ( GL_VERTEX_ARRAY_BINDING , & oldBinding ) ;
glGenVertexArrays ( 1 , ( GLuint * ) & obj - > VertexArray ) ;
glGenBuffers ( 1 , ( GLuint * ) & obj - > Buffer ) ;
glBindVertexArray ( obj - > VertexArray ) ;
glBindBuffer ( GL_ARRAY_BUFFER , obj - > Buffer ) ;
2016-10-11 08:27:18 +00:00
FGLDebug : : LabelObject ( GL_BUFFER , obj - > Buffer , " VertexBuffer " ) ;
2016-10-10 05:39:02 +00:00
glBufferData ( GL_ARRAY_BUFFER , size , nullptr , GL_STREAM_DRAW ) ;
glEnableVertexAttribArray ( 0 ) ;
glEnableVertexAttribArray ( 1 ) ;
glEnableVertexAttribArray ( 2 ) ;
glEnableVertexAttribArray ( 3 ) ;
glVertexAttribPointer ( 0 , 4 , GL_FLOAT , GL_FALSE , sizeof ( FBVERTEX ) , ( const GLvoid * ) offsetof ( FBVERTEX , x ) ) ;
glVertexAttribPointer ( 1 , 4 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof ( FBVERTEX ) , ( const GLvoid * ) offsetof ( FBVERTEX , color0 ) ) ;
glVertexAttribPointer ( 2 , 4 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof ( FBVERTEX ) , ( const GLvoid * ) offsetof ( FBVERTEX , color1 ) ) ;
glVertexAttribPointer ( 3 , 2 , GL_FLOAT , GL_FALSE , sizeof ( FBVERTEX ) , ( const GLvoid * ) offsetof ( FBVERTEX , tu ) ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindVertexArray ( oldBinding ) ;
2017-05-27 04:27:36 +00:00
return obj ;
2016-10-10 05:39:02 +00:00
}
2017-05-27 00:31:15 +00:00
std : : unique_ptr < OpenGLSWFrameBuffer : : HWIndexBuffer > OpenGLSWFrameBuffer : : CreateIndexBuffer ( int size )
2016-10-10 05:39:02 +00:00
{
2017-04-10 23:25:44 +00:00
std : : unique_ptr < HWIndexBuffer > obj ( new HWIndexBuffer ( ) ) ;
2016-10-10 05:39:02 +00:00
2016-10-11 08:27:18 +00:00
obj - > Size = size ;
2016-10-10 05:39:02 +00:00
GLint oldBinding = 0 ;
glGetIntegerv ( GL_ELEMENT_ARRAY_BUFFER_BINDING , & oldBinding ) ;
glGenBuffers ( 1 , ( GLuint * ) & obj - > Buffer ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , obj - > Buffer ) ;
2016-10-11 08:27:18 +00:00
FGLDebug : : LabelObject ( GL_BUFFER , obj - > Buffer , " IndexBuffer " ) ;
2016-10-10 05:39:02 +00:00
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , size , nullptr , GL_STREAM_DRAW ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , oldBinding ) ;
2017-05-27 04:27:36 +00:00
return obj ;
2016-10-10 05:39:02 +00:00
}
2017-05-27 00:31:15 +00:00
std : : unique_ptr < OpenGLSWFrameBuffer : : HWTexture > OpenGLSWFrameBuffer : : CreateTexture ( const FString & name , int width , int height , int levels , int format )
2016-10-10 05:39:02 +00:00
{
2017-04-10 23:25:44 +00:00
std : : unique_ptr < HWTexture > obj ( new HWTexture ( ) ) ;
2016-10-10 05:39:02 +00:00
2016-10-10 19:03:55 +00:00
obj - > Format = format ;
2016-10-10 05:39:02 +00:00
GLint oldBinding = 0 ;
glGetIntegerv ( GL_TEXTURE_BINDING_2D , & oldBinding ) ;
glGenTextures ( 1 , ( GLuint * ) & obj - > Texture ) ;
glBindTexture ( GL_TEXTURE_2D , obj - > Texture ) ;
GLenum srcformat ;
switch ( format )
{
2017-01-25 06:18:26 +00:00
case GL_RGB : srcformat = GL_RGB ; break ;
2016-10-10 05:39:02 +00:00
case GL_R8 : srcformat = GL_RED ; break ;
2017-01-25 06:18:26 +00:00
case GL_RGBA8 : srcformat = gl . es ? GL_RGBA : GL_BGRA ; break ;
2016-10-11 11:09:32 +00:00
case GL_RGBA16F : srcformat = GL_RGBA ; break ;
2016-10-10 05:39:02 +00:00
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT : srcformat = GL_RGB ; break ;
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : srcformat = GL_RGBA ; break ;
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT : srcformat = GL_RGBA ; break ;
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : srcformat = GL_RGBA ; break ;
default :
I_FatalError ( " Unknown format passed to CreateTexture " ) ;
2017-05-27 04:27:36 +00:00
return nullptr ;
2016-10-10 05:39:02 +00:00
}
glTexImage2D ( GL_TEXTURE_2D , 0 , format , width , height , 0 , srcformat , GL_UNSIGNED_BYTE , nullptr ) ;
2016-10-10 19:03:55 +00:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
2016-10-10 05:39:02 +00:00
2016-10-10 22:03:46 +00:00
FGLDebug : : LabelObject ( GL_TEXTURE , obj - > Texture , name ) ;
2016-10-10 05:39:02 +00:00
glBindTexture ( GL_TEXTURE_2D , oldBinding ) ;
2017-05-27 04:27:36 +00:00
return obj ;
2016-10-10 05:39:02 +00:00
}
2017-05-27 00:31:15 +00:00
std : : unique_ptr < OpenGLSWFrameBuffer : : HWTexture > OpenGLSWFrameBuffer : : CopyCurrentScreen ( )
2016-10-11 12:37:57 +00:00
{
2017-04-10 23:25:44 +00:00
std : : unique_ptr < HWTexture > obj ( new HWTexture ( ) ) ;
2016-10-11 12:37:57 +00:00
obj - > Format = GL_RGBA16F ;
GLint oldBinding = 0 ;
glGetIntegerv ( GL_TEXTURE_BINDING_2D , & oldBinding ) ;
glGenTextures ( 1 , ( GLuint * ) & obj - > Texture ) ;
glBindTexture ( GL_TEXTURE_2D , obj - > Texture ) ;
glCopyTexImage2D ( GL_TEXTURE_2D , 0 , obj - > Format , 0 , 0 , Width , Height , 0 ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
FGLDebug : : LabelObject ( GL_TEXTURE , obj - > Texture , " CopyCurrentScreen " ) ;
glBindTexture ( GL_TEXTURE_2D , oldBinding ) ;
2017-05-27 04:27:36 +00:00
return obj ;
2016-10-11 12:37:57 +00:00
}
2016-10-10 19:03:55 +00:00
void OpenGLSWFrameBuffer : : SetGammaRamp ( const GammaRamp * ramp )
{
}
2016-10-10 05:39:02 +00:00
void OpenGLSWFrameBuffer : : SetPixelShaderConstantF ( int uniformIndex , const float * data , int vec4fcount )
{
2016-10-11 08:27:18 +00:00
assert ( uniformIndex < NumPSCONST & & vec4fcount = = 1 ) ; // This emulation of d3d9 only works for very simple stuff
2016-10-10 22:03:46 +00:00
for ( int i = 0 ; i < 4 ; i + + )
ShaderConstants [ uniformIndex * 4 + i ] = data [ i ] ;
if ( CurrentShader & & CurrentShader - > ConstantLocations [ uniformIndex ] ! = - 1 )
glUniform4fv ( CurrentShader - > ConstantLocations [ uniformIndex ] , vec4fcount , data ) ;
2016-10-10 05:39:02 +00:00
}
void OpenGLSWFrameBuffer : : SetHWPixelShader ( HWPixelShader * shader )
{
2016-10-10 22:03:46 +00:00
if ( shader ! = CurrentShader )
{
if ( shader )
{
glUseProgram ( shader - > Program ) ;
2016-10-11 08:27:18 +00:00
for ( int i = 0 ; i < NumPSCONST ; i + + )
2016-10-10 22:03:46 +00:00
{
if ( shader - > ConstantLocations [ i ] ! = - 1 )
glUniform4fv ( shader - > ConstantLocations [ i ] , 1 , & ShaderConstants [ i * 4 ] ) ;
}
}
else
{
glUseProgram ( 0 ) ;
}
}
CurrentShader = shader ;
2016-10-10 05:39:02 +00:00
}
void OpenGLSWFrameBuffer : : SetStreamSource ( HWVertexBuffer * vertexBuffer )
{
if ( vertexBuffer )
glBindVertexArray ( vertexBuffer - > VertexArray ) ;
else
glBindVertexArray ( 0 ) ;
}
void OpenGLSWFrameBuffer : : SetIndices ( HWIndexBuffer * indexBuffer )
{
if ( indexBuffer )
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , indexBuffer - > Buffer ) ;
else
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
}
void OpenGLSWFrameBuffer : : DrawTriangleFans ( int count , const FBVERTEX * vertices )
{
2016-10-10 22:03:46 +00:00
count = 2 + count ;
2016-10-10 05:39:02 +00:00
GLint oldBinding = 0 ;
glGetIntegerv ( GL_VERTEX_ARRAY_BINDING , & oldBinding ) ;
if ( ! StreamVertexBuffer )
{
2017-04-10 23:25:44 +00:00
StreamVertexBuffer . reset ( new HWVertexBuffer ( ) ) ;
2016-10-10 05:39:02 +00:00
glGenVertexArrays ( 1 , ( GLuint * ) & StreamVertexBuffer - > VertexArray ) ;
glGenBuffers ( 1 , ( GLuint * ) & StreamVertexBuffer - > Buffer ) ;
glBindVertexArray ( StreamVertexBuffer - > VertexArray ) ;
glBindBuffer ( GL_ARRAY_BUFFER , StreamVertexBuffer - > Buffer ) ;
glBufferData ( GL_ARRAY_BUFFER , count * sizeof ( FBVERTEX ) , vertices , GL_STREAM_DRAW ) ;
glEnableVertexAttribArray ( 0 ) ;
glEnableVertexAttribArray ( 1 ) ;
glEnableVertexAttribArray ( 2 ) ;
glEnableVertexAttribArray ( 3 ) ;
glVertexAttribPointer ( 0 , 4 , GL_FLOAT , GL_FALSE , sizeof ( FBVERTEX ) , ( const GLvoid * ) offsetof ( FBVERTEX , x ) ) ;
glVertexAttribPointer ( 1 , 4 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof ( FBVERTEX ) , ( const GLvoid * ) offsetof ( FBVERTEX , color0 ) ) ;
glVertexAttribPointer ( 2 , 4 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof ( FBVERTEX ) , ( const GLvoid * ) offsetof ( FBVERTEX , color1 ) ) ;
glVertexAttribPointer ( 3 , 2 , GL_FLOAT , GL_FALSE , sizeof ( FBVERTEX ) , ( const GLvoid * ) offsetof ( FBVERTEX , tu ) ) ;
}
else
{
glBindVertexArray ( StreamVertexBuffer - > VertexArray ) ;
glBindBuffer ( GL_ARRAY_BUFFER , StreamVertexBuffer - > Buffer ) ;
glBufferData ( GL_ARRAY_BUFFER , count * sizeof ( FBVERTEX ) , vertices , GL_STREAM_DRAW ) ;
}
glDrawArrays ( GL_TRIANGLE_FAN , 0 , count ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindVertexArray ( oldBinding ) ;
}
2016-10-11 12:37:57 +00:00
void OpenGLSWFrameBuffer : : DrawTriangleFans ( int count , const BURNVERTEX * vertices )
{
count = 2 + count ;
GLint oldBinding = 0 ;
glGetIntegerv ( GL_VERTEX_ARRAY_BINDING , & oldBinding ) ;
if ( ! StreamVertexBufferBurn )
{
2017-04-10 23:25:44 +00:00
StreamVertexBufferBurn . reset ( new HWVertexBuffer ( ) ) ;
2016-10-11 12:37:57 +00:00
glGenVertexArrays ( 1 , ( GLuint * ) & StreamVertexBufferBurn - > VertexArray ) ;
glGenBuffers ( 1 , ( GLuint * ) & StreamVertexBufferBurn - > Buffer ) ;
glBindVertexArray ( StreamVertexBufferBurn - > VertexArray ) ;
glBindBuffer ( GL_ARRAY_BUFFER , StreamVertexBufferBurn - > Buffer ) ;
glBufferData ( GL_ARRAY_BUFFER , count * sizeof ( BURNVERTEX ) , vertices , GL_STREAM_DRAW ) ;
glEnableVertexAttribArray ( 0 ) ;
glEnableVertexAttribArray ( 1 ) ;
glVertexAttribPointer ( 0 , 4 , GL_FLOAT , GL_FALSE , sizeof ( BURNVERTEX ) , ( const GLvoid * ) offsetof ( BURNVERTEX , x ) ) ;
glVertexAttribPointer ( 1 , 4 , GL_FLOAT , GL_FALSE , sizeof ( BURNVERTEX ) , ( const GLvoid * ) offsetof ( BURNVERTEX , tu0 ) ) ;
}
else
{
glBindVertexArray ( StreamVertexBufferBurn - > VertexArray ) ;
glBindBuffer ( GL_ARRAY_BUFFER , StreamVertexBufferBurn - > Buffer ) ;
glBufferData ( GL_ARRAY_BUFFER , count * sizeof ( BURNVERTEX ) , vertices , GL_STREAM_DRAW ) ;
}
glDrawArrays ( GL_TRIANGLE_FAN , 0 , count ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindVertexArray ( oldBinding ) ;
}
2016-10-10 05:39:02 +00:00
void OpenGLSWFrameBuffer : : DrawPoints ( int count , const FBVERTEX * vertices )
{
GLint oldBinding = 0 ;
glGetIntegerv ( GL_VERTEX_ARRAY_BINDING , & oldBinding ) ;
if ( ! StreamVertexBuffer )
{
2017-04-10 23:25:44 +00:00
StreamVertexBuffer . reset ( new HWVertexBuffer ( ) ) ;
2016-10-10 05:39:02 +00:00
glGenVertexArrays ( 1 , ( GLuint * ) & StreamVertexBuffer - > VertexArray ) ;
glGenBuffers ( 1 , ( GLuint * ) & StreamVertexBuffer - > Buffer ) ;
glBindVertexArray ( StreamVertexBuffer - > VertexArray ) ;
glBindBuffer ( GL_ARRAY_BUFFER , StreamVertexBuffer - > Buffer ) ;
glBufferData ( GL_ARRAY_BUFFER , count * sizeof ( FBVERTEX ) , vertices , GL_STREAM_DRAW ) ;
glEnableVertexAttribArray ( 0 ) ;
glEnableVertexAttribArray ( 1 ) ;
glEnableVertexAttribArray ( 2 ) ;
glEnableVertexAttribArray ( 3 ) ;
glVertexAttribPointer ( 0 , 4 , GL_FLOAT , GL_FALSE , sizeof ( FBVERTEX ) , ( const GLvoid * ) offsetof ( FBVERTEX , x ) ) ;
glVertexAttribPointer ( 1 , 4 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof ( FBVERTEX ) , ( const GLvoid * ) offsetof ( FBVERTEX , color0 ) ) ;
glVertexAttribPointer ( 2 , 4 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof ( FBVERTEX ) , ( const GLvoid * ) offsetof ( FBVERTEX , color1 ) ) ;
glVertexAttribPointer ( 3 , 2 , GL_FLOAT , GL_FALSE , sizeof ( FBVERTEX ) , ( const GLvoid * ) offsetof ( FBVERTEX , tu ) ) ;
}
else
{
glBindVertexArray ( StreamVertexBuffer - > VertexArray ) ;
glBindBuffer ( GL_ARRAY_BUFFER , StreamVertexBuffer - > Buffer ) ;
glBufferData ( GL_ARRAY_BUFFER , count * sizeof ( FBVERTEX ) , vertices , GL_STREAM_DRAW ) ;
}
glDrawArrays ( GL_POINTS , 0 , count ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindVertexArray ( oldBinding ) ;
}
void OpenGLSWFrameBuffer : : DrawLineList ( int count )
{
2016-10-10 22:03:46 +00:00
glDrawArrays ( GL_LINES , 0 , count * 2 ) ;
2016-10-10 05:39:02 +00:00
}
void OpenGLSWFrameBuffer : : DrawTriangleList ( int minIndex , int numVertices , int startIndex , int primitiveCount )
{
glDrawRangeElements ( GL_TRIANGLES , minIndex , minIndex + numVertices - 1 , primitiveCount * 3 , GL_UNSIGNED_SHORT , ( const void * ) ( startIndex * sizeof ( uint16_t ) ) ) ;
}
void OpenGLSWFrameBuffer : : Present ( )
{
2016-10-12 05:34:07 +00:00
int clientWidth = GetClientWidth ( ) ;
int clientHeight = GetClientHeight ( ) ;
if ( clientWidth > 0 & & clientHeight > 0 )
{
glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ;
glViewport ( 0 , 0 , clientWidth , clientHeight ) ;
2016-10-11 11:09:32 +00:00
2016-10-12 05:34:07 +00:00
float scale = MIN ( clientWidth / ( float ) Width , clientHeight / ( float ) Height ) ;
int letterboxWidth = ( int ) round ( Width * scale ) ;
int letterboxHeight = ( int ) round ( Height * scale ) ;
int letterboxX = ( clientWidth - letterboxWidth ) / 2 ;
int letterboxY = ( clientHeight - letterboxHeight ) / 2 ;
2016-10-11 11:09:32 +00:00
2016-10-12 05:34:07 +00:00
DrawLetterbox ( letterboxX , letterboxY , letterboxWidth , letterboxHeight ) ;
glViewport ( letterboxX , letterboxY , letterboxWidth , letterboxHeight ) ;
FBVERTEX verts [ 4 ] ;
CalcFullscreenCoords ( verts , false , 0 , 0xFFFFFFFF ) ;
2017-05-27 00:31:15 +00:00
SetTexture ( 0 , OutputFB - > Texture . get ( ) ) ;
SetPixelShader ( Shaders [ SHADER_GammaCorrection ] . get ( ) ) ;
2016-10-12 05:34:07 +00:00
SetAlphaBlend ( 0 ) ;
EnableAlphaTest ( false ) ;
DrawTriangleFans ( 2 , verts ) ;
}
2016-10-11 11:09:32 +00:00
2016-10-10 05:39:02 +00:00
SwapBuffers ( ) ;
2016-10-11 11:09:32 +00:00
Debug - > Update ( ) ;
2016-10-12 05:34:07 +00:00
float screensize [ 4 ] = { ( float ) Width , ( float ) Height , 1.0f , 1.0f } ;
2016-10-11 08:27:18 +00:00
SetPixelShaderConstantF ( PSCONST_ScreenSize , screensize , 1 ) ;
2016-10-11 11:09:32 +00:00
glBindFramebuffer ( GL_FRAMEBUFFER , OutputFB - > Framebuffer ) ;
2016-10-12 05:34:07 +00:00
glViewport ( 0 , 0 , Width , Height ) ;
2016-10-10 05:39:02 +00:00
}
2016-10-09 10:50:57 +00:00
//==========================================================================
//
// OpenGLSWFrameBuffer :: SetInitialState
//
// Called after initial device creation and reset, when everything is set
// to OpenGL's defaults.
//
//==========================================================================
void OpenGLSWFrameBuffer : : SetInitialState ( )
{
2017-01-25 06:18:26 +00:00
if ( gl . es ) UseMappedMemBuffer = false ;
2016-10-11 08:27:18 +00:00
AlphaBlendEnabled = false ;
2016-10-09 10:50:57 +00:00
AlphaBlendOp = GL_FUNC_ADD ;
AlphaSrcBlend = 0 ;
AlphaDestBlend = 0 ;
CurPixelShader = nullptr ;
memset ( Constant , 0 , sizeof ( Constant ) ) ;
for ( unsigned i = 0 ; i < countof ( Texture ) ; + + i )
{
Texture [ i ] = nullptr ;
SamplerWrapS [ i ] = GL_CLAMP_TO_EDGE ;
SamplerWrapT [ i ] = GL_CLAMP_TO_EDGE ;
}
NeedGammaUpdate = true ;
NeedPalUpdate = true ;
// This constant is used for grayscaling weights (.xyz) and color inversion (.w)
float weights [ 4 ] = { 77 / 256.f , 143 / 256.f , 37 / 256.f , 1 } ;
SetPixelShaderConstantF ( PSCONST_Weights , weights , 1 ) ;
2016-10-12 05:34:07 +00:00
float screensize [ 4 ] = { ( float ) Width , ( float ) Height , 1.0f , 1.0f } ;
2016-10-11 08:27:18 +00:00
SetPixelShaderConstantF ( PSCONST_ScreenSize , screensize , 1 ) ;
AlphaTestEnabled = false ;
2016-10-09 10:50:57 +00:00
CurBorderColor = 0 ;
// Clear to black, just in case it wasn't done already.
glClearColor ( 0.0f , 0.0f , 0.0f , 1.0f ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: CreateResources
//
//==========================================================================
bool OpenGLSWFrameBuffer : : CreateResources ( )
{
Atlases = nullptr ;
if ( ! LoadShaders ( ) )
return false ;
2016-10-11 11:09:32 +00:00
2017-05-27 00:31:15 +00:00
OutputFB = CreateFrameBuffer ( " OutputFB " , Width , Height ) ;
if ( ! OutputFB )
2016-10-11 11:09:32 +00:00
return false ;
2017-01-25 06:18:26 +00:00
2016-10-11 11:09:32 +00:00
glBindFramebuffer ( GL_FRAMEBUFFER , OutputFB - > Framebuffer ) ;
2016-10-09 10:50:57 +00:00
if ( ! CreateFBTexture ( ) | |
! CreatePaletteTexture ( ) )
{
return false ;
}
if ( ! CreateVertexes ( ) )
{
return false ;
}
return true ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: LoadShaders
//
// Returns true if all required shaders were loaded. (Gamma and burn wipe
// are the only ones not considered "required".)
//
//==========================================================================
bool OpenGLSWFrameBuffer : : LoadShaders ( )
{
2016-10-10 19:03:55 +00:00
int lumpvert = Wads . CheckNumForFullName ( " shaders/glsl/swshader.vp " ) ;
int lumpfrag = Wads . CheckNumForFullName ( " shaders/glsl/swshader.fp " ) ;
if ( lumpvert < 0 | | lumpfrag < 0 )
return false ;
FString vertsource = Wads . ReadLump ( lumpvert ) . GetString ( ) ;
FString fragsource = Wads . ReadLump ( lumpfrag ) . GetString ( ) ;
2016-10-09 10:50:57 +00:00
FString shaderdir , shaderpath ;
2016-10-10 19:03:55 +00:00
unsigned int i ;
2016-10-09 10:50:57 +00:00
2016-10-10 19:03:55 +00:00
for ( i = 0 ; i < NUM_SHADERS ; + + i )
2016-10-09 10:50:57 +00:00
{
2016-10-10 19:03:55 +00:00
shaderpath = shaderdir ;
2017-05-27 00:31:15 +00:00
Shaders [ i ] = CreatePixelShader ( vertsource , fragsource , ShaderDefines [ i ] ) ;
if ( ! Shaders [ i ] & & i < SHADER_BurnWipe )
2016-10-09 10:50:57 +00:00
{
2016-10-10 19:03:55 +00:00
break ;
2016-10-09 10:50:57 +00:00
}
2016-10-10 22:03:46 +00:00
glUseProgram ( Shaders [ i ] - > Program ) ;
if ( Shaders [ i ] - > ImageLocation ! = - 1 ) glUniform1i ( Shaders [ i ] - > ImageLocation , 0 ) ;
if ( Shaders [ i ] - > PaletteLocation ! = - 1 ) glUniform1i ( Shaders [ i ] - > PaletteLocation , 1 ) ;
if ( Shaders [ i ] - > NewScreenLocation ! = - 1 ) glUniform1i ( Shaders [ i ] - > NewScreenLocation , 0 ) ;
if ( Shaders [ i ] - > BurnLocation ! = - 1 ) glUniform1i ( Shaders [ i ] - > BurnLocation , 1 ) ;
glUseProgram ( 0 ) ;
2016-10-09 10:50:57 +00:00
}
2016-10-10 19:03:55 +00:00
if ( i = = NUM_SHADERS )
{ // Success!
return true ;
}
// Failure. Release whatever managed to load (which is probably nothing.)
for ( i = 0 ; i < NUM_SHADERS ; + + i )
{
2017-05-27 00:31:15 +00:00
Shaders [ i ] . reset ( ) ;
2016-10-10 19:03:55 +00:00
}
2016-10-09 10:50:57 +00:00
return false ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: ReleaseResources
//
//==========================================================================
void OpenGLSWFrameBuffer : : ReleaseResources ( )
{
2016-10-16 20:40:08 +00:00
# ifdef WIN32
2016-10-09 10:50:57 +00:00
I_SaveWindowedPos ( ) ;
2016-10-16 20:40:08 +00:00
# endif
2016-10-09 10:50:57 +00:00
KillNativeTexs ( ) ;
KillNativePals ( ) ;
ReleaseDefaultPoolItems ( ) ;
2017-05-27 00:31:15 +00:00
ScreenshotTexture . reset ( ) ;
PaletteTexture . reset ( ) ;
2016-10-09 10:50:57 +00:00
for ( int i = 0 ; i < NUM_SHADERS ; + + i )
{
2017-05-27 00:31:15 +00:00
Shaders [ i ] . reset ( ) ;
2016-10-09 10:50:57 +00:00
}
if ( ScreenWipe ! = nullptr )
{
delete ScreenWipe ;
ScreenWipe = nullptr ;
}
Atlas * pack , * next ;
for ( pack = Atlases ; pack ! = nullptr ; pack = next )
{
next = pack - > Next ;
delete pack ;
}
GatheringWipeScreen = false ;
}
void OpenGLSWFrameBuffer : : ReleaseDefaultPoolItems ( )
{
2017-05-27 00:31:15 +00:00
FBTexture . reset ( ) ;
FinalWipeScreen . reset ( ) ;
InitialWipeScreen . reset ( ) ;
VertexBuffer . reset ( ) ;
IndexBuffer . reset ( ) ;
OutputFB . reset ( ) ;
2016-10-09 10:50:57 +00:00
}
bool OpenGLSWFrameBuffer : : Reset ( )
{
ReleaseDefaultPoolItems ( ) ;
2016-10-11 11:09:32 +00:00
2017-05-27 00:31:15 +00:00
OutputFB = CreateFrameBuffer ( " OutputFB " , Width , Height ) ;
if ( ! OutputFB | | ! CreateFBTexture ( ) | | ! CreateVertexes ( ) )
2016-10-09 10:50:57 +00:00
{
return false ;
}
2016-10-12 05:34:07 +00:00
glBindFramebuffer ( GL_FRAMEBUFFER , OutputFB - > Framebuffer ) ;
glViewport ( 0 , 0 , Width , Height ) ;
2016-10-09 10:50:57 +00:00
SetInitialState ( ) ;
return true ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: KillNativePals
//
// Frees all native palettes.
//
//==========================================================================
void OpenGLSWFrameBuffer : : KillNativePals ( )
{
while ( Palettes ! = nullptr )
{
delete Palettes ;
}
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: KillNativeTexs
//
// Frees all native textures.
//
//==========================================================================
void OpenGLSWFrameBuffer : : KillNativeTexs ( )
{
while ( Textures ! = nullptr )
{
delete Textures ;
}
}
bool OpenGLSWFrameBuffer : : CreateFBTexture ( )
{
2017-05-27 00:31:15 +00:00
FBTexture = CreateTexture ( " FBTexture " , Width , Height , 1 , IsBgra ( ) ? GL_RGBA8 : GL_R8 ) ;
return FBTexture ! = nullptr ;
2016-10-09 10:50:57 +00:00
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: CreatePaletteTexture
//
//==========================================================================
bool OpenGLSWFrameBuffer : : CreatePaletteTexture ( )
{
2017-05-27 00:31:15 +00:00
PaletteTexture = CreateTexture ( " PaletteTexture " , 256 , 1 , 1 , GL_RGBA8 ) ;
return PaletteTexture ! = nullptr ;
2016-10-09 10:50:57 +00:00
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: CreateVertexes
//
//==========================================================================
bool OpenGLSWFrameBuffer : : CreateVertexes ( )
{
VertexPos = - 1 ;
IndexPos = - 1 ;
QuadBatchPos = - 1 ;
BatchType = BATCH_None ;
2017-05-27 00:31:15 +00:00
VertexBuffer = CreateVertexBuffer ( sizeof ( FBVERTEX ) * NUM_VERTS ) ;
if ( ! VertexBuffer )
2016-10-09 10:50:57 +00:00
{
return false ;
}
2017-05-27 00:31:15 +00:00
IndexBuffer = CreateIndexBuffer ( sizeof ( uint16_t ) * NUM_INDEXES ) ;
if ( ! IndexBuffer )
2016-10-09 10:50:57 +00:00
{
return false ;
}
return true ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: CalcFullscreenCoords
//
//==========================================================================
2016-10-12 05:34:07 +00:00
void OpenGLSWFrameBuffer : : CalcFullscreenCoords ( FBVERTEX verts [ 4 ] , bool viewarea_only , uint32_t color0 , uint32_t color1 ) const
2016-10-09 10:50:57 +00:00
{
float mxl , mxr , myt , myb , tmxl , tmxr , tmyt , tmyb ;
if ( viewarea_only )
{ // Just calculate vertices for the viewarea/BlendingRect
2016-10-12 05:34:07 +00:00
mxl = float ( BlendingRect . left ) ;
mxr = float ( BlendingRect . right ) ;
myt = float ( BlendingRect . top ) ;
myb = float ( BlendingRect . bottom ) ;
tmxl = float ( BlendingRect . left ) / float ( Width ) ;
tmxr = float ( BlendingRect . right ) / float ( Width ) ;
tmyt = float ( BlendingRect . top ) / float ( Height ) ;
tmyb = float ( BlendingRect . bottom ) / float ( Height ) ;
2016-10-09 10:50:57 +00:00
}
else
{ // Calculate vertices for the whole screen
2016-10-12 05:34:07 +00:00
mxl = 0.0f ;
mxr = float ( Width ) ;
myt = 0.0f ;
myb = float ( Height ) ;
2016-10-09 10:50:57 +00:00
tmxl = 0 ;
2016-10-12 05:34:07 +00:00
tmxr = 1.0f ;
2016-10-09 10:50:57 +00:00
tmyt = 0 ;
2016-10-12 05:34:07 +00:00
tmyb = 1.0f ;
2016-10-09 10:50:57 +00:00
}
//{ mxl, myt, 0, 1, 0, 0xFFFFFFFF, tmxl, tmyt },
//{ mxr, myt, 0, 1, 0, 0xFFFFFFFF, tmxr, tmyt },
//{ mxr, myb, 0, 1, 0, 0xFFFFFFFF, tmxr, tmyb },
//{ mxl, myb, 0, 1, 0, 0xFFFFFFFF, tmxl, tmyb },
verts [ 0 ] . x = mxl ;
verts [ 0 ] . y = myt ;
verts [ 0 ] . z = 0 ;
verts [ 0 ] . rhw = 1 ;
verts [ 0 ] . color0 = color0 ;
verts [ 0 ] . color1 = color1 ;
verts [ 0 ] . tu = tmxl ;
verts [ 0 ] . tv = tmyt ;
verts [ 1 ] . x = mxr ;
verts [ 1 ] . y = myt ;
verts [ 1 ] . z = 0 ;
verts [ 1 ] . rhw = 1 ;
verts [ 1 ] . color0 = color0 ;
verts [ 1 ] . color1 = color1 ;
verts [ 1 ] . tu = tmxr ;
verts [ 1 ] . tv = tmyt ;
verts [ 2 ] . x = mxr ;
verts [ 2 ] . y = myb ;
verts [ 2 ] . z = 0 ;
verts [ 2 ] . rhw = 1 ;
verts [ 2 ] . color0 = color0 ;
verts [ 2 ] . color1 = color1 ;
verts [ 2 ] . tu = tmxr ;
verts [ 2 ] . tv = tmyb ;
verts [ 3 ] . x = mxl ;
verts [ 3 ] . y = myb ;
verts [ 3 ] . z = 0 ;
verts [ 3 ] . rhw = 1 ;
verts [ 3 ] . color0 = color0 ;
verts [ 3 ] . color1 = color1 ;
verts [ 3 ] . tu = tmxl ;
verts [ 3 ] . tv = tmyb ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: GetPageCount
//
//==========================================================================
int OpenGLSWFrameBuffer : : GetPageCount ( )
{
2017-03-10 14:40:33 +00:00
return 2 ;
2016-10-09 10:50:57 +00:00
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: IsValid
//
//==========================================================================
bool OpenGLSWFrameBuffer : : IsValid ( )
{
2017-01-24 00:43:45 +00:00
return Valid ;
2016-10-09 10:50:57 +00:00
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: Lock
//
//==========================================================================
bool OpenGLSWFrameBuffer : : Lock ( bool buffered )
{
2017-04-01 22:47:18 +00:00
if ( m_Lock + + > 0 )
2016-10-09 10:50:57 +00:00
{
return false ;
}
assert ( ! In2D ) ;
Accel2D = vid_hw2d ;
2016-10-17 23:16:36 +00:00
if ( UseMappedMemBuffer )
{
if ( ! MappedMemBuffer )
{
BindFBBuffer ( ) ;
MappedMemBuffer = glMapBuffer ( GL_PIXEL_UNPACK_BUFFER , GL_READ_WRITE ) ;
Pitch = Width ;
if ( MappedMemBuffer = = nullptr )
return true ;
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , 0 ) ;
}
Buffer = ( uint8_t * ) MappedMemBuffer ;
}
else
{
Buffer = MemBuffer ;
}
2016-10-09 10:50:57 +00:00
return false ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: Unlock
//
//==========================================================================
void OpenGLSWFrameBuffer : : Unlock ( )
{
2017-04-01 22:47:18 +00:00
if ( m_Lock = = 0 )
2016-10-09 10:50:57 +00:00
{
return ;
}
2017-04-01 22:47:18 +00:00
if ( UpdatePending & & m_Lock = = 1 )
2016-10-09 10:50:57 +00:00
{
Update ( ) ;
}
2017-04-01 22:47:18 +00:00
else if ( - - m_Lock = = 0 )
2016-10-09 10:50:57 +00:00
{
Buffer = nullptr ;
}
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: Update
//
// When In2D == 0: Copy buffer to screen and present
// When In2D == 1: Copy buffer to screen but do not present
// When In2D == 2: Set up for 2D drawing but do not draw anything
// When In2D == 3: Present and set In2D to 0
//
//==========================================================================
void OpenGLSWFrameBuffer : : Update ( )
{
if ( In2D = = 3 )
{
if ( InScene )
{
DrawRateStuff ( ) ;
DrawPackedTextures ( gl_showpacks ) ;
EndBatch ( ) ; // Make sure all batched primitives are drawn.
Flip ( ) ;
}
In2D = 0 ;
return ;
}
2017-04-01 22:47:18 +00:00
if ( m_Lock ! = 1 )
2016-10-09 10:50:57 +00:00
{
I_FatalError ( " Framebuffer must have exactly 1 lock to be updated " ) ;
2017-04-01 22:47:18 +00:00
if ( m_Lock > 0 )
2016-10-09 10:50:57 +00:00
{
UpdatePending = true ;
2017-04-01 22:47:18 +00:00
- - m_Lock ;
2016-10-09 10:50:57 +00:00
}
return ;
}
if ( In2D = = 0 )
{
DrawRateStuff ( ) ;
}
if ( NeedGammaUpdate )
{
float psgamma [ 4 ] ;
float igamma ;
NeedGammaUpdate = false ;
igamma = 1 / Gamma ;
2016-10-16 20:40:08 +00:00
if ( IsFullscreen ( ) )
2016-10-09 10:50:57 +00:00
{
GammaRamp ramp ;
for ( int i = 0 ; i < 256 ; + + i )
{
ramp . blue [ i ] = ramp . green [ i ] = ramp . red [ i ] = uint16_t ( 65535.f * powf ( i / 255.f , igamma ) ) ;
}
SetGammaRamp ( & ramp ) ;
}
psgamma [ 2 ] = psgamma [ 1 ] = psgamma [ 0 ] = igamma ;
psgamma [ 3 ] = 0.5 ; // For SM14 version
SetPixelShaderConstantF ( PSCONST_Gamma , psgamma , 1 ) ;
}
if ( NeedPalUpdate )
{
UploadPalette ( ) ;
2016-10-11 08:27:18 +00:00
NeedPalUpdate = false ;
2016-10-09 10:50:57 +00:00
}
2016-10-16 20:40:08 +00:00
# ifdef WIN32
2016-10-09 10:50:57 +00:00
BlitCycles . Reset ( ) ;
BlitCycles . Clock ( ) ;
2016-10-16 20:40:08 +00:00
# endif
2016-10-09 10:50:57 +00:00
2017-04-01 22:47:18 +00:00
m_Lock = 0 ;
2016-10-09 10:50:57 +00:00
Draw3DPart ( In2D < = 1 ) ;
if ( In2D = = 0 )
{
Flip ( ) ;
}
2016-10-16 20:40:08 +00:00
# ifdef WIN32
2016-10-09 10:50:57 +00:00
BlitCycles . Unclock ( ) ;
//LOG1 ("cycles = %d\n", BlitCycles);
2016-10-16 20:40:08 +00:00
# endif
2016-10-09 10:50:57 +00:00
Buffer = nullptr ;
UpdatePending = false ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: Flip
//
//==========================================================================
void OpenGLSWFrameBuffer : : Flip ( )
{
assert ( InScene ) ;
Present ( ) ;
InScene = false ;
2016-10-16 20:40:08 +00:00
if ( ! IsFullscreen ( ) )
2016-10-09 10:50:57 +00:00
{
2016-10-12 06:05:31 +00:00
int clientWidth = ClampWidth ( GetClientWidth ( ) ) ;
int clientHeight = ClampHeight ( GetClientHeight ( ) ) ;
2016-10-09 10:50:57 +00:00
if ( clientWidth > 0 & & clientHeight > 0 & & ( Width ! = clientWidth | | Height ! = clientHeight ) )
{
Resize ( clientWidth , clientHeight ) ;
TrueHeight = Height ;
PixelDoubling = 0 ;
Reset ( ) ;
V_OutputResized ( Width , Height ) ;
}
}
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: PaintToWindow
//
//==========================================================================
2016-10-16 20:40:08 +00:00
# ifdef WIN32
2016-10-09 10:50:57 +00:00
bool OpenGLSWFrameBuffer : : PaintToWindow ( )
{
2017-04-01 22:47:18 +00:00
if ( m_Lock ! = 0 )
2016-10-09 10:50:57 +00:00
{
return false ;
}
Draw3DPart ( true ) ;
return true ;
}
2016-10-16 20:40:08 +00:00
# endif
2016-10-09 10:50:57 +00:00
//==========================================================================
//
// OpenGLSWFrameBuffer :: Draw3DPart
//
// The software 3D part, to be exact.
//
//==========================================================================
2016-10-17 23:16:36 +00:00
void OpenGLSWFrameBuffer : : BindFBBuffer ( )
{
int usage = UseMappedMemBuffer ? GL_DYNAMIC_DRAW : GL_STREAM_DRAW ;
int pixelsize = IsBgra ( ) ? 4 : 1 ;
int size = Width * Height * pixelsize ;
if ( FBTexture - > Buffers [ 0 ] = = 0 )
{
glGenBuffers ( 2 , ( GLuint * ) FBTexture - > Buffers ) ;
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , FBTexture - > Buffers [ 1 ] ) ;
glBufferData ( GL_PIXEL_UNPACK_BUFFER , size , nullptr , usage ) ;
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , FBTexture - > Buffers [ 0 ] ) ;
glBufferData ( GL_PIXEL_UNPACK_BUFFER , size , nullptr , usage ) ;
}
else
{
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , FBTexture - > Buffers [ FBTexture - > CurrentBuffer ] ) ;
}
}
2017-01-25 06:18:26 +00:00
void OpenGLSWFrameBuffer : : BgraToRgba ( uint32_t * dest , const uint32_t * src , int width , int height , int srcpitch )
{
for ( int y = 0 ; y < height ; y + + )
{
for ( int x = 0 ; x < width ; x + + )
{
uint32_t r = RPART ( src [ x ] ) ;
uint32_t g = GPART ( src [ x ] ) ;
uint32_t b = BPART ( src [ x ] ) ;
uint32_t a = APART ( src [ x ] ) ;
dest [ x ] = r | ( g < < 8 ) | ( b < < 16 ) | ( a < < 24 ) ;
}
dest + = width ;
src + = srcpitch ;
}
}
2016-10-09 10:50:57 +00:00
void OpenGLSWFrameBuffer : : Draw3DPart ( bool copy3d )
{
if ( copy3d )
{
2016-10-17 23:16:36 +00:00
BindFBBuffer ( ) ;
FBTexture - > CurrentBuffer = ( FBTexture - > CurrentBuffer + 1 ) & 1 ;
2016-10-11 13:43:12 +00:00
2016-10-17 23:16:36 +00:00
if ( ! UseMappedMemBuffer )
2016-10-10 19:03:55 +00:00
{
2016-10-17 23:16:36 +00:00
int pixelsize = IsBgra ( ) ? 4 : 1 ;
int size = Width * Height * pixelsize ;
2016-10-09 10:50:57 +00:00
2016-10-17 23:16:36 +00:00
uint8_t * dest = ( uint8_t * ) MapBuffer ( GL_PIXEL_UNPACK_BUFFER , size ) ;
if ( dest )
2016-10-09 10:50:57 +00:00
{
2017-01-25 06:18:26 +00:00
if ( gl . es & & pixelsize = = 4 )
{
BgraToRgba ( ( uint32_t * ) dest , ( const uint32_t * ) MemBuffer , Width , Height , Pitch ) ;
}
else if ( Pitch = = Width )
2016-10-17 23:16:36 +00:00
{
memcpy ( dest , MemBuffer , Width * Height * pixelsize ) ;
}
else
2016-10-09 10:50:57 +00:00
{
2016-10-17 23:16:36 +00:00
uint8_t * src = MemBuffer ;
for ( int y = 0 ; y < Height ; y + + )
{
memcpy ( dest , src , Width * pixelsize ) ;
dest + = Width * pixelsize ;
src + = Pitch * pixelsize ;
}
2016-10-09 10:50:57 +00:00
}
2016-10-17 23:16:36 +00:00
glUnmapBuffer ( GL_PIXEL_UNPACK_BUFFER ) ;
2016-10-09 10:50:57 +00:00
}
2016-10-17 23:16:36 +00:00
}
else if ( MappedMemBuffer )
{
2016-10-10 19:03:55 +00:00
glUnmapBuffer ( GL_PIXEL_UNPACK_BUFFER ) ;
2016-10-17 23:16:36 +00:00
MappedMemBuffer = nullptr ;
2016-10-09 10:50:57 +00:00
}
2016-10-10 19:03:55 +00:00
2016-10-17 23:16:36 +00:00
GLint oldBinding = 0 ;
glGetIntegerv ( GL_TEXTURE_BINDING_2D , & oldBinding ) ;
glBindTexture ( GL_TEXTURE_2D , FBTexture - > Texture ) ;
if ( IsBgra ( ) )
2017-01-25 06:18:26 +00:00
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , Width , Height , gl . es ? GL_RGBA : GL_BGRA , GL_UNSIGNED_BYTE , 0 ) ;
2016-10-17 23:16:36 +00:00
else
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , Width , Height , GL_RED , GL_UNSIGNED_BYTE , 0 ) ;
glBindTexture ( GL_TEXTURE_2D , oldBinding ) ;
2016-10-10 19:03:55 +00:00
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , 0 ) ;
2016-10-09 10:50:57 +00:00
}
InScene = true ;
if ( vid_hwaalines )
glEnable ( GL_LINE_SMOOTH ) ;
else
glDisable ( GL_LINE_SMOOTH ) ;
2017-05-27 00:31:15 +00:00
SetTexture ( 0 , FBTexture . get ( ) ) ;
SetPaletteTexture ( PaletteTexture . get ( ) , 256 , BorderColor ) ;
2016-10-09 10:50:57 +00:00
memset ( Constant , 0 , sizeof ( Constant ) ) ;
SetAlphaBlend ( 0 ) ;
2016-10-11 08:27:18 +00:00
EnableAlphaTest ( false ) ;
2016-10-11 13:43:12 +00:00
if ( IsBgra ( ) )
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ SHADER_NormalColor ] . get ( ) ) ;
2016-10-11 13:43:12 +00:00
else
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ SHADER_NormalColorPal ] . get ( ) ) ;
2016-10-09 10:50:57 +00:00
if ( copy3d )
{
FBVERTEX verts [ 4 ] ;
uint32_t color0 , color1 ;
if ( Accel2D )
{
2017-02-03 08:00:46 +00:00
auto map = swrenderer : : CameraLight : : Instance ( ) - > ShaderColormap ( ) ;
2016-12-01 01:38:32 +00:00
if ( map = = nullptr )
2016-10-09 10:50:57 +00:00
{
color0 = 0 ;
color1 = 0xFFFFFFF ;
}
else
{
2016-12-01 01:38:32 +00:00
color0 = ColorValue ( map - > ColorizeStart [ 0 ] / 2 , map - > ColorizeStart [ 1 ] / 2 , map - > ColorizeStart [ 2 ] / 2 , 0 ) ;
color1 = ColorValue ( map - > ColorizeEnd [ 0 ] / 2 , map - > ColorizeEnd [ 1 ] / 2 , map - > ColorizeEnd [ 2 ] / 2 , 1 ) ;
2016-10-11 13:43:12 +00:00
if ( IsBgra ( ) )
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ SHADER_SpecialColormap ] . get ( ) ) ;
2016-10-11 13:43:12 +00:00
else
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ SHADER_SpecialColormapPal ] . get ( ) ) ;
2016-10-09 10:50:57 +00:00
}
}
else
{
color0 = FlashColor0 ;
color1 = FlashColor1 ;
}
2016-10-12 05:34:07 +00:00
CalcFullscreenCoords ( verts , Accel2D , color0 , color1 ) ;
2016-10-09 10:50:57 +00:00
DrawTriangleFans ( 2 , verts ) ;
}
2016-10-11 13:43:12 +00:00
if ( IsBgra ( ) )
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ SHADER_NormalColor ] . get ( ) ) ;
2016-10-11 13:43:12 +00:00
else
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ SHADER_NormalColorPal ] . get ( ) ) ;
2016-10-09 10:50:57 +00:00
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: DrawLetterbox
//
// Draws the black bars at the top and bottom of the screen for letterboxed
// modes.
//
//==========================================================================
2016-10-12 05:34:07 +00:00
void OpenGLSWFrameBuffer : : DrawLetterbox ( int x , int y , int width , int height )
2016-10-09 10:50:57 +00:00
{
2016-10-12 05:34:07 +00:00
int clientWidth = GetClientWidth ( ) ;
int clientHeight = GetClientHeight ( ) ;
if ( clientWidth = = 0 | | clientHeight = = 0 )
return ;
glClearColor ( 0.0f , 0.0f , 0.0f , 1.0f ) ;
glEnable ( GL_SCISSOR_TEST ) ;
2016-10-12 06:04:42 +00:00
if ( y > 0 )
2016-10-09 10:50:57 +00:00
{
2016-10-12 06:04:42 +00:00
glScissor ( 0 , 0 , clientWidth , y ) ;
2016-10-09 10:50:57 +00:00
glClear ( GL_COLOR_BUFFER_BIT ) ;
2016-10-12 05:34:07 +00:00
}
2016-10-12 06:04:42 +00:00
if ( clientHeight - y - height > 0 )
2016-10-12 05:34:07 +00:00
{
2016-10-12 06:04:42 +00:00
glScissor ( 0 , y + height , clientWidth , clientHeight - y - height ) ;
2016-10-09 10:50:57 +00:00
glClear ( GL_COLOR_BUFFER_BIT ) ;
}
2016-10-12 06:04:42 +00:00
if ( x > 0 )
2016-10-12 05:34:07 +00:00
{
2016-10-12 06:04:42 +00:00
glScissor ( 0 , y , x , height ) ;
2016-10-12 05:34:07 +00:00
glClear ( GL_COLOR_BUFFER_BIT ) ;
}
2016-10-12 06:04:42 +00:00
if ( clientWidth - x - width > 0 )
2016-10-12 05:34:07 +00:00
{
2016-10-12 06:04:42 +00:00
glScissor ( x + width , y , clientWidth - x - width , height ) ;
2016-10-12 05:34:07 +00:00
glClear ( GL_COLOR_BUFFER_BIT ) ;
}
glDisable ( GL_SCISSOR_TEST ) ;
2016-10-09 10:50:57 +00:00
}
2016-10-10 19:03:55 +00:00
void OpenGLSWFrameBuffer : : UploadPalette ( )
2016-10-09 10:50:57 +00:00
{
2016-10-11 08:27:18 +00:00
if ( PaletteTexture - > Buffers [ 0 ] = = 0 )
2016-10-09 10:50:57 +00:00
{
2016-10-11 08:27:18 +00:00
glGenBuffers ( 2 , ( GLuint * ) PaletteTexture - > Buffers ) ;
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , PaletteTexture - > Buffers [ 0 ] ) ;
glBufferData ( GL_PIXEL_UNPACK_BUFFER , 256 * 4 , nullptr , GL_STREAM_DRAW ) ;
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , PaletteTexture - > Buffers [ 1 ] ) ;
2016-10-10 19:03:55 +00:00
glBufferData ( GL_PIXEL_UNPACK_BUFFER , 256 * 4 , nullptr , GL_STREAM_DRAW ) ;
2017-01-25 06:18:26 +00:00
if ( gl . es ) PaletteTexture - > MapBuffer . resize ( 256 * 4 ) ;
2016-10-09 10:50:57 +00:00
}
2016-10-10 19:03:55 +00:00
else
2016-10-09 10:50:57 +00:00
{
2016-10-11 08:27:18 +00:00
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , PaletteTexture - > Buffers [ PaletteTexture - > CurrentBuffer ] ) ;
PaletteTexture - > CurrentBuffer = ( PaletteTexture - > CurrentBuffer + 1 ) & 1 ;
2016-10-09 10:50:57 +00:00
}
2016-10-10 19:03:55 +00:00
2017-01-25 06:18:26 +00:00
uint8_t * pix = gl . es ? PaletteTexture - > MapBuffer . data ( ) : ( uint8_t * ) MapBuffer ( GL_PIXEL_UNPACK_BUFFER , 256 * 4 ) ;
2016-10-10 19:03:55 +00:00
if ( pix )
2016-10-09 10:50:57 +00:00
{
int i ;
2016-10-10 19:03:55 +00:00
for ( i = 0 ; i < 256 ; + + i , pix + = 4 )
2016-10-09 10:50:57 +00:00
{
pix [ 0 ] = SourcePalette [ i ] . b ;
pix [ 1 ] = SourcePalette [ i ] . g ;
pix [ 2 ] = SourcePalette [ i ] . r ;
pix [ 3 ] = ( i = = 0 ? 0 : 255 ) ;
// To let masked textures work, the first palette entry's alpha is 0.
}
pix + = 4 ;
for ( ; i < 255 ; + + i , pix + = 4 )
{
pix [ 0 ] = SourcePalette [ i ] . b ;
pix [ 1 ] = SourcePalette [ i ] . g ;
pix [ 2 ] = SourcePalette [ i ] . r ;
pix [ 3 ] = 255 ;
}
2017-01-25 06:18:26 +00:00
if ( gl . es )
{
uint8_t * tempbuffer = PaletteTexture - > MapBuffer . data ( ) ;
BgraToRgba ( ( uint32_t * ) tempbuffer , ( const uint32_t * ) tempbuffer , 256 , 1 , 256 ) ;
glBufferSubData ( GL_PIXEL_UNPACK_BUFFER , 0 , 256 * 4 , tempbuffer ) ;
}
else
{
glUnmapBuffer ( GL_PIXEL_UNPACK_BUFFER ) ;
}
2016-10-10 19:03:55 +00:00
GLint oldBinding = 0 ;
glGetIntegerv ( GL_TEXTURE_BINDING_2D , & oldBinding ) ;
glBindTexture ( GL_TEXTURE_2D , PaletteTexture - > Texture ) ;
2017-01-25 06:18:26 +00:00
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , 256 , 1 , gl . es ? GL_RGBA : GL_BGRA , GL_UNSIGNED_BYTE , 0 ) ;
2016-10-10 19:03:55 +00:00
glBindTexture ( GL_TEXTURE_2D , oldBinding ) ;
2016-10-09 10:50:57 +00:00
BorderColor = ColorXRGB ( SourcePalette [ 255 ] . r , SourcePalette [ 255 ] . g , SourcePalette [ 255 ] . b ) ;
}
2016-10-10 19:03:55 +00:00
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , 0 ) ;
2016-10-09 10:50:57 +00:00
}
PalEntry * OpenGLSWFrameBuffer : : GetPalette ( )
{
return SourcePalette ;
}
void OpenGLSWFrameBuffer : : UpdatePalette ( )
{
NeedPalUpdate = true ;
}
bool OpenGLSWFrameBuffer : : SetGamma ( float gamma )
{
Gamma = gamma ;
NeedGammaUpdate = true ;
return true ;
}
bool OpenGLSWFrameBuffer : : SetFlash ( PalEntry rgb , int amount )
{
FlashColor = rgb ;
FlashAmount = amount ;
// Fill in the constants for the pixel shader to do linear interpolation between the palette and the flash:
float r = rgb . r / 255.f , g = rgb . g / 255.f , b = rgb . b / 255.f , a = amount / 256.f ;
FlashColor0 = ColorValue ( r * a , g * a , b * a , 0 ) ;
a = 1 - a ;
FlashColor1 = ColorValue ( a , a , a , 1 ) ;
return true ;
}
void OpenGLSWFrameBuffer : : GetFlash ( PalEntry & rgb , int & amount )
{
rgb = FlashColor ;
amount = FlashAmount ;
}
void OpenGLSWFrameBuffer : : GetFlashedPalette ( PalEntry pal [ 256 ] )
{
memcpy ( pal , SourcePalette , 256 * sizeof ( PalEntry ) ) ;
if ( FlashAmount )
{
DoBlending ( pal , pal , 256 , FlashColor . r , FlashColor . g , FlashColor . b , FlashAmount ) ;
}
}
void OpenGLSWFrameBuffer : : SetVSync ( bool vsync )
{
2016-11-19 11:30:58 +00:00
// Switch to the default frame buffer because Nvidia's driver associates the vsync state with the bound FB object.
GLint oldDrawFramebufferBinding = 0 , oldReadFramebufferBinding = 0 ;
glGetIntegerv ( GL_DRAW_FRAMEBUFFER_BINDING , & oldDrawFramebufferBinding ) ;
glGetIntegerv ( GL_READ_FRAMEBUFFER_BINDING , & oldReadFramebufferBinding ) ;
glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , 0 ) ;
glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 ) ;
2016-10-11 08:27:18 +00:00
Super : : SetVSync ( vsync ) ;
2016-11-19 11:30:58 +00:00
glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , oldDrawFramebufferBinding ) ;
glBindFramebuffer ( GL_READ_FRAMEBUFFER , oldReadFramebufferBinding ) ;
2016-10-09 10:50:57 +00:00
}
void OpenGLSWFrameBuffer : : NewRefreshRate ( )
{
}
void OpenGLSWFrameBuffer : : SetBlendingRect ( int x1 , int y1 , int x2 , int y2 )
{
BlendingRect . left = x1 ;
BlendingRect . top = y1 ;
BlendingRect . right = x2 ;
BlendingRect . bottom = y2 ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: GetScreenshotBuffer
//
// Returns a pointer into a surface holding the current screen data.
//
//==========================================================================
void OpenGLSWFrameBuffer : : GetScreenshotBuffer ( const uint8_t * & buffer , int & pitch , ESSType & color_type )
{
2016-10-10 19:03:55 +00:00
Super : : GetScreenshotBuffer ( buffer , pitch , color_type ) ;
/*
2016-10-09 10:50:57 +00:00
LockedRect lrect ;
if ( ! Accel2D )
{
Super : : GetScreenshotBuffer ( buffer , pitch , color_type ) ;
return ;
}
buffer = nullptr ;
if ( ( ScreenshotTexture = GetCurrentScreen ( ) ) ! = nullptr )
{
if ( ! ScreenshotTexture - > GetSurfaceLevel ( 0 , & ScreenshotSurface ) )
{
delete ScreenshotTexture ;
ScreenshotTexture = nullptr ;
}
else if ( ! ScreenshotSurface - > LockRect ( & lrect , nullptr , false ) )
{
delete ScreenshotSurface ;
ScreenshotSurface = nullptr ;
delete ScreenshotTexture ;
ScreenshotTexture = nullptr ;
}
else
{
buffer = ( const uint8_t * ) lrect . pBits ;
pitch = lrect . Pitch ;
color_type = SS_BGRA ;
}
}
2016-10-10 19:03:55 +00:00
*/
2016-10-09 10:50:57 +00:00
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: ReleaseScreenshotBuffer
//
//==========================================================================
void OpenGLSWFrameBuffer : : ReleaseScreenshotBuffer ( )
{
2017-04-01 22:47:18 +00:00
if ( m_Lock > 0 )
2016-10-09 10:50:57 +00:00
{
Super : : ReleaseScreenshotBuffer ( ) ;
}
2017-05-27 00:31:15 +00:00
ScreenshotTexture . reset ( ) ;
2016-10-09 10:50:57 +00:00
}
/**************************************************************************/
/* 2D Stuff */
/**************************************************************************/
//==========================================================================
//
// OpenGLSWFrameBuffer :: DrawPackedTextures
//
// DEBUG: Draws the texture atlases to the screen, starting with the
// 1-based packnum. Ignores atlases that are flagged for use by one
// texture only.
//
//==========================================================================
void OpenGLSWFrameBuffer : : DrawPackedTextures ( int packnum )
{
uint32_t empty_colors [ 8 ] =
{
0x50FF0000 , 0x5000FF00 , 0x500000FF , 0x50FFFF00 ,
0x50FF00FF , 0x5000FFFF , 0x50FF8000 , 0x500080FF
} ;
Atlas * pack ;
int x = 8 , y = 8 ;
if ( packnum < = 0 )
{
return ;
}
pack = Atlases ;
// Find the first texture atlas that is an actual atlas.
while ( pack ! = nullptr & & pack - > OneUse )
{ // Skip textures that aren't used as atlases
pack = pack - > Next ;
}
// Skip however many atlases we would have otherwise drawn
// until we've skipped <packnum> of them.
while ( pack ! = nullptr & & packnum ! = 1 )
{
if ( ! pack - > OneUse )
{ // Skip textures that aren't used as atlases
packnum - - ;
}
pack = pack - > Next ;
}
// Draw atlases until we run out of room on the screen.
while ( pack ! = nullptr )
{
if ( pack - > OneUse )
{ // Skip textures that aren't used as atlases
pack = pack - > Next ;
continue ;
}
2016-10-12 05:34:07 +00:00
AddColorOnlyRect ( x - 1 , y - 1 , 258 , 258 , ColorXRGB ( 255 , 255 , 0 ) ) ;
2016-10-09 10:50:57 +00:00
int back = 0 ;
for ( PackedTexture * box = pack - > UsedList ; box ! = nullptr ; box = box - > Next )
{
AddColorOnlyQuad (
x + box - > Area . left * 256 / pack - > Width ,
y + box - > Area . top * 256 / pack - > Height ,
( box - > Area . right - box - > Area . left ) * 256 / pack - > Width ,
( box - > Area . bottom - box - > Area . top ) * 256 / pack - > Height , empty_colors [ back ] ) ;
back = ( back + 1 ) & 7 ;
}
// AddColorOnlyQuad(x, y-LBOffsetI, 256, 256, ColorARGB(180,0,0,0));
CheckQuadBatch ( ) ;
BufferedTris * quad = & QuadExtra [ QuadBatchPos ] ;
FBVERTEX * vert = & VertexData [ VertexPos ] ;
2016-10-11 08:27:18 +00:00
quad - > ClearSetup ( ) ;
2016-10-09 10:50:57 +00:00
if ( pack - > Format = = GL_R8 /* && !tex->IsGray*/ )
{
quad - > Flags = BQF_WrapUV | BQF_GamePalette /* | BQF_DisableAlphaTest*/ ;
quad - > ShaderNum = BQS_PalTex ;
}
else
{
quad - > Flags = BQF_WrapUV /* | BQF_DisableAlphaTest*/ ;
quad - > ShaderNum = BQS_Plain ;
}
quad - > Palette = nullptr ;
2017-05-27 00:31:15 +00:00
quad - > Texture = pack - > Tex . get ( ) ;
2016-10-09 10:50:57 +00:00
quad - > NumVerts = 4 ;
quad - > NumTris = 2 ;
2016-10-12 05:34:07 +00:00
float x0 = float ( x ) ;
float y0 = float ( y ) ;
2016-10-09 10:50:57 +00:00
float x1 = x0 + 256.f ;
float y1 = y0 + 256.f ;
vert [ 0 ] . x = x0 ;
vert [ 0 ] . y = y0 ;
vert [ 0 ] . z = 0 ;
vert [ 0 ] . rhw = 1 ;
vert [ 0 ] . color0 = 0 ;
vert [ 0 ] . color1 = 0xFFFFFFFF ;
vert [ 0 ] . tu = 0 ;
vert [ 0 ] . tv = 0 ;
vert [ 1 ] . x = x1 ;
vert [ 1 ] . y = y0 ;
vert [ 1 ] . z = 0 ;
vert [ 1 ] . rhw = 1 ;
vert [ 1 ] . color0 = 0 ;
vert [ 1 ] . color1 = 0xFFFFFFFF ;
vert [ 1 ] . tu = 1 ;
vert [ 1 ] . tv = 0 ;
vert [ 2 ] . x = x1 ;
vert [ 2 ] . y = y1 ;
vert [ 2 ] . z = 0 ;
vert [ 2 ] . rhw = 1 ;
vert [ 2 ] . color0 = 0 ;
vert [ 2 ] . color1 = 0xFFFFFFFF ;
vert [ 2 ] . tu = 1 ;
vert [ 2 ] . tv = 1 ;
vert [ 3 ] . x = x0 ;
vert [ 3 ] . y = y1 ;
vert [ 3 ] . z = 0 ;
vert [ 3 ] . rhw = 1 ;
vert [ 3 ] . color0 = 0 ;
vert [ 3 ] . color1 = 0xFFFFFFFF ;
vert [ 3 ] . tu = 0 ;
vert [ 3 ] . tv = 1 ;
IndexData [ IndexPos ] = VertexPos ;
IndexData [ IndexPos + 1 ] = VertexPos + 1 ;
IndexData [ IndexPos + 2 ] = VertexPos + 2 ;
IndexData [ IndexPos + 3 ] = VertexPos ;
IndexData [ IndexPos + 4 ] = VertexPos + 2 ;
IndexData [ IndexPos + 5 ] = VertexPos + 3 ;
QuadBatchPos + + ;
VertexPos + = 4 ;
IndexPos + = 6 ;
x + = 256 + 8 ;
if ( x > Width - 256 )
{
x = 8 ;
y + = 256 + 8 ;
2016-10-12 05:34:07 +00:00
if ( y > Height - 256 )
2016-10-09 10:50:57 +00:00
{
return ;
}
}
pack = pack - > Next ;
}
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: AllocPackedTexture
//
// Finds space to pack an image inside a texture atlas and returns it.
// Large images and those that need to wrap always get their own textures.
//
//==========================================================================
OpenGLSWFrameBuffer : : PackedTexture * OpenGLSWFrameBuffer : : AllocPackedTexture ( int w , int h , bool wrapping , int format )
{
Atlas * pack ;
Rect box ;
bool padded ;
// The - 2 to account for padding
if ( w > 256 - 2 | | h > 256 - 2 | | wrapping )
{ // Create a new texture atlas.
pack = new Atlas ( this , w , h , format ) ;
pack - > OneUse = true ;
box = pack - > Packer . Insert ( w , h ) ;
padded = false ;
}
else
{ // Try to find space in an existing texture atlas.
w + = 2 ; // Add padding
h + = 2 ;
for ( pack = Atlases ; pack ! = nullptr ; pack = pack - > Next )
{
// Use the first atlas it fits in.
if ( pack - > Format = = format )
{
box = pack - > Packer . Insert ( w , h ) ;
if ( box . width ! = 0 )
{
break ;
}
}
}
if ( pack = = nullptr )
{ // Create a new texture atlas.
pack = new Atlas ( this , DEF_ATLAS_WIDTH , DEF_ATLAS_HEIGHT , format ) ;
box = pack - > Packer . Insert ( w , h ) ;
}
padded = true ;
}
assert ( box . width ! = 0 & & box . height ! = 0 ) ;
return pack - > AllocateImage ( box , padded ) ;
}
//==========================================================================
//
// Atlas Constructor
//
//==========================================================================
OpenGLSWFrameBuffer : : Atlas : : Atlas ( OpenGLSWFrameBuffer * fb , int w , int h , int format )
: Packer ( w , h , true )
{
Format = format ;
UsedList = nullptr ;
OneUse = false ;
Width = 0 ;
Height = 0 ;
Next = nullptr ;
// Attach to the end of the atlas list
Atlas * * prev = & fb - > Atlases ;
while ( * prev ! = nullptr )
{
prev = & ( ( * prev ) - > Next ) ;
}
* prev = this ;
2017-05-27 00:31:15 +00:00
Tex = fb - > CreateTexture ( " Atlas " , w , h , 1 , format ) ;
2016-10-09 10:50:57 +00:00
Width = w ;
Height = h ;
}
//==========================================================================
//
// Atlas Destructor
//
//==========================================================================
OpenGLSWFrameBuffer : : Atlas : : ~ Atlas ( )
{
PackedTexture * box , * next ;
2017-05-27 00:31:15 +00:00
Tex . reset ( ) ;
2016-10-09 10:50:57 +00:00
for ( box = UsedList ; box ! = nullptr ; box = next )
{
next = box - > Next ;
delete box ;
}
}
//==========================================================================
//
// Atlas :: AllocateImage
//
// Moves the box from the empty list to the used list, sizing it to the
// requested dimensions and adding additional boxes to the empty list if
// needed.
//
// The passed box *MUST* be in this texture atlas's empty list.
//
//==========================================================================
OpenGLSWFrameBuffer : : PackedTexture * OpenGLSWFrameBuffer : : Atlas : : AllocateImage ( const Rect & rect , bool padded )
{
PackedTexture * box = new PackedTexture ;
box - > Owner = this ;
box - > Area . left = rect . x ;
box - > Area . top = rect . y ;
box - > Area . right = rect . x + rect . width ;
box - > Area . bottom = rect . y + rect . height ;
box - > Left = float ( box - > Area . left + padded ) / Width ;
box - > Right = float ( box - > Area . right - padded ) / Width ;
box - > Top = float ( box - > Area . top + padded ) / Height ;
box - > Bottom = float ( box - > Area . bottom - padded ) / Height ;
box - > Padded = padded ;
// Add it to the used list.
box - > Next = UsedList ;
if ( box - > Next ! = nullptr )
{
box - > Next - > Prev = & box - > Next ;
}
UsedList = box ;
box - > Prev = & UsedList ;
return box ;
}
//==========================================================================
//
// Atlas :: FreeBox
//
// Removes a box from the used list and deletes it. Space is returned to the
// waste list. Once all boxes for this atlas are freed, the entire bin
// packer is reinitialized for maximum efficiency.
//
//==========================================================================
void OpenGLSWFrameBuffer : : Atlas : : FreeBox ( OpenGLSWFrameBuffer : : PackedTexture * box )
{
* ( box - > Prev ) = box - > Next ;
if ( box - > Next ! = nullptr )
{
box - > Next - > Prev = box - > Prev ;
}
Rect waste ;
waste . x = box - > Area . left ;
waste . y = box - > Area . top ;
waste . width = box - > Area . right - box - > Area . left ;
waste . height = box - > Area . bottom - box - > Area . top ;
box - > Owner - > Packer . AddWaste ( waste ) ;
delete box ;
if ( UsedList = = nullptr )
{
Packer . Init ( Width , Height , true ) ;
}
}
//==========================================================================
//
// OpenGLTex Constructor
//
//==========================================================================
OpenGLSWFrameBuffer : : OpenGLTex : : OpenGLTex ( FTexture * tex , OpenGLSWFrameBuffer * fb , bool wrapping )
{
// Attach to the texture list for the OpenGLSWFrameBuffer
Next = fb - > Textures ;
if ( Next ! = nullptr )
{
Next - > Prev = & Next ;
}
Prev = & fb - > Textures ;
fb - > Textures = this ;
GameTex = tex ;
Box = nullptr ;
IsGray = false ;
Create ( fb , wrapping ) ;
}
//==========================================================================
//
// OpenGLTex Destructor
//
//==========================================================================
OpenGLSWFrameBuffer : : OpenGLTex : : ~ OpenGLTex ( )
{
if ( Box ! = nullptr )
{
Box - > Owner - > FreeBox ( Box ) ;
Box = nullptr ;
}
// Detach from the texture list
* Prev = Next ;
if ( Next ! = nullptr )
{
Next - > Prev = Prev ;
}
// Remove link from the game texture
if ( GameTex ! = nullptr )
{
GameTex - > Native = nullptr ;
}
}
//==========================================================================
//
// OpenGLTex :: CheckWrapping
//
// Returns true if the texture is compatible with the specified wrapping
// mode.
//
//==========================================================================
bool OpenGLSWFrameBuffer : : OpenGLTex : : CheckWrapping ( bool wrapping )
{
// If it doesn't need to wrap, then it works.
if ( ! wrapping )
{
return true ;
}
// If it needs to wrap, then it can't be packed inside another texture.
return Box - > Owner - > OneUse ;
}
//==========================================================================
//
// OpenGLTex :: Create
//
// Creates an HWTexture for the texture and copies the image data
// to it. Note that unlike FTexture, this image is row-major.
//
//==========================================================================
bool OpenGLSWFrameBuffer : : OpenGLTex : : Create ( OpenGLSWFrameBuffer * fb , bool wrapping )
{
assert ( Box = = nullptr ) ;
if ( Box ! = nullptr )
{
Box - > Owner - > FreeBox ( Box ) ;
}
Box = fb - > AllocPackedTexture ( GameTex - > GetWidth ( ) , GameTex - > GetHeight ( ) , wrapping , GetTexFormat ( ) ) ;
if ( Box = = nullptr )
{
return false ;
}
if ( ! Update ( ) )
{
Box - > Owner - > FreeBox ( Box ) ;
Box = nullptr ;
return false ;
}
return true ;
}
//==========================================================================
//
// OpenGLTex :: Update
//
// Copies image data from the underlying FTexture to the OpenGL texture.
//
//==========================================================================
bool OpenGLSWFrameBuffer : : OpenGLTex : : Update ( )
{
LTRBRect rect ;
uint8_t * dest ;
assert ( Box ! = nullptr ) ;
assert ( Box - > Owner ! = nullptr ) ;
assert ( Box - > Owner - > Tex ! = nullptr ) ;
assert ( GameTex ! = nullptr ) ;
int format = Box - > Owner - > Tex - > Format ;
rect = Box - > Area ;
2016-10-10 19:03:55 +00:00
2016-10-11 08:27:18 +00:00
if ( Box - > Owner - > Tex - > Buffers [ 0 ] = = 0 )
glGenBuffers ( 2 , ( GLuint * ) Box - > Owner - > Tex - > Buffers ) ;
2016-10-10 19:03:55 +00:00
int bytesPerPixel = 4 ;
switch ( format )
{
case GL_R8 : bytesPerPixel = 1 ; break ;
case GL_RGBA8 : bytesPerPixel = 4 ; break ;
default : return false ;
}
2016-10-11 08:27:18 +00:00
int buffersize = ( rect . right - rect . left ) * ( rect . bottom - rect . top ) * bytesPerPixel ;
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , Box - > Owner - > Tex - > Buffers [ Box - > Owner - > Tex - > CurrentBuffer ] ) ;
glBufferData ( GL_PIXEL_UNPACK_BUFFER , buffersize , nullptr , GL_STREAM_DRAW ) ;
Box - > Owner - > Tex - > CurrentBuffer = ( Box - > Owner - > Tex - > CurrentBuffer + 1 ) & 1 ;
2017-01-25 06:18:26 +00:00
static std : : vector < uint8_t > tempbuffer ;
if ( gl . es )
tempbuffer . resize ( buffersize ) ;
2016-10-10 19:03:55 +00:00
int pitch = ( rect . right - rect . left ) * bytesPerPixel ;
2017-01-25 06:18:26 +00:00
uint8_t * bits = gl . es ? tempbuffer . data ( ) : ( uint8_t * ) MapBuffer ( GL_PIXEL_UNPACK_BUFFER , buffersize ) ;
2016-10-10 19:03:55 +00:00
dest = bits ;
if ( ! dest )
2016-10-09 10:50:57 +00:00
{
return false ;
}
if ( Box - > Padded )
{
2016-10-10 19:03:55 +00:00
dest + = pitch + ( format = = GL_R8 ? 1 : 4 ) ;
2016-10-09 10:50:57 +00:00
}
2016-10-10 19:03:55 +00:00
GameTex - > FillBuffer ( dest , pitch , GameTex - > GetHeight ( ) , ToTexFmt ( format ) ) ;
2016-10-09 10:50:57 +00:00
if ( Box - > Padded )
{
// Clear top padding row.
2016-10-10 19:03:55 +00:00
dest = bits ;
2016-10-09 10:50:57 +00:00
int numbytes = GameTex - > GetWidth ( ) + 2 ;
if ( format ! = GL_R8 )
{
numbytes < < = 2 ;
}
memset ( dest , 0 , numbytes ) ;
2016-10-10 19:03:55 +00:00
dest + = pitch ;
2016-10-09 10:50:57 +00:00
// Clear left and right padding columns.
if ( format = = GL_R8 )
{
for ( int y = Box - > Area . bottom - Box - > Area . top - 2 ; y > 0 ; - - y )
{
dest [ 0 ] = 0 ;
dest [ numbytes - 1 ] = 0 ;
2016-10-10 19:03:55 +00:00
dest + = pitch ;
2016-10-09 10:50:57 +00:00
}
}
else
{
for ( int y = Box - > Area . bottom - Box - > Area . top - 2 ; y > 0 ; - - y )
{
* ( uint32_t * ) dest = 0 ;
* ( uint32_t * ) ( dest + numbytes - 4 ) = 0 ;
2016-10-10 19:03:55 +00:00
dest + = pitch ;
2016-10-09 10:50:57 +00:00
}
}
// Clear bottom padding row.
memset ( dest , 0 , numbytes ) ;
}
2017-01-25 06:18:26 +00:00
if ( gl . es & & format = = GL_RGBA8 )
{
BgraToRgba ( ( uint32_t * ) bits , ( const uint32_t * ) bits , rect . right - rect . left , rect . bottom - rect . top , rect . right - rect . left ) ;
}
2016-10-10 19:03:55 +00:00
2017-01-25 06:18:26 +00:00
if ( gl . es )
glBufferSubData ( GL_PIXEL_UNPACK_BUFFER , 0 , buffersize , bits ) ;
else
glUnmapBuffer ( GL_PIXEL_UNPACK_BUFFER ) ;
2016-10-10 19:03:55 +00:00
GLint oldBinding = 0 ;
glGetIntegerv ( GL_TEXTURE_BINDING_2D , & oldBinding ) ;
glBindTexture ( GL_TEXTURE_2D , Box - > Owner - > Tex - > Texture ) ;
2016-10-16 20:40:08 +00:00
if ( format = = GL_RGBA8 )
2017-01-25 06:18:26 +00:00
glTexSubImage2D ( GL_TEXTURE_2D , 0 , rect . left , rect . top , rect . right - rect . left , rect . bottom - rect . top , gl . es ? GL_RGBA : GL_BGRA , GL_UNSIGNED_BYTE , 0 ) ;
2016-10-16 20:40:08 +00:00
else
glTexSubImage2D ( GL_TEXTURE_2D , 0 , rect . left , rect . top , rect . right - rect . left , rect . bottom - rect . top , GL_RED , GL_UNSIGNED_BYTE , 0 ) ;
2016-10-10 19:03:55 +00:00
glBindTexture ( GL_TEXTURE_2D , oldBinding ) ;
2016-10-10 22:03:46 +00:00
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , 0 ) ;
2016-10-09 10:50:57 +00:00
return true ;
}
//==========================================================================
//
// OpenGLTex :: GetTexFormat
//
// Returns the texture format that would best fit this texture.
//
//==========================================================================
int OpenGLSWFrameBuffer : : OpenGLTex : : GetTexFormat ( )
{
FTextureFormat fmt = GameTex - > GetFormat ( ) ;
IsGray = false ;
switch ( fmt )
{
case TEX_Pal : return GL_R8 ;
case TEX_Gray : IsGray = true ; return GL_R8 ;
case TEX_RGB : return GL_RGBA8 ;
case TEX_DXT1 : return GL_COMPRESSED_RGB_S3TC_DXT1_EXT ;
case TEX_DXT2 : return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ;
case TEX_DXT3 : return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ;
case TEX_DXT4 : return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ; // Doesn't exist in OpenGL. Closest match is DXT5.
case TEX_DXT5 : return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ;
default : I_FatalError ( " GameTex->GetFormat() returned invalid format. " ) ;
}
return GL_R8 ;
}
//==========================================================================
//
// OpenGLTex :: ToTexFmt
//
// Converts an OpenGL internal format constant to something the FTexture system
// understands.
//
//==========================================================================
FTextureFormat OpenGLSWFrameBuffer : : OpenGLTex : : ToTexFmt ( int fmt )
{
switch ( fmt )
{
case GL_R8 : return IsGray ? TEX_Gray : TEX_Pal ;
case GL_RGBA8 : return TEX_RGB ;
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT : return TEX_DXT1 ;
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : return TEX_DXT2 ;
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT : return TEX_DXT3 ;
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : return TEX_DXT5 ;
default :
assert ( 0 ) ; // LOL WUT?
return TEX_Pal ;
}
}
//==========================================================================
//
// OpenGLPal Constructor
//
//==========================================================================
OpenGLSWFrameBuffer : : OpenGLPal : : OpenGLPal ( FRemapTable * remap , OpenGLSWFrameBuffer * fb )
2017-05-27 00:31:15 +00:00
: Remap ( remap )
2016-10-09 10:50:57 +00:00
{
int count ;
// Attach to the palette list for the OpenGLSWFrameBuffer
Next = fb - > Palettes ;
if ( Next ! = nullptr )
{
Next - > Prev = & Next ;
}
Prev = & fb - > Palettes ;
fb - > Palettes = this ;
int pow2count ;
// Round up to the nearest power of 2.
for ( pow2count = 1 ; pow2count < remap - > NumEntries ; pow2count < < = 1 )
{
}
count = pow2count ;
DoColorSkip = false ;
BorderColor = 0 ;
RoundedPaletteSize = count ;
2017-05-27 00:31:15 +00:00
Tex = fb - > CreateTexture ( " Pal " , count , 1 , 1 , GL_RGBA8 ) ;
if ( Tex )
2016-10-09 10:50:57 +00:00
{
if ( ! Update ( ) )
{
2017-05-27 00:31:15 +00:00
Tex . reset ( ) ;
2016-10-09 10:50:57 +00:00
}
}
}
//==========================================================================
//
// OpenGLPal Destructor
//
//==========================================================================
OpenGLSWFrameBuffer : : OpenGLPal : : ~ OpenGLPal ( )
{
2017-05-27 00:31:15 +00:00
Tex . reset ( ) ;
2016-10-09 10:50:57 +00:00
// Detach from the palette list
* Prev = Next ;
if ( Next ! = nullptr )
{
Next - > Prev = Prev ;
}
// Remove link from the remap table
if ( Remap ! = nullptr )
{
Remap - > Native = nullptr ;
}
}
//==========================================================================
//
// OpenGLPal :: Update
//
// Copies the palette to the texture.
//
//==========================================================================
bool OpenGLSWFrameBuffer : : OpenGLPal : : Update ( )
{
uint32_t * buff ;
const PalEntry * pal ;
int skipat , i ;
assert ( Tex ! = nullptr ) ;
2016-10-11 08:27:18 +00:00
if ( Tex - > Buffers [ 0 ] = = 0 )
2016-10-10 19:03:55 +00:00
{
2016-10-11 08:27:18 +00:00
glGenBuffers ( 2 , ( GLuint * ) Tex - > Buffers ) ;
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , Tex - > Buffers [ 0 ] ) ;
2016-11-29 21:16:40 +00:00
glBufferData ( GL_PIXEL_UNPACK_BUFFER , RoundedPaletteSize * 4 , nullptr , GL_STREAM_DRAW ) ;
2016-10-11 08:27:18 +00:00
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , Tex - > Buffers [ 1 ] ) ;
2016-11-29 21:16:40 +00:00
glBufferData ( GL_PIXEL_UNPACK_BUFFER , RoundedPaletteSize * 4 , nullptr , GL_STREAM_DRAW ) ;
2016-10-10 19:03:55 +00:00
}
else
{
2016-10-11 08:27:18 +00:00
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , Tex - > Buffers [ Tex - > CurrentBuffer ] ) ;
Tex - > CurrentBuffer = ( Tex - > CurrentBuffer + 1 ) & 1 ;
2016-10-10 19:03:55 +00:00
}
2016-11-29 21:16:40 +00:00
int numEntries = MIN ( Remap - > NumEntries , RoundedPaletteSize ) ;
2017-01-25 06:18:26 +00:00
std : : vector < uint8_t > & tempbuffer = Tex - > MapBuffer ;
if ( gl . es )
tempbuffer . resize ( numEntries * 4 ) ;
2016-11-29 21:16:40 +00:00
2017-01-25 06:18:26 +00:00
buff = gl . es ? ( uint32_t * ) tempbuffer . data ( ) : ( uint32_t * ) MapBuffer ( GL_PIXEL_UNPACK_BUFFER , numEntries * 4 ) ;
2016-10-10 19:03:55 +00:00
if ( buff = = nullptr )
2016-10-09 10:50:57 +00:00
{
return false ;
}
pal = Remap - > Palette ;
// See explanation in UploadPalette() for skipat rationale.
2016-11-29 21:16:40 +00:00
skipat = MIN ( numEntries , DoColorSkip ? 256 - 8 : 256 ) ;
2016-10-09 10:50:57 +00:00
2017-05-06 20:03:32 +00:00
# ifndef NO_SSE
// Manual SSE vectorized version here to workaround a bug in GCC's auto-vectorizer
int sse_count = skipat / 4 * 4 ;
for ( i = 0 ; i < sse_count ; i + = 4 )
{
_mm_storeu_si128 ( ( __m128i * ) ( & buff [ i ] ) , _mm_loadu_si128 ( ( __m128i * ) ( & pal [ i ] ) ) ) ;
}
switch ( skipat - i )
{
// fall through is intentional
case 3 : buff [ i ] = pal [ i ] . d ; i + + ;
case 2 : buff [ i ] = pal [ i ] . d ; i + + ;
case 1 : buff [ i ] = pal [ i ] . d ; i + + ;
default : i + + ;
}
sse_count = numEntries / 4 * 4 ;
__m128i alphamask = _mm_set1_epi32 ( 0xff000000 ) ;
while ( i < sse_count )
{
__m128i lastcolor = _mm_loadu_si128 ( ( __m128i * ) ( & pal [ i - 1 ] ) ) ;
__m128i color = _mm_loadu_si128 ( ( __m128i * ) ( & pal [ i ] ) ) ;
_mm_storeu_si128 ( ( __m128i * ) ( & buff [ i ] ) , _mm_or_si128 ( _mm_and_si128 ( alphamask , color ) , _mm_andnot_si128 ( alphamask , lastcolor ) ) ) ;
i + = 4 ;
}
switch ( numEntries - i )
{
// fall through is intentional
case 3 : buff [ i ] = ColorARGB ( pal [ i ] . a , pal [ i - 1 ] . r , pal [ i - 1 ] . g , pal [ i - 1 ] . b ) ; i + + ;
case 2 : buff [ i ] = ColorARGB ( pal [ i ] . a , pal [ i - 1 ] . r , pal [ i - 1 ] . g , pal [ i - 1 ] . b ) ; i + + ;
case 1 : buff [ i ] = ColorARGB ( pal [ i ] . a , pal [ i - 1 ] . r , pal [ i - 1 ] . g , pal [ i - 1 ] . b ) ; i + + ;
default : break ;
}
# else
2016-10-09 10:50:57 +00:00
for ( i = 0 ; i < skipat ; + + i )
{
buff [ i ] = ColorARGB ( pal [ i ] . a , pal [ i ] . r , pal [ i ] . g , pal [ i ] . b ) ;
}
2016-11-29 21:16:40 +00:00
for ( + + i ; i < numEntries ; + + i )
2016-10-09 10:50:57 +00:00
{
buff [ i ] = ColorARGB ( pal [ i ] . a , pal [ i - 1 ] . r , pal [ i - 1 ] . g , pal [ i - 1 ] . b ) ;
}
2017-05-06 20:03:32 +00:00
# endif
2016-12-05 03:05:55 +00:00
if ( numEntries > 1 )
{
i = numEntries - 1 ;
BorderColor = ColorARGB ( pal [ i ] . a , pal [ i - 1 ] . r , pal [ i - 1 ] . g , pal [ i - 1 ] . b ) ;
}
2017-01-25 06:18:26 +00:00
if ( gl . es )
{
BgraToRgba ( ( uint32_t * ) buff , ( const uint32_t * ) buff , numEntries , 1 , numEntries ) ;
glBufferSubData ( GL_PIXEL_UNPACK_BUFFER , 0 , numEntries * 4 , buff ) ;
}
else
{
glUnmapBuffer ( GL_PIXEL_UNPACK_BUFFER ) ;
}
2016-10-10 19:03:55 +00:00
GLint oldBinding = 0 ;
glGetIntegerv ( GL_TEXTURE_BINDING_2D , & oldBinding ) ;
glBindTexture ( GL_TEXTURE_2D , Tex - > Texture ) ;
2017-01-25 06:18:26 +00:00
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , numEntries , 1 , gl . es ? GL_RGBA : GL_BGRA , GL_UNSIGNED_BYTE , 0 ) ;
2016-10-10 19:03:55 +00:00
glBindTexture ( GL_TEXTURE_2D , oldBinding ) ;
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , 0 ) ;
2016-10-09 10:50:57 +00:00
return true ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: Begin2D
//
// Begins 2D mode drawing operations. In particular, DrawTexture is
// rerouted to use Direct3D instead of the software renderer.
//
//==========================================================================
bool OpenGLSWFrameBuffer : : Begin2D ( bool copy3d )
{
2017-04-01 20:14:04 +00:00
Super : : Begin2D ( copy3d ) ;
2016-10-09 10:50:57 +00:00
if ( ! Accel2D )
{
return false ;
}
if ( In2D )
{
return true ;
}
In2D = 2 - copy3d ;
Update ( ) ;
In2D = 3 ;
return true ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: DrawBlendingRect
//
// Call after Begin2D to blend the 3D view.
//
//==========================================================================
void OpenGLSWFrameBuffer : : DrawBlendingRect ( )
{
if ( ! In2D | | ! Accel2D )
{
return ;
}
Dim ( FlashColor , FlashAmount / 256.f , viewwindowx , viewwindowy , viewwidth , viewheight ) ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: CreateTexture
//
// Returns a native texture that wraps a FTexture.
//
//==========================================================================
FNativeTexture * OpenGLSWFrameBuffer : : CreateTexture ( FTexture * gametex , bool wrapping )
{
OpenGLTex * tex = new OpenGLTex ( gametex , this , wrapping ) ;
if ( tex - > Box = = nullptr )
{
delete tex ;
return nullptr ;
}
return tex ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: CreatePalette
//
// Returns a native texture that contains a palette.
//
//==========================================================================
FNativePalette * OpenGLSWFrameBuffer : : CreatePalette ( FRemapTable * remap )
{
OpenGLPal * tex = new OpenGLPal ( remap , this ) ;
if ( tex - > Tex = = nullptr )
{
delete tex ;
return nullptr ;
}
return tex ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: Clear
//
// Fills the specified region with a color.
//
//==========================================================================
2017-03-28 11:06:24 +00:00
void OpenGLSWFrameBuffer : : DoClear ( int left , int top , int right , int bottom , int palcolor , uint32_t color )
2016-10-09 10:50:57 +00:00
{
if ( In2D < 2 )
{
2017-05-02 20:56:31 +00:00
Super : : DoClear ( left , top , right , bottom , palcolor , color ) ;
2016-10-09 10:50:57 +00:00
return ;
}
if ( ! InScene )
{
return ;
}
if ( palcolor > = 0 & & color = = 0 )
{
color = GPalette . BaseColors [ palcolor ] ;
}
else if ( APART ( color ) < 255 )
{
Dim ( color , APART ( color ) / 255.f , left , top , right - left , bottom - top ) ;
return ;
}
AddColorOnlyQuad ( left , top , right - left , bottom - top , color | 0xFF000000 ) ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: Dim
//
//==========================================================================
2017-03-28 11:06:24 +00:00
void OpenGLSWFrameBuffer : : DoDim ( PalEntry color , float amount , int x1 , int y1 , int w , int h )
2016-10-09 10:50:57 +00:00
{
if ( amount < = 0 )
{
return ;
}
if ( In2D < 2 )
{
2017-05-06 08:43:48 +00:00
Super : : DoDim ( color , amount , x1 , y1 , w , h ) ;
2016-10-09 10:50:57 +00:00
return ;
}
if ( ! InScene )
{
return ;
}
if ( amount > 1 )
{
amount = 1 ;
}
AddColorOnlyQuad ( x1 , y1 , w , h , color | ( int ( amount * 255 ) < < 24 ) ) ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: BeginLineBatch
//
//==========================================================================
void OpenGLSWFrameBuffer : : BeginLineBatch ( )
{
if ( In2D < 2 | | ! InScene | | BatchType = = BATCH_Lines )
{
return ;
}
EndQuadBatch ( ) ; // Make sure all quads have been drawn first.
VertexData = VertexBuffer - > Lock ( ) ;
VertexPos = 0 ;
BatchType = BATCH_Lines ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: EndLineBatch
//
//==========================================================================
void OpenGLSWFrameBuffer : : EndLineBatch ( )
{
if ( In2D < 2 | | ! InScene | | BatchType ! = BATCH_Lines )
{
return ;
}
VertexBuffer - > Unlock ( ) ;
if ( VertexPos > 0 )
{
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ SHADER_VertexColor ] . get ( ) ) ;
2016-10-09 10:50:57 +00:00
SetAlphaBlend ( GL_FUNC_ADD , GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
2017-05-27 00:31:15 +00:00
SetStreamSource ( VertexBuffer . get ( ) ) ;
2016-10-09 10:50:57 +00:00
DrawLineList ( VertexPos / 2 ) ;
}
VertexPos = - 1 ;
BatchType = BATCH_None ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: DrawLine
//
//==========================================================================
2017-03-09 18:31:45 +00:00
void OpenGLSWFrameBuffer : : DrawLine ( int x0 , int y0 , int x1 , int y1 , int palcolor , uint32_t color )
2016-10-09 10:50:57 +00:00
{
if ( In2D < 2 )
{
Super : : DrawLine ( x0 , y0 , x1 , y1 , palcolor , color ) ;
return ;
}
if ( ! InScene )
{
return ;
}
if ( BatchType ! = BATCH_Lines )
{
BeginLineBatch ( ) ;
}
if ( VertexPos = = NUM_VERTS )
{ // Flush the buffer and refill it.
EndLineBatch ( ) ;
BeginLineBatch ( ) ;
}
// Add the endpoints to the vertex buffer.
VertexData [ VertexPos ] . x = float ( x0 ) ;
2016-10-12 05:34:07 +00:00
VertexData [ VertexPos ] . y = float ( y0 ) ;
2016-10-09 10:50:57 +00:00
VertexData [ VertexPos ] . z = 0 ;
VertexData [ VertexPos ] . rhw = 1 ;
VertexData [ VertexPos ] . color0 = color ;
VertexData [ VertexPos ] . color1 = 0 ;
VertexData [ VertexPos ] . tu = 0 ;
VertexData [ VertexPos ] . tv = 0 ;
VertexData [ VertexPos + 1 ] . x = float ( x1 ) ;
2016-10-12 05:34:07 +00:00
VertexData [ VertexPos + 1 ] . y = float ( y1 ) ;
2016-10-09 10:50:57 +00:00
VertexData [ VertexPos + 1 ] . z = 0 ;
VertexData [ VertexPos + 1 ] . rhw = 1 ;
VertexData [ VertexPos + 1 ] . color0 = color ;
VertexData [ VertexPos + 1 ] . color1 = 0 ;
VertexData [ VertexPos + 1 ] . tu = 0 ;
VertexData [ VertexPos + 1 ] . tv = 0 ;
VertexPos + = 2 ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: DrawPixel
//
//==========================================================================
2017-03-09 18:31:45 +00:00
void OpenGLSWFrameBuffer : : DrawPixel ( int x , int y , int palcolor , uint32_t color )
2016-10-09 10:50:57 +00:00
{
if ( In2D < 2 )
{
Super : : DrawPixel ( x , y , palcolor , color ) ;
return ;
}
if ( ! InScene )
{
return ;
}
FBVERTEX pt =
{
float ( x ) , float ( y ) , 0 , 1 , color
} ;
EndBatch ( ) ; // Draw out any batched operations.
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ SHADER_VertexColor ] . get ( ) ) ;
2016-10-09 10:50:57 +00:00
SetAlphaBlend ( GL_FUNC_ADD , GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
DrawPoints ( 1 , & pt ) ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: DrawTextureV
//
// If not in 2D mode, just call the normal software version.
// If in 2D mode, then use Direct3D calls to perform the drawing.
//
//==========================================================================
void OpenGLSWFrameBuffer : : DrawTextureParms ( FTexture * img , DrawParms & parms )
{
if ( In2D < 2 )
{
Super : : DrawTextureParms ( img , parms ) ;
return ;
}
if ( ! InScene )
{
return ;
}
OpenGLTex * tex = static_cast < OpenGLTex * > ( img - > GetNative ( false ) ) ;
if ( tex = = nullptr )
{
assert ( tex ! = nullptr ) ;
return ;
}
CheckQuadBatch ( ) ;
double xscale = parms . destwidth / parms . texwidth ;
double yscale = parms . destheight / parms . texheight ;
double x0 = parms . x - parms . left * xscale ;
double y0 = parms . y - parms . top * yscale ;
double x1 = x0 + parms . destwidth ;
double y1 = y0 + parms . destheight ;
float u0 = tex - > Box - > Left ;
float v0 = tex - > Box - > Top ;
float u1 = tex - > Box - > Right ;
float v1 = tex - > Box - > Bottom ;
double uscale = 1.f / tex - > Box - > Owner - > Width ;
bool scissoring = false ;
FBVERTEX * vert ;
if ( parms . flipX )
{
swapvalues ( u0 , u1 ) ;
}
if ( parms . windowleft > 0 | | parms . windowright < parms . texwidth )
{
double wi = MIN ( parms . windowright , parms . texwidth ) ;
x0 + = parms . windowleft * xscale ;
u0 = float ( u0 + parms . windowleft * uscale ) ;
x1 - = ( parms . texwidth - wi ) * xscale ;
u1 = float ( u1 - ( parms . texwidth - wi ) * uscale ) ;
}
#if 0
float vscale = 1.f / tex - > Box - > Owner - > Height / yscale ;
if ( y0 < parms . uclip )
{
v0 + = ( float ( parms . uclip ) - y0 ) * vscale ;
y0 = float ( parms . uclip ) ;
}
if ( y1 > parms . dclip )
{
v1 - = ( y1 - float ( parms . dclip ) ) * vscale ;
y1 = float ( parms . dclip ) ;
}
if ( x0 < parms . lclip )
{
u0 + = float ( parms . lclip - x0 ) * uscale / xscale * 2 ;
x0 = float ( parms . lclip ) ;
}
if ( x1 > parms . rclip )
{
u1 - = ( x1 - parms . rclip ) * uscale / xscale * 2 ;
x1 = float ( parms . rclip ) ;
}
# else
// Use a scissor test because the math above introduces some jitter
// that is noticeable at low resolutions. Unfortunately, this means this
// quad has to be in a batch by itself.
if ( y0 < parms . uclip | | y1 > parms . dclip | | x0 < parms . lclip | | x1 > parms . rclip )
{
scissoring = true ;
if ( QuadBatchPos > 0 )
{
EndQuadBatch ( ) ;
BeginQuadBatch ( ) ;
}
glEnable ( GL_SCISSOR_TEST ) ;
2016-10-12 05:34:07 +00:00
glScissor ( parms . lclip , parms . uclip , parms . rclip - parms . lclip , parms . dclip - parms . uclip ) ;
2016-10-09 10:50:57 +00:00
}
# endif
parms . bilinear = false ;
uint32_t color0 , color1 ;
BufferedTris * quad = & QuadExtra [ QuadBatchPos ] ;
if ( ! SetStyle ( tex , parms , color0 , color1 , * quad ) )
{
goto done ;
}
2017-05-27 00:31:15 +00:00
quad - > Texture = tex - > Box - > Owner - > Tex . get ( ) ;
2016-10-09 10:50:57 +00:00
if ( parms . bilinear )
{
quad - > Flags | = BQF_Bilinear ;
}
quad - > NumTris = 2 ;
quad - > NumVerts = 4 ;
vert = & VertexData [ VertexPos ] ;
2017-03-29 19:22:05 +00:00
{
PalEntry color = color1 ;
color = PalEntry ( ( color . a * parms . color . a ) / 255 , ( color . r * parms . color . r ) / 255 , ( color . g * parms . color . g ) / 255 , ( color . b * parms . color . b ) / 255 ) ;
color1 = color ;
}
2016-10-09 10:50:57 +00:00
// Fill the vertex buffer.
vert [ 0 ] . x = float ( x0 ) ;
vert [ 0 ] . y = float ( y0 ) ;
vert [ 0 ] . z = 0 ;
vert [ 0 ] . rhw = 1 ;
vert [ 0 ] . color0 = color0 ;
vert [ 0 ] . color1 = color1 ;
vert [ 0 ] . tu = u0 ;
vert [ 0 ] . tv = v0 ;
vert [ 1 ] . x = float ( x1 ) ;
vert [ 1 ] . y = float ( y0 ) ;
vert [ 1 ] . z = 0 ;
vert [ 1 ] . rhw = 1 ;
vert [ 1 ] . color0 = color0 ;
vert [ 1 ] . color1 = color1 ;
vert [ 1 ] . tu = u1 ;
vert [ 1 ] . tv = v0 ;
vert [ 2 ] . x = float ( x1 ) ;
vert [ 2 ] . y = float ( y1 ) ;
vert [ 2 ] . z = 0 ;
vert [ 2 ] . rhw = 1 ;
vert [ 2 ] . color0 = color0 ;
vert [ 2 ] . color1 = color1 ;
vert [ 2 ] . tu = u1 ;
vert [ 2 ] . tv = v1 ;
vert [ 3 ] . x = float ( x0 ) ;
vert [ 3 ] . y = float ( y1 ) ;
vert [ 3 ] . z = 0 ;
vert [ 3 ] . rhw = 1 ;
vert [ 3 ] . color0 = color0 ;
vert [ 3 ] . color1 = color1 ;
vert [ 3 ] . tu = u0 ;
vert [ 3 ] . tv = v1 ;
// Fill the vertex index buffer.
IndexData [ IndexPos ] = VertexPos ;
IndexData [ IndexPos + 1 ] = VertexPos + 1 ;
IndexData [ IndexPos + 2 ] = VertexPos + 2 ;
IndexData [ IndexPos + 3 ] = VertexPos ;
IndexData [ IndexPos + 4 ] = VertexPos + 2 ;
IndexData [ IndexPos + 5 ] = VertexPos + 3 ;
// Batch the quad.
QuadBatchPos + + ;
VertexPos + = 4 ;
IndexPos + = 6 ;
done :
if ( scissoring )
{
EndQuadBatch ( ) ;
glDisable ( GL_SCISSOR_TEST ) ;
}
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: FlatFill
//
// Fills an area with a repeating copy of the texture.
//
//==========================================================================
void OpenGLSWFrameBuffer : : FlatFill ( int left , int top , int right , int bottom , FTexture * src , bool local_origin )
{
if ( In2D < 2 )
{
Super : : FlatFill ( left , top , right , bottom , src , local_origin ) ;
return ;
}
if ( ! InScene )
{
return ;
}
OpenGLTex * tex = static_cast < OpenGLTex * > ( src - > GetNative ( true ) ) ;
if ( tex = = nullptr )
{
return ;
}
float x0 = float ( left ) ;
float y0 = float ( top ) ;
float x1 = float ( right ) ;
float y1 = float ( bottom ) ;
float itw = 1.f / float ( src - > GetWidth ( ) ) ;
float ith = 1.f / float ( src - > GetHeight ( ) ) ;
float xo = local_origin ? x0 : 0 ;
float yo = local_origin ? y0 : 0 ;
float u0 = ( x0 - xo ) * itw ;
float v0 = ( y0 - yo ) * ith ;
float u1 = ( x1 - xo ) * itw ;
float v1 = ( y1 - yo ) * ith ;
CheckQuadBatch ( ) ;
BufferedTris * quad = & QuadExtra [ QuadBatchPos ] ;
FBVERTEX * vert = & VertexData [ VertexPos ] ;
2016-10-11 08:27:18 +00:00
quad - > ClearSetup ( ) ;
2016-10-09 10:50:57 +00:00
if ( tex - > GetTexFormat ( ) = = GL_R8 & & ! tex - > IsGray )
{
quad - > Flags = BQF_WrapUV | BQF_GamePalette ; // | BQF_DisableAlphaTest;
quad - > ShaderNum = BQS_PalTex ;
}
else
{
quad - > Flags = BQF_WrapUV ; // | BQF_DisableAlphaTest;
quad - > ShaderNum = BQS_Plain ;
}
quad - > Palette = nullptr ;
2017-05-27 00:31:15 +00:00
quad - > Texture = tex - > Box - > Owner - > Tex . get ( ) ;
2016-10-09 10:50:57 +00:00
quad - > NumVerts = 4 ;
quad - > NumTris = 2 ;
vert [ 0 ] . x = x0 ;
vert [ 0 ] . y = y0 ;
vert [ 0 ] . z = 0 ;
vert [ 0 ] . rhw = 1 ;
vert [ 0 ] . color0 = 0 ;
vert [ 0 ] . color1 = 0xFFFFFFFF ;
vert [ 0 ] . tu = u0 ;
vert [ 0 ] . tv = v0 ;
vert [ 1 ] . x = x1 ;
vert [ 1 ] . y = y0 ;
vert [ 1 ] . z = 0 ;
vert [ 1 ] . rhw = 1 ;
vert [ 1 ] . color0 = 0 ;
vert [ 1 ] . color1 = 0xFFFFFFFF ;
vert [ 1 ] . tu = u1 ;
vert [ 1 ] . tv = v0 ;
vert [ 2 ] . x = x1 ;
vert [ 2 ] . y = y1 ;
vert [ 2 ] . z = 0 ;
vert [ 2 ] . rhw = 1 ;
vert [ 2 ] . color0 = 0 ;
vert [ 2 ] . color1 = 0xFFFFFFFF ;
vert [ 2 ] . tu = u1 ;
vert [ 2 ] . tv = v1 ;
vert [ 3 ] . x = x0 ;
vert [ 3 ] . y = y1 ;
vert [ 3 ] . z = 0 ;
vert [ 3 ] . rhw = 1 ;
vert [ 3 ] . color0 = 0 ;
vert [ 3 ] . color1 = 0xFFFFFFFF ;
vert [ 3 ] . tu = u0 ;
vert [ 3 ] . tv = v1 ;
IndexData [ IndexPos ] = VertexPos ;
IndexData [ IndexPos + 1 ] = VertexPos + 1 ;
IndexData [ IndexPos + 2 ] = VertexPos + 2 ;
IndexData [ IndexPos + 3 ] = VertexPos ;
IndexData [ IndexPos + 4 ] = VertexPos + 2 ;
IndexData [ IndexPos + 5 ] = VertexPos + 3 ;
QuadBatchPos + + ;
VertexPos + = 4 ;
IndexPos + = 6 ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: FillSimplePoly
//
// Here, "simple" means that a simple triangle fan can draw it.
//
//==========================================================================
void OpenGLSWFrameBuffer : : FillSimplePoly ( FTexture * texture , FVector2 * points , int npoints ,
double originx , double originy , double scalex , double scaley ,
2017-03-15 15:47:42 +00:00
DAngle rotation , const FColormap & colormap , PalEntry flatcolor , int lightlevel , int bottomclip )
2016-10-09 10:50:57 +00:00
{
// Use an equation similar to player sprites to determine shade
2017-03-06 22:27:02 +00:00
double fadelevel = clamp ( ( swrenderer : : LightVisibility : : LightLevelToShade ( lightlevel , true ) / 65536. - 12 ) / NUMCOLORMAPS , 0.0 , 1.0 ) ;
2016-10-09 10:50:57 +00:00
BufferedTris * quad ;
FBVERTEX * verts ;
OpenGLTex * tex ;
2016-10-12 05:34:07 +00:00
float uscale , vscale ;
2016-10-09 10:50:57 +00:00
int i , ipos ;
uint32_t color0 , color1 ;
float ox , oy ;
float cosrot , sinrot ;
bool dorotate = rotation ! = 0 ;
if ( npoints < 3 )
{ // This is no polygon.
return ;
}
if ( In2D < 2 )
{
2017-01-29 06:49:04 +00:00
Super : : FillSimplePoly ( texture , points , npoints , originx , originy , scalex , scaley , rotation , colormap , lightlevel , flatcolor , bottomclip ) ;
2016-10-09 10:50:57 +00:00
return ;
}
if ( ! InScene )
{
return ;
}
tex = static_cast < OpenGLTex * > ( texture - > GetNative ( true ) ) ;
if ( tex = = nullptr )
{
return ;
}
cosrot = ( float ) cos ( rotation . Radians ( ) ) ;
sinrot = ( float ) sin ( rotation . Radians ( ) ) ;
CheckQuadBatch ( npoints - 2 , npoints ) ;
quad = & QuadExtra [ QuadBatchPos ] ;
verts = & VertexData [ VertexPos ] ;
color0 = 0 ;
color1 = 0xFFFFFFFF ;
2016-10-11 08:27:18 +00:00
quad - > ClearSetup ( ) ;
2016-10-09 10:50:57 +00:00
if ( tex - > GetTexFormat ( ) = = GL_R8 & & ! tex - > IsGray )
{
quad - > Flags = BQF_WrapUV | BQF_GamePalette | BQF_DisableAlphaTest ;
quad - > ShaderNum = BQS_PalTex ;
2017-03-15 15:47:42 +00:00
if ( colormap . Desaturation ! = 0 )
2016-10-09 10:50:57 +00:00
{
2017-03-15 15:47:42 +00:00
quad - > Flags | = BQF_Desaturated ;
2016-10-09 10:50:57 +00:00
}
2017-03-15 15:47:42 +00:00
quad - > ShaderNum = BQS_InGameColormap ;
quad - > Desat = colormap . Desaturation ;
color0 = ColorARGB ( 255 , colormap . LightColor . r , colormap . LightColor . g , colormap . LightColor . b ) ;
color1 = ColorARGB ( uint32_t ( ( 1 - fadelevel ) * 255 ) ,
uint32_t ( colormap . FadeColor . r * fadelevel ) ,
uint32_t ( colormap . FadeColor . g * fadelevel ) ,
uint32_t ( colormap . FadeColor . b * fadelevel ) ) ;
2016-10-09 10:50:57 +00:00
}
else
{
quad - > Flags = BQF_WrapUV | BQF_DisableAlphaTest ;
quad - > ShaderNum = BQS_Plain ;
}
quad - > Palette = nullptr ;
2017-05-27 00:31:15 +00:00
quad - > Texture = tex - > Box - > Owner - > Tex . get ( ) ;
2016-10-09 10:50:57 +00:00
quad - > NumVerts = npoints ;
quad - > NumTris = npoints - 2 ;
uscale = float ( 1.f / ( texture - > GetScaledWidth ( ) * scalex ) ) ;
vscale = float ( 1.f / ( texture - > GetScaledHeight ( ) * scaley ) ) ;
ox = float ( originx ) ;
oy = float ( originy ) ;
for ( i = 0 ; i < npoints ; + + i )
{
verts [ i ] . x = points [ i ] . X ;
2016-10-12 05:34:07 +00:00
verts [ i ] . y = points [ i ] . Y ;
2016-10-09 10:50:57 +00:00
verts [ i ] . z = 0 ;
verts [ i ] . rhw = 1 ;
verts [ i ] . color0 = color0 ;
verts [ i ] . color1 = color1 ;
2016-10-12 05:34:07 +00:00
float u = points [ i ] . X - ox ;
float v = points [ i ] . Y - oy ;
2016-10-09 10:50:57 +00:00
if ( dorotate )
{
float t = u ;
u = t * cosrot - v * sinrot ;
v = v * cosrot + t * sinrot ;
}
verts [ i ] . tu = u * uscale ;
verts [ i ] . tv = v * vscale ;
}
for ( ipos = IndexPos , i = 2 ; i < npoints ; + + i , ipos + = 3 )
{
IndexData [ ipos ] = VertexPos ;
IndexData [ ipos + 1 ] = VertexPos + i - 1 ;
IndexData [ ipos + 2 ] = VertexPos + i ;
}
QuadBatchPos + + ;
VertexPos + = npoints ;
IndexPos = ipos ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: AddColorOnlyQuad
//
// Adds a single-color, untextured quad to the batch.
//
//==========================================================================
void OpenGLSWFrameBuffer : : AddColorOnlyQuad ( int left , int top , int width , int height , uint32_t color )
{
BufferedTris * quad ;
FBVERTEX * verts ;
CheckQuadBatch ( ) ;
quad = & QuadExtra [ QuadBatchPos ] ;
verts = & VertexData [ VertexPos ] ;
2016-10-12 05:34:07 +00:00
float x = float ( left ) ;
float y = float ( top ) ;
2016-10-09 10:50:57 +00:00
2016-10-11 08:27:18 +00:00
quad - > ClearSetup ( ) ;
2016-10-09 10:50:57 +00:00
quad - > ShaderNum = BQS_ColorOnly ;
if ( ( color & 0xFF000000 ) ! = 0xFF000000 )
{
quad - > BlendOp = GL_FUNC_ADD ;
quad - > SrcBlend = GL_SRC_ALPHA ;
quad - > DestBlend = GL_ONE_MINUS_SRC_ALPHA ;
}
quad - > Palette = nullptr ;
quad - > Texture = nullptr ;
quad - > NumVerts = 4 ;
quad - > NumTris = 2 ;
verts [ 0 ] . x = x ;
verts [ 0 ] . y = y ;
verts [ 0 ] . z = 0 ;
verts [ 0 ] . rhw = 1 ;
verts [ 0 ] . color0 = color ;
verts [ 0 ] . color1 = 0 ;
verts [ 0 ] . tu = 0 ;
verts [ 0 ] . tv = 0 ;
verts [ 1 ] . x = x + width ;
verts [ 1 ] . y = y ;
verts [ 1 ] . z = 0 ;
verts [ 1 ] . rhw = 1 ;
verts [ 1 ] . color0 = color ;
verts [ 1 ] . color1 = 0 ;
verts [ 1 ] . tu = 0 ;
verts [ 1 ] . tv = 0 ;
verts [ 2 ] . x = x + width ;
verts [ 2 ] . y = y + height ;
verts [ 2 ] . z = 0 ;
verts [ 2 ] . rhw = 1 ;
verts [ 2 ] . color0 = color ;
verts [ 2 ] . color1 = 0 ;
verts [ 2 ] . tu = 0 ;
verts [ 2 ] . tv = 0 ;
verts [ 3 ] . x = x ;
verts [ 3 ] . y = y + height ;
verts [ 3 ] . z = 0 ;
verts [ 3 ] . rhw = 1 ;
verts [ 3 ] . color0 = color ;
verts [ 3 ] . color1 = 0 ;
verts [ 3 ] . tu = 0 ;
verts [ 3 ] . tv = 0 ;
IndexData [ IndexPos ] = VertexPos ;
IndexData [ IndexPos + 1 ] = VertexPos + 1 ;
IndexData [ IndexPos + 2 ] = VertexPos + 2 ;
IndexData [ IndexPos + 3 ] = VertexPos ;
IndexData [ IndexPos + 4 ] = VertexPos + 2 ;
IndexData [ IndexPos + 5 ] = VertexPos + 3 ;
QuadBatchPos + + ;
VertexPos + = 4 ;
IndexPos + = 6 ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: AddColorOnlyRect
//
// Like AddColorOnlyQuad, except it's hollow.
//
//==========================================================================
void OpenGLSWFrameBuffer : : AddColorOnlyRect ( int left , int top , int width , int height , uint32_t color )
{
AddColorOnlyQuad ( left , top , width - 1 , 1 , color ) ; // top
AddColorOnlyQuad ( left + width - 1 , top , 1 , height - 1 , color ) ; // right
AddColorOnlyQuad ( left + 1 , top + height - 1 , width - 1 , 1 , color ) ; // bottom
AddColorOnlyQuad ( left , top + 1 , 1 , height - 1 , color ) ; // left
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: CheckQuadBatch
//
// Make sure there's enough room in the batch for one more set of triangles.
//
//==========================================================================
void OpenGLSWFrameBuffer : : CheckQuadBatch ( int numtris , int numverts )
{
if ( BatchType = = BATCH_Lines )
{
EndLineBatch ( ) ;
}
else if ( QuadBatchPos = = MAX_QUAD_BATCH | |
VertexPos + numverts > NUM_VERTS | |
IndexPos + numtris * 3 > NUM_INDEXES )
{
EndQuadBatch ( ) ;
}
if ( QuadBatchPos < 0 )
{
BeginQuadBatch ( ) ;
}
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: BeginQuadBatch
//
// Locks the vertex buffer for quads and sets the cursor to 0.
//
//==========================================================================
void OpenGLSWFrameBuffer : : BeginQuadBatch ( )
{
if ( In2D < 2 | | ! InScene | | QuadBatchPos > = 0 )
{
return ;
}
EndLineBatch ( ) ; // Make sure all lines have been drawn first.
VertexData = VertexBuffer - > Lock ( ) ;
IndexData = IndexBuffer - > Lock ( ) ;
VertexPos = 0 ;
IndexPos = 0 ;
QuadBatchPos = 0 ;
BatchType = BATCH_Quads ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: EndQuadBatch
//
// Draws all the quads that have been batched up.
//
//==========================================================================
void OpenGLSWFrameBuffer : : EndQuadBatch ( )
{
if ( In2D < 2 | | ! InScene | | BatchType ! = BATCH_Quads )
{
return ;
}
BatchType = BATCH_None ;
VertexBuffer - > Unlock ( ) ;
IndexBuffer - > Unlock ( ) ;
if ( QuadBatchPos = = 0 )
{
QuadBatchPos = - 1 ;
VertexPos = - 1 ;
IndexPos = - 1 ;
return ;
}
2017-05-27 00:31:15 +00:00
SetStreamSource ( VertexBuffer . get ( ) ) ;
SetIndices ( IndexBuffer . get ( ) ) ;
2016-10-09 10:50:57 +00:00
bool uv_wrapped = false ;
bool uv_should_wrap ;
int indexpos , vertpos ;
indexpos = vertpos = 0 ;
for ( int i = 0 ; i < QuadBatchPos ; )
{
const BufferedTris * quad = & QuadExtra [ i ] ;
int j ;
int startindex = indexpos ;
int startvertex = vertpos ;
indexpos + = quad - > NumTris * 3 ;
vertpos + = quad - > NumVerts ;
// Quads with matching parameters should be done with a single
// DrawPrimitive call.
for ( j = i + 1 ; j < QuadBatchPos ; + + j )
{
const BufferedTris * q2 = & QuadExtra [ j ] ;
if ( quad - > Texture ! = q2 - > Texture | |
2016-10-11 08:27:18 +00:00
! quad - > IsSameSetup ( * q2 ) | |
2016-10-09 10:50:57 +00:00
quad - > Palette ! = q2 - > Palette )
{
break ;
}
if ( quad - > ShaderNum = = BQS_InGameColormap & & ( quad - > Flags & BQF_Desaturated ) & & quad - > Desat ! = q2 - > Desat )
{
break ;
}
indexpos + = q2 - > NumTris * 3 ;
vertpos + = q2 - > NumVerts ;
}
// Set the palette (if one)
if ( ( quad - > Flags & BQF_Paletted ) = = BQF_GamePalette )
{
2017-05-27 00:31:15 +00:00
SetPaletteTexture ( PaletteTexture . get ( ) , 256 , BorderColor ) ;
2016-10-09 10:50:57 +00:00
}
else if ( ( quad - > Flags & BQF_Paletted ) = = BQF_CustomPalette )
{
assert ( quad - > Palette ! = nullptr ) ;
2017-05-27 00:31:15 +00:00
SetPaletteTexture ( quad - > Palette - > Tex . get ( ) , quad - > Palette - > RoundedPaletteSize , quad - > Palette - > BorderColor ) ;
2016-10-09 10:50:57 +00:00
}
// Set the alpha blending
SetAlphaBlend ( quad - > BlendOp , quad - > SrcBlend , quad - > DestBlend ) ;
// Set the alpha test
EnableAlphaTest ( ! ( quad - > Flags & BQF_DisableAlphaTest ) ) ;
// Set the pixel shader
if ( quad - > ShaderNum = = BQS_PalTex )
{
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ ( quad - > Flags & BQF_InvertSource ) ? SHADER_NormalColorPalInv : SHADER_NormalColorPal ] . get ( ) ) ;
2016-10-09 10:50:57 +00:00
}
else if ( quad - > ShaderNum = = BQS_Plain )
{
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ ( quad - > Flags & BQF_InvertSource ) ? SHADER_NormalColorInv : SHADER_NormalColor ] . get ( ) ) ;
2016-10-09 10:50:57 +00:00
}
else if ( quad - > ShaderNum = = BQS_RedToAlpha )
{
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ ( quad - > Flags & BQF_InvertSource ) ? SHADER_RedToAlphaInv : SHADER_RedToAlpha ] . get ( ) ) ;
2016-10-09 10:50:57 +00:00
}
else if ( quad - > ShaderNum = = BQS_ColorOnly )
{
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ SHADER_VertexColor ] . get ( ) ) ;
2016-10-09 10:50:57 +00:00
}
else if ( quad - > ShaderNum = = BQS_SpecialColormap )
{
int select ;
select = ! ! ( quad - > Flags & BQF_Paletted ) ;
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ SHADER_SpecialColormap + select ] . get ( ) ) ;
2016-10-09 10:50:57 +00:00
}
else if ( quad - > ShaderNum = = BQS_InGameColormap )
{
int select ;
select = ! ! ( quad - > Flags & BQF_Desaturated ) ;
select | = ! ! ( quad - > Flags & BQF_InvertSource ) < < 1 ;
select | = ! ! ( quad - > Flags & BQF_Paletted ) < < 2 ;
if ( quad - > Flags & BQF_Desaturated )
{
SetConstant ( PSCONST_Desaturation , quad - > Desat / 255.f , ( 255 - quad - > Desat ) / 255.f , 0 , 0 ) ;
}
2017-05-27 00:31:15 +00:00
SetPixelShader ( Shaders [ SHADER_InGameColormap + select ] . get ( ) ) ;
2016-10-09 10:50:57 +00:00
}
// Set the texture clamp addressing mode
uv_should_wrap = ! ! ( quad - > Flags & BQF_WrapUV ) ;
if ( uv_wrapped ! = uv_should_wrap )
{
uint32_t mode = uv_should_wrap ? GL_REPEAT : GL_CLAMP_TO_EDGE ;
uv_wrapped = uv_should_wrap ;
SetSamplerWrapS ( 0 , mode ) ;
SetSamplerWrapT ( 0 , mode ) ;
}
// Set the texture
if ( quad - > Texture ! = nullptr )
{
SetTexture ( 0 , quad - > Texture ) ;
}
// Draw the quad
DrawTriangleList (
startvertex , // MinIndex
vertpos - startvertex , // NumVertices
startindex , // StartIndex
( indexpos - startindex ) / 3 // PrimitiveCount
/*4 * i, 4 * (j - i), 6 * i, 2 * (j - i)*/ ) ;
i = j ;
}
if ( uv_wrapped )
{
SetSamplerWrapS ( 0 , GL_CLAMP_TO_EDGE ) ;
SetSamplerWrapT ( 0 , GL_CLAMP_TO_EDGE ) ;
}
QuadBatchPos = - 1 ;
VertexPos = - 1 ;
IndexPos = - 1 ;
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: EndBatch
//
// Draws whichever type of primitive is currently being batched.
//
//==========================================================================
void OpenGLSWFrameBuffer : : EndBatch ( )
{
if ( BatchType = = BATCH_Quads )
{
EndQuadBatch ( ) ;
}
else if ( BatchType = = BATCH_Lines )
{
EndLineBatch ( ) ;
}
}
//==========================================================================
//
// OpenGLSWFrameBuffer :: SetStyle
//
// Patterned after R_SetPatchStyle.
//
//==========================================================================
bool OpenGLSWFrameBuffer : : SetStyle ( OpenGLTex * tex , DrawParms & parms , uint32_t & color0 , uint32_t & color1 , BufferedTris & quad )
{
int fmt = tex - > GetTexFormat ( ) ;
FRenderStyle style = parms . style ;
float alpha ;
bool stencilling ;
if ( style . Flags & STYLEF_TransSoulsAlpha )
{
alpha = transsouls ;
}
else if ( style . Flags & STYLEF_Alpha1 )
{
alpha = 1 ;
}
else
{
alpha = clamp ( parms . Alpha , 0.f , 1.f ) ;
}
style . CheckFuzz ( ) ;
if ( style . BlendOp = = STYLEOP_Shadow )
{
style = LegacyRenderStyles [ STYLE_TranslucentStencil ] ;
alpha = 0.3f ;
parms . fillcolor = 0 ;
}
// FIXME: Fuzz effect is not written
if ( style . BlendOp = = STYLEOP_FuzzOrAdd | | style . BlendOp = = STYLEOP_Fuzz )
{
style . BlendOp = STYLEOP_Add ;
}
else if ( style . BlendOp = = STYLEOP_FuzzOrSub )
{
style . BlendOp = STYLEOP_Sub ;
}
else if ( style . BlendOp = = STYLEOP_FuzzOrRevSub )
{
style . BlendOp = STYLEOP_RevSub ;
}
stencilling = false ;
quad . Palette = nullptr ;
quad . Flags = 0 ;
quad . Desat = 0 ;
switch ( style . BlendOp )
{
default :
case STYLEOP_Add : quad . BlendOp = GL_FUNC_ADD ; break ;
case STYLEOP_Sub : quad . BlendOp = GL_FUNC_SUBTRACT ; break ;
case STYLEOP_RevSub : quad . BlendOp = GL_FUNC_REVERSE_SUBTRACT ; break ;
case STYLEOP_None : return false ;
}
quad . SrcBlend = GetStyleAlpha ( style . SrcAlpha ) ;
quad . DestBlend = GetStyleAlpha ( style . DestAlpha ) ;
if ( style . Flags & STYLEF_InvertOverlay )
{
// Only the overlay color is inverted, not the overlay alpha.
parms . colorOverlay = ColorARGB ( APART ( parms . colorOverlay ) ,
255 - RPART ( parms . colorOverlay ) , 255 - GPART ( parms . colorOverlay ) ,
255 - BPART ( parms . colorOverlay ) ) ;
}
SetColorOverlay ( parms . colorOverlay , alpha , color0 , color1 ) ;
if ( style . Flags & STYLEF_ColorIsFixed )
{
if ( style . Flags & STYLEF_InvertSource )
{ // Since the source color is a constant, we can invert it now
// without spending time doing it in the shader.
parms . fillcolor = ColorXRGB ( 255 - RPART ( parms . fillcolor ) ,
255 - GPART ( parms . fillcolor ) , 255 - BPART ( parms . fillcolor ) ) ;
}
// Set up the color mod to replace the color from the image data.
color0 = ( color0 & ColorRGBA ( 0 , 0 , 0 , 255 ) ) | ( parms . fillcolor & ColorRGBA ( 255 , 255 , 255 , 0 ) ) ;
color1 & = ColorRGBA ( 0 , 0 , 0 , 255 ) ;
if ( style . Flags & STYLEF_RedIsAlpha )
{
// Note that if the source texture is paletted, the palette is ignored.
quad . Flags = 0 ;
quad . ShaderNum = BQS_RedToAlpha ;
}
else if ( fmt = = GL_R8 )
{
quad . Flags = BQF_GamePalette ;
quad . ShaderNum = BQS_PalTex ;
}
else
{
quad . Flags = 0 ;
quad . ShaderNum = BQS_Plain ;
}
}
else
{
if ( style . Flags & STYLEF_RedIsAlpha )
{
quad . Flags = 0 ;
quad . ShaderNum = BQS_RedToAlpha ;
}
else if ( fmt = = GL_R8 )
{
if ( parms . remap ! = nullptr )
{
quad . Flags = BQF_CustomPalette ;
quad . Palette = reinterpret_cast < OpenGLPal * > ( parms . remap - > GetNative ( ) ) ;
quad . ShaderNum = BQS_PalTex ;
}
else if ( tex - > IsGray )
{
quad . Flags = 0 ;
quad . ShaderNum = BQS_Plain ;
}
else
{
quad . Flags = BQF_GamePalette ;
quad . ShaderNum = BQS_PalTex ;
}
}
else
{
quad . Flags = 0 ;
quad . ShaderNum = BQS_Plain ;
}
if ( style . Flags & STYLEF_InvertSource )
{
quad . Flags | = BQF_InvertSource ;
}
if ( parms . specialcolormap ! = nullptr )
{ // Emulate an invulnerability or similar colormap.
float * start , * end ;
start = parms . specialcolormap - > ColorizeStart ;
end = parms . specialcolormap - > ColorizeEnd ;
if ( quad . Flags & BQF_InvertSource )
{
quad . Flags & = ~ BQF_InvertSource ;
swapvalues ( start , end ) ;
}
quad . ShaderNum = BQS_SpecialColormap ;
color0 = ColorRGBA ( uint32_t ( start [ 0 ] / 2 * 255 ) , uint32_t ( start [ 1 ] / 2 * 255 ) , uint32_t ( start [ 2 ] / 2 * 255 ) , color0 > > 24 ) ;
color1 = ColorRGBA ( uint32_t ( end [ 0 ] / 2 * 255 ) , uint32_t ( end [ 1 ] / 2 * 255 ) , uint32_t ( end [ 2 ] / 2 * 255 ) , color1 > > 24 ) ;
}
else if ( parms . colormapstyle ! = nullptr )
{ // Emulate the fading from an in-game colormap (colorized, faded, and desaturated)
if ( parms . colormapstyle - > Desaturate ! = 0 )
{
quad . Flags | = BQF_Desaturated ;
}
quad . ShaderNum = BQS_InGameColormap ;
quad . Desat = parms . colormapstyle - > Desaturate ;
color0 = ColorARGB ( color1 > > 24 ,
parms . colormapstyle - > Color . r ,
parms . colormapstyle - > Color . g ,
parms . colormapstyle - > Color . b ) ;
double fadelevel = parms . colormapstyle - > FadeLevel ;
color1 = ColorARGB ( uint32_t ( ( 1 - fadelevel ) * 255 ) ,
uint32_t ( parms . colormapstyle - > Fade . r * fadelevel ) ,
uint32_t ( parms . colormapstyle - > Fade . g * fadelevel ) ,
uint32_t ( parms . colormapstyle - > Fade . b * fadelevel ) ) ;
}
}
// For unmasked images, force the alpha from the image data to be ignored.
if ( ! parms . masked & & quad . ShaderNum ! = BQS_InGameColormap )
{
color0 = ( color0 & ColorRGBA ( 255 , 255 , 255 , 0 ) ) | ColorValue ( 0 , 0 , 0 , alpha ) ;
color1 & = ColorRGBA ( 255 , 255 , 255 , 0 ) ;
// If our alpha is one and we are doing normal adding, then we can turn the blend off completely.
if ( quad . BlendOp = = GL_FUNC_ADD & &
( ( alpha = = 1 & & quad . SrcBlend = = GL_SRC_ALPHA ) | | quad . SrcBlend = = GL_ONE ) & &
( ( alpha = = 1 & & quad . DestBlend = = GL_ONE_MINUS_SRC_ALPHA ) | | quad . DestBlend = = GL_ZERO ) )
{
quad . BlendOp = 0 ;
}
quad . Flags | = BQF_DisableAlphaTest ;
}
return true ;
}
int OpenGLSWFrameBuffer : : GetStyleAlpha ( int type )
{
switch ( type )
{
case STYLEALPHA_Zero : return GL_ZERO ;
case STYLEALPHA_One : return GL_ONE ;
case STYLEALPHA_Src : return GL_SRC_ALPHA ;
case STYLEALPHA_InvSrc : return GL_ONE_MINUS_SRC_ALPHA ;
default : return GL_ZERO ;
}
}
void OpenGLSWFrameBuffer : : SetColorOverlay ( uint32_t color , float alpha , uint32_t & color0 , uint32_t & color1 )
{
if ( APART ( color ) ! = 0 )
{
int a = APART ( color ) * 256 / 255 ;
color0 = ColorRGBA (
( RPART ( color ) * a ) > > 8 ,
( GPART ( color ) * a ) > > 8 ,
( BPART ( color ) * a ) > > 8 ,
0 ) ;
a = 256 - a ;
color1 = ColorRGBA ( a , a , a , int ( alpha * 255 ) ) ;
}
else
{
color0 = 0 ;
color1 = ColorValue ( 1 , 1 , 1 , alpha ) ;
}
}
2016-10-10 22:03:46 +00:00
void OpenGLSWFrameBuffer : : EnableAlphaTest ( bool enabled )
2016-10-09 10:50:57 +00:00
{
if ( enabled ! = AlphaTestEnabled )
{
AlphaTestEnabled = enabled ;
//glEnable(GL_ALPHA_TEST); // To do: move to shader as this is only in the compatibility profile
}
}
void OpenGLSWFrameBuffer : : SetAlphaBlend ( int op , int srcblend , int destblend )
{
if ( op = = 0 )
{ // Disable alpha blend
if ( AlphaBlendEnabled )
{
2016-10-11 08:27:18 +00:00
AlphaBlendEnabled = false ;
2016-10-09 10:50:57 +00:00
glDisable ( GL_BLEND ) ;
}
}
else
{ // Enable alpha blend
assert ( srcblend ! = 0 ) ;
assert ( destblend ! = 0 ) ;
if ( ! AlphaBlendEnabled )
{
2016-10-11 08:27:18 +00:00
AlphaBlendEnabled = true ;
2016-10-09 10:50:57 +00:00
glEnable ( GL_BLEND ) ;
}
if ( AlphaBlendOp ! = op )
{
AlphaBlendOp = op ;
glBlendEquation ( op ) ;
}
if ( AlphaSrcBlend ! = srcblend | | AlphaDestBlend ! = destblend )
{
AlphaSrcBlend = srcblend ;
AlphaDestBlend = destblend ;
glBlendFunc ( srcblend , destblend ) ;
}
}
}
void OpenGLSWFrameBuffer : : SetConstant ( int cnum , float r , float g , float b , float a )
{
if ( Constant [ cnum ] [ 0 ] ! = r | |
Constant [ cnum ] [ 1 ] ! = g | |
Constant [ cnum ] [ 2 ] ! = b | |
Constant [ cnum ] [ 3 ] ! = a )
{
Constant [ cnum ] [ 0 ] = r ;
Constant [ cnum ] [ 1 ] = g ;
Constant [ cnum ] [ 2 ] = b ;
Constant [ cnum ] [ 3 ] = a ;
SetPixelShaderConstantF ( cnum , Constant [ cnum ] , 1 ) ;
}
}
void OpenGLSWFrameBuffer : : SetPixelShader ( HWPixelShader * shader )
{
if ( CurPixelShader ! = shader )
{
CurPixelShader = shader ;
SetHWPixelShader ( shader ) ;
}
}
void OpenGLSWFrameBuffer : : SetTexture ( int tnum , HWTexture * texture )
{
assert ( unsigned ( tnum ) < countof ( Texture ) ) ;
if ( texture )
{
if ( Texture [ tnum ] ! = texture | | SamplerWrapS [ tnum ] ! = texture - > WrapS | | SamplerWrapT [ tnum ] ! = texture - > WrapT )
{
Texture [ tnum ] = texture ;
glActiveTexture ( GL_TEXTURE0 + tnum ) ;
2016-10-10 05:39:02 +00:00
glBindTexture ( GL_TEXTURE_2D , texture - > Texture ) ;
2016-10-09 10:50:57 +00:00
if ( Texture [ tnum ] - > WrapS ! = SamplerWrapS [ tnum ] )
{
Texture [ tnum ] - > WrapS = SamplerWrapS [ tnum ] ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , SamplerWrapS [ tnum ] ) ;
}
if ( Texture [ tnum ] - > WrapT ! = SamplerWrapT [ tnum ] )
{
Texture [ tnum ] - > WrapT = SamplerWrapT [ tnum ] ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , SamplerWrapT [ tnum ] ) ;
}
}
}
else if ( Texture [ tnum ] ! = texture )
{
Texture [ tnum ] = texture ;
glActiveTexture ( GL_TEXTURE0 + tnum ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
}
}
void OpenGLSWFrameBuffer : : SetSamplerWrapS ( int tnum , int mode )
{
assert ( unsigned ( tnum ) < countof ( Texture ) ) ;
if ( Texture [ tnum ] & & SamplerWrapS [ tnum ] ! = mode )
{
SamplerWrapS [ tnum ] = mode ;
Texture [ tnum ] - > WrapS = mode ;
glActiveTexture ( GL_TEXTURE0 + tnum ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , SamplerWrapS [ tnum ] ) ;
}
}
void OpenGLSWFrameBuffer : : SetSamplerWrapT ( int tnum , int mode )
{
assert ( unsigned ( tnum ) < countof ( Texture ) ) ;
if ( Texture [ tnum ] & & SamplerWrapT [ tnum ] ! = mode )
{
SamplerWrapT [ tnum ] = mode ;
Texture [ tnum ] - > WrapT = mode ;
glActiveTexture ( GL_TEXTURE0 + tnum ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , SamplerWrapT [ tnum ] ) ;
}
}
void OpenGLSWFrameBuffer : : SetPaletteTexture ( HWTexture * texture , int count , uint32_t border_color )
{
// The pixel shader receives color indexes in the range [0.0,1.0].
// The palette texture is also addressed in the range [0.0,1.0],
// HOWEVER the coordinate 1.0 is the right edge of the texture and
// not actually the texture itself. We need to scale and shift
// the palette indexes so they lie exactly in the center of each
// texel. For a normal palette with 256 entries, that means the
// range we use should be [0.5,255.5], adjusted so the coordinate
// is still within [0.0,1.0].
//
// The constant register c2 is used to hold the multiplier in the
// x part and the adder in the y part.
float fcount = 1 / float ( count ) ;
SetConstant ( PSCONST_PaletteMod , 255 * fcount , 0.5f * fcount , 0 , 0 ) ;
SetTexture ( 1 , texture ) ;
}