Forum

Tutorial: Adding Alpha Transparency to stock GLQUAKE

Post tutorials on how to do certain tasks within game or engine code here.

Moderator: InsideQC Admins

Tutorial: Adding Alpha Transparency to stock GLQUAKE

Postby Baker » Fri Nov 21, 2008 1:43 pm

Image

Adding Alpha Transparency to stock GLQUAKE

Alpha support lets an engine render func_walls and other map objects with transparency.

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.


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:

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

Code: Select all
.float alpha;


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
{
"classname" "func_wall"
"alpha" "0.2"
"targetname" "fiendfield"
...
}


Instuctions adding it to stock GLQuake

We are going to do some light changes to NINE files.

Let's hit the header files first.

1. glquake.h

glquake.h - Locate "texture_t *R_TextureAnimation ..."; insert this line right after:

Code: Select all
#define ISTRANSPARENT(ent)   ((ent)->istransparent && (ent)->transparency > 0 && (ent)->transparency < 1)


We are defining the test of whether or not a brush is transparent.


2. progs.h

progs.h - At bottom of file add ...:

Code: Select all
#define   GETEDICTFIELDVALUE(ed, fieldoffset) (fieldoffset ? (eval_t *)((byte *)&ed->v + fieldoffset) : NULL)
// alpha support
extern   int   eval_alpha;


We are borrowing JoeQuake's speedy field lookup shortcut for checking entities. The field we will be using this with is alpha, of course.


3. protocol.h

protocol.h - Find "#define U_LONGENTITY (1<<14)", insert after:
Code: Select all
// alpha support
#ifdef GLQUAKE
#define   U_TRANS      (1<<15)
#endif


We need to add a transparency bit to the protocol definition.


4. render.h

render.h - Find "struct mnode_s *topnode;", insert after:

Code: Select all
#ifdef GLQUAKE
      
      // alpha support
      float         transparency;
      qboolean       istransparent;
#endif


We need the transparency characteristics of the entities stored and readily available.


Now let's do the actual code ...

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:

Code: Select all
#ifdef GLQUAKE
      if (!ent->transparency)
         ent->transparency = 1;
#endif


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.



6. cl_parse.c

cl_parse.c: Find "void CL_ParseUpdate (int bits)". Find this ...

Code: Select all
   if ( bits & U_NOLERP )
      ent->forcelink = true;


Add immediately before:

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


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.


7. gl_rsurf.c

gl_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:

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


There is where we are drawing the brush models and if there is transparency we need it to render tranparently.

Now we need to turn it off afterwards ...

FIND about 30 lines further down in gl_rsurf.c:
Code: Select all
   R_BlendLightmaps ();

   glPopMatrix ();


(7B) Add after:

Code: Select all
   if (ISTRANSPARENT(e))
   {
      glColor3f (1,1,1);
      glDisable (GL_BLEND);
   }



8. pr_edict.c

pr_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:

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


(8B) Now at bottom of PR_LoadProgs find:

Code: Select all
   for (i=0 ; i<progs->numglobals ; i++)
      ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);


And replace with:

Code: Select all
   for (i=0 ; i<progs->numglobals ; i++)
      ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);

   FindEdictFieldOffsets ();


Speedy field lookup functions.


9. LAST: sv_main.c

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:

Code: Select all
#ifdef GLQUAKE
   eval_t  *val;
   float   alpha;
#endif


Find:

Code: Select all
      if (ent->baseline.modelindex != ent->v.modelindex)
         bits |= U_MODEL;


(9B) Add this immediately after:

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


Find:

Code: Select all
      if (bits & U_ANGLE3)
         MSG_WriteAngle(msg, ent->v.angles[2]);


(9C) Add this immediately after:

Code: Select all
#ifdef GLQUAKE
      if (bits & U_TRANS)
      {
                MSG_WriteFloat (msg, 2);
                MSG_WriteFloat (msg, alpha);
      }
#endif


The above is where the server sends the data to the client if the tranparency bit is set.



THE END
User avatar
Baker
 
Posts: 3661
Joined: Tue Mar 14, 2006 5:15 am

Postby avirox » Fri Nov 21, 2008 8:28 pm

Baker you are a life saver! I've been trying to figure out getting alpha to work on brush models in ezquake. I'll give this a shot tomorrow night, and hopefully if it works the ezq devs will add my alpha .patch to the SVN build along with your brush alpha method. Thanks!
avirox
 
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Postby Spike » Fri Nov 21, 2008 8:29 pm

MSG_WriteFloat (msg, 2);
MSG_WriteFloat (msg, alpha);


I'm sorry, but... EGADS!
the first float is redundant, the second float is 4 times as large as is really useful.

looks good other than that though.
Spike
 
Posts: 2878
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby metlslime » Fri Nov 21, 2008 10:31 pm

currently, this tutorial creates an engine that is only good for single-player, and won't even be able to read stock quake demos. might be good to:

1. change the protocol number so other clients will know they can't talk to this modified server, and can't play demos recorded with this modified client.

2. have the modified client check for protocol version and not read the extra bytes if protocol == 15, otherwise you won't be able to read stock protocol 15 demos.

3. get rid of most of the #ifdef GLQUAKE lines, otherwise the win and gl compiles from the same codebase won't even be able to talk to each other. The only thing winquake should do differently from glquake is ignore transparency when rendering. The network code and server-side stuff should be identical between the two builds.
metlslime
 
Posts: 316
Joined: Tue Feb 05, 2008 11:03 pm

Postby Baker » Fri Nov 21, 2008 11:11 pm

metlslime wrote:currently, this tutorial creates an engine that is only good for single-player, and won't even be able to read stock quake demos.


I'm going to write another tutorial replacing this one with the rest of the Nehahra supported added in, so the protocol used in the demos at least matches something that exists.

I'll have to check and find out what protocol number Nehahra uses (I hope it does have its own and isn't using 15).

The above is derived from JoeQuake and it will play regular Quake demos. And write regular Quake demos if the transparency feature isn't being used (id1, etc.), but if a demo were recorded running a mod with the alpha transparency field ...

I'll do some research into what aguirRe's convert demos utility does and see maybe if there is some way to add some sort of universal [ish] demo compatibility.

So I'll see what the existing Nehahra standard is.
Last edited by Baker on Fri Nov 21, 2008 11:40 pm, edited 1 time in total.
User avatar
Baker
 
Posts: 3661
Joined: Tue Mar 14, 2006 5:15 am

Postby metlslime » Fri Nov 21, 2008 11:39 pm

actually, i realize i was wrong about demos, it won't actually choke on quake demos, since without that U_TRANS flag, it won't try to read extra floats.

But i think it is correct that if the modified server claims to use protocol 15, regular clients will crash when they see a transparent entity. And a demo with transparent entities in it will crash a standard client.
metlslime
 
Posts: 316
Joined: Tue Feb 05, 2008 11:03 pm

Postby Baker » Fri Nov 21, 2008 11:43 pm

metlslime wrote:actually, i realize i was wrong about demos, it won't actually choke on quake demos, since without that U_TRANS flag, it won't try to read extra floats.

But i think it is correct that if the modified server claims to use protocol 15, regular clients will crash when they see a transparent entity. And a demo with transparent entities in it will crash a standard client.


Ah you replied while I was editing.

This topic needs some more research. Because I can see some very fun protocol breaking extras that would keep running into this issue again, again above and beyond this.

Not to mention whatever you have cooked up for FitzQuake 0.85 (aguirRe's protocol?) ;)
User avatar
Baker
 
Posts: 3661
Joined: Tue Mar 14, 2006 5:15 am

Postby metlslime » Sat Nov 22, 2008 12:58 am

Baker wrote:This topic needs some more research. Because I can see some very fun protocol breaking extras that would keep running into this issue again, again above and beyond this.

Not to mention whatever you have cooked up for FitzQuake 0.85 (aguirRe's protocol?) ;)


I think in general, when making a new protocol you should use a new number, and have both client and server be able to handle both 15 and any new number you want to implement. There are several protocol 15s out there now, and most of them do operate on the philosophy that "if you happen to run a map/mod that doesn't use these features, it will devolve into standard protocol 15."

That may be true, but the whole point of a protocol number is to tell the client whether the information is going to be comprehensible. But with these protocols, you get a "maybe it'll work, maybe you'll crash halfway through."

As for fitzquake's protocol, my plan has been to basically pack as much as i reasonably can into one new protocol, rather than slowly adding a feature at a time and then having 10 different versions to support. There will still probably be a need for another version someday, but I'd rather have a few major version than many minor versions floating around.
metlslime
 
Posts: 316
Joined: Tue Feb 05, 2008 11:03 pm

Postby goldenboy » Fri Nov 28, 2008 7:38 pm

The only thing winquake should do differently from glquake is ignore transparency when rendering. The network code and server-side stuff should be identical between the two builds.

Exactly.

I think "Winquake" should simply not render transparent brush entities - so you can still look through windows. All other transparent entities should just be solid in software.

Sure, you'll alert monsters etc - but that's the mapper's problem. Monsters behind an .alpha func_wall really should be alerted, too.
User avatar
goldenboy
 
Posts: 924
Joined: Fri Sep 05, 2008 11:04 pm
Location: Kiel

Postby leileilol » Fri Nov 28, 2008 7:50 pm

Then add alpha transparency to stock winquake too to make it fair - just rip the code from Quake2!
leileilol
 
Posts: 2783
Joined: Fri Oct 15, 2004 3:23 am

Postby Baker » Sat Nov 29, 2008 12:23 pm

leileilol wrote:Then add alpha transparency to stock winquake too to make it fair - just rip the code from Quake2!


Looks like that might take a little bit of work

Quake 2 source wrote: pixel_t *alphamap; // 256 * 256 translucency map


It is my understanding that the winquake window is initialized in a 256 color palette mode. It looks like the q2 method uses 256^2 colors.

Options:

1. Increase the # of colors available in WinQuake to 2^16 or 2^24 so the alpha effect is possible (might reduce speed, then again Quake 2 doesn't seem slow in software rendering mode .. but I don't have a slow 400 Mhz machine to be able to know).

2. Render transparent brushes in WinQuake as a "screen" where 25% of the pixels [or some other ratio] are fully transparent (Half-Life appears to do this.] using a predefined mask.

3. Some sort of improvised 256 color method that fakes an alpha table with the palette.
User avatar
Baker
 
Posts: 3661
Joined: Tue Mar 14, 2006 5:15 am

Postby leileilol » Sat Nov 29, 2008 12:59 pm

*facepalm*

You completely misunderstood the code. Quake2 uses 256 colors. What that comment means a lookup table at the size of 256x256 pixels big.
leileilol
 
Posts: 2783
Joined: Fri Oct 15, 2004 3:23 am

Postby Baker » Sat Nov 29, 2008 3:08 pm

leileilol wrote:*facepalm*

You completely misunderstood the code. Quake2 uses 256 colors. What that comment means a lookup table at the size of 256x256 pixels big.


Color mixing table: color (0-255) with alpha (0-255) = result color2 (0-255)? If so, wouldn't this table be sort of a pain to figure out for Quake 1.

Either way, that leads to a simple idea on how to quickly make a translucent looking glass in software not as fancy as Quake 2.

Image

Create a function to return the palette position a couple of columns shifted based on the alpha. It could look wrong with colors in the fullbright rows, but a minor side downside on a small minority of colors.

Or at least in theory it might look ok.

Unless someone has an idea on how the Quake 2 method (that I still may or may not completely understand) could be employed.
User avatar
Baker
 
Posts: 3661
Joined: Tue Mar 14, 2006 5:15 am

Postby r00k » Sat Nov 29, 2008 7:06 pm

Spike wrote:MSG_WriteFloat (msg, 2);
MSG_WriteFloat (msg, alpha);


I'm sorry, but... EGADS!
the first float is redundant, the second float is 4 times as large as is really useful.

looks good other than that though.


the 1st float of 2 it to set FULLBRIGHT entities in Nehahra,
Code: Select all
   if (bits & U_TRANS)
   {
      int   fullbright, temp;

      temp = MSG_ReadFloat ();
      ent->transparency = MSG_ReadFloat ();
      if (temp == 2)
         fullbright = MSG_ReadFloat ();
   }


which seems odd cause temp is always going to be 2
Code: Select all
      if (bits & U_TRANS)
      {
         MSG_WriteFloat (msg, 2);
         MSG_WriteFloat (msg, alpha);
         MSG_WriteFloat (msg, fullbright);
      }
r00k
 
Posts: 1108
Joined: Sat Nov 13, 2004 10:39 pm

Postby goldenboy » Sat Nov 29, 2008 11:33 pm

I can run Quake 2 on a Pentium 1, and software transparency on those windows seems no problem at all in 512x384 or 400x300. It's fluid. Same FPS as Quake on that machine. Well maybe 10% less or something. But it runs OK.

I think I remember transparent water in software in FTE - dunno if it also does alpha entities. I should test. :-E
User avatar
goldenboy
 
Posts: 924
Joined: Fri Sep 05, 2008 11:04 pm
Location: Kiel

Next

Return to Programming Tutorials

Who is online

Users browsing this forum: No registered users and 1 guest