Inside3D!
     

Tutorial: Adding Alpha Transparency to stock GLQUAKE
Goto page 1, 2  Next
 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> Programming Tutorials
View previous topic :: View next topic  
Author Message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Fri Nov 21, 2008 1:43 pm    Post subject: Tutorial: Adding Alpha Transparency to stock GLQUAKE Reply with quote



Adding Alpha Transparency to stock GLQUAKE

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

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

Quote:
Code:
.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.)

Quote:
Code:
{
"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

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

Code:
#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

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

Code:
#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

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


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


4. render.h

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

Code:
#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

Quote:
cl_main.c: Find our CL_RelinkEntities. Find this at the end of the procedure ...

Code:
      if (cl_numvisedicts < MAX_VISEDICTS)
      {
         cl_visedicts[cl_numvisedicts] = ent;
         cl_numvisedicts++;
      }


Add this immediately after:

Code:
#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

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

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


Add immediately before:

Code:
#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

Quote:
gl_rsurf.c: Find "void R_DrawBrushModel (entity_t *e)". Find this ...

Code:
   if (R_CullBox (mins, maxs))
      return;

   glColor3f (1,1,1);


(7A) Replace with this:

Code:
   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:
   R_BlendLightmaps ();

   glPopMatrix ();


(7B) Add after:

Code:

   if (ISTRANSPARENT(e))
   {
      glColor3f (1,1,1);
      glDisable (GL_BLEND);
   }



8. pr_edict.c

Quote:
pr_edict.c: Find:

Code:
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:
// 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:
   for (i=0 ; i<progs->numglobals ; i++)
      ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);


And replace with:

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

Quote:
sv_main.c
: Find "SV_WriteEntitiesToClient" ... you will see:

Code:
   int      e, i;
   int      bits;
   byte   *pvs;
   vec3_t   org;
   float   miss;
   edict_t   *ent;


(9A) Add this immediately after:

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


Find:

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


(9B) Add this immediately after:

Code:
#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:
      if (bits & U_ANGLE3)
         MSG_WriteAngle(msg, ent->v.angles[2]);


(9C) Add this immediately after:

Code:
#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
Back to top
View user's profile Send private message
avirox



Joined: 16 Aug 2006
Posts: 109

PostPosted: Fri Nov 21, 2008 8:28 pm    Post subject: Reply with quote

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!
Back to top
View user's profile Send private message
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Fri Nov 21, 2008 8:29 pm    Post subject: Reply with quote

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.
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
metlslime



Joined: 05 Feb 2008
Posts: 177

PostPosted: Fri Nov 21, 2008 10:31 pm    Post subject: Reply with quote

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.
Back to top
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Fri Nov 21, 2008 11:11 pm    Post subject: Reply with quote

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
Back to top
View user's profile Send private message
metlslime



Joined: 05 Feb 2008
Posts: 177

PostPosted: Fri Nov 21, 2008 11:39 pm    Post subject: Reply with quote

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.
Back to top
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Fri Nov 21, 2008 11:43 pm    Post subject: Reply with quote

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?) Wink
Back to top
View user's profile Send private message
metlslime



Joined: 05 Feb 2008
Posts: 177

PostPosted: Sat Nov 22, 2008 12:58 am    Post subject: Reply with quote

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?) Wink


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.
Back to top
View user's profile Send private message
goldenboy



Joined: 05 Sep 2008
Posts: 310
Location: Kiel

PostPosted: Fri Nov 28, 2008 7:38 pm    Post subject: Reply with quote

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.
Back to top
View user's profile Send private message
leileilol



Joined: 15 Oct 2004
Posts: 1321

PostPosted: Fri Nov 28, 2008 7:50 pm    Post subject: Reply with quote

Then add alpha transparency to stock winquake too to make it fair - just rip the code from Quake2!
Back to top
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Sat Nov 29, 2008 12:23 pm    Post subject: Reply with quote

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.
Back to top
View user's profile Send private message
leileilol



Joined: 15 Oct 2004
Posts: 1321

PostPosted: Sat Nov 29, 2008 12:59 pm    Post subject: Reply with quote

*facepalm*

You completely misunderstood the code. Quake2 uses 256 colors. What that comment means a lookup table at the size of 256x256 pixels big.
Back to top
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Sat Nov 29, 2008 3:08 pm    Post subject: Reply with quote

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.



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.
Back to top
View user's profile Send private message
r00k



Joined: 13 Nov 2004
Posts: 483

PostPosted: Sat Nov 29, 2008 7:06 pm    Post subject: Reply with quote

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:

   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:

      if (bits & U_TRANS)
      {
         MSG_WriteFloat (msg, 2);
         MSG_WriteFloat (msg, alpha);
         MSG_WriteFloat (msg, fullbright);
      }
Back to top
View user's profile Send private message
goldenboy



Joined: 05 Sep 2008
Posts: 310
Location: Kiel

PostPosted: Sat Nov 29, 2008 11:33 pm    Post subject: Reply with quote

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
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Inside3d Forums Forum Index -> Programming Tutorials All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2004 phpBB Group