/* Copyright (C) 1997-2001 Id Software, Inc. 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. */ // r_draw.c #include "r_local.h" image_t *draw_chars; // vertex arrays float tex_array[MAX_ARRAY][2]; float vert_array[MAX_ARRAY][3]; float col_array[MAX_ARRAY][4]; extern qboolean scrap_dirty; void Scrap_Upload (void); #define DEFAULT_FONT_SIZE 8.0f void RefreshFont (void) { con_font->modified = false; draw_chars = GL_FindImage (va("fonts/%s.pcx", con_font->string), it_pic); if (!draw_chars) // fall back on default font draw_chars = GL_FindImage ("fonts/default.pcx", it_pic); if (!draw_chars) // fall back on old Q2 conchars draw_chars = GL_FindImage ("pics/conchars.pcx", it_pic); if (!draw_chars) // Knightmare- prevent crash caused by missing font VID_Error (ERR_FATAL, "RefreshFont: couldn't load pics/conchars"); GL_Bind( draw_chars->texnum ); } /* =============== R_DrawInitLocal =============== */ void R_DrawInitLocal (void) { // load console characters (don't bilerp characters) //draw_chars = GL_FindImage ("pics/conchars.pcx", it_pic); //GL_Bind( draw_chars->texnum ); image_t *R_DrawFindPic (char *name); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // load console characters (don't bilerp characters) RefreshFont(); } /* ================ R_CharMapScale ================ */ float R_CharMapScale (void) { return (draw_chars->width/128.0); //current width / original width } /* ================ R_DrawCharacter ================ */ void R_DrawCharacter (float x, float y, float frow, float fcol, float size, float scale, int red, int green, int blue, int alpha, qboolean italic) { float italicAdd = 0; if (italic) italicAdd = scale*0.25; qglColor4ub( (byte)red, (byte)green, (byte)blue, (byte)alpha); qglTexCoord2f (fcol, frow); qglVertex2f (x+italicAdd, y); qglTexCoord2f (fcol + size, frow); qglVertex2f (x+scale+italicAdd, y); qglTexCoord2f (fcol + size, frow + size); qglVertex2f (x+scale-italicAdd, y+scale); qglTexCoord2f (fcol, frow + size); qglVertex2f (x-italicAdd, y+scale); } /* ================ R_DrawScaledChar ================ */ void R_DrawScaledChar (float x, float y, int num, float scale, int red, int green, int blue, int alpha, qboolean italic) { int row, col; float frow, fcol, size, cscale; num &= 255; if (alpha >= 255) alpha = 255; else if (alpha <= 1) alpha = 1; 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; cscale = scale * DEFAULT_FONT_SIZE; { GLSTATE_DISABLE_ALPHATEST GL_TexEnv( GL_MODULATE ); GLSTATE_ENABLE_BLEND qglDepthMask (false); } GL_Bind(draw_chars->texnum); qglBegin (GL_QUADS); { R_DrawCharacter(x, y, frow, fcol, size, cscale, red, green, blue, alpha, italic); } qglEnd (); { qglDepthMask (true); GL_TexEnv( GL_REPLACE ); GLSTATE_DISABLE_BLEND qglColor4f (1,1,1,1); GLSTATE_ENABLE_ALPHATEST } } /* ================ R_DrawChar 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. ================ */ // Knightmare- added alpha for Psychospaz's fading centerprints void R_DrawChar (int x, int y, int num, int alpha) { int row, col; float frow, fcol, size, scale; num &= 255; if (alpha >= 255) alpha = 255; else if (alpha <= 1) alpha = 1; 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; scale = DEFAULT_FONT_SIZE; // Knightmare- added Psychospaz's alpha for chars { qglDisable (GL_ALPHA_TEST); GL_TexEnv( GL_MODULATE ); qglColor4ub( 255, 255, 255, (byte)alpha); qglEnable( GL_BLEND ); qglDepthMask (false); } GL_Bind (draw_chars->texnum); qglBegin (GL_QUADS); { R_DrawCharacter(x, y, frow, fcol, size, scale, 255, 255, 255, alpha, false); /*qglTexCoord2f (fcol, frow); qglVertex2f (x, y); qglTexCoord2f (fcol + size, frow); qglVertex2f (x+scale, y); qglTexCoord2f (fcol + size, frow + size); qglVertex2f (x+scale, y+scale); qglTexCoord2f (fcol, frow + size); qglVertex2f (x, y+scale);*/ } qglEnd (); { qglDepthMask (true); GL_TexEnv( GL_REPLACE ); qglDisable ( GL_BLEND ); qglColor4f (1,1,1,1); qglEnable (GL_ALPHA_TEST); } } /* ============= R_DrawFindPic ============= */ image_t *R_DrawFindPic (char *name) { image_t *gl; char fullname[MAX_QPATH]; if (name[0] != '/' && name[0] != '\\') { Com_sprintf (fullname, sizeof(fullname), "pics/%s.pcx", name); gl = GL_FindImage (fullname, it_pic); } else gl = GL_FindImage (name+1, it_pic); // jitfix //if (!gl) // jit -- remove "can't find pic" spam // return r_notexture; //else // end jitfix return gl; } /* ============= R_DrawGetPicSize ============= */ void R_DrawGetPicSize (int *w, int *h, char *pic) { image_t *gl; gl = R_DrawFindPic (pic); if (!gl) { *w = *h = -1; return; } *w = gl->width; *h = gl->height; } /* ============= R_DrawStretchPic ============= */ void R_DrawStretchPic (int x, int y, int w, int h, char *pic, float alpha) { image_t *gl; gl = R_DrawFindPic (pic); if (!gl) { VID_Printf (PRINT_ALL, "Can't find pic: %s\n", pic); return; } if (scrap_dirty) Scrap_Upload (); if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( gl_config.renderer & GL_RENDERER_RENDITION ) ) && !gl->has_alpha) qglDisable (GL_ALPHA_TEST); //Knightare- Psychospaz's transparent console support if (gl->has_alpha || alpha<1) { qglDisable (GL_ALPHA_TEST); GL_Bind(gl->texnum); GL_TexEnv(GL_MODULATE); qglColor4f(1,1,1,alpha); qglEnable(GL_BLEND); qglDepthMask(false); } else //end Knightmare GL_Bind (gl->texnum); qglBegin (GL_QUADS); qglTexCoord2f (gl->sl, gl->tl); qglVertex2f (x, y); qglTexCoord2f (gl->sh, gl->tl); qglVertex2f (x+w, y); qglTexCoord2f (gl->sh, gl->th); qglVertex2f (x+w, y+h); qglTexCoord2f (gl->sl, gl->th); qglVertex2f (x, y+h); qglEnd (); //Knightare- Psychospaz's transparent console support if (gl->has_alpha || alpha < 1) { qglDepthMask (true); GL_TexEnv(GL_REPLACE); qglDisable(GL_BLEND); qglColor4f(1,1,1,1); qglEnable(GL_ALPHA_TEST); } //end Knightmare if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( gl_config.renderer & GL_RENDERER_RENDITION ) ) && !gl->has_alpha) qglEnable (GL_ALPHA_TEST); } //Knightmare- this is Psychospaz's code for drawing stretched crosshairs /* ============= R_DrawScaledPic ============= */ void R_DrawScaledPic (int x, int y, float scale, float alpha, char *pic, qboolean fixcoords, qboolean repscale) { float xoff, yoff; float scale_x, scale_y; image_t *gl; gl = R_DrawFindPic (pic); if (!gl) { VID_Printf (PRINT_ALL, "Can't find pic: %s\n", pic); return; } if (scrap_dirty) Scrap_Upload (); if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( gl_config.renderer & GL_RENDERER_RENDITION ) ) && !gl->has_alpha) qglDisable (GL_ALPHA_TEST); //add alpha support { qglDisable (GL_ALPHA_TEST); qglBindTexture (GL_TEXTURE_2D, gl->texnum); GL_TexEnv( GL_MODULATE ); qglColor4f(1,1,1, alpha); //need <1 for trans to work qglEnable( GL_BLEND ); qglDepthMask (false); } scale_x = scale_y = scale; // NOTE: replace this with shaders as soon as they are supported if (repscale) { scale_x *= gl->replace_scale_w; // scale down if replacing a pcx image scale_y *= gl->replace_scale_h; // scale down if replacing a pcx image } if (fixcoords) // Knightmare- whether to adjust coordinates for scaling { xoff = (gl->width*scale_x-gl->width)/2; yoff = (gl->height*scale_y-gl->height)/2; GL_Bind (gl->texnum); qglBegin (GL_QUADS); qglTexCoord2f (gl->sl, gl->tl); qglVertex2f (x-xoff, y-yoff); qglTexCoord2f (gl->sh, gl->tl); qglVertex2f (x+gl->width+xoff, y-yoff); qglTexCoord2f (gl->sh, gl->th); qglVertex2f (x+gl->width+xoff, y+gl->height+yoff); qglTexCoord2f (gl->sl, gl->th); qglVertex2f (x-xoff, y+gl->height+yoff); qglEnd (); } else { xoff = gl->width*scale_x-gl->width; yoff = gl->height*scale_y-gl->height; GL_Bind (gl->texnum); qglBegin (GL_QUADS); qglTexCoord2f (gl->sl, gl->tl); qglVertex2f (x, y); qglTexCoord2f (gl->sh, gl->tl); qglVertex2f (x+gl->width+xoff, y); qglTexCoord2f (gl->sh, gl->th); qglVertex2f (x+gl->width+xoff, y+gl->height+yoff); qglTexCoord2f (gl->sl, gl->th); qglVertex2f (x, y+gl->height+yoff); qglEnd (); } //add alpha support { qglDepthMask (true); GL_TexEnv( GL_REPLACE ); qglDisable ( GL_BLEND ); qglColor4f (1,1,1,1); qglEnable (GL_ALPHA_TEST); } if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( gl_config.renderer & GL_RENDERER_RENDITION ) ) && !gl->has_alpha) qglEnable (GL_ALPHA_TEST); } /* ============= R_DrawPic ============= */ void R_DrawPic (int x, int y, char *pic) { image_t *gl; gl = R_DrawFindPic (pic); if (!gl) { VID_Printf (PRINT_ALL, "Can't find pic: %s\n", pic); return; } if (scrap_dirty) Scrap_Upload (); if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( gl_config.renderer & GL_RENDERER_RENDITION ) ) && !gl->has_alpha) qglDisable (GL_ALPHA_TEST); GL_Bind (gl->texnum); qglBegin (GL_QUADS); qglTexCoord2f (gl->sl, gl->tl); qglVertex2f (x, y); qglTexCoord2f (gl->sh, gl->tl); qglVertex2f (x+gl->width, y); qglTexCoord2f (gl->sh, gl->th); qglVertex2f (x+gl->width, y+gl->height); qglTexCoord2f (gl->sl, gl->th); qglVertex2f (x, y+gl->height); qglEnd (); if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( gl_config.renderer & GL_RENDERER_RENDITION ) ) && !gl->has_alpha) qglEnable (GL_ALPHA_TEST); } /* ============= R_DrawTileClear This repeats a 64*64 tile graphic to fill the screen around a sized down refresh window. ============= */ void R_DrawTileClear (int x, int y, int w, int h, char *pic) { image_t *image; image = R_DrawFindPic (pic); if (!image) { VID_Printf (PRINT_ALL, "Can't find pic: %s\n", pic); return; } if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( gl_config.renderer & GL_RENDERER_RENDITION ) ) && !image->has_alpha) qglDisable (GL_ALPHA_TEST); GL_Bind (image->texnum); qglBegin (GL_QUADS); qglTexCoord2f (x/64.0, y/64.0); qglVertex2f (x, y); qglTexCoord2f ( (x+w)/64.0, y/64.0); qglVertex2f (x+w, y); qglTexCoord2f ( (x+w)/64.0, (y+h)/64.0); qglVertex2f (x+w, y+h); qglTexCoord2f ( x/64.0, (y+h)/64.0 ); qglVertex2f (x, y+h); qglEnd (); if ( ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( gl_config.renderer & GL_RENDERER_RENDITION ) ) && !image->has_alpha) qglEnable (GL_ALPHA_TEST); } /* ============= R_DrawFill Fills a box of pixels with a single color ============= */ void R_DrawFill (int x, int y, int w, int h, int c) { union { unsigned c; byte v[4]; } color; if ( (unsigned)c > 255) VID_Error (ERR_FATAL, "Draw_Fill: bad color"); qglDisable (GL_TEXTURE_2D); color.c = d_8to24table[c]; qglColor3f (color.v[0]/255.0, color.v[1]/255.0, color.v[2]/255.0); qglBegin (GL_QUADS); qglVertex2f (x,y); qglVertex2f (x+w, y); qglVertex2f (x+w, y+h); qglVertex2f (x, y+h); qglEnd (); qglColor3f (1,1,1); qglEnable (GL_TEXTURE_2D); } // Knightmare added /* ====================== R_DrawFill2 Fills a box of pixels with a 24-bit color w/ alpha =========================== */ void R_DrawFill2 (int x, int y, int w, int h, int red, int green, int blue, int alpha) { red = min(red, 255); green = min(green, 255); blue = min(blue, 255); if (alpha >= 255) alpha = 255; else if (alpha <= 1) alpha = 1; qglDisable (GL_TEXTURE_2D); GLSTATE_DISABLE_ALPHATEST GL_TexEnv( GL_MODULATE ); GLSTATE_ENABLE_BLEND qglDepthMask (false); qglColor4ub ((byte)red, (byte)green, (byte)blue, (byte)alpha); qglBegin (GL_QUADS); qglVertex2f (x,y); qglVertex2f (x+w, y); qglVertex2f (x+w, y+h); qglVertex2f (x, y+h); qglEnd (); qglDepthMask (true); GL_TexEnv( GL_REPLACE ); GLSTATE_DISABLE_BLEND qglColor4f (1,1,1,1); GLSTATE_ENABLE_ALPHATEST qglEnable (GL_TEXTURE_2D); } //============================================================================= /* ================ R_DrawFadeScreen ================ */ void R_DrawFadeScreen (void) { qglEnable (GL_BLEND); qglDisable (GL_TEXTURE_2D); qglColor4f (0, 0, 0, 0.8); qglBegin (GL_QUADS); qglVertex2f (0,0); qglVertex2f (vid.width, 0); qglVertex2f (vid.width, vid.height); qglVertex2f (0, vid.height); qglEnd (); qglColor4f (1,1,1,1); qglEnable (GL_TEXTURE_2D); qglDisable (GL_BLEND); } //==================================================================== /* ============= R_DrawStretchRaw Cinematic streaming ============= */ #ifdef ROQ_SUPPORT void R_DrawStretchRaw (int x, int y, int w, int h, const byte *raw, int rawWidth, int rawHeight) //qboolean noDraw) { int width = 1, height = 1; // Make sure everything is flushed if needed //if (!noDraw) // RB_RenderMesh(); // Check the dimensions while (width < rawWidth) width <<= 1; while (height < rawHeight) height <<= 1; if (rawWidth != width || rawHeight != height) VID_Error(ERR_DROP, "Draw_StretchRaw2: size is not a power of two (%i x %i)", rawWidth, rawHeight); if (rawWidth > gl_config.max_texsize || rawHeight > gl_config.max_texsize) VID_Error(ERR_DROP, "Draw_StretchRaw2: size exceeds hardware limits (%i > %i or %i > %i)", rawWidth, gl_config.max_texsize, rawHeight, gl_config.max_texsize); // Update the texture as appropriate GL_Bind(r_rawtexture->texnum); if (rawWidth == r_rawtexture->upload_width && rawHeight == r_rawtexture->upload_height) qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rawWidth, rawHeight, GL_RGBA, GL_UNSIGNED_BYTE, raw); else { r_rawtexture->upload_width = rawWidth; r_rawtexture->upload_height = rawHeight; qglTexImage2D(GL_TEXTURE_2D, 0, gl_tex_solid_format, rawWidth, rawHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, raw); } //if (noDraw) // return; qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Draw it qglColor4ub(255, 255, 255, 255); qglBegin(GL_QUADS); qglTexCoord2f(0, 0); qglVertex2f(x, y); qglTexCoord2f(1, 0); qglVertex2f(x+w, y); qglTexCoord2f(1, 1); qglVertex2f(x+w, y+h); qglTexCoord2f(0, 1); qglVertex2f(x, y+h); qglEnd(); } #else // old 8-bit, 256x256 version extern unsigned r_rawpalette[256]; void R_DrawStretchRaw (int x, int y, int w, int h, int cols, int rows, byte *data) { unsigned image32[256*256]; unsigned char image8[256*256]; int i, j, trows; byte *source; int frac, fracstep; float hscale; int row; float t; // Knightmare- Nicolas' fix for stray pixels at bottom and top memset(image32, 0, sizeof(image32)); GL_Bind (0); if (rows<=256) { hscale = 1; trows = rows; } else { hscale = rows/256.0; trows = 256; } t = rows*hscale / 256; if ( !qglColorTableEXT ) { unsigned *dest; for (i=0 ; i rows) break; source = data + cols*row; dest = &image32[i*256]; fracstep = cols*0x10000/256; frac = fracstep >> 1; for (j=0 ; j<256 ; j++) { dest[j] = r_rawpalette[source[frac>>16]]; frac += fracstep; } } qglTexImage2D (GL_TEXTURE_2D, 0, gl_tex_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, image32); } else { unsigned char *dest; for (i=0 ; i rows) break; source = data + cols*row; dest = &image8[i*256]; fracstep = cols*0x10000/256; frac = fracstep >> 1; for (j=0 ; j<256 ; j++) { dest[j] = source[frac>>16]; frac += fracstep; } } qglTexImage2D( GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, 256, 256, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, image8 ); } qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( gl_config.renderer & GL_RENDERER_RENDITION ) ) qglDisable (GL_ALPHA_TEST); qglBegin (GL_QUADS); qglTexCoord2f (0, 0); qglVertex2f (x, y); qglTexCoord2f (1, 0); qglVertex2f (x+w, y); qglTexCoord2f (1, t); qglVertex2f (x+w, y+h); qglTexCoord2f (0, t); qglVertex2f (x, y+h); qglEnd (); if ( ( gl_config.renderer == GL_RENDERER_MCD ) || ( gl_config.renderer & GL_RENDERER_RENDITION ) ) qglEnable (GL_ALPHA_TEST); } #endif // ROQ_SUPPORT