/* 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. */ // draw.c #include "gl_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); void RefreshFont (void) { draw_chars = GL_FindImage (va("fonts/%s.pcx", con_font->string), it_pic); if (!draw_chars) { draw_chars = GL_FindImage ("fonts/default.pcx", it_pic); ri.Cvar_Set( "con_font", "default" ); } GL_Bind( draw_chars->texnum ); con_font->modified = false; } /* =============== Draw_InitLocal =============== */ void Draw_InitLocal (void) { image_t *Draw_FindPic (char *name); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); RefreshFont(); } /* ================ Draw_Char 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. ================ */ float CharMapScale (void) { return (draw_chars->width/128.0); //current width / original width } void Draw_Character (int character, float x, float y, float size, float scale, int red, int green, int blue, int alpha, qboolean italic) { float frow, fcol, italicAdd = 0; int row, col; if (italic) italicAdd = scale*0.25; { row = character>>4; col = character&15; frow = row*0.0625; fcol = col*0.0625; GL_Bind(draw_chars->texnum); } qglColor4ub( red, green, blue, alpha); qglBegin (GL_QUADS); { 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); } qglEnd(); } void Draw_ScaledChar (float x, float y, int num, float scale, int red, int green, int blue, int alpha, qboolean italic) { float size, cscale; num &= 255; if (alpha >= 254) alpha = 254; else if (alpha <= 1) alpha = 1; if ( (num&127) == 32 ) return; // space if (y <= -8) return; // totally off screen size = 0.0625; cscale = scale * FONT_SIZE; { GLSTATE_DISABLE_ALPHATEST GL_TexEnv( GL_MODULATE ); GLSTATE_ENABLE_BLEND qglDepthMask (false); qglEnable(GL_POLYGON_SMOOTH); } Draw_Character(num, x, y, size, cscale, red, green, blue, alpha, italic); { qglDepthMask (true); GL_TexEnv( GL_REPLACE ); GLSTATE_DISABLE_BLEND qglColor4f (1,1,1,1); GLSTATE_ENABLE_ALPHATEST qglDisable(GL_POLYGON_SMOOTH); } } void Draw_Char (float x, float y, int num, int alpha) { float size, scale; num &= 255; if (alpha >= 254) alpha = 254; else if (alpha <= 1) alpha = 1; if (y <= -FONT_SIZE) return; // totally off screen size = 0.0625; scale = FONT_SIZE; { GLSTATE_DISABLE_ALPHATEST GL_TexEnv( GL_MODULATE ); GLSTATE_ENABLE_BLEND qglDepthMask (false); qglEnable(GL_POLYGON_SMOOTH); } Draw_Character(num, x, y, size, scale, 255, 255, 255, alpha, false); { qglDepthMask (true); GL_TexEnv( GL_REPLACE ); GLSTATE_DISABLE_BLEND qglColor4f (1,1,1,1); GLSTATE_ENABLE_ALPHATEST qglDisable(GL_POLYGON_SMOOTH); } } /* ============= Draw_FindPic ============= */ image_t *Draw_FindPic (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); return gl; } /* ============= Draw_GetPicSize ============= */ void ShaderResizePic( image_t *gl, int *w, int *h) { rscript_t *rs = NULL; if (r_shaders->value) rs=RS_FindScript(gl->bare_name); if (!rs) return; if (!rs->picsize.enable) return; *w = rs->picsize.width; *h = rs->picsize.height; } void Draw_GetPicSize (int *w, int *h, char *pic) { image_t *gl; gl = Draw_FindPic (pic); if (!gl) { *w = *h = -1; return; } *w = gl->width; *h = gl->height; ShaderResizePic(gl, w, h); } void Draw_ShaderPic (image_t *gl) { rscript_t *rs = NULL; float txm,tym, alpha,s,t; rs_stage_t *stage; if (r_shaders->value) rs=RS_FindScript(gl->bare_name); if (!rs) { GLSTATE_DISABLE_ALPHATEST GLSTATE_ENABLE_BLEND GL_TexEnv( GL_MODULATE ); qglColor4f(1,1,1,DIV254BY255); VA_SetElem4(col_array[0], 1,1,1,1); VA_SetElem4(col_array[1], 1,1,1,1); VA_SetElem4(col_array[2], 1,1,1,1); VA_SetElem4(col_array[3], 1,1,1,1); GL_Bind (gl->texnum); VA_SetElem2(tex_array[0],gl->sl, gl->tl); VA_SetElem2(tex_array[1],gl->sh, gl->tl); VA_SetElem2(tex_array[2],gl->sh, gl->th); VA_SetElem2(tex_array[3],gl->sl, gl->th); qglDrawArrays (GL_QUADS, 0, 4); } else { RS_ReadyScript(rs); stage=rs->stage; while (stage) { float red = 1, green = 1, blue = 1; if (stage->blendfunc.blend) { GLSTATE_ENABLE_BLEND GL_BlendFunction(stage->blendfunc.source,stage->blendfunc.dest); } else { GLSTATE_DISABLE_BLEND } alpha=1.0f; if (stage->alphashift.min || stage->alphashift.speed) { if (!stage->alphashift.speed && stage->alphashift.min > 0) { alpha=stage->alphashift.min; } else if (stage->alphashift.speed) { alpha=sin(rs_realtime * stage->alphashift.speed); alpha=(alpha+1)*0.5f; if (alpha > stage->alphashift.max) alpha=stage->alphashift.max; if (alpha < stage->alphashift.min) alpha=stage->alphashift.min; } if (alpha > 1) alpha=1; if (alpha < 0) alpha=0; } if (stage->alphamask) { GLSTATE_ENABLE_ALPHATEST } else { GLSTATE_DISABLE_ALPHATEST } if (stage->colormap.enabled) { red = stage->colormap.red/255.0; green = stage->colormap.green/255.0; blue = stage->colormap.blue/255.0; } qglColor4f(red,green,blue,alpha); VA_SetElem4(col_array[0], red,green,blue, alpha); VA_SetElem4(col_array[1], red,green,blue, alpha); VA_SetElem4(col_array[2], red,green,blue, alpha); VA_SetElem4(col_array[3], red,green,blue, alpha); if (stage->colormap.enabled) qglDisable (GL_TEXTURE_2D); else if (stage->anim_count) GL_Bind(RS_Animate(stage)); else GL_Bind (stage->texture->texnum); s = 0; t = 1; RS_SetTexcoords2D (stage, &s, &t); VA_SetElem2(tex_array[3],s, t); s = 0; t = 0; RS_SetTexcoords2D (stage, &s, &t); VA_SetElem2(tex_array[0],s, t); s = 1; t = 0; RS_SetTexcoords2D (stage, &s, &t); VA_SetElem2(tex_array[1],s, t); s = 1; t = 1; RS_SetTexcoords2D (stage, &s, &t); VA_SetElem2(tex_array[2],s, t); qglDrawArrays(GL_QUADS,0,4); if (stage->colormap.enabled) qglEnable (GL_TEXTURE_2D); stage=stage->next; } qglColor4f(1,1,1,1); GLSTATE_ENABLE_ALPHATEST GLSTATE_DISABLE_BLEND GL_BlendFunction (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } float CalcFov (float fov_x, float width, float height) { float a; float x; if (fov_x < 1 || fov_x > 179) ri.Sys_Error (ERR_DROP, "Bad fov: %f", fov_x); x = width/tan(fov_x/360*M_PI); a = atan (height/x)*360/M_PI; return a; } struct model_s *R_RegisterModel (char *name); void *RS_AnimateSkin (rs_stage_t *stage); qboolean Draw_Shader_Model (image_t *gl, int x, int y, int w, int h) { refdef_t refdef; char scratch[MAX_QPATH]; int maxframe = 29; entity_t entity[64], *ent; float alpha; rscript_t *rs = NULL; rs_stage_t *stage; if (r_shaders->value) rs=RS_FindScript(gl->bare_name); if (!rs) return false; if (!rs->model) return false; //dont draw if x or y are off the screen if (x > vid.width || y > vid.height ||x<0 || y<0) return false; RS_ReadyScript(rs); stage=rs->stage; memset( &refdef, 0, sizeof( refdef ) ); refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; refdef.fov_x = 50; refdef.fov_y = CalcFov( refdef.fov_x, refdef.width, refdef.height ); refdef.time = rs_realtime; refdef.num_entities = 0; refdef.entities = &entity[0]; while (stage) { memset( &entity[refdef.num_entities], 0, sizeof( entity[refdef.num_entities] ) ); ent = &entity[refdef.num_entities]; ent->model = R_RegisterModel( stage->model ); if (stage->anim_count) ent->skin = RS_AnimateSkin (stage); else ent->skin = stage->texture; alpha=1.0f; if (stage->alphashift.min || stage->alphashift.speed) { if (!stage->alphashift.speed && stage->alphashift.min > 0) { alpha=stage->alphashift.min; } else if (stage->alphashift.speed) { alpha=sin(rs_realtime * stage->alphashift.speed); alpha=(alpha+1)*0.5f; if (alpha > stage->alphashift.max) alpha=stage->alphashift.max; if (alpha < stage->alphashift.min) alpha=stage->alphashift.min; } } ent->alpha = alpha; ent->flags = RF_FULLBRIGHT|RF_DEPTHHACK; VectorCopy( stage->origin, ent->origin ); VectorCopy( ent->origin, ent->oldorigin ); ent->angles[0] = anglemod( stage->angle[0] ); ent->angles[1] = anglemod( stage->angle[1] + stage->rot_speed * rs_realtime); ent->angles[2] = anglemod( stage->angle[2] ); if (stage->frames.enabled) { int framecount, framenum, frametime; float backlerp, realtime = rs_realtime * stage->frames.speed * 10.0; frametime = realtime; backlerp = 1 - (realtime - frametime); if (stage->frames.start == stage->frames.end) { ent->frame = stage->frames.start; ent->oldframe = stage->frames.end; ent->backlerp = 0.0; } else if (stage->frames.start < stage->frames.end) //frames positive { framecount = stage->frames.end - stage->frames.start + 1; framenum = stage->frames.start + frametime % framecount; ent->frame = framenum; ent->oldframe = (framenum == stage->frames.start)? stage->frames.end: framenum-1; ent->backlerp = backlerp; } else //frames negative { framecount = stage->frames.start - stage->frames.end + 1; framenum = stage->frames.start - frametime % framecount; ent->frame = framenum; ent->oldframe = (framenum == stage->frames.start)? stage->frames.end: framenum+1; ent->backlerp = backlerp; } } else { ent->frame = 0; ent->oldframe = 0; ent->backlerp = 0.0; } // scale ent->scale = 1; if (stage->scale.scaleX) { switch (stage->scale.typeX) { case 0: // static ent->scale *= stage->scale.scaleX; break; case 1: // sine ent->scale *= stage->scale.scaleX*sin(rs_realtime*0.05); break; case 2: // cosine ent->scale *= stage->scale.scaleX*cos(rs_realtime*0.05); break; } } refdef.num_entities++; stage = stage->next; } refdef.areabits = 0; refdef.lightstyles = 0; refdef.rdflags = RDF_NOWORLDMODEL; R_RenderFrame( &refdef ); return true; } /* ============= Draw_StretchPic ============= */ void Draw_StretchPic (int x, int y, int w, int h, char *pic) { image_t *gl; gl = Draw_FindPic (pic); if (!gl) { ri.Con_Printf (PRINT_ALL, "Can't find pic: %s\n", pic); return; } if (Draw_Shader_Model (gl, x, y, w, h)) return; if (scrap_dirty) Scrap_Upload (); VA_SetElem2(vert_array[0],x, y); VA_SetElem2(vert_array[1],x+w, y); VA_SetElem2(vert_array[2],x+w, y+h); VA_SetElem2(vert_array[3],x, y+h); Draw_ShaderPic(gl); } /* ============= Draw_ScaledPic ============= */ void Draw_ScaledPic (int x, int y, float scale, char *pic) { int w, h; float xoff, yoff; image_t *gl; gl = Draw_FindPic (pic); if (!gl) { ri.Con_Printf (PRINT_ALL, "Can't find pic: %s\n", pic); return; } w = gl->width; h = gl->height; ShaderResizePic( gl, &w, &h); xoff = (w*scale-w); yoff = (h*scale-h); if (Draw_Shader_Model (gl, x, y, w+xoff, h+yoff)) return; if (scrap_dirty) Scrap_Upload (); VA_SetElem2(vert_array[0],x, y); VA_SetElem2(vert_array[1],x+w+xoff, y); VA_SetElem2(vert_array[2],x+w+xoff, y+h+yoff); VA_SetElem2(vert_array[3],x, y+h+yoff); Draw_ShaderPic(gl); } /* ============= Draw_Pic ============= */ void Draw_Pic (int x, int y, char *pic) { image_t *gl; int w, h; gl = Draw_FindPic (pic); if (!gl) { ri.Con_Printf (PRINT_ALL, "Can't find pic: %s\n", pic); return; } w = gl->width; h = gl->height; ShaderResizePic( gl, &w, &h); if (Draw_Shader_Model (gl, x, y, w, h)) return; if (scrap_dirty) Scrap_Upload (); VA_SetElem2(vert_array[0],x, y); VA_SetElem2(vert_array[1],x+w, y); VA_SetElem2(vert_array[2],x+w, y+h); VA_SetElem2(vert_array[3],x, y+h); Draw_ShaderPic(gl); } /* ============= Draw_TileClear This repeats a 64*64 tile graphic to fill the screen around a sized down refresh window. ============= */ void Draw_TileClear (int x, int y, int w, int h, char *pic) { image_t *image; image = Draw_FindPic (pic); if (!image) { ri.Con_Printf (PRINT_ALL, "Can't find pic: %s\n", pic); return; } 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 (); } /* ============= Draw_Fill Fills a box of pixels with a single color ============= */ void Draw_Fill (int x, int y, int w, int h, int c) { union { unsigned c; byte v[4]; } color; if ( (unsigned)c > 255) ri.Sys_Error (ERR_FATAL, "Draw_Fill: bad color"); qglDisable (GL_TEXTURE_2D); color.c = d_8to24table[c]; qglColor3f (color.v[0]*DIV255, color.v[1]*DIV255, color.v[2]*DIV255); 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); } //============================================================================= /* ================ Draw_FadeBox ================ */ void Draw_FadeBox (int x, int y, int w, int h, float r, float g, float b, float alpha) { GLSTATE_DISABLE_ALPHATEST GL_TexEnv( GL_MODULATE ); GLSTATE_ENABLE_BLEND qglDisable (GL_TEXTURE_2D); qglColor4f (r, g, b, alpha); qglBegin (GL_QUADS); { qglVertex2f (x,y); qglVertex2f (w+x, y); qglVertex2f (w+x, h+y); qglVertex2f (x, h+y); } qglEnd (); qglColor4f (1,1,1,1); qglEnable (GL_TEXTURE_2D); GLSTATE_DISABLE_BLEND GL_TexEnv( GL_REPLACE ); GLSTATE_ENABLE_ALPHATEST } //==================================================================== /* ============= Draw_StretchRaw ============= */ extern unsigned r_rawpalette[256]; void Draw_StretchRaw (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; GL_Bind (0); if (rows<=256) { hscale = 1; trows = rows; } else { hscale = rows*DIV256; 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*DIV256; 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*DIV256; 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); 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 (); }