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);