|
Once I had implemented Coloured Dynamic Lights in GLQuake
(http://qsg.telefragged.com/tut/gfx/lights.shtml) I was happy with the
effect I achieved, but it was still missing something. I figured it must
be that all lights around the coloured lights were plain, old boring white,
and figured I might as well do something about it. Before following this tutorial, make sure you have followed the Coloured Dynamic Lights tutorial (http://qsg.telefragged.com/tut/gfx/lights.shtml), as this code makes use of some changes made by it. Also remember to run the program with -lm_4 to enable RGBA lightmaps, as GLQuake will just render normal lightmaps otherwise. To implement the lights, I had to change the way lightmaps are calculated by light.exe, so you will need the source code for light.exe available in the file qutils.zip from your favourite idsoftware mirror. Here are the changes I had to make to implement Coloured Static Lights.
Part A: Light.exe Changes Currently, they are stored as a single value, that tells the engine how light or dark the area is. We must change this to store 4 values, the red component, the blue component, the green component and the alpha component.
First, we must create a way to save the colour of each light, found in the
bsp file. struct entity_s *targetent;
// CSL - epca@powerup.com.au
// Store the colour of the light
int lightcolour[3];
// CSL
In Entities.c in LoadEntities After :
else if (!strcmp(key, "angle"))
{
entity->angle = atof(com_token);
}
// CSL - epca@powerup.com.au
else if (!strcmp(key, "_color") || !strcmp(key, "color"))
{
// scan into doubles, then assign
// which makes it vec_t size independent
if (sscanf(com_token, "%lf %lf %lf",
&vec[0], &vec[1], &vec[2]) != 3)
Error ("LoadEntities: not 3 values for colour");
for (i=0 ; i<3 ; i++)
entity->lightcolour[i] = vec[i];
}
// CSL
In ltface.c in the definition of lightinfo_t After : dface_t *face;
// CSL - epca@powerup.com.au
vec3_t lightmapcolours[MAXLIGHTMAPS][SINGLEMAP];
// CSL
In ltface.c replace SingleLightFace, with the following version
void SingleLightFace (entity_t *light, lightinfo_t *l)
{
vec_t dist;
vec3_t incoming;
vec_t angle;
vec_t add;
vec_t *surf;
qboolean hit;
int mapnum;
int size;
int c, i;
vec3_t rel;
vec3_t spotvec;
vec_t falloff;
vec_t *lightsamp;
// CSL - epca@powerup.com.au
vec3_t *lightcoloursamp;
// CSL
VectorSubtract (light->origin, bsp_origin, rel);
dist = scaledist * (DotProduct (rel, l->facenormal) - l->facedist);
// don't bother with lights behind the surface
if (dist <= 0)
return;
// don't bother with light too far away
if (dist > light->light)
{
c_culldistplane++;
return;
}
if (light->targetent)
{
VectorSubtract (light->targetent->origin, light->origin, spotvec);
VectorNormalize (spotvec);
if (!light->angle)
falloff = -cos(20*Q_PI/180);
else
falloff = -cos(light->angle/2*Q_PI/180);
}
else
falloff = 0; // shut up compiler warnings
mapnum = 0;
for (mapnum=0 ; mapnum<l->numlightstyles ; mapnum++)
if (l->lightstyles[mapnum] == light->style)
break;
lightsamp = l->lightmaps[mapnum];
// CSL - epca@powerup.com.au
lightcoloursamp = l->lightmapcolours[mapnum];
// CSL
if (mapnum == l->numlightstyles)
{ // init a new light map
if (mapnum == MAXLIGHTMAPS)
{
printf ("WARNING: Too many light styles on a face\n");
return;
}
size = (l->texsize[1]+1)*(l->texsize[0]+1);
for (i=0 ; i<size ; i++)
{
// CSL - epca@powerup.com.au
lightcoloursamp[i][0] = lightcoloursamp[i][1] = lightcoloursamp[i][2] = 0;
// CSL
lightsamp[i] = 0;
}
}
//
// check it for real
//
hit = false;
c_proper++;
surf = l->surfpt[0];
for (c=0 ; c<l->numsurfpt ; c++, surf+=3)
{
dist = CastRay(light->origin, surf)*scaledist;
if (dist < 0)
continue; // light doesn't reach
VectorSubtract (light->origin, surf, incoming);
VectorNormalize (incoming);
angle = DotProduct (incoming, l->facenormal);
if (light->targetent)
{ // spotlight cutoff
if (DotProduct (spotvec, incoming) > falloff)
continue;
}
angle = (1.0-scalecos) + scalecos*angle;
add = light->light - dist;
add *= angle;
if (add < 0)
continue;
lightsamp[c] += add;
// CSL - epca@powerup.com.au
if (!light->lightcolour[0] && !light->lightcolour[1] && !light->lightcolour[2])
{
// No colour
lightcoloursamp[c][0] += add;
lightcoloursamp[c][1] += add;
lightcoloursamp[c][2] += add;
}
else
{
lightcoloursamp[c][0] += (add * light->lightcolour[0]) /255;
lightcoloursamp[c][1] += (add * light->lightcolour[1]) /255;
lightcoloursamp[c][2] += (add * light->lightcolour[2]) /255;
}
// CSL
if (lightsamp[c] > 1) // ignore real tiny lights
hit = true;
}
if (mapnum == l->numlightstyles && hit)
{
l->lightstyles[mapnum] = light->style;
l->numlightstyles++; // the style has some real data now
}
}
In ltface.c replace LightFace, with the following version
void LightFace (int surfnum)
{
dface_t *f;
lightinfo_t l;
int s, t;
int i,j,c;
vec_t total;
int size;
int lightmapwidth, lightmapsize;
byte *out;
vec_t *light;
// CSL - epca@powerup.com.au
vec3_t *lightcolour;
vec3_t totalcolours;
// CSL
int w, h;
f = dfaces + surfnum;
//
// some surfaces don't need lightmaps
//
f->lightofs = -1;
for (j=0 ; j<MAXLIGHTMAPS ; j++)
f->styles[j] = 255;
if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
{ // non-lit texture
return;
}
memset (&l, 0, sizeof(l));
l.surfnum = surfnum;
l.face = f;
//
// rotate plane
//
VectorCopy (dplanes[f->planenum].normal, l.facenormal);
l.facedist = dplanes[f->planenum].dist;
if (f->side)
{
VectorSubtract (vec3_origin, l.facenormal, l.facenormal);
l.facedist = -l.facedist;
}
CalcFaceVectors (&l);
CalcFaceExtents (&l);
CalcPoints (&l);
lightmapwidth = l.texsize[0]+1;
size = lightmapwidth*(l.texsize[1]+1);
if (size > SINGLEMAP)
Error ("Bad lightmap size");
for (i=0 ; i<MAXLIGHTMAPS ; i++)
l.lightstyles[i] = 255;
//
// cast all lights
//
l.numlightstyles = 0;
for (i=0 ; i<num_entities ; i++)
{
if (entities[i].light)
SingleLightFace (&entities[i], &l);
}
FixMinlight (&l);
if (!l.numlightstyles)
{ // no light hitting it
return;
}
//
// save out the values
//
for (i=0 ; i <MAXLIGHTMAPS ; i++)
f->styles[i] = l.lightstyles[i];
// CSL - epca@powerup.com.au
file://lightmapsize = size*l.numlightstyles;
// Now that we are storing RGBA, we need extra room
lightmapsize = size*l.numlightstyles*4;
// CSL
out = GetFileSpace (lightmapsize);
f->lightofs = out - filebase;
// extra filtering
h = (l.texsize[1]+1)*2;
w = (l.texsize[0]+1)*2;
for (i=0 ; i< l.numlightstyles ; i++)
{
if (l.lightstyles[i] == 0xff)
Error ("Wrote empty lightmap");
light = l.lightmaps[i];
// CSL - epca@powerup.com.au
lightcolour = l.lightmapcolours[i];
// CSL
c = 0;
for (t=0 ; t<=l.texsize[1] ; t++)
for (s=0 ; s<=l.texsize[0] ; s++, c++)
{
if (extrasamples)
{ // filtered sample
total = light[t*2*w+s*2] + light[t*2*w+s*2+1]
+ light[(t*2+1)*w+s*2] + light[(t*2+1)*w+s*2+1];
total *= 0.25;
// CSL - epca@powerup.com.au
// Calculate the colour
totalcolours[0] = lightcolour[t*2*w+s*2][0] + lightcolour[t*2*w+s*2+1][0]
+ lightcolour[(t*2+1)*w+s*2][0] + lightcolour[(t*2+1)*w+s*2+1][0];
totalcolours[0] *= 0.25;
totalcolours[1] = lightcolour[t*2*w+s*2][1] + lightcolour[t*2*w+s*2+1][1]
+ lightcolour[(t*2+1)*w+s*2][1] + lightcolour[(t*2+1)*w+s*2+1][1];
totalcolours[1] *= 0.25;
totalcolours[2] = lightcolour[t*2*w+s*2][2] + lightcolour[t*2*w+s*2+1][2]
+ lightcolour[(t*2+1)*w+s*2][2] + lightcolour[(t*2+1)*w+s*2+1][2];
totalcolours[2] *= 0.25;
// CSL
}
else
{
total = light[c];
// CSL - epca@powerup.com.au
// Calculate the colour
VectorCopy (lightcolour[c], totalcolours);
// CSL
}
total *= rangescale; // scale before clamping
// CSL - epca@powerup.com.au
VectorScale (totalcolours, rangescale, totalcolours);
for (j = 0; j < 3; j++)
{
if (totalcolours[j] > 255)
totalcolours[j] = 255;
if (totalcolours[j] < 0)
Error ("colour %i < 0", j);
}
// CSL
if (total > 255)
total = 255;
if (total < 0)
Error ("light < 0");
// CSL - epca@powerup.com.au
// Write out the lightmap in RGBA format
*out++ = totalcolours[0];
*out++ = totalcolours[1];
*out++ = totalcolours[2];
// CSL
*out++ = total;
}
}
}
In the common directory at the base of the utils package, you will find
bspfile.h #define BSPVERSION 29
// CSL - epca@powerup.com.au
#define BSPVERSION 30
// CSL
After : #define BSPVERSION 29
// CSL - epca@powerup.com.au
#define CSLBSPVERSION 30
// CSL
After :
cvar_t temp1 = {"temp1","0"};
// CSL - epca@powerup.com.au
// Use bspextensions to determine changes in format to bsp files
int bspextensions = 0;
// CSL
extern double realtime; // not bounded in any way, changed at
// start of every frame, never reset
// CSL - epca@powerup.com.au
extern int bspextensions;
// CSL
if (i != BSPVERSION)
Sys_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION);
// CSL - epca@powerup.com.au
file://if (i != BSPVERSION)
if ((i != BSPVERSION) && (i != CSLBSPVERSION))
Sys_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, CSLBSPVERSION);
bspextensions = i - BSPVERSION;
// CSL
In gl_rsurf.c in R_BuildLightMap
for (i=0 ; i<size ; i++)
{
// CDL - epca@powerup.com.au
blocklightcolours[0][i] += lightmap[i] * scale;
blocklightcolours[1][i] += lightmap[i] * scale;
blocklightcolours[2][i] += lightmap[i] * scale;
// CDL
blocklights[i] += lightmap[i] * scale;
}
lightmap += size; // skip to next lightmap
for (i=0, j=0 ; i<size ; i++)
{
// CDL - epca@powerup.com.au
// CSL - epca@powerup.com.au
if (bspextensions)
{
blocklightcolours[0][i] += lightmap[j] * scale; j++;
blocklightcolours[1][i] += lightmap[j] * scale; j++;
blocklightcolours[2][i] += lightmap[j] * scale; j++;
}
else
{
blocklightcolours[0][i] += lightmap[j] * scale;
blocklightcolours[1][i] += lightmap[j] * scale;
blocklightcolours[2][i] += lightmap[j] * scale;
}
blocklights[i] += lightmap[j] * scale; j++;
// CSL
// CDL
}
// CSL - epca@powerup.com.au
if (bspextensions)
lightmap += size * 4;
else
lightmap += size; // skip to next lightmap
// CSL
=========== Since SaRcaZm originally wrote this tutorial, it has came to light that several video cards, with various drivers, exibit the "white" effect. This has been determined to be caused by a few lines of code that iD forgot to impliment in it's RGBA code. To fix this, find "R_BlendLightmaps" in GL_RSurf.c, and add the code shown in blue, to the top part of this function:
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); Creating Maps ============= To add coloured lights to your map, all you need to do is add a "_color" or "color" tag to the light entity e.g.
{
"classname" "light"
"origin" "-128 128 -64"
"_color" "0 255 0"
}
Here is a simple map, that demonstrates the coloured lighting, as well as the limits of my mapping ability. If anyone creates a cool level using this code, I would love to see it, as with my abilities, I will never get to see the full extent of this effect.
{
"classname" "worldspawn"
"message" "Coloured Static Lights Example\nby SaRcaZm"
"wad" "Quake.wad"
{
( -192 192 64 ) ( 256 192 64 ) ( 256 -320 64 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -192 192 64 ) ( -192 -320 64 ) ( -192 -320 -192 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 256 192 -192 ) ( 256 -320 -192 ) ( 256 -320 64 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 256 192 64 ) ( -192 192 64 ) ( -192 192 -192 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 256 -320 -192 ) ( -192 -320 -192 ) ( -192 -320 64 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 224 -288 32 ) ( 224 160 32 ) ( -160 160 32 ) AFLOOR1_3 0 0 0 1.000000 1.000000
}
{
( -192 -320 -192 ) ( 256 -320 -192 ) ( 256 192 -192 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -192 192 64 ) ( -192 -320 64 ) ( -192 -320 -192 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 256 192 -192 ) ( 256 -320 -192 ) ( 256 -320 64 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 256 192 64 ) ( -192 192 64 ) ( -192 192 -192 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 256 -320 -192 ) ( -192 -320 -192 ) ( -192 -320 64 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 224 160 -160 ) ( 224 -288 -160 ) ( -160 -288 -160 ) AFLOOR1_3 0 0 0 1.000000 1.000000
}
{
( -192 192 64 ) ( -192 -320 64 ) ( -192 -320 -192 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 256 192 64 ) ( -192 192 64 ) ( -192 192 -192 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 256 -320 -192 ) ( -192 -320 -192 ) ( -192 -320 64 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 160 32 ) ( 224 160 32 ) ( 224 -288 32 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 -288 -160 ) ( 224 -288 -160 ) ( 224 160 -160 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 -288 -160 ) ( -160 -288 32 ) ( -160 160 32 ) AFLOOR1_3 0 0 0 1.000000 1.000000
}
{
( 256 192 -192 ) ( 256 -320 -192 ) ( 256 -320 64 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 256 192 64 ) ( -192 192 64 ) ( -192 192 -192 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 256 -320 -192 ) ( -192 -320 -192 ) ( -192 -320 64 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 160 32 ) ( 224 160 32 ) ( 224 -288 32 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 -288 -160 ) ( 224 -288 -160 ) ( 224 160 -160 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 224 -288 32 ) ( 224 -288 -160 ) ( 224 160 -160 ) AFLOOR1_3 0 0 0 1.000000 1.000000
}
{
( 256 192 64 ) ( -192 192 64 ) ( -192 192 -192 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 160 32 ) ( 224 160 32 ) ( 224 -288 32 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 -288 -160 ) ( 224 -288 -160 ) ( 224 160 -160 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 160 32 ) ( -160 -288 32 ) ( -160 -288 -160 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 224 160 -160 ) ( 224 -288 -160 ) ( 224 -288 32 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 160 -160 ) ( -160 160 32 ) ( 224 160 32 ) AFLOOR1_3 0 0 0 1.000000 1.000000
}
{
( 256 -320 -192 ) ( -192 -320 -192 ) ( -192 -320 64 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 160 32 ) ( 224 160 32 ) ( 224 -288 32 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 -288 -160 ) ( 224 -288 -160 ) ( 224 160 -160 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 160 32 ) ( -160 -288 32 ) ( -160 -288 -160 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( 224 160 -160 ) ( 224 -288 -160 ) ( 224 -288 32 ) AFLOOR1_3 0 0 0 1.000000 1.000000
( -160 -288 32 ) ( -160 -288 -160 ) ( 224 -288 -160 ) AFLOOR1_3 0 0 0 1.000000 1.000000
}
}
{
"classname" "info_player_start"
"origin" "64 -64 -64"
}
{
"classname" "light"
"origin" "-128 -256 -64"
"_color" "255 0 0"
}
{
"classname" "light"
"origin" "192 -256 -64"
"_color" "0 0 255"
}
{
"classname" "light"
"origin" "-128 128 -64"
"_color" "0 255 0"
}
{
"classname" "light"
"origin" "192 128 -64"
}
========== Notes : 1. Values less than 200 or so don't seem to make that much of a difference. Not sure why, you can tweak this by changing the 255 constants in lines 108, 109 and 110 of SingleLightFace. 2. You can overbright colours, meaning you can use values of greater than 255. 3. Green doesn't come out very well. May have to overbright it if you want it to show.
|