Adding Alpha Transparency to stock GLQUAKE
Alpha support lets an engine render func_walls and other map objects with transparency.
There are some other modified engines with alpha brush support, but I'm not going to do research on it except I know the following engines do NOT have it:Currently alpha brush supporting:
DarkPlaces, FTEQW, JoeQuake, Qrack and aguirRe GLQuake and probably a number of modder engines.
Currently NOT alpha brush supporting:
FitzQuake, ezQuake, FuhQuake, TyrQuake, most others.
This tutorial is for support of alpha brushes (i.e. not monsters and sprites). I can't think of a good use of transparent monsters or sprites, but I do like the option of maps with glass looking objects in them.
This tutorial won't give WinQuake this effect, but nothing in this tutorial adversely affects WinQuake (it will compile and run fine with this code).
Before we begin:
1. A good example map is Forwards Compatible (link)
2. Enabling alpha support requires a custom progs.dat file. Merely grab the QuakeC 1.06 source (download). Open up defs.qc and add this to the bottom of the file and compile it with your QuakeC compiler of choice (like FTEQCC).
3. To make a func_wall or other map object transparent, make sure the brush has an alpha field with some value less than 1 but greater than 0 in the .map file (you'd probably need to manually add the field to a map editor entity definition file.)Code: Select all
.float alpha;
Instuctions adding it to stock GLQuakeCode: Select all
{ "classname" "func_wall" "alpha" "0.2" "targetname" "fiendfield" ... }
We are going to do some light changes to NINE files.
Let's hit the header files first.
1. glquake.h
2. progs.hglquake.h - Locate "texture_t *R_TextureAnimation ..."; insert this line right after:
We are defining the test of whether or not a brush is transparent.Code: Select all
#define ISTRANSPARENT(ent) ((ent)->istransparent && (ent)->transparency > 0 && (ent)->transparency < 1)
3. protocol.hprogs.h - At bottom of file add ...:
We are borrowing JoeQuake's speedy field lookup shortcut for checking entities. The field we will be using this with is alpha, of course.Code: Select all
#define GETEDICTFIELDVALUE(ed, fieldoffset) (fieldoffset ? (eval_t *)((byte *)&ed->v + fieldoffset) : NULL) // alpha support extern int eval_alpha;
4. render.hprotocol.h - Find "#define U_LONGENTITY (1<<14)", insert after:We need to add a transparency bit to the protocol definition.Code: Select all
// alpha support #ifdef GLQUAKE #define U_TRANS (1<<15) #endif
Now let's do the actual code ...render.h - Find "struct mnode_s *topnode;", insert after:
We need the transparency characteristics of the entities stored and readily available.Code: Select all
#ifdef GLQUAKE // alpha support float transparency; qboolean istransparent; #endif
5. cl_main.c
cl_main.c: Find our CL_RelinkEntities. Find this at the end of the procedure ...
Code: Select all
if (cl_numvisedicts < MAX_VISEDICTS) { cl_visedicts[cl_numvisedicts] = ent; cl_numvisedicts++; }
Add this immediately after:
This was in JoeQuake 0.14 Build 839. Seems superfluous to me. Looks like it is making sure any value of 0 is set as 1. I'm pretty sure this isn't needed, but just to be safe.Code: Select all
#ifdef GLQUAKE if (!ent->transparency) ent->transparency = 1; #endif
6. cl_parse.c
7. gl_rsurf.ccl_parse.c: Find "void CL_ParseUpdate (int bits)". Find this ...
Code: Select all
if ( bits & U_NOLERP ) ent->forcelink = true;
Add immediately before:
This is where the client is reading the data from the server and if the transparency bit is set, it needs to read in the transparency and set the entity attributes accordingly.Code: Select all
#ifdef GLQUAKE if (bits & U_TRANS) { int temp; temp = MSG_ReadFloat (); ent->istransparent = true; ent->transparency = MSG_ReadFloat (); } else { ent->istransparent = false; ent->transparency = 1.0; } #endif
8. pr_edict.cgl_rsurf.c: Find "void R_DrawBrushModel (entity_t *e)". Find this ...
Code: Select all
if (R_CullBox (mins, maxs)) return; glColor3f (1,1,1);
(7A) Replace with this:
There is where we are drawing the brush models and if there is transparency we need it to render tranparently.Code: Select all
if (R_CullBox (mins, maxs)) return; if (ISTRANSPARENT(e)) { glEnable (GL_BLEND); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f (1, 1, 1, e->transparency); } else { glColor3f (1,1,1); }
Now we need to turn it off afterwards ...
FIND about 30 lines further down in gl_rsurf.c:(7B) Add after:Code: Select all
R_BlendLightmaps (); glPopMatrix ();
Code: Select all
if (ISTRANSPARENT(e)) { glColor3f (1,1,1); glDisable (GL_BLEND); }
9. LAST: sv_main.cpr_edict.c: Find:
Code: Select all
typedef struct { ddef_t *pcache; char field[MAX_FIELD_LEN]; } gefv_cache; static gefv_cache gefvCache[GEFV_CACHESIZE] = {{NULL, ""}, {NULL, ""}};
(8A) Add this immediately after:
(8B) Now at bottom of PR_LoadProgs find:Code: Select all
// alpha specific int eval_alpha; ddef_t *ED_FindField (char *name); int FindFieldOffset (char *field) { ddef_t *d; if (!(d = ED_FindField(field))) return 0; return d->ofs*4; } void FindEdictFieldOffsets (void) { eval_alpha = FindFieldOffset ("alpha"); }
And replace with:Code: Select all
for (i=0 ; i<progs->numglobals ; i++) ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);
Speedy field lookup functions.Code: Select all
for (i=0 ; i<progs->numglobals ; i++) ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]); FindEdictFieldOffsets ();
sv_main.c
: Find "SV_WriteEntitiesToClient" ... you will see:
Code: Select all
int e, i; int bits; byte *pvs; vec3_t org; float miss; edict_t *ent;
(9A) Add this immediately after:
Find:Code: Select all
#ifdef GLQUAKE eval_t *val; float alpha; #endif
(9B) Add this immediately after:Code: Select all
if (ent->baseline.modelindex != ent->v.modelindex) bits |= U_MODEL;
Find:Code: Select all
#ifdef GLQUAKE // nehahra: model alpha if ((val = GETEDICTFIELDVALUE(ent, eval_alpha))) alpha = val->_float; else alpha = 1; if (alpha < 1 && alpha > 0) bits |= U_TRANS; #endif
(9C) Add this immediately after:Code: Select all
if (bits & U_ANGLE3) MSG_WriteAngle(msg, ent->v.angles[2]);
The above is where the server sends the data to the client if the tranparency bit is set.Code: Select all
#ifdef GLQUAKE if (bits & U_TRANS) { MSG_WriteFloat (msg, 2); MSG_WriteFloat (msg, alpha); } #endif
THE END