I don't know if this is particular helpful because the implementation in Mark V does extra stuff so isn't very simple, but it works like this:
1. model.h (handles both WinQuake and GLQuake in Mark V)
flag values for a surface
Code: Select all
#define SURF_DRAWLAVA 0x400
#define SURF_DRAWSLIME 0x800
#define SURF_DRAWTELE 0x1000
#define SURF_DRAWWATER 0x2000
And keeping track of info for the level
Code: Select all
typedef struct
{
// The following are definitely known by client/server immediately because they occur in model load.
cbool water;
cbool lava;
cbool slime;
cbool teleporter;
cbool sky;
// The following are not immediately known. Although could probably have loader cross these off if no liquid textures.
cbool water_vis_known;
cbool water_vis;
} level_info_t;
extern level_info_t level;
Keeping track of info for the frame
Code: Select all
typedef struct
{
cbool oldwater; // Doesn't apply to WinQuake, just GL and the Direct3D version forces to 1 every frame.
cbool nearwaterportal; // GL + WinQuake
cbool has_underwater; // GL + WinQuake
cbool has_abovewater; // GL + WinQuake
cbool has_sky; // Baker: Could work for WinQuake but not relevant.
cbool has_mirror; // Baker: Would be real hard to make it work for WinQuake.
// Baker: Direct3D wrapper doesn't have stencil, so will have to have the mirror some other way.
// Baker: Will have to draw the sky the traditional way
float liquid_alpha; // WinQuake: A per surface indicator of how much alpha to apply for current liquid texture.
float wateralpha; // GL + WinQuake, alpha for the frame
float slimealpha; // GL + WinQuake, alpha for the frame
float lavaalpha; // GL + WinQuake, alpha for the frame
float mirroralpha; // Baker: Don't see this working in WinQuake easily, unless I add a stencil buffer to WinQuake
} frame_render_t;
extern frame_render_t frame;
2. model.c (handles both WinQuake and GLQuake in Mark V)
Code: Select all
else if (out->texinfo->texture->name[0] == '*') // warp surface
{
out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED);
if (!strncmp (out->texinfo->texture->name, "*lava", 5))
level.lava = true, out->flags |= SURF_DRAWLAVA;
else if (!strncmp (out->texinfo->texture->name, "*slime", 6))
level.slime = true, out->flags |= SURF_DRAWSLIME;
else if (Is_Texture_Prefix (out->texinfo->texture->name, r_texprefix_tele.string))
level.teleporter = true, out->flags |= SURF_DRAWTELE; // *tele ... so that r_wateralpha doesn't affect teleporters.
else level.water = true, out->flags |= SURF_DRAWWATER;
#ifdef GLQUAKE_RENDERER_SUPPORT
Mod_PolyForUnlitSurface (out);
GL_SubdivideSurface (out);
#endif // GLQUAKE_RENDERER_SUPPORT
#ifdef WINQUAKE_RENDERER_SUPPORT
for (i = 0; i < 2; i++)
{
out->extents[i] = 16384;
out->texturemins[i] = -8192;
}
#endif // WINQUAKE_RENDERER_SUPPORT
continue; // Baker: Done with warp surface
}
3. r_common.c (common renderer file shared between WinQuake and GLQuake) -- SetLiquidAlpha -- done once per frame
Code: Select all
void R_SetLiquidAlpha (void)
{
if (!level.water_vis_known && frame.has_abovewater && frame.has_underwater)
{
// Baker: Don't let this scenario lock us into a false reading.
// Although this would be an extremely hard scenario to generate, would take an incredibly well placed saved game
// and I tried hard to stand somewhere a save game cause this problem and couldn't despite my best efforts.
if (!frame.nearwaterportal && !r_novis.value)
{
Con_DPrintf ("AUTO WATER VIS: Level is vised!\n");
level.water_vis_known = true;
level.water_vis = true;
}
}
if (r_novis.value) frame.liquid_alpha = true; // r_novis 1
else if (level.water_vis_known && level.water_vis) frame.liquid_alpha = true; // Known to be watervised
else if (level.water_vis_known && !level.water_vis) frame.liquid_alpha = false; // Known to be not watervised
else if (frame.has_abovewater && frame.has_underwater) frame.liquid_alpha = true; // Weird situation almost impossible
else frame.liquid_alpha = false; // Vis not known yet, but no water brushes in scene
if (frame.liquid_alpha)
{
frame.wateralpha = CLAMP(0, r_wateralpha.value, 1.0);
frame.slimealpha = CLAMP(0, r_slimealpha.value, 1.0);
frame.lavaalpha = CLAMP(0, r_lavaalpha.value, 1.0);
} else frame.wateralpha = frame.slimealpha = frame.lavaalpha = 1;
}
4. R_MarkLeaves in WinQuake in r_main.c, R_MarkSurfaces in FitzQuake renderer
Code: Select all
// Baker:
{
msurface_t **mark;
for (i = 0, mark = cl.r_viewleaf->firstmarksurface; i < cl.r_viewleaf->nummarksurfaces; i++, mark++)
if ((*mark)->flags & SURF_DRAWTURB)
frame.nearwaterportal = true; // Baker: frame vars are reset to false each frame
}
...
if (frame.nearwaterportal && !level.water_vis_known)
{
// Baker: This is to avoid a spawn nearwaterportal situation confusing the detection which could be
// in theory caused by a save game happening to save in such a rare place. This is an almost impossible
// scenario.
if (cl.r_visframecount != 0)
{
Con_DPrintf ("AUTO WATER VIS: Level is NOT vised!\n");
level.water_vis_known = true;
level.water_vis = false;
}
}
5. R_DrawTextureChains_Multitexture in GL, R_RecursiveWorldNode in WinQuake
Code: Select all
if (!level.water_vis_known)
{
if (s->flags & SURF_UNDERWATER)
frame.has_underwater = true;
else
frame.has_abovewater = true;
}