/* Copyright (C) 1996-1997 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. */ // gl_draw.c -- this is the only file outside the refresh that touches the vid buffer #include "quakedef.h" cvar_t gl_picmip = {"gl_picmip", "0"}; cvar_t gl_picmip_all = {"gl_picmip_all", "0"}; cvar_t gl_conalpha = {"gl_conalpha", "0.8"}; // by joe cvar_t gl_max_size = {"gl_max_size", "1024", false, false, OnChange_gl_max_size}; cvar_t gl_texturemode = {"gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", false, false, OnChange_gl_texturemode}; cvar_t gl_crosshairimage = {"crosshairimage", "", false, false, OnChange_gl_crosshairimage}; cvar_t gl_smoothfont = {"gl_smoothfont", "1", false, false, OnChange_gl_smoothfont}; cvar_t gl_crosshairalpha = {"crosshairalpha", "1"}; static qboolean no24bit; byte *draw_chars; // 8*8 graphic characters mpic_t *draw_disc; mpic_t *draw_backtile; int texture_extension_number = 1; int translate_texture; int char_texture; mpic_t crosshairpic; static qboolean crosshairimage_loaded = false; mpic_t conback_data; mpic_t *conback = &conback_data; int gl_max_size_default; int gl_lightmap_format = 3, gl_solid_format = 3, gl_alpha_format = 4; static int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; static int gl_filter_max = GL_LINEAR; #define NUMCROSSHAIRS 5 static int crosshairtextures[NUMCROSSHAIRS]; static byte crosshairdata[NUMCROSSHAIRS][64] = { {0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, {0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}, {0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} }; typedef struct { char identifier[MAX_QPATH]; char *pathname; int texnum; int width, height; int scaled_width, scaled_height; int texmode; int bpp; unsigned crc; } gltexture_t; gltexture_t gltextures[MAX_GLTEXTURES]; int numgltextures; int currenttexture = -1; // to avoid unnecessary texture sets void GL_Bind (int texnum) { if (currenttexture == texnum) return; currenttexture = texnum; glBindTexture (GL_TEXTURE_2D, texnum); } /* ============================================================================= scrap allocation Allocate all the little status bar objects into a single texture to crutch up stupid hardware / drivers ============================================================================= */ // some cards have low quality of alpha pics, so load the pics // without transparent pixels into a different scrap block. // scrap 0 is solid pics, 1 is transparent #define MAX_SCRAPS 2 #define BLOCK_WIDTH 256 #define BLOCK_HEIGHT 256 int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT*4]; int scrap_dirty = 0; // bit mask int scrap_texnum; int scrap_uploads; // returns false if allocation failed static qboolean Scrap_AllocBlock (int scrapnum, int w, int h, int *x, int *y) { int i, j, best, best2; best = BLOCK_HEIGHT; for (i=0 ; i < BLOCK_WIDTH - w ; i++) { best2 = 0; for (j=0 ; j= best) break; if (scrap_allocated[scrapnum][i+j] > best2) best2 = scrap_allocated[scrapnum][i+j]; } if (j == w) { // this is a valid spot *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) return false; for (i=0 ; itexnum, &pic_24bit->texnum, sizeof(mpic_t) - 8); return pic; } // load little ones into the scrap if (p->width < 64 && p->height < 64) { int x, y, i, j, k, texnum; texnum = memchr (p->data, 255, p->width*p->height) != NULL; if (!Scrap_AllocBlock (texnum, p->width, p->height, &x, &y)) { GL_LoadPicTexture (name, pic, p->data); return pic; } k = 0; for (i=0 ; iheight ; i++) for (j=0 ; jwidth ; j++, k++) scrap_texels[texnum][(y+i)*BLOCK_WIDTH+x+j] = p->data[k]; texnum += scrap_texnum; pic->texnum = texnum; pic->sl = (x + 0.01) / (float)BLOCK_WIDTH; pic->sh = (x + p->width - 0.01) / (float)BLOCK_WIDTH; pic->tl = (y + 0.01) / (float)BLOCK_WIDTH; pic->th = (y + p->height - 0.01) / (float)BLOCK_WIDTH; pic_count++; pic_texels += p->width * p->height; } else { GL_LoadPicTexture (name, pic, p->data); } return pic; } /* ================ Draw_CachePic ================ */ mpic_t *Draw_CachePic (char *path) { cachepic_t *pic; int i; qpic_t *dat; mpic_t *pic_24bit; for (pic=cachepics, i=0 ; iname)) { return &pic->pic; } } if (numcachepics == MAX_CACHED_PICS) { Sys_Error ("numcachepics == MAX_CACHED_PICS"); } numcachepics++; strcpy (pic->name, path); // load the pic from disk if (!(dat = (qpic_t *)COM_LoadTempFile(path))) { Sys_Error ("Draw_CachePic: failed to load %s", path); } SwapPic (dat); // HACK HACK HACK --- we need to keep the bytes for // the translatable player picture just for the menu // configuration dialog if (!strcmp(path, "gfx/menuplyr.lmp")) memcpy (menuplyr_pixels, dat->data, dat->width * dat->height); pic->pic.width = dat->width; pic->pic.height = dat->height; if ((pic_24bit = GL_LoadPicImage(path, NULL, 0, 0, TEX_ALPHA))) { memcpy (&pic->pic.texnum, &pic_24bit->texnum, sizeof(mpic_t) - 8); } else { GL_LoadPicTexture (path, &pic->pic, dat->data); } return &pic->pic; } void Draw_CharToConback (int num, byte *dest) { int row, col, drawline, x; byte *source; row = num >> 4; col = num & 15; source = draw_chars + (row<<10) + (col<<3); drawline = 8; while (drawline--) { for (x=0 ; x<8 ; x++) { if (source[x] != 255) { dest[x] = 0x60 + source[x]; } } source += 128; dest += 320; } } void Draw_LoadConback (void) { qpic_t *cb; int start; mpic_t *pic_24bit; start = Hunk_LowMark (); if (!(cb = (qpic_t *)COM_LoadHunkFile("gfx/conback.lmp"))) { Sys_Error ("Couldn't load gfx/conback.lmp"); } SwapPic (cb); if (cb->width != 320 || cb->height != 200) { Con_Printf ("Draw_LoadConback: conback.lmp size is not 320x200"); } // hack the version number directly into the pic if ((pic_24bit = GL_LoadPicImage("gfx/conback", "conback", 0, 0, 0))) { memcpy (&conback->texnum, &pic_24bit->texnum, sizeof(mpic_t) - 8); } else { conback->width = cb->width; conback->height = cb->height; GL_LoadPicTexture ("conback", conback, cb->data); } conback->width = vid.conwidth; conback->height = vid.conheight; // free loaded console Hunk_FreeToLowMark (start); } qboolean OnChange_gl_max_size (cvar_t *var, char *string) { int i; float newval = Q_atof(string); if (newval > gl_max_size_default) { Con_Printf ("Your hardware doesn't support texture sizes bigger than %dx%d\n", gl_max_size_default, gl_max_size_default); return true; } for (i = 1 ; i < newval ; i <<= 1); { if (i != newval) { Con_Printf ("Valid values for %s are powers of 2 only\n", var->name); return true; } } return false; } typedef struct { char *name; int minimize, maximize; } glmode_t; glmode_t modes[] = { {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} }; #define GLMODE_NUMODES (sizeof(modes) / sizeof(glmode_t)) qboolean OnChange_gl_texturemode (cvar_t *var, char *string) { int i; gltexture_t *glt; for (i=0 ; itexmode & TEX_MIPMAP) { GL_Bind (glt->texnum); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } } return false; } void Draw_LoadCharset (void) { int i; char buf[128*256]; char *src, *dest; draw_chars = W_GetLumpName ("conchars"); for (i=0 ; i<256*64 ; i++) if (draw_chars[i] == 0) draw_chars[i] = 255; // proper transparent color if (!(char_texture = GL_LoadCharsetImage("charset", "pic:charset"))) { // Convert the 128*128 conchars texture to 128*256 leaving empty space between // rows so that chars don't stumble on each other because of texture smoothing. // This hack costs us 64K of GL texture memory memset (buf, 255, sizeof(buf)); src = (char *)draw_chars; dest = buf; for (i=0 ; i<16 ; i++) { memcpy (dest, src, 128*8); src += 128*8; dest += 128*8*2; } char_texture = GL_LoadTexture ("pic:charset", 128, 256, (byte *)buf, TEX_ALPHA, 1); } // smooth fonts if (gl_smoothfont.value) { glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } } qboolean OnChange_gl_smoothfont (cvar_t *var, char *string) { float newval; newval = Q_atof (string); if (!newval == !gl_smoothfont.value || !char_texture) return false; GL_Bind (char_texture); if (newval) { glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } return false; } qboolean OnChange_gl_crosshairimage (cvar_t *var, char *string) { mpic_t *pic; if (!string[0]) { crosshairimage_loaded = false; return false; } if (!(pic = GL_LoadPicImage(va("crosshairs/%s", string), "crosshair", 0, 0, TEX_ALPHA))) { crosshairimage_loaded = false; Con_Printf ("Couldn't load image %s\n", string); return false; } glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); crosshairpic = *pic; crosshairimage_loaded = true; return false; } /* =============== Draw_Init =============== */ void Draw_Init (void) { int i; Cvar_RegisterVariable (&gl_max_size); Cvar_RegisterVariable (&gl_picmip); Cvar_RegisterVariable (&gl_picmip_all); Cvar_RegisterVariable (&gl_conalpha); Cvar_RegisterVariable (&gl_smoothfont); Cvar_RegisterVariable (&gl_crosshairalpha); Cvar_RegisterVariable (&gl_crosshairimage); Cvar_RegisterVariable (&gl_texturemode); glGetIntegerv (GL_MAX_TEXTURE_SIZE, &gl_max_size_default); Cvar_SetValue ("gl_max_size", gl_max_size_default); no24bit = COM_CheckParm("-no24bit") ? true : false; // 3dfx can only handle 256 wide textures if (!Q_strncasecmp((char *)gl_renderer, "3dfx", 4) || strstr((char *)gl_renderer, "Glide")) Cvar_Set ("gl_max_size", "256"); // load the console background and the charset // by hand, because we need to write the version // string into the background before turning // it into a texture Draw_LoadCharset (); Draw_LoadConback (); // save a texture slot for translated picture translate_texture = texture_extension_number++; // save slots for scraps scrap_texnum = texture_extension_number; texture_extension_number += MAX_SCRAPS; // Load the crosshair pics for (i=0 ; i> 4; col = num & 15; frow = row * 0.0625; fcol = col * 0.0625; GL_Bind (char_texture); glBegin (GL_QUADS); glTexCoord2f (fcol, frow); glVertex2f (x, y); glTexCoord2f (fcol + 0.0625, frow); glVertex2f (x+8, y); glTexCoord2f (fcol + 0.0625, frow + 0.03125); glVertex2f (x+8, y+8); glTexCoord2f (fcol, frow + 0.03125); glVertex2f (x, y+8); glEnd (); } /* ================ Draw_String ================ */ void Draw_String (int x, int y, char *str) { float frow, fcol; int num; if (y <= -8) return; // totally off screen if (!*str) return; GL_Bind (char_texture); glBegin (GL_QUADS); while (*str) // stop rendering when out of characters { if ((num = *str++) != 32) // skip spaces { frow = (float)(num >> 4)*0.0625; fcol = (float)(num & 15)*0.0625; glTexCoord2f (fcol, frow); glVertex2f (x, y); glTexCoord2f (fcol + 0.0625, frow); glVertex2f (x+8, y); glTexCoord2f (fcol + 0.0625, frow + 0.03125); glVertex2f (x+8, y+8); glTexCoord2f (fcol, frow + 0.03125); glVertex2f (x, y+8); } x += 8; } glEnd (); } /* ================ Draw_Alt_String ================ */ void Draw_Alt_String (int x, int y, char *str) { float frow, fcol; int num; if (y <= -8) return; // totally off screen if (!*str) return; GL_Bind (char_texture); glBegin (GL_QUADS); while (*str) // stop rendering when out of characters { if ((num = *str++|0x80) != (32|0x80)) // skip spaces { frow = (float)(num >> 4)*0.0625; fcol = (float)(num & 15)*0.0625; glTexCoord2f (fcol, frow); glVertex2f (x, y); glTexCoord2f (fcol + 0.0625, frow); glVertex2f (x+8, y); glTexCoord2f (fcol + 0.0625, frow + 0.03125); glVertex2f (x+8, y+8); glTexCoord2f (fcol, frow + 0.03125); glVertex2f (x, y+8); } x += 8; } glEnd (); } byte *Draw_StringToRGB (char *s) { byte *col; static byte rgb[4]; Cmd_TokenizeString (s); if (Cmd_Argc() == 3) { rgb[0] = (byte)Q_atoi(Cmd_Argv(0)); rgb[1] = (byte)Q_atoi(Cmd_Argv(1)); rgb[2] = (byte)Q_atoi(Cmd_Argv(2)); } else { col = (byte *)&d_8to24table[(byte)Q_atoi(s)]; rgb[0] = col[0]; rgb[1] = col[1]; rgb[2] = col[2]; } rgb[3] = 255; return rgb; } /* ================ Draw_Crosshair -- joe, from FuhQuake ================ */ void Draw_Crosshair (void) { float x, y, ofs1, ofs2, sh, th, sl, tl; byte *col; extern vrect_t scr_vrect; if ((crosshair.value >= 2 && crosshair.value <= NUMCROSSHAIRS + 1) || crosshairimage_loaded) { x = scr_vrect.x + scr_vrect.width / 2 + cl_crossx.value; y = scr_vrect.y + scr_vrect.height / 2 + cl_crossy.value; if (!gl_crosshairalpha.value) return; glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); col = Draw_StringToRGB (crosshaircolor.string); if (gl_crosshairalpha.value) { glDisable (GL_ALPHA_TEST); glEnable (GL_BLEND); col[3] = bound(0, gl_crosshairalpha.value, 1) * 255; glColor4ubv (col); } else { glColor3ubv (col); } if (crosshairimage_loaded) { GL_Bind (crosshairpic.texnum); ofs1 = 4 - 4.0 / crosshairpic.width; ofs2 = 4 + 4.0 / crosshairpic.width; sh = crosshairpic.sh; sl = crosshairpic.sl; th = crosshairpic.th; tl = crosshairpic.tl; } else { GL_Bind (crosshairtextures[(int)crosshair.value-2]); ofs1 = 3.5; ofs2 = 4.5; tl = sl = 0; sh = th = 1; } ofs1 *= (vid.width / 320) * bound(0, crosshairsize.value, 20); ofs2 *= (vid.width / 320) * bound(0, crosshairsize.value, 20); glBegin (GL_QUADS); glTexCoord2f (sl, tl); glVertex2f (x - ofs1, y - ofs1); glTexCoord2f (sh, tl); glVertex2f (x + ofs2, y - ofs1); glTexCoord2f (sh, th); glVertex2f (x + ofs2, y + ofs2); glTexCoord2f (sl, th); glVertex2f (x - ofs1, y + ofs2); glEnd (); if (gl_crosshairalpha.value) { glDisable (GL_BLEND); glEnable (GL_ALPHA_TEST); } glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor3f (1, 1, 1); } else if (crosshair.value) { Draw_Character (scr_vrect.x + scr_vrect.width / 2 - 4 + cl_crossx.value, scr_vrect.y + scr_vrect.height / 2 - 4 + cl_crossy.value, '+'); } } /* ================ Draw_TextBox ================ */ void Draw_TextBox (int x, int y, int width, int lines) { mpic_t *p; int cx, cy, n; // draw left side cx = x; cy = y; p = Draw_CachePic ("gfx/box_tl.lmp"); Draw_TransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_ml.lmp"); for (n = 0; n < lines; n++) { cy += 8; Draw_TransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_bl.lmp"); Draw_TransPic (cx, cy+8, p); // draw middle cx += 8; while (width > 0) { cy = y; p = Draw_CachePic ("gfx/box_tm.lmp"); Draw_TransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_mm.lmp"); for (n = 0; n < lines; n++) { cy += 8; if (n == 1) p = Draw_CachePic ("gfx/box_mm2.lmp"); Draw_TransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_bm.lmp"); Draw_TransPic (cx, cy+8, p); width -= 2; cx += 16; } // draw right side cy = y; p = Draw_CachePic ("gfx/box_tr.lmp"); Draw_TransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_mr.lmp"); for (n = 0; n < lines; n++) { cy += 8; Draw_TransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_br.lmp"); Draw_TransPic (cx, cy+8, p); } /* ================ Draw_DebugChar Draws a single character directly to the upper right corner of the screen. This is for debugging lockups by drawing different chars in different parts of the code. ================ */ void Draw_DebugChar (char num) { } /* ============= Draw_AlphaPic ============= */ void Draw_AlphaPic (int x, int y, mpic_t *pic, float alpha) { if (scrap_dirty) Scrap_Upload (); glDisable(GL_ALPHA_TEST); glEnable (GL_BLEND); glCullFace (GL_FRONT); glColor4f (1, 1, 1, alpha); GL_Bind (pic->texnum); glBegin (GL_QUADS); glTexCoord2f (pic->sl, pic->tl); glVertex2f (x, y); glTexCoord2f (pic->sh, pic->tl); glVertex2f (x+pic->width, y); glTexCoord2f (pic->sh, pic->th); glVertex2f (x+pic->width, y+pic->height); glTexCoord2f (pic->sl, pic->th); glVertex2f (x, y+pic->height); glEnd (); glColor3f (1, 1, 1); glEnable(GL_ALPHA_TEST); glDisable (GL_BLEND); } /* ============= Draw_Pic ============= */ void Draw_Pic (int x, int y, mpic_t *pic) { if (scrap_dirty) Scrap_Upload (); GL_Bind (pic->texnum); glBegin (GL_QUADS); glTexCoord2f (pic->sl, pic->tl); glVertex2f (x, y); glTexCoord2f (pic->sh, pic->tl); glVertex2f (x+pic->width, y); glTexCoord2f (pic->sh, pic->th); glVertex2f (x+pic->width, y+pic->height); glTexCoord2f (pic->sl, pic->th); glVertex2f (x, y+pic->height); glEnd (); } void Draw_SubPic (int x, int y, mpic_t *pic, int srcx, int srcy, int width, int height) { float newsl, newtl, newsh, newth, oldglwidth, oldglheight; if (scrap_dirty) Scrap_Upload (); oldglwidth = pic->sh - pic->sl; oldglheight = pic->th - pic->tl; newsl = pic->sl + (srcx * oldglwidth) / pic->width; newsh = newsl + (width * oldglwidth) / pic->width; newtl = pic->tl + (srcy * oldglheight) / pic->height; newth = newtl + (height * oldglheight) / pic->height; GL_Bind (pic->texnum); glBegin (GL_QUADS); glTexCoord2f (newsl, newtl); glVertex2f (x, y); glTexCoord2f (newsh, newtl); glVertex2f (x+width, y); glTexCoord2f (newsh, newth); glVertex2f (x+width, y+height); glTexCoord2f (newsl, newth); glVertex2f (x, y+height); glEnd (); } /* ============= Draw_TransPic ============= */ void Draw_TransPic (int x, int y, mpic_t *pic) { if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || (unsigned)(y + pic->height) > vid.height) Sys_Error ("Draw_TransPic: bad coordinates"); Draw_Pic (x, y, pic); } /* ============= Draw_TransPicTranslate Only used for the player color selection menu ============= */ void Draw_TransPicTranslate (int x, int y, mpic_t *pic, byte *translation) { int v, u, c, p; unsigned trans[64*64], *dest; byte *src; GL_Bind (translate_texture); c = pic->width * pic->height; dest = trans; for (v=0 ; v<64 ; v++, dest += 64) { src = &menuplyr_pixels[((v*pic->height)>>6)*pic->width]; for (u=0 ; u<64 ; u++) { p = src[(u*pic->width)>>6]; dest[u] = (p == 255) ? p : d_8to24table[translation[p]]; } } glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBegin (GL_QUADS); glTexCoord2f (0, 0); glVertex2f (x, y); glTexCoord2f (1, 0); glVertex2f (x+pic->width, y); glTexCoord2f (1, 1); glVertex2f (x+pic->width, y+pic->height); glTexCoord2f (0, 1); glVertex2f (x, y+pic->height); glEnd (); } /* ================ Draw_ConsoleBackground ================ */ void Draw_ConsoleBackground (int lines) { char ver[80]; if (!gl_conalpha.value && lines < vid.height) goto end; if (lines == vid.height) Draw_Pic (0, lines - vid.height, conback); else Draw_AlphaPic (0, lines - vid.height, conback, bound(0, gl_conalpha.value, 1)); end: sprintf (ver, "DemonQuake %s", DEMONQUAKE_VERSION); Draw_Alt_String (vid.conwidth - strlen(ver) * 8 - 8, lines - 10, ver); } /* ============= 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) { GL_Bind (draw_backtile->texnum); glBegin (GL_QUADS); glTexCoord2f (x/64.0, y/64.0); glVertex2f (x, y); glTexCoord2f ((x+w)/64.0, y/64.0); glVertex2f (x+w, y); glTexCoord2f ((x+w)/64.0, (y+h)/64.0); glVertex2f (x+w, y+h); glTexCoord2f (x/64.0, (y+h)/64.0); glVertex2f (x, y+h); glEnd (); } /* ============= Draw_Fill Fills a box of pixels with a single color ============= */ void Draw_Fill (int x, int y, int w, int h, int c) { glDisable (GL_TEXTURE_2D); glColor3f (host_basepal[c*3] / 255.0, host_basepal[c*3+1] / 255.0, host_basepal[c*3+2] / 255.0); glBegin (GL_QUADS); glVertex2f (x, y); glVertex2f (x+w, y); glVertex2f (x+w, y+h); glVertex2f (x, y+h); glEnd (); glColor3f (1, 1, 1); glEnable (GL_TEXTURE_2D); } //============================================================================= /* ================ Draw_FadeScreen ================ */ void Draw_FadeScreen (void) { glEnable (GL_BLEND); glDisable (GL_TEXTURE_2D); glDisable(GL_ALPHA_TEST); glColor4f (0, 0, 0, 0.4); glBegin (GL_QUADS); glVertex2f (0,0); glVertex2f (vid.width, 0); glVertex2f (vid.width, vid.height); glVertex2f (0, vid.height); glEnd (); glColor4f (1,1,1,1); glEnable(GL_ALPHA_TEST); glEnable (GL_TEXTURE_2D); glDisable (GL_BLEND); Sbar_Changed(); } //============================================================================= /* ================ Draw_BeginDisc Draws the little blue disc in the corner of the screen. Call before beginning any disc IO. ================ */ void Draw_BeginDisc (void) { if (!draw_disc) return; glDrawBuffer (GL_FRONT); Draw_Pic (vid.width - 24, 0, draw_disc); glDrawBuffer (GL_BACK); } /* ================ Draw_EndDisc Erases the disc icon. Call after completing any disc IO ================ */ void Draw_EndDisc (void) { } /* ================ GL_Set2D Setup as if the screen was 320*200 ================ */ void GL_Set2D (void) { glViewport (glx, gly, glwidth, glheight); 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); glColor3f (1, 1, 1); } //==================================================================== /* =============== ColorDiff Get the difference between 2 colours. This is accomplished by first converting to greyscale, then comparing and subtracting. There are better ways of doing this, but greyscaling gives the most appropriate visual results for Quake textures. The alpha component is generally ignored. =============== */ static unsigned int ColorDiff (unsigned int p1, unsigned int p2) { byte *rgba1 = (byte *) &p1; byte *rgba2 = (byte *) &p2; int avg1; int avg2; avg1 = ((int) rgba1[0] + (int) rgba1[1] + (int) rgba1[2]) / 3; avg2 = ((int) rgba2[0] + (int) rgba2[1] + (int) rgba2[2]) / 3; if (avg1 > avg2) { return avg1 - avg2; } else { return avg2 - avg1; } } /* =============== ColorAvg Get the average of 2 colours, including alpha component =============== */ static unsigned int ColorAvg (unsigned int p1, unsigned int p2) { byte *rgba1 = (byte *) &p1; byte *rgba2 = (byte *) &p2; byte *rgbaavg; unsigned int avg; rgbaavg = (byte *) &avg; rgbaavg[0] = (byte) (((int) rgba1[0] + (int) rgba2[0]) / 2); rgbaavg[1] = (byte) (((int) rgba1[1] + (int) rgba2[1]) / 2); rgbaavg[2] = (byte) (((int) rgba1[2] + (int) rgba2[2]) / 2); rgbaavg[3] = (byte) (((int) rgba1[3] + (int) rgba2[3]) / 2); return avg; } /* =============== ChooseLerp Choose an interpolation function based on the lowest difference (i.e. which colour is nearest to the original pixel): "colormin" is a confusing name, really... =============== */ static unsigned int ChooseLerp (unsigned int me, unsigned int row, unsigned int col, unsigned int diag) { unsigned int d1; unsigned int d2; unsigned int d3; unsigned int d4; unsigned int colormin; d1 = ColorDiff (me, row); d2 = ColorDiff (me, col); d3 = ColorDiff (me, diag); d4 = ColorDiff (row, col); colormin = d1; if (colormin > d2) colormin = d2; if (colormin > d3) colormin = d3; if (colormin > d4) colormin = d4; if (colormin == d1) { return ColorAvg (me, row); } else if (colormin == d2) { return ColorAvg (me, col); } else if (colormin == d3) { return ColorAvg (me, diag); } else { return ColorAvg (me, ColorAvg (row, col)); } } /* ================================= InterpolatePixels "Nice" directional average scaling algorithm from http://www.compuphase.com/graphic/scale2.htm the paper at the url above discusses use of bit interleaving to determine colour differences. i've abandoned that as it gives undesirable results with quake textures (likely the fault of quake textures rather than anything in the paper). instead i'm using option 2: converting to greyscale. the speed implications mentioned in this paper don't really apply to quake as the calculations don't occur at run time and the bottleneck is elsewhere (OpenGL texture uploading). ================================= */ __inline void InterpolatePixels (unsigned int *scaled, int w, int h) { int hcount; int wcount; int x; // 2D arrays to make life easier for all of us static unsigned int temp1[MAX_GLTEXTURES][MAX_GLTEXTURES]; // copy of original static unsigned int temp2[MAX_GLTEXTURES][MAX_GLTEXTURES]; // upsampled // row up, col left, row down, col right, same row, same col int pos[6]; // copy the original data into our first 2D array for (hcount = 0, x = 0; hcount < h; hcount++) { for (wcount = 0; wcount < w; wcount++, x++) { temp1[hcount][wcount] = scaled[x]; } } for (hcount = 0; hcount < h; hcount++) { // determine the positions of the 8 surrounding pixels - part one: rows // row up pos[0] = hcount - 1; if (pos[0] < 0) pos[0] += h; // row down pos[2] = hcount + 1; if (pos[2] >= h) pos[2] -= h; // same row pos[4] = hcount; for (wcount = 0; wcount < w; wcount++) { // determine the positions of the 8 surrounding pixels - part two: columns // col left pos[1] = wcount - 1; if (pos[1] < 0) pos[1] += w; // col right pos[3] = wcount + 1; if (pos[3] >= w) pos[3] -= w; // same col pos[5] = wcount; // each pixel in temp1 maps to 4 pixels in temp2 temp2[hcount * 2][wcount * 2] = ChooseLerp (temp1[pos[4]][pos[5]], temp1[pos[0]][pos[5]], temp1[pos[4]][pos[1]], temp1[pos[0]][pos[1]]); temp2[hcount * 2][wcount * 2 + 1] = ChooseLerp (temp1[pos[4]][pos[5]], temp1[pos[0]][pos[5]], temp1[pos[4]][pos[3]], temp1[pos[0]][pos[3]]); temp2[hcount * 2 + 1][wcount * 2] = ChooseLerp (temp1[pos[4]][pos[5]], temp1[pos[2]][pos[5]], temp1[pos[4]][pos[1]], temp1[pos[2]][pos[1]]); temp2[hcount * 2 + 1][wcount * 2 + 1] = ChooseLerp (temp1[pos[4]][pos[5]], temp1[pos[2]][pos[5]], temp1[pos[4]][pos[3]], temp1[pos[2]][pos[3]]); } } // copy temp2 back to scaled for (hcount = 0, x = 0; hcount < h * 2; hcount++, x++) { for (wcount = 0, x = 0; wcount < w * 2; wcount++, x++) { scaled[x] = temp2[hcount][wcount]; } } } /* ================ ResampleTextureLerpLine ================ */ __inline void ResampleTextureLerpLine (byte *in, byte *out, int inwidth, int outwidth) { int j, xi, oldx = 0, f, fstep, endx; fstep = (int) (inwidth * 65536.0f / outwidth); endx = (inwidth - 1); for (j = 0, f = 0; j < outwidth; j++, f += fstep) { xi = (int) f >> 16; if (xi != oldx) { in += (xi - oldx) * 4; oldx = xi; } if (xi < endx) { int lerp = f & 0xFFFF; *out++ = (byte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]); *out++ = (byte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]); *out++ = (byte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]); *out++ = (byte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]); } else { // last pixel of the line has no pixel to lerp to *out++ = in[0]; *out++ = in[1]; *out++ = in[2]; *out++ = in[3]; } } } /* ================ ResampleTexture ================ */ __inline void ResampleTexture (unsigned *indata, int inwidth, int inheight, unsigned *outdata, int outwidth, int outheight) { if (gl_lerpimages.value) { int i, j, yi, oldy, f, fstep, endy = (inheight - 1); byte *inrow, *out, *row1, *row2, *memalloc; out = (byte *) outdata; fstep = (int) (inheight * 65536.0f / outheight); memalloc = Q_Malloc(2 * (outwidth * 4)); row1 = memalloc; row2 = memalloc + (outwidth * 4); inrow = (byte *) indata; oldy = 0; ResampleTextureLerpLine (inrow, row1, inwidth, outwidth); ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth); for (i = 0, f = 0; i < outheight; i++, f += fstep) { yi = f >> 16; if (yi < endy) { int lerp = f & 0xFFFF; if (yi != oldy) { inrow = (byte *)indata + inwidth * 4 * yi; if (yi == oldy + 1) memcpy(row1, row2, outwidth*4); else ResampleTextureLerpLine (inrow, row1, inwidth, outwidth); ResampleTextureLerpLine (inrow + inwidth * 4, row2, inwidth, outwidth); oldy = yi; } for (j = outwidth ; j ; j--) { out[0] = (byte) ((((row2[0] - row1[0]) * lerp) >> 16) + row1[0]); out[1] = (byte) ((((row2[1] - row1[1]) * lerp) >> 16) + row1[1]); out[2] = (byte) ((((row2[2] - row1[2]) * lerp) >> 16) + row1[2]); out[3] = (byte) ((((row2[3] - row1[3]) * lerp) >> 16) + row1[3]); out += 4; row1 += 4; row2 += 4; } row1 -= outwidth * 4; row2 -= outwidth * 4; } else { if (yi != oldy) { inrow = (byte *) indata + inwidth * 4 * yi; if (yi == oldy+1) memcpy(row1, row2, outwidth * 4); else ResampleTextureLerpLine (inrow, row1, inwidth, outwidth); oldy = yi; } memcpy(out, row1, outwidth * 4); } } free(memalloc); } else { int i, j; unsigned int frac, fracstep, *inrow, *out; out = outdata; fracstep = inwidth * 0x10000 / outwidth; for (i = 0; i < outheight; i++) { inrow = (unsigned int *) indata + inwidth * (i * inheight / outheight); frac = fracstep >> 1; for (j = outwidth >> 2 ; j ; j--) { out[0] = inrow[frac >> 16]; frac += fracstep; out[1] = inrow[frac >> 16]; frac += fracstep; out[2] = inrow[frac >> 16]; frac += fracstep; out[3] = inrow[frac >> 16]; frac += fracstep; out += 4; } for (j = outwidth & 3; j; j--) { *out = inrow[frac >> 16]; frac += fracstep; out++; } } } } /* ================ MipMap Operates in place, quartering the size of the texture ================ */ __inline void MipMap (byte *in, int *width, int *height) { int i, j, nextrow; byte *out; nextrow = *width << 2; out = in; if (*width > 1) { *width >>= 1; if (*height > 1) { *height >>= 1; for (i = 0 ; i < *height ; i++, in += nextrow) { for (j = 0 ; j < *width ; j++, out += 4, in += 8) { out[0] = (in[0] + in[4] + in[nextrow+0] + in[nextrow+4]) >> 2; out[1] = (in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2; out[2] = (in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2; out[3] = (in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2; } } } else { for (i = 0 ; i < *height ; i++) { for (j = 0 ; j < *width ; j++, out += 4, in += 8) { out[0] = (in[0] + in[4]) >> 1; out[1] = (in[1] + in[5]) >> 1; out[2] = (in[2] + in[6]) >> 1; out[3] = (in[3] + in[7]) >> 1; } } } } else if (*height > 1) { *height >>= 1; for (i = 0 ; i < *height ; i++, in += nextrow) { for (j = 0 ; j < *width ; j++, out += 4, in += 4) { out[0] = (in[0] + in[nextrow+0]) >> 1; out[1] = (in[1] + in[nextrow+1]) >> 1; out[2] = (in[2] + in[nextrow+2]) >> 1; out[3] = (in[3] + in[nextrow+3]) >> 1; } } } } /* ================ ScaleDimensions ================ */ __inline void ScaleDimensions (int width, int height, int *scaled_width, int *scaled_height, int mode) { int maxsize; for (*scaled_width = 1 ; *scaled_width < width ; *scaled_width *= 2); for (*scaled_height = 1 ; *scaled_height < height ; *scaled_height *= 2); if (mode & TEX_MIPMAP) { *scaled_width >>= (int)gl_picmip.value; *scaled_height >>= (int)gl_picmip.value; } maxsize = (mode & TEX_MIPMAP) ? gl_max_size.value : gl_max_size_default; *scaled_width = bound(1, *scaled_width, maxsize); *scaled_height = bound(1, *scaled_height, maxsize); } /* =============== GL_Upload32 =============== */ void GL_Upload32 (unsigned *data, int width, int height, int mode) { int internal_format, scaled_width, scaled_height, miplevel; unsigned int *scaled; for (scaled_width = 1 ; scaled_width < width ; scaled_width *= 2); for (scaled_height = 1 ; scaled_height < height ; scaled_height *= 2); scaled = Q_Malloc (scaled_width * scaled_height * 4); if (!data) { Host_Error("GL_Upload32: out of memory\n"); } if (width < scaled_width || height < scaled_height) { ResampleTexture (data, width, height, scaled, scaled_width, scaled_height); // only interpolate after resampling ? if (mode & TEX_BLEND) { // allmost newer happens heh Con_DPrintf ("Pixel interpolation is on\n"); // don't interpolate if it's too big if (width < 512 || height < 512) { InterpolatePixels (scaled, scaled_width, scaled_height); } } width = scaled_width; height = scaled_height; } else { memcpy (scaled, data, width * height * 4); } ScaleDimensions (width, height, &scaled_width, &scaled_height, mode); while (width > scaled_width || height > scaled_height) { MipMap ((byte *)scaled, &width, &height); } internal_format = (mode & TEX_ALPHA) ? gl_alpha_format : gl_solid_format; miplevel = 0; glTexImage2D (GL_TEXTURE_2D, 0, internal_format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); if (mode & TEX_MIPMAP) { while (width > 1 || height > 1) { MipMap ((byte *)scaled, &width, &height); miplevel++; glTexImage2D (GL_TEXTURE_2D, miplevel, internal_format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } free (scaled); } /* =============== GL_Upload8 =============== */ void GL_Upload8 (byte *data, int width, int height, int mode) { static unsigned *uploadimage; int i, size, p; unsigned *table; table = (mode & TEX_BRIGHTEN) ? d_8to24table2 : d_8to24table; // get size size = width * height; // allocate dynamic image size uploadimage = Q_Malloc(size * sizeof(unsigned)); if (mode & TEX_FULLBRIGHT) { // this is a fullbright mask, so make all non-fullbright // colors transparent mode |= TEX_ALPHA; for (i=0 ; iidentifier, sizeof(glt->identifier)-1)) { if (width == glt->width && height == glt->height && scaled_width == glt->scaled_width && scaled_height == glt->scaled_height && crc == glt->crc && bpp == glt->bpp && (mode & ~TEX_COMPLAIN) == (glt->texmode & ~TEX_COMPLAIN)) { GL_Bind (gltextures[i].texnum); return gltextures[i].texnum; // texture cached } else { goto GL_LoadTexture_setup; // reload the texture into the same slot } } } } if (numgltextures == MAX_GLTEXTURES) { Sys_Error ("GL_LoadTexture: numgltextures == MAX_GLTEXTURES"); } glt = &gltextures[numgltextures]; numgltextures++; Q_strncpyz (glt->identifier, identifier, sizeof(glt->identifier)); glt->texnum = texture_extension_number; texture_extension_number++; GL_LoadTexture_setup: glt->width = width; glt->height = height; glt->scaled_width = scaled_width; glt->scaled_height = scaled_height; glt->texmode = mode; glt->crc = crc; glt->bpp = bpp; if (glt->pathname) { Z_Free (glt->pathname); glt->pathname = NULL; } if (bpp == 4 && com_netpath[0]) { glt->pathname = CopyString (com_netpath); } GL_Bind (glt->texnum); switch (bpp) { case 1: GL_Upload8 (data, width, height, mode); break; case 4: GL_Upload32 ((void *)data, width, height, mode); break; default: Sys_Error ("GL_LoadTexture: unknown bpp\n"); break; } return glt->texnum; } /* ================ GL_LoadPicTexture ================ */ int GL_LoadPicTexture (char *name, mpic_t *pic, byte *data) { int i, glwidth, glheight; char fullname[MAX_QPATH] = "pic:"; for (glwidth = 1 ; glwidth < pic->width ; glwidth <<= 1) ; for (glheight = 1 ; glheight < pic->height ; glheight <<= 1) ; Q_strncpyz (fullname + 4, name, sizeof(fullname) - 4); if (glwidth == pic->width && glheight == pic->height) { pic->texnum = GL_LoadTexture (fullname, glwidth, glheight, data, TEX_ALPHA, 1); pic->sl = 0; pic->sh = 1; pic->tl = 0; pic->th = 1; } else { byte *src, *dest, *buf; buf = Q_Calloc (glwidth * glheight, 1); src = data; dest = buf; for (i = 0 ; i < pic->height ; i++) { memcpy (dest, src, pic->width); src += pic->width; dest += glwidth; } pic->texnum = GL_LoadTexture ("", glwidth, glheight, buf, TEX_ALPHA, 1); pic->sl = 0; pic->sh = (float)pic->width / glwidth; pic->tl = 0; pic->th = (float)pic->height / glheight; free (buf); } return pic->texnum; } /* ================ GL_FindTexture ================ */ gltexture_t *GL_FindTexture (char *identifier) { int i; for (i=0 ; ipathname && !strcmp(com_netpath, current_texture->pathname)) { ScaleDimensions (current_texture->width, current_texture->height, &scaled_width, &scaled_height, mode); if (current_texture->scaled_width == scaled_width && current_texture->scaled_height == scaled_height) { return true; } } return false; } byte *GL_LoadImagePixels (char* filename, int matchwidth, int matchheight, int mode) { char basename[MAX_QPATH], name[MAX_QPATH]; byte *c, *data; FILE *f; COM_StripExtension (filename, basename); for (c = (byte *)basename ; *c ; c++) { if (*c == '*') { *c = '#'; } } Q_snprintfz (name, sizeof(name), "%s.tga", basename); if (COM_FOpenFile(name, &f, !(hipnotic || rogue)) != -1) { CHECK_TEXTURE_ALREADY_LOADED; if ((data = LoadTGA(f, name, matchwidth, matchheight))) { return data; } } Q_snprintfz (name, sizeof(name), "%s.png", basename); if (COM_FOpenFile(name, &f, !(hipnotic || rogue)) != -1) { CHECK_TEXTURE_ALREADY_LOADED; if ((data = LoadPNG(f, name, matchwidth, matchheight))) { return data; } } if (mode & TEX_COMPLAIN) { if (!no24bit) { Con_Printf ("Couldn't load %s image\n", COM_SkipPath(filename)); } } return NULL; } int GL_LoadTexturePixels (byte *data, char *identifier, int width, int height, int mode) { int i, j, image_size; qboolean gamma; image_size = width * height; gamma = (vid_gamma != 1); if (mode & TEX_LUMA) { gamma = false; } else if (mode & TEX_ALPHA) { mode &= ~TEX_ALPHA; for (j=0 ; j> 24) & 0xFF) < 255) { mode |= TEX_ALPHA; break; } } } if (gamma) { for (i=0 ; itexnum : 0; } else { texnum = GL_LoadTexturePixels (data, identifier, image_width, image_height, mode); free (data); } current_texture = NULL; return texnum; } mpic_t *GL_LoadPicImage (char *filename, char *id, int matchwidth, int matchheight, int mode) { int glwidth, glheight, i; char identifier[MAX_QPATH] = "pic:"; byte *data, *src, *dest, *buf; static mpic_t pic; if (no24bit) { return NULL; } if (!(data = GL_LoadImagePixels(filename, matchwidth, matchheight, 0))) { return NULL; } pic.width = image_width; pic.height = image_height; if (mode & TEX_ALPHA) { mode &= ~TEX_ALPHA; for (i=0 ; i < image_width * image_height ; i++) { if (((((unsigned *)data)[i] >> 24) & 0xFF) < 255) { mode |= TEX_ALPHA; break; } } } for (glwidth = 1 ; glwidth < pic.width ; glwidth <<= 1) ; for (glheight = 1 ; glheight < pic.height ; glheight <<= 1) ; Q_strncpyz (identifier + 4, id ? id : filename, sizeof(identifier) - 4); if (glwidth == pic.width && glheight == pic.height) { pic.texnum = GL_LoadTexture (identifier, pic.width, pic.height, data, mode, 4); pic.sl = 0; pic.sh = 1; pic.tl = 0; pic.th = 1; } else { buf = Q_Calloc (glwidth * glheight, 4); src = data; dest = buf; for (i=0 ; i> 24) & 0xFF) < 255) { transparent = true; break; } } if (!transparent) { for (i=0 ; i < image_width * image_height ; i++) { if (data[i*4] == data[0] && data[i*4+1] == data[1] && data[i*4+2] == data[2]) data[i*4+3] = 0; } } buf = dest = Q_Calloc (image_size * 2, 4); src = data; for (i=0 ; i<16 ; i++) { memcpy (dest, src, image_size >> 2); src += image_size >> 2; dest += image_size >> 1; } texnum = GL_LoadTexture (identifier, image_width, image_height * 2, buf, TEX_ALPHA, 4); free (buf); free (data); return texnum; } static GLenum oldtarget = GL_TEXTURE0_ARB; static int cnttextures[4] = {-1, -1, -1, -1}; // cached static qboolean mtexenabled = false; void GL_SelectTexture (GLenum target) { if (target == oldtarget) return; qglActiveTexture (target); cnttextures[oldtarget-GL_TEXTURE0_ARB] = currenttexture; currenttexture = cnttextures[target-GL_TEXTURE0_ARB]; oldtarget = target; } void GL_DisableMultitexture (void) { if (mtexenabled) { glDisable (GL_TEXTURE_2D); GL_SelectTexture (GL_TEXTURE0_ARB); mtexenabled = false; } } void GL_EnableMultitexture (void) { if (gl_mtexable) { GL_SelectTexture (GL_TEXTURE1_ARB); glEnable (GL_TEXTURE_2D); mtexenabled = true; } } void GL_EnableTMU (GLenum target) { GL_SelectTexture (target); glEnable (GL_TEXTURE_2D); } void GL_DisableTMU (GLenum target) { GL_SelectTexture (target); glDisable (GL_TEXTURE_2D); }