Code could use a good overhaul sometime cause it has a problem in fast matches where it stalls a lot because of all the data being pushed through the arrays.
Moving to VBO's would probably be a good starting point, also batching up some of the state changes.
Little addition in next version
Code: Select all
/*
================
R_TurbDepthOn
Utility function,
Newer turn of depth checking,
unless the alpha channel is below 1.0f
================
*/
__forceinline void R_TurbDepthOn(float alpha)
{
glEnable (GL_BLEND);
// the Z check is newer needed,
// when there is no alpha.
if (alpha < 1.0f)
{
glDepthMask (GL_FALSE);
}
}
/*
================
R_TurbDepthOff
Utility function,
Newer turn of depth checking,
unless the alpha channel is below 1.0f
================
*/
__forceinline void R_TurbDepthOff(float alpha)
{
glDisable (GL_BLEND);
// the Z check is newer needed,
// when there is no alpha.
if (alpha < 1.0f)
{
glDepthMask (GL_TRUE);
}
}
/*
================
R_TurbShaderOn
Fragment shader waterwarp MrG.
================
*/
__forceinline void R_TurbShaderOn (void)
{
// added arb shader for non nvidia cards
if (GL_ExtensionBits & HAS_ARBFRAGMENTSHADERS)
{
// values are rgb + alpha but can also be texture coords.
glClientActiveTexture (GL_TEXTURE1);
glBindTexture (GL_TEXTURE_2D, r_dst_texture.gltexnum);
// enable TMU1
glActiveTexture (GL_TEXTURE1);
glEnable (GL_TEXTURE_2D);
// run fragment shader
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, fragment_programs[F_PROG_WARP]);
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, TURB_NOALPHA_CONST, TURB_NOALPHA_CONST, TURB_NOALPHA_CONST, TURB_NOALPHA_CONST);
glEnable (GL_FRAGMENT_PROGRAM_ARB);
}
}
/*
================
R_TurbShaderOff
Fragment shader waterwarp MrG.
================
*/
__forceinline void R_TurbShaderOff (void)
{
if (GL_ExtensionBits & HAS_ARBFRAGMENTSHADERS)
{
// back to TMU0
glDisable (GL_TEXTURE_2D);
glActiveTexture (GL_TEXTURE0);
// kill fragment program
glDisable (GL_FRAGMENT_PROGRAM_ARB);
}
}
/*
================
R_DrawTurbFog
Underwater fog.
================
*/
__forceinline void R_DrawTurbFog (void)
{
gltexture_t *tex;
msurface_t *surf;
glpoly_t *p;
float fogdist;
float fogalpha;
float *v;
int i;
// if teleports or lava are drawn last they'll leave blending disabled coming in here, so
// make a potentially redundant state change. it's only once per frame, so live with it.
R_TurbDepthOn(fogalpha);
// default blendfunc for this
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// turn off texturing and ensure we have smooth shading
glDisable (GL_TEXTURE_2D);
glShadeModel (GL_SMOOTH);
// enable vertex pointer - index 0 is the regular verts
glEnableClientState (GL_VERTEX_ARRAY);
glVertexPointer (3, GL_FLOAT, sizeof (waterverts_t), &waterarray[0]);
// enable color array
glEnableClientState (GL_COLOR_ARRAY);
glColorPointer (4, GL_FLOAT, sizeof (waterverts_t), &waterarray[7]);
// draw fog in liquids only
if (GL_RenderState.waterthisframe)
{
// go through the texture list
for (tex = TextureList; tex; tex = tex->TextureList)
{
// nothing chained
if (!(surf = tex->texturechain)) continue;
// we are not below water
if (!(surf->flags & SURF_UNDERWATER)) continue;
// it ain't liquid
if (!(surf->flags & SURF_DRAWTURB)) continue;
// don't put fog on teleports
if (surf->flags & SURF_DRAWTELE) continue;
// go through the surfs in the chain
for (/**/; surf; surf = surf->texturechain)
{
// not a volume fit for this
if (!surf->volumetexture) continue;
// water surfs have a chained list of polys rather than just 1
for (p = surf->polys; p; p = p->next)
{
// need to go through each vert in the list to set the colours
for (i = 0; i < p->numverts; i++)
{
// get a pointer to the verts to keep the code more readable
v = p->water[i].v;
p->water[i].rgba[0] = surf->texinfo->texture->gl_texture->rgba[0];
p->water[i].rgba[1] = surf->texinfo->texture->gl_texture->rgba[1];
p->water[i].rgba[2] = surf->texinfo->texture->gl_texture->rgba[2];
if (surf->flags & SURF_DRAWLAVA)
{
// constant fog
fogalpha = FOG_ALPHA_LAVA_CONST;
}
else if (surf->flags & SURF_DRAWSLIME)
{
// constant fog
fogalpha = FOG_ALPHA_SLIME_CONST;
}
else
{
// distance fog
// use the transformed verts to get the dist
fogdist = (v[0] - r_origin[0]) * vpn[0] + (v[1] - r_origin[1]) * vpn[1] + (v[2] - r_origin[2]) * vpn[2];
if (fogdist < FOG_MINRANGE)
{
fogdist = FOG_MINRANGE;
}
else if (fogdist > FOG_MAXRANGE)
{
fogdist = FOG_MAXRANGE;
}
fogalpha = 0.3f + (fogdist / FOG_MAXRANGE * FOG_ALPHA_MAX) * 0.5f;
// boost alpha a little cos the translucent water can make it hard to see
fogalpha *= 1.25f;
}
p->water[i].rgba[3] = fogalpha;
}
// draw the vertex array
glDrawArrays (GL_TRIANGLE_FAN, p->waterindex, p->numverts);
}
}
}
}
// now bring color arrays back to the way they were.
glDisableClientState (GL_COLOR_ARRAY);
// kill vertex array
glDisableClientState (GL_VERTEX_ARRAY);
// bring texturing back
glEnable (GL_TEXTURE_2D);
// turn on Z and bring blend down again.
R_TurbDepthOff(fogalpha);
// some brain-dead drivers don;t restore from glColorMask properly, so...
glColor4f (TURB_NOALPHA_CONST, TURB_NOALPHA_CONST, TURB_NOALPHA_CONST, TURB_NOALPHA_CONST);
}
/*
================
R_DrawWaterSurfaces
Emulated water shaders inc.
================
*/
void R_DrawWaterSurfaces (void)
{
gltexture_t *tex;
msurface_t *surf;
glpoly_t *p;
int i;
float sadd, tadd;
float *v;
// drawing parameters
vector_t warp1scale;
vector_t warp2scale;
float warp2sign;
float warpalpha;
GLenum WARP1_TEXENV;
GLenum WARP2_TEXENV;
int Warp1Texture;
int Warp2Texture;
if (!GL_RenderState.waterthisframe) return;
// back to the world matrix
glLoadMatrixf (cl_entities[0].mvievmatrix);
// enable blending
R_TurbDepthOn(warpalpha);
// default blendfunc for this
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// enable tmu 1
glActiveTexture (GL_TEXTURE1);
glEnable (GL_TEXTURE_2D);
// enable vertex pointer - index 0 is the regular verts
glEnableClientState (GL_VERTEX_ARRAY);
glVertexPointer (3, GL_FLOAT, sizeof (waterverts_t), &waterarray[0]);
// enable texcoord pointer for textures - index 3 is the st coords for warp 1
glClientActiveTexture(GL_TEXTURE0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof( waterverts_t ), &waterarray[3]);
// lets do some shader magic here :)
R_TurbShaderOn();
// enable texcoord pointer for textures - index 5 is the st coords for warp 2
// added arb shader for non nvidia cards
glClientActiveTexture(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof( waterverts_t ), &waterarray[5]);
// enable color array
glEnableClientState (GL_COLOR_ARRAY);
glColorPointer (4, GL_FLOAT, sizeof (waterverts_t), &waterarray[7]);
// go through the texture list
for (tex = TextureList; tex; tex = tex->TextureList)
{
// nothing chained
if (!(surf = tex->texturechain)) continue;
// it ain't liquid
if (!(surf->flags & SURF_DRAWTURB)) continue;
// setup rendering parameters
// these are actually absolutely excellent
if (surf->flags & SURF_DRAWLAVA)
{
R_TurbDepthOff(warpalpha);
// because this layer isn't visible, give it a massive size to make rendering it less expensive
// (actually this doesn't matter cos the texture matrix is ignored here...)
warp1scale.vec.x = 1.0f / 1024.0f;
warp1scale.vec.y = 1.0f / 1024.0f;
warp1scale.vec.z = 1.0f;
warp2scale.vec.x = 1.0f / 128.0f;
warp2scale.vec.y = 1.0f / 128.0f;
warp2scale.vec.z = 1.0f;
warp2sign = WARPSIGN_POSITIVE;
warpalpha = TURB_NOALPHA_CONST;
WARP1_TEXENV = GL_ZERO; // special - instruct the renderer to ignore fancy stuff here
WARP2_TEXENV = GL_REPLACE; // make the 2nd warp a replace texenv also to avoid dual layer madness with lava
// we could use any old crap for warp1 here..
Warp1Texture = surf->texinfo->texture->gl_texture->gltexnum;
Warp2Texture = surf->texinfo->texture->gl_texture->gltexnum;
}
else if (surf->flags & SURF_DRAWTELE)
{
R_TurbDepthOff(warpalpha);
warp1scale.vec.x = 1.0f / 64.0f;
warp1scale.vec.y = 1.0f / 64.0f;
warp1scale.vec.z = 1.0f;
warp2scale.vec.x = 1.0f / 24.0f;
warp2scale.vec.y = 1.0f / 24.0f;
warp2scale.vec.z = 1.0f;
warp2sign = WARPSIGN_POSITIVE;
warpalpha = TURB_NOALPHA_CONST;
WARP1_TEXENV = GL_REPLACE;
WARP2_TEXENV = GL_ADD;
// we could use any old crap for warp1 here..
Warp1Texture = surf->texinfo->texture->gl_texture->gltexnum;
Warp2Texture = surf->texinfo->texture->gl_texture->gltexnum;
}
else if (surf->flags & SURF_DRAWSLIME)
{
R_TurbDepthOn(warpalpha);
warp1scale.vec.x = 1.0f / 128.0f;
warp1scale.vec.y = 1.0f / 128.0f;
warp1scale.vec.z = 1.0f;
warp2scale.vec.x = 1.0f / 256.0f;
warp2scale.vec.y = 1.0f / 256.0f;
warp2scale.vec.z = 1.0f;
warp2sign = WARPSIGN_NEGATIVE;
warpalpha = (r_wateralpha.value < TURB_ALPHA_MAX) ? TURB_ALPHA_SLIME_CONST : TURB_NOALPHA_CONST; // if it aint transparent to begin with...
WARP1_TEXENV = GL_MODULATE;
WARP2_TEXENV = GL_ADD;
// we could use any old crap for warp1 here..
Warp1Texture = surf->texinfo->texture->gl_texture->gltexnum;
Warp2Texture = surf->texinfo->texture->gl_texture->gltexnum;
}
else
{
R_TurbDepthOn(warpalpha);
warp1scale.vec.x = 1.0f / 128.0f;
warp1scale.vec.y = 1.0f / 128.0f;
warp1scale.vec.z = 1.0f;
warp2scale.vec.x = 1.0f / 256.0f;
warp2scale.vec.y = 1.0f / 256.0f;
warp2scale.vec.z = 1.0f;
warp2sign = WARPSIGN_POSITIVE;
warpalpha = bound(TURB_ALPHA_MIN, r_wateralpha.value, TURB_ALPHA_MAX);
WARP1_TEXENV = GL_MODULATE;
WARP2_TEXENV = GL_ADD;
// we could use any old crap for warp1 here..
Warp1Texture = surf->texinfo->texture->gl_texture->gltexnum;
Warp2Texture = surf->texinfo->texture->gl_texture->gltexnum;
}
// select texture 0
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, Warp1Texture);
// only do this if we're doing dual layer, otherwise it's cheaper to do a very basic render than it
// would be to change TMU states
if (WARP1_TEXENV != GL_ZERO)
{
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, WARP1_TEXENV);
glMatrixMode (GL_TEXTURE);
glLoadIdentity ();
glScalef (warp1scale.vecs[0], warp1scale.vecs[1], warp1scale.vecs[2]);
glMatrixMode (GL_MODELVIEW);
}
// select texture 1
glActiveTexture (GL_TEXTURE1);
glBindTexture (GL_TEXTURE_2D, Warp2Texture);
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, WARP2_TEXENV);
glMatrixMode (GL_TEXTURE);
glLoadIdentity ();
glScalef (warp2scale.vecs[0], warp2scale.vecs[1], warp2scale.vecs[2]);
glMatrixMode (GL_MODELVIEW);
// go through the surfs in the chain
for (/**/; surf; surf = surf->texturechain)
{
// water surfs have a chained list of polys rather than just 1
for (p = surf->polys; p; p = p->next)
{
// need to go through each vert in the list to set the warped texcoords
for (i = 0; i < p->numverts; i++)
{
// get a pointer to the verts to keep the code more readable
v = p->water[i].v;
// calculate the amount to add for the warp
sadd = STSinTable[(int) ((v[surf->warpindex[1]] * 0.025 + realtime) * STMult) & STBitMask];
tadd = STSinTable[(int) ((v[surf->warpindex[0]] * 0.025 + realtime) * STMult) & STBitMask];
// set the warp verts
// fix water warping - derive the texcoords from the verts!!! mad, but it works!!!
// warpindex is precalculated in gl_model.c - use the x2 STSinTable for 8 unit warps
p->water[i].warp1[0] = v[surf->warpindex[0]] + sadd;
p->water[i].warp1[1] = v[surf->warpindex[1]] + tadd;
// warp 2
p->water[i].warp2[0] = (v[surf->warpindex[0]] * warp2sign) + sadd;
p->water[i].warp2[1] = (v[surf->warpindex[1]] * warp2sign) + tadd;
// transparent water, yes we can have both this and fog alpha.
p->water[i].rgba[0] = TURB_NOALPHA_CONST;
p->water[i].rgba[1] = TURB_NOALPHA_CONST;
p->water[i].rgba[2] = TURB_NOALPHA_CONST;
p->water[i].rgba[3] = warpalpha;
}
// draw the vertex array
glDrawArrays (GL_TRIANGLE_FAN, p->waterindex, p->numverts);
}
}
}
// restore state on tmu 0
glActiveTexture (GL_TEXTURE1);
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glMatrixMode (GL_TEXTURE);
glLoadIdentity ();
glMatrixMode (GL_MODELVIEW);
glDisable(GL_TEXTURE_2D);
// restore state on tmu 1
glActiveTexture (GL_TEXTURE0);
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glMatrixMode (GL_TEXTURE);
glLoadIdentity ();
glMatrixMode (GL_MODELVIEW);
// now bring color arrays back to the way they were.
glDisableClientState (GL_COLOR_ARRAY);
// kill texcoord arrays
glClientActiveTexture (GL_TEXTURE1);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
// now bring it down again
R_TurbShaderOff();
glClientActiveTexture (GL_TEXTURE0);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
// kill vertex array
glDisableClientState (GL_VERTEX_ARRAY);
// now do the turbfog,
// we had both functions batched up here at one point,
// but it became a mess to maintain.
// besides it turns out we can have both,
// transparent water and transparent fog on the same leaf so :).
R_DrawTurbFog();
// potentially redundant if teleports or lava were drawn last and we're not underwater.
// it's only once per frame so live with it.
R_TurbDepthOff(warpalpha);
// some brain-dead drivers don;t restore from glColorMask properly, so...
glColor4f (TURB_NOALPHA_CONST, TURB_NOALPHA_CONST, TURB_NOALPHA_CONST, TURB_NOALPHA_CONST);
}
Dual layer liquids with fragment shaded water warp and underwater fog.
I went through the code with a sledgehammer and fixed most of the problems
(mostly some oversights in regards to not drawing to Z when there was no alpha channel active) and i cleaned it up a bit in the process by splitting out the fog code into a seperate function + creating a few utility functions for depth checking etc.
Phew so enough for today
now i need a good nights sleep.