From 0b35ceabdcf5bb4eeb0524a16df043f2aeea8ce9 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 29 Jan 2017 05:21:27 +0100 Subject: [PATCH] GL3: added missing source files and stubs of most functions still no 3D rendering, but in theory it should be able to load models, bsps etc, just not render them yet. also moved/copied md2.c and sp2.c to gl/ and gl3/ because they use renderer-specific types --- Makefile | 17 +- src/client/refresh/gl/header/local.h | 1 - src/client/refresh/gl/r_main.c | 2 + .../refresh/{files/md2.c => gl/r_md2.c} | 0 .../refresh/{files/sp2.c => gl/r_sp2.c} | 0 src/client/refresh/gl3/gl3_draw.c | 6 +- src/client/refresh/gl3/gl3_image.c | 1 + src/client/refresh/gl3/gl3_light.c | 699 ++++++++++ src/client/refresh/gl3/gl3_lightmap.c | 297 +++++ src/client/refresh/gl3/gl3_main.c | 374 ++++-- src/client/refresh/gl3/gl3_md2.c | 163 +++ src/client/refresh/gl3/gl3_mesh.c | 817 ++++++++++++ src/client/refresh/gl3/gl3_model.c | 882 ++++++++++++- src/client/refresh/gl3/gl3_sdl.c | 4 +- src/client/refresh/gl3/gl3_shaders.c | 39 +- src/client/refresh/gl3/gl3_sp2.c | 67 + src/client/refresh/gl3/gl3_surf.c | 1124 +++++++++++++++++ src/client/refresh/gl3/gl3_warp.c | 700 +++++++++- src/client/refresh/gl3/header/local.h | 99 +- src/client/refresh/ref_shared.h | 2 + 20 files changed, 5120 insertions(+), 174 deletions(-) rename src/client/refresh/{files/md2.c => gl/r_md2.c} (100%) rename src/client/refresh/{files/sp2.c => gl/r_sp2.c} (100%) create mode 100644 src/client/refresh/gl3/gl3_light.c create mode 100644 src/client/refresh/gl3/gl3_lightmap.c create mode 100644 src/client/refresh/gl3/gl3_md2.c create mode 100644 src/client/refresh/gl3/gl3_mesh.c create mode 100644 src/client/refresh/gl3/gl3_sp2.c create mode 100644 src/client/refresh/gl3/gl3_surf.c diff --git a/Makefile b/Makefile index 92db27e3..c223dc35 100755 --- a/Makefile +++ b/Makefile @@ -823,9 +823,9 @@ REFGL_OBJS_ := \ src/client/refresh/gl/r_surf.o \ src/client/refresh/gl/r_warp.o \ src/client/refresh/gl/r_sdl.o \ - src/client/refresh/files/md2.o \ + src/client/refresh/gl/r_md2.o \ + src/client/refresh/gl/r_sp2.o \ src/client/refresh/files/pcx.o \ - src/client/refresh/files/sp2.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ src/common/shared/flash.o \ @@ -845,12 +845,18 @@ endif REFGL3_OBJS_ := \ src/client/refresh/gl3/gl3_draw.o \ src/client/refresh/gl3/gl3_image.o \ + src/client/refresh/gl3/gl3_light.o \ + src/client/refresh/gl3/gl3_lightmap.o \ src/client/refresh/gl3/gl3_main.o \ + src/client/refresh/gl3/gl3_mesh.o \ src/client/refresh/gl3/gl3_misc.o \ src/client/refresh/gl3/gl3_model.o \ src/client/refresh/gl3/gl3_sdl.o \ + src/client/refresh/gl3/gl3_surf.o \ src/client/refresh/gl3/gl3_warp.o \ src/client/refresh/gl3/gl3_shaders.o \ + src/client/refresh/gl3/gl3_md2.o \ + src/client/refresh/gl3/gl3_sp2.o \ src/client/refresh/files/pcx.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ @@ -858,13 +864,6 @@ REFGL3_OBJS_ := \ src/common/shared/rand.o \ src/common/shared/shared.o -# TODO: filetype support for gl3 renderer - can we reuse same code? -REFGL3_TODO_ := \ - src/client/refresh/files/md2.o \ - src/client/refresh/files/pcx.o \ - src/client/refresh/files/sp2.o - - # TODO: glad_dbg support REFGL3_OBJS_ += \ src/client/refresh/gl3/glad/src/glad.o diff --git a/src/client/refresh/gl/header/local.h b/src/client/refresh/gl/header/local.h index 1e6bc501..09bedc8d 100644 --- a/src/client/refresh/gl/header/local.h +++ b/src/client/refresh/gl/header/local.h @@ -65,7 +65,6 @@ #define BLOCK_WIDTH 128 #define BLOCK_HEIGHT 128 #define REF_VERSION "Yamagi Quake II OpenGL Refresher" -#define MAX_LBM_HEIGHT 480 #define BACKFACE_EPSILON 0.01 #define LIGHTMAP_BYTES 4 #define MAX_LIGHTMAPS 128 diff --git a/src/client/refresh/gl/r_main.c b/src/client/refresh/gl/r_main.c index b4aac33f..3150c115 100644 --- a/src/client/refresh/gl/r_main.c +++ b/src/client/refresh/gl/r_main.c @@ -1627,6 +1627,8 @@ RI_BeginFrame(float camera_separation) /* go into 2D mode */ + // FIXME: just call R_SetGL2D(); + int x, w, y, h; qboolean drawing_left_eye = gl_state.camera_separation < 0; qboolean stereo_split_tb = ((gl_state.stereo_mode == STEREO_SPLIT_VERTICAL) && gl_state.camera_separation); diff --git a/src/client/refresh/files/md2.c b/src/client/refresh/gl/r_md2.c similarity index 100% rename from src/client/refresh/files/md2.c rename to src/client/refresh/gl/r_md2.c diff --git a/src/client/refresh/files/sp2.c b/src/client/refresh/gl/r_sp2.c similarity index 100% rename from src/client/refresh/files/sp2.c rename to src/client/refresh/gl/r_sp2.c diff --git a/src/client/refresh/gl3/gl3_draw.c b/src/client/refresh/gl3/gl3_draw.c index 73513004..5e3c47c6 100644 --- a/src/client/refresh/gl3/gl3_draw.c +++ b/src/client/refresh/gl3/gl3_draw.c @@ -300,11 +300,7 @@ GL3_Draw_FadeScreen(void) void GL3_Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte *data) { - byte *source; - float hscale = 1.0f; - int frac, fracstep; - int i, j, trows; - int row; + int i, j; GL3_Bind(0); diff --git a/src/client/refresh/gl3/gl3_image.c b/src/client/refresh/gl3/gl3_image.c index 65030680..506ecb0b 100644 --- a/src/client/refresh/gl3/gl3_image.c +++ b/src/client/refresh/gl3/gl3_image.c @@ -88,6 +88,7 @@ GL3_TextureMode(char *string) ri.Cvar_SetValue("gl_anisotropic", 0.0); } + STUB_ONCE("TODO: fix existing textures' modes!"); #if 0 // TODO! gl3image_t *glt; diff --git a/src/client/refresh/gl3/gl3_light.c b/src/client/refresh/gl3/gl3_light.c new file mode 100644 index 00000000..7ba55d1e --- /dev/null +++ b/src/client/refresh/gl3/gl3_light.c @@ -0,0 +1,699 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * ======================================================================= + * + * Lightmaps and dynamic lighting + * + * ======================================================================= + */ + +#include "header/local.h" + +#define DLIGHT_CUTOFF 64 + +static int r_dlightframecount; +vec3_t pointcolor; +cplane_t *lightplane; /* used as shadow plane */ +vec3_t lightspot; +static float s_blocklights[34 * 34 * 3]; + +static void +RenderDlight(dlight_t *light) +{ + STUB_ONCE("TODO: Implement!"); + +#if 0 + int i, j; + float a; + float rad; + + rad = light->intensity * 0.35; + + GLfloat vtx[3*18]; + GLfloat clr[4*18]; + + unsigned int index_vtx = 4; + unsigned int index_clr = 0; + + glEnableClientState( GL_VERTEX_ARRAY ); + glEnableClientState( GL_COLOR_ARRAY ); + + clr[index_clr++] = light->color [ 0 ] * 0.2; + clr[index_clr++] = light->color [ 1 ] * 0.2; + clr[index_clr++] = light->color [ 2 ] * 0.2; + clr[index_clr++] = 1; + + for ( i = 0; i < 3; i++ ) + { + vtx [ i ] = light->origin [ i ] - vpn [ i ] * rad; + } + + for ( i = 16; i >= 0; i-- ) + { + clr[index_clr++] = 0; + clr[index_clr++] = 0; + clr[index_clr++] = 0; + clr[index_clr++] = 1; + + a = i / 16.0 * M_PI * 2; + + for ( j = 0; j < 3; j++ ) + { + vtx[index_vtx++] = light->origin [ j ] + vright [ j ] * cos( a ) * rad + + vup [ j ] * sin( a ) * rad; + } + } + + glVertexPointer( 3, GL_FLOAT, 0, vtx ); + glColorPointer( 4, GL_FLOAT, 0, clr ); + glDrawArrays( GL_TRIANGLE_FAN, 0, 18 ); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_COLOR_ARRAY ); +#endif // 0 +} + +void +GL3_RenderDlights(void) +{ + STUB_ONCE("TODO: Implement!"); + +#if 0 + int i; + dlight_t *l; + + if (!gl_flashblend->value) + { + return; + } + + /* because the count hasn't advanced yet for this frame */ + r_dlightframecount = r_framecount + 1; + + glDepthMask(0); + glDisable(GL_TEXTURE_2D); + glShadeModel(GL_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + + l = r_newrefdef.dlights; + + for (i = 0; i < r_newrefdef.num_dlights; i++, l++) + { + RenderDlight(l); + } + + glColor4f(1, 1, 1, 1); + glDisable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(1); +#endif // 0 +} + +void +GL3_MarkLights(dlight_t *light, int bit, mnode_t *node) +{ + cplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + int sidebit; + + if (node->contents != -1) + { + return; + } + + splitplane = node->plane; + dist = DotProduct(light->origin, splitplane->normal) - splitplane->dist; + + if (dist > light->intensity - DLIGHT_CUTOFF) + { + GL3_MarkLights(light, bit, node->children[0]); + return; + } + + if (dist < -light->intensity + DLIGHT_CUTOFF) + { + GL3_MarkLights(light, bit, node->children[1]); + return; + } + + /* mark the polygons */ + surf = gl3_worldmodel->surfaces + node->firstsurface; + + for (i = 0; i < node->numsurfaces; i++, surf++) + { + dist = DotProduct(light->origin, surf->plane->normal) - surf->plane->dist; + + if (dist >= 0) + { + sidebit = 0; + } + else + { + sidebit = SURF_PLANEBACK; + } + + if ((surf->flags & SURF_PLANEBACK) != sidebit) + { + continue; + } + + if (surf->dlightframe != r_dlightframecount) + { + surf->dlightbits = 0; + surf->dlightframe = r_dlightframecount; + } + + surf->dlightbits |= bit; + } + + GL3_MarkLights(light, bit, node->children[0]); + GL3_MarkLights(light, bit, node->children[1]); +} + +void +GL3_PushDlights(void) +{ + int i; + dlight_t *l; + + if (gl_flashblend->value) + { + return; + } + + /* because the count hasn't advanced yet for this frame */ + r_dlightframecount = gl3_framecount + 1; + + l = gl3_newrefdef.dlights; + + for (i = 0; i < gl3_newrefdef.num_dlights; i++, l++) + { + GL3_MarkLights(l, 1 << i, gl3_worldmodel->nodes); + } +} + +static int +RecursiveLightPoint(mnode_t *node, vec3_t start, vec3_t end) +{ + float front, back, frac; + int side; + cplane_t *plane; + vec3_t mid; + msurface_t *surf; + int s, t, ds, dt; + int i; + mtexinfo_t *tex; + byte *lightmap; + int maps; + int r; + + if (node->contents != -1) + { + return -1; /* didn't hit anything */ + } + + /* calculate mid point */ + 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 RecursiveLightPoint(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 = RecursiveLightPoint(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 = gl3_worldmodel->surfaces + node->firstsurface; + + for (i = 0; i < node->numsurfaces; i++, surf++) + { + if (surf->flags & (SURF_DRAWTURB | SURF_DRAWSKY)) + { + 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; + VectorCopy(vec3_origin, pointcolor); + + if (lightmap) + { + vec3_t scale; + + lightmap += 3 * (dt * ((surf->extents[0] >> 4) + 1) + ds); + + for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; + maps++) + { + for (i = 0; i < 3; i++) + { + scale[i] = gl_modulate->value * + gl3_newrefdef.lightstyles[surf->styles[maps]].rgb[i]; + } + + pointcolor[0] += lightmap[0] * scale[0] * (1.0 / 255); + pointcolor[1] += lightmap[1] * scale[1] * (1.0 / 255); + pointcolor[2] += lightmap[2] * scale[2] * (1.0 / 255); + lightmap += 3 * ((surf->extents[0] >> 4) + 1) * + ((surf->extents[1] >> 4) + 1); + } + } + + return 1; + } + + /* go down back side */ + return RecursiveLightPoint(node->children[!side], mid, end); +} + +void +GL3_LightPoint(vec3_t p, vec3_t color) +{ + vec3_t end; + float r; + int lnum; + dlight_t *dl; + vec3_t dist; + float add; + + if (!gl3_worldmodel->lightdata) + { + color[0] = color[1] = color[2] = 1.0; + return; + } + + end[0] = p[0]; + end[1] = p[1]; + end[2] = p[2] - 2048; + + r = RecursiveLightPoint(gl3_worldmodel->nodes, p, end); + + if (r == -1) + { + VectorCopy(vec3_origin, color); + } + else + { + VectorCopy(pointcolor, color); + } + + /* add dynamic lights */ + dl = gl3_newrefdef.dlights; + + for (lnum = 0; lnum < gl3_newrefdef.num_dlights; lnum++, dl++) + { + VectorSubtract(currententity->origin, + dl->origin, dist); + add = dl->intensity - VectorLength(dist); + add *= (1.0 / 256); + + if (add > 0) + { + VectorMA(color, add, dl->color, color); + } + } + + VectorScale(color, gl_modulate->value, color); +} + +static void +AddDynamicLights(msurface_t *surf) +{ + int lnum; + int sd, td; + float fdist, frad, fminlight; + vec3_t impact, local; + int s, t; + int i; + int smax, tmax; + mtexinfo_t *tex; + dlight_t *dl; + float *pfBL; + float fsacc, ftacc; + + smax = (surf->extents[0] >> 4) + 1; + tmax = (surf->extents[1] >> 4) + 1; + tex = surf->texinfo; + + for (lnum = 0; lnum < gl3_newrefdef.num_dlights; lnum++) + { + if (!(surf->dlightbits & (1 << lnum))) + { + continue; /* not lit by this light */ + } + + dl = &gl3_newrefdef.dlights[lnum]; + frad = dl->intensity; + fdist = DotProduct(dl->origin, surf->plane->normal) - + surf->plane->dist; + frad -= fabs(fdist); + + /* rad is now the highest intensity on the plane */ + fminlight = DLIGHT_CUTOFF; + + if (frad < fminlight) + { + continue; + } + + fminlight = frad - fminlight; + + for (i = 0; i < 3; i++) + { + impact[i] = dl->origin[i] - + surf->plane->normal[i] * fdist; + } + + local[0] = DotProduct(impact, + tex->vecs[0]) + tex->vecs[0][3] - surf->texturemins[0]; + local[1] = DotProduct(impact, + tex->vecs[1]) + tex->vecs[1][3] - surf->texturemins[1]; + + pfBL = s_blocklights; + + for (t = 0, ftacc = 0; t < tmax; t++, ftacc += 16) + { + td = local[1] - ftacc; + + if (td < 0) + { + td = -td; + } + + for (s = 0, fsacc = 0; s < smax; s++, fsacc += 16, pfBL += 3) + { + sd = Q_ftol(local[0] - fsacc); + + if (sd < 0) + { + sd = -sd; + } + + if (sd > td) + { + fdist = sd + (td >> 1); + } + else + { + fdist = td + (sd >> 1); + } + + if (fdist < fminlight) + { + pfBL[0] += (frad - fdist) * dl->color[0]; + pfBL[1] += (frad - fdist) * dl->color[1]; + pfBL[2] += (frad - fdist) * dl->color[2]; + } + } + } + } +} + +void +GL3_SetCacheState(msurface_t *surf) +{ + int maps; + + for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) + { + surf->cached_light[maps] = + gl3_newrefdef.lightstyles[surf->styles[maps]].white; + } +} + +/* + * Combine and scale multiple lightmaps into the floating format in blocklights + */ +void +GL3_BuildLightMap(msurface_t *surf, byte *dest, int stride) +{ + int smax, tmax; + int r, g, b, a, max; + int i, j, size; + byte *lightmap; + float scale[4]; + int nummaps; + float *bl; + + if (surf->texinfo->flags & + (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)) + { + ri.Sys_Error(ERR_DROP, "R_BuildLightMap called for non-lit surface"); + } + + smax = (surf->extents[0] >> 4) + 1; + tmax = (surf->extents[1] >> 4) + 1; + size = smax * tmax; + + if (size > (sizeof(s_blocklights) >> 4)) + { + ri.Sys_Error(ERR_DROP, "Bad s_blocklights size"); + } + + /* set to full bright if no light data */ + if (!surf->samples) + { + for (i = 0; i < size * 3; i++) + { + s_blocklights[i] = 255; + } + + goto store; + } + + /* count the # of maps */ + for (nummaps = 0; nummaps < MAXLIGHTMAPS && surf->styles[nummaps] != 255; + nummaps++) + { + } + + lightmap = surf->samples; + + /* add all the lightmaps */ + if (nummaps == 1) + { + int maps; + + for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) + { + bl = s_blocklights; + + for (i = 0; i < 3; i++) + { + scale[i] = gl_modulate->value * + gl3_newrefdef.lightstyles[surf->styles[maps]].rgb[i]; + } + + if ((scale[0] == 1.0F) && + (scale[1] == 1.0F) && + (scale[2] == 1.0F)) + { + for (i = 0; i < size; i++, bl += 3) + { + bl[0] = lightmap[i * 3 + 0]; + bl[1] = lightmap[i * 3 + 1]; + bl[2] = lightmap[i * 3 + 2]; + } + } + else + { + for (i = 0; i < size; i++, bl += 3) + { + bl[0] = lightmap[i * 3 + 0] * scale[0]; + bl[1] = lightmap[i * 3 + 1] * scale[1]; + bl[2] = lightmap[i * 3 + 2] * scale[2]; + } + } + + lightmap += size * 3; /* skip to next lightmap */ + } + } + else + { + int maps; + + memset(s_blocklights, 0, sizeof(s_blocklights[0]) * size * 3); + + for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) + { + bl = s_blocklights; + + for (i = 0; i < 3; i++) + { + scale[i] = gl_modulate->value * + gl3_newrefdef.lightstyles[surf->styles[maps]].rgb[i]; + } + + if ((scale[0] == 1.0F) && + (scale[1] == 1.0F) && + (scale[2] == 1.0F)) + { + for (i = 0; i < size; i++, bl += 3) + { + bl[0] += lightmap[i * 3 + 0]; + bl[1] += lightmap[i * 3 + 1]; + bl[2] += lightmap[i * 3 + 2]; + } + } + else + { + for (i = 0; i < size; i++, bl += 3) + { + bl[0] += lightmap[i * 3 + 0] * scale[0]; + bl[1] += lightmap[i * 3 + 1] * scale[1]; + bl[2] += lightmap[i * 3 + 2] * scale[2]; + } + } + + lightmap += size * 3; /* skip to next lightmap */ + } + } + + /* add all the dynamic lights */ + if (surf->dlightframe == gl3_framecount) + { + AddDynamicLights(surf); + } + +store: + + stride -= (smax << 2); + bl = s_blocklights; + + for (i = 0; i < tmax; i++, dest += stride) + { + for (j = 0; j < smax; j++) + { + r = Q_ftol(bl[0]); + g = Q_ftol(bl[1]); + b = Q_ftol(bl[2]); + + /* catch negative lights */ + if (r < 0) + { + r = 0; + } + + if (g < 0) + { + g = 0; + } + + if (b < 0) + { + b = 0; + } + + /* determine the brightest of the three color components */ + if (r > g) + { + max = r; + } + else + { + max = g; + } + + if (b > max) + { + max = b; + } + + /* alpha is ONLY used for the mono lightmap case. For this + reason we set it to the brightest of the color components + so that things don't get too dim. */ + a = max; + + /* rescale all the color components if the + intensity of the greatest channel exceeds + 1.0 */ + if (max > 255) + { + float t = 255.0F / max; + + r = r * t; + g = g * t; + b = b * t; + a = a * t; + } + + dest[0] = r; + dest[1] = g; + dest[2] = b; + dest[3] = a; + + bl += 3; + dest += 4; + } + } +} + diff --git a/src/client/refresh/gl3/gl3_lightmap.c b/src/client/refresh/gl3/gl3_lightmap.c new file mode 100644 index 00000000..d7bdd91e --- /dev/null +++ b/src/client/refresh/gl3/gl3_lightmap.c @@ -0,0 +1,297 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * ======================================================================= + * + * Lightmap handling + * + * ======================================================================= + */ + +#include "header/local.h" + + +#define TEXNUM_LIGHTMAPS 1024 + +extern gl3lightmapstate_t gl3_lms; + +void +GL3_LM_InitBlock(void) +{ + memset(gl3_lms.allocated, 0, sizeof(gl3_lms.allocated)); +} + +void +GL3_LM_UploadBlock(qboolean dynamic) +{ + STUB_ONCE("TODO: Implement!"); +#if 0 + int texture; + int height = 0; + + if (dynamic) + { + texture = 0; + } + else + { + texture = gl3_lms.current_lightmap_texture; + } + + GL3_Bind(gl3state.lightmap_textures + texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (dynamic) + { + int i; + + for (i = 0; i < BLOCK_WIDTH; i++) + { + if (gl3_lms.allocated[i] > height) + { + height = gl3_lms.allocated[i]; + } + } + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, BLOCK_WIDTH, + height, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, + gl3_lms.lightmap_buffer); + } + else + { + gl3_lms.internal_format = GL_LIGHTMAP_FORMAT; + glTexImage2D(GL_TEXTURE_2D, 0, gl3_lms.internal_format, + BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, + GL_UNSIGNED_BYTE, gl3_lms.lightmap_buffer); + + if (++gl3_lms.current_lightmap_texture == MAX_LIGHTMAPS) + { + ri.Sys_Error(ERR_DROP, + "LM_UploadBlock() - MAX_LIGHTMAPS exceeded\n"); + } + } +#endif // 0 +} + +/* + * returns a texture number and the position inside it + */ +qboolean +GL3_LM_AllocBlock(int w, int h, int *x, int *y) +{ + int i, j; + int best, best2; + + best = BLOCK_HEIGHT; + + for (i = 0; i < BLOCK_WIDTH - w; i++) + { + best2 = 0; + + for (j = 0; j < w; j++) + { + if (gl3_lms.allocated[i + j] >= best) + { + break; + } + + if (gl3_lms.allocated[i + j] > best2) + { + best2 = gl3_lms.allocated[i + j]; + } + } + + if (j == w) + { + /* this is a valid spot */ + *x = i; + *y = best = best2; + } + } + + if (best + h > BLOCK_HEIGHT) + { + return false; + } + + for (i = 0; i < w; i++) + { + gl3_lms.allocated[*x + i] = best + h; + } + + return true; +} + +void +GL3_LM_BuildPolygonFromSurface(msurface_t *fa) +{ + int i, lindex, lnumverts; + medge_t *pedges, *r_pedge; + float *vec; + float s, t; + glpoly_t *poly; + vec3_t total; + + /* reconstruct the polygon */ + pedges = currentmodel->edges; + lnumverts = fa->numedges; + + VectorClear(total); + + /* draw texture */ + poly = Hunk_Alloc(sizeof(glpoly_t) + + (lnumverts - 4) * VERTEXSIZE * sizeof(float)); + poly->next = fa->polys; + poly->flags = fa->flags; + fa->polys = poly; + poly->numverts = lnumverts; + + for (i = 0; i < lnumverts; i++) + { + lindex = currentmodel->surfedges[fa->firstedge + i]; + + if (lindex > 0) + { + r_pedge = &pedges[lindex]; + vec = currentmodel->vertexes[r_pedge->v[0]].position; + } + else + { + r_pedge = &pedges[-lindex]; + vec = currentmodel->vertexes[r_pedge->v[1]].position; + } + + s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; + s /= fa->texinfo->image->width; + + t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; + t /= fa->texinfo->image->height; + + VectorAdd(total, vec, total); + 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 /= BLOCK_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 /= BLOCK_HEIGHT * 16; /* fa->texinfo->texture->height; */ + + poly->verts[i][5] = s; + poly->verts[i][6] = t; + } + + poly->numverts = lnumverts; +} + +void +GL3_LM_CreateSurfaceLightmap(msurface_t *surf) +{ + int smax, tmax; + byte *base; + + if (surf->flags & (SURF_DRAWSKY | SURF_DRAWTURB)) + { + return; + } + + smax = (surf->extents[0] >> 4) + 1; + tmax = (surf->extents[1] >> 4) + 1; + + if (!GL3_LM_AllocBlock(smax, tmax, &surf->light_s, &surf->light_t)) + { + GL3_LM_UploadBlock(false); + GL3_LM_InitBlock(); + + if (!GL3_LM_AllocBlock(smax, tmax, &surf->light_s, &surf->light_t)) + { + ri.Sys_Error(ERR_FATAL, "Consecutive calls to LM_AllocBlock(%d,%d) failed\n", + smax, tmax); + } + } + + surf->lightmaptexturenum = gl3_lms.current_lightmap_texture; + + base = gl3_lms.lightmap_buffer; + base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * LIGHTMAP_BYTES; + + GL3_SetCacheState(surf); + GL3_BuildLightMap(surf, base, BLOCK_WIDTH * LIGHTMAP_BYTES); +} + +void +GL3_LM_BeginBuildingLightmaps(gl3model_t *m) +{ + + static lightstyle_t lightstyles[MAX_LIGHTSTYLES]; + int i; + unsigned dummy[128 * 128]; + + memset(gl3_lms.allocated, 0, sizeof(gl3_lms.allocated)); + + gl3_framecount = 1; /* no dlightcache */ + + /* setup the base lightstyles so the lightmaps + won't have to be regenerated the first time + they're seen */ + for (i = 0; i < MAX_LIGHTSTYLES; i++) + { + lightstyles[i].rgb[0] = 1; + lightstyles[i].rgb[1] = 1; + lightstyles[i].rgb[2] = 1; + lightstyles[i].white = 3; + } + + gl3_newrefdef.lightstyles = lightstyles; + + STUB_ONCE("TODO: IMPLEMENT!"); +#if 0 + if (!gl_state.lightmap_textures) + { + gl3state.lightmap_textures = TEXNUM_LIGHTMAPS; + } + + gl3_lms.current_lightmap_texture = 1; + gl3_lms.internal_format = GL_LIGHTMAP_FORMAT; + + /* initialize the dynamic lightmap texture */ + GL3_Bind(gl_state.lightmap_textures + 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, gl3_lms.internal_format, + BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, + GL_UNSIGNED_BYTE, dummy); +#endif // 0 +} + +void +GL3_LM_EndBuildingLightmaps(void) +{ + GL3_LM_UploadBlock(false); +} + diff --git a/src/client/refresh/gl3/gl3_main.c b/src/client/refresh/gl3/gl3_main.c index 0d6ca801..763c4d39 100644 --- a/src/client/refresh/gl3/gl3_main.c +++ b/src/client/refresh/gl3/gl3_main.c @@ -45,6 +45,24 @@ unsigned gl3_rawpalette[256]; refdef_t gl3_newrefdef; viddef_t vid; +gl3model_t *gl3_worldmodel; +gl3model_t *currentmodel; +entity_t *currententity; + +float gl3depthmin=0.0f, gl3depthmax=1.0f; + +cplane_t frustum[4]; + +/* view origin */ +vec3_t vup; +vec3_t vpn; +vec3_t vright; +vec3_t gl3_origin; + +int gl3_visframecount; /* bumped when going to a new PVS */ +int gl3_framecount; /* used for dlight push checking */ + +int c_brush_polys, c_alias_polys; int gl3_viewcluster, gl3_viewcluster2, gl3_oldviewcluster, gl3_oldviewcluster2; @@ -57,15 +75,48 @@ cvar_t *gl_customwidth; cvar_t *gl_customheight; cvar_t *vid_gamma; cvar_t *gl_anisotropic; +cvar_t *gl_texturemode; +cvar_t *gl_drawbuffer; +cvar_t *gl_clear; + +cvar_t *gl_lefthand; +cvar_t *gl_farsee; cvar_t *intensity; +cvar_t *gl_lightlevel; +cvar_t *gl_overbrightbits; cvar_t *gl_norefresh; cvar_t *gl_nolerp_list; cvar_t *gl_nobind; +cvar_t *gl_lockpvs; +cvar_t *gl_novis; + +cvar_t *gl_cull; +cvar_t *gl_zfix; +cvar_t *gl_fullbright; +cvar_t *gl_flashblend; +cvar_t *gl_modulate; +cvar_t *gl_lightmap; +cvar_t *gl_shadows; // TODO: do we really need 2 cvars for shadows here? +cvar_t *gl_stencilshadow; cvar_t *gl3_debugcontext; +void +GL3_RotateForEntity(entity_t *e) +{ + STUB_ONCE("TODO: Implement for OpenGL3!"); +#if 0 + glTranslatef(e->origin[0], e->origin[1], e->origin[2]); + + glRotatef(e->angles[1], 0, 0, 1); + glRotatef(-e->angles[0], 0, 1, 0); + glRotatef(-e->angles[2], 1, 0, 0); +#endif // 0 +} + + static void GL3_Strings(void) { @@ -88,6 +139,10 @@ GL3_Strings(void) static void GL3_Register(void) { + gl_lefthand = ri.Cvar_Get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE); + gl_farsee = ri.Cvar_Get("gl_farsee", "0", CVAR_LATCH | CVAR_ARCHIVE); + + gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0); gl_swapinterval = ri.Cvar_Get("gl_swapinterval", "1", CVAR_ARCHIVE); gl_msaa_samples = ri.Cvar_Get ( "gl_msaa_samples", "0", CVAR_ARCHIVE ); gl_retexturing = ri.Cvar_Get("gl_retexturing", "1", CVAR_ARCHIVE); @@ -97,27 +152,44 @@ GL3_Register(void) gl_customheight = ri.Cvar_Get("gl_customheight", "768", CVAR_ARCHIVE); gl_norefresh = ri.Cvar_Get("gl_norefresh", "0", 0); + gl_fullbright = ri.Cvar_Get("gl_fullbright", "0", 0); /* don't bilerp characters and crosshairs */ gl_nolerp_list = ri.Cvar_Get("gl_nolerp_list", "pics/conchars.pcx pics/ch1.pcx pics/ch2.pcx pics/ch3.pcx", 0); gl_nobind = ri.Cvar_Get("gl_nobind", "0", 0); + gl_texturemode = ri.Cvar_Get("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE); gl_anisotropic = ri.Cvar_Get("gl_anisotropic", "0", CVAR_ARCHIVE); vid_fullscreen = ri.Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); vid_gamma = ri.Cvar_Get("vid_gamma", "1.0", CVAR_ARCHIVE); intensity = ri.Cvar_Get("intensity", "1.0", CVAR_ARCHIVE); + gl_lightlevel = ri.Cvar_Get("gl_lightlevel", "0", 0); + gl_overbrightbits = ri.Cvar_Get("gl_overbrightbits", "0", CVAR_ARCHIVE); + + gl_lightmap = ri.Cvar_Get("gl_lightmap", "0", 0); + gl_shadows = ri.Cvar_Get("gl_shadows", "0", CVAR_ARCHIVE); + gl_stencilshadow = ri.Cvar_Get("gl_stencilshadow", "0", CVAR_ARCHIVE); + + gl_flashblend = ri.Cvar_Get("gl_flashblend", "0", 0); + gl_modulate = ri.Cvar_Get("gl_modulate", "1", CVAR_ARCHIVE); + gl_zfix = ri.Cvar_Get("gl_zfix", "0", 0); + gl_clear = ri.Cvar_Get("gl_clear", "0", 0); + gl_cull = ri.Cvar_Get("gl_cull", "1", 0); + gl_lockpvs = ri.Cvar_Get("gl_lockpvs", "0", 0); + gl_novis = ri.Cvar_Get("gl_novis", "0", 0); + #if 0 // TODO! - gl_lefthand = ri.Cvar_Get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE); - gl_farsee = ri.Cvar_Get("gl_farsee", "0", CVAR_LATCH | CVAR_ARCHIVE); + //gl_lefthand = ri.Cvar_Get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE); + //gl_farsee = ri.Cvar_Get("gl_farsee", "0", CVAR_LATCH | CVAR_ARCHIVE); //gl_norefresh = ri.Cvar_Get("gl_norefresh", "0", 0); - gl_fullbright = ri.Cvar_Get("gl_fullbright", "0", 0); + //gl_fullbright = ri.Cvar_Get("gl_fullbright", "0", 0); gl_drawentities = ri.Cvar_Get("gl_drawentities", "1", 0); gl_drawworld = ri.Cvar_Get("gl_drawworld", "1", 0); - gl_novis = ri.Cvar_Get("gl_novis", "0", 0); - gl_lerpmodels = ri.Cvar_Get("gl_lerpmodels", "1", 0); + //gl_novis = ri.Cvar_Get("gl_novis", "0", 0); + //gl_lerpmodels = ri.Cvar_Get("gl_lerpmodels", "1", 0); NOTE: screw this, it looks horrible without gl_speeds = ri.Cvar_Get("gl_speeds", "0", 0); gl_lightlevel = ri.Cvar_Get("gl_lightlevel", "0", 0); @@ -130,34 +202,34 @@ GL3_Register(void) gl_particle_att_b = ri.Cvar_Get("gl_particle_att_b", "0.0", CVAR_ARCHIVE); gl_particle_att_c = ri.Cvar_Get("gl_particle_att_c", "0.01", CVAR_ARCHIVE); - gl_modulate = ri.Cvar_Get("gl_modulate", "1", CVAR_ARCHIVE); + //gl_modulate = ri.Cvar_Get("gl_modulate", "1", CVAR_ARCHIVE); //gl_mode = ri.Cvar_Get("gl_mode", "4", CVAR_ARCHIVE); - gl_lightmap = ri.Cvar_Get("gl_lightmap", "0", 0); - gl_shadows = ri.Cvar_Get("gl_shadows", "0", CVAR_ARCHIVE); - gl_stencilshadow = ri.Cvar_Get("gl_stencilshadow", "0", CVAR_ARCHIVE); + //gl_lightmap = ri.Cvar_Get("gl_lightmap", "0", 0); + //gl_shadows = ri.Cvar_Get("gl_shadows", "0", CVAR_ARCHIVE); + //gl_stencilshadow = ri.Cvar_Get("gl_stencilshadow", "0", CVAR_ARCHIVE); gl_dynamic = ri.Cvar_Get("gl_dynamic", "1", 0); //gl_nobind = ri.Cvar_Get("gl_nobind", "0", 0); gl_round_down = ri.Cvar_Get("gl_round_down", "1", 0); gl_picmip = ri.Cvar_Get("gl_picmip", "0", 0); gl_showtris = ri.Cvar_Get("gl_showtris", "0", 0); - gl_ztrick = ri.Cvar_Get("gl_ztrick", "0", 0); - gl_zfix = ri.Cvar_Get("gl_zfix", "0", 0); + //gl_ztrick = ri.Cvar_Get("gl_ztrick", "0", 0); NOTE: dump this. + //gl_zfix = ri.Cvar_Get("gl_zfix", "0", 0); gl_finish = ri.Cvar_Get("gl_finish", "0", CVAR_ARCHIVE); gl_clear = ri.Cvar_Get("gl_clear", "0", 0); - gl_cull = ri.Cvar_Get("gl_cull", "1", 0); +// gl_cull = ri.Cvar_Get("gl_cull", "1", 0); gl_polyblend = ri.Cvar_Get("gl_polyblend", "1", 0); - gl_flashblend = ri.Cvar_Get("gl_flashblend", "0", 0); + //gl_flashblend = ri.Cvar_Get("gl_flashblend", "0", 0); - gl_texturemode = ri.Cvar_Get("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE); + //gl_texturemode = ri.Cvar_Get("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE); gl_texturealphamode = ri.Cvar_Get("gl_texturealphamode", "default", CVAR_ARCHIVE); gl_texturesolidmode = ri.Cvar_Get("gl_texturesolidmode", "default", CVAR_ARCHIVE); //gl_anisotropic = ri.Cvar_Get("gl_anisotropic", "0", CVAR_ARCHIVE); - gl_lockpvs = ri.Cvar_Get("gl_lockpvs", "0", 0); + //gl_lockpvs = ri.Cvar_Get("gl_lockpvs", "0", 0); - gl_palettedtexture = ri.Cvar_Get("gl_palettedtexture", "0", CVAR_ARCHIVE); + //gl_palettedtexture = ri.Cvar_Get("gl_palettedtexture", "0", CVAR_ARCHIVE); NOPE. gl_pointparameters = ri.Cvar_Get("gl_pointparameters", "1", CVAR_ARCHIVE); - gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0); + //gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0); //gl_swapinterval = ri.Cvar_Get("gl_swapinterval", "1", CVAR_ARCHIVE); gl_saturatelighting = ri.Cvar_Get("gl_saturatelighting", "0", 0); @@ -285,13 +357,12 @@ GL3_SetMode(void) static qboolean GL3_Init(void) { - /* TODO! int j; - extern float r_turbsin[256]; + extern float gl3_turbsin[256]; for (j = 0; j < 256; j++) { - r_turbsin[j] *= 0.5; - }*/ + gl3_turbsin[j] *= 0.5; + } Swap_Init(); // FIXME: for fucks sake, this doesn't have to be done at runtime! @@ -430,23 +501,125 @@ GL3_Shutdown(void) GL3_ShutdownWindow(false); } +static void +GL3_DrawEntitiesOnList(void) +{ + STUB_ONCE("TODO: Implement!"); +#if 0 + int i; +/* + if (!gl_drawentities->value) + { + return; + } +*/ + + /* draw non-transparent first */ + for (i = 0; i < gl3_newrefdef.num_entities; i++) + { + currententity = &gl3_newrefdef.entities[i]; + + if (currententity->flags & RF_TRANSLUCENT) + { + continue; /* solid */ + } + + if (currententity->flags & RF_BEAM) + { + R_DrawBeam(currententity); + } + else + { + currentmodel = currententity->model; + + if (!currentmodel) + { + R_DrawNullModel(); + continue; + } + + switch (currentmodel->type) + { + case mod_alias: + GL3_DrawAliasModel(currententity); + break; + case mod_brush: + R_DrawBrushModel(currententity); + break; + case mod_sprite: + R_DrawSpriteModel(currententity); + break; + default: + ri.Sys_Error(ERR_DROP, "Bad modeltype"); + break; + } + } + } + + /* draw transparent entities + we could sort these if it ever + becomes a problem... */ + glDepthMask(0); + + for (i = 0; i < gl3_newrefdef.num_entities; i++) + { + currententity = &gl3_newrefdef.entities[i]; + + if (!(currententity->flags & RF_TRANSLUCENT)) + { + continue; /* solid */ + } + + if (currententity->flags & RF_BEAM) + { + R_DrawBeam(currententity); + } + else + { + currentmodel = currententity->model; + + if (!currentmodel) + { + R_DrawNullModel(); + continue; + } + + switch (currentmodel->type) + { + case mod_alias: + R_DrawAliasModel(currententity); + break; + case mod_brush: + R_DrawBrushModel(currententity); + break; + case mod_sprite: + R_DrawSpriteModel(currententity); + break; + default: + ri.Sys_Error(ERR_DROP, "Bad modeltype"); + break; + } + } + } + + glDepthMask(1); /* back to writing */ +#endif // 0 +} + static void GL3_SetGL2D(void) { - int x, w, y, h; + int x = 0; + int w = vid.width; + int y = 0; + int h = vid.height; + #if 0 // TODO: stereo /* set 2D virtual screen size */ qboolean drawing_left_eye = gl_state.camera_separation < 0; qboolean stereo_split_tb = ((gl_state.stereo_mode == STEREO_SPLIT_VERTICAL) && gl_state.camera_separation); qboolean stereo_split_lr = ((gl_state.stereo_mode == STEREO_SPLIT_HORIZONTAL) && gl_state.camera_separation); -#endif // 0 - x = 0; - w = vid.width; - y = 0; - h = vid.height; - -#if 0 // TODO: stereo if(stereo_split_lr) { w = w / 2; x = drawing_left_eye ? 0 : w; @@ -467,23 +640,14 @@ GL3_SetGL2D(void) glUseProgram(gl3state.si2D.shaderProgram); glUniformMatrix4fv(gl3state.si2D.uniTransMatrix , 1, GL_FALSE, transMatr.Elements[0]); - // FIXME: change to GL3 code! -#if 0 - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, vid.width, vid.height, 0, -99999, 99999); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -#endif // 0 glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glDisable(GL_BLEND); - // glColor4f(1, 1, 1, 1); - + // glColor4f(1, 1, 1, 1); // FIXME: change to GL3 code! } /* - * r_newrefdef must be set before the first call + * gl3_newrefdef must be set before the first call */ static void GL3_RenderView(refdef_t *fd) @@ -638,7 +802,7 @@ GL3_RenderView(refdef_t *fd) R_DrawEntitiesOnList(); - R_RenderDlights(); + GL3_RenderDlights(); R_DrawParticles(); @@ -707,7 +871,7 @@ GL3_SetLightLevel(void) #if 0 // TODO! /* save off light value for server to look at */ - R_LightPoint(r_newrefdef.vieworg, shadelight); + R_LightPoint(gl3_newrefdef.vieworg, shadelight); /* pick the greatest component, which should be the * same as the mono value returned by software */ @@ -736,6 +900,7 @@ GL3_SetLightLevel(void) #endif // 0 } + static void GL3_RenderFrame(refdef_t *fd) { @@ -744,27 +909,68 @@ GL3_RenderFrame(refdef_t *fd) GL3_SetGL2D(); } +static void +GL3_Clear(void) +{ + // Check whether the stencil buffer needs clearing, and do so if need be. + GLbitfield stencilFlags = 0; +#if 0 // TODO: stereo stuff + if (gl3state.stereo_mode >= STEREO_MODE_ROW_INTERLEAVED && gl_state.stereo_mode <= STEREO_MODE_PIXEL_INTERLEAVED) { + glClearStencil(0); + stencilFlags |= GL_STENCIL_BUFFER_BIT; + } +#endif // 0 + + + if (gl_clear->value) + { + glClear(GL_COLOR_BUFFER_BIT | stencilFlags | GL_DEPTH_BUFFER_BIT); + } + else + { + glClear(GL_DEPTH_BUFFER_BIT | stencilFlags); + } + + gl3depthmin = 0; + gl3depthmax = 1; + glDepthFunc(GL_LEQUAL); + + glDepthRange(gl3depthmin, gl3depthmax); + + if (gl_zfix->value) + { + if (gl3depthmax > gl3depthmin) + { + glPolygonOffset(0.05, 1); + } + else + { + glPolygonOffset(-0.05, -1); + } + } + + /* stencilbuffer shadows */ + if (gl_shadows->value && have_stencil && gl_stencilshadow->value) + { + glClearStencil(1); + glClear(GL_STENCIL_BUFFER_BIT); + } + + glClear(GL_COLOR_BUFFER_BIT); // TODO: I added this and the next line - keep? + glClearColor(0.5, 0.5, 1, 0.5); +} + void GL3_BeginFrame(float camera_separation) { STUB_ONCE("TODO: Implement!"); - + /* glClearColor(0, 0, 0, 0); // FIXME: not sure this should stay glClear(GL_COLOR_BUFFER_BIT); glClearColor(1, 0, 0.5, 0.5); GL3_SetGL2D(); - - if (vid_gamma->modified || intensity->modified) - { - vid_gamma->modified = false; - intensity->modified = false; - - GL3_SetGammaAndIntensity(); - } - -#if 0 - gl_state.camera_separation = camera_separation; + */ /* change modes if necessary */ if (gl_mode->modified) @@ -772,6 +978,8 @@ GL3_BeginFrame(float camera_separation) vid_fullscreen->modified = true; } +#if 0 // TODO: stereo stuff + gl_state.camera_separation = camera_separation; // force a vid_restart if gl_stereo has been modified. if ( gl_state.stereo_mode != gl_stereo->value ) { // If we've gone from one mode to another with the same special buffer requirements there's no need to restart. @@ -781,20 +989,17 @@ GL3_BeginFrame(float camera_separation) else { R_Printf(PRINT_ALL, "stereo supermode changed, restarting video!\n"); - cvar_t *ref; - ref = ri.Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); - ref->modified = true; + vid_fullscreen->modified = true; } } +#endif // 0 - if (vid_gamma->modified) + if (vid_gamma->modified || intensity->modified) { vid_gamma->modified = false; + intensity->modified = false; - if (gl_state.hwgamma) - { - UpdateHardwareGamma(); - } + GL3_SetGammaAndIntensity(); } // Clamp overbrightbits @@ -814,44 +1019,15 @@ GL3_BeginFrame(float camera_separation) /* go into 2D mode */ - int x, w, y, h; - qboolean drawing_left_eye = gl_state.camera_separation < 0; - qboolean stereo_split_tb = ((gl_state.stereo_mode == STEREO_SPLIT_VERTICAL) && gl_state.camera_separation); - qboolean stereo_split_lr = ((gl_state.stereo_mode == STEREO_SPLIT_HORIZONTAL) && gl_state.camera_separation); - - x = 0; - w = vid.width; - y = 0; - h = vid.height; - - if(stereo_split_lr) { - w = w / 2; - x = drawing_left_eye ? 0 : w; - } - - if(stereo_split_tb) { - h = h / 2; - y = drawing_left_eye ? h : 0; - } - - glViewport(x, y, w, h); - 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); - glColor4f(1, 1, 1, 1); + GL3_SetGL2D(); /* draw buffer stuff */ if (gl_drawbuffer->modified) { gl_drawbuffer->modified = false; - if ((gl_state.camera_separation == 0) || gl_state.stereo_mode != STEREO_MODE_OPENGL) + // TODO: stereo stuff + //if ((gl3state.camera_separation == 0) || gl3state.stereo_mode != STEREO_MODE_OPENGL) { if (Q_stricmp(gl_drawbuffer->string, "GL_FRONT") == 0) { @@ -865,13 +1041,15 @@ GL3_BeginFrame(float camera_separation) } /* texturemode stuff */ - if (gl_texturemode->modified || (gl_config.anisotropic && gl_anisotropic->modified)) + if (gl_texturemode->modified || (gl3config.anisotropic && gl_anisotropic->modified)) { - R_TextureMode(gl_texturemode->string); + GL3_TextureMode(gl_texturemode->string); gl_texturemode->modified = false; gl_anisotropic->modified = false; } + STUB_ONCE("TODO: texture-alpha/solid-mode stuff??") +#if 0 if (gl_texturealphamode->modified) { R_TextureAlphaMode(gl_texturealphamode->string); @@ -883,10 +1061,10 @@ GL3_BeginFrame(float camera_separation) R_TextureSolidMode(gl_texturesolidmode->string); gl_texturesolidmode->modified = false; } +#endif // 0 /* clear screen if desired */ - R_Clear(); -#endif // 0 + GL3_Clear(); } static void diff --git a/src/client/refresh/gl3/gl3_md2.c b/src/client/refresh/gl3/gl3_md2.c new file mode 100644 index 00000000..6ea1037e --- /dev/null +++ b/src/client/refresh/gl3/gl3_md2.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * ======================================================================= + * + * MD2 file format + * + * ======================================================================= + */ + +#include "header/local.h" + +void +GL3_LoadMD2(gl3model_t *mod, void *buffer) +{ + int i, j; + dmdl_t *pinmodel, *pheader; + dstvert_t *pinst, *poutst; + dtriangle_t *pintri, *pouttri; + daliasframe_t *pinframe, *poutframe; + int *pincmd, *poutcmd; + int version; + + pinmodel = (dmdl_t *)buffer; + + version = LittleLong(pinmodel->version); + + if (version != ALIAS_VERSION) + { + ri.Sys_Error(ERR_DROP, "%s has wrong version number (%i should be %i)", + mod->name, version, ALIAS_VERSION); + } + + pheader = Hunk_Alloc(LittleLong(pinmodel->ofs_end)); + + /* byte swap the header fields and sanity check */ + for (i = 0; i < sizeof(dmdl_t) / 4; i++) + { + ((int *)pheader)[i] = LittleLong(((int *)buffer)[i]); + } + + if (pheader->skinheight > MAX_LBM_HEIGHT) + { + ri.Sys_Error(ERR_DROP, "model %s has a skin taller than %d", mod->name, + MAX_LBM_HEIGHT); + } + + if (pheader->num_xyz <= 0) + { + ri.Sys_Error(ERR_DROP, "model %s has no vertices", mod->name); + } + + if (pheader->num_xyz > MAX_VERTS) + { + ri.Sys_Error(ERR_DROP, "model %s has too many vertices", mod->name); + } + + if (pheader->num_st <= 0) + { + ri.Sys_Error(ERR_DROP, "model %s has no st vertices", mod->name); + } + + if (pheader->num_tris <= 0) + { + ri.Sys_Error(ERR_DROP, "model %s has no triangles", mod->name); + } + + if (pheader->num_frames <= 0) + { + ri.Sys_Error(ERR_DROP, "model %s has no frames", mod->name); + } + + /* load base s and t vertices (not used in gl version) */ + pinst = (dstvert_t *)((byte *)pinmodel + pheader->ofs_st); + poutst = (dstvert_t *)((byte *)pheader + pheader->ofs_st); + + for (i = 0; i < pheader->num_st; i++) + { + poutst[i].s = LittleShort(pinst[i].s); + poutst[i].t = LittleShort(pinst[i].t); + } + + /* load triangle lists */ + pintri = (dtriangle_t *)((byte *)pinmodel + pheader->ofs_tris); + pouttri = (dtriangle_t *)((byte *)pheader + pheader->ofs_tris); + + for (i = 0; i < pheader->num_tris; i++) + { + for (j = 0; j < 3; j++) + { + pouttri[i].index_xyz[j] = LittleShort(pintri[i].index_xyz[j]); + pouttri[i].index_st[j] = LittleShort(pintri[i].index_st[j]); + } + } + + /* load the frames */ + for (i = 0; i < pheader->num_frames; i++) + { + pinframe = (daliasframe_t *)((byte *)pinmodel + + pheader->ofs_frames + i * pheader->framesize); + poutframe = (daliasframe_t *)((byte *)pheader + + pheader->ofs_frames + i * pheader->framesize); + + memcpy(poutframe->name, pinframe->name, sizeof(poutframe->name)); + + for (j = 0; j < 3; j++) + { + poutframe->scale[j] = LittleFloat(pinframe->scale[j]); + poutframe->translate[j] = LittleFloat(pinframe->translate[j]); + } + + /* verts are all 8 bit, so no swapping needed */ + memcpy(poutframe->verts, pinframe->verts, + pheader->num_xyz * sizeof(dtrivertx_t)); + } + + mod->type = mod_alias; + + /* load the glcmds */ + pincmd = (int *)((byte *)pinmodel + pheader->ofs_glcmds); + poutcmd = (int *)((byte *)pheader + pheader->ofs_glcmds); + + for (i = 0; i < pheader->num_glcmds; i++) + { + poutcmd[i] = LittleLong(pincmd[i]); + } + + /* register all skins */ + memcpy((char *)pheader + pheader->ofs_skins, + (char *)pinmodel + pheader->ofs_skins, + pheader->num_skins * MAX_SKINNAME); + + for (i = 0; i < pheader->num_skins; i++) + { + mod->skins[i] = GL3_FindImage( + (char *)pheader + pheader->ofs_skins + i * MAX_SKINNAME, + it_skin); + } + + mod->mins[0] = -32; + mod->mins[1] = -32; + mod->mins[2] = -32; + mod->maxs[0] = 32; + mod->maxs[1] = 32; + mod->maxs[2] = 32; +} + diff --git a/src/client/refresh/gl3/gl3_mesh.c b/src/client/refresh/gl3/gl3_mesh.c new file mode 100644 index 00000000..a4a10de5 --- /dev/null +++ b/src/client/refresh/gl3/gl3_mesh.c @@ -0,0 +1,817 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * ======================================================================= + * + * Mesh handling + * + * ======================================================================= + */ + +#include "header/local.h" + +#define NUMVERTEXNORMALS 162 +#define SHADEDOT_QUANT 16 + +static float r_avertexnormals[NUMVERTEXNORMALS][3] = { +#include "../constants/anorms.h" +}; + +/* precalculated dot products for quantized angles */ +static float r_avertexnormal_dots[SHADEDOT_QUANT][256] = +#include "../constants/anormtab.h" +; + +typedef float vec4_t[4]; +static vec4_t s_lerped[MAX_VERTS]; +vec3_t shadevector; +float shadelight[3]; +float *shadedots = r_avertexnormal_dots[0]; +extern vec3_t lightspot; +extern qboolean have_stencil; + +static void +LerpVerts(int nverts, dtrivertx_t *v, dtrivertx_t *ov, + dtrivertx_t *verts, float *lerp, float move[3], + float frontv[3], float backv[3]) +{ + int i; + + if (currententity->flags & + (RF_SHELL_RED | RF_SHELL_GREEN | + RF_SHELL_BLUE | RF_SHELL_DOUBLE | + RF_SHELL_HALF_DAM)) + { + for (i = 0; i < nverts; i++, v++, ov++, lerp += 4) + { + float *normal = r_avertexnormals[verts[i].lightnormalindex]; + + lerp[0] = move[0] + ov->v[0] * backv[0] + v->v[0] * frontv[0] + + normal[0] * POWERSUIT_SCALE; + lerp[1] = move[1] + ov->v[1] * backv[1] + v->v[1] * frontv[1] + + normal[1] * POWERSUIT_SCALE; + lerp[2] = move[2] + ov->v[2] * backv[2] + v->v[2] * frontv[2] + + normal[2] * POWERSUIT_SCALE; + } + } + else + { + for (i = 0; i < nverts; i++, v++, ov++, lerp += 4) + { + lerp[0] = move[0] + ov->v[0] * backv[0] + v->v[0] * frontv[0]; + lerp[1] = move[1] + ov->v[1] * backv[1] + v->v[1] * frontv[1]; + lerp[2] = move[2] + ov->v[2] * backv[2] + v->v[2] * frontv[2]; + } + } +} + +/* + * Interpolates between two frames and origins + */ +static void +DrawAliasFrameLerp(dmdl_t *paliashdr, float backlerp) +{ + unsigned short total; + GLenum type; + float l; + daliasframe_t *frame, *oldframe; + dtrivertx_t *v, *ov, *verts; + int *order; + int count; + float frontlerp; + float alpha; + vec3_t move, delta, vectors[3]; + vec3_t frontv, backv; + int i; + int index_xyz; + float *lerp; + + frame = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + + currententity->frame * paliashdr->framesize); + verts = v = frame->verts; + + oldframe = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + + currententity->oldframe * paliashdr->framesize); + ov = oldframe->verts; + + order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds); + + if (currententity->flags & RF_TRANSLUCENT) + { + alpha = currententity->alpha; + } + else + { + alpha = 1.0; + } + + if (currententity->flags & + (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | + RF_SHELL_HALF_DAM)) + { + //glDisable(GL_TEXTURE_2D); + STUB_ONCE("TODO: OpenGL: use color-only shader!"); + } + + frontlerp = 1.0 - backlerp; + + /* move should be the delta back to the previous frame * backlerp */ + VectorSubtract(currententity->oldorigin, currententity->origin, delta); + AngleVectors(currententity->angles, vectors[0], vectors[1], vectors[2]); + + move[0] = DotProduct(delta, vectors[0]); /* forward */ + move[1] = -DotProduct(delta, vectors[1]); /* left */ + move[2] = DotProduct(delta, vectors[2]); /* up */ + + VectorAdd(move, oldframe->translate, move); + + for (i = 0; i < 3; i++) + { + move[i] = backlerp * move[i] + frontlerp * frame->translate[i]; + } + + for (i = 0; i < 3; i++) + { + frontv[i] = frontlerp * frame->scale[i]; + backv[i] = backlerp * oldframe->scale[i]; + } + + lerp = s_lerped[0]; + + LerpVerts(paliashdr->num_xyz, v, ov, verts, lerp, move, frontv, backv); + + while (1) + { + /* get the vertex count and primitive type */ + count = *order++; + + if (!count) + { + break; /* done */ + } + + if (count < 0) + { + count = -count; + + type = GL_TRIANGLE_FAN; + } + else + { + type = GL_TRIANGLE_STRIP; + } + + total = count; + GLfloat vtx[3*total]; + GLfloat tex[2*total]; + GLfloat clr[4 * total]; + unsigned int index_vtx = 0; + unsigned int index_tex = 0; + unsigned int index_clr = 0; + + if (currententity->flags & + (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE)) + { + do + { + index_xyz = order[2]; + order += 3; + + clr[index_clr++] = shadelight[0]; + clr[index_clr++] = shadelight[1]; + clr[index_clr++] = shadelight[2]; + clr[index_clr++] = alpha; + + vtx[index_vtx++] = s_lerped[index_xyz][0]; + vtx[index_vtx++] = s_lerped[index_xyz][1]; + vtx[index_vtx++] = s_lerped[index_xyz][2]; + } + while (--count); + } + else + { + do + { + /* texture coordinates come from the draw list */ + tex[index_tex++] = ((float *) order)[0]; + tex[index_tex++] = ((float *) order)[1]; + + index_xyz = order[2]; + order += 3; + + /* normals and vertexes come from the frame list */ + l = shadedots[verts[index_xyz].lightnormalindex]; + + clr[index_clr++] = l * shadelight[0]; + clr[index_clr++] = l * shadelight[1]; + clr[index_clr++] = l * shadelight[2]; + clr[index_clr++] = alpha; + + vtx[index_vtx++] = s_lerped[index_xyz][0]; + vtx[index_vtx++] = s_lerped[index_xyz][1]; + vtx[index_vtx++] = s_lerped[index_xyz][2]; + } + while (--count); + } + + STUB_ONCE("TODO: OpenGL!"); +#if 0 + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + glVertexPointer(3, GL_FLOAT, 0, vtx); + glTexCoordPointer(2, GL_FLOAT, 0, tex); + glColorPointer(4, GL_FLOAT, 0, clr); + glDrawArrays(type, 0, total); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); +#endif // 0 + } + + if (currententity->flags & + (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | + RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) + { + STUB_ONCE("TODO: OpenGL: use textured shader!"); + //glEnable(GL_TEXTURE_2D); + } +} + +static void +DrawAliasShadow(dmdl_t *paliashdr, int posenum) +{ + unsigned short total; + GLenum type; + int *order; + vec3_t point; + float height = 0, lheight; + int count; + + lheight = currententity->origin[2] - lightspot[2]; + order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds); + height = -lheight + 0.1f; + + /* stencilbuffer shadows */ + STUB_ONCE("TODO: OpenGL: stencil shadows!"); +#if 0 + if (have_stencil && gl_stencilshadow->value) + { + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_EQUAL, 1, 2); + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); + } +#endif // 0 + + while (1) + { + /* get the vertex count and primitive type */ + count = *order++; + + if (!count) + { + break; /* done */ + } + + if (count < 0) + { + count = -count; + + type = GL_TRIANGLE_FAN; + } + else + { + type = GL_TRIANGLE_STRIP; + } + + total = count; + GLfloat vtx[3*total]; + unsigned int index_vtx = 0; + + do + { + /* normals and vertexes come from the frame list */ + memcpy(point, s_lerped[order[2]], sizeof(point)); + + point[0] -= shadevector[0] * (point[2] + lheight); + point[1] -= shadevector[1] * (point[2] + lheight); + point[2] = height; + + vtx[index_vtx++] = point [ 0 ]; + vtx[index_vtx++] = point [ 1 ]; + vtx[index_vtx++] = point [ 2 ]; + + order += 3; + } + while (--count); + + STUB_ONCE("TODO: OpenGL!"); +#if 0 + glEnableClientState( GL_VERTEX_ARRAY ); + + glVertexPointer( 3, GL_FLOAT, 0, vtx ); + glDrawArrays( type, 0, total ); + + glDisableClientState( GL_VERTEX_ARRAY ); +#endif // 0 + } + + /* stencilbuffer shadows */ + if (have_stencil && gl_stencilshadow->value) + { + STUB_ONCE("TODO: OpenGL: stencil shadows!"); + //glDisable(GL_STENCIL_TEST); + } +} + +static qboolean +CullAliasModel(vec3_t bbox[8], entity_t *e) +{ + int i; + vec3_t mins, maxs; + dmdl_t *paliashdr; + vec3_t vectors[3]; + vec3_t thismins, oldmins, thismaxs, oldmaxs; + daliasframe_t *pframe, *poldframe; + vec3_t angles; + + paliashdr = (dmdl_t *)currentmodel->extradata; + + if ((e->frame >= paliashdr->num_frames) || (e->frame < 0)) + { + R_Printf(PRINT_DEVELOPER, "R_CullAliasModel %s: no such frame %d\n", + currentmodel->name, e->frame); + e->frame = 0; + } + + if ((e->oldframe >= paliashdr->num_frames) || (e->oldframe < 0)) + { + R_Printf(PRINT_DEVELOPER, "R_CullAliasModel %s: no such oldframe %d\n", + currentmodel->name, e->oldframe); + e->oldframe = 0; + } + + pframe = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + + e->frame * paliashdr->framesize); + + poldframe = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + + e->oldframe * paliashdr->framesize); + + /* compute axially aligned mins and maxs */ + if (pframe == poldframe) + { + for (i = 0; i < 3; i++) + { + mins[i] = pframe->translate[i]; + maxs[i] = mins[i] + pframe->scale[i] * 255; + } + } + else + { + for (i = 0; i < 3; i++) + { + thismins[i] = pframe->translate[i]; + thismaxs[i] = thismins[i] + pframe->scale[i] * 255; + + oldmins[i] = poldframe->translate[i]; + oldmaxs[i] = oldmins[i] + poldframe->scale[i] * 255; + + if (thismins[i] < oldmins[i]) + { + mins[i] = thismins[i]; + } + else + { + mins[i] = oldmins[i]; + } + + if (thismaxs[i] > oldmaxs[i]) + { + maxs[i] = thismaxs[i]; + } + else + { + maxs[i] = oldmaxs[i]; + } + } + } + + /* compute a full bounding box */ + for (i = 0; i < 8; i++) + { + vec3_t tmp; + + if (i & 1) + { + tmp[0] = mins[0]; + } + else + { + tmp[0] = maxs[0]; + } + + if (i & 2) + { + tmp[1] = mins[1]; + } + else + { + tmp[1] = maxs[1]; + } + + if (i & 4) + { + tmp[2] = mins[2]; + } + else + { + tmp[2] = maxs[2]; + } + + VectorCopy(tmp, bbox[i]); + } + + /* rotate the bounding box */ + VectorCopy(e->angles, angles); + angles[YAW] = -angles[YAW]; + AngleVectors(angles, vectors[0], vectors[1], vectors[2]); + + for (i = 0; i < 8; i++) + { + vec3_t tmp; + + VectorCopy(bbox[i], tmp); + + bbox[i][0] = DotProduct(vectors[0], tmp); + bbox[i][1] = -DotProduct(vectors[1], tmp); + bbox[i][2] = DotProduct(vectors[2], tmp); + + VectorAdd(e->origin, bbox[i], bbox[i]); + } + + int p, f, aggregatemask = ~0; + + for (p = 0; p < 8; p++) + { + int mask = 0; + + for (f = 0; f < 4; f++) + { + float dp = DotProduct(frustum[f].normal, bbox[p]); + + if ((dp - frustum[f].dist) < 0) + { + mask |= (1 << f); + } + } + + aggregatemask &= mask; + } + + if (aggregatemask) + { + return true; + } + + return false; +} + +void +GL3_DrawAliasModel(entity_t *e) +{ + int i; + dmdl_t *paliashdr; + float an; + vec3_t bbox[8]; + gl3image_t *skin; + + if (!(e->flags & RF_WEAPONMODEL)) + { + if (CullAliasModel(bbox, e)) + { + return; + } + } + + if (e->flags & RF_WEAPONMODEL) + { + if (gl_lefthand->value == 2) + { + return; + } + } + + paliashdr = (dmdl_t *)currentmodel->extradata; + + /* get lighting information */ + if (currententity->flags & + (RF_SHELL_HALF_DAM | RF_SHELL_GREEN | RF_SHELL_RED | + RF_SHELL_BLUE | RF_SHELL_DOUBLE)) + { + VectorClear(shadelight); + + if (currententity->flags & RF_SHELL_HALF_DAM) + { + shadelight[0] = 0.56; + shadelight[1] = 0.59; + shadelight[2] = 0.45; + } + + if (currententity->flags & RF_SHELL_DOUBLE) + { + shadelight[0] = 0.9; + shadelight[1] = 0.7; + } + + if (currententity->flags & RF_SHELL_RED) + { + shadelight[0] = 1.0; + } + + if (currententity->flags & RF_SHELL_GREEN) + { + shadelight[1] = 1.0; + } + + if (currententity->flags & RF_SHELL_BLUE) + { + shadelight[2] = 1.0; + } + } + else if (currententity->flags & RF_FULLBRIGHT) + { + for (i = 0; i < 3; i++) + { + shadelight[i] = 1.0; + } + } + else + { + GL3_LightPoint(currententity->origin, shadelight); + + /* player lighting hack for communication back to server */ + if (currententity->flags & RF_WEAPONMODEL) + { + /* pick the greatest component, which should be + the same as the mono value returned by software */ + if (shadelight[0] > shadelight[1]) + { + if (shadelight[0] > shadelight[2]) + { + gl_lightlevel->value = 150 * shadelight[0]; + } + else + { + gl_lightlevel->value = 150 * shadelight[2]; + } + } + else + { + if (shadelight[1] > shadelight[2]) + { + gl_lightlevel->value = 150 * shadelight[1]; + } + else + { + gl_lightlevel->value = 150 * shadelight[2]; + } + } + } + } + + if (currententity->flags & RF_MINLIGHT) + { + for (i = 0; i < 3; i++) + { + if (shadelight[i] > 0.1) + { + break; + } + } + + if (i == 3) + { + shadelight[0] = 0.1; + shadelight[1] = 0.1; + shadelight[2] = 0.1; + } + } + + if (currententity->flags & RF_GLOW) + { + /* bonus items will pulse with time */ + float scale; + float min; + + scale = 0.1 * sin(gl3_newrefdef.time * 7); + + for (i = 0; i < 3; i++) + { + min = shadelight[i] * 0.8; + shadelight[i] += scale; + + if (shadelight[i] < min) + { + shadelight[i] = min; + } + } + } + + + // Apply gl_overbrightbits to the mesh. If we don't do this they will appear slightly dimmer relative to walls. + if (gl_overbrightbits->value) + { + for (i = 0; i < 3; ++i) + { + shadelight[i] *= gl_overbrightbits->value; + } + } + + + + /* ir goggles color override */ + if ((gl3_newrefdef.rdflags & RDF_IRGOGGLES) && (currententity->flags & RF_IR_VISIBLE)) + { + shadelight[0] = 1.0; + shadelight[1] = 0.0; + shadelight[2] = 0.0; + } + + shadedots = r_avertexnormal_dots[((int)(currententity->angles[1] * + (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; + + an = currententity->angles[1] / 180 * M_PI; + shadevector[0] = cos(-an); + shadevector[1] = sin(-an); + shadevector[2] = 1; + VectorNormalize(shadevector); + + /* locate the proper data */ + c_alias_polys += paliashdr->num_tris; + + STUB_ONCE("TODO: OpenGL3 stuff!"); +#if 0 + /* draw all the triangles */ + if (currententity->flags & RF_DEPTHHACK) + { + /* hack the depth range to prevent view model from poking into walls */ + glDepthRange(gl3depthmin, gl3depthmin + 0.3 * (gl3depthmax - gl3depthmin)); + } + + if ((currententity->flags & RF_WEAPONMODEL) && (gl_lefthand->value == 1.0F)) + { + extern void R_MYgluPerspective(GLdouble fovy, GLdouble aspect, + GLdouble zNear, GLdouble zFar); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glScalef(-1, 1, 1); + R_MYgluPerspective(gl3_newrefdef.fov_y, + (float)gl3_newrefdef.width / gl3_newrefdef.height, + 4, 4096); + glMatrixMode(GL_MODELVIEW); + + glCullFace(GL_BACK); + } + + glPushMatrix(); + e->angles[PITCH] = -e->angles[PITCH]; + GL3_RotateForEntity(e); + e->angles[PITCH] = -e->angles[PITCH]; +#endif // 0 + + /* select skin */ + if (currententity->skin) + { + skin = currententity->skin; /* custom player skin */ + } + else + { + if (currententity->skinnum >= MAX_MD2SKINS) + { + skin = currentmodel->skins[0]; + } + else + { + skin = currentmodel->skins[currententity->skinnum]; + + if (!skin) + { + skin = currentmodel->skins[0]; + } + } + } + + if (!skin) + { + skin = gl3_notexture; /* fallback... */ + } + + GL3_Bind(skin->texnum); + + STUB_ONCE("TODO: OpenGL3 stuff!"); +#if 0 + /* draw it */ + glShadeModel(GL_SMOOTH); + + R_TexEnv(GL_MODULATE); + + if (currententity->flags & RF_TRANSLUCENT) + { + glEnable(GL_BLEND); + } +#endif // 0 + + if ((currententity->frame >= paliashdr->num_frames) || + (currententity->frame < 0)) + { + R_Printf(PRINT_DEVELOPER, "R_DrawAliasModel %s: no such frame %d\n", + currentmodel->name, currententity->frame); + currententity->frame = 0; + currententity->oldframe = 0; + } + + if ((currententity->oldframe >= paliashdr->num_frames) || + (currententity->oldframe < 0)) + { + R_Printf(PRINT_DEVELOPER, "R_DrawAliasModel %s: no such oldframe %d\n", + currentmodel->name, currententity->oldframe); + currententity->frame = 0; + currententity->oldframe = 0; + } + + /* Fuck this, gl_lerpmodels 0 looks horrible anyway + if (!gl_lerpmodels->value) + { + currententity->backlerp = 0; + } + */ + + DrawAliasFrameLerp(paliashdr, currententity->backlerp); + + STUB_ONCE("TODO: even more OpenGL3 stuff!"); +#if 0 + R_TexEnv(GL_REPLACE); + glShadeModel(GL_FLAT); + + glPopMatrix(); + + if ((currententity->flags & RF_WEAPONMODEL) && (gl_lefthand->value == 1.0F)) + { + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glCullFace(GL_FRONT); + } + + if (currententity->flags & RF_TRANSLUCENT) + { + glDisable(GL_BLEND); + } + + if (currententity->flags & RF_DEPTHHACK) + { + glDepthRange(gldepthmin, gldepthmax); + } + + if (gl_shadows->value && + !(currententity->flags & (RF_TRANSLUCENT | RF_WEAPONMODEL | RF_NOSHADOW))) + { + glPushMatrix(); + + /* don't rotate shadows on ungodly axes */ + glTranslatef(e->origin[0], e->origin[1], e->origin[2]); + glRotatef(e->angles[1], 0, 0, 1); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glColor4f(0, 0, 0, 0.5f); + DrawAliasShadow(paliashdr, currententity->frame); + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glPopMatrix(); + } + + glColor4f(1, 1, 1, 1); +#endif // 0 +} + diff --git a/src/client/refresh/gl3/gl3_model.c b/src/client/refresh/gl3/gl3_model.c index dba47304..d8191dcf 100644 --- a/src/client/refresh/gl3/gl3_model.c +++ b/src/client/refresh/gl3/gl3_model.c @@ -29,17 +29,79 @@ enum { MAX_MOD_KNOWN = 512 }; -int registration_sequence; - +static gl3model_t *loadmodel; +static byte mod_novis[MAX_MAP_LEAFS / 8]; gl3model_t mod_known[MAX_MOD_KNOWN]; -int mod_numknown; +static int mod_numknown; +int registration_sequence; +static byte *mod_base; + +/* the inline * models from the current map are kept seperate */ +gl3model_t mod_inline[MAX_MOD_KNOWN]; + +static byte * +Mod_DecompressVis(byte *in, gl3model_t *model) +{ + static byte decompressed[MAX_MAP_LEAFS / 8]; + int c; + byte *out; + int row; + + row = (model->vis->numclusters + 7) >> 3; + out = decompressed; + + 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); + + return decompressed; +} + +byte* +GL3_Mod_ClusterPVS(int cluster, gl3model_t *model) +{ + if ((cluster == -1) || !model->vis) + { + return mod_novis; + } + + return Mod_DecompressVis((byte *)model->vis + + model->vis->bitofs[cluster][DVIS_PVS], + model); +} void GL3_Mod_Modellist_f(void) { -#if 0 // TODO! int i; - model_t *mod; + gl3model_t *mod; int total; total = 0; @@ -57,16 +119,687 @@ GL3_Mod_Modellist_f(void) } R_Printf(PRINT_ALL, "Total resident: %i\n", total); -#endif } void GL3_Mod_Init(void) { - STUB("TODO: Implement!"); - // memset(mod_novis, 0xff, sizeof(mod_novis)); + memset(mod_novis, 0xff, sizeof(mod_novis)); } +static void +Mod_LoadLighting(lump_t *l) +{ + if (!l->filelen) + { + loadmodel->lightdata = NULL; + return; + } + + loadmodel->lightdata = Hunk_Alloc(l->filelen); + memcpy(loadmodel->lightdata, mod_base + l->fileofs, l->filelen); +} + +static void +Mod_LoadVisibility(lump_t *l) +{ + int i; + + if (!l->filelen) + { + loadmodel->vis = NULL; + return; + } + + loadmodel->vis = Hunk_Alloc(l->filelen); + memcpy(loadmodel->vis, mod_base + l->fileofs, l->filelen); + + loadmodel->vis->numclusters = LittleLong(loadmodel->vis->numclusters); + + for (i = 0; i < loadmodel->vis->numclusters; i++) + { + loadmodel->vis->bitofs[i][0] = LittleLong(loadmodel->vis->bitofs[i][0]); + loadmodel->vis->bitofs[i][1] = LittleLong(loadmodel->vis->bitofs[i][1]); + } +} + +static void +Mod_LoadVertexes(lump_t *l) +{ + dvertex_t *in; + mvertex_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + + if (l->filelen % sizeof(*in)) + { + ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", + loadmodel->name); + } + + count = l->filelen / sizeof(*in); + out = Hunk_Alloc(count * sizeof(*out)); + + loadmodel->vertexes = out; + loadmodel->numvertexes = count; + + for (i = 0; i < count; i++, in++, out++) + { + out->position[0] = LittleFloat(in->point[0]); + out->position[1] = LittleFloat(in->point[1]); + out->position[2] = LittleFloat(in->point[2]); + } +} + +static float +Mod_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 VectorLength(corner); +} + +static void +Mod_LoadSubmodels(lump_t *l) +{ + dmodel_t *in; + mmodel_t *out; + int i, j, count; + + in = (void *)(mod_base + l->fileofs); + + if (l->filelen % sizeof(*in)) + { + ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", + loadmodel->name); + } + + count = l->filelen / sizeof(*in); + out = Hunk_Alloc(count * sizeof(*out)); + + loadmodel->submodels = out; + loadmodel->numsubmodels = count; + + for (i = 0; i < count; i++, in++, out++) + { + for (j = 0; j < 3; j++) + { + /* spread the mins / maxs by a pixel */ + out->mins[j] = LittleFloat(in->mins[j]) - 1; + out->maxs[j] = LittleFloat(in->maxs[j]) + 1; + out->origin[j] = LittleFloat(in->origin[j]); + } + + out->radius = Mod_RadiusFromBounds(out->mins, out->maxs); + out->headnode = LittleLong(in->headnode); + out->firstface = LittleLong(in->firstface); + out->numfaces = LittleLong(in->numfaces); + } +} + +static void +Mod_LoadEdges(lump_t *l) +{ + dedge_t *in; + medge_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + + if (l->filelen % sizeof(*in)) + { + ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", + loadmodel->name); + } + + count = l->filelen / sizeof(*in); + out = Hunk_Alloc((count + 1) * sizeof(*out)); + + loadmodel->edges = out; + loadmodel->numedges = count; + + for (i = 0; i < count; i++, in++, out++) + { + out->v[0] = (unsigned short)LittleShort(in->v[0]); + out->v[1] = (unsigned short)LittleShort(in->v[1]); + } +} + +static void +Mod_LoadTexinfo(lump_t *l) +{ + texinfo_t *in; + mtexinfo_t *out, *step; + int i, j, count; + char name[MAX_QPATH]; + int next; + + in = (void *)(mod_base + l->fileofs); + + if (l->filelen % sizeof(*in)) + { + ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", + loadmodel->name); + } + + count = l->filelen / sizeof(*in); + out = Hunk_Alloc(count * sizeof(*out)); + + loadmodel->texinfo = out; + loadmodel->numtexinfo = count; + + for (i = 0; i < count; i++, in++, out++) + { + for (j = 0; j < 4; j++) + { + out->vecs[0][j] = LittleFloat(in->vecs[0][j]); + out->vecs[1][j] = LittleFloat(in->vecs[1][j]); + } + + out->flags = LittleLong(in->flags); + next = LittleLong(in->nexttexinfo); + + if (next > 0) + { + out->next = loadmodel->texinfo + next; + } + else + { + out->next = NULL; + } + + Com_sprintf(name, sizeof(name), "textures/%s.wal", in->texture); + + out->image = GL3_FindImage(name, it_wall); + + if (!out->image) + { + R_Printf(PRINT_ALL, "Couldn't load %s\n", name); + out->image = gl3_notexture; + } + } + + /* count animation frames */ + for (i = 0; i < count; i++) + { + out = &loadmodel->texinfo[i]; + out->numframes = 1; + + for (step = out->next; step && step != out; step = step->next) + { + out->numframes++; + } + } +} + +/* + * Fills in s->texturemins[] and s->extents[] + */ +static void +Mod_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; i < s->numedges; 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; + } +} + +extern void +GL3_SubdivideSurface(msurface_t *fa, gl3model_t* loadmodel); + +static void +Mod_LoadFaces(lump_t *l) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + int planenum, side; + int ti; + + in = (void *)(mod_base + l->fileofs); + + if (l->filelen % sizeof(*in)) + { + ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", + loadmodel->name); + } + + count = l->filelen / sizeof(*in); + out = Hunk_Alloc(count * sizeof(*out)); + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + + currentmodel = loadmodel; + + GL3_LM_BeginBuildingLightmaps(loadmodel); + + for (surfnum = 0; surfnum < count; surfnum++, in++, out++) + { + out->firstedge = LittleLong(in->firstedge); + out->numedges = LittleShort(in->numedges); + out->flags = 0; + out->polys = NULL; + + planenum = LittleShort(in->planenum); + side = LittleShort(in->side); + + if (side) + { + out->flags |= SURF_PLANEBACK; + } + + out->plane = loadmodel->planes + planenum; + + ti = LittleShort(in->texinfo); + + if ((ti < 0) || (ti >= loadmodel->numtexinfo)) + { + ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: bad texinfo number"); + } + + out->texinfo = loadmodel->texinfo + ti; + + Mod_CalcSurfaceExtents(out); + + /* lighting info */ + for (i = 0; i < MAXLIGHTMAPS; i++) + { + out->styles[i] = in->styles[i]; + } + + i = LittleLong(in->lightofs); + + if (i == -1) + { + out->samples = NULL; + } + else + { + out->samples = loadmodel->lightdata + i; + } + + /* set the drawing flags */ + if (out->texinfo->flags & SURF_WARP) + { + out->flags |= SURF_DRAWTURB; + + for (i = 0; i < 2; i++) + { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + + GL3_SubdivideSurface(out, loadmodel); /* cut up polygon for warps */ + } + + /* create lightmaps and polygons */ + if (!(out->texinfo->flags & + (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP))) + { + GL3_LM_CreateSurfaceLightmap(out); + } + + if (!(out->texinfo->flags & SURF_WARP)) + { + GL3_LM_BuildPolygonFromSurface(out); + } + } + + GL3_LM_EndBuildingLightmaps(); +} + +static void +Mod_SetParent(mnode_t *node, mnode_t *parent) +{ + node->parent = parent; + + if (node->contents != -1) + { + return; + } + + Mod_SetParent(node->children[0], node); + Mod_SetParent(node->children[1], node); +} + +static void +Mod_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)) + { + ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", + loadmodel->name); + } + + count = l->filelen / sizeof(*in); + out = Hunk_Alloc(count * sizeof(*out)); + + loadmodel->nodes = out; + loadmodel->numnodes = count; + + for (i = 0; i < count; i++, in++, out++) + { + for (j = 0; j < 3; j++) + { + out->minmaxs[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); + out->contents = -1; /* differentiate from leafs */ + + for (j = 0; j < 2; j++) + { + p = LittleLong(in->children[j]); + + if (p >= 0) + { + out->children[j] = loadmodel->nodes + p; + } + else + { + out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); + } + } + } + + Mod_SetParent(loadmodel->nodes, NULL); /* sets nodes and leafs */ +} + +static void +Mod_LoadLeafs(lump_t *l) +{ + dleaf_t *in; + mleaf_t *out; + int i, j, count, p; + + in = (void *)(mod_base + l->fileofs); + + if (l->filelen % sizeof(*in)) + { + ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", + loadmodel->name); + } + + count = l->filelen / sizeof(*in); + out = Hunk_Alloc(count * sizeof(*out)); + + loadmodel->leafs = out; + loadmodel->numleafs = count; + + for (i = 0; i < count; i++, in++, out++) + { + for (j = 0; j < 3; j++) + { + out->minmaxs[j] = LittleShort(in->mins[j]); + out->minmaxs[3 + j] = LittleShort(in->maxs[j]); + } + + p = LittleLong(in->contents); + out->contents = p; + + out->cluster = LittleShort(in->cluster); + out->area = LittleShort(in->area); + + out->firstmarksurface = loadmodel->marksurfaces + + LittleShort(in->firstleafface); + out->nummarksurfaces = LittleShort(in->numleaffaces); + } +} + +static void +Mod_LoadMarksurfaces(lump_t *l) +{ + int i, j, count; + short *in; + msurface_t **out; + + in = (void *)(mod_base + l->fileofs); + + if (l->filelen % sizeof(*in)) + { + ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", + loadmodel->name); + } + + count = l->filelen / sizeof(*in); + out = Hunk_Alloc(count * sizeof(*out)); + + loadmodel->marksurfaces = out; + loadmodel->nummarksurfaces = count; + + for (i = 0; i < count; i++) + { + j = LittleShort(in[i]); + + if ((j < 0) || (j >= loadmodel->numsurfaces)) + { + ri.Sys_Error(ERR_DROP, "Mod_ParseMarksurfaces: bad surface number"); + } + + out[i] = loadmodel->surfaces + j; + } +} + +static void +Mod_LoadSurfedges(lump_t *l) +{ + int i, count; + int *in, *out; + + in = (void *)(mod_base + l->fileofs); + + if (l->filelen % sizeof(*in)) + { + ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", + loadmodel->name); + } + + count = l->filelen / sizeof(*in); + + if ((count < 1) || (count >= MAX_MAP_SURFEDGES)) + { + ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: bad surfedges count in %s: %i", + loadmodel->name, count); + } + + out = Hunk_Alloc(count * sizeof(*out)); + + loadmodel->surfedges = out; + loadmodel->numsurfedges = count; + + for (i = 0; i < count; i++) + { + out[i] = LittleLong(in[i]); + } +} + +static void +Mod_LoadPlanes(lump_t *l) +{ + int i, j; + cplane_t *out; + dplane_t *in; + int count; + int bits; + + in = (void *)(mod_base + l->fileofs); + + if (l->filelen % sizeof(*in)) + { + ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", + loadmodel->name); + } + + count = l->filelen / sizeof(*in); + out = Hunk_Alloc(count * 2 * sizeof(*out)); + + loadmodel->planes = out; + loadmodel->numplanes = count; + + for (i = 0; i < count; i++, in++, out++) + { + bits = 0; + + for (j = 0; j < 3; j++) + { + out->normal[j] = LittleFloat(in->normal[j]); + + if (out->normal[j] < 0) + { + bits |= 1 << j; + } + } + + out->dist = LittleFloat(in->dist); + out->type = LittleLong(in->type); + out->signbits = bits; + } +} + +static void +Mod_LoadBrushModel(gl3model_t *mod, void *buffer) +{ + int i; + dheader_t *header; + mmodel_t *bm; + + loadmodel->type = mod_brush; + + if (loadmodel != mod_known) + { + ri.Sys_Error(ERR_DROP, "Loaded a brush model after the world"); + } + + header = (dheader_t *)buffer; + + i = LittleLong(header->version); + + if (i != BSPVERSION) + { + ri.Sys_Error(ERR_DROP, "Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", + mod->name, i, BSPVERSION); + } + + /* swap all the lumps */ + mod_base = (byte *)header; + + for (i = 0; i < sizeof(dheader_t) / 4; i++) + { + ((int *)header)[i] = LittleLong(((int *)header)[i]); + } + + /* load into heap */ + Mod_LoadVertexes(&header->lumps[LUMP_VERTEXES]); + Mod_LoadEdges(&header->lumps[LUMP_EDGES]); + Mod_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]); + Mod_LoadLighting(&header->lumps[LUMP_LIGHTING]); + Mod_LoadPlanes(&header->lumps[LUMP_PLANES]); + Mod_LoadTexinfo(&header->lumps[LUMP_TEXINFO]); + Mod_LoadFaces(&header->lumps[LUMP_FACES]); + Mod_LoadMarksurfaces(&header->lumps[LUMP_LEAFFACES]); + Mod_LoadVisibility(&header->lumps[LUMP_VISIBILITY]); + Mod_LoadLeafs(&header->lumps[LUMP_LEAFS]); + Mod_LoadNodes(&header->lumps[LUMP_NODES]); + Mod_LoadSubmodels(&header->lumps[LUMP_MODELS]); + mod->numframes = 2; /* regular and alternate animation */ + + /* set up the submodels */ + for (i = 0; i < mod->numsubmodels; i++) + { + gl3model_t *starmod; + + bm = &mod->submodels[i]; + starmod = &mod_inline[i]; + + *starmod = *loadmodel; + + starmod->firstmodelsurface = bm->firstface; + starmod->nummodelsurfaces = bm->numfaces; + starmod->firstnode = bm->headnode; + + if (starmod->firstnode >= loadmodel->numnodes) + { + ri.Sys_Error(ERR_DROP, "Inline model %i has bad firstnode", i); + } + + VectorCopy(bm->maxs, starmod->maxs); + VectorCopy(bm->mins, starmod->mins); + starmod->radius = bm->radius; + + if (i == 0) + { + *loadmodel = *starmod; + } + + starmod->numleafs = bm->visleafs; + } +} static void Mod_Free(gl3model_t *mod) @@ -89,6 +822,119 @@ GL3_Mod_FreeAll(void) } } +extern void GL3_LoadMD2(gl3model_t *mod, void *buffer); +extern void GL3_LoadSP2(gl3model_t *mod, void *buffer, int modfilelen); + +/* + * Loads in a model for the given name + */ +static gl3model_t * +Mod_ForName(char *name, qboolean crash) +{ + gl3model_t *mod; + unsigned *buf; + int i; + + if (!name[0]) + { + ri.Sys_Error(ERR_DROP, "Mod_ForName: NULL name"); + } + + /* inline models are grabbed only from worldmodel */ + if (name[0] == '*') + { + i = (int)strtol(name + 1, (char **)NULL, 10); + + if ((i < 1) || !gl3_worldmodel || (i >= gl3_worldmodel->numsubmodels)) + { + ri.Sys_Error(ERR_DROP, "bad inline model number"); + } + + return &mod_inline[i]; + } + + /* search the currently loaded models */ + for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) + { + if (!mod->name[0]) + { + continue; + } + + if (!strcmp(mod->name, name)) + { + return mod; + } + } + + /* find a free model slot spot */ + for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) + { + if (!mod->name[0]) + { + break; /* free spot */ + } + } + + if (i == mod_numknown) + { + if (mod_numknown == MAX_MOD_KNOWN) + { + ri.Sys_Error(ERR_DROP, "mod_numknown == MAX_MOD_KNOWN"); + } + + mod_numknown++; + } + + strcpy(mod->name, name); + + /* load the file */ + int modfilelen = ri.FS_LoadFile(mod->name, (void **)&buf); + + if (!buf) + { + if (crash) + { + ri.Sys_Error(ERR_DROP, "Mod_NumForName: %s not found", mod->name); + } + + memset(mod->name, 0, sizeof(mod->name)); + return NULL; + } + + loadmodel = mod; + + /* call the apropriate loader */ + switch (LittleLong(*(unsigned *)buf)) + { + case IDALIASHEADER: + loadmodel->extradata = Hunk_Begin(0x200000); + GL3_LoadMD2(mod, buf); + break; + + case IDSPRITEHEADER: + loadmodel->extradata = Hunk_Begin(0x10000); + GL3_LoadSP2(mod, buf, modfilelen); + break; + + case IDBSPHEADER: + loadmodel->extradata = Hunk_Begin(0x1000000); + Mod_LoadBrushModel(mod, buf); + break; + + default: + ri.Sys_Error(ERR_DROP, + "Mod_NumForName: unknown fileid for %s", + mod->name); + break; + } + + loadmodel->extradatasize = Hunk_End(); + + ri.FS_FreeFile(buf); + + return mod; +} /* * Specifies the model that will be used as the world @@ -114,20 +960,15 @@ GL3_BeginRegistration(char *model) Mod_Free(&mod_known[0]); } - STUB_ONCE("TODO: Implement Mod_ForName()!"); -#if 0 // TODO! - r_worldmodel = Mod_ForName(fullname, true); -#endif // 0 + gl3_worldmodel = Mod_ForName(fullname, true); + gl3_viewcluster = -1; } struct model_s * GL3_RegisterModel(char *name) { - STUB_ONCE("TODO: Implement!"); - return NULL; -#if 0 - model_t *mod; + gl3model_t *mod; int i; dsprite_t *sprout; dmdl_t *pheader; @@ -145,7 +986,7 @@ GL3_RegisterModel(char *name) for (i = 0; i < sprout->numframes; i++) { - mod->skins[i] = R_FindImage(sprout->frames[i].name, it_sprite); + mod->skins[i] = GL3_FindImage(sprout->frames[i].name, it_sprite); } } else if (mod->type == mod_alias) @@ -154,8 +995,7 @@ GL3_RegisterModel(char *name) for (i = 0; i < pheader->num_skins; i++) { - mod->skins[i] = R_FindImage((char *)pheader + pheader->ofs_skins + - i * MAX_SKINNAME, it_skin); + mod->skins[i] = GL3_FindImage((char *)pheader + pheader->ofs_skins + i * MAX_SKINNAME, it_skin); } mod->numframes = pheader->num_frames; @@ -164,14 +1004,12 @@ GL3_RegisterModel(char *name) { for (i = 0; i < mod->numtexinfo; i++) { - mod->texinfo[i].image->registration_sequence = - registration_sequence; + mod->texinfo[i].image->registration_sequence = registration_sequence; } } } return mod; -#endif // 0 } void diff --git a/src/client/refresh/gl3/gl3_sdl.c b/src/client/refresh/gl3/gl3_sdl.c index d9bc7f97..3bdd3cbf 100644 --- a/src/client/refresh/gl3/gl3_sdl.c +++ b/src/client/refresh/gl3/gl3_sdl.c @@ -71,8 +71,8 @@ int GL3_PrepareForWindow(void) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); - // SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); // TODO - int contextFlags = 0; // SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG TODO + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + int contextFlags = SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG; if(gl3_debugcontext && gl3_debugcontext->value) { contextFlags |= SDL_GL_CONTEXT_DEBUG_FLAG; diff --git a/src/client/refresh/gl3/gl3_shaders.c b/src/client/refresh/gl3/gl3_shaders.c index f84ed43b..47dc4e0e 100644 --- a/src/client/refresh/gl3/gl3_shaders.c +++ b/src/client/refresh/gl3/gl3_shaders.c @@ -31,40 +31,6 @@ #define eprintf(...) R_Printf(PRINT_ALL, __VA_ARGS__) -#if 0 -static const char* vertexSrc = MULTILINE_STRING(#version 150\n - in vec2 position; - // I renamed color to inColor and Color to passColor for more clarity - in vec3 inColor; - // same for texcoord -> inTexCoord, Textcoord -> passTexCoord - in vec2 inTexCoord; - - out vec3 passColor; - out vec2 passTexCoord; - - void main() { - passColor = inColor; - passTexCoord = inTexCoord; - gl_Position = vec4(position, 0.0, 1.0); - } -); - -static const char* fragmentSrc = MULTILINE_STRING(#version 150\n - in vec3 passColor; // I renamed color to passColor (it's from the vertex shader above) - in vec2 passTexCoord; // same for Texcoord -> passTexCoord - out vec4 outColor; - - uniform sampler2D tex; - - void main() - { - outColor = texture(tex, passTexCoord) * vec4(passColor, 1.0); - //outColor = texture(tex, passTexCoord); - //outColor = vec4(passColor, 1.0); - } -); -#endif // 0 - static GLuint CompileShader(GLenum shaderType, const char* shaderSrc) { @@ -209,7 +175,10 @@ static const char* fragmentSrc2D = MULTILINE_STRING(#version 150\n void main() { vec4 texel = texture(tex, passTexCoord); - if(texel.a < 0.666) + // the gl1 renderer used glAlphaFunc(GL_GREATER, 0.666); + // and glEnable(GL_ALPHA_TEST); for 2D rendering + // this should do the same + if(texel.a <= 0.666) discard; // apply gamma correction and intensity diff --git a/src/client/refresh/gl3/gl3_sp2.c b/src/client/refresh/gl3/gl3_sp2.c new file mode 100644 index 00000000..bd2a7f47 --- /dev/null +++ b/src/client/refresh/gl3/gl3_sp2.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * ======================================================================= + * + * .sp2 sprites + * + * ======================================================================= + */ + +#include "header/local.h" + +void +GL3_LoadSP2(gl3model_t *mod, void *buffer, int modfilelen) +{ + dsprite_t *sprin, *sprout; + int i; + + sprin = (dsprite_t *)buffer; + sprout = Hunk_Alloc(modfilelen); + + sprout->ident = LittleLong(sprin->ident); + sprout->version = LittleLong(sprin->version); + sprout->numframes = LittleLong(sprin->numframes); + + if (sprout->version != SPRITE_VERSION) + { + ri.Sys_Error(ERR_DROP, "%s has wrong version number (%i should be %i)", + mod->name, sprout->version, SPRITE_VERSION); + } + + if (sprout->numframes > MAX_MD2SKINS) + { + ri.Sys_Error(ERR_DROP, "%s has too many frames (%i > %i)", + mod->name, sprout->numframes, MAX_MD2SKINS); + } + + /* byte swap everything */ + for (i = 0; i < sprout->numframes; i++) + { + sprout->frames[i].width = LittleLong(sprin->frames[i].width); + sprout->frames[i].height = LittleLong(sprin->frames[i].height); + sprout->frames[i].origin_x = LittleLong(sprin->frames[i].origin_x); + sprout->frames[i].origin_y = LittleLong(sprin->frames[i].origin_y); + memcpy(sprout->frames[i].name, sprin->frames[i].name, MAX_SKINNAME); + mod->skins[i] = GL3_FindImage(sprout->frames[i].name, it_sprite); + } + + mod->type = mod_sprite; +} + diff --git a/src/client/refresh/gl3/gl3_surf.c b/src/client/refresh/gl3/gl3_surf.c new file mode 100644 index 00000000..a47382a7 --- /dev/null +++ b/src/client/refresh/gl3/gl3_surf.c @@ -0,0 +1,1124 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * ======================================================================= + * + * Surface generation and drawing + * + * ======================================================================= + */ + +#include + +#include "header/local.h" + +int c_visible_lightmaps; +int c_visible_textures; +static vec3_t modelorg; /* relative to viewpoint */ +static msurface_t *gl3_alpha_surfaces; + +gl3lightmapstate_t gl3_lms; + +#define BACKFACE_EPSILON 0.01 + +extern gl3image_t gl3textures[MAX_GL3TEXTURES]; +extern int numgl3textures; + +/* + * Returns true if the box is completely outside the frustom + */ +static qboolean +CullBox(vec3_t mins, vec3_t maxs) +{ + int i; + + if (!gl_cull->value) + { + return false; + } + + for (i = 0; i < 4; i++) + { + if (BOX_ON_PLANE_SIDE(mins, maxs, &frustum[i]) == 2) + { + return true; + } + } + + return false; +} + +/* + * Returns the proper texture for a given time and base texture + */ +static gl3image_t * +TextureAnimation(mtexinfo_t *tex) +{ + int c; + + if (!tex->next) + { + return tex->image; + } + + c = currententity->frame % tex->numframes; + + while (c) + { + tex = tex->next; + c--; + } + + return tex->image; +} + +void +GL3_DrawGLPoly(glpoly_t *p) +{ + float *v; + + v = p->verts[0]; + + STUB_ONCE("TODO: Implement!"); +#if 0 + glEnableClientState( GL_VERTEX_ARRAY ); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v ); + glTexCoordPointer( 2, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v+3 ); + glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts ); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); +#endif // 0 +} + +void +GL3_DrawGLFlowingPoly(msurface_t *fa) +{ + int i; + float *v; + glpoly_t *p; + float scroll; + + p = fa->polys; + + scroll = -64 * ((gl3_newrefdef.time / 40.0) - (int)(gl3_newrefdef.time / 40.0)); + + if (scroll == 0.0) + { + scroll = -64.0; + } + + GLfloat tex[2*p->numverts]; + unsigned int index_tex = 0; + + v = p->verts [ 0 ]; + + for ( i = 0; i < p->numverts; i++, v += VERTEXSIZE ) + { + tex[index_tex++] = v [ 3 ] + scroll; + tex[index_tex++] = v [ 4 ]; + } + v = p->verts [ 0 ]; + + STUB_ONCE("TODO: Implement OpenGL stuff!"); +#if 0 + glEnableClientState( GL_VERTEX_ARRAY ); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v ); + glTexCoordPointer( 2, GL_FLOAT, 0, tex ); + glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts ); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); +#endif // 0 +} + +static void +DrawTriangleOutlines(void) +{ + int i, j; + glpoly_t *p; + + STUB_ONCE("TODO: Implement for gl_showtris support!"); + +#if 0 + if (!gl_showtris->value) + { + return; + } + + glDisable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + glColor4f(1, 1, 1, 1); + + for (i = 0; i < MAX_LIGHTMAPS; i++) + { + msurface_t *surf; + + for (surf = gl3_lms.lightmap_surfaces[i]; + surf != 0; + surf = surf->lightmapchain) + { + p = surf->polys; + + for ( ; p; p = p->chain) + { + for (j = 2; j < p->numverts; j++) + { + GLfloat vtx[12]; + unsigned int k; + + for (k=0; k<3; k++) + { + vtx[0+k] = p->verts [ 0 ][ k ]; + vtx[3+k] = p->verts [ j - 1 ][ k ]; + vtx[6+k] = p->verts [ j ][ k ]; + vtx[9+k] = p->verts [ 0 ][ k ]; + } + + glEnableClientState( GL_VERTEX_ARRAY ); + + glVertexPointer( 3, GL_FLOAT, 0, vtx ); + glDrawArrays( GL_LINE_STRIP, 0, 4 ); + + glDisableClientState( GL_VERTEX_ARRAY ); + } + } + } + } + + glEnable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); +#endif // 0 +} + +static void +DrawGLPolyChain(glpoly_t *p, float soffset, float toffset) +{ + STUB_ONCE("TODO: Implement!"); +#if 0 + if ((soffset == 0) && (toffset == 0)) + { + for ( ; p != 0; p = p->chain) + { + float *v; + + v = p->verts[0]; + + if (v == NULL) + { + fprintf(stderr, "BUGFIX: R_DrawGLPolyChain: v==NULL\n"); + return; + } + + glEnableClientState( GL_VERTEX_ARRAY ); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v ); + glTexCoordPointer( 2, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v+5 ); + glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts ); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + } + } + else + { + for ( ; p != 0; p = p->chain) + { + float *v; + int j; + + v = p->verts[0]; + + GLfloat tex[2*p->numverts]; + unsigned int index_tex = 0; + + for ( j = 0; j < p->numverts; j++, v += VERTEXSIZE ) + { + tex[index_tex++] = v [ 5 ] - soffset; + tex[index_tex++] = v [ 6 ] - toffset; + } + + v = p->verts [ 0 ]; + + glEnableClientState( GL_VERTEX_ARRAY ); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v ); + glTexCoordPointer( 2, GL_FLOAT, 0, tex ); + glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts ); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + } + } +#endif // 0 +} + +/* + * This routine takes all the given light mapped surfaces + * in the world and blends them into the framebuffer. + */ +static void +BlendLightmaps(void) +{ + int i; + msurface_t *surf, *newdrawsurf = 0; + + /* don't bother if we're set to fullbright */ + if (gl_fullbright->value) + { + return; + } + + if (!gl3_worldmodel->lightdata) + { + return; + } + + STUB_ONCE("TODO: Implement!"); +#if 0 + /* don't bother writing Z */ + glDepthMask(0); + + /* set the appropriate blending mode unless + we're only looking at the lightmaps. */ + if (!gl_lightmap->value) + { + glEnable(GL_BLEND); + + if (gl_saturatelighting->value) + { + glBlendFunc(GL_ONE, GL_ONE); + } + else + { + glBlendFunc(GL_ZERO, GL_SRC_COLOR); + } + } + + if (currentmodel == gl3_worldmodel) + { + c_visible_lightmaps = 0; + } + + /* render static lightmaps first */ + for (i = 1; i < MAX_LIGHTMAPS; i++) + { + if (gl3_lms.lightmap_surfaces[i]) + { + if (currentmodel == gl3_worldmodel) + { + c_visible_lightmaps++; + } + + GL3_Bind(gl_state.lightmap_textures + i); + + for (surf = gl3_lms.lightmap_surfaces[i]; + surf != 0; + surf = surf->lightmapchain) + { + if (surf->polys) + { + // Apply overbright bits to the static lightmaps + if (gl_overbrightbits->value) + { + R_TexEnv(GL_COMBINE_EXT); + glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl_overbrightbits->value); + } + + DrawGLPolyChain(surf->polys, 0, 0); + } + } + } + } + + /* render dynamic lightmaps */ + if (gl_dynamic->value) + { + LM_InitBlock(); + + GL3_Bind(gl_state.lightmap_textures + 0); + + if (currentmodel == gl3_worldmodel) + { + c_visible_lightmaps++; + } + + newdrawsurf = gl3_lms.lightmap_surfaces[0]; + + for (surf = gl3_lms.lightmap_surfaces[0]; + surf != 0; + surf = surf->lightmapchain) + { + int smax, tmax; + byte *base; + + smax = (surf->extents[0] >> 4) + 1; + tmax = (surf->extents[1] >> 4) + 1; + + if (LM_AllocBlock(smax, tmax, &surf->dlight_s, &surf->dlight_t)) + { + base = gl3_lms.lightmap_buffer; + base += (surf->dlight_t * BLOCK_WIDTH + + surf->dlight_s) * LIGHTMAP_BYTES; + + R_BuildLightMap(surf, base, BLOCK_WIDTH * LIGHTMAP_BYTES); + } + else + { + msurface_t *drawsurf; + + /* upload what we have so far */ + LM_UploadBlock(true); + + /* draw all surfaces that use this lightmap */ + for (drawsurf = newdrawsurf; + drawsurf != surf; + drawsurf = drawsurf->lightmapchain) + { + if (drawsurf->polys) + { + // Apply overbright bits to the dynamic lightmaps + if (gl_overbrightbits->value) + { + R_TexEnv(GL_COMBINE_EXT); + glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl_overbrightbits->value); + } + + DrawGLPolyChain(drawsurf->polys, + (drawsurf->light_s - drawsurf->dlight_s) * (1.0 / 128.0), + (drawsurf->light_t - drawsurf->dlight_t) * (1.0 / 128.0)); + } + } + + newdrawsurf = drawsurf; + + /* clear the block */ + LM_InitBlock(); + + /* try uploading the block now */ + if (!LM_AllocBlock(smax, tmax, &surf->dlight_s, &surf->dlight_t)) + { + ri.Sys_Error(ERR_FATAL, + "Consecutive calls to LM_AllocBlock(%d,%d) failed (dynamic)\n", + smax, tmax); + } + + base = gl3_lms.lightmap_buffer; + base += (surf->dlight_t * BLOCK_WIDTH + + surf->dlight_s) * LIGHTMAP_BYTES; + + R_BuildLightMap(surf, base, BLOCK_WIDTH * LIGHTMAP_BYTES); + } + } + + /* draw remainder of dynamic lightmaps that haven't been uploaded yet */ + if (newdrawsurf) + { + LM_UploadBlock(true); + } + + for (surf = newdrawsurf; surf != 0; surf = surf->lightmapchain) + { + if (surf->polys) + { + // Apply overbright bits to the remainder lightmaps + if (gl_overbrightbits->value) + { + R_TexEnv(GL_COMBINE_EXT); + glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl_overbrightbits->value); + } + + DrawGLPolyChain(surf->polys, + (surf->light_s - surf->dlight_s) * (1.0 / 128.0), + (surf->light_t - surf->dlight_t) * (1.0 / 128.0)); + } + } + } + + /* restore state */ + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(1); +#endif // 0 +} + +static void +RenderBrushPoly(msurface_t *fa) +{ + int maps; + gl3image_t *image; + qboolean is_dynamic = false; + + c_brush_polys++; + + image = TextureAnimation(fa->texinfo); + + STUB("TODO: implement!"); +#if 0 + if (fa->flags & SURF_DRAWTURB) + { + GL3_Bind(image->texnum); + + /* This is a hack ontop of a hack. Warping surfaces like those generated + by R_EmitWaterPolys() don't have a lightmap. Original Quake II therefore + negated the global intensity on those surfaces, because otherwise they + would show up much too bright. When we implemented overbright bits this + hack modified the global GL state in an incompatible way. So implement + a new hack, based on overbright bits... Depending on the value set to + gl_overbrightbits the result is different: + + 0: Old behaviour. + 1: No overbright bits on the global scene but correct lightning on + warping surfaces. + 2: Overbright bits on the global scene but not on warping surfaces. + They oversaturate otherwise. */ + if (gl_overbrightbits->value) + { + R_TexEnv(GL_COMBINE_EXT); + glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1); + } + else + { + R_TexEnv(GL_MODULATE); + glColor4f(gl_state.inverse_intensity, gl_state.inverse_intensity, + gl_state.inverse_intensity, 1.0f); + } + + GL3_EmitWaterPolys(fa); + R_TexEnv(GL_REPLACE); + + return; + } + else + { + GL3_Bind(image->texnum); + + R_TexEnv(GL_REPLACE); + } + + if (fa->texinfo->flags & SURF_FLOWING) + { + R_DrawGLFlowingPoly(fa); + } + else + { + R_DrawGLPoly(fa->polys); + } + + /* check for lightmap modification */ + for (maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++) + { + if (gl3_newrefdef.lightstyles[fa->styles[maps]].white != + fa->cached_light[maps]) + { + goto dynamic; + } + } + + /* dynamic this frame or dynamic previously */ + if (fa->dlightframe == gl3_framecount) + { + dynamic: + + if (gl_dynamic->value) + { + if (!(fa->texinfo->flags & + (SURF_SKY | SURF_TRANS33 | + SURF_TRANS66 | SURF_WARP))) + { + is_dynamic = true; + } + } + } + + if (is_dynamic) + { + if (((fa->styles[maps] >= 32) || + (fa->styles[maps] == 0)) && + (fa->dlightframe != gl3_framecount)) + { + unsigned temp[34 * 34]; + int smax, tmax; + + smax = (fa->extents[0] >> 4) + 1; + tmax = (fa->extents[1] >> 4) + 1; + + GL3_BuildLightMap(fa, (void *)temp, smax * 4); + GL3_SetCacheState(fa); + + GL3_Bind(gl_state.lightmap_textures + fa->lightmaptexturenum); + + glTexSubImage2D(GL_TEXTURE_2D, 0, fa->light_s, fa->light_t, + smax, tmax, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, temp); + + fa->lightmapchain = gl3_lms.lightmap_surfaces[fa->lightmaptexturenum]; + gl3_lms.lightmap_surfaces[fa->lightmaptexturenum] = fa; + } + else + { + fa->lightmapchain = gl3_lms.lightmap_surfaces[0]; + gl3_lms.lightmap_surfaces[0] = fa; + } + } + else + { + fa->lightmapchain = gl3_lms.lightmap_surfaces[fa->lightmaptexturenum]; + gl3_lms.lightmap_surfaces[fa->lightmaptexturenum] = fa; + } +#endif // 0 +} + +/* + * Draw water surfaces and windows. + * The BSP tree is waled front to back, so unwinding the chain + * of alpha_surfaces will draw back to front, giving proper ordering. + */ +void +GL3_DrawAlphaSurfaces(void) +{ + STUB("TODO: implement!"); +#if 0 + msurface_t *s; + float intens; + + /* go back to the world matrix */ + glLoadMatrixf(r_world_matrix); + + glEnable(GL_BLEND); + R_TexEnv(GL_MODULATE); + + /* the textures are prescaled up for a better + lighting range, so scale it back down */ + intens = gl_state.inverse_intensity; + + for (s = gl3_alpha_surfaces; s; s = s->texturechain) + { + GL3_Bind(s->texinfo->image->texnum); + c_brush_polys++; + + if (s->texinfo->flags & SURF_TRANS33) + { + glColor4f(intens, intens, intens, 0.33); + } + else if (s->texinfo->flags & SURF_TRANS66) + { + glColor4f(intens, intens, intens, 0.66); + } + else + { + glColor4f(intens, intens, intens, 1); + } + + if (s->flags & SURF_DRAWTURB) + { + R_EmitWaterPolys(s); + } + else if (s->texinfo->flags & SURF_FLOWING) + { + R_DrawGLFlowingPoly(s); + } + else + { + R_DrawGLPoly(s->polys); + } + } + + R_TexEnv(GL_REPLACE); + glColor4f(1, 1, 1, 1); + glDisable(GL_BLEND); + + gl3_alpha_surfaces = NULL; +#endif // 0 +} + +static void +DrawTextureChains(void) +{ + int i; + msurface_t *s; + gl3image_t *image; + + c_visible_textures = 0; + + for (i = 0, image = gl3textures; i < numgl3textures; i++, image++) + { + if (!image->registration_sequence) + { + continue; + } + + s = image->texturechain; + + if (!s) + { + continue; + } + + c_visible_textures++; + + for ( ; s; s = s->texturechain) + { + RenderBrushPoly(s); + } + + image->texturechain = NULL; + } + + STUB("TODO: do something about R_TexEnv()!"); + // R_TexEnv(GL_REPLACE); +} + +static void +DrawInlineBModel(void) +{ + int i, k; + cplane_t *pplane; + float dot; + msurface_t *psurf; + dlight_t *lt; + + /* calculate dynamic lighting for bmodel */ + if (!gl_flashblend->value) + { + lt = gl3_newrefdef.dlights; + + for (k = 0; k < gl3_newrefdef.num_dlights; k++, lt++) + { + GL3_MarkLights(lt, 1 << k, currentmodel->nodes + currentmodel->firstnode); + } + } + + psurf = ¤tmodel->surfaces[currentmodel->firstmodelsurface]; + + if (currententity->flags & RF_TRANSLUCENT) + { + STUB_ONCE("TODO: implement for OpenGL3"); + /* + glEnable(GL_BLEND); + glColor4f(1, 1, 1, 0.25); + R_TexEnv(GL_MODULATE); + */ + } + + /* draw texture */ + for (i = 0; i < currentmodel->nummodelsurfaces; i++, psurf++) + { + /* find which side of the node we are on */ + pplane = 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))) + { + if (psurf->texinfo->flags & (SURF_TRANS33 | SURF_TRANS66)) + { + /* add to the translucent chain */ + psurf->texturechain = gl3_alpha_surfaces; + gl3_alpha_surfaces = psurf; + } + else + { + RenderBrushPoly(psurf); + } + } + } + + if (!(currententity->flags & RF_TRANSLUCENT)) + { + BlendLightmaps(); + } + else + { + STUB_ONCE("TODO: implement for OpenGL3"); + /* + glDisable(GL_BLEND); + glColor4f(1, 1, 1, 1); + R_TexEnv(GL_REPLACE); + */ + } +} + +void +GL3_DrawBrushModel(entity_t *e) +{ + vec3_t mins, maxs; + int i; + qboolean rotated; + + if (currentmodel->nummodelsurfaces == 0) + { + return; + } + + currententity = e; + //gl3state.currenttextures[0] = gl3state.currenttextures[1] = -1; + gl3state.currenttexture = -1; + + 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 (CullBox(mins, maxs)) + { + return; + } + + if (gl_zfix->value) + { + glEnable(GL_POLYGON_OFFSET_FILL); + } + + STUB_ONCE("TODO: something about setting color to 1,1,1,1"); + //glColor4f(1, 1, 1, 1); + memset(gl3_lms.lightmap_surfaces, 0, sizeof(gl3_lms.lightmap_surfaces)); + + VectorSubtract(gl3_newrefdef.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); + } + + STUB_ONCE("TODO: something to the view matrix and R_TexEnv()"); +#if 0 + glPushMatrix(); + e->angles[0] = -e->angles[0]; + e->angles[2] = -e->angles[2]; + R_RotateForEntity(e); + e->angles[0] = -e->angles[0]; + e->angles[2] = -e->angles[2]; + + R_TexEnv(GL_REPLACE); + + if (gl_lightmap->value) + { + R_TexEnv(GL_REPLACE); + } + else + { + R_TexEnv(GL_MODULATE); + } + + DrawInlineBModel(); + + glPopMatrix(); +#endif // 0 + + if (gl_zfix->value) + { + glDisable(GL_POLYGON_OFFSET_FILL); + } +} + +static void +RecursiveWorldNode(mnode_t *node) +{ + int c, side, sidebit; + cplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + float dot; + gl3image_t *image; + + if (node->contents == CONTENTS_SOLID) + { + return; /* solid */ + } + + if (node->visframe != gl3_visframecount) + { + return; + } + + if (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 (gl3_newrefdef.areabits) + { + if (!(gl3_newrefdef.areabits[pleaf->area >> 3] & (1 << (pleaf->area & 7)))) + { + return; /* not visible */ + } + } + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + (*mark)->visframe = gl3_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 */ + RecursiveWorldNode(node->children[side]); + + /* draw stuff */ + for (c = node->numsurfaces, + surf = gl3_worldmodel->surfaces + node->firstsurface; + c; c--, surf++) + { + if (surf->visframe != gl3_framecount) + { + continue; + } + + if ((surf->flags & SURF_PLANEBACK) != sidebit) + { + continue; /* wrong side */ + } + + if (surf->texinfo->flags & SURF_SKY) + { + /* just adds to visible sky bounds */ + GL3_AddSkySurface(surf); + } + else if (surf->texinfo->flags & (SURF_TRANS33 | SURF_TRANS66)) + { + /* add to the translucent chain */ + surf->texturechain = gl3_alpha_surfaces; + gl3_alpha_surfaces = surf; + gl3_alpha_surfaces->texinfo->image = TextureAnimation(surf->texinfo); + } + else + { + /* the polygon is visible, so add it to the texture sorted chain */ + image = TextureAnimation(surf->texinfo); + surf->texturechain = image->texturechain; + image->texturechain = surf; + } + } + + /* recurse down the back side */ + RecursiveWorldNode(node->children[!side]); +} + +void +GL3_DrawWorld(void) +{ + entity_t ent; + + STUB_ONCE("TODO: gl_drawworld cvar"); + /* + if (!gl_drawworld->value) + { + return; + }*/ + + if (gl3_newrefdef.rdflags & RDF_NOWORLDMODEL) + { + return; + } + + currentmodel = gl3_worldmodel; + + VectorCopy(gl3_newrefdef.vieworg, modelorg); + + /* auto cycle the world frame for texture animation */ + memset(&ent, 0, sizeof(ent)); + ent.frame = (int)(gl3_newrefdef.time * 2); + currententity = &ent; + + gl3state.currenttexture = -1; //s[0] = gl3state.currenttextures[1] = -1; + + STUB_ONCE("somehow set color to 1,1,1,1 maybe"); + //glColor4f(1, 1, 1, 1); + + memset(gl3_lms.lightmap_surfaces, 0, sizeof(gl3_lms.lightmap_surfaces)); + + GL3_ClearSkyBox(); + RecursiveWorldNode(gl3_worldmodel->nodes); + DrawTextureChains(); + BlendLightmaps(); + GL3_DrawSkyBox(); + DrawTriangleOutlines(); + + currententity = NULL; +} + +/* + * Mark the leaves and nodes that are + * in the PVS for the current cluster + */ +void +GL3_MarkLeaves(void) +{ + byte *vis; + byte fatvis[MAX_MAP_LEAFS / 8]; + mnode_t *node; + int i, c; + mleaf_t *leaf; + int cluster; + + if ((gl3_oldviewcluster == gl3_viewcluster) && + (gl3_oldviewcluster2 == gl3_viewcluster2) && + !gl_novis->value && + (gl3_viewcluster != -1)) + { + return; + } + + /* development aid to let you run around + and see exactly where the pvs ends */ + if (gl_lockpvs->value) + { + return; + } + + gl3_visframecount++; + gl3_oldviewcluster = gl3_viewcluster; + gl3_oldviewcluster2 = gl3_viewcluster2; + + if (gl_novis->value || (gl3_viewcluster == -1) || !gl3_worldmodel->vis) + { + /* mark everything */ + for (i = 0; i < gl3_worldmodel->numleafs; i++) + { + gl3_worldmodel->leafs[i].visframe = gl3_visframecount; + } + + for (i = 0; i < gl3_worldmodel->numnodes; i++) + { + gl3_worldmodel->nodes[i].visframe = gl3_visframecount; + } + + return; + } + + vis = GL3_Mod_ClusterPVS(gl3_viewcluster, gl3_worldmodel); + + /* may have to combine two clusters because of solid water boundaries */ + if (gl3_viewcluster2 != gl3_viewcluster) + { + memcpy(fatvis, vis, (gl3_worldmodel->numleafs + 7) / 8); + vis = GL3_Mod_ClusterPVS(gl3_viewcluster2, gl3_worldmodel); + c = (gl3_worldmodel->numleafs + 31) / 32; + + for (i = 0; i < c; i++) + { + ((int *)fatvis)[i] |= ((int *)vis)[i]; + } + + vis = fatvis; + } + + for (i = 0, leaf = gl3_worldmodel->leafs; + i < gl3_worldmodel->numleafs; + i++, leaf++) + { + cluster = leaf->cluster; + + if (cluster == -1) + { + continue; + } + + if (vis[cluster >> 3] & (1 << (cluster & 7))) + { + node = (mnode_t *)leaf; + + do + { + if (node->visframe == gl3_visframecount) + { + break; + } + + node->visframe = gl3_visframecount; + node = node->parent; + } + while (node); + } + } +} + diff --git a/src/client/refresh/gl3/gl3_warp.c b/src/client/refresh/gl3/gl3_warp.c index 8e269e17..b18e97b7 100644 --- a/src/client/refresh/gl3/gl3_warp.c +++ b/src/client/refresh/gl3/gl3_warp.c @@ -27,14 +27,318 @@ #include "header/local.h" +// TODO: can we get rid of this? +float gl3_turbsin[] = { +#include "../constants/warpsin.h" +}; + +static void +R_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 < numverts; i++) + { + for (j = 0; j < 3; j++, v++) + { + if (*v < mins[j]) + { + mins[j] = *v; + } + + if (*v > maxs[j]) + { + maxs[j] = *v; + } + } + } +} + +static const float SUBDIVIDE_SIZE = 64.0f; + +static void +R_SubdividePolygon(int numverts, float *verts, msurface_t *warpface) +{ + 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; + vec3_t total; + float total_s, total_t; + + if (numverts > 60) + { + ri.Sys_Error(ERR_DROP, "numverts = %i", numverts); + } + + R_BoundPoly(numverts, verts, mins, maxs); + + for (i = 0; i < 3; i++) + { + m = (mins[i] + maxs[i]) * 0.5; + m = SUBDIVIDE_SIZE * floor(m / SUBDIVIDE_SIZE + 0.5); + + if (maxs[i] - m < 8) + { + continue; + } + + if (m - mins[i] < 8) + { + continue; + } + + /* cut it */ + v = verts + i; + + for (j = 0; j < numverts; j++, v += 3) + { + dist[j] = *v - m; + } + + /* wrap cases */ + dist[j] = dist[0]; + v -= i; + VectorCopy(verts, v); + + f = b = 0; + v = verts; + + for (j = 0; j < numverts; j++, v += 3) + { + if (dist[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++; + } + } + + R_SubdividePolygon(f, front[0], warpface); + R_SubdividePolygon(b, back[0], warpface); + return; + } + + /* add a point in the center to help keep warp valid */ + poly = Hunk_Alloc(sizeof(glpoly_t) + ((numverts - 4) + 2) * VERTEXSIZE * sizeof(float)); + poly->next = warpface->polys; + warpface->polys = poly; + poly->numverts = numverts + 2; + VectorClear(total); + total_s = 0; + total_t = 0; + + for (i = 0; i < numverts; i++, verts += 3) + { + VectorCopy(verts, poly->verts[i + 1]); + s = DotProduct(verts, warpface->texinfo->vecs[0]); + t = DotProduct(verts, warpface->texinfo->vecs[1]); + + total_s += s; + total_t += t; + VectorAdd(total, verts, total); + + poly->verts[i + 1][3] = s; + poly->verts[i + 1][4] = t; + } + + VectorScale(total, (1.0 / numverts), poly->verts[0]); + poly->verts[0][3] = total_s / numverts; + poly->verts[0][4] = total_t / numverts; + + /* copy first vertex to last */ + memcpy(poly->verts[i + 1], poly->verts[1], sizeof(poly->verts[0])); +} + +/* + * Breaks a polygon up along axial 64 unit + * boundaries so that turbulent and sky warps + * can be done reasonably. + */ +void +GL3_SubdivideSurface(msurface_t *fa, gl3model_t* loadmodel) +{ + vec3_t verts[64]; + int numverts; + int i; + int lindex; + float *vec; + + /* convert edges back to a normal polygon */ + numverts = 0; + + for (i = 0; i < fa->numedges; 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++; + } + + R_SubdividePolygon(numverts, verts[0], fa); +} + +/* + * Does a water warp on the pre-fragmented glpoly_t chain + */ +void +GL3_EmitWaterPolys(msurface_t *fa) +{ + glpoly_t *p, *bp; + float *v; + int i; + float s, t, os, ot; + float scroll; + float rdt = gl3_newrefdef.time; + + static const float TURBSCALE = (256.0 / (2 * M_PI)); + + if (fa->texinfo->flags & SURF_FLOWING) + { + scroll = -64 * ((gl3_newrefdef.time * 0.5) - (int)(gl3_newrefdef.time * 0.5)); + } + else + { + scroll = 0; + } + + for (bp = fa->polys; bp; bp = bp->next) + { + p = bp; + + GLfloat tex[2*p->numverts]; + unsigned int index_tex = 0; + + for ( i = 0, v = p->verts [ 0 ]; i < p->numverts; i++, v += VERTEXSIZE ) + { + os = v [ 3 ]; + ot = v [ 4 ]; + + s = os + gl3_turbsin [ (int) ( ( ot * 0.125 + gl3_newrefdef.time ) * TURBSCALE ) & 255 ]; + s += scroll; + tex[index_tex++] = s * ( 1.0 / 64 ); + + t = ot + gl3_turbsin [ (int) ( ( os * 0.125 + rdt ) * TURBSCALE ) & 255 ]; + tex[index_tex++] = t * ( 1.0 / 64 ); + } + + v = p->verts [ 0 ]; + + STUB_ONCE("TODO: Implement OpenGL3 part!"); +#if 0 + glEnableClientState( GL_VERTEX_ARRAY ); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v ); + glTexCoordPointer( 2, GL_FLOAT, 0, tex ); + glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts ); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); +#endif // 0 + } +} + +// ########### below: Sky-specific stuff ########## + +#define ON_EPSILON 0.1 /* point on plane side epsilon */ +enum { MAX_CLIP_VERTS = 64 }; + + +static const int skytexorder[6] = {0, 2, 1, 3, 4, 5}; + +static GLfloat vtx_sky[12]; +static GLfloat tex_sky[8]; +static unsigned int index_vtx = 0; +static unsigned int index_tex = 0; + +static float skymins[2][6], skymaxs[2][6]; +static float sky_min, sky_max; + static float skyrotate; static vec3_t skyaxis; static gl3image_t* sky_images[6]; -static float sky_min, sky_max; /* 3dstudio environment map names */ static const char* suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; +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; + +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 */ +}; + +int vec_to_st[6][3] = { + {-2, 3, 1}, + {2, 3, -1}, + + {1, 3, 2}, + {-1, 3, -2}, + + {-2, -1, 3}, + {-2, 1, -3} +}; + + void GL3_SetSky(char *name, float rotate, vec3_t axis) { @@ -64,3 +368,397 @@ GL3_SetSky(char *name, float rotate, vec3_t axis) sky_max = 511.0 / 512; } } + +static void +DrawSkyPolygon(int nump, vec3_t vecs) +{ + int i, j; + vec3_t v, av; + float s, t, dv; + int axis; + float *vp; + + c_sky++; + + /* decide which face it maps to */ + VectorCopy(vec3_origin, v); + + for (i = 0, vp = vecs; i < nump; i++, vp += 3) + { + VectorAdd(vp, v, v); + } + + av[0] = fabs(v[0]); + av[1] = fabs(v[1]); + av[2] = fabs(v[2]); + + if ((av[0] > 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 < nump; i++, vecs += 3) + { + j = vec_to_st[axis][2]; + + if (j > 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; + } + } +} + +static 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) + { + ri.Sys_Error(ERR_DROP, "R_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 < nump; i++, v += 3) + { + d = DotProduct(v, norm); + + if (d > 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; i < nump; i++, v += 3) + { + switch (sides[i]) + { + case SIDE_FRONT: + VectorCopy(v, newv[0][newc[0]]); + newc[0]++; + break; + case SIDE_BACK: + VectorCopy(v, newv[1][newc[1]]); + newc[1]++; + break; + case SIDE_ON: + VectorCopy(v, newv[0][newc[0]]); + newc[0]++; + VectorCopy(v, newv[1][newc[1]]); + newc[1]++; + break; + } + + if ((sides[i] == SIDE_ON) || + (sides[i + 1] == SIDE_ON) || + (sides[i + 1] == sides[i])) + { + continue; + } + + d = dists[i] / (dists[i] - dists[i + 1]); + + for (j = 0; j < 3; j++) + { + e = v[j] + d * (v[j + 3] - v[j]); + newv[0][newc[0]][j] = e; + newv[1][newc[1]][j] = e; + } + + newc[0]++; + newc[1]++; + } + + /* continue */ + ClipSkyPolygon(newc[0], newv[0][0], stage + 1); + ClipSkyPolygon(newc[1], newv[1][0], stage + 1); +} + +void +GL3_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; i < p->numverts; i++) + { + VectorSubtract(p->verts[i], gl3_origin, verts[i]); + } + + ClipSkyPolygon(p->numverts, verts[0], 0); + } +} + +void +GL3_ClearSkyBox(void) +{ + int i; + + for (i = 0; i < 6; i++) + { + skymins[0][i] = skymins[1][i] = 9999; + skymaxs[0][i] = skymaxs[1][i] = -9999; + } +} + +static void +MakeSkyVec(float s, float t, int axis) +{ + vec3_t v, b; + int j, k; + + if (gl_farsee->value == 0) + { + b[0] = s * 2300; + b[1] = t * 2300; + b[2] = 2300; + } + else + { + b[0] = s * 4096; + b[1] = t * 4096; + b[2] = 4096; + } + + 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]; + } + } + + /* avoid bilerp seam */ + s = (s + 1) * 0.5; + t = (t + 1) * 0.5; + + if (s < sky_min) + { + s = sky_min; + } + else if (s > sky_max) + { + s = sky_max; + } + + if (t < sky_min) + { + t = sky_min; + } + else if (t > sky_max) + { + t = sky_max; + } + + t = 1.0 - t; + + tex_sky[index_tex++] = s; + tex_sky[index_tex++] = t; + + vtx_sky[index_vtx++] = v[ 0 ]; + vtx_sky[index_vtx++] = v[ 1 ]; + vtx_sky[index_vtx++] = v[ 2 ]; +} + +void +GL3_DrawSkyBox(void) +{ + int i; + + if (skyrotate) + { /* check for no sky at all */ + for (i = 0; i < 6; i++) + { + if ((skymins[0][i] < skymaxs[0][i]) && + (skymins[1][i] < skymaxs[1][i])) + { + break; + } + } + + if (i == 6) + { + return; /* nothing visible */ + } + } + + STUB_ONCE("TODO: Implement OpenGL3 stuff"); +#if 0 + glPushMatrix(); + glTranslatef(gl3_origin[0], gl3_origin[1], gl3_origin[2]); + glRotatef(gl3_newrefdef.time * skyrotate, skyaxis[0], skyaxis[1], skyaxis[2]); + + for (i = 0; i < 6; i++) + { + if (skyrotate) + { + skymins[0][i] = -1; + skymins[1][i] = -1; + skymaxs[0][i] = 1; + skymaxs[1][i] = 1; + } + + if ((skymins[0][i] >= skymaxs[0][i]) || + (skymins[1][i] >= skymaxs[1][i])) + { + continue; + } + + GL3_Bind(sky_images[skytexorder[i]]->texnum); + + glEnableClientState( GL_VERTEX_ARRAY ); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + + index_vtx = 0; + index_tex = 0; + + 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 ); + + glVertexPointer( 3, GL_FLOAT, 0, vtx_sky ); + glTexCoordPointer( 2, GL_FLOAT, 0, tex_sky ); + glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + } + + glPopMatrix(); +#endif // 0 +} diff --git a/src/client/refresh/gl3/header/local.h b/src/client/refresh/gl3/header/local.h index a1ec5cf2..5ba8e4f3 100644 --- a/src/client/refresh/gl3/header/local.h +++ b/src/client/refresh/gl3/header/local.h @@ -116,7 +116,7 @@ typedef struct unsigned char *d_16to8table; - int lightmap_textures; + //int lightmap_textures; //int currenttextures[2]; GLuint currenttexture; @@ -140,8 +140,15 @@ extern gl3state_t gl3state; extern viddef_t vid; +extern refdef_t gl3_newrefdef; + +extern int gl3_visframecount; /* bumped when going to a new PVS */ +extern int gl3_framecount; /* used for dlight push checking */ + extern int gl3_viewcluster, gl3_viewcluster2, gl3_oldviewcluster, gl3_oldviewcluster2; +extern int c_brush_polys, c_alias_polys; + /* NOTE: struct image_s* is what re.RegisterSkin() etc return so no gl3image_s! * (I think the client only passes the pointer around and doesn't know the * definition of this struct, so this being different from struct image_s @@ -169,13 +176,49 @@ enum {MAX_GL3TEXTURES = 1024}; // include this down here so it can use gl3image_t #include "model.h" +enum { + BLOCK_WIDTH = 128, + BLOCK_HEIGHT = 128, + LIGHTMAP_BYTES = 4, + MAX_LIGHTMAPS = 128 +}; + +typedef struct +{ + int internal_format; + int current_lightmap_texture; + + msurface_t *lightmap_surfaces[MAX_LIGHTMAPS]; + + int allocated[BLOCK_WIDTH]; + + /* the lightmap texture data needs to be kept in + main memory so texsubimage can update properly */ + byte lightmap_buffer[4 * BLOCK_WIDTH * BLOCK_HEIGHT]; +} gl3lightmapstate_t; + +extern gl3model_t *gl3_worldmodel; +extern gl3model_t *currentmodel; +extern entity_t *currententity; + +extern float gl3depthmin, gl3depthmax; + +extern cplane_t frustum[4]; + +vec3_t gl3_origin; + extern gl3image_t *gl3_notexture; /* use for bad textures */ extern gl3image_t *gl3_particletexture; /* little dot for particles */ extern int gl_filter_min; extern int gl_filter_max; +extern qboolean GL3_CullBox(vec3_t mins, vec3_t maxs); +extern void GL3_RotateForEntity(entity_t *e); + // gl3_sdl.c +extern qboolean have_stencil; + extern int GL3_PrepareForWindow(void); extern int GL3_InitContext(void* win); extern void GL3_EndFrame(void); @@ -194,6 +237,7 @@ extern void GL3_BeginRegistration(char *model); extern struct model_s * GL3_RegisterModel(char *name); extern void GL3_EndRegistration(void); extern void GL3_Mod_Modellist_f(void); +extern byte* GL3_Mod_ClusterPVS(int cluster, gl3model_t *model); // gl3_draw.c extern void GL3_Draw_InitLocal(void); @@ -221,8 +265,43 @@ extern void GL3_ShutdownImages(void); extern void GL3_FreeUnusedImages(void); extern void GL3_ImageList_f(void); +// gl3_light.c +extern void GL3_RenderDlights(void); +extern void GL3_MarkLights(dlight_t *light, int bit, mnode_t *node); +extern void GL3_PushDlights(void); +extern void GL3_LightPoint(vec3_t p, vec3_t color); +extern void GL3_SetCacheState(msurface_t *surf); +extern void GL3_BuildLightMap(msurface_t *surf, byte *dest, int stride); + +// gl3_lightmap.c +#define GL_LIGHTMAP_FORMAT GL_RGBA + +extern void GL3_LM_InitBlock(void); +extern void GL3_LM_UploadBlock(qboolean dynamic); +extern qboolean GL3_LM_AllocBlock(int w, int h, int *x, int *y); +extern void GL3_LM_BuildPolygonFromSurface(msurface_t *fa); +extern void GL3_LM_CreateSurfaceLightmap(msurface_t *surf); +extern void GL3_LM_BeginBuildingLightmaps(gl3model_t *m); +extern void GL3_LM_EndBuildingLightmaps(void); + // gl3_warp.c +extern void GL3_EmitWaterPolys(msurface_t *fa); +extern void GL3_SubdivideSurface(msurface_t *fa, gl3model_t* loadmodel); + extern void GL3_SetSky(char *name, float rotate, vec3_t axis); +extern void GL3_DrawSkyBox(void); +extern void GL3_ClearSkyBox(void); +extern void GL3_AddSkySurface(msurface_t *fa); + + +// gl3_surf.c +extern void GL3_DrawGLPoly(glpoly_t *p); +extern void GL3_DrawGLFlowingPoly(msurface_t *fa); +extern void GL3_DrawTriangleOutlines(void); +extern void GL3_DrawAlphaSurfaces(void); +extern void GL3_DrawBrushModel(entity_t *e); +extern void GL3_DrawWorld(void); +extern void GL3_MarkLeaves(void); // gl3_shaders.c @@ -242,11 +321,29 @@ extern cvar_t *gl_customheight; extern cvar_t *gl_nolerp_list; extern cvar_t *gl_nobind; +extern cvar_t *gl_lockpvs; +extern cvar_t *gl_novis; + +extern cvar_t *gl_cull; +extern cvar_t *gl_zfix; +extern cvar_t *gl_fullbright; + +extern cvar_t *gl_norefresh; +extern cvar_t *gl_lefthand; +extern cvar_t *gl_farsee; extern cvar_t *vid_gamma; extern cvar_t *intensity; extern cvar_t *gl_anisotropic; +extern cvar_t *gl_lightlevel; +extern cvar_t *gl_overbrightbits; + +extern cvar_t *gl_flashblend; +extern cvar_t *gl_modulate; + +extern cvar_t *gl_stencilshadow; + extern cvar_t *gl3_debugcontext; #endif /* SRC_CLIENT_REFRESH_GL3_HEADER_LOCAL_H_ */ diff --git a/src/client/refresh/ref_shared.h b/src/client/refresh/ref_shared.h index 2d2266c1..bd4586a7 100644 --- a/src/client/refresh/ref_shared.h +++ b/src/client/refresh/ref_shared.h @@ -56,6 +56,8 @@ typedef enum mod_alias } modtype_t; +#define MAX_LBM_HEIGHT 480 + extern void R_Printf(int level, const char* msg, ...) __attribute__ ((format (printf, 2, 3))); extern void LoadPCX(char *origname, byte **pic, byte **palette, int *width, int *height);