GL3_Draw_* is done, menu, console, videos work

This commit is contained in:
Daniel Gibson 2017-01-23 00:28:38 +01:00
parent 678410bfb4
commit 43e108fb9b
4 changed files with 193 additions and 245 deletions

View File

@ -56,6 +56,43 @@ GL3_Draw_ShutdownLocal(void)
gl3state.vao2D = 0; gl3state.vao2D = 0;
} }
// bind the texture before calling this
static void
drawTexturedRectangle(float x, float y, float w, float h,
float sl, float tl, float sh, float th)
{
/*
* x,y+h x+w,y+h
* sl,th--------sh,th
* | |
* | |
* | |
* sl,tl--------sh,tl
* x,y x+w,y
*/
GLfloat vBuf[16] = {
// X, Y, S, T
x, y+h, sl, th,
x, y, sl, tl,
x+w, y+h, sh, th,
x+w, y, sh, tl
};
glBindVertexArray(gl3state.vao2D);
glBindBuffer(GL_ARRAY_BUFFER, gl3state.vbo2D);
glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW);
glEnableVertexAttribArray(gl3state.si2D.attribPosition);
qglVertexAttribPointer(gl3state.si2D.attribPosition, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 0);
glEnableVertexAttribArray(gl3state.si2D.attribTexCoord);
qglVertexAttribPointer(gl3state.si2D.attribTexCoord, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 2*sizeof(float));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
/* /*
* Draws one 8*8 graphics character with 0 being transparent. * Draws one 8*8 graphics character with 0 being transparent.
* It can be clipped to the top of the screen to allow the console to be * It can be clipped to the top of the screen to allow the console to be
@ -64,11 +101,8 @@ GL3_Draw_ShutdownLocal(void)
void void
GL3_Draw_CharScaled(int x, int y, int num, float scale) GL3_Draw_CharScaled(int x, int y, int num, float scale)
{ {
STUB_ONCE("TODO: Implement!");
#if 0
int row, col; int row, col;
float frow, fcol, size, scaledSize; float frow, fcol, size, scaledSize;
num &= 255; num &= 255;
if ((num & 127) == 32) if ((num & 127) == 32)
@ -90,32 +124,8 @@ GL3_Draw_CharScaled(int x, int y, int num, float scale)
scaledSize = 8*scale; scaledSize = 8*scale;
R_Bind(draw_chars->texnum); GL3_Bind(draw_chars->texnum);
drawTexturedRectangle(x, y, scaledSize, scaledSize, fcol, frow, fcol+size, frow+size);
GLfloat vtx[] = {
x, y,
x + scaledSize, y,
x + scaledSize, y + scaledSize,
x, y + scaledSize
};
GLfloat tex[] = {
fcol, frow,
fcol + size, frow,
fcol + size, frow + size,
fcol, frow + size
};
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glVertexPointer( 2, GL_FLOAT, 0, vtx );
glTexCoordPointer( 2, GL_FLOAT, 0, tex );
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
#endif // 0
} }
gl3image_t * gl3image_t *
@ -157,11 +167,7 @@ GL3_Draw_GetPicSize(int *w, int *h, char *pic)
void void
GL3_Draw_StretchPic(int x, int y, int w, int h, char *pic) GL3_Draw_StretchPic(int x, int y, int w, int h, char *pic)
{ {
STUB_ONCE("TODO: Implement!"); gl3image_t *gl = GL3_Draw_FindPic(pic);
#if 0
image_t *gl;
gl = RDraw_FindPic(pic);
if (!gl) if (!gl)
{ {
@ -169,85 +175,24 @@ GL3_Draw_StretchPic(int x, int y, int w, int h, char *pic)
return; return;
} }
if (scrap_dirty) GL3_Bind(gl->texnum);
{
Scrap_Upload();
}
R_Bind(gl->texnum); drawTexturedRectangle(x, y, w, h, gl->sl, gl->tl, gl->sh, gl->th);
GLfloat vtx[] = {
x, y,
x + w, y,
x + w, y + h,
x, y + h
};
GLfloat tex[] = {
gl->sl, gl->tl,
gl->sh, gl->tl,
gl->sh, gl->th,
gl->sl, gl->th
};
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glVertexPointer( 2, GL_FLOAT, 0, vtx );
glTexCoordPointer( 2, GL_FLOAT, 0, tex );
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
#endif // 0
} }
void void
GL3_Draw_PicScaled(int x, int y, char *pic, float factor) GL3_Draw_PicScaled(int x, int y, char *pic, float factor)
{ {
STUB_ONCE("TODO: Implement!"); gl3image_t *gl = GL3_Draw_FindPic(pic);
#if 0
image_t *gl;
gl = RDraw_FindPic(pic);
if (!gl) if (!gl)
{ {
R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic);
return; return;
} }
if (scrap_dirty) GL3_Bind(gl->texnum);
{
Scrap_Upload();
}
R_Bind(gl->texnum); drawTexturedRectangle(x, y, gl->width*factor, gl->height*factor, gl->sl, gl->tl, gl->sh, gl->th);
GLfloat vtx[] = {
x, y,
x + gl->width * factor, y,
x + gl->width * factor, y + gl->height * factor,
x, y + gl->height * factor
};
GLfloat tex[] = {
gl->sl, gl->tl,
gl->sh, gl->tl,
gl->sh, gl->th,
gl->sl, gl->th
};
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glVertexPointer( 2, GL_FLOAT, 0, vtx );
glTexCoordPointer( 2, GL_FLOAT, 0, tex );
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
#endif // 0
} }
/* /*
@ -258,44 +203,16 @@ GL3_Draw_PicScaled(int x, int y, char *pic, float factor)
void void
GL3_Draw_TileClear(int x, int y, int w, int h, char *pic) GL3_Draw_TileClear(int x, int y, int w, int h, char *pic)
{ {
STUB_ONCE("TODO: Implement!"); gl3image_t *image = GL3_Draw_FindPic(pic);
#if 0
image_t *image;
image = RDraw_FindPic(pic);
if (!image) if (!image)
{ {
R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic);
return; return;
} }
R_Bind(image->texnum); GL3_Bind(image->texnum);
GLfloat vtx[] = { drawTexturedRectangle(x, y, w, h, x/64.0f, y/64.0f, (x+w)/64.0f, (y+h)/64.0f);
x, y,
x + w, y,
x + w, y + h,
x, y + h
};
GLfloat tex[] = {
x / 64.0, y / 64.0,
( x + w ) / 64.0, y / 64.0,
( x + w ) / 64.0, ( y + h ) / 64.0,
x / 64.0, ( y + h ) / 64.0
};
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glVertexPointer( 2, GL_FLOAT, 0, vtx );
glTexCoordPointer( 2, GL_FLOAT, 0, tex );
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
#endif // 0
} }
/* /*
@ -304,71 +221,80 @@ GL3_Draw_TileClear(int x, int y, int w, int h, char *pic)
void void
GL3_Draw_Fill(int x, int y, int w, int h, int c) GL3_Draw_Fill(int x, int y, int w, int h, int c)
{ {
STUB_ONCE("TODO: Implement!");
#if 0
union union
{ {
unsigned c; unsigned c;
byte v[4]; byte v[4];
} color; } color;
int i;
float cf[3];
if ((unsigned)c > 255) if ((unsigned)c > 255)
{ {
ri.Sys_Error(ERR_FATAL, "Draw_Fill: bad color"); ri.Sys_Error(ERR_FATAL, "Draw_Fill: bad color");
} }
glDisable(GL_TEXTURE_2D);
color.c = d_8to24table[c]; color.c = d_8to24table[c];
glColor4f(color.v [ 0 ] / 255.0, color.v [ 1 ] / 255.0,
color.v [ 2 ] / 255.0, 1);
GLfloat vtx[] = { for(i=0; i<3; ++i)
x, y, {
x + w, y, cf[i] = color.v[i] * (1.0f/255.0f);
x + w, y + h, }
x, y + h
GLfloat vBuf[24] = {
// X, Y, R, G, B, A
x, y+h, cf[0], cf[1], cf[2], 1.0f,
x, y, cf[0], cf[1], cf[2], 1.0f,
x+w, y+h, cf[0], cf[1], cf[2], 1.0f,
x+w, y, cf[0], cf[1], cf[2], 1.0f
}; };
glEnableClientState( GL_VERTEX_ARRAY ); glUseProgram(gl3state.si2Dcolor.shaderProgram);
glVertexPointer( 2, GL_FLOAT, 0, vtx ); glBindVertexArray(gl3state.vao2D);
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
glDisableClientState( GL_VERTEX_ARRAY ); glBindBuffer(GL_ARRAY_BUFFER, gl3state.vbo2D);
glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW);
glColor4f( 1, 1, 1, 1 ); glEnableVertexAttribArray(gl3state.si2Dcolor.attribPosition);
glEnable(GL_TEXTURE_2D); qglVertexAttribPointer(gl3state.si2Dcolor.attribPosition, 2, GL_FLOAT, GL_FALSE, 6*sizeof(float), 0);
#endif // 0
glEnableVertexAttribArray(gl3state.si2Dcolor.attribColor);
qglVertexAttribPointer(gl3state.si2Dcolor.attribColor, 2, GL_FLOAT, GL_FALSE, 6*sizeof(float), 2*sizeof(float));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glUseProgram(gl3state.si2D.shaderProgram);
} }
void void
GL3_Draw_FadeScreen(void) GL3_Draw_FadeScreen(void)
{ {
STUB_ONCE("TODO: Implement!"); float w = vid.width;
#if 0 float h = vid.height;
glEnable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glColor4f(0, 0, 0, 0.8);
GLfloat vtx[] = { GLfloat vBuf[24] = {
0, 0, // X, Y, R, G, B, A
vid.width, 0, 0, h, 0.0f, 0.0f, 0.0f, 0.8f,
vid.width, vid.height, 0, 0, 0.0f, 0.0f, 0.0f, 0.8f,
0, vid.height w, h, 0.0f, 0.0f, 0.0f, 0.8f,
w, 0, 0.0f, 0.0f, 0.0f, 0.8f
}; };
glEnableClientState( GL_VERTEX_ARRAY ); glUseProgram(gl3state.si2Dcolor.shaderProgram);
glVertexPointer( 2, GL_FLOAT, 0, vtx ); glBindVertexArray(gl3state.vao2D);
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
glDisableClientState( GL_VERTEX_ARRAY ); glBindBuffer(GL_ARRAY_BUFFER, gl3state.vbo2D);
glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW);
glColor4f(1, 1, 1, 1); glEnableVertexAttribArray(gl3state.si2Dcolor.attribPosition);
glEnable(GL_TEXTURE_2D); qglVertexAttribPointer(gl3state.si2Dcolor.attribPosition, 2, GL_FLOAT, GL_FALSE, 6*sizeof(float), 0);
glDisable(GL_BLEND);
#endif // 0 glEnableVertexAttribArray(gl3state.si2Dcolor.attribColor);
qglVertexAttribPointer(gl3state.si2Dcolor.attribColor, 2, GL_FLOAT, GL_FALSE, 6*sizeof(float), 2*sizeof(float));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glUseProgram(gl3state.si2D.shaderProgram);
} }
void void
@ -380,14 +306,6 @@ GL3_Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte *data)
int i, j, trows; int i, j, trows;
int row; int row;
GLfloat vBuf[] = {
// posX, posY, texS, texT
x, y + h, 0.0f, 1.0f,
x, y, 0.0f, 0.0f,
x + w, y + h, 1.0f, 1.0f,
x + w, y, 1.0f, 0.0f,
};
GL3_Bind(0); GL3_Bind(0);
unsigned image32[320*240]; /* was 256 * 256, but we want a bit more space */ unsigned image32[320*240]; /* was 256 * 256, but we want a bit more space */
@ -426,19 +344,7 @@ GL3_Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte *data)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
drawTexturedRectangle(x, y, w, h, 0.0f, 0.0f, 1.0f, 1.0f);
glBindVertexArray(gl3state.vao2D);
glBindBuffer(GL_ARRAY_BUFFER, gl3state.vbo2D);
glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW);
glEnableVertexAttribArray(gl3state.si2D.attribPosition);
qglVertexAttribPointer(gl3state.si2D.attribPosition, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 0);
glEnableVertexAttribArray(gl3state.si2D.attribTexCoord);
qglVertexAttribPointer(gl3state.si2D.attribTexCoord, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), sizeof(float)*2);
glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
glDeleteTextures(1, &glTex); glDeleteTextures(1, &glTex);
GL3_Bind(0); GL3_Bind(0);

View File

@ -422,6 +422,8 @@ GL3_Shutdown(void)
GL3_Mod_FreeAll(); GL3_Mod_FreeAll();
GL3_ShutdownImages(); GL3_ShutdownImages();
GL3_Draw_ShutdownLocal();
GL3_ShutdownShaders();
/* shutdown OS specific OpenGL stuff like contexts, etc. */ /* shutdown OS specific OpenGL stuff like contexts, etc. */
GL3_ShutdownWindow(false); GL3_ShutdownWindow(false);
@ -476,6 +478,7 @@ GL3_SetGL2D(void)
glDisable(GL_BLEND); glDisable(GL_BLEND);
// glEnable(GL_ALPHA_TEST); TODO: do in shader https://www.khronos.org/opengl/wiki/Transparency_Sorting#Alpha_test // glEnable(GL_ALPHA_TEST); TODO: do in shader https://www.khronos.org/opengl/wiki/Transparency_Sorting#Alpha_test
// glColor4f(1, 1, 1, 1); // glColor4f(1, 1, 1, 1);
} }
/* /*
@ -744,6 +747,11 @@ void
GL3_BeginFrame(float camera_separation) GL3_BeginFrame(float camera_separation)
{ {
STUB_ONCE("TODO: Implement!"); STUB_ONCE("TODO: Implement!");
glClearColor(0, 0, 0, 0); // FIXME: not sure this should stay
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(1, 0, 0.5, 0.5);
GL3_SetGL2D(); GL3_SetGL2D();
#if 0 #if 0
gl_state.camera_separation = camera_separation; gl_state.camera_separation = camera_separation;

View File

@ -200,17 +200,21 @@ static const char* vertexSrc2D = MULTILINE_STRING(#version 150\n
static const char* fragmentSrc2D = MULTILINE_STRING(#version 150\n static const char* fragmentSrc2D = MULTILINE_STRING(#version 150\n
in vec2 passTexCoord; in vec2 passTexCoord;
out vec4 outColor;
uniform sampler2D tex; uniform sampler2D tex;
out vec4 outColor;
void main() void main()
{ {
// TODO: gamma, intensity // TODO: gamma, intensity
outColor = texture(tex, passTexCoord); vec4 texel = texture(tex, passTexCoord);
if(texel.a < 0.666)
discard;
outColor = texel;
} }
); );
// 2D color only rendering, GL3_Draw_Fill(), GL3_Draw_FadeScreen()
static const char* vertexSrc2Dcolor = MULTILINE_STRING(#version 150\n static const char* vertexSrc2Dcolor = MULTILINE_STRING(#version 150\n
in vec4 color; in vec4 color;
in vec2 position; in vec2 position;
@ -229,79 +233,107 @@ static const char* vertexSrc2Dcolor = MULTILINE_STRING(#version 150\n
static const char* fragmentSrc2Dcolor = MULTILINE_STRING(#version 150\n static const char* fragmentSrc2Dcolor = MULTILINE_STRING(#version 150\n
in vec4 passColor; in vec4 passColor;
out vec4 outColor;
uniform sampler2D tex; uniform sampler2D tex;
out vec4 outColor;
void main() void main()
{ {
// TODO: gamma, intensity // TODO: gamma, intensity? (not sure we need that here)
outColor = passColor; outColor = passColor;
} }
); );
#undef MULTILINE_STRING #undef MULTILINE_STRING
qboolean GL3_InitShaders(void) static qboolean
initShader2D(gl3ShaderInfo_t* shaderInfo, const char* vertSrc, const char* fragSrc)
{ {
{
GLuint shaders2D[2] = {0}; GLuint shaders2D[2] = {0};
shaders2D[0] = CompileShader(GL_VERTEX_SHADER, vertexSrc2D); GLint i = -1;
GLuint prog = 0;
if(shaderInfo->shaderProgram != 0)
{
R_Printf(PRINT_ALL, "WARNING: calling initShader2D for gl3ShaderInfo_t that already has a shaderProgram!\n");
glDeleteProgram(shaderInfo->shaderProgram);
}
shaderInfo->attribColor = shaderInfo->attribPosition = shaderInfo->attribTexCoord = -1;
shaderInfo->uniTransMatrix = -1;
shaderInfo->shaderProgram = 0;
shaders2D[0] = CompileShader(GL_VERTEX_SHADER, vertSrc);
if(shaders2D[0] == 0) return false; if(shaders2D[0] == 0) return false;
shaders2D[1] = CompileShader(GL_FRAGMENT_SHADER, fragmentSrc2D);
shaders2D[1] = CompileShader(GL_FRAGMENT_SHADER, fragSrc);
if(shaders2D[1] == 0) if(shaders2D[1] == 0)
{ {
glDeleteShader(shaders2D[0]); glDeleteShader(shaders2D[0]);
return false; return false;
} }
gl3state.si2D.shaderProgram = CreateShaderProgram(2, shaders2D); prog = CreateShaderProgram(2, shaders2D);
// I think the shaders aren't needed anymore once they're linked into the program // I think the shaders aren't needed anymore once they're linked into the program
glDeleteShader(shaders2D[0]); glDeleteShader(shaders2D[0]);
glDeleteShader(shaders2D[1]); glDeleteShader(shaders2D[1]);
if(gl3state.si2D.shaderProgram != 0) if(prog == 0)
{
glUseProgram(gl3state.si2D.shaderProgram);
gl3state.si2D.attribTexCoord = glGetAttribLocation(gl3state.si2D.shaderProgram, "texCoord");
gl3state.si2D.attribPosition = glGetAttribLocation(gl3state.si2D.shaderProgram, "position");
gl3state.si2D.attribColor = -1;
gl3state.si2D.uniTransMatrix = glGetUniformLocation(gl3state.si2D.shaderProgram, "trans");
}
else
{ {
return false; return false;
} }
}
shaderInfo->shaderProgram = prog;
glUseProgram(prog);
i = glGetAttribLocation(prog, "position");
if( i == -1)
{ {
GLuint shaders2Dcol[2] = {0}; R_Printf(PRINT_ALL, "WARNING: Couldn't get 'position' attribute in shader\n");
return false;
shaders2Dcol[0] = CompileShader(GL_VERTEX_SHADER, vertexSrc2Dcolor);
shaders2Dcol[1] = CompileShader(GL_FRAGMENT_SHADER, fragmentSrc2Dcolor);
// TODO: error handling!
gl3state.si2Dcolor.shaderProgram = CreateShaderProgram(2, shaders2Dcol);
// I think the shaders aren't needed anymore once they're linked into the program
glDeleteShader(shaders2Dcol[0]);
glDeleteShader(shaders2Dcol[1]);
if(gl3state.si2Dcolor.shaderProgram != 0)
{
glUseProgram(gl3state.si2Dcolor.shaderProgram);
gl3state.si2Dcolor.attribColor = glGetAttribLocation(gl3state.si2Dcolor.shaderProgram, "color");
gl3state.si2Dcolor.attribPosition = glGetAttribLocation(gl3state.si2Dcolor.shaderProgram, "position");
gl3state.si2Dcolor.attribTexCoord = -1;
gl3state.si2Dcolor.uniTransMatrix = glGetUniformLocation(gl3state.si2Dcolor.shaderProgram, "trans");
} }
else shaderInfo->attribPosition = i;
// the following line will set it to -1 for the textured case, that's ok.
shaderInfo->attribColor = glGetAttribLocation(prog, "color");
// the following line will set it to -1 for the color-only case, that's ok.
shaderInfo->attribTexCoord = glGetAttribLocation(prog, "texCoord");
i = glGetUniformLocation(prog, "trans");
if( i == -1)
{ {
// TODO: error handling! R_Printf(PRINT_ALL, "WARNING: Couldn't get 'trans' uniform in shader\n");
return false;
} }
shaderInfo->uniTransMatrix = i;
return true;
}
qboolean GL3_InitShaders(void)
{
if(!initShader2D(&gl3state.si2D, vertexSrc2D, fragmentSrc2D))
{
R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for textured 2D rendering!\n");
return false;
}
if(!initShader2D(&gl3state.si2Dcolor, vertexSrc2Dcolor, fragmentSrc2Dcolor))
{
R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for color-only 2D rendering!\n");
return false;
} }
return true; return true;
} }
void GL3_ShutdownShaders(void)
{
if(gl3state.si2D.shaderProgram != 0)
glDeleteProgram(gl3state.si2D.shaderProgram);
memset(&gl3state.si2D, 0, sizeof(gl3ShaderInfo_t));
if(gl3state.si2Dcolor.shaderProgram != 0)
glDeleteProgram(gl3state.si2Dcolor.shaderProgram);
memset(&gl3state.si2Dcolor, 0, sizeof(gl3ShaderInfo_t));
}

View File

@ -92,7 +92,8 @@ typedef struct
float max_anisotropy; float max_anisotropy;
} gl3config_t; } gl3config_t;
typedef struct { typedef struct
{
GLuint shaderProgram; GLuint shaderProgram;
GLint attribPosition; GLint attribPosition;
@ -226,6 +227,7 @@ extern void GL3_SetSky(char *name, float rotate, vec3_t axis);
// gl3_shaders.c // gl3_shaders.c
extern qboolean GL3_InitShaders(void); extern qboolean GL3_InitShaders(void);
extern void GL3_ShutdownShaders(void);
// ############ Cvars ########### // ############ Cvars ###########