/* 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. */ // draw.c -- this is the only file outside the refresh that touches the // vid buffer #include "quakedef.h" #define GL_COLOR_INDEX8_EXT 0x80E5 extern unsigned char d_15to8table[65536]; cvar_t qsg_version = {"qsg_version", "1"}; cvar_t gl_max_size = {"gl_max_size", "1024"}; cvar_t gl_picmip = {"gl_picmip", "0"}; cvar_t gl_conalpha = {"gl_conalpha", "1"}; cvar_t gl_lerpimages = {"gl_lerpimages", "1"}; byte *draw_chars; // 8*8 graphic characters qpic_t *draw_disc; int translate_texture; int char_texture; typedef struct { int texnum; float sl, tl, sh, th; } glpic_t; byte conback_buffer[sizeof(qpic_t) + sizeof(glpic_t)]; qpic_t *conback = (qpic_t *)&conback_buffer; int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; int gl_filter_max = GL_LINEAR; int texels; typedef struct { int texnum; char identifier[64]; int width, height; qboolean mipmap; // LordHavoc: 32bit textures int bytesperpixel; // LordHavoc: CRC to identify cache mismatchs int crc; int lerped; // whether this texture was uploaded with or without interpolation } gltexture_t; #define MAX_GLTEXTURES 4096 gltexture_t gltextures[MAX_GLTEXTURES]; int numgltextures; /* ============================================================================= scrap allocation Allocate all the little status bar obejcts into a single texture to crutch up stupid hardware / drivers ============================================================================= */ #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]; qboolean scrap_dirty; int scrap_texnum; // returns a texture number and the position inside it int Scrap_AllocBlock (int w, int h, int *x, int *y) { int i, j; int best, best2; int texnum; for (texnum=0 ; texnum= best) break; if (scrap_allocated[texnum][i+j] > best2) best2 = scrap_allocated[texnum][i+j]; } if (j == w) { // this is a valid spot *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) continue; for (i=0 ; idata; // load little ones into the scrap if (p->width < 64 && p->height < 64) { int x, y; int i, j, k; int texnum; texnum = Scrap_AllocBlock (p->width, p->height, &x, &y); scrap_dirty = true; 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; gl->texnum = texnum; gl->sl = (x+0.01)/(float)BLOCK_WIDTH; gl->sh = (x+p->width-0.01)/(float)BLOCK_WIDTH; gl->tl = (y+0.01)/(float)BLOCK_WIDTH; gl->th = (y+p->height-0.01)/(float)BLOCK_WIDTH; pic_count++; pic_texels += p->width*p->height; // LordHavoc: LINEAR interpolation glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { gl->texnum = GL_LoadPicTexture (p); gl->sl = 0; gl->sh = 1; gl->tl = 0; gl->th = 1; // LordHavoc: LINEAR interpolation glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //NEAREST); } return p; } /* ================ Draw_CachePic ================ */ qpic_t *Draw_CachePic (char *path) { cachepic_t *pic; int i; qpic_t *dat; glpic_t *gl; for (pic=menu_cachepics, i=0 ; iname)) return &pic->pic; if (menu_numcachepics == MAX_CACHED_PICS) Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); menu_numcachepics++; strcpy (pic->name, path); // // load the pic from disk // dat = (qpic_t *)COM_LoadTempFile (path, false); if (!dat) 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; gl = (glpic_t *)pic->pic.data; gl->texnum = GL_LoadPicTexture (dat); gl->sl = 0; gl->sh = 1; gl->tl = 0; gl->th = 1; return &pic->pic; } void Draw_CharToConback (int num, byte *dest) { int row, col; byte *source; int drawline; int x; 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; } } 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} }; /* =============== Draw_TextureMode_f =============== */ void Draw_TextureMode_f (void) { int i; gltexture_t *glt; if (Cmd_Argc() == 1) { for (i=0 ; i< 6 ; i++) if (gl_filter_min == modes[i].minimize) { Con_Printf ("%s\n", modes[i].name); return; } Con_Printf ("current filter is unknown???\n"); return; } for (i=0 ; i< 6 ; i++) { if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) ) break; } if (i == 6) { Con_Printf ("bad filter name\n"); return; } gl_filter_min = modes[i].minimize; gl_filter_max = modes[i].maximize; // change all the existing mipmap texture objects for (i=0, glt=gltextures ; imipmap) { glBindTexture(GL_TEXTURE_2D, glt->texnum); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } } } extern void LoadSky_f(void); extern char *QSG_EXTENSIONS; /* =============== Draw_Init =============== */ void rmain_registercvars(); void Draw_Init (void) { int i; qpic_t *cb; byte *dest; int x, y; char ver[40]; glpic_t *gl; int start; Cvar_RegisterVariable (&qsg_version); Cvar_RegisterVariable (&gl_max_size); Cvar_RegisterVariable (&gl_picmip); Cvar_RegisterVariable (&gl_conalpha); Cvar_RegisterVariable (&gl_lerpimages); // 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"); Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f); Cmd_AddCommand ("loadsky", &LoadSky_f); // 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_chars = W_GetLumpName ("conchars"); for (i=0 ; i<256*64 ; i++) if (draw_chars[i] == 0) draw_chars[i] = 255; // proper transparent color // now turn them into textures char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true, 1); start = Hunk_LowMark(); cb = (qpic_t *)COM_LoadTempFile ("gfx/conback.lmp", false); if (!cb) Sys_Error ("Couldn't load gfx/conback.lmp"); SwapPic (cb); // hack the version number directly into the pic #ifdef NEHAHRA #if defined(__linux__) sprintf (ver, "(DPNehahra %.2f, Linux %2.2f, gl %.2f) %.2f", (float) DP_VERSION, (float)LINUX_VERSION, (float)GLQUAKE_VERSION, (float)VERSION); #else sprintf (ver, "(DPNehahra %.2f, gl %.2f) %.2f", (float) DP_VERSION, (float)GLQUAKE_VERSION, (float)VERSION); #endif #else #if defined(__linux__) sprintf (ver, "(DarkPlaces %.2f, Linux %2.2f, gl %.2f) %.2f", (float) DP_VERSION, (float)LINUX_VERSION, (float)GLQUAKE_VERSION, (float)VERSION); #else sprintf (ver, "(DarkPlaces %.2f, gl %.2f) %.2f", (float) DP_VERSION, (float)GLQUAKE_VERSION, (float)VERSION); #endif #endif dest = cb->data + 320*186 + 320 - 11 - 8*strlen(ver); y = strlen(ver); for (x=0 ; xdata; gl->texnum = GL_LoadTexture ("conback", cb->width, cb->height, cb->data, false, false, 1); gl->sl = 0; gl->sh = 1; gl->tl = 0; gl->th = 1; conback->width = vid.width; conback->height = vid.height; // free loaded console Hunk_FreeToLowMark(start); // 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; // // get the other pics we need // draw_disc = Draw_PicFromWad ("disc"); rmain_registercvars(); } /* ================ Draw_Character 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 Draw_Character (int x, int y, int num) { int row, col; float frow, fcol, size; if (num == 32) return; // space num &= 255; if (y <= -8) return; // totally off screen row = num>>4; col = num&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; glBindTexture(GL_TEXTURE_2D, char_texture); // LordHavoc: NEAREST mode on text if not scaling up if ((int) vid.width < glwidth) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } glColor3f(1,1,1); glBegin (GL_QUADS); glTexCoord2f (fcol, frow); glVertex2f (x, y); glTexCoord2f (fcol + size, frow); glVertex2f (x+8, y); glTexCoord2f (fcol + size, frow + size); glVertex2f (x+8, y+8); glTexCoord2f (fcol, frow + size); glVertex2f (x, y+8); glEnd (); // LordHavoc: revert to LINEAR mode glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } /* ================ Draw_String ================ */ // LordHavoc: sped this up a lot, and added maxlen void Draw_String (int x, int y, char *str, int maxlen) { int num; float frow, fcol; if (y <= -8 || y >= (int) vid.height || x >= (int) vid.width || *str == 0) // completely offscreen or no text to print return; if (maxlen < 1) maxlen = strlen(str); else if (maxlen > (int) strlen(str)) maxlen = strlen(str); glBindTexture(GL_TEXTURE_2D, char_texture); // LordHavoc: NEAREST mode on text if not scaling up if ((int) vid.width < glwidth) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } glColor3f(1,1,1); glBegin (GL_QUADS); while (maxlen-- && x < (int) vid.width) // stop rendering when out of characters or room { if ((num = *str++) != 32) // skip spaces { frow = (float) ((int) num >> 4)*0.0625; fcol = (float) ((int) 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.0625); glVertex2f (x+8, y+8); glTexCoord2f (fcol, frow + 0.0625); glVertex2f (x, y+8); } x += 8; } glEnd (); // LordHavoc: revert to LINEAR mode glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } /* ============= Draw_AlphaPic ============= */ void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha) { glpic_t *gl; if (scrap_dirty) Scrap_Upload (); gl = (glpic_t *)pic->data; // glDisable(GL_ALPHA_TEST); // glEnable (GL_BLEND); // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glCullFace(GL_FRONT); glColor4f(0.8,0.8,0.8,alpha); glBindTexture(GL_TEXTURE_2D, gl->texnum); // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glBegin (GL_QUADS); glTexCoord2f (gl->sl, gl->tl); glVertex2f (x, y); glTexCoord2f (gl->sh, gl->tl); glVertex2f (x+pic->width, y); glTexCoord2f (gl->sh, gl->th); glVertex2f (x+pic->width, y+pic->height); glTexCoord2f (gl->sl, gl->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, qpic_t *pic) { glpic_t *gl; if (scrap_dirty) Scrap_Upload (); gl = (glpic_t *)pic->data; glColor3f(0.8,0.8,0.8); glBindTexture(GL_TEXTURE_2D, gl->texnum); glBegin (GL_QUADS); glTexCoord2f (gl->sl, gl->tl); glVertex2f (x, y); glTexCoord2f (gl->sh, gl->tl); glVertex2f (x+pic->width, y); glTexCoord2f (gl->sh, gl->th); glVertex2f (x+pic->width, y+pic->height); glTexCoord2f (gl->sl, gl->th); glVertex2f (x, y+pic->height); glEnd (); } /* ============= Draw_TransPic ============= */ void Draw_TransPic (int x, int y, qpic_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"); // glEnable(GL_BLEND); // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glDisable(GL_ALPHA_TEST); Draw_Pic (x, y, pic); // glDisable(GL_BLEND); } /* ============= Draw_TransPicTranslate Only used for the player color selection menu ============= */ void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation) { int v, u, c; unsigned trans[64*64], *dest; byte *src; int p; glBindTexture(GL_TEXTURE_2D, 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]; if (p == 255) dest[u] = p; else dest[u] = d_8to24table[translation[p]]; } } glTexImage2D (GL_TEXTURE_2D, 0, 4, 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); glColor3f(0.8,0.8,0.8); 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) { // LordHavoc: changed alpha //int y = (vid.height >> 1); if (lines >= (int) vid.height) Draw_Pic(0, lines - vid.height, conback); else Draw_AlphaPic (0, lines - vid.height, conback, gl_conalpha.value*lines/vid.height); // Draw_AlphaPic (0, lines - vid.height, conback, (float)(1.2 * lines)/y); } /* ============= 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); } //============================================================================= //============================================================================= /* ================ 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); glEnable (GL_BLEND); // was Disable // glEnable (GL_ALPHA_TEST); glDisable (GL_ALPHA_TEST); glEnable(GL_TEXTURE_2D); // LordHavoc: added this glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f(1,1,1); } // LordHavoc: SHOWLMP stuff #define SHOWLMP_MAXLABELS 256 typedef struct showlmp_s { qboolean isactive; float x; float y; char label[32]; char pic[128]; } showlmp_t; showlmp_t showlmp[SHOWLMP_MAXLABELS]; void SHOWLMP_decodehide() { int i; byte *lmplabel; lmplabel = MSG_ReadString(); for (i = 0;i < SHOWLMP_MAXLABELS;i++) if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0) { showlmp[i].isactive = false; return; } } void SHOWLMP_decodeshow() { int i, k; byte lmplabel[256], picname[256]; float x, y; strcpy(lmplabel,MSG_ReadString()); strcpy(picname, MSG_ReadString()); x = MSG_ReadByte(); y = MSG_ReadByte(); k = -1; for (i = 0;i < SHOWLMP_MAXLABELS;i++) if (showlmp[i].isactive) { if (strcmp(showlmp[i].label, lmplabel) == 0) { k = i; break; // drop out to replace it } } else if (k < 0) // find first empty one to replace k = i; if (k < 0) return; // none found to replace // change existing one showlmp[k].isactive = true; strcpy(showlmp[k].label, lmplabel); strcpy(showlmp[k].pic, picname); showlmp[k].x = x; showlmp[k].y = y; } void SHOWLMP_drawall() { int i; for (i = 0;i < SHOWLMP_MAXLABELS;i++) if (showlmp[i].isactive) Draw_TransPic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic)); } void SHOWLMP_clear() { int i; for (i = 0;i < SHOWLMP_MAXLABELS;i++) showlmp[i].isactive = false; } //==================================================================== /* ================ GL_FindTexture ================ */ int GL_FindTexture (char *identifier) { int i; gltexture_t *glt; for (i=0, glt=gltextures ; iidentifier)) return gltextures[i].texnum; } return -1; } extern byte gamma[]; // LordHavoc: gamma correction and improved resampling void GL_ResampleTextureLerpLine (byte *in, byte *out, int inwidth, int outwidth) { int j, xi, oldx = 0; float f, fstep, l1, l2; fstep = (float) inwidth/outwidth; for (j = 0,f = 0;j < outwidth;j++, f += fstep) { xi = (int) f; if (xi != oldx) { in += (xi - oldx) * 4; oldx = xi; } if (xi < (inwidth-1)) { l2 = f - xi; l1 = 1 - l2; *out++ = gamma[(byte) (in[0] * l1 + in[4] * l2)]; *out++ = gamma[(byte) (in[1] * l1 + in[5] * l2)]; *out++ = gamma[(byte) (in[2] * l1 + in[6] * l2)]; *out++ = (byte) (in[3] * l1 + in[7] * l2) ; } else // last pixel of the line has no pixel to lerp to { *out++ = gamma[in[0]]; *out++ = gamma[in[1]]; *out++ = gamma[in[2]]; *out++ = in[3] ; } } } /* ================ GL_ResampleTexture ================ */ void GL_ResampleTexture (void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight) { // LordHavoc: gamma correction and greatly improved resampling if (gl_lerpimages.value) { int i, j, yi, oldy; byte *inrow, *out, *row1, *row2; float f, fstep, l1, l2; out = outdata; fstep = (float) inheight/outheight; row1 = malloc(outwidth*4); row2 = malloc(outwidth*4); inrow = indata; oldy = 0; GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth); GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth); for (i = 0, f = 0;i < outheight;i++,f += fstep) { yi = (int) f; if (yi != oldy) { inrow = (byte *)((int)indata + inwidth*4*yi); if (yi == oldy+1) memcpy(row1, row2, outwidth*4); else GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth); if (yi < (inheight-1)) GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth); else memcpy(row2, row1, outwidth*4); oldy = yi; } if (yi < (inheight-1)) { l2 = f - yi; l1 = 1 - l2; for (j = 0;j < outwidth;j++) { *out++ = (byte) (*row1++ * l1 + *row2++ * l2); *out++ = (byte) (*row1++ * l1 + *row2++ * l2); *out++ = (byte) (*row1++ * l1 + *row2++ * l2); *out++ = (byte) (*row1++ * l1 + *row2++ * l2); } row1 -= outwidth*4; row2 -= outwidth*4; } else // last line has no pixels to lerp to { for (j = 0;j < outwidth;j++) { *out++ = *row1++; *out++ = *row1++; *out++ = *row1++; *out++ = *row1++; } row1 -= outwidth*4; } } free(row1); free(row2); } else { int i, j; unsigned frac, fracstep; byte *inrow, *out, *inpix; out = outdata; fracstep = inwidth*0x10000/outwidth; for (i=0 ; i> 1; for (j=0 ; j> 14) & ~3);*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ = *inpix++ ;frac += fracstep; inpix = inrow + ((frac >> 14) & ~3);*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ = *inpix++ ;frac += fracstep; inpix = inrow + ((frac >> 14) & ~3);*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ = *inpix++ ;frac += fracstep; inpix = inrow + ((frac >> 14) & ~3);*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ = gamma[*inpix++];*out++ = *inpix++ ;frac += fracstep; } } } } /* ================ GL_Resample8BitTexture -- JACK ================ */ void GL_Resample8BitTexture (unsigned char *in, int inwidth, int inheight, unsigned char *out, int outwidth, int outheight) { int i, j; unsigned char *inrow; unsigned frac, fracstep; fracstep = inwidth*0x10000/outwidth; for (i=0 ; i> 1; for (j=0 ; j>16]; frac += fracstep; out[j+1] = inrow[frac>>16]; frac += fracstep; out[j+2] = inrow[frac>>16]; frac += fracstep; out[j+3] = inrow[frac>>16]; frac += fracstep; } } } /* ================ GL_MipMap Operates in place, quartering the size of the texture ================ */ void GL_MipMap (byte *in, int width, int height) { int i, j; byte *out; width <<=2; height >>= 1; out = in; for (i=0 ; i>2; out[1] = (in[1] + in[5] + in[width+1] + in[width+5])>>2; out[2] = (in[2] + in[6] + in[width+2] + in[width+6])>>2; out[3] = (in[3] + in[7] + in[width+3] + in[width+7])>>2; } } } /* ================ GL_MipMap8Bit Mipping for 8 bit textures ================ */ void GL_MipMap8Bit (byte *in, int width, int height) { int i, j; unsigned short r,g,b; byte *out, *at1, *at2, *at3, *at4; height >>= 1; out = in; for (i=0 ; i>=5; g = (at1[1]+at2[1]+at3[1]+at4[1]); g>>=5; b = (at1[2]+at2[2]+at3[2]+at4[2]); b>>=5; out[0] = d_15to8table[(r<<0) + (g<<5) + (b<<10)]; } } } /* =============== GL_Upload32 =============== */ void GL_Upload32 (void *data, int width, int height, qboolean mipmap, qboolean alpha) { int samples, scaled_width, scaled_height, i; byte *in, *out, *scaled; for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) ; for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) ; scaled_width >>= (int)gl_picmip.value; scaled_height >>= (int)gl_picmip.value; if (scaled_width > gl_max_size.value) scaled_width = gl_max_size.value; if (scaled_height > gl_max_size.value) scaled_height = gl_max_size.value; samples = alpha ? gl_alpha_format : gl_solid_format; #if 0 if (mipmap) gluBuild2DMipmaps (GL_TEXTURE_2D, samples, width, height, GL_RGBA, GL_UNSIGNED_BYTE, scaled); else if (scaled_width == width && scaled_height == height) glTexImage2D (GL_TEXTURE_2D, 0, samples, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); else { gluScaleImage (GL_RGBA, width, height, GL_UNSIGNED_BYTE, scaled, scaled_width, scaled_height, GL_UNSIGNED_BYTE, scaled); glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } #else texels += scaled_width * scaled_height; scaled = malloc(scaled_width*scaled_height*4); if (scaled_width == width && scaled_height == height) { // LordHavoc: gamma correct while copying in = (byte *)data; out = (byte *)scaled; for (i = 0;i < width*height;i++) { *out++ = gamma[*in++]; *out++ = gamma[*in++]; *out++ = gamma[*in++]; *out++ = *in++; } } else GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height); glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); if (mipmap) { int miplevel; miplevel = 0; while (scaled_width > 1 || scaled_height > 1) { GL_MipMap ((byte *)scaled, scaled_width, scaled_height); scaled_width >>= 1; scaled_height >>= 1; if (scaled_width < 1) scaled_width = 1; if (scaled_height < 1) scaled_height = 1; miplevel++; glTexImage2D (GL_TEXTURE_2D, miplevel, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } } #endif if (mipmap) { 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); } void GL_Upload8_EXT (byte *data, int width, int height, qboolean mipmap) { int scaled_width, scaled_height; byte *scaled; for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) ; for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) ; scaled_width >>= (int)gl_picmip.value; scaled_height >>= (int)gl_picmip.value; if (scaled_width > gl_max_size.value) scaled_width = gl_max_size.value; if (scaled_height > gl_max_size.value) scaled_height = gl_max_size.value; texels += scaled_width * scaled_height; if (scaled_width == width && scaled_height == height) { if (!mipmap) { glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX , GL_UNSIGNED_BYTE, data); goto done; } scaled = malloc(scaled_width*scaled_height*4); memcpy (scaled, data, width*height); } else { scaled = malloc(scaled_width*scaled_height*4); GL_Resample8BitTexture (data, width, height, (void*) &scaled, scaled_width, scaled_height); } glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); if (mipmap) { int miplevel; miplevel = 0; while (scaled_width > 1 || scaled_height > 1) { GL_MipMap8Bit ((byte *)scaled, scaled_width, scaled_height); scaled_width >>= 1; scaled_height >>= 1; if (scaled_width < 1) scaled_width = 1; if (scaled_height < 1) scaled_height = 1; miplevel++; glTexImage2D (GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); } } done: ; if (mipmap) { 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); } qboolean VID_Is8bit(); /* =============== GL_Upload8 =============== */ void GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean alpha) { static unsigned *trans; int i, s; qboolean noalpha; int p; byte *indata; int *outdata; s = width*height; trans = malloc(s*4); // if there are no transparent pixels, make it a 3 component // texture even if it was specified as otherwise if (alpha) { noalpha = true; for (i=0 ; iidentifier)) { // LordHavoc: everyone hates cache mismatchs, so I fixed it if (crc != glt->crc || width != glt->width || height != glt->height) { Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n"); goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace //Sys_Error ("GL_LoadTexture: cache mismatch"); } if ((gl_lerpimages.value != 0) != glt->lerped) goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace return glt->texnum; } } } // LordHavoc: although this could be an else condition as it was in the original id code, // it is more clear this way // LordHavoc: check if there are still slots available if (numgltextures >= MAX_GLTEXTURES) Sys_Error ("GL_LoadTexture: ran out of texture slots (%d)\n", MAX_GLTEXTURES); glt = &gltextures[numgltextures++]; strcpy (glt->identifier, identifier); glt->texnum = texture_extension_number; texture_extension_number++; // LordHavoc: label to drop out of the loop into the setup code GL_LoadTexture_setup: glt->crc = crc; // LordHavoc: used to verify textures are identical glt->width = width; glt->height = height; glt->mipmap = mipmap; glt->bytesperpixel = bytesperpixel; glt->lerped = gl_lerpimages.value != 0; glBindTexture(GL_TEXTURE_2D, glt->texnum); if (bytesperpixel == 1) // 8bit GL_Upload8 (data, width, height, mipmap, alpha); else // 32bit GL_Upload32 (data, width, height, mipmap, true); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); return glt->texnum; } /* ================ GL_LoadPicTexture ================ */ int GL_LoadPicTexture (qpic_t *pic) { return GL_LoadTexture ("", pic->width, pic->height, pic->data, false, true, 1); } int GL_GetTextureSlots (int count) { gltexture_t *glt, *first; first = glt = &gltextures[numgltextures]; while (count--) { glt->identifier[0] = 0; glt->texnum = texture_extension_number++; glt->crc = 0; glt->width = 0; glt->height = 0; glt->bytesperpixel = 0; glt++; numgltextures++; } return first->texnum; }