From 8aa0eee611e6d46ee42038356f0a7a28b6dcdca0 Mon Sep 17 00:00:00 2001 From: Spoike Date: Sun, 22 Aug 2004 22:29:09 +0000 Subject: [PATCH] Initial checkin of OpenGL renderer git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/gl/LTFACE.C | 949 ++++++++++ engine/gl/gl_alias.c | 2476 +++++++++++++++++++++++++ engine/gl/gl_backend.c | 489 +++++ engine/gl/gl_draw.c | 3005 ++++++++++++++++++++++++++++++ engine/gl/gl_hlmdl.c | 649 +++++++ engine/gl/gl_model.c | 3005 ++++++++++++++++++++++++++++++ engine/gl/gl_model.h | 817 +++++++++ engine/gl/gl_ngraph.c | 143 ++ engine/gl/gl_ppl.c | 2297 +++++++++++++++++++++++ engine/gl/gl_rlight.c | 926 ++++++++++ engine/gl/gl_rmain.c | 1607 +++++++++++++++++ engine/gl/gl_rmisc.c | 940 ++++++++++ engine/gl/gl_rsurf.c | 3510 ++++++++++++++++++++++++++++++++++++ engine/gl/gl_screen.c | 239 +++ engine/gl/gl_vidcommon.c | 369 ++++ engine/gl/gl_vidlinuxglx.c | 899 +++++++++ engine/gl/gl_vidnt.c | 1283 +++++++++++++ engine/gl/gl_warp.c | 956 ++++++++++ engine/gl/gl_warp_sin.h | 51 + engine/gl/glmod_doom.c | 1993 ++++++++++++++++++++ engine/gl/glquake.h | 813 +++++++++ engine/gl/glsupp.h | 204 +++ 22 files changed, 27620 insertions(+) create mode 100644 engine/gl/LTFACE.C create mode 100644 engine/gl/gl_alias.c create mode 100644 engine/gl/gl_backend.c create mode 100644 engine/gl/gl_draw.c create mode 100644 engine/gl/gl_hlmdl.c create mode 100644 engine/gl/gl_model.c create mode 100644 engine/gl/gl_model.h create mode 100644 engine/gl/gl_ngraph.c create mode 100644 engine/gl/gl_ppl.c create mode 100644 engine/gl/gl_rlight.c create mode 100644 engine/gl/gl_rmain.c create mode 100644 engine/gl/gl_rmisc.c create mode 100644 engine/gl/gl_rsurf.c create mode 100644 engine/gl/gl_screen.c create mode 100644 engine/gl/gl_vidcommon.c create mode 100644 engine/gl/gl_vidlinuxglx.c create mode 100644 engine/gl/gl_vidnt.c create mode 100644 engine/gl/gl_warp.c create mode 100644 engine/gl/gl_warp_sin.h create mode 100644 engine/gl/glmod_doom.c create mode 100644 engine/gl/glquake.h create mode 100644 engine/gl/glsupp.h diff --git a/engine/gl/LTFACE.C b/engine/gl/LTFACE.C new file mode 100644 index 00000000..e2cfb140 --- /dev/null +++ b/engine/gl/LTFACE.C @@ -0,0 +1,949 @@ +#include "bothdefs.h" + +#if defined(GLQUAKE) || (!defined(GLQUAKE) && !defined(SWQUAKE)) + +#ifdef RUNTIMELIGHTING +#if defined(GLQUAKE) +#include "quakedef.h" + + +extern model_t *lightmodel; +#define bsptexinfo(i) (*i) +#define dsurfedges lightmodel->surfedges +#define dvertexes lightmodel->vertexes +#define dedges lightmodel->edges +#define texinfo_t mtexinfo_t +#define Q_PI M_PI +#define Error Host_Error +#define byte qbyte + +#define dfaces lightmodel->surfaces +#define dplanes lightmodel->planes +#define dface_t msurface_t +#define dvertex_t mvertex_t +#define point position + +#define side flags & SURF_PLANEBACK + +#define scaledist 1 +#define rangescale 0.5 +#define extrasamples 1 +#define scalecos 0.5 + + + + +typedef struct mentity_s { + vec3_t origin; + float light; + float angle; + float cone; + int style; + vec3_t colour; + char classname[64]; + char target[64]; + char targetname[64]; + + struct mentity_s *targetent; +} mentity_t; + +static mentity_t entities[8192]; +static int num_entities; + +#define bsp_origin vec3_origin + +/* +============ +CastRay + +Returns the distance between the points, or -1 if blocked +============= +*/ +vec_t CastRay (vec3_t p1, vec3_t p2) +{ + trace_t trace; + vec3_t move; + hull_t *hull; + + hull = &lightmodel->hulls[0]; + memset (&trace, 0, sizeof(trace)); + if (!hull->funcs.RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, p1, p2, &trace)) + return -1; + + VectorSubtract(p1, p2, move); + return VectorLength(move); +} + + + + +static void ParseEpair (mentity_t *mapent, char *key, char *value) +{ + double vec[3]; + + if (!strcmp(key, "classname")) + strcpy(mapent->classname, value); + + else if (!strcmp(key, "target")) + strcpy(mapent->target, value); + + else if (!strcmp(key, "targetname")) + strcpy(mapent->targetname, value); + + else if (!strcmp(key, "light") || !strcmp(key, "_light")) + mapent->light = atoi(value); + + else if (!strcmp(key, "style") || !strcmp(key, "_style")) + mapent->style = atoi(value); + + else if (!strcmp(key, "angle") || !strcmp(key, "_angle")) + mapent->angle = atof(value); + + else if (!strcmp(key, "cone") || !strcmp(key, "_cone")) + mapent->cone = atof(value); + + else if (!strcmp(key, "origin")) + { + sscanf (value, "%lf %lf %lf", &vec[0], &vec[1], &vec[2]); + mapent->origin[0]=vec[0]; + mapent->origin[1]=vec[1]; + mapent->origin[2]=vec[2]; + } + + else if (!strcmp(key, "colour") || !strcmp(key, "color") || !strcmp(key, "_colour") || !strcmp(key, "_color")) + { + sscanf (value, "%lf %lf %lf", &vec[0], &vec[1], &vec[2]); + mapent->colour[0]=vec[0]; + mapent->colour[1]=vec[1]; + mapent->colour[2]=vec[2]; + } +} + +void LightLoadEntities(char *entstring) +{ +#define DEFAULTLIGHTLEVEL 300 + mentity_t *mapent; + char key[1024]; + int i; + int switchedstyle=32; + num_entities = 0; + + while(1) + { + entstring = COM_Parse(entstring); + if (!entstring || !*com_token) + break; + if (strcmp(com_token, "{")) + Host_Error("token wasn't an open brace\n"); + + mapent = &entities[num_entities]; + memset(mapent, 0, sizeof(*mapent)); + mapent->colour[0] = 0; + mapent->colour[1] = 0; + mapent->colour[2] = 0; + while(1) + { + entstring = COM_Parse(entstring); + if (!strcmp(com_token, "}")) + break; + strcpy(key, com_token); + entstring = COM_Parse(entstring); + ParseEpair(mapent, key, com_token); + } + if (!mapent->colour[0] && !mapent->colour[1] && !mapent->colour[2]) + { + int cont; + vec3_t v; + hull_t *hull = &lightmodel->hulls[0]; + v[0] = mapent->origin[0]; + v[1] = mapent->origin[1]; + cont=0; + for (i = 0; i < 256; i+=16) + { + v[2] = mapent->origin[2]-i; + cont = hull->funcs.HullPointContents (hull, v); + if (cont & (FTECONTENTS_LAVA | FTECONTENTS_SLIME | FTECONTENTS_SOLID)) + break; + } + if (cont & FTECONTENTS_LAVA) + { + mapent->colour[0] = 1; + mapent->colour[1] = i/256.0; + mapent->colour[2] = i/256.0; + } + else if (cont & FTECONTENTS_SLIME) + { + mapent->colour[0] = 0.5+0.5*i/256.0; + mapent->colour[1] = 1; + mapent->colour[2] = 0.5+0.5*i/256.0; + } + else + { + if (mapent->style == 9) //hmm.. + { + mapent->colour[1] = 1; + } + else + { + if (!strncmp(mapent->classname, "light_torch_small_walltorch", 12)) + { + mapent->colour[0] = 1; + mapent->colour[1] = 0.7; + mapent->colour[2] = 0.7; + } + else + { + mapent->colour[0] = 1; + mapent->colour[1] = 1; + if (strncmp(mapent->classname, "light_fluoro", 12)) + mapent->colour[2] = 1; + } + } + } + } + + if (!mapent->light && !strncmp (mapent->classname, "light", 5)) + mapent->light = DEFAULTLIGHTLEVEL; + + if (*mapent->targetname && !mapent->style && !strcmp(mapent->classname, "light")) + { + for (i = 0; i <= num_entities; i++) + { + if (entities[i].style >= 32 && !strcmp(entities[i].targetname, mapent->targetname)) + { + mapent->style = entities[i].style; + break; + } + } + + if (i == num_entities) + mapent->style = switchedstyle++; + } + + + num_entities++; + } + + for (mapent = entities; mapent < &entities[num_entities]; mapent++) + { + if (*mapent->target) + { + for (i = 0; i < num_entities; i++) + { + if (mapent == &entities[i]) + continue; + + if (!strcmp(mapent->target, entities[i].targetname)) + { + mapent->targetent = &entities[i]; + break; + } + } + } + } +} + +#else +#define mentity_t entity_t +#define UTILITY +#include "light.h" + +#define bsptexinfo(i) texinfo[i] + + +/* +============ +CastRay + +Returns the distance between the points, or -1 if blocked +============= +*/ +vec_t CastRay (vec3_t p1, vec3_t p2) +{ + int i; + vec_t t; + qboolean trace; + + trace = TestLine (p1, p2); + + if (!trace) + return -1; // ray was blocked + + t = 0; + for (i=0 ; i< 3 ; i++) + t += (p2[i]-p1[i]) * (p2[i]-p1[i]); + + if (t < 1) + t = 1; // don't blow up... + return sqrt(t); +} + +#endif + +/* +=============================================================================== + +SAMPLE POINT DETERMINATION + +void SetupBlock (dface_t *f) Returns with surfpt[] set + +This is a little tricky because the lightmap covers more area than the face. +If done in the straightforward fashion, some of the +sample points will be inside walls or on the other side of walls, causing +false shadows and light bleeds. + +To solve this, I only consider a sample point valid if a line can be drawn +between it and the exact midpoint of the face. If invalid, it is adjusted +towards the center until it is valid. + +(this doesn't completely work) + +=============================================================================== +*/ + +#define SINGLEMAP (18*18*4) + +typedef struct +{ + vec3_t lightmaps[MAXLIGHTMAPS][SINGLEMAP]; + vec3_t lightnorm[MAXLIGHTMAPS][SINGLEMAP]; + int numlightstyles; + vec_t *light; + vec_t facedist; + vec3_t facenormal; + + int numsurfpt; + vec3_t surfpt[SINGLEMAP]; + + vec3_t texorg; + vec3_t worldtotex[2]; // s = (world - texorg) . worldtotex[0] + vec3_t textoworld[2]; // world = texorg + s * textoworld[0] + + vec_t exactmins[2], exactmaxs[2]; + + int texmins[2], texsize[2]; + int lightstyles[256]; + int surfnum; + dface_t *face; +} llightinfo_t; + + +/* +================ +CalcFaceVectors + +Fills in texorg, worldtotex. and textoworld +================ +*/ +static void LightCalcFaceVectors (llightinfo_t *l) +{ + texinfo_t *tex; + int i, j; + vec3_t texnormal; + float distscale; + vec_t dist, len; + + tex = &bsptexinfo(l->face->texinfo); + +// convert from float to vec_t + for (i=0 ; i<2 ; i++) + for (j=0 ; j<3 ; j++) + l->worldtotex[i][j] = tex->vecs[i][j]; + +// calculate a normal to the texture axis. points can be moved along this +// without changing their S/T + texnormal[0] = tex->vecs[1][1]*tex->vecs[0][2] + - tex->vecs[1][2]*tex->vecs[0][1]; + texnormal[1] = tex->vecs[1][2]*tex->vecs[0][0] + - tex->vecs[1][0]*tex->vecs[0][2]; + texnormal[2] = tex->vecs[1][0]*tex->vecs[0][1] + - tex->vecs[1][1]*tex->vecs[0][0]; + VectorNormalize (texnormal); + +// flip it towards plane normal + distscale = DotProduct (texnormal, l->facenormal); + if (!distscale) + Error ("Texture axis perpendicular to face"); + if (distscale < 0) + { + distscale = -distscale; + VectorSubtract (vec3_origin, texnormal, texnormal); + } + +// distscale is the ratio of the distance along the texture normal to +// the distance along the plane normal + distscale = 1/distscale; + + for (i=0 ; i<2 ; i++) + { + len = VectorLength (l->worldtotex[i]); + dist = DotProduct (l->worldtotex[i], l->facenormal); + dist *= distscale; + VectorMA (l->worldtotex[i], -dist, texnormal, l->textoworld[i]); + VectorScale (l->textoworld[i], (1/len)*(1/len), l->textoworld[i]); + } + + +// calculate texorg on the texture plane + for (i=0 ; i<3 ; i++) + l->texorg[i] = -tex->vecs[0][3]* l->textoworld[0][i] - tex->vecs[1][3] * l->textoworld[1][i]; + +// project back to the face plane + dist = DotProduct (l->texorg, l->facenormal) - l->facedist - 1; + dist *= distscale; + VectorMA (l->texorg, -dist, texnormal, l->texorg); + +} + +/* +================ +CalcFaceExtents + +Fills in s->texmins[] and s->texsize[] +also sets exactmins[] and exactmaxs[] +================ +*/ +static void LightCalcFaceExtents (llightinfo_t *l) +{ + dface_t *s; + vec_t mins[2], maxs[2], val; + int i,j, e; + dvertex_t *v; + texinfo_t *tex; + + s = l->face; + + mins[0] = mins[1] = 999999; + maxs[0] = maxs[1] = -99999; + + tex = &bsptexinfo(s->texinfo); + + for (i=0 ; inumedges ; i++) + { + e = dsurfedges[s->firstedge+i]; + if (e >= 0) + v = dvertexes + dedges[e].v[0]; + else + v = dvertexes + dedges[-e].v[1]; + + for (j=0 ; j<2 ; j++) + { + val = v->point[0] * tex->vecs[j][0] + + v->point[1] * tex->vecs[j][1] + + v->point[2] * tex->vecs[j][2] + + tex->vecs[j][3]; + if (val < mins[j]) + mins[j] = val; + if (val > maxs[j]) + maxs[j] = val; + } + } + + for (i=0 ; i<2 ; i++) + { + l->exactmins[i] = mins[i]; + l->exactmaxs[i] = maxs[i]; + + mins[i] = floor(mins[i]/16); + maxs[i] = ceil(maxs[i]/16); + + l->texmins[i] = mins[i]; + l->texsize[i] = maxs[i] - mins[i]; + if (l->texsize[i] > 17) + Error ("Bad surface extents"); + } +} + +/* +================= +CalcPoints + +For each texture aligned grid point, back project onto the plane +to get the world xyz value of the sample point +================= +*/ +int c_bad; +static void LightCalcPoints (llightinfo_t *l) +{ + int i; + int s, t, j; + int w, h, step; + vec_t starts, startt, us, ut; + vec_t *surf; + vec_t mids, midt; + vec3_t facemid, move; + +// +// fill in surforg +// the points are biased towards the center of the surface +// to help avoid edge cases just inside walls +// + surf = l->surfpt[0]; + mids = (l->exactmaxs[0] + l->exactmins[0])/2; + midt = (l->exactmaxs[1] + l->exactmins[1])/2; + + for (j=0 ; j<3 ; j++) + facemid[j] = l->texorg[j] + l->textoworld[0][j]*mids + l->textoworld[1][j]*midt; + + if (extrasamples) + { // extra filtering + h = (l->texsize[1]+1)*2; + w = (l->texsize[0]+1)*2; + starts = (l->texmins[0]-0.5)*16; + startt = (l->texmins[1]-0.5)*16; + step = 8; + } + else + { + h = l->texsize[1]+1; + w = l->texsize[0]+1; + starts = l->texmins[0]*16; + startt = l->texmins[1]*16; + step = 16; + } + + l->numsurfpt = w * h; + for (t=0 ; ttexorg[j] + l->textoworld[0][j]*us + + l->textoworld[1][j]*ut; + + if (CastRay (facemid, surf) != -1) + break; // got it + if (i & 1) + { + if (us > mids) + { + us -= 8; + if (us < mids) + us = mids; + } + else + { + us += 8; + if (us > mids) + us = mids; + } + } + else + { + if (ut > midt) + { + ut -= 8; + if (ut < midt) + ut = midt; + } + else + { + ut += 8; + if (ut > midt) + ut = midt; + } + } + + // move surf 8 pixels towards the center + VectorSubtract (facemid, surf, move); + VectorNormalize (move); + VectorMA (surf, 8, move, surf); + } + if (i == 2) + c_bad++; + } + } + +} + + +/* +=============================================================================== + +FACE LIGHTING + +=============================================================================== +*/ + +int c_culldistplane, c_proper; + +/* +================ +SingleLightFace +================ +*/ +static void SingleLightFace (mentity_t *light, llightinfo_t *l) +{ + vec_t dist; + vec3_t incoming; + vec_t angle; + vec_t add; + vec_t *surf; + qboolean hit; + int mapnum; + int size; + int c, i; + vec3_t rel; + vec3_t spotvec; + vec_t falloff; + vec3_t *lightsamp; + vec3_t *norms; + + VectorSubtract (light->origin, bsp_origin, rel); + dist = scaledist * (DotProduct (rel, l->facenormal) - l->facedist); + +// don't bother with lights behind the surface + if (dist <= 0) + return; + +// don't bother with light too far away + if (dist > light->light) + { + c_culldistplane++; + return; + } + + if (light->targetent) + { + VectorSubtract (light->targetent->origin, light->origin, spotvec); + VectorNormalize (spotvec); + if (!light->angle) + falloff = -cos(20*Q_PI/180); + else + falloff = -cos(light->angle/2*Q_PI/180); + } + else + falloff = 0; // shut up compiler warnings + + mapnum = 0; + for (mapnum=0 ; mapnumnumlightstyles ; mapnum++) + if (l->lightstyles[mapnum] == light->style) + break; + + lightsamp = l->lightmaps[mapnum]; + norms = l->lightnorm[mapnum]; + if (mapnum == l->numlightstyles) + { // init a new light map + if (mapnum == MAXLIGHTMAPS) + { + printf ("WARNING: Too many light styles on a face\n"); + return; + } + size = (l->texsize[1]+1)*(l->texsize[0]+1); + for (i=0 ; isurfpt[0]; + for (c=0 ; cnumsurfpt ; c++, surf+=3) + { + dist = CastRay(light->origin, surf)*scaledist; + if (dist < 0) + continue; // light doesn't reach + + VectorSubtract (light->origin, surf, incoming); + VectorNormalize (incoming); + if (light->targetent) + { // spotlight cutoff + if (DotProduct (spotvec, incoming) > falloff) + continue; + } + angle = DotProduct (incoming, l->facenormal); + + angle = (1.0-scalecos) + scalecos*angle; + add = light->light - dist; + add *= angle; + if (add < 0) + continue; + + lightsamp[c][0] += add*light->colour[0]; + lightsamp[c][1] += add*light->colour[1]; + lightsamp[c][2] += add*light->colour[2]; + + norms[c][0] += add * incoming[0]; //Quake doesn't make sence some times. + norms[c][1] -= add * incoming[1]; + norms[c][2] -= add * incoming[2]; + + if (add > 1) // ignore real tiny lights + hit = true; + } + + if (mapnum == l->numlightstyles && hit) + { + l->lightstyles[mapnum] = light->style; + l->numlightstyles++; // the style has some real data now + } +} + +/* +============ +FixMinlight +============ +*/ +static void FixMinlight (llightinfo_t *l) +{ + int i, j; + float minlight; + + minlight = 0; + +// if minlight is set, there must be a style 0 light map + if (!minlight) + return; + + for (i=0 ; i< l->numlightstyles ; i++) + { + if (l->lightstyles[i] == 0) + break; + } + if (i == l->numlightstyles) + { + if (l->numlightstyles == MAXLIGHTMAPS) + return; // oh well.. + for (j=0 ; jnumsurfpt ; j++) + { + l->lightmaps[i][j][0] = minlight; + l->lightmaps[i][j][1] = minlight; + l->lightmaps[i][j][2] = minlight; + } + l->lightstyles[i] = 0; + l->numlightstyles++; + } + else + { + for (j=0 ; jnumsurfpt ; j++) + { + if ( l->lightmaps[i][j][0] < minlight) + l->lightmaps[i][j][0] = minlight; + if ( l->lightmaps[i][j][1] < minlight) + l->lightmaps[i][j][1] = minlight; + if ( l->lightmaps[i][j][2] < minlight) + l->lightmaps[i][j][2] = minlight; + } + } +} + + +/* +============ +LightFace +============ +*/ +void LightFace (int surfnum) +{ + dface_t *f; + llightinfo_t l; + int s, t; + int i,j,c,ch; + vec_t total, mean; + int size; + int lightmapwidth, lightmapsize; +#ifdef UTILITY + byte *out; +#endif + byte *rgbout; + byte *dulout; + vec3_t *light, *norm; + vec3_t wnorm, temp; + int w, h; + + extern byte *runningrgblightdatabase; + extern byte *runninglightnormbase; + + f = dfaces + surfnum; + +// +// some surfaces don't need lightmaps +// + for (j=0 ; jstyles[j] = 255; + + if ( bsptexinfo(f->texinfo).flags & TEX_SPECIAL) + { // non-lit texture +#ifdef UTILITY + f->lightofs = -1; +#endif + return; + } + +#ifndef UTILITY + if (!f->samples) + return; +#endif + + memset (&l, 0, sizeof(l)); + l.surfnum = surfnum; + l.face = f; + +// +// rotate plane +// +#ifndef UTILITY + VectorCopy (f->plane->normal, l.facenormal); + l.facedist = f->plane->dist; +#else + VectorCopy (dplanes[f->planenum].normal, l.facenormal); + l.facedist = dplanes[f->planenum].dist; +#endif + if (f->side) + { + VectorSubtract (vec3_origin, l.facenormal, l.facenormal); + l.facedist = -l.facedist; + } + + + + LightCalcFaceVectors (&l); + LightCalcFaceExtents (&l); + LightCalcPoints (&l); + + lightmapwidth = l.texsize[0]+1; + + size = lightmapwidth*(l.texsize[1]+1); + if (size > SINGLEMAP) + Error ("Bad lightmap size"); + + for (i=0 ; ilightofs = -1; +#endif + return; + } + +// +// save out the values +// + for (i=0 ; i styles[i] = l.lightstyles[i]; + + lightmapsize = size*l.numlightstyles; + +#ifdef UTILITY + if (runningrgblightdatabase) + { + out = GetFakeFileSpace(&f->lightofs, size); + rgbout = runningrgblightdatabase + f->lightofs*3; + dulout = runninglightnormbase + f->lightofs*3; + } + else + { + out = GetFileSpace (&f->lightofs, lightmapsize); + + rgbout = GetRGBFileSpace (f->lightofs, lightmapsize); + dulout = GetNormFileSpace (f->lightofs, lightmapsize); + } +#else + rgbout = f->samples; + if (lightmodel->deluxdata) + dulout = f->samples - lightmodel->lightdata + lightmodel->deluxdata; + else + dulout = NULL; +#endif + + + +// extra filtering + h = (l.texsize[1]+1)*2; + w = (l.texsize[0]+1)*2; + + for (i=0 ; i< l.numlightstyles ; i++) + { + if (l.lightstyles[i] == 0xff) + Error ("Wrote empty lightmap"); + light = l.lightmaps[i]; + norm = l.lightnorm[i]; + c = 0; + for (t=0 ; t<=l.texsize[1] ; t++) + { + for (s=0 ; s<=l.texsize[0] ; s++, c++) + { + mean = 0; + + for (ch = 0; ch < 3; ch++) + { + if (extrasamples) + { // filtered sample + total = light[t*2*w+s*2][ch] + light[t*2*w+s*2+1][ch] + + light[(t*2+1)*w+s*2][ch] + light[(t*2+1)*w+s*2+1][ch]; + total *= 0.25; + + wnorm[ch] = norm[t*2*w+s*2][ch] + norm[t*2*w+s*2+1][ch] + + norm[(t*2+1)*w+s*2][ch] + norm[(t*2+1)*w+s*2+1][ch]; + } + else + { + total = light[c][ch]; + wnorm[ch] = norm[c][ch]; + } + total *= rangescale; // scale before clamping +#ifdef UTILITY + if (total > *out) //sorry - for qw + total = *out; +#else + if (total > *rgbout) //sorry - for qw + total = *rgbout; +#endif + if (total < 0) + Error ("light < 0"); + + *rgbout++ = total; + mean += total; + } +#ifdef UTILITY + *out++ = mean/3; +#endif + + if (dulout) + { + temp[0] = DotProduct(wnorm, bsptexinfo(f->texinfo).vecs[0]); + temp[1] = DotProduct(wnorm, bsptexinfo(f->texinfo).vecs[1]); + temp[2] = DotProduct(wnorm, l.facenormal); + VectorNormalize(temp); + *dulout++ = (temp[0]+1)/2 * 255; + *dulout++ = (temp[1]+1)/2 * 255; + *dulout++ = (temp[2]+1)/2 * 255; + } + } + } + } +} + +#endif + +#endif \ No newline at end of file diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c new file mode 100644 index 00000000..71aceeb4 --- /dev/null +++ b/engine/gl/gl_alias.c @@ -0,0 +1,2476 @@ +#include "quakedef.h" +#include "glquake.h" +#include "shader.h" +#include "hash.h" + +//FIXME +typedef struct +{ + float scale[3]; // multiply qbyte verts by this + float translate[3]; // then add this + char name[16]; // frame name from grabbing + dtrivertx_t verts[1]; // variable sized +} dmd2aliasframe_t; + + + +// entity_state_t->renderfx flags +#define Q2RF_MINLIGHT 1 // allways have some light (viewmodel) +#define Q2RF_VIEWERMODEL 2 // don't draw through eyes, only mirrors +#define Q2RF_WEAPONMODEL 4 // only draw through eyes +#define Q2RF_FULLBRIGHT 8 // allways draw full intensity +#define Q2RF_DEPTHHACK 16 // for view weapon Z crunching +#define Q2RF_TRANSLUCENT 32 +#define Q2RF_FRAMELERP 64 +#define Q2RF_BEAM 128 +#define Q2RF_CUSTOMSKIN 256 // skin is an index in image_precache +#define Q2RF_GLOW 512 // pulse lighting for bonus items +#define Q2RF_SHELL_RED 1024 +#define Q2RF_SHELL_GREEN 2048 +#define Q2RF_SHELL_BLUE 4096 + +//ROGUE +#define Q2RF_IR_VISIBLE 0x00008000 // 32768 +#define Q2RF_SHELL_DOUBLE 0x00010000 // 65536 +#define Q2RF_SHELL_HALF_DAM 0x00020000 +#define Q2RF_USE_DISGUISE 0x00040000 +//ROGUE + + + + +extern cvar_t gl_part_flame, gl_part_torch, r_fullbrightSkins, r_fb_models; +extern cvar_t r_noaliasshadows; +vec3_t *GLR_LightPoint3C (vec3_t p); +void R_TorchEffect (vec3_t pos, int type); +void GL_DrawMesh(mesh_t *mesh, shader_t *shader, int texturenum, int lmtexturenum); + +void GLMod_FloodFillSkin( qbyte *skin, int skinwidth, int skinheight ); + + + +extern char loadname[32]; // for hunk tags + + +int numTempColours; +byte_vec4_t *tempColours; + +int numTempVertexCoords; +vec4_t *tempVertexCoords; + +int numTempNormals; +vec3_t *tempNormals; + +extern cvar_t gl_ati_truform; + +typedef struct { + int ofs_indexes; + int numindexes; + + int ofs_trineighbours; + + int numskins; + int ofsskins; + + + int numverts; + + int ofs_st_array; + + int groups; + int groupofs; + + int nextsurf; +} galiasinfo_t; + +//frame is an index into this +typedef struct { + int numposes; + float rate; + int poseofs; +} galiasgroup_t; + +typedef struct { + int ofsverts; + int ofsnormals; + + vec3_t scale; + vec3_t scale_origin; +} galiaspose_t; + +//we can't be bothered with animating skins. +//We'll load up to four of them but after that you're on your own +typedef struct { + int skinwidth; + int skinheight; + int ofstexels; //this is 8bit for frame 0 only. only valid in q1 models without replacement textures, used for colourising player skins. + float skinspeed; + int texnums; + int ofstexnums; +} galiasskin_t; + +typedef struct { + int base; + int bump; + int fullbright; +} galiastexnum_t; + +typedef struct { + char name[MAX_QPATH]; + galiastexnum_t texnum; + int colour; + int skinnum; + bucket_t bucket; +} galiascolourmapped_t; + +static hashtable_t skincolourmapped; + +static vec3_t shadevector; +static vec3_t shadelight, ambientlight; +static void R_LerpFrames(mesh_t *mesh, galiaspose_t *p1, galiaspose_t *p2, float lerp, qbyte alpha, float expand) +{ + extern cvar_t r_nolerp, r_nolightdir; + float blerp = 1-lerp; + int i; + float l; + int temp; + vec3_t *p1v, *p2v; + vec3_t *p1n, *p2n; + (char *)p1v = (char *)p1 + p1->ofsverts; + (char *)p2v = (char *)p2 + p2->ofsverts; + + (char *)p1n = (char *)p1 + p1->ofsnormals; + (char *)p2n = (char *)p2 + p2->ofsnormals; + + if (p1v == p2v || r_nolerp.value) + { + (char *)mesh->normals_array = (char *)p1 + p1->ofsnormals; + if (r_nolightdir.value) + { + for (i = 0; i < mesh->numvertexes; i++) + { + mesh->xyz_array[i][0] = p1v[i][0]; + mesh->xyz_array[i][1] = p1v[i][1]; + mesh->xyz_array[i][2] = p1v[i][2]; + + mesh->colors_array[i][0] = ambientlight[0]+shadelight[0]; + mesh->colors_array[i][1] = ambientlight[1]+shadelight[1]; + mesh->colors_array[i][2] = ambientlight[2]+shadelight[2]; + mesh->colors_array[i][3] = alpha; + } + } + else + { + for (i = 0; i < mesh->numvertexes; i++) + { + mesh->xyz_array[i][0] = p1v[i][0]; + mesh->xyz_array[i][1] = p1v[i][1]; + mesh->xyz_array[i][2] = p1v[i][2]; + + l = DotProduct(mesh->normals_array[i], shadevector); + + temp = l*ambientlight[0]+shadelight[0]; + if (temp < 0) temp = 0; + else if (temp > 255) temp = 255; + mesh->colors_array[i][0] = temp; + + temp = l*ambientlight[1]+shadelight[1]; + if (temp < 0) temp = 0; + else if (temp > 255) temp = 255; + mesh->colors_array[i][1] = temp; + + temp = l*ambientlight[2]+shadelight[2]; + if (temp < 0) temp = 0; + else if (temp > 255) temp = 255; + mesh->colors_array[i][2] = temp; + + mesh->colors_array[i][3] = alpha; + } + } + } + else + { + if (r_nolightdir.value) + { + for (i = 0; i < mesh->numvertexes; i++) + { + mesh->normals_array[i][0] = p1n[i][0]*lerp + p2n[i][0]*blerp; + mesh->normals_array[i][1] = p1n[i][1]*lerp + p2n[i][1]*blerp; + mesh->normals_array[i][2] = p1n[i][2]*lerp + p2n[i][2]*blerp; + + mesh->xyz_array[i][0] = p1v[i][0]*lerp + p2v[i][0]*blerp; + mesh->xyz_array[i][1] = p1v[i][1]*lerp + p2v[i][1]*blerp; + mesh->xyz_array[i][2] = p1v[i][2]*lerp + p2v[i][2]*blerp; + + mesh->colors_array[i][0] = ambientlight[0]; + mesh->colors_array[i][1] = ambientlight[1]; + mesh->colors_array[i][2] = ambientlight[2]; + mesh->colors_array[i][3] = alpha; + } + } + else + { + for (i = 0; i < mesh->numvertexes; i++) + { + mesh->normals_array[i][0] = p1n[i][0]*lerp + p2n[i][0]*blerp; + mesh->normals_array[i][1] = p1n[i][1]*lerp + p2n[i][1]*blerp; + mesh->normals_array[i][2] = p1n[i][2]*lerp + p2n[i][2]*blerp; + + mesh->xyz_array[i][0] = p1v[i][0]*lerp + p2v[i][0]*blerp; + mesh->xyz_array[i][1] = p1v[i][1]*lerp + p2v[i][1]*blerp; + mesh->xyz_array[i][2] = p1v[i][2]*lerp + p2v[i][2]*blerp; + + l = DotProduct(mesh->normals_array[i], shadevector); + temp = l*ambientlight[0]+shadelight[0]; + if (temp < 0) temp = 0; + else if (temp > 255) temp = 255; + mesh->colors_array[i][0] = temp; + + temp = l*ambientlight[1]+shadelight[1]; + if (temp < 0) temp = 0; + else if (temp > 255) temp = 255; + mesh->colors_array[i][1] = temp; + + temp = l*ambientlight[2]+shadelight[2]; + if (temp < 0) temp = 0; + else if (temp > 255) temp = 255; + mesh->colors_array[i][2] = temp; + + mesh->colors_array[i][3] = alpha; + } + } + } + if (expand) + { + for (i = 0; i < mesh->numvertexes; i++) + { + mesh->xyz_array[i][0] += mesh->normals_array[i][0]*expand; + mesh->xyz_array[i][1] += mesh->normals_array[i][1]*expand; + mesh->xyz_array[i][2] += mesh->normals_array[i][2]*expand; + } + } +} + +static void R_GAliasBuildMesh(mesh_t *mesh, galiasinfo_t *inf, int frame1, int frame2, float lerp, float alpha) +{ + galiasgroup_t *g1, *g2; + if (!inf->groups) + { + Con_DPrintf("Model with no frames\n"); + return; + } + if (frame1 < 0) + { + Con_DPrintf("Negative frame\n"); + frame1 = 0; + } + if (frame2 < 0) + { + Con_DPrintf("Negative frame\n"); + frame2 = frame1; + } + if (frame1 >= inf->groups) + { + Con_DPrintf("Too high frame\n"); + frame1 = 0; + } + if (frame2 >= inf->groups) + { + Con_DPrintf("Too high frame\n"); + frame2 = frame1; + } + + if (lerp <= 0) + frame2 = frame1; + if (lerp >= 1) + frame1 = frame2; + + if (numTempColours < inf->numverts) + { + if (tempColours) + BZ_Free(tempColours); + tempColours = BZ_Malloc(sizeof(*tempColours)*inf->numverts); + numTempColours = inf->numverts; + } + if (numTempNormals < inf->numverts) + { + if (tempNormals) + BZ_Free(tempNormals); + tempNormals = BZ_Malloc(sizeof(*tempNormals)*inf->numverts); + numTempNormals = inf->numverts; + } + if (numTempVertexCoords < inf->numverts) + { + if (tempVertexCoords) + BZ_Free(tempVertexCoords); + tempVertexCoords = BZ_Malloc(sizeof(*tempVertexCoords)*inf->numverts); + numTempVertexCoords = inf->numverts; + } + + (char *)mesh->indexes = (char *)inf + inf->ofs_indexes; + mesh->numindexes = inf->numindexes; + (char *)mesh->st_array = (char *)inf + inf->ofs_st_array; + mesh->lmst_array = NULL; + mesh->colors_array = tempColours; + mesh->normals_array = tempNormals; + mesh->xyz_array = tempVertexCoords; + mesh->numvertexes = inf->numverts; + (char *)mesh->trneighbors = (char *)inf + inf->ofs_trineighbours; + + (char *)g1 = (char *)inf + inf->groupofs + sizeof(galiasgroup_t)*frame1; + (char *)g2 = (char *)inf + inf->groupofs + sizeof(galiasgroup_t)*frame2; + + if (g1 == g2) //lerping within group is only done if not changing group + { + lerp = cl.time*g1->rate; + frame1=lerp; + frame2=frame1+1; + lerp-=frame1; + frame1=frame1%g1->numposes; + frame2=frame2%g1->numposes; + + } + else //don't bother with a four way lerp + { + frame1=0; + frame2=0; + } + + R_LerpFrames(mesh, (galiaspose_t *)((char *)g1 + g1->poseofs + sizeof(galiaspose_t)*frame1), + (galiaspose_t *)((char *)g2 + g2->poseofs + sizeof(galiaspose_t)*frame2), + 1-lerp, (qbyte)(alpha*255), currententity->fatness);//20*sin(cl.time)); +} + +void GL_GAliasFlushSkinCache(void) +{ + int i; + bucket_t *b; + for (i = 0; i < skincolourmapped.numbuckets; i++) + { + while((b = skincolourmapped.bucket[i])) + { + skincolourmapped.bucket[i] = b->next; + BZ_Free(b->data); + } + } + if (skincolourmapped.bucket) + BZ_Free(skincolourmapped.bucket); + skincolourmapped.bucket = NULL; + skincolourmapped.numbuckets = 0; +} + +static galiastexnum_t *GL_ChooseSkin(galiasinfo_t *inf, char *modelname, entity_t *e) +{ + galiasskin_t *skins; + galiastexnum_t *texnums; + int frame; + + int tc, bc; + + if (e->scoreboard) + { + if (!e->scoreboard->skin && !gl_nocolors.value) + Skin_Find(e->scoreboard); + tc = e->scoreboard->topcolor; + bc = e->scoreboard->bottomcolor; + } + else + { + tc = 1; + bc = 1; + } + + if (!gl_nocolors.value && (tc != 1 || bc != 1 || (e->scoreboard && e->scoreboard->skin))) + { + int inwidth, inheight; + int tinwidth, tinheight; + char *skinname; + qbyte *original; + int cc; + galiascolourmapped_t *cm; + cc = (tc<<4)|bc; + + if (!strstr(modelname, "player")) + skinname = modelname; + else + { + if (e->scoreboard && e->scoreboard->skin && !gl_nocolors.value) + skinname = e->scoreboard->skin->name; + else + skinname = modelname; + } + + if (!skincolourmapped.numbuckets) + Hash_InitTable(&skincolourmapped, 256, BZ_Malloc(Hash_BytesForBuckets(256))); + + for (cm = Hash_Get(&skincolourmapped, skinname); cm; cm = Hash_GetNext(&skincolourmapped, skinname, cm)) + { + if (cm->colour == cc && cm->skinnum == e->skinnum) + { + return &cm->texnum; + } + } + + (char *)skins = (char *)inf + inf->ofsskins; + if (!skins->texnums) + return NULL; + if (e->skinnum >= 0 && e->skinnum < inf->numskins) + skins += e->skinnum; + (char *)texnums = (char *)skins + skins->ofstexnums; + + + //colourmap isn't present yet. + cm = BZ_Malloc(sizeof(*cm)); + Q_strncpyz(cm->name, skinname, sizeof(cm->name)); + Hash_Add2(&skincolourmapped, cm->name, cm, &cm->bucket); + cm->colour = cc; + cm->skinnum = e->skinnum; + cm->texnum.fullbright = 0; + cm->texnum.base = 0; + cm->texnum.bump = texnums[cm->skinnum].bump; //can't colour bumpmapping + if (skins->ofstexels) + { + original = (qbyte *)skins + skins->ofstexels; + inwidth = skins->skinwidth; + inheight = skins->skinheight; + } + else if (skinname!=modelname && e->scoreboard && e->scoreboard->skin) + { + original = Skin_Cache8(e->scoreboard->skin); + inwidth = e->scoreboard->skin->width; + inheight = e->scoreboard->skin->height; + } + else + { + original = NULL; + inwidth = 0; + inheight = 0; + } + tinwidth = skins->skinwidth; + tinheight = skins->skinheight; + if (original) + { + int i, j; + qbyte translate[256]; + unsigned translate32[256]; + static unsigned pixels[512*512]; + unsigned *out; + unsigned frac, fracstep; + + unsigned scaled_width, scaled_height; + qbyte *inrow; + + texnums = &cm->texnum; + + texnums->base = 0; + texnums->fullbright = 0; + + scaled_width = gl_max_size.value < 512 ? gl_max_size.value : 512; + scaled_height = gl_max_size.value < 512 ? gl_max_size.value : 512; + + for (i=0 ; i<256 ; i++) + translate[i] = i; + + tc<<=4; + bc<<=4; + + for (i=0 ; i<16 ; i++) + { + if (tc < 128) // the artists made some backwards ranges. sigh. + translate[TOP_RANGE+i] = tc+i; + else + translate[TOP_RANGE+i] = tc+15-i; + + if (bc < 128) + translate[BOTTOM_RANGE+i] = bc+i; + else + translate[BOTTOM_RANGE+i] = bc+15-i; + } + + + for (i=0 ; i<256 ; i++) + translate32[i] = d_8to24rgbtable[translate[i]]; + + out = pixels; + fracstep = tinwidth*0x10000/scaled_width; + for (i=0 ; i> 1; + for (j=0 ; j>16]]; + frac += fracstep; + out[j+1] = translate32[inrow[frac>>16]]; + frac += fracstep; + out[j+2] = translate32[inrow[frac>>16]]; + frac += fracstep; + out[j+3] = translate32[inrow[frac>>16]]; + frac += fracstep; + } + } + texnums->base = texture_extension_number++; + GL_Bind(texnums->base); + glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + + //now do the fullbrights. + out = pixels; + fracstep = tinwidth*0x10000/scaled_width; + for (i=0 ; i> 1; + for (j=0 ; j>16] < 255-vid.fullbright) + ((char *) (&out[j]))[3] = 0; //alpha 0 + frac += fracstep; + } + } + texnums->fullbright = texture_extension_number++; + GL_Bind(texnums->fullbright); + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + (char *)skins = (char *)inf + inf->ofsskins; + if (e->skinnum >= 0 && e->skinnum < inf->numskins) + skins += e->skinnum; + + if (!skins->texnums) + return NULL; + + frame = cl.time*skins->skinspeed; + frame = frame%skins->texnums; + (char *)texnums = (char *)skins + skins->ofstexnums + frame*sizeof(galiastexnum_t); + memcpy(&cm->texnum, texnums, sizeof(cm->texnum)); + } + return &cm->texnum; + } + + (char *)skins = (char *)inf + inf->ofsskins; + if (e->skinnum >= 0 && e->skinnum < inf->numskins) + skins += e->skinnum; + + if (!skins->texnums) + return NULL; + + frame = cl.time*skins->skinspeed; + frame = frame%skins->texnums; + (char *)texnums = (char *)skins + skins->ofstexnums + frame*sizeof(galiastexnum_t); + + return texnums; +} + + +static int numFacing; +static qbyte *triangleFacing; +static void R_CalcFacing(mesh_t *mesh, vec3_t lightpos) +{ + float *v1, *v2, *v3; + vec3_t d1, d2, norm; + + int i; + + int *indexes = mesh->indexes; + int numtris = mesh->numindexes/3; + + + if (numFacing < numtris) + { + if (triangleFacing) + BZ_Free(triangleFacing); + triangleFacing = BZ_Malloc(sizeof(*triangleFacing)*numtris); + numFacing = numtris; + } + + for (i = 0; i < numtris; i++, indexes+=3) + { + v1 = (float *)(mesh->xyz_array + indexes[0]); + v2 = (float *)(mesh->xyz_array + indexes[1]); + v3 = (float *)(mesh->xyz_array + indexes[2]); + + VectorSubtract(v1, v2, d1); + VectorSubtract(v3, v2, d2); + CrossProduct(d1, d2, norm); + + triangleFacing[i] = (( lightpos[0] - v1[0] ) * norm[0] + ( lightpos[1] - v1[1] ) * norm[1] + ( lightpos[2] - v1[2] ) * norm[2]) > 0; + } +} + +#define PROJECTION_DISTANCE 30000 +static int numProjectedShadowVerts; +static vec3_t *ProjectedShadowVerts; +static void R_ProjectShadowVolume(mesh_t *mesh, vec3_t lightpos) +{ + int numverts = mesh->numvertexes; + int i; + vec4_t *input = mesh->xyz_array; + vec3_t *projected; + if (numProjectedShadowVerts < numverts) + { + if (ProjectedShadowVerts) + BZ_Free(ProjectedShadowVerts); + ProjectedShadowVerts = BZ_Malloc(sizeof(*ProjectedShadowVerts)*numverts); + numProjectedShadowVerts = numverts; + } + projected = ProjectedShadowVerts; + for (i = 0; i < numverts; i++) + { + projected[i][0] = input[i][0] + (input[i][0]-lightpos[0])*PROJECTION_DISTANCE; + projected[i][1] = input[i][1] + (input[i][1]-lightpos[1])*PROJECTION_DISTANCE; + projected[i][2] = input[i][2] + (input[i][2]-lightpos[2])*PROJECTION_DISTANCE; + } +} + +static void R_DrawShadowVolume(mesh_t *mesh) +{ + int t; + vec3_t *proj = ProjectedShadowVerts; + vec4_t *verts = mesh->xyz_array; + int *indexes = mesh->indexes; + int *neighbours = mesh->trneighbors; + int numtris = mesh->numindexes/3; + + glBegin(GL_TRIANGLES); + for (t = 0; t < numtris; t++) + { + if (triangleFacing[t]) + { + //draw front + glVertex3fv(verts[indexes[t*3+0]]); + glVertex3fv(verts[indexes[t*3+1]]); + glVertex3fv(verts[indexes[t*3+2]]); + + //draw back + glVertex3fv(proj[indexes[t*3+1]]); + glVertex3fv(proj[indexes[t*3+0]]); + glVertex3fv(proj[indexes[t*3+2]]); + + //draw side caps + if (neighbours[t*3+0] < 0 || !triangleFacing[neighbours[t*3+0]]) + { + glVertex3fv(verts[indexes[t*3+1]]); + glVertex3fv(verts[indexes[t*3+0]]); + glVertex3fv(proj [indexes[t*3+0]]); + glVertex3fv(verts[indexes[t*3+1]]); + glVertex3fv(proj [indexes[t*3+0]]); + glVertex3fv(proj [indexes[t*3+1]]); + } + + if (neighbours[t*3+1] < 0 || !triangleFacing[neighbours[t*3+1]]) + { + glVertex3fv(verts[indexes[t*3+2]]); + glVertex3fv(verts[indexes[t*3+1]]); + glVertex3fv(proj [indexes[t*3+1]]); + glVertex3fv(verts[indexes[t*3+2]]); + glVertex3fv(proj [indexes[t*3+1]]); + glVertex3fv(proj [indexes[t*3+2]]); + } + + if (neighbours[t*3+2] < 0 || !triangleFacing[neighbours[t*3+2]]) + { + glVertex3fv(verts[indexes[t*3+0]]); + glVertex3fv(verts[indexes[t*3+2]]); + glVertex3fv(proj [indexes[t*3+2]]); + glVertex3fv(verts[indexes[t*3+0]]); + glVertex3fv(proj [indexes[t*3+2]]); + glVertex3fv(proj [indexes[t*3+0]]); + } + } + } + glEnd(); +} + +void R_DrawGAliasModel (entity_t *e) +{ + model_t *clmodel = e->model; + vec3_t mins, maxs; + vec3_t dist; + vec_t add; + int i; + galiasinfo_t *inf; + mesh_t mesh; + galiastexnum_t *skin; + float entScale; + vec3_t lightdir; + + float tmatrix[3][4]; + + if (e->flags & Q2RF_VIEWERMODEL && e->keynum == cl.playernum[r_refdef.currentplayernum]+1) + return; + + VectorAdd (e->origin, clmodel->mins, mins); + VectorAdd (e->origin, clmodel->maxs, maxs); + + if (!(e->flags & Q2RF_WEAPONMODEL)) + if (R_CullBox (mins, maxs)) + return; + + if (!(r_refdef.flags & 1)) //RDF_NOWORLDMODEL + { + cl.worldmodel->funcs.LightPointValues(e->origin, shadelight, ambientlight, lightdir); + } + else + { + ambientlight[0] = ambientlight[1] = ambientlight[2] = shadelight[0] = shadelight[1] = shadelight[2] = 255; + lightdir[0] = 0; + lightdir[1] = 1; + lightdir[2] = 1; + } + + for (i=0 ; i= cl.time) + { + VectorSubtract (e->origin, + cl_dlights[i].origin, + dist); + add = cl_dlights[i].radius - Length(dist); + + if (add > 0) { + add*=5; + ambientlight[0] += add * cl_dlights[i].color[0]; + ambientlight[1] += add * cl_dlights[i].color[1]; + ambientlight[2] += add * cl_dlights[i].color[2]; + //ZOID models should be affected by dlights as well + shadelight[0] += add * cl_dlights[i].color[0]; + shadelight[1] += add * cl_dlights[i].color[1]; + shadelight[2] += add * cl_dlights[i].color[2]; + } + } + } + + for (i = 0; i < 3; i++) //clamp light so it doesn't get vulgar. + { + if (ambientlight[i] > 128) + ambientlight[i] = 128; + if (ambientlight[i] + shadelight[i] > 192) + shadelight[i] = 192 - ambientlight[i]; + } + + if (e->flags & Q2RF_WEAPONMODEL) + { + for (i = 0; i < 3; i++) + { + if (ambientlight[i] < 24) + ambientlight[i] = shadelight[i] = 24; + } + } + +//MORE HUGE HACKS! WHEN WILL THEY CEASE! + // clamp lighting so it doesn't overbright as much + // ZOID: never allow players to go totally black + if (!strcmp(clmodel->name, "progs/player.mdl")) + { + float fb = r_fullbrightSkins.value; + if (fb > cls.allow_fbskins) + fb = cls.allow_fbskins; + if (fb < 0) + fb = 0; + if (fb) + { + for (i = 0; i < 3; i++) + { + ambientlight[i] = max(ambientlight[i], 8 + fb * 120); + shadelight[i] = max(shadelight[i], 8 + fb * 120); + } + } + for (i = 0; i < 3; i++) + { + if (ambientlight[i] < 8) + ambientlight[i] = shadelight[i] = 8; + } + + } + for (i = 0; i < 3; i++) + { + if (ambientlight[i] > 128) + ambientlight[i] = 128; + + shadelight[i] /= 200.0/255; + ambientlight[i] /= 200.0/255; + } + + if ((e->drawflags & MLS_MASKIN) == MLS_ABSLIGHT) + { + shadelight[0] = shadelight[1] = shadelight[2] = e->abslight; + ambientlight[0] = ambientlight[1] = ambientlight[2] = 0; + } + +//#define SHOWLIGHTDIR + { //lightdir is absolute, shadevector is relative + vec3_t entaxis[3]; + e->angles[0]*=-1; + AngleVectors(e->angles, entaxis[0], entaxis[1], entaxis[2]); + e->angles[0]*=-1; + entaxis[1][0]*=-1; + entaxis[1][1]*=-1; + entaxis[1][2]*=-1; + shadevector[0] = DotProduct(lightdir, entaxis[0]); + shadevector[1] = DotProduct(lightdir, entaxis[1]); + shadevector[2] = DotProduct(lightdir, entaxis[2]); + VectorNormalize(shadevector); + + VectorCopy(shadevector, mesh.lightaxis[2]); + VectorVectors(mesh.lightaxis[2], mesh.lightaxis[1], mesh.lightaxis[0]); + mesh.lightaxis[0][0]*=-1; + mesh.lightaxis[0][1]*=-1; + mesh.lightaxis[0][2]*=-1; + } + /* + an = e->angles[1]/180*M_PI; + shadevector[0] = cos(-an); + shadevector[1] = sin(-an); + shadevector[2] = 1; + VectorNormalize (shadevector); + */ + + GL_DisableMultitexture(); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + if (gl_smoothmodels.value) + glShadeModel (GL_SMOOTH); + if (gl_affinemodels.value) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + + glDisable (GL_ALPHA_TEST); + + if (e->flags & Q2RF_DEPTHHACK) + glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); + +// glColor3f( 1,1,1); + if ((e->model->flags & EF_SPECIAL_TRANS)) //hexen2 flags. + { + glEnable (GL_BLEND); + glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); +// glColor3f( 1,1,1); + glDisable( GL_CULL_FACE ); + } + else if (e->drawflags & DRF_TRANSLUCENT) + { + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + e->alpha = r_wateralpha.value; +// glColor4f( 1,1,1,r_wateralpha.value); + } + else if ((e->model->flags & EF_TRANSPARENT)) + { + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +// glColor3f( 1,1,1); + } + else if ((e->model->flags & EF_HOLEY)) + { + glEnable (GL_ALPHA_TEST); +// glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +// glColor3f( 1,1,1); + } + else if (e->alpha < 1) + { + glEnable(GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else + { + glDisable(GL_BLEND); + } + // glEnable (GL_ALPHA_TEST); + + glPushMatrix(); + R_RotateForEntity(e); + + if (e->scale != 1 && e->scale != 0) //hexen 2 stuff + { + vec3_t scale; + vec3_t scale_origin; + float xyfact, zfact; + scale[0] = (clmodel->maxs[0]-clmodel->mins[0])/255; + scale[1] = (clmodel->maxs[1]-clmodel->mins[1])/255; + scale[2] = (clmodel->maxs[2]-clmodel->mins[2])/255; + scale_origin[0] = clmodel->mins[0]; + scale_origin[1] = clmodel->mins[1]; + scale_origin[2] = clmodel->mins[2]; + +/* glScalef( 1/scale[0], + 1/scale[1], + 1/scale[2]); + glTranslatef ( -scale_origin[0], + -scale_origin[1], + -scale_origin[2]); +*/ + + if(e->scale != 0 && e->scale != 1) + { + entScale = (float)e->scale; + switch(e->drawflags&SCALE_TYPE_MASKIN) + { + default: + case SCALE_TYPE_UNIFORM: + tmatrix[0][0] = scale[0]*entScale; + tmatrix[1][1] = scale[1]*entScale; + tmatrix[2][2] = scale[2]*entScale; + xyfact = zfact = (entScale-1.0)*127.95; + break; + case SCALE_TYPE_XYONLY: + tmatrix[0][0] = scale[0]*entScale; + tmatrix[1][1] = scale[1]*entScale; + tmatrix[2][2] = scale[2]; + xyfact = (entScale-1.0)*127.95; + zfact = 1.0; + break; + case SCALE_TYPE_ZONLY: + tmatrix[0][0] = scale[0]; + tmatrix[1][1] = scale[1]; + tmatrix[2][2] = scale[2]*entScale; + xyfact = 1.0; + zfact = (entScale-1.0)*127.95; + break; + } + switch(currententity->drawflags&SCALE_ORIGIN_MASKIN) + { + default: + case SCALE_ORIGIN_CENTER: + tmatrix[0][3] = scale_origin[0]-scale[0]*xyfact; + tmatrix[1][3] = scale_origin[1]-scale[1]*xyfact; + tmatrix[2][3] = scale_origin[2]-scale[2]*zfact; + break; + case SCALE_ORIGIN_BOTTOM: + tmatrix[0][3] = scale_origin[0]-scale[0]*xyfact; + tmatrix[1][3] = scale_origin[1]-scale[1]*xyfact; + tmatrix[2][3] = scale_origin[2]; + break; + case SCALE_ORIGIN_TOP: + tmatrix[0][3] = scale_origin[0]-scale[0]*xyfact; + tmatrix[1][3] = scale_origin[1]-scale[1]*xyfact; + tmatrix[2][3] = scale_origin[2]-scale[2]*zfact*2.0; + break; + } + } + else + { + tmatrix[0][0] = scale[0]; + tmatrix[1][1] = scale[1]; + tmatrix[2][2] = scale[2]; + tmatrix[0][3] = scale_origin[0]; + tmatrix[1][3] = scale_origin[1]; + tmatrix[2][3] = scale_origin[2]; + } + +/* if(clmodel->flags&EF_ROTATE) + { // Floating motion + tmatrix[2][3] += sin(currententity->origin[0] + +currententity->origin[1]+(cl.time*3))*5.5; + }*/ + + glTranslatef (tmatrix[0][3],tmatrix[1][3],tmatrix[2][3]); + glScalef (tmatrix[0][0],tmatrix[1][1],tmatrix[2][2]); + + glScalef( 1/scale[0], + 1/scale[1], + 1/scale[2]); + glTranslatef ( -scale_origin[0], + -scale_origin[1], + -scale_origin[2]); + } + + inf = GLMod_Extradata (clmodel); + if (qglPNTrianglesfATI && gl_ati_truform.value) + glEnable(GL_PN_TRIANGLES_ATI); + + while(inf) + { + R_GAliasBuildMesh(&mesh, inf, e->frame, e->oldframe, e->lerptime, e->alpha); + skin = GL_ChooseSkin(inf, clmodel->name, e); + c_alias_polys += mesh.numindexes/3; + if (!skin) + GL_DrawMesh(&mesh, NULL, 1, 0); + else + { + if (skin->bump) + GL_DrawMeshBump(&mesh, skin->base, 0, skin->bump, 0); + else + GL_DrawMesh(&mesh, NULL, skin->base, 0); + if (skin->fullbright && r_fb_models.value && cls.allow_luma) + { + mesh.colors_array = NULL; + glEnable(GL_BLEND); + glColor4f(1, 1, 1, e->alpha*r_fb_models.value); + c_alias_polys += mesh.numindexes/3; + GL_DrawMesh(&mesh, NULL, 0, skin->fullbright); + } + + } + if (inf->nextsurf) + (char *)inf = (char *)inf + inf->nextsurf; + else + inf = NULL; + } + + if (qglPNTrianglesfATI && gl_ati_truform.value) + glDisable(GL_PN_TRIANGLES_ATI); + +#ifdef SHOWLIGHTDIR //testing + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINES); + glColor3f(1,0,0); + glVertex3f( 0, + 0, + 0); + glVertex3f( 100*mesh.lightaxis[0][0], + 100*mesh.lightaxis[0][1], + 100*mesh.lightaxis[0][2]); + +glColor3f(0,1,0); + glVertex3f( 0, + 0, + 0); + glVertex3f( 100*mesh.lightaxis[1][0], + 100*mesh.lightaxis[1][1], + 100*mesh.lightaxis[1][2]); + +glColor3f(0,0,1); + glVertex3f( 0, + 0, + 0); + glVertex3f( 100*mesh.lightaxis[2][0], + 100*mesh.lightaxis[2][1], + 100*mesh.lightaxis[2][2]); + glEnd(); + glEnable(GL_TEXTURE_2D); +#endif + + glPopMatrix(); + + glDisable(GL_BLEND); + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glEnable(GL_TEXTURE_2D); + + glShadeModel (GL_FLAT); + if (gl_affinemodels.value) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + if (e->flags & Q2RF_DEPTHHACK) + glDepthRange (gldepthmin, gldepthmax); + + if ((currententity->model->flags & EF_SPECIAL_TRANS) && gl_cull.value) + glEnable( GL_CULL_FACE ); + if ((currententity->model->flags & EF_HOLEY)) + glDisable( GL_ALPHA_TEST ); + +#ifdef SHOWLIGHTDIR //testing + glDisable(GL_TEXTURE_2D); + glColor3f(1,1,1); + glBegin(GL_LINES); + glVertex3f( currententity->origin[0], + currententity->origin[1], + currententity->origin[2]); + glVertex3f( currententity->origin[0]+100*lightdir[0], + currententity->origin[1]+100*lightdir[1], + currententity->origin[2]+100*lightdir[2]); + glEnd(); + glEnable(GL_TEXTURE_2D); +#endif +} + +void R_DrawGAliasModelLighting (entity_t *e) +{ + model_t *clmodel = e->model; + vec3_t mins, maxs; + vec3_t dist; + vec_t add, an; + vec3_t lightdir; + int i; + galiasinfo_t *inf; + mesh_t mesh; + + if (e->flags & Q2RF_VIEWERMODEL) + return; + + //Total insanity with r_shadows 2... +// if (!strcmp (clmodel->name, "progs/flame2.mdl")) +// CL_NewDlight (e, e->origin[0]-1, e->origin[1]+1, e->origin[2]+24, 200 + (rand()&31), host_frametime*2, 3); + +// if (!strcmp (clmodel->name, "progs/armor.mdl")) +// CL_NewDlight (e->keynum, e->origin[0]-1, e->origin[1]+1, e->origin[2]+25, 200 + (rand()&31), host_frametime*2, 3); + + VectorAdd (e->origin, clmodel->mins, mins); + VectorAdd (e->origin, clmodel->maxs, maxs); + + if (!(e->flags & Q2RF_WEAPONMODEL)) + if (R_CullBox (mins, maxs)) + return; + + if (!(r_refdef.flags & 1)) //RDF_NOWORLDMODEL + { + cl.worldmodel->funcs.LightPointValues(e->origin, shadelight, ambientlight, lightdir); + } + else + { + ambientlight[0] = ambientlight[1] = ambientlight[2] = shadelight[0] = shadelight[1] = shadelight[2] = 255; + lightdir[0] = 0; + lightdir[1] = 1; + lightdir[2] = 1; + } + + if (e->flags & 4) + { + if (ambientlight[0] < 24) + ambientlight[0] = shadelight[0] = 24; + if (ambientlight[1] < 24) + ambientlight[1] = shadelight[1] = 24; + if (ambientlight[2] < 24) + ambientlight[2] = shadelight[2] = 24; + } + + for (i=0 ; i= cl.time) + { + VectorSubtract (e->origin, + cl_dlights[i].origin, + dist); + add = cl_dlights[i].radius - Length(dist); + + if (add > 0) { + add*=5; + ambientlight[0] += add * cl_dlights[i].color[0]; + ambientlight[1] += add * cl_dlights[i].color[1]; + ambientlight[2] += add * cl_dlights[i].color[2]; + //ZOID models should be affected by dlights as well + shadelight[0] += add * cl_dlights[i].color[0]; + shadelight[1] += add * cl_dlights[i].color[1]; + shadelight[2] += add * cl_dlights[i].color[2]; + } + } + } + +//MORE HUGE HACKS! WHEN WILL THEY CEASE! + // clamp lighting so it doesn't overbright as much + // ZOID: never allow players to go totally black + if (!strcmp(clmodel->name, "progs/player.mdl")) + { + for (i = 0; i < 3; i++) + if (ambientlight[i] < 8) + ambientlight[i] = shadelight[i] = 8; + } + for (i = 0; i < 3; i++) + { + if (ambientlight[i] > 128) + ambientlight[i] = 128; + + shadelight[i] /= 200.0/255; + ambientlight[i] /= 200.0/255; + } + + an = e->angles[1]/180*M_PI; + shadevector[0] = cos(-an); + shadevector[1] = sin(-an); + shadevector[2] = 1; + VectorNormalize (shadevector); + + GL_DisableMultitexture(); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + if (gl_smoothmodels.value) + glShadeModel (GL_SMOOTH); + if (gl_affinemodels.value) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + + + if (e->flags & Q2RF_DEPTHHACK) + glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); + + glPushMatrix(); + R_RotateForEntity(e); + inf = GLMod_Extradata (clmodel); + if (gl_ati_truform.value) + glEnable(GL_PN_TRIANGLES_ATI); + while(inf) + { + R_GAliasBuildMesh(&mesh, inf, e->frame, e->oldframe, e->lerptime, e->alpha); + mesh.colors_array = NULL; + GL_DrawMesh(&mesh, NULL, 0, 0); + + if (inf->nextsurf) + (char *)inf = (char *)inf + inf->nextsurf; + else + inf = NULL; + } + glPopMatrix(); + if (gl_ati_truform.value) + glDisable(GL_PN_TRIANGLES_ATI); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glShadeModel (GL_FLAT); + if (gl_affinemodels.value) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + if (e->flags & Q2RF_DEPTHHACK) + glDepthRange (gldepthmin, gldepthmax); +} + +//returns result in the form of the result vector +void RotateLightVector(vec3_t angles, vec3_t origin, vec3_t lightpoint, vec3_t result) +{ + vec3_t f, r, u, offs; + + angles[0]*=-1; + AngleVectors(angles, f, r, u); + angles[0]*=-1; + + offs[0] = lightpoint[0] - origin[0]; + offs[1] = lightpoint[1] - origin[1]; + offs[2] = lightpoint[2] - origin[2]; + + result[0] = DotProduct (offs, f); + result[1] = -DotProduct (offs, r); + result[2] = DotProduct (offs, u); +} + +//FIXME: Be less agressive. +//This function will have to be called twice (for geforce cards), with the same data, so do the building once and rendering twice. +void R_DrawGAliasShadowVolume(entity_t *e, vec3_t lightpos, float radius) +{ + model_t *clmodel = e->model; + galiasinfo_t *inf; + mesh_t mesh; + vec3_t lightorg; + + if (!strcmp (clmodel->name, "progs/flame2.mdl")) + return; + if (!strncmp (clmodel->name, "progs/bolt", 10)) + return; + if (r_noaliasshadows.value) + return; + + RotateLightVector(e->angles, e->origin, lightpos, lightorg); + + if (Length(lightorg) > radius + clmodel->radius) + return; + + glPushMatrix(); + R_RotateForEntity(e); + + + inf = GLMod_Extradata (clmodel); + while(inf) + { + if (inf->ofs_trineighbours) + { + R_GAliasBuildMesh(&mesh, inf, e->frame, e->oldframe, e->lerptime, e->alpha); + R_CalcFacing(&mesh, lightorg); + R_ProjectShadowVolume(&mesh, lightorg); + R_DrawShadowVolume(&mesh); + } + + if (inf->nextsurf) + (char *)inf = (char *)inf + inf->nextsurf; + else + inf = NULL; + } + + glPopMatrix(); +} + + + + + + +static int R_FindTriangleWithEdge ( int *indexes, int numtris, int start, int end, int ignore) +{ + int i; + int match, count; + + count = 0; + match = -1; + + for (i = 0; i < numtris; i++, indexes += 3) + { + if ( (indexes[0] == start && indexes[1] == end) + || (indexes[1] == start && indexes[2] == end) + || (indexes[2] == start && indexes[0] == end) ) { + if (i != ignore) + match = i; + count++; + } else if ( (indexes[1] == start && indexes[0] == end) + || (indexes[2] == start && indexes[1] == end) + || (indexes[0] == start && indexes[2] == end) ) { + count++; + } + } + + // detect edges shared by three triangles and make them seams + if (count > 2) + match = -1; + + return match; +} + +static void R_BuildTriangleNeighbours ( int *neighbours, int *indexes, int numtris ) +{ + int i, *n; + int *index; + + for (i = 0, index = indexes, n = neighbours; i < numtris; i++, index += 3, n += 3) + { + n[0] = R_FindTriangleWithEdge (indexes, numtris, index[1], index[0], i); + n[1] = R_FindTriangleWithEdge (indexes, numtris, index[2], index[1], i); + n[2] = R_FindTriangleWithEdge (indexes, numtris, index[0], index[2], i); + } +} + + + + + +void GL_GenerateNormals(float *orgs, float *normals, int *indicies, int numtris, int numverts) +{ + vec3_t d1, d2; + vec3_t norm; + int t, i, v1, v2, v3; + int tricounts[MD2MAX_VERTS]; + vec3_t combined[MD2MAX_VERTS]; + int triremap[MD2MAX_VERTS]; + if (numverts > MD2MAX_VERTS) + return; //not an issue, you just loose the normals. + + memset(triremap, 0, numverts*sizeof(triremap[0])); + + v2=0; + for (i = 0; i < numverts; i++) //weld points + { + for (v1 = 0; v1 < v2; v1++) + { + if (orgs[i*3+0] == combined[v1][0] && + orgs[i*3+1] == combined[v1][1] && + orgs[i*3+2] == combined[v1][2]) + { + triremap[i] = v1; + break; + } + } + if (v1 == v2) + { + combined[v1][0] = orgs[i*3+0]; + combined[v1][1] = orgs[i*3+1]; + combined[v1][2] = orgs[i*3+2]; + v2++; + + triremap[i] = v1; + } + } + memset(tricounts, 0, v2*sizeof(tricounts[0])); + memset(combined, 0, v2*sizeof(*combined)); + + for (t = 0; t < numtris; t++) + { + v1 = triremap[indicies[t*3]]; + v2 = triremap[indicies[t*3+1]]; + v3 = triremap[indicies[t*3+2]]; + + VectorSubtract((orgs+v2*3), (orgs+v1*3), d1); + VectorSubtract((orgs+v3*3), (orgs+v1*3), d2); + CrossProduct(d1, d2, norm); + VectorNormalize(norm); + + VectorAdd(norm, combined[v1], combined[v1]); + VectorAdd(norm, combined[v2], combined[v2]); + VectorAdd(norm, combined[v3], combined[v3]); + + tricounts[v1]++; + tricounts[v2]++; + tricounts[v3]++; + } + + for (i = 0; i < numverts; i++) + { + if (tricounts[triremap[i]]) + { + VectorScale(combined[triremap[i]], 1.0f/tricounts[triremap[i]], normals+i*3); + } + } +} + + + +//Q1 model loading +#if 1 +static galiasinfo_t *galias; +static model_t *loadmodel; +static dmdl_t *pq1inmodel; +#define NUMVERTEXNORMALS 162 +extern float r_avertexnormals[NUMVERTEXNORMALS][3]; +static void *Q1_LoadFrameGroup (daliasframetype_t *pframetype, int *seamremaps) +{ + galiaspose_t *pose; + galiasgroup_t *frame; + dtrivertx_t *pinframe; + int i, j, k; + daliasgroup_t *ingroup; + daliasinterval_t *intervals; + + vec3_t *normals; + vec3_t *verts; + + (char *)frame = (char *)galias + galias->groupofs; + + for (i = 0; i < pq1inmodel->numframes; i++) + { + switch(LittleLong(pframetype->type)) + { + case ALIAS_SINGLE: + (char *)pinframe = (char *)(pframetype+1)+sizeof(daliasframe_t); + pose = (galiaspose_t *)Hunk_Alloc(sizeof(galiaspose_t) + sizeof(vec3_t)*2*galias->numverts); + frame->poseofs = (char *)pose - (char *)frame; + frame->numposes = 1; + galias->groups++; + + verts = (vec3_t *)(pose+1); + normals = &verts[galias->numverts]; + pose->ofsverts = (char *)verts - (char *)pose; + pose->ofsnormals = (char *)normals - (char *)pose; + + for (j = 0; j < pq1inmodel->numverts; j++) + { + verts[j][0] = pinframe[j].v[0]*pq1inmodel->scale[0]+pq1inmodel->scale_origin[0]; + verts[j][1] = pinframe[j].v[1]*pq1inmodel->scale[1]+pq1inmodel->scale_origin[1]; + verts[j][2] = pinframe[j].v[2]*pq1inmodel->scale[2]+pq1inmodel->scale_origin[2]; + + VectorCopy(r_avertexnormals[pinframe[j].lightnormalindex], normals[j]); + + if (seamremaps[j] != j) + { + VectorCopy(verts[j], verts[seamremaps[j]]); + VectorCopy(normals[j], normals[seamremaps[j]]); + } + } + +// GL_GenerateNormals((float*)verts, (float*)normals, (int *)((char *)galias + galias->ofs_indexes), galias->numindexes/3, galias->numverts); + + (char *)pframetype = (char *)&pinframe[pq1inmodel->numverts]; + break; + case ALIAS_GROUP: + (char *)ingroup = (char *)(pframetype+1); + + pose = (galiaspose_t *)Hunk_Alloc(ingroup->numframes*(sizeof(galiaspose_t) + sizeof(vec3_t)*2*galias->numverts)); + frame->poseofs = (char *)pose - (char *)frame; + frame->numposes = LittleLong(ingroup->numframes); + galias->groups++; + + verts = (vec3_t *)(pose+frame->numposes); + normals = &verts[galias->numverts]; + + intervals = (daliasinterval_t *)(ingroup+1); + frame->rate = 1/LittleFloat(intervals->interval); + + pinframe = (dtrivertx_t *)(intervals+frame->numposes); + for (k = 0; k < frame->numposes; k++) + { + pose->ofsverts = (char *)verts - (char *)pose; + pose->ofsnormals = (char *)normals - (char *)pose; + + pinframe = (dtrivertx_t *)((char *)pinframe + sizeof(daliasframe_t)); + for (j = 0; j < pq1inmodel->numverts; j++) + { + verts[j][0] = pinframe[j].v[0]*pq1inmodel->scale[0]+pq1inmodel->scale_origin[0]; + verts[j][1] = pinframe[j].v[1]*pq1inmodel->scale[1]+pq1inmodel->scale_origin[1]; + verts[j][2] = pinframe[j].v[2]*pq1inmodel->scale[2]+pq1inmodel->scale_origin[2]; + + VectorCopy(r_avertexnormals[pinframe[j].lightnormalindex], normals[j]); + if (seamremaps[j] != j) + { + VectorCopy(verts[j], verts[seamremaps[j]]); + VectorCopy(normals[j], normals[seamremaps[j]]); + } + } + verts = &normals[galias->numverts]; + normals = &verts[galias->numverts]; + pose++; + + pinframe += pq1inmodel->numverts; + } + +// GL_GenerateNormals((float*)verts, (float*)normals, (int *)((char *)galias + galias->ofs_indexes), galias->numindexes/3, galias->numverts); + + (char *)pframetype = (char *)pinframe; + break; + default: + Sys_Error("Bad frame type\n"); + } + frame++; + } + return pframetype; +} + +static void *Q1_LoadSkins (daliasskintype_t *pskintype, qboolean alpha) +{ + extern int gl_bumpmappingpossible; + galiastexnum_t *texnums; + char skinname[MAX_QPATH]; + int i; + int s, t; + int *count; + float *intervals; + qbyte *data, *saved; + galiasskin_t *outskin = (galiasskin_t *)((char *)galias + galias->ofsskins); + + int texture; + int fbtexture; + + s = pq1inmodel->skinwidth*pq1inmodel->skinheight; + for (i = 0; i < pq1inmodel->numskins; i++) + { + switch(LittleLong(pskintype->type)) + { + case ALIAS_SKIN_SINGLE: + outskin->skinwidth = pq1inmodel->skinwidth; + outskin->skinheight = pq1inmodel->skinheight; + + sprintf(skinname, "%s_%i", loadname, i); + texture = Mod_LoadReplacementTexture(skinname, true, false); + if (!texture) + { + sprintf(skinname, "textures/models/%s_%i", loadname, i); + texture = Mod_LoadReplacementTexture(skinname, true, false); + if (texture && r_fb_models.value) + { + sprintf(skinname, "textures/models/%s_%i_luma", loadname, i); + fbtexture = Mod_LoadReplacementTexture(skinname, true, true); + } + else + fbtexture = 0; + } + else if (texture && r_fb_models.value) + { + sprintf(skinname, "%s_%i_luma", loadname, i); + fbtexture = Mod_LoadReplacementTexture(skinname, true, true); + } + else + fbtexture = 0; + + if (!texture) + { + texnums = Hunk_Alloc(sizeof(*texnums)+s); + saved = (qbyte*)(texnums+1); + outskin->ofstexels = (qbyte *)(saved) - (qbyte *)outskin; + memcpy(saved, pskintype+1, s); + GLMod_FloodFillSkin(saved, outskin->skinwidth, outskin->skinheight); + sprintf(skinname, "%s_%i", loadname, i); + texture = GL_LoadTexture(skinname, outskin->skinwidth, outskin->skinheight, saved, true, alpha); + if (r_fb_models.value) + { + sprintf(skinname, "%s_%i_luma", loadname, i); + fbtexture = GL_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, true, true); + } + } + else + { + texnums = Hunk_Alloc(sizeof(*texnums)); + outskin->ofstexels = 0; + } + outskin->texnums=1; + + outskin->ofstexnums = (char *)texnums - (char *)outskin; + + texnums->base = texture; + texnums->fullbright = fbtexture; + + (char *)pskintype = (char *)(pskintype+1)+s; + break; + + default: + outskin->skinwidth = pq1inmodel->skinwidth; + outskin->skinheight = pq1inmodel->skinheight; + count = (int *)(pskintype+1); + intervals = (float *)(count+1); + outskin->texnums = LittleLong(*count); + data = (qbyte *)(intervals + outskin->texnums); + texnums = Hunk_Alloc(sizeof(*texnums)*outskin->texnums); + outskin->ofstexnums = (char *)texnums - (char *)outskin; + outskin->ofstexels = 0; + for (t = 0; t < outskin->texnums; t++,data+=s, texnums++) + { + sprintf(skinname, "%s_%i%c", loadname, i, t+'a'); + texture = Mod_LoadReplacementTexture(skinname, true, false); + if (texture) + { + texnums->base = texture; + if (r_fb_models.value) + { + sprintf(skinname, "%s_%i%c_luma", loadname, i, t+'a'); + texnums->fullbright = Mod_LoadReplacementTexture(skinname, true, true); + } + } + else + { + if (t == 0) + { + saved = Hunk_Alloc(s); + outskin->ofstexels = (qbyte *)(saved) - (qbyte *)outskin; + } + else + saved = BZ_Malloc(s); + memcpy(saved, pskintype+1, s); + GLMod_FloodFillSkin(saved, outskin->skinwidth, outskin->skinheight); + sprintf(skinname, "%s_%i%c", loadname, i, t+'a'); + texnums->base = GL_LoadTexture(skinname, outskin->skinwidth, outskin->skinheight, saved, true, alpha); + + if (gl_bumpmappingpossible) + { + char name[MAX_QPATH]; + COM_StripExtension(skinname, name); //go for the normalmap + strcat(name, "_norm"); + texnums->bump = Mod_LoadHiResTexture(name, true, true); + if (!texnums->bump) + { + strcpy(name, loadmodel->name); + COM_StripExtension(COM_SkipPath(skinname), COM_SkipPath(name)); + strcat(name, "_norm"); + texnums->bump = Mod_LoadHiResTexture(name, true, true); + if (!texnums->bump) + { + COM_StripExtension(skinname, name); //bother, go for heightmap and convert + strcat(name, "_bump"); + texnums->bump = Mod_LoadBumpmapTexture(name); + if (!texnums->bump) + { + strcpy(name, loadmodel->name); + strcpy(COM_SkipPath(name), COM_SkipPath(skinname)); //eviile eh? + COM_StripExtension(name, name); + strcat(name, "_bump"); + texnums->bump = Mod_LoadBumpmapTexture(name); + } + } + } + } + + if (r_fb_models.value) + { + sprintf(skinname, "%s_%i%c_luma", loadname, i, t+'a'); + texnums->fullbright = GL_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, true, true); + } + + if (t != 0) //only keep the first. + BZ_Free(saved); + } + } + (char *)pskintype = (char *)data; + break; + } + outskin++; + } + galias->numskins=pq1inmodel->numskins; + return pskintype; +} + +void GL_LoadQ1Model (model_t *mod, void *buffer) +{ + vec2_t *st_array; + int hunkstart, hunkend, hunktotal; + int version; + int i, j, onseams; + dstvert_t *pinstverts; + dtriangle_t *pintriangles; + int *seamremap; + int *indexes; + + int size; + + loadmodel=mod; + + //we've got to have this bit + if (!strcmp(loadmodel->name, "progs/player.mdl") || + !strcmp(loadmodel->name, "progs/eyes.mdl")) + { + unsigned short crc; + qbyte *p; + int len; + char st[40]; + + CRC_Init(&crc); + for (len = com_filesize, p = buffer; len; len--, p++) + CRC_ProcessByte(&crc, *p); + + sprintf(st, "%d", (int) crc); + Info_SetValueForKey (cls.userinfo, + !strcmp(loadmodel->name, "progs/player.mdl") ? pmodel_name : emodel_name, + st, MAX_INFO_STRING); + + if (cls.state >= ca_connected) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + sprintf(st, "setinfo %s %d", + !strcmp(loadmodel->name, "progs/player.mdl") ? pmodel_name : emodel_name, + (int)crc); + SZ_Print (&cls.netchan.message, st); + } + } + + hunkstart = Hunk_LowMark (); + + pq1inmodel = (dmdl_t *)buffer; + + version = LittleLong (pq1inmodel->version); + if (version != ALIAS_VERSION) + Sys_Error ("%s has wrong version number (%i should be %i)", + mod->name, version, ALIAS_VERSION); + + if (pq1inmodel->numframes < 1 || + pq1inmodel->numskins < 1 || + pq1inmodel->numtris < 1 || + pq1inmodel->numverts < 3 || + pq1inmodel->skinheight < 1 || + pq1inmodel->skinwidth < 1) + Sys_Error("Model %s has an invalid quantity\n", mod->name); + + mod->flags = LittleLong (pq1inmodel->flags); + + size = sizeof(galiasinfo_t) + + pq1inmodel->numframes*sizeof(galiasgroup_t) + + pq1inmodel->numskins*sizeof(galiasskin_t); + + galias = Hunk_Alloc(size); + galias->groupofs = sizeof(*galias); + galias->ofsskins = sizeof(*galias)+pq1inmodel->numframes*sizeof(galiasgroup_t); + galias->nextsurf = 0; + +//skins + if( mod->flags & EF_HOLEY ) + pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1), 3); + else if( mod->flags & EF_TRANSPARENT ) + pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1), 2); + else if( mod->flags & EF_SPECIAL_TRANS ) + pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1), 4); + else + pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1), 0); + +// pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1)); + + //count number of verts that are onseam. + for (onseams=0,i = 0; i < pq1inmodel->numverts; i++) + { + if (pinstverts[i].onseam) + onseams++; + } + seamremap = BZ_Malloc(sizeof(int)*pq1inmodel->numverts); + + galias->numverts = pq1inmodel->numverts+onseams; + //st + st_array = Hunk_Alloc(sizeof(*st_array)*(pq1inmodel->numverts+onseams)); + galias->ofs_st_array = (char *)st_array - (char *)galias; + for (j=pq1inmodel->numverts,i = 0; i < pq1inmodel->numverts; i++) + { + st_array[i][0] = LittleLong(pinstverts[i].s)/(float)pq1inmodel->skinwidth; + st_array[i][1] = LittleLong(pinstverts[i].t)/(float)pq1inmodel->skinheight; + + if (pinstverts[i].onseam) + { + st_array[j][0] = st_array[i][0]+0.5; + st_array[j][1] = st_array[i][1]; + seamremap[i] = j; + j++; + } + else + seamremap[i] = i; + } + + //trianglelists; + pintriangles = (dtriangle_t *)&pinstverts[pq1inmodel->numverts]; + + galias->numindexes = pq1inmodel->numtris*3; + indexes = Hunk_Alloc(galias->numindexes*sizeof(*indexes)); + galias->ofs_indexes = (char *)indexes - (char *)galias; + for (i=0 ; inumtris ; i++) + { + if (!pintriangles[i].facesfront) + { + indexes[i*3+0] = seamremap[LittleLong(pintriangles[i].vertindex[0])]; + indexes[i*3+1] = seamremap[LittleLong(pintriangles[i].vertindex[1])]; + indexes[i*3+2] = seamremap[LittleLong(pintriangles[i].vertindex[2])]; + } + else + { + indexes[i*3+0] = LittleLong(pintriangles[i].vertindex[0]); + indexes[i*3+1] = LittleLong(pintriangles[i].vertindex[1]); + indexes[i*3+2] = LittleLong(pintriangles[i].vertindex[2]); + } + } + + //frames + Q1_LoadFrameGroup((daliasframetype_t *)&pintriangles[pq1inmodel->numtris], seamremap); + BZ_Free(seamremap); + + if (r_shadows.value) + { + int *neighbours; + neighbours = Hunk_Alloc(sizeof(int)*3*pq1inmodel->numtris); + galias->ofs_trineighbours = (qbyte *)neighbours - (qbyte *)galias; + R_BuildTriangleNeighbours(neighbours, indexes, pq1inmodel->numtris); + } + + VectorCopy (pq1inmodel->scale_origin, mod->mins); + VectorMA (mod->mins, 255, pq1inmodel->scale, mod->maxs); +// +// move the complete, relocatable alias model to the cache +// + hunkend = Hunk_LowMark (); + hunktotal = hunkend - hunkstart; + + Cache_Alloc (&mod->cache, hunktotal, loadname); + mod->type = mod_alias; + if (!mod->cache.data) + { + Hunk_FreeToLowMark (hunkstart); + return; + } + memcpy (mod->cache.data, galias, hunktotal); + + Hunk_FreeToLowMark (hunkstart); + +} +#endif + + +int Mod_ReadFlagsFromMD1(char *name, int md3version) +{ + dmdl_t *pinmodel; + char fname[MAX_QPATH]; + COM_StripExtension(name, fname); + COM_DefaultExtension(fname, ".mdl"); + + if (!strcmp(name, fname)) //md3 renamed as mdl + { + COM_StripExtension(name, fname); //seeing as the md3 is named over the mdl, + COM_DefaultExtension(fname, ".md1");//read from a file with md1 (one, not an ell) + return 0; + } + + pinmodel = (dmdl_t *)COM_LoadTempFile(fname); + + if (!pinmodel) //not found + return 0; + + if (pinmodel->ident != IDPOLYHEADER) + return 0; + if (pinmodel->version != ALIAS_VERSION) + return 0; + return pinmodel->flags; +} + +#ifdef MD2MODELS + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//Q2 model loading + +static galiasinfo_t *galias; +static model_t *loadmodel; +static md2_t *pq2inmodel; +#define Q2NUMVERTEXNORMALS 162 +extern vec3_t bytedirs[Q2NUMVERTEXNORMALS]; + +static void Q2_LoadSkins(char *skins) +{ + int i; + galiastexnum_t *texnums; + galiasskin_t *outskin = (galiasskin_t *)((char *)galias + galias->ofsskins); + + for (i = 0; i < pq2inmodel->num_skins; i++) + { + texnums = Hunk_Alloc(sizeof(*texnums)); + outskin->ofstexnums = (char *)texnums - (char *)outskin; + outskin->texnums=1; + + texnums->base = Mod_LoadReplacementTexture(skins, true, false); + outskin->skinwidth = 0; + outskin->skinheight = 0; + outskin->skinspeed = 0; + + skins += MD2MAX_SKINNAME; + } +} + +#define MD2_MAX_TRIANGLES 4096 +void GL_LoadQ2Model (model_t *mod, void *buffer) +{ + int hunkstart, hunkend, hunktotal; + int version; + int i, j; + dmd2stvert_t *pinstverts; + dmd2triangle_t *pintri; + int *indexes; + int numindexes; + + vec3_t min; + vec3_t max; + + galiaspose_t *pose; + galiasgroup_t *poutframe; + dmd2aliasframe_t *pinframe; + int framesize; + vec3_t *verts; + vec3_t *normals; + vec2_t *st_array; + + int indremap[MD2_MAX_TRIANGLES*3]; + unsigned short ptempindex[MD2_MAX_TRIANGLES*3], ptempstindex[MD2_MAX_TRIANGLES*3]; + + int numverts; + + int size; + + + loadmodel=mod; + + hunkstart = Hunk_LowMark (); + + pq2inmodel = (md2_t *)buffer; + + version = LittleLong (pq2inmodel->version); + if (version != MD2ALIAS_VERSION) + Sys_Error ("%s has wrong version number (%i should be %i)", + mod->name, version, MD2ALIAS_VERSION); + + if (pq2inmodel->num_frames < 1 || + pq2inmodel->num_skins < 0 || + pq2inmodel->num_tris < 1 || + pq2inmodel->num_xyz < 3 || + pq2inmodel->num_st < 3 || + pq2inmodel->skinheight < 1 || + pq2inmodel->skinwidth < 1) + Sys_Error("Model %s has an invalid quantity\n", mod->name); + + mod->flags = 0; + + size = sizeof(galiasinfo_t) + + pq2inmodel->num_frames*sizeof(galiasgroup_t) + + pq2inmodel->num_skins*sizeof(galiasskin_t); + + galias = Hunk_Alloc(size); + galias->groupofs = sizeof(*galias); + galias->ofsskins = sizeof(*galias)+pq2inmodel->num_frames*sizeof(galiasgroup_t); + galias->nextsurf = 0; + +//skins + Q2_LoadSkins(((char *)pq2inmodel+pq2inmodel->ofs_skins)); + + //trianglelists; + pintri = (dmd2triangle_t *)((char *)pq2inmodel + pq2inmodel->ofs_tris); + + + for (i=0 ; inum_tris ; i++, pintri++) + { + for (j=0 ; j<3 ; j++) + { + ptempindex[i*3+j] = ( unsigned short )LittleShort ( pintri->xyz_index[j] ); + ptempstindex[i*3+j] = ( unsigned short )LittleShort ( pintri->st_index[j] ); + } + } + + numindexes = galias->numindexes = pq2inmodel->num_tris*3; + indexes = Hunk_Alloc(galias->numindexes*sizeof(*indexes)); + galias->ofs_indexes = (char *)indexes - (char *)galias; + memset ( indremap, -1, sizeof(indremap) ); + numverts=0; + + for ( i = 0; i < numindexes; i++ ) + { + if ( indremap[i] != -1 ) { + continue; + } + + for ( j = 0; j < numindexes; j++ ) + { + if ( j == i ) { + continue; + } + + if ( (ptempindex[i] == ptempindex[j]) && (ptempstindex[i] == ptempstindex[j]) ) { + indremap[j] = i; + } + } + } + + // count unique vertexes + for ( i = 0; i < numindexes; i++ ) + { + if ( indremap[i] != -1 ) { + continue; + } + + indexes[i] = numverts++; + indremap[i] = i; + } + + Con_DPrintf ( "%s: remapped %i verts to %i\n", mod->name, pq2inmodel->num_xyz, numverts ); + + galias->numverts = numverts; + + // remap remaining indexes + for ( i = 0; i < numindexes; i++ ) + { + if ( indremap[i] != i ) { + indexes[i] = indexes[indremap[i]]; + } + } + +// s and t vertices + pinstverts = ( dmd2stvert_t * ) ( ( qbyte * )pq2inmodel + LittleLong (pq2inmodel->ofs_st) ); + st_array = Hunk_Alloc(sizeof(*st_array)*(numverts)); + galias->ofs_st_array = (char *)st_array - (char *)galias; + + for (j=0 ; jskinwidth); + st_array[indexes[j]][1] = (float)(((double)LittleShort (pinstverts[ptempstindex[indremap[j]]].t) + 0.5f) /pq2inmodel->skinheight); + } + + //frames + ClearBounds ( mod->mins, mod->maxs ); + + poutframe = (galiasgroup_t*)((char *)galias + galias->groupofs); + framesize = LittleLong (pq2inmodel->framesize); + for (i=0 ; inum_frames ; i++) + { + pose = (galiaspose_t *)Hunk_Alloc(sizeof(galiaspose_t) + sizeof(vec3_t)*2*numverts); + poutframe->poseofs = (char *)pose - (char *)poutframe; + poutframe->numposes = 1; + galias->groups++; + + verts = (vec3_t *)(pose+1); + normals = &verts[galias->numverts]; + pose->ofsverts = (char *)verts - (char *)pose; + pose->ofsnormals = (char *)normals - (char *)pose; + + + pinframe = ( dmd2aliasframe_t * )( ( qbyte * )pq2inmodel + LittleLong (pq2inmodel->ofs_frames) + i * framesize ); + + for (j=0 ; j<3 ; j++) + { + pose->scale[j] = LittleFloat (pinframe->scale[j]); + pose->scale_origin[j] = LittleFloat (pinframe->translate[j]); + } + + for (j=0 ; jscale_origin[0]+pose->scale[0]*pinframe->verts[ptempindex[indremap[j]]].v[0]; + verts[indexes[j]][1] = pose->scale_origin[1]+pose->scale[1]*pinframe->verts[ptempindex[indremap[j]]].v[1]; + verts[indexes[j]][2] = pose->scale_origin[2]+pose->scale[2]*pinframe->verts[ptempindex[indremap[j]]].v[2]; + VectorCopy(bytedirs[pinframe->verts[ptempindex[indremap[j]]].lightnormalindex], normals[indexes[j]]); + } + +// Mod_AliasCalculateVertexNormals ( numindexes, poutindex, numverts, poutvertex, qfalse ); + + VectorCopy ( pose->scale_origin, min ); + VectorMA ( pose->scale_origin, 255, pose->scale, max ); + +// poutframe->radius = RadiusFromBounds ( min, max ); + +// mod->radius = max ( mod->radius, poutframe->radius ); + AddPointToBounds ( min, mod->mins, mod->maxs ); + AddPointToBounds ( max, mod->mins, mod->maxs ); + +// GL_GenerateNormals((float*)verts, (float*)normals, indexes, numindexes/3, numverts); + + poutframe++; + } + + + + + if (r_shadows.value) + { + int *neighbours; + neighbours = Hunk_Alloc(sizeof(int)*3*pq2inmodel->num_tris); + galias->ofs_trineighbours = (qbyte *)neighbours - (qbyte *)galias; + R_BuildTriangleNeighbours(neighbours, indexes, pq2inmodel->num_tris); + } + + /* + VectorCopy (pq2inmodel->scale_origin, mod->mins); + VectorMA (mod->mins, 255, pq2inmodel->scale, mod->maxs); + */ +// +// move the complete, relocatable alias model to the cache +// + hunkend = Hunk_LowMark (); + hunktotal = hunkend - hunkstart; + + Cache_Alloc (&mod->cache, hunktotal, loadname); + mod->type = mod_alias; + if (!mod->cache.data) + { + Hunk_FreeToLowMark (hunkstart); + return; + } + memcpy (mod->cache.data, galias, hunktotal); + + Hunk_FreeToLowMark (hunkstart); + +} + +#endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifdef MD3MODELS + +//structures from Tenebrae +typedef struct { + int ident; + int version; + + char name[MAX_QPATH]; + + int flags; //Does anyone know what these are? + + int numFrames; + int numTags; + int numSurfaces; + + int numSkins; + + int ofsFrames; + int ofsTags; + int ofsSurfaces; + int ofsEnd; +} md3Header_t; + +//then has header->numFrames of these at header->ofs_Frames +typedef struct md3Frame_s { + vec3_t bounds[2]; + vec3_t localOrigin; + float radius; + char name[16]; +} md3Frame_t; + +//there are header->numSurfaces of these at header->ofsSurfaces, following from ofsEnd +typedef struct { + int ident; // + + char name[MAX_QPATH]; // polyset name + + int flags; + int numFrames; // all surfaces in a model should have the same + + int numShaders; // all surfaces in a model should have the same + int numVerts; + + int numTriangles; + int ofsTriangles; + + int ofsShaders; // offset from start of md3Surface_t + int ofsSt; // texture coords are common for all frames + int ofsXyzNormals; // numVerts * numFrames + + int ofsEnd; // next surface follows +} md3Surface_t; + +//at surf+surf->ofsXyzNormals +typedef struct { + short xyz[3]; + qbyte latlong[2]; +} md3XyzNormal_t; + +//surf->numTriangles at surf+surf->ofsTriangles +typedef struct { + int indexes[3]; +} md3Triangle_t; + +//surf->numVerts at surf+surf->ofsSt +typedef struct { + float s; + float t; +} md3St_t; + +typedef struct { + char name[MAX_QPATH]; + int shaderIndex; +} md3Shader_t; +//End of Tenebrae 'assistance' + +typedef struct { + char name[MAX_QPATH]; + vec3_t org; + float ang[3][3]; +} md3tag_t; + + +void GL_LoadQ3Model(model_t *mod, void *buffer) +{ + extern qboolean gl_bumpmappingpossible; + int hunkstart, hunkend, hunktotal; +// int version; + int s, i, j, d; + + int *indexes; + + vec3_t min; + vec3_t max; + + galiaspose_t *pose; + galiasinfo_t *parent; + galiasgroup_t *group; + + galiasskin_t *skin; + galiastexnum_t *texnum; + + vec3_t *verts; + vec3_t *normals; + vec2_t *st_array; + + float lat, lng; + + md3St_t *inst; + md3Triangle_t *intris; + md3XyzNormal_t *invert; + md3Shader_t *inshader; + + + int size; + + md3Header_t *header; + md3Surface_t *surf; + + + loadmodel=mod; + + hunkstart = Hunk_LowMark (); + + header = buffer; + +// if (header->version != sdfs) +// Sys_Error("GL_LoadQ3Model: Bad version\n"); + + + parent = NULL; + + min[0] = min[1] = min[2] = 0; + max[0] = max[1] = max[2] = 0; + + surf = (md3Surface_t *)((qbyte *)header + header->ofsSurfaces); + for (s = 0; s < header->numSurfaces; s++) + { + size = sizeof(galiasinfo_t) + sizeof(galiasgroup_t)*header->numFrames; + galias = Hunk_Alloc(size); + galias->groupofs = sizeof(*galias); //frame groups + galias->groups = header->numFrames; + galias->numverts = surf->numVerts; + galias->numindexes = surf->numTriangles*3; + galias->numskins = 1; + if (parent) + parent->nextsurf = (qbyte *)surf - (qbyte *)parent; + + st_array = Hunk_Alloc(sizeof(vec2_t)*galias->numindexes); + galias->ofs_st_array = (qbyte*)st_array - (qbyte*)galias; + + inst = (md3St_t*)((qbyte*)surf + surf->ofsSt); + for (i = 0; i < galias->numverts; i++) + { + st_array[i][0] = inst[i].s; + st_array[i][1] = inst[i].t; + } + + indexes = Hunk_Alloc(sizeof(*indexes)*galias->numindexes); + galias->ofs_indexes = (qbyte*)indexes - (qbyte*)galias; + intris = (md3Triangle_t *)((qbyte*)surf + surf->ofsTriangles); + for (i = 0; i < surf->numTriangles; i++) + { + indexes[i*3+0] = intris[i].indexes[0]; + indexes[i*3+1] = intris[i].indexes[1]; + indexes[i*3+2] = intris[i].indexes[2]; + } + + group = (galiasgroup_t *)(galias+1); + invert = (md3XyzNormal_t *)((qbyte*)surf + surf->ofsXyzNormals); + for (i = 0; i < surf->numFrames; i++) + { + pose = (galiaspose_t *)Hunk_Alloc(sizeof(galiaspose_t) + sizeof(vec3_t)*2*surf->numVerts); + normals = (vec3_t*)(pose+1); + verts = normals + surf->numVerts; + + pose->ofsnormals = (qbyte*)normals - (qbyte*)pose; + pose->ofsverts = (qbyte*)verts - (qbyte*)pose; + + for (j = 0; j < surf->numVerts; j++) + { + lat = (float)invert[j].latlong[0] * (2 * M_PI)*(1.0 / 255.0); + lng = (float)invert[j].latlong[1] * (2 * M_PI)*(1.0 / 255.0); + normals[j][0] = cos ( lng ) * sin ( lat ); + normals[j][1] = sin ( lng ) * sin ( lat ); + normals[j][2] = cos ( lat ); + + for (d = 0; d < 3; d++) + { + verts[j][d] = invert[j].xyz[d]/64.0f; + if (verts[j][d]max[d]) + max[d] = verts[j][d]; + } + } + + pose->scale[0] = 1; + pose->scale[1] = 1; + pose->scale[2] = 1; + + pose->scale_origin[0] = 0; + pose->scale_origin[1] = 0; + pose->scale_origin[2] = 0; + + group->numposes = 1; + group->rate = 1; + group->poseofs = (qbyte*)pose - (qbyte*)group; + + group++; + invert += surf->numVerts; + } + + if (surf->numShaders) + { + char name[1024]; + skin = Hunk_Alloc(surf->numShaders*(sizeof(galiasskin_t)+sizeof(galiastexnum_t))); + galias->ofsskins = (qbyte *)skin - (qbyte *)galias; + texnum = (galiastexnum_t *)(skin + surf->numShaders); + inshader = (md3Shader_t *)((qbyte *)surf + surf->ofsShaders); + for (i = 0; i < surf->numShaders; i++) + { + skin->texnums = 1; + skin->ofstexnums = (qbyte *)texnum - (qbyte *)skin; + skin->ofstexels = 0; + skin->skinwidth = 0; + skin->skinheight = 0; + skin->skinspeed = 0; + + texnum->base = Mod_LoadHiResTexture(inshader->name, true, true); + if (!texnum->base) + { + strcpy(name, loadmodel->name); + strcpy(COM_SkipPath(name), COM_SkipPath(inshader->name)); //eviile eh? + texnum->base = Mod_LoadHiResTexture(name, true, true); + } + + texnum->bump = 0; + if (gl_bumpmappingpossible) + { + COM_StripExtension(inshader->name, name); //go for the normalmap + strcat(name, "_norm"); + texnum->bump = Mod_LoadHiResTexture(name, true, true); + if (!texnum->bump) + { + strcpy(name, loadmodel->name); + COM_StripExtension(COM_SkipPath(inshader->name), COM_SkipPath(name)); + strcat(name, "_norm"); + texnum->bump = Mod_LoadHiResTexture(name, true, true); + if (!texnum->bump) + { + COM_StripExtension(inshader->name, name); //bother, go for heightmap and convert + strcat(name, "_bump"); + texnum->bump = Mod_LoadBumpmapTexture(name); + if (!texnum->bump) + { + strcpy(name, loadmodel->name); + strcpy(COM_SkipPath(name), COM_SkipPath(inshader->name)); //eviile eh? + COM_StripExtension(name, name); + strcat(name, "_bump"); + texnum->bump = Mod_LoadBumpmapTexture(name); + } + } + } + } + if (r_fb_models.value) + { + COM_StripExtension(inshader->name, name); //go for the normalmap + strcat(name, "_luma"); + texnum->fullbright = Mod_LoadHiResTexture(name, true, true); + if (!texnum->base) + { + strcpy(name, loadmodel->name); + strcpy(COM_SkipPath(name), COM_SkipPath(inshader->name)); //eviile eh? + COM_StripExtension(name, name); + strcat(name, "_luma"); + texnum->fullbright = Mod_LoadBumpmapTexture(name); + } + } + + + skin++; + texnum++; + } + } + + VectorCopy(min, loadmodel->mins); + VectorCopy(max, loadmodel->maxs); + + + + if (r_shadows.value) + { + int *neighbours; + neighbours = Hunk_Alloc(sizeof(int)*3*surf->numTriangles); + galias->ofs_trineighbours = (qbyte *)neighbours - (qbyte *)galias; + R_BuildTriangleNeighbours(neighbours, indexes, surf->numTriangles); + } + surf = (md3Surface_t *)((qbyte *)surf + surf->ofsEnd); + } + +// +// move the complete, relocatable alias model to the cache +// + + mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0); + + hunkend = Hunk_LowMark (); + hunktotal = hunkend - hunkstart; + + Cache_Alloc (&mod->cache, hunktotal, loadname); + mod->type = mod_alias; + if (!mod->cache.data) + { + Hunk_FreeToLowMark (hunkstart); + return; + } + memcpy (mod->cache.data, galias, hunktotal); + + Hunk_FreeToLowMark (hunkstart); +} +#endif diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c new file mode 100644 index 00000000..bd459ed1 --- /dev/null +++ b/engine/gl/gl_backend.c @@ -0,0 +1,489 @@ +#include "quakedef.h" +#include "glquake.h" +#include "shader.h" + +#ifdef RGLQUAKE + + +#define MAX_MESH_VERTS 8192 + +//we don't support multitexturing yet. + + +static float tempstarray[MAX_MESH_VERTS*3]; +static vec4_t tempxyzarray[MAX_MESH_VERTS]; + +shader_t nullshader, wallbumpshader, modelbumpshader; +#define frand() (rand()&32767)* (1.0/32767) + +#define FTABLE_SIZE 1024 +#define FTABLE_CLAMP(x) (((int)((x)*FTABLE_SIZE) & (FTABLE_SIZE-1))) +#define FTABLE_EVALUATE(table,x) (table ? table[FTABLE_CLAMP(x)] : frand())//*((x)-floor(x))) + +static float r_sintable[FTABLE_SIZE]; +static float r_triangletable[FTABLE_SIZE]; +static float r_squaretable[FTABLE_SIZE]; +static float r_sawtoothtable[FTABLE_SIZE]; +static float r_inversesawtoothtable[FTABLE_SIZE]; + +void GLR_MeshInit(void) +{ + int i; + double t; + for ( i = 0; i < FTABLE_SIZE; i++ ) + { + t = (double)i / (double)FTABLE_SIZE; + + r_sintable[i] = sin ( t * M_PI*2 ); + + if (t < 0.25) + r_triangletable[i] = t * 4.0; + else if (t < 0.75) + r_triangletable[i] = 2 - 4.0 * t; + else + r_triangletable[i] = (t - 0.75) * 4.0 - 1.0; + + if (t < 0.5) + r_squaretable[i] = 1.0f; + else + r_squaretable[i] = -1.0f; + + r_sawtoothtable[i] = t; + r_inversesawtoothtable[i] = 1.0 - t; + } + + { + nullshader.numdeforms = 0;//1; + nullshader.deforms[0].type = DEFORMV_WAVE; + nullshader.deforms[0].args[0] = 10; + nullshader.deforms[0].func.type = SHADER_FUNC_SIN; + nullshader.deforms[0].func.args[1] = 1; + nullshader.deforms[0].func.args[3] = 10; + + nullshader.pass[0].texturetype = GL_TEXTURE_2D; + nullshader.pass[0].envmode = GL_MODULATE; + nullshader.pass[0].blendsrc = GL_SRC_ALPHA; + nullshader.pass[0].blenddst = GL_ONE_MINUS_SRC_ALPHA; + + nullshader.pass[1].flags |= SHADER_PASS_BLEND; + nullshader.pass[1].tcgen = TC_GEN_LIGHTMAP; + nullshader.pass[1].blendsrc = GL_SRC_ALPHA; + nullshader.pass[1].blenddst = GL_ONE_MINUS_SRC_ALPHA; + nullshader.pass[1].texturetype = GL_TEXTURE_2D; + } + + { + modelbumpshader.numpasses = 3; + + if (1) + modelbumpshader.pass[0].mergedpasses = 4; + else + modelbumpshader.pass[0].mergedpasses = 2; + modelbumpshader.pass[2].mergedpasses = 1; + + modelbumpshader.pass[0].tcgen = TC_GEN_BASE; + modelbumpshader.pass[0].envmode = GL_COMBINE_ARB; + modelbumpshader.pass[0].combinesrc0 = GL_TEXTURE; + modelbumpshader.pass[0].combinemode = GL_REPLACE; + modelbumpshader.pass[0].blendsrc = GL_SRC_ALPHA; + modelbumpshader.pass[0].blenddst = GL_ONE_MINUS_SRC_ALPHA; + modelbumpshader.pass[0].anim_frames[0] = 0;//bumpmap + modelbumpshader.pass[0].texturetype = GL_TEXTURE_2D; + + modelbumpshader.pass[1].tcgen = TC_GEN_DOTPRODUCT; + modelbumpshader.pass[1].envmode = GL_COMBINE_ARB; + modelbumpshader.pass[1].combinesrc0 = GL_TEXTURE; + modelbumpshader.pass[1].combinesrc1 = GL_PREVIOUS_ARB; + modelbumpshader.pass[1].combinemode = GL_DOT3_RGB_ARB; + modelbumpshader.pass[1].anim_frames[0] = 0;//delux + modelbumpshader.pass[1].texturetype = GL_TEXTURE_2D; + + modelbumpshader.pass[2].flags |= SHADER_PASS_BLEND; + modelbumpshader.pass[2].tcgen = TC_GEN_BASE; + modelbumpshader.pass[2].envmode = GL_MODULATE; + modelbumpshader.pass[2].blendsrc = GL_DST_COLOR; + modelbumpshader.pass[2].blenddst = GL_ZERO; + modelbumpshader.pass[2].anim_frames[0] = 0;//texture + modelbumpshader.pass[2].texturetype = GL_TEXTURE_2D; + + //gl_combine states that we need to use a textures. + modelbumpshader.pass[3].tcgen = TC_GEN_BASE; //multiply by colors + modelbumpshader.pass[3].envmode = GL_COMBINE_ARB; + modelbumpshader.pass[3].combinesrc0 = GL_PREVIOUS_ARB; + modelbumpshader.pass[3].combinesrc1 = GL_PRIMARY_COLOR_ARB; + modelbumpshader.pass[3].combinemode = GL_MODULATE; + modelbumpshader.pass[3].anim_frames[0] = 1; //any, has to be present + modelbumpshader.pass[3].texturetype = GL_TEXTURE_2D; + } + + { + wallbumpshader.numpasses = 4; + + if (1) + wallbumpshader.pass[0].mergedpasses = 4; + else + wallbumpshader.pass[0].mergedpasses = 2; + wallbumpshader.pass[2].mergedpasses = 2; + + wallbumpshader.pass[0].tcgen = TC_GEN_BASE; + wallbumpshader.pass[0].envmode = GL_COMBINE_ARB; + wallbumpshader.pass[0].combinesrc0 = GL_TEXTURE; + wallbumpshader.pass[0].combinemode = GL_REPLACE; + wallbumpshader.pass[0].anim_frames[0] = 0;//bumpmap + wallbumpshader.pass[0].blendsrc = GL_SRC_ALPHA; + wallbumpshader.pass[0].blenddst = GL_ONE_MINUS_SRC_ALPHA; + wallbumpshader.pass[0].texturetype = GL_TEXTURE_2D; + + wallbumpshader.pass[1].tcgen = TC_GEN_LIGHTMAP; + wallbumpshader.pass[1].envmode = GL_COMBINE_ARB; + wallbumpshader.pass[1].combinesrc0 = GL_TEXTURE; + wallbumpshader.pass[1].combinesrc1 = GL_PREVIOUS_ARB; + wallbumpshader.pass[1].combinemode = GL_DOT3_RGB_ARB; + wallbumpshader.pass[1].anim_frames[0] = 0;//delux + wallbumpshader.pass[1].texturetype = GL_TEXTURE_2D; + + wallbumpshader.pass[2].flags |= SHADER_PASS_BLEND; + wallbumpshader.pass[2].tcgen = TC_GEN_BASE; + wallbumpshader.pass[2].envmode = GL_MODULATE; + wallbumpshader.pass[2].blendsrc = GL_DST_COLOR; + wallbumpshader.pass[2].blenddst = GL_ZERO; + wallbumpshader.pass[2].anim_frames[0] = 0;//texture + wallbumpshader.pass[2].texturetype = GL_TEXTURE_2D; + + wallbumpshader.pass[3].tcgen = TC_GEN_LIGHTMAP; + wallbumpshader.pass[3].envmode = GL_BLEND; + wallbumpshader.pass[3].anim_frames[0] = 0;//lightmap + wallbumpshader.pass[3].texturetype = GL_TEXTURE_2D; + } +} + +static float *R_TableForFunc ( unsigned int func ) +{ + switch (func) + { + case SHADER_FUNC_SIN: + return r_sintable; + + case SHADER_FUNC_TRIANGLE: + return r_triangletable; + + case SHADER_FUNC_SQUARE: + return r_squaretable; + + case SHADER_FUNC_SAWTOOTH: + return r_sawtoothtable; + + case SHADER_FUNC_INVERSESAWTOOTH: + return r_inversesawtoothtable; + } + + // assume noise + return NULL; +} + +static void MakeDeforms(shader_t *shader, vec4_t *out, vec4_t *in, int number) +{ + float *table; + int d, j; + float args[4], deflect; + deformv_t *dfrm = shader->deforms; + + for (d = 0; d < shader->numdeforms; d++, dfrm++) + { + switch(dfrm->type) + { + case DEFORMV_WAVE: + args[0] = dfrm->func.args[0]; + args[1] = dfrm->func.args[1]; + args[3] = dfrm->func.args[2] + dfrm->func.args[3] * realtime; + table = R_TableForFunc ( dfrm->func.type ); + + for ( j = 0; j < number; j++ ) { + deflect = dfrm->args[0] * (in[j][0]+in[j][1]+in[j][2]) + args[3]; + deflect = sin(deflect)/*FTABLE_EVALUATE ( table, deflect )*/ * args[1] + args[0]; + + out[j][0] = in[j][0]+deflect; + out[j][1] = in[j][1]+deflect; + out[j][2] = in[j][2]+deflect; + + // Deflect vertex along its normal by wave amount +// VectorMA ( out[j], deflect, normalsArray[j], in[j] ); + } + break; + case DEFORMV_MOVE: + table = R_TableForFunc ( dfrm->func.type ); + deflect = dfrm->func.args[2] + realtime * dfrm->func.args[3]; + deflect = FTABLE_EVALUATE (table, deflect) * dfrm->func.args[1] + dfrm->func.args[0]; + + for ( j = 0; j < number; j++ ) + VectorMA ( out[j], deflect, dfrm->args, in[j] ); + break; + default: + Sys_Error("Bad deform type %i\n", dfrm->type); + } + + in = out; + } +} + +static void Mesh_DeformTextureCoords(mesh_t *mesh, shaderpass_t *pass) +{ + int d; + tcmod_t *tcmod = pass->tcmod; + float *in, *out; + switch(pass->tcgen) + { + case TC_GEN_DOTPRODUCT: //take normals, use the dotproduct and produce texture coords for bumpmapping + { + out = tempstarray; + in = (float*)mesh->normals_array; + + for (d = 0; d < mesh->numvertexes; d++) + { + out[d*3+0] = DotProduct((in+d*3), mesh->lightaxis[0]); + out[d*3+1] = DotProduct((in+d*3), mesh->lightaxis[1]); + out[d*3+2] = DotProduct((in+d*3), mesh->lightaxis[2]); + } + glTexCoordPointer(3, GL_FLOAT, 0, out); + + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + } + return; + case TC_GEN_LIGHTMAP: + in = (float*)mesh->lmst_array; + if (in) + break; //fallthrought + case TC_GEN_BASE: + in = (float*)mesh->st_array; + break; + default: + Sys_Error("Mesh_DeformTextureCoords: Bad TC_GEN type\n"); + return; + } +/* + for (d = 0; d < pass->numtcmods; d++, dfrm++) + { + } +*/ + + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + glTexCoordPointer(2, GL_FLOAT, 0, in); +} + +static void Mesh_SetShaderpassState ( shaderpass_t *pass, qboolean mtex ) +{ + if ( (mtex && (pass->blendmode != GL_REPLACE)) || (pass->flags & SHADER_PASS_BLEND) ) + { + glEnable (GL_BLEND); + glBlendFunc (pass->blendsrc, pass->blenddst); + } + else + { + glDisable (GL_BLEND); + } + + if (pass->flags & SHADER_PASS_ALPHAFUNC) + { + glEnable (GL_ALPHA_TEST); + + if (pass->alphafunc == SHADER_ALPHA_GT0) + { + glAlphaFunc (GL_GREATER, 0); + } + else if (pass->alphafunc == SHADER_ALPHA_LT128) + { + glAlphaFunc (GL_LESS, 0.5f); + } + else if (pass->alphafunc == SHADER_ALPHA_GE128) + { + glAlphaFunc (GL_GEQUAL, 0.5f); + } + } + else + { +// glDisable (GL_ALPHA_TEST); + } + +// glDepthFunc (pass->depthfunc); + + if (pass->flags & SHADER_PASS_DEPTHWRITE) + { + glDepthMask (GL_TRUE); + } + else + { +// glDepthMask (GL_FALSE); + } +} + +static void Mesh_DrawPass(shaderpass_t *pass, mesh_t *mesh) +{ + Mesh_SetShaderpassState(pass, false); + if (pass->mergedpasses>1 && gl_mtexarbable) + { + int p; +// Mesh_DrawPass(pass+2,mesh); +// return; + for (p = 0; p < pass->mergedpasses; p++) + { + qglActiveTextureARB(GL_TEXTURE0_ARB+p); + qglClientActiveTextureARB(GL_TEXTURE0_ARB+p); + GL_BindType(pass[p].texturetype, pass[p].anim_frames[0]); + glEnable(pass[p].texturetype); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, pass[p].envmode); + if (pass[p].envmode == GL_COMBINE_ARB) + { + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, pass[p].combinesrc0); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, pass[p].combinesrc1); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, pass[p].combinemode); + } + + Mesh_DeformTextureCoords(mesh, pass+p); + } + glDrawElements(GL_TRIANGLES, mesh->numindexes, GL_UNSIGNED_INT, mesh->indexes); + for (p = pass->mergedpasses-1; p >= 0; p--) + { + qglActiveTextureARB(GL_TEXTURE0_ARB+p); + qglClientActiveTextureARB(GL_TEXTURE0_ARB+p); + glDisable(pass[p].texturetype); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + } + } + else + { + Mesh_DeformTextureCoords(mesh, pass); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, pass->envmode); + + GL_Bind(pass->anim_frames[0]); + if (pass->texturetype != GL_TEXTURE_2D) + { + glDisable(pass->texturetype); + glEnable(pass->texturetype); + } + glDrawElements(GL_TRIANGLES, mesh->numindexes, GL_UNSIGNED_INT, mesh->indexes); + + if (pass->texturetype != GL_TEXTURE_2D) + { + glDisable(pass->texturetype); + glEnable(GL_TEXTURE_2D); + } + + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + } +} + + +void GL_DrawMesh(mesh_t *mesh, shader_t *shader, int texturenum, int lmtexturenum) +{ + int i; + if (!shader) + { + shader = &nullshader; + shader->pass[0].anim_frames[0] = texturenum; + shader->pass[0].mergedpasses=1; + + if (lmtexturenum && !texturenum) + { + shader->pass[0].anim_frames[0] = lmtexturenum; + shader->numpasses = 1; + shader->pass[0].flags |= SHADER_PASS_BLEND; + } + else if (lmtexturenum) + { + shader->numpasses = 2; + shader->pass[1].anim_frames[0] = lmtexturenum;//lmtexture; + shader->pass[0].flags &= ~SHADER_PASS_BLEND; + } + else + { + shader->pass[0].flags &= ~SHADER_PASS_BLEND; + shader->numpasses = 1; + } + + shader->pass[0].texturetype = GL_TEXTURE_2D; + } + if (!shader->numdeforms) + glVertexPointer(3, GL_FLOAT, 16, mesh->xyz_array); + else + { + MakeDeforms(shader, tempxyzarray, mesh->xyz_array, mesh->numvertexes); + glVertexPointer(3, GL_FLOAT, 16, tempxyzarray); + } + if (mesh->normals_array && glNormalPointer) + { + glNormalPointer(GL_FLOAT, 0, mesh->normals_array); + glEnableClientState( GL_NORMAL_ARRAY ); + } + + glEnableClientState( GL_VERTEX_ARRAY ); + if (mesh->colors_array && glColorPointer) + { + glColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh->colors_array); + glEnableClientState( GL_COLOR_ARRAY ); + } + + for (i =0 ; i < shader->numpasses; i+=shader->pass[i].mergedpasses) + Mesh_DrawPass(shader->pass+i, mesh); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_COLOR_ARRAY ); + glDisableClientState( GL_NORMAL_ARRAY ); + +/* //show normals + if (mesh->normals_array) + { + glColor3f(1,1,1); + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINES); + for (i = 0; i < mesh->numvertexes; i++) + { + glVertex3f( mesh->xyz_array[i][0], + mesh->xyz_array[i][1], + mesh->xyz_array[i][2]); + + glVertex3f( mesh->xyz_array[i][0] + mesh->normals_array[i][0], + mesh->xyz_array[i][1] + mesh->normals_array[i][1], + mesh->xyz_array[i][2] + mesh->normals_array[i][2]); + } + glEnd(); + glEnable(GL_TEXTURE_2D); + } +*/ +} + +void GL_DrawMeshBump(mesh_t *mesh, int texturenum, int lmtexturenum, int bumpnum, int deluxnum) +{ + shader_t *shader; + extern int normalisationCubeMap; + + if (lmtexturenum) + { + shader = &wallbumpshader; + shader->pass[3].anim_frames[0] = lmtexturenum; + } + else + shader = &modelbumpshader; + + shader->pass[0].anim_frames[0] = bumpnum; + if (deluxnum) + { + shader->pass[1].anim_frames[0] = deluxnum; + shader->pass[1].tcgen = TC_GEN_LIGHTMAP; + shader->pass[1].texturetype = GL_TEXTURE_2D; + } + else + { + shader->pass[1].anim_frames[0] = normalisationCubeMap; + shader->pass[1].tcgen = TC_GEN_DOTPRODUCT; + shader->pass[1].texturetype = GL_TEXTURE_CUBE_MAP_ARB; + } + shader->pass[2].anim_frames[0] = texturenum; + +// mesh->colors_array=NULL; //don't bother coloring it. + + GL_DrawMesh(mesh, shader, 0, 0); +} + +#endif diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c new file mode 100644 index 00000000..056d22c9 --- /dev/null +++ b/engine/gl/gl_draw.c @@ -0,0 +1,3005 @@ +/* +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" +#include "glquake.h" +#include "shader.h" + +//#define GL_USE8BITTEX + +int glx, gly, glwidth, glheight; + +mesh_t draw_mesh; +vec4_t draw_mesh_xyz[4]; +vec3_t draw_mesh_normals[4]; +vec2_t draw_mesh_st[4]; +vec2_t draw_mesh_lmst[4]; +//byte_vec4_t draw_mesh_colors[4]; + +qbyte *uploadmemorybuffer; +int sizeofuploadmemorybuffer; +qbyte *uploadmemorybufferintermediate; +int sizeofuploadmemorybufferintermediate; + +int r_quad_indexes[6] = {0, 1, 2, 0, 2, 3}; + +extern qbyte gammatable[256]; + +unsigned char *d_15to8table; +qboolean inited15to8; +extern cvar_t crosshair, cl_crossx, cl_crossy, crosshaircolor; + +extern cvar_t gl_nobind; +extern cvar_t gl_max_size; +extern cvar_t gl_picmip; +extern cvar_t r_drawdisk; +extern cvar_t gl_compress; +extern cvar_t gl_font, gl_conback, gl_smoothfont; + +extern cvar_t gl_savecompressedtex; + +extern cvar_t gl_load24bit; + +extern qboolean gl_compressable; + +qbyte *draw_chars; // 8*8 graphic characters +qpic_t *draw_disc; +qpic_t *draw_backtile; + +int translate_texture; +int char_texture, char_tex2, default_char_texture; +int cs_texture; // crosshair texture +extern int detailtexture; + +static unsigned cs_data[16*16]; +int cachedcrosshair; + +typedef struct +{ + int texnum; + float sl, tl, sh, th; +} glpic_t; + +qbyte conback_buffer[sizeof(qpic_t) + sizeof(glpic_t)]; +qbyte custconback_buffer[sizeof(qpic_t) + sizeof(glpic_t)]; +qpic_t *default_conback = (qpic_t *)&conback_buffer, *conback, *custom_conback = (qpic_t *)&custconback_buffer; + +#include "hash.h" +hashtable_t gltexturetable; +bucket_t *gltexturetablebuckets[256]; + +int gl_lightmap_format = 4; +int gl_solid_format = 3; +int gl_alpha_format = 4; + +int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; +int gl_filter_max = GL_LINEAR; + + +int texels; + +typedef struct gltexture_s +{ + int texnum; + char identifier[64]; + int width, height, bpp; + qboolean mipmap; + struct gltexture_s *next; +} gltexture_t; + +static gltexture_t *gltextures; + +void GL_Bind (int texnum) +{ + if (gl_nobind.value) + texnum = char_texture; + if (currenttexture == texnum) + return; + currenttexture = texnum; + + bindTexFunc (GL_TEXTURE_2D, texnum); +} +void GL_BindType (int type, int texnum) +{ + bindTexFunc (type, texnum); + +currenttexture=-1; +} + + +/* +============================================================================= + + scrap allocation + + Allocate all the little status bar obejcts into a single texture + to crutch up stupid hardware / drivers + +============================================================================= +*/ + +#define MAX_SCRAPS 4 +#define BLOCK_WIDTH 256 +#define BLOCK_HEIGHT 256 + +int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; +qbyte 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; + + if (in) + { + out->width = in->width; + out->height = in->height; + } + else + { //default the size. + out->width = 24; //hmm...? + out->height = 24; + } + + //standard names substitution + texnum = Mod_LoadReplacementTexture(name, false, true); + if (!in && !texnum) //try a q2 texture + { + sprintf(name2, "pics/%s", name); + texnum = Mod_LoadHiResTexture(name2, false, true); + } + + if (texnum) + { + gl->texnum = texnum; + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + return true; + } + //all the others require an actual infile rather than a replacement image + else if (!in) + { + return false; + } + + // load little ones into the scrap + else if (in->width < 64 && in->height < 64) + { + int x, y; + int i, j, k; + int texnum; + + texnum = Scrap_AllocBlock (in->width, in->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] = in->data[k]; + texnum += scrap_texnum; + gl->texnum = texnum; + gl->sl = (x+0.01)/(float)BLOCK_WIDTH; + gl->sh = (x+in->width-0.01)/(float)BLOCK_WIDTH; + gl->tl = (y+0.01)/(float)BLOCK_WIDTH; + gl->th = (y+in->height-0.01)/(float)BLOCK_WIDTH; + + pic_count++; + pic_texels += in->width*in->height; + } + else + { + gl->texnum = GL_LoadPicTexture (in); + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + } + return true; +} + +char *failedpic; //easier this way +qpic_t *GLDraw_SafePicFromWad (char *name) +{ + int i; + glcachepic_t *pic; + for (pic=glmenu_cachepics, i=0 ; iname)) + return &pic->pic; + + if (glmenu_numcachepics == MAX_CACHED_PICS) + Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); + glmenu_numcachepics++; + + strcpy(pic->name, name); + if (!Draw_RealPicFromWad(&pic->pic, name)) + { + glmenu_numcachepics--; + failedpic = name; + return NULL; + } + + return &pic->pic; +} + +qpic_t *GLDraw_PicFromWad (char *name) +{ + qpic_t *pic = GLDraw_SafePicFromWad (name); + if (!pic) + Sys_Error ("GLDraw_PicFromWad: failed to load %s", name); + + return pic; +} + +qpic_t *GLDraw_SafeCachePic (char *path) +{ + glcachepic_t *pic; + int i; + qpic_t *dat; + glpic_t *gl; + + for (pic=glmenu_cachepics, i=0 ; iname)) + return &pic->pic; + + if (glmenu_numcachepics == MAX_CACHED_PICS) + Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); + + + +// +// load the pic from disk +// + { + char *mem; + char alternatename[MAX_QPATH]; + _snprintf(alternatename, MAX_QPATH-1, "pics/%s.pcx", path); + dat = (qpic_t *)COM_LoadMallocFile (alternatename); + if (dat) + { + strcpy(pic->name, path); + if ((mem = ReadPCXFile((qbyte *)dat, com_filesize, &pic->pic.width, &pic->pic.height))) + { + gl = (glpic_t *)pic->pic.data; + if (!(gl->texnum = Mod_LoadReplacementTexture(alternatename, false, true))) + gl->texnum = GL_LoadTexture32(path, pic->pic.width, pic->pic.height, (unsigned *)dat, false, true); + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + + BZ_Free(dat); + BZ_Free(mem); + glmenu_numcachepics++; + return &pic->pic; + } + BZ_Free(dat); + } + } +#ifdef AVAIL_JPEGLIB + { + char *mem; + char alternatename[MAX_QPATH]; + _snprintf(alternatename, MAX_QPATH-1,"%s.jpg", path); + dat = (qpic_t *)COM_LoadMallocFile (alternatename); + if (dat) + { + strcpy(pic->name, path); + if ((mem = ReadJPEGFile((qbyte *)dat, com_filesize, &pic->pic.width, &pic->pic.height))) + { + gl = (glpic_t *)pic->pic.data; + if (!(gl->texnum = Mod_LoadReplacementTexture(alternatename, false, true))) + gl->texnum = GL_LoadTexture32(path, pic->pic.width, pic->pic.height, (unsigned *)mem, false, true); + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + + BZ_Free(dat); + BZ_Free(mem); + glmenu_numcachepics++; + return &pic->pic; + } + BZ_Free(dat); + } + } +#endif +/* + { + char *mem; + char alternatename[MAX_QPATH]; + _snprintf(alternatename, MAX_QPATH-1,"%s.tga", path); + dat = (qpic_t *)COM_LoadMallocFile (alternatename); + if (dat) + { + strcpy(pic->name, path); + if (mem = ReadTargaFile ((qbyte *)dat, com_filesize, &pic->pic.width, &pic->pic.height, false)) + { + gl = (glpic_t *)pic->pic.data; + if (!(gl->texnum = Mod_LoadReplacementTexture(alternatename, false, true))) + gl->texnum = GL_LoadTexture32(path, pic->pic.width, pic->pic.height, (unsigned *)dat, false, true); + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + + BZ_Free(dat); + BZ_Free(mem); + glmenu_numcachepics++; + return &pic->pic; + } + BZ_Free(dat); + } + } +*/ + dat = (qpic_t *)COM_LoadTempFile (path); + if (!dat) + { + char alternatename[MAX_QPATH]; + sprintf(alternatename, "gfx/%s.lmp", path); + dat = (qpic_t *)COM_LoadTempFile (alternatename); + if (!dat) + return GLDraw_SafePicFromWad(path); + } + + SwapPic (dat); + + // HACK HACK HACK --- we need to keep the bytes for + // the translatable player picture just for the menu + // configuration dialog + + if (!strncmp (path, "gfx/player/", 11) || !strcmp (path, "gfx/menuplyr.lmp")) //these arn't cached. I hate hacks. + memcpy (menuplyr_pixels, dat->data, dat->width*dat->height); + else + { + glmenu_numcachepics++; + Q_strncpyz (pic->name, path, sizeof(pic->name)); + } + + pic->pic.width = dat->width; + pic->pic.height = dat->height; + + gl = (glpic_t *)pic->pic.data; + if (!(gl->texnum = Mod_LoadReplacementTexture(path, false, true))) + gl->texnum = GL_LoadPicTexture (dat); + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + + return &pic->pic; +} +qpic_t *GLDraw_CachePic (char *path) +{ + qpic_t *pic = GLDraw_SafeCachePic (path); + if (!pic) + Sys_Error ("GLDraw_CachePic: failed to load %s", path); + + return pic; +} + +void GLDraw_CharToConback (int num, qbyte *dest) +{ + int row, col; + qbyte *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 GLDraw_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 (glt=gltextures ; glt ; glt=glt->next) + { + if (glt->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); + } + } +} + +/* +=============== +Draw_Init +=============== +*/ + +void GLDraw_ReInit (void) +{ + int i; + qpic_t *cb; + qbyte *dest; + int x; + char ver[40]; + glpic_t *gl; + qpic_t *bigfont; + int start; + qbyte *ncdata; + qbyte *tinyfont; + extern int solidskytexture; + extern int alphaskytexture; + extern int skyboxtex[6]; + extern int lightmap_textures; + extern int filmtexture; + + int maxtexsize; + + gltexture_t *glt; + while(gltextures) + { + glt = gltextures; + gltextures = gltextures->next; + BZ_Free(glt); + } + + memset(gltexturetablebuckets, 0, sizeof(gltexturetablebuckets)); + Hash_InitTable(&gltexturetable, sizeof(gltexturetablebuckets)/sizeof(gltexturetablebuckets[0]), gltexturetablebuckets); + + + texture_extension_number=1; + solidskytexture=0; + alphaskytexture=0; + skyboxtex[0] = 0; skyboxtex[1] = 0; skyboxtex[2] = 0; skyboxtex[3] = 0; skyboxtex[4] = 0; skyboxtex[5] = 0; + lightmap_textures=0; + filmtexture=0; + currenttexture=0; + glmenu_numcachepics=0; +// GL_FlushSkinCache(); + GL_GAliasFlushSkinCache(); + + memset(scrap_allocated, 0, sizeof(scrap_allocated)); + + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsize); + if (gl_max_size.value > maxtexsize) + { + sprintf(ver, "%i", maxtexsize); + Cvar_ForceSet (&gl_max_size, ver); + } + + if (maxtexsize < 2048) //this needs to be able to hold the image in unscaled form. + sizeofuploadmemorybufferintermediate = 2048*2048*4; //make sure we can load 2048*2048 images whatever happens. + else + sizeofuploadmemorybufferintermediate = maxtexsize*maxtexsize*4; //gl supports huge images, so so shall we. + + //required to hold the image after scaling has occured + sizeofuploadmemorybuffer = maxtexsize*maxtexsize*4; + + uploadmemorybuffer = BZ_Realloc(uploadmemorybuffer, sizeofuploadmemorybuffer); + uploadmemorybufferintermediate = BZ_Realloc(uploadmemorybufferintermediate, sizeofuploadmemorybufferintermediate); + + // 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_SafeGetLumpName ("conchars"); + if (draw_chars) + { + for (i=0 ; i<128*128 ; i++) + if (draw_chars[i] == 0) + draw_chars[i] = 255; // proper transparent color + } + + // now turn them into textures + if (!(char_texture=Mod_LoadReplacementTexture("gfx/conchars.lmp", false, true))) //no high res + { + if (!draw_chars) //or low res. + { + if (!(char_texture=Mod_LoadHiResTexture("pics/conchars.pcx", false, true))) //try low res q2 path + { + char *tempchars = COM_LoadMallocFile("gfx/menu/conchars.lmp"); + char *in, *out; + if (!tempchars) + Sys_Error("No charset found\n"); + + draw_chars = BZ_Malloc(8*8*256*8); + + out = draw_chars; + for (i = 0; i < 8*8; i+=1) + { + if ((i/8)&1) + { + in = tempchars + ((i)/8)*16*8*8+(i&7)*32*8 - 256*4+128; + for (x = 0; x < 16*8; x++) + *out++ = *in++; + } + else + { + in = tempchars + (i/8)*16*8*8+(i&7)*32*8; + for (x = 0; x < 16*8; x++) + *out++ = *in++; + } + } + for (i = 0; i < 8*8; i+=1) + { + if ((i/8)&1) + { + in = tempchars+128*128 + ((i)/8)*16*8*8+(i&7)*32*8 - 256*4+128; + for (x = 0; x < 16*8; x++) + *out++ = *in++; + } + else + { + in = tempchars+128*128 + (i/8)*16*8*8+(i&7)*32*8; + for (x = 0; x < 16*8; x++) + *out++ = *in++; + } + } + Z_Free(tempchars); + + for (i=0 ; i<128*128 ; i++) + if (draw_chars[i] == 0) + draw_chars[i] = 255; // proper transparent color + char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true); + Z_Free(draw_chars); + draw_chars = NULL; + } + } + else + char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true); + } + default_char_texture=char_texture; + + gl_font.modified = true; + + gl_smoothfont.modified = 1; + + + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + SCR_DrawLoading (); + GL_EndRendering (); + + //now emit the conchars picture as if from a wad. + strcpy(glmenu_cachepics[glmenu_numcachepics].name, "conchars"); + glmenu_cachepics[glmenu_numcachepics].pic.width = 128; + glmenu_cachepics[glmenu_numcachepics].pic.height = 128; + gl = (glpic_t *)&glmenu_cachepics[glmenu_numcachepics].pic.data; + gl->texnum = char_texture; + gl->sl = 0; + gl->tl = 0; + gl->sh = 1; + gl->th = 1; + glmenu_numcachepics++; + + tinyfont = W_SafeGetLumpName ("tinyfont"); + if (tinyfont) + { + for (i=0 ; i<128*32 ; i++) + if (tinyfont[i] == 0) + tinyfont[i] = 255; // proper transparent color + strcpy(glmenu_cachepics[glmenu_numcachepics].name, "tinyfont"); + glmenu_cachepics[glmenu_numcachepics].pic.width = 128; + glmenu_cachepics[glmenu_numcachepics].pic.height = 32; + gl = (glpic_t *)&glmenu_cachepics[glmenu_numcachepics].pic.data; + gl->texnum = GL_LoadTexture ("tinyfont", 128, 32, tinyfont, false, true); + gl->sl = 0; + gl->tl = 0; + gl->sh = 1; + gl->th = 1; + glmenu_numcachepics++; + } + bigfont = (qpic_t *)COM_LoadMallocFile ("gfx/menu/bigfont.lmp"); + if (bigfont) + { + char *data; + data = bigfont->data; + for (i=0 ; iwidth*bigfont->height ; i++) + if (data[i] == 0) + data[i] = 255; // proper transparent color + strcpy(glmenu_cachepics[glmenu_numcachepics].name, "gfx/menu/bigfont.lmp"); + glmenu_cachepics[glmenu_numcachepics].pic.width = bigfont->width; + glmenu_cachepics[glmenu_numcachepics].pic.height = bigfont->height; + gl = (glpic_t *)&glmenu_cachepics[glmenu_numcachepics].pic.data; + gl->texnum = GL_LoadTexture ("gfx/menu/bigfont.lmp", bigfont->width, bigfont->height, data, false, true); + gl->sl = 0; + gl->tl = 0; + gl->sh = 1; + gl->th = 1; + glmenu_numcachepics++; + } + + + if (!(char_tex2=Mod_LoadReplacementTexture("gfx/conchars2.lmp", false, true))) + { + if (!draw_chars) + char_tex2 = char_texture; + else + char_tex2 = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true); + } + + cs_texture = texture_extension_number++; + cachedcrosshair=0; + + start = Hunk_LowMark (); + conback = default_conback; + + if (COM_FDepthFile("gfx/conback.lmp", false) <= COM_FDepthFile("gfx/menu/conback.lmp", false)) + cb = (qpic_t *)COM_LoadHunkFile ("gfx/conback.lmp"); + else + cb = (qpic_t *)COM_LoadHunkFile ("gfx/menu/conback.lmp"); + if (cb) + { + SwapPic (cb); + + if (draw_chars) + { +#ifdef DISTRIBUTION + sprintf (ver, "%s %4.2f", DISTRIBUTION, VERSION); +#else + sprintf (ver, "%4.2f", VERSION); +#endif + dest = cb->data + 320 + 320*186 - 11 - 8*strlen(ver); + for (x=0 ; xwidth = vid.conwidth; + conback->height = vid.conheight; + + // scale console to vid size + dest = ncdata = Hunk_AllocName(vid.conwidth * vid.conheight, "conback"); + + for (y=0 ; ydata + cb->width * (y*cb->height/vid.conheight); + if (vid.conwidth == cb->width) + memcpy (dest, src, vid.conwidth); + else + { + f = 0; + fstep = cb->width*0x10000/vid.conwidth; + for (x=0 ; x>16]; + f += fstep; + dest[x+1] = src[f>>16]; + f += fstep; + dest[x+2] = src[f>>16]; + f += fstep; + dest[x+3] = src[f>>16]; + f += fstep; + } + } + } +#else + conback->width = cb->width; + conback->height = cb->height; + ncdata = cb->data; +#endif + } + else + { + ncdata = NULL; + } + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + gl = (glpic_t *)conback->data; + if (!(gl->texnum=Mod_LoadReplacementTexture("gfx/conback.lmp", false, true))) + { + if (!ncdata) //no fallback + { + if (!(gl->texnum=Mod_LoadHiResTexture("pics/conback.pcx", false, true))) + if (!(gl->texnum=Mod_LoadReplacementTexture("gfx/menu/conback.lmp", false, true))) + Sys_Error ("Couldn't load gfx/conback.lmp"); //that's messed it up, hasn't it?... + } + else + { + gl->texnum = GL_LoadTexture ("conback", conback->width, conback->height, ncdata, false, false); + } + } + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + conback->width = vid.conwidth; + conback->height = vid.conheight; + + memcpy(custconback_buffer, conback_buffer, sizeof(custconback_buffer)); + + custom_conback->width = vid.conwidth; + custom_conback->height = vid.conheight; + gl = (glpic_t *)custom_conback->data; + gl->texnum = 0; + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + custom_conback->width = vid.conwidth; + custom_conback->height = vid.conheight; + + gl_conback.modified = true; + + // 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_SafePicFromWad ("disc"); + draw_backtile = Draw_SafePicFromWad ("backtile"); + if (!draw_backtile) + draw_backtile = Draw_SafeCachePic ("gfx/menu/backtile.lmp"); + + detailtexture = Mod_LoadReplacementTexture("textures/detail", true, false); + + inited15to8 = false; +} + +void GLDraw_Init (void) +{ + + memset(scrap_allocated, 0, sizeof(scrap_allocated)); + + GLR_MeshInit(); + + Cmd_AddRemCommand ("gl_texturemode", &GLDraw_TextureMode_f); + + GLDraw_ReInit(); + + + + draw_mesh.numindexes = 6; + draw_mesh.indexes = r_quad_indexes; + draw_mesh.trneighbors = NULL; + + draw_mesh.numvertexes = 4; + draw_mesh.xyz_array = draw_mesh_xyz; + draw_mesh.normals_array = draw_mesh_normals; + draw_mesh.st_array = draw_mesh_st; + draw_mesh.lmst_array = draw_mesh_lmst; +} +void GLDraw_DeInit (void) +{ + Cmd_RemoveCommand("gl_texturemode"); + draw_disc = NULL; + + BZ_Free(uploadmemorybuffer); //free the mem + BZ_Free(uploadmemorybufferintermediate); + uploadmemorybuffer = NULL; //make sure we know it's free + uploadmemorybufferintermediate = NULL; + sizeofuploadmemorybuffer = 0; //and give a nice safe sys_error if we try using it. + sizeofuploadmemorybufferintermediate = 0; +} + + + +/* +================ +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 GLDraw_Character (int x, int y, unsigned int num) +{ + int row, col; + float frow, fcol, size; + + if (num == 32) + return; // space + + if (y <= -8) + return; // totally off screen + + + num &= 255; + + row = num>>4; + col = num&15; + + frow = row*0.0625; + fcol = col*0.0625; + size = 0.0625; + + draw_mesh_xyz[0][0] = x; + draw_mesh_xyz[0][1] = y; + draw_mesh_st[0][0] = fcol; + draw_mesh_st[0][1] = frow; + + draw_mesh_xyz[1][0] = x+8; + draw_mesh_xyz[1][1] = y; + draw_mesh_st[1][0] = fcol+size; + draw_mesh_st[1][1] = frow; + + draw_mesh_xyz[2][0] = x+8; + draw_mesh_xyz[2][1] = y+8; + draw_mesh_st[2][0] = fcol+size; + draw_mesh_st[2][1] = frow+size; + + draw_mesh_xyz[3][0] = x; + draw_mesh_xyz[3][1] = y+8; + draw_mesh_st[3][0] = fcol; + draw_mesh_st[3][1] = frow+size; + + if (num&CON_2NDCHARSETTEXT) + GL_DrawMesh(&draw_mesh, NULL, char_tex2, 0); + else + GL_DrawMesh(&draw_mesh, NULL, char_texture, 0); +return; + + + + if (num&CON_2NDCHARSETTEXT) + GL_Bind (char_tex2); + else + GL_Bind (char_texture); + + num &= 255; + + row = num>>4; + col = num&15; + + frow = row*0.0625; + fcol = col*0.0625; + size = 0.0625; + + 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 (); +} + +void GLDraw_ColouredCharacter (int x, int y, unsigned int num) +{ + int col; + + if (num & CON_BLINKTEXT) + { + if ((int)(cl.time*3) & 1) + return; + } + + { + col = (num&CON_COLOURMASK)/256; + glColor3f(consolecolours[col].r, consolecolours[col].g, consolecolours[col].b); + } + + Draw_Character(x, y, num); +} +/* +================ +Draw_String +================ +*/ +void GLDraw_String (int x, int y, const qbyte *str) +{ + while (*str) + { + Draw_Character (x, y, *str); + str++; + x += 8; + } +} + +/* +================ +Draw_Alt_String +================ +*/ +void GLDraw_Alt_String (int x, int y, const qbyte *str) +{ + while (*str) + { + Draw_Character (x, y, (*str) | 0x80); + str++; + x += 8; + } +} + +#include "crosshairs.dat" +void GLDraw_Crosshair(void) +{ + int x, y; + int sc; + vrect_t rect; + + if (crosshair.value == 1) + { + for (sc = 0; sc < cl.splitclients; sc++) + { + SCR_VRectForPlayer(&rect, sc); + GLDraw_Character (rect.x + rect.width/2-4 + cl_crossx.value, + rect.y + rect.height/2-4 + cl_crossy.value, + '+'); + } + return; + } + glDisable (GL_BLEND); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (crosshair.value) + { + GL_Bind (cs_texture); + + if (cachedcrosshair != crosshair.value || crosshair.value >= FIRSTANIMATEDCROSHAIR) + { + int c = d_8to24rgbtable[(qbyte) crosshaircolor.value]; + int c2 = d_8to24rgbtable[(qbyte) crosshaircolor.value]; + +#define Pix(x,y,c) { \ + if (y+8<0)c=0; \ + if (y+8>=16)c=0; \ + if (x+8<0)c=0; \ + if (x+8>=16)c=0; \ + \ + cs_data[(y+8)*16+(x+8)] = c; \ + } + memset(cs_data, 0, sizeof(cs_data)); + switch((int)crosshair.value) + { + default: +#include "crosshairs.dat" + } + GL_Upload32(NULL, cs_data, 16, 16, 0, true); + +#undef Pix + cachedcrosshair = crosshair.value; + } + } + else if ((*crosshair.string>='a' && *crosshair.string<='z') || (*crosshair.string>='A' && *crosshair.string<='Z')) + { + int i = Mod_LoadHiResTexture (crosshair.string, false, true); + GL_Bind (i); + } + else + return; + + for (sc = 0; sc < cl.splitclients; sc++) + { + SCR_VRectForPlayer(&rect, sc); + + x = rect.x + rect.width/2 - 3 + cl_crossx.value; + y = rect.y + rect.height/2 - 3 + cl_crossy.value; + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x - 4, y - 4); + glTexCoord2f (1, 0); + glVertex2f (x+12, y-4); + glTexCoord2f (1, 1); + glVertex2f (x+12, y+12); + glTexCoord2f (0, 1); + glVertex2f (x - 4, y+12); + glEnd (); + } + +// glTexEnvf ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); +// glTexEnvf ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); +} + + +/* +================ +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 GLDraw_DebugChar (qbyte num) +{ +} + +/* +============= +Draw_Pic +============= +*/ +void GLDraw_Pic (int x, int y, qpic_t *pic) +{ + glpic_t *gl; + + if (!pic) + return; + + if (scrap_dirty) + Scrap_Upload (); + gl = (glpic_t *)pic->data; + + draw_mesh_xyz[0][0] = x; + draw_mesh_xyz[0][1] = y; + draw_mesh_st[0][0] = gl->sl; + draw_mesh_st[0][1] = gl->tl; + + draw_mesh_xyz[1][0] = x+pic->width; + draw_mesh_xyz[1][1] = y; + draw_mesh_st[1][0] = gl->sh; + draw_mesh_st[1][1] = gl->tl; + + draw_mesh_xyz[2][0] = x+pic->width; + draw_mesh_xyz[2][1] = y+pic->height; + draw_mesh_st[2][0] = gl->sh; + draw_mesh_st[2][1] = gl->th; + + draw_mesh_xyz[3][0] = x; + draw_mesh_xyz[3][1] = y+pic->height; + draw_mesh_st[3][0] = gl->sl; + draw_mesh_st[3][1] = gl->th; + + GL_DrawMesh(&draw_mesh, NULL, gl->texnum, 0); + +/* + glColor4f (1,1,1,1); + GL_Bind (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 (); + */ +} + +void GLDraw_LevelPic (qpic_t *pic) //Fullscreen and stuff +{ + glpic_t *gl; + + if (!pic) + return; + + if (scrap_dirty) + Scrap_Upload (); + gl = (glpic_t *)pic->data; + glColor4f (1,1,1,1); + GL_Bind (gl->texnum); + glBegin (GL_QUADS); + glTexCoord2f (gl->sl, gl->tl); + glVertex2f (0, 0); + glTexCoord2f (gl->sh, gl->tl); + glVertex2f (vid.conwidth, 0); + glTexCoord2f (gl->sh, gl->th); + glVertex2f (vid.conwidth, vid.conheight); + glTexCoord2f (gl->sl, gl->th); + glVertex2f (0, vid.conheight); + glEnd (); +} + +/* +============= +Draw_AlphaPic +============= +*/ +void GLDraw_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 (1,1,1,alpha); + GL_Bind (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 (); + glColor4f (1,1,1,1); + glEnable(GL_ALPHA_TEST); + glDisable (GL_BLEND); +} + +void GLDraw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) +{ + glpic_t *gl; + float newsl, newtl, newsh, newth; + float oldglwidth, oldglheight; + + if (scrap_dirty) + Scrap_Upload (); + gl = (glpic_t *)pic->data; + + oldglwidth = gl->sh - gl->sl; + oldglheight = gl->th - gl->tl; + + newsl = gl->sl + (srcx*oldglwidth)/pic->width; + newsh = newsl + (width*oldglwidth)/pic->width; + + newtl = gl->tl + (srcy*oldglheight)/pic->height; + newth = newtl + (height*oldglheight)/pic->height; + + + draw_mesh_xyz[0][0] = x; + draw_mesh_xyz[0][1] = y; + draw_mesh_st[0][0] = newsl; + draw_mesh_st[0][1] = newtl; + + draw_mesh_xyz[1][0] = x+width; + draw_mesh_xyz[1][1] = y; + draw_mesh_st[1][0] = newsh; + draw_mesh_st[1][1] = newtl; + + draw_mesh_xyz[2][0] = x+width; + draw_mesh_xyz[2][1] = y+height; + draw_mesh_st[2][0] = newsh; + draw_mesh_st[2][1] = newth; + + draw_mesh_xyz[3][0] = x; + draw_mesh_xyz[3][1] = y+height; + draw_mesh_st[3][0] = newsl; + draw_mesh_st[3][1] = newth; + + GL_DrawMesh(&draw_mesh, NULL, gl->texnum, 0); +} + +/* +============= +Draw_TransPic +============= +*/ +void GLDraw_TransPic (int x, int y, qpic_t *pic) +{ + if (!pic) + return; + if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || + (unsigned)(y + pic->height) > vid.height) + { + Con_DPrintf("Draw_TransPic: bad coordinates\n"); + return; +// Sys_Error ("Draw_TransPic: bad coordinates"); + } + + GLDraw_Pic (x, y, pic); +} + + +/* +============= +Draw_TransPicTranslate + +Only used for the player color selection menu +============= +*/ +void GLDraw_TransPicTranslate (int x, int y, qpic_t *pic, qbyte *translation) +{ + int v, u, c; + unsigned trans[64*64], *dest; + qbyte *src; + int p; + + 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]; + if (p == 255) + dest[u] = p; + else + dest[u] = d_8to24rgbtable[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); + + glColor3f (1,1,1); + 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 GLDraw_ConsoleBackground (int lines) +{ +// char ver[80]; +// int x, i; + int y; + + y = (vid.height * 3) >> 2; + conback->width = vid.conwidth; + conback->height = vid.conheight; + + if (lines > y) + { + glColor3f (1,1,1); + GLDraw_Pic(0, lines-vid.height, conback); + } + else + { + GLDraw_AlphaPic (0, lines - vid.height, conback, (float)(1.2 * lines)/y); + } +/* + // hack the version number directly into the pic +// y = lines-186; + y = lines-14; + if (!cls.download) { +#ifdef __linux__ + sprintf (ver, "LinuxGL (%4.2f) QuakeWorld", LINUX_VERSION); +#else + sprintf (ver, "GL (%4.2f) QuakeWorld", GLQUAKE_VERSION); +#endif + x = vid.conwidth - (strlen(ver)*8 + 11) - (vid.conwidth*8/320)*7; + for (i=0 ; i> 2; + if (lines > y) + GLDraw_Pic(0, lines-vid.height, conback); + else + GLDraw_AlphaPic (0, lines - vid.height, conback, (float)(1.2 * lines)/y); +} + +/* +============= +Draw_TileClear + +This repeats a 64*64 tile graphic to fill the screen around a sized down +refresh window. +============= +*/ +void GLDraw_TileClear (int x, int y, int w, int h) +{ + glColor3f (1,1,1); + if (!draw_backtile) + { + glDisable(GL_TEXTURE_2D); + 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 (); + glEnable(GL_TEXTURE_2D); + } + else + { + GL_Bind (*(int *)draw_backtile->data); + 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 GLDraw_Fill (int x, int y, int w, int h, int c) +{ + glDisable (GL_TEXTURE_2D); + glColor3f (gammatable[host_basepal[c*3]]/255.0, + gammatable[host_basepal[c*3+1]]/255.0, + gammatable[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 GLDraw_FadeScreen (void) +{ + glEnable (GL_BLEND); + glDisable(GL_ALPHA_TEST); + glDisable (GL_TEXTURE_2D); + glColor4f (0, 0, 0, 0.5); + 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_TEXTURE_2D); + glDisable (GL_BLEND); + glEnable(GL_ALPHA_TEST); + + Sbar_Changed(); +} + +void GLDraw_Image(float x, float y, float w, float h, float s1, float t1, float s2, float t2, qpic_t *pic) +{ + glpic_t *gl; + + if (!pic) + return; + + if (w == 0 && h == 0) + { + w = 64; + h = 64; + } + + if (scrap_dirty) + Scrap_Upload (); + gl = (glpic_t *)pic->data; +/* + s2 = s2 + + newsl = gl->sl + (srcx*oldglwidth)/pic->width; + newsh = newsl + (width*oldglwidth)/pic->width; + + newtl = gl->tl + (srcy*oldglheight)/pic->height; + newth = newtl + (height*oldglheight)/pic->height; +*/ + s2 = s1 + (s2-s1)*gl->sh; + s1 += gl->sl; + t2 = t1 + (t2-t1)*gl->th; + t1 += gl->tl; + + draw_mesh_xyz[0][0] = x; + draw_mesh_xyz[0][1] = y; + draw_mesh_st[0][0] = s1; + draw_mesh_st[0][1] = t1; + + draw_mesh_xyz[1][0] = x+w; + draw_mesh_xyz[1][1] = y; + draw_mesh_st[1][0] = s2; + draw_mesh_st[1][1] = t1; + + draw_mesh_xyz[2][0] = x+w; + draw_mesh_xyz[2][1] = y+h; + draw_mesh_st[2][0] = s2; + draw_mesh_st[2][1] = t2; + + draw_mesh_xyz[3][0] = x; + draw_mesh_xyz[3][1] = y+h; + draw_mesh_st[3][0] = s1; + draw_mesh_st[3][1] = t2; + + GL_DrawMesh(&draw_mesh, NULL, gl->texnum, 0); +} + +//============================================================================= + +/* +================ +Draw_BeginDisc + +Draws the little blue disc in the corner of the screen. +Call before beginning any disc IO. +================ +*/ +void GLDraw_BeginDisc (void) +{ + if (!draw_disc || !r_drawdisk.value) + 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 GLDraw_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); +// glDisable (GL_ALPHA_TEST); + + glColor4f (1,1,1,1); + + if (gl_font.modified) + { + gl_font.modified = 0; + if (!*gl_font.string || !(char_texture=Mod_LoadHiResTexture(va("fonts/%s", gl_font.string), false, true))) + char_texture = default_char_texture; + + gl_smoothfont.modified = 1; + } + if (gl_conback.modified) + { + int newtex = 0; + gl_conback.modified = 0; + if (!*gl_conback.string || !(newtex=Mod_LoadHiResTexture(va("conbacks/%s", gl_conback.string), false, true))) + conback = default_conback; + else + { + conback = custom_conback; + ((glpic_t *)conback->data)->texnum = newtex; + } + } + + if (gl_smoothfont.modified) + { + GL_Bind(char_texture); + 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); + } + } +} + +//==================================================================== + +/* +================ +GL_FindTexture +================ +*/ +int GL_FindTexture (char *identifier) +{ + gltexture_t *glt; + + glt = Hash_Get(&gltexturetable, identifier); + if (glt) + return glt->texnum; +/* + for (glt=gltextures ; glt ; glt=glt->next) + { + if (!strcmp (identifier, glt->identifier)) + return glt->texnum; + } +*/ + + return -1; +} + +gltexture_t *GL_MatchTexture (char *identifier, int bits, int width, int height) +{ + gltexture_t *glt; + + glt = Hash_Get(&gltexturetable, identifier); + while(glt) + { + if (glt->bpp == bits && width == glt->width && height == glt->height) + return glt; + + glt = Hash_GetNext(&gltexturetable, identifier, glt); + } + +/* + for (glt=gltextures ; glt ; glt=glt->next) + { + if (glt->bpp == bits && width == glt->width && height == glt->height) + { + if (!strcmp (identifier, glt->identifier)) + { + return glt; + } + } + } +*/ + + return NULL; +} + +/* +================ +GL_ResampleTexture +================ +*/ +void GL_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight) +{ + int i, j; + unsigned *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_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 (qbyte *in, int width, int height) +{ + int i, j; + qbyte *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; + } + } +} + +#ifdef GL_USE8BITTEX +#ifdef GL_EXT_paletted_texture +void GLDraw_Init15to8(void) +{ + int i, r, g, b, v, k; + int r1, g1, b1; + qbyte *pal; + float dist, bestdist; + FILE *f; + + qboolean savetable; + + // JACK: 3D distance calcs - k is last closest, l is the distance. + if (inited15to8) + return; + if (!d_15to8table) + d_15to8table = BZ_Malloc(sizeof(qbyte) * 32768); + inited15to8 = true; + + savetable = COM_CheckParm("-save15to8"); + + if (savetable) + COM_FOpenFile("glquake/15to8.pal", &f); + else + f = NULL; + if (f) + { + fread(d_15to8table, 1<<15, 1, f); + fclose(f); + } + else + { + for (i=0; i < (1<<15); i++) + { + /* Maps + 000000000000000 + 000000000011111 = Red = 0x1F + 000001111100000 = Blue = 0x03E0 + 111110000000000 = Grn = 0x7C00 + */ + r = ((i & 0x1F) << 3)+4; + g = ((i & 0x03E0) >> 2)+4; + b = ((i & 0x7C00) >> 7)+4; + pal = (unsigned char *)d_8to24rgbtable; + for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) { + r1 = (int)r - (int)pal[0]; + g1 = (int)g - (int)pal[1]; + b1 = (int)b - (int)pal[2]; + dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1))); + if (dist < bestdist) { + k=v; + bestdist = dist; + } + } + d_15to8table[i]=k; + } + if (savetable) + { + char s[256]; + sprintf(s, "%s/glquake", com_gamedir); + Sys_mkdir (s); + sprintf(s, "%s/glquake/15to8.pal", com_gamedir); + if ((f = fopen(s, "wb")) != NULL) { + fwrite(d_15to8table, 1<<15, 1, f); + fclose(f); + } + } + } +} + +/* +================ +GL_MipMap8Bit + +Mipping for 8 bit textures +================ +*/ +void GL_MipMap8Bit (qbyte *in, int width, int height) +{ + int i, j; + qbyte *out; + unsigned short r,g,b; + qbyte *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)]; + } +} +#endif +#endif + +qboolean GL_UploadCompressed (qbyte *file, int *out_width, int *out_height, unsigned int *out_mipmap) +{ + int miplevel; + int width; + int height; + int compressed_size; + int internalformat; + int nummips; +#define GETVAR(var) memcpy(var, file, sizeof(*var));file+=sizeof(*var); + + if (!gl_compressable || !gl_compress.value) + return false; + + GETVAR(&nummips) + GETVAR(out_width) + GETVAR(out_height) + GETVAR(out_mipmap) + for (miplevel = 0; miplevel < nummips; miplevel++) + { + GETVAR(&width); + GETVAR(&height); + GETVAR(&compressed_size); + GETVAR(&internalformat); + width = LittleLong(width); + height = LittleLong(height); + compressed_size = LittleLong(compressed_size); + internalformat = LittleLong(internalformat); + + qglCompressedTexImage2DARB(GL_TEXTURE_2D, miplevel, internalformat, width, height, 0, compressed_size, file); + file += compressed_size; + } + + if (*out_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); + } + return true; +} + +/* +=============== +GL_Upload32 +=============== +*/ +void GL_Upload32 (char *name, unsigned *data, int width, int height, qboolean mipmap, qboolean alpha) +{ + int miplevel=0; + int samples; + unsigned *scaled = (unsigned *)uploadmemorybuffer; + int scaled_width, scaled_height; + + 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 (gl_max_size.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; + } + + if (scaled_width * scaled_height > sizeofuploadmemorybuffer/4) + Sys_Error ("GL_LoadTexture: too big"); + + samples = alpha ? gl_alpha_format : gl_solid_format; + if (gl_compressable && gl_compress.value && name&&mipmap) + samples = alpha ? GL_COMPRESSED_RGBA_ARB : GL_COMPRESSED_RGB_ARB; + +#if 0 + if (mipmap) + gluBuild2DMipmaps (GL_TEXTURE_2D, samples, width, height, GL_RGBA, GL_UNSIGNED_BYTE, trans); + else if (scaled_width == width && scaled_height == height) + glTexImage2D (GL_TEXTURE_2D, 0, samples, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + else + { + gluScaleImage (GL_RGBA, width, height, GL_UNSIGNED_BYTE, trans, + 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; + + if (scaled_width == width && scaled_height == height) + { + if (!mipmap) + { + glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + goto done; + } + memcpy (scaled, data, width*height*4); + } + 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) + { + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) + { + GL_MipMap ((qbyte *)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); + } + } + if (gl_compressable && gl_compress.value && gl_savecompressedtex.value && name&&mipmap) + { + FILE *out; + int miplevels; + GLint compressed; + GLint compressed_size; + GLint internalformat; + unsigned char *img; + char outname[MAX_OSPATH]; + int i; + miplevels = miplevel+1; + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_ARB, &compressed); + if (compressed == GL_TRUE && !strstr(name, "..")) //is there any point in bothering with the whole endian thing? + { + sprintf(outname, "%s/tex/%s.tex", com_gamedir, name); + COM_CreatePath(outname); + out = fopen(outname, "wb"); + if (out) + { + i = LittleLong(miplevels); + fwrite(&i, 1, sizeof(i), out); + i = LittleLong(width); + fwrite(&i, 1, sizeof(i), out); + i = LittleLong(height); + fwrite(&i, 1, sizeof(i), out); + i = LittleLong(mipmap); + fwrite(&i, 1, sizeof(i), out); + for (miplevel = 0; miplevel < miplevels; miplevel++) + { + glGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_COMPRESSED_ARB, &compressed); + glGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_INTERNAL_FORMAT, &internalformat); + glGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, &compressed_size); + glGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_WIDTH, &width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_HEIGHT, &height); + img = (unsigned char *)BZ_Malloc(compressed_size * sizeof(unsigned char)); + qglGetCompressedTexImageARB(GL_TEXTURE_2D, miplevel, img); + + i = LittleLong(width); + fwrite(&i, 1, sizeof(i), out); + i = LittleLong(height); + fwrite(&i, 1, sizeof(i), out); + i = LittleLong(compressed_size); + fwrite(&i, 1, sizeof(i), out); + i = LittleLong(internalformat); + fwrite(&i, 1, sizeof(i), out); + fwrite(img, 1, compressed_size, out); + BZ_Free(img); + } + fclose(out); + } + } + } +done: ; +#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); + } +} + +void GL_Upload8Grey (unsigned char*data, int width, int height, qboolean mipmap) +{ + int samples; + unsigned char *scaled = uploadmemorybuffer; + int scaled_width, scaled_height; + + 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 (gl_max_size.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; + } + + if (scaled_width * scaled_height > sizeofuploadmemorybuffer/4) + Sys_Error ("GL_LoadTexture: too big"); + + samples = 1;//alpha ? gl_alpha_format : gl_solid_format; + +#if 0 + if (mipmap) + gluBuild2DMipmaps (GL_TEXTURE_2D, samples, width, height, GL_RGBA, GL_UNSIGNED_BYTE, trans); + else if (scaled_width == width && scaled_height == height) + glTexImage2D (GL_TEXTURE_2D, 0, samples, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + else + { + gluScaleImage (GL_RGBA, width, height, GL_UNSIGNED_BYTE, trans, + 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; + + if (scaled_width == width && scaled_height == height) + { + if (!mipmap) + { + glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + goto done; + } + memcpy (scaled, data, width*height); + } + else + GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height); + + glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, scaled); + if (mipmap) + { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) + { + GL_MipMap ((qbyte *)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_LUMINANCE, GL_UNSIGNED_BYTE, scaled); + } + } +done: ; +#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); + } +} + + + + + + + + + + + +void GL_MipMapNormal (qbyte *in, int width, int height) +{ + int i, j; + qbyte *out; + float inv255 = 1.0f/255.0f; + float inv127 = 1.0f/127.0f; + float x,y,z,l,mag00,mag01,mag10,mag11; + + + width <<=2; + height >>= 1; + out = in; + for (i=0 ; i 1.0) { + out[3] = 255; + } else { + out[3] = (qbyte)(255.0*l); + } + } + } +} + +//PENTA + +//sizeofuploadmemorybufferintermediate is guarenteed to be bigger or equal to the normal uploadbuffer size +unsigned int * genNormalMap(qbyte *pixels, int w, int h, float scale) +{ + int i, j, wr, hr; + unsigned char r, g, b; + unsigned *nmap = (unsigned *)uploadmemorybufferintermediate; + float sqlen, reciplen, nx, ny, nz; + + const float oneOver255 = 1.0f/255.0f; + + float c, cx, cy, dcx, dcy; + + wr = w; + hr = h; + + for (i=0; i Added support for big endian. + } + } + + return &nmap[0]; +} + +//PENTA +void GL_UploadBump(qbyte *data, int width, int height, qboolean mipmap) { + + int s; + unsigned char *scaled = uploadmemorybuffer; + int scaled_width, scaled_height; + qbyte *nmap; + + s = width*height; + + //Resize to power of 2 and maximum texture size + 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 (gl_max_size.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; + } + + if (scaled_width * scaled_height > sizeofuploadmemorybuffer/4) + Sys_Error ("GL_LoadTexture: too big"); + + //To resize or not to resize + if (scaled_width == width && scaled_height == height) + { + memcpy (scaled, data, width*height); + scaled_width = width; + scaled_height = height; + } + else { + //Just picks pixels so grayscale is equivalent with 8 bit. + GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height); + } + +// if (is_overriden) +// nmap = (qbyte *)genNormalMap(scaled,scaled_width,scaled_height,10.0f); +// else + nmap = (qbyte *)genNormalMap(scaled,scaled_width,scaled_height,4.0f); + + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA + , scaled_width, scaled_height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, nmap); + + //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (mipmap) + { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) + { + GL_MipMapNormal(nmap,scaled_width,scaled_height); + //GL_MipMapGray((qbyte *)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_RGBA, scaled_width, scaled_height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, nmap); + //glTexImage2D (GL_TEXTURE_2D, miplevel, GL_RGBA, scaled_width, scaled_height, 0, GL_RGBA, + // GL_UNSIGNED_BYTE, genNormalMap(scaled,scaled_width,scaled_height,4.0f)); + } + } + + 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); + } + +// if (gl_texturefilteranisotropic) +// glTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &gl_texureanisotropylevel); + +} + + + + +#ifdef GL_USE8BITTEX +#ifdef GL_EXT_paletted_texture +void GL_Upload8_EXT (qbyte *data, int width, int height, qboolean mipmap, qboolean alpha) +{ + int i, s; + qboolean noalpha; + int samples; + unsigned char *scaled = uploadmemorybuffer; + int scaled_width, scaled_height; + + GLDraw_Init15to8(); + + s = width*height; + // 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 ; i>= (int)gl_picmip.value; + scaled_height >>= (int)gl_picmip.value; + + if (gl_max_size.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; + } + + if (scaled_width * scaled_height > sizeofuploadmemorybufferintermediate/4) + Sys_Error ("GL_LoadTexture: too big"); + + samples = 1; // alpha ? gl_alpha_format : gl_solid_format; + + 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; + } + memcpy (scaled, data, width*height); + } + else + GL_Resample8BitTexture (data, width, height, 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 ((qbyte *)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); + } +} +#endif +#endif + +/* +=============== +GL_Upload8 +=============== +*/ +int ColorIndex[16] = +{ + 0, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 199, 207, 223, 231 +}; + +unsigned ColorPercent[16] = +{ + 25, 51, 76, 102, 114, 127, 140, 153, 165, 178, 191, 204, 216, 229, 237, 247 +}; + +void GL_Upload8 (qbyte *data, int width, int height, qboolean mipmap, qboolean alpha) +{ + unsigned *trans = (unsigned *)uploadmemorybufferintermediate; + int i, s; + qboolean noalpha; + int p; + + if (width*height > sizeofuploadmemorybufferintermediate/4) + Sys_Error("GL_Upload8: image too big (%i*%i)", width, height); + + s = width*height; + // 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 ; i>4]] & 0x00ffffff; + trans[i] |= ( int )ColorPercent[p&15] << 24; + //trans[i] = 0x7fff0000; + } + break; + } + } + else + { + if (s&3) + Sys_Error ("GL_Upload8: s&3"); + for (i=0 ; i sizeofuploadmemorybufferintermediate/4) + Sys_Error("GL_Upload8FB: image too big (%i*%i)", width, height); + // if there are no transparent pixels, make it a 3 component + // texture even if it was specified as otherwise + noalpha = true; + for (i=0 ; i sizeofuploadmemorybufferintermediate/4) + Sys_Error("GL_Upload8Pal24: image too big (%i*%i)", width, height); + + // 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 ; itexnum; + } + + glt = BZ_Malloc(sizeof(*glt)+sizeof(bucket_t)); + glt->next = gltextures; + gltextures = glt; + + strcpy (glt->identifier, identifier); + glt->texnum = texture_extension_number; + glt->width = width; + glt->height = height; + glt->bpp = 8; + glt->mipmap = mipmap; + + Hash_Add2(&gltexturetable, glt->identifier, glt, (bucket_t*)(glt+1)); + + GL_Bind(texture_extension_number ); + + GL_Upload8 (data, width, height, mipmap, alpha); + + texture_extension_number++; + + return texture_extension_number-1; +} + +int GL_LoadTextureFB (char *identifier, int width, int height, qbyte *data, qboolean mipmap, qboolean alpha) +{ + int i; + gltexture_t *glt; + + // see if the texture is allready present + if (identifier[0]) + { + glt = GL_MatchTexture(identifier, 8, width, height); + if (glt) + return glt->texnum; + } + + glt = BZ_Malloc(sizeof(*glt)+sizeof(bucket_t)); + glt->next = gltextures; + gltextures = glt; + + for (i = 0; i < width*height; i++) + if (data[i] > 255-vid.fullbright) + break; + + if (i == width*height) + return 0; //none found, don't bother uploading. + + strcpy (glt->identifier, identifier); + glt->texnum = texture_extension_number; + glt->width = width; + glt->height = height; + glt->bpp = 8; + glt->mipmap = mipmap; + + Hash_Add2(&gltexturetable, glt->identifier, glt, (bucket_t*)(glt+1)); + + GL_Bind(texture_extension_number ); + + GL_Upload8FB (data, width, height, mipmap); + + texture_extension_number++; + + return texture_extension_number-1; +} + +int GL_LoadTexture8Pal24 (char *identifier, int width, int height, qbyte *data, qbyte *palette24, qboolean mipmap, qboolean alpha) +{ + gltexture_t *glt; + + // see if the texture is allready present + if (identifier[0]) + { + glt = GL_MatchTexture(identifier, 24, width, height); + if (glt) + return glt->texnum; + } + + glt = BZ_Malloc(sizeof(*glt)+sizeof(bucket_t)); + glt->next = gltextures; + gltextures = glt; + + + strcpy (glt->identifier, identifier); + glt->texnum = texture_extension_number; + glt->width = width; + glt->height = height; + glt->bpp = 24; + glt->mipmap = mipmap; + + Hash_Add2(&gltexturetable, glt->identifier, glt, (bucket_t*)(glt+1)); + + GL_Bind(texture_extension_number ); + + GL_Upload8Pal24 (data, palette24, width, height, mipmap, alpha); + + texture_extension_number++; + + return texture_extension_number-1; +} + +int GL_LoadTexture32 (char *identifier, int width, int height, unsigned *data, qboolean mipmap, qboolean alpha) +{ +// qboolean noalpha; +// int p, s; + gltexture_t *glt; + + // see if the texture is allready present + if (identifier[0]) + { + glt = GL_MatchTexture(identifier, 32, width, height); + if (glt) + return glt->texnum; + } + + glt = BZ_Malloc(sizeof(*glt)+sizeof(bucket_t)); + glt->next = gltextures; + gltextures = glt; + + strcpy (glt->identifier, identifier); + glt->texnum = texture_extension_number; + glt->width = width; + glt->height = height; + glt->bpp = 32; + glt->mipmap = mipmap; + + Hash_Add2(&gltexturetable, glt->identifier, glt, (bucket_t*)(glt+1)); + +// if (!isDedicated) + { + GL_Bind(texture_extension_number ); + + GL_Upload32 (identifier, data, width, height, mipmap, alpha); + } + + texture_extension_number++; + + return texture_extension_number-1; +} + +int GL_LoadCompressed(char *name) +{ + qbyte *COM_LoadFile (char *path, int usehunk); + unsigned char *file; + gltexture_t *glt; + char inname[MAX_OSPATH]; + + if (!gl_compressable || !gl_compress.value) + return 0; + + + // see if the texture is allready present + if (name[0]) + { + int num = GL_FindTexture(name); + if (num != -1) + return num; + } + else + return 0; + + + _snprintf(inname, sizeof(inname)-1, "tex/%s.tex", name); + file = COM_LoadFile(inname, 5); + if (!file) + return 0; + + glt = BZ_Malloc(sizeof(*glt)+sizeof(bucket_t)); + glt->next = gltextures; + gltextures = glt; + + strcpy (glt->identifier, name); + glt->texnum = texture_extension_number; + glt->bpp = 32; + + Hash_Add2(&gltexturetable, glt->identifier, glt, (bucket_t*)(glt+1)); + + GL_Bind(texture_extension_number ); + + if (!GL_UploadCompressed (file, &glt->width, &glt->height, (unsigned int *)&glt->mipmap)) + return 0; + + texture_extension_number++; + + return texture_extension_number-1; +} + +int GL_LoadTexture8Grey (char *identifier, int width, int height, unsigned char *data, qboolean mipmap) +{ +// qboolean noalpha; +// int p, s; + gltexture_t *glt; + + // see if the texture is allready present + if (identifier[0]) + { + glt = GL_MatchTexture(identifier, 8, width, height); + if (glt) + return glt->texnum; + } + + glt = BZ_Malloc(sizeof(*glt)+sizeof(bucket_t)); + glt->next = gltextures; + gltextures = glt; + + strcpy (glt->identifier, identifier); + glt->texnum = texture_extension_number; + glt->width = width; + glt->height = height; + glt->bpp = 8; + glt->mipmap = mipmap; + + Hash_Add2(&gltexturetable, glt->identifier, glt, (bucket_t*)(glt+1)); + +// if (!isDedicated) + { + GL_Bind(texture_extension_number ); + + GL_Upload8Grey (data, width, height, mipmap); + } + + texture_extension_number++; + + return texture_extension_number-1; +} + +int GL_LoadTexture8Bump (char *identifier, int width, int height, unsigned char *data, qboolean mipmap) +{ +// qboolean noalpha; + // int p, s; + gltexture_t *glt; + + // see if the texture is allready present + if (identifier[0]) + { + glt = GL_MatchTexture(identifier, 8, width, height); + if (glt) + return glt->texnum; + } + + glt = BZ_Malloc(sizeof(*glt)+sizeof(bucket_t)); + glt->next = gltextures; + gltextures = glt; + + strcpy (glt->identifier, identifier); + glt->texnum = texture_extension_number; + glt->width = width; + glt->height = height; + glt->bpp = 8; + glt->mipmap = mipmap; + + Hash_Add2(&gltexturetable, glt->identifier, glt, (bucket_t*)(glt+1)); + +// if (!isDedicated) + { + GL_Bind(texture_extension_number ); + + GL_UploadBump (data, width, height, mipmap); + } + + texture_extension_number++; + + return texture_extension_number-1; +} + +/* +================ +GL_LoadPicTexture +================ +*/ +int GL_LoadPicTexture (qpic_t *pic) +{ + return GL_LoadTexture ("", pic->width, pic->height, pic->data, false, true); +} + +/****************************************/ + +void GL_SelectTexture (GLenum target) +{ + if (!gl_mtexable) + return; + qglSelectTextureSGIS(target); +} diff --git a/engine/gl/gl_hlmdl.c b/engine/gl/gl_hlmdl.c new file mode 100644 index 00000000..c37852c2 --- /dev/null +++ b/engine/gl/gl_hlmdl.c @@ -0,0 +1,649 @@ +#include "bothdefs.h" + +#ifdef HALFLIFEMODELS + +#include "quakedef.h" +#include "glquake.h" +/* + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + Half-Life Model Renderer (Experimental) Copyright (C) 2001 James 'Ender' Brown [ender@quakesrc.org] 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. fromquake.h - + + render.c - apart from calculations (mostly range checking or value conversion code is a mix of standard Quake 1 + meshing, and vertex deforms. The rendering loop uses standard Quake 1 drawing, after SetupBones deforms the vertex. + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + + + Also, please note that it won't do all hl models.... + Nor will it work 100% + */ +#include "model_hl.h" + +void VectorTransform (const vec3_t in1, const float in2[3][4], vec3_t out); + +void QuaternionGLMatrix(float x, float y, float z, float w, vec4_t *GLM) +{ + GLM[0][0] = 1 - 2 * y * y - 2 * z * z; + GLM[1][0] = 2 * x * y + 2 * w * z; + GLM[2][0] = 2 * x * z - 2 * w * y; + GLM[0][1] = 2 * x * y - 2 * w * z; + GLM[1][1] = 1 - 2 * x * x - 2 * z * z; + GLM[2][1] = 2 * y * z + 2 * w * x; + GLM[0][2] = 2 * x * z + 2 * w * y; + GLM[1][2] = 2 * y * z - 2 * w * x; + GLM[2][2] = 1 - 2 * x * x - 2 * y * y; +} + +/* + ======================================================================================================================= + QuaternionGLAngle - Convert a GL angle to a quaternion matrix + ======================================================================================================================= + */ +void QuaternionGLAngle(const vec3_t angles, vec4_t quaternion) +{ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + float yaw = angles[2] * 0.5; + float pitch = angles[1] * 0.5; + float roll = angles[0] * 0.5; + float siny = sin(yaw); + float cosy = cos(yaw); + float sinp = sin(pitch); + float cosp = cos(pitch); + float sinr = sin(roll); + float cosr = cos(roll); + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + quaternion[0] = sinr * cosp * cosy - cosr * sinp * siny; + quaternion[1] = cosr * sinp * cosy + sinr * cosp * siny; + quaternion[2] = cosr * cosp * siny - sinr * sinp * cosy; + quaternion[3] = cosr * cosp * cosy + sinr * sinp * siny; +} + + + + + +float transform_matrix[128][3][4]; /* Vertex transformation matrix */ + +void GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float tex_h); + +/* + ======================================================================================================================= + Mod_LoadHLModel - read in the model's constituent parts + ======================================================================================================================= + */ +extern char loadname[]; +void Mod_LoadHLModel (model_t *mod, void *buffer) +{ + /*~~*/ + int i; + + hlmodelcache_t *model; + hlmdl_header_t *header; + hlmdl_tex_t *tex; + hlmdl_bone_t *bones; + hlmdl_bonecontroller_t *bonectls; + + int start, end, total; + /*~~*/ + + + //checksum the model + + if (!strcmp(mod->name, "progs/player.mdl") || + !strcmp(mod->name, "progs/eyes.mdl")) { + unsigned short crc; + qbyte *p; + int len; + char st[40]; + + CRC_Init(&crc); + for (len = com_filesize, p = buffer; len; len--, p++) + CRC_ProcessByte(&crc, *p); + + sprintf(st, "%d", (int) crc); + Info_SetValueForKey (cls.userinfo, + !strcmp(mod->name, "progs/player.mdl") ? pmodel_name : emodel_name, + st, MAX_INFO_STRING); + + if (cls.state >= ca_connected) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + sprintf(st, "setinfo %s %d", + !strcmp(mod->name, "progs/player.mdl") ? pmodel_name : emodel_name, + (int)crc); + SZ_Print (&cls.netchan.message, st); + } + } + + start = Hunk_LowMark (); + + + //load the model into hunk + model = Hunk_Alloc(sizeof(hlmodelcache_t)); + + header = Hunk_Alloc(com_filesize); + memcpy(header, buffer, com_filesize); + + if (header->version != 10) + Host_EndGame("Cannot load model %s - unknown version %i\n", mod->name, header->version); + + tex = (hlmdl_tex_t *) ((qbyte *) header + header->textures); + bones = (hlmdl_bone_t *) ((qbyte *) header + header->boneindex); + bonectls = (hlmdl_bonecontroller_t *) ((qbyte *) header + header->controllerindex); + + +/* won't work - doesn't know exact sizes. + + header = Hunk_Alloc(sizeof(hlmdl_header_t)); + memcpy(header, (hlmdl_header_t *) buffer, sizeof(hlmdl_header_t)); + + tex = Hunk_Alloc(sizeof(hlmdl_tex_t)*header->numtextures); + memcpy(tex, (hlmdl_tex_t *) buffer, sizeof(hlmdl_tex_t)*header->numtextures); + + bones = Hunk_Alloc(sizeof(hlmdl_bone_t)*header->numtextures); + memcpy(bones, (hlmdl_bone_t *) buffer, sizeof(hlmdl_bone_t)*header->numbones); + + bonectls = Hunk_Alloc(sizeof(hlmdl_bonecontroller_t)*header->numcontrollers); + memcpy(bonectls, (hlmdl_bonecontroller_t *) buffer, sizeof(hlmdl_bonecontroller_t)*header->numcontrollers); +*/ + + model->header = (char *)header - (char *)model; + model->textures = (char *)tex - (char *)model; + model->bones = (char *)bones - (char *)model; + model->bonectls = (char *)bonectls - (char *)model; + + for(i = 0; i < header->numtextures; i++) + { + tex[i].i = GL_LoadTexture8Pal24("", tex[i].w, tex[i].h, (qbyte *) header + tex[i].i, (qbyte *) header + tex[i].w * tex[i].h + tex[i].i, true, false); + } + + +// +// move the complete, relocatable alias model to the cache +// + end = Hunk_LowMark (); + total = end - start; + + mod->type = mod_halflife; + + Cache_Alloc (&mod->cache, total, loadname); + if (!mod->cache.data) + return; + memcpy (mod->cache.data, model, total); + + Hunk_FreeToLowMark (start); +} + +/* + ======================================================================================================================= + HL_CurSequence - return the current sequence + ======================================================================================================================= + */ +int HL_CurSequence(hlmodel_t model) +{ + return model.sequence; +} + +/* + ======================================================================================================================= + HL_NewSequence - animation control (just some range checking really) + ======================================================================================================================= + */ +int HL_NewSequence(hlmodel_t *model, int _inew) +{ + if(_inew < 0) + _inew = model->header->numseq - 1; + else if(_inew >= model->header->numseq) + _inew = 0; + + model->sequence = _inew; + model->frame = 0; + { + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + hlmdl_sequencelist_t *pseqdesc; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + if(_inew == 0) + { + pseqdesc = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) + model->sequence; + } + else + { + pseqdesc = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) + model->sequence; + } + + Sys_Printf("Current Sequence: %s\n", pseqdesc->name); + } + + return model->sequence; +} + +/* + ======================================================================================================================= + HL_SetController - control where the model is facing (upper body usually) + ======================================================================================================================= + */ +void HL_SetController(hlmodel_t *model, int num, float value) +{ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + int real, limit; + hlmdl_bonecontroller_t *control = (hlmdl_bonecontroller_t *) + ((qbyte *) model->header + model->header->controllerindex); + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + if(num >= model->header->numcontrollers) return; + + if(num == 4) + { + limit = 64; + } + else + { + limit = 255; + } + + if(control->type & (0x0008 | 0x0010 | 0x0020)) + { + if(control->end < control->start) value = -value; + + if(control->start + 359.0 >= control->end) + { + if(value > ((control->start + control->end) / 2.0) + 180) value = value - 360; + if(value < ((control->start + control->end) / 2.0) - 180) value = value + 360; + } + else + { + if(value > 360) + value = value - (int) (value / 360.0) * 360.0; + else if(value < 0) + value = value + (int) ((value / -360.0) + 1) * 360.0; + } + } + + real = limit * (value - control[num].start) / (control[num].end - control[num].start); + if(real < 0) real = 0; + if(real > limit) real = limit; + model->controller[num] = real; +} + +/* + ======================================================================================================================= + HL_CalculateBones - calculate bone positions - quaternion+vector in one function + ======================================================================================================================= + */ +void HL_CalculateBones +( + int offset, + int frame, + vec4_t adjust, + hlmdl_bone_t *bone, + hlmdl_anim_t *animation, + float *destination +) +{ + /*~~~~~~~~~~*/ + int i; + vec3_t angle; + /*~~~~~~~~~~*/ + + /* For each vector */ + for(i = 0; i < 3; i++) + { + /*~~~~~~~~~~~~~~~*/ + int o = i + offset; /* Take the value offset - allows quaternion & vector in one function */ + /*~~~~~~~~~~~~~~~*/ + + angle[i] = bone->value[o]; /* Take the bone value */ + + if(animation->offset[o] != 0) + { + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + int tempframe = frame; + hlmdl_animvalue_t *animvalue = (hlmdl_animvalue_t *) ((qbyte *) animation + animation->offset[o]); + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + /* find values including the required frame */ + while(animvalue->num.total <= tempframe) + { + tempframe -= animvalue->num.total; + animvalue += animvalue->num.valid + 1; + } + if(animvalue->num.valid > tempframe) + { + if(animvalue->num.valid > (tempframe + 1)) + angle[i] += animvalue[tempframe + 1].value * 1; // + 0 * animvalue[tempframe + 2].value * bone->scale[o]; + else + angle[i] = animvalue[animvalue->num.valid].value; + angle[i] = bone->value[o] + angle[i] * bone->scale[o]; + } + else + { + if(animvalue->num.total <= tempframe + 1) + { + angle[i] += + (animvalue[animvalue->num.valid].value * 1 + + 0 * animvalue[animvalue->num.valid + 2].value) * + bone->scale[o]; + } + else + { + angle[i] += animvalue[animvalue->num.valid].value * bone->scale[o]; + } + } + } + + if(bone->bonecontroller[o] != -1) { /* Add the programmable offset. */ + angle[i] += adjust[bone->bonecontroller[o]]; + } + } + + if(offset < 3) + { + VectorCopy(angle, destination); /* Just a standard vector */ + } + else + { + QuaternionGLAngle(angle, destination); /* A quaternion */ + } +} + +/* + ======================================================================================================================= + HL_CalcBoneAdj - Calculate the adjustment values for the programmable controllers + ======================================================================================================================= + */ +void HL_CalcBoneAdj(hlmodel_t *model) +{ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + int i; + float value; + hlmdl_bonecontroller_t *control = (hlmdl_bonecontroller_t *) + ((qbyte *) model->header + model->header->controllerindex); + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + for(i = 0; i < model->header->numcontrollers; i++) + { + /*~~~~~~~~~~~~~~~~~~~~~*/ + int j = control[i].index; + /*~~~~~~~~~~~~~~~~~~~~~*/ + + if(control[i].type & 0x8000) + { + value = model->controller[j] * (360.0 / 256.0) + control[i].start; + } + else + { + value = model->controller[j] / 255.0; + if(value < 0) + value = 0; + else if(value > 1.0) + value = 1.0; + value = (1.0 - value) * control[i].start + value * control[i].end; + } + + /* Rotational controllers need their values converted */ + if(control[i].type >= 0x0008 && control[i].type <= 0x0020) + model->adjust[i] = M_PI * value / 180; + else + model->adjust[i] = value; + } +} + +/* + ======================================================================================================================= + HL_SetupBones - determine where vertex should be using bone movements + ======================================================================================================================= + */ +void HL_SetupBones(hlmodel_t *model) +{ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + int i; + float matrix[3][4]; + static vec3_t positions[128]; + static vec4_t quaternions[128]; + hlmdl_sequencelist_t *sequence = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) + + model->sequence; + hlmdl_sequencedata_t *sequencedata = (hlmdl_sequencedata_t *) + ((qbyte *) model->header + model->header->seqgroups) + + sequence->seqindex; + hlmdl_anim_t *animation = (hlmdl_anim_t *) + ((qbyte *) model->header + sequencedata->data + sequence->index); + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + HL_CalcBoneAdj(model); /* Deal with programmable controllers */ + + if(sequence->motiontype & 0x0001) positions[sequence->motionbone][0] = 0.0; + if(sequence->motiontype & 0x0002) positions[sequence->motionbone][1] = 0.0; + if(sequence->motiontype & 0x0004) positions[sequence->motionbone][2] = 0.0; + + /* Sys_Printf("Frame: %i\n", model->frame); */ + for(i = 0; i < model->header->numbones; i++) + { + /* + * There are two vector offsets in the structure. The first seems to be the + * positions of the bones, the second the quats of the bone matrix itself. We + * convert it inside the routine - Inconsistant, but hey.. so's the whole model + * format. + */ + HL_CalculateBones(0, model->frame, model->adjust, model->bones + i, animation + i, positions[i]); + HL_CalculateBones(3, model->frame, model->adjust, model->bones + i, animation + i, quaternions[i]); + + /* FIXME: Blend the bones and make them cry :) */ + QuaternionGLMatrix(quaternions[i][0], quaternions[i][1], quaternions[i][2], quaternions[i][3], matrix); + matrix[0][3] = positions[i][0]; + matrix[1][3] = positions[i][1]; + matrix[2][3] = positions[i][2]; + + /* If we have a parent, take the addition. Otherwise just copy the values */ + if(model->bones[i].parent>=0) + { + R_ConcatTransforms(transform_matrix[model->bones[i].parent], matrix, transform_matrix[i]); + } + else + { + memcpy(transform_matrix[i], matrix, 12 * sizeof(float)); + } + } +} + +/* + ======================================================================================================================= + R_Draw_HL_AliasModel - main drawing function + ======================================================================================================================= + */ +void R_DrawHLModel(entity_t *curent) +{ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + hlmodelcache_t *modelc = Mod_Extradata(curent->model); + hlmodel_t model; + int b, m, v; + short *skins; + hlmdl_sequencelist_t *sequence; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + //general model + model.header = (hlmdl_header_t *) ((char *)modelc + modelc->header); + model.textures = (hlmdl_tex_t *) ((char *)modelc + modelc->textures); + model.bones = (hlmdl_bone_t *) ((char *)modelc + modelc->bones); + model.bonectls = (hlmdl_bonecontroller_t *) ((char *)modelc + modelc->bonectls); + + //specific to entity + model.sequence = curent->frame; + model.frame = 0; + model.frametime = 0; + + HL_NewSequence(&model, curent->frame); + + skins = (short *) ((qbyte *) model.header + model.header->skins); + sequence = (hlmdl_sequencelist_t *) ((qbyte *) model.header + model.header->seqindex) + + model.sequence; + + model.controller[0] = 127; + model.controller[1] = 127; + model.controller[2] = 127; + model.controller[3] = 127; + model.controller[4] = 0;//sin(cl.time)*127+127; + + model.frametime += (cl.time - cl.lerpents[curent->keynum].framechange)*sequence->timing; + + if (model.frametime>=1) + { + model.frame += (int) model.frametime; + model.frametime -= (int)model.frametime; + } + + if (!sequence->numframes) + return; + if(model.frame >= sequence->numframes) + model.frame %= sequence->numframes; + + if (sequence->motiontype) + model.frame = sequence->numframes-1; + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (curent->alpha<1) + { + glEnable(GL_BLEND); + } + else + { + glDisable(GL_BLEND); + } + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +// Con_Printf("%s %i\n", sequence->name, sequence->unknown1[0]); + + glPushMatrix(); + + { + vec3_t difuse, ambient, ldir; + cl.worldmodel->funcs.LightPointValues(curent->origin, difuse, ambient, ldir); + glColor4f(difuse[0]/255+ambient[0]/255, difuse[1]/255+ambient[1]/255, difuse[2]/255+ambient[2]/255, curent->alpha); + } + + R_RotateForEntity (curent); + + HL_SetupBones(&model); /* Setup the bones */ + + /* Manipulate each mesh directly */ + for(b = 0; b < model.header->numbodyparts; b++) + { + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + hlmdl_bodypart_t *bodypart = (hlmdl_bodypart_t *) ((qbyte *) model.header + model.header->bodypartindex) + + b; + int bodyindex = (0 / bodypart->base) % bodypart->nummodels; + hlmdl_model_t *amodel = (hlmdl_model_t *) ((qbyte *) model.header + bodypart->modelindex) + bodyindex; + qbyte *bone = ((qbyte *) model.header + amodel->vertinfoindex); + vec3_t *verts = (vec3_t *) ((qbyte *) model.header + amodel->vertindex); + vec3_t transformed[2048]; + +// vec3_t *norms = (vec3_t *) ((qbyte *) model.header + amodel->unknown3[2]); +// vec3_t transformednorms[2048]; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + for(v = 0; v < amodel->numverts; v++) // Transform per the matrix + { + VectorTransform(verts[v], transform_matrix[bone[v]], transformed[v]); +// glVertex3fv(verts[v]); +// glVertex3f( verts[v][0]+10*verts[v][0], +// verts[v][1]+10*verts[v][1], +// verts[v][2]+10*verts[v][2]); + } + + //Need to work out what we have! + //raw data appears to be unit vectors + //transformed gives some points on the skeleton. + //what's also weird is that the meshes use these up! +/* glDisable(GL_TEXTURE_2D); + glBegin(GL_LINES); + for(v = 0; v < amodel->unknown3[0]; v++) // Transform per the matrix + { + VectorTransform(norms[v], transform_matrix[bone[v]], transformednorms[v]); + glVertex3fv(transformednorms[v]); + glVertex3f( transformednorms[v][0]+10*transformednorms[v][0], + transformednorms[v][1]+10*transformednorms[v][1], + transformednorms[v][2]+10*transformednorms[v][2]); + } + glEnd(); + glEnable(GL_TEXTURE_2D); +*/ + + + /* Draw each mesh */ + for(m = 0; m < amodel->nummesh; m++) + { + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + hlmdl_mesh_t *mesh = (hlmdl_mesh_t *) ((qbyte *) model.header + amodel->meshindex) + m; + float tex_w = 1.0f / model.textures[skins[mesh->skinindex]].w; + float tex_h = 1.0f / model.textures[skins[mesh->skinindex]].h; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + GL_Bind(model.textures[skins[mesh->skinindex]].i); + GL_Draw_HL_AliasFrame((short *) ((qbyte *) model.header + mesh->index), transformed, tex_w, tex_h); + } + } + + glPopMatrix(); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +} + +/* + ======================================================================================================================= + GL_Draw_HL_AliasFrame - clip and draw all triangles + ======================================================================================================================= + */ +void GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float tex_h) +{ + /*~~~~~~~~~~*/ + int count = 0; + /*~~~~~~~~~~*/ + +// int c_tris=0; +// int c_verts=0; +// int c_chains=0; + + for(;;) + { + count = *order++; /* get the vertex count and primitive type */ + if(!count) break; /* done */ + + if(count < 0) + { + count = -count; + glBegin(GL_TRIANGLE_FAN); + } + else + { + glBegin(GL_TRIANGLE_STRIP); + } +// c_tris += count-2; +// c_chains++; + + do + { + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + float *verts = transformed[order[0]]; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + /* texture coordinates come from the draw list */ + glTexCoord2f(order[2] * tex_w, order[3] * tex_h); + order += 4; + + glVertex3fv(verts); +// c_verts++; + } while(--count); + + glEnd(); + } +} + +#endif diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c new file mode 100644 index 00000000..eb63e46e --- /dev/null +++ b/engine/gl/gl_model.c @@ -0,0 +1,3005 @@ +/* +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. + +*/ +// models.c -- model loading and caching + +// models are the only shared resource between a client and server running +// on the same machine. + +#include "quakedef.h" +#include "glquake.h" + +extern int gl_bumpmappingpossible; +qboolean isnotmap = true; //used to not warp ammo models. + + + +#ifndef SWQUAKE +model_t *loadmodel; +char loadname[32]; // for hunk tags +#else +extern model_t *loadmodel; +extern char loadname[32]; // for hunk tags +#endif + +void CM_Init(void); + +void GLMod_LoadSpriteModel (model_t *mod, void *buffer); +void GLMod_LoadSprite2Model (model_t *mod, void *buffer); +void GLMod_LoadBrushModel (model_t *mod, void *buffer); +#ifdef Q2BSPS +void Mod_LoadQ2BrushModel (model_t *mod, void *buffer); +#endif +void Mod_LoadHLModel (model_t *mod, void *buffer); +void Mod_LoadAlias3Model (model_t *mod, void *buffer); +void Mod_LoadGroupModel (model_t *mod, void *buffer); +model_t *GLMod_LoadModel (model_t *mod, qboolean crash); + +#ifdef DOOMWADS +qboolean Mod_LoadDoomLevel(model_t *mod); +#endif + +void GL_LoadQ1Model (model_t *mod, void *buffer); +#ifdef MD2MODELS +void GL_LoadQ2Model (model_t *mod, void *buffer); +#endif +#ifdef MD3MODELS +void GL_LoadQ3Model (model_t *mod, void *buffer); +#endif + +#ifdef DOOMWADS +void GLMod_LoadDoomSprite (model_t *mod); +#endif + +#define MAX_MOD_KNOWN 2048 +#ifndef SWQUAKE +qbyte mod_novis[MAX_MAP_LEAFS/8]; + +model_t mod_known[MAX_MOD_KNOWN]; +int mod_numknown; + +#else + +extern qbyte mod_novis[MAX_MAP_LEAFS/8]; + +extern model_t mod_known[MAX_MOD_KNOWN]; +extern int mod_numknown; +#endif + +extern cvar_t r_loadlits; +extern cvar_t r_fb_bmodels; +extern cvar_t gl_subdivide_size; +extern cvar_t gl_subdivide_water; + + +#ifdef RUNTIMELIGHTING +model_t *lightmodel; +int numlightdata; + +int relitsurface; +void GLMod_UpdateLightmap(int snum) +{ + msurface_t *s; + if (lightmodel) + { +// int i; +// for (s = lightmodel->surfaces,i=0; i < lightmodel->numsurfaces; i++,s++) +// s->cached_dlight = -1; + + if (snum < lightmodel->numsurfaces) + { + s = lightmodel->surfaces + snum; + s->cached_dlight = -1; + } + else + Con_Printf("lit non-existant surface\n"); + } +} +#endif + + +void GLMod_TextureList_f(void) +{ + int m, i; + texture_t *tx; + model_t *mod; + qboolean shownmodelname; + for (m=0 , mod=mod_known ; mtype == mod_brush && !mod->needload) + { + if (*mod->name == '*') + continue;// inlines don't count + shownmodelname = false; + for (i = 0; i < mod->numtextures; i++) + { + tx = mod->textures[i]; + if (!tx) + continue; //happens on e1m2 + + if (!shownmodelname) + { + shownmodelname = true; + Con_Printf("%s\n", mod->name); + } + + Con_Printf("%s\n", tx->name); + } + } + } +} + +void GLMod_BlockTextureColour_f (void) +{ + char texname[64]; + model_t *mod; + texture_t *tx; + char *match = Cmd_Argv(1); + + int i, m; + unsigned int colour[8*8]; + unsigned int rgba; + ((char *)&rgba)[0] = atoi(Cmd_Argv(2)); + ((char *)&rgba)[1] = atoi(Cmd_Argv(3)); + ((char *)&rgba)[2] = atoi(Cmd_Argv(4)); + ((char *)&rgba)[3] = 255; + + sprintf(texname, "8*8_%i_%i_%i", (int)((char *)&rgba)[0], (int)((char *)&rgba)[1], (int)((char *)&rgba)[2]); + + for (i = 0; i < sizeof(colour)/sizeof(colour[0]); i++) + colour[i] = rgba; + + for (m=0 , mod=mod_known ; mtype == mod_brush && !mod->needload) + { + for (i = 0; i < mod->numtextures; i++) + { + tx = mod->textures[i]; + if (!tx) + continue; //happens on e1m2 + + if (!stricmp(tx->name, match)) + { + tx->gl_texturenum = GL_LoadTexture32(texname, 8, 8, colour, true, false); + } + } + } + } +} + +/* +=============== +Mod_Init +=============== +*/ +void GLMod_Init (void) +{ + mod_numknown = 0; + memset (mod_novis, 0xff, sizeof(mod_novis)); + + Cmd_AddRemCommand("mod_texturelist", GLMod_TextureList_f); + Cmd_AddRemCommand("mod_usetexture", GLMod_BlockTextureColour_f); +} + +void GLMod_Shutdown (void) +{ + mod_numknown = 0; + memset (mod_novis, 0xff, sizeof(mod_novis)); + + Cmd_RemoveCommand("mod_texturelist"); + Cmd_RemoveCommand("mod_usetexture"); + +#ifdef RUNTIMELIGHTING + lightmodel = NULL; +#endif +} + +/* +=============== +Mod_Init + +Caches the data if needed +=============== +*/ +void *GLMod_Extradata (model_t *mod) +{ + void *r; + + r = Cache_Check (&mod->cache); + if (r) + return r; + + GLMod_LoadModel (mod, true); + + if (!mod->cache.data) + Sys_Error ("Mod_Extradata: caching failed"); + return mod->cache.data; +} + +/* +=============== +Mod_PointInLeaf +=============== +*/ +#ifdef Q2BSPS +int CM_PointLeafnum (vec3_t p); +#endif +mleaf_t *GLMod_PointInLeaf (vec3_t p, model_t *model) +{ + mnode_t *node; + float d; + mplane_t *plane; + + if (!model) + { + Sys_Error ("Mod_PointInLeaf: bad model"); + } + if (!model->nodes) + return NULL; +#ifdef Q2BSPS + if (model->fromgame == fg_quake2 || model->fromgame == fg_quake3) + { + return model->leafs + CM_PointLeafnum(p); + } +#endif + if (model->fromgame == fg_doom) + { + return NULL; + } + + node = model->nodes; + while (1) + { + if (node->contents < 0) + return (mleaf_t *)node; + plane = node->plane; + d = DotProduct (p,plane->normal) - plane->dist; + if (d > 0) + node = node->children[0]; + else + node = node->children[1]; + } + + return NULL; // never reached +} + +int GLMod_LeafForPoint (vec3_t p, model_t *model) +{ + mnode_t *node; + float d; + mplane_t *plane; + + if (!model) + { + Sys_Error ("Mod_PointInLeaf: bad model"); + } + if (!model->nodes) + return 0; + + node = model->nodes; + while (1) + { + if (node->contents < 0) + return (mleaf_t *)node - model->leafs; + plane = node->plane; + d = DotProduct (p,plane->normal) - plane->dist; + if (d > 0) + node = node->children[0]; + else + node = node->children[1]; + } + + return 0; // never reached +} + +/* +=================== +Mod_DecompressVis +=================== +*/ +qbyte *GLMod_DecompressVis (qbyte *in, model_t *model, qbyte *decompressed) +{ + int c; + qbyte *out; + int row; + + row = (model->numleafs+7)>>3; + out = decompressed; + +#if 0 + memcpy (out, in, row); +#else + if (!in) + { // no vis info, so make all visible + while (row) + { + *out++ = 0xff; + row--; + } + return decompressed; + } + + do + { + if (*in) + { + *out++ = *in++; + continue; + } + + c = in[1]; + in += 2; + while (c) + { + *out++ = 0; + c--; + } + } while (out - decompressed < row); +#endif + + return decompressed; +} + + + +qbyte *GLMod_LeafPVS (mleaf_t *leaf, model_t *model, qbyte *buffer) +{ + static qbyte decompressed[MAX_MAP_LEAFS/8]; + + if (leaf == model->leafs) + return mod_novis; + + if (!buffer) + buffer = decompressed; + + return GLMod_DecompressVis (leaf->compressed_vis, model, buffer); +} + +qbyte *GLMod_LeafnumPVS (int leafnum, model_t *model, qbyte *buffer) +{ + return GLMod_LeafPVS(model->leafs + leafnum, model, buffer); +} + +/* +=================== +Mod_ClearAll +=================== +*/ +void GLMod_ClearAll (void) +{ + int i; + model_t *mod; + +#ifdef RUNTIMELIGHTING + lightmodel = NULL; +#endif + + for (i=0 , mod=mod_known ; itype != mod_alias + && mod->type != mod_halflife + ) + mod->needload = true; +} + +void GLMod_Think (void) +{ +#ifdef RUNTIMELIGHTING + if (lightmodel) + { + if (relitsurface >= lightmodel->numsurfaces) + { + return; + } + LightFace(relitsurface); + GLMod_UpdateLightmap(relitsurface); + + relitsurface++; + + if (relitsurface >= lightmodel->numsurfaces) + { + char filename[MAX_QPATH]; + Con_Printf("Finished lighting level\n"); + + COM_StripExtension(lightmodel->name, filename); + COM_DefaultExtension(filename, ".lux"); + COM_WriteFile(filename, lightmodel->deluxdata-8, numlightdata*3+8); + + COM_StripExtension(lightmodel->name, filename); + COM_DefaultExtension(filename, ".lit"); + COM_WriteFile(filename, lightmodel->lightdata-8, numlightdata*3+8); + } + } +#endif +} + +/* +================== +Mod_FindName + +================== +*/ +model_t *GLMod_FindName (char *name) +{ + int i; + model_t *mod; + +// if (!name[0]) +// Sys_Error ("Mod_ForName: NULL name"); + +// +// search the currently loaded models +// + for (i=0 , mod=mod_known ; iname, name) ) + break; + + if (i == mod_numknown) + { + if (mod_numknown == MAX_MOD_KNOWN) + Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); + memset(mod, 0, sizeof(model_t)); //clear the old model as the renderers use the same globals + strcpy (mod->name, name); + mod->needload = true; + mod_numknown++; + mod->particleeffect = -1; + mod->nodefaulttrail = false; + } + + return mod; +} + +/* +================== +Mod_TouchModel + +================== +*/ +void GLMod_TouchModel (char *name) +{ + model_t *mod; + + mod = GLMod_FindName (name); + + if (!mod->needload) + { + if (mod->type == mod_alias + || mod->type == mod_halflife + ) + Cache_Check (&mod->cache); + } +} + +/* +================== +Mod_LoadModel + +Loads a model into the cache +================== +*/ +char *COM_FileExtension (char *in); +model_t *GLMod_LoadModel (model_t *mod, qboolean crash) +{ + void *d; + unsigned *buf = NULL; + qbyte stackbuf[1024]; // avoid dirtying the cache heap + + char *ext; + + if (!mod->needload && mod->type != mod_dummy) + { + if (mod->type == mod_alias + || mod->type == mod_halflife + ) + { + d = Cache_Check (&mod->cache); + if (d) + return mod; + } + else + return mod; // not cached at all + } + + loadmodel = mod; + +#ifdef Q2BSPS + if (!*mod->name) + { + Mod_LoadQ2BrushModel (mod, buf); + mod->needload = false; + return mod; + } +#endif + +// +// load the file +// + //look for a replacement +// ext = COM_FileExtension(mod->name); +// if (!Q_strcasecmp(ext, "mdl") || !Q_strcasecmp(ext, "bsp")) + { + char mdlbase[MAX_QPATH]; + COM_StripExtension(mod->name, mdlbase); + + if (!buf) + buf = (unsigned *)COM_LoadStackFile (va("%s.md3", mdlbase), stackbuf, sizeof(stackbuf)); + if (!buf) + buf = (unsigned *)COM_LoadStackFile (va("%s.md2", mdlbase), stackbuf, sizeof(stackbuf)); + } + if (!buf) + { + buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); + if (!buf) + { + ext = COM_FileExtension(mod->name); +#ifdef DOOMWADS + if (!stricmp(ext, "dsp")) + { + mod->needload = false; + GLMod_LoadDoomSprite(mod); + return mod; + } +#endif + + if (crash) + Host_EndGame ("Mod_NumForName: %s not found", mod->name); + + mod->type = mod_dummy; + mod->mins[0] = -16; + mod->mins[1] = -16; + mod->mins[2] = -16; + mod->maxs[0] = 16; + mod->maxs[1] = 16; + mod->maxs[2] = 16; + mod->needload = true; + return mod; + return NULL; + } + } + +// +// allocate a new model +// + COM_FileBase (mod->name, loadname); + Validation_IncludeFile(mod->name, (char *)buf, com_filesize); + +// +// fill it in +// + +// call the apropriate loader + mod->needload = false; + + switch (LittleLong(*(unsigned *)buf)) + { + case IDPOLYHEADER: + GL_LoadQ1Model(mod, buf); + break; + +#ifdef MD2MODELS + case MD2IDALIASHEADER: + GL_LoadQ2Model(mod, buf); + break; +#endif + +#ifdef MD3MODELS + case MD3_IDENT: + GL_LoadQ3Model (mod, buf); + break; +#endif + +#ifdef SP2MODELS + case IDSPRITE2HEADER: + GLMod_LoadSprite2Model (mod, buf); + break; +#endif + + case IDSPRITEHEADER: + GLMod_LoadSpriteModel (mod, buf); + break; +#ifdef Q2BSPS + case IDBSPHEADER: //looks like id switched to have proper ids + Mod_LoadQ2BrushModel (mod, buf); + break; +#endif +#ifdef HALFLIFEMODELS + case (('T'<<24)+('S'<<16)+('D'<<8)+'I'): + Mod_LoadHLModel (mod, buf); + break; +#endif +#ifdef TERRAINMAPS + case (('R'<<24)+('R'<<16)+('E'<<8)+'T'): + Mod_LoadTerrain(mod, buf); + break; +#endif +#ifdef DOOMWADS + case (('D'<<24)+('A'<<16)+('W'<<8)+'I'): + case (('D'<<24)+('A'<<16)+('W'<<8)+'P'): + Mod_LoadDoomLevel (mod); + break; +#endif + + default: + GLMod_LoadBrushModel (mod, buf); + break; + } + + R_DefaultTrail(mod); + + return mod; +} + +/* +================== +Mod_ForName + +Loads in a model for the given name +================== +*/ +model_t *GLMod_ForName (char *name, qboolean crash) +{ + model_t *mod; + + mod = GLMod_FindName (name); + + return GLMod_LoadModel (mod, crash); +} + + +/* +=============================================================================== + + BRUSHMODEL LOADING + +=============================================================================== +*/ + +#ifdef SWQUAKE +extern qbyte *mod_base; +#else +qbyte *mod_base; +#endif + +/* +================= +Mod_LoadTextures +================= +*/ +void GLMod_LoadTextures (lump_t *l) +{ + extern int gl_bumpmappingpossible; + int i, j, pixels, num, max, altmax; + miptex_t *mt; + texture_t *tx, *tx2; + texture_t *anims[10]; + texture_t *altanims[10]; + char altname[256]; + dmiptexlump_t *m; + qboolean alphaed; + qbyte *base; + + if (!l->filelen) + { + loadmodel->textures = NULL; + return; + } + m = (dmiptexlump_t *)(mod_base + l->fileofs); + + m->nummiptex = LittleLong (m->nummiptex); + + loadmodel->numtextures = m->nummiptex; + loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); + + for (i=0 ; inummiptex ; i++) + { + m->dataofs[i] = LittleLong(m->dataofs[i]); + if (m->dataofs[i] == -1) //e1m2, this happens + continue; + mt = (miptex_t *)((qbyte *)m + m->dataofs[i]); + mt->width = LittleLong (mt->width); + mt->height = LittleLong (mt->height); + for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); + + if ( (mt->width & 15) || (mt->height & 15) ) + Sys_Error ("Texture %s is not 16 aligned", mt->name); + pixels = mt->width*mt->height/64*85; + tx = Hunk_AllocName (sizeof(texture_t)/* +pixels*/, loadname ); + loadmodel->textures[i] = tx; + + memcpy (tx->name, mt->name, sizeof(tx->name)); + tx->width = mt->width; + tx->height = mt->height; + + tx->parttype = ParticleTypeForName(va("tex_%s", tx->name)); + + if (!mt->offsets[0]) //this is a hl external style texture, load it a little later (from a wad) + { +// tx->gl_texturenum = Mod_LoadReplacementTexture("light1_4", true, false); + continue; + } +// for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); + // the pixels immediately follow the structures +// memcpy ( tx+1, mt+1, pixels); //have to be saved for dynamic screen changing (done by reloading entire vid/draw subsystem and all textures) + + + if (!Q_strncmp(mt->name,"sky",3)) + { + tx->offsets[0] = (char *)mt + mt->offsets[0] - (char *)tx; + R_InitSky (tx); + } + else +#ifdef PEXT_BULLETENS + if (!R_AddBulleten(tx)) +#endif + { + base = (qbyte *)(mt+1); + if (loadmodel->fromgame == fg_halflife) + {//external textures have already been filtered. + base = W_ConvertWAD3Texture(mt, &mt->width, &mt->height, &alphaed); //convert texture to 32 bit. + tx->alphaed = alphaed; + texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR; + if (!(tx->gl_texturenum = Mod_LoadReplacementTexture(mt->name, true, alphaed))) + tx->gl_texturenum = GL_LoadTexture32 (mt->name, tx->width, tx->height, (unsigned int *)base, true, alphaed); + + *tx->name = *mt->name; + texture_mode = GL_LINEAR; + } + else + { + texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR; + if (!(tx->gl_texturenum = Mod_LoadReplacementTexture(mt->name, true, false))) + tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, base, true, false); + texture_mode = GL_LINEAR; + + if (r_fb_bmodels.value) + { + if (gl_load24bit.value && r_fb_bmodels.value) + { + _snprintf(altname, sizeof(altname)-1, "%s_luma", mt->name); + tx->gl_texturenumfb = Mod_LoadReplacementTexture(altname, true, false); + } + if (!tx->gl_texturenumfb) //generate one (if possible). + tx->gl_texturenumfb = GL_LoadTextureFB(altname, tx->width, tx->height, base, true, true); + } + } + } + + tx->gl_texturenumbumpmap = 0; + if (gl_bumpmappingpossible && cls.allow_bump) + { + _snprintf(altname, sizeof(altname)-1, "%s_bump", mt->name); + + if (gl_load24bit.value) + tx->gl_texturenumbumpmap = Mod_LoadBumpmapTexture(altname); + + if (!(tx->gl_texturenumbumpmap) && loadmodel->fromgame != fg_halflife) + { + base = (qbyte *)(mt+1); //convert to greyscale. + for (j = 0; j < pixels; j++) + base[j] = (host_basepal[base[j]*3] + host_basepal[base[j]*3+1] + host_basepal[base[j]*3+2]) / 3; + + tx->gl_texturenumbumpmap = GL_LoadTexture8Bump(altname, tx->width, tx->height, base, true); //normalise it and then bump it. + } + } + } +// +// sequence the animations +// + for (i=0 ; inummiptex ; i++) + { + tx = loadmodel->textures[i]; + if (!tx || tx->name[0] != '+') + continue; + if (tx->anim_next) + continue; // allready sequenced + + // find the number of frames in the animation + memset (anims, 0, sizeof(anims)); + memset (altanims, 0, sizeof(altanims)); + + max = tx->name[1]; + altmax = 0; + if (max >= 'a' && max <= 'z') + max -= 'a' - 'A'; + if (max >= '0' && max <= '9') + { + max -= '0'; + altmax = 0; + anims[max] = tx; + max++; + } + else if (max >= 'A' && max <= 'J') + { + altmax = max - 'A'; + max = 0; + altanims[altmax] = tx; + altmax++; + } + else + Sys_Error ("Bad animating texture %s", tx->name); + + for (j=i+1 ; jnummiptex ; j++) + { + tx2 = loadmodel->textures[j]; + if (!tx2 || tx2->name[0] != '+') + continue; + if (strcmp (tx2->name+2, tx->name+2)) + continue; + + num = tx2->name[1]; + if (num >= 'a' && num <= 'z') + num -= 'a' - 'A'; + if (num >= '0' && num <= '9') + { + num -= '0'; + anims[num] = tx2; + if (num+1 > max) + max = num + 1; + } + else if (num >= 'A' && num <= 'J') + { + num = num - 'A'; + altanims[num] = tx2; + if (num+1 > altmax) + altmax = num+1; + } + else + Sys_Error ("Bad animating texture %s", tx->name); + } + +#define ANIM_CYCLE 2 + // link them all together + for (j=0 ; jname); + tx2->anim_total = max * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = anims[ (j+1)%max ]; + if (altmax) + tx2->alternate_anims = altanims[0]; + } + for (j=0 ; jname); + tx2->anim_total = altmax * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = altanims[ (j+1)%altmax ]; + if (max) + tx2->alternate_anims = anims[0]; + } + } +} + +int GL_LoadTexture8Bump (char *identifier, int width, int height, unsigned char *data, qboolean mipmap); +void GLMod_NowLoadExternal(void) +{ + extern int gl_bumpmappingpossible; + int i, width, height; + qboolean alphaed; + texture_t *tx; + for (i=0 ; inumtextures ; i++) + { + tx = loadmodel->textures[i]; + if (!tx) //e1m2, this happens + continue; + + if (!tx->gl_texturenum) + { +#ifdef PEXT_BULLETENS + if (!R_AddBulleten(tx)) +#endif + { + qbyte * data; + texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR; + + data = W_GetTexture(tx->name, &width, &height, &alphaed); + if (data) + { //data is from temp hunk, so no need to free. + tx->alphaed = alphaed; + } + + if (!(tx->gl_texturenum = Mod_LoadHiResTexture(tx->name, true, false))) + tx->gl_texturenum = Mod_LoadReplacementTexture("light1_4", true, false); + texture_mode = GL_LINEAR; + } + } + if (!tx->gl_texturenumbumpmap && *tx->name != '{' && gl_bumpmappingpossible && cls.allow_bump) + { + tx->gl_texturenumbumpmap = Mod_LoadBumpmapTexture(va("%s_bump", tx->name)); + if (!tx->gl_texturenumbumpmap) + { + qbyte *data; + qbyte *heightmap; + int width, height; + int j; + + data = W_GetTexture(tx->name, &width, &height, &alphaed); + if (!data) + continue; + + heightmap = Hunk_TempAllocMore(width*height); + for (j = 0; j < width*height; j++) + { + *heightmap++ = (data[j*4+0] + data[j*4+1] + data[j*4+2])/3; + } + + tx->gl_texturenumbumpmap = GL_LoadTexture8Bump (va("%s_bump", tx->name), width, height, heightmap-j, true); + } + } + } +} + +void GLMod_ReloadTextures(void) +{ + texture_t *tx; + model_t *mod; + int i; + int t; + Cache_Flush(); + for (i = 0, mod=mod_known; i < mod_numknown; i++, mod++) + { + if (mod->needload) + continue; + if (mod->type == mod_brush) //make this reload bsp? + { + if (mod->textures) + for (t = 0; t < mod->numtextures; t++) + { + tx = mod->textures[t]; + if (!Q_strncmp(tx->name,"sky",3)) + R_InitSky (tx); + else + { + texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR; + if (!(tx->gl_texturenum = Mod_LoadReplacementTexture(tx->name, true, false))) + tx->gl_texturenum = GL_LoadTexture (tx->name, tx->width, tx->height, (qbyte *)(tx+1), true, false); + texture_mode = GL_LINEAR; + } + } + } + else if (mod->type == mod_sprite) + { + loadmodel = mod; + GLMod_LoadSpriteModel(mod, COM_LoadTempFile(mod->name)); + } + } +} + + +qbyte lmgamma[256]; +void BuildLightMapGammaTable (float g, float c) +{ + int i, inf; + +// g = bound (0.1, g, 3); +// c = bound (1, c, 3); + + if (g == 1 && c == 1) + { + for (i = 0; i < 256; i++) + lmgamma[i] = i; + return; + } + + for (i = 0; i < 256; i++) + { + inf = 255 * pow ((i + 0.5) / 255.5 * c, g) + 0.5; + if (inf < 0) + inf = 0; + else if (inf > 255) + inf = 255; + lmgamma[i] = inf; + } +} + + +/* +================= +Mod_LoadLighting +================= +*/ +void GLMod_LoadLighting (lump_t *l) +{ + qbyte *luxdata = NULL; + loadmodel->rgblighting = false; + + //lit file light intensity is made to match the world's light intensity. + if (cls.allow_lightmapgamma) + BuildLightMapGammaTable(0.6, 2); + else + BuildLightMapGammaTable(1, 1); + + loadmodel->deluxdata = NULL; + if (r_loadlits.value && gl_bumpmappingpossible) //fixme: adjust the light intensities. + { //the map util has a '-scalecos X' parameter. use 0 if you're going to use only just lux. without lux scalecos 0 is hideous. + char luxname[MAX_QPATH]; + if (!luxdata) + { + strcpy(luxname, loadmodel->name); + COM_StripExtension(loadmodel->name, luxname); + COM_DefaultExtension(luxname, ".lux"); + luxdata = COM_LoadHunkFile(luxname); + } + if (!luxdata) + { + strcpy(luxname, "luxs/"); + COM_StripExtension(COM_SkipPath(loadmodel->name), luxname+5); + strcat(luxname, ".lux"); + + luxdata = COM_LoadHunkFile(luxname); + } + COM_StripExtension(COM_SkipPath(loadmodel->name), luxname+5); + strcat(luxname, ".lux"); + if (luxdata) + { + if (l->filelen && l->filelen != (com_filesize-8)/3) + { + Con_Printf("deluxmap \"%s\" doesn't match level. Ignored.\n", luxname); + luxdata=NULL; + } + else if (luxdata[0] == 'Q' && luxdata[1] == 'L' && luxdata[2] == 'I' && luxdata[3] == 'T') + { + if (LittleLong(*(int *)&luxdata[4]) == 1) + { + luxdata+=8; + loadmodel->deluxdata = luxdata; + } + else + { + Con_Printf("\"%s\" isn't a version 1 deluxmap\n", luxname); + luxdata=NULL; + } + } + else + { + Con_Printf("lit \"%s\" isn't a deluxmap\n", luxname); + luxdata=NULL; + } + } + } + + if (r_loadlits.value) + { + qbyte *litdata = NULL; + char litname[MAX_QPATH]; + if (!litdata) + { + strcpy(litname, loadmodel->name); + COM_StripExtension(loadmodel->name, litname); + COM_DefaultExtension(litname, ".lit"); + litdata = COM_LoadHunkFile(litname); + } + if (!litdata) + { + strcpy(litname, "lits/"); + COM_StripExtension(COM_SkipPath(loadmodel->name), litname+5); + strcat(litname, ".lit"); + + litdata = COM_LoadHunkFile(litname); + } + COM_StripExtension(COM_SkipPath(loadmodel->name), litname+5); + strcat(litname, ".lit"); + if (litdata && (litdata[0] == 'Q' && litdata[1] == 'L' && litdata[2] == 'I' && litdata[3] == 'T')) + { + if (LittleLong(*(int *)&litdata[4]) == 1 && l->filelen && l->filelen != (com_filesize-8)/3) + Con_Printf("lit \"%s\" doesn't match level. Ignored.\n", litname); + else if (LittleLong(*(int *)&litdata[4]) != 1) + Con_Printf("lit \"%s\" isn't version 1.\n", litname); + else + { + float prop; + int i; + qbyte *normal; + + //load it + loadmodel->lightdata = litdata+8; + loadmodel->rgblighting = true; + + + //now some cheat protection. + if (!l->filelen) + { //would otherwise be full bright so no need to check. + return; + } + + normal = mod_base + l->fileofs; + litdata = loadmodel->lightdata; + + for (i = 0; i < l->filelen; i++) //force it to the same intensity. (or less, depending on how you see it...) + { +#define m(a, b, c) (a>(b>c?b:c)?a:(b>c?b:c)) + prop = (float)m(litdata[0], litdata[1], litdata[2]); + + if (!prop) + { + litdata[0] = lmgamma[*normal]; + litdata[1] = lmgamma[*normal]; + litdata[2] = lmgamma[*normal]; + } + else + { + prop = lmgamma[*normal] / prop; + litdata[0] *= prop; + litdata[1] *= prop; + litdata[2] *= prop; + } + + normal++; + litdata+=3; + } + //end anti-cheat + + return; + } + } + else if (litdata) + Con_Printf("lit \"%s\" isn't a lit\n", litname); +// else + //failed to find + } + if (loadmodel->fromgame == fg_halflife || loadmodel->fromgame == fg_quake2 || loadmodel->fromgame == fg_quake3) + loadmodel->rgblighting = true; + + if (!l->filelen) + { + loadmodel->lightdata = NULL; + return; + } +#ifdef RUNTIMELIGHTING + if (!loadmodel->rgblighting && r_loadlits.value == 2 && !lightmodel) + { + qbyte *litdata = NULL; + int i; + qbyte *normal; + loadmodel->rgblighting = true; + loadmodel->lightdata = Hunk_AllocName ( l->filelen*3+8, loadname); + strcpy(loadmodel->lightdata, "QLIT"); + ((int*)loadmodel->lightdata)[1] = LittleLong(1); + loadmodel->lightdata += 8; + + litdata = loadmodel->lightdata; + normal = mod_base + l->fileofs; + for (i = 0; i < l->filelen; i++) + { + *litdata++ = lmgamma[*normal]; + *litdata++ = lmgamma[*normal]; + *litdata++ = lmgamma[*normal]; + normal++; + } + + if (gl_bumpmappingpossible) + { + loadmodel->deluxdata = Hunk_AllocName ( l->filelen*3+8, loadname); + strcpy(loadmodel->deluxdata, "QLIT"); + ((int*)loadmodel->deluxdata)[1] = LittleLong(1); + loadmodel->deluxdata += 8; + litdata = loadmodel->deluxdata; + { + for (i = 0; i < l->filelen*3; i++) + *litdata++ = 0.5f*255; + } + } + + numlightdata = l->filelen; + lightmodel = loadmodel; + relitsurface = 0; + return; + } +#endif + + loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname); + + { + int i; + qbyte *in, *out; + + in = mod_base + l->fileofs; + out = loadmodel->lightdata; + for (i = 0; i < l->filelen; i++) + { + *out++ = lmgamma[*in++]; + } + } + //memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); +} + +/* +================= +Mod_LoadVisibility +================= +*/ +void GLMod_LoadVisibility (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->visdata = NULL; + return; + } + loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadEntities +================= +*/ +void GLMod_LoadEntities (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->entities = NULL; + return; + } + loadmodel->entities = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadVertexes +================= +*/ +void GLMod_LoadVertexes (lump_t *l) +{ + dvertex_t *in; + mvertex_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->vertexes = out; + loadmodel->numvertexes = count; + + for ( i=0 ; iposition[0] = LittleFloat (in->point[0]); + out->position[1] = LittleFloat (in->point[1]); + out->position[2] = LittleFloat (in->point[2]); + } +} + +/* +================= +Mod_LoadSubmodels +================= +*/ +static qboolean hexen2map; +void GLMod_LoadSubmodels (lump_t *l) +{ + dq1model_t *inq; + dh2model_t *inh; + mmodel_t *out; + int i, j, count; + + //this is crazy! + + inq = (void *)(mod_base + l->fileofs); + inh = (void *)(mod_base + l->fileofs); + if (!inq->numfaces) + { + hexen2map = true; + if (l->filelen % sizeof(*inh)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*inh); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->submodels = out; + loadmodel->numsubmodels = count; + + for ( i=0 ; imins[j] = LittleFloat (inh->mins[j]) - 1; + out->maxs[j] = LittleFloat (inh->maxs[j]) + 1; + out->origin[j] = LittleFloat (inh->origin[j]); + } + for (j=0 ; jheadnode[j] = LittleLong (inh->headnode[j]); + } + for ( ; jheadnode[j] = 0; + for (j=0 ; jhullavailable[j] = true; + for ( ; jhullavailable[j] = false; + out->visleafs = LittleLong (inh->visleafs); + out->firstface = LittleLong (inh->firstface); + out->numfaces = LittleLong (inh->numfaces); + } + + } + else + { + hexen2map = false; + if (l->filelen % sizeof(*inq)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*inq); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->submodels = out; + loadmodel->numsubmodels = count; + + for ( i=0 ; imins[j] = LittleFloat (inq->mins[j]) - 1; + out->maxs[j] = LittleFloat (inq->maxs[j]) + 1; + out->origin[j] = LittleFloat (inq->origin[j]); + } + for (j=0 ; jheadnode[j] = LittleLong (inq->headnode[j]); + } + for ( ; jheadnode[j] = 0; + for (j=0 ; j<3 ; j++) + out->hullavailable[j] = true; + for ( ; jhullavailable[j] = false; + out->visleafs = LittleLong (inq->visleafs); + out->firstface = LittleLong (inq->firstface); + out->numfaces = LittleLong (inq->numfaces); + } + } +} + +/* +================= +Mod_LoadEdges +================= +*/ +void GLMod_LoadEdges (lump_t *l) +{ + dedge_t *in; + medge_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); + + loadmodel->edges = out; + loadmodel->numedges = count; + + for ( i=0 ; iv[0] = (unsigned short)LittleShort(in->v[0]); + out->v[1] = (unsigned short)LittleShort(in->v[1]); + } +} + +/* +================= +Mod_LoadTexinfo +================= +*/ +void GLMod_LoadTexinfo (lump_t *l) +{ + texinfo_t *in; + mtexinfo_t *out; + int i, j, count; + int miptex; + float len1, len2; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->texinfo = out; + loadmodel->numtexinfo = count; + + for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); + len1 = Length (out->vecs[0]); + len2 = Length (out->vecs[1]); + len1 = (len1 + len2)/2; + if (len1 < 0.32) + out->mipadjust = 4; + else if (len1 < 0.49) + out->mipadjust = 3; + else if (len1 < 0.99) + out->mipadjust = 2; + else + out->mipadjust = 1; +#if 0 + if (len1 + len2 < 0.001) + out->mipadjust = 1; // don't crash + else + out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 ); +#endif + + miptex = LittleLong (in->miptex); + out->flags = LittleLong (in->flags); + + if (!loadmodel->textures) + { + out->texture = r_notexture_mip; // checkerboard texture + out->flags = 0; + } + else + { + if (miptex >= loadmodel->numtextures) + Sys_Error ("miptex >= loadmodel->numtextures"); + out->texture = loadmodel->textures[miptex]; + if (!out->texture) + { + out->texture = r_notexture_mip; // texture not found + out->flags = 0; + } + } + } +} + +/* +================ +CalcSurfaceExtents + +Fills in s->texturemins[] and s->extents[] +================ +*/ + +void CalcSurfaceExtents (msurface_t *s); +/* +{ + float mins[2], maxs[2], val; + int i,j, e; + mvertex_t *v; + mtexinfo_t *tex; + int bmins[2], bmaxs[2]; + + mins[0] = mins[1] = 999999; + maxs[0] = maxs[1] = -99999; + + tex = s->texinfo; + + for (i=0 ; inumedges ; i++) + { + e = loadmodel->surfedges[s->firstedge+i]; + if (e >= 0) + v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; + else + v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; + + for (j=0 ; j<2 ; j++) + { + val = v->position[0] * tex->vecs[j][0] + + v->position[1] * tex->vecs[j][1] + + v->position[2] * tex->vecs[j][2] + + tex->vecs[j][3]; + if (val < mins[j]) + mins[j] = val; + if (val > maxs[j]) + maxs[j] = val; + } + } + + for (i=0 ; i<2 ; i++) + { + bmins[i] = floor(mins[i]/16); + bmaxs[i] = ceil(maxs[i]/16); + + s->texturemins[i] = bmins[i] * 16; + s->extents[i] = (bmaxs[i] - bmins[i]) * 16; + +// if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 ) //q2 uses 512. +// Sys_Error ("Bad surface extents"); + } +} +*/ + +/* +================= +Mod_LoadFaces +================= +*/ +void GLMod_LoadFaces (lump_t *l) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + int planenum, side; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); + out->numedges = LittleShort(in->numedges); + out->flags = 0; + + planenum = LittleShort(in->planenum); + side = LittleShort(in->side); + if (side) + out->flags |= SURF_PLANEBACK; + + out->plane = loadmodel->planes + planenum; + + out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo); + + CalcSurfaceExtents (out); + + // lighting info + + for (i=0 ; istyles[i] = in->styles[i]; + i = LittleLong(in->lightofs); + if (i == -1) + out->samples = NULL; + else if (loadmodel->rgblighting && loadmodel->fromgame != fg_halflife) + out->samples = loadmodel->lightdata + i*3; + else + out->samples = loadmodel->lightdata + i; + + if (!out->texinfo->texture) + continue; + + + // set the drawing flags flag + if (!Q_strncmp(out->texinfo->texture->name,"sky",3)) // sky + { + out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); +#ifndef QUAKE2 + GL_SubdivideSurface (out, gl_subdivide_size.value); // cut up polygon for warps +#endif + continue; + } + + if (!Q_strncmp(out->texinfo->texture->name,"*",1)) // turbulent + { + out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); + for (i=0 ; i<2 ; i++) + { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + GL_SubdivideSurface (out, (gl_subdivide_water.value?gl_subdivide_water.value:gl_subdivide_size.value)); // cut up polygon for warps + continue; + } + + if (!Q_strncmp(out->texinfo->texture->name,"{",1)) // alpha + { + out->flags |= (SURF_DRAWALPHA); + continue; + } + if (!Q_strncmp(out->texinfo->texture->name,"glass",5)) // alpha + { + out->flags |= (SURF_DRAWALPHA); + continue; + } + if (out->flags & SURF_DRAWALPHA) + out->flags &= ~SURF_DRAWALPHA; + } +} + + +/* +================= +Mod_SetParent +================= +*/ +void GLMod_SetParent (mnode_t *node, mnode_t *parent) +{ + if (!node) + return; + node->parent = parent; + if (node->contents < 0) + return; + GLMod_SetParent (node->children[0], node); + GLMod_SetParent (node->children[1], node); +} + +/* +================= +Mod_LoadNodes +================= +*/ +void GLMod_LoadNodes (lump_t *l) +{ + int i, j, count, p; + dnode_t *in; + mnode_t *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->nodes = out; + loadmodel->numnodes = count; + + for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + p = LittleLong(in->planenum); + out->plane = loadmodel->planes + p; + + out->firstsurface = LittleShort (in->firstface); + out->numsurfaces = LittleShort (in->numfaces); + + for (j=0 ; j<2 ; j++) + { + p = LittleShort (in->children[j]); + if (p >= 0) + out->children[j] = loadmodel->nodes + p; + else + out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); + } + } + + GLMod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs +} + +/* +================= +Mod_LoadLeafs +================= +*/ +void GLMod_LoadLeafs (lump_t *l) +{ + dleaf_t *in; + mleaf_t *out; + int i, j, count, p; +// char s[80]; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->leafs = out; + loadmodel->numleafs = count; + + for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + p = LittleLong(in->contents); + out->contents = p; + + out->firstmarksurface = loadmodel->marksurfaces + + LittleShort(in->firstmarksurface); + out->nummarksurfaces = LittleShort(in->nummarksurfaces); + + p = LittleLong(in->visofs); + if (p == -1) + out->compressed_vis = NULL; + else + out->compressed_vis = loadmodel->visdata + p; + out->efrags = NULL; + + for (j=0 ; j<4 ; j++) + out->ambient_sound_level[j] = in->ambient_level[j]; + +#ifndef CLIENTONLY + if (!isDedicated) +#endif + { + // gl underwater warp + if (out->contents != Q1CONTENTS_EMPTY) + { + for (j=0 ; jnummarksurfaces ; j++) + out->firstmarksurface[j]->flags |= SURF_UNDERWATER; + } + if (isnotmap) + { + for (j=0 ; jnummarksurfaces ; j++) + out->firstmarksurface[j]->flags |= SURF_DONTWARP; + } + } + } +} + + + + +//these are used to boost other info sizes +int numsuplementryplanes; +int numsuplementryclipnodes; +void *suplementryclipnodes; +void *suplementryplanes; +void *crouchhullfile; + +qbyte *COM_LoadMallocFile (char *path); +void GLMod_LoadCrouchHull(void) +{ + int i, h; + int numsm; + char crouchhullname[MAX_QPATH]; + int *data; + int hulls; + +// dclipnode_t *cn; + + memset(loadmodel->hulls, 0, sizeof(loadmodel->hulls)); //ensure all the sizes are 0 (this is how we check for the existance of a hull + + numsuplementryplanes = numsuplementryclipnodes = 0; + + //find a name for a ccn and try to load it. + strcpy(crouchhullname, loadmodel->name); + COM_StripExtension(loadmodel->name, crouchhullname); + COM_DefaultExtension(crouchhullname, ".crh"); //crouch hull + + crouchhullfile = COM_LoadMallocFile(crouchhullname); //or otherwise temporary storage. load on hunk if you want, but that would be a waste. + if (!crouchhullfile) + return; + + data = crouchhullfile; + + if (LittleLong(*data++) != ('S') + ('C'<<8) + ('N'<<16) + ('P'<<24)) //make sure it's the right version + return; + + if (LittleLong(*data) == 2) + { + data++; + hulls = LittleLong(*data++); + } + else + return; + + if (hulls > MAX_MAP_HULLSM - MAX_MAP_HULLSDQ1) + { + return; + } + + numsm = LittleLong(*data++); + if (numsm != loadmodel->numsubmodels) //not compatable + return; + + numsuplementryplanes = LittleLong(*data++); + numsuplementryclipnodes = LittleLong(*data++); + + for (h = 0; h < hulls; h++) + { + for (i = 0; i < 3; i++) + loadmodel->hulls[3+h].clip_mins[i] = LittleLong(*data++); + for (i = 0; i < 3; i++) + loadmodel->hulls[3+h].clip_maxs[i] = LittleLong(*data++); + + for (i = 0; i < numsm; i++) //load headnode references + { + loadmodel->submodels[i].headnode[3+h] = LittleLong(*data)+1; + data++; + } + } + + suplementryplanes = data; + suplementryclipnodes = (qbyte*)data + sizeof(dplane_t)*numsuplementryplanes; +} + +/* +================= +Mod_LoadClipnodes +================= +*/ +void GLMod_LoadClipnodes (lump_t *l) +{ + dclipnode_t *in, *out; + int i, count; + hull_t *hull; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( (count+numsuplementryclipnodes)*sizeof(*out), loadname);//space for both + + loadmodel->clipnodes = out; + loadmodel->numclipnodes = count+numsuplementryclipnodes; + + + if (hexen2map) + { //hexen2. + hexen2map=false; + hull = &loadmodel->hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 32; + hull->available = true; + + hull = &loadmodel->hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -24; + hull->clip_mins[1] = -24; + hull->clip_mins[2] = -20; + hull->clip_maxs[0] = 24; + hull->clip_maxs[1] = 24; + hull->clip_maxs[2] = 20; + hull->available = true; + + hull = &loadmodel->hulls[3]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -12; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 16; + hull->available = true; + + hull = &loadmodel->hulls[4]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -40; + hull->clip_mins[1] = -40; + hull->clip_mins[2] = -42; + hull->clip_maxs[0] = 40; + hull->clip_maxs[1] = 40; + hull->clip_maxs[2] = 42; + hull->available = true; + + hull = &loadmodel->hulls[5]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -48; + hull->clip_mins[1] = -48; + hull->clip_mins[2] = -50; + hull->clip_maxs[0] = 48; + hull->clip_maxs[1] = 48; + hull->clip_maxs[2] = 50; + hull->available = true; + } + else if (loadmodel->fromgame == fg_halflife) + { + hull = &loadmodel->hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -36; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 36; + hull->available = true; + + hull = &loadmodel->hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -32; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 32; + hull->available = true; + + hull = &loadmodel->hulls[3]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -18; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 18; + hull->available = true; + } + else + { + hull = &loadmodel->hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 32; + hull->available = true; + + hull = &loadmodel->hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 64; + hull->available = true; + + hull = &loadmodel->hulls[3]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -6; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 30; + hull->available = false; + } + + for (i=0 ; iplanenum = LittleLong(in->planenum); + out->children[0] = LittleShort(in->children[0]); + out->children[1] = LittleShort(in->children[1]); + } + + if (numsuplementryclipnodes) //now load the crouch ones. + { + for (i = 3; i < MAX_MAP_HULLSM; i++) + { + hull = &loadmodel->hulls[i]; + hull->planes = suplementryplanes; + hull->clipnodes = out-1; + hull->firstclipnode = 0; + hull->lastclipnode = numsuplementryclipnodes; + hull->available = true; + } + + in = suplementryclipnodes; + + for (i=0 ; iplanenum = LittleLong(in->planenum); + out->children[0] = LittleShort(in->children[0]); + out->children[0] += out->children[0]>=0?1:0; + out->children[1] = LittleShort(in->children[1]); + out->children[1] += out->children[1]>=0?1:0; + } + } +} + +/* +================= +Mod_MakeHull0 + +Deplicate the drawing hull structure as a clipping hull +================= +*/ +void GLMod_MakeHull0 (void) +{ + mnode_t *in, *child; + dclipnode_t *out; + int i, j, count; + hull_t *hull; + + hull = &loadmodel->hulls[0]; + + in = loadmodel->nodes; + count = loadmodel->numnodes; + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + + for (i=0 ; iplanenum = in->plane - loadmodel->planes; + for (j=0 ; j<2 ; j++) + { + child = in->children[j]; + if (child->contents < 0) + out->children[j] = child->contents; + else + out->children[j] = child - loadmodel->nodes; + } + } +} + +/* +================= +Mod_LoadMarksurfaces +================= +*/ +void GLMod_LoadMarksurfaces (lump_t *l) +{ + int i, j, count; + short *in; + msurface_t **out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->marksurfaces = out; + loadmodel->nummarksurfaces = count; + + for ( i=0 ; i= loadmodel->numsurfaces) + Sys_Error ("Mod_ParseMarksurfaces: bad surface number"); + out[i] = loadmodel->surfaces + j; + } +} + +/* +================= +Mod_LoadSurfedges +================= +*/ +void GLMod_LoadSurfedges (lump_t *l) +{ + int i, count; + int *in, *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfedges = out; + loadmodel->numsurfedges = count; + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( (count+numsuplementryplanes)*2*sizeof(*out), loadname); + + loadmodel->planes = out; + loadmodel->numplanes = count+numsuplementryplanes; + + for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); + if (out->normal[j] < 0) + bits |= 1<dist = LittleFloat (in->dist); + out->type = LittleLong (in->type); + out->signbits = bits; + } + + if (numsuplementryplanes) + { + in = suplementryplanes; + suplementryplanes = out; + for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); + if (out->normal[j] < 0) + bits |= 1<dist = LittleFloat (in->dist); + out->type = LittleLong (in->type); + out->signbits = bits; + } + } +} + +/* +================= +RadiusFromBounds +================= +*/ + +float RadiusFromBounds (vec3_t mins, vec3_t maxs); +/* +{ + int i; + vec3_t corner; + + for (i=0 ; i<3 ; i++) + { + corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); + } + + return Length (corner); +} +*/ + +//combination of R_AddDynamicLights and R_MarkLights +void GLR_StainSurf (msurface_t *surf, float *parms); +static void Q1BSP_StainNode (mnode_t *node, float *parms) +{ + mplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + + if (node->contents < 0) + return; + + splitplane = node->plane; + dist = DotProduct ((parms+1), splitplane->normal) - splitplane->dist; + + if (dist > (*parms)) + { + Q1BSP_StainNode (node->children[0], parms); + return; + } + if (dist < (-*parms)) + { + Q1BSP_StainNode (node->children[1], parms); + return; + } + +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags&~(SURF_DONTWARP|SURF_PLANEBACK)) + continue; + GLR_StainSurf(surf, parms); + } + + Q1BSP_StainNode (node->children[0], parms); + Q1BSP_StainNode (node->children[1], parms); +} + + +void Q1BSP_MarkLights (dlight_t *light, int bit, mnode_t *node); +void Q1BSP_FatPVS (vec3_t org, qboolean add); +qboolean Q1BSP_EdictInFatPVS(struct edict_s *ent); +void Q1BSP_FindTouchedLeafs(struct edict_s *ent); +void GLQ1BSP_LightPointValues(vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir); + + +/* +================= +Mod_LoadBrushModel +================= +*/ +void GLMod_LoadBrushModel (model_t *mod, void *buffer) +{ + int i, j; + dheader_t *header; + mmodel_t *bm; + model_t *lm=mod; + + loadmodel->type = mod_brush; + + header = (dheader_t *)buffer; + + if ((!cl.worldmodel && cls.state>=ca_connected) +#ifndef CLIENTONLY + || (!sv.worldmodel && sv.active) +#endif + ) + isnotmap = false; + else + isnotmap = true; + + i = LittleLong (header->version); + + if (i == BSPVERSION) + loadmodel->fromgame = fg_quake; + else if (i == BSPVERSIONHL) //halflife support + loadmodel->fromgame = fg_halflife; + else + Sys_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION); + +// swap all the lumps + mod_base = (qbyte *)header; + + for (i=0 ; iname, sizeof(dheader_t)); + } + } + + + +// checksum all of the map, except for entities + mod->checksum = 0; + mod->checksum2 = 0; + + for (i = 0; i < HEADER_LUMPS; i++) { + if (i == LUMP_ENTITIES) + continue; + mod->checksum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen); + + if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) + continue; + mod->checksum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen); + } + +// load into heap +#ifndef CLIENTONLY + if (!isDedicated) +#endif + { + GLMod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); + GLMod_LoadEdges (&header->lumps[LUMP_EDGES]); + GLMod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); + GLMod_LoadTextures (&header->lumps[LUMP_TEXTURES]); + GLMod_LoadLighting (&header->lumps[LUMP_LIGHTING]); + } + GLMod_LoadSubmodels (&header->lumps[LUMP_MODELS]); + GLMod_LoadCrouchHull(); + GLMod_LoadPlanes (&header->lumps[LUMP_PLANES]); +#ifndef CLIENTONLY + if (!isDedicated) +#endif + { + GLMod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); + GLMod_LoadFaces (&header->lumps[LUMP_FACES]); + GLMod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]); + } + GLMod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); + GLMod_LoadLeafs (&header->lumps[LUMP_LEAFS]); + GLMod_LoadNodes (&header->lumps[LUMP_NODES]); + GLMod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); + GLMod_LoadEntities (&header->lumps[LUMP_ENTITIES]); + GLMod_MakeHull0 (); + + if (crouchhullfile) + { + BZ_Free(crouchhullfile); + crouchhullfile=NULL; + } + +#ifndef CLIENTONLY + if (sv.state) //if the server is running + { + if (!strcmp(loadmodel->name, va("maps/%s.bsp", sv.name))) + Mod_ParseInfoFromEntityLump(mod_base + header->lumps[LUMP_ENTITIES].fileofs); + } + else +#endif + { + if (!cl.model_precache[1]) //not copied across yet + Mod_ParseInfoFromEntityLump(mod_base + header->lumps[LUMP_ENTITIES].fileofs); + } + + mod->funcs.FatPVS = Q1BSP_FatPVS; + mod->funcs.EdictInFatPVS = Q1BSP_EdictInFatPVS; + mod->funcs.FindTouchedLeafs_Q1 = Q1BSP_FindTouchedLeafs; + mod->funcs.LightPointValues = GLQ1BSP_LightPointValues; + mod->funcs.StainNode = Q1BSP_StainNode; + mod->funcs.MarkLights = Q1BSP_MarkLights; + + mod->funcs.LeafForPoint = GLMod_LeafForPoint; + mod->funcs.LeafPVS = GLMod_LeafnumPVS; + + mod->numframes = 2; // regular and alternate animation + +// +// set up the submodels (FIXME: this is confusing) +// + for (i=0 ; inumsubmodels ; i++) + { + bm = &mod->submodels[i]; + + mod->hulls[0].firstclipnode = bm->headnode[0]; + mod->hulls[0].available = true; + Q1BSP_SetHullFuncs(&mod->hulls[0]); + + for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; + mod->hulls[j].lastclipnode = mod->numclipnodes-1; + + mod->hulls[j].available = bm->hullavailable[j]; + if (mod->hulls[j].firstclipnode > mod->hulls[j].lastclipnode) + mod->hulls[j].available = false; + + Q1BSP_SetHullFuncs(&mod->hulls[j]); + } + + mod->firstmodelsurface = bm->firstface; + mod->nummodelsurfaces = bm->numfaces; + + VectorCopy (bm->maxs, mod->maxs); + VectorCopy (bm->mins, mod->mins); + + mod->radius = RadiusFromBounds (mod->mins, mod->maxs); + + mod->numleafs = bm->visleafs; + + if (i < mod->numsubmodels-1) + { // duplicate the basic information + char name[10]; + + sprintf (name, "*%i", i+1); + loadmodel = Mod_FindName (name); + *loadmodel = *mod; + strcpy (loadmodel->name, name); + mod = loadmodel; + + R_DefaultTrail(mod); + } + } +#ifdef RUNTIMELIGHTING + if (lightmodel == lm) + LightLoadEntities(lightmodel->entities); +#endif +} + +/* +============================================================================== + +ALIAS MODELS + +============================================================================== +*/ + +//aliashdr_t *pheader; + +//mstvert_t stverts[MAXALIASVERTS*2]; +//mtriangle_t triangles[MAXALIASTRIS]; + +// a pose is a single set of vertexes. a frame may be +// an animating sequence of poses +//dtrivertx_t *poseverts[MAXALIASFRAMES]; +//int posenum; + +qbyte *player_8bit_texels/*[320*200]*/; + + +//========================================================= + +/* +================= +Mod_FloodFillSkin + +Fill background pixels so mipmapping doesn't have haloes - Ed +================= +*/ + +typedef struct +{ + short x, y; +} floodfill_t; + +// must be a power of 2 +#define FLOODFILL_FIFO_SIZE 0x1000 +#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1) + +#define FLOODFILL_STEP( off, dx, dy ) \ +{ \ + if (pos[off] == fillcolor) \ + { \ + pos[off] = 255; \ + fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \ + inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \ + } \ + else if (pos[off] != 255) fdc = pos[off]; \ +} + +void GLMod_FloodFillSkin( qbyte *skin, int skinwidth, int skinheight ) +{ + qbyte fillcolor = *skin; // assume this is the pixel to fill + floodfill_t fifo[FLOODFILL_FIFO_SIZE]; + int inpt = 0, outpt = 0; + int filledcolor = -1; + int i; + + if (filledcolor == -1) + { + filledcolor = 0; + // attempt to find opaque black + for (i = 0; i < 256; ++i) + if (d_8to24rgbtable[i] == (255 << 0)) // alpha 1.0 + { + filledcolor = i; + break; + } + } + + // can't fill to filled color or to transparent color (used as visited marker) + if ((fillcolor == filledcolor) || (fillcolor == 255)) + { + //printf( "not filling skin from %d to %d\n", fillcolor, filledcolor ); + return; + } + + fifo[inpt].x = 0, fifo[inpt].y = 0; + inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; + + while (outpt != inpt) + { + int x = fifo[outpt].x, y = fifo[outpt].y; + int fdc = filledcolor; + qbyte *pos = &skin[x + skinwidth * y]; + + outpt = (outpt + 1) & FLOODFILL_FIFO_MASK; + + if (x > 0) FLOODFILL_STEP( -1, -1, 0 ); + if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 ); + if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 ); + if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 ); + skin[x + skinwidth * y] = fdc; + } +} + +//============================================================================= + +/* +================= +Mod_LoadSpriteFrame +================= +*/ +void * GLMod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum, int version) +{ + dspriteframe_t *pinframe; + mspriteframe_t *pspriteframe; + int width, height, size, origin[2]; + char name[64]; + + pinframe = (dspriteframe_t *)pin; + + width = LittleLong (pinframe->width); + height = LittleLong (pinframe->height); + size = width * height; + + pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t),loadname); + + Q_memset (pspriteframe, 0, sizeof (mspriteframe_t)); + + *ppframe = pspriteframe; + + pspriteframe->width = width; + pspriteframe->height = height; + origin[0] = LittleLong (pinframe->origin[0]); + origin[1] = LittleLong (pinframe->origin[1]); + + pspriteframe->up = origin[1]; + pspriteframe->down = origin[1] - height; + pspriteframe->left = origin[0]; + pspriteframe->right = width + origin[0]; + + COM_StripExtension(loadmodel->name, name); + strcat(name, va("_%i", framenum)); + pspriteframe->gl_texturenum = Mod_LoadReplacementTexture(name, true, true); + if (version == SPRITE32_VERSION) + { + size *= 4; + if (!pspriteframe->gl_texturenum) pspriteframe->gl_texturenum = GL_LoadTexture32 (name, width, height, (unsigned *)(pinframe + 1), true, true); + } + else + if (!pspriteframe->gl_texturenum) pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (qbyte *)(pinframe + 1), true, true); + + return (void *)((qbyte *)pinframe + sizeof (dspriteframe_t) + size); +} + + +/* +================= +Mod_LoadSpriteGroup +================= +*/ +void * GLMod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum, int version) +{ + dspritegroup_t *pingroup; + mspritegroup_t *pspritegroup; + int i, numframes; + dspriteinterval_t *pin_intervals; + float *poutintervals; + void *ptemp; + + pingroup = (dspritegroup_t *)pin; + + numframes = LittleLong (pingroup->numframes); + + pspritegroup = Hunk_AllocName (sizeof (mspritegroup_t) + + (numframes - 1) * sizeof (pspritegroup->frames[0]), loadname); + + pspritegroup->numframes = numframes; + + *ppframe = (mspriteframe_t *)pspritegroup; + + pin_intervals = (dspriteinterval_t *)(pingroup + 1); + + poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); + + pspritegroup->intervals = poutintervals; + + for (i=0 ; iinterval); + if (*poutintervals <= 0.0) + Sys_Error ("Mod_LoadSpriteGroup: interval<=0"); + + poutintervals++; + pin_intervals++; + } + + ptemp = (void *)pin_intervals; + + for (i=0 ; iframes[i], framenum * 100 + i, version); + } + + return ptemp; +} + +/* +================= +Mod_LoadSpriteModel +================= +*/ +void GLMod_LoadSpriteModel (model_t *mod, void *buffer) +{ + int i; + int version; + dsprite_t *pin; + msprite_t *psprite; + int numframes; + int size; + dspriteframetype_t *pframetype; + + pin = (dsprite_t *)buffer; + + version = LittleLong (pin->version); + if (version != SPRITE32_VERSION) + if (version != SPRITE_VERSION) + Sys_Error ("%s has wrong version number " + "(%i should be %i)", mod->name, version, SPRITE_VERSION); + + numframes = LittleLong (pin->numframes); + + size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); + + psprite = Hunk_AllocName (size, loadname); + + mod->cache.data = psprite; + + psprite->type = LittleLong (pin->type); + psprite->maxwidth = LittleLong (pin->width); + psprite->maxheight = LittleLong (pin->height); + psprite->beamlength = LittleFloat (pin->beamlength); + mod->synctype = LittleLong (pin->synctype); + psprite->numframes = numframes; + + mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2; + mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2; + mod->mins[2] = -psprite->maxheight/2; + mod->maxs[2] = psprite->maxheight/2; + +// +// load the frames +// + if (numframes < 1) + Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes); + + mod->numframes = numframes; + + pframetype = (dspriteframetype_t *)(pin + 1); + + for (i=0 ; itype); + psprite->frames[i].type = frametype; + + if (frametype == SPR_SINGLE) + { + pframetype = (dspriteframetype_t *) + GLMod_LoadSpriteFrame (pframetype + 1, + &psprite->frames[i].frameptr, i, version); + } + else + { + pframetype = (dspriteframetype_t *) + GLMod_LoadSpriteGroup (pframetype + 1, + &psprite->frames[i].frameptr, i, version); + } + } + + mod->type = mod_sprite; +} + +void GLMod_LoadSprite2Model (model_t *mod, void *buffer) +{ + int i; + int version; + dmd2sprite_t *pin; + msprite_t *psprite; + int numframes; + int size; + dmd2sprframe_t *pframetype; + mspriteframe_t *frame; + float origin[2]; + + pin = (dmd2sprite_t *)buffer; + + version = LittleLong (pin->version); + if (version != SPRITE2_VERSION) + Sys_Error ("%s has wrong version number " + "(%i should be %i)", mod->name, version, SPRITE2_VERSION); + + numframes = LittleLong (pin->numframes); + + size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); + + psprite = Hunk_AllocName (size, loadname); + + mod->cache.data = psprite; + + psprite->type = SPR_VP_PARALLEL; + psprite->maxwidth = 1; + psprite->maxheight = 1; + psprite->beamlength = 1; + mod->synctype = 0; + psprite->numframes = numframes; + + mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2; + mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2; + mod->mins[2] = -psprite->maxheight/2; + mod->maxs[2] = psprite->maxheight/2; + +// +// load the frames +// + if (numframes < 1) + Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes); + + mod->numframes = numframes; + + pframetype = pin->frames; + + for (i=0 ; iframes[i].type = frametype; + + frame = psprite->frames[i].frameptr = Hunk_AllocName(sizeof(mspriteframe_t), loadname); + + frame->gl_texturenum = Mod_LoadHiResTexture(pframetype->name, true, true); + frame->width = LittleLong(pframetype->width); + frame->height = LittleLong(pframetype->height); + origin[0] = LittleLong (pframetype->origin_x); + origin[1] = LittleLong (pframetype->origin_y); + + frame->up = -origin[1]; + frame->down = frame->height - origin[1]; + frame->left = -origin[0]; + frame->right = frame->width - origin[0]; + } + + mod->type = mod_sprite; +} + + +#ifdef DOOMWADS + +typedef struct { + short width; + short height; + short xpos; + short ypos; +} doomimage_t; +static int FindDoomSprites(char *name, int size, void *param) +{ + if (*(int *)param + strlen(name)+1 > 16000) + Sys_Error("Too many doom sprites\n"); + + strcpy((char *)param + *(int *)param, name); + *(int *)param += strlen(name)+1; + + return true; +} + + +static void LoadDoomSpriteFrame(char *imagename, mspriteframedesc_t *pdesc, int anglenum, qboolean xmirrored) +{ + int c; + int fr; + int rc; + unsigned int *colpointers; + qbyte *data; + doomimage_t *header; + + qbyte image[256*256]; + qbyte *palette; + qbyte *coldata; + mspriteframe_t *pframe; + + if (!anglenum) + { + pdesc->type = SPR_SINGLE; + pdesc->frameptr = pframe = Hunk_AllocName(sizeof(*pframe), loadname); + } + else + { + mspritegroup_t *group; + + if (!pdesc->frameptr || pdesc->type != SPR_ANGLED) + { + pdesc->type = SPR_ANGLED; + group = Hunk_AllocName(sizeof(*group)+sizeof(mspriteframe_t *)*(8-1), loadname); + pdesc->frameptr = (mspriteframe_t *)group; + group->numframes = 8; + } + else + group = (mspritegroup_t *)pdesc->frameptr; + + pframe = Hunk_AllocName(sizeof(*pframe), loadname); + group->frames[anglenum-1] = pframe; + } + + palette = COM_LoadTempFile("wad/playpal"); + header = (doomimage_t *)COM_LoadTempFile2(imagename); + data = (qbyte *)header; + pframe->width = header->width; + pframe->height = header->height; + pframe->up = +header->ypos; + pframe->down = -header->height + header->ypos; + + if (xmirrored) + { + pframe->right = -header->xpos; + pframe->left = header->width - header->xpos; + } + else + { + pframe->left = -header->xpos; + pframe->right = header->width - header->xpos; + } + + if (header->width*header->height > sizeof(image)) + return; + + memset(image, 255, header->width*header->height); + colpointers = (unsigned int*)(data+sizeof(doomimage_t)); + for (c = 0; c < header->width; c++) + { + if (colpointers[c] >= com_filesize) + break; + coldata = data + colpointers[c]; + while(1) + { + fr = *coldata++; + if (fr == 255) + break; + + rc = *coldata++; + + coldata++; + + if ((fr+rc) > header->height) + break; + + while(rc) + { + image[c + fr*header->width] = *coldata++; + fr++; + rc--; + } + + coldata++; + } + } + + pframe->gl_texturenum = GL_LoadTexture8Pal24(imagename, pframe->width, pframe->height, image, palette, true, true); +} + +/* +================= +Doom Sprites +================= +*/ +void GLMod_LoadDoomSprite (model_t *mod) +{ + char files[16384]; + char basename[MAX_QPATH]; + int baselen; + char *name; + + int numframes=0; + int ofs; + + int size; + + int elements=0; + + int framenum; + int anglenum; + + msprite_t *psprite; + + + COM_StripExtension(mod->name, basename); + baselen = strlen(basename); + strcat(basename, "*"); + *(int *)files=4; + COM_EnumerateFiles(basename, FindDoomSprites, files); + + //find maxframes and validate the rest. + for (ofs = 4; ofs < *(int*)files; ofs+=strlen(files+ofs)+1) + { + name = files+ofs+baselen; + + if (!*name) + Host_Error("Doom sprite componant lacks frame name"); + if (*name - 'a'+1 > numframes) + numframes = *name - 'a'+1; + if (name[1] < '0' || name[1] > '8') + Host_Error("Doom sprite componant has bad angle number"); + if (name[1] == '0') + elements+=8; + else + elements++; + if (name[2]) //is there a second element? + { + if (name[2] - 'a'+1 > numframes) + numframes = name[2] - 'a'+1; + if (name[3] < '0' || name[3] > '8') + Host_Error("Doom sprite componant has bad angle number"); + + if (name[3] == '0') + elements+=8; + else + elements++; + } + } + if (elements != numframes*8) + Host_Error("Doom sprite has wrong componant count"); + if (!numframes) + Host_Error("Doom sprite componant has no frames"); + + size = sizeof (msprite_t) + (elements - 1) * sizeof (psprite->frames); + psprite = Hunk_AllocName (size, loadname); + + psprite->numframes = numframes; + + //do the actual loading. + for (ofs = 4; ofs < *(int*)files; ofs+=strlen(files+ofs)+1) + { + name = files+ofs; + framenum = name[baselen+0] - 'a'; + anglenum = name[baselen+1] - '0'; + + LoadDoomSpriteFrame(name, &psprite->frames[framenum], anglenum, false); + + if (name[baselen+2]) //is there a second element? + { + framenum = name[baselen+2] - 'a'; + anglenum = name[baselen+3] - '0'; + + LoadDoomSpriteFrame(name, &psprite->frames[framenum], anglenum, true); + } + } + + + psprite->type = SPR_FACING_UPRIGHT; + mod->type = mod_sprite; + + mod->cache.data = psprite; +} +#endif + +//============================================================================= + +/* +================ +Mod_Print +================ +*/ +void GLMod_Print (void) +{ + int i; + model_t *mod; + + Con_Printf ("Cached models:\n"); + for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) + { + Con_Printf ("%8p : %s\n",mod->cache.data, mod->name); + } +} + + diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h new file mode 100644 index 00000000..5a0eac7c --- /dev/null +++ b/engine/gl/gl_model.h @@ -0,0 +1,817 @@ +/* +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. + +*/ + +#ifndef __MODEL__ +#define __MODEL__ + +#include "modelgen.h" +#include "spritegn.h" + +struct hull_s; +struct trace_s; +struct edict_s; + +typedef struct { + qboolean (*RecursiveHullCheck) (struct hull_s *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, struct trace_s *trace); + int (*HullPointContents) (struct hull_s *hull, vec3_t p); //return FTE contents +} hullfuncs_t; + +typedef struct { + void (*FatPVS) (vec3_t org, qboolean add); + qboolean (*EdictInFatPVS) (struct edict_s *edict); + void (*FindTouchedLeafs_Q1) (struct edict_s *ent); //edict system as opposed to q2 game dll system. + + void (*LightPointValues) (vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir); + void (*StainNode) (struct mnode_s *node, float *parms); + void (*MarkLights) (struct dlight_s *light, int bit, struct mnode_s *node); + + qbyte *(*LeafPVS) (int num, struct model_s *model, qbyte *buffer); + int (*LeafForPoint) (vec3_t point, struct model_s *model); +} bspfuncs_t; + + +typedef struct mesh_s +{ + int numvertexes; + vec4_t *xyz_array; //structure is used only at load. + vec3_t *normals_array; //so what harm is there in doing this? + vec2_t *st_array; + vec2_t *lmst_array; + byte_vec4_t *colors_array; + + int numindexes; + int *indexes; + int *trneighbors; + vec3_t *trnormals; + + vec3_t mins, maxs; + float radius; + + vec3_t lightaxis[3]; + + unsigned int patchWidth; + unsigned int patchHeight; +} mesh_t; + + +extern int gl_canbumpmap; + + + + +/* + +d*_t structures are on-disk representations +m*_t structures are in-memory + +*/ + +// entity effects + +#define EF_BRIGHTFIELD 1 +#define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_DIMLIGHT 8 +#define EF_FLAG1 16 +#define EF_FLAG2 32 +#define EF_BLUE 64 +#define EF_RED 128 + +#define EF_NODRAW 0x80 //this is going to get complicated... + +/* +============================================================================== + +BRUSH MODELS + +============================================================================== +*/ + + +// +// in memory representation +// +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct +{ + vec3_t position; +} mvertex_t; + +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 + + +// plane_t structure +// !!! if this is changed, it must be changed in asm_i386.h too !!! +typedef struct mplane_s +{ + vec3_t normal; + float dist; + qbyte type; // for texture axis selection and fast side tests + qbyte signbits; // signx + signy<<1 + signz<<1 + qbyte pad[2]; +} mplane_t; + +typedef struct texture_s +{ + char name[64]; + unsigned width, height; + + qbyte pixbytes; + qbyte alphaed; //gl_blend needed on this surface. + + int parttype; + + int gl_texturenum; + int gl_texturenumfb; + int gl_texturenumbumpmap; + struct msurface_s *texturechain; // for gl_texsort drawing + int anim_total; // total tenths in sequence ( 0 = no) + int anim_min, anim_max; // time for this frame min <=time< max + struct texture_s *anim_next; // in the animation sequence + struct texture_s *alternate_anims; // bmodels in frmae 1 use these + unsigned offsets[MIPLEVELS]; // four mip maps stored +} texture_t; + +#define SURF_DRAWSKYBOX 1 +#define SURF_PLANEBACK 2 +#define SURF_DRAWSKY 4 +#define SURF_DRAWSPRITE 8 +#define SURF_DRAWTURB 0x10 +#define SURF_DRAWTILED 0x20 +#define SURF_DRAWBACKGROUND 0x40 +#define SURF_UNDERWATER 0x80 +#define SURF_DONTWARP 0x100 +#define SURF_BULLETEN 0x200 +#define SURF_DRAWALPHA 0x10000 + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct +{ + unsigned short v[2]; + unsigned int cachededgeoffset; +} medge_t; + +typedef struct mtexinfo_s +{ + float vecs[2][4]; + float mipadjust; + texture_t *texture; + int flags; + + //it's a q2 thing. + int numframes; + struct mtexinfo_s *next; +} mtexinfo_t; + +#define VERTEXSIZE 7 + +typedef struct glpoly_s +{ + struct glpoly_s *next; + struct glpoly_s *chain; + int numverts; + int flags; // for SURF_UNDERWATER +#ifdef SHADERS + float texcenter[2]; //center of texture made by adveraging the tex coords +#endif + float verts[4][VERTEXSIZE]; // variable sized (xyz s1t1 s2t2) +} glpoly_t; + +typedef struct msurface_s +{ + int visframe; // should be drawn when node is crossed + int shadowframe; + + mplane_t *plane; + int flags; + + int firstedge; // look up in model->surfedges[], negative numbers + int numedges; // are backwards edges + +#ifdef SWQUAKE + struct surfcache_s *cachespots[MIPLEVELS]; +#endif + struct msurface_s *nextalphasurface; + + short texturemins[2]; + short extents[2]; + + int light_s, light_t; // gl lightmap coordinates + + mesh_t *mesh; + entity_t *ownerent; + glpoly_t *polys; // multiple if warped + vec3_t center; //adverage + float radius; //max dist from center + struct msurface_s *texturechain; + + vec3_t normal; + + mtexinfo_t *texinfo; + +// lighting info + int dlightframe; + int dlightbits; + + int lightmaptexturenum; + qbyte styles[MAXLIGHTMAPS]; + int cached_light[MAXLIGHTMAPS]; // values currently used in lightmap + qboolean cached_dlight; // true if dynamic light in cache +#ifdef PEXT_LIGHTSTYLECOL + qbyte cached_colour[MAXLIGHTMAPS]; +#endif +#ifndef NOSTAINS + qboolean stained; +#endif + qbyte *samples; // [numstyles*surfsize] +} msurface_t; + +typedef struct mnode_s +{ +// common with leaf + int contents; // 0, to differentiate from leafs + int visframe; // node needs to be traversed if current + int shadowframe; + + float minmaxs[6]; // for bounding box culling + + struct mnode_s *parent; + +// node specific + mplane_t *plane; + struct mnode_s *children[2]; +#ifdef Q2BSPS + int childnum[2]; +#endif + + unsigned short firstsurface; + unsigned short numsurfaces; +} mnode_t; + + + +typedef struct mleaf_s +{ +// common with node + int contents; // wil be a negative contents number + int visframe; // node needs to be traversed if current + int shadowframe; + + float minmaxs[6]; // for bounding box culling + + struct mnode_s *parent; + +// leaf specific + qbyte *compressed_vis; + struct efrag_s *efrags; + + msurface_t **firstmarksurface; + int nummarksurfaces; + int key; // BSP sequence number for leaf's contents + qbyte ambient_sound_level[NUM_AMBIENTS]; +#ifdef Q2BSPS + //it's a q2 thing + int cluster; + int area; + unsigned short firstleafbrush; + unsigned short numleafbrushes; + + unsigned short firstleafface; //q3 addititions + unsigned short numleaffaces; + + unsigned short numleafpatches; + unsigned short firstleafpatch; + + struct mleaf_s *vischain; +#endif +} mleaf_t; + + +typedef struct +{ + float mins[3], maxs[3]; + float origin[3]; + int headnode[MAX_MAP_HULLSM]; + int visleafs; // not including the solid leaf 0 + int firstface, numfaces; + qboolean hullavailable[MAX_MAP_HULLSM]; +} mmodel_t; + + +// !!! if this is changed, it must be changed in asm_i386.h too !!! +typedef struct hull_s +{ + dclipnode_t *clipnodes; + mplane_t *planes; + int firstclipnode; + int lastclipnode; + vec3_t clip_mins; + vec3_t clip_maxs; + int available; + + hullfuncs_t funcs; +} hull_t; + + +void Q1BSP_SetHullFuncs(hull_t *hull); + +/* +============================================================================== + +SPRITE MODELS + +============================================================================== +*/ + + +// FIXME: shorten these? +typedef struct mspriteframe_s +{ + int width; + int height; + float up, down, left, right; + int gl_texturenum; +#ifdef SWQUAKE + qbyte pixels[4]; +#endif +} mspriteframe_t; + +typedef struct +{ + int numframes; + float *intervals; + mspriteframe_t *frames[1]; +} mspritegroup_t; + +typedef struct +{ + spriteframetype_t type; + mspriteframe_t *frameptr; +} mspriteframedesc_t; + +typedef struct +{ + int type; + int maxwidth; + int maxheight; + int numframes; + float beamlength; // remove? + void *cachespot; // remove? + mspriteframedesc_t frames[1]; +} msprite_t; + + +/* +============================================================================== + +ALIAS MODELS + +Alias models are position independent, so the cache manager can move them. +============================================================================== +*/ + +typedef struct { + int s; + int t; +} mstvert_t; + +typedef struct +{ +#ifdef SWQUAKE + aliasframetype_t type; +#endif + int firstpose; + int numposes; + float interval; + dtrivertx_t bboxmin; + dtrivertx_t bboxmax; + + vec3_t scale; + vec3_t scale_origin; + + int frame; + char name[16]; +} maliasframedesc_t; + +typedef struct +{ + dtrivertx_t bboxmin; + dtrivertx_t bboxmax; + int frame; +} maliasgroupframedesc_t; + +typedef struct +{ + int numframes; + int intervals; + maliasgroupframedesc_t frames[1]; +} maliasgroup_t; + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct mtriangle_s { + int xyz_index[3]; + int st_index[3]; + int neighbors[3]; +} mtriangle_t; + + +#define MAX_SKINS 32 +typedef struct { + int ident; + int version; + vec3_t scale; + vec3_t scale_origin; + float boundingradius; + vec3_t eyeposition; + int numskins; + int skinwidth; + int skinheight; + int numverts; + int numtris; + int numframes; + synctype_t synctype; + int flags; + float size; +#ifdef SWQUAKE + int model; + int stverts; + int skindesc; +#endif + int numposes; + int poseverts; + int posedata; // numposes*poseverts trivert_t + + int baseposedata; //original verts for triangles to reference + int triangles; //we need tri data for shadow volumes + + int commands; // gl command list with embedded s/t + int gl_texturenum[MAX_SKINS][4]; + int texels[MAX_SKINS]; + maliasframedesc_t frames[1]; // variable sized +} aliashdr_t; + +#define MAXALIASVERTS 2048 +#define ALIAS_Z_CLIP_PLANE 5 +#define MAXALIASFRAMES 256 +#define MAXALIASTRIS 2048 +extern aliashdr_t *pheader; +extern mstvert_t stverts[MAXALIASVERTS*2]; +extern mtriangle_t triangles[MAXALIASTRIS]; +extern dtrivertx_t *poseverts[MAXALIASFRAMES]; + + + + +/* +======================================================================== + +.MD2 triangle model file format + +======================================================================== +*/ + +// LordHavoc: grabbed this from the Q2 utility source, +// renamed a things to avoid conflicts + +#define MD2IDALIASHEADER (('2'<<24)+('P'<<16)+('D'<<8)+'I') +#define MD2ALIAS_VERSION 8 + +#define MD2MAX_TRIANGLES 4096 +#define MD2MAX_VERTS 2048 +#define MD2MAX_FRAMES 512 +#define MD2MAX_SKINS 32 +#define MD2MAX_SKINNAME 64 +// sanity checking size +#define MD2MAX_SIZE (1024*4200) + +typedef struct +{ + short s; + short t; +} md2stvert_t; + +typedef struct +{ + short index_xyz[3]; + short index_st[3]; +} md2triangle_t; + +typedef struct +{ + qbyte v[3]; // scaled qbyte to fit in frame mins/maxs + qbyte lightnormalindex; +} md2trivertx_t; + +#define MD2TRIVERTX_V0 0 +#define MD2TRIVERTX_V1 1 +#define MD2TRIVERTX_V2 2 +#define MD2TRIVERTX_LNI 3 +#define MD2TRIVERTX_SIZE 4 + +typedef struct +{ + float scale[3]; // multiply qbyte verts by this + float translate[3]; // then add this + char name[16]; // frame name from grabbing + md2trivertx_t verts[1]; // variable sized +} md2frame_t; + + +// the glcmd format: +// a positive integer starts a tristrip command, followed by that many +// vertex structures. +// a negative integer starts a trifan command, followed by -x vertexes +// a zero indicates the end of the command list. +// a vertex consists of a floating point s, a floating point t, +// and an integer vertex index. + + +typedef struct +{ + int ident; + int version; + + int skinwidth; + int skinheight; + int framesize; // qbyte size of each frame + + int num_skins; + int num_xyz; + int num_st; // greater than num_xyz for seams + int num_tris; + int num_glcmds; // dwords in strip/fan command list + int num_frames; + + int ofs_skins; // each skin is a MAX_SKINNAME string + int ofs_st; // qbyte offset from start for stverts + int ofs_tris; // offset for dtriangles + int ofs_frames; // offset for first frame + int ofs_glcmds; + int ofs_end; // end of file + + int gl_texturenum[MAX_SKINS]; +} md2_t; + +#define ALIASTYPE_MDL 1 +#define ALIASTYPE_MD2 2 + + + + + +//=================================================================== + + +typedef struct +{ + qbyte ambient[3]; + qbyte diffuse[3]; + qbyte direction[2]; +} dq3gridlight_t; +typedef struct { + vec3_t gridBounds; + vec3_t gridMins; + vec3_t gridSize; + int numlightgridelems; + dq3gridlight_t lightgrid[1]; +} q3lightgridinfo_t; + + +// +// Whole model +// + +typedef enum {mod_brush, mod_sprite, mod_alias, mod_dummy, mod_halflife} modtype_t; +typedef enum {fg_quake, fg_quake2, fg_quake3, fg_halflife, fg_new, fg_doom} fromgame_t; //useful when we have very similar model types. (eg quake/halflife bsps) + +#define EF_ROCKET 1 // leave a trail +#define EF_GRENADE 2 // leave a trail +#define EF_GIB 4 // leave a trail +#define EF_ROTATE 8 // rotate (bonus items) +#define EF_TRACER 16 // green split trail +#define EF_ZOMGIB 32 // small blood trail +#define EF_TRACER2 64 // orange split trail + rotate +#define EF_TRACER3 128 // purple trail + +//hexen2. +#define EF_FIREBALL 256 // Yellow transparent trail in all directions +#define EF_ICE 512 // Blue-white transparent trail, with gravity +#define EF_MIP_MAP 1024 // This model has mip-maps +#define EF_SPIT 2048 // Black transparent trail with negative light +#define EF_TRANSPARENT 4096 // Transparent sprite +#define EF_SPELL 8192 // Vertical spray of particles +#define EF_HOLEY 16384 // Solid model with color 0 +#define EF_SPECIAL_TRANS 32768 // Translucency through the particle table +#define EF_FACE_VIEW 65536 // Poly Model always faces you +#define EF_VORP_MISSILE 131072 // leave a trail at top and bottom of model +#define EF_SET_STAFF 262144 // slowly move up and left/right +#define EF_MAGICMISSILE 524288 // a trickle of blue/white particles with gravity +#define EF_BONESHARD 1048576 // a trickle of brown particles with gravity +#define EF_SCARAB 2097152 // white transparent particles with little gravity +#define EF_ACIDBALL 4194304 // Green drippy acid shit +#define EF_BLOODSHOT 8388608 // Blood rain shot trail + +typedef union { + struct { + int numlinedefs; + int numsidedefs; + int numsectors; + } doom; +} specificmodeltype_t; + +typedef struct model_s +{ + char name[MAX_QPATH]; + qboolean needload; // bmodels and sprites don't cache normally + + modtype_t type; + fromgame_t fromgame; + + int numframes; + synctype_t synctype; + + int flags; + int particleeffect; + qboolean particleengulphs; + int particletrail; + qboolean nodefaulttrail; + + qboolean rgblighting; //.lit, halflife. + +// +// volume occupied by the model graphics +// + vec3_t mins, maxs; + float radius; + +// +// solid volume for clipping +// + qboolean clipbox; + vec3_t clipmins, clipmaxs; + +// +// brush model +// + int firstmodelsurface, nummodelsurfaces; + + int numsubmodels; + mmodel_t *submodels; + + int numplanes; + mplane_t *planes; + + int numleafs; // number of visible leafs, not counting 0 + mleaf_t *leafs; + + int numvertexes; + mvertex_t *vertexes; + + int numedges; + medge_t *edges; + + int numnodes; + mnode_t *nodes; + + int numtexinfo; + mtexinfo_t *texinfo; + + int numsurfaces; + msurface_t *surfaces; + + int numsurfedges; + int *surfedges; + + int numclipnodes; + dclipnode_t *clipnodes; + + int nummarksurfaces; + msurface_t **marksurfaces; + + hull_t hulls[MAX_MAP_HULLSM]; + + int numtextures; + texture_t **textures; + + qbyte *visdata; + void *vis; + qbyte *lightdata; + qbyte *deluxdata; + q3lightgridinfo_t *lightgrid; + char *entities; + + struct terrain_s *terrain; + + unsigned checksum; + unsigned checksum2; + + + bspfuncs_t funcs; +// +// additional model data +// + cache_user_t cache; // only access through Mod_Extradata + +} model_t; + +//============================================================================ +/* +void Mod_Init (void); +void Mod_ClearAll (void); +model_t *Mod_ForName (char *name, qboolean crash); +model_t *Mod_FindName (char *name); +void *Mod_Extradata (model_t *mod); // handles caching +void Mod_TouchModel (char *name); + +mleaf_t *Mod_PointInLeaf (float *p, model_t *model); +qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model); +*/ +#endif // __MODEL__ + + + + + + + + + + + + +#ifdef Q2BSPS + +#ifdef __cplusplus +//#pragma message (" c++ stinks") +#else + +void CM_Init(void); + +qboolean CM_SetAreaPortalState (int portalnum, qboolean open); +qboolean CM_HeadnodeVisible (int nodenum, qbyte *visbits); +qboolean CM_AreasConnected (int area1, int area2); +int CM_NumClusters (void); +int CM_ClusterSize (void); +int CM_LeafContents (int leafnum); +int CM_LeafCluster (int leafnum); +int CM_LeafArea (int leafnum); +int CM_WriteAreaBits (qbyte *buffer, int area); +int CM_PointLeafnum (vec3_t p); +qbyte *CM_ClusterPVS (int cluster, qbyte *buffer); +qbyte *CM_ClusterPHS (int cluster); +int CM_BoxLeafnums (vec3_t mins, vec3_t maxs, int *list, int listsize, int *topnode); +int CM_PointContents (vec3_t p, int headnode); +struct trace_s CM_BoxTrace (vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int headnode, int brushmask); +int CM_HeadnodeForBox (vec3_t mins, vec3_t maxs); +struct trace_s CM_TransformedBoxTrace (vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int headnode, int brushmask, vec3_t origin, vec3_t angles); + +void Mod_ParseInfoFromEntityLump(char *data); + +qboolean CMQ2_SetAreaPortalState (int portalnum, qboolean open); +#endif + + + + + + + +#ifdef SWQUAKE +typedef struct +{ + aliasskintype_t type; + void *pcachespot; + int skin; +} maliasskindesc_t; + +typedef struct +{ + int numskins; + int intervals; + maliasskindesc_t skindescs[1]; +} maliasskingroup_t; + +#endif +#endif //__MODEL__ + + + diff --git a/engine/gl/gl_ngraph.c b/engine/gl/gl_ngraph.c new file mode 100644 index 00000000..e542ec90 --- /dev/null +++ b/engine/gl/gl_ngraph.c @@ -0,0 +1,143 @@ +/* +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_ngraph.c + +#include "quakedef.h" +#include "glquake.h" + +extern qbyte *draw_chars; // 8*8 graphic characters + +int netgraphtexture; // netgraph texture + +#define NET_GRAPHHEIGHT 32 + +static qbyte ngraph_texels[NET_GRAPHHEIGHT][NET_TIMINGS]; + +static void R_LineGraph (int x, int h) +{ + int i; + int s; + int color; + + s = NET_GRAPHHEIGHT; + + if (h == 10000) + color = 0x6f; // yellow + else if (h == 9999) + color = 0x4f; // red + else if (h == 9998) + color = 0xd0; // blue + else + color = 0xfe; // white + + if (h>s) + h = s; + + for (i=0 ; i>4; + col = num&15; + source = draw_chars + (row<<10) + (col<<3); + + for (drawline = 8; drawline; drawline--, y++) + { + for (nx=0 ; nx<8 ; nx++) + if (source[nx] != 255) + ngraph_texels[y][nx+x] = 0x60 + source[nx]; + source += 128; + } +} + + +/* +============== +R_NetGraph +============== +*/ +void GLR_NetGraph (void) +{ + int a, x, i, y; + int lost; + char st[80]; + unsigned ngraph_pixels[NET_GRAPHHEIGHT][NET_TIMINGS]; + + x = 0; + lost = CL_CalcNet(); + for (a=0 ; a>1); + x=-x; + y = vid.height - sb_lines - 24 - NET_GRAPHHEIGHT - 1; + + M_DrawTextBox (x, y, NET_TIMINGS/8, NET_GRAPHHEIGHT/8 + 1); + y += 8; + + sprintf(st, "%3i%% packet loss", lost); + Draw_String(8, y, st); + y += 8; + + GL_Bind(netgraphtexture); + + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, + NET_TIMINGS, NET_GRAPHHEIGHT, 0, GL_RGBA, + GL_UNSIGNED_BYTE, ngraph_pixels); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + x = 8; + glColor3f (1,1,1); + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x, y); + glTexCoord2f (1, 0); + glVertex2f (x+NET_TIMINGS, y); + glTexCoord2f (1, 1); + glVertex2f (x+NET_TIMINGS, y+NET_GRAPHHEIGHT); + glTexCoord2f (0, 1); + glVertex2f (x, y+NET_GRAPHHEIGHT); + glEnd (); +} + diff --git a/engine/gl/gl_ppl.c b/engine/gl/gl_ppl.c new file mode 100644 index 00000000..b827c2ad --- /dev/null +++ b/engine/gl/gl_ppl.c @@ -0,0 +1,2297 @@ +#include "quakedef.h" +#ifdef RGLQUAKE +#include "glquake.h" + +//these are shared with gl_rsurf - move to header +void R_MirrorChain (msurface_t *s); +void GL_SelectTexture (GLenum target); +void R_RenderDynamicLightmaps (msurface_t *fa); +void R_BlendLightmaps (void); + +extern int gldepthfunc; +extern int *lightmap_textures; +extern int lightmap_bytes; // 1, 2, or 4 + +extern cvar_t gl_detail; +extern cvar_t r_fb_bmodels; +extern cvar_t gl_part_flame; + +extern cvar_t gl_part_flame; +extern cvar_t gl_maxshadowlights; +extern int detailtexture; +//end header confict + +extern lightmapinfo_t **lightmap; + +extern model_t *currentmodel; + +//#define glBegin glEnd + + +#define Q2RF_WEAPONMODEL 4 // only draw through eyes + +#define EDGEOPTIMISE +#ifdef EDGEOPTIMISE +struct { + short count; + short count2; + short next; + short prev; +} edge[MAX_MAP_EDGES]; +int firstedge; +#endif + +vec3_t lightorg = {0, 0, 0}; +float lightradius; + +static void PPL_BaseTextureChain(msurface_t *first) +{ + extern int *deluxmap_textures; + extern cvar_t gl_bump; + texture_t *t; + + msurface_t *s = first; + + int vi; + glRect_t *theRect; + glpoly_t *p; + float *v; + + glEnable(GL_TEXTURE_2D); + + t = GLR_TextureAnimation (s->texinfo->texture); + + if (s->flags & SURF_DRAWTURB) + { + GL_DisableMultitexture(); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GL_Bind (t->gl_texturenum); + for (; s ; s=s->texturechain) + EmitWaterPolys (s); + + glDisable(GL_BLEND); + glColor4f(1,1,1, 1); + + t->texturechain = NULL; //no lighting effects. (good job these don't animate eh?) + return; + } + + if (s->lightmaptexturenum < 0) //no lightmap + { + GL_DisableMultitexture(); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + GL_Bind (t->gl_texturenum); + + + for (; s ; s=s->texturechain) + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); + } + } + else if (!gl_mtexable) + { //multitexture isn't supported. + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + GL_Bind (t->gl_texturenum); + for (s = first; s ; s=s->texturechain) + { + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); + } + } + + glEnable(GL_BLEND); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (gl_lightmap_format == GL_LUMINANCE || gl_lightmap_format == GL_RGB) + glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + else if (gl_lightmap_format == GL_INTENSITY) + { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4f (0,0,0,1); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else if (gl_lightmap_format == GL_RGBA) + { + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); + } + + for (s = first; s ; s=s->texturechain) + { + vi = s->lightmaptexturenum; + // Binds lightmap to texenv 1 + GL_Bind (lightmap_textures[vi]); + if (lightmap[vi]->modified) + { + lightmap[vi]->modified = false; + theRect = &lightmap[vi]->rectchange; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes); + theRect->l = LMBLOCK_WIDTH; + theRect->t = LMBLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + } + } + } + else + { + if (gl_bump.value && currentmodel->deluxdata && t->gl_texturenumbumpmap) + { + qglActiveTextureARB(GL_TEXTURE0_ARB); + + //Bind normal map to texture unit 0 + GL_BindType(GL_TEXTURE_2D, t->gl_texturenumbumpmap); + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); + + qglActiveTextureARB(GL_TEXTURE1_ARB); //the deluxmap + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB); +//we now have normalmap.deluxmap on the screen. + if (gl_mtexarbable>=4) //go the whole hog. bumpmapping in one pass. + { + //continue going to give (normalmap.deluxemap)*texture*lightmap. + qglActiveTextureARB(GL_TEXTURE2_ARB); + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + GL_Bind (t->gl_texturenum); + + qglActiveTextureARB(GL_TEXTURE3_ARB); + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + + vi = -1; + for (; s ; s=s->texturechain) + { +// if (vi != s->lightmaptexturenum) + { + vi = s->lightmaptexturenum; + qglActiveTextureARB(GL_TEXTURE1_ARB); + GL_BindType(GL_TEXTURE_2D, deluxmap_textures[vi] ); + if (lightmap[vi]->deluxmodified) + { + lightmap[vi]->deluxmodified = false; + theRect = &lightmap[vi]->deluxrectchange; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + LMBLOCK_WIDTH, theRect->h, GL_RGB, GL_UNSIGNED_BYTE, + lightmap[vi]->deluxmaps+(theRect->t) *LMBLOCK_WIDTH*3); + theRect->l = LMBLOCK_WIDTH; + theRect->t = LMBLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + qglActiveTextureARB(GL_TEXTURE3_ARB); + GL_BindType(GL_TEXTURE_2D, lightmap_textures[vi] ); + if (lightmap[vi]->modified) + { + lightmap[vi]->modified = false; + theRect = &lightmap[vi]->rectchange; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes); + theRect->l = LMBLOCK_WIDTH; + theRect->t = LMBLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + } + + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]); + qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, v[5], v[6]); + qglMultiTexCoord2fARB(GL_TEXTURE2_ARB, v[3], v[4]); + qglMultiTexCoord2fARB(GL_TEXTURE3_ARB, v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + } + } + + qglActiveTextureARB(GL_TEXTURE3_ARB); + glDisable(GL_TEXTURE_2D); + qglActiveTextureARB(GL_TEXTURE2_ARB); + glDisable(GL_TEXTURE_2D); + qglActiveTextureARB(GL_TEXTURE1_ARB); + glDisable(GL_TEXTURE_2D); + qglActiveTextureARB(GL_TEXTURE0_ARB); //the deluxmap + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + return; + } + + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + for (; s ; s=s->texturechain) + { + vi = s->lightmaptexturenum; + GL_BindType(GL_TEXTURE_2D, deluxmap_textures[vi] ); + if (lightmap[vi]->deluxmodified) + { + lightmap[vi]->deluxmodified = false; + theRect = &lightmap[vi]->deluxrectchange; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + LMBLOCK_WIDTH, theRect->h, GL_RGB, GL_UNSIGNED_BYTE, + lightmap[vi]->deluxmaps+(theRect->t) *LMBLOCK_WIDTH*3); + theRect->l = LMBLOCK_WIDTH; + theRect->t = LMBLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]); + qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + } + } + glDisable(GL_TEXTURE_2D); + qglActiveTextureARB(GL_TEXTURE0_ARB); + + glBlendFunc(GL_DST_COLOR, GL_ZERO); //tell the texture + lightmap to do current*tex*light (where current is normalmap.deluxemap) + glEnable(GL_BLEND); + + s = first; + + GL_SelectTexture(mtexid0); + GL_Bind(t->gl_texturenum); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + GL_EnableMultitexture(); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + } + else + { + // Binds world to texture env 0 + GL_SelectTexture(mtexid0); + GL_Bind (t->gl_texturenum); + if (t->alphaed) + { + glEnable(GL_BLEND); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + else + { + glDisable(GL_BLEND); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } + GL_EnableMultitexture(); // Same as SelectTexture (TEXTURE1) + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + } + + for (; s; s=s->texturechain) + { + vi = s->lightmaptexturenum; + // Binds lightmap to texenv 1 + GL_Bind (lightmap_textures[vi]); + if (lightmap[vi]->modified) + { + lightmap[vi]->modified = false; + theRect = &lightmap[vi]->rectchange; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes); + theRect->l = LMBLOCK_WIDTH; + theRect->t = LMBLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + qglMTexCoord2fSGIS (mtexid0, v[3], v[4]); + qglMTexCoord2fSGIS (mtexid1, v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + } + } + } +} + +static void PPL_FullBrightTextureChain(msurface_t *first) +{ + glpoly_t *p; + float *v; + texture_t *t; + msurface_t *s; + int i; + + t = GLR_TextureAnimation (first->texinfo->texture); + + if (detailtexture && gl_detail.value) + { + GL_Bind(detailtexture); + glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); + + for (s = first; s ; s=s->texturechain) + { + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (i = 0; i < p->numverts; i++, v += VERTEXSIZE) + { + glTexCoord2f (v[5] * 18, v[6] * 18); + glVertex3fv (v); + } + glEnd(); + } + } + } + + if (t->gl_texturenumfb && r_fb_bmodels.value && cls.allow_luma) + { + GL_Bind(t->gl_texturenumfb); + glBlendFunc(GL_DST_COLOR, GL_ONE); + + for (s = first; s ; s=s->texturechain) + { + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (i = 0; i < p->numverts; i++, v += VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd(); + } + } + } +} + +//requires multitexture +void PPL_BaseTextures(model_t *model) +{ + int i; + msurface_t *s; + texture_t *t; + + glDisable(GL_BLEND); + glColor4f(1,1,1, 1); +// glDepthFunc(GL_LESS); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glShadeModel(GL_FLAT); + + currentmodel = model; + + if (model == cl.worldmodel && skytexturenum>=0) + { + t = model->textures[skytexturenum]; + if (t) + { + s = t->texturechain; + if (s) + { + t->texturechain = NULL; + R_DrawSkyChain (s); + } + } + } + if (mirrortexturenum>=0 && model == cl.worldmodel && r_mirroralpha.value != 1.0) + { + t = model->textures[mirrortexturenum]; + if (t) + { + s = t->texturechain; + if (s) + { + t->texturechain = NULL; + R_MirrorChain (s); + } + } + } + + for (i=0 ; inumtextures ; i++) + { + t = model->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + + if ((s->flags & SURF_DRAWTURB) && r_wateralphaval != 1.0) + { + t->texturechain = NULL; + continue; // draw translucent water later + } + + PPL_BaseTextureChain(s); + } + + GL_DisableMultitexture(); +} + +void PPL_BaseBModelTextures(entity_t *e) +{ + int i, k; + model_t *model; + msurface_t *s; + msurface_t *chain = NULL; + + glPushMatrix(); + R_RotateForEntity(e); + currentmodel = model = e->model; + s = model->surfaces+model->firstmodelsurface; + + glDisable(GL_BLEND); + glColor4f(1, 1, 1, 1); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +// calculate dynamic lighting for bmodel if it's not an +// instanced model + if (currentmodel->firstmodelsurface != 0 && r_dynamic.value) + { + for (k=0 ; kfuncs.MarkLights (&cl_dlights[k], 1<nodes + currentmodel->hulls[0].firstclipnode); + } + } + +//update lightmaps. + for (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++) + R_RenderDynamicLightmaps (s); + + + for (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++) + { + if (chain && s->texinfo->texture != chain->texinfo->texture) //last surface or not the same as the next + { + PPL_BaseTextureChain(chain); + chain = NULL; + } + + s->texturechain = chain; + chain = s; + } + + if (chain) + PPL_BaseTextureChain(chain); + + glPopMatrix(); + GL_DisableMultitexture(); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void PPL_BaseEntTextures(void) +{ + extern model_t *currentmodel; + int i; + + if (!r_drawentities.value) + return; + + // draw sprites seperately, because of alpha blending + for (i=0 ; ikeynum == cl.viewentity[r_refdef.currentplayernum]) + continue; + + if (!currententity->model) + continue; + + + if (cls.allow_anyparticles || currententity->visframe) //allowed or static + { + if (currententity->model->particleeffect>=0) + { + if (currententity->model->particleengulphs) + { + if (gl_part_flame.value) + { //particle effect is addedin GLR_DrawEntitiesOnList. Is this so wrong? + continue; + } + } + } + } + + switch (currententity->model->type) + { + case mod_alias: + R_DrawGAliasModel (currententity); + break; + + case mod_brush: + PPL_BaseBModelTextures (currententity); + break; + + default: + break; + } + } + + currentmodel = cl.worldmodel; +} + +void PPL_LightTextures(model_t *model, vec3_t modelorigin, dlight_t *light) +{ + int i; + msurface_t *s; + texture_t *t; + extern cvar_t gl_bump; + + int vi; + glpoly_t *p; + float *v; + float dist; + + if (gl_bump.value) + { + vec3_t relativelightorigin; + + VectorSubtract(light->origin, modelorigin, relativelightorigin); + glShadeModel(GL_SMOOTH); + for (i=0 ; inumtextures ; i++) + { + t = model->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + + + { + extern int normalisationCubeMap; + vec3_t lightdir; + + t = GLR_TextureAnimation (t); + + + if (t->gl_texturenumbumpmap) + { + qglActiveTextureARB(GL_TEXTURE0_ARB); + GL_BindType(GL_TEXTURE_2D, t->gl_texturenumbumpmap); + glEnable(GL_TEXTURE_2D); + //Set up texture environment to do (tex0 dot tex1)*color + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); //make texture normalmap available. + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); + + qglActiveTextureARB(GL_TEXTURE1_ARB); + GL_BindType(GL_TEXTURE_CUBE_MAP_ARB, normalisationCubeMap); + glEnable(GL_TEXTURE_CUBE_MAP_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); //normalisation cubemap * normalmap + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB); + + qglActiveTextureARB(GL_TEXTURE2_ARB); + GL_BindType(GL_TEXTURE_2D, t->gl_texturenumbumpmap); + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); //bumps * color (the attenuation) + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB); //(doesn't actually use the bound texture) + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + } + else + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDisable(GL_TEXTURE_2D); + qglActiveTextureARB(GL_TEXTURE1_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDisable(GL_TEXTURE_CUBE_MAP_ARB); + qglActiveTextureARB(GL_TEXTURE0_ARB); + } + + for (; s; s=s->texturechain) + { + + /* if (fabs(s->center[0] - lightorg[0]) > lightradius+s->radius || + fabs(s->center[1] - lightorg[1]) > lightradius+s->radius || + fabs(s->center[2] - lightorg[2]) > lightradius+s->radius) + continue;*/ + + + if (s->flags & SURF_PLANEBACK) + {//inverted normal. + if (-DotProduct(s->plane->normal, relativelightorigin)+s->plane->dist > lightradius) + continue; + } + else + { + if (DotProduct(s->plane->normal, relativelightorigin)-s->plane->dist > lightradius) + continue; + } + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + lightdir[0] = relativelightorigin[0] - v[0]; + lightdir[1] = relativelightorigin[1] - v[1]; + lightdir[2] = relativelightorigin[2] - v[2]; + + dist = 1-(sqrt( (lightdir[0])*(lightdir[0]) + + (lightdir[1])*(lightdir[1]) + + (lightdir[2])*(lightdir[2])) / light->radius); + + VectorNormalize(lightdir); + + glColor3f(light->color[0]*dist, light->color[1]*dist, light->color[2]*dist); + qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]); + qglMultiTexCoord3fARB(GL_TEXTURE1_ARB, DotProduct(lightdir, s->texinfo->vecs[0]), -DotProduct(lightdir, s->texinfo->vecs[1]), DotProduct(lightdir, s->normal)); + glVertex3fv (v); + } + glEnd (); + } + } + } + } + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDisable(GL_TEXTURE_2D); + qglActiveTextureARB(GL_TEXTURE1_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDisable(GL_TEXTURE_CUBE_MAP_ARB); + qglActiveTextureARB(GL_TEXTURE0_ARB); + } + else + { + vec3_t relativelightorigin; + vec3_t lightdir; + + glDisable(GL_TEXTURE_2D); + + VectorSubtract(light->origin, modelorigin, relativelightorigin); + + glShadeModel(GL_SMOOTH); + for (i=0 ; inumtextures ; i++) + { + t = model->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + + + + { +// t = GLR_TextureAnimation (t); + + +// GL_Bind (t->gl_texturenum); + for (; s; s=s->texturechain) + { + + /* if (fabs(s->center[0] - lightorg[0]) > lightradius+s->radius || + fabs(s->center[1] - lightorg[1]) > lightradius+s->radius || + fabs(s->center[2] - lightorg[2]) > lightradius+s->radius) + continue;*/ + + + if (s->flags & SURF_PLANEBACK) + {//inverted normal. + if (DotProduct(s->plane->normal, lightorg)-s->plane->dist <= -lightradius) + continue; + } + else + { + if (DotProduct(s->plane->normal, lightorg)-s->plane->dist >= lightradius) + continue; + } + + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + lightdir[0] = relativelightorigin[0] - v[0]; + lightdir[1] = relativelightorigin[1] - v[1]; + lightdir[2] = relativelightorigin[2] - v[2]; + + dist = 1-(sqrt( (lightdir[0])*(lightdir[0]) + + (lightdir[1])*(lightdir[1]) + + (lightdir[2])*(lightdir[2])) / light->radius); + + VectorNormalize(lightdir); + + glColor3f(light->color[0]*dist, light->color[1]*dist, light->color[2]*dist); +// glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); + } + } + } + } + } +} +void PPL_LightBModelTextures(entity_t *e, dlight_t *light) +{ + glpoly_t *p; + + int i; + model_t *model = e->model; + + msurface_t *s; + texture_t *t; + extern cvar_t gl_bump; + + int vi; + float *v; + float dist; + + glPushMatrix(); + R_RotateForEntity(e); + glColor4f(1, 1, 1, 1); + + if (gl_bump.value) + { + vec3_t relativelightorigin; + + VectorSubtract(light->origin, e->origin, relativelightorigin); + glShadeModel(GL_SMOOTH); + + for (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++) + { + t = GLR_TextureAnimation (s->texinfo->texture); + + { + extern int normalisationCubeMap; + vec3_t lightdir; + + if (t->gl_texturenumbumpmap) + { + qglActiveTextureARB(GL_TEXTURE0_ARB); + GL_BindType(GL_TEXTURE_2D, t->gl_texturenumbumpmap); + glEnable(GL_TEXTURE_2D); + //Set up texture environment to do (tex0 dot tex1)*color + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); //make texture normalmap available. + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); + + qglActiveTextureARB(GL_TEXTURE1_ARB); + GL_BindType(GL_TEXTURE_CUBE_MAP_ARB, normalisationCubeMap); + glEnable(GL_TEXTURE_CUBE_MAP_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); //normalisation cubemap * normalmap + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB); + + qglActiveTextureARB(GL_TEXTURE2_ARB); + GL_BindType(GL_TEXTURE_2D, t->gl_texturenumbumpmap); + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); //bumps * color + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB); //(doesn't actually use the bound texture) + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + } + else + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDisable(GL_TEXTURE_2D); + qglActiveTextureARB(GL_TEXTURE1_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDisable(GL_TEXTURE_CUBE_MAP_ARB); + qglActiveTextureARB(GL_TEXTURE0_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + + { +/* + if (fabs(s->center[0] - lightorg[0]) > lightradius+s->radius || + fabs(s->center[1] - lightorg[1]) > lightradius+s->radius || + fabs(s->center[2] - lightorg[2]) > lightradius+s->radius) + continue; +*/ + + if (s->flags & SURF_PLANEBACK) + {//inverted normal. + if (-DotProduct(s->plane->normal, relativelightorigin)-s->plane->dist >= lightradius) + continue; + } + else + { + if (DotProduct(s->plane->normal, relativelightorigin)-s->plane->dist >= lightradius) + continue; + } + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + lightdir[0] = relativelightorigin[0] - v[0]; + lightdir[1] = relativelightorigin[1] - v[1]; + lightdir[2] = relativelightorigin[2] - v[2]; + + dist = 1-(sqrt( (lightdir[0])*(lightdir[0]) + + (lightdir[1])*(lightdir[1]) + + (lightdir[2])*(lightdir[2])) / light->radius); + + VectorNormalize(lightdir); + + glColor3f(light->color[0]*dist, light->color[1]*dist, light->color[2]*dist); + qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]); + qglMultiTexCoord3fARB(GL_TEXTURE1_ARB, DotProduct(lightdir, s->texinfo->vecs[0]), -DotProduct(lightdir, s->texinfo->vecs[1]), DotProduct(lightdir, s->normal)); + glVertex3fv (v); + } + glEnd (); + } + } + } + } + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDisable(GL_TEXTURE_2D); + qglActiveTextureARB(GL_TEXTURE1_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDisable(GL_TEXTURE_CUBE_MAP_ARB); + qglActiveTextureARB(GL_TEXTURE0_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + else + { + vec3_t relativelightorigin; + vec3_t lightdir; + + glDisable(GL_TEXTURE_2D); + + VectorSubtract(light->origin, e->origin, relativelightorigin); + + glShadeModel(GL_SMOOTH); + for (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++) + { + { + { + + /* if (fabs(s->center[0] - lightorg[0]) > lightradius+s->radius || + fabs(s->center[1] - lightorg[1]) > lightradius+s->radius || + fabs(s->center[2] - lightorg[2]) > lightradius+s->radius) + continue;*/ + + + if (s->flags & SURF_PLANEBACK) + {//inverted normal. + if (DotProduct(s->plane->normal, lightorg)-s->plane->dist <= -lightradius) + continue; + } + else + { + if (DotProduct(s->plane->normal, lightorg)-s->plane->dist >= lightradius) + continue; + } + + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + lightdir[0] = relativelightorigin[0] - v[0]; + lightdir[1] = relativelightorigin[1] - v[1]; + lightdir[2] = relativelightorigin[2] - v[2]; + + dist = 1-(sqrt( (lightdir[0])*(lightdir[0]) + + (lightdir[1])*(lightdir[1]) + + (lightdir[2])*(lightdir[2])) / light->radius); + + VectorNormalize(lightdir); + + glColor3f(light->color[0]*dist, light->color[1]*dist, light->color[2]*dist); +// glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); + } + } + } + } + } + + + + + glPopMatrix(); +} + +//draw the bumps on the models for each light. +void PPL_DrawEntLighting(dlight_t *light) +{ + int i; + + PPL_LightTextures(cl.worldmodel, r_worldentity.origin, light); + + if (!r_drawentities.value) + return; + + for (i=0 ; ikeynum == cl.viewentity[r_refdef.currentplayernum]) + continue; + + if (!currententity->model) + continue; + + if (cls.allow_anyparticles || currententity->visframe) //allowed or static + { + if (currententity->model->particleeffect>=0) + { + if (currententity->model->particleengulphs) + { + if (gl_part_flame.value) + { + continue; + } + } + } + } + + switch (currententity->model->type) + { + case mod_alias: +// R_DrawGAliasModelLighting (currententity); + break; + + case mod_brush: + PPL_LightBModelTextures (currententity, light); + break; + + default: + break; + } + } +} + +void PPL_FullBrights(model_t *model) +{ + int tn; + msurface_t *s; + texture_t *t; + + glColor3f(1,1,1); + + glDepthMask(0); //don't bother writing depth + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glShadeModel(GL_FLAT); + + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + + for (tn=0 ; tnnumtextures ; tn++) + { + t = model->textures[tn]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + + PPL_FullBrightTextureChain(s); + + t->texturechain=NULL; + } + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDepthMask(1); +} + +void PPL_FullBrightBModelTextures(entity_t *e) +{ + int i; + model_t *model; + msurface_t *s; + msurface_t *chain = NULL; + + glPushMatrix(); + R_RotateForEntity(e); + currentmodel = model = e->model; + s = model->surfaces+model->firstmodelsurface; + + glColor4f(1, 1, 1, 1); + glDepthMask(0); //don't bother writing depth + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glShadeModel(GL_FLAT); + + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + + for (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++) + { + if (chain && s->texinfo->texture != chain->texinfo->texture) //last surface or not the same as the next + { + PPL_FullBrightTextureChain(chain); + chain = NULL; + } + + s->texturechain = chain; + chain = s; + } + + if (chain) + PPL_FullBrightTextureChain(chain); + + glPopMatrix(); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDepthMask(1); +} + +//draw the bumps on the models for each light. +void PPL_DrawEntFullBrights(void) +{ + int i; + + PPL_FullBrights(cl.worldmodel); + + if (!r_drawentities.value) + return; + + for (i=0 ; ikeynum == cl.viewentity[r_refdef.currentplayernum]) + continue; + + if (!currententity->model) + continue; + + if (cls.allow_anyparticles || currententity->visframe) //allowed or static + { + if (currententity->model->particleeffect>=0) + { + if (currententity->model->particleengulphs) + { + if (gl_part_flame.value) + { + continue; + } + } + } + } + + switch (currententity->model->type) + { + case mod_alias: +// R_DrawGAliasModelLighting (currententity); + break; + + case mod_brush: + PPL_FullBrightBModelTextures (currententity); + break; + + default: + break; + } + } +} + + + + + + + + + + + + + + + + + +qboolean PPL_VisOverlaps(qbyte *v1, qbyte *v2) +{ + int i, m; + m = (cl.worldmodel->numleafs-1)>>3; + for (i=0 ; ishadowframe != r_shadowframe) + return; + + if (node->contents == Q1CONTENTS_SOLID) + return; // solid + +// if (R_CullBox (node->minmaxs, node->minmaxs+3)) +// return; + +// if a leaf node, draw stuff + if (node->contents < 0) + { + pleaf = (mleaf_t *)node; + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + (*mark++)->shadowframe = r_shadowframe; + } while (--c); + } + return; + } + +// node is just a decision point, so go down the apropriate sides + +// find which side of the node we are on + plane = node->plane; + + switch (plane->type) + { + case PLANE_X: + dot = modelorg[0] - plane->dist; + break; + case PLANE_Y: + dot = modelorg[1] - plane->dist; + break; + case PLANE_Z: + dot = modelorg[2] - plane->dist; + break; + default: + dot = DotProduct (modelorg, plane->normal) - plane->dist; + break; + } + + if (dot >= 0) + side = 0; + else + side = 1; + +// recurse down the children, front side first + PPL_RecursiveWorldNode_r (node->children[side]); + +// draw stuff + c = node->numsurfaces; + + if (c) + { + surf = cl.worldmodel->surfaces + node->firstsurface; + + { + for ( ; c ; c--, surf++) + { + if (surf->shadowframe != r_shadowframe) + continue; + +// if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) +// continue; // wrong side + +// if (surf->flags & SURF_PLANEBACK) +// continue; + + if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED)) + { // no shadows + continue; + } + + //is the light on the right side? + if (surf->flags & SURF_PLANEBACK) + {//inverted normal. + if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist <= -lightradius) + continue; + } + else + { + if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist >= lightradius) + continue; + } + if (fabs(surf->center[0] - lightorg[0]) > lightradius+surf->radius || + fabs(surf->center[1] - lightorg[1]) > lightradius+surf->radius || + fabs(surf->center[2] - lightorg[2]) > lightradius+surf->radius) + continue; + +#define PROJECTION_DISTANCE (float)0x7fffffff +#ifdef EDGEOPTIMISE + //build a list of the edges that are to be drawn. + for (v = 0; v < surf->numedges; v++) + { + int e, delta; + e = cl.worldmodel->surfedges[surf->firstedge+v]; + //negative edge means backwards edge. + if (e < 0) + { + e=-e; + delta = -1; + } + else + { + delta = 1; + } + + if (!edge[e].count) + { + if (firstedge) + edge[firstedge].prev = e; + edge[e].next = firstedge; + edge[e].prev = 0; + firstedge = e; + edge[e].count = delta; + } + else + { + edge[e].count += delta; + + if (!edge[e].count) //unlink + { + if (edge[e].next) + { + edge[edge[e].next].prev = edge[e].prev; + } + if (edge[e].prev) + edge[edge[e].prev].next = edge[e].next; + else + firstedge = edge[e].next; + } + } + } +#endif + for (p = surf->polys; p; p=p->next) + { + //front face + glBegin(GL_POLYGON); + for (v = 0; v < p->numverts; v++) + glVertex3fv(p->verts[v]); + glEnd(); + +#ifndef EDGEOPTIMISE + for (v = 0; v < p->numverts; v++) + { + //border + v1 = p->verts[v]; + v2 = p->verts[( v+1 )%p->numverts]; + + //get positions of v3 and v4 based on the light position + v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE; + v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE; + v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE; + + v4[0] = ( v2[0]-lightorg[0] )*PROJECTION_DISTANCE; + v4[1] = ( v2[1]-lightorg[1] )*PROJECTION_DISTANCE; + v4[2] = ( v2[2]-lightorg[2] )*PROJECTION_DISTANCE; + + //Now draw the quad from the two verts to the projected light + //verts + glBegin( GL_QUADS ); + glVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] ); + glVertex3f( v2[0]+v4[0], v2[1]+v4[1], v2[2]+v4[2] ); + glVertex3f( v2[0], v2[1], v2[2] ); + glVertex3f( v1[0], v1[1], v1[2] ); + glEnd(); + } +#endif +//back + + glBegin(GL_POLYGON); + for (v = p->numverts-1; v >=0; v--) + { + v1 = p->verts[v]; + v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE; + v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE; + v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE; + + glVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] ); + } + glEnd(); + + } + } + } + } + +// recurse down the back side + PPL_RecursiveWorldNode_r (node->children[!side]); +} + +//2 changes, but otherwise the same +void PPL_RecursiveWorldNodeQ2_r (mnode_t *node) +{ + int c, side; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double dot; + glpoly_t *p; + int v; +#ifndef EDGEOPTIMISE + float *v2; + vec3_t v4; +#endif + float *v1; + vec3_t v3; + + if (node->contents == Q2CONTENTS_SOLID) + return; // solid + + if (node->shadowframe != r_shadowframe) + return; +// if (R_CullBox (node->minmaxs, node->minmaxs+3)) +// return; + +// if a leaf node, draw stuff + if (node->contents != -1) + { + pleaf = (mleaf_t *)node; + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + (*mark++)->shadowframe = r_shadowframe; + } while (--c); + } + return; + } + +// node is just a decision point, so go down the apropriate sides + +// find which side of the node we are on + plane = node->plane; + + switch (plane->type) + { + case PLANE_X: + dot = modelorg[0] - plane->dist; + break; + case PLANE_Y: + dot = modelorg[1] - plane->dist; + break; + case PLANE_Z: + dot = modelorg[2] - plane->dist; + break; + default: + dot = DotProduct (modelorg, plane->normal) - plane->dist; + break; + } + + if (dot >= 0) + side = 0; + else + side = 1; + +// recurse down the children, front side first + PPL_RecursiveWorldNodeQ2_r (node->children[side]); + +// draw stuff + c = node->numsurfaces; + + if (c) + { + surf = cl.worldmodel->surfaces + node->firstsurface; + + { + for ( ; c ; c--, surf++) + { + if (surf->shadowframe != r_shadowframe) + continue; + +// if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) +// continue; // wrong side + +// if (surf->flags & SURF_PLANEBACK) +// continue; + + if (surf->flags & SURF_PLANEBACK) + {//inverted normal. + if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist >= 0) + continue; + } + else + { + if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist <= 0) + continue; + } +#define PROJECTION_DISTANCE (float)0x7fffffff + if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED)) + { // no shadows + continue; + } + +#ifdef EDGEOPTIMISE + //build a list of the edges that are to be drawn. + for (v = 0; v < surf->numedges; v++) + { + int e, delta; + e = cl.worldmodel->surfedges[surf->firstedge+v]; + //negative edge means backwards edge. + if (e < 0) + { + e=-e; + delta = -1; + } + else + { + delta = 1; + } + + if (!edge[e].count) + { + if (firstedge) + edge[firstedge].prev = e; + edge[e].next = firstedge; + edge[e].prev = 0; + firstedge = e; + edge[e].count = delta; + } + else + { + edge[e].count += delta; + + if (!edge[e].count) //unlink + { + if (edge[e].next) + { + edge[edge[e].next].prev = edge[e].prev; + } + if (edge[e].prev) + edge[edge[e].prev].next = edge[e].next; + else + firstedge = edge[e].next; + } + } + } +#endif + + for (p = surf->polys; p; p=p->next) + { + //front face + glBegin(GL_POLYGON); + for (v = 0; v < p->numverts; v++) + glVertex3fv(p->verts[v]); + glEnd(); +#ifndef EDGEOPTIMISE + for (v = 0; v < p->numverts; v++) + { + //border + v1 = p->verts[v]; + v2 = p->verts[( v+1 )%p->numverts]; + + //get positions of v3 and v4 based on the light position + v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE; + v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE; + v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE; + + v4[0] = ( v2[0]-lightorg[0] )*PROJECTION_DISTANCE; + v4[1] = ( v2[1]-lightorg[1] )*PROJECTION_DISTANCE; + v4[2] = ( v2[2]-lightorg[2] )*PROJECTION_DISTANCE; + + //Now draw the quad from the two verts to the projected light + //verts + glBegin( GL_QUAD_STRIP ); + glVertex3f( v1[0], v1[1], v1[2] ); + glVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] ); + glVertex3f( v2[0], v2[1], v2[2] ); + glVertex3f( v2[0]+v4[0], v2[1]+v4[1], v2[2]+v4[2] ); + glEnd(); + } +#endif +//back + glBegin(GL_POLYGON); + for (v = p->numverts-1; v >=0; v--) + { + v1 = p->verts[v]; + v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE; + v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE; + v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE; + + glVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] ); + } + glEnd(); + + } + } + } + } + +// recurse down the back side + PPL_RecursiveWorldNodeQ2_r (node->children[!side]); +} + +void PPL_RecursiveWorldNodeQ3_r (mnode_t *node) +{ + int c, side; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double dot; + glpoly_t *p; + int v; +//#ifndef EDGEOPTIMISE + float *v2; + vec3_t v4; +//#endif + float *v1; + vec3_t v3; + + if (node->contents == Q2CONTENTS_SOLID) + return; // solid + + if (node->shadowframe != r_shadowframe) + return; +// if (R_CullBox (node->minmaxs, node->minmaxs+3)) +// return; + +// if a leaf node, draw stuff + if (node->contents != -1) + { + pleaf = (mleaf_t *)node; + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + surf = *mark; + (*mark++)->shadowframe = r_shadowframe; + +/* if (surf->shadowframe != r_shadowframe) + continue; +*/ +// if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) +// continue; // wrong side + +// if (surf->flags & SURF_PLANEBACK) +// continue; + + if (surf->flags & SURF_PLANEBACK) + {//inverted normal. + if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist <= -lightradius) + continue; + } + else + { + if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist >= lightradius) + continue; + } +#define PROJECTION_DISTANCE (float)0x7fffffff + /*if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED)) + { // no shadows + continue; + }*/ + +/*#ifdef EDGEOPTIMISE + //build a list of the edges that are to be drawn. + for (v = 0; v < surf->numedges; v++) + { + int e, delta; + e = cl.worldmodel->surfedges[surf->firstedge+v]; + //negative edge means backwards edge. + if (e < 0) + { + e=-e; + delta = -1; + } + else + { + delta = 1; + } + + if (!edge[e].count) + { + if (firstedge) + edge[firstedge].prev = e; + edge[e].next = firstedge; + edge[e].prev = 0; + firstedge = e; + edge[e].count = delta; + } + else + { + edge[e].count += delta; + + if (!edge[e].count) //unlink + { + if (edge[e].next) + { + edge[edge[e].next].prev = edge[e].prev; + } + if (edge[e].prev) + edge[edge[e].prev].next = edge[e].next; + else + firstedge = edge[e].next; + } + } + } +#endif*/ + + for (p = surf->polys; p; p=p->next) + { + //front face + glBegin(GL_POLYGON); + for (v = 0; v < p->numverts; v++) + glVertex3fv(p->verts[v]); + glEnd(); +//#ifndef EDGEOPTIMISE + for (v = 0; v < p->numverts; v++) + { + //border + v1 = p->verts[v]; + v2 = p->verts[( v+1 )%p->numverts]; + + //get positions of v3 and v4 based on the light position + v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE; + v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE; + v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE; + + v4[0] = ( v2[0]-lightorg[0] )*PROJECTION_DISTANCE; + v4[1] = ( v2[1]-lightorg[1] )*PROJECTION_DISTANCE; + v4[2] = ( v2[2]-lightorg[2] )*PROJECTION_DISTANCE; + + //Now draw the quad from the two verts to the projected light + //verts + glBegin( GL_QUAD_STRIP ); + glVertex3f( v1[0], v1[1], v1[2] ); + glVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] ); + glVertex3f( v2[0], v2[1], v2[2] ); + glVertex3f( v2[0]+v4[0], v2[1]+v4[1], v2[2]+v4[2] ); + glEnd(); + } +//#endif +//back + glBegin(GL_POLYGON); + for (v = p->numverts-1; v >=0; v--) + { + v1 = p->verts[v]; + v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE; + v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE; + v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE; + + glVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] ); + } + glEnd(); + + } + } while (--c); + } + return; + } + +// node is just a decision point, so go down the apropriate sides + +// find which side of the node we are on + plane = node->plane; + + switch (plane->type) + { + case PLANE_X: + dot = modelorg[0] - plane->dist; + break; + case PLANE_Y: + dot = modelorg[1] - plane->dist; + break; + case PLANE_Z: + dot = modelorg[2] - plane->dist; + break; + default: + dot = DotProduct (modelorg, plane->normal) - plane->dist; + break; + } + + if (dot >= 0) + side = 0; + else + side = 1; + +// recurse down the children, front side first + PPL_RecursiveWorldNodeQ3_r (node->children[side]); + +// draw stuff + c = node->numsurfaces; + + if (c) + { + surf = cl.worldmodel->surfaces + node->firstsurface; + + { + for ( ; c ; c--, surf++) + { + + } + } + } + +// recurse down the back side + PPL_RecursiveWorldNodeQ3_r (node->children[!side]); +} + +void PPL_RecursiveWorldNode (dlight_t *dl) +{ + float *v1, *v2; + vec3_t v3, v4; + + lightradius = dl->radius; + + lightorg[0] = dl->origin[0]+0.5; + lightorg[1] = dl->origin[1]+0.5; + lightorg[2] = dl->origin[2]+0.5; + + modelorg[0] = lightorg[0]; + modelorg[1] = lightorg[1]; + modelorg[2] = lightorg[2]; + + if (cl.worldmodel->fromgame == fg_quake3) + PPL_RecursiveWorldNodeQ3_r(cl.worldmodel->nodes); + else if (cl.worldmodel->fromgame == fg_quake2) + PPL_RecursiveWorldNodeQ2_r(cl.worldmodel->nodes); + else + PPL_RecursiveWorldNode_r(cl.worldmodel->nodes); + +#ifdef EDGEOPTIMISE + glBegin( GL_QUADS ); + while(firstedge) +// for (firstedge = 0; firstedge < cl.worldmodel->numedges; firstedge++) + { + //border + v1 = cl.worldmodel->vertexes[cl.worldmodel->edges[firstedge].v[0]].position; + v2 = cl.worldmodel->vertexes[cl.worldmodel->edges[firstedge].v[1]].position; + + //get positions of v3 and v4 based on the light position + v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE; + v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE; + v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE; + + v4[0] = ( v2[0]-lightorg[0] )*PROJECTION_DISTANCE; + v4[1] = ( v2[1]-lightorg[1] )*PROJECTION_DISTANCE; + v4[2] = ( v2[2]-lightorg[2] )*PROJECTION_DISTANCE; + + //Now draw the quad from the two verts to the projected light + //verts + while (edge[firstedge].count > 0) + { + glVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] ); + glVertex3f( v2[0]+v4[0], v2[1]+v4[1], v2[2]+v4[2] ); + glVertex3f( v2[0], v2[1], v2[2] ); + glVertex3f( v1[0], v1[1], v1[2] ); + edge[firstedge].count--; + } + while (edge[firstedge].count < 0) + { + glVertex3f( v1[0], v1[1], v1[2] ); + glVertex3f( v2[0], v2[1], v2[2] ); + glVertex3f( v2[0]+v4[0], v2[1]+v4[1], v2[2]+v4[2] ); + glVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] ); + edge[firstedge].count++; + } + + firstedge = edge[firstedge].next; + } + glEnd(); + for (firstedge = 0; firstedge < cl.worldmodel->numedges; firstedge++) + edge[firstedge].count = 0; + firstedge=0; +#endif +} + +void PPL_DrawBrushModel(dlight_t *dl, entity_t *e) +{ + glpoly_t *p; + int v; + float *v1, *v2; + vec3_t v3, v4; + + int i; + model_t *model; + msurface_t *surf; + + RotateLightVector(e->angles, e->origin, dl->origin, lightorg); + + glPushMatrix(); + R_RotateForEntity(e); + model = e->model; + surf = model->surfaces+model->firstmodelsurface; + for (i = 0; i < model->nummodelsurfaces; i++, surf++) + { + if (surf->flags & SURF_PLANEBACK) + {//inverted normal. + if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist >= -0.1) + continue; + } + else + { + if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist <= 0.1) + continue; + } +#define PROJECTION_DISTANCE (float)0x7fffffff + if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED)) + { // no shadows + continue; + } + + for (p = surf->polys; p; p=p->next) + { + //front face + glBegin(GL_POLYGON); + for (v = 0; v < p->numverts; v++) + glVertex3fv(p->verts[v]); + glEnd(); + + for (v = 0; v < p->numverts; v++) + { + //border + v1 = p->verts[v]; + v2 = p->verts[( v+1 )%p->numverts]; + + //get positions of v3 and v4 based on the light position + v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE; + v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE; + v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE; + + v4[0] = ( v2[0]-lightorg[0] )*PROJECTION_DISTANCE; + v4[1] = ( v2[1]-lightorg[1] )*PROJECTION_DISTANCE; + v4[2] = ( v2[2]-lightorg[2] )*PROJECTION_DISTANCE; + + //Now draw the quad from the two verts to the projected light + //verts + glBegin( GL_QUAD_STRIP ); + glVertex3f( v1[0], v1[1], v1[2] ); + glVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] ); + glVertex3f( v2[0], v2[1], v2[2] ); + glVertex3f( v2[0]+v4[0], v2[1]+v4[1], v2[2]+v4[2] ); + glEnd(); + } + +//back + + glBegin(GL_POLYGON); + for (v = p->numverts-1; v >=0; v--) + { + v1 = p->verts[v]; + v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE; + v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE; + v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE; + + glVertex3f( v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2] ); + } + glEnd(); + + } + } + glPopMatrix(); +} + +void PPL_DrawShadowMeshes(dlight_t *dl) +{ + int i; + + if (!r_drawentities.value) + return; + + // draw sprites seperately, because of alpha blending + for (i=0 ; ikeynum == cl.viewentity[r_refdef.currentplayernum]) + continue; + + if (!currententity->model) + continue; + + if (dl->key == currententity->keynum) + continue; + + if (currententity->flags & Q2RF_WEAPONMODEL) + continue; //weapon models don't cast shadows. + + if (cls.allow_anyparticles || currententity->visframe) //allowed or static + { + if (currententity->model->particleeffect>=0) + { + if (currententity->model->particleengulphs) + { + if (gl_part_flame.value) + { + continue; + } + } + } + } + + switch (currententity->model->type) + { + case mod_alias: + R_DrawGAliasShadowVolume (currententity, dl->origin, dl->radius); + break; + + case mod_brush: + PPL_DrawBrushModel (dl, currententity); + break; + + default: + break; + } + } +} + +void CL_NewDlight (int key, float x, float y, float z, float radius, float time, + int type); +//generates stencil shadows of the world geometry. +//redraws world geometry +void PPL_AddLight(dlight_t *dl) +{ + int i; + int sdecrw; + int sincrw; + mnode_t *node; + int leaf; + qbyte *lvis; + qbyte *vvis; + + qbyte lvisb[MAX_MAP_LEAFS/8]; + qbyte vvisb[MAX_MAP_LEAFS/8]; + + vec3_t mins; + vec3_t maxs; + + mins[0] = dl->origin[0] - dl->radius; + mins[1] = dl->origin[1] - dl->radius; + mins[2] = dl->origin[2] - dl->radius; + + maxs[0] = dl->origin[0] + dl->radius; + maxs[1] = dl->origin[1] + dl->radius; + maxs[2] = dl->origin[2] + dl->radius; + + if (R_CullBox(mins, maxs)) + return; + + if (cl.worldmodel->fromgame == fg_quake3) + i = cl.worldmodel->funcs.LeafForPoint(r_refdef.vieworg, cl.worldmodel); + else + i = r_viewleaf - cl.worldmodel->leafs; + + leaf = cl.worldmodel->funcs.LeafForPoint(dl->origin, cl.worldmodel); + lvis = cl.worldmodel->funcs.LeafPVS(leaf, cl.worldmodel, lvisb); + vvis = cl.worldmodel->funcs.LeafPVS(i, cl.worldmodel, vvisb); + +// if (!(lvis[i>>3] & (1<<(i&7)))) //light might not be visible, but it's effects probably should be. +// return; + if (!PPL_VisOverlaps(lvis, vvis)) //The two viewing areas do not intersect. + return; + +#ifdef Q3BSPS + if (cl.worldmodel->fromgame == fg_quake3) + { + mleaf_t *leaf; + r_shadowframe++; + for (i=0, leaf=cl.worldmodel->leafs; inumleafs ; i++, leaf++) + { + node = (mnode_t *)leaf; + while (node) + { + if (node->shadowframe == r_shadowframe) + break; + node->shadowframe = r_shadowframe; + node = node->parent; + } + } + } + else +#endif +#ifdef Q2BSPS + if (cl.worldmodel->fromgame == fg_quake2) + { + mleaf_t *leaf; + int cluster; + r_shadowframe++; + + for (i=0, leaf=cl.worldmodel->leafs; inumleafs ; i++, leaf++) + { + cluster = leaf->cluster; + if (cluster == -1) + continue; + if (lvis[cluster>>3] & (1<<(cluster&7))) + { + node = (mnode_t *)leaf; + do + { + if (node->shadowframe == r_shadowframe) + break; + node->shadowframe = r_shadowframe; + node = node->parent; + } while (node); + } + } + } + else +#endif + { + if (r_novis.value != 2) + { + r_shadowframe++; + + //variation on mark leaves + for (i=0 ; inumleafs ; i++) + { + if (lvis[i>>3] & (1<<(i&7)))// && vvis[i>>3] & (1<<(i&7))) + { + node = (mnode_t *)&cl.worldmodel->leafs[i+1]; + do + { + if (node->shadowframe == r_shadowframe) + break; + node->shadowframe = r_shadowframe; + node = node->parent; + } while (node); + } + } + } + } + glStencilFunc( GL_ALWAYS, 1, ~0 ); + + glDisable(GL_BLEND); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDisable(GL_TEXTURE_2D); + glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); + glDepthMask(0); + + if (gldepthfunc==GL_LEQUAL) + glDepthFunc(GL_LESS); + else + glDepthFunc(GL_GREATER); + glEnable(GL_DEPTH_TEST); + glEnable(GL_STENCIL_TEST); + + sincrw = GL_INCR; + sdecrw = GL_DECR; + if (gl_ext_stencil_wrap) + { //minamlise damage... + sincrw = GL_INCR_WRAP_EXT; + sdecrw = GL_DECR_WRAP_EXT; + } +//our stencil writes. + +#ifdef _DEBUG + if (r_shadows.value == 666) //testing (visible shadow volumes) + { + glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + glColor3f(dl->color[0], dl->color[1], dl->color[2]); + glDisable(GL_STENCIL_TEST); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + PPL_RecursiveWorldNode(dl); + PPL_DrawShadowMeshes(dl); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + else +#endif + + if (qglStencilOpSeparateATI && r_shadows.value != 667)//GL_ATI_separate_stencil + { + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + glDisable(GL_CULL_FACE); + + qglStencilOpSeparateATI(GL_BACK, GL_KEEP, sincrw, GL_KEEP); + qglStencilOpSeparateATI(GL_FRONT, GL_KEEP, sdecrw, GL_KEEP); + PPL_RecursiveWorldNode(dl); + PPL_DrawShadowMeshes(dl); + qglStencilOpSeparateATI(GL_FRONT_AND_BACK, GL_KEEP, GL_KEEP, GL_KEEP); + + glEnable(GL_CULL_FACE); + + glStencilFunc( GL_EQUAL, 0, ~0 ); + } + else if (qglActiveStencilFaceEXT && r_shadows.value != 667) //NVidias variation on a theme. (GFFX class) + { + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + glDisable(GL_CULL_FACE); + + glCullFace(GL_BACK); + qglActiveStencilFaceEXT(GL_BACK); + glStencilOp(GL_KEEP, sincrw, GL_KEEP); + + qglActiveStencilFaceEXT(GL_FRONT); + glStencilOp(GL_KEEP, sdecrw, GL_KEEP); + + PPL_RecursiveWorldNode(dl); + PPL_DrawShadowMeshes(dl); + + qglActiveStencilFaceEXT(GL_BACK); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + qglActiveStencilFaceEXT(GL_FRONT); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + glEnable(GL_CULL_FACE); + + glStencilFunc( GL_EQUAL, 0, ~0 ); + } + else //your graphics card sucks and lacks efficient stencil shadow techniques. + { //centered around 0. Will only be increased then decreased less. + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + + glCullFace(GL_BACK); + glStencilOp(GL_KEEP, sincrw, GL_KEEP); + PPL_RecursiveWorldNode(dl); + PPL_DrawShadowMeshes(dl); + + glCullFace(GL_FRONT); + glStencilOp(GL_KEEP, sdecrw, GL_KEEP); + PPL_RecursiveWorldNode(dl); + PPL_DrawShadowMeshes(dl); + + glStencilFunc( GL_EQUAL, 0, ~0 ); + } +//end stencil writing. + glEnable(GL_DEPTH_TEST); + glDepthMask(0); + glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); + glCullFace(GL_FRONT); + + glColor3f(1,1,1); + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + glColor4f(dl->color[0], dl->color[1], dl->color[2], 1); + glDepthFunc(GL_EQUAL); + + lightorg[0] = dl->origin[0]; + lightorg[1] = dl->origin[1]; + lightorg[2] = dl->origin[2]; + + PPL_DrawEntLighting(dl); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(1); + glDepthFunc(gldepthfunc); + glEnable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); +} + +void PPL_DrawWorld (void) +{ + dlight_t *l; + int i; + + int maxshadowlights = gl_maxshadowlights.value; + + if (maxshadowlights < 1) + maxshadowlights = 1; + + PPL_BaseTextures(cl.worldmodel); + PPL_BaseEntTextures(); +// CL_NewDlightRGB(1, r_refdef.vieworg[0], r_refdef.vieworg[1]-16, r_refdef.vieworg[2]-24, 128, 1, 1, 1, 1); + if (r_shadows.value && glStencilFunc) + { + if (cl.worldmodel->fromgame == fg_quake || cl.worldmodel->fromgame == fg_halflife || cl.worldmodel->fromgame == fg_quake2 /*|| cl.worldmodel->fromgame == fg_quake3*/) + { + for (l = cl_dlights, i=0 ; idie < cl.time || !l->radius || l->noppl) + continue; + if (!maxshadowlights--) + break; + l->color[0]*=2.5; + l->color[1]*=2.5; + l->color[2]*=2.5; + + PPL_AddLight(l); + l->color[0]/=2.5; + l->color[1]/=2.5; + l->color[2]/=2.5; + } + glEnable(GL_TEXTURE_2D); + } + } + PPL_DrawEntFullBrights(); +} +#endif diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c new file mode 100644 index 00000000..74ced627 --- /dev/null +++ b/engine/gl/gl_rlight.c @@ -0,0 +1,926 @@ +/* +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. + +*/ +// r_light.c + +#include "quakedef.h" +#include "glquake.h" + +int r_dlightframecount; + + +/* +================== +R_AnimateLight +================== +*/ +void GLR_AnimateLight (void) +{ + int i,j,k; + +// +// light animations +// 'm' is normal light, 'a' is no light, 'z' is double bright + i = (int)(cl.time*10); + for (j=0 ; j=0 ; i--) + { + a = i/16.0 * M_PI*2; + *bub_sin++ = sin(a); + *bub_cos++ = cos(a); + } +} + +void R_RenderDlight (dlight_t *light) +{ + int i, j; +// float a; + vec3_t v; + float rad; + float *bub_sin, *bub_cos; + + bub_sin = bubble_sintable; + bub_cos = bubble_costable; + rad = light->radius * 0.35; + + VectorSubtract (light->origin, r_origin, v); + if (Length (v) < rad) + { // view is inside the dlight + AddLightBlend (1, 0.5, 0, light->radius * 0.0003); + return; + } + + glBegin (GL_TRIANGLE_FAN); +// glColor3f (0.2,0.1,0.0); +// glColor3f (0.2,0.1,0.05); // changed dimlight effect + glColor4f (light->color[0]*2, light->color[1]*2, light->color[2]*2, + 1);//light->color[3]); + for (i=0 ; i<3 ; i++) + v[i] = light->origin[i] - vpn[i]*rad/1.5; + glVertex3fv (v); + glColor3f (0,0,0); + for (i=16 ; i>=0 ; i--) + { +// a = i/16.0 * M_PI*2; + for (j=0 ; j<3 ; j++) + v[j] = light->origin[j] + (vright[j]*(*bub_cos) + + + vup[j]*(*bub_sin)) * rad; + bub_sin++; + bub_cos++; + glVertex3fv (v); + } + glEnd (); +} + +/* +============= +R_RenderDlights +============= +*/ +void R_RenderDlights (void) +{ + int i; + dlight_t *l; + + if (!r_flashblend.value) + return; + +// r_dlightframecount = r_framecount + 1; // because the count hasn't + // advanced yet for this frame + glDepthMask (0); + glDisable (GL_TEXTURE_2D); + glShadeModel (GL_SMOOTH); + glEnable (GL_BLEND); + glBlendFunc (GL_ONE, GL_ONE); + + l = cl_dlights; + for (i=0 ; idie < cl.time || !l->radius) + continue; + R_RenderDlight (l); + } + + glColor3f (1,1,1); + glDisable (GL_BLEND); + glEnable (GL_TEXTURE_2D); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask (1); +} + + +/* +============================================================================= + +DYNAMIC LIGHTS + +============================================================================= +*/ + +/* +============= +R_MarkLights +============= +*/ +/*void GLR_MarkLights (dlight_t *light, int bit, mnode_t *node) +{ + mplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + + if (node->contents < 0) + return; + + splitplane = node->plane; + dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) + { + GLR_MarkLights (light, bit, node->children[0]); + return; + } + if (dist < -light->radius) + { + GLR_MarkLights (light, bit, node->children[1]); + return; + } + +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->dlightframe != r_dlightframecount) + { + surf->dlightbits = 0; + surf->dlightframe = r_dlightframecount; + } + surf->dlightbits |= bit; + } + + GLR_MarkLights (light, bit, node->children[0]); + GLR_MarkLights (light, bit, node->children[1]); +}*/ +/*void Q2BSP_MarkLights (dlight_t *light, int bit, mnode_t *node) +{ + mplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + + if (node->contents != -1) + return; + + splitplane = node->plane; + dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) + { + Q2BSP_MarkLights (light, bit, node->children[0]); + return; + } + if (dist < -light->radius) + { + Q2BSP_MarkLights (light, bit, node->children[1]); + return; + } + +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->dlightframe != r_dlightframecount) + { + surf->dlightbits = 0; + surf->dlightframe = r_dlightframecount; + } + surf->dlightbits |= bit; + } + + Q2BSP_MarkLights (light, bit, node->children[0]); + Q2BSP_MarkLights (light, bit, node->children[1]); +}*/ + +void GLR_MarkQ3Lights (dlight_t *light, int bit, mnode_t *node) +{ + mplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + + return; //we need to get the texinfos right first. + +/* + //mark all + for (surf = cl.worldmodel->surfaces, i = 0; i < cl.worldmodel->numsurfaces; i++, surf++) + { + if (surf->dlightframe != r_dlightframecount) + { + surf->dlightbits = 0; + surf->dlightframe = r_dlightframecount; + } + surf->dlightbits |= bit; + } + return; +*/ + if (node->contents != -1) + { + msurface_t **mark; + mleaf_t *leaf; + + // mark the polygons + leaf = (mleaf_t *)node; + mark = leaf->firstmarksurface; + for (i=0 ; inummarksurfaces ; i++, surf++) + { + surf = *mark++; + if (surf->dlightframe != r_dlightframecount) + { + surf->dlightbits = 0; + surf->dlightframe = r_dlightframecount; + } + surf->dlightbits |= bit; + } + + return; + } + + splitplane = node->plane; + dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) + { + GLR_MarkQ3Lights (light, bit, node->children[0]); + return; + } + if (dist < -light->radius) + { + GLR_MarkQ3Lights (light, bit, node->children[1]); + return; + } + + GLR_MarkQ3Lights (light, bit, node->children[0]); + GLR_MarkQ3Lights (light, bit, node->children[1]); +} + + + +/* +============= +R_PushDlights +============= +*/ +void GLR_PushDlights (void) +{ + int i; + dlight_t *l; + + r_dlightframecount = r_framecount + 1; // because the count hasn't + // advanced yet for this frame + + if (!r_dynamic.value) + return; + +// if (!cl.worldmodel->nodes) +// return; + + + l = cl_dlights; + for (i=0 ; idie < cl.time || !l->radius) + continue; + cl.worldmodel->funcs.MarkLights( l, 1<nodes ); + } + +/* + if (cl.worldmodel->fromgame == fg_quake3) + { + for (i=0 ; idie < cl.time || !l->radius) + continue; + GLR_MarkQ3Lights ( l, 1<nodes ); + } + return; + } + if (cl.worldmodel->fromgame == fg_quake2) + { + for (i=0 ; idie < cl.time || !l->radius) + continue; + GLR_MarkQ2Lights ( l, 1<nodes ); + } + return; + } + for (i=0 ; idie < cl.time || !l->radius) + continue; + GLR_MarkLights ( l, 1<nodes ); + }*/ +} + + +/* +============================================================================= + +LIGHT SAMPLING + +============================================================================= +*/ + +mplane_t *lightplane; +vec3_t lightspot; + +void GLQ3_LightGrid(vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir) +{ + q3lightgridinfo_t *lg = (q3lightgridinfo_t *)cl.worldmodel->lightgrid; + int index[4]; + int vi[3]; + int i, j; + float t[8], direction_uv[3]; + vec3_t vf, vf2; + vec3_t ambient, diffuse; + + res_dir[0] = 1; + res_dir[1] = 1; + res_dir[2] = 0.1; + + if (!lg) + { + res_ambient[0] = 255; + res_ambient[1] = 255; + res_ambient[2] = 255; + return; + } + + //If in doubt, steal someone else's code... + //Thanks QFusion. + + for ( i = 0; i < 3; i++ ) + { + vf[i] = (point[i] - lg->gridMins[i]) / lg->gridSize[i]; + vi[i] = (int)vf[i]; + vf[i] = vf[i] - floor(vf[i]); + vf2[i] = 1.0f - vf[i]; + } + + index[0] = vi[2]*lg->gridBounds[3] + vi[1]*lg->gridBounds[0] + vi[0]; + index[1] = index[0] + lg->gridBounds[0]; + index[2] = index[0] + lg->gridBounds[3]; + index[3] = index[2] + lg->gridBounds[0]; + + for ( i = 0; i < 4; i++ ) + { + if ( index[i] < 0 || index[i] >= (lg->numlightgridelems-1) ) + { + res_ambient[0] = 255; //out of the map + res_ambient[1] = 255; + res_ambient[2] = 255; + return; + } + } + + t[0] = vf2[0] * vf2[1] * vf2[2]; + t[1] = vf[0] * vf2[1] * vf2[2]; + t[2] = vf2[0] * vf[1] * vf2[2]; + t[3] = vf[0] * vf[1] * vf2[2]; + t[4] = vf2[0] * vf2[1] * vf[2]; + t[5] = vf[0] * vf2[1] * vf[2]; + t[6] = vf2[0] * vf[1] * vf[2]; + t[7] = vf[0] * vf[1] * vf[2]; + + for ( j = 0; j < 3; j++ ) + { + ambient[j] = 0; + diffuse[j] = 0; + + for ( i = 0; i < 4; i++ ) + { + ambient[j] += t[i*2] * lg->lightgrid[ index[i] ].ambient[j]; + ambient[j] += t[i*2+1] * lg->lightgrid[ index[i] + 1 ].ambient[j]; + + diffuse[j] += t[i*2] * lg->lightgrid[ index[i] ].diffuse[j]; + diffuse[j] += t[i*2+1] * lg->lightgrid[ index[i] + 1 ].diffuse[j]; + } + } + + for ( j = 0; j < 2; j++ ) + { + direction_uv[j] = 0; + + for ( i = 0; i < 4; i++ ) + { + direction_uv[j] += t[i*2] * lg->lightgrid[ index[i] ].direction[j]; + direction_uv[j] += t[i*2+1] * lg->lightgrid[ index[i] + 1 ].direction[j]; + } + + direction_uv[j] = anglemod ( direction_uv[j] ); + } + + VectorCopy(ambient, res_ambient); + if (res_diffuse) + VectorCopy(diffuse, res_diffuse); + if (res_dir) + { + vec3_t right, left; + direction_uv[2] = 0; + AngleVectors(direction_uv, res_dir, right, left); + } +} + +int GLRecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) +{ + int r; + float front, back, frac; + int side; + mplane_t *plane; + vec3_t mid; + msurface_t *surf; + int s, t, ds, dt; + int i; + mtexinfo_t *tex; + qbyte *lightmap; + unsigned scale; + int maps; + + if (cl.worldmodel->fromgame == fg_quake2) + { + if (node->contents != -1) + return -1; // solid + } + else if (node->contents < 0) + return -1; // didn't hit anything + +// calculate mid point + +// FIXME: optimize for axial + plane = node->plane; + front = DotProduct (start, plane->normal) - plane->dist; + back = DotProduct (end, plane->normal) - plane->dist; + side = front < 0; + + if ( (back < 0) == side) + return GLRecursiveLightPoint (node->children[side], start, end); + + frac = front / (front-back); + mid[0] = start[0] + (end[0] - start[0])*frac; + mid[1] = start[1] + (end[1] - start[1])*frac; + mid[2] = start[2] + (end[2] - start[2])*frac; + +// go down front side + r = GLRecursiveLightPoint (node->children[side], start, mid); + if (r >= 0) + return r; // hit something + + if ( (back < 0) == side ) + return -1; // didn't hit anuthing + +// check for impact on this node + VectorCopy (mid, lightspot); + lightplane = plane; + + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags & SURF_DRAWTILED) + continue; // no lightmaps + + tex = surf->texinfo; + + s = DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3]; + t = DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3];; + + if (s < surf->texturemins[0] || + t < surf->texturemins[1]) + continue; + + ds = s - surf->texturemins[0]; + dt = t - surf->texturemins[1]; + + if ( ds > surf->extents[0] || dt > surf->extents[1] ) + continue; + + if (!surf->samples) + return 0; + + ds >>= 4; + dt >>= 4; + + lightmap = surf->samples; + r = 0; + if (lightmap) + { + if (cl.worldmodel->rgblighting) + { + lightmap += (dt * ((surf->extents[0]>>4)+1) + ds)*3; + + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + r += (lightmap[0]+lightmap[1]+lightmap[2]) * scale / 3; + lightmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1)*3; + } + + } + else + { + lightmap += dt * ((surf->extents[0]>>4)+1) + ds; + + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + r += *lightmap * scale; + lightmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1); + } + } + + r >>= 8; + } + + return r; + } + +// go down back side + return GLRecursiveLightPoint (node->children[!side], mid, end); +} + + + +int GLR_LightPoint (vec3_t p) +{ + vec3_t end; + int r; + + if (r_refdef.flags & 1) + return 255; + + if (!cl.worldmodel || !cl.worldmodel->lightdata) + return 255; + + if (cl.worldmodel->fromgame == fg_quake3) + { + GLQ3_LightGrid(p, NULL, end, NULL); + return (end[0] + end[1] + end[2])/3; + } + + end[0] = p[0]; + end[1] = p[1]; + end[2] = p[2] - 2048; + + r = GLRecursiveLightPoint (cl.worldmodel->nodes, p, end); + + if (r == -1) + r = 0; + + return r; +} + + + +#ifdef PEXT_LIGHTSTYLECOL + +float *GLRecursiveLightPoint3C (mnode_t *node, vec3_t start, vec3_t end) +{ + static float l[6]; + float *r; + float front, back, frac; + int side; + mplane_t *plane; + vec3_t mid; + msurface_t *surf; + int s, t, ds, dt; + int i; + mtexinfo_t *tex; + qbyte *lightmap, *deluxmap; + float scale; + int maps; + + if (cl.worldmodel->fromgame == fg_quake2) + { + if (node->contents != -1) + return NULL; // solid + } + else if (node->contents < 0) + return NULL; // didn't hit anything + +// calculate mid point + +// FIXME: optimize for axial + plane = node->plane; + front = DotProduct (start, plane->normal) - plane->dist; + back = DotProduct (end, plane->normal) - plane->dist; + side = front < 0; + + if ( (back < 0) == side) + return GLRecursiveLightPoint3C (node->children[side], start, end); + + frac = front / (front-back); + mid[0] = start[0] + (end[0] - start[0])*frac; + mid[1] = start[1] + (end[1] - start[1])*frac; + mid[2] = start[2] + (end[2] - start[2])*frac; + +// go down front side + r = GLRecursiveLightPoint3C (node->children[side], start, mid); + if (r && r[0]+r[1]+r[2] >= 0) + return r; // hit something + + if ( (back < 0) == side ) + return NULL; // didn't hit anuthing + +// check for impact on this node + VectorCopy (mid, lightspot); + lightplane = plane; + + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags & SURF_DRAWTILED) + continue; // no lightmaps + + tex = surf->texinfo; + + s = DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3]; + t = DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3]; + + if (s < surf->texturemins[0] || + t < surf->texturemins[1]) + continue; + + ds = s - surf->texturemins[0]; + dt = t - surf->texturemins[1]; + + if ( ds > surf->extents[0] || dt > surf->extents[1] ) + continue; + + if (!surf->samples) + { + l[0]=0;l[1]=0;l[2]=0; + l[3]=0;l[4]=1;l[5]=1; + return l; + } + + ds >>= 4; + dt >>= 4; + + lightmap = surf->samples; + l[0]=0;l[1]=0;l[2]=0; + l[3]=0;l[4]=0;l[5]=0; + if (lightmap) + { + if (cl.worldmodel->deluxdata) + { + if (cl.worldmodel->rgblighting) + { + deluxmap = surf->samples - cl.worldmodel->lightdata + cl.worldmodel->deluxdata; + + lightmap += (dt * ((surf->extents[0]>>4)+1) + ds)*3; + deluxmap += (dt * ((surf->extents[0]>>4)+1) + ds)*3; + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]/256.0f; + + if (cl_lightstyle[surf->styles[maps]].colour & 1) + l[0] += lightmap[0] * scale; + if (cl_lightstyle[surf->styles[maps]].colour & 2) + l[1] += lightmap[1] * scale; + if (cl_lightstyle[surf->styles[maps]].colour & 4) + l[2] += lightmap[2] * scale; + + l[3] += (deluxmap[0]-127)*scale; + l[4] += (deluxmap[1]-127)*scale; + l[5] += (deluxmap[2]-127)*scale; + + lightmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1) * 3; + deluxmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1) * 3; + } + + } + else + { + deluxmap = (surf->samples - cl.worldmodel->lightdata)*3 + cl.worldmodel->deluxdata; + + lightmap += (dt * ((surf->extents[0]>>4)+1) + ds); + deluxmap += (dt * ((surf->extents[0]>>4)+1) + ds)*3; + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]/256.0f; + + if (cl_lightstyle[surf->styles[maps]].colour & 1) + l[0] += *lightmap * scale; + if (cl_lightstyle[surf->styles[maps]].colour & 2) + l[1] += *lightmap * scale; + if (cl_lightstyle[surf->styles[maps]].colour & 4) + l[2] += *lightmap * scale; + + l[3] += deluxmap[0]*scale; + l[4] += deluxmap[1]*scale; + l[5] += deluxmap[2]*scale; + + lightmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1); + deluxmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1) * 3; + } + } + + } + else + { + if (cl.worldmodel->rgblighting) + { + lightmap += (dt * ((surf->extents[0]>>4)+1) + ds)*3; + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]/256.0f; + + if (cl_lightstyle[surf->styles[maps]].colour & 1) + l[0] += lightmap[0] * scale; + if (cl_lightstyle[surf->styles[maps]].colour & 2) + l[1] += lightmap[1] * scale; + if (cl_lightstyle[surf->styles[maps]].colour & 4) + l[2] += lightmap[2] * scale; + + lightmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1) * 3; + } + + } + else + { + lightmap += (dt * ((surf->extents[0]>>4)+1) + ds); + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]/256.0f; + + if (cl_lightstyle[surf->styles[maps]].colour & 1) + l[0] += *lightmap * scale; + if (cl_lightstyle[surf->styles[maps]].colour & 2) + l[1] += *lightmap * scale; + if (cl_lightstyle[surf->styles[maps]].colour & 4) + l[2] += *lightmap * scale; + + lightmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1); + } + } + } + } + + return l; + } + +// go down back side + return GLRecursiveLightPoint3C (node->children[!side], mid, end); +} + +float *GLR_LightPoint3C (vec3_t p) +{ + static vec3_t l = {255, 255, 255}; + static vec3_t nl = {0, 0, 0}; + float *r; + vec3_t end; + + if (!cl.worldmodel->lightdata) + { + return l; + } + + if (cl.worldmodel->fromgame == fg_quake3) + return nl; + + if (cl.worldmodel->fromgame == fg_doom) + return nl; + + end[0] = p[0]; + end[1] = p[1]; + end[2] = p[2] - 2048; + + r = GLRecursiveLightPoint3C (cl.worldmodel->nodes, p, end); + + if (r == NULL) + return nl; + + return r; +} + +#endif + +void GLQ1BSP_LightPointValues(vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir) +{ + vec3_t end; + float *r; + + end[0] = point[0]; + end[1] = point[1]; + end[2] = point[2] - 2048; + + r = GLRecursiveLightPoint3C(cl.worldmodel->nodes, point, end); + if (r == NULL) + { + res_diffuse[0] = 0; + res_diffuse[1] = 0; + res_diffuse[2] = 0; + + res_ambient[0] = 0; + res_ambient[1] = 0; + res_ambient[2] = 0; + + res_dir[0] = 0; + res_dir[1] = 1; + res_dir[2] = 1; + } + else + { + res_diffuse[0] = r[0]; + res_diffuse[1] = r[1]; + res_diffuse[2] = r[2]; + + res_ambient[0] = r[0]; + res_ambient[1] = r[1]; + res_ambient[2] = r[2]; + + res_dir[0] = r[3]; + res_dir[1] = r[4]; + res_dir[2] = -r[5]; + VectorNormalize(res_dir); + + if (!res_dir[0] && !res_dir[1] && !res_dir[2]) + res_dir[1] = res_dir[2] = 1; + } +} + + diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c new file mode 100644 index 00000000..478654b8 --- /dev/null +++ b/engine/gl/gl_rmain.c @@ -0,0 +1,1607 @@ +/* +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. + +*/ +// r_main.c + +#include "quakedef.h" +#include "glquake.h" + +void R_RenderBrushPoly (msurface_t *fa); + +#define PROJECTION_DISTANCE 200 +#define MAX_STENCIL_ENTS 128 + +static entity_t *g_stencilEnts[MAX_STENCIL_ENTS]; +static int g_numStencilEnts = 0; +extern int gl_canstencil; + +PFNGLCOMPRESSEDTEXIMAGE2DARBPROC qglCompressedTexImage2DARB; +PFNGLGETCOMPRESSEDTEXIMAGEARBPROC qglGetCompressedTexImageARB; + +extern struct mleaf_s *GLMod_PointInLeaf (float *p, struct model_s *model); + +#define Q2RF_WEAPONMODEL 4 // only draw through eyes +#define Q2RF_DEPTHHACK 16 + +entity_t r_worldentity; + +qboolean r_cache_thrash; // compatability + +vec3_t modelorg, r_entorigin; +entity_t *currententity; + +int r_visframecount; // bumped when going to a new PVS +int r_framecount; // used for dlight push checking + +float r_wateralphaval; //allowed or not... + +mplane_t frustum[4]; + +int c_brush_polys, c_alias_polys; + +qboolean envmap; // true during envmap command capture + +int currenttexture = -1; // to avoid unnecessary texture sets + + +int particletexture; // little dot for particles +int explosiontexture; +int playertextures; // up to 16 color translated skins + +int mirrortexturenum; // quake texturenum, not gltexturenum +qboolean mirror; +mplane_t *mirror_plane; + +void R_DrawAliasModel (entity_t *e); + +// +// view origin +// +vec3_t vup; +vec3_t vpn; +vec3_t vright; +vec3_t r_origin; + +float r_world_matrix[16]; +float r_base_world_matrix[16]; + +// +// screen size info +// +refdef_t r_refdef; + +mleaf_t *r_viewleaf, *r_oldviewleaf; +mleaf_t *r_viewleaf2, *r_oldviewleaf2; +int r_viewcluster, r_viewcluster2, r_oldviewcluster, r_oldviewcluster2; + +texture_t *r_notexture_mip; + +int d_lightstylevalue[256]; // 8.8 fraction of base light value + + +void GLR_MarkLeaves (void); + +cvar_t r_norefresh = {"r_norefresh","0"}; +//cvar_t r_drawentities = {"r_drawentities","1"}; +//cvar_t r_drawviewmodel = {"r_drawviewmodel","1"}; +//cvar_t r_speeds = {"r_speeds","0"}; +//cvar_t r_fullbright = {"r_fullbright","0"}; +cvar_t r_lightmap = {"r_lightmap","0", NULL, CVAR_CHEAT}; +cvar_t r_mirroralpha = {"r_mirroralpha","1", NULL, CVAR_CHEAT}; +cvar_t r_wateralpha = {"r_wateralpha","1", NULL}; +//cvar_t r_waterwarp = {"r_waterwarp", "0"}; +cvar_t r_novis = {"r_novis","0"}; +//cvar_t r_netgraph = {"r_netgraph","0"}; + +extern cvar_t gl_part_flame; +extern cvar_t gl_part_torch; + +cvar_t gl_clear = {"gl_clear","0"}; +cvar_t gl_cull = {"gl_cull","1"}; +cvar_t gl_smoothmodels = {"gl_smoothmodels","1"}; +cvar_t gl_affinemodels = {"gl_affinemodels","0"}; +cvar_t gl_polyblend = {"gl_polyblend","1"}; +cvar_t gl_playermip = {"gl_playermip","0"}; +cvar_t gl_keeptjunctions = {"gl_keeptjunctions","1"}; +cvar_t gl_reporttjunctions = {"gl_reporttjunctions","0"}; +cvar_t gl_finish = {"gl_finish","0"}; +cvar_t gl_contrast = {"gl_contrast", "1"}; +cvar_t gl_dither = {"gl_dither", "1"}; + +extern cvar_t gl_ati_truform; +extern cvar_t gl_ati_truform_type; +extern cvar_t gl_ati_truform_tesselation; + + +#ifdef R_XFLIP +cvar_t r_xflip = {"leftisright", "0"}; +#endif + +extern cvar_t gl_ztrick; +extern cvar_t scr_fov; + + + + + +/* +================= +R_CullBox + +Returns true if the box is completely outside the frustom +================= +*/ +qboolean R_CullBox (vec3_t mins, vec3_t maxs) +{ + int i; + + for (i=0 ; i<4 ; i++) + if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) + return true; + return false; +} + + +void R_RotateForEntity (entity_t *e) +{ + glTranslatef (e->origin[0], e->origin[1], e->origin[2]); + + glRotatef (e->angles[1], 0, 0, 1); + glRotatef (-e->angles[0], 0, 1, 0); + //ZOID: fixed z angle + glRotatef (e->angles[2], 1, 0, 0); +} + +/* +============================================================= + + SPRITE MODELS + +============================================================= +*/ + +/* +================ +R_GetSpriteFrame +================ +*/ +mspriteframe_t *R_GetSpriteFrame (entity_t *currententity) +{ + msprite_t *psprite; + mspritegroup_t *pspritegroup; + mspriteframe_t *pspriteframe; + int i, numframes, frame; + float *pintervals, fullinterval, targettime, time; + + psprite = currententity->model->cache.data; + frame = currententity->frame; + + if ((frame >= psprite->numframes) || (frame < 0)) + { + Con_Printf ("R_DrawSprite: no such frame %d\n", frame); + frame = 0; + } + + if (psprite->frames[frame].type == SPR_SINGLE) + { + pspriteframe = psprite->frames[frame].frameptr; + } + else if (psprite->frames[frame].type == SPR_ANGLED) + { + pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; + pspriteframe = pspritegroup->frames[(int)((r_refdef.viewangles[1]-currententity->angles[1])/360*8 + 0.5-4)&7]; + } + else + { + pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; + pintervals = pspritegroup->intervals; + numframes = pspritegroup->numframes; + fullinterval = pintervals[numframes-1]; + + time = cl.time + currententity->syncbase; + + // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values + // are positive, so we don't have to worry about division by 0 + targettime = time - ((int)(time / fullinterval)) * fullinterval; + + for (i=0 ; i<(numframes-1) ; i++) + { + if (pintervals[i] > targettime) + break; + } + + pspriteframe = pspritegroup->frames[i]; + } + + return pspriteframe; +} + + +/* +================= +R_DrawSpriteModel + +================= +*/ +void R_DrawSpriteModel (entity_t *e) +{ + vec3_t point; + mspriteframe_t *frame; + vec3_t forward, right, up; + msprite_t *psprite; + + // don't even bother culling, because it's just a single + // polygon without a surface cache + frame = R_GetSpriteFrame (e); + psprite = currententity->model->cache.data; +// frame = 0x05b94140; + + if (psprite->type == SPR_ORIENTED) + { // bullet marks on walls + AngleVectors (currententity->angles, forward, right, up); + } + else if (psprite->type == SPR_FACING_UPRIGHT) + { + up[0] = 0;up[1] = 0;up[2]=1; + right[0] = e->origin[1] - r_origin[1]; + right[1] = -(e->origin[0] - r_origin[0]); + right[2] = 0; + VectorNormalize (right); + } + else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) + { + up[0] = 0;up[1] = 0;up[2]=1; + VectorCopy (vright, right); + } + else + { // normal sprite + VectorCopy(vup, up); + VectorCopy(vright, right); + } + up[0]*=currententity->scale; + up[1]*=currententity->scale; + up[2]*=currententity->scale; + right[0]*=currententity->scale; + right[1]*=currententity->scale; + right[2]*=currententity->scale; + + glColor4f (1,1,1, e->alpha); + + GL_DisableMultitexture(); + + GL_Bind(frame->gl_texturenum); + + if (e->alpha<1) + { + glEnable(GL_BLEND); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + else + glEnable (GL_ALPHA_TEST); + + glDisable(GL_CULL_FACE); + glBegin (GL_QUADS); + + glTexCoord2f (0, 1); + VectorMA (e->origin, frame->down, up, point); + VectorMA (point, frame->left, right, point); + glVertex3fv (point); + + glTexCoord2f (0, 0); + VectorMA (e->origin, frame->up, up, point); + VectorMA (point, frame->left, right, point); + glVertex3fv (point); + + glTexCoord2f (1, 0); + VectorMA (e->origin, frame->up, up, point); + VectorMA (point, frame->right, right, point); + glVertex3fv (point); + + glTexCoord2f (1, 1); + VectorMA (e->origin, frame->down, up, point); + VectorMA (point, frame->right, right, point); + glVertex3fv (point); + + glEnd (); + + glDisable(GL_BLEND); + glDisable (GL_ALPHA_TEST); +} +#if 0 +extern int gldepthfunc; +typedef struct decal_s { + vec3_t origin; + vec3_t normal; + int modelindex; + float endtime; + float starttime; + float size; + struct decal_s *next; +} decal_t; +decal_t *firstdecal; +void vectoangles(vec3_t vec, vec3_t ang); +void R_DrawDecals(void) +{ +// vec3_t point; +// vec3_t right, up; + + entity_t ent; + extern int cl_spikeindex; + extern model_t mod_known[]; + + decal_t *dec = firstdecal; +// glDisable(GL_TEXTURE_2D); + glDisable (GL_ALPHA_TEST); + glEnable (GL_BLEND); +// glDepthFunc(GL_LEQUAL); +// glDisable(GL_CULL_TEST); +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + GL_Bind(particletexture); +// glDepthMask(0); +// glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(0, 0, 0, 0.5); + + + +// glClearStencil(0x0); +// glEnable(GL_STENCIL_TEST); + + +memset(&ent, 0, sizeof(ent)); + + while(dec) + { +// if (dec->modelindex) + { + ent.origin[0] = dec->origin[0]; + ent.origin[1] = dec->origin[1]; + ent.origin[2] = dec->origin[2]; + ent.angles[0] = -dec->normal[0]; + ent.angles[1] = -dec->normal[1]; + ent.angles[2] = -dec->normal[2]; + vectoangles(ent.angles, ent.angles); + ent.model = &mod_known[cl_spikeindex];//dec->modelindex; + currententity = &ent; + switch(currententity->model->type) + { + case mod_alias: + R_DrawAliasModel(currententity); + break; + case mod_alias3: + R_DrawAlias3Model(currententity); + break; + } + dec = dec->next; + continue; + } +/* + PerpendicularVector(up, dec->normal); + CrossProduct(dec->normal, up, right); + +#if 0 + glClear(GL_STENCIL_BUFFER_BIT); + glStencilFunc (GL_ALWAYS, 0x1, 0x1); + glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE); + glBegin(GL_QUADS); + glVertex2f (-1.0, 0.0); + glVertex2f (0.0, 1.0); + glVertex2f (1.0, 0.0); + glVertex2f (0.0, -1.0); + glEnd(); + + + + glStencilFunc (GL_EQUAL, 0x1, 0x1); //where we drew to the stencil buffer. + glStencilOp (GL_ZERO, GL_KEEP, GL_KEEP); +#endif + + +// glColor4f(1, 1, 1, (dec->starttime-dec->endtime) * (cl.time-dec->starttime)); + + glBegin (GL_QUADS); + + glTexCoord2f (0, 0.5); + VectorMA (dec->origin, dec->size, up, point); + VectorMA (point, -dec->size, right, point); + glVertex3fv (point); + + glTexCoord2f (0, 0); + VectorMA (dec->origin, -dec->size, up, point); + VectorMA (point, -dec->size, right, point); + glVertex3fv (point); + + glTexCoord2f (0.5, 0); + VectorMA (dec->origin, -dec->size, up, point); + VectorMA (point, dec->size, right, point); + glVertex3fv (point); + + glTexCoord2f (0.5, 0.5); + VectorMA (dec->origin, dec->size, up, point); + VectorMA (point, dec->size, right, point); + glVertex3fv (point); + glEnd (); + + dec = dec->next; + */ + } +// glDisable(GL_STENCIL_TEST); +// glDepthMask(1); + glEnable(GL_TEXTURE_2D); + glDisable (GL_BLEND); + +// glDepthFunc(gldepthfunc); +} + +void TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal); +void GLR_AddDecals(vec3_t org) +{ + decal_t *dec; + vec3_t end, impact, norm; + vec3_t dir[] = { + {0, 0, 10}, + {0, 0, -10}, + {0, 10, 0}, + {0, -10, 0}, + {10, 0, 0}, + {-10, 0, 0} + }; + int i; +#define STOP_EPSILON 0.01 + + return; + + for (i = 0; i < 6; i++) + { + VectorAdd(org, dir[i], end); + TraceLineN(org, end, impact, norm); + + if (!((end[0]==impact[0] && end[1]==impact[1] && end[2]==impact[2]) || (!impact[0] && !impact[1] && !impact[2]))) + { + dec = Z_Malloc(sizeof(decal_t)); + VectorCopy(norm, dec->normal); +// VectorCopy(impact, dec->origin); + VectorMA(impact, STOP_EPSILON, norm, dec->origin); + dec->next = firstdecal; + firstdecal = dec; + } + } +} +#endif + + +//================================================================================== + +/* +============= +R_DrawEntitiesOnList +============= +*/ +void GLR_DrawEntitiesOnList (void) +{ + int i; + + if (!r_drawentities.value) + return; + + // draw sprites seperately, because of alpha blending + for (i=0 ; ikeynum == cl.viewentity[r_refdef.currentplayernum]) + continue; + + if (!currententity->model) + continue; + + if (cls.allow_anyparticles || currententity->visframe) //allowed or static + { + if (currententity->model->particleeffect>=0) + { + if (currententity->model->particleengulphs) + { + if (gl_part_flame.value) + { + R_TorchEffect(currententity->origin, currententity->model->particleeffect); + currententity->model = NULL; + continue; + } + } + else + { + if (gl_part_torch.value) + { + R_TorchEffect(currententity->origin, currententity->model->particleeffect); + } + } + } + } + + switch (currententity->model->type) + { + case mod_alias: + if (cl.worldmodel->fromgame == fg_doom) + R_DrawGAliasModel (currententity); + break; + +#ifdef HALFLIFEMODELS + case mod_halflife: + R_DrawHLModel (currententity); + break; +#endif + + case mod_brush: + if (cl.worldmodel->fromgame == fg_doom) + PPL_BaseBModelTextures (currententity); + break; + + default: + break; + } + } + + for (i=0 ; ikeynum == cl.viewentity[r_refdef.currentplayernum]) + continue; + + if (!currententity->model) + continue; + + if (cls.allow_anyparticles || currententity->visframe) //allowed or static + { + if (currententity->model->particleeffect>=0) + { + if (currententity->model->particleengulphs) + { + if (gl_part_flame.value) + { + continue; + } + } + } + } + + + switch (currententity->model->type) + { + case mod_sprite: + R_DrawSpriteModel (currententity); + break; + + default : + break; + } + } +} + +/* +============= +R_DrawViewModel +============= +*/ +void GLR_DrawViewModel (void) +{ +// float ambient[4], diffuse[4]; +// int j; +// int lnum; +// vec3_t dist; +// float add; +// dlight_t *dl; +// int ambientlight, shadelight; + + static struct model_s *oldmodel[MAX_SPLITS]; + static float lerptime[MAX_SPLITS]; + static int prevframe[MAX_SPLITS]; + +#ifdef SIDEVIEWS + extern qboolean r_secondaryview; + if (r_secondaryview==1) + return; +#endif + + if (!r_drawviewmodel.value || !Cam_DrawViewModel(r_refdef.currentplayernum)) + return; + + if (envmap) + return; + +#ifdef Q2CLIENT + if (cls.q2server) + return; +#endif + + if (!r_drawentities.value) + return; + + if (cl.stats[r_refdef.currentplayernum][STAT_ITEMS] & IT_INVISIBILITY) + return; + + if (cl.stats[r_refdef.currentplayernum][STAT_HEALTH] <= 0) + return; + + currententity = &cl.viewent[r_refdef.currentplayernum]; + if (!currententity->model) + return; + +// if (cls.allow_anyparticles || currententity->visframe) //allowed or static + { + if (currententity->model->particleeffect>=0) + { + if (currententity->model->particleengulphs) + { + if (gl_part_flame.value) + { + R_TorchEffect(currententity->origin, currententity->model->particleeffect); + currententity->model = NULL; + return; + } + } + else + { + if (gl_part_torch.value) + { + R_TorchEffect(currententity->origin, currententity->model->particleeffect); + } + } + } + } + + + +#ifdef PEXT_SCALE + currententity->scale = 1; +#endif + if (r_drawviewmodel.value > 0 && r_drawviewmodel.value < 1) + currententity->alpha = r_drawviewmodel.value; + else + currententity->alpha = 1; + + if (currententity->frame != prevframe[r_refdef.currentplayernum]) + { + currententity->oldframe = prevframe[r_refdef.currentplayernum]; + lerptime[r_refdef.currentplayernum] = realtime; + } + prevframe[r_refdef.currentplayernum] = currententity->frame; + + if (currententity->model != oldmodel[r_refdef.currentplayernum]) + { + oldmodel[r_refdef.currentplayernum] = currententity->model; + currententity->oldframe = currententity->frame; + lerptime[r_refdef.currentplayernum] = realtime; + } + currententity->lerptime = 1-(realtime-lerptime[r_refdef.currentplayernum])*10; + if (currententity->lerptime<0)currententity->lerptime=0; + if (currententity->lerptime>1)currententity->lerptime=1; + + + currententity->flags = Q2RF_WEAPONMODEL|Q2RF_DEPTHHACK; + + switch(currententity->model->type) + { + case mod_sprite: + R_DrawSpriteModel (currententity); + break; + + case mod_alias: + R_DrawGAliasModel (currententity); + break; + +#ifdef HALFLIFEMODELS + case mod_halflife: + R_DrawHLModel (currententity); + break; +#endif + + case mod_brush: + break; + + case mod_dummy: + break; + } +} + + +/* +============ +R_PolyBlend +============ +*/ +void R_PolyBlend (void) +{ + if (!v_blend[3]) + return; + +//Con_Printf("R_PolyBlend(): %4.2f %4.2f %4.2f %4.2f\n",v_blend[0], v_blend[1], v_blend[2], v_blend[3]); + + GL_DisableMultitexture(); + + glDisable (GL_ALPHA_TEST); + glEnable (GL_BLEND); + glDisable (GL_DEPTH_TEST); + glDisable (GL_TEXTURE_2D); + + glLoadIdentity (); + + glRotatef (-90, 1, 0, 0); // put Z going up + glRotatef (90, 0, 0, 1); // put Z going up + + glColor4fv (v_blend); + + glBegin (GL_QUADS); + + glVertex3f (10, 100, 100); + glVertex3f (10, -100, 100); + glVertex3f (10, -100, -100); + glVertex3f (10, 100, -100); + glEnd (); + + glDisable (GL_BLEND); + glEnable (GL_TEXTURE_2D); + glEnable (GL_ALPHA_TEST); +} + +void GLR_BrightenScreen (void) +{ + extern float vid_gamma; + float f; + + if (gl_contrast.value <= 1.0) + return; + + f = gl_contrast.value; + f = min (f, 3); + + f = pow (f, vid_gamma); + + glDisable (GL_TEXTURE_2D); + glEnable (GL_BLEND); + glBlendFunc (GL_DST_COLOR, GL_ONE); + glBegin (GL_QUADS); + while (f > 1) { + if (f >= 2) + glColor3f (1,1,1); + else + glColor3f (f - 1, f - 1, f - 1); + glVertex2f (0, 0); + glVertex2f (vid.width, 0); + glVertex2f (vid.width, vid.height); + glVertex2f (0, vid.height); + f *= 0.5; + } + glEnd (); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_TEXTURE_2D); + glDisable (GL_BLEND); + glColor3f(1, 1, 1); +} + +int SignbitsForPlane (mplane_t *out) +{ + int bits, j; + + // for fast box on planeside test + + bits = 0; + for (j=0 ; j<3 ; j++) + { + if (out->normal[j] < 0) + bits |= 1<fromgame == fg_quake2 || cl.worldmodel->fromgame == fg_quake3)) + { + static mleaf_t fakeleaf; + mleaf_t *leaf; + + r_viewleaf = &fakeleaf; //so we can use quake1 rendering routines for q2 bsps. + r_viewleaf->contents = Q1CONTENTS_EMPTY; + r_viewleaf2 = NULL; + + r_oldviewcluster = r_viewcluster; + r_oldviewcluster2 = r_viewcluster2; + leaf = GLMod_PointInLeaf (r_origin, cl.worldmodel); + r_viewcluster = r_viewcluster2 = leaf->cluster; + + // check above and below so crossing solid water doesn't draw wrong + if (!leaf->contents) + { // look down a bit + vec3_t temp; + + VectorCopy (r_origin, temp); + temp[2] -= 16; + leaf = GLMod_PointInLeaf (temp, cl.worldmodel); + if ( !(leaf->contents & Q2CONTENTS_SOLID) && + (leaf->cluster != r_viewcluster2) ) + r_viewcluster2 = leaf->cluster; + } + else + { // look up a bit + vec3_t temp; + + VectorCopy (r_origin, temp); + temp[2] += 16; + leaf = GLMod_PointInLeaf (temp, cl.worldmodel); + if ( !(leaf->contents & Q2CONTENTS_SOLID) && + (leaf->cluster != r_viewcluster2) ) + r_viewcluster2 = leaf->cluster; + } + } +#endif + else + { + mleaf_t *leaf; + vec3_t temp; + + r_oldviewleaf = r_viewleaf; + r_oldviewleaf2 = r_viewleaf2; + r_viewleaf = GLMod_PointInLeaf (r_origin, cl.worldmodel); + + if (!r_viewleaf) + { + } + else if (r_viewleaf->contents == Q1CONTENTS_EMPTY) + { //look down a bit + VectorCopy (r_origin, temp); + temp[2] -= 16; + leaf = GLMod_PointInLeaf (temp, cl.worldmodel); + if (leaf->contents <= Q1CONTENTS_WATER && leaf->contents >= Q1CONTENTS_LAVA) + r_viewleaf2 = leaf; + else + r_viewleaf2 = NULL; + } + else if (r_viewleaf->contents <= Q1CONTENTS_WATER && r_viewleaf->contents >= Q1CONTENTS_LAVA) + { //in water, look up a bit. + + VectorCopy (r_origin, temp); + temp[2] += 16; + leaf = GLMod_PointInLeaf (temp, cl.worldmodel); + if (leaf->contents == Q1CONTENTS_EMPTY) + r_viewleaf2 = leaf; + else + r_viewleaf2 = NULL; + } + else + r_viewleaf2 = NULL; + + if (r_viewleaf) + V_SetContentsColor (r_viewleaf->contents); + } + GLV_CalcBlend (); + + r_cache_thrash = false; + + c_brush_polys = 0; + c_alias_polys = 0; + +} + + +void MYgluPerspective( GLdouble fovy, GLdouble aspect, + GLdouble zNear, GLdouble zFar ) +{ + GLfloat matrix[16]; + GLdouble xmin, xmax, ymin, ymax; + + ymax = zNear * tan( fovy * M_PI / 360.0 ); + ymin = -ymax; + + xmin = ymin * aspect; + xmax = ymax * aspect; + +#if 1 + glFrustum( xmin, xmax, ymin, ymax, zNear, zFar ); +#else + + matrix[0] = (2*zNear) / (xmax - xmin); + matrix[4] = 0; + matrix[8] = (xmax + xmin) / (xmax - xmin); + matrix[12] = 0; + + matrix[1] = 0; + matrix[5] = (2*zNear) / (ymax - ymin); + matrix[9] = (ymax + ymin) / (ymax - ymin); + matrix[13] = 0; + + matrix[2] = 0; + matrix[6] = 0; + matrix[10] = - (zFar+zNear)/(zFar-zNear); + matrix[14] = - (2.0f*zFar*zNear)/(zFar-zNear); + + matrix[3] = 0; + matrix[7] = 0; + matrix[11] = -1; + matrix[15] = 0; + + glMultMatrixf(matrix); +#endif +} + +void GL_InfinatePerspective( GLdouble fovy, GLdouble aspect, + GLdouble zNear) +{ + GLfloat matrix[16]; + + // nudge infinity in just slightly for lsb slop + GLfloat nudge = 1;// - 1.0 / (1<<23); + + GLdouble xmin, xmax, ymin, ymax; + + ymax = zNear * tan( fovy * M_PI / 360.0 ); + ymin = -ymax; + + xmin = ymin * aspect; + xmax = ymax * aspect; + + matrix[0] = (2*zNear) / (xmax - xmin); + matrix[4] = 0; + matrix[8] = (xmax + xmin) / (xmax - xmin); + matrix[12] = 0; + + matrix[1] = 0; + matrix[5] = (2*zNear) / (ymax - ymin); + matrix[9] = (ymax + ymin) / (ymax - ymin); + matrix[13] = 0; + + matrix[2] = 0; + matrix[6] = 0; + matrix[10] = -1 * nudge; + matrix[14] = -2*zNear * nudge; + + matrix[3] = 0; + matrix[7] = 0; + matrix[11] = -1; + matrix[15] = 0; + + glMultMatrixf(matrix); +} + + +/* +============= +R_SetupGL +============= +*/ +void R_SetupGL (void) +{ + float screenaspect; + extern int glwidth, glheight; + int x, x2, y2, y, w, h; + + // + // set up viewpoint + // + x = r_refdef.vrect.x * glwidth/vid.width; + x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width; + y = (vid.height-r_refdef.vrect.y) * glheight/vid.height; + y2 = ((int)vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/(int)vid.height; + + // fudge around because of frac screen scale + if (x > 0) + x--; + if (x2 < glwidth) + x2++; + if (y2 < 0) + y2--; + if (y < glheight) + y++; + + w = x2 - x; + h = y - y2; + + if (envmap) + { + x = y2 = 0; + w = h = 256; + } + + glViewport (glx + x, gly + y2, w, h); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity (); + + screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height; + if (!r_shadows.value || !gl_canstencil)//gl_nv_range_clamp) + { +// yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*180/M_PI; +// yfov = (2.0 * tan (scr_fov.value/360*M_PI)) / screenaspect; +// yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*(scr_fov.value*2)/M_PI; +// MYgluPerspective (yfov, screenaspect, 4, 4096); + MYgluPerspective (r_refdef.fov_y, screenaspect, 4, 4096); + } + else + { + GL_InfinatePerspective(r_refdef.fov_y, screenaspect, 4); + } + + if (mirror) + { + if (mirror_plane->normal[2]) + glScalef (1, -1, 1); + else + glScalef (-1, 1, 1); + glCullFace(GL_BACK); + } + else + glCullFace(GL_FRONT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity (); + + glRotatef (-90, 1, 0, 0); // put Z going up + glRotatef (90, 0, 0, 1); // put Z going up + +#ifdef R_XFLIP + if (r_xflip.value) + { + glScalef (1, -1, 1); + glCullFace(GL_BACK); + } +#endif + + + glRotatef (-r_refdef.viewangles[2], 1, 0, 0); + glRotatef (-r_refdef.viewangles[0], 0, 1, 0); + glRotatef (-r_refdef.viewangles[1], 0, 0, 1); + glTranslatef (-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]); + + glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix); + + // + // set drawing parms + // + if (gl_cull.value) + glEnable(GL_CULL_FACE); + else + glDisable(GL_CULL_FACE); + + glDisable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); + +//#ifndef D3DQUAKE +// glClearDepth(1.0f); +//#endif + +// if (gl_lightmap_format == GL_LUMINANCE) +// glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); +/* else if (gl_lightmap_format == GL_INTENSITY) + { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glColor4f (0,0,0,1); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else if (gl_lightmap_format == GL_RGBA) + { + glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + } + + */ + + if (gl_dither.value) + { + glEnable(GL_DITHER); + } + else + { + glDisable(GL_DITHER); + } + + GL_DisableMultitexture(); +} + +/* +================ +R_RenderScene + +r_refdef must be set before the first call +================ +*/ +void R_RenderScene (void) +{ + qboolean GLR_DoomWorld(void); + if (!mirror) + GLR_SetupFrame (); + + R_SetFrustum (); + + R_SetupGL (); + + if (!(r_refdef.flags & 1)) + { +#ifdef DOOMWADS + if (!GLR_DoomWorld ()) +#endif + { + GLR_MarkLeaves (); // done here so we know if we're in water + R_DrawWorld (); // adds static entities to the list + } + } + + S_ExtraUpdate (); // don't let sound get messed up if going slow + + GLR_DrawEntitiesOnList (); + +// R_DrawDecals(); + + GL_DisableMultitexture(); + + R_RenderDlights (); + + if (cl.worldmodel) + R_DrawParticles (); + +#ifdef GLTEST + Test_Draw (); +#endif + +} + + +/* +============= +R_Clear +============= +*/ +int gldepthfunc; +void R_Clear (void) +{ + if (r_mirroralpha.value != 1.0) + { + if (gl_clear.value && !r_secondaryview) + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + glClear (GL_DEPTH_BUFFER_BIT); + gldepthmin = 0; + gldepthmax = 0.5; + glDepthFunc (gldepthfunc=GL_LEQUAL); + } +#ifdef SIDEVIEWS + else if (gl_ztrick.value && !gl_ztrickdisabled) +#else + else if (gl_ztrick.value) +#endif + { + static int trickframe; + + if (gl_clear.value && !(r_refdef.flags & 1)) + glClear (GL_COLOR_BUFFER_BIT); + + trickframe++; + if (trickframe & 1) + { + gldepthmin = 0; + gldepthmax = 0.49999; + glDepthFunc (gldepthfunc=GL_LEQUAL); + } + else + { + gldepthmin = 1; + gldepthmax = 0.5; + glDepthFunc (gldepthfunc=GL_GEQUAL); + } + } + else + { + if (gl_clear.value) + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + glClear (GL_DEPTH_BUFFER_BIT); + gldepthmin = 0; + gldepthmax = 1; + glDepthFunc (gldepthfunc=GL_LEQUAL); + } + + glDepthRange (gldepthmin, gldepthmax); +} + +//#if 0 //!!! FIXME, Zoid, mirror is disabled for now +/* +============= +R_Mirror +============= +*/ + +void CL_AddFlagModels (entity_t *ent, int team); +void R_MirrorAddPlayerModels (void) +{ + extern int cl_playerindex; + extern cvar_t cl_predict_players, cl_predict_players2; + player_state_t *state; + player_state_t exact; + player_info_t *info=cl.players + cl.playernum[0]; + double playertime; + entity_t *ent; + int msec; + frame_t *frame; + int oldphysent; + extern cvar_t spectator; + + playertime = realtime - cls.latency + 0.02; + if (playertime > realtime) + playertime = realtime; + + frame = &cl.frames[cl.parsecount&UPDATE_MASK]; + + state=&frame->playerstate[cl.playernum[0]]; + + if (!state->modelindex || spectator.value) + return; + + ent = &cl_visedicts[cl_numvisedicts]; + cl_numvisedicts++; + + ent->keynum = cl.playernum[0]+1; + + ent->model = cl.model_precache[state->modelindex]; + ent->skinnum = state->skinnum; + ent->frame = state->frame; + ent->oldframe = state->oldframe; + if (state->lerpstarttime) + { + ent->lerptime = 1-(realtime - state->lerpstarttime)*10; + if (ent->lerptime < 0) + ent->lerptime = 0; + } + else + ent->lerptime = 0; + + ent->colormap = cl.players[cl.playernum[0]].translations; + if (state->modelindex == cl_playerindex) + ent->scoreboard = &cl.players[cl.playernum[0]]; // use custom skin + else + ent->scoreboard = NULL; + +#ifdef PEXT_SCALE + ent->scale = state->scale; + if (!ent->scale) + ent->scale = 1; +#endif +#ifdef PEXT_TRANS + ent->alpha = state->trans; + if (!ent->alpha) + ent->alpha = 1; +#endif + + // + // angles + // + ent->angles[PITCH] = -r_refdef.viewangles[PITCH]/3; + ent->angles[YAW] = r_refdef.viewangles[YAW]; +// ent->angles[ROLL] = 0; + ent->angles[ROLL] = V_CalcRoll (ent->angles, state->velocity)*4; + + // only predict half the move to minimize overruns + msec = 500*(playertime - state->state_time); + if (msec <= 0 || (!cl_predict_players.value && !cl_predict_players2.value)) + { + VectorCopy (state->origin, ent->origin); +//Con_DPrintf ("nopredict\n"); + } + else + { + // predict players movement + if (msec > 255) + msec = 255; + state->command.msec = msec; +//Con_DPrintf ("predict: %i\n", msec); + + oldphysent = pmove.numphysent; + CL_SetSolidPlayers (cl.playernum[0]); + CL_PredictUsercmd (0, state, &exact, &state->command); + pmove.numphysent = oldphysent; + VectorCopy (exact.origin, ent->origin); + } + + if (state->effects & EF_FLAG1) + CL_AddFlagModels (ent, 0); + else if (state->effects & EF_FLAG2) + CL_AddFlagModels (ent, 1); + + if (info->vweapindex) + CL_AddVWeapModel(ent, info->vweapindex); + +} + + +void R_Mirror (void) +{ + float d; + msurface_t *s; +// entity_t *ent; + + int oldvisents; + vec3_t oldangles, oldorg; //cache - for rear view mirror and stuff. + + if (!mirror) + return; + + oldvisents = cl_numvisedicts; + R_MirrorAddPlayerModels(); //we need to add the player model. Invisible in mirror otherwise. + + memcpy(oldangles, r_refdef.viewangles, sizeof(vec3_t)); + memcpy(oldorg, r_refdef.vieworg, sizeof(vec3_t)); + + memcpy (r_base_world_matrix, r_world_matrix, sizeof(r_base_world_matrix)); + + d = DotProduct (r_refdef.vieworg, mirror_plane->normal) - mirror_plane->dist; + VectorMA (r_refdef.vieworg, -2*d, mirror_plane->normal, r_refdef.vieworg); + memcpy(r_origin, r_refdef.vieworg, sizeof(vec3_t)); + + d = DotProduct (vpn, mirror_plane->normal); + VectorMA (vpn, -2*d, mirror_plane->normal, vpn); + + r_refdef.viewangles[0] = -asin (vpn[2])/M_PI*180; + r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0])/M_PI*180; + r_refdef.viewangles[2] = -r_refdef.viewangles[2]; +/* + r_refdef.vieworg[0] = 400; + r_refdef.vieworg[1] = 575; + r_refdef.vieworg[2] = 64; +*/ + + + gldepthmin = 0.5; + gldepthmax = 1; + glDepthRange (gldepthmin, gldepthmax); + glDepthFunc (GL_LEQUAL); + + R_RenderScene (); + GLR_DrawWaterSurfaces (); + + + gldepthmin = 0; + gldepthmax = 0.5; + glDepthRange (gldepthmin, gldepthmax); + glDepthFunc (GL_LEQUAL); + + + memcpy(r_refdef.viewangles, oldangles, sizeof(vec3_t)); + + glMatrixMode(GL_PROJECTION); + if (mirror_plane->normal[2]) + glScalef (1,-1,1); + else + glScalef (-1,1,1); + glCullFace(GL_FRONT); + glMatrixMode(GL_MODELVIEW); + + glLoadMatrixf (r_base_world_matrix); + + + + // blend on top + glDisable(GL_ALPHA_TEST); + glEnable (GL_BLEND); + glColor4f (1,1,1,r_mirroralpha.value); + s = cl.worldmodel->textures[mirrortexturenum]->texturechain; + for ( ; s ; s=s->texturechain) + R_RenderBrushPoly (s); + cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL; + glDisable (GL_BLEND); + glColor4f (1,1,1,1); + + //put things back for rear views + glCullFace(GL_BACK); + + memcpy(r_refdef.viewangles, oldangles, sizeof(vec3_t)); + memcpy(r_refdef.vieworg, oldorg, sizeof(vec3_t)); + cl_numvisedicts = oldvisents; +} +//#endif + +/* +================ +R_RenderView + +r_refdef must be set before the first call +================ +*/ +void GLR_RenderView (void) +{ + extern msurface_t *r_alpha_surfaces; + double time1 = 0, time2; + + if (r_norefresh.value) + return; + + if (!(r_refdef.flags & 1)) + if (!r_worldentity.model || !cl.worldmodel) + + return; +// Sys_Error ("R_RenderView: NULL worldmodel"); + + + + if (qglPNTrianglesiATI) + { + if (gl_ati_truform_type.value) + { //linear + qglPNTrianglesiATI(GL_PN_TRIANGLES_NORMAL_MODE_ATI, GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI); + qglPNTrianglesiATI(GL_PN_TRIANGLES_POINT_MODE_ATI, GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI); + } + else + { //quadric + qglPNTrianglesiATI(GL_PN_TRIANGLES_NORMAL_MODE_ATI, GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI); + qglPNTrianglesiATI(GL_PN_TRIANGLES_POINT_MODE_ATI, GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI); + } + qglPNTrianglesfATI(GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI, gl_ati_truform_tesselation.value); + } + + + + + if (r_speeds.value) + { + glFinish (); + time1 = Sys_DoubleTime (); + c_brush_polys = 0; + c_alias_polys = 0; + } + + mirror = false; + + if (gl_finish.value) + glFinish (); + + R_Clear (); +/* + if (r_viewleaf)// && r_viewleaf->contents != CONTENTS_EMPTY) + { + // static fogcolour; + float fogcol[4]={0}; + float fogperc; + float fogdist; +#pragma comment (lib, "opengl32.lib") //temp only. + + fogperc=0; + fogdist=512; + switch(r_viewleaf->contents) + { + case CONTENTS_WATER: + fogcol[0] = 64/255.0; + fogcol[1] = 128/255.0; + fogcol[2] = 192/255.0; + fogperc=0.2; + fogdist=512; + break; + case CONTENTS_SLIME: + fogcol[0] = 32/255.0; + fogcol[1] = 192/255.0; + fogcol[2] = 92/255.0; + fogperc=1; + fogdist=256; + break; + case CONTENTS_LAVA: + fogcol[0] = 192/255.0; + fogcol[1] = 32/255.0; + fogcol[2] = 64/255.0; + fogperc=1; + fogdist=128; + break; + default: + fogcol[0] = 192/255.0; + fogcol[1] = 192/255.0; + fogcol[2] = 192/255.0; + fogperc=1; + fogdist=1024; + break; + } + if (fogperc) + { + glFogi(GL_FOG_MODE, GL_LINEAR); + glFogfv(GL_FOG_COLOR, fogcol); + glFogf(GL_FOG_DENSITY, fogperc); + glFogf(GL_FOG_START, 1); + glFogf(GL_FOG_END, fogdist); + glEnable(GL_FOG); + } + } +*/ + r_alpha_surfaces = NULL; + + // render normal view + R_RenderScene (); + GLR_DrawViewModel (); + GLR_DrawWaterSurfaces (); + GLR_DrawAlphaSurfaces (); + + // render mirror view + R_Mirror (); + + R_PolyBlend (); + +// glDisable(GL_FOG); + + if (r_speeds.value) + { +// glFinish (); + time2 = Sys_DoubleTime (); + Con_Printf ("%3i ms %4i wpoly %4i epoly\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys); + } +} diff --git a/engine/gl/gl_rmisc.c b/engine/gl/gl_rmisc.c new file mode 100644 index 00000000..e2971607 --- /dev/null +++ b/engine/gl/gl_rmisc.c @@ -0,0 +1,940 @@ +/* +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. + +*/ +// r_misc.c + +#include "quakedef.h" +#include "glquake.h" + +#ifdef WATERLAYERS +cvar_t r_waterlayers = {"r_waterlayers","3"}; +#endif + +cvar_t gl_skyboxname = {"r_skybox", ""}; + +extern void R_InitBubble(); + +/* +================== +R_InitTextures +================== +* +void GLR_InitTextures (void) +{ + int x,y, m; + qbyte *dest; + +// create a simple checkerboard texture for the default + r_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture"); + + r_notexture_mip->width = r_notexture_mip->height = 16; + r_notexture_mip->offsets[0] = sizeof(texture_t); + r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16; + r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8; + r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4; + + for (m=0 ; m<4 ; m++) + { + dest = (qbyte *)r_notexture_mip + r_notexture_mip->offsets[m]; + for (y=0 ; y< (16>>m) ; y++) + for (x=0 ; x< (16>>m) ; x++) + { + if ( (y< (8>>m) ) ^ (x< (8>>m) ) ) + *dest++ = 0; + else + *dest++ = 0xff; + } + } +}*/ +//we could go for nice smooth round particles... but then we would loose a little bit of the chaotic nature of the particles. +static qbyte dottexture[8][8] = +{ + {0,0,0,0,0,0,0,0}, + {0,0,0,1,1,0,0,0}, + {0,0,1,1,1,1,0,0}, + {0,1,1,1,1,1,1,0}, + {0,1,1,1,1,1,1,0}, + {0,0,1,1,1,1,0,0}, + {0,0,0,1,1,0,0,0}, + {0,0,0,0,0,0,0,0}, +}; +static qbyte exptexture[16][16] = +{ + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0}, + {0,0,0,1,1,1,1,1,3,1,1,2,1,0,0,0}, + {0,0,0,1,1,1,1,4,4,4,5,4,2,1,1,0}, + {0,0,1,1,6,5,5,8,6,8,3,6,3,2,1,0}, + {0,0,1,5,6,7,5,6,8,8,8,3,3,1,0,0}, + {0,0,0,1,6,8,9,9,9,9,4,6,3,1,0,0}, + {0,0,2,1,7,7,9,9,9,9,5,3,1,0,0,0}, + {0,0,2,4,6,8,9,9,9,9,8,6,1,0,0,0}, + {0,0,2,2,3,5,6,8,9,8,8,4,4,1,0,0}, + {0,0,1,2,4,1,8,7,8,8,6,5,4,1,0,0}, + {0,1,1,1,7,8,1,6,7,5,4,7,1,0,0,0}, + {0,1,2,1,1,5,1,3,4,3,1,1,0,0,0,0}, + {0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +}; +void R_InitParticleTexture (void) +{ + int x,y; + qbyte data[16*16][4]; + + // + // particle texture + // + particletexture = texture_extension_number++; + GL_Bind(particletexture); + + for (x=0 ; x<8 ; x++) + { + for (y=0 ; y<8 ; y++) + { + data[y*8+x][0] = 255; + data[y*8+x][1] = 255; + data[y*8+x][2] = 255; + data[y*8+x][3] = dottexture[x][y]*255; + } + } + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + + + + explosiontexture = texture_extension_number++; + GL_Bind(explosiontexture); + + for (x=0 ; x<16 ; x++) + { + for (y=0 ; y<16 ; y++) + { + data[y*16+x][0] = 255; + data[y*16+x][1] = 255; + data[y*16+x][2] = 255; + data[y*16+x][3] = exptexture[x][y]*255/9.0; + } + } + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} + +/* +=============== +R_Envmap_f + +Grab six views for environment mapping tests +=============== +*/ +void R_Envmap_f (void) +{ + qbyte buffer[256*256*4]; + + glDrawBuffer (GL_FRONT); + glReadBuffer (GL_FRONT); + envmap = true; + + r_refdef.vrect.x = 0; + r_refdef.vrect.y = 0; + r_refdef.vrect.width = 256; + r_refdef.vrect.height = 256; + + r_refdef.viewangles[0] = 0; + r_refdef.viewangles[1] = 0; + r_refdef.viewangles[2] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env0.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[1] = 90; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env1.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[1] = 180; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env2.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[1] = 270; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env3.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[0] = -90; + r_refdef.viewangles[1] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env4.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[0] = 90; + r_refdef.viewangles[1] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env5.rgb", buffer, sizeof(buffer)); + + envmap = false; + glDrawBuffer (GL_BACK); + glReadBuffer (GL_BACK); + GL_EndRendering (); +} + + + + + + + + + + + + +qboolean GenerateNormalisationCubeMap() +{ + unsigned char data[32*32*3]; + + //some useful variables + int size=32; + float offset=0.5f; + float halfSize=16.0f; + vec3_t tempVector; + unsigned char * bytePtr; + + int i, j; + + //positive x + bytePtr=data; + + for(j=0; jnext) + { + texname = shad->editorname; + if (!*texname) + continue; + COM_StripExtension(shad->name, base); + base[15]=0; + for (i =0; i < entries; i++) + if (!strcmp(entry[entries].name, base)) + break; + if (i != entries) + { + Con_Printf("Skipped %s - duplicated shrunken name\n", texname); + continue; + } + entry[entries].offset = ftell(f); + entry[entries].dsize = entry[entries].size = 0; + entry[entries].type = TYP_MIPTEX; + entry[entries].cmprs = 0; + entry[entries].dummy = 0; + strcpy(entry[entries].name, base); + + strcpy(dummymip.name, base); + + { + + qbyte *data; + int h; + float x, xi; + float y, yi; + + char *path[] ={ + "%s", + "override/%s.tga", + "override/%s.pcx", + "%s.tga", + "progs/%s"}; + for (h = 0, buf=NULL; h < sizeof(path)/sizeof(char *); h++) + { + buf = COM_LoadStackFile(va(path[h], texname), stack, 1024*1024*4+1024); + if (buf) + break; + } + if (!buf) + { + Con_Printf("Failed to find texture \"%s\"\n", texname); + continue; + } + + +data = ReadTargaFile(buf, com_filesize, &width, &height, false); +if (!data) +{ + BZ_Free(data); + Con_Printf("Skipped %s - file type not supported (bad bpp?)\n", texname); + continue; +} + + dummymip.width = (int)(width/scale) & ~0xf; + dummymip.height = (int)(height/scale) & ~0xf; + if (dummymip.width<=0) + dummymip.width=16; + if (dummymip.height<=0) + dummymip.height=16; + + dummymip.offsets[0] = sizeof(dummymip); + dummymip.offsets[1] = dummymip.offsets[0]+dummymip.width*dummymip.height; + dummymip.offsets[2] = dummymip.offsets[1]+dummymip.width/2*dummymip.height/2; + dummymip.offsets[3] = dummymip.offsets[2]+dummymip.width/4*dummymip.height/4; + entry[entries].dsize = entry[entries].size = dummymip.offsets[3]+dummymip.width/8*dummymip.height/8; + + xi = (float)width/dummymip.width; + yi = (float)height/dummymip.height; + + + fwrite(&dummymip, 1, sizeof(dummymip), f); + outmip=mip; + for (outmip=mip, y = 0; y < height; y+=yi) + for (x = 0; x < width; x+=xi) + { + *outmip++ = GetPalette( data[(int)(x+y*width)*4+0], + data[(int)(x+y*width)*4+1], + data[(int)(x+y*width)*4+2]); + } + fwrite(mip, dummymip.width, dummymip.height, f); + for (outmip=mip, y = 0; y < height; y+=yi*2) + for (x = 0; x < width; x+=xi*2) + { + *outmip++ = GetPalette( data[(int)(x+y*width)*4+0], + data[(int)(x+y*width)*4+1], + data[(int)(x+y*width)*4+2]); + } + fwrite(mip, dummymip.width/2, dummymip.height/2, f); + for (outmip=mip, y = 0; y < height; y+=yi*4) + for (x = 0; x < width; x+=xi*4) + { + *outmip++ = GetPalette( data[(int)(x+y*width)*4+0], + data[(int)(x+y*width)*4+1], + data[(int)(x+y*width)*4+2]); + } + fwrite(mip, dummymip.width/4, dummymip.height/4, f); + for (outmip=mip, y = 0; y < height; y+=yi*8) + for (x = 0; x < width; x+=xi*8) + { + *outmip++ = GetPalette( data[(int)(x+y*width)*4+0], + data[(int)(x+y*width)*4+1], + data[(int)(x+y*width)*4+2]); + } + fwrite(mip, dummymip.width/8, dummymip.height/8, f); + + BZ_Free(data); + } + entries++; + Con_Printf("Added %s\n", base); + GLSCR_UpdateScreen(); + } + + wad2.offset = ftell(f); + wad2.num = entries; + fwrite(entry, entries, sizeof(wad2entry_t), f); + fseek(f, 0, SEEK_SET); + fwrite(&wad2, 1, sizeof(wad2_t), f); + fclose(f); + + + BZ_Free(mip); +// BZ_Free(initbuf); + BZ_Free(stack); + + Con_Printf("Written %i mips to textures.wad\n", entries); +} +*/ +void GLR_TimeRefresh_f (void); + +extern cvar_t gl_bump; +extern cvar_t r_stains, r_stainfadetime, r_stainfadeammount; + +void GLR_DeInit (void) +{ + Cmd_RemoveCommand ("timerefresh"); + Cmd_RemoveCommand ("envmap"); + Cmd_RemoveCommand ("pointfile"); + + Cmd_RemoveCommand ("makewad"); + + GLDraw_DeInit(); + + GLSurf_DeInit(); +} + +void GLR_Init (void) +{ + Cmd_AddRemCommand ("timerefresh", GLR_TimeRefresh_f); + Cmd_AddRemCommand ("envmap", R_Envmap_f); + Cmd_AddRemCommand ("pointfile", R_ReadPointFile_f); + +// Cmd_AddRemCommand ("makewad", R_MakeTexWad_f); + + R_InitBubble(); + + GLR_ReInit(); +} + +/* +=============== +R_TranslatePlayerSkin + +Translates a skin texture by the per-player color lookup +=============== +*/ +void R_TranslatePlayerSkin (int playernum) +{ + int top, bottom; + qbyte translate[256]; + unsigned translate32[256]; + int i, j; + qbyte *original; + unsigned pixels[512*256], *out; + unsigned scaled_width, scaled_height; + int inwidth, inheight; + int tinwidth, tinheight; + qbyte *inrow; + unsigned frac, fracstep; + player_info_t *player; + extern qbyte *player_8bit_texels/*[320*200]*/; + char s[512]; + + GL_DisableMultitexture(); + + player = &cl.players[playernum]; + if (!player->name[0]) + return; + + strcpy(s, Info_ValueForKey(player->userinfo, "skin")); + COM_StripExtension(s, s); + if (player->skin && !stricmp(s, player->skin->name)) + player->skin = NULL; + + if (player->_topcolor != player->topcolor || + player->_bottomcolor != player->bottomcolor || !player->skin) { + player->_topcolor = player->topcolor; + player->_bottomcolor = player->bottomcolor; + + top = player->topcolor; + bottom = player->bottomcolor; + top = (top < 0) ? 0 : ((top > 13) ? 13 : top); + bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom); + top *= 16; + bottom *= 16; + + for (i=0 ; i<256 ; i++) + translate[i] = i; + + for (i=0 ; i<16 ; i++) + { + if (top < 128) // the artists made some backwards ranges. sigh. + translate[TOP_RANGE+i] = top+i; + else + translate[TOP_RANGE+i] = top+15-i; + + if (bottom < 128) + translate[BOTTOM_RANGE+i] = bottom+i; + else + translate[BOTTOM_RANGE+i] = bottom+15-i; + } + + // + // locate the original skin pixels + // + // real model width + tinwidth = 296; + tinheight = 194; + + if (!player->skin) + Skin_Find(player); + if ((original = Skin_Cache8(player->skin)) != NULL) { + //skin data width + inwidth = player->skin->width; + inheight = player->skin->height; + } else { + original = player_8bit_texels; + inwidth = 296; + inheight = 194; + } + //tinwidth = 251&~3; + //tinheight = 194&~3; + //tinwidth = 319&~3; + //tinheight = 199&~3; + + if (!original) //can't. + return; + + + // because this happens during gameplay, do it fast + // instead of sending it through gl_upload 8 + GL_Bind(playertextures + playernum); + + #if 0 + s = 320*200; + qbyte translated[320*200]; + + for (i=0 ; iskinwidth, paliashdr->skinheight, + false, false, true); + #endif + + scaled_width = gl_max_size.value < 512 ? gl_max_size.value : 512; + scaled_height = gl_max_size.value < 256 ? gl_max_size.value : 256; + // allow users to crunch sizes down even more if they want + scaled_width >>= (int)gl_playermip.value; + scaled_height >>= (int)gl_playermip.value; + + if (scaled_width < 8) + scaled_width = 8; + if (scaled_height < 8) + scaled_height = 8; +#ifdef GL_USE8BITTEX +#ifdef GL_EXT_paletted_texture + if (GLVID_Is8bit()) + {// 8bit texture upload + qbyte *out2; + + out2 = (qbyte *)pixels; + memset(pixels, 0, sizeof(pixels)); + fracstep = tinwidth*0x10000/scaled_width; + for (i=0 ; i> 1; + for (j=0 ; j>16]]; + frac += fracstep; + out2[j+1] = translate[inrow[frac>>16]]; + frac += fracstep; + out2[j+2] = translate[inrow[frac>>16]]; + frac += fracstep; + out2[j+3] = translate[inrow[frac>>16]]; + frac += fracstep; + } + } + + GL_Upload8_EXT ((qbyte *)pixels, scaled_width, scaled_height, false, false); + return; + } +#endif +#endif + +#ifdef Q2BSP + if (cls.q2server) + { + extern unsigned char d_q28to24table[768]; + for (i=0 ; i<256 ; i++) + { + translate32[i] = d_q28to24table[i*3] | + (d_q28to24table[i*3+1]<<8) | + (d_q28to24table[i*3+2]<<16) | + 255<<24; + } + } + else +#endif + for (i=0 ; i<256 ; i++) + translate32[i] = d_8to24rgbtable[translate[i]]; + + out = pixels; + memset(pixels, 0, sizeof(pixels)); + fracstep = tinwidth*0x10000/scaled_width; + for (i=0 ; i> 1; + for (j=0 ; j>16]]; + frac += fracstep; + out[j+1] = translate32[inrow[frac>>16]]; + frac += fracstep; + out[j+2] = translate32[inrow[frac>>16]]; + frac += fracstep; + out[j+3] = translate32[inrow[frac>>16]]; + frac += fracstep; + } + } + + glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, + scaled_width, scaled_height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, pixels); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } +} + +/* +=============== +R_NewMap +=============== +*/ +void GLR_NewMap (void) +{ + extern cvar_t host_mapname; + int i; +/* + if (cl.worldmodel->fromgame == fg_quake3 && cls.netchan.remote_address.type != NA_LOOPBACK) + { + if (!cls.allow_cheats) + { + CL_Disconnect(); + Host_EndGame("\n\nThe quake3 map implementation is still experimental and contains many bugs that could be considered cheats. Therefore, the engine is handicapped to quake3 maps only when hosting - it's single player only.\n\nYou can allow it on the server by activating cheats, at which point this check will be ignored\n"); + return; + } +// Cbuf_AddText("disconnect\n", RESTRICT_LOCAL); + } +*/ + + for (i=0 ; i<256 ; i++) + d_lightstylevalue[i] = 264; // normal light value + + memset (&r_worldentity, 0, sizeof(r_worldentity)); + r_worldentity.model = cl.worldmodel; + + Cvar_Set(&host_mapname, cl.worldmodel->name); + +// clear out efrags in case the level hasn't been reloaded +// FIXME: is this one short? + for (i=0 ; inumleafs ; i++) + cl.worldmodel->leafs[i].efrags = NULL; + + r_viewleaf = NULL; + r_viewcluster = -1; + r_oldviewcluster = 0; + r_viewcluster2 = -1; + R_ClearParticles (); + GLR_WipeStains(); + + GL_BuildLightmaps (); + + // identify sky texture + if (cl.worldmodel->fromgame != fg_quake2 && cl.worldmodel->fromgame != fg_quake3) + { + skytexturenum = -1; + mirrortexturenum = -1; + } + for (i=0 ; inumtextures ; i++) + { + if (!cl.worldmodel->textures[i]) + continue; + if (!Q_strncmp(cl.worldmodel->textures[i]->name,"sky",3) ) + skytexturenum = i; + if (!Q_strncmp(cl.worldmodel->textures[i]->name,"window02_1",10) ) + mirrortexturenum = i; + cl.worldmodel->textures[i]->texturechain = NULL; + } + +//#ifdef QUAKE2 + R_LoadSkys (); +//#endif + + UI_Reset(); +} + +void GLR_PreNewMap(void) +{ +} + + +/* +==================== +R_TimeRefresh_f + +For program optimization +==================== +*/ +void GLR_TimeRefresh_f (void) +{ + int i; + float start, stop, time; + + glDrawBuffer (GL_FRONT); + glFinish (); + + start = Sys_DoubleTime (); + for (i=0 ; i<128 ; i++) + { + r_refdef.viewangles[1] = i/128.0*360.0; + R_RenderView (); + } + + glFinish (); + stop = Sys_DoubleTime (); + time = stop-start; + Con_Printf ("%f seconds (%f fps)\n", time, 128/time); + + glDrawBuffer (GL_BACK); + GL_EndRendering (); +} + +#ifndef SWQUAKE +void D_FlushCaches (void) +{ +} +#endif + + diff --git a/engine/gl/gl_rsurf.c b/engine/gl/gl_rsurf.c new file mode 100644 index 00000000..2f059981 --- /dev/null +++ b/engine/gl/gl_rsurf.c @@ -0,0 +1,3510 @@ +/* +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. + +*/ +// r_surf.c: surface-related refresh code + +#include "quakedef.h" +#include "glquake.h" +#include "shader.h" +#include "renderque.h" +#include + +int skytexturenum; + +extern cvar_t gl_bump; + +extern qbyte areabits[MAX_Q2MAP_AREAS/8]; + +model_t *currentmodel; + + +int lightmap_bytes; // 1, 3 or 4 + +int *lightmap_textures; +int *deluxmap_textures; +int detailtexture; + +#define MAX_LIGHTMAP_SIZE 256 + +vec3_t blocknormals[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE]; +unsigned blocklights[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE]; +#ifdef PEXT_LIGHTSTYLECOL +unsigned greenblklights[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE]; +unsigned blueblklights[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE]; +#endif + +lightmapinfo_t **lightmap; +int numlightmaps; + +msurface_t *r_alpha_surfaces = NULL; + +mleaf_t *r_vischain; // linked list of visible leafs + +void R_RenderDynamicLightmaps (msurface_t *fa); + +extern cvar_t gl_detail; +extern cvar_t r_stains; +extern cvar_t r_loadlits; +extern cvar_t r_stainfadetime; +extern cvar_t r_stainfadeammount; + +extern cvar_t gl_waterripples; +extern cvar_t gl_lightmapmode; + + +//radius, x y z, r g b +void GLR_StainSurf (msurface_t *surf, float *parms) +{ + int sd, td; + float dist, rad, minlight; + float change; + vec3_t impact, local; + int s, t; + int i; + int smax, tmax; + float amm; + mtexinfo_t *tex; + +#define stain(x) \ + \ + change = amm*parms[4+x]; \ + if (change < 0) \ + { if(change<-128)change=-128; \ + if (stainbase[(s)*3+x] < change) \ + {} \ + else if (stainbase[(s)*3+x] < 0) \ + stainbase[(s)*3+x] = change; \ + else \ + stainbase[(s)*3+x] += change; \ + } \ + else \ + { if(change>127)change=127; \ + if (stainbase[(s)*3+x] > change) \ + {} \ + else if (stainbase[(s)*3+x] > 0) \ + stainbase[(s)*3+x] = change; \ + else \ + stainbase[(s)*3+x] += change; \ + } + + + + + + + stmap *stainbase; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + tex = surf->texinfo; + + stainbase = lightmap[surf->lightmaptexturenum]->stainmaps; + stainbase += (surf->light_t * LMBLOCK_WIDTH + surf->light_s) * 3; + + + + rad = *parms; + dist = DotProduct ((parms+1), surf->plane->normal) - surf->plane->dist; + rad -= fabs(dist); + minlight = 0; + if (rad < minlight) //not hit + return; + minlight = rad - minlight; + + for (i=0 ; i<3 ; i++) + { + impact[i] = (parms+1)[i] - surf->plane->normal[i]*dist; + } + + local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; + local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; + + local[0] -= surf->texturemins[0]; + local[1] -= surf->texturemins[1]; + + for (t = 0 ; t td) + dist = sd + (td>>1); + else + dist = td + (sd>>1); + if (dist < minlight) + { + amm = (rad - dist); + stain(0); + stain(1); + stain(2); + + surf->stained = true; + } + } + stainbase += 3*LMBLOCK_WIDTH; + } + + if (surf->stained) + surf->cached_dlight=-1; +} + +//combination of R_AddDynamicLights and R_MarkLights +/* +void GLR_StainNode (mnode_t *node, float *parms) +{ + mplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + + if (node->contents < 0) + return; + + splitplane = node->plane; + dist = DotProduct ((parms+1), splitplane->normal) - splitplane->dist; + + if (dist > (*parms)) + { + GLR_StainNode (node->children[0], parms); + return; + } + if (dist < (-*parms)) + { + GLR_StainNode (node->children[1], parms); + return; + } + +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags&~(SURF_DONTWARP|SURF_PLANEBACK)) + continue; + GLR_StainSurf(surf, parms); + } + + GLR_StainNode (node->children[0], parms); + GLR_StainNode (node->children[1], parms); +} +*/ + +void GLR_StainQ3Node (mnode_t *node, float *parms) +{ +// mplane_t *splitplane; +// float dist; + int i; + + if (node->contents != -1) + { + msurface_t **mark; + mleaf_t *leaf; + + // mark the polygons + leaf = (mleaf_t *)node; + mark = leaf->firstmarksurface; + for (i=0 ; inummarksurfaces ; i++) + { + GLR_StainSurf(*mark++, parms); + } + + return; + } + /* + splitplane = node->plane; + dist = DotProduct ((parms+1), splitplane->normal) - splitplane->dist; + + if (dist > (*parms)) + { + GLR_StainQ2Node (node->children[0], parms); + return; + } + if (dist < (-*parms)) + { + GLR_StainQ2Node (node->children[1], parms); + return; + }*/ + + GLR_StainQ3Node (node->children[0], parms); + GLR_StainQ3Node (node->children[1], parms); +} + +void GLR_AddStain(vec3_t org, float red, float green, float blue, float radius) +{ + physent_t *pe; + int i; + + float parms[7]; + if (!cl.worldmodel || !r_stains.value) + return; + parms[0] = radius; + parms[1] = org[0]; + parms[2] = org[1]; + parms[3] = org[2]; + parms[4] = red; + parms[5] = green; + parms[6] = blue; + + + cl.worldmodel->funcs.StainNode(cl.worldmodel->nodes+cl.worldmodel->hulls[0].firstclipnode, parms); + + //now stain bsp models other than world. + + for (i=1 ; i< pmove.numphysent ; i++) //0 is world... + { + pe = &pmove.physents[i]; + if (pe->model && pe->model->surfaces == cl.worldmodel->surfaces) + { + parms[1] = org[0] - pe->origin[0]; + parms[2] = org[1] - pe->origin[1]; + parms[3] = org[2] - pe->origin[2]; + pe->model->funcs.StainNode(pe->model->nodes+pe->model->hulls[0].firstclipnode, parms); + } + } +} + +void GLR_WipeStains(void) +{ + int i; + for (i = 0; i < numlightmaps; i++) + { + if (!lightmap[i]) + break; + memset(lightmap[i]->stainmaps, 0, sizeof(lightmap[i]->stainmaps)); + } +} + +void GLR_LessenStains(void) +{ + int i; + msurface_t *surf; + + int smax, tmax; + int s, t; + stmap *stain; + int stride; + int ammount; + + static float time; + + if (!r_stains.value) + return; + + time += host_frametime; + if (time < r_stainfadetime.value) + return; + time-=r_stainfadetime.value; + + ammount = r_stainfadeammount.value; + + surf = cl.worldmodel->surfaces; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->stained) + { + surf->cached_dlight=-1;//nice hack here... + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + + stain = lightmap[surf->lightmaptexturenum]->stainmaps; + stain += (surf->light_t * LMBLOCK_WIDTH + surf->light_s) * 3; + + stride = (LMBLOCK_WIDTH-smax)*3; + + surf->stained = false; + + smax*=3; + + for (t = 0 ; tstained=true; + } + else if (*stain > ammount) //positive values reduce to 0 + { + *stain -= ammount; + surf->stained=true; + } + else //close to 0 or 0 already. + *stain = 0; + + stain++; + } + } + } + } +} + +/* +=============== +R_AddDynamicLights +=============== +*/ +void GLR_AddDynamicLights (msurface_t *surf) +{ + int lnum; + int sd, td; + float dist, rad, minlight; + vec3_t impact, local; + int s, t; + int i; + int smax, tmax; + mtexinfo_t *tex; + float a; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + tex = surf->texinfo; + + for (lnum=0 ; lnumdlightbits & (1<plane->normal) - + surf->plane->dist; + rad -= fabs(dist); + minlight = cl_dlights[lnum].minlight; + if (rad < minlight) + continue; + minlight = rad - minlight; + + for (i=0 ; i<3 ; i++) + { + impact[i] = cl_dlights[lnum].origin[i] - + surf->plane->normal[i]*dist; + } + + local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; + local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; + + local[0] -= surf->texturemins[0]; + local[1] -= surf->texturemins[1]; + + a = 256*(cl_dlights[lnum].color[0]*1.5 + cl_dlights[lnum].color[1]*2.95 + cl_dlights[lnum].color[2]*0.55); + + for (t = 0 ; t td) + dist = sd + (td>>1); + else + dist = td + (sd>>1); + if (dist < minlight) + blocklights[t*smax + s] += (rad - dist)*a; + } + } + } +} + +void GLR_AddDynamicLightNorms (msurface_t *surf) +{ + int lnum; + int sd, td; + float dist, rad, minlight; + vec3_t impact, local; + int s, t; + int i; + int smax, tmax; + mtexinfo_t *tex; + float a; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + tex = surf->texinfo; + + for (lnum=0 ; lnumdlightbits & (1<plane->normal) - + surf->plane->dist; + rad -= fabs(dist); + minlight = cl_dlights[lnum].minlight; + if (rad < minlight) + continue; + minlight = rad - minlight; + + for (i=0 ; i<3 ; i++) + { + impact[i] = cl_dlights[lnum].origin[i] - + surf->plane->normal[i]*dist; + } + + local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; + local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; + + local[0] -= surf->texturemins[0]; + local[1] -= surf->texturemins[1]; + + a = 256*(cl_dlights[lnum].color[0]*1.5 + cl_dlights[lnum].color[1]*2.95 + cl_dlights[lnum].color[2]*0.55); + + for (t = 0 ; t td) + dist = sd + (td>>1); + else + dist = td + (sd>>1); + if (dist < minlight) + { +// blocknormals[t*smax + s][0] -= (rad - dist)*(impact[0]-local[0])/8192.0; +// blocknormals[t*smax + s][1] -= (rad - dist)*(impact[1]-local[1])/8192.0; + blocknormals[t*smax + s][2] += 0.5*blocknormals[t*smax + s][2]*(rad - dist)/256; + } + } + } + } +} + +#ifdef PEXT_LIGHTSTYLECOL +void GLR_AddDynamicLightsColours (msurface_t *surf) +{ + int lnum; + int sd, td; + float dist, rad, minlight; + vec3_t impact, local; + int s, t; + int i; + int smax, tmax; + mtexinfo_t *tex; +// float temp; + float r, g, b; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + tex = surf->texinfo; + + for (lnum=0 ; lnumdlightbits & (1<plane->normal) - + surf->plane->dist; + rad -= fabs(dist); + minlight = cl_dlights[lnum].minlight; + if (rad < minlight) + continue; + minlight = rad - minlight; + + for (i=0 ; i<3 ; i++) + { + impact[i] = cl_dlights[lnum].origin[i] - + surf->plane->normal[i]*dist; + } + + local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; + local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; + + local[0] -= surf->texturemins[0]; + local[1] -= surf->texturemins[1]; + + r = cl_dlights[lnum].color[0]*3*256; + g = cl_dlights[lnum].color[1]*3*256; + b = cl_dlights[lnum].color[2]*3*256; + +/* if (cl_dlights[lnum].type == 1) //a wierd effect. + { + for (t = 0 ; t td) + dist = sd + (td>>1); + else + dist = td + (sd>>1); + if (dist < minlight) + { + blocklights[t*smax + s] += 2*sin(dist/10+cl.time*20)*(rad - dist)*256 * cl_dlights[lnum].colour[0]*3; + greenblklights[t*smax + s] += 2*sin(M_PI/3+dist/10+cl.time*20)*(rad - dist)*256 * cl_dlights[lnum].colour[1]*3; + blueblklights[t*smax + s] += 2*sin(2*M_PI/3+dist/10+cl.time*20)*(rad - dist)*256 * cl_dlights[lnum].colour[2]*3; + } + } + } + } + else + { +*/ for (t = 0 ; t td) + dist = sd + (td>>1); + else + dist = td + (sd>>1); + if (dist < minlight) + { + blocklights[t*smax + s] += (rad - dist)*r; + greenblklights[t*smax + s] += (rad - dist)*g; + blueblklights[t*smax + s] += (rad - dist)*b; + } + } + } +// } + } +} +#endif + + + +void GLR_BuildDeluxMap (msurface_t *surf, qbyte *dest) +{ + int smax, tmax; + int i, j, size; + qbyte *lightmap; + qbyte *deluxmap; + unsigned scale; + int maps; + extern cvar_t r_ambient; + float intensity; + vec_t *bnorm; + vec3_t temp; + + int stride = LMBLOCK_WIDTH*3; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + size = smax*tmax; + lightmap = surf->samples; + + // set to full bright if no light data + if (!currentmodel->deluxdata) + { + for (i=0 ; iorientation[2][0]; + blocknormals[i][1] = 0.8;//surf->orientation[2][1]; + blocknormals[i][2] = 1;//surf->orientation[2][2]; + } + goto store; + } + + if (currentmodel->rgblighting) + deluxmap = surf->samples - currentmodel->lightdata + currentmodel->deluxdata; + else + deluxmap = (surf->samples - currentmodel->lightdata)*3 + currentmodel->deluxdata; + + +// clear to no light + for (i=0 ; irgblighting) + { + deluxmap = surf->samples - currentmodel->lightdata + currentmodel->deluxdata; + + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + for (i=0 ; isamples - currentmodel->lightdata)*3 + currentmodel->deluxdata; + + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + for (i=0 ; idlightframe == r_framecount) +// GLR_AddDynamicLightNorms (surf); + +// bound, invert, and shift + + stride -= smax*3; + + bnorm = blocknormals[0]; + for (i=0 ; icached_dlight = (surf->dlightframe == r_framecount); + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + size = smax*tmax; + lightmap = surf->samples; + + if (size > MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE) + { //fixme: fill in? + Con_Printf("Lightmap too large\n"); + return; + } + + if (currentmodel->deluxdata) + GLR_BuildDeluxMap(surf, deluxdest); + + +#ifdef PEXT_LIGHTSTYLECOL + if (gl_lightmap_format == GL_RGBA || gl_lightmap_format == GL_RGB) + { + // set to full bright if no light data + if (r_fullbright.value>0) //not qw + { + for (i=0 ; idlightframe == r_framecount) + GLR_AddDynamicLightsColours (surf); + } + goto store; + } + if (!currentmodel->lightdata) + { + for (i=0 ; idlightframe == r_framecount) + GLR_AddDynamicLightsColours (surf); + goto store; + } + +// clear to no light + t = r_ambient.value*255; + for (i=0 ; ifromgame == fg_quake3) //rgb + { + /* for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) //no light styles in q3 apparently. + { + scale = d_lightstylevalue[surf->styles[maps]]; + surf->cached_light[maps] = scale; // 8.8 fraction + surf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colour; + } + */ + for (i = 0; i < tmax; i++) //q3 maps store thier light in a block fashion, q1/q2/hl store it in a linear fashion. + { + for (j = 0; j < smax; j++) + { + blocklights[i*smax+j] = 255*lightmap[(i*LMBLOCK_WIDTH+j)*3]; + greenblklights[i*smax+j] = 255*lightmap[(i*LMBLOCK_WIDTH+j)*3+1]; + blueblklights[i*smax+j] = 255*lightmap[(i*LMBLOCK_WIDTH+j)*3+2]; + } + } +// memset(blocklights, 255, sizeof(blocklights)); + } + else if (currentmodel->rgblighting) //rgb + { + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + surf->cached_light[maps] = scale; // 8.8 fraction + surf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colour; + + + if (cl_lightstyle[surf->styles[maps]].colour == 7) //hopefully a faster alternative. + { + for (i=0 ; istyles[maps]].colour & 1) + for (i=0 ; istyles[maps]].colour & 2) + for (i=0 ; istyles[maps]].colour & 4) + for (i=0 ; istyles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + surf->cached_light[maps] = scale; // 8.8 fraction + surf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colour; + + if (cl_lightstyle[surf->styles[maps]].colour & 1) + for (i=0 ; istyles[maps]].colour & 2) + for (i=0 ; istyles[maps]].colour & 4) + for (i=0 ; idlightframe == r_framecount) + GLR_AddDynamicLightsColours (surf); + } + else + { +#endif + // set to full bright if no light data + if (r_fullbright.value || !currentmodel->lightdata) + { + for (i=0 ; irgblighting) //rgb + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]/3; + surf->cached_light[maps] = scale; // 8.8 fraction + for (i=0 ; istyles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + surf->cached_light[maps] = scale; // 8.8 fraction + for (i=0 ; idlightframe == r_framecount) + GLR_AddDynamicLights (surf); +#ifdef PEXT_LIGHTSTYLECOL + } +#endif + +// bound, invert, and shift +store: + switch (gl_lightmap_format) + { +#ifdef PEXT_LIGHTSTYLECOL + case GL_RGBA: + stride -= (smax<<2); + bl = blocklights; + blg = greenblklights; + blb = blueblklights; + + if (!r_stains.value) + isstained = false; + else + isstained = surf->stained; + +/* if (!gl_lightcomponantreduction.value) + { + for (i=0 ; i>= 7; + if (t > 255) + dest[0] = 0; + else if (t < 0) + dest[0] = 256; + else + dest[0] = (255-t); + + t = *blg++; + t >>= 7; + if (t > 255) + dest[1] = 0; + else if (t < 0) + dest[1] = 256; + else + dest[1] = (255-t); + + t = *blb++; + t >>= 7; + if (t > 255) + dest[2] = 0; + else if (t < 0) + dest[2] = 256; + else + dest[2] = (255-t); + + dest[3] = 0;//(dest[0]+dest[1]+dest[2])/3; + dest += 4; + } + } + } + else +*/ { + stmap *stain; + for (i=0 ; i>= 7; + + g = *blg++; + g >>= 7; + + b = *blb++; + b >>= 7; + + if (isstained) //do we need to add the stain? + { + r += *stain++; + g += *stain++; + b += *stain++; + } + + cr = 0; + cg = 0; + cb = 0; + + if (r > 255) //ak too much red + { + cr -= (255-r)/2; + cg += (255-r)/4; //reduce it, and indicate to drop the others too. + cb += (255-r)/4; + r = 255; + } +// else if (r < 0) +// r = 0; + + if (g > 255) + { + cr += (255-g)/4; + cg -= (255-g)/2; + cb += (255-g)/4; + g = 255; + } +// else if (g < 0) +// g = 0; + + if (b > 255) + { + cr += (255-b)/4; + cg += (255-b)/4; + cb -= (255-b)/2; + b = 255; + } +// else if (b < 0) +// b = 0; + //* + if ((r+cr) > 255) + dest[0] = 0; //inverse lighting + else if ((r+cr) < 0) + dest[0] = 255; + else + dest[0] = 255-(r+cr); + + if ((g+cg) > 255) + dest[1] = 0; + else if ((g+cg) < 0) + dest[1] = 255; + else + dest[1] = 255-(g+cg); + + if ((b+cb) > 255) + dest[2] = 0; + else if ((b+cb) < 0) + dest[2] = 255; + else + dest[2] = 255-(b+cb); +/*/ + if ((r+cr) > 255) + dest[0] = 255; //non-inverse lighting + else if ((r+cr) < 0) + dest[0] = 0; + else + dest[0] = (r+cr); + + if ((g+cg) > 255) + dest[1] = 255; + else if ((g+cg) < 0) + dest[1] = 0; + else + dest[1] = (g+cg); + + if ((b+cb) > 255) + dest[2] = 255; + else if ((b+cb) < 0) + dest[2] = 0; + else + dest[2] = (b+cb); +*/ + + + + dest[3] = (dest[0]+dest[1]+dest[2])/3; //alpha?!?! + dest += 4; + } + } + } + break; + + case GL_RGB: + stride -= smax*3; + bl = blocklights; + blg = greenblklights; + blb = blueblklights; + + if (!r_stains.value) + isstained = false; + else + isstained = surf->stained; + +/* if (!gl_lightcomponantreduction.value) + { + for (i=0 ; i>= 7; + if (t > 255) + dest[0] = 0; + else if (t < 0) + dest[0] = 256; + else + dest[0] = (255-t); + + t = *blg++; + t >>= 7; + if (t > 255) + dest[1] = 0; + else if (t < 0) + dest[1] = 256; + else + dest[1] = (255-t); + + t = *blb++; + t >>= 7; + if (t > 255) + dest[2] = 0; + else if (t < 0) + dest[2] = 256; + else + dest[2] = (255-t); + + dest += 3; + } + } + } + else +*/ { + stmap *stain; + for (i=0 ; i>= 7; + + g = *blg++; + g >>= 7; + + b = *blb++; + b >>= 7; + + if (isstained) //do we need to add the stain? + { + r += *stain++; + g += *stain++; + b += *stain++; + } + + cr = 0; + cg = 0; + cb = 0; + + if (r > 255) //ak too much red + { + cr -= (255-r)/2; + cg += (255-r)/4; //reduce it, and indicate to drop the others too. + cb += (255-r)/4; + r = 255; + } +// else if (r < 0) +// r = 0; + + if (g > 255) + { + cr += (255-g)/4; + cg -= (255-g)/2; + cb += (255-g)/4; + g = 255; + } +// else if (g < 0) +// g = 0; + + if (b > 255) + { + cr += (255-b)/4; + cg += (255-b)/4; + cb -= (255-b)/2; + b = 255; + } +// else if (b < 0) +// b = 0; + //* + if ((r+cr) > 255) + dest[0] = 0; //inverse lighting + else if ((r+cr) < 0) + dest[0] = 255; + else + dest[0] = 255-(r+cr); + + if ((g+cg) > 255) + dest[1] = 0; + else if ((g+cg) < 0) + dest[1] = 255; + else + dest[1] = 255-(g+cg); + + if ((b+cb) > 255) + dest[2] = 0; + else if ((b+cb) < 0) + dest[2] = 255; + else + dest[2] = 255-(b+cb); +/*/ + if ((r+cr) > 255) + dest[0] = 255; //non-inverse lighting + else if ((r+cr) < 0) + dest[0] = 0; + else + dest[0] = (r+cr); + + if ((g+cg) > 255) + dest[1] = 255; + else if ((g+cg) < 0) + dest[1] = 0; + else + dest[1] = (g+cg); + + if ((b+cb) > 255) + dest[2] = 255; + else if ((b+cb) < 0) + dest[2] = 0; + else + dest[2] = (b+cb); +// */ + dest += 3; + } + } + } + break; +#else + case GL_RGBA: + stride -= (smax<<2); + bl = blocklights; + for (i=0 ; i>= 7; + if (t > 255) + t = 255; + dest[3] = 255-t; + dest += 4; + } + } + break; +#endif + case GL_ALPHA: + case GL_LUMINANCE: + case GL_INTENSITY: + bl = blocklights; + for (i=0 ; i>= 7; + if (t > 255) + t = 255; + dest[j] = 255-t; + } + } + break; + default: + Sys_Error ("Bad lightmap format"); + } +} + +/* +=============== +R_TextureAnimation + +Returns the proper texture for a given time and base texture +=============== +*/ +texture_t *GLR_TextureAnimation (texture_t *base) +{ + int reletive; + int count; + + if (currententity->frame) + { + if (base->alternate_anims) + base = base->alternate_anims; + } + + if (!base->anim_total) + return base; + + reletive = (int)(cl.time*10) % base->anim_total; + + count = 0; + while (base->anim_min > reletive || base->anim_max <= reletive) + { + base = base->anim_next; + if (!base) + Sys_Error ("R_TextureAnimation: broken cycle"); + if (++count > 100) + Sys_Error ("R_TextureAnimation: infinite cycle"); + } + + return base; +} + + +/* +============================================================= + + BRUSH MODELS + +============================================================= +*/ + + +extern int solidskytexture; +extern int alphaskytexture; +extern float speedscale; // for top sky and bottom sky + +#if 0 +static void DrawGLWaterPoly (glpoly_t *p); +static void DrawGLWaterPolyLightmap (glpoly_t *p); +#endif + +qboolean mtexenabled = false; + +void GL_SelectTexture (GLenum target); + +void GL_DisableMultitexture(void) +{ + if (mtexenabled) { + glDisable(GL_TEXTURE_2D); + GL_SelectTexture(mtexid0); + mtexenabled = false; + } +} + +void GL_EnableMultitexture(void) +{ + if (gl_mtexable) { + GL_SelectTexture(mtexid1); + glEnable(GL_TEXTURE_2D); + mtexenabled = true; + } +} + +/* +================ +DrawGLWaterPoly + +Warp the vertex coordinates +================ +*/ +static void DrawGLWaterPoly (glpoly_t *p) +{ + int i; + float *v; + vec3_t nv; + + GL_DisableMultitexture(); + + glBegin (GL_TRIANGLE_FAN); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + + nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[2] = v[2]; + + glVertex3fv (nv); + } + glEnd (); +} +#if 0 +static void DrawGLWaterPolyLightmap (glpoly_t *p) +{ + int i; + float *v; + vec3_t nv; + + GL_DisableMultitexture(); + + glBegin (GL_TRIANGLE_FAN); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + + nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[2] = v[2]; + + glVertex3fv (nv); + } + glEnd (); +} +#endif +/* +================ +DrawGLPoly +================ +*/ +static void DrawGLPoly (glpoly_t *p) +{ + int i; + float *v; + + while(p) + { + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); + p=p->next; + } +} + + +/* +================ +R_BlendLightmaps +================ +*/ +#if 0 +static void R_BlendLightmaps (void) +{ + int i, j; + glpoly_t *p; + float *v; + glRect_t *theRect; + +#if 0 + if (r_fullbright.value) + return; +#endif + + glDepthMask (0); // don't bother writing Z + + if (gl_lightmap_format == GL_LUMINANCE || gl_lightmap_format == GL_RGB) + glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + else if (gl_lightmap_format == GL_INTENSITY) + { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4f (0,0,0,1); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else if (gl_lightmap_format == GL_RGBA) + { + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); + } + + if (!r_lightmap.value) + { + glEnable (GL_BLEND); + } + else + glDisable (GL_BLEND); + + for (i=0 ; ipolys; + if (!p) + continue; + lightmap[i]->polys = NULL; + GL_Bind(lightmap_textures[i]); + if (lightmap[i]->modified) + { + lightmap[i]->modified = false; + theRect = &lightmap[i]->rectchange; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmap[i]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes); + theRect->l = LMBLOCK_WIDTH; + theRect->t = LMBLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + for ( ; p ; p=p->chain) + { +// if (p->flags & SURF_UNDERWATER) +// DrawGLWaterPolyLightmap (p); + if (((r_viewleaf->contents==Q1CONTENTS_EMPTY && (p->flags & SURF_UNDERWATER)) || + (r_viewleaf->contents!=Q1CONTENTS_EMPTY && !(p->flags & SURF_UNDERWATER))) + && !(p->flags & SURF_DONTWARP)) + DrawGLWaterPolyLightmap (p); + else + { + glBegin (GL_POLYGON); + v = p->verts[0]; + for (j=0 ; jnumverts ; j++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + } + } + } + + glDisable (GL_BLEND); + if (gl_lightmap_format == GL_LUMINANCE) + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA || gl_lightmap_format == GL_RGB); + else if (gl_lightmap_format == GL_INTENSITY) + { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glColor4f (1,1,1,1); + } + else if (gl_lightmap_format == GL_RGBA) + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glDepthMask (1); // back to normal Z buffering +} +#endif + +/* +================ +R_RenderBrushPoly +================ +*/ +void R_RenderBrushPoly (msurface_t *fa) +{ + texture_t *t; + + c_brush_polys++; + + if (fa->flags & SURF_DRAWSKY) + { // warp texture, no lightmaps + EmitBothSkyLayers (fa); + return; + } + + t = GLR_TextureAnimation (fa->texinfo->texture); + GL_Bind (t->gl_texturenum); + + if (fa->flags & SURF_DRAWTURB) + { // warp texture, no lightmaps + EmitWaterPolys (fa); + glDisable(GL_BLEND); //to ensure. + return; + } + +//moved so lightmap is made first. + if (((r_viewleaf->contents==Q1CONTENTS_EMPTY && (fa->flags & SURF_UNDERWATER)) || + (r_viewleaf->contents!=Q1CONTENTS_EMPTY && !(fa->flags & SURF_UNDERWATER))) + && !(fa->flags & SURF_DONTWARP)) + DrawGLWaterPoly (fa->polys); + else + DrawGLPoly (fa->polys); +} + +/* +================ +R_RenderDynamicLightmaps +Multitexture +================ +*/ +void R_RenderDynamicLightmaps (msurface_t *fa) +{ + qbyte *base, *luxbase; stmap *stainbase; + int maps; + glRect_t *theRect; + int smax, tmax; + + if (!fa->polys) + return; + + c_brush_polys++; + + if (fa->lightmaptexturenum<0) + return; + + if (fa->flags & ( SURF_DRAWSKY | SURF_DRAWTURB) ) + return; + + if (fa->texinfo->flags & (SURF_SKY|SURF_TRANS33|SURF_TRANS66|SURF_WARP)) + return; + + if (fa->texinfo->flags & (TEX_SPECIAL)) + { + if (cl.worldmodel->fromgame == fg_halflife) + return; //some textures do this. + } + + + fa->polys->chain = lightmap[fa->lightmaptexturenum]->polys; + lightmap[fa->lightmaptexturenum]->polys = fa->polys; + + // check for lightmap modification + if (cl.worldmodel->fromgame != fg_quake3) //no lightstyles on q3 maps + { + for (maps = 0 ; maps < MAXLIGHTMAPS && fa->styles[maps] != 255 ; + maps++) + if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps] + #ifdef PEXT_LIGHTSTYLECOL + || cl_lightstyle[fa->styles[maps]].colour != fa->cached_colour[maps] + #endif + ) + goto dynamic; + } + + if (fa->dlightframe == r_framecount // dynamic this frame + || fa->cached_dlight) // dynamic previously + { +dynamic: + lightmap[fa->lightmaptexturenum]->modified = true; + + smax = (fa->extents[0]>>4)+1; + tmax = (fa->extents[1]>>4)+1; + + theRect = &lightmap[fa->lightmaptexturenum]->rectchange; + if (fa->light_t < theRect->t) { + if (theRect->h) + theRect->h += theRect->t - fa->light_t; + theRect->t = fa->light_t; + } + if (fa->light_s < theRect->l) { + if (theRect->w) + theRect->w += theRect->l - fa->light_s; + theRect->l = fa->light_s; + } + if ((theRect->w + theRect->l) < (fa->light_s + smax)) + theRect->w = (fa->light_s-theRect->l)+smax; + if ((theRect->h + theRect->t) < (fa->light_t + tmax)) + theRect->h = (fa->light_t-theRect->t)+tmax; + + + lightmap[fa->lightmaptexturenum]->deluxmodified = true; + theRect = &lightmap[fa->lightmaptexturenum]->deluxrectchange; + if (fa->light_t < theRect->t) { + if (theRect->h) + theRect->h += theRect->t - fa->light_t; + theRect->t = fa->light_t; + } + if (fa->light_s < theRect->l) { + if (theRect->w) + theRect->w += theRect->l - fa->light_s; + theRect->l = fa->light_s; + } + + if ((theRect->w + theRect->l) < (fa->light_s + smax)) + theRect->w = (fa->light_s-theRect->l)+smax; + if ((theRect->h + theRect->t) < (fa->light_t + tmax)) + theRect->h = (fa->light_t-theRect->t)+tmax; + + + base = lightmap[fa->lightmaptexturenum]->lightmaps; + base += fa->light_t * LMBLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; + luxbase = lightmap[fa->lightmaptexturenum]->deluxmaps; + luxbase += fa->light_t * LMBLOCK_WIDTH * 3 + fa->light_s * 3; + stainbase = lightmap[fa->lightmaptexturenum]->stainmaps; + stainbase += (fa->light_t * LMBLOCK_WIDTH + fa->light_s) * 3; + GLR_BuildLightMap (fa, base, luxbase, stainbase); + } +} + +/* +================ +R_MirrorChain +================ +*/ +void R_MirrorChain (msurface_t *s) +{ + if (mirror) + return; + mirror = true; + mirror_plane = s->plane; +} + + +/* +================ +R_DrawWaterSurfaces +================ +*/ +void GLR_DrawWaterSurfaces (void) +{ + int i; + msurface_t *s; + texture_t *t; + + if (r_wateralphaval == 1.0) + return; + + // + // go back to the world matrix + // + + glLoadMatrixf (r_world_matrix); + + if (r_wateralphaval < 1.0) { + glEnable (GL_BLEND); + glDisable (GL_ALPHA_TEST); + glColor4f (1,1,1,r_wateralphaval); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + + if (gl_waterripples.value) + { + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + } + + for (i=0 ; inumtextures ; i++) + { + t = cl.worldmodel->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + if ( !(s->flags & SURF_DRAWTURB ) ) + continue; + + // set modulate mode explicitly + + GL_Bind (t->gl_texturenum); + + for ( ; s ; s=s->texturechain) + EmitWaterPolys (s); + + t->texturechain = NULL; + } + + if (r_wateralphaval < 1.0) { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glColor4f (1,1,1,1); + glDisable (GL_BLEND); + } + + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + +} + + +static void GLR_DrawAlphaSurface(msurface_t *s) +{ + glPushMatrix(); + R_RotateForEntity(s->ownerent); + + GL_Bind(s->texinfo->texture->gl_texturenum); + + if (s->texinfo->flags & SURF_TRANS33) + glColor4f (1,1,1,0.33); + else if (s->texinfo->flags & SURF_TRANS66) + glColor4f (1,1,1,0.66); + else + { + if (s->flags & SURF_DRAWTURB) + { + glColor4f (1,1,1,1); + EmitWaterPolys (s); + } + else + { + if (gl_mtexable) + { + int i; + float *v; + glpoly_t *p; + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + GL_EnableMultitexture(); + GL_Bind(lightmap_textures[s->lightmaptexturenum]); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + p = s->polys; + + glColor4f (1,1,1,1); + while(p) + { + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + qglMTexCoord2fSGIS (mtexid0, v[3], v[4]); + qglMTexCoord2fSGIS (mtexid1, v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + p=p->next; + } + GL_DisableMultitexture(); + } + else + { + if (s->samples) //could do true vertex lighting... ? + glColor4ub (*s->samples,*s->samples,*s->samples,255); + else + glColor4f (1,1,1,1); + DrawGLPoly (s->polys); + } + } + + glPopMatrix(); + return; + } + + if (s->flags & SURF_DRAWTURB) + EmitWaterPolys (s); +// else if(s->texinfo->flags & SURF_FLOWING) // PGM 9/16/98 +// DrawGLFlowingPoly (s); // PGM + else + DrawGLPoly (s->polys); + + glPopMatrix(); +} + +void GLR_DrawAlphaSurfaces (void) +{ + msurface_t *s; + vec3_t v; + + // + // go back to the world matrix + // + + glLoadMatrixf (r_world_matrix); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glEnable(GL_ALPHA_TEST); + glDisable(GL_BLEND); + if (cl.worldmodel && cl.worldmodel->fromgame == fg_quake3) + { //this is a mahoosive hack. + //we need to use different blending modes for lights and 'rugs'... + //we could do this by seeing if a texture includes alpha - rug, or if it's fully solid - light. + glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_COLOR); + glDepthMask(0); //this makes no difference to the cheating. + + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + } + glColor4f (1,1,1,1); + for (s=r_alpha_surfaces ; s ; s=s->nextalphasurface) + { + if (s->flags&0x80000) + { + Con_Printf("Infinate alpha surface loop detected\n"); + break; + } + s->flags |= 0x80000; + if (*s->texinfo->texture->name == '{') + { //simple alpha testing. + + if (s->ownerent != currententity) + { + currententity = s->ownerent; + glPopMatrix(); + glPushMatrix(); + R_RotateForEntity(currententity); + } + + if (gl_mtexable) + { + int i; + float *v; + glpoly_t *p; + GL_Bind(s->texinfo->texture->gl_texturenum); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + GL_EnableMultitexture(); + GL_Bind(lightmap_textures[s->lightmaptexturenum]); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + p = s->polys; + + + while(p) + { + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + { + qglMTexCoord2fSGIS (mtexid0, v[3], v[4]); + qglMTexCoord2fSGIS (mtexid1, v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + p=p->next; + } + GL_DisableMultitexture(); + } + else + { + if (s->samples) //could do true vertex lighting... ? + glColor4ub (*s->samples,*s->samples,*s->samples,255); + else + glColor4f (1,1,1,1); + DrawGLPoly (s->polys); + glColor4f (1,1,1,1); + } + continue; + } + v[0] = s->plane->normal[0] * s->plane->dist+s->ownerent->origin[0]; + v[1] = s->plane->normal[1] * s->plane->dist+s->ownerent->origin[1]; + v[2] = s->plane->normal[2] * s->plane->dist+s->ownerent->origin[2]; + RQ_AddDistReorder((void*)GLR_DrawAlphaSurface, s, NULL, v); + } + for (s=r_alpha_surfaces ; s ; s=s->nextalphasurface) + { + if (!(s->flags&0x80000)) + break; + s->flags &= ~0x80000; + } + RQ_RenderDistAndClear(); + glDepthMask(1); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glColor4f (1,1,1,1); + glDisable (GL_BLEND); + + r_alpha_surfaces = NULL; + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +#if 0 +static void +vecMatMult(GLfloat vecIn[3], GLfloat m[16], GLfloat vecOut[3]) { + vecOut[0] = (vecIn[0]*m[ 0]) + (vecIn[1]*m[ 4]) + (vecIn[2]*m[ 8]) + m[12]; + vecOut[1] = (vecIn[0]*m[ 1]) + (vecIn[1]*m[ 5]) + (vecIn[2]*m[ 9]) + m[13]; + vecOut[2] = (vecIn[0]*m[ 2]) + (vecIn[1]*m[ 6]) + (vecIn[2]*m[10]) + m[14]; +} + +static void +matrixInvert(GLfloat in[16], GLfloat out[16]) +{ + // Transpose rotation + out[ 0] = in[ 0]; out[ 1] = in[ 4]; out[ 2] = in[ 8]; + out[ 4] = in[ 1]; out[ 5] = in[ 5]; out[ 6] = in[ 9]; + out[ 8] = in[ 2]; out[ 9] = in[ 6]; out[10] = in[10]; + + // Clear shearing terms + out[3] = 0.0f; out[7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f; + + // Translation is minus the dot of tranlation and rotations + out[12] = -(in[12]*in[ 0]) - (in[13]*in[ 1]) - (in[14]*in[ 2]); + out[13] = -(in[12]*in[ 4]) - (in[13]*in[ 5]) - (in[14]*in[ 6]); + out[14] = -(in[12]*in[ 8]) - (in[13]*in[ 9]) - (in[14]*in[10]); +} +#endif + +void VectorVectors(vec3_t forward, vec3_t right, vec3_t up); +/* +================ +DrawTextureChains +================ +*/ +#if 0 +static void DrawTextureChains (model_t *model, float alpha, vec3_t relativelightorigin) +{ + int i; + msurface_t *s, *last = NULL, *first=NULL, *cf; + texture_t *t; + + int vi; + glRect_t *theRect; + glpoly_t *p; + float *v; + + extern int gl_bumpmappingpossible; + extern int normalisationCubeMap; + qboolean bumpmapping=gl_bump.value && gl_bumpmappingpossible && (alpha == 1) && (normalisationCubeMap || currentmodel->deluxdata); + + if (model == cl.worldmodel && skytexturenum>=0) + { + t = model->textures[skytexturenum]; + if (t) + { + s = t->texturechain; + if (s) + { + t->texturechain = NULL; + R_DrawSkyChain (s); + } + } + } + if (alpha == 1) + { + glDisable(GL_BLEND); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } + else + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + if (currententity->drawflags & MLS_ABSLIGHT) + glColor4f(currententity->abslight/255.0f, currententity->abslight/255.0f, currententity->abslight/255.0f, alpha); + else + glColor4f(1, 1, 1, alpha); + + for (i=0 ; inumtextures ; i++) + { + t = model->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + t->texturechain = NULL; + if (i == skytexturenum && model == cl.worldmodel) + R_DrawSkyChain (s); + else if (i == mirrortexturenum && model == cl.worldmodel && r_mirroralpha.value != 1.0) + R_MirrorChain (s); + else + { + if ((s->flags & SURF_DRAWTURB) && r_wateralphaval != 1.0) + { + t->texturechain = s; + continue; // draw translucent water later + } + + if (last) + last->texturechain = s; + else + first = s; + + t = GLR_TextureAnimation (t); + + cf = s; + + if (gl_mtexable && alpha == 1) + { + if (s->lightmaptexturenum<0 || currententity->drawflags & MLS_ABSLIGHT) + { //vertex lighting required. + GL_DisableMultitexture(); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + for (s=cf ; s ; s=s->texturechain) + { + R_RenderBrushPoly (s); + } + continue; + } + + + if (cf->flags & SURF_DRAWTURB) + { + GL_DisableMultitexture(); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GL_Bind (s->texinfo->texture->gl_texturenum); + for (s=cf; s ; s=s->texturechain) + EmitWaterPolys (s); + + if (alpha == 1) + { + glDisable(GL_BLEND); + glColor4f(1, 1, 1, 1); + } + else + { + glEnable(GL_BLEND); + glColor4f(1, 1, 1, alpha); + } + + if (last) //don't include this chain for details. + last->texturechain = NULL; + continue; + } + + if (bumpmapping && t->gl_texturenumbumpmap) + { + vec3_t light; + + GL_DisableMultitexture(); +// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +// glEnable(GL_ALPHA_TEST); + glColor4f(1, 1, 1, 1); + glDisable(GL_BLEND); + + //Bind normal map to texture unit 0 + GL_BindType(GL_TEXTURE_2D, t->gl_texturenumbumpmap); + glEnable(GL_TEXTURE_2D); + + //Set up texture environment to do (tex0 dot tex1)*color + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); + + qglActiveTextureARB(GL_TEXTURE1_ARB); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB); + + if (gl_bump.value < 0) + { + if (currentmodel->deluxdata) + { + glEnable(GL_TEXTURE_2D); + for (s = cf; s ; s=s->texturechain) + { + vi = s->lightmaptexturenum; + GL_BindType(GL_TEXTURE_2D, deluxmap_textures[vi] ); + if (lightmap[vi]->deluxmodified) + { + lightmap[vi]->deluxmodified = false; + theRect = &lightmap[vi]->deluxrectchange; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + LMBLOCK_WIDTH, theRect->h, GL_RGB, GL_UNSIGNED_BYTE, + lightmap[vi]->deluxmaps+(theRect->t) *LMBLOCK_WIDTH*3); + theRect->l = LMBLOCK_WIDTH; + theRect->t = LMBLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]); + qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + } + } + glDisable(GL_TEXTURE_2D); + } + else + { + GL_BindType(GL_TEXTURE_CUBE_MAP_ARB, normalisationCubeMap); + glEnable(GL_TEXTURE_CUBE_MAP_ARB); + qglMultiTexCoord3fARB(GL_TEXTURE1_ARB, sin(-r_refdef.viewangles[1]/180*M_PI), cos(-r_refdef.viewangles[1]/180*M_PI), 1); + for (s = cf; s ; s=s->texturechain) + { + vi = s->lightmaptexturenum; + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]); + glVertex3fv (v); + } + glEnd (); + } + } + glDisable(GL_TEXTURE_CUBE_MAP_ARB); + } + } + else + { + GL_BindType(GL_TEXTURE_CUBE_MAP_ARB, normalisationCubeMap); + glEnable(GL_TEXTURE_CUBE_MAP_ARB); + for (s = cf; s ; s=s->texturechain) + { + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + light[0] = relativelightorigin[0] - v[0]; + light[1] = relativelightorigin[1] - v[1]; + light[2] = relativelightorigin[2] - v[2]; + + qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]); + qglMultiTexCoord3fARB(GL_TEXTURE1_ARB, -DotProduct(vup, light), -DotProduct(vright, light), gl_bump.value/2*-DotProduct(vpn, light)); + glVertex3fv (v); + } + glEnd (); + } + } + glDisable(GL_TEXTURE_CUBE_MAP_ARB); + } + + qglActiveTextureARB(GL_TEXTURE0_ARB); + currenttexture=0; + glEnable (GL_BLEND); + glBlendFunc(GL_DST_COLOR, GL_ZERO); + glColor4f(1, 1, 1, 1); + + + // Binds world to texture env 0 + GL_SelectTexture(mtexid0); + GL_Bind (t->gl_texturenum); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + GL_EnableMultitexture(); // Same as SelectTexture (TEXTURE1) + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + } + else + { + + // Binds world to texture env 0 + GL_SelectTexture(mtexid0); + GL_Bind (t->gl_texturenum); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + GL_EnableMultitexture(); // Same as SelectTexture (TEXTURE1) + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + } + + for (s=cf; s; s=s->texturechain) + { +// R_RenderDynamicLightmaps (s); + vi = s->lightmaptexturenum; + // Binds lightmap to texenv 1 + GL_Bind (lightmap_textures[vi]); + if (lightmap[vi]->modified) + { + lightmap[vi]->modified = false; + theRect = &lightmap[vi]->rectchange; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + LMBLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmap[vi]->lightmaps+(theRect->t) *LMBLOCK_WIDTH*lightmap_bytes); + theRect->l = LMBLOCK_WIDTH; + theRect->t = LMBLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (vi=0 ; vinumverts ; vi++, v+= VERTEXSIZE) + { + qglMTexCoord2fSGIS (mtexid0, v[3], v[4]); + qglMTexCoord2fSGIS (mtexid1, v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + } + last = s; + } + } + else + { + for (s=cf ; s ; s=s->texturechain) + { + R_RenderBrushPoly (s); + last = s; + } + } + + if (alpha == 1) + { + glDisable(GL_BLEND); + glColor4f(1, 1, 1, 1); + } + else + { + glEnable(GL_BLEND); + glColor4f(1, 1, 1, alpha); + } + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + } + + if (gl_mtexable) + GL_DisableMultitexture(); + else + R_BlendLightmaps(); + + //add luminance? + if (first && detailtexture && gl_detail.value && alpha == 1) + { + GL_Bind(detailtexture); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); + glEnable(GL_BLEND); + glDepthMask(0); + + for (s=first ; s ; s=s->texturechain) + { + for (p = s->polys; p; p=p->next) + { + glBegin(GL_POLYGON); + v = p->verts[0]; + for (i = 0; i < p->numverts; i++, v += VERTEXSIZE) + { + glTexCoord2f (v[5] * 18, v[6] * 18); + glVertex3fv (v); + } + glEnd(); + } + } + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDisable(GL_BLEND); + + glDepthMask(1); + } + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} +#endif +/* +================= +R_DrawBrushModel +================= +*/ +#if 0 +static void R_DrawBrushModel (entity_t *e) +{ + int i; + int k; + vec3_t mins, maxs; + msurface_t *psurf, *first; + float dot; + mplane_t *pplane; + qboolean rotated; + + currententity = e; + currenttexture = -1; + + currentmodel = e->model; + + if (e->angles[0] || e->angles[1] || e->angles[2]) + { + rotated = true; + for (i=0 ; i<3 ; i++) + { + mins[i] = e->origin[i] - currentmodel->radius; + maxs[i] = e->origin[i] + currentmodel->radius; + } + } + else + { + rotated = false; + VectorAdd (e->origin, currentmodel->mins, mins); + VectorAdd (e->origin, currentmodel->maxs, maxs); + } + + if (R_CullBox (mins, maxs)) + return; + + VectorSubtract (r_refdef.vieworg, e->origin, modelorg); + if (rotated) + { + vec3_t temp; + vec3_t forward, right, up; + + VectorCopy (modelorg, temp); + AngleVectors (e->angles, forward, right, up); + modelorg[0] = DotProduct (temp, forward); + modelorg[1] = -DotProduct (temp, right); + modelorg[2] = DotProduct (temp, up); + } + + psurf = ¤tmodel->surfaces[currentmodel->firstmodelsurface]; + +// calculate dynamic lighting for bmodel if it's not an +// instanced model + if (currentmodel->firstmodelsurface != 0 && !r_flashblend.value) + { + for (k=0 ; kfuncs.MarkLights (&cl_dlights[k], 1<nodes + currentmodel->hulls[0].firstclipnode); + } + } + + glPushMatrix (); +e->angles[0] = -e->angles[0]; // stupid quake bug + glTranslatef(-0.03, -0.03, 0.03); + R_RotateForEntity (e); +e->angles[0] = -e->angles[0]; // stupid quake bug + + + first = NULL; + // + // draw texture + // + for (i=0 ; inummodelsurfaces ; i++, psurf++) + { + // find which side of the node we are on + pplane = psurf->plane; + +// if (psurf->plane) + { + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + + // draw the polygon + if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) + { + R_RenderDynamicLightmaps (psurf); + if (psurf->flags & SURF_DRAWALPHA || psurf->texinfo->flags & (SURF_TRANS33|SURF_TRANS66) ) + { // add to the translucent chain + psurf->nextalphasurface = r_alpha_surfaces; + r_alpha_surfaces = psurf; + psurf->ownerent = e; + } + else + { + psurf->texturechain = psurf->texinfo->texture->texturechain; + psurf->texinfo->texture->texturechain = psurf; + } + } + } + } + + VectorSubtract(r_refdef.vieworg, e->origin, mins); //fixme: rotation. + if (e->drawflags & DRF_TRANSLUCENT) + DrawTextureChains(currentmodel, e->alpha*0.4, mins); + else + DrawTextureChains(currentmodel, e->alpha, mins); + + glPopMatrix (); +} +#endif + +/* +============================================================= + + WORLD MODEL + +============================================================= +*/ + +/* +================ +R_RecursiveWorldNode +================ +*/ +static void GLR_RecursiveWorldNode (mnode_t *node) +{ + int c, side; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double dot; + + if (node->contents == Q1CONTENTS_SOLID) + return; // solid + + if (node->visframe != r_visframecount) + return; + if (R_CullBox (node->minmaxs, node->minmaxs+3)) + return; + +// if a leaf node, draw stuff + if (node->contents < 0) + { + pleaf = (mleaf_t *)node; + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + (*mark++)->visframe = r_framecount; + } while (--c); + } + + // deal with model fragments in this leaf + if (pleaf->efrags) + R_StoreEfrags (&pleaf->efrags); + + return; + } + +// node is just a decision point, so go down the apropriate sides + +// find which side of the node we are on + plane = node->plane; + + switch (plane->type) + { + case PLANE_X: + dot = modelorg[0] - plane->dist; + break; + case PLANE_Y: + dot = modelorg[1] - plane->dist; + break; + case PLANE_Z: + dot = modelorg[2] - plane->dist; + break; + default: + dot = DotProduct (modelorg, plane->normal) - plane->dist; + break; + } + + if (dot >= 0) + side = 0; + else + side = 1; + +// recurse down the children, front side first + GLR_RecursiveWorldNode (node->children[side]); + +// draw stuff + c = node->numsurfaces; + + if (c) + { + surf = cl.worldmodel->surfaces + node->firstsurface; + + if (dot < 0 -BACKFACE_EPSILON) + side = SURF_PLANEBACK; + else if (dot > BACKFACE_EPSILON) + side = 0; + { + for ( ; c ; c--, surf++) + { + if (surf->visframe != r_framecount) + continue; + +// surf->visframe = -1; + + // don't backface underwater surfaces, because they warp + if ( !(surf->flags & SURF_UNDERWATER) && ( (dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) ) + continue; // wrong side + if ( !(((r_viewleaf->contents==Q1CONTENTS_EMPTY && (surf->flags & SURF_UNDERWATER)) || + (r_viewleaf->contents!=Q1CONTENTS_EMPTY && !(surf->flags & SURF_UNDERWATER))) + && !(surf->flags & SURF_DONTWARP)) && ( (dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) ) + continue; // wrong side + + R_RenderDynamicLightmaps (surf); + // if sorting by texture, just store it out + if (surf->flags & SURF_DRAWALPHA) + { // add to the translucent chain + surf->nextalphasurface = r_alpha_surfaces; + r_alpha_surfaces = surf; + surf->ownerent = &r_worldentity; + } + else + { + surf->texturechain = surf->texinfo->texture->texturechain; + surf->texinfo->texture->texturechain = surf; + } + } + } + } + +// recurse down the back side + GLR_RecursiveWorldNode (node->children[!side]); +} + +#ifdef Q2BSPS +static void GLR_RecursiveQ2WorldNode (mnode_t *node) +{ + int c, side; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double dot; + + int sidebit; + + if (node->contents == Q2CONTENTS_SOLID) + return; // solid + + if (node->visframe != r_visframecount) + return; + if (R_CullBox (node->minmaxs, node->minmaxs+3)) + return; + +// if a leaf node, draw stuff + if (node->contents != -1) + { + pleaf = (mleaf_t *)node; + + // check for door connected areas +// if (areabits) + { + if (! (areabits[pleaf->area>>3] & (1<<(pleaf->area&7)) ) ) + return; // not visible + } + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + (*mark)->visframe = r_framecount; + mark++; + } while (--c); + } + return; + } + +// node is just a decision point, so go down the apropriate sides + +// find which side of the node we are on + plane = node->plane; + + switch (plane->type) + { + case PLANE_X: + dot = modelorg[0] - plane->dist; + break; + case PLANE_Y: + dot = modelorg[1] - plane->dist; + break; + case PLANE_Z: + dot = modelorg[2] - plane->dist; + break; + default: + dot = DotProduct (modelorg, plane->normal) - plane->dist; + break; + } + + if (dot >= 0) + { + side = 0; + sidebit = 0; + } + else + { + side = 1; + sidebit = SURF_PLANEBACK; + } + +// recurse down the children, front side first + GLR_RecursiveQ2WorldNode (node->children[side]); + + // draw stuff + for ( c = node->numsurfaces, surf = currentmodel->surfaces + node->firstsurface; c ; c--, surf++) + { + if (surf->visframe != r_framecount) + continue; + + if ( (surf->flags & SURF_PLANEBACK) != sidebit ) + continue; // wrong side + + surf->visframe = r_framecount+1;//-1; + + R_RenderDynamicLightmaps (surf); + + if (surf->texinfo->flags & (SURF_TRANS33|SURF_TRANS66)) + { // add to the translucent chain + surf->nextalphasurface = r_alpha_surfaces; + r_alpha_surfaces = surf; + surf->ownerent = &r_worldentity; + continue; + } + + surf->texturechain = surf->texinfo->texture->texturechain; + surf->texinfo->texture->texturechain = surf; + } + + +// recurse down the back side + GLR_RecursiveQ2WorldNode (node->children[!side]); +} +#endif + +#ifdef Q3BSPS +static void GLR_LeafWorldNode (void) +{ + int i; + int clipflags; + msurface_t **mark, *surf; + mleaf_t *pleaf; + + + int clipped; + mplane_t *clipplane; + + + for ( pleaf = r_vischain; pleaf; pleaf = pleaf->vischain ) + { + // check for door connected areas +// if ( areabits ) + { + if (! (areabits[pleaf->area>>3] & (1<<(pleaf->area&7)) ) ) + { + continue; // not visible + } + } + + clipflags = 15; // 1 | 2 | 4 | 8 +// if ( !r_nocull->value ) + { + + for (i=0,clipplane=frustum ; i<4 ; i++,clipplane++) + { + clipped = BoxOnPlaneSide ( pleaf->minmaxs, pleaf->minmaxs+3, clipplane ); + if ( clipped == 2 ) { + break; + } else if ( clipped == 1 ) { + clipflags &= ~(1<nummarksurfaces; + mark = pleaf->firstmarksurface; + + do + { + surf = *mark++; + if ( surf->visframe != r_framecount ) //sufraces exist in multiple leafs. + { + surf->visframe = r_framecount; +// if (surf->mesh) +// { +// GL_DrawMesh(surf->mesh, NULL, surf->texinfo->texture->gl_texturenum, lightmap_textures+ surf->lightmaptexturenum); +// } +// else +// R_DrawSequentialPoly ( surf ); + + + + /* if (surf->flags & SURF_DRAWALPHA) + { // add to the translucent chain + surf->nextalphasurface = r_alpha_surfaces; + r_alpha_surfaces = surf; + surf->ownerent = &r_worldentity; + continue; + } + else*/ + { + /* if (surf->texinfo->flags & (SURF_TRANS33|SURF_TRANS66)) + { // add to the translucent chain + surf->nextalphasurface = r_alpha_surfaces; + r_alpha_surfaces = surf; + surf->ownerent = &r_worldentity; + continue; + }*/ + surf->texturechain = surf->texinfo->texture->texturechain; + surf->texinfo->texture->texturechain = surf; + } + } + } while (--i); + +// c_world_leafs++; + } +} +#endif + +/* +============= +R_DrawWorld +============= +*/ + +void R_DrawWorld (void) +{ + entity_t ent; + + memset (&ent, 0, sizeof(ent)); + ent.model = cl.worldmodel; + currentmodel = cl.worldmodel; + + VectorCopy (r_refdef.vieworg, modelorg); + + currententity = &ent; + currenttexture = -1; + +#ifdef TERRAINMAPS + if (ent.model->type == mod_terrain) + DrawTerrain(); + else +#endif + { + glColor3f (1,1,1); + //#ifdef QUAKE2 + R_ClearSkyBox (); + //#endif + +#ifdef Q2BSPS + if (ent.model->fromgame == fg_quake2 || ent.model->fromgame == fg_quake3) + { + int leafnum; + int clientarea; + int CM_WriteAreaBits (qbyte *buffer, int area); + if (cls.q2server) //we can get server sent info + memcpy(areabits, cl.q2frame.areabits, sizeof(areabits)); + else + { //generate the info each frame. + leafnum = CM_PointLeafnum (r_refdef.vieworg); + clientarea = CM_LeafArea (leafnum); + CM_WriteAreaBits(areabits, clientarea); + } +#ifdef Q3BSPS + if (ent.model->fromgame == fg_quake3) + { + GLR_LeafWorldNode (); + } + else +#endif + GLR_RecursiveQ2WorldNode (cl.worldmodel->nodes); + } + else +#endif + GLR_RecursiveWorldNode (cl.worldmodel->nodes); + +// if (r_shadows.value >= 2 && gl_canstencil && gl_mtexable) + PPL_DrawWorld(); +// else +// DrawTextureChains (cl.worldmodel, 1, r_refdef.vieworg); + +glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + GLR_LessenStains(); + } +} + + +/* +=============== +R_MarkLeaves +=============== +*/ +void GLR_MarkLeaves (void) +{ + qbyte fatvis[MAX_MAP_LEAFS/8]; + qbyte *vis; + mnode_t *node; + int i; + qbyte solid[4096]; +#ifdef Q3BSPS + if (cl.worldmodel->fromgame == fg_quake3) + { + int cluster; + mleaf_t *leaf; + + if (r_oldviewcluster == r_viewcluster && !r_novis.value && r_viewcluster != -1) + return; + + // development aid to let you run around and see exactly where + // the pvs ends +// if (r_lockpvs->value) +// return; + + r_vischain = NULL; + r_visframecount++; + r_oldviewcluster = r_viewcluster; + + if (r_novis.value || r_viewcluster == -1 || !cl.worldmodel->vis ) + { + // mark everything + for (i=0,leaf=cl.worldmodel->leafs ; inumleafs ; i++, leaf++) + { + if ( !leaf->nummarksurfaces ) { + continue; + } + + leaf->visframe = r_visframecount; + leaf->vischain = r_vischain; + r_vischain = leaf; + } + return; + } + + vis = CM_ClusterPVS (r_viewcluster, NULL);//, cl.worldmodel); + for (i=0,leaf=cl.worldmodel->leafs ; inumleafs ; i++, leaf++) + { + cluster = leaf->cluster; + if ( cluster == -1 || !leaf->nummarksurfaces ) { + continue; + } + if ( vis[cluster>>3] & (1<<(cluster&7)) ) { + leaf->visframe = r_visframecount; + leaf->vischain = r_vischain; + r_vischain = leaf; + } + } + return; + } +#endif + +#ifdef Q2BSPS + if (cl.worldmodel->fromgame == fg_quake2) + { + int c; + mleaf_t *leaf; + int cluster; + + if (r_oldviewcluster == r_viewcluster && r_oldviewcluster2 == r_viewcluster2) + return; + + r_oldviewcluster = r_viewcluster; + r_oldviewcluster2 = r_viewcluster2; + + if (r_novis.value == 2) + return; + r_visframecount++; + if (r_novis.value || r_viewcluster == -1 || !cl.worldmodel->vis) + { + // mark everything + for (i=0 ; inumleafs ; i++) + cl.worldmodel->leafs[i].visframe = r_visframecount; + for (i=0 ; inumnodes ; i++) + cl.worldmodel->nodes[i].visframe = r_visframecount; + return; + } + + vis = CM_ClusterPVS (r_viewcluster, NULL);//, cl.worldmodel); + // may have to combine two clusters because of solid water boundaries + if (r_viewcluster2 != r_viewcluster) + { + memcpy (fatvis, vis, (cl.worldmodel->numleafs+7)/8); + vis = CM_ClusterPVS (r_viewcluster2, NULL);//, cl.worldmodel); + c = (cl.worldmodel->numleafs+31)/32; + for (i=0 ; ileafs ; inumleafs ; i++, leaf++) + { + cluster = leaf->cluster; + if (cluster == -1) + continue; + if (vis[cluster>>3] & (1<<(cluster&7))) + { + node = (mnode_t *)leaf; + do + { + if (node->visframe == r_visframecount) + break; + node->visframe = r_visframecount; + node = node->parent; + } while (node); + } + } + return; + } +#endif + + if ((r_oldviewleaf == r_viewleaf && r_oldviewleaf2 == r_viewleaf2) && !r_novis.value || r_novis.value == 2) + return; + +// if (mirror) +// return; + + r_visframecount++; + + r_oldviewleaf = r_viewleaf; + r_oldviewleaf2 = r_viewleaf2; + + if (r_novis.value) + { + vis = solid; + memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3); + } + else if (r_viewleaf2) + { + int c; + GLMod_LeafPVS (r_viewleaf2, cl.worldmodel, fatvis); + vis = GLMod_LeafPVS (r_viewleaf, cl.worldmodel, NULL); + c = (cl.worldmodel->numleafs+31)/32; + for (i=0 ; inumleafs ; i++) + { + if (vis[i>>3] & (1<<(i&7))) + { + node = (mnode_t *)&cl.worldmodel->leafs[i+1]; + do + { + if (node->visframe == r_visframecount) + break; + node->visframe = r_visframecount; + node = node->parent; + } while (node); + } + } +} + + + +/* +============================================================================= + + LIGHTMAP ALLOCATION + +============================================================================= +*/ + +// returns a texture number and the position inside it +int GLAllocBlock (int w, int h, int *x, int *y) +{ + int i, j; + int best, best2; + int texnum; + + for (texnum=0 ; ; texnum++) + { + if (texnum == numlightmaps) //allocate 4 more lightmap slots. not much memory usage, but we don't want any caps here. + { + lightmap = BZ_Realloc(lightmap, sizeof(*lightmap)*(numlightmaps+4)); + lightmap_textures = BZ_Realloc(lightmap_textures, sizeof(*lightmap_textures)*(numlightmaps+4)); + lightmap_textures[numlightmaps+0] = texture_extension_number++; + lightmap_textures[numlightmaps+1] = texture_extension_number++; + lightmap_textures[numlightmaps+2] = texture_extension_number++; + lightmap_textures[numlightmaps+3] = texture_extension_number++; + + deluxmap_textures = BZ_Realloc(deluxmap_textures, sizeof(*deluxmap_textures)*(numlightmaps+4)); + deluxmap_textures[numlightmaps+0] = texture_extension_number++; + deluxmap_textures[numlightmaps+1] = texture_extension_number++; + deluxmap_textures[numlightmaps+2] = texture_extension_number++; + deluxmap_textures[numlightmaps+3] = texture_extension_number++; + numlightmaps+=4; + } + if (!lightmap[texnum]) + { + lightmap[texnum] = BZ_Malloc(sizeof(*lightmap[texnum])); + } + + + best = LMBLOCK_HEIGHT; + + for (i=0 ; iallocated[i+j] >= best) + break; + if (lightmap[texnum]->allocated[i+j] > best2) + best2 = lightmap[texnum]->allocated[i+j]; + } + if (j == w) + { // this is a valid spot + *x = i; + *y = best = best2; + } + } + + if (best + h > LMBLOCK_HEIGHT) + continue; + + for (i=0 ; iallocated[*x + i] = best + h; + + return texnum; + } + + Sys_Error ("AllocBlock: full"); + return 0; +} + +//quake3 maps have thier lightmaps in gl style already. +//rather than forgetting that and redoing it, let's just keep the data. +int GLFillBlock (int texnum, int w, int h, int x, int y) +{ + int i; + while (texnum >= numlightmaps) //allocate 4 more lightmap slots. not much memory usage, but we don't want any caps here. + { + lightmap = BZ_Realloc(lightmap, sizeof(*lightmap)*(numlightmaps+4)); + lightmap_textures = BZ_Realloc(lightmap_textures, sizeof(*lightmap_textures)*(numlightmaps+4)); + lightmap_textures[numlightmaps+0] = texture_extension_number++; + lightmap_textures[numlightmaps+1] = texture_extension_number++; + lightmap_textures[numlightmaps+2] = texture_extension_number++; + lightmap_textures[numlightmaps+3] = texture_extension_number++; + + deluxmap_textures = BZ_Realloc(deluxmap_textures, sizeof(*deluxmap_textures)*(numlightmaps+4)); + deluxmap_textures[numlightmaps+0] = texture_extension_number++; + deluxmap_textures[numlightmaps+1] = texture_extension_number++; + deluxmap_textures[numlightmaps+2] = texture_extension_number++; + deluxmap_textures[numlightmaps+3] = texture_extension_number++; + numlightmaps+=4; + } + for (i = texnum; i >= 0; i--) + { + if (!lightmap[i]) + lightmap[i] = BZ_Malloc(sizeof(*lightmap[i])); + else + break; + } + + for (i=0 ; iallocated[x + i] = y + h; + } + return texnum; +} + +mvertex_t *r_pcurrentvertbase; + +int nColinElim; + +/* +================ +BuildSurfaceDisplayList +================ +*/ +void BuildSurfaceDisplayList (msurface_t *fa) +{ + int i, lindex, lnumverts; + medge_t *pedges, *r_pedge; + int vertpage; + float *vec; + float s, t; + float distoff; + vec3_t offcenter; + glpoly_t *poly; + +// reconstruct the polygon + pedges = currentmodel->edges; + lnumverts = fa->numedges; + vertpage = 0; + + if (!currentmodel->surfedges) + return; //q3 map. + // + // draw texture + // + poly = Hunk_AllocName (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float), "SDList"); + poly->next = fa->polys; + poly->flags = fa->flags; + fa->polys = poly; + poly->numverts = lnumverts; + + fa->center[0]=0; + fa->center[1]=0; + fa->center[2]=0; + + for (i=0 ; isurfedges[fa->firstedge + i]; + + if (lindex > 0) + { + r_pedge = &pedges[lindex]; + vec = r_pcurrentvertbase[r_pedge->v[0]].position; + } + else + { + r_pedge = &pedges[-lindex]; + vec = r_pcurrentvertbase[r_pedge->v[1]].position; + } + s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; + s /= fa->texinfo->texture->width; + + t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; + t /= fa->texinfo->texture->height; + + VectorAdd(vec, fa->center, fa->center); + + VectorCopy (vec, poly->verts[i]); + poly->verts[i][3] = s; + poly->verts[i][4] = t; + + // + // lightmap texture coordinates + // + s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; + s -= fa->texturemins[0]; + s += fa->light_s*16; + s += 8; + s /= LMBLOCK_WIDTH*16; //fa->texinfo->texture->width; + + t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; + t -= fa->texturemins[1]; + t += fa->light_t*16; + t += 8; + t /= LMBLOCK_HEIGHT*16; //fa->texinfo->texture->height; + + poly->verts[i][5] = s; + poly->verts[i][6] = t; + } + + fa->center[0]/=lnumverts; + fa->center[1]/=lnumverts; + fa->center[2]/=lnumverts; + fa->radius = 0; + for (i=0 ; iverts[0], fa->center, offcenter); + distoff = Length(offcenter); + if (distoff > fa->radius) + fa->radius = distoff; + } + + // + // remove co-linear points - Ed + // + if (!gl_keeptjunctions.value && !(fa->flags & SURF_UNDERWATER) ) + { + for (i = 0 ; i < lnumverts ; ++i) + { + vec3_t v1, v2; + float *prev, *this, *next; + + prev = poly->verts[(i + lnumverts - 1) % lnumverts]; + this = poly->verts[i]; + next = poly->verts[(i + 1) % lnumverts]; + + VectorSubtract( this, prev, v1 ); + VectorNormalize( v1 ); + VectorSubtract( next, prev, v2 ); + VectorNormalize( v2 ); + + // skip co-linear points + #define COLINEAR_EPSILON 0.001 + if ((fabs( v1[0] - v2[0] ) <= COLINEAR_EPSILON) && + (fabs( v1[1] - v2[1] ) <= COLINEAR_EPSILON) && + (fabs( v1[2] - v2[2] ) <= COLINEAR_EPSILON)) + { + int j; + for (j = i + 1; j < lnumverts; ++j) + { + int k; + for (k = 0; k < VERTEXSIZE; ++k) + poly->verts[j - 1][k] = poly->verts[j][k]; + } + --lnumverts; + ++nColinElim; + // retry next vertex next time, which is now current vertex + --i; + } + } + } + +#ifdef SHADERS //adjust the s + t coords so we can rotate around the center of the texture rather than the center of the world. + s=0;t=0; + for (i=0 ; iverts[i][3]; + t+=poly->verts[i][4]; + } + poly->texcenter[0] = s/lnumverts; + poly->texcenter[1] = t/lnumverts; + + s = (int)poly->texcenter[0]; + t = (int)poly->texcenter[1]; + if (s <=0)s--; + if (t <=0)t--; + poly->texcenter[0] -= s; + poly->texcenter[1] -= t; + for (i=0 ; iverts[i][3] -= s; + poly->verts[i][4] -= t; + } +#endif + + poly->numverts = lnumverts; + +} + +/* +======================== +GL_CreateSurfaceLightmap +======================== +*/ +void GL_CreateSurfaceLightmap (msurface_t *surf) +{ + int smax, tmax; + qbyte *base, *luxbase; stmap *stainbase; + + if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) + { + surf->lightmaptexturenum = -1; + return; + } + if (currentmodel->fromgame == fg_halflife) + if (surf->texinfo->flags & TEX_SPECIAL) + { + surf->lightmaptexturenum = -1; + return; //it comes in stupid sizes. + } + if (surf->lightmaptexturenum<0) + return; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + + if (currentmodel->fromgame == fg_quake3) + GLFillBlock(surf->lightmaptexturenum, smax, tmax, surf->light_s, surf->light_t); + else + surf->lightmaptexturenum = GLAllocBlock (smax, tmax, &surf->light_s, &surf->light_t); + base = lightmap[surf->lightmaptexturenum]->lightmaps; + base += (surf->light_t * LMBLOCK_WIDTH + surf->light_s) * lightmap_bytes; + + luxbase = lightmap[surf->lightmaptexturenum]->deluxmaps; + luxbase += (surf->light_t * LMBLOCK_WIDTH + surf->light_s) * 3; + + stainbase = lightmap[surf->lightmaptexturenum]->stainmaps; + stainbase += (surf->light_t * LMBLOCK_WIDTH + surf->light_s) * 3; + + GLR_BuildLightMap (surf, base, luxbase, stainbase); +} + + + +void GLSurf_DeInit(void) +{ + int i; + for (i = 0; i < numlightmaps; i++) + { + if (!lightmap[i]) + break; + BZ_Free(lightmap[i]); + lightmap[i] = NULL; + } + + if (lightmap_textures) + BZ_Free(lightmap_textures); + if (lightmap) + BZ_Free(lightmap); + + lightmap_textures=NULL; + lightmap=NULL; + numlightmaps=0; +} + + +/* +================== +GL_BuildLightmaps + +Builds the lightmap texture +with all the surfaces from all brush models +================== +*/ +void GL_BuildLightmaps (void) +{ + int i, j; + model_t *m; + msurface_t *fa; + + r_framecount = 1; // no dlightcache + + for (i = 0; i < numlightmaps; i++) + { + if (!lightmap[i]) + break; + BZ_Free(lightmap[i]); + lightmap[i] = NULL; + } + + if (cl.worldmodel->fromgame == fg_doom) + return; //no lightmaps. + + if (cl.worldmodel->rgblighting || cl.worldmodel->deluxdata || r_loadlits.value) + gl_lightmap_format = GL_RGB; + else + gl_lightmap_format = GL_LUMINANCE; + + if (COM_CheckParm ("-lm_1")) + gl_lightmap_format = GL_LUMINANCE; + if (COM_CheckParm ("-lm_a")) + gl_lightmap_format = GL_ALPHA; + if (COM_CheckParm ("-lm_i")) + gl_lightmap_format = GL_INTENSITY; + if (COM_CheckParm ("-lm_3")) + gl_lightmap_format = GL_RGB; + if (COM_CheckParm ("-lm_4")) + gl_lightmap_format = GL_RGBA; + if (*gl_lightmapmode.string) + { + switch(*gl_lightmapmode.string) + { + case '1': + gl_lightmap_format = GL_LUMINANCE; + break; + case 'a': + gl_lightmap_format = GL_ALPHA; + break; + case 'i': + gl_lightmap_format = GL_INTENSITY; + break; + case '3': + gl_lightmap_format = GL_RGB; + break; + case '4': + gl_lightmap_format = GL_RGBA; + break; + default: + Con_Printf("%s contains unrecognised type\n", gl_lightmapmode.name); + case '0': + break; + } + } + if (cl.worldmodel->fromgame == fg_quake3 && gl_lightmap_format != GL_RGB && gl_lightmap_format != GL_RGBA) + gl_lightmap_format = GL_RGB; + + + switch (gl_lightmap_format) + { + case GL_RGBA: + lightmap_bytes = 4; + break; + case GL_RGB: + lightmap_bytes = 3; + break; + case GL_LUMINANCE: + case GL_INTENSITY: + case GL_ALPHA: + lightmap_bytes = 1; + break; + } + + for (j=1 ; jname[0] == '*') + continue; + + r_pcurrentvertbase = m->vertexes; + currentmodel = m; + for (i=0 ; inumsurfaces ; i++) + { + fa = &m->surfaces[i]; + VectorCopy(fa->plane->normal, fa->normal); + if (fa->flags & SURF_PLANEBACK) + { + fa->normal[0]*=-1; + fa->normal[1]*=-1; + fa->normal[2]*=-1; + } + + GL_CreateSurfaceLightmap (m->surfaces + i); + if (m->surfaces[i].texinfo->texture->parttype>=0) + R_EmitSkyEffectTris(m, &m->surfaces[i]); + if ( m->surfaces[i].flags & SURF_DRAWTURB ) + continue; + if ( m->surfaces[i].flags & SURF_DRAWSKY ) + { + if (currentmodel->fromgame != fg_quake2) + continue; + } + BuildSurfaceDisplayList (m->surfaces + i); + } + } + + // + // upload all lightmaps that were filled + // + for (i=0 ; imodified = false; + lightmap[i]->rectchange.l = LMBLOCK_WIDTH; + lightmap[i]->rectchange.t = LMBLOCK_HEIGHT; + lightmap[i]->rectchange.w = 0; + lightmap[i]->rectchange.h = 0; + GL_Bind(lightmap_textures[i]); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D (GL_TEXTURE_2D, 0, lightmap_bytes + , LMBLOCK_WIDTH, LMBLOCK_HEIGHT, 0, + gl_lightmap_format, GL_UNSIGNED_BYTE, lightmap[i]->lightmaps); + + lightmap[i]->deluxmodified = false; + lightmap[i]->deluxrectchange.l = LMBLOCK_WIDTH; + lightmap[i]->deluxrectchange.t = LMBLOCK_HEIGHT; + lightmap[i]->deluxrectchange.w = 0; + lightmap[i]->deluxrectchange.h = 0; + GL_Bind(deluxmap_textures[i]); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D (GL_TEXTURE_2D, 0, 3 + , LMBLOCK_WIDTH, LMBLOCK_HEIGHT, 0, + GL_RGB, GL_UNSIGNED_BYTE, lightmap[i]->deluxmaps); + } +} diff --git a/engine/gl/gl_screen.c b/engine/gl/gl_screen.c new file mode 100644 index 00000000..caf5e254 --- /dev/null +++ b/engine/gl/gl_screen.c @@ -0,0 +1,239 @@ +/* +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. + +*/ + +// screen.c -- master for refresh, status bar, console, chat, notify, etc + +#include "quakedef.h" +#include "glquake.h" + +#include + +void GLSCR_UpdateScreen (void); + + +extern qboolean scr_drawdialog; + +extern cvar_t gl_triplebuffer; +extern cvar_t scr_fov; + +extern qboolean scr_initialized; +extern float oldsbar; +extern qboolean scr_drawloading; + +extern float oldfov; + + +extern int scr_chatmode; +extern cvar_t scr_chatmodecvar; + + +/* +================== +SCR_UpdateScreen + +This is called every frame, and can also be called explicitly to flush +text to the screen. + +WARNING: be very careful calling this from elsewhere, because the refresh +needs almost the entire 256k of stack space! +================== +*/ + +void GLSCR_UpdateScreen (void) +{ + extern cvar_t gl_2dscale; + static float old2dscale=1; + int uimenu; +#ifdef TEXTEDITOR + extern qboolean editormodal, editoractive; +#endif + if (block_drawing) + return; + + if (gl_2dscale.modified) + { + gl_2dscale.modified=false; + if (gl_2dscale.value < 0) //lower would be wrong + Cvar_Set(&gl_2dscale, "0"); + if (gl_2dscale.value > 2) //anything higher is unreadable. + Cvar_Set(&gl_2dscale, "2"); + + old2dscale = gl_2dscale.value; + vid.width = vid.conwidth = (glwidth - 320) * gl_2dscale.value + 320; + vid.height = vid.conheight = (glheight - 240) * gl_2dscale.value + 240; + + vid.recalc_refdef = true; + Con_CheckResize(); + } + + vid.numpages = 2 + gl_triplebuffer.value; + + scr_copytop = 0; + scr_copyeverything = 0; + + if (scr_disabled_for_loading) + { +/* if (Sys_DoubleTime() - scr_disabled_time > 60 || key_dest != key_game) + { + scr_disabled_for_loading = false; + Con_Printf ("load failed.\n"); + } + else +*/ { + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + SCR_DrawLoading (); + GL_EndRendering (); + return; + } + } + + if (!scr_initialized || !con_initialized) + return; // not initialized yet + + uimenu = UI_MenuState(); + + + if (oldsbar != cl_sbar.value) { + oldsbar = cl_sbar.value; + vid.recalc_refdef = true; + } + + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + +#ifdef TEXTEDITOR + if (editormodal) + { + Editor_Draw(); + GLV_UpdatePalette (); +#if defined(_WIN32) && defined(RGLQUAKE) + Media_RecordFrame(); +#endif + GLR_BrightenScreen(); + GL_EndRendering (); + return; + } +#endif + if (Media_ShowFilm()) + { + M_Draw(0); + GLV_UpdatePalette (); +#if defined(_WIN32) && defined(RGLQUAKE) + Media_RecordFrame(); +#endif + GLR_BrightenScreen(); + GL_EndRendering (); + return; + } + + // + // determine size of refresh window + // + if (oldfov != scr_fov.value) + { + oldfov = scr_fov.value; + vid.recalc_refdef = true; + } + + if (scr_chatmode != scr_chatmodecvar.value) + vid.recalc_refdef = true; + + if (vid.recalc_refdef || scr_viewsize.modified) + SCR_CalcRefdef (); + +// +// do 3D refresh drawing, and then update the screen +// + SCR_SetUpToDrawConsole (); + if (cl.worldmodel && uimenu != 1) + V_RenderView (); + + GL_Set2D (); + + // + // draw any areas not covered by the refresh + // + SCR_TileClear (); + + if (r_netgraph.value) + GLR_NetGraph (); + + if (scr_drawdialog) + { + Sbar_Draw (); + Draw_FadeScreen (); + SCR_DrawNotifyString (); + scr_copyeverything = true; + } + else if (scr_drawloading) + { + SCR_DrawLoading (); + Sbar_Draw (); + } + else if (cl.intermission == 1 && key_dest == key_game) + { + Sbar_IntermissionOverlay (); + M_Draw (uimenu); + } + else if (cl.intermission == 2 && key_dest == key_game) + { + Sbar_FinaleOverlay (); + SCR_CheckDrawCenterString (); + } + else + { + Draw_Crosshair(); + + SCR_DrawRam (); + SCR_DrawNet (); + SCR_DrawFPS (); + SCR_DrawTurtle (); + SCR_DrawPause (); + SCR_CheckDrawCenterString (); + Sbar_Draw (); + glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); +#ifdef TEXTEDITOR + if (editoractive) + Editor_Draw(); +#endif + M_Draw (uimenu); + SCR_DrawConsole (false); + } + + GLR_BrightenScreen(); + + GLV_UpdatePalette (); +#if defined(_WIN32) && defined(RGLQUAKE) + Media_RecordFrame(); +#endif + GL_EndRendering (); +} + + +char *GLVID_GetRGBInfo(int prepadbytes, int *truewidth, int *trueheight) +{ //returns a BZ_Malloced array + qbyte *ret = BZ_Malloc(prepadbytes + glwidth*glheight*3); + + glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, ret + prepadbytes); + + *truewidth = glwidth; + *trueheight = glheight; + + return ret; +} diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c new file mode 100644 index 00000000..bbc3a075 --- /dev/null +++ b/engine/gl/gl_vidcommon.c @@ -0,0 +1,369 @@ +#include "quakedef.h" +#include "glquake.h" + +//standard 1.1 opengl calls +void (APIENTRY *qglAlphaFunc) (GLenum func, GLclampf ref); +void (APIENTRY *qglBegin) (GLenum mode); +void (APIENTRY *qglBlendFunc) (GLenum sfactor, GLenum dfactor); +void (APIENTRY *qglClear) (GLbitfield mask); +void (APIENTRY *qglClearColor) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +void (APIENTRY *qglClearDepth) (GLclampd depth); +void (APIENTRY *qglClearStencil) (GLint s); +void (APIENTRY *qglColor3f) (GLfloat red, GLfloat green, GLfloat blue); +void (APIENTRY *qglColor3ub) (GLubyte red, GLubyte green, GLubyte blue); +void (APIENTRY *qglColor4f) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +void (APIENTRY *qglColor4fv) (const GLfloat *v); +void (APIENTRY *qglColor4ub) (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +void (APIENTRY *qglColor4ubv) (const GLubyte *v); +void (APIENTRY *qglColorMask) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +void (APIENTRY *qglCullFace) (GLenum mode); +void (APIENTRY *qglDepthFunc) (GLenum func); +void (APIENTRY *qglDepthMask) (GLboolean flag); +void (APIENTRY *qglDepthRange) (GLclampd zNear, GLclampd zFar); +void (APIENTRY *qglDisable) (GLenum cap); +void (APIENTRY *qglDrawBuffer) (GLenum mode); +void (APIENTRY *qglDrawPixels) (GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +void (APIENTRY *qglEnable) (GLenum cap); +void (APIENTRY *qglEnd) (void); +void (APIENTRY *qglFinish) (void); +void (APIENTRY *qglFlush) (void); +void (APIENTRY *qglFrustum) (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +void (APIENTRY *qglGetFloatv) (GLenum pname, GLfloat *params); +void (APIENTRY *qglGetIntegerv) (GLenum pname, GLint *params); +const GLubyte * (APIENTRY *qglGetString) (GLenum name); +void (APIENTRY *qglHint) (GLenum target, GLenum mode); +void (APIENTRY *qglLoadIdentity) (void); +void (APIENTRY *qglLoadMatrixf) (const GLfloat *m); +void (APIENTRY *qglNormal3f) (GLfloat nx, GLfloat ny, GLfloat nz); +void (APIENTRY *qglNormal3fv) (const GLfloat *v); +void (APIENTRY *qglMatrixMode) (GLenum mode); +void (APIENTRY *qglMultMatrixf) (const GLfloat *m); +void (APIENTRY *qglOrtho) (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +void (APIENTRY *qglPolygonMode) (GLenum face, GLenum mode); +void (APIENTRY *qglPopMatrix) (void); +void (APIENTRY *qglPushMatrix) (void); +void (APIENTRY *qglReadBuffer) (GLenum mode); +void (APIENTRY *qglReadPixels) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); +void (APIENTRY *qglRotatef) (GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +void (APIENTRY *qglScalef) (GLfloat x, GLfloat y, GLfloat z); +void (APIENTRY *qglShadeModel) (GLenum mode); +void (APIENTRY *qglTexCoord1f) (GLfloat s); +void (APIENTRY *qglTexCoord2f) (GLfloat s, GLfloat t); +void (APIENTRY *qglTexCoord2fv) (const GLfloat *v); +void (APIENTRY *qglTexEnvf) (GLenum target, GLenum pname, GLfloat param); +void (APIENTRY *qglTexEnvi) (GLenum target, GLenum pname, GLint param); +void (APIENTRY *qglTexGeni) (GLenum coord, GLenum pname, GLint param); +void (APIENTRY *qglTexImage2D) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +void (APIENTRY *qglTexParameteri) (GLenum target, GLenum pname, GLint param); +void (APIENTRY *qglTexParameterf) (GLenum target, GLenum pname, GLfloat param); +void (APIENTRY *qglTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +void (APIENTRY *qglTranslatef) (GLfloat x, GLfloat y, GLfloat z); +void (APIENTRY *qglVertex2f) (GLfloat x, GLfloat y); +void (APIENTRY *qglVertex3f) (GLfloat x, GLfloat y, GLfloat z); +void (APIENTRY *qglVertex3fv) (const GLfloat *v); +void (APIENTRY *qglViewport) (GLint x, GLint y, GLsizei width, GLsizei height); +void (APIENTRY *qglGetTexLevelParameteriv) (GLenum target, GLint level, GLenum pname, GLint *params); + +void (APIENTRY *qglDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); +void (APIENTRY *qglVertexPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +void (APIENTRY *qglNormalPointer) (GLenum type, GLsizei stride, const GLvoid *pointer); +void (APIENTRY *qglTexCoordPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +void (APIENTRY *qglColorPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +void (APIENTRY *qglDisableClientState) (GLenum array); +void (APIENTRY *qglEnableClientState) (GLenum array); + + +void (APIENTRY *qglStencilOp) (GLenum fail, GLenum zfail, GLenum zpass); +void (APIENTRY *qglStencilFunc) (GLenum func, GLint ref, GLuint mask); +void (APIENTRY *qglPushAttrib) (GLbitfield mask); +void (APIENTRY *qglPopAttrib) (void); + + + + +//extensions +//arb multitexture +qlpSelTexFUNC qglActiveTextureARB; +qlpSelTexFUNC qglClientActiveTextureARB; +qlpMTex3FUNC qglMultiTexCoord3fARB; +qlpMTex2FUNC qglMultiTexCoord2fARB; + +//generic multitexture +lpMTexFUNC qglMTexCoord2fSGIS; +lpSelTexFUNC qglSelectTextureSGIS; +int mtexid0; +int mtexid1; + +//ati_truform +PFNGLPNTRIANGLESIATIPROC qglPNTrianglesiATI; +PFNGLPNTRIANGLESFATIPROC qglPNTrianglesfATI; + +//stencil shadowing +void (APIENTRY *qglStencilOpSeparateATI) (GLenum face, GLenum fail, GLenum zfail, GLenum zpass); +PFNGLACTIVESTENCILFACEEXTPROC qglActiveStencilFaceEXT; + +//quick hack that made quake work on both 1 and 1.1 gl implementations. +BINDTEXFUNCPTR bindTexFunc; + + +qboolean gl_ext_stencil_wrap; +int gl_mtexarbable=0; //max texture units +qboolean gl_mtexable = false; +qboolean gl_compressable=false; +int gl_bumpmappingpossible; + + + + +//int texture_mode = GL_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; +int texture_mode = GL_LINEAR; +//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; +//int texture_mode = GL_LINEAR_MIPMAP_LINEAR; + +int texture_extension_number = 1; + +#define fieldvector(name) QC_RegisterFieldVar(svprogfuncs, ev_vector, #name, (int)&((edict_t*)0)->v.name - (int)&((edict_t*)0)->v, -1) +#define getglcore getglfunction +#define getglext(name) getglfunction(name) +void GL_CheckExtensions (void *(*getglfunction) (char *name)) +{ + extern cvar_t gl_bump; + qboolean support_GL_ARB_texture_env_combine, support_GL_ARB_texture_env_dot3, support_GL_ARB_texture_cube_map; + + + //multitexture + gl_mtexable = false; + gl_mtexarbable = 0; + qglActiveTextureARB = NULL; + qglMultiTexCoord2fARB = NULL; + qglMultiTexCoord3fARB = NULL; + qglMTexCoord2fSGIS = NULL; + qglSelectTextureSGIS = NULL; + mtexid0 = 0; + mtexid1 = 0; + + //none of them bumpmapping possabilities. + gl_bumpmappingpossible = false; + + //no GL_EXT_stencil_wrap + gl_ext_stencil_wrap = false; + + //no GL_ATI_separate_stencil + qglStencilOpSeparateATI = NULL; + + //no GL_EXT_stencil_two_side + qglActiveStencilFaceEXT = NULL; + + //no GL_ARB_texture_compression + gl_compressable = false; + + //no truform. sorry. + qglPNTrianglesfATI = NULL; + qglPNTrianglesiATI = NULL; + + if (strstr(gl_extensions, "GL_ARB_multitexture") && !COM_CheckParm("-noamtex")) + { //ARB multitexture is the popular choice. + Con_SafePrintf("ARB Multitexture extensions found. Use -noamtex to disable.\n"); + qglActiveTextureARB = (void *) getglext("glActiveTextureARB"); + qglClientActiveTextureARB = (void *) getglext("glClientActiveTextureARB"); + qglMultiTexCoord2fARB = (void *) getglext("glMultiTexCoord2fARB"); + qglMultiTexCoord3fARB = (void *) getglext("glMultiTexCoord3fARB"); + + glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &gl_mtexarbable); + gl_mtexable = true; + + qglMTexCoord2fSGIS = qglMultiTexCoord2fARB; + qglSelectTextureSGIS = qglActiveTextureARB; + + mtexid0 = GL_TEXTURE0_ARB; + mtexid1 = GL_TEXTURE1_ARB; + + if (!qglActiveTextureARB || !qglClientActiveTextureARB || !qglMultiTexCoord2fARB || !qglMultiTexCoord3fARB) + { + qglActiveTextureARB = NULL; + qglMultiTexCoord2fARB = NULL; + qglMultiTexCoord3fARB = NULL; + qglMTexCoord2fSGIS = NULL; + qglSelectTextureSGIS = NULL; + gl_mtexable=false; + gl_mtexarbable = false; + } + + } + else if (strstr(gl_extensions, "GL_SGIS_multitexture") && !COM_CheckParm("-nomtex")) + { //SGIS multitexture, limited in many ways but basic functionality is identical to ARB + Con_SafePrintf("Multitexture extensions found.\n"); + qglMTexCoord2fSGIS = (void *) getglext("glMTexCoord2fSGIS"); + qglSelectTextureSGIS = (void *) getglext("glSelectTextureSGIS"); + gl_mtexable = true; + + mtexid0 = GL_TEXTURE0_SGIS; + mtexid1 = GL_TEXTURE1_SGIS; + } + + if (strstr(gl_extensions, "GL_EXT_stencil_wrap")) + gl_ext_stencil_wrap = true; + + if (strstr(gl_extensions, "GL_ATI_separate_stencil")) + qglStencilOpSeparateATI = (void *) getglext("glStencilOpSeparateATI"); + if (strstr(gl_extensions, "GL_EXT_stencil_two_side")) + qglActiveStencilFaceEXT = (void *) getglext("glActiveStencilFaceEXT"); + + if (strstr(gl_extensions, "GL_ARB_texture_compression")) + { + qglCompressedTexImage2DARB = (void *)getglext("glCompressedTexImage2DARB"); + qglGetCompressedTexImageARB = (void *)getglext("glGetCompressedTexImageARB"); + + if (!qglCompressedTexImage2DARB || !qglGetCompressedTexImageARB) + { + qglCompressedTexImage2DARB = NULL; + qglGetCompressedTexImageARB = NULL; + } + else + gl_compressable = true; + } + + if (strstr(gl_extensions, "GL_ATI_pn_triangles")) + { + qglPNTrianglesfATI = (void *)getglext("glPNTrianglesfATI"); + qglPNTrianglesiATI = (void *)getglext("glPNTrianglesiATI"); + } + + if (strstr(gl_extensions, "GL_EXT_texture_object")) + { + bindTexFunc = (void *)getglext("glBindTextureEXT"); + if (!bindTexFunc) //grrr + bindTexFunc = (void *)getglext("glBindTexture"); + } + + support_GL_ARB_texture_env_combine = !!strstr(gl_extensions, "GL_ARB_texture_env_combine"); + support_GL_ARB_texture_env_dot3 = !!strstr(gl_extensions, "GL_ARB_texture_env_dot3"); + support_GL_ARB_texture_cube_map = !!strstr(gl_extensions, "GL_ARB_texture_cube_map"); + + if (gl_mtexarbable && support_GL_ARB_texture_cube_map && support_GL_ARB_texture_env_combine && support_GL_ARB_texture_env_dot3 && !COM_CheckParm("-nobump") && gl_bump.value) + gl_bumpmappingpossible = true; +} + +//the vid routines have initialised a window, and now they are giving us a reference to some of of GetProcAddress to get pointers to the funcs. +void GL_Init(void *(*getglfunction) (char *name)) +{ + qglAlphaFunc = (void *)getglcore("glAlphaFunc"); + qglBegin = (void *)getglcore("glBegin"); + qglBlendFunc = (void *)getglcore("glBlendFunc"); + bindTexFunc = (void *)getglcore("glBindTexture"); //for compleateness + qglClear = (void *)getglcore("glClear"); + qglClearColor = (void *)getglcore("glClearColor"); + qglClearDepth = (void *)getglcore("glClearDepth"); + qglClearStencil = (void *)getglcore("glClearStencil"); + qglColor3f = (void *)getglcore("glColor3f"); + qglColor3ub = (void *)getglcore("glColor3ub"); + qglColor4f = (void *)getglcore("glColor4f"); + qglColor4fv = (void *)getglcore("glColor4fv"); + qglColor4ub = (void *)getglcore("glColor4ub"); + qglColor4ubv = (void *)getglcore("glColor4ubv"); + qglColorMask = (void *)getglcore("glColorMask"); + qglCullFace = (void *)getglcore("glCullFace"); + qglDepthFunc = (void *)getglcore("glDepthFunc"); + qglDepthMask = (void *)getglcore("glDepthMask"); + qglDepthRange = (void *)getglcore("glDepthRange"); + qglDisable = (void *)getglcore("glDisable"); + qglDrawBuffer = (void *)getglcore("glDrawBuffer"); + qglDrawPixels = (void *)getglcore("glDrawPixels"); + qglEnable = (void *)getglcore("glEnable"); + qglEnd = (void *)getglcore("glEnd"); + qglFinish = (void *)getglcore("glFinish"); + qglFlush = (void *)getglcore("glFlush"); + qglFrustum = (void *)getglcore("glFrustum"); + qglGetFloatv = (void *)getglcore("glGetFloatv"); + qglGetIntegerv = (void *)getglcore("glGetIntegerv"); + qglGetString = (void *)getglcore("glGetString"); + qglGetTexLevelParameteriv = (void *)getglcore("glGetTexLevelParameteriv"); + qglHint = (void *)getglcore("glHint"); + qglLoadIdentity = (void *)getglcore("glLoadIdentity"); + qglLoadMatrixf = (void *)getglcore("glLoadMatrixf"); + qglNormal3f = (void *)getglcore("glNormal3f"); + qglNormal3fv = (void *)getglcore("glNormal3fv"); + qglMatrixMode = (void *)getglcore("glMatrixMode"); + qglMultMatrixf = (void *)getglcore("glMultMatrixf"); + qglOrtho = (void *)getglcore("glOrtho"); + qglPolygonMode = (void *)getglcore("glPolygonMode"); + qglPopMatrix = (void *)getglcore("glPopMatrix"); + qglPushMatrix = (void *)getglcore("glPushMatrix"); + qglReadBuffer = (void *)getglcore("glReadBuffer"); + qglReadPixels = (void *)getglcore("glReadPixels"); + qglRotatef = (void *)getglcore("glRotatef"); + qglScalef = (void *)getglcore("glScalef"); + qglShadeModel = (void *)getglcore("glShadeModel"); + qglTexCoord1f = (void *)getglcore("glTexCoord1f"); + qglTexCoord2f = (void *)getglcore("glTexCoord2f"); + qglTexCoord2fv = (void *)getglcore("glTexCoord2fv"); + qglTexEnvf = (void *)getglcore("glTexEnvf"); + qglTexEnvi = (void *)getglcore("glTexEnvi"); + qglTexGeni = (void *)getglcore("glTexGeni"); + qglTexImage2D = (void *)getglcore("glTexImage2D"); + qglTexParameteri = (void *)getglcore("glTexParameteri"); + qglTexParameterf = (void *)getglcore("glTexParameterf"); + qglTexSubImage2D = (void *)getglcore("glTexSubImage2D"); + qglTranslatef = (void *)getglcore("glTranslatef"); + qglVertex2f = (void *)getglcore("glVertex2f"); + qglVertex3f = (void *)getglcore("glVertex3f"); + qglVertex3fv = (void *)getglcore("glVertex3fv"); + qglViewport = (void *)getglcore("glViewport"); + + //fixme: make non-core? + qglDrawElements = (void *)getglcore("glDrawElements"); + qglVertexPointer = (void *)getglcore("glVertexPointer"); + qglNormalPointer = (void *)getglcore("glNormalPointer"); + qglTexCoordPointer = (void *)getglcore("glTexCoordPointer"); + qglColorPointer = (void *)getglcore("glColorPointer"); + qglEnableClientState = (void *)getglcore("glEnableClientState"); + qglDisableClientState = (void *)getglcore("glDisableClientState"); + + //fixme: definatly make non-core + qglStencilOp = (void *)getglcore("glStencilOp"); + qglStencilFunc = (void *)getglcore("glStencilFunc"); + qglPushAttrib = (void *)getglcore("glPushAttrib"); + qglPopAttrib = (void *)getglcore("glPopAttrib"); + + + + + + gl_vendor = glGetString (GL_VENDOR); + Con_SafePrintf ("GL_VENDOR: %s\n", gl_vendor); + gl_renderer = glGetString (GL_RENDERER); + Con_SafePrintf ("GL_RENDERER: %s\n", gl_renderer); + + gl_version = glGetString (GL_VERSION); + Con_SafePrintf ("GL_VERSION: %s\n", gl_version); + gl_extensions = glGetString (GL_EXTENSIONS); + Con_DPrintf ("GL_EXTENSIONS: %s\n", gl_extensions); + + GL_CheckExtensions (getglfunction); + + glClearColor (0,0,0,0); //clear to black so that it looks a little nicer on start. + glClear(GL_COLOR_BUFFER_BIT); + glClearColor (1,0,0,0); + glCullFace(GL_FRONT); + glEnable(GL_TEXTURE_2D); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.666); + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glShadeModel (GL_FLAT); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +} + +unsigned int d_8to24rgbtable[256]; diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c new file mode 100644 index 00000000..2879ba5f --- /dev/null +++ b/engine/gl/gl_vidlinuxglx.c @@ -0,0 +1,899 @@ +/* +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. + +*/ +#include +#include +#include +#ifndef __CYGWIN__ +#include +#endif +#include +#include +#include + +#include + +#include "quakedef.h" +#include "glquake.h" + +#include + +#include +#include + +#ifdef USE_DGA +#include +#endif + +#define WITH_VMODE //undefine this if the following include fails. +#ifdef WITH_VMODE +#include +#endif + + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +#ifdef SWQUAKE +Display *vid_dpy = NULL; +#else +static Display *vid_dpy = NULL; +#endif +static Window vid_window; +static GLXContext ctx = NULL; +int scrnum; + + +static float old_windowed_mouse = 0; + +#define KEY_MASK (KeyPressMask | KeyReleaseMask) +#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \ + PointerMotionMask) + +#define X_MASK (KEY_MASK | MOUSE_MASK | FocusChangeMask | VisibilityChangeMask) + + +#ifdef WITH_VMODE +static qboolean vidmode_ext = false; +static XF86VidModeModeInfo **vidmodes; +static int num_vidmodes; +static qboolean vidmode_active = false; +#endif + +extern cvar_t _windowed_mouse; + + +#ifndef SWQUAKE +cvar_t m_filter = {"m_filter", "0"}; +#ifdef IN_XFLIP +cvar_t in_xflip = {"in_xflip", "0"}; +#endif + +static float mouse_x, mouse_y; +static float old_mouse_x, old_mouse_y; + +#else + +extern float mouse_x, mouse_y; +extern float old_mouse_x, old_mouse_y; +#endif + +static int scr_width, scr_height; + +/*-----------------------------------------------------------------------*/ + +float gldepthmin, gldepthmax; + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +qboolean is8bit = false; +qboolean isPermedia = false; +float vid_gamma = 1.0; +qboolean mouseactive = false; +qboolean has_focus = false; + +int gl_canstencil; + + +/*-----------------------------------------------------------------------*/ + +static void *gllibrary; + +XVisualInfo* (*qglXChooseVisual) (Display *dpy, int screen, int *attribList); +void (*qglXSwapBuffers) (Display *dpy, GLXDrawable drawable); +Bool (*qglXMakeCurrent) (Display *dpy, GLXDrawable drawable, GLXContext ctx); +GLXContext (*qglXCreateContext) (Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct); +void (*qglXDestroyContext) (Display *dpy, GLXContext ctx); +void *(*qglXGetProcAddress) (char *name); + +void GLX_CloseLibrary(void) +{ + dlclose(gllibrary); + gllibrary = NULL; +} + +qboolean GLX_InitLibrary(char *driver) +{ + if (driver && *driver) + gllibrary = dlopen(driver, RTLD_LOCAL | RTLD_LAZY); + else + gllibrary = NULL; + if (!gllibrary) + gllibrary = dlopen("libGL.so", RTLD_LOCAL | RTLD_LAZY); + if (!gllibrary) + return false; + + qglXChooseVisual = dlsym(gllibrary, "glXChooseVisual"); + qglXSwapBuffers = dlsym(gllibrary, "glXSwapBuffers"); + qglXMakeCurrent = dlsym(gllibrary, "glXMakeCurrent"); + qglXCreateContext = dlsym(gllibrary, "glXCreateContext"); + qglXDestroyContext = dlsym(gllibrary, "glXDestroyContext"); + qglXDestroyContext = dlsym(gllibrary, "glXDestroyContext"); + qglXGetProcAddress = dlsym(gllibrary, "glXGetProcAddress"); + if (!qglXGetProcAddress) + qglXGetProcAddress = dlsym(gllibrary, "glXGetProcAddressARB"); + + if (!qglXSwapBuffers && !qglXDestroyContext && !qglXCreateContext && !qglXMakeCurrent && !qglXChooseVisual) + return false; + + return true; +} + +void *GLX_GetSymbol(char *name) +{ + void *symb; + if (qglXGetProcAddress) + symb = qglXGetProcAddress(name); + else + symb = NULL; + + if (!symb) + symb = dlsym(gllibrary, name); + return symb; +} + + +void GLD_BeginDirectRect (int x, int y, qbyte *pbitmap, int width, int height) +{ +} + +void GLD_EndDirectRect (int x, int y, int width, int height) +{ +} + +static int XLateKey(XKeyEvent *ev) +{ + + int key; + char buf[64]; + KeySym keysym; + + key = 0; + + XLookupString(ev, buf, sizeof buf, &keysym, 0); + + switch(keysym) + { + case XK_KP_Page_Up: + case XK_Page_Up: key = K_PGUP; break; + + case XK_KP_Page_Down: + case XK_Page_Down: key = K_PGDN; break; + + case XK_KP_Home: + case XK_Home: key = K_HOME; break; + + case XK_KP_End: + case XK_End: key = K_END; break; + + case XK_KP_Left: + case XK_Left: key = K_LEFTARROW; break; + + case XK_KP_Right: + case XK_Right: key = K_RIGHTARROW; break; + + case XK_KP_Down: + case XK_Down: key = K_DOWNARROW; break; + + case XK_KP_Up: + case XK_Up: key = K_UPARROW; break; + + case XK_Escape: key = K_ESCAPE; break; + + case XK_KP_Enter: + case XK_Return: key = K_ENTER; break; + + case XK_Tab: key = K_TAB; break; + + case XK_F1: key = K_F1; break; + + case XK_F2: key = K_F2; break; + + case XK_F3: key = K_F3; break; + + case XK_F4: key = K_F4; break; + + case XK_F5: key = K_F5; break; + + case XK_F6: key = K_F6; break; + + case XK_F7: key = K_F7; break; + + case XK_F8: key = K_F8; break; + + case XK_F9: key = K_F9; break; + + case XK_F10: key = K_F10; break; + + case XK_F11: key = K_F11; break; + + case XK_F12: key = K_F12; break; + + case XK_BackSpace: key = K_BACKSPACE; break; + + case XK_KP_Delete: + case XK_Delete: key = K_DEL; break; + + case XK_Pause: key = K_PAUSE; break; + + case XK_Shift_L: + case XK_Shift_R: key = K_SHIFT; break; + + case XK_Execute: + case XK_Control_L: + case XK_Control_R: key = K_CTRL; break; + + case XK_Alt_L: + case XK_Meta_L: + case XK_Alt_R: + case XK_Meta_R: key = K_ALT; break; + + case XK_KP_Begin: key = '5'; break; + + case XK_KP_Insert: + case XK_Insert:key = K_INS; break; + + case XK_KP_Multiply: key = '*'; break; + case XK_KP_Add: key = '+'; break; + case XK_KP_Subtract: key = '-'; break; + case XK_KP_Divide: key = '/'; break; + +#if 0 + case 0x021: key = '1';break;/* [!] */ + case 0x040: key = '2';break;/* [@] */ + case 0x023: key = '3';break;/* [#] */ + case 0x024: key = '4';break;/* [$] */ + case 0x025: key = '5';break;/* [%] */ + case 0x05e: key = '6';break;/* [^] */ + case 0x026: key = '7';break;/* [&] */ + case 0x02a: key = '8';break;/* [*] */ + case 0x028: key = '9';;break;/* [(] */ + case 0x029: key = '0';break;/* [)] */ + case 0x05f: key = '-';break;/* [_] */ + case 0x02b: key = '=';break;/* [+] */ + case 0x07c: key = '\'';break;/* [|] */ + case 0x07d: key = '[';break;/* [}] */ + case 0x07b: key = ']';break;/* [{] */ + case 0x022: key = '\'';break;/* ["] */ + case 0x03a: key = ';';break;/* [:] */ + case 0x03f: key = '/';break;/* [?] */ + case 0x03e: key = '.';break;/* [>] */ + case 0x03c: key = ',';break;/* [<] */ +#endif + + default: + key = *(unsigned char*)buf; + if (key >= 'A' && key <= 'Z') + key = key - 'A' + 'a'; + break; + } + + return key; +} + +static void install_grabs(void) +{ + XGrabPointer(vid_dpy, vid_window, + True, + 0, + GrabModeAsync, GrabModeAsync, + vid_window, + None, + CurrentTime); + +#ifdef USE_DGA + XF86DGADirectVideo(vid_dpy, DefaultScreen(vid_dpy), XF86DGADirectMouse); + dgamouse = 1; +#else + XWarpPointer(vid_dpy, None, vid_window, + 0, 0, 0, 0, + vid.width / 2, vid.height / 2); +#endif + +// XSync(vid_dpy, True); +} + +static void uninstall_grabs(void) +{ +#ifdef USE_DGA + XF86DGADirectVideo(vid_dpy, DefaultScreen(vid_dpy), 0); + dgamouse = 0; +#endif + + XUngrabPointer(vid_dpy, CurrentTime); + +// XSync(vid_dpy, True); +} + +static void GetEvent(void) +{ + XEvent event; + int b; + qboolean wantwindowed; + + if (!vid_dpy) + return; + + XNextEvent(vid_dpy, &event); + + switch (event.type) { + case KeyPress: + case KeyRelease: + Key_Event(XLateKey(&event.xkey), event.type == KeyPress); + break; + + case MotionNotify: +#ifdef USE_DGA + if (dgamouse && old_windowed_mouse) + { + mouse_x = event.xmotion.x_root; + mouse_y = event.xmotion.y_root; + } + else +#endif + { + if (old_windowed_mouse) + { + mouse_x = (float) ((int)event.xmotion.x - (int)(vid.width/2)); + mouse_y = (float) ((int)event.xmotion.y - (int)(vid.height/2)); + + /* move the mouse to the window center again */ + XSelectInput(vid_dpy, vid_window, X_MASK & ~PointerMotionMask); + XWarpPointer(vid_dpy, None, vid_window, 0, 0, 0, 0, + (vid.width/2), (vid.height/2)); + XSelectInput(vid_dpy, vid_window, X_MASK); + } + } + break; + + case ButtonPress: + b=-1; + if (event.xbutton.button == 1) + b = 0; + else if (event.xbutton.button == 2) + b = 2; + else if (event.xbutton.button == 3) + b = 1; + if (b>=0) + Key_Event(K_MOUSE1 + b, true); + break; + + case ButtonRelease: + b=-1; + if (event.xbutton.button == 1) + b = 0; + else if (event.xbutton.button == 2) + b = 2; + else if (event.xbutton.button == 3) + b = 1; + if (b>=0) + Key_Event(K_MOUSE1 + b, false); + break; + + case FocusIn: + has_focus = true; + break; + case FocusOut: + has_focus = false; + break; + } + + wantwindowed = !!_windowed_mouse.value; + if (!has_focus) + wantwindowed = false; + if (key_dest == key_console) + wantwindowed = false; + + if (old_windowed_mouse != wantwindowed) + { + old_windowed_mouse = wantwindowed; + + if (!wantwindowed) + { + /* ungrab the pointer */ + uninstall_grabs(); + } + else + { + /* grab the pointer */ + install_grabs(); + } + } +} + + +void GLVID_Shutdown(void) +{ +printf("GLVID_Shutdown"); + if (!ctx) + return; + + XUngrabKeyboard(vid_dpy, CurrentTime); + if (old_windowed_mouse) + uninstall_grabs(); + + qglXDestroyContext(vid_dpy, ctx); + if (vid_window) + XDestroyWindow(vid_dpy, vid_window); +#ifdef WITH_VMODE + if (vid_dpy) { + if (vidmode_active) + XF86VidModeSwitchToMode(vid_dpy, scrnum, vidmodes[0]); + vidmode_active = false; + } +#endif +// XCloseDisplay(vid_dpy); +// vid_dpy = NULL; + vid_window = (Window)NULL; +} + +void GLVID_DeInit(void) //FIXME:.... +{ + GLVID_Shutdown(); +} + + +void signal_handler(int sig) +{ + printf("Received signal %d, exiting...\n", sig); + Sys_Quit(); + exit(0); +} + +void InitSig(void) +{ + signal(SIGHUP, signal_handler); + signal(SIGINT, signal_handler); + signal(SIGQUIT, signal_handler); + signal(SIGILL, signal_handler); + signal(SIGTRAP, signal_handler); +#ifndef __CYGWIN__ + signal(SIGIOT, signal_handler); +#endif + signal(SIGBUS, signal_handler); + signal(SIGFPE, signal_handler); + signal(SIGSEGV, signal_handler); + signal(SIGTERM, signal_handler); +} + +static Cursor CreateNullCursor(Display *display, Window root) +{ + Pixmap cursormask; + XGCValues xgc; + GC gc; + XColor dummycolour; + Cursor cursor; + + cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/); + xgc.function = GXclear; + gc = XCreateGC(display, cursormask, GCFunction, &xgc); + XFillRectangle(display, cursormask, gc, 0, 0, 1, 1); + dummycolour.pixel = 0; + dummycolour.red = 0; + dummycolour.flags = 04; + cursor = XCreatePixmapCursor(display, cursormask, cursormask, + &dummycolour,&dummycolour, 0,0); + XFreePixmap(display,cursormask); + XFreeGC(display,gc); + return cursor; +} + +void GLVID_ShiftPalette(unsigned char *p) +{ +// VID_SetPalette(p); +} + +void GLVID_SetPalette (unsigned char *palette) +{ + qbyte *pal; + unsigned r,g,b; + unsigned v; + unsigned short i; + unsigned *table; + unsigned *table2; + extern qbyte gammatable[256]; + +// +// 8 8 8 encoding +// + Con_Printf("Converting 8to24\n"); + + pal = palette; + table = d_8to24rgbtable; + table2 = d_8to24bgrtable; + for (i=0 ; i<256 ; i++) + { + r = gammatable[pal[0]]; + g = gammatable[pal[1]]; + b = gammatable[pal[2]]; + pal += 3; + +// v = (255<<24) + (r<<16) + (g<<8) + (b<<0); +// v = (255<<0) + (r<<8) + (g<<16) + (b<<24); + v = (255<<24) + (r<<0) + (g<<8) + (b<<16); + *table++ = v; + *table2++ = (255<<24) + (r<<16) + (g<<8) + (b<<0); + } + d_8to24bgrtable[255] &= 0xffffff; // 255 is transparent + d_8to24rgbtable[255] &= 0xffffff; // 255 is transparent +} + +/* +================= +GL_BeginRendering + +================= +*/ +void GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + *x = *y = 0; + *width = scr_width; + *height = scr_height; + +// if (!wglMakeCurrent( maindc, baseRC )) +// Sys_Error ("wglMakeCurrent failed"); + +// glViewport (*x, *y, *width, *height); +} + + +void GL_EndRendering (void) +{ +//return; + glFlush(); + qglXSwapBuffers(vid_dpy, vid_window); +} + +qboolean GLVID_Is8bit(void) +{ + return is8bit; +} + +qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) +{ + int i; + int attrib[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, 1, + None + }; + char gldir[MAX_OSPATH]; + XSetWindowAttributes attr; + unsigned long mask; + Window root; + XVisualInfo *visinfo; + +#ifdef WITH_VMODE + qboolean fullscreen = false; + int MajorVersion, MinorVersion, actualWidth, actualHeight; + + if (info->fullscreen) + fullscreen = true; +#endif + + + S_Startup(); + + GLX_InitLibrary(info->glrenderer); + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + +// interpret command-line params + +// set vid parameters + if ((i = COM_CheckParm("-conwidth")) != 0) + vid.conwidth = Q_atoi(com_argv[i+1]); + else + vid.conwidth = 640; + + vid.conwidth &= ~7; // make it a multiple of eight + + if (vid.conwidth < 320) + vid.conwidth = 320; + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth*3 / 4; + + if ((i = COM_CheckParm("-conheight")) != 0) + vid.conheight = Q_atoi(com_argv[i+1]); + if (vid.conheight < 200) + vid.conheight = 200; + if (!vid_dpy) + vid_dpy = XOpenDisplay(NULL); + if (!vid_dpy) + { + Sys_Error("Error couldn't open the X display\n"); + } + + scrnum = DefaultScreen(vid_dpy); + root = RootWindow(vid_dpy, scrnum); + +#ifdef WITH_VMODE //find out if it's supported on this pc. + MajorVersion = MinorVersion = 0; + if (!XF86VidModeQueryVersion(vid_dpy, &MajorVersion, &MinorVersion)) + { + vidmode_ext = false; + } + else + { + Con_Printf("Using XF86-VidModeExtension Ver. %d.%d\n", MajorVersion, MinorVersion); + vidmode_ext = true; + } +#endif + + visinfo = qglXChooseVisual(vid_dpy, scrnum, attrib); + if (!visinfo) + { + Sys_Error("qkHack: Error couldn't get an RGB, Double-buffered, Depth visual\n"); + } + +#ifdef WITH_VMODE + if (vidmode_ext) + { + int best_fit, best_dist, dist, x, y; + + XF86VidModeGetAllModeLines(vid_dpy, scrnum, &num_vidmodes, &vidmodes); + // Are we going fullscreen? If so, let's change video mode + if (fullscreen) + { + best_dist = 9999999; + best_fit = -1; + + for (i = 0; i < num_vidmodes; i++) + { + if (info->width > vidmodes[i]->hdisplay || + info->height > vidmodes[i]->vdisplay) + continue; + + x = info->width - vidmodes[i]->hdisplay; + y = info->height - vidmodes[i]->vdisplay; + dist = (x * x) + (y * y); + if (dist < best_dist) + { + best_dist = dist; + best_fit = i; + } + } + + if (best_fit != -1 && (!best_dist || COM_CheckParm("-fullscreen"))) + { + actualWidth = vidmodes[best_fit]->hdisplay; + actualHeight = vidmodes[best_fit]->vdisplay; + // change to the mode + XF86VidModeSwitchToMode(vid_dpy, scrnum, vidmodes[best_fit]); + vidmode_active = true; + // Move the viewport to top left + XF86VidModeSetViewPort(vid_dpy, scrnum, 0, 0); + + } + else + fullscreen = 0; + } + } +#endif + + /* window attributes */ + attr.background_pixel = 0; + attr.border_pixel = 0; + attr.colormap = XCreateColormap(vid_dpy, root, visinfo->visual, AllocNone); + attr.event_mask = X_MASK; + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + +#ifdef WITH_VMODE //get rid of borders + // fullscreen + if (vidmode_active) { + mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | + CWEventMask | CWOverrideRedirect; + attr.override_redirect = True; + attr.backing_store = NotUseful; + attr.save_under = False; + } +#endif + + has_focus = false; + vid_window = XCreateWindow(vid_dpy, root, 0, 0, info->width, info->height, + 0, visinfo->depth, InputOutput, + visinfo->visual, mask, &attr); + XMapWindow(vid_dpy, vid_window); + + XMoveWindow(vid_dpy, vid_window, 0, 0); +#ifdef WITH_VMODE + if (vidmode_active) { + XRaiseWindow(vid_dpy, vid_window); + XWarpPointer(vid_dpy, None, vid_window, 0, 0, 0, 0, 0, 0); + XFlush(vid_dpy); + // Move the viewport to top left + XF86VidModeSetViewPort(vid_dpy, scrnum, 0, 0); + } +#endif + XStoreName(vid_dpy, vid_window, "GLX QuakeWorld Cient"); + +//hide the cursor. + XDefineCursor(vid_dpy, vid_window, CreateNullCursor(vid_dpy, vid_window)); + + XFlush(vid_dpy); + + ctx = qglXCreateContext(vid_dpy, visinfo, NULL, True); + + qglXMakeCurrent(vid_dpy, vid_window, ctx); + + scr_width = info->width; + scr_height = info->height; + + if (vid.conheight > info->height) + vid.conheight = info->height; + if (vid.conwidth > info->width) + vid.conwidth = info->width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); + vid.numpages = 2; + + InitSig(); // trap evil signals + + GL_Init(&GLX_GetSymbol); + + sprintf (gldir, "%s/glquake", com_gamedir); + Sys_mkdir (gldir); + + VID_SetPalette(palette); + + Con_SafePrintf ("Video mode %dx%d initialized.\n", info->width, info->height); + + vid.recalc_refdef = 1; // force a surface cache flush + + if (Cvar_Get("vidx_grabkeyboard", "0", 0, "Additional video options")->value) + XGrabKeyboard(vid_dpy, vid_window, + False, + GrabModeAsync, GrabModeAsync, + CurrentTime); + else + XSetInputFocus(vid_dpy, vid_window, RevertToParent, CurrentTime); + XRaiseWindow(vid_dpy, vid_window); + + return true; +} + +#ifdef SWQUAKE +void GLSys_SendKeyEvents(void) +#else +void Sys_SendKeyEvents(void) +#endif +{ + if (vid_dpy && vid_window) { + while (XPending(vid_dpy)) + GetEvent(); + } +} + +void Force_CenterView_f (void) +{ + cl.viewangles[0][PITCH] = 0; +} + +#ifndef SWQUAKE +void IN_Init(void) +{ +#ifdef IN_XFLIP + Cvar_Register (&in_xflip, "Input variables"); +#endif +} + +void IN_Shutdown(void) +{ +} + +/* +=========== +IN_Commands +=========== +*/ +void IN_Commands (void) +{ +} + +/* +=========== +IN_Move +=========== +*/ +void IN_MouseMove (usercmd_t *cmd, int pnum) +{ + if (m_filter.value) + { + mouse_x = (mouse_x + old_mouse_x) * 0.5; + mouse_y = (mouse_y + old_mouse_y) * 0.5; + } + old_mouse_x = mouse_x; + old_mouse_y = mouse_y; + + mouse_x *= sensitivity.value; + mouse_y *= sensitivity.value; + +#ifdef IN_XFLIP + if(in_xflip.value) mouse_x *= -1; +#endif + +// add mouse X/Y movement to cmd + if ( (in_strafe.state[pnum] & 1) || (lookstrafe.value && (in_mlook.state[pnum] & 1) )) + cmd->sidemove += m_side.value * mouse_x; + else + cl.viewangles[pnum][YAW] -= m_yaw.value * mouse_x; + + if (in_mlook.state[pnum] & 1) + V_StopPitchDrift (pnum); + + if ( (in_mlook.state[pnum] & 1) && !(in_strafe.state[pnum] & 1)) + { + cl.viewangles[pnum][PITCH] += m_pitch.value * mouse_y; + CL_ClampPitch(pnum); + } + else + { + if ((in_strafe.state[pnum] & 1) && noclip_anglehack) + cmd->upmove -= m_forward.value * mouse_y; + else + cmd->forwardmove -= m_forward.value * mouse_y; + } + mouse_x = mouse_y = 0.0; +} + +void IN_Move (usercmd_t *cmd, int pnum) +{ + IN_MouseMove(cmd, pnum); +} +#endif + + +void GLVID_UnlockBuffer() {} +void GLVID_LockBuffer() {} + +int GLVID_ForceUnlockedAndReturnState (void) {return 0;} +void GLVID_ForceLockState (int lk) {} +void GLVID_HandlePause (qboolean pause) {} diff --git a/engine/gl/gl_vidnt.c b/engine/gl/gl_vidnt.c new file mode 100644 index 00000000..2961df63 --- /dev/null +++ b/engine/gl/gl_vidnt.c @@ -0,0 +1,1283 @@ +/* +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_vidnt.c -- NT GL vid component + +#include "quakedef.h" +#include "glquake.h" +#include "winquake.h" +#include "resource.h" +#include + + +#ifndef CDS_FULLSCREEN +#define CDS_FULLSCREEN 4 +#endif + + +#ifndef WM_XBUTTONDOWN + #define WM_XBUTTONDOWN 0x020B + #define WM_XBUTTONUP 0x020C +#endif +#ifndef MK_XBUTTON1 + #define MK_XBUTTON1 0x0020 + #define MK_XBUTTON2 0x0040 +#endif + + + + + +#define WINDOW_CLASS_NAME "WinQuake" + +#define MAX_MODE_LIST 128 +#define VID_ROW_SIZE 3 +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 +#define MAXWIDTH 10000 +#define MAXHEIGHT 10000 +#define BASEWIDTH 320 +#define BASEHEIGHT 200 + +typedef enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_UNINIT} modestate_t; + +#ifdef AVAIL_DX7 +void D3DInitialize(void); +void d3dSetMode(int fullscreen, int width, int height, int bpp, int zbpp); +#endif +BOOL bSetupPixelFormat(HDC hDC); + +//qboolean VID_SetWindowedMode (int modenum); +//qboolean VID_SetFullDIBMode (int modenum); +qboolean VID_SetWindowedMode (rendererstate_t *info); //-1 on bpp or hz for default. +qboolean VID_SetFullDIBMode (rendererstate_t *info); //-1 on bpp or hz for default. + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +qboolean DDActive; +qboolean scr_skipupdate; + +int gl_canstencil; + +static DEVMODE gdevmode; +static qboolean vid_initialized = false; +static qboolean leavecurrentmode= true; +static qboolean vid_canalttab = false; +static qboolean vid_wassuspended = false; +static int windowed_mouse; +extern qboolean mouseactive; // from in_win.c +static HICON hIcon; +extern qboolean vid_isfullscreen; + +#ifdef SWQUAKE +extern +#endif + qboolean vid_initializing; + + + +qboolean VID_AttachGL (rendererstate_t *info); + +int DIBWidth, DIBHeight; +RECT WindowRect; +DWORD WindowStyle, ExWindowStyle; + +HWND mainwindow, dibwindow; + +unsigned char vid_curpal[256*3]; +static qboolean fullsbardraw = false; + +float vid_gamma = 1.0; + +HGLRC baseRC; +HDC maindc; + +glvert_t glv; + + +HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow); + +viddef_t vid; // global video state + +//unsigned short d_8to16rgbtable[256]; +//unsigned d_8to24rgbtable[256]; +//unsigned short d_8to16bgrtable[256]; +//unsigned d_8to24bgrtable[256]; + +float gldepthmin, gldepthmax; + +modestate_t modestate = MS_UNINIT; + + +LONG WINAPI GLMainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +void GLAppActivate(BOOL fActive, BOOL minimize); +char *VID_GetModeDescription (int mode); +void ClearAllStates (void); +void VID_UpdateWindowStatus (void); +void GL_Init(void *(*getglfunction) (char *name)); + +PROC glArrayElementEXT; +PROC glColorPointerEXT; +PROC glTexCoordPointerEXT; +PROC glVertexPointerEXT; + +typedef void (APIENTRY *lp3DFXFUNC) (int, int, int, int, int, const void*); +lp3DFXFUNC glColorTableEXT; +qboolean is8bit = false; +qboolean isPermedia = false; + +//==================================== +// Note that 0 is MODE_WINDOWED +extern cvar_t vid_mode; +extern cvar_t _vid_default_mode; +// Note that 3 is MODE_FULLSCREEN_DEFAULT +extern cvar_t _vid_default_mode_win; +extern cvar_t vid_wait; +extern cvar_t vid_nopageflip; +extern cvar_t _vid_wait_override; +extern cvar_t vid_stretch_by_2; +extern cvar_t _windowed_mouse; + +int window_center_x, window_center_y, window_x, window_y, window_width, window_height; +RECT window_rect; + +HMODULE hInstGL = NULL; +HMODULE hInstwgl = NULL; +char opengldllname[MAX_OSPATH]; + +//just GetProcAddress with a safty net. +void *getglfunc(char *name) +{ + FARPROC proc; + proc = qwglGetProcAddress?qwglGetProcAddress(name):NULL; + if (!proc) + { + proc = GetProcAddress(hInstGL, name); + return proc; + } + return proc; +} +void *getwglfunc(char *name) +{ + FARPROC proc; + proc = GetProcAddress(hInstGL, name); + if (!proc) + { + if (!hInstwgl) + hInstwgl = LoadLibrary("opengl32.dll"); + proc = GetProcAddress(hInstwgl, name); + if (!proc) + Sys_Error("GL function %s was not found in %s\nPossibly you do not have a full enough gl implementation", name, opengldllname); + } + return proc; +} + +HGLRC (WINAPI *qwglCreateContext)(HDC); +BOOL (WINAPI *qwglDeleteContext)(HGLRC); +HGLRC (WINAPI *qwglGetCurrentContext)(VOID); +HDC (WINAPI *qwglGetCurrentDC)(VOID); +PROC (WINAPI *qwglGetProcAddress)(LPCSTR); +BOOL (WINAPI *qwglMakeCurrent)(HDC, HGLRC); +BOOL (WINAPI *qSwapBuffers)(HDC); + +BOOL (WINAPI *qwglSwapIntervalEXT) (int); + + +qboolean GLInitialise (char *renderer) +{ + if (hInstGL) + FreeModule(hInstGL); + if (hInstwgl) + FreeModule(hInstwgl); + hInstwgl=NULL; + + strcpy(opengldllname, renderer); + + hInstGL = LoadLibrary(opengldllname); + + if (!hInstGL) + { + Con_SafePrintf ("Couldn't load %s\n", opengldllname); + return false; + } + + // windows dependant + qwglCreateContext = (void *)getwglfunc("wglCreateContext"); + qwglDeleteContext = (void *)getwglfunc("wglDeleteContext"); + qwglGetCurrentContext = (void *)getwglfunc("wglGetCurrentContext"); + qwglGetCurrentDC = (void *)getwglfunc("wglGetCurrentDC"); + qwglGetProcAddress = (void *)getwglfunc("wglGetProcAddress"); + qwglMakeCurrent = (void *)getwglfunc("wglMakeCurrent"); + qSwapBuffers = SwapBuffers; + + return true; +} + +// direct draw software compatability stuff + +void GLVID_HandlePause (qboolean pause) +{ +} + +void GLVID_ForceLockState (int lk) +{ +} + +void GLVID_LockBuffer (void) +{ +} + +void GLVID_UnlockBuffer (void) +{ +} + +int GLVID_ForceUnlockedAndReturnState (void) +{ + return 0; +} + +void GLD_BeginDirectRect (int x, int y, qbyte *pbitmap, int width, int height) +{ +} + +void GLD_EndDirectRect (int x, int y, int width, int height) +{ +} + + +void CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify) +{ +// RECT rect; + int CenterX, CenterY; + + CenterX = (GetSystemMetrics(SM_CXSCREEN) - width) / 2; + CenterY = (GetSystemMetrics(SM_CYSCREEN) - height) / 2; + if (CenterX > CenterY*2) + CenterX >>= 1; // dual screens + CenterX = (CenterX < 0) ? 0: CenterX; + CenterY = (CenterY < 0) ? 0: CenterY; + SetWindowPos (hWndCenter, NULL, CenterX, CenterY, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); +} + +qboolean VID_SetWindowedMode (rendererstate_t *info) +//qboolean VID_SetWindowedMode (int modenum) +{ + int i; + HDC hdc; + int lastmodestate, wwidth, wheight; + RECT rect; + + hdc = GetDC(NULL); + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) + { + ReleaseDC(NULL, hdc); + Con_Printf("Can't run GL in non-RGB mode\n"); + return false; + } + ReleaseDC(NULL, hdc); + + lastmodestate = modestate; + + WindowRect.top = WindowRect.left = 0; + + WindowRect.right = info->width; + WindowRect.bottom = info->height; + + DIBWidth = info->width; + DIBHeight = info->height; + + WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | + WS_MINIMIZEBOX; + ExWindowStyle = 0; + + WindowStyle |= WS_SIZEBOX | WS_MAXIMIZEBOX; + + rect = WindowRect; + AdjustWindowRectEx(&rect, WindowStyle, FALSE, 0); + + wwidth = rect.right - rect.left; + wheight = rect.bottom - rect.top; + + // Create the DIB window + dibwindow = CreateWindowEx ( + ExWindowStyle, + WINDOW_CLASS_NAME, + "FTE QuakeWorld", + WindowStyle, + rect.left, rect.top, + wwidth, + wheight, + NULL, + NULL, + global_hInstance, + NULL); + + if (!dibwindow) + { + Con_Printf ("Couldn't create DIB window"); + return false; + } + + // Center and show the DIB window + CenterWindow(dibwindow, WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, false); + + ShowWindow (dibwindow, SW_SHOWDEFAULT); + UpdateWindow (dibwindow); + + modestate = MS_WINDOWED; + +// because we have set the background brush for the window to NULL +// (to avoid flickering when re-sizing the window on the desktop), +// we clear the window to black when created, otherwise it will be +// empty while Quake starts up. + hdc = GetDC(dibwindow); + PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); + ReleaseDC(dibwindow, hdc); + + + if ((i = COM_CheckParm("-conwidth")) != 0) + vid.conwidth = Q_atoi(com_argv[i+1]); + else + { + extern cvar_t gl_2dscale; + vid.conwidth = 640; + gl_2dscale.modified = true; + } + + vid.conwidth &= 0xfff8; // make it a multiple of eight + + if (vid.conwidth < 320) + vid.conwidth = 320; + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth*3 / 4; + + if ((i = COM_CheckParm("-conheight")) != 0) + vid.conheight = Q_atoi(com_argv[i+1]); + if (vid.conheight < 200) + vid.conheight = 200; + + if (vid.conheight > info->height) + vid.conheight = info->height; + if (vid.conwidth > info->width) + vid.conwidth = info->width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.numpages = 2; + + mainwindow = dibwindow; + vid_isfullscreen=false; + + SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); + + return true; +} + + +qboolean VID_SetFullDIBMode (rendererstate_t *info) +{ + int i; + HDC hdc; + int lastmodestate, wwidth, wheight; + RECT rect; + + if (leavecurrentmode) //make windows change res. + { + gdevmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + if (info->bpp) + gdevmode.dmFields |= DM_BITSPERPEL; + if (info->rate) + gdevmode.dmFields |= DM_DISPLAYFREQUENCY; + gdevmode.dmBitsPerPel = info->bpp; + gdevmode.dmDisplayFrequency = info->rate; + gdevmode.dmPelsWidth = info->width; + gdevmode.dmPelsHeight = info->height; + gdevmode.dmSize = sizeof (gdevmode); + + if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) + { + Con_SafePrintf((gdevmode.dmFields&DM_DISPLAYFREQUENCY)?"Windows rejected mode %i*%i*%i*%i\n":"Windows rejected mode %i*%i*%i\n", gdevmode.dmPelsWidth, gdevmode.dmPelsHeight, gdevmode.dmBitsPerPel, gdevmode.dmDisplayFrequency); + return false; + } + } + + lastmodestate = modestate; + modestate = MS_FULLDIB; + + WindowRect.top = WindowRect.left = 0; + + WindowRect.right = info->width; + WindowRect.bottom = info->height; + + DIBWidth = info->width; + DIBHeight = info->height; + + WindowStyle = WS_POPUP; + ExWindowStyle = 0; + + rect = WindowRect; + AdjustWindowRectEx(&rect, WindowStyle, FALSE, 0); + + wwidth = rect.right - rect.left; + wheight = rect.bottom - rect.top; + + // Create the DIB window + dibwindow = CreateWindowEx ( + ExWindowStyle, + WINDOW_CLASS_NAME, + "FTE QuakeWorld", + WindowStyle, + rect.left, rect.top, + wwidth, + wheight, + NULL, + NULL, + global_hInstance, + NULL); + + if (!dibwindow) + Sys_Error ("Couldn't create DIB window"); + + ShowWindow (dibwindow, SW_SHOWDEFAULT); + UpdateWindow (dibwindow); + + // Because we have set the background brush for the window to NULL + // (to avoid flickering when re-sizing the window on the desktop), we + // clear the window to black when created, otherwise it will be + // empty while Quake starts up. + hdc = GetDC(dibwindow); + PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); + ReleaseDC(dibwindow, hdc); + + + if ((i = COM_CheckParm("-conwidth")) != 0) + vid.conwidth = Q_atoi(com_argv[i+1]); + else + vid.conwidth = 640; + + vid.conwidth &= 0xfff8; // make it a multiple of eight + + if (vid.conwidth < 320) + vid.conwidth = 320; + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth*3 / 4; + + if ((i = COM_CheckParm("-conheight")) != 0) + vid.conheight = Q_atoi(com_argv[i+1]); + if (vid.conheight < 200) + vid.conheight = 200; + + if (vid.conheight > info->height) + vid.conheight = info->height; + if (vid.conwidth > info->width) + vid.conwidth = info->width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.numpages = 2; + +// needed because we're not getting WM_MOVE messages fullscreen on NT + window_x = 0; + window_y = 0; + vid_isfullscreen=true; + + mainwindow = dibwindow; + + SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); + + return true; +} + + +int GLVID_SetMode (rendererstate_t *info, unsigned char *palette) +{ + int temp; + qboolean stat; + MSG msg; +// HDC hdc; + +// so Con_Printfs don't mess us up by forcing vid and snd updates + temp = scr_disabled_for_loading; + scr_disabled_for_loading = true; + + CDAudio_Pause (); + + // Set either the fullscreen or windowed mode + if (!info->fullscreen) + { + if (_windowed_mouse.value && (key_dest == key_game || key_dest == key_menu)) + { + stat = VID_SetWindowedMode(info); + IN_ActivateMouse (); + IN_HideMouse (); + } + else + { + IN_DeactivateMouse (); + IN_ShowMouse (); + stat = VID_SetWindowedMode(info); + } + } + else + { + stat = VID_SetFullDIBMode(info); + IN_ActivateMouse (); + IN_HideMouse (); + } + + window_width = DIBWidth; + window_height = DIBHeight; + VID_UpdateWindowStatus (); + + CDAudio_Resume (); + scr_disabled_for_loading = temp; + + if (!stat) + { + return false; + } + +// now we try to make sure we get the focus on the mode switch, because +// sometimes in some systems we don't. We grab the foreground, then +// finish setting up, pump all our messages, and sleep for a little while +// to let messages finish bouncing around the system, then we put +// ourselves at the top of the z order, then grab the foreground again, +// Who knows if it helps, but it probably doesn't hurt + SetForegroundWindow (mainwindow); + VID_SetPalette (palette); + + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + Sleep (100); + + SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, + SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + SWP_NOCOPYBITS); + + SetForegroundWindow (mainwindow); + +// fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + GLVID_SetPalette (palette); + + vid.recalc_refdef = 1; + + if (!VID_AttachGL(info)) + return false; + + return true; +} + +void VID_UnSetMode (void) +{ + HGLRC hRC; + HDC hDC = NULL; + + if (mainwindow && vid_initialized) + { + GLAppActivate(false, false); + + vid_canalttab = false; + if (qwglGetCurrentContext) + { + hRC = qwglGetCurrentContext(); + hDC = qwglGetCurrentDC(); + + qwglMakeCurrent(NULL, NULL); + + if (hRC) + qwglDeleteContext(hRC); + } + qwglGetCurrentContext=NULL; + + if (hDC && dibwindow) + ReleaseDC(dibwindow, hDC); + + if (modestate == MS_FULLDIB) + ChangeDisplaySettings (NULL, 0); + + if (maindc && dibwindow) + ReleaseDC (dibwindow, maindc); + } + + if (mainwindow) + { + dibwindow=NULL; +// SendMessage(mainwindow, WM_CLOSE, 0, 0); + DestroyWindow(mainwindow); + mainwindow = NULL; + } + if (hInstGL) + { + FreeLibrary(hInstGL); + hInstGL = NULL; + } +} + + +/* +================ +VID_UpdateWindowStatus +================ +*/ +void VID_UpdateWindowStatus (void) +{ + + window_rect.left = window_x; + window_rect.top = window_y; + window_rect.right = window_x + window_width; + window_rect.bottom = window_y + window_height; + window_center_x = (window_rect.left + window_rect.right) / 2; + window_center_y = (window_rect.top + window_rect.bottom) / 2; + + IN_UpdateClipCursor (); +} + + +//==================================== + +qboolean VID_AttachGL (rendererstate_t *info) +{ //make sure we can get a valid renderer. + do + { +#ifdef AVAIL_DX7 + if (!Q_strcasecmp(info->glrenderer, "D3D")) + { + extern cvar_t gl_ztrick; + int zbpp = info->bpp > 16 ? 24 : 16; + gl_canstencil = false; + D3DInitialize(); //replacement of GLInitialise, to get the function pointers set up. + if (COM_CheckParm("-zbpp")) + { + zbpp = Q_atoi(com_argv[COM_CheckParm("-zbpp")+1]); + } + d3dSetMode(vid_isfullscreen, info->width, info->height, info->bpp, zbpp); //d3d cheats to get it's dimensions and stuff... One that we can currently live with though. + + gl_ztrickdisabled |= 2; //ztrick does funny things. + Cvar_Set(&gl_ztrick, "0"); + + maindc = GetDC(mainwindow); + + Con_SafePrintf(S_COLOR_GREEN"OpenGL to Direct3D wrapper enabled\n"); //green to make it show. + break; + } +#endif + gl_ztrickdisabled &= ~2; + if (GLInitialise(info->glrenderer)) + { + maindc = GetDC(mainwindow); + bSetupPixelFormat(maindc); + break; + } + + if (!stricmp(info->glrenderer, "opengl32.dll") || !stricmp(info->glrenderer, "opengl32")) //go for windows system dir if we failed with the default. Should help to avoid the 3dfx problem. + { + char systemgl[MAX_OSPATH+1]; + GetSystemDirectory(systemgl, sizeof(systemgl)-1); + strncat(systemgl, "\\", sizeof(systemgl)-1); + strncat(systemgl, info->glrenderer, sizeof(systemgl)-1); + if (GLInitialise(systemgl)) + { + maindc = GetDC(mainwindow); + bSetupPixelFormat(maindc); + break; + } + } + + return false; + } while(1); + + + baseRC = qwglCreateContext( maindc ); + if (!baseRC) + { + Con_SafePrintf(S_COLOR_RED"Could not initialize GL (wglCreateContext failed).\n\nMake sure you in are 65535 color mode, and try running -window.\n"); //green to make it show. + return false; + } + if (!qwglMakeCurrent( maindc, baseRC )) + { + Con_SafePrintf(S_COLOR_RED"wglMakeCurrent failed\n"); //green to make it show. + return false; + } + + GL_Init(getglfunc); + qwglSwapIntervalEXT = getglfunc("wglSwapIntervalEXT"); + if (qwglSwapIntervalEXT && _vid_wait_override.value>=0) + qwglSwapIntervalEXT(_vid_wait_override.value); + _vid_wait_override.modified = false; + qSwapBuffers(maindc); + + return true; +} + +/* +================= +GL_BeginRendering + +================= +*/ +void GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + extern cvar_t gl_clear; + + *x = *y = 0; + *width = WindowRect.right - WindowRect.left; + *height = WindowRect.bottom - WindowRect.top; + +// if (!wglMakeCurrent( maindc, baseRC )) +// Sys_Error ("wglMakeCurrent failed"); + +// glViewport (*x, *y, *width, *height); +} + +void GL_EndRendering (void) +{ + if (!scr_skipupdate || block_drawing) + qSwapBuffers(maindc); + + if (_vid_wait_override.modified && qwglSwapIntervalEXT && _vid_wait_override.value>=0) + { + qwglSwapIntervalEXT(_vid_wait_override.value); + _vid_wait_override.modified = false; + } + +// handle the mouse state when windowed if that's changed + if (modestate == MS_WINDOWED) + { + if (!_windowed_mouse.value) { + if (windowed_mouse) { + IN_DeactivateMouse (); + IN_ShowMouse (); + windowed_mouse = false; + } + } else { + windowed_mouse = true; + if (key_dest == key_game && !mouseactive && ActiveApp) { + IN_ActivateMouse (); + IN_HideMouse (); + } else if (mouseactive && key_dest != key_game) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } + } +} + +void GLVID_SetPalette (unsigned char *palette) +{ + qbyte *pal; + unsigned r,g,b; + unsigned v; + unsigned short i; + unsigned *table; + extern qbyte gammatable[256]; + +// +// 8 8 8 encoding +// + pal = palette; + table = d_8to24rgbtable; + for (i=0 ; i<256 ; i++) + { + r = gammatable[pal[0]]; + g = gammatable[pal[1]]; + b = gammatable[pal[2]]; + pal += 3; + +// v = (255<<24) + (r<<16) + (g<<8) + (b<<0); +// v = (255<<0) + (r<<8) + (g<<16) + (b<<24); + v = (255<<24) + (r<<0) + (g<<8) + (b<<16); + *table++ = v; + } + d_8to24rgbtable[255] &= 0xffffff; // 255 is transparent +} + +BOOL gammaworks; + +void GLVID_ShiftPalette (unsigned char *palette) +{ + extern qbyte ramps[3][256]; + +// VID_SetPalette (palette); + +// gammaworks = SetDeviceGammaRamp (maindc, ramps); +} + + +void VID_SetDefaultMode (void) +{ + IN_DeactivateMouse (); +} + + +void GLVID_Shutdown (void) +{ + VID_UnSetMode(); +} + + +//========================================================================== + + +BOOL bSetupPixelFormat(HDC hDC) +{ + static PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd + 1, // version number + PFD_DRAW_TO_WINDOW // support window + | PFD_SUPPORT_OPENGL // support OpenGL + | PFD_DOUBLEBUFFER , // double buffered + PFD_TYPE_RGBA, // RGBA type + 24, // 24-bit color depth + 0, 0, 0, 0, 0, 0, // color bits ignored + 0, // no alpha buffer + 0, // shift bit ignored + 0, // no accumulation buffer + 0, 0, 0, 0, // accum bits ignored + 32, // 32-bit z-buffer + 8, // stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0 // layer masks ignored + }; + int pixelformat; + + if ((pixelformat = ChoosePixelFormat(hDC, &pfd))) + if (SetPixelFormat(hDC, pixelformat, &pfd)) + { + gl_canstencil = pfd.cStencilBits; + return TRUE; + } + + pfd.cStencilBits = 0; + gl_canstencil = false; + + if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 ) + { + MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK); + return FALSE; + } + + if (SetPixelFormat(hDC, pixelformat, &pfd) == FALSE) + { + MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK); + return FALSE; + } + + return TRUE; +} + +/* +=================================================================== + +MAIN WINDOW + +=================================================================== +*/ + +/* +================ +ClearAllStates +================ +*/ +void ClearAllStates (void) +{ + int i; + +// send an up event for each key, to make sure the server clears them all + for (i=0 ; i<256 ; i++) + { + Key_Event (i, false); + } + + Key_ClearStates (); + IN_ClearStates (); +} + +void GLAppActivate(BOOL fActive, BOOL minimize) +/**************************************************************************** +* +* Function: AppActivate +* Parameters: fActive - True if app is activating +* +* Description: If the application is activating, then swap the system +* into SYSPAL_NOSTATIC mode so that our palettes will display +* correctly. +* +****************************************************************************/ +{ + static BOOL sound_active; + + ActiveApp = fActive; + Minimized = minimize; + +// enable/disable sound on focus gain/loss + if (!ActiveApp && sound_active) + { + S_BlockSound (); + sound_active = false; + } + else if (ActiveApp && !sound_active) + { + S_UnblockSound (); + sound_active = true; + } + + if (fActive) + { + if (modestate == MS_FULLDIB) + { + IN_ActivateMouse (); + IN_HideMouse (); + if (vid_canalttab && vid_wassuspended) { + vid_wassuspended = false; + ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN); + ShowWindow(mainwindow, SW_SHOWNORMAL); + + // Fix for alt-tab bug in NVidia drivers + MoveWindow (mainwindow, 0, 0, gdevmode.dmPelsWidth, gdevmode.dmPelsHeight, false); + } + } + else if ((modestate == MS_WINDOWED) && _windowed_mouse.value && (key_dest == key_game || key_dest == key_menu)) + { + IN_ActivateMouse (); + IN_HideMouse (); + } + } + + if (!fActive) + { + if (modestate == MS_FULLDIB) + { + IN_DeactivateMouse (); + IN_ShowMouse (); + if (vid_canalttab) { + ChangeDisplaySettings (NULL, 0); + vid_wassuspended = true; + } + } + else if ((modestate == MS_WINDOWED) && _windowed_mouse.value) + { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } +} + + +/* main window procedure */ +LONG WINAPI GLMainWndProc ( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + LONG lRet = 1; + int fActive, fMinimized, temp; + extern unsigned int uiWheelMessage; + + if ( uMsg == uiWheelMessage ) + uMsg = WM_MOUSEWHEEL; + + switch (uMsg) + { + case WM_KILLFOCUS: + if (modestate == MS_FULLDIB) + ShowWindow(mainwindow, SW_SHOWMINNOACTIVE); + break; + + case WM_CREATE: + break; + + case WM_MOVE: + window_x = (int) LOWORD(lParam); + window_y = (int) HIWORD(lParam); + VID_UpdateWindowStatus (); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (!vid_initializing) + Key_Event (MapKey(lParam), true); + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + if (!vid_initializing) + Key_Event (MapKey(lParam), false); + break; + + case WM_SYSCHAR: + // keep Alt-Space from happening + break; + + // this is complicated because Win32 seems to pack multiple mouse events into + // one update sometimes, so we always check all states and look for events + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEMOVE: + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + temp = 0; + + if (wParam & MK_LBUTTON) + temp |= 1; + + if (wParam & MK_RBUTTON) + temp |= 2; + + if (wParam & MK_MBUTTON) + temp |= 4; + + if (wParam & MK_XBUTTON1) + temp |= 8; + + if (wParam & MK_XBUTTON2) + temp |= 16; + + if (!vid_initializing) + IN_MouseEvent (temp); + + break; + + // JACK: This is the mouse wheel with the Intellimouse + // Its delta is either positive or neg, and we generate the proper + // Event. + case WM_MOUSEWHEEL: + if (!vid_initializing) + if ((short) HIWORD(wParam) > 0) { + Key_Event(K_MWHEELUP, true); + Key_Event(K_MWHEELUP, false); + } else { + Key_Event(K_MWHEELDOWN, true); + Key_Event(K_MWHEELDOWN, false); + } + break; + + case WM_SIZE: + if (!vid_initializing) + { + WindowRect.right = ((short*)&lParam)[0] - WindowRect.left; + WindowRect.bottom = ((short*)&lParam)[1] - WindowRect.top; + } + break; + + case WM_CLOSE: + if (!vid_initializing) + if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", + MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) + { + Sys_Quit (); + } + + break; + + case WM_ACTIVATE: + fActive = LOWORD(wParam); + fMinimized = (BOOL) HIWORD(wParam); + GLAppActivate(!(fActive == WA_INACTIVE), fMinimized); + if (modestate == MS_FULLDIB) + ShowWindow(mainwindow, SW_SHOWNORMAL); + + // fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + break; + + case WM_DESTROY: + { + if (dibwindow) + DestroyWindow (dibwindow); + } + break; + + case MM_MCINOTIFY: + lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); + break; + + + case WM_MWHOOK: + if (!vid_initializing) + MW_Hook_Message (lParam); + break; + + default: + /* pass all unhandled messages to DefWindowProc */ + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } + + /* return 1 if handled message, 0 if not */ + return lRet; +} + + +qboolean GLVID_Is8bit() { + return is8bit; +} + +#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB + +void VID_Init8bitPalette() +{ + // Check for 8bit Extensions and initialize them. + int i; + char thePalette[256*3]; + char *oldPalette, *newPalette; + + glColorTableEXT = (void *)qwglGetProcAddress("glColorTableEXT"); + if (!glColorTableEXT || strstr(gl_extensions, "GL_EXT_shared_texture_palette") || + COM_CheckParm("-no8bit")) + return; + + Con_SafePrintf("8-bit GL extensions enabled.\n"); + glEnable( GL_SHARED_TEXTURE_PALETTE_EXT ); + oldPalette = (char *) d_8to24rgbtable; //d_8to24table3dfx; + newPalette = thePalette; + for (i=0;i<256;i++) { + *newPalette++ = *oldPalette++; + *newPalette++ = *oldPalette++; + *newPalette++ = *oldPalette++; + oldPalette++; + } + glColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, + (void *) thePalette); + is8bit = TRUE; +} + +static void Check_Gamma (unsigned char *pal, float usegammaval) +{ +// float f, inf; +// unsigned char palette[768]; +// int i; +/* + if (usegammaval) + vid_gamma = usegammaval; + else if ((i = COM_CheckParm("-gamma")) == 0) { + if ((gl_renderer && strstr(gl_renderer, "Voodoo")) || + (gl_vendor && strstr(gl_vendor, "3Dfx"))) + vid_gamma = 1; + else + vid_gamma = 0.7; // default to 0.7 on non-3dfx hardware + } else + vid_gamma = Q_atof(com_argv[i+1]); + + for (i=0 ; i<768 ; i++) + { + f = pow ( (pal[i]+1)/256.0 , vid_gamma ); + inf = f*255 + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + palette[i] = inf; + } + + memcpy (pal, palette, sizeof(palette)); + */ +} + + +void GLVID_DeInit (void) +{ + GLVID_Shutdown(); + + UnregisterClass(WINDOW_CLASS_NAME, global_hInstance); +} +/* +=================== +VID_Init +=================== +*/ +qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) +{ +// qbyte *ptmp; + DEVMODE devmode; + WNDCLASS wc; + + memset(&devmode, 0, sizeof(devmode)); + + /* Register the frame class */ + wc.style = 0; + wc.lpfnWndProc = (WNDPROC)GLMainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = global_hInstance; + wc.hIcon = 0; + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = 0; + wc.lpszClassName = WINDOW_CLASS_NAME; + + if (!RegisterClass (&wc) ) + Sys_Error ("Couldn't register window class"); + + hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON2)); + + vid_initialized = false; + vid_initializing = true; + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + + DestroyWindow (hwnd_dialog); + + Check_Gamma(palette, 0); + VID_SetPalette (palette); + + if (!GLVID_SetMode (info, palette)) + { + VID_UnSetMode(); + return false; + } + + maindc = GetDC(mainwindow); + + // Check for 3DFX Extensions and initialize them. + VID_Init8bitPalette(); + + vid_canalttab = true; + + S_Restart_f(); + + vid_initialized = true; + vid_initializing = false; + + return true; +} diff --git a/engine/gl/gl_warp.c b/engine/gl/gl_warp.c new file mode 100644 index 00000000..10462c86 --- /dev/null +++ b/engine/gl/gl_warp.c @@ -0,0 +1,956 @@ +/* +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_warp.c -- sky and water polygons + +#include "quakedef.h" +#include "glquake.h" + +extern model_t *loadmodel; + +int skytexturenum; + +int solidskytexture; +int alphaskytexture; +float speedscale; // for top sky and bottom sky + +qboolean usingskybox; + +msurface_t *warpface; + +extern cvar_t gl_skyboxname; +extern cvar_t gl_waterripples; +char loadedskybox[256]; + +void R_DrawSkyBox (msurface_t *s); +void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs) +{ + int i, j; + float *v; + + mins[0] = mins[1] = mins[2] = 9999; + maxs[0] = maxs[1] = maxs[2] = -9999; + v = verts; + for (i=0 ; i maxs[j]) + maxs[j] = *v; + } +} + +void SubdividePolygon (int numverts, float *verts, float dividesize) +{ + int i, j, k; + vec3_t mins, maxs; + float m; + float *v; + vec3_t front[64], back[64]; + int f, b; + float dist[64]; + float frac; + glpoly_t *poly; + float s, t; + + if (numverts > 60 || numverts <= 0) + Sys_Error ("numverts = %i", numverts); + + BoundPoly (numverts, verts, mins, maxs); + + for (i=0 ; i<3 ; i++) + { + m = (mins[i] + maxs[i]) * 0.5; + m = dividesize * floor (m/dividesize + 0.5); + if (maxs[i] - m < 8) + continue; + if (m - mins[i] < 8) + continue; + + // cut it + v = verts + i; + for (j=0 ; j= 0) + { + VectorCopy (v, front[f]); + f++; + } + if (dist[j] <= 0) + { + VectorCopy (v, back[b]); + b++; + } + if (dist[j] == 0 || dist[j+1] == 0) + continue; + if ( (dist[j] > 0) != (dist[j+1] > 0) ) + { + // clip point + frac = dist[j] / (dist[j] - dist[j+1]); + for (k=0 ; k<3 ; k++) + front[f][k] = back[b][k] = v[k] + frac*(v[3+k] - v[k]); + f++; + b++; + } + } + + SubdividePolygon (f, front[0], dividesize); + SubdividePolygon (b, back[0], dividesize); + return; + } + + poly = Hunk_AllocName (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float), "subpoly"); + poly->next = warpface->polys; + warpface->polys = poly; + poly->numverts = numverts; + for (i=0 ; iverts[i]); + s = DotProduct (verts, warpface->texinfo->vecs[0]); + t = DotProduct (verts, warpface->texinfo->vecs[1]); + poly->verts[i][3] = s; + poly->verts[i][4] = t; + } +} + +/* +================ +GL_SubdivideSurface + +Breaks a polygon up along axial 64 unit +boundaries so that turbulent and sky warps +can be done reasonably. +================ +*/ +void GL_SubdivideSurface (msurface_t *fa, float dividesize) +{ + vec3_t verts[64]; + int numverts; + int i; + int lindex; + float *vec; + + warpface = fa; + + // + // convert edges back to a normal polygon + // + numverts = 0; + for (i=0 ; inumedges ; i++) + { + lindex = loadmodel->surfedges[fa->firstedge + i]; + + if (lindex > 0) + vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position; + else + vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position; + VectorCopy (vec, verts[numverts]); + numverts++; + + if(numverts >= 64) + { + Con_Printf("Too many verts on surface\n"); + break; + } + } + + SubdividePolygon (numverts, verts[0], dividesize); +} + +//========================================================= + + + +// speed up sin calculations - Ed +float turbsin[] = +{ + #include "gl_warp_sin.h" +}; +#define TURBSCALE (256.0 / (2 * M_PI)) + +/* +============= +EmitWaterPolys + +Does a water warp on the pre-fragmented glpoly_t chain +============= +*/ +void EmitWaterPolys (msurface_t *fa) +{ + glpoly_t *p; + float *v; + int i; + float s, t, os, ot; + +#ifdef WATERLAYERS + extern cvar_t r_waterlayers; + + + if (gl_waterripples.value) + { + float f = 10; + + glEnable(GL_AUTO_NORMAL); + for (p=fa->polys ; p ; p=p->next) + { + glBegin (GL_POLYGON); + for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) + { + os = v[3]; + ot = v[4]; + + s = os + turbsin[(int)((ot*0.125+realtime) * TURBSCALE) & 255]; + s *= (1.0/64); + + t = ot + turbsin[(int)((os*0.125+realtime) * TURBSCALE) & 255]; + t *= (1.0/64); + + glNormal3f(fa->plane->normal[0] + (sin(realtime+v[0]/f+v[1]/f))/4, fa->plane->normal[1] +(sin(realtime-v[1]/f))/4, fa->plane->normal[2] + (sin(realtime+v[2]/f))/4); + glTexCoord2f (s, t); + glVertex3fv (v); + } + glEnd (); + } + glDisable(GL_AUTO_NORMAL); + } + else if (r_waterlayers.value>=1) + { + float a, stm, ttm; + int l; + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); //to ensure. + for (a=r_wateralphaval,l = 0; l < r_waterlayers.value; l++,a=a*4/6) + { + glColor4f(1, 1, 1, a); + stm =cos(l)/10; + ttm =sin(l)/10; + for (p=fa->polys ; p ; p=p->next) + { + glBegin (GL_POLYGON); + for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) + { + os = v[3]/(l*0.5+0.2); + ot = v[4]/(l*0.5+0.2); + + s = os + turbsin[(int)((ot*0.125+cl.time+l*8) * TURBSCALE) & 255];//*r_watersurfacewarp.value; + s *= (1.0/64); + + t = ot + turbsin[(int)((os*0.125+cl.time+l*8) * TURBSCALE) & 255];//*r_watersurfacewarp.value; + t *= (1.0/64); + + glTexCoord2f (s+cl.time*stm, t+cl.time*ttm); + glVertex3fv (v); + } + glEnd (); + } + } + + } + else + { +#endif + for (p=fa->polys ; p ; p=p->next) + { + glBegin (GL_POLYGON); + for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) + { + os = v[3]; + ot = v[4]; + + s = os + turbsin[(int)((ot*0.125+realtime) * TURBSCALE) & 255]; + s *= (1.0/64); + + t = ot + turbsin[(int)((os*0.125+realtime) * TURBSCALE) & 255]; + t *= (1.0/64); + + glTexCoord2f (s, t); + glVertex3fv (v); + } + glEnd (); + } +#ifdef WATERLAYERS + } +#endif +} + + + + +/* +============= +EmitSkyPolys +============= +*/ +void EmitSkyPolys (msurface_t *fa) +{ + glpoly_t *p; + float *v; + int i; + float s, t; + vec3_t dir; + float length; + + for (p=fa->polys ; p ; p=p->next) + { + glBegin (GL_POLYGON); + for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) + { + VectorSubtract (v, r_origin, dir); + dir[2] *= 3; // flatten the sphere + + length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]; + length = sqrt (length); + length = 6*63/length; + + dir[0] *= length; + dir[1] *= length; + + s = (speedscale + dir[0]) * (1.0/128); + t = (speedscale + dir[1]) * (1.0/128); + + glTexCoord2f (s, t); + glVertex3fv (v); + } + glEnd (); + } +} + +/* +=============== +EmitBothSkyLayers + +Does a sky warp on the pre-fragmented glpoly_t chain +This will be called for brushmodels, the world +will have them chained together. +=============== +*/ +void EmitBothSkyLayers (msurface_t *fa) +{ + GL_DisableMultitexture(); + + GL_Bind (solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127 ; + + EmitSkyPolys (fa); + + glEnable (GL_BLEND); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127 ; + + EmitSkyPolys (fa); + + glDisable (GL_BLEND); +} + +/* +================= +R_DrawSkyChain +================= +*/ +void R_DrawSkyBoxChain (msurface_t *s); +void R_DrawSkyChain (msurface_t *s) +{ + msurface_t *fa; + + if (usingskybox) + { + R_DrawSkyBoxChain(s); + return; + } + + GL_DisableMultitexture(); + + // used when gl_texsort is on + GL_Bind(solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127 ; + + for (fa=s ; fa ; fa=fa->texturechain) + EmitSkyPolys (fa); + + glEnable (GL_BLEND); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127 ; + + for (fa=s ; fa ; fa=fa->texturechain) + EmitSkyPolys (fa); + + glDisable (GL_BLEND); +} + +/* +================================================================= + + Quake 2 environment sky + +================================================================= +*/ + + +/* +================== +R_LoadSkys +================== +*/ +static char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; +int skyboxtex[6]; +void R_LoadSkys (void) +{ + int i; + char name[64]; + + for (i=0 ; i<6 ; i++) + { + sprintf (name, "env/%s%s.tga", gl_skyboxname.string, suf[i]); + + skyboxtex[i] = Mod_LoadHiResTexture(name, false, false); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + Q_strncpyz(loadedskybox, gl_skyboxname.string, sizeof(loadedskybox)); + gl_skyboxname.modified = false; +} + + +qboolean GLR_CheckSky() +{ + return true; +} +void GLR_SetSky(char *name, float rotate, vec3_t axis) +{ + if (*name) + Cvar_Set(&gl_skyboxname, name); +} + +vec3_t skyclip[6] = { + {1,1,0}, + {1,-1,0}, + {0,-1,1}, + {0,1,1}, + {1,0,1}, + {-1,0,1} +}; +int c_sky; + +// 1 = s, 2 = t, 3 = 2048 +int st_to_vec[6][3] = +{ + {3,-1,2}, + {-3,1,2}, + + {1,3,2}, + {-1,-3,2}, + + {-2,-1,3}, // 0 degrees yaw, look straight up + {2,-1,-3} // look straight down + +// {-1,2,3}, +// {1,2,-3} +}; + +// s = [0]/[2], t = [1]/[2] +int vec_to_st[6][3] = +{ + {-2,3,1}, + {2,3,-1}, + + {1,3,2}, + {-1,3,-2}, + + {-2,-1,3}, + {-2,1,-3} + +// {-1,2,3}, +// {1,2,-3} +}; + +float skymins[2][6], skymaxs[2][6]; + +void DrawSkyPolygon (int nump, vec3_t vecs) +{ + int i,j; + vec3_t v, av; + float s, t, dv; + int axis; + float *vp; + + c_sky++; +#if 0 +glBegin (GL_POLYGON); +for (i=0 ; i av[1] && av[0] > av[2]) + { + if (v[0] < 0) + axis = 1; + else + axis = 0; + } + else if (av[1] > av[2] && av[1] > av[0]) + { + if (v[1] < 0) + axis = 3; + else + axis = 2; + } + else + { + if (v[2] < 0) + axis = 5; + else + axis = 4; + } + + // project new texture coords + for (i=0 ; i 0) + dv = vecs[j - 1]; + else + dv = -vecs[-j - 1]; + + if (dv < 0.001) + continue; // don't divide by zero + + j = vec_to_st[axis][0]; + if (j < 0) + s = -vecs[-j -1] / dv; + else + s = vecs[j-1] / dv; + j = vec_to_st[axis][1]; + if (j < 0) + t = -vecs[-j -1] / dv; + else + t = vecs[j-1] / dv; + + if (s < skymins[0][axis]) + skymins[0][axis] = s; + if (t < skymins[1][axis]) + skymins[1][axis] = t; + if (s > skymaxs[0][axis]) + skymaxs[0][axis] = s; + if (t > skymaxs[1][axis]) + skymaxs[1][axis] = t; + } +} + +#define MAX_CLIP_VERTS 64 +void ClipSkyPolygon (int nump, vec3_t vecs, int stage) +{ + float *norm; + float *v; + qboolean front, back; + float d, e; + float dists[MAX_CLIP_VERTS]; + int sides[MAX_CLIP_VERTS]; + vec3_t newv[2][MAX_CLIP_VERTS]; + int newc[2]; + int i, j; + + if (nump > MAX_CLIP_VERTS-2) + Sys_Error ("ClipSkyPolygon: MAX_CLIP_VERTS"); + if (stage == 6) + { // fully clipped, so draw it + DrawSkyPolygon (nump, vecs); + return; + } + + front = back = false; + norm = skyclip[stage]; + for (i=0, v = vecs ; i ON_EPSILON) + { + front = true; + sides[i] = SIDE_FRONT; + } + else if (d < -ON_EPSILON) + { + back = true; + sides[i] = SIDE_BACK; + } + else + sides[i] = SIDE_ON; + dists[i] = d; + } + + if (!front || !back) + { // not clipped + ClipSkyPolygon (nump, vecs, stage+1); + return; + } + + // clip it + sides[i] = sides[0]; + dists[i] = dists[0]; + VectorCopy (vecs, (vecs+(i*3)) ); + newc[0] = newc[1] = 0; + + for (i=0, v = vecs ; itexturechain) + { + for (p=fa->polys ; p ; p=p->next) + { + for (i=0 ; inumverts ; i++) + { + VectorSubtract (p->verts[i], r_origin, verts[i]); + } + ClipSkyPolygon (p->numverts, verts[0], 0); + } + } + + R_DrawSkyBox (s); + + glColorMask(0, 0, 0, 0); + for (fa=s ; fa ; fa=fa->texturechain) + { + for (p=fa->polys ; p ; p=p->next) + { + glBegin(GL_POLYGON); + for (i = 0; i < p->numverts; i++) + glVertex3fv(p->verts[i]); + glEnd(); + } + } + glColorMask(1, 1, 1, 1); +} + +void R_AddSkySurface (msurface_t *fa) +{ + int i; + vec3_t verts[MAX_CLIP_VERTS]; + glpoly_t *p; + + // calculate vertex values for sky box + for (p=fa->polys ; p ; p=p->next) + { + for (i=0 ; inumverts ; i++) + { + VectorSubtract (p->verts[i], r_origin, verts[i]); + } + ClipSkyPolygon (p->numverts, verts[0], 0); + } +} + + +/* +============== +R_ClearSkyBox +============== +*/ +void R_ClearSkyBox (void) +{ + int i; + + if (!cl.worldmodel) //allow skyboxes on non quake1 maps. (expect them even) + { + usingskybox = false; + return; + } + if (gl_skyboxname.modified) + R_LoadSkys(); + + if (!skyboxtex[0] || !skyboxtex[1] || !skyboxtex[2] || !skyboxtex[3] || !skyboxtex[4] || !skyboxtex[5]) + { + usingskybox = false; + return; + } + + usingskybox = true; + + for (i=0 ; i<6 ; i++) + { + skymins[0][i] = skymins[1][i] = 9999; + skymaxs[0][i] = skymaxs[1][i] = -9999; + } +} + + +void MakeSkyVec (float s, float t, int axis) +{ + vec3_t v, b; + int j, k; + + b[0] = s*2300; + b[1] = t*2300; + b[2] = 2300; + + for (j=0 ; j<3 ; j++) + { + k = st_to_vec[axis][j]; + if (k < 0) + v[j] = -b[-k - 1]; + else + v[j] = b[k - 1]; + v[j] += r_origin[j]; + } + + // avoid bilerp seam + s = (s+1)*0.5; + t = (t+1)*0.5; + + if (s < 1.0/512) + s = 1.0/512; + else if (s > 511.0/512) + s = 511.0/512; + if (t < 1.0/512) + t = 1.0/512; + else if (t > 511.0/512) + t = 511.0/512; + + t = 1.0 - t; + glTexCoord2f (s, t); + glVertex3fv (v); +} + +/* +============== +R_DrawSkyBox +============== +*/ +int skytexorder[6] = {0,2,1,3,4,5}; +void R_DrawSkyBox (msurface_t *s) +{ + msurface_t *fa; + glpoly_t *poly; + int i; + + if (!usingskybox) + return; + +#if 0 +glEnable (GL_BLEND); +glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +glColor4f (1,1,1,0.5); +glDisable (GL_DEPTH_TEST); +#endif + for (i=0 ; i<6 ; i++) + { + if (skymins[0][i] >= skymaxs[0][i] + || skymins[1][i] >= skymaxs[1][i]) + continue; + + GL_Bind (skyboxtex[skytexorder[i]]); +#if 0 +skymins[0][i] = -1; +skymins[1][i] = -1; +skymaxs[0][i] = 1; +skymaxs[1][i] = 1; +#endif + glBegin (GL_QUADS); + MakeSkyVec (skymins[0][i], skymins[1][i], i); + MakeSkyVec (skymins[0][i], skymaxs[1][i], i); + MakeSkyVec (skymaxs[0][i], skymaxs[1][i], i); + MakeSkyVec (skymaxs[0][i], skymins[1][i], i); + glEnd (); + } +#if 0 +glDisable (GL_BLEND); +glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +glColor4f (1,1,1,0.5); +glEnable (GL_DEPTH_TEST); +#endif + + if (!cls.allow_skyboxes && s) //allow a little extra fps. + { + glColorMask(0, 0, 0, 0); //depth only. + for (fa = s; fa; fa = fa->texturechain) + { + for (poly = fa->polys; poly; poly = poly->next) + { + glBegin (GL_POLYGON); + for (i = 0; i < poly->numverts; i++) + glVertex3fv (&poly->verts[0][0]+i*VERTEXSIZE); + glEnd (); + } + } + glColorMask(1, 1, 1, 1); + } +} + + + +//=============================================================== + +/* +============= +R_InitSky + +A sky texture is 256*128, with the right side being a masked overlay +============== +*/ +void GLR_InitSky (texture_t *mt) +{ + int i, j, p; + qbyte *src; + unsigned trans[128*128]; + unsigned transpix; + int r, g, b; + unsigned *rgba; + char name[MAX_QPATH]; + + src = (qbyte *)mt + mt->offsets[0]; + + // make an average value for the back to avoid + // a fringe on the top level + + r = g = b = 0; + for (i=0 ; i<128 ; i++) + for (j=0 ; j<128 ; j++) + { + p = src[i*256 + j + 128]; + rgba = &d_8to24rgbtable[p]; + trans[(i*128) + j] = *rgba; + r += ((qbyte *)rgba)[0]; + g += ((qbyte *)rgba)[1]; + b += ((qbyte *)rgba)[2]; + } + + ((qbyte *)&transpix)[0] = r/(128*128); + ((qbyte *)&transpix)[1] = g/(128*128); + ((qbyte *)&transpix)[2] = b/(128*128); + ((qbyte *)&transpix)[3] = 0; + + sprintf(name, "%s_solid", mt->name); + strlwr(name); + solidskytexture = Mod_LoadReplacementTexture(name, true, false); + if (!solidskytexture) + solidskytexture = GL_LoadTexture32(name, 128, 128, trans, true, false); +/* + if (!solidskytexture) + solidskytexture = texture_extension_number++; + + GL_Bind (solidskytexture ); + glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 128, 128, 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); +*/ + + for (i=0 ; i<128 ; i++) + for (j=0 ; j<128 ; j++) + { + p = src[i*256 + j]; + if (p == 0) + trans[(i*128) + j] = transpix; + else + trans[(i*128) + j] = d_8to24rgbtable[p]; + } + + sprintf(name, "%s_trans", mt->name); + strlwr(name); + alphaskytexture = Mod_LoadReplacementTexture(name, true, true); + if (!alphaskytexture) + alphaskytexture = GL_LoadTexture32(name, 128, 128, trans, true, true); +/* + if (!alphaskytexture) + alphaskytexture = texture_extension_number++; + GL_Bind(alphaskytexture); + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 128, 128, 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); +*/ +} diff --git a/engine/gl/gl_warp_sin.h b/engine/gl/gl_warp_sin.h new file mode 100644 index 00000000..22976a73 --- /dev/null +++ b/engine/gl/gl_warp_sin.h @@ -0,0 +1,51 @@ +/* +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. + +*/ + 0, 0.19633, 0.392541, 0.588517, 0.784137, 0.979285, 1.17384, 1.3677, + 1.56072, 1.75281, 1.94384, 2.1337, 2.32228, 2.50945, 2.69512, 2.87916, + 3.06147, 3.24193, 3.42044, 3.59689, 3.77117, 3.94319, 4.11282, 4.27998, + 4.44456, 4.60647, 4.76559, 4.92185, 5.07515, 5.22538, 5.37247, 5.51632, + 5.65685, 5.79398, 5.92761, 6.05767, 6.18408, 6.30677, 6.42566, 6.54068, + 6.65176, 6.75883, 6.86183, 6.9607, 7.05537, 7.14579, 7.23191, 7.31368, + 7.39104, 7.46394, 7.53235, 7.59623, 7.65552, 7.71021, 7.76025, 7.80562, + 7.84628, 7.88222, 7.91341, 7.93984, 7.96148, 7.97832, 7.99036, 7.99759, + 8, 7.99759, 7.99036, 7.97832, 7.96148, 7.93984, 7.91341, 7.88222, + 7.84628, 7.80562, 7.76025, 7.71021, 7.65552, 7.59623, 7.53235, 7.46394, + 7.39104, 7.31368, 7.23191, 7.14579, 7.05537, 6.9607, 6.86183, 6.75883, + 6.65176, 6.54068, 6.42566, 6.30677, 6.18408, 6.05767, 5.92761, 5.79398, + 5.65685, 5.51632, 5.37247, 5.22538, 5.07515, 4.92185, 4.76559, 4.60647, + 4.44456, 4.27998, 4.11282, 3.94319, 3.77117, 3.59689, 3.42044, 3.24193, + 3.06147, 2.87916, 2.69512, 2.50945, 2.32228, 2.1337, 1.94384, 1.75281, + 1.56072, 1.3677, 1.17384, 0.979285, 0.784137, 0.588517, 0.392541, 0.19633, + 9.79717e-16, -0.19633, -0.392541, -0.588517, -0.784137, -0.979285, -1.17384, -1.3677, + -1.56072, -1.75281, -1.94384, -2.1337, -2.32228, -2.50945, -2.69512, -2.87916, + -3.06147, -3.24193, -3.42044, -3.59689, -3.77117, -3.94319, -4.11282, -4.27998, + -4.44456, -4.60647, -4.76559, -4.92185, -5.07515, -5.22538, -5.37247, -5.51632, + -5.65685, -5.79398, -5.92761, -6.05767, -6.18408, -6.30677, -6.42566, -6.54068, + -6.65176, -6.75883, -6.86183, -6.9607, -7.05537, -7.14579, -7.23191, -7.31368, + -7.39104, -7.46394, -7.53235, -7.59623, -7.65552, -7.71021, -7.76025, -7.80562, + -7.84628, -7.88222, -7.91341, -7.93984, -7.96148, -7.97832, -7.99036, -7.99759, + -8, -7.99759, -7.99036, -7.97832, -7.96148, -7.93984, -7.91341, -7.88222, + -7.84628, -7.80562, -7.76025, -7.71021, -7.65552, -7.59623, -7.53235, -7.46394, + -7.39104, -7.31368, -7.23191, -7.14579, -7.05537, -6.9607, -6.86183, -6.75883, + -6.65176, -6.54068, -6.42566, -6.30677, -6.18408, -6.05767, -5.92761, -5.79398, + -5.65685, -5.51632, -5.37247, -5.22538, -5.07515, -4.92185, -4.76559, -4.60647, + -4.44456, -4.27998, -4.11282, -3.94319, -3.77117, -3.59689, -3.42044, -3.24193, + -3.06147, -2.87916, -2.69512, -2.50945, -2.32228, -2.1337, -1.94384, -1.75281, + -1.56072, -1.3677, -1.17384, -0.979285, -0.784137, -0.588517, -0.392541, -0.19633, diff --git a/engine/gl/glmod_doom.c b/engine/gl/glmod_doom.c new file mode 100644 index 00000000..27859dee --- /dev/null +++ b/engine/gl/glmod_doom.c @@ -0,0 +1,1993 @@ +#include "quakedef.h" +#include "glquake.h" + +#ifdef __linux__ +void strlwr(char *s) +{ + while(s) + { + if (*s >= 'A' && *s <= 'Z') + *s -= 'A' + 'a'; + s++; + } +} +#endif + +#ifdef DOOMWADS + +vec_t VectorNormalize2 (vec3_t v, vec3_t out); +int SignbitsForPlane (mplane_t *out); +int PlaneTypeForNormal ( vec3_t normal ); + +#undef strncpy + +//coded from file specifications provided by: +//Matthew S Fell (msfell@aol.com) +//Unofficial Doom Specs + +void Doom_SetHullFuncs(hull_t *hull); +void Doom_SetModelFunc(model_t *mod); + +//expand to Quake style ent lump +typedef struct { + short xpos; + short ypos; + short angle; + unsigned short type; + unsigned short flags; +} dthing_t; +//skill/dm is appears in rather than quake's excuded in. +#define THING_EASY 1 +#define THING_MEDIUM 2 +#define THING_HARD 4 +#define THING_DEAF 8 +#define THING_DEATHMATCH 16 +//other bits are ignored + +int Doom_SectorNearPoint(vec3_t p); + +//assumptions: +//1. That there is a node, and thus two ssectors. +//2. That the user doesn't want textures... +//3. That all segs ssectors for a single sector are all the same. +//4. That ALL sectors are fully enclosed, and not made of two areas. +//5. That no sectors are inside out. + +enum { + THING_PLAYER = 1, + THING_PLAYER2 = 2, + THING_PLAYER3 = 3, + THING_PLAYER4 = 4, + THING_DMSPAWN = 11, + +//we need to balance weapons according to ammo types. + THING_WCHAINSAW = 2005, //-> quad + THING_WSHOTGUN1 = 2001, //-> ng + THING_WSHOTGUN2 = 82, //-> sng + THING_WCHAINGUN = 2002, //-> ssg + THING_WROCKETL = 2003, //-> lightning + THING_WPLASMA = 2004, //-> grenade + THING_WBFG = 2006 //-> rocket + + +} THING_TYPES; + +typedef struct { + short xpos; + short ypos; +} ddoomvertex_t; + +typedef struct { + float xpos; + float ypos; +} mdoomvertex_t; + +typedef struct { + unsigned short vert[2]; + unsigned short flags; + short types; + short tag; + unsigned short sidedef[2]; //(0xffff is none for sidedef[1]) +} dlinedef_t; +#define LINEDEF_IMPASSABLE 1 +#define LINEDEF_BLOCKMONSTERS 2 +#define LINEDEF_TWOSIDED 4 +#define LINEDEF_UPPERUNPEGGED 8 +#define LINEDEF_LOWERUNPEGGED 16 +#define LINEDEF_SECRET 32 //seen as singlesided on automap, does nothing else. +#define LINEDEF_BLOCKSOUND 64 +#define LINEDEF_NOTONMAP 128 //doesn't appear on automap. +#define LINEDEF_STARTONMAP 256 +//others are ignored. + +typedef struct { + short texx; + short texy; + char uppertex[8]; + char lowertex[8]; + char middletex[8]; + unsigned short sector; +} dsidedef_t; +typedef struct { + float texx; + float texy; + int uppertex; + int lowertex; + int middletex; + unsigned short sector; +} msidedef_t; + +typedef struct { //figure out which linedef to use and throw the rest away. + unsigned short vert[2]; + short angle; + unsigned short linedef; + short direction; + short offset; +} dseg_t; + +typedef struct { + unsigned short vert[2]; + unsigned short linedef; + short direction; + unsigned short Partner; //the one on the other side of the owner's linedef +} dgl_seg1_t; + +typedef struct { + unsigned int vert[2]; + unsigned short linedef; + short direction; + unsigned int Partner; //the one on the other side of the owner's linedef +} dgl_seg3_t; + +typedef struct { + unsigned short segcount; + unsigned short first; +} dssector_t; + +typedef struct { + short x; + short y; + short dx; + short dy; + short y1upper; + short y1lower; + short x1lower; + short x1upper; + short y2upper; + short y2lower; + short x2lower; + short x2upper; + unsigned short node1; + unsigned short node2; +} ddoomnode_t; +#define NODE_IS_SSECTOR 0x8000 + +typedef struct { + short floorheight; + short ceilingheight; + char floortexture[8]; + char ceilingtexture[8]; + short lightlevel; + short specialtype; + short tag; +} dsector_t; + +typedef struct { + int visframe; + int floortex; + int ceilingtex; + + short floorheight; + short ceilingheight; + + qbyte lightlev; + qbyte pad; + int numflattris; + short tag; + short specialtype; + + unsigned short *flats; +} msector_t; + + +typedef struct { + short xorg; + short yorg; + short columns; + short rows; +} blockmapheader_t; + + + + +static ddoomnode_t *nodel; +static dssector_t *ssectorsl; +static dthing_t *thingsl; +static mdoomvertex_t *vertexesl; +static dgl_seg3_t *segsl; +static dlinedef_t *linedefsl; +static msidedef_t *sidedefsm; +static msector_t *sectorm; +static plane_t *nodeplanes; +static plane_t *lineplanes; +static blockmapheader_t *blockmapl; +static unsigned short *blockmapofs; +static unsigned int nodec; +static unsigned int sectorc; +static unsigned int segsc; +static unsigned int ssectorsc; +static unsigned int thingsc; +static unsigned int linedefsc; +static unsigned int sidedefsc; +static unsigned int vertexesc; +static unsigned int vertexsglbase; +extern model_t *loadmodel; +extern char loadname[]; + + +#ifdef RGLQUAKE + +#ifdef _MSC_VER +//#pragma comment (lib, "../../../glbsp/glbsp-2.05/plugin/libglbsp.a") +#endif + + + + +qbyte doompalette[768]; +static qboolean paletteloaded; + +int Doom_LoadFlat(char *name) +{ + char *file; + char texname[64]; + int tex; + + if (!paletteloaded) + { + paletteloaded = true; + file = COM_LoadMallocFile("wad/playpal"); + if (file) + { + memcpy(doompalette, file, 768); + Z_Free(file); + } + else + { + for (tex = 0; tex < 256; tex++) + { + doompalette[tex*3+0] = tex; + doompalette[tex*3+1] = tex; + doompalette[tex*3+2] = tex; + } + } + } + + sprintf(texname, "flat-%-0.8s", name); + strlwr(texname); + tex = Mod_LoadReplacementTexture(texname, true, false); + if (tex) + return tex; + + sprintf(texname, "flats/%-0.8s", name); + strlwr(texname); + file = COM_LoadMallocFile(texname); + if (file) + { + tex = GL_LoadTexture8Pal24(texname, 64, 64, file, doompalette, true, false); + Z_Free(file); + } + else + { + tex = 0; + Con_Printf("Flat %-0.8s not found\n", name); + } + + return tex; +} + +static void GLR_DrawSSector(unsigned int ssec) +{ + short v0, v1; + int sd; + dlinedef_t *ld; + int seg; + msector_t *sec, *sec2; + + for (seg = ssectorsl[ssec].first + ssectorsl[ssec].segcount-1; seg >= ssectorsl[ssec].first; seg--) + if (segsl[seg].linedef != 0xffff) + break; + sec = sectorm + sidedefsm[linedefsl[segsl[seg].linedef].sidedef[segsl[seg].direction]].sector; + + glColor4ub(sec->lightlev, sec->lightlev, sec->lightlev, 255); + sec->visframe = r_visframecount; + for (seg = ssectorsl[ssec].first + ssectorsl[ssec].segcount-1; seg >= ssectorsl[ssec].first; seg--) + { + if (segsl[seg].linedef == 0xffff) + continue; + + v0 = segsl[seg].vert[0]; + v1 = segsl[seg].vert[1]; + if (v0==v1) + continue; + ld = linedefsl + segsl[seg].linedef; + sd = ld->sidedef[segsl[seg].direction]; + + if (ld->sidedef[1] != 0xffff) //we can see through this linedef + { + sec2 = sectorm + sidedefsm[ld->sidedef[1-segsl[seg].direction]].sector; + + if (sec->floorheight < sec2->floorheight) + { + GL_Bind(sidedefsm[sd].lowertex); + glBegin(GL_QUADS); + glTexCoord2f(0, 1); + glVertex3f(vertexesl[v0].xpos, vertexesl[v0].ypos, sec->floorheight); + glTexCoord2f(0, 0); + glVertex3f(vertexesl[v0].xpos, vertexesl[v0].ypos, sec2->floorheight); + glTexCoord2f(1, 0); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sec2->floorheight); + glTexCoord2f(1, 1); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sec->floorheight); + glEnd(); + c_brush_polys++; + } + + if (sec->ceilingheight > sec2->ceilingheight) + { + // if (sec2->ceilingheight != sec2->floorheight) + // sec2->floorheight = sec2->ceilingheight-32; + GL_Bind(sidedefsm[sd].uppertex); + glBegin(GL_QUADS); + glTexCoord2f(0, 1); + glVertex3f(vertexesl[v0].xpos, vertexesl[v0].ypos, sec2->ceilingheight); + glTexCoord2f(0, 0); + glVertex3f(vertexesl[v0].xpos, vertexesl[v0].ypos, sec->ceilingheight); + glTexCoord2f(1, 0); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sec->ceilingheight); + glTexCoord2f(1, 1); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sec2->ceilingheight); + glEnd(); + c_brush_polys++; + } + + if (sidedefsm[sd].middletex) + { + GL_Bind(sidedefsm[sd].middletex); + glBegin(GL_QUADS); + if (sec2->ceilingheight < sec->ceilingheight) + { + glTexCoord2f(0, 0); + glVertex3f(vertexesl[v0].xpos, vertexesl[v0].ypos, sec2->ceilingheight); + glTexCoord2f(1, 0); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sec2->ceilingheight); + } + else + { + glTexCoord2f(0, 0); + glVertex3f(vertexesl[v0].xpos, vertexesl[v0].ypos, sec->ceilingheight); + glTexCoord2f(1, 0); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sec->ceilingheight); + } + if (sec2->floorheight > sec->floorheight) + { + glTexCoord2f(1, 1); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sec2->floorheight); + glTexCoord2f(0, 1); + glVertex3f(vertexesl[v0].xpos, vertexesl[v0].ypos, sec2->floorheight); + } + else + { + glTexCoord2f(1, 1); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sec->floorheight); + glTexCoord2f(0, 1); + glVertex3f(vertexesl[v0].xpos, vertexesl[v0].ypos, sec->floorheight); + } + glEnd(); + c_brush_polys++; + } + } + else + { //solid wall, draw full wall. + GL_Bind(sidedefsm[sd].middletex); + glBegin(GL_QUADS); + /* if (ld->flags & LINEDEF_LOWERUNPEGGED) + { + glTexCoord2f(0, 0); + glVertex3f(vertexesl[v0].xpos, vertexesl[v0].ypos, sec->floorheight); + glTexCoord2f(0, (sec->ceilingheight-sec->floorheight)/64.0f); + glVertex3f(vertexesl[v0].xpos, vertexesl[v0].ypos, sec->ceilingheight); + glTexCoord2f(1, (sec->ceilingheight-sec->floorheight)/64.0f); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sec->ceilingheight); + glTexCoord2f(1, 0); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sec->floorheight); + } + else*/ + { + glTexCoord2f(0, (sec->ceilingheight)/128.0f); + glVertex3f(vertexesl[v0].xpos, vertexesl[v0].ypos, sec->floorheight); + glTexCoord2f(0, (sec->floorheight)/128.0f); + glVertex3f(vertexesl[v0].xpos, vertexesl[v0].ypos, sec->ceilingheight); + glTexCoord2f(1, (sec->floorheight)/128.0f); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sec->ceilingheight); + glTexCoord2f(1, (sec->ceilingheight)/128.0f); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sec->floorheight); + } + glEnd(); + c_brush_polys++; + } + } +} + +mplane_t frustum2d[2]; +static int Box2DOnPlaneSide (short emins[2], short emaxs[2], mplane_t *p) +{ + float dist1, dist2; + int sides; + +// general case + switch (p->signbits) + { + case 0: +dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1]; +dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1]; + break; + case 1: +dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1]; +dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1]; + break; + case 2: +dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1]; +dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1]; + break; + case 3: +dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1]; +dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1]; + break; + case 4: +dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1]; +dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1]; + break; + case 5: +dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1]; +dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1]; + break; + case 6: +dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1]; +dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1]; + break; + case 7: +dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1]; +dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1]; + break; + default: + dist1 = dist2 = 0; // shut up compiler +// BOPS_Error (); + break; + } + + sides = 0; + if (dist1 >= p->dist) + sides = 1; + if (dist2 < p->dist) + sides |= 2; + +#ifdef PARANOID +if (sides == 0) + Sys_Error ("Box2DOnPlaneSide: sides==0"); +#endif + + return sides; +} +static qboolean R_Cull2DBox (short mins_x, short mins_y, short maxs_x, short maxs_y) +{ + short mins[2], maxs[2]; + int i; +//return false; + mins[0] = mins_x; + mins[1] = mins_y; + maxs[0] = maxs_x; + maxs[1] = maxs_y; + + for (i=0 ; i<2 ; i++) + if (Box2DOnPlaneSide (mins, maxs, &frustum2d[i]) == 2) + return true; + return false; +} + +void R_Set2DFrustum (void) +{ + int i; + vec3_t vpn, vright, vup, viewang; + + if ((int)r_novis.value & 4) + return; + + viewang[0] = 0; + viewang[1] = r_refdef.viewangles[1]; + viewang[2] = 0; + AngleVectors (viewang, vpn, vright, vup); + + if (r_refdef.fov_x == 90) + { + // front side is visible + + VectorAdd (vpn, vright, frustum2d[0].normal); + VectorSubtract (vpn, vright, frustum2d[1].normal); + } + else + { + + // rotate VPN right by FOV_X/2 degrees + RotatePointAroundVector( frustum2d[0].normal, vup, vpn, -(90-r_refdef.fov_x / 2 ) ); + // rotate VPN left by FOV_X/2 degrees + RotatePointAroundVector( frustum2d[1].normal, vup, vpn, 90-r_refdef.fov_x / 2 ); + } + + for (i=0 ; i<2 ; i++) + { + frustum2d[i].type = PLANE_ANYZ; + frustum2d[i].dist = DotProduct (r_origin, frustum2d[i].normal); + frustum2d[i].signbits = SignbitsForPlane (&frustum2d[i]); + } +} + + +static void GLR_RecursiveDoomNode(unsigned int node) +{ + if (node & NODE_IS_SSECTOR) + { + GLR_DrawSSector(node & ~NODE_IS_SSECTOR); + + return; + } + + if (!R_Cull2DBox(nodel[node].x1lower, nodel[node].y1lower, nodel[node].x1upper, nodel[node].y1upper)||1) + GLR_RecursiveDoomNode(nodel[node].node1); + if (!R_Cull2DBox(nodel[node].x2lower, nodel[node].y2lower, nodel[node].x2upper, nodel[node].y2upper)||1) + GLR_RecursiveDoomNode(nodel[node].node2); +} + +qboolean GLR_DoomWorld(void) +{ + int i, v; + int v1; + + if (cl.worldmodel->fromgame != fg_doom) + return false; + + if (!nodel || !nodec) + return true; //err... buggy + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDisable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glEnable(GL_CULL_FACE); + GL_DisableMultitexture(); + r_visframecount++; + GLR_RecursiveDoomNode(nodec-1); + + if (developer.value) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + for (i = 0; i < sectorc; i++) + { + if (sectorm[i].visframe == r_visframecount) + { + glColor4ub(sectorm[i].lightlev, sectorm[i].lightlev, sectorm[i].lightlev, 255); + GL_Bind(sectorm[i].floortex); + glBegin(GL_TRIANGLES); + for (v = 0; v < sectorm[i].numflattris*3; v++) + { + v1 = sectorm[i].flats[v]; + glTexCoord2f(vertexesl[v1].xpos/64.0f, vertexesl[v1].ypos/64.0f); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sectorm[i].floorheight); + } + glEnd(); + + GL_Bind(sectorm[i].ceilingtex); + glBegin(GL_TRIANGLES); + for (v = sectorm[i].numflattris*3-1; v >= 0; v--) + { + v1 = sectorm[i].flats[v]; + glTexCoord2f(vertexesl[v1].xpos/64.0f, vertexesl[v1].ypos/64.0f); + glVertex3f(vertexesl[v1].xpos, vertexesl[v1].ypos, sectorm[i].ceilingheight); + } + glEnd(); + + c_brush_polys += sectorm[i].numflattris; + } + } + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + return true; +} +#endif + +//find the first ssector, go through it's list/ +//grab the lines into multiple arrays. +//make sure all arrays are looped fully. If not, error out. +//if we have two arrays, we have a hole in the middle. +//with multiple arrays, from the second onwards +// grab two adjacent verts and find the nearest point in any other array, that is also on the positive side. +// One of the two should be an extreeme, and the external point should be in the direction that the angle points at. +// none found = error +// create a triangle from these points, fix array links. +// move on to next spare array. +//we now have a concave polygon with no holes. +//pick a point, follow along the walls making a triangle fan, until an angle of > 180, throw out fan, rebuild arrays. +//at new point, start a new fan. Be prepared to not be able to generate one. + +#ifdef RGLQUAKE + +#define MAX_REGIONS 256 +#define MAX_POLYVERTS (MAX_FLATTRIS*3) +#define MAX_FLATTRIS 1024 + +//buffer to hold tris +static unsigned short indexes[MAX_POLYVERTS]; +static unsigned int numindexes; + +typedef struct { + int vertex[MAX_POLYVERTS]; + unsigned int numverts; + float angle; +} conectedregion_t; +static conectedregion_t polyregions[MAX_REGIONS]; //we need to be able to join them as we go. +static unsigned int regions; + +//throw out duplicates. +static void Triangulate_AddLine(int v1, int v2) //order makes a difference +{ + int r, v; + int beginingof = -1; + int endof = -1; + int freer = -1; + + for (r = 0; r < regions; r++) + { + if (!polyregions[r].numverts) + { + freer = r; + continue; + } + if (polyregions[r].vertex[0] == v2) + beginingof = r; + if (polyregions[r].vertex[polyregions[r].numverts-1] == v1) + endof = r; + + for (v = polyregions[r].numverts-2; v >= 0; v--) + if (polyregions[r].vertex[v] == v1 && polyregions[r].vertex[v+1] == v2) + return; //whoops. Duplicate line. + } + if (beginingof >= 0 && endof >= 0) + { //merge two regions. Copy one onto the end of the other. + if (beginingof == endof) + { //close up + if (polyregions[endof].numverts+1 >= MAX_POLYVERTS) + { + Con_Printf("^1WARNING: Map region is too large.\n"); + return; + } + polyregions[endof].vertex[polyregions[endof].numverts] = v2; + polyregions[endof].numverts++; + } + else + { + if (polyregions[endof].numverts+polyregions[beginingof].numverts >= MAX_POLYVERTS) + { + Con_Printf("^1WARNING: Map region is too large.\n"); + return; + } + memcpy(polyregions[endof].vertex + polyregions[endof].numverts, + polyregions[beginingof].vertex, + sizeof(polyregions[beginingof].vertex[0])*polyregions[beginingof].numverts); + polyregions[endof].numverts += polyregions[beginingof].numverts; + polyregions[beginingof].numverts = 0; + } + } + else if (beginingof >= 0) + { //insert into + if (polyregions[beginingof].numverts+1 >= MAX_POLYVERTS) + { + Con_Printf("^1WARNING: Map region is too large.\n"); + return; + } + + memmove(polyregions[beginingof].vertex + 1, + polyregions[beginingof].vertex, + sizeof(polyregions[beginingof].vertex[0])*polyregions[beginingof].numverts); + polyregions[beginingof].vertex[0] = v1; + polyregions[beginingof].numverts++; + } + else if (endof >= 0) + { //stick outselves on the end + if (polyregions[endof].numverts+1 >= MAX_POLYVERTS) + { + Con_Printf("^1WARNING: Map region is too large.\n"); + return; + } + polyregions[endof].vertex[polyregions[endof].numverts] = v2; + polyregions[endof].numverts++; + } + else + { //new region. + if (freer < 0) + { + freer = regions++; + if (regions > MAX_REGIONS) + { + Con_Printf("^1WARNING: Too many regions. Sector is too chaotic/complicated.\n"); + freer = 0; + regions = 1; + } + } + + polyregions[freer].numverts = 2; + polyregions[freer].vertex[0] = v1; + polyregions[freer].vertex[1] = v2; + } +} + +static unsigned short *Triangulate_Finish(int *numtris, unsigned short *old, int oldindexcount) +{ + unsigned short *out; + unsigned int v1, v2, v3, v; + unsigned int r, v2s, f; + float a1; + float a2; + for (r = 0; r < regions; r++) + { + if (!polyregions[r].numverts) + continue; + + if (polyregions[r].vertex[0] != polyregions[r].vertex[polyregions[r].numverts-1]) + { + Con_Printf("Sector is not enclosed\n"); + polyregions[r].vertex[polyregions[r].numverts] = polyregions[r].vertex[0]; + polyregions[r].numverts++; + + /* + *numtris = 0; + regions = 0; + + + return NULL;*/ + } + + polyregions[r].angle = 0; + polyregions[r].numverts--;//start == end + for (v = 0; v < polyregions[r].numverts; v++) + { + v1 = polyregions[r].vertex[v]; + v2 = polyregions[r].vertex[(v+1)%(polyregions[r].numverts)]; + v3 = polyregions[r].vertex[(v+2)%(polyregions[r].numverts)]; + a1 = atan2(vertexesl[v3].ypos - vertexesl[v2].ypos, vertexesl[v3].xpos - vertexesl[v2].xpos); + a2 = atan2(vertexesl[v1].ypos - vertexesl[v2].ypos, vertexesl[v1].xpos - vertexesl[v2].xpos); + polyregions[r].angle += fabs(a1 - a2); + } + } + + //FIXME: inner loops should find the nearest point in a forwards direction from one of the extreeme points. + + //angle should be either (numverts-2)*PI //inner loop + //or PI*numverts+2*PI //outer loop + //unfortuantly it's rarly either of them... + + for (r = 0; r < regions; r++) + { + if (polyregions[r].numverts<3) + continue; + v1 = polyregions[r].vertex[0]; + v2 = polyregions[r].vertex[1]; + v2s = 1; + f=0; + for (v = 2; polyregions[r].numverts>=3; ) + { //build a triangle fan. + if (numindexes+3 > MAX_POLYVERTS) + { + Con_Printf("^1WARNING: Sector is too big for triangulation\n"); + break; + } + v3 = polyregions[r].vertex[v]; + + a1 = atan2(vertexesl[v3].ypos - vertexesl[v2].ypos, vertexesl[v3].xpos - vertexesl[v2].xpos); + a2 = atan2(vertexesl[v1].ypos - vertexesl[v2].ypos, vertexesl[v1].xpos - vertexesl[v2].xpos); + if (fabs(a1-a2) > M_PI+0.01) //this would be a reflex angle then.;. + { +/* indexes[numindexes++] = 0; + indexes[numindexes++] = v2; + indexes[numindexes++] = 1; +*/ + v1 = v2; + v2 = v3; + v2s = v; + v=(v+1)%polyregions[r].numverts; + f++; + if (f >= 1000) + { //infinate loop - shouldn't happen. must have got the angle stuff wrong. + Con_Printf("^1WARNING: Failed to triangulate polygon\n"); + break; + } + continue; + } + + //FIXME: make sure v1 -> v3 doesn't clip any same-region lines. + + indexes[numindexes++] = v1; + indexes[numindexes++] = v2; + indexes[numindexes++] = v3; + memmove(polyregions[r].vertex+v2s, polyregions[r].vertex+v2s+1, (polyregions[r].numverts-v2s)*sizeof(polyregions[r].vertex[0])); + polyregions[r].numverts--; + v=(v)%polyregions[r].numverts; + v2 = v3; + v2s = v; + polyregions[r].vertex[polyregions[r].numverts] = 0; + } + } + + if (!numindexes) + { + Con_Printf("^1Warning: Sector is empty\n"); + + *numtris = 0; + regions = 0; + + return NULL; + } + + out = BZ_Realloc(old, sizeof(*out)*(numindexes+oldindexcount*3)); + memcpy(out+oldindexcount*3, indexes, sizeof(*out)*numindexes); + + *numtris = numindexes/3+oldindexcount; + regions = 0; + numindexes = 0; + + return out; +} +#endif + +static void Triangulate_Sectors(dsector_t *sectorl, qboolean glbspinuse) +{ + int seg, nsec; + int i, sec=-1; + + sectorm = BZ_Malloc(sectorc * sizeof(*sectorm)); + +#ifdef RGLQUAKE + if (glbspinuse) + { + for (i = 0; i < ssectorsc; i++) + { //only do linedefs. + for (seg = ssectorsl[i].first; seg < ssectorsl[i].first + ssectorsl[i].segcount; seg++) + if (segsl[seg].linedef != 0xffff) + break; + + if (seg == ssectorsl[i].first + ssectorsl[i].segcount) //throw a fit. + { + Con_Printf("SubSector %i has absolutly no walls\n", i); + continue; + } + + nsec = sidedefsm[linedefsl[segsl[seg].linedef].sidedef[segsl[seg].direction]].sector; + if (sec != nsec) + { + if (sec>=0) + sectorm[sec].flats = Triangulate_Finish(§orm[sec].numflattris, sectorm[sec].flats, sectorm[sec].numflattris); + sec = nsec; + } + for (seg = ssectorsl[i].first; seg < ssectorsl[i].first + ssectorsl[i].segcount; seg++) + { //ignore direction, it's do do with the intersection rather than the draw direction. + Triangulate_AddLine(segsl[seg].vert[0], segsl[seg].vert[1]); + } + } + if (sec>=0) + sectorm[sec].flats = Triangulate_Finish(§orm[sec].numflattris, sectorm[sec].flats, sectorm[sec].numflattris); + } + else + { + for (sec = 0; sec < sectorc; sec++) + { + for (i = 0; i < linedefsc; i++) + { + if (sidedefsm[linedefsl[i].sidedef[0]].sector == sec) + Triangulate_AddLine(linedefsl[i].vert[0], linedefsl[i].vert[1]); + if (linedefsl[i].sidedef[1] != 0xffff && sidedefsm[linedefsl[i].sidedef[1]].sector == sec) + Triangulate_AddLine(linedefsl[i].vert[1], linedefsl[i].vert[0]); + } + sectorm[sec].flats = Triangulate_Finish(§orm[sec].numflattris, sectorm[sec].flats, sectorm[sec].numflattris); + } + } +#endif + /* + for (i = 0; i < ssectorsc; i++) + { //only do linedefs. + seg = ssectorsl[i].first; + nsec = sidedefsm[linedefsl[segsl[seg].linedef].sidedef[segsl[seg].direction]].sector; + if (sec != nsec) + { + if (sec>=0) + sectorm[sec].flats = Triangulate_Finish(§orm[sec].numflattris); + sec = nsec; + } + for (seg = ssectorsl[i].first; seg < ssectorsl[i].first + ssectorsl[i].segcount; seg++) + { //ignore direction, it's do do with the intersection rather than the draw direction. + Triangulate_AddLine(segsl[seg].vert[0], segsl[seg].vert[1]); + } + } + if (sec>=0) + sectorm[sec].flats = Triangulate_Finish(§orm[sec].numflattris); + */ + + for (i = 0; i < sectorc; i++) + { +#ifdef RGLQUAKE + sectorm[i].ceilingtex = Doom_LoadFlat(sectorl[i].ceilingtexture); + sectorm[i].floortex = Doom_LoadFlat(sectorl[i].floortexture); +#endif + sectorm[i].lightlev = sectorl[i].lightlevel; + sectorm[i].specialtype = sectorl[i].specialtype; + sectorm[i].tag = sectorl[i].tag; + sectorm[i].ceilingheight = sectorl[i].ceilingheight; + sectorm[i].floorheight = sectorl[i].floorheight; + } +} + +static void *textures1; +static void *textures2; +static char *pnames; +static void Doom_LoadTextureInfos(void) +{ + textures1 = COM_LoadMallocFile("wad/texture1"); + textures2 = COM_LoadMallocFile("wad/texture2"); + pnames = COM_LoadMallocFile("wad/pnames"); +} + +typedef struct { + char name[8]; + short always0_0; + short always0_1; + short width; + short height; + short always0_2; + short always0_3; + short componantcount; +} ddoomtexture_t; +typedef struct { + short xoffset; + short yoffset; + unsigned short patchnum; + unsigned short always_1; + unsigned short always_0; +} ddoomtexturecomponant_t; + +typedef struct { + short width; + short height; + short xpos; + short ypos; +} doomimage_t; + +static void Doom_ExtractPName(unsigned int *out, doomimage_t *di, int outwidth, int outheight, int x, int y) +{ + unsigned int *colpointers; + int c, fr, rc, extra; + unsigned char *data, *coldata; + extern qbyte gammatable[256]; + + if (!di) + return; + + data = (char *)di; + + +// out += x/*+di->xpos*/; +// out += (y/*+di->ypos*/)*outwidth; + + colpointers = (unsigned int*)(data+sizeof(doomimage_t)); + for (c = 0; c < di->width; c++) + { + if (c < 0) + continue; + if (c > outwidth) + break; + + if (colpointers[c] >= com_filesize) + break; + coldata = data + colpointers[c]; + while(1) + { + fr = *coldata++; + if (fr == 255) + break; + + rc = *coldata++; + + coldata++; + + if ((fr+rc) > outheight) + { + extra = rc - outheight - fr +1; + rc = outheight - fr; + if (rc < 0) + break; + } + else + extra = 1; + + while(rc) + { + out[c + fr*outwidth] = (gammatable[doompalette[*coldata*3]]) + (gammatable[doompalette[*coldata*3+1]]<<8) + (gammatable[doompalette[*coldata*3+2]]<<16); + coldata++; + fr++; + rc--; + } + + coldata+=extra; + } + } +} + +static int Doom_LoadPatchFromTexWad(char *name, void *texlump) +{ + char patch[32] = "patches/"; + unsigned int *tex; + ddoomtexture_t *tx; + ddoomtexturecomponant_t *tc; + int i; + int count; + + count = *(int *)texlump; + tex = (int *)texlump+1; + + for (i = 0; i < count; i++) + { + tx = (ddoomtexture_t*)((unsigned char*)texlump + *tex); + if (!strncmp(tx->name, name, 8)) + { + tex = BZ_Malloc(tx->width*tx->height*4); + tc = (ddoomtexturecomponant_t*)(tx+1); + for (i = 0; i < tx->componantcount; i++, tc++) + { + strncpy(patch+8, pnames+4+8*tc->patchnum, 8); + strlwr(patch+8); + patch[16] = '\0'; + + Doom_ExtractPName(tex, (doomimage_t *)COM_LoadTempFile(patch), tx->width, tx->height, tc->xoffset, tc->yoffset); + } + + i = GL_LoadTexture32(name, tx->width, tx->height, tex, true, true); + BZ_Free(tex); + return i; + } + + tex++; + } + + return 0; +} +static int Doom_LoadPatch(char *name) +{ + int texnum; + if (textures1) + { + texnum = Doom_LoadPatchFromTexWad(name, textures1); + if (texnum) + return texnum; + } + if (textures2) + { + texnum = Doom_LoadPatchFromTexWad(name, textures2); + if (texnum) + return texnum; + } + //all else failed. + return Mod_LoadHiResTexture(name, true, false); +} + +static void CleanWalls(dsidedef_t *sidedefsl) +{ + int i; + char texname[64]; + char lastmiddle[9]="-"; + char lastlower[9]="-"; + char lastupper[9]="-"; + int lastmidtex=0, lastuptex=0, lastlowtex=0; + sidedefsm = BZ_Malloc(sidedefsc * sizeof(*sidedefsm)); + for (i = 0; i < sidedefsc; i++) + { +#ifdef RGLQUAKE + strncpy(texname, sidedefsl[i].middletex, 8); + texname[8] = '\0'; + if (!strcmp(texname, "-")) + sidedefsm[i].middletex = 0; + else + { + if (!strncmp(texname, lastmiddle, 8)) + sidedefsm[i].middletex = lastmidtex; + else + { + strncpy(lastmiddle, texname, 8); + sidedefsm[i].middletex = lastmidtex = Doom_LoadPatch(texname);//Mod_LoadHiResTexture(texname, true, false); + } + } + + strncpy(texname, sidedefsl[i].lowertex, 8); + texname[8] = '\0'; + if (!strcmp(texname, "-")) + sidedefsm[i].lowertex = 0; + else + { + if (!strncmp(texname, lastlower, 8)) + sidedefsm[i].lowertex = lastlowtex; + else + { + strncpy(lastlower, texname, 8); + sidedefsm[i].lowertex = lastlowtex = Doom_LoadPatch(texname);//Mod_LoadHiResTexture(texname, true, true); + } + } + + strncpy(texname, sidedefsl[i].uppertex, 8); + texname[8] = '\0'; + if (!strcmp(texname, "-")) + sidedefsm[i].uppertex = 0; + else + { + if (!strncmp(texname, lastupper, 8)) + sidedefsm[i].uppertex = lastuptex; + else + { + strncpy(lastupper, texname, 8); + sidedefsm[i].uppertex = lastuptex = Doom_LoadPatch(texname);//Mod_LoadHiResTexture(texname, true, false); + } + } +#endif + sidedefsm[i].sector = sidedefsl[i].sector; + sidedefsm[i].texx = sidedefsl[i].texx/64.0f; + sidedefsm[i].texy = sidedefsl[i].texy/64.0f; + } +} + +void QuakifyThings(dthing_t *thingsl) +{ + int sector; + int spawnflags; + char *name; + int i; + int zpos; + static char newlump[1024*1024]; + char *ptr = newlump; + vec3_t point; + + sprintf(ptr, "{\n" + "\"classname\" \"worldspawn\"\n" + "}\n"); + ptr += strlen(ptr); + + for (i = 0; i < thingsc; i++) + { + switch(thingsl[i].type) + { + case THING_PLAYER: //fixme: spit out a coop spawn too. + name = "info_player_start"; + break; + case THING_PLAYER2: + case THING_PLAYER3: + case THING_PLAYER4: + name = "info_player_coop"; + break; + case THING_DMSPAWN: + name = "info_player_deathmatch"; + break; + + + case THING_WCHAINSAW: + name = "item_artifact_super_damage"; + break; + case THING_WSHOTGUN1: + name = "weapon_nailgun"; + break; + case THING_WSHOTGUN2: + name = "weapon_supernailgun"; + break; + case THING_WCHAINGUN: + name = "weapon_supershotgun"; + break; + case THING_WROCKETL: + name = "weapon_lightning"; + break; + case THING_WPLASMA: + name = "weapon_grenadelauncher"; + break; + case THING_WBFG: + name = "weapon_rocketlauncher"; + break; + + default: + name = va("thing_%i", thingsl[i].type); + break; + } + + point[0] = thingsl[i].xpos; + point[1] = thingsl[i].ypos; + point[2] = 0; + sector = Doom_SectorNearPoint(point); + zpos = sectorm[sector].floorheight + 24; + + spawnflags = SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | SPAWNFLAG_NOT_HARD | SPAWNFLAG_NOT_DEATHMATCH; + if (thingsl[i].flags & THING_EASY) + spawnflags -= SPAWNFLAG_NOT_EASY; + if (thingsl[i].flags & THING_MEDIUM) + spawnflags -= SPAWNFLAG_NOT_MEDIUM; + if (thingsl[i].flags & THING_HARD) + spawnflags -= SPAWNFLAG_NOT_HARD; + if (thingsl[i].flags & THING_DEATHMATCH) + spawnflags -= SPAWNFLAG_NOT_DEATHMATCH; + if (thingsl[i].flags & THING_DEAF) + spawnflags |= 1; + + sprintf(ptr, "{\n" + "\"classname\" \"%s\"\n" + "\"origin\" \"%i %i %i\"\n" + "\"spawnflags\" \"%i\"\n" + "\"angle\" \"%i\"\n" + "}\n", + name, + thingsl[i].xpos, thingsl[i].ypos, zpos, + spawnflags, + thingsl[i].angle + ); + ptr += strlen(ptr); + } + + loadmodel->entities = BZ_Malloc(ptr-newlump+1); + memcpy(loadmodel->entities, newlump, ptr-newlump+1); +} + +void Doom_GeneratePlanes(ddoomnode_t *nodel) +{ + vec3_t point, up, line; + int n; + up[0] = 0; + up[1] = 0; + up[2] = 1; + line[2] = 0; + nodeplanes = BZ_Malloc(sizeof(*nodeplanes)*nodec); + lineplanes = BZ_Malloc(sizeof(*lineplanes)*linedefsc); + point[2] = 0; + for (n = 0; n < nodec; n++) + { + line[0] = nodel[n].dx; + line[1] = nodel[n].dy; + point[0] = nodel[n].x; + point[1] = nodel[n].y; + CrossProduct(line, up, nodeplanes[n].normal); + VectorNormalize(nodeplanes[n].normal); + nodeplanes[n].dist = DotProduct (point, nodeplanes[n].normal); + } + + for (n = 0; n < linedefsc; n++) + { + point[0] = vertexesl[linedefsl[n].vert[0]].xpos; + point[1] = vertexesl[linedefsl[n].vert[0]].ypos; + line[0] = vertexesl[linedefsl[n].vert[1]].xpos-point[0]; + line[1] = vertexesl[linedefsl[n].vert[1]].ypos-point[1]; + CrossProduct(line, up, lineplanes[n].normal); + VectorNormalize(lineplanes[n].normal); + lineplanes[n].dist = DotProduct (point, lineplanes[n].normal); + } +} + +static void MoveWorld(void) +{ + int v; + short adj[2]; + short min[2], max[2]; + min[0] = 4096; + min[1] = 4096; + max[0] = -4096; + max[1] = -4096; + + for (v = 0; v < vertexesc; v++) + { + if (min[0] > vertexesl[v].xpos) + min[0] = vertexesl[v].xpos; + if (min[1] > vertexesl[v].ypos) + min[1] = vertexesl[v].ypos; + + if (max[0] < vertexesl[v].xpos) + max[0] = vertexesl[v].xpos; + if (max[1] < vertexesl[v].ypos) + max[1] = vertexesl[v].ypos; + } + + if (min[0]>=-4096 && max[0]<=4096) + if (min[1]>=-4096 && max[1]<=4096) + return; //doesn't need adjusting, live with it. + + if (max[0]-min[0]>=8192 || max[1]-min[1]>=8192) + Con_Printf("^1Warning: Map is too large for the network protocol\n"); + + Con_Printf("Adjusting map\n"); + + adj[0] = (max[0]-4096)&~63; //don't harm the tiling. + adj[1] = (max[1]-4096)&~63; + + for (v = 0; v < vertexesc; v++) + { + vertexesl[v].xpos -= adj[0]; + vertexesl[v].ypos -= adj[1]; + } + + for (v = 0; v < nodec; v++) + { + nodel[v].x -= adj[0]; + nodel[v].y -= adj[1]; + + nodel[v].x1lower -= adj[0]; + nodel[v].x1upper -= adj[1]; + nodel[v].y1lower -= adj[0]; + nodel[v].y1upper -= adj[1]; + + nodel[v].x2lower -= adj[0]; + nodel[v].x2upper -= adj[1]; + nodel[v].y2lower -= adj[0]; + nodel[v].y2upper -= adj[1]; + } + + for (v = 0; v < thingsc; v++) + { + thingsl[v].xpos -= adj[0]; + thingsl[v].ypos -= adj[1]; + } + + blockmapl->xorg -= adj[0]; + blockmapl->yorg -= adj[1]; +} + + +static void Doom_LoadVerticies(char *name) +{ + ddoomvertex_t *std, *gl1; + int stdc, glc; + int *gl2; + int i; + + std = (void *)COM_LoadTempFile (va("%s.vertexes", name)); + stdc = com_filesize/sizeof(*std); + + gl2 = (void *)COM_LoadTempFile2 (va("%s.gl_vert", name)); + if (!gl2) + { + glc = 0; + gl1 = NULL; + } + else if (gl2[0] == *(int*)"gNd2") + { + gl2++; + glc = (com_filesize-4)/sizeof(int)/2; + gl1 = NULL; + } + else + { + glc = com_filesize/sizeof(*gl1); + gl1 = (ddoomvertex_t*)gl2; + } + + if (!stdc) + return; + + vertexesc = stdc + glc; + vertexesl = BZ_Malloc(vertexesc*sizeof(*vertexesl)); + + vertexsglbase = stdc; + + for (i = 0; i < stdc; i++) + { + vertexesl[i].xpos = std[i].xpos; + vertexesl[i].ypos = std[i].ypos; + } + if (gl1) + { + for (i = 0; i < glc; i++) + { + vertexesl[stdc+i].xpos = gl1[i].xpos; + vertexesl[stdc+i].ypos = gl1[i].ypos; + } + } + else + { + for (i = 0; i < glc; i++) + { + vertexesl[stdc+i].xpos = (float)gl2[i*2] / 0x10000; + vertexesl[stdc+i].ypos = (float)gl2[i*2+1] / 0x10000; + } + } +} + +static void Doom_LoadSSectors(char *name) +{ + ssectorsl = (void *)COM_LoadMallocFile (va("%s.gl_ssect", name)); + if (!ssectorsl) + ssectorsl = (void *)COM_LoadMallocFile (va("%s.ssectors", name)); + //FIXME: "gNd3" means that it's glbsp version 3. + ssectorsc = com_filesize/sizeof(*ssectorsl); +} + +static void Doom_LoadSSegs(char *name) +{ //these skirt the subsectors + + void *file; + dgl_seg3_t *s3; + dgl_seg1_t *s1; + dseg_t *s0; + int i; + + file = (void *)COM_LoadMallocFile (va("%s.gl_segs", name)); + if (!file) + { + s0 = (void *)COM_LoadMallocFile (va("%s.segs", name)); + segsc = com_filesize/sizeof(*s0); + + segsl = BZ_Malloc(segsc * sizeof(*segsl)); + for (i = 0; i < segsc; i++) + { + segsl[i].vert[0] = s0[i].vert[0]; + segsl[i].vert[1] = s0[i].vert[1]; + segsl[i].linedef = s0[i].linedef; + segsl[i].direction = s0[i].direction; + segsl[i].Partner = 0xffff; + } + } + else if (*(int *)file == *(int *)"gNd3") + { + s3 = file; + segsc = com_filesize/sizeof(*s3); + + segsl = s3; + } + else if (!file) + return; + else + { + s1 = file; + segsc = com_filesize/sizeof(*s1); + + segsl = BZ_Malloc(segsc * sizeof(*segsl)); + for (i = 0; i < segsc; i++) + { + if (s1[i].vert[0] & 0x8000) + segsl[i].vert[0] = (s1[i].vert[0]&0x7fff)+vertexsglbase; + else + segsl[i].vert[0] = s1[i].vert[0]; + if (s1[i].vert[1] & 0x8000) + segsl[i].vert[1] = (s1[i].vert[1]&0x7fff)+vertexsglbase; + else + segsl[i].vert[1] = s1[i].vert[1]; + segsl[i].linedef = s1[i].linedef; + segsl[i].direction = s1[i].direction; + if (s1[i].Partner == 0xffff) + segsl[i].Partner = 0xffffffff; + else + segsl[i].Partner = s1[i].Partner; + } + } +} + +qboolean Mod_LoadDoomLevel(model_t *mod) +{ + int h; + dsector_t *sectorl; + dsidedef_t *sidedefsl; + char name[MAX_QPATH]; + + int *gl_nodes; + + COM_StripExtension(mod->name, name); + + if (!COM_LoadTempFile(va("%s", mod->name))) + Host_EndGame("Wad map %s does not exist\n", mod->name); + + gl_nodes = (void *)COM_LoadMallocFile (va("%s.gl_nodes", name)); + if (gl_nodes && com_filesize>0) + { + nodel = (void *)gl_nodes; + nodec = com_filesize/sizeof(*nodel); + } + else + { + gl_nodes=NULL; + nodel = (void *)COM_LoadMallocFile (va("%s.nodes", name)); + nodec = com_filesize/sizeof(*nodel); + } + sectorl = (void *)COM_LoadMallocFile (va("%s.sectors", name)); + sectorc = com_filesize/sizeof(*sectorl); + + Doom_LoadVerticies(name); + + Doom_LoadSSegs(name); + Doom_LoadSSectors(name); + + thingsl = (void *)COM_LoadMallocFile (va("%s.things", name)); + thingsc = com_filesize/sizeof(*thingsl); + linedefsl = (void *)COM_LoadMallocFile (va("%s.linedefs", name)); + linedefsc = com_filesize/sizeof(*linedefsl); + sidedefsl = (void *)COM_LoadMallocFile (va("%s.sidedefs", name)); + sidedefsc = com_filesize/sizeof(*sidedefsl); + blockmapl = (void *)COM_LoadMallocFile (va("%s.blockmap", name)); +// blockmaps = com_filesize; + + Doom_LoadTextureInfos(); + + blockmapofs = (unsigned short*)(blockmapl+1); + + if (!nodel || !sectorl || !segsl || !ssectorsl || !thingsl || !linedefsl || !sidedefsl || !vertexesl) + { + Sys_Error("Wad map doesn't contain enough lumps\n"); + nodel = NULL; + return false; + } + + MoveWorld(); + + Doom_GeneratePlanes(nodel); + + mod->hulls[0].clip_mins[0] = 0; + mod->hulls[0].clip_mins[1] = 0; + mod->hulls[0].clip_mins[2] = 0; + mod->hulls[0].clip_maxs[0] = 0; + mod->hulls[0].clip_maxs[1] = 0; + mod->hulls[0].clip_maxs[2] = 0; + mod->hulls[0].available = true; + + mod->hulls[1].clip_mins[0] = -16; + mod->hulls[1].clip_mins[1] = -16; + mod->hulls[1].clip_mins[2] = -24; + mod->hulls[1].clip_maxs[0] = 16; + mod->hulls[1].clip_maxs[1] = 16; + mod->hulls[1].clip_maxs[2] = 32; + mod->hulls[1].available = true; + + mod->hulls[2].clip_mins[0] = -32; + mod->hulls[2].clip_mins[1] = -32; + mod->hulls[2].clip_mins[2] = -24; + mod->hulls[2].clip_maxs[0] = 32; + mod->hulls[2].clip_maxs[1] = 32; + mod->hulls[2].clip_maxs[2] = 64; + mod->hulls[2].available = true; + + mod->hulls[3].clip_mins[0] = -16; //allow a crouched sized hull. + mod->hulls[3].clip_mins[1] = -16; + mod->hulls[3].clip_mins[2] = -6; + mod->hulls[3].clip_maxs[0] = 16; + mod->hulls[3].clip_maxs[1] = 16; + mod->hulls[3].clip_maxs[2] = 30; + mod->hulls[3].available = true; + + for (h = 4; h < MAX_MAP_HULLSM; h++) + mod->hulls[h].available = false; + + for (h = 0; h < MAX_MAP_HULLSM; h++) + Doom_SetHullFuncs(&mod->hulls[h]); + + Doom_SetModelFunc(mod); + + mod->needload = false; + mod->fromgame = fg_doom; + + CleanWalls(sidedefsl); + + Triangulate_Sectors(sectorl, !!gl_nodes); + + QuakifyThings(thingsl); + + return true; +} + +int Doom_SectorNearPoint(vec3_t p) +{ + ddoomnode_t *node; + plane_t *plane; + int num; + int seg; + float d; + num = nodec-1; + while (1) + { + if (num & NODE_IS_SSECTOR) + { + num -= NODE_IS_SSECTOR; + for (seg = ssectorsl[num].first; seg < ssectorsl[num].first + ssectorsl[num].segcount; seg++) + if (segsl[seg].linedef != 0xffff) + break; + + return sidedefsm[linedefsl[segsl[seg].linedef].sidedef[segsl[seg].direction]].sector; + } + + node = nodel + num; + plane = nodeplanes + num; + +// if (plane->type < 3) +// d = p[plane->type] - plane->dist; +// else + d = DotProduct (plane->normal, p) - plane->dist; + if (d < 0) + num = node->node2; + else + num = node->node1; + } + + return num; +} + +int Doom_PointContents(hull_t *hull, vec3_t p) +{ + int sec = Doom_SectorNearPoint(p); + if (p[2] < sectorm[sec].floorheight-hull->clip_mins[2]) + return FTECONTENTS_SOLID; + if (p[2] > sectorm[sec].ceilingheight-hull->clip_maxs[2]) + return FTECONTENTS_SOLID; + return FTECONTENTS_EMPTY; +} + +qboolean Doom_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t start, vec3_t p2, trace_t *trace) +{ +#define TRACESTEP 16 + unsigned short *linedefs; + dlinedef_t *ld; + int bmi, obmi; + vec3_t delta; + int sec1 = Doom_SectorNearPoint(start); + vec3_t p1, pointonplane; + float d1, d2, c1, c2; + plane_t *lp; + mdoomvertex_t *v1, *v2; + + float clipfrac; +#define DIST_EPSILON (0.03125) + + float cylinder = 0;//hull->clip_maxs[0]; //FIXME: //lines and gravity cause major problems. the player is only ever in one sector at a time but should be in multiple. +// hull->clip_mins[2]=0; +// hull->clip_maxs[2]=0; + +// Con_Printf("%i\n", sec1); + + if (start[2] < sectorm[sec1].floorheight-hull->clip_mins[2]) //whoops, started outside... ? + { + trace->fraction = 0; + trace->allsolid = trace->startsolid = true; + trace->endpos[0] = start[0]; + trace->endpos[1] = start[1]; + trace->endpos[2] = start[2]; +// if (IS_NAN(trace->endpos[2])) +// Con_Printf("Nanny\n"); + trace->plane.normal[0] = 0; + trace->plane.normal[1] = 0; + trace->plane.normal[2] = 1; + trace->plane.dist = sectorm[sec1].floorheight-hull->clip_mins[2]; + + return false; + } + if (start[2] > sectorm[sec1].ceilingheight-hull->clip_maxs[2]) //whoops, started outside... ? + { + trace->fraction = 0; + trace->allsolid = trace->startsolid = true; + trace->endpos[0] = start[0]; + trace->endpos[1] = start[1]; + trace->endpos[2] = start[2]; + trace->plane.normal[0] = 0; + trace->plane.normal[1] = 0; + trace->plane.normal[2] = -1; + trace->plane.dist = -(sectorm[sec1].ceilingheight-hull->clip_maxs[2]); + return false; + } + + VectorCopy(start, p1); + obmi = -1; + VectorSubtract(p2, p1, delta); + p2f = Length(delta)+DIST_EPSILON; + if (IS_NAN(p2f)) + p2f = 100000; + VectorNormalize(delta); + + trace->endpos[0] = p2[0]; + trace->endpos[1] = p2[1]; + trace->endpos[2] = p2[2]; + + trace->fraction = 1; + while(1) + { + bmi = ((int)p1[0] - blockmapl->xorg)/128 + (((int)p1[1] - blockmapl->yorg)/128)*blockmapl->columns; +// Con_Printf("%i of %i ", bmi, blockmapl->rows*blockmapl->columns); + if (bmi >= 0 && bmi < blockmapl->rows*blockmapl->columns) + if (bmi != obmi) + { +#if 1 + short dummy; + linedefs = &dummy; + for (dummy = 0; dummy < linedefsc; dummy++) +#else + for(linedefs = (short*)blockmapl + blockmapofs[bmi]+1; *linedefs != 0xffff; linedefs++) +#endif + { + ld = linedefsl + *linedefs; + if (ld->sidedef[1] != 0xffff) + { + if (sectorm[sidedefsm[ld->sidedef[0]].sector].floorheight == sectorm[sidedefsm[ld->sidedef[1]].sector].floorheight && + sectorm[sidedefsm[ld->sidedef[0]].sector].ceilingheight == sectorm[sidedefsm[ld->sidedef[1]].sector].ceilingheight) + continue; + } + + lp = lineplanes + *linedefs; + d1 = DotProduct(lp->normal, start) - (lp->dist); + d2 = DotProduct(lp->normal, p2) - (lp->dist); + if (d1 >= cylinder && d2 >= cylinder) + continue; //both points on the front side. + if (d1 <= cylinder && d2 <= cylinder) + continue; //both points on the reverse side. + + //line crosses plane. + + v1 = &vertexesl[ld->vert[0]]; + v2 = &vertexesl[ld->vert[1]]; + + if (d1sidedef[1] == 0xffff) + continue; //allow them to pass + c1 = (d1+DIST_EPSILON) / (d1 - d2); + } + else + c1 = (d1-DIST_EPSILON) / (d1 - d2); + c2 = 1-c1; + pointonplane[0] = start[0]*c2 + p2[0]*c1; + if (pointonplane[0] > v1->xpos+DIST_EPSILON*2+cylinder && pointonplane[0] > v2->xpos+DIST_EPSILON*2+cylinder) + continue; + if (pointonplane[0] < v1->xpos-DIST_EPSILON*2-cylinder && pointonplane[0] < v2->xpos-DIST_EPSILON*2-cylinder) + continue; + pointonplane[1] = start[1]*c2 + p2[1]*c1; + if (pointonplane[1] > v1->ypos+DIST_EPSILON*2+cylinder && pointonplane[1] > v2->ypos+DIST_EPSILON*2+cylinder) + continue; + if (pointonplane[1] < v1->ypos-DIST_EPSILON*2-cylinder && pointonplane[1] < v2->ypos-DIST_EPSILON*2-cylinder) + continue; + pointonplane[2] = start[2]*c2 + p2[2]*c1; + + if (ld->flags & LINEDEF_IMPASSABLE || ld->sidedef[1] == 0xffff) //unconditionally unpassable. + { //unconditionally clipped. + } + else + { //ensure that the side we are passing on to passes the clip + msector_t *sec2; + + if (d1sidedef[1]].sector]; + else + sec2 = §orm[sidedefsm[ld->sidedef[0]].sector]; + + if (pointonplane[2] < sec2->floorheight-hull->clip_mins[2]) + { //hit the floor first. + c1 = fabs(sectorm[sec1].floorheight-hull->clip_mins[2] - start[2]); + c2 = fabs(p2[2] - start[2]); + if (!c2) + c1 = 1; + else + c1 = (c1-DIST_EPSILON) / c2; + if (trace->fraction > c1) + { +// Con_Printf("Hit floor\n"); + trace->fraction = c1; + trace->allsolid = trace->startsolid = true; + trace->endpos[0] = start[0] + trace->fraction*(p2[0]-start[0]); + trace->endpos[1] = start[1] + trace->fraction*(p2[1]-start[1]); + trace->endpos[2] = start[2] + trace->fraction*(p2[2]-start[2]); + trace->plane.normal[0] = 0; + trace->plane.normal[1] = 0; + trace->plane.normal[2] = 1; + trace->plane.dist = sectorm[sec1].floorheight-hull->clip_mins[2]; + } + continue; + } + + if (pointonplane[2] > sec2->ceilingheight-hull->clip_maxs[2]) + { //hit the floor first. + c1 = fabs((sectorm[sec1].ceilingheight-hull->clip_maxs[2]) - start[2]); + c2 = fabs(p2[2] - start[2]); + if (!c2) + c1 = 1; + else + c1 = (c1-DIST_EPSILON) / c2; + + + if (trace->fraction > c1) + { +// Con_Printf("Hit ceiling\n"); + trace->fraction = c1; + trace->allsolid = trace->startsolid = true; + trace->endpos[0] = start[0] + trace->fraction*(p2[0]-start[0]); + trace->endpos[1] = start[1] + trace->fraction*(p2[1]-start[1]); + trace->endpos[2] = start[2] + trace->fraction*(p2[2]-start[2]); + trace->plane.normal[0] = 0; + trace->plane.normal[1] = 0; + trace->plane.normal[2] = -1; + trace->plane.dist = -(sectorm[sec1].ceilingheight-hull->clip_maxs[2]); + } + continue; + } + + if (d1sidedef[0]].sector]; + else + sec2 = §orm[sidedefsm[ld->sidedef[1]].sector]; + + if(sec2->ceilingheight == sec2->floorheight) + sec2->ceilingheight += 64; + + if (pointonplane[2] > sec2->floorheight-hull->clip_mins[2] && + pointonplane[2] < sec2->ceilingheight-hull->clip_maxs[2]) + { +// Con_Printf("Two sided passed\n"); + continue; + } + +// Con_Printf("blocked by two sided line\n"); +// sec2->floorheight--; + } + + if (d1 1) + clipfrac = 1; + + if (trace->fraction > clipfrac) + { + trace->fraction = clipfrac; + VectorMA(pointonplane, cylinder, lp->normal, trace->endpos); +// if (IS_NAN(trace->endpos[2])) +// Con_Printf("Buggy clipping\n"); + VectorCopy(lp->normal, trace->plane.normal); + trace->plane.dist = lp->dist-cylinder; +// if (IS_NAN(trace->plane.normal[2])) +// Con_Printf("Buggy clipping\n"); + +// if (clipfrac) +// Con_Printf("Clip Wall %f\n", clipfrac); + } + } + + obmi = bmi; + } + + p1f += TRACESTEP; + if (p1f >= p2f) + break; + + VectorMA(p1, TRACESTEP, delta, p1); + } + +// VectorMA(start, p2f*trace->fraction, delta, p2); + + if (p2[2] != start[2]) + { + if (sec1 == Doom_SectorNearPoint(p2)) //special test. + { + if (p2[2] <= sectorm[sec1].floorheight-hull->clip_mins[2]) //whoops, started outside... ? + { + p1f = fabs(sectorm[sec1].floorheight-hull->clip_mins[2] - start[2]); + p2f = fabs(p2[2] - start[2]); + if (!p2f) + c1 = 1; + else + c1 = (p1f-DIST_EPSILON) / p2f; + if (trace->fraction > c1) + { + trace->fraction = c1; + trace->allsolid = trace->startsolid = false; + trace->endpos[0] = start[0] + trace->fraction*(p2[0]-start[0]); + trace->endpos[1] = start[1] + trace->fraction*(p2[1]-start[1]); + trace->endpos[2] = start[2] + trace->fraction*(p2[2]-start[2]); + trace->plane.normal[0] = 0; + trace->plane.normal[1] = 0; + trace->plane.normal[2] = 1; + trace->plane.dist = sectorm[sec1].floorheight-hull->clip_mins[2]; + } + +// if (IS_NAN(trace->endpos[2])) +// Con_Printf("Nanny\n"); + } + if (p2[2] >= sectorm[sec1].ceilingheight-hull->clip_maxs[2]) //whoops, started outside... ? + { + p1f = fabs(sectorm[sec1].ceilingheight-hull->clip_maxs[2] - start[2]); + p2f = fabs(p2[2] - start[2]); + if (!p2f) + c1 = 1; + else + c1 = (p1f-DIST_EPSILON) / p2f; + if (trace->fraction > c1) + { + trace->fraction = c1; + trace->allsolid = trace->startsolid = false; + trace->endpos[0] = start[0] + trace->fraction*(p2[0]-start[0]); + trace->endpos[1] = start[1] + trace->fraction*(p2[1]-start[1]); + trace->endpos[2] = start[2] + trace->fraction*(p2[2]-start[2]); + trace->plane.normal[0] = 0; + trace->plane.normal[1] = 0; + trace->plane.normal[2] = -1; + trace->plane.dist = -(sectorm[sec1].ceilingheight-hull->clip_maxs[2]); + } + +// if (IS_NAN(trace->endpos[2])) +// Con_Printf("Nanny\n"); + } + } + } + + //we made it all the way through. yay. + + trace->allsolid = trace->startsolid = false; +//Con_Printf("total = %f\n", trace->fraction); + return trace->fraction==1; +} + +void Doom_SetHullFuncs(hull_t *hull) +{ + hull->funcs.RecursiveHullCheck = Doom_RecursiveHullCheck; + hull->funcs.HullPointContents = Doom_PointContents; +} + +void Doom_LightPointValues(vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir) +{ + msector_t *sec; + sec = sectorm + Doom_SectorNearPoint(point); + + res_dir[0] = 0; + res_dir[1] = 1; + res_dir[2] = 1; + res_diffuse[0] = sec->lightlev; + res_diffuse[1] = sec->lightlev; + res_diffuse[2] = sec->lightlev; + res_ambient[0] = sec->lightlev; + res_ambient[1] = sec->lightlev; + res_ambient[2] = sec->lightlev; +} + +void Doom_FatPVS(vec3_t org, qboolean add) +{ + //FIXME: use REJECT lump. +} + +qboolean Doom_EdictInFatPVS(edict_t *edict) +{ //FIXME: use REJECT lump. + return true; +} +void Doom_FindTouchedLeafs(edict_t *edict) +{ + //work out the sectors this ent is in for easy pvs. +} + +void Doom_StainNode(struct mnode_s *node, float *parms) +{ + //not supported +} + +void Doom_MarkLights(struct dlight_s *light, int bit, struct mnode_s *node) +{ + //not supported +} + +void Doom_SetModelFunc(model_t *mod) +{ + mod->funcs.FatPVS = Doom_FatPVS; + mod->funcs.EdictInFatPVS = Doom_EdictInFatPVS; + mod->funcs.FindTouchedLeafs_Q1 = Doom_FindTouchedLeafs; + mod->funcs.LightPointValues = Doom_LightPointValues; + mod->funcs.StainNode = Doom_StainNode; + mod->funcs.MarkLights = Doom_MarkLights; +} + +#endif diff --git a/engine/gl/glquake.h b/engine/gl/glquake.h new file mode 100644 index 00000000..01aa557b --- /dev/null +++ b/engine/gl/glquake.h @@ -0,0 +1,813 @@ +/* +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. + +*/ +// disable data conversion warnings +#ifdef MSVCDISABLEWARNINGS +#pragma warning(disable : 4244) // MIPS +#pragma warning(disable : 4136) // X86 +#pragma warning(disable : 4051) // ALPHA +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif + +typedef qbyte pal77[128][128]; +pal77 *pal777to8; + +void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs); +qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2); +void ClearBounds (vec3_t mins, vec3_t maxs); + +#ifdef RGLQUAKE +#include +//#include +#include "glsupp.h" + + + +void GL_BeginRendering (int *x, int *y, int *width, int *height); +void GL_EndRendering (void); + +void R_EmitSkyEffectTris(model_t *mod, msurface_t *fa); +void GLR_BrightenScreen (void); +void GLR_NetGraph (void); +void GLR_DrawAlphaSurfaces (void); +void GL_FlushSkinCache(void); +void GL_GAliasFlushSkinCache(void); + + +// Function prototypes for the Texture Object Extension routines +typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *, + const GLboolean *); +typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint); +typedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *); +typedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *); +typedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint); +typedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *, + const GLclampf *); +typedef void (APIENTRY *TEXSUBIMAGEPTR)(int, int, int, int, int, int, int, int, void *); + +extern BINDTEXFUNCPTR bindTexFunc; +extern DELTEXFUNCPTR delTexFunc; +extern TEXSUBIMAGEPTR TexSubImage2DFunc; +extern void (APIENTRY *qglStencilOpSeparateATI) (GLenum face, GLenum fail, GLenum zfail, GLenum zpass); +extern PFNGLCOMPRESSEDTEXIMAGE2DARBPROC qglCompressedTexImage2DARB; +extern PFNGLGETCOMPRESSEDTEXIMAGEARBPROC qglGetCompressedTexImageARB; +extern PFNGLPNTRIANGLESIATIPROC qglPNTrianglesiATI; +extern PFNGLPNTRIANGLESFATIPROC qglPNTrianglesfATI; + +extern int texture_extension_number; +extern int texture_mode; + +extern float gldepthmin, gldepthmax; + +void GL_Upload32 (char *name, unsigned *data, int width, int height, qboolean mipmap, qboolean alpha); //name was added for texture compression output +void GL_Upload8 (qbyte *data, int width, int height, qboolean mipmap, qboolean alpha); +#ifdef GL_EXT_paletted_texture +void GL_Upload8_EXT (qbyte *data, int width, int height, qboolean mipmap, qboolean alpha); +#endif +int GL_LoadTexture (char *identifier, int width, int height, qbyte *data, qboolean mipmap, qboolean alpha); +int GL_LoadTexture8Bump (char *identifier, int width, int height, unsigned char *data, qboolean mipmap); +int GL_LoadTexture8Pal24 (char *identifier, int width, int height, qbyte *data, qbyte *palette24, qboolean mipmap, qboolean alpha); +int GL_LoadTexture32 (char *identifier, int width, int height, unsigned *data, qboolean mipmap, qboolean alpha); +int GL_LoadCompressed(char *name); +int GL_FindTexture (char *identifier); + +int GL_LoadTextureFB (char *identifier, int width, int height, qbyte *data, qboolean mipmap, qboolean alpha); +void GL_Upload8Pal24 (qbyte *data, qbyte *pal, int width, int height, qboolean mipmap, qboolean alpha); + +typedef struct +{ + float x, y, z; + float s, t; + float r, g, b; +} glvert_t; + +extern glvert_t glv; + +extern int glx, gly, glwidth, glheight; + +#endif + +// r_local.h -- private refresh defs + +#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) + // normalizing factor so player model works out to about + // 1 pixel per triangle +#define MAX_LBM_HEIGHT 480 + +#define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf + +#define SKYSHIFT 7 +#define SKYSIZE (1 << SKYSHIFT) +#define SKYMASK (SKYSIZE - 1) + +#define BACKFACE_EPSILON 0.01 + + +void R_TimeRefresh_f (void); +void R_ReadPointFile_f (void); +texture_t *SWR_TextureAnimation (texture_t *base); +texture_t *GLR_TextureAnimation (texture_t *base); + +#include "particles.h" + +//==================================================== + + +extern entity_t r_worldentity; +extern qboolean r_cache_thrash; // compatability +extern vec3_t modelorg, r_entorigin; +extern entity_t *currententity; +extern int r_visframecount; // ??? what difs? +extern int r_framecount; +extern mplane_t frustum[4]; +extern int c_brush_polys, c_alias_polys; + +extern float r_wateralphaval; + +// +// view origin +// +extern vec3_t vup; +extern vec3_t vpn; +extern vec3_t vright; +extern vec3_t r_origin; + +// +// screen size info +// +extern refdef_t r_refdef; +extern mleaf_t *r_viewleaf, *r_oldviewleaf; +extern mleaf_t *r_viewleaf2, *r_oldviewleaf2; +extern int r_viewcluster, r_viewcluster2, r_oldviewcluster, r_oldviewcluster2; //q2 +extern texture_t *r_notexture_mip; +extern int d_lightstylevalue[256]; // 8.8 fraction of base light value + +extern qboolean envmap; +extern int currenttexture; +extern int particletexture; +extern int explosiontexture; +extern int netgraphtexture; // netgraph texture +extern int playertextures; + +extern int skytexturenum; // index in cl.loadmodel, not gl texture object + +extern int gl_lightmap_format; +extern int gl_solid_format; +extern int gl_alpha_format; + +extern int mirrortexturenum; // quake texturenum, not gltexturenum +extern qboolean mirror; +extern mplane_t *mirror_plane; + +extern float r_world_matrix[16]; + +extern const char *gl_vendor; +extern const char *gl_renderer; +extern const char *gl_version; +extern const char *gl_extensions; + +void R_TranslatePlayerSkin (int playernum); +void GL_Bind (int texnum); +void GL_BindType (int type, int texnum); + +#if defined(RGLQUAKE) +// Multitexture +#define GL_TEXTURE0_SGIS 0x835E +#define GL_TEXTURE1_SGIS 0x835F + +typedef void (APIENTRY *lpMTexFUNC) (GLenum en, GLfloat f1, GLfloat f2); +typedef void (APIENTRY *lpSelTexFUNC) (GLenum en); +extern lpMTexFUNC qglMTexCoord2fSGIS; +extern lpSelTexFUNC qglSelectTextureSGIS; + +extern int gl_canstencil; +extern PFNGLACTIVESTENCILFACEEXTPROC qglActiveStencilFaceEXT; + +extern lpMTexFUNC qglMTexCoord2fSGIS; +extern lpSelTexFUNC qglSelectTextureSGIS; + + +extern qboolean gl_ext_stencil_wrap; +extern int gl_mtexarbable; //max texture units +extern qboolean gl_mtexable; +extern qboolean gl_compressable; + +extern int mtexid0; +extern int mtexid1; + +extern qboolean gl_mtexable; + +void GL_DisableMultitexture(void); +void GL_EnableMultitexture(void); + +// +// gl_warp.c +// +void GL_SubdivideSurface (msurface_t *fa, float dividesize); +void EmitBothSkyLayers (msurface_t *fa); +void EmitWaterPolys (msurface_t *fa); +void EmitSkyPolys (msurface_t *fa); +void R_DrawSkyChain (msurface_t *s); + +void R_ClearSkyBox (void); +void R_AddSkySurface (msurface_t *fa); + +// +// gl_draw.c +// +int GL_LoadPicTexture (qpic_t *pic); +void GL_Set2D (void); + +// +// gl_rmain.c +// +qboolean R_CullBox (vec3_t mins, vec3_t maxs); +void R_RotateForEntity (entity_t *e); +void R_DrawAliasModel (entity_t *e); + +//gl_alias.c +void R_DrawGAliasModel (entity_t *e); +void R_DrawGAliasShadowVolume(entity_t *e, vec3_t lightpos, float radius); +void R_DrawGAliasModelLighting (entity_t *e); + +//misc model formats +void R_DrawAlias3Model (entity_t *ent); +void R_DrawHLModel(entity_t *curent); +void R_DrawGroupModel (entity_t *ent); + +//typedef float m3by3_t[3][3]; +//int GetTag(model_t *mod, char *tagname, int frame, float **org, m3by3_t **ang); + +// +// gl_rlight.c +// +void GLR_MarkLights (dlight_t *light, int bit, mnode_t *node); +void GLR_MarkQ2Lights (dlight_t *light, int bit, mnode_t *node); +void GLR_AnimateLight (void); +void R_RenderDlights (void); +int GLR_LightPoint (vec3_t p); + +void GLQ3_LightGrid(vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_lightdir); + +// +// gl_rsurf.c +// +void R_DrawBrushModel (entity_t *e); +void R_DrawWorld (void); +void GL_BuildLightmaps (void); + +void GL_LoadShaders(void); +int Mod_LoadReplacementTexture(char *name, qboolean mipmap, qboolean alpha); +int Mod_LoadHiResTexture(char *name, qboolean mipmap, qboolean alpha); +int Mod_LoadBumpmapTexture(char *name); + +#define LMBLOCK_WIDTH 128 +#define LMBLOCK_HEIGHT 128 +typedef struct glRect_s { + unsigned char l,t,w,h; +} glRect_t; +typedef char stmap; +typedef struct { + glpoly_t *polys; + qboolean modified; + qboolean deluxmodified; + glRect_t rectchange; + glRect_t deluxrectchange; + int allocated[LMBLOCK_WIDTH]; + qbyte lightmaps[4*LMBLOCK_WIDTH*LMBLOCK_HEIGHT]; + qbyte deluxmaps[4*LMBLOCK_WIDTH*LMBLOCK_HEIGHT]; //fixme: make seperate structure for easy disabling with less memory usage. + stmap stainmaps[3*LMBLOCK_WIDTH*LMBLOCK_HEIGHT]; //rgb no a. added to lightmap for added (hopefully) speed. +} lightmapinfo_t; + +//gl_ppl.c +void PPL_DrawWorld (void); +void RotateLightVector(vec3_t angles, vec3_t origin, vec3_t lightpoint, vec3_t result); + +#endif + +// +// gl_refrag.c +// +void R_StoreEfrags (efrag_t **ppefrag); + + +// +// gl_ngraph.c +// +void R_NetGraph (void); + + +#if defined(RGLQUAKE) + +extern void (APIENTRY *qglAccum) (GLenum op, GLfloat value); +extern void (APIENTRY *qglAlphaFunc) (GLenum func, GLclampf ref); +extern GLboolean (APIENTRY *qglAreTexturesResident) (GLsizei n, const GLuint *textures, GLboolean *residences); +extern void (APIENTRY *qglArrayElement) (GLint i); +extern void (APIENTRY *qglBegin) (GLenum mode); +extern void (APIENTRY *qglBindTexture) (GLenum target, GLuint texture); +extern void (APIENTRY *qglBitmap) (GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap); +extern void (APIENTRY *qglBlendFunc) (GLenum sfactor, GLenum dfactor); +extern void (APIENTRY *qglCallList) (GLuint list); +extern void (APIENTRY *qglCallLists) (GLsizei n, GLenum type, const GLvoid *lists); +extern void (APIENTRY *qglClear) (GLbitfield mask); +extern void (APIENTRY *qglClearAccum) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +extern void (APIENTRY *qglClearColor) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +extern void (APIENTRY *qglClearDepth) (GLclampd depth); +extern void (APIENTRY *qglClearIndex) (GLfloat c); +extern void (APIENTRY *qglClearStencil) (GLint s); +extern void (APIENTRY *qglClipPlane) (GLenum plane, const GLdouble *equation); +extern void (APIENTRY *qglColor3b) (GLbyte red, GLbyte green, GLbyte blue); +extern void (APIENTRY *qglColor3bv) (const GLbyte *v); +extern void (APIENTRY *qglColor3d) (GLdouble red, GLdouble green, GLdouble blue); +extern void (APIENTRY *qglColor3dv) (const GLdouble *v); +extern void (APIENTRY *qglColor3f) (GLfloat red, GLfloat green, GLfloat blue); +extern void (APIENTRY *qglColor3fv) (const GLfloat *v); +extern void (APIENTRY *qglColor3i) (GLint red, GLint green, GLint blue); +extern void (APIENTRY *qglColor3iv) (const GLint *v); +extern void (APIENTRY *qglColor3s) (GLshort red, GLshort green, GLshort blue); +extern void (APIENTRY *qglColor3sv) (const GLshort *v); +extern void (APIENTRY *qglColor3ub) (GLubyte red, GLubyte green, GLubyte blue); +extern void (APIENTRY *qglColor3ubv) (const GLubyte *v); +extern void (APIENTRY *qglColor3ui) (GLuint red, GLuint green, GLuint blue); +extern void (APIENTRY *qglColor3uiv) (const GLuint *v); +extern void (APIENTRY *qglColor3us) (GLushort red, GLushort green, GLushort blue); +extern void (APIENTRY *qglColor3usv) (const GLushort *v); +extern void (APIENTRY *qglColor4b) (GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha); +extern void (APIENTRY *qglColor4bv) (const GLbyte *v); +extern void (APIENTRY *qglColor4d) (GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); +extern void (APIENTRY *qglColor4dv) (const GLdouble *v); +extern void (APIENTRY *qglColor4f) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +extern void (APIENTRY *qglColor4fv) (const GLfloat *v); +extern void (APIENTRY *qglColor4i) (GLint red, GLint green, GLint blue, GLint alpha); +extern void (APIENTRY *qglColor4iv) (const GLint *v); +extern void (APIENTRY *qglColor4s) (GLshort red, GLshort green, GLshort blue, GLshort alpha); +extern void (APIENTRY *qglColor4sv) (const GLshort *v); +extern void (APIENTRY *qglColor4ub) (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +extern void (APIENTRY *qglColor4ubv) (const GLubyte *v); +extern void (APIENTRY *qglColor4ui) (GLuint red, GLuint green, GLuint blue, GLuint alpha); +extern void (APIENTRY *qglColor4uiv) (const GLuint *v); +extern void (APIENTRY *qglColor4us) (GLushort red, GLushort green, GLushort blue, GLushort alpha); +extern void (APIENTRY *qglColor4usv) (const GLushort *v); +extern void (APIENTRY *qglColorMask) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +extern void (APIENTRY *qglColorMaterial) (GLenum face, GLenum mode); +extern void (APIENTRY *qglColorPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +extern void (APIENTRY *qglCopyPixels) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); +extern void (APIENTRY *qglCopyTexImage1D) (GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLint border); +extern void (APIENTRY *qglCopyTexImage2D) (GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +extern void (APIENTRY *qglCopyTexSubImage1D) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +extern void (APIENTRY *qglCopyTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +extern void (APIENTRY *qglCullFace) (GLenum mode); +extern void (APIENTRY *qglDeleteLists) (GLuint list, GLsizei range); +extern void (APIENTRY *qglDeleteTextures) (GLsizei n, const GLuint *textures); +extern void (APIENTRY *qglDepthFunc) (GLenum func); +extern void (APIENTRY *qglDepthMask) (GLboolean flag); +extern void (APIENTRY *qglDepthRange) (GLclampd zNear, GLclampd zFar); +extern void (APIENTRY *qglDisable) (GLenum cap); +extern void (APIENTRY *qglDisableClientState) (GLenum array); +extern void (APIENTRY *qglDrawArrays) (GLenum mode, GLint first, GLsizei count); +extern void (APIENTRY *qglDrawBuffer) (GLenum mode); +extern void (APIENTRY *qglDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); +extern void (APIENTRY *qglDrawPixels) (GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +extern void (APIENTRY *qglEdgeFlag) (GLboolean flag); +extern void (APIENTRY *qglEdgeFlagPointer) (GLsizei stride, const GLvoid *pointer); +extern void (APIENTRY *qglEdgeFlagv) (const GLboolean *flag); +extern void (APIENTRY *qglEnable) (GLenum cap); +extern void (APIENTRY *qglEnableClientState) (GLenum array); +extern void (APIENTRY *qglEnd) (void); +extern void (APIENTRY *qglEndList) (void); +extern void (APIENTRY *qglEvalCoord1d) (GLdouble u); +extern void (APIENTRY *qglEvalCoord1dv) (const GLdouble *u); +extern void (APIENTRY *qglEvalCoord1f) (GLfloat u); +extern void (APIENTRY *qglEvalCoord1fv) (const GLfloat *u); +extern void (APIENTRY *qglEvalCoord2d) (GLdouble u, GLdouble v); +extern void (APIENTRY *qglEvalCoord2dv) (const GLdouble *u); +extern void (APIENTRY *qglEvalCoord2f) (GLfloat u, GLfloat v); +extern void (APIENTRY *qglEvalCoord2fv) (const GLfloat *u); +extern void (APIENTRY *qglEvalMesh1) (GLenum mode, GLint i1, GLint i2); +extern void (APIENTRY *qglEvalMesh2) (GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2); +extern void (APIENTRY *qglEvalPoint1) (GLint i); +extern void (APIENTRY *qglEvalPoint2) (GLint i, GLint j); +extern void (APIENTRY *qglFeedbackBuffer) (GLsizei size, GLenum type, GLfloat *buffer); +extern void (APIENTRY *qglFinish) (void); +extern void (APIENTRY *qglFlush) (void); +extern void (APIENTRY *qglFogf) (GLenum pname, GLfloat param); +extern void (APIENTRY *qglFogfv) (GLenum pname, const GLfloat *params); +extern void (APIENTRY *qglFogi) (GLenum pname, GLint param); +extern void (APIENTRY *qglFogiv) (GLenum pname, const GLint *params); +extern void (APIENTRY *qglFrontFace) (GLenum mode); +extern void (APIENTRY *qglFrustum) (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +extern GLuint (APIENTRY *qglGenLists) (GLsizei range); +extern void (APIENTRY *qglGenTextures) (GLsizei n, GLuint *textures); +extern void (APIENTRY *qglGetBooleanv) (GLenum pname, GLboolean *params); +extern void (APIENTRY *qglGetClipPlane) (GLenum plane, GLdouble *equation); +extern void (APIENTRY *qglGetDoublev) (GLenum pname, GLdouble *params); +extern GLenum (APIENTRY *qglGetError) (void); +extern void (APIENTRY *qglGetFloatv) (GLenum pname, GLfloat *params); +extern void (APIENTRY *qglGetIntegerv) (GLenum pname, GLint *params); +extern void (APIENTRY *qglGetLightfv) (GLenum light, GLenum pname, GLfloat *params); +extern void (APIENTRY *qglGetLightiv) (GLenum light, GLenum pname, GLint *params); +extern void (APIENTRY *qglGetMapdv) (GLenum target, GLenum query, GLdouble *v); +extern void (APIENTRY *qglGetMapfv) (GLenum target, GLenum query, GLfloat *v); +extern void (APIENTRY *qglGetMapiv) (GLenum target, GLenum query, GLint *v); +extern void (APIENTRY *qglGetMaterialfv) (GLenum face, GLenum pname, GLfloat *params); +extern void (APIENTRY *qglGetMaterialiv) (GLenum face, GLenum pname, GLint *params); +extern void (APIENTRY *qglGetPixelMapfv) (GLenum map, GLfloat *values); +extern void (APIENTRY *qglGetPixelMapuiv) (GLenum map, GLuint *values); +extern void (APIENTRY *qglGetPixelMapusv) (GLenum map, GLushort *values); +extern void (APIENTRY *qglGetPointerv) (GLenum pname, GLvoid* *params); +extern void (APIENTRY *qglGetPolygonStipple) (GLubyte *mask); +extern const GLubyte * (APIENTRY *qglGetString) (GLenum name); +extern void (APIENTRY *qglGetTexEnvfv) (GLenum target, GLenum pname, GLfloat *params); +extern void (APIENTRY *qglGetTexEnviv) (GLenum target, GLenum pname, GLint *params); +extern void (APIENTRY *qglGetTexGendv) (GLenum coord, GLenum pname, GLdouble *params); +extern void (APIENTRY *qglGetTexGenfv) (GLenum coord, GLenum pname, GLfloat *params); +extern void (APIENTRY *qglGetTexGeniv) (GLenum coord, GLenum pname, GLint *params); +extern void (APIENTRY *qglGetTexImage) (GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels); +extern void (APIENTRY *qglGetTexLevelParameterfv) (GLenum target, GLint level, GLenum pname, GLfloat *params); +extern void (APIENTRY *qglGetTexLevelParameteriv) (GLenum target, GLint level, GLenum pname, GLint *params); +extern void (APIENTRY *qglGetTexParameterfv) (GLenum target, GLenum pname, GLfloat *params); +extern void (APIENTRY *qglGetTexParameteriv) (GLenum target, GLenum pname, GLint *params); +extern void (APIENTRY *qglHint) (GLenum target, GLenum mode); +extern void (APIENTRY *qglIndexMask) (GLuint mask); +extern void (APIENTRY *qglIndexPointer) (GLenum type, GLsizei stride, const GLvoid *pointer); +extern void (APIENTRY *qglIndexd) (GLdouble c); +extern void (APIENTRY *qglIndexdv) (const GLdouble *c); +extern void (APIENTRY *qglIndexf) (GLfloat c); +extern void (APIENTRY *qglIndexfv) (const GLfloat *c); +extern void (APIENTRY *qglIndexi) (GLint c); +extern void (APIENTRY *qglIndexiv) (const GLint *c); +extern void (APIENTRY *qglIndexs) (GLshort c); +extern void (APIENTRY *qglIndexsv) (const GLshort *c); +extern void (APIENTRY *qglIndexub) (GLubyte c); +extern void (APIENTRY *qglIndexubv) (const GLubyte *c); +extern void (APIENTRY *qglInitNames) (void); +extern void (APIENTRY *qglInterleavedArrays) (GLenum format, GLsizei stride, const GLvoid *pointer); +extern GLboolean (APIENTRY *qglIsEnabled) (GLenum cap); +extern GLboolean (APIENTRY *qglIsList) (GLuint list); +extern GLboolean (APIENTRY *qglIsTexture) (GLuint texture); +extern void (APIENTRY *qglLightModelf) (GLenum pname, GLfloat param); +extern void (APIENTRY *qglLightModelfv) (GLenum pname, const GLfloat *params); +extern void (APIENTRY *qglLightModeli) (GLenum pname, GLint param); +extern void (APIENTRY *qglLightModeliv) (GLenum pname, const GLint *params); +extern void (APIENTRY *qglLightf) (GLenum light, GLenum pname, GLfloat param); +extern void (APIENTRY *qglLightfv) (GLenum light, GLenum pname, const GLfloat *params); +extern void (APIENTRY *qglLighti) (GLenum light, GLenum pname, GLint param); +extern void (APIENTRY *qglLightiv) (GLenum light, GLenum pname, const GLint *params); +extern void (APIENTRY *qglLineStipple) (GLint factor, GLushort pattern); +extern void (APIENTRY *qglLineWidth) (GLfloat width); +extern void (APIENTRY *qglListBase) (GLuint base); +extern void (APIENTRY *qglLoadIdentity) (void); +extern void (APIENTRY *qglLoadMatrixd) (const GLdouble *m); +extern void (APIENTRY *qglLoadMatrixf) (const GLfloat *m); +extern void (APIENTRY *qglLoadName) (GLuint name); +extern void (APIENTRY *qglLogicOp) (GLenum opcode); +extern void (APIENTRY *qglMap1d) (GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +extern void (APIENTRY *qglMap1f) (GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +extern void (APIENTRY *qglMap2d) (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +extern void (APIENTRY *qglMap2f) (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +extern void (APIENTRY *qglMapGrid1d) (GLint un, GLdouble u1, GLdouble u2); +extern void (APIENTRY *qglMapGrid1f) (GLint un, GLfloat u1, GLfloat u2); +extern void (APIENTRY *qglMapGrid2d) (GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2); +extern void (APIENTRY *qglMapGrid2f) (GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2); +extern void (APIENTRY *qglMaterialf) (GLenum face, GLenum pname, GLfloat param); +extern void (APIENTRY *qglMaterialfv) (GLenum face, GLenum pname, const GLfloat *params); +extern void (APIENTRY *qglMateriali) (GLenum face, GLenum pname, GLint param); +extern void (APIENTRY *qglMaterialiv) (GLenum face, GLenum pname, const GLint *params); +extern void (APIENTRY *qglMatrixMode) (GLenum mode); +extern void (APIENTRY *qglMultMatrixd) (const GLdouble *m); +extern void (APIENTRY *qglMultMatrixf) (const GLfloat *m); +extern void (APIENTRY *qglNewList) (GLuint list, GLenum mode); +extern void (APIENTRY *qglNormal3b) (GLbyte nx, GLbyte ny, GLbyte nz); +extern void (APIENTRY *qglNormal3bv) (const GLbyte *v); +extern void (APIENTRY *qglNormal3d) (GLdouble nx, GLdouble ny, GLdouble nz); +extern void (APIENTRY *qglNormal3dv) (const GLdouble *v); +extern void (APIENTRY *qglNormal3f) (GLfloat nx, GLfloat ny, GLfloat nz); +extern void (APIENTRY *qglNormal3fv) (const GLfloat *v); +extern void (APIENTRY *qglNormal3i) (GLint nx, GLint ny, GLint nz); +extern void (APIENTRY *qglNormal3iv) (const GLint *v); +extern void (APIENTRY *qglNormal3s) (GLshort nx, GLshort ny, GLshort nz); +extern void (APIENTRY *qglNormal3sv) (const GLshort *v); +extern void (APIENTRY *qglNormalPointer) (GLenum type, GLsizei stride, const GLvoid *pointer); +extern void (APIENTRY *qglOrtho) (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +extern void (APIENTRY *qglPassThrough) (GLfloat token); +extern void (APIENTRY *qglPixelMapfv) (GLenum map, GLsizei mapsize, const GLfloat *values); +extern void (APIENTRY *qglPixelMapuiv) (GLenum map, GLsizei mapsize, const GLuint *values); +extern void (APIENTRY *qglPixelMapusv) (GLenum map, GLsizei mapsize, const GLushort *values); +extern void (APIENTRY *qglPixelStoref) (GLenum pname, GLfloat param); +extern void (APIENTRY *qglPixelStorei) (GLenum pname, GLint param); +extern void (APIENTRY *qglPixelTransferf) (GLenum pname, GLfloat param); +extern void (APIENTRY *qglPixelTransferi) (GLenum pname, GLint param); +extern void (APIENTRY *qglPixelZoom) (GLfloat xfactor, GLfloat yfactor); +extern void (APIENTRY *qglPointSize) (GLfloat size); +extern void (APIENTRY *qglPolygonMode) (GLenum face, GLenum mode); +extern void (APIENTRY *qglPolygonOffset) (GLfloat factor, GLfloat units); +extern void (APIENTRY *qglPolygonStipple) (const GLubyte *mask); +extern void (APIENTRY *qglPopAttrib) (void); +extern void (APIENTRY *qglPopClientAttrib) (void); +extern void (APIENTRY *qglPopMatrix) (void); +extern void (APIENTRY *qglPopName) (void); +extern void (APIENTRY *qglPrioritizeTextures) (GLsizei n, const GLuint *textures, const GLclampf *priorities); +extern void (APIENTRY *qglPushAttrib) (GLbitfield mask); +extern void (APIENTRY *qglPushClientAttrib) (GLbitfield mask); +extern void (APIENTRY *qglPushMatrix) (void); +extern void (APIENTRY *qglPushName) (GLuint name); +extern void (APIENTRY *qglRasterPos2d) (GLdouble x, GLdouble y); +extern void (APIENTRY *qglRasterPos2dv) (const GLdouble *v); +extern void (APIENTRY *qglRasterPos2f) (GLfloat x, GLfloat y); +extern void (APIENTRY *qglRasterPos2fv) (const GLfloat *v); +extern void (APIENTRY *qglRasterPos2i) (GLint x, GLint y); +extern void (APIENTRY *qglRasterPos2iv) (const GLint *v); +extern void (APIENTRY *qglRasterPos2s) (GLshort x, GLshort y); +extern void (APIENTRY *qglRasterPos2sv) (const GLshort *v); +extern void (APIENTRY *qglRasterPos3d) (GLdouble x, GLdouble y, GLdouble z); +extern void (APIENTRY *qglRasterPos3dv) (const GLdouble *v); +extern void (APIENTRY *qglRasterPos3f) (GLfloat x, GLfloat y, GLfloat z); +extern void (APIENTRY *qglRasterPos3fv) (const GLfloat *v); +extern void (APIENTRY *qglRasterPos3i) (GLint x, GLint y, GLint z); +extern void (APIENTRY *qglRasterPos3iv) (const GLint *v); +extern void (APIENTRY *qglRasterPos3s) (GLshort x, GLshort y, GLshort z); +extern void (APIENTRY *qglRasterPos3sv) (const GLshort *v); +extern void (APIENTRY *qglRasterPos4d) (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +extern void (APIENTRY *qglRasterPos4dv) (const GLdouble *v); +extern void (APIENTRY *qglRasterPos4f) (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +extern void (APIENTRY *qglRasterPos4fv) (const GLfloat *v); +extern void (APIENTRY *qglRasterPos4i) (GLint x, GLint y, GLint z, GLint w); +extern void (APIENTRY *qglRasterPos4iv) (const GLint *v); +extern void (APIENTRY *qglRasterPos4s) (GLshort x, GLshort y, GLshort z, GLshort w); +extern void (APIENTRY *qglRasterPos4sv) (const GLshort *v); +extern void (APIENTRY *qglReadBuffer) (GLenum mode); +extern void (APIENTRY *qglReadPixels) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); +extern void (APIENTRY *qglRectd) (GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2); +extern void (APIENTRY *qglRectdv) (const GLdouble *v1, const GLdouble *v2); +extern void (APIENTRY *qglRectf) (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); +extern void (APIENTRY *qglRectfv) (const GLfloat *v1, const GLfloat *v2); +extern void (APIENTRY *qglRecti) (GLint x1, GLint y1, GLint x2, GLint y2); +extern void (APIENTRY *qglRectiv) (const GLint *v1, const GLint *v2); +extern void (APIENTRY *qglRects) (GLshort x1, GLshort y1, GLshort x2, GLshort y2); +extern void (APIENTRY *qglRectsv) (const GLshort *v1, const GLshort *v2); +extern GLint (APIENTRY *qglRenderMode) (GLenum mode); +extern void (APIENTRY *qglRotated) (GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +extern void (APIENTRY *qglRotatef) (GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +extern void (APIENTRY *qglScaled) (GLdouble x, GLdouble y, GLdouble z); +extern void (APIENTRY *qglScalef) (GLfloat x, GLfloat y, GLfloat z); +extern void (APIENTRY *qglScissor) (GLint x, GLint y, GLsizei width, GLsizei height); +extern void (APIENTRY *qglSelectBuffer) (GLsizei size, GLuint *buffer); +extern void (APIENTRY *qglShadeModel) (GLenum mode); +extern void (APIENTRY *qglStencilFunc) (GLenum func, GLint ref, GLuint mask); +extern void (APIENTRY *qglStencilMask) (GLuint mask); +extern void (APIENTRY *qglStencilOp) (GLenum fail, GLenum zfail, GLenum zpass); +extern void (APIENTRY *qglTexCoord1d) (GLdouble s); +extern void (APIENTRY *qglTexCoord1dv) (const GLdouble *v); +extern void (APIENTRY *qglTexCoord1f) (GLfloat s); +extern void (APIENTRY *qglTexCoord1fv) (const GLfloat *v); +extern void (APIENTRY *qglTexCoord1i) (GLint s); +extern void (APIENTRY *qglTexCoord1iv) (const GLint *v); +extern void (APIENTRY *qglTexCoord1s) (GLshort s); +extern void (APIENTRY *qglTexCoord1sv) (const GLshort *v); +extern void (APIENTRY *qglTexCoord2d) (GLdouble s, GLdouble t); +extern void (APIENTRY *qglTexCoord2dv) (const GLdouble *v); +extern void (APIENTRY *qglTexCoord2f) (GLfloat s, GLfloat t); +extern void (APIENTRY *qglTexCoord2fv) (const GLfloat *v); +extern void (APIENTRY *qglTexCoord2i) (GLint s, GLint t); +extern void (APIENTRY *qglTexCoord2iv) (const GLint *v); +extern void (APIENTRY *qglTexCoord2s) (GLshort s, GLshort t); +extern void (APIENTRY *qglTexCoord2sv) (const GLshort *v); +extern void (APIENTRY *qglTexCoord3d) (GLdouble s, GLdouble t, GLdouble r); +extern void (APIENTRY *qglTexCoord3dv) (const GLdouble *v); +extern void (APIENTRY *qglTexCoord3f) (GLfloat s, GLfloat t, GLfloat r); +extern void (APIENTRY *qglTexCoord3fv) (const GLfloat *v); +extern void (APIENTRY *qglTexCoord3i) (GLint s, GLint t, GLint r); +extern void (APIENTRY *qglTexCoord3iv) (const GLint *v); +extern void (APIENTRY *qglTexCoord3s) (GLshort s, GLshort t, GLshort r); +extern void (APIENTRY *qglTexCoord3sv) (const GLshort *v); +extern void (APIENTRY *qglTexCoord4d) (GLdouble s, GLdouble t, GLdouble r, GLdouble q); +extern void (APIENTRY *qglTexCoord4dv) (const GLdouble *v); +extern void (APIENTRY *qglTexCoord4f) (GLfloat s, GLfloat t, GLfloat r, GLfloat q); +extern void (APIENTRY *qglTexCoord4fv) (const GLfloat *v); +extern void (APIENTRY *qglTexCoord4i) (GLint s, GLint t, GLint r, GLint q); +extern void (APIENTRY *qglTexCoord4iv) (const GLint *v); +extern void (APIENTRY *qglTexCoord4s) (GLshort s, GLshort t, GLshort r, GLshort q); +extern void (APIENTRY *qglTexCoord4sv) (const GLshort *v); +extern void (APIENTRY *qglTexCoordPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +extern void (APIENTRY *qglTexEnvf) (GLenum target, GLenum pname, GLfloat param); +extern void (APIENTRY *qglTexEnvfv) (GLenum target, GLenum pname, const GLfloat *params); +extern void (APIENTRY *qglTexEnvi) (GLenum target, GLenum pname, GLint param); +extern void (APIENTRY *qglTexEnviv) (GLenum target, GLenum pname, const GLint *params); +extern void (APIENTRY *qglTexGend) (GLenum coord, GLenum pname, GLdouble param); +extern void (APIENTRY *qglTexGendv) (GLenum coord, GLenum pname, const GLdouble *params); +extern void (APIENTRY *qglTexGenf) (GLenum coord, GLenum pname, GLfloat param); +extern void (APIENTRY *qglTexGenfv) (GLenum coord, GLenum pname, const GLfloat *params); +extern void (APIENTRY *qglTexGeni) (GLenum coord, GLenum pname, GLint param); +extern void (APIENTRY *qglTexGeniv) (GLenum coord, GLenum pname, const GLint *params); +extern void (APIENTRY *qglTexImage1D) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +extern void (APIENTRY *qglTexImage2D) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +extern void (APIENTRY *qglTexParameterf) (GLenum target, GLenum pname, GLfloat param); +extern void (APIENTRY *qglTexParameterfv) (GLenum target, GLenum pname, const GLfloat *params); +extern void (APIENTRY *qglTexParameteri) (GLenum target, GLenum pname, GLint param); +extern void (APIENTRY *qglTexParameteriv) (GLenum target, GLenum pname, const GLint *params); +extern void (APIENTRY *qglTexSubImage1D) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +extern void (APIENTRY *qglTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +extern void (APIENTRY *qglTranslated) (GLdouble x, GLdouble y, GLdouble z); +extern void (APIENTRY *qglTranslatef) (GLfloat x, GLfloat y, GLfloat z); +extern void (APIENTRY *qglVertex2d) (GLdouble x, GLdouble y); +extern void (APIENTRY *qglVertex2dv) (const GLdouble *v); +extern void (APIENTRY *qglVertex2f) (GLfloat x, GLfloat y); +extern void (APIENTRY *qglVertex2fv) (const GLfloat *v); +extern void (APIENTRY *qglVertex2i) (GLint x, GLint y); +extern void (APIENTRY *qglVertex2iv) (const GLint *v); +extern void (APIENTRY *qglVertex2s) (GLshort x, GLshort y); +extern void (APIENTRY *qglVertex2sv) (const GLshort *v); +extern void (APIENTRY *qglVertex3d) (GLdouble x, GLdouble y, GLdouble z); +extern void (APIENTRY *qglVertex3dv) (const GLdouble *v); +extern void (APIENTRY *qglVertex3f) (GLfloat x, GLfloat y, GLfloat z); +extern void (APIENTRY *qglVertex3fv) (const GLfloat *v); +extern void (APIENTRY *qglVertex3i) (GLint x, GLint y, GLint z); +extern void (APIENTRY *qglVertex3iv) (const GLint *v); +extern void (APIENTRY *qglVertex3s) (GLshort x, GLshort y, GLshort z); +extern void (APIENTRY *qglVertex3sv) (const GLshort *v); +extern void (APIENTRY *qglVertex4d) (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +extern void (APIENTRY *qglVertex4dv) (const GLdouble *v); +extern void (APIENTRY *qglVertex4f) (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +extern void (APIENTRY *qglVertex4fv) (const GLfloat *v); +extern void (APIENTRY *qglVertex4i) (GLint x, GLint y, GLint z, GLint w); +extern void (APIENTRY *qglVertex4iv) (const GLint *v); +extern void (APIENTRY *qglVertex4s) (GLshort x, GLshort y, GLshort z, GLshort w); +extern void (APIENTRY *qglVertex4sv) (const GLshort *v); +extern void (APIENTRY *qglVertexPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +extern void (APIENTRY *qglViewport) (GLint x, GLint y, GLsizei width, GLsizei height); + +#ifdef _WIN32 +extern BOOL (WINAPI *qwglCopyContext)(HGLRC, HGLRC, UINT); +extern HGLRC (WINAPI *qwglCreateContext)(HDC); +extern HGLRC (WINAPI *qwglCreateLayerContext)(HDC, int); +extern BOOL (WINAPI *qwglDeleteContext)(HGLRC); +extern HGLRC (WINAPI *qwglGetCurrentContext)(VOID); +extern HDC (WINAPI *qwglGetCurrentDC)(VOID); +extern PROC (WINAPI *qwglGetProcAddress)(LPCSTR); +extern BOOL (WINAPI *qwglMakeCurrent)(HDC, HGLRC); +extern BOOL (WINAPI *qSwapBuffers)(HDC); +#endif + + +#define glAlphaFunc qglAlphaFunc +#define glBlendFunc qglBlendFunc +#define glBegin qglBegin +#define glColor3f qglColor3f +#define glColor4f qglColor4f +#define glColor4ub qglColor4ub +#define glColor4ubv qglColor4ubv +#define glColorMask qglColorMask +#define glClearDepth qglClearDepth +#define glClearStencil qglClearStencil +#define glDisable qglDisable +#define glEnable qglEnable +#define glEnd qglEnd +#define glFlush qglFlush +#define glScalef qglScalef +#define glTranslatef qglTranslatef +#define glGetInteger qglGetInteger +#define glTexCoord1f qglTexCoord1f +#define glTexCoord2f qglTexCoord2f +#define glTexCoord2fv qglTexCoord2fv +#define glTexParameterf qglTexParameterf +#define glNormal3f qglNormal3f +#define glNormal3fv qglNormal3fv +#define glTexParameteri qglTexParameteri +#define glTexImage2D qglTexImage2D +#define glMultMatrixf qglMultMatrixf +#define glGetIntegerv qglGetIntegerv +#define glTexEnvf qglTexEnvf +#define glTexEnvi qglTexEnvi +#define glTexGeni qglTexGeni +#define glVertex2f qglVertex2f +#define glVertex3f qglVertex3f +#define glVertex3fv qglVertex3fv +#define glCullFace qglCullFace +#define glPushMatrix qglPushMatrix +#define glPopMatrix qglPopMatrix +#define glViewport qglViewport +#define glRotatef qglRotatef +#define glShadeModel qglShadeModel +#define glDepthMask qglDepthMask +#define glDepthFunc qglDepthFunc +#define glClearColor qglClearColor +#define glFinish qglFinish +#define glClear qglClear +#define glHint qglHint +#define glDepthRange qglDepthRange +#define glMatrixMode qglMatrixMode +#define glOrtho qglOrtho +#define glDrawBuffer qglDrawBuffer +#define glTexSubImage2D qglTexSubImage2D +#define glGetString qglGetString +#define glPolygonMode qglPolygonMode +#define glReadPixels qglReadPixels +#define glLoadMatrixf qglLoadMatrixf +#define glColor4fv qglColor4fv +#define glFrustum qglFrustum +#define glGetFloatv qglGetFloatv +#define glReadBuffer qglReadBuffer +#define glLoadIdentity qglLoadIdentity +#define glGetTexLevelParameteriv qglGetTexLevelParameteriv + +//vertex lists +#define glTexCoordPointer qglTexCoordPointer +#define glVertexPointer qglVertexPointer +#define glNormalPointer qglNormalPointer +#define glColorPointer qglColorPointer +#define glDrawElements qglDrawElements +#define glDisableClientState qglDisableClientState +#define glEnableClientState qglEnableClientState + +//stencil functions +#define glStencilOp qglStencilOp +#define glStencilFunc qglStencilFunc +#define glPushAttrib qglPushAttrib +#define glPopAttrib qglPopAttrib + +void GL_Init(void *(*getglfunction) (char *name)); + +#endif + +qbyte GetPalette(int red, int green, int blue); +int Mod_ReadFlagsFromMD1(char *name, int md3version); + + + + + +#ifdef SWQUAKE + + +#define CYCLE 128 // turbulent cycle size +extern int r_pixbytes; + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + + +extern cvar_t r_drawflat; +extern int d_spanpixcount; +extern int r_framecount; // sequence # of current frame since Quake + // started +extern qboolean r_drawpolys; // 1 if driver wants clipped polygons + // rather than a span list +extern qboolean r_drawculledpolys; // 1 if driver wants clipped polygons that + // have been culled by the edge list +extern qboolean r_worldpolysbacktofront; // 1 if driver wants polygons + // delivered back to front rather + // than front to back +extern qboolean r_recursiveaffinetriangles; // true if a driver wants to use + // recursive triangular subdivison + // and vertex drawing via + // D_PolysetDrawFinalVerts() past + // a certain distance (normally + // only used by the software + // driver) +extern float r_aliasuvscale; // scale-up factor for screen u and v + // on Alias vertices passed to driver +extern int r_pixbytes; +extern qboolean r_dowarp; + +/*extern affinetridesc_t r_affinetridesc; +extern spritedesc_t r_spritedesc; +extern zpointdesc_t r_zpointdesc; +extern polydesc_t r_polydesc; +*/ +extern int d_con_indirect; // if 0, Quake will draw console directly + // to vid.buffer; if 1, Quake will + // draw console via D_DrawRect. Must be + // defined by driver + +extern vec3_t r_pright, r_pup, r_ppn; + + + + +extern float xscaleshrink, yscaleshrink; +#endif diff --git a/engine/gl/glsupp.h b/engine/gl/glsupp.h new file mode 100644 index 00000000..0853371d --- /dev/null +++ b/engine/gl/glsupp.h @@ -0,0 +1,204 @@ +//gl suppliment for Quake + +//contains the extra things that would otherwise be found in glext.h + +typedef void (APIENTRY *qlpMTex2FUNC) (GLenum, GLfloat, GLfloat); +typedef void (APIENTRY *qlpMTex3FUNC) (GLenum, GLfloat, GLfloat, GLfloat); +typedef void (APIENTRY *qlpSelTexFUNC) (GLenum); + +extern qlpSelTexFUNC qglActiveTextureARB; +extern qlpSelTexFUNC qglClientActiveTextureARB; +extern qlpMTex3FUNC qglMultiTexCoord3fARB; +extern qlpMTex2FUNC qglMultiTexCoord2fARB; + +//This stuff is normally supplied in the header file. I don't actually have one of them, so it's here instead. +#if 0 //change to 1 if you do actually have the file in question. +#include //would be ideal. +#else + + +//#ifndef GL_VERSION_1_2 +#define GL_CLAMP_TO_EDGE 0x812F +//#endif + + +#ifndef GL_ARB_multitexture +#define GL_ARB_multitexture 1 +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31_ARB 0x84DF +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 +#endif + +#ifndef GL_ARB_texture_cube_map +#define GL_ARB_texture_cube_map 1 +#define GL_NORMAL_MAP_ARB 0x8511 +#define GL_REFLECTION_MAP_ARB 0x8512 +#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C +#endif + + + +#ifndef GL_EXT_texture3D +#define define GL_EXT_texture3D 1 +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_SKIP_IMAGES_EXT 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_TEXTURE_3D_EXT 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_PROXY_TEXTURE_3D_EXT 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_DEPTH_EXT 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_TEXTURE_WRAP_R_EXT 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073 +#endif + + + + + + + + +//some of these were needed. +//They were also not in the ones I could find on the web. +#define GL_COMBINE_ARB 0x8570 +#define GL_COMBINE_RGB_ARB 0x8571 +#define GL_COMBINE_ALPHA_ARB 0x8572 +#define GL_SOURCE0_RGB_ARB 0x8580 +#define GL_SOURCE1_RGB_ARB 0x8581 +#define GL_SOURCE2_RGB_ARB 0x8582 +#define GL_SOURCE0_ALPHA_ARB 0x8588 +#define GL_SOURCE1_ALPHA_ARB 0x8589 +#define GL_SOURCE2_ALPHA_ARB 0x858A +#define GL_OPERAND0_RGB_ARB 0x8590 +#define GL_OPERAND1_RGB_ARB 0x8591 +#define GL_OPERAND2_RGB_ARB 0x8592 +#define GL_OPERAND0_ALPHA_ARB 0x8598 +#define GL_OPERAND1_ALPHA_ARB 0x8599 +#define GL_OPERAND2_ALPHA_ARB 0x859A +#define GL_RGB_SCALE_ARB 0x8573 +#define GL_ADD_SIGNED_ARB 0x8574 +#define GL_INTERPOLATE_ARB 0x8575 +#define GL_SUBTRACT_ARB 0x84E7 +#define GL_CONSTANT_ARB 0x8576 +#define GL_PRIMARY_COLOR_ARB 0x8577 +#define GL_PREVIOUS_ARB 0x8578 +#define GL_DOT3_RGB_ARB 0x86AE +#define GL_DOT3_RGBA_ARB 0x86AF + + + +/* GL_ARB_texture_compression */ +#ifndef GL_ARB_texture_compression +#define GL_ARB_texture_compression 1 + +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 +#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB +#define GL_COMPRESSED_INTENSITY_ARB 0x84EC +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 +#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 + +typedef void (APIENTRY *PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data); +typedef void (APIENTRY *PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data); +typedef void (APIENTRY *PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid* data); +typedef void (APIENTRY *PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data); +typedef void (APIENTRY *PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data); +typedef void (APIENTRY *PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid* data); +typedef void (APIENTRY *PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, const GLvoid* img); + +#endif /* GL_ARB_texture_compression */ + + +#ifndef GL_ATI_pn_triangles //ati truform +#define GL_PN_TRIANGLES_ATI 0x87F0 +#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1 +#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2 +#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3 +#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4 +#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5 +#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6 +#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7 +#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8 + +typedef void (APIENTRY *PFNGLPNTRIANGLESIATIPROC)(GLenum pname, GLint param); +typedef void (APIENTRY *PFNGLPNTRIANGLESFATIPROC)(GLenum pname, GLfloat param); +#endif + + +#ifndef GL_EXT_stencil_two_side +#define GL_EXT_stencil_two_side 1 + +#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910 +#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911 + +typedef void (APIENTRY * PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face); +#endif + +#ifndef GL_EXT_stencil_wrap +#define GL_EXT_stencil_wrap 1 +#define GL_INCR_WRAP_EXT 0x8507 +#define GL_DECR_WRAP_EXT 0x8508 +#endif + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +#endif