Urban Software - UrbanCTF, UrbanDM, Earn A Weapon, CTG...
Coding Tutorials - Handling shaders on the Client Side

 

Date : 03/12/00
Author(s) :
SpK
Skill(s) :
Easy
Source Project(s) :
CGame
Revision :
1.0

 

1) What you'll learn
In this tutorial, we'll see how shaders (scripts that add special effects to the textures) are handled on the client side. We're going to add a "quad effect" on all the items around the map. And yes, this is totally absurd, but this is a great exemple to explain how to apply a new shader on an entity of the game.

2) Code it !
Open the file called cg_ents.c of your cgame project (make sure Cgame is your active project) and find the function static void CG_Item( centity_t *cent ). This function, called on each frames, runs all the game's items on the client side, and this is where we'll call our new shader function, in order to add a quad layer to our items, every frames.

Now copy/paste this code above the CG_Item function :

void AddQuadLayer (centity_t *cent)
{
       refEntity_t re;
       gitem_t *item;

       // if set to invisible, skip
       if (!cent->currentState.modelindex ||
           (cent->currentState.eFlags & EF_NODRAW )) 
              return;

       // Pointer to the item's properties
       item = &bg_itemlist[cent->currentState.modelindex];
       if (!item)
              return;

       memset( &re, 0, sizeof( re ) );
       re.reType = RT_MODEL;

       // Setup our custom shader (quad shader) and specify which model
       //
it will be applied to
       re.customShader = cgs.media.quadShader;
       re.hModel = cg_items[cent->currentState.modelindex].models[0];

       // We need to clear the refEntity's axis, else it won't show up
       // correctly (in won't show up at all in fact)

       AxisClear( re.axis );

       // Now, we make sure we're following the rotation of our item
       AnglesToAxis( cent->lerpAngles, re.axis );

       // world weapons models are bigger than "hand" weapons
       // so we need to scale the quadShader model

       if (item->giType == IT_WEAPON)
       {
              VectorScale( re.axis[0], 1.5, re.axis[0] );
              VectorScale( re.axis[1], 1.5, re.axis[1] );
              VectorScale( re.axis[2], 1.5, re.axis[2] );
       }

       // Set our refEntity's origin to the origin of the item
       VectorCopy( cent->lerpOrigin, re.origin );

       // Add our refEntity to the scene, so it can be rendered
       trap_R_AddRefEntityToScene( &re );
}

So, what are we doing ?

      refEntity_t re; // our ref entity

ref-entities ("render entities") are entities that are used to render special elements in the scene, such as grapple trails, and are usually accompanied with a shader call.

      memset( &re, 0, sizeof( re ));
   re.reType = RT_MODEL;

The first line resets our "re" refEntity's memory to 0, so we're sure it won't contain anything else than what we're gonna add to its content. The second line specify which render type of  we'll be using. In this case, we're using a model render type, as our quad shader will depend of the model of our items. reType can takes the following values :

      RT_MODEL
   RT_POLY
   RT_SPRITE
   RT_BEAM
   RT_RAIL_CORE
   RT_RAIL_RINGS
   RT_LIGHTNING
   RT_PORTALSURFACE

The most commons are RT_SPRITE and RT_BEAM.

   re.customShader = cgs.media.quadShader;
   re.hModel = cg_items[es->modelindex].models[0];

Here, we specify the shader we'll use, and the model it will apply to. The cgs.media.quadShader is defined in cg_main.c with the following line :

   cgs.media.quadShader = trap_R_RegisterShader("powerups/quad" );

In fact, quadShader is just an index to the quad shader script.

   AxisClear( re.axis ); // Important line
   AnglesToAxis( cent->lerpAngles, re.axis );

Here, we're makking sure that our quad layer will rotate in the same way as our item, else it won't have a good looking !

   VectorCopy( cent->lerpOrigin, re.origin );

Here, we're setting our refEntity's origin to the item's origin, so the quad layer will wrap it up.

    trap_R_AddRefEntityToScene( &re );

That's the most important line of all. If you forget it, the quad layer will just not be rendered at all. This function adds our refEntity to the "rendering queue".

Calling our new function
Okay, now we only need to call our new function at the end of the CG_Item function, so our quad layer will be rendered on each frame :

static void CG_Item( centity_t *cent ) 
{
       refEntity_t ent;
       entityState_t *es;
       gitem_t *item;

       ...
       ...

                     trap_R_AddRefEntityToScene( &ent );
                     }
              }
       }

       // SPK : apply a quad layer on this item
       AddQuadLayer (cent);
}

Now compile your code, and run the game with +set fs_game <your dir> +set sv_pure 0, and look at all those quaded items around the map... This is just an exemple of what you can do with the cgame part of the Q3Dll. But I'll let you discover the rest :)