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