MuckyFoot-UrbanChaos/fallen/Source/light.cpp
2017-05-20 11:14:17 +10:00

2077 lines
34 KiB
C++

//
// Lights...
//
#include "game.h"
#ifndef PSX
#include "light.h"
#include "fmatrix.h"
#include "memory.h"
#define IMPLIES(a,b) (!(a) || (b))
typedef struct
{
GameCoord pos;
LIGHT_Colour colour;
UBYTE range;
UBYTE type;
UBYTE param;
UBYTE counter;
UBYTE next; // Free list or mapwho list. 0 is the NULL index.
} LIGHT_Light;
#define LIGHT_MAX_LIGHTS 128
LIGHT_Light LIGHT_light[LIGHT_MAX_LIGHTS];
UBYTE LIGHT_free;
//
// The light mapwho
//
typedef struct
{
UBYTE next;
} LIGHT_Square;
#define LIGHT_MAP_SIZE (MAP_WIDTH / 2)
LIGHT_Square LIGHT_map[LIGHT_MAP_SIZE][LIGHT_MAP_SIZE];
//
// Converts a world map coordinate to a mapwho square coordinate.
//
#define LIGHT_TO_MAP(x) ((x) >> 9)
//
// The ambient light. The length of the light normal is 255.
//
LIGHT_Colour LIGHT_amb_colour;
SLONG LIGHT_amb_norm_x;
SLONG LIGHT_amb_norm_y;
SLONG LIGHT_amb_norm_z;
//
// The height field lit by the lights.
//
LIGHT_Map LIGHT_hf;
//
// Lighting on buildings.
//
LIGHT_Colour LIGHT_building_point[MAX_PRIM_POINTS];
//
// The cache of light values.
//
#define LIGHT_PER_SLOT 16
typedef struct
{
LIGHT_Colour colour[LIGHT_PER_SLOT];
UWORD next;
} LIGHT_Slot;
#ifdef PSX
#define LIGHT_MAX_SLOTS 128
#else
#define LIGHT_MAX_SLOTS 1280
#endif
LIGHT_Slot LIGHT_slot[LIGHT_MAX_SLOTS];
UWORD LIGHT_slot_free;
//
// The cache elements.
//
typedef struct
{
THING_INDEX me; // The thing whose lighting this is for.
LIGHT_Index light[3]; // The lights shining on this thing.
UBYTE num_lights;
UWORD next;
} LIGHT_Cache;
#ifdef PSX
#define LIGHT_MAX_CACHES 128
#else
#define LIGHT_MAX_CACHES 1280
#endif
LIGHT_Cache LIGHT_cache[LIGHT_MAX_CACHES];
UBYTE LIGHT_cache_free;
//
// The lit point colours.
//
LIGHT_Colour LIGHT_point_colour[LIGHT_MAX_POINTS];
SLONG LIGHT_point_colour_upto;
//
// Builds the free list of lights.
//
void LIGHT_build_free_list(void)
{
SLONG i;
LIGHT_free = 1;
for (i = 1; i < LIGHT_MAX_LIGHTS - 1; i++)
{
LIGHT_light[i].next = i + 1;
}
LIGHT_light[LIGHT_MAX_LIGHTS - 1].next = 0;
}
//
// Gives and gets lights.
//
UBYTE LIGHT_get(void)
{
if (LIGHT_free == NULL)
{
return NULL;
}
else
{
UBYTE ans = LIGHT_free;
ASSERT(WITHIN(LIGHT_free, 1, LIGHT_MAX_LIGHTS - 1));
LIGHT_free = LIGHT_light[LIGHT_free].next;
return ans;
}
}
void LIGHT_give(UBYTE l_index)
{
ASSERT(WITHIN(l_index, 1, LIGHT_MAX_LIGHTS - 1));
LIGHT_light[l_index].next = LIGHT_free;
LIGHT_free = l_index;
}
//
// Places, removes and moves a light on the light mapwho.
//
void LIGHT_map_place(LIGHT_Index l_index)
{
SLONG map_x;
SLONG map_z;
LIGHT_Light *ll;
LIGHT_Square *ls;
ASSERT(WITHIN(l_index, 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[l_index];
//
// The light must be on the map!
//
map_x = LIGHT_TO_MAP(ll->pos.X);
map_z = LIGHT_TO_MAP(ll->pos.Z);
ASSERT(WITHIN(map_x, 0, LIGHT_MAP_SIZE - 1));
ASSERT(WITHIN(map_z, 0, LIGHT_MAP_SIZE - 1));
//
// The map square.
//
ls = &LIGHT_map[map_x][map_z];
//
// Place it on the map.
//
ll->next = ls->next;
ls->next = l_index;
}
void LIGHT_map_remove(LIGHT_Index l_index)
{
SLONG map_x;
SLONG map_z;
LIGHT_Light *ll;
LIGHT_Square *ls;
UBYTE next;
UBYTE *prev;
ASSERT(WITHIN(l_index, 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[l_index];
//
// The light must be on the map!
//
map_x = LIGHT_TO_MAP(ll->pos.X);
map_z = LIGHT_TO_MAP(ll->pos.Z);
ASSERT(WITHIN(map_x, 0, LIGHT_MAP_SIZE - 1));
ASSERT(WITHIN(map_z, 0, LIGHT_MAP_SIZE - 1));
//
// The map square.
//
ls = &LIGHT_map[map_x][map_z];
//
// Look for this light in the linked list above this square.
//
prev = &ls->next;
next = ls->next;
while(1)
{
//
// This ASSERT catches the end of the linked list.
//
ASSERT(WITHIN(next, 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[next];
if (next == l_index)
{
//
// Found the light to take out of the linked list.
//
*prev = ll->next;
return;
}
prev = &ll->next;
next = ll->next;
}
}
void LIGHT_map_move(UBYTE l_index, GameCoord newpos)
{
SLONG map_x_old;
SLONG map_z_old;
SLONG map_x_new;
SLONG map_z_new;
LIGHT_Light *ll;
ASSERT(WITHIN(l_index, 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[l_index];
//
// Does its mapwho change?
//
map_x_old = LIGHT_TO_MAP(ll->pos.X);
map_z_old = LIGHT_TO_MAP(ll->pos.Z);
map_x_new = LIGHT_TO_MAP(newpos.X);
map_z_new = LIGHT_TO_MAP(newpos.Z);
if (map_x_old == map_x_new &&
map_z_old == map_z_new)
{
//
// This is an easy function!
//
ll->pos = newpos;
}
else
{
//
// Still pretty easy!
//
LIGHT_map_remove(l_index);
ll->pos = newpos;
LIGHT_map_place(l_index);
}
}
//
// Clears the map.
//
void LIGHT_map_clear(void)
{
SLONG x;
SLONG z;
for (x = 0; x < LIGHT_MAP_SIZE; x++)
for (z = 0; z < LIGHT_MAP_SIZE; z++)
{
LIGHT_map[x][z].next = 0;
}
}
//
// Makes up the free list of slots.
//
void LIGHT_slot_build_free_list(void)
{
SLONG i;
LIGHT_slot_free = 1;
for (i = 1; i < LIGHT_MAX_SLOTS - 1; i++)
{
LIGHT_slot[i].next = i + 1;
}
LIGHT_slot[LIGHT_MAX_SLOTS - 1].next = 0;
}
//
// Gets an unused slot.
//
UBYTE LIGHT_slot_get(void)
{
UBYTE ans = LIGHT_slot_free;
if (ans == NULL)
{
return NULL;
}
else
{
ASSERT(WITHIN(ans, 1, LIGHT_MAX_SLOTS - 1));
LIGHT_slot_free = LIGHT_slot[ans].next;
return ans;
}
}
//
// Gives up a slot.
//
void LIGHT_slot_give(UBYTE s_index)
{
ASSERT(WITHIN(s_index, 1, LIGHT_MAX_SLOTS - 1));
LIGHT_slot[s_index].next = LIGHT_slot_free;
LIGHT_slot_free = s_index;
}
//
// Makes up the free list of cache entries.
//
void LIGHT_cache_build_free_list(void)
{
SLONG i;
LIGHT_cache_free = 1;
for (i = 1; i < LIGHT_MAX_CACHES - 1; i++)
{
LIGHT_cache[i].next = i + 1;
}
LIGHT_cache[LIGHT_MAX_CACHES - 1].next = 0;
}
//
// Gets an unused cache entry.
//
UBYTE LIGHT_cache_get(UBYTE c_index)
{
UBYTE ans;
ans = LIGHT_cache_free;
if (ans == NULL)
{
return NULL;
}
else
{
ASSERT(WITHIN(ans, 1, LIGHT_MAX_CACHES - 1));
LIGHT_cache_free = LIGHT_cache[ans].next;
return ans;
}
}
//
// Gives back a dead cache entry.
//
void LIGHT_cache_give(UBYTE c_index)
{
ASSERT(WITHIN(c_index, 1, LIGHT_MAX_CACHES - 1));
LIGHT_cache[c_index].next = LIGHT_cache_free;
LIGHT_cache_free = c_index;
}
void LIGHT_set_hf(LIGHT_Map *map)
{
LIGHT_hf = *map;
}
void LIGHT_set_ambient(
LIGHT_Colour amb_colour,
SLONG amb_norm_x,
SLONG amb_norm_y,
SLONG amb_norm_z)
{
LIGHT_amb_colour = amb_colour;
LIGHT_amb_norm_x = amb_norm_x;
LIGHT_amb_norm_y = amb_norm_y;
LIGHT_amb_norm_z = amb_norm_z;
}
//
// Lights up/down the given building.
//
void LIGHT_building_up(LIGHT_Index l_index, THING_INDEX t_index)
{
SLONG i;
SLONG facet;
SLONG point;
SLONG dpx;
SLONG dpy;
SLONG dpz;
SLONG lposx;
SLONG lposy;
SLONG lposz;
SLONG dprod;
SLONG dist;
SLONG range;
LIGHT_Light *ll;
Thing *p_thing = TO_THING(t_index);
PrimPoint *pp;
SVector *pn;
LIGHT_Colour *pc;
BuildingObject *bo;
BuildingFacet *bf;
ASSERT(WITHIN(l_index, 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[l_index];
//
// The range of the light.
//
range = ll->range * LIGHT_MAX_RANGE >> 8;
//
// The light position in relative to the building.
//
lposx = ll->pos.X - (p_thing->WorldPos.X >> 8);
lposy = ll->pos.Y - (p_thing->WorldPos.Y >> 8);
lposz = ll->pos.Z - (p_thing->WorldPos.Z >> 8);
bo = &building_objects[p_thing->BuildingList];
for (facet = bo->FacetHead; facet; facet = bf->NextFacet)
{
bf = &building_facets[facet];
//
// Light each point.
//
for (point = bf->StartPoint; point < bf->EndPoint; point++)
{
pp = &prim_points [point];
pn = &prim_normal [point];
pc = &LIGHT_building_point[point];
//
// Do the lighting.
//
dpx = pp->X - lposx;
dpy = pp->Y - lposy;
dpz = pp->Z - lposz;
dist = QDIST3(abs(dpx), abs(dpy), abs(dpz));
if (dist < range)
{
//
// The angle the light hits.
//
dprod = pn->X*dpx + pn->Y*dpy + pn->Z*dpz;
dprod /= dist;
dprod = dprod * (256 - ((dist * 256) / range)) >> 8;
dprod = -dprod;
if (dprod > 0)
{
#if LIGHT_COLOURED
pc->red += ll->colour.red * dprod >> 8;
pc->green += ll->colour.green * dprod >> 8;
pc->blue += ll->colour.blue * dprod >> 8;
#else
*pc += ll->colour * dprod >> 8;
#endif
}
}
}
}
}
void LIGHT_building_down(LIGHT_Index l_index, THING_INDEX t_index)
{
SLONG i;
SLONG facet;
SLONG point;
SLONG dpx;
SLONG dpy;
SLONG dpz;
SLONG lposx;
SLONG lposy;
SLONG lposz;
SLONG dprod;
SLONG dist;
SLONG range;
LIGHT_Light *ll;
Thing *p_thing = TO_THING(t_index);
PrimPoint *pp;
SVector *pn;
LIGHT_Colour *pc;
BuildingObject *bo;
BuildingFacet *bf;
ASSERT(WITHIN(l_index, 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[l_index];
//
// The range of the light.
//
range = ll->range * LIGHT_MAX_RANGE >> 8;
//
// The light position in relative to the building.
//
lposx = ll->pos.X - (p_thing->WorldPos.X >> 8);
lposy = ll->pos.Y - (p_thing->WorldPos.Y >> 8);
lposz = ll->pos.Z - (p_thing->WorldPos.Z >> 8);
bo = &building_objects[p_thing->BuildingList];
for (facet = bo->FacetHead; facet; facet = bf->NextFacet)
{
bf = &building_facets[facet];
//
// Light each point.
//
for (point = bf->StartPoint; point < bf->EndPoint; point++)
{
pp = &prim_points [point];
pn = &prim_normal [point];
pc = &LIGHT_building_point[point];
//
// Do the lighting.
//
dpx = pp->X - lposx;
dpy = pp->Y - lposy;
dpz = pp->Z - lposz;
dist = QDIST3(abs(dpx), abs(dpy), abs(dpz));
if (dist < range)
{
//
// The angle the light hits.
//
dprod = pn->X*dpx + pn->Y*dpy + pn->Z*dpz;
dprod /= dist;
dprod = dprod * (256 - ((dist * 256) / range)) >> 8;
dprod = -dprod;
if (dprod > 0)
{
#if LIGHT_COLOURED
pc->red -= ll->colour.red * dprod >> 8;
pc->green -= ll->colour.green * dprod >> 8;
pc->blue -= ll->colour.blue * dprod >> 8;
#else
*pc -= ll->colour * dprod >> 8;
#endif
}
}
}
}
}
//
// Removes a light from the hf
//
void LIGHT_hf_light_up(LIGHT_Index l_index)
{
#ifdef TARGET_DC
// Shouldn't be using this, apparently.
ASSERT ( FALSE );
#endif
SLONG x;
SLONG y;
SLONG z;
SLONG mx;
SLONG mz;
SLONG dx;
SLONG dy;
SLONG dz;
SLONG x1;
SLONG z1;
SLONG x2;
SLONG z2;
SLONG building;
SLONG storey;
SLONG wall;
SLONG v_list;
SLONG i_vect;
SLONG dist;
SLONG range;
SLONG brightness;
UWORD litkey;
LIGHT_Light *ll;
LIGHT_Colour col;
CollisionVect *p_vect;
ASSERT(WITHIN(l_index, 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[l_index];
//
// When we light up a building, we put this value in 'LastDrawn'
// to mark the building as lit.
//
litkey = rand();
//
// The range of the light.
//
range = ll->range * LIGHT_MAX_RANGE >> 8;
x1 = ll->pos.X - range;
z1 = ll->pos.Z - range;
x2 = ll->pos.X + range;
z2 = ll->pos.Z + range;
x1 >>= 8;
z1 >>= 8;
x2 = (x2 + 0xff) >> 8;
z2 = (z2 + 0xff) >> 8;
for (mx = x1; mx <= x2; mx += 1)
for (mz = z1; mz <= z2; mz += 1)
{
x = mx << 8;
z = mz << 8;
y = LIGHT_hf.get_height(mx, mz);
dx = abs(ll->pos.X - x);
dy = abs(ll->pos.Y - y);
dz = abs(ll->pos.Z - z);
dist = QDIST3(dx, dy, dz);
brightness = 256 - ((dist * 256) / range); // Shouldn't we convert this to a multiply?
if (brightness > 0)
{
col = LIGHT_hf.get_light(mx, mz);
#if LIGHT_COLOURED
col.red += ll->colour.red * brightness >> 8;
col.green += ll->colour.green * brightness >> 8;
col.blue += ll->colour.blue * brightness >> 8;
#else
col += ll->colour * brightness >> 8;
#endif
LIGHT_hf.set_light(mx, mz, col);
}
//
// Look for a building we might have to light.
//
v_list = MAP[MAP_INDEX(mx,mz)].ColVectHead;
if (v_list)
{
i_vect = col_vects_links[v_list].VectIndex;
p_vect = &col_vects[i_vect];
wall = -p_vect->Face;
storey = wall_list[wall].StoreyHead;
building = storey_list[storey].BuildingHead;
if (building_list[building].LastDrawn != litkey)
{
//
// Light up this building.
//
LIGHT_building_up(l_index, building_list[building].ThingIndex);
//
// Mark the building as list.
//
building_list[building].LastDrawn = litkey;
}
}
}
}
void LIGHT_hf_light_down(LIGHT_Index l_index)
{
#ifdef TARGET_DC
// Shouldn't be using this, apparently.
ASSERT ( FALSE );
#endif
SLONG x;
SLONG y;
SLONG z;
SLONG mx;
SLONG mz;
SLONG dx;
SLONG dy;
SLONG dz;
SLONG x1;
SLONG z1;
SLONG x2;
SLONG z2;
SLONG building;
SLONG storey;
SLONG wall;
SLONG v_list;
SLONG i_vect;
SLONG dist;
SLONG range;
SLONG brightness;
UWORD litkey;
LIGHT_Colour col;
LIGHT_Light *ll;
CollisionVect *p_vect;
//
// When we light up a building, we put this value in
//
litkey = rand();
ASSERT(WITHIN(l_index, 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[l_index];
//
// The range of the light.
//
range = ll->range * LIGHT_MAX_RANGE >> 8;
x1 = ll->pos.X - range;
z1 = ll->pos.Z - range;
x2 = ll->pos.X + range;
z2 = ll->pos.Z + range;
x1 >>= 8;
z1 >>= 8;
x2 = (x2 + 0xff) >> 8;
z2 = (z2 + 0xff) >> 8;
for (mx = x1; mx <= x2; mx += 1)
for (mz = z1; mz <= z2; mz += 1)
{
x = mx << 8;
z = mz << 8;
y = LIGHT_hf.get_height(mx, mz);
dx = abs(ll->pos.X - x);
dy = abs(ll->pos.Y - y);
dz = abs(ll->pos.Z - z);
dist = QDIST3(dx, dy, dz);
brightness = 256 - ((dist * 256) / range); // Shouldn't we convert this to a multiply?
if (brightness > 0)
{
col = LIGHT_hf.get_light(mx, mz);
#if LIGHT_COLOURED
col.red -= ll->colour.red * brightness >> 8;
col.green -= ll->colour.green * brightness >> 8;
col.blue -= ll->colour.blue * brightness >> 8;
#else
col -= ll->colour * brightness >> 8;
#endif
LIGHT_hf.set_light(mx, mz, col);
}
//
// Look for a building we might have to light down.
//
v_list = MAP[MAP_INDEX(mx,mz)].ColVectHead;
if (v_list)
{
i_vect = col_vects_links[v_list].VectIndex;
p_vect = &col_vects[i_vect];
wall = -p_vect->Face;
storey = wall_list[wall].StoreyHead;
building = storey_list[storey].BuildingHead;
if (building_list[building].LastDrawn != litkey)
{
//
// Light down this building.
//
LIGHT_building_down(l_index, building_list[building].ThingIndex);
//
// Mark the building as list.
//
building_list[building].LastDrawn = litkey;
}
}
}
}
void LIGHT_recalc_hf(void)
{
SLONG i;
SLONG j;
SLONG x;
SLONG z;
SLONG ho;
SLONG h1;
SLONG h2;
SLONG a;
SLONG ao;
SLONG a1;
SLONG a2;
SLONG nx;
SLONG ny;
SLONG nz;
SLONG ny2;
SLONG dprod;
LIGHT_Colour col;
ASSERT(LIGHT_hf.get_height);
ASSERT(LIGHT_hf.get_light);
ASSERT(LIGHT_hf.set_light);
//
// Clear the ambient light on the hf.
//
#if LIGHT_COLOURED
col.red = 0;
col.green = 0;
col.blue = 0;
#else
col = 0;
#endif
for (x = 0; x < LIGHT_hf.width; x++)
for (z = 0; z < LIGHT_hf.height; z++)
{
LIGHT_hf.set_light(x, z, col);
}
//
// Puts the ambient light on the map.
//
for (x = 1; x < LIGHT_hf.width - 1; x++)
for (z = 1; z < LIGHT_hf.height - 1; z++)
{
//
// The height of this point.
//
ho = LIGHT_hf.get_height(x,z);
//
// Find an approximate normal for this point.
//
//
// The x-component of the normal.
//
h1 = LIGHT_hf.get_height(x + 1, z) - ho;
h2 = LIGHT_hf.get_height(x - 1, z) - ho;
a1 = Arctan(h1, -0x100);
a2 = Arctan(h2, -0x100);
a = 1024 - a1 - a2;
ao = a2 + (a >> 1);
nx = COS(ao) >> 8;
//
// The z-component of the normal.
//
h1 = LIGHT_hf.get_height(x, z + 1) - ho;
h2 = LIGHT_hf.get_height(x, z - 1) - ho;
a1 = Arctan(h1, -0x100);
a2 = Arctan(h2, -0x100);
a = 1024 - a1 - a2;
ao = a2 + (a >> 1);
nz = COS(ao) >> 8;
//
// Set the y-component of the normal so that
// (nx,ny,nz) has a length of 0x100.
//
ny2 = 0x10000 - nx*nx - nz*nz;
if (ny2 <= 0)
{
TRACE("ny * ny < 0\n");
ny2 = 0;
}
else
{
ny = Root(ny2);
}
//
// Find the intensity of ambient light on this point.
//
dprod = nx*LIGHT_amb_norm_x + ny*LIGHT_amb_norm_y + nz*LIGHT_amb_norm_z;
dprod >>= 9;
dprod = -dprod;
dprod += 128;
SATURATE(dprod, 0, 256);
#if LIGHT_COLOURED
col.red = LIGHT_amb_colour.red * dprod >> 8;
col.green = LIGHT_amb_colour.green * dprod >> 8;
col.blue = LIGHT_amb_colour.blue * dprod >> 8;
#else
col = LIGHT_amb_colour * dprod >> 8;
#endif
LIGHT_hf.set_light(x, z, col);
}
{
//
// The ambient light on all the building objects.
//
SLONG facet;
SLONG point;
SVector normal;
BuildingFacet *bf;
BuildingObject *bo;
PrimPoint *pp;
SVector *pn;
LIGHT_Colour *pc;
for (i = 1; i < next_building_object; i++)
{
bo = &building_objects[i];
for (facet = bo->FacetHead; facet; facet = bf->NextFacet)
{
bf = &building_facets[facet];
//
// Light each point.
//
for (point = bf->StartPoint; point < bf->EndPoint; point++)
{
pn = &prim_normal [point];
pc = &LIGHT_building_point[point];
//
// Do the lighting.
//
dprod = pn->X*LIGHT_amb_norm_x + pn->Y*LIGHT_amb_norm_y + pn->Z*LIGHT_amb_norm_z;
dprod >>= 9;
dprod = -dprod;
dprod += 128;
SATURATE(dprod, 0, 256);
#if LIGHT_COLOURED
pc->red = LIGHT_amb_colour.red * dprod >> 8;
pc->green = LIGHT_amb_colour.green * dprod >> 8;
pc->blue = LIGHT_amb_colour.blue * dprod >> 8;
#else
pc->red = LIGHT_amb_colour * dprod >> 8;
#endif
}
#if WE_WANT_LIGHTING_ON_A_PER_FACE_INSTEAD_OF_PER_POINT_BASIS
//
// Only do face4s...
//
for (face = bf->StartFace4; face < bf->EndFace4; face++)
{
f4 = &prim_faces4[face];
//
// Calculate the face normal.
//
calc_normal(face, &normal);
//
// Do the lighting.
//
dprod = normal.X*LIGHT_amb_norm_x + normal.Y*LIGHT_amb_norm_y + normal.Z*LIGHT_amb_norm_z;
dprod >>= 9;
dprod = -dprod;
dprod += 128;
SATURATE(dprod, 0, 256);
#if LIGHT_COLOURED
f4->col.red = LIGHT_amb_colour.red * dprod >> 8;
f4->col.green = LIGHT_amb_colour.green * dprod >> 8;
f4->col.blue = LIGHT_amb_colour.blue * dprod >> 8;
#else
f4->col.red = LIGHT_amb_colour * dprod >> 8;
#endif
}
#endif
}
}
}
for (i = 0; i < LIGHT_MAX_LIGHTS; i++)
{
if (LIGHT_light[i].type)
{
//
// Place this light on the map.
//
LIGHT_hf_light_up(i);
}
}
}
void LIGHT_init()
{
SLONG i;
//
// Mark all lights as unused.
//
for (i = 0; i < LIGHT_MAX_LIGHTS; i++)
{
LIGHT_light[i].type = 0;
}
//
// Create the free lists...
//
LIGHT_build_free_list ();
LIGHT_slot_build_free_list ();
LIGHT_cache_build_free_list();
//
// Clears the mapwho.
//
LIGHT_map_clear();
}
LIGHT_Index LIGHT_create(
GameCoord where, //fix 8
LIGHT_Colour colour,
UBYTE range,
UBYTE type,
UBYTE param)
{
UBYTE l_index;
LIGHT_Light *ll;
//
// Get a new light.
//
l_index = LIGHT_get();
if (l_index == NULL)
{
//
// No more lights left!
//
return NULL;
}
else
{
ASSERT(WITHIN(l_index, 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[l_index];
ll->type = type;
ll->param = param;
ll->range = range;
ll->colour = colour;
ll->pos.X = where.X;
ll->pos.Y = where.Y;
ll->pos.Z = where.Z;
ll->counter = 0;
if (ll->type == LIGHT_TYPE_BROKEN)
{
//
// Mark the light as being on.
//
ll->counter |= 0x80; // Top bit set => light is on.
}
//
// Place the light on the light mapwho.
//
LIGHT_map_place(l_index);
//
// Put the light on the hf.
//
LIGHT_hf_light_up(l_index);
return l_index;
}
}
void LIGHT_destroy(LIGHT_Index l_index)
{
LIGHT_hf_light_down(l_index);
LIGHT_map_remove(l_index);
LIGHT_give(l_index);
}
//
// Gets/sets a light's position.
//
GameCoord LIGHT_pos_get(LIGHT_Index l_index)
{
ASSERT(WITHIN(l_index, 1, LIGHT_MAX_LIGHTS - 1));
return LIGHT_light[l_index].pos;
}
void LIGHT_pos_set(LIGHT_Index l_index, GameCoord newpos)
{
ASSERT(WITHIN(l_index, 0, LIGHT_MAX_LIGHTS - 1));
LIGHT_Light *ll = &LIGHT_light[l_index];
if (IMPLIES(ll->type == LIGHT_TYPE_BROKEN, ll->counter & 0x80) &&
IMPLIES(ll->type == LIGHT_TYPE_PULSE, ll->counter > (ll->param >> 1)))
{
//
// The light is on.
//
LIGHT_hf_light_down(l_index);
LIGHT_map_move(l_index, newpos);
LIGHT_hf_light_up(l_index);
}
else
{
//
// The light is off.
//
LIGHT_map_move(l_index, newpos);
}
}
//
// Processes all the lights.
//
void LIGHT_process()
{
SLONG i;
UBYTE just_on;
UBYTE just_off;
LIGHT_Light *ll;
for (i = 0; i < LIGHT_MAX_LIGHTS; i++)
{
ll = &LIGHT_light[i];
if (ll->type)
{
just_on = FALSE;
just_off = FALSE;
if (ll->type == LIGHT_TYPE_PULSE)
{
//
// If the counter > (ll->param / 2) then
// the light is off, otherwise it is on.
//
ll->counter += 1;
if (ll->counter == (ll->param >> 1))
{
just_off = TRUE;
}
if (ll->counter >= ll->param)
{
ll->counter = 0;
just_on = TRUE;
}
}
if (ll->type == LIGHT_TYPE_BROKEN)
{
//
// The top bit of counter is whether the light is on or off.
//
SLONG countdown = ll->counter & 0x7f;
if (countdown == 0)
{
countdown = rand() % ((ll->param >> 1) + 1);
ll->counter ^= 0x80;
ll->counter &= 0x80;
ll->counter |= countdown;
if (ll->counter & 0x80)
{
just_on = TRUE;
}
else
{
just_off = TRUE;
}
}
else
{
countdown -= 1;
ll->counter &= 0x80;
ll->counter |= countdown;
}
}
if (just_on)
{
LIGHT_hf_light_up(i);
}
if (just_off)
{
LIGHT_hf_light_down(i);
}
}
}
}
//
// Remember the light context, incase we need that info
// shortly afterwards in a call to LIGHT_prim.
//
#define LIGHT_MAX_PER_PRIM 8
THING_INDEX LIGHT_context_t_index;
LIGHT_Index LIGHT_context_l_index[LIGHT_MAX_PER_PRIM];
SLONG LIGHT_context_l_num;
SLONG LIGHT_context_gameturn;
SLONG LIGHT_context_context;
SLONG LIGHT_get_context(THING_INDEX t_index)
{
SLONG i;
SLONG x;
SLONG z;
SLONG dx;
SLONG dy;
SLONG dz;
SLONG dist;
SLONG range;
SLONG x1, z1;
SLONG x2, z2;
SLONG context;
LIGHT_Index next;
LIGHT_Light *ll;
Thing *p_thing = TO_THING(t_index);
//
// Find all the lights acting on this prim.
//
#define LIGHT_THING_SIZE (256)
#define LIGHT_SEARCH_RANGE (LIGHT_MAX_RANGE + LIGHT_THING_SIZE)
x1 = LIGHT_TO_MAP((p_thing->WorldPos.X >> 8) - LIGHT_SEARCH_RANGE);
z1 = LIGHT_TO_MAP((p_thing->WorldPos.Z >> 8) - LIGHT_SEARCH_RANGE);
x2 = LIGHT_TO_MAP((p_thing->WorldPos.X >> 8) + LIGHT_SEARCH_RANGE);
z2 = LIGHT_TO_MAP((p_thing->WorldPos.Z >> 8) + LIGHT_SEARCH_RANGE);
SATURATE(x1, 0, LIGHT_MAP_SIZE - 1);
SATURATE(z1, 0, LIGHT_MAP_SIZE - 1);
SATURATE(x2, 0, LIGHT_MAP_SIZE - 1);
SATURATE(z2, 0, LIGHT_MAP_SIZE - 1);
LIGHT_context_t_index = t_index;
LIGHT_context_l_num = 0;
LIGHT_context_gameturn = GAME_TURN;
for (x = x1; x <= x2; x++)
for (z = z1; z <= z2; z++)
{
ASSERT(WITHIN(x, 0, LIGHT_MAP_SIZE - 1));
ASSERT(WITHIN(z, 0, LIGHT_MAP_SIZE - 1));
next = LIGHT_map[x][z].next;
while(next)
{
ASSERT(WITHIN(next, 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[next];
if (IMPLIES(ll->type == LIGHT_TYPE_BROKEN, ll->counter & 0x80) &&
IMPLIES(ll->type == LIGHT_TYPE_PULSE, ll->counter > (ll->param >> 1)))
{
//
// What is the range of this light?
//
range = LIGHT_MAX_RANGE * ll->range >> 8;
//
// How far is the prim from the light?
//
dx = abs(ll->pos.X - (p_thing->WorldPos.X >> 8));
dy = abs(ll->pos.Y - (p_thing->WorldPos.Y >> 8));
dz = abs(ll->pos.Z - (p_thing->WorldPos.Z >> 8));
dist = QDIST3(dx, dy, dz);
if (dist <= range)
{
//
// This light is acting on the prim.
//
if (LIGHT_context_l_num < LIGHT_MAX_PER_PRIM)
{
LIGHT_context_l_index[LIGHT_context_l_num++] = next;
}
}
}
next = ll->next;
}
}
//
// Work out the context.
//
context = 0;
context ^= LIGHT_context_t_index << 5;
context ^= LIGHT_context_l_num << 13;
context ^= p_thing->WorldPos.X;
context ^= p_thing->WorldPos.Z;
if (p_thing->DrawType == DT_MESH)
{
context ^= p_thing->Draw.Mesh->Angle << 2;
context ^= p_thing->Draw.Mesh->Tilt << 5;
context ^= p_thing->Draw.Mesh->Roll << 9;
}
for (i = 0; i < LIGHT_context_l_num; i++)
{
ASSERT(WITHIN(LIGHT_context_l_index[i], 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[LIGHT_context_l_index[i]];
context ^= ll->pos.X << (i + 0);
context ^= ll->pos.Y << (i + 5);
context ^= ll->pos.Z << (i + 10);
context ^= ll->range << (i + 11);
#if LIGHT_COLOURED
context ^= ll->colour.red << (8 - i);
context ^= ll->colour.green << (16 - i);
context ^= ll->colour.blue << (24 - i);
#else
context ^= ll->colour << (24 - i);
#endif
}
LIGHT_context_context = context;
return context;
}
LIGHT_Colour LIGHT_get_point(SLONG x, SLONG y, SLONG z)
{
SLONG i;
SLONG dx;
SLONG dy;
SLONG dz;
SLONG dist;
SLONG range;
SLONG brightness;
LIGHT_Light *ll;
LIGHT_Colour ans;
//
// Since we have no normal info, assume that only
// have the ambient light shines off this point.
//
#if LIGHT_COLOURED
ans.red = LIGHT_amb_colour.red >> 1;
ans.green = LIGHT_amb_colour.green >> 1;
ans.blue = LIGHT_amb_colour.blue >> 1;
#else
ans = LIGHT_amb_colour;
#endif
//
// Add the light from all the lights.
//
for (i = 0; i < LIGHT_context_l_num; i++)
{
ASSERT(WITHIN(LIGHT_context_l_index[i], 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[LIGHT_context_l_index[i]];
//
// What is the range of this light?
//
range = LIGHT_MAX_RANGE * ll->range >> 8;
//
// Light each point.
//
dx = abs(x - ll->pos.X);
dy = abs(y - ll->pos.Y);
dz = abs(z - ll->pos.Z);
dist = QDIST3(dx, dy, dz);
brightness = 256 - ((dist * 256) / range); // Shouldn't we convert this to a multiply?
if (brightness > 0)
{
#if LIGHT_COLOURED
ans.red += ll->colour.red * brightness >> 8;
ans.green += ll->colour.green * brightness >> 8;
ans.blue += ll->colour.blue * brightness >> 8;
#else
ans += ll->colour * brightness >> 8;
#endif
}
}
return ans;
}
void LIGHT_prim(THING_INDEX t_index)
{
SLONG i;
SLONG j;
SLONG x;
SLONG z;
SLONG dx;
SLONG dy;
SLONG dz;
SLONG dist;
SLONG range;
SLONG brightness;
SLONG x1;
SLONG x2;
SLONG z1;
SLONG z2;
UBYTE next;
SLONG prim;
SLONG num_points;
SLONG matrix[9];
GameCoord lp;
DrawMesh *dm;
Thing *p_thing = TO_THING(t_index);
LIGHT_Light *ll;
PrimObject *p_obj;
//
// This must have a DT_MESH drawtype...
//
ASSERT(p_thing->DrawType == DT_MESH);
ASSERT(WITHIN(p_thing->Draw.Mesh, &DRAW_MESHES[0], &DRAW_MESHES[MAX_DRAW_MESHES]));
dm = p_thing->Draw.Mesh;
//
// Find out the lighting context of the thing, if we have to...
//
if (LIGHT_context_t_index == t_index &&
LIGHT_context_gameturn == GAME_TURN)
{
//
// It is already worked out!
//
}
else
{
LIGHT_get_context(t_index);
}
//
// What is the prim?
//
ASSERT(WITHIN(dm->ObjectId, 1, next_prim_object - 1));
p_obj = &prim_objects[dm->ObjectId];
//
// What is the rotation matrix of the prim?
//
FMATRIX_calc(matrix, dm->Angle, dm->Tilt, dm->Roll);
//
// Initialise all the point colours to the ambient light colour.
//
num_points = p_obj->EndPoint - p_obj->StartPoint;
for (i = 0; i < num_points; i++)
{
LIGHT_point_colour[i] = LIGHT_amb_colour;
}
//
// Remember how many points we have.
//
LIGHT_point_colour_upto = num_points;
//
// Go through all the lights that shine on this prim.
//
for (i = 0; i < LIGHT_context_l_num; i++)
{
ASSERT(WITHIN(LIGHT_context_l_index[i], 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[LIGHT_context_l_index[i]];
//
// What is the range of this light?
//
range = LIGHT_MAX_RANGE * ll->range >> 8;
//
// Rotate this light into the space of the prim.
//
lp.X = ll->pos.X - (p_thing->WorldPos.X >> 8);
lp.Y = ll->pos.Y - (p_thing->WorldPos.Y >> 8);
lp.Z = ll->pos.Z - (p_thing->WorldPos.Z >> 8);
FMATRIX_MUL(
matrix,
lp.X,
lp.Y,
lp.Z);
//
// Light each point.
//
for (j = 0; j < num_points; j++)
{
dx = abs(prim_points[i + p_obj->StartPoint].X - lp.X);
dy = abs(prim_points[i + p_obj->StartPoint].Y - lp.Y);
dz = abs(prim_points[i + p_obj->StartPoint].Z - lp.Z);
dist = QDIST3(dx, dy, dz);
brightness = 256 - ((dist * 256) / range); // Shouldn't we convert this to a multiply?
if (brightness > 0)
{
#if LIGHT_COLOURED
LIGHT_point_colour[j].red += ll->colour.red * brightness >> 8;
LIGHT_point_colour[j].green += ll->colour.green * brightness >> 8;
LIGHT_point_colour[j].blue += ll->colour.blue * brightness >> 8;
#else
LIGHT_point_colour[j] += ll->colour * brightness >> 8;
#endif
}
}
}
}
void LIGHT_prim_use_normals(THING_INDEX t_index)
{
SLONG i;
SLONG j;
SLONG x;
SLONG z;
SLONG dx;
SLONG dy;
SLONG dz;
SLONG dist;
SLONG range;
SLONG brightness;
SLONG amb_x;
SLONG amb_y;
SLONG amb_z;
SLONG x1;
SLONG x2;
SLONG z1;
SLONG z2;
SLONG dprod;
UBYTE next;
SLONG prim;
SLONG num_points;
SLONG matrix[9];
GameCoord lp;
DrawMesh *dm;
Thing *p_thing = TO_THING(t_index);
LIGHT_Light *ll;
PrimObject *p_obj;
PrimNormal *p_normal;
PrimPoint *p_point;
SVector l_normal;
//
// This must have a DT_MESH drawtype...
//
ASSERT(p_thing->DrawType == DT_MESH);
ASSERT(WITHIN(p_thing->Draw.Mesh, &DRAW_MESHES[0], &DRAW_MESHES[MAX_DRAW_MESHES]));
dm = p_thing->Draw.Mesh;
//
// Find out the lighting context of the thing, if we have to...
//
if (LIGHT_context_t_index == t_index &&
LIGHT_context_gameturn == GAME_TURN)
{
//
// It is already worked out!
//
}
else
{
LIGHT_get_context(t_index);
}
//
// What is the prim?
//
ASSERT(WITHIN(dm->ObjectId, 1, next_prim_object - 1));
p_obj = &prim_objects[dm->ObjectId];
//
// What is the rotation matrix of the prim?
//
FMATRIX_calc(matrix, dm->Angle, dm->Tilt, dm->Roll);
//
// Rotate the ambient light into the space of the prim.
//
amb_x = LIGHT_amb_norm_x;
amb_y = LIGHT_amb_norm_y;
amb_z = LIGHT_amb_norm_z;
FMATRIX_MUL(
matrix,
amb_x,
amb_y,
amb_z);
//
// Initialise all the point colours to the ambient light colour.
//
num_points = p_obj->EndPoint - p_obj->StartPoint;
for (i = 0; i < num_points; i++)
{
dprod =
amb_x * prim_normal[p_obj->StartPoint + i].X +
amb_y * prim_normal[p_obj->StartPoint + i].Y +
amb_z * prim_normal[p_obj->StartPoint + i].Z;
dprod >>= 9;
dprod = -dprod;
dprod += 128;
#if LIGHT_COLOURED
LIGHT_point_colour[i].red = LIGHT_amb_colour.red * dprod >> 8;
LIGHT_point_colour[i].green = LIGHT_amb_colour.green * dprod >> 8;
LIGHT_point_colour[i].blue = LIGHT_amb_colour.blue * dprod >> 8;
#else
LIGHT_point_colour[i] = LIGHT_amb_colour * dprod >> 8;
#endif
}
//
// Remember how many points we have.
//
LIGHT_point_colour_upto = num_points;
//
// Go through all the lights that shine on this prim.
//
for (i = 0; i < LIGHT_context_l_num; i++)
{
ASSERT(WITHIN(LIGHT_context_l_index[i], 1, LIGHT_MAX_LIGHTS - 1));
ll = &LIGHT_light[LIGHT_context_l_index[i]];
//
// What is the range of this light?
//
range = LIGHT_MAX_RANGE * ll->range >> 8;
//
// Rotate this light into the space of the prim.
//
lp.X = ll->pos.X - (p_thing->WorldPos.X >> 8);
lp.Y = ll->pos.Y - (p_thing->WorldPos.Y >> 8);
lp.Z = ll->pos.Z - (p_thing->WorldPos.Z >> 8);
FMATRIX_MUL_BY_TRANSPOSE(
matrix,
lp.X,
lp.Y,
lp.Z);
//
// The normalised vector from the light to the centre of the prim.
//
l_normal.X = -lp.X;
l_normal.Y = -lp.Y;
l_normal.Z = -lp.Z;
dx = abs(l_normal.X);
dy = abs(l_normal.Y);
dz = abs(l_normal.Z);
dist = QDIST3(dx, dy, dz);
l_normal.X <<= 8;
l_normal.Y <<= 8;
l_normal.Z <<= 8;
l_normal.X /= dist;
l_normal.Y /= dist;
l_normal.Z /= dist;
//
// Light each point.
//
for (j = 0; j < num_points; j++)
{
p_point = &prim_points[j + p_obj->StartPoint];
p_normal = &prim_normal[j + p_obj->StartPoint];
//
// Take the light vector and the point normal into account.
//
dprod = l_normal.X * p_normal->X + l_normal.Y * p_normal->Y + l_normal.Z * p_normal->Z >> 8;
if (dprod > 0)
{
//
// Distance from the light.
//
dx = lp.X - p_point->X;
dy = lp.Y - p_point->Y;
dz = lp.Z - p_point->Z;
dx = abs(dx);
dy = abs(dy);
dz = abs(dz);
dist = QDIST3(dx, dy, dz);
brightness = 256 - ((dist * 256) / range); // Shouldn't we convert this to a multiply?
brightness = brightness * dprod >> 8;
if (brightness > 0)
{
#if LIGHT_COLOURED
LIGHT_point_colour[j].red += ll->colour.red * brightness >> 8;
LIGHT_point_colour[j].green += ll->colour.green * brightness >> 8;
LIGHT_point_colour[j].blue += ll->colour.blue * brightness >> 8;
#else
LIGHT_point_colour[j] += ll->colour * brightness >> 8;
#endif
}
}
}
}
}
// ########################################################
// ========================================================
// """"""""""""""""""""""""""""""""""""""""""""""""""""""""
// OLD LIGHT STUFF
// ........................................................
// ========================================================
// ########################################################
#ifdef EDITOR
#include "c:\fallen\editor\headers\scan.h"
extern void scan_undo_ambient(SLONG face,SLONG x,SLONG y,SLONG z,SLONG extra);
extern void apply_ambient_to_floor(void);
extern void remove_ambient_from_floor(void);
extern void scan_apply_ambient(SLONG face,SLONG x,SLONG y,SLONG z,SLONG extra);
#endif
void apply_global_amb_to_map(void)
{
#ifdef EDITOR
scan_function=scan_undo_ambient;
scan_map();
remove_ambient_from_floor();
extern void setup_ambient(SLONG dx,SLONG dy,SLONG dz,SLONG bright,SLONG flags);
// setup_ambient(100,100,-70,1024,2);
setup_ambient(90,-100,-90,655,2);
scan_function=scan_apply_ambient;
scan_map();
apply_ambient_to_floor();
#endif
}
#endif