Tutorial *90*

Well, after giving coloured light to the Quake worlds I have created, I received an email from Alien_Hunter. He pointed out to me, that even though the worlds have coloured lighting, it does not effect the models that are in the world. I hate to do a job and not complete it, so here is the solution to this problem.
Please Note :
  • You must have completed the Coloured Dynamic Lights and the Coloured Static Lights tutorials before doing this one.
  • Some code in RecursiveLightPoint is from Alien_Hunter (thanks).
  • You must run the program with the -lm_4 switch to get all of these coloured lighting tutorials to work. [ed: Unless you hardcode the gl_lightmap_format in GL_BuildLightMaps (gl_rsurf.c)]
  • People that have followed the interpolation tutorials, will already have modified some of the functions that we must change in this tutorial. If that is your case, I have clearly commented where my code changes begin and end, so you should be able to just copy and paste my changes to your code without too many hassles. Remember to change the interpolated drawing function as well as the normal one. [ed: It's easier to just replace the functions within, and re-add the few lines of coded needed for interpolation. At least for model & animation interpolation. In Gl_DrawAliasBlendedFrame, remember to use d[0] not shadedots :]

    The light for alias models, is calculated in R_LightPoint, which calls RecursiveLightPoint to do the work, so that is as good a place to start as any. The idea behind this tutorial, is to change the calculating of light, so as to return the RGBA components, so that we can apply them to our models.

    The easiest way to do this without disturbing too much of id's code, is to change the R_LightPoint, and RecursiveLightPoint functions to return int*'s instead of int's, that way we can return the full RGBA array. So let's get started...

    In gl_rlight.c replace RecursiveLightPoint with the following version
    
    
    // CSL - epca@powerup.com.au
    
    // No idea what "r" stands for so...
    
    static int      myr[4];
    
    int *RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end)
    
    // CSL
    
    {
    
        // CSL - epca@powerup.com.au
    
        // "r[3]" is the old "r"
    
        int         *r = myr;
    
        // CSL
    
        float       front, back, frac;
    
        int         side;
    
        mplane_t    *plane;
    
        vec3_t      mid;
    
        msurface_t  *surf;
    
        int         s, t, ds, dt;
    
        int         i;
    
        mtexinfo_t  *tex;
    
        byte        *lightmap;
    
        unsigned    scale;
    
        int         maps;
    
    
    
        // CSL - epca@powerup.com.au
    
        r[0] = 0; r[1] = 0; r[2] = 0; r[3] = 0;
    
        // CSL
    
    
    
        if (node->contents < 0)
    
        {
    
            // CSL - epca@powerup.com.au
    
            // didn't hit anything
    
            r[3] = -1;
    
            return r;
    
            // CSL
    
        }
    
    
    
    // calculate mid point
    
    
    
    // FIXME: optimize for axial
    
        plane = node->plane;
    
        front = DotProduct (start, plane->normal) - plane->dist;
    
        back = DotProduct (end, plane->normal) - plane->dist;
    
        side = front < 0;
    
    
    
        if ( (back < 0) == side)
    
            return RecursiveLightPoint (node->children[side], start, end);
    
    
    
        frac = front / (front-back);
    
        mid[0] = start[0] + (end[0] - start[0])*frac;
    
        mid[1] = start[1] + (end[1] - start[1])*frac;
    
        mid[2] = start[2] + (end[2] - start[2])*frac;
    
    
    
    // go down front side
    
        r = RecursiveLightPoint (node->children[side], start, mid);
    
        // CSL - epca@powerup.com.au
    
        if (r[3] >= 0)
    
        // CSL
    
            return r;       // hit something
    
    
    
        if ( (back < 0) == side )
    
        {
    
            // CSL - epca@powerup.com.au
    
            // didn't hit anything
    
            r[3] = -1;
    
            return r;
    
            // CSL
    
        }
    
    
    
    // check for impact on this node
    
        VectorCopy (mid, lightspot);
    
        lightplane = plane;
    
    
    
        surf = cl.worldmodel->surfaces + node->firstsurface;
    
        for (i=0 ; i<node->numsurfaces ; i++, surf++)
    
        {
    
            if (surf->flags & SURF_DRAWTILED)
    
                continue;   // no lightmaps
    
    
    
            tex = surf->texinfo;
    
    
    
            s = DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3];
    
            t = DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3];;
    
    
    
            if (s < surf->texturemins[0] ||
    
            t < surf->texturemins[1])
    
                continue;
    
    
    
            ds = s - surf->texturemins[0];
    
            dt = t - surf->texturemins[1];
    
    
    
            if ( ds > surf->extents[0] || dt > surf->extents[1] )
    
                continue;
    
    
    
            if (!surf->samples)
    
            {
    
                // CSL - epca@powerup.com.au
    
                return r;
    
                // CSL
    
            }
    
    
    
            ds >>= 4;
    
            dt >>= 4;
    
    
    
            lightmap = surf->samples;
    
            if (lightmap)
    
            {
    
                // CSL - epca@powerup.com.au
    
                if (bspextensions)
    
                    lightmap += (dt * ((surf->extents[0]>>4)+1) + ds) * 4;
    
                else
    
                    lightmap += dt * ((surf->extents[0]>>4)+1) + ds;
    
                // CSL
    
    
    
                for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; maps++)
    
                {
    
                    scale = d_lightstylevalue[surf->styles[maps]];
    
                    // CSL - epca@powerup.com.au
    
                    if (bspextensions)
    
                    {
    
                        // Calculate effect of coloured lights on alias model
    
                        r[0] += lightmap[0] * scale;
    
                        r[1] += lightmap[1] * scale;
    
                        r[2] += lightmap[2] * scale;
    
                        r[3] += lightmap[3] * scale;
    
                        lightmap += (((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)) * 4;
    
                    }
    
                    else
    
                    {
    
                        // No coloured lights to play with :(
    
                        r[3] += *lightmap * scale;
    
                        lightmap += ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1);
    
                    }
    
                    // CSL
    
                }
    
    
    
                // CSL - epca@powerup.com.au
    
                if (!bspextensions)
    
                {
    
                    r[0] = 0; r[1] = 0; r[2] = 0;
    
                }
    
                else
    
                {
    
                    // Faster to assign each, than with a loop
    
                    r[0] >>= 8;
    
                    r[1] >>= 8;
    
                    r[2] >>= 8;
    
                }
    
                r[3] >>= 8;
    
                // CSL
    
            }
    
            return r;
    
        }
    
    
    
        // go down back side
    
        return RecursiveLightPoint (node->children[!side], mid, end);
    
    }
    
    
    That function, will now return the RGBA compenents of the colours that are effecting this point in space. It does not take into account dynamic lights, just those colours from the lightmaps (dynamic lights are taken care of whilst preparing to draw the model). Now that RecursiveLightPoint returns the correct values, R_LightPoint must be changed, to return the RGBA components as well.

    In gl_rlight.c replace R_LightPoint with the following version
    
    
    // CSL - epca@powerup.com.au
    
    int *R_LightPoint (vec3_t p)
    
    // CSL
    
    {
    
        vec3_t      end;
    
        // CSL - epca@powerup.com.au
    
        // "r[3]" is old "r"
    
        int         *r = myr;
    
        // CSL
    
    
    
        if (!cl.worldmodel->lightdata)
    
        {
    
            // CSL - epca@powerup.com.au
    
            r[0] = 255; r[1] = 255; r[2] = 255; r[3] = 255;
    
            return r;
    
            // return 255;
    
            // CSL
    
        }
    
    
    
        end[0] = p[0];
    
        end[1] = p[1];
    
        end[2] = p[2] - 2048;
    
    
    
        r = RecursiveLightPoint (cl.worldmodel->nodes, p, end);
    
    
    
        // CSL - epca@powerup.com.au
    
        if (r[3] == -1)
    
            r[3] = 0;
    
        // CSL
    
    
    
        return r;
    
    }
    
    
    Now that R_LightPoint returns the correct RGBA components of the lightmap colours effecting the point in space, we must change the way that they are interpreted when they are returned.

    To start with, the old variable that handled the shading of models, was shadelight. shadelight stored the intensity of light at a point in space, and we want to store the intensity as well as the colour. So we must search through the file gl_rmain.c, and replace all references to shadelight with the appropriate new variable. To start with, let's declare the new variable.

    In gl_rmain.c Replace :
    
    
    float   shadelight, ambientlight;
    
    
    With :
    
    
    // CSL - epca@powerup.com.au
    
    float   shadelight[4];
    
    // CSL
    
    float   ambientlight;
    
    
    Now, R_LightPoint is called from 2 functions, R_DrawAliasModel, and R_DrawViewModel. We must go into each of these functions and correct them for our new versions of these functions, and also for our new definition of shadelight.

    In gl_rmain.c replace R_DrawAliasModel with the following version
    
    
    void R_DrawAliasModel (entity_t *e)
    
    {
    
        int         i;
    
        // CSL - epca@powerup.com.au
    
        int         *j;
    
        // CSL
    
        int         lnum;
    
        vec3_t      dist;
    
        float       add;
    
        model_t     *clmodel;
    
        vec3_t      mins, maxs;
    
        aliashdr_t  *paliashdr;
    
        trivertx_t  *verts, *v;
    
        int         index;
    
        float       s, t, an;
    
        int         anim;
    
    
    
        clmodel = currententity->model;
    
    
    
        VectorAdd (currententity->origin, clmodel->mins, mins);
    
        VectorAdd (currententity->origin, clmodel->maxs, maxs);
    
    
    
        if (R_CullBox (mins, maxs))
    
            return;
    
    
    
    
    
        VectorCopy (currententity->origin, r_entorigin);
    
        VectorSubtract (r_origin, r_entorigin, modelorg);
    
    
    
        //
    
        // get lighting information
    
        //
    
    
    
        // CSL - epca@powerup.com.au
    
        j = R_LightPoint (currententity->origin);
    
        // Can't assign int* to float[4]...damn
    
        shadelight[0] = (float) j[0]; shadelight[1] = (float) j[1];
    
        shadelight[2] = (float) j[2]; shadelight[3] = (float) j[3];
    
        ambientlight = shadelight[3];
    
        // CSL
    
    
    
        // allways give the gun some light
    
        if (e == &cl.viewent && ambientlight < 24)
    
        {
    
            // CSL - epca@powerup.com.au
    
            ambientlight = shadelight[3] = 24;
    
            // CSL
    
        }
    
    
    
        for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
    
        {
    
            if (cl_dlights[lnum].die >= cl.time)
    
            {
    
                VectorSubtract (currententity->origin,
    
                                cl_dlights[lnum].origin,
    
                                dist);
    
                add = cl_dlights[lnum].radius - Length(dist);
    
    
    
                if (add > 0) {
    
                    ambientlight += add;
    
                    //ZOID models should be affected by dlights as well
    
                    // CSL - epca@powerup.com.au
    
                    shadelight[0] += cl_dlights[lnum].colour[0];
    
                    shadelight[1] += cl_dlights[lnum].colour[1];
    
                    shadelight[2] += cl_dlights[lnum].colour[2];
    
                    shadelight[3] += add;
    
                    // CSL
    
                }
    
            }
    
        }
    
    
    
        // clamp lighting so it doesn't overbright as much
    
        if (ambientlight > 128)
    
            ambientlight = 128;
    
        // CSL - epca@powerup.com.au
    
        if (ambientlight + shadelight[3] > 192)
    
            shadelight[3] = 192 - ambientlight;
    
        // CSL
    
    
    
        // ZOID: never allow players to go totally black
    
        i = currententity - cl_entities;
    
        if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */)
    
            if (ambientlight < 8)
    
                // CSL - epca@powerup.com.au
    
                ambientlight = shadelight[3] = 8;
    
                // CSL
    
    
    
        // HACK HACK HACK -- no fullbright colors, so make torches full light
    
        if (!strcmp (clmodel->name, "progs/flame2.mdl")
    
            || !strcmp (clmodel->name, "progs/flame.mdl") )
    
            // CSL - epca@powerup.com.au
    
            ambientlight = shadelight[3] = 256;
    
            // CSL
    
    
    
        shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];
    
        // CSL - epca@powerup.com.au
    
        shadelight[3] = shadelight[3] / 200.0;
    
        // CSL - epca@powerup.com.au
    
    
    
        an = e->angles[1]/180*M_PI;
    
        shadevector[0] = cos(-an);
    
        shadevector[1] = sin(-an);
    
        shadevector[2] = 1;
    
        VectorNormalize (shadevector);
    
    
    
        //
    
        // locate the proper data
    
        //
    
        paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model);
    
    
    
        c_alias_polys += paliashdr->numtris;
    
    
    
        //
    
        // draw all the triangles
    
        //
    
    
    
        GL_DisableMultitexture();
    
    
    
        glPushMatrix ();
    
    
    
        R_RotateForEntity (e);
    
    
    
        if (!strcmp (clmodel->name, "progs/eyes.mdl") && gl_doubleeyes.value) {
    
            glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - (22 + 8));
    
    // double size of eyes, since they are really hard to see in gl
    
            glScalef (paliashdr->scale[0]*2, paliashdr->scale[1]*2, paliashdr->scale[2]*2);
    
        } else {
    
            glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]);
    
            glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]);
    
        }
    
    
    
        anim = (int)(cl.time*10) & 3;
    
        GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]);
    
    
    
        // we can't dynamically colormap textures, so they are cached
    
        // seperately for the players.  Heads are just uncolored.
    
        if (currententity->colormap != vid.colormap && !gl_nocolors.value)
    
        {
    
            i = currententity - cl_entities;
    
            if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */)
    
                GL_Bind(playertextures - 1 + i);
    
        }
    
    
    
        if (gl_smoothmodels.value)
    
            glShadeModel (GL_SMOOTH);
    
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    
    
    
        if (gl_affinemodels.value)
    
            glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
    
    
    
        R_SetupAliasFrame (currententity->frame, paliashdr);
    
    
    
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    
    
    
        glShadeModel (GL_FLAT);
    
        if (gl_affinemodels.value)
    
            glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    
    
    
        glPopMatrix ();
    
    
    
        if (r_shadows.value)
    
        {
    
            glPushMatrix ();
    
    
    
            R_RotateForEntity (e);
    
    
    
            glDisable (GL_TEXTURE_2D);
    
            glEnable (GL_BLEND);
    
            glColor4f (0,0,0,0.5);
    
    
    
            GL_DrawAliasShadow (paliashdr, lastposenum);
    
    
    
            glEnable (GL_TEXTURE_2D);
    
            glDisable (GL_BLEND);
    
            glColor4f (1,1,1,1);
    
            glPopMatrix ();
    
        }
    
    }
    
    
    Then in gl_rmain.c replace R_DrawViewModel with the following version
    
    
    void R_DrawViewModel (void)
    
    {
    
        float       ambient[4], diffuse[4];
    
        // CSL - epca@powerup.com.au
    
        int         *j;
    
        int         shadelight[4];
    
        // CSL
    
        int         lnum;
    
        vec3_t      dist;
    
        float       add;
    
        dlight_t    *dl;
    
        int         ambientlight;
    
        float old_interpolate_model_transform;
    
    
    
        if (!r_drawviewmodel.value)
    
            return;
    
    
    
        if (chase_active.value)
    
            return;
    
    
    
        if (envmap)
    
            return;
    
    
    
        if (!r_drawentities.value)
    
            return;
    
    
    
        if (cl.items & IT_INVISIBILITY)
    
            return;
    
    
    
        if (cl.stats[STAT_HEALTH] <= 0)
    
            return;
    
    
    
        currententity = &cl.viewent;
    
        if (!currententity->model)
    
            return;
    
    
    
        j = R_LightPoint (currententity->origin);
    
    
    
        // CSL - epca@powerup.com.au
    
        if (j[3] < 24)
    
            j[3] = 24;      // allways give some light on gun
    
        ambientlight = j[3];
    
        // Can't assign int* to int[4]...damn
    
        shadelight[0] = j[0]; shadelight[1] = j[1];
    
        shadelight[2] = j[2]; shadelight[3] = j[3];
    
        // CSL
    
    
    
    // add dynamic lights
    
        for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
    
        {
    
            dl = &cl_dlights[lnum];
    
            if (!dl->radius)
    
                continue;
    
            if (!dl->radius)
    
                continue;
    
            if (dl->die < cl.time)
    
                continue;
    
    
    
            VectorSubtract (currententity->origin, dl->origin, dist);
    
            add = dl->radius - Length(dist);
    
            if (add > 0)
    
            {
    
                // CSL - epca@powerup.com.au
    
                shadelight[0] += dl->colour[0];
    
                shadelight[1] += dl->colour[1];
    
                shadelight[2] += dl->colour[2];
    
                shadelight[3] += add;
    
                // CSL
    
                ambientlight += add;
    
            }
    
        }
    
    
    
        ambient[0] = ambient[1] = ambient[2] = ambient[3] = (float)ambientlight / 128;
    
        // CSL - epca@powerup.com.au
    
        diffuse[0] = diffuse[1] = diffuse[2] = diffuse[3] = (float)shadelight[3] / 128;
    
        // CSL
    
    
    
        // hack the depth range to prevent view model from poking into walls
    
        glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin));
    
    
    
        R_DrawAliasModel (currententity);
    
    
    
        glDepthRange (gldepthmin, gldepthmax);
    
    }
    
    
    There is only one thing left to do, and that is to change how the models are drawn, so that our new shadelight varible, can effect the colour as well as the intensity. For those that have completed the interpolation tutorial, this is the code that will need to be placed in your new drawing function, if you want the interpolated models to be drawn with coloured lighting as well.
    In gl_rmain.c in GL_DrawAliasFrame Replace :
    
    
    		l = shadedots[verts->lightnormalindex] * shadelight;
    
    		glColor3f (l, l, l);
    
    
    With :
    
    
    		// CSL - epca@powerup.com.au
    
                    l = shadedots[verts->lightnormalindex] * shadelight[3];
    
                    if (shadelight[0] || shadelight[1] || shadelight[2])
    
                        glColor3f (l*shadelight[0], l*shadelight[1], l*shadelight[2]);
    
                    else
    
                        glColor3f (l, l, l);
    
                    // CSL
    
    
    Well that is it. I am sure, now that everyone has seen how easy it has been to add cool new features to the Quake engine, that we will see no end of great new mods out there. If anyone does anything fantastic with this coloured lighting code, I would love to hear about it, so feel free to contact me.

    As far as I can tell, that has just been the last tutorial in the coloured lighting series. If I have missed anything that needs to be done to make this a complete effect, make sure you tell me, I hate leaving things unfinished. Any problems, comments or corrections, mail me at epca@powerup.com.au
    editors note:
    some people were having problems with this tutorial and this fix was send in to fix it: In gl_rmain.c in GL_DrawAliasFrame Replace :
    
    
    		// CSL - epca@powerup.com.au
    
                    l = shadedots[verts->lightnormalindex] * shadelight[3];
    
                    if (shadelight[0] || shadelight[1] || shadelight[2])
    
                        glColor3f (l*shadelight[0], l*shadelight[1], l*shadelight[2]);
    
                    else
    
                        glColor3f (l, l, l);
    
                    // CSL
    
    
    with:
    
    
    		// CSL - epca@powerup.com.au
    
                    l = shadedots[verts->lightnormalindex] * shadelight[3];
    
                    if (shadelight[0] || shadelight[1] || shadelight[2])
    
                        glColor3f (l+(shadelight[0]/255), l+(shadelight[1]/255), l+(shadelight[2]/255));
    
                    else
    
                        glColor3f (l, l, l);
    
                    // CSL
    
    
    Thanks go out to Jacques Krige for fixing this.


 
Sign up
Login:
Passwd:
[Remember Me]