diff --git a/Makefile b/Makefile index bfa98f54..92db27e3 100755 --- a/Makefile +++ b/Makefile @@ -850,6 +850,7 @@ REFGL3_OBJS_ := \ src/client/refresh/gl3/gl3_model.o \ src/client/refresh/gl3/gl3_sdl.o \ src/client/refresh/gl3/gl3_warp.o \ + src/client/refresh/gl3/gl3_shaders.o \ src/client/refresh/files/pcx.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ diff --git a/src/client/refresh/gl3/gl3_draw.c b/src/client/refresh/gl3/gl3_draw.c index 95ce1efc..933544b7 100644 --- a/src/client/refresh/gl3/gl3_draw.c +++ b/src/client/refresh/gl3/gl3_draw.c @@ -36,6 +36,86 @@ GL3_Draw_InitLocal(void) { /* load console characters */ draw_chars = GL3_FindImage("pics/conchars.pcx", it_pic); + + glGenVertexArrays(1, &gl3state.vao2D); + glBindVertexArray(gl3state.vao2D); + + glGenBuffers(1, &gl3state.vbo2D); + glBindBuffer(GL_ARRAY_BUFFER, gl3state.vbo2D); // TODO ?? + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void +GL3_Draw_ShutdownLocal(void) +{ + glDeleteBuffers(1, &gl3state.vbo2D); + gl3state.vbo2D = 0; + glDeleteVertexArrays(1, &gl3state.vao2D); + gl3state.vao2D = 0; +} + +/* + * 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 + * smoothly scrolled off. + */ +void +GL3_Draw_CharScaled(int x, int y, int num, float scale) +{ + STUB_ONCE("TODO: Implement!"); +#if 0 + int row, col; + float frow, fcol, size, scaledSize; + + num &= 255; + + if ((num & 127) == 32) + { + return; /* space */ + } + + if (y <= -8) + { + return; /* totally off screen */ + } + + row = num >> 4; + col = num & 15; + + frow = row * 0.0625; + fcol = col * 0.0625; + size = 0.0625; + + scaledSize = 8*scale; + + R_Bind(draw_chars->texnum); + + 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 * @@ -62,7 +142,7 @@ GL3_Draw_GetPicSize(int *w, int *h, char *pic) { gl3image_t *gl; - gl = GL3Draw_FindPic(pic); + gl = GL3_Draw_FindPic(pic); if (!gl) { @@ -74,6 +154,296 @@ GL3_Draw_GetPicSize(int *w, int *h, char *pic) *h = gl->height; } +void +GL3_Draw_StretchPic(int x, int y, int w, int h, char *pic) +{ + STUB_ONCE("TODO: Implement!"); +#if 0 + image_t *gl; + + gl = RDraw_FindPic(pic); + + if (!gl) + { + R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); + return; + } + + if (scrap_dirty) + { + Scrap_Upload(); + } + + R_Bind(gl->texnum); + + 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 +GL3_Draw_PicScaled(int x, int y, char *pic, float factor) +{ + STUB_ONCE("TODO: Implement!"); +#if 0 + image_t *gl; + + gl = RDraw_FindPic(pic); + + if (!gl) + { + R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); + return; + } + + if (scrap_dirty) + { + Scrap_Upload(); + } + + R_Bind(gl->texnum); + + 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 +} + +/* + * This repeats a 64*64 tile graphic to fill + * the screen around a sized down + * refresh window. + */ +void +GL3_Draw_TileClear(int x, int y, int w, int h, char *pic) +{ + STUB_ONCE("TODO: Implement!"); +#if 0 + image_t *image; + + image = RDraw_FindPic(pic); + + if (!image) + { + R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); + return; + } + + R_Bind(image->texnum); + + GLfloat vtx[] = { + 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 +} + +/* + * Fills a box of pixels with a single color + */ +void +GL3_Draw_Fill(int x, int y, int w, int h, int c) +{ + STUB_ONCE("TODO: Implement!"); +#if 0 + union + { + unsigned c; + byte v[4]; + } color; + + if ((unsigned)c > 255) + { + ri.Sys_Error(ERR_FATAL, "Draw_Fill: bad color"); + } + + glDisable(GL_TEXTURE_2D); + + color.c = d_8to24table[c]; + glColor4f(color.v [ 0 ] / 255.0, color.v [ 1 ] / 255.0, + color.v [ 2 ] / 255.0, 1); + + GLfloat vtx[] = { + x, y, + x + w, y, + x + w, y + h, + x, y + h + }; + + glEnableClientState( GL_VERTEX_ARRAY ); + + glVertexPointer( 2, GL_FLOAT, 0, vtx ); + glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); + + glDisableClientState( GL_VERTEX_ARRAY ); + + glColor4f( 1, 1, 1, 1 ); + glEnable(GL_TEXTURE_2D); +#endif // 0 +} + +void +GL3_Draw_FadeScreen(void) +{ + STUB_ONCE("TODO: Implement!"); +#if 0 + glEnable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glColor4f(0, 0, 0, 0.8); + + GLfloat vtx[] = { + 0, 0, + vid.width, 0, + vid.width, vid.height, + 0, vid.height + }; + + glEnableClientState( GL_VERTEX_ARRAY ); + + glVertexPointer( 2, GL_FLOAT, 0, vtx ); + glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); + + glDisableClientState( GL_VERTEX_ARRAY ); + + glColor4f(1, 1, 1, 1); + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); +#endif // 0 +} + +void +GL3_Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte *data) +{ + byte *source; + float hscale = 1.0f; + int frac, fracstep; + int i, j, trows; + 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); + + unsigned image32[320*240]; /* was 256 * 256, but we want a bit more space */ + + unsigned* img = image32; + + if(cols*rows > 320*240) + { + /* in case there is a bigger video after all, + * malloc enough space to hold the frame */ + img = (unsigned*)malloc(cols*rows*4); + } + + for(i=0; imodified) + { + vid_fullscreen->modified = true; + } + + // force a vid_restart if gl_stereo has been modified. + if ( gl_state.stereo_mode != gl_stereo->value ) { + // If we've gone from one mode to another with the same special buffer requirements there's no need to restart. + if ( GL_GetSpecialBufferModeForStereoMode( gl_state.stereo_mode ) == GL_GetSpecialBufferModeForStereoMode( gl_stereo->value ) ) { + gl_state.stereo_mode = gl_stereo->value; + } + else + { + R_Printf(PRINT_ALL, "stereo supermode changed, restarting video!\n"); + cvar_t *ref; + ref = ri.Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); + ref->modified = true; + } + } + + if (vid_gamma->modified) + { + vid_gamma->modified = false; + + if (gl_state.hwgamma) + { + UpdateHardwareGamma(); + } + } + + // Clamp overbrightbits + if (gl_overbrightbits->modified) + { + if (gl_overbrightbits->value > 2 && gl_overbrightbits->value < 4) + { + ri.Cvar_Set("gl_overbrightbits", "2"); + } + else if (gl_overbrightbits->value > 4) + { + ri.Cvar_Set("gl_overbrightbits", "4"); + } + + gl_overbrightbits->modified = false; + } + + /* go into 2D mode */ + + int x, w, y, h; + qboolean drawing_left_eye = gl_state.camera_separation < 0; + qboolean stereo_split_tb = ((gl_state.stereo_mode == STEREO_SPLIT_VERTICAL) && gl_state.camera_separation); + qboolean stereo_split_lr = ((gl_state.stereo_mode == STEREO_SPLIT_HORIZONTAL) && gl_state.camera_separation); + + x = 0; + w = vid.width; + y = 0; + h = vid.height; + + if(stereo_split_lr) { + w = w / 2; + x = drawing_left_eye ? 0 : w; + } + + if(stereo_split_tb) { + h = h / 2; + y = drawing_left_eye ? h : 0; + } + + glViewport(x, y, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, vid.width, vid.height, 0, -99999, 99999); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glColor4f(1, 1, 1, 1); + + /* draw buffer stuff */ + if (gl_drawbuffer->modified) + { + gl_drawbuffer->modified = false; + + if ((gl_state.camera_separation == 0) || gl_state.stereo_mode != STEREO_MODE_OPENGL) + { + if (Q_stricmp(gl_drawbuffer->string, "GL_FRONT") == 0) + { + glDrawBuffer(GL_FRONT); + } + else + { + glDrawBuffer(GL_BACK); + } + } + } + + /* texturemode stuff */ + if (gl_texturemode->modified || (gl_config.anisotropic && gl_anisotropic->modified)) + { + R_TextureMode(gl_texturemode->string); + gl_texturemode->modified = false; + gl_anisotropic->modified = false; + } + + if (gl_texturealphamode->modified) + { + R_TextureAlphaMode(gl_texturealphamode->string); + gl_texturealphamode->modified = false; + } + + if (gl_texturesolidmode->modified) + { + R_TextureSolidMode(gl_texturesolidmode->string); + gl_texturesolidmode->modified = false; + } + + /* clear screen if desired */ + R_Clear(); +#endif // 0 +} + +static void +GL3_SetPalette(const unsigned char *palette) +{ + int i; + byte *rp = (byte *)gl3_rawpalette; + + if (palette) + { + for (i = 0; i < 256; i++) + { + rp[i * 4 + 0] = palette[i * 3 + 0]; + rp[i * 4 + 1] = palette[i * 3 + 1]; + rp[i * 4 + 2] = palette[i * 3 + 2]; + rp[i * 4 + 3] = 0xff; + } + } + else + { + for (i = 0; i < 256; i++) + { + rp[i * 4 + 0] = LittleLong(d_8to24table[i]) & 0xff; + rp[i * 4 + 1] = (LittleLong(d_8to24table[i]) >> 8) & 0xff; + rp[i * 4 + 2] = (LittleLong(d_8to24table[i]) >> 16) & 0xff; + rp[i * 4 + 3] = 0xff; + } + } + + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(1, 0, 0.5, 0.5); +} + Q2_DLL_EXPORTED refexport_t GetRefAPI(refimport_t imp) { @@ -743,20 +930,18 @@ GetRefAPI(refimport_t imp) re.DrawFindPic = GL3_Draw_FindPic; re.DrawGetPicSize = GL3_Draw_GetPicSize; -#if 0 // TODO! - re.DrawPicScaled = RDraw_PicScaled; - re.DrawStretchPic = RDraw_StretchPic; + re.DrawPicScaled = GL3_Draw_PicScaled; + re.DrawStretchPic = GL3_Draw_StretchPic; - re.DrawCharScaled = RDraw_CharScaled; - re.DrawTileClear = RDraw_TileClear; - re.DrawFill = RDraw_Fill; - re.DrawFadeScreen = RDraw_FadeScreen; + re.DrawCharScaled = GL3_Draw_CharScaled; + re.DrawTileClear = GL3_Draw_TileClear; + re.DrawFill = GL3_Draw_Fill; + re.DrawFadeScreen = GL3_Draw_FadeScreen; - re.DrawStretchRaw = RDraw_StretchRaw; - re.SetPalette = RI_SetPalette; + re.DrawStretchRaw = GL3_Draw_StretchRaw; + re.SetPalette = GL3_SetPalette; - re.BeginFrame = RI_BeginFrame; -#endif // 0 + re.BeginFrame = GL3_BeginFrame; re.EndFrame = GL3_EndFrame; return re; diff --git a/src/client/refresh/gl3/gl3_misc.c b/src/client/refresh/gl3/gl3_misc.c index 1f0f7b67..7fa4fce4 100644 --- a/src/client/refresh/gl3/gl3_misc.c +++ b/src/client/refresh/gl3/gl3_misc.c @@ -36,8 +36,10 @@ GL3_SetDefaultState(void) glClearColor(1, 0, 0.5, 0.5); glDisable(GL_MULTISAMPLE); glCullFace(GL_FRONT); - glEnable(GL_TEXTURE_2D); + //glEnable(GL_TEXTURE_2D); + // TODO: this must be done in shader instead, + // see https://www.khronos.org/opengl/wiki/Transparency_Sorting#Alpha_test //glEnable(GL_ALPHA_TEST); //glAlphaFunc(GL_GREATER, 0.666); diff --git a/src/client/refresh/gl3/gl3_sdl.c b/src/client/refresh/gl3/gl3_sdl.c index e3a7786c..d9bc7f97 100644 --- a/src/client/refresh/gl3/gl3_sdl.c +++ b/src/client/refresh/gl3/gl3_sdl.c @@ -118,13 +118,30 @@ int GL3_PrepareForWindow(void) return flags; } +enum { + // for some reason my driver calls the DebugCallback with the following severity + // even though I think it shouldn't for the extension I'm using? + // anyway, my gl headers don't know GL_DEBUG_SEVERITY_NOTIFICATION_* so I define it here + QGL_DEBUG_SEVERITY_NOTIFICATION = 0x826B +}; + static void DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, - const GLchar *message, const void *userParam) + const GLchar *message, const void *userParam) { const char* sourceStr = "Source: Unknown"; const char* typeStr = "Type: Unknown"; const char* severityStr = "Severity: Unknown"; + switch(severity) + { + case QGL_DEBUG_SEVERITY_NOTIFICATION: + // severityStr = "Severity: Note"; break; + return; // ignore these + + case GL_DEBUG_SEVERITY_HIGH_ARB: severityStr = "Severity: High"; break; + case GL_DEBUG_SEVERITY_MEDIUM_ARB: severityStr = "Severity: Medium"; break; + case GL_DEBUG_SEVERITY_LOW_ARB: severityStr = "Severity: Low"; break; + } switch(source) { #define SRCCASE(X) case GL_DEBUG_SOURCE_ ## X ## _ARB: sourceStr = "Source: " #X; break; @@ -147,12 +164,6 @@ DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei le TYPECASE(OTHER); #undef TYPECASE } - switch(severity) - { - case GL_DEBUG_SEVERITY_HIGH_ARB: severityStr = "Severity: High"; break; - case GL_DEBUG_SEVERITY_MEDIUM_ARB: severityStr = "Severity: Medium"; break; - case GL_DEBUG_SEVERITY_LOW_ARB: severityStr = "Severity: Low"; break; - } // use PRINT_ALL - this is only called with gl3_debugcontext != 0 anyway. R_Printf(PRINT_ALL, "GLDBG %s %s %s: %s\n", sourceStr, typeStr, severityStr, message); diff --git a/src/client/refresh/gl3/gl3_shaders.c b/src/client/refresh/gl3/gl3_shaders.c new file mode 100644 index 00000000..8d6ec88a --- /dev/null +++ b/src/client/refresh/gl3/gl3_shaders.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * Copyright (C) 2016-2017 Daniel Gibson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * ======================================================================= + * + * OpenGL3 refresher: Handling shaders + * + * ======================================================================= + */ + +#include "header/local.h" + +// TODO: remove eprintf() usage +#define eprintf(...) R_Printf(PRINT_ALL, __VA_ARGS__) + + +#if 0 +static const char* vertexSrc = MULTILINE_STRING(#version 150\n + in vec2 position; + // I renamed color to inColor and Color to passColor for more clarity + in vec3 inColor; + // same for texcoord -> inTexCoord, Textcoord -> passTexCoord + in vec2 inTexCoord; + + out vec3 passColor; + out vec2 passTexCoord; + + void main() { + passColor = inColor; + passTexCoord = inTexCoord; + gl_Position = vec4(position, 0.0, 1.0); + } +); + +static const char* fragmentSrc = MULTILINE_STRING(#version 150\n + in vec3 passColor; // I renamed color to passColor (it's from the vertex shader above) + in vec2 passTexCoord; // same for Texcoord -> passTexCoord + out vec4 outColor; + + uniform sampler2D tex; + + void main() + { + outColor = texture(tex, passTexCoord) * vec4(passColor, 1.0); + //outColor = texture(tex, passTexCoord); + //outColor = vec4(passColor, 1.0); + } +); +#endif // 0 + +static GLuint +CompileShader(GLenum shaderType, const char* shaderSrc) +{ + GLuint shader = glCreateShader(shaderType); + glShaderSource(shader, 1, &shaderSrc, NULL); + glCompileShader(shader); + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if(status != GL_TRUE) + { + char buf[2048]; + char* bufPtr = buf; + int bufLen = sizeof(buf); + GLint infoLogLength; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); + if(infoLogLength >= bufLen) + { + bufPtr = malloc(infoLogLength+1); + bufLen = infoLogLength+1; + if(bufPtr == NULL) + { + bufPtr = buf; + bufLen = sizeof(buf); + eprintf("WARN: In CompileShader(), malloc(%d) failed!\n", infoLogLength+1); + } + } + + glGetShaderInfoLog(shader, bufLen, NULL, bufPtr); + + const char* shaderTypeStr = ""; + switch(shaderType) + { + case GL_VERTEX_SHADER: shaderTypeStr = "Vertex"; break; + case GL_FRAGMENT_SHADER: shaderTypeStr = "Fragment"; break; + case GL_GEOMETRY_SHADER: shaderTypeStr = "Geometry"; break; + /* not supported in OpenGL3.2 and we're unlikely to need/use them anyway + case GL_COMPUTE_SHADER: shaderTypeStr = "Compute"; break; + case GL_TESS_CONTROL_SHADER: shaderTypeStr = "TessControl"; break; + case GL_TESS_EVALUATION_SHADER: shaderTypeStr = "TessEvaluation"; break; + */ + } + eprintf("ERROR: Compiling %s Shader failed: %s\n", shaderTypeStr, bufPtr); + glDeleteShader(shader); + + if(bufPtr != buf) free(bufPtr); + + return 0; + } + + return shader; +} + +static GLuint +CreateShaderProgram(int numShaders, const GLuint* shaders) +{ + int i=0; + GLuint shaderProgram = glCreateProgram(); + if(shaderProgram == 0) + { + eprintf("ERROR: Couldn't create a new Shader Program!\n"); + return 0; + } + + for(i=0; i= bufLen) + { + bufPtr = malloc(infoLogLength+1); + bufLen = infoLogLength+1; + if(bufPtr == NULL) + { + bufPtr = buf; + bufLen = sizeof(buf); + eprintf("WARN: In CreateShaderProgram(), malloc(%d) failed!\n", infoLogLength+1); + } + } + + glGetProgramInfoLog(shaderProgram, bufLen, NULL, bufPtr); + + eprintf("ERROR: Linking shader program failed: %s\n", bufPtr); + + glDeleteProgram(shaderProgram); + + if(bufPtr != buf) free(bufPtr); + + return 0; + } + + for(i=0; i