2013-06-23 07:49:34 +00:00
/*
* * gl_shader . cpp
* *
* * GLSL shader handling
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 2004 - 2009 Christoph Oelckers
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* * 4. When not used as part of GZDoom or a GZDoom derivative , this code will be
* * covered by the terms of the GNU Lesser General Public License as published
* * by the Free Software Foundation ; either version 2.1 of the License , or ( at
* * your option ) any later version .
* * 5. Full disclosure of the entire project ' s source code , except for third
* * party libraries is mandatory . ( NOTE : This clause is non - negotiable ! )
* *
* * 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 .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
# include "gl/system/gl_system.h"
# include "c_cvars.h"
# include "v_video.h"
# include "name.h"
# include "w_wad.h"
# include "i_system.h"
# include "doomerrors.h"
# include "v_palette.h"
# include "sc_man.h"
# include "cmdlib.h"
2013-09-03 16:29:39 +00:00
# include "gl/system/gl_interface.h"
2013-06-23 07:49:34 +00:00
# include "gl/data/gl_data.h"
# include "gl/renderer/gl_renderer.h"
# include "gl/renderer/gl_renderstate.h"
# include "gl/system/gl_cvars.h"
# include "gl/shaders/gl_shader.h"
# include "gl/textures/gl_material.h"
//==========================================================================
//
//
//
//==========================================================================
bool FShader : : Load ( const char * name , const char * vert_prog_lump , const char * frag_prog_lump , const char * proc_prog_lump , const char * defines )
{
static char buffer [ 10000 ] ;
FString error ;
2014-06-21 13:50:32 +00:00
int i_lump = Wads . CheckNumForFullName ( " shaders/glsl/shaderdefs.i " ) ;
if ( i_lump = = - 1 ) I_Error ( " Unable to load 'shaders/glsl/shaderdefs.i' " ) ;
FMemLump i_data = Wads . ReadLump ( i_lump ) ;
2014-05-12 12:45:41 +00:00
2014-06-21 13:50:32 +00:00
int vp_lump = Wads . CheckNumForFullName ( vert_prog_lump ) ;
if ( vp_lump = = - 1 ) I_Error ( " Unable to load '%s' " , vert_prog_lump ) ;
FMemLump vp_data = Wads . ReadLump ( vp_lump ) ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
int fp_lump = Wads . CheckNumForFullName ( frag_prog_lump ) ;
if ( fp_lump = = - 1 ) I_Error ( " Unable to load '%s' " , frag_prog_lump ) ;
FMemLump fp_data = Wads . ReadLump ( fp_lump ) ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
//
// The following code uses GetChars on the strings to get rid of terminating 0 characters. Do not remove or the code may break!
//
2014-05-12 12:45:41 +00:00
2014-06-21 13:50:32 +00:00
FString vp_comb = " #version 130 \n " ;
if ( gl . glslversion > = 3.3f ) vp_comb = " #version 330 compatibility \n " ; // I can't shut up the deprecation warnings in GLSL 1.3 so if available use a version with compatibility profile.
// todo when using shader storage buffers, add
// "#version 400 compatibility\n#extension GL_ARB_shader_storage_buffer_object : require\n" instead.
2014-06-30 16:10:55 +00:00
if ( ! ( gl . flags & RFL_BUFFER_STORAGE ) )
{
// we only want the uniform array hack in the shader if we actually need it.
vp_comb < < " #define UNIFORM_VB \n " ;
}
2014-06-21 13:50:32 +00:00
vp_comb < < defines < < i_data . GetString ( ) . GetChars ( ) ;
FString fp_comb = vp_comb ;
2014-05-12 12:45:41 +00:00
2014-06-21 13:50:32 +00:00
vp_comb < < vp_data . GetString ( ) . GetChars ( ) < < " \n " ;
fp_comb < < fp_data . GetString ( ) . GetChars ( ) < < " \n " ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
if ( proc_prog_lump ! = NULL )
{
if ( * proc_prog_lump ! = ' # ' )
2013-06-23 07:49:34 +00:00
{
2014-06-21 13:50:32 +00:00
int pp_lump = Wads . CheckNumForFullName ( proc_prog_lump ) ;
if ( pp_lump = = - 1 ) I_Error ( " Unable to load '%s' " , proc_prog_lump ) ;
FMemLump pp_data = Wads . ReadLump ( pp_lump ) ;
if ( pp_data . GetString ( ) . IndexOf ( " ProcessTexel " ) < 0 )
2013-06-23 07:49:34 +00:00
{
2014-06-21 13:50:32 +00:00
// this looks like an old custom hardware shader.
// We need to replace the ProcessTexel call to make it work.
fp_comb . Substitute ( " vec4 frag = ProcessTexel(); " , " vec4 frag = Process(vec4(1.0)); " ) ;
2013-06-23 07:49:34 +00:00
}
2014-06-21 13:50:32 +00:00
fp_comb < < pp_data . GetString ( ) . GetChars ( ) ;
if ( pp_data . GetString ( ) . IndexOf ( " ProcessLight " ) < 0 )
2013-06-23 07:49:34 +00:00
{
2014-06-21 13:50:32 +00:00
int pl_lump = Wads . CheckNumForFullName ( " shaders/glsl/func_defaultlight.fp " ) ;
if ( pl_lump = = - 1 ) I_Error ( " Unable to load '%s' " , " shaders/glsl/func_defaultlight.fp " ) ;
FMemLump pl_data = Wads . ReadLump ( pl_lump ) ;
fp_comb < < " \n " < < pl_data . GetString ( ) . GetChars ( ) ;
2013-06-23 07:49:34 +00:00
}
}
2014-06-21 13:50:32 +00:00
else
{
// Proc_prog_lump is not a lump name but the source itself (from generated shaders)
fp_comb < < proc_prog_lump + 1 ;
}
}
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
hVertProg = glCreateShader ( GL_VERTEX_SHADER ) ;
hFragProg = glCreateShader ( GL_FRAGMENT_SHADER ) ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
int vp_size = ( int ) vp_comb . Len ( ) ;
int fp_size = ( int ) fp_comb . Len ( ) ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
const char * vp_ptr = vp_comb . GetChars ( ) ;
const char * fp_ptr = fp_comb . GetChars ( ) ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
glShaderSource ( hVertProg , 1 , & vp_ptr , & vp_size ) ;
glShaderSource ( hFragProg , 1 , & fp_ptr , & fp_size ) ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
glCompileShader ( hVertProg ) ;
glCompileShader ( hFragProg ) ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
hShader = glCreateProgram ( ) ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
glAttachShader ( hShader , hVertProg ) ;
glAttachShader ( hShader , hFragProg ) ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
glLinkProgram ( hShader ) ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
glGetShaderInfoLog ( hVertProg , 10000 , NULL , buffer ) ;
if ( * buffer )
{
error < < " Vertex shader: \n " < < buffer < < " \n " ;
}
glGetShaderInfoLog ( hFragProg , 10000 , NULL , buffer ) ;
if ( * buffer )
{
error < < " Fragment shader: \n " < < buffer < < " \n " ;
}
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
glGetProgramInfoLog ( hShader , 10000 , NULL , buffer ) ;
if ( * buffer )
{
error < < " Linking: \n " < < buffer < < " \n " ;
}
int linked ;
glGetProgramiv ( hShader , GL_LINK_STATUS , & linked ) ;
if ( linked = = 0 )
{
// only print message if there's an error.
I_Error ( " Init Shader '%s': \n %s \n " , name , error . GetChars ( ) ) ;
}
2014-05-12 12:45:41 +00:00
2014-06-21 13:50:32 +00:00
muDesaturation . Init ( hShader , " uDesaturationFactor " ) ;
muFogEnabled . Init ( hShader , " uFogEnabled " ) ;
muTextureMode . Init ( hShader , " uTextureMode " ) ;
muCameraPos . Init ( hShader , " uCameraPos " ) ;
muLightParms . Init ( hShader , " uLightAttr " ) ;
muColormapStart . Init ( hShader , " uFixedColormapStart " ) ;
muColormapRange . Init ( hShader , " uFixedColormapRange " ) ;
muLightRange . Init ( hShader , " uLightRange " ) ;
muFogColor . Init ( hShader , " uFogColor " ) ;
muDynLightColor . Init ( hShader , " uDynLightColor " ) ;
muObjectColor . Init ( hShader , " uObjectColor " ) ;
muGlowBottomColor . Init ( hShader , " uGlowBottomColor " ) ;
muGlowTopColor . Init ( hShader , " uGlowTopColor " ) ;
muGlowBottomPlane . Init ( hShader , " uGlowBottomPlane " ) ;
muGlowTopPlane . Init ( hShader , " uGlowTopPlane " ) ;
muFixedColormap . Init ( hShader , " uFixedColormap " ) ;
2014-06-29 09:00:21 +00:00
muInterpolationFactor . Init ( hShader , " uInterpolationFactor " ) ;
2014-05-12 12:45:41 +00:00
2014-06-21 13:50:32 +00:00
timer_index = glGetUniformLocation ( hShader , " timer " ) ;
lights_index = glGetUniformLocation ( hShader , " lights " ) ;
2014-06-30 16:10:55 +00:00
fakevb_index = glGetUniformLocation ( hShader , " fakeVB " ) ;
2013-09-03 16:29:39 +00:00
2014-06-29 09:00:21 +00:00
glBindAttribLocation ( hShader , VATTR_VERTEX2 , " aVertex2 " ) ;
2014-05-10 15:09:43 +00:00
2014-06-21 13:50:32 +00:00
glUseProgram ( hShader ) ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
int texture_index = glGetUniformLocation ( hShader , " texture2 " ) ;
if ( texture_index > 0 ) glUniform1i ( texture_index , 1 ) ;
2013-06-23 07:49:34 +00:00
2014-06-21 13:50:32 +00:00
glUseProgram ( 0 ) ;
return ! ! linked ;
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
FShader : : ~ FShader ( )
{
2014-06-21 13:50:32 +00:00
glDeleteProgram ( hShader ) ;
glDeleteShader ( hVertProg ) ;
glDeleteShader ( hFragProg ) ;
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
2014-05-12 12:45:41 +00:00
bool FShader : : Bind ( )
2013-06-23 07:49:34 +00:00
{
GLRenderer - > mShaderManager - > SetActiveShader ( this ) ;
return true ;
}
//==========================================================================
//
2014-05-12 12:45:41 +00:00
// Since all shaders are REQUIRED, any error here needs to be fatal
2013-06-23 07:49:34 +00:00
//
//==========================================================================
2014-05-12 12:45:41 +00:00
FShader * FShaderManager : : Compile ( const char * ShaderName , const char * ShaderPath )
2013-06-23 07:49:34 +00:00
{
2014-05-12 12:45:41 +00:00
// this can't be in the shader code due to ATI strangeness.
const char * str = ( gl . MaxLights ( ) = = 128 ) ? " #define MAXLIGHTS128 \n " : " " ;
2013-06-23 07:49:34 +00:00
2014-05-12 12:45:41 +00:00
FShader * shader = NULL ;
2013-06-23 07:49:34 +00:00
try
{
2014-05-12 12:45:41 +00:00
shader = new FShader ( ShaderName ) ;
if ( ! shader - > Load ( ShaderName , " shaders/glsl/main.vp " , " shaders/glsl/main.fp " , ShaderPath , str ) )
2013-06-23 07:49:34 +00:00
{
2014-06-23 07:26:29 +00:00
I_FatalError ( " Unable to load shader %s \n " , ShaderName ) ;
2013-06-23 07:49:34 +00:00
}
}
catch ( CRecoverableError & err )
{
2014-05-12 12:45:41 +00:00
if ( shader ! = NULL ) delete shader ;
shader = NULL ;
2014-06-23 07:26:29 +00:00
I_FatalError ( " Unable to load shader %s: \n %s \n " , ShaderName , err . GetMessage ( ) ) ;
2013-12-05 14:06:10 +00:00
}
2014-05-12 12:45:41 +00:00
return shader ;
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
struct FDefaultShader
{
const char * ShaderName ;
const char * gettexelfunc ;
} ;
// Note: the FIRST_USER_SHADER constant in gl_shader.h needs
// to be updated whenever the size of this array is modified.
static const FDefaultShader defaultshaders [ ] =
{
{ " Default " , " shaders/glsl/func_normal.fp " } ,
{ " Warp 1 " , " shaders/glsl/func_warp1.fp " } ,
{ " Warp 2 " , " shaders/glsl/func_warp2.fp " } ,
{ " Brightmap " , " shaders/glsl/func_brightmap.fp " } ,
{ " No Texture " , " shaders/glsl/func_notexture.fp " } ,
{ " Basic Fuzz " , " shaders/glsl/fuzz_standard.fp " } ,
{ " Smooth Fuzz " , " shaders/glsl/fuzz_smooth.fp " } ,
{ " Swirly Fuzz " , " shaders/glsl/fuzz_swirly.fp " } ,
{ " Translucent Fuzz " , " shaders/glsl/fuzz_smoothtranslucent.fp " } ,
{ " Jagged Fuzz " , " shaders/glsl/fuzz_jagged.fp " } ,
{ " Noise Fuzz " , " shaders/glsl/fuzz_noise.fp " } ,
{ " Smooth Noise Fuzz " , " shaders/glsl/fuzz_smoothnoise.fp " } ,
{ NULL , NULL }
} ;
static TArray < FString > usershaders ;
struct FEffectShader
{
const char * ShaderName ;
const char * vp ;
const char * fp1 ;
const char * fp2 ;
const char * defines ;
} ;
static const FEffectShader effectshaders [ ] =
{
2014-05-12 12:45:41 +00:00
{ " fogboundary " , " shaders/glsl/main.vp " , " shaders/glsl/fogboundary.fp " , NULL , " " } ,
{ " spheremap " , " shaders/glsl/main.vp " , " shaders/glsl/main.fp " , " shaders/glsl/func_normal.fp " , " #define SPHEREMAP \n " } ,
{ " burn " , " shaders/glsl/burn.vp " , " shaders/glsl/burn.fp " , NULL , " " } ,
{ " stencil " , " shaders/glsl/stencil.vp " , " shaders/glsl/stencil.fp " , NULL , " " } ,
2013-06-23 07:49:34 +00:00
} ;
//==========================================================================
//
//
//
//==========================================================================
FShaderManager : : FShaderManager ( )
{
CompileShaders ( ) ;
}
//==========================================================================
//
//
//
//==========================================================================
FShaderManager : : ~ FShaderManager ( )
{
Clean ( ) ;
}
//==========================================================================
//
//
//
//==========================================================================
void FShaderManager : : CompileShaders ( )
{
2014-06-23 07:26:29 +00:00
mActiveShader = mEffectShaders [ 0 ] = mEffectShaders [ 1 ] = NULL ;
for ( int i = 0 ; defaultshaders [ i ] . ShaderName ! = NULL ; i + + )
2013-06-23 07:49:34 +00:00
{
2014-06-23 07:26:29 +00:00
FShader * shc = Compile ( defaultshaders [ i ] . ShaderName , defaultshaders [ i ] . gettexelfunc ) ;
mTextureEffects . Push ( shc ) ;
}
2013-06-23 07:49:34 +00:00
2014-06-23 07:26:29 +00:00
for ( unsigned i = 0 ; i < usershaders . Size ( ) ; i + + )
{
FString name = ExtractFileBase ( usershaders [ i ] ) ;
FName sfn = name ;
2013-06-23 07:49:34 +00:00
2014-06-23 07:26:29 +00:00
FShader * shc = Compile ( sfn , usershaders [ i ] ) ;
mTextureEffects . Push ( shc ) ;
}
2013-06-23 07:49:34 +00:00
2014-06-23 07:26:29 +00:00
for ( int i = 0 ; i < MAX_EFFECTS ; i + + )
{
FShader * eff = new FShader ( effectshaders [ i ] . ShaderName ) ;
if ( ! eff - > Load ( effectshaders [ i ] . ShaderName , effectshaders [ i ] . vp , effectshaders [ i ] . fp1 ,
effectshaders [ i ] . fp2 , effectshaders [ i ] . defines ) )
2014-06-21 13:50:32 +00:00
{
2014-06-23 07:26:29 +00:00
delete eff ;
2013-06-23 07:49:34 +00:00
}
2014-06-23 07:26:29 +00:00
else mEffectShaders [ i ] = eff ;
2014-05-12 12:45:41 +00:00
}
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
void FShaderManager : : Clean ( )
{
2014-06-21 13:50:32 +00:00
glUseProgram ( 0 ) ;
mActiveShader = NULL ;
2014-05-12 12:45:41 +00:00
2014-06-21 13:50:32 +00:00
for ( unsigned int i = 0 ; i < mTextureEffects . Size ( ) ; i + + )
{
if ( mTextureEffects [ i ] ! = NULL ) delete mTextureEffects [ i ] ;
}
for ( int i = 0 ; i < MAX_EFFECTS ; i + + )
{
if ( mEffectShaders [ i ] ! = NULL ) delete mEffectShaders [ i ] ;
mEffectShaders [ i ] = NULL ;
2013-06-23 07:49:34 +00:00
}
2014-06-21 13:50:32 +00:00
mTextureEffects . Clear ( ) ;
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
int FShaderManager : : Find ( const char * shn )
{
FName sfn = shn ;
for ( unsigned int i = 0 ; i < mTextureEffects . Size ( ) ; i + + )
{
2014-05-12 12:45:41 +00:00
if ( mTextureEffects [ i ] - > mName = = sfn )
2013-06-23 07:49:34 +00:00
{
return i ;
}
}
return - 1 ;
}
//==========================================================================
//
//
//
//==========================================================================
void FShaderManager : : SetActiveShader ( FShader * sh )
{
2014-06-21 13:50:32 +00:00
if ( mActiveShader ! = sh )
2013-06-23 07:49:34 +00:00
{
2014-05-24 14:53:57 +00:00
glUseProgram ( sh ! = NULL ? sh - > GetHandle ( ) : 0 ) ;
2013-06-23 07:49:34 +00:00
mActiveShader = sh ;
}
}
2014-05-12 12:45:41 +00:00
2013-06-23 07:49:34 +00:00
//==========================================================================
//
2014-05-12 12:45:41 +00:00
// To avoid maintenance this will be set when a warped texture is bound
// because at that point the draw buffer needs to be flushed anyway.
2013-06-23 07:49:34 +00:00
//
2014-05-12 12:45:41 +00:00
//==========================================================================
void FShaderManager : : SetWarpSpeed ( unsigned int eff , float speed )
{
// indices 0-2 match the warping modes, 3 is brightmap, 4 no texture, the following are custom
if ( eff < mTextureEffects . Size ( ) )
{
FShader * sh = mTextureEffects [ eff ] ;
float warpphase = gl_frameMS * speed / 1000.f ;
glProgramUniform1f ( sh - > GetHandle ( ) , sh - > timer_index , warpphase ) ;
}
}
2014-06-23 07:26:29 +00:00
//==========================================================================
//
//
2013-06-23 07:49:34 +00:00
//
//==========================================================================
FShader * FShaderManager : : BindEffect ( int effect )
{
2014-05-12 12:45:41 +00:00
if ( effect > = 0 & & effect < MAX_EFFECTS & & mEffectShaders [ effect ] ! = NULL )
2013-06-23 07:49:34 +00:00
{
2014-05-12 12:45:41 +00:00
mEffectShaders [ effect ] - > Bind ( ) ;
return mEffectShaders [ effect ] ;
2013-06-23 07:49:34 +00:00
}
return NULL ;
}
//==========================================================================
//
//
//
//==========================================================================
void gl_DestroyUserShaders ( )
{
// todo
}
//==========================================================================
//
// Parses a shader definition
//
//==========================================================================
void gl_ParseHardwareShader ( FScanner & sc , int deflump )
{
int type = FTexture : : TEX_Any ;
bool disable_fullbright = false ;
bool thiswad = false ;
bool iwad = false ;
int maplump = - 1 ;
FString maplumpname ;
float speed = 1.f ;
sc . MustGetString ( ) ;
if ( sc . Compare ( " texture " ) ) type = FTexture : : TEX_Wall ;
else if ( sc . Compare ( " flat " ) ) type = FTexture : : TEX_Flat ;
else if ( sc . Compare ( " sprite " ) ) type = FTexture : : TEX_Sprite ;
else sc . UnGet ( ) ;
sc . MustGetString ( ) ;
FTextureID no = TexMan . CheckForTexture ( sc . String , type ) ;
FTexture * tex = TexMan [ no ] ;
sc . MustGetToken ( ' { ' ) ;
while ( ! sc . CheckToken ( ' } ' ) )
{
sc . MustGetString ( ) ;
if ( sc . Compare ( " shader " ) )
{
sc . MustGetString ( ) ;
maplumpname = sc . String ;
}
else if ( sc . Compare ( " speed " ) )
{
sc . MustGetFloat ( ) ;
speed = float ( sc . Float ) ;
}
}
if ( ! tex )
{
return ;
}
if ( maplumpname . IsNotEmpty ( ) )
{
if ( tex - > bWarped ! = 0 )
{
2014-06-01 07:27:16 +00:00
Printf ( " Cannot combine warping with hardware shader on texture '%s' \n " , tex - > Name . GetChars ( ) ) ;
2013-06-23 07:49:34 +00:00
return ;
}
tex - > gl_info . shaderspeed = speed ;
for ( unsigned i = 0 ; i < usershaders . Size ( ) ; i + + )
{
if ( ! usershaders [ i ] . CompareNoCase ( maplumpname ) )
{
tex - > gl_info . shaderindex = i + FIRST_USER_SHADER ;
return ;
}
}
tex - > gl_info . shaderindex = usershaders . Push ( maplumpname ) + FIRST_USER_SHADER ;
}
}