dxx-rebirth/main/lighting.c

861 lines
25 KiB
C

/*
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* Lighting functions.
*
*/
#include <stdio.h>
#include <string.h> // for memset()
#include "fix.h"
#include "vecmat.h"
#include "gr.h"
#include "inferno.h"
#include "segment.h"
#include "error.h"
#include "render.h"
#include "game.h"
#include "vclip.h"
#include "lighting.h"
#include "3d.h"
#include "interp.h"
#include "laser.h"
#include "timer.h"
#include "player.h"
#include "playsave.h"
#include "weapon.h"
#include "powerup.h"
#include "fvi.h"
#include "robot.h"
#include "multi.h"
#include "palette.h"
#include "bm.h"
#include "rle.h"
int Do_dynamic_light=1;
//int Use_fvi_lighting = 0;
g3s_lrgb Dynamic_light[MAX_VERTICES];
#define LIGHTING_CACHE_SIZE 4096 // Must be power of 2!
#define LIGHTING_FRAME_DELTA 256 // Recompute cache value every 8 frames.
#define LIGHTING_CACHE_SHIFT 8
int Lighting_frame_delta = 1;
int Lighting_cache[LIGHTING_CACHE_SIZE];
int Cache_hits=0, Cache_lookups=1;
// Return true if we think vertex vertnum is visible from segment segnum.
// If some amount of time has gone by, then recompute, else use cached value.
int lighting_cache_visible(int vertnum, int segnum, int objnum, vms_vector *obj_pos, int obj_seg, vms_vector *vertpos)
{
int cache_val, cache_frame, cache_vis;
cache_val = Lighting_cache[((segnum << LIGHTING_CACHE_SHIFT) ^ vertnum) & (LIGHTING_CACHE_SIZE-1)];
cache_frame = cache_val >> 1;
cache_vis = cache_val & 1;
Cache_lookups++;
if ((cache_frame == 0) || (cache_frame + Lighting_frame_delta <= FrameCount)) {
int apply_light=0;
fvi_query fq;
fvi_info hit_data;
int segnum, hit_type;
segnum = -1;
#ifndef NDEBUG
segnum = find_point_seg(obj_pos, obj_seg);
if (segnum == -1) {
Int3(); // Obj_pos is not in obj_seg!
return 0; // Done processing this object.
}
#endif
fq.p0 = obj_pos;
fq.startseg = obj_seg;
fq.p1 = vertpos;
fq.rad = 0;
fq.thisobjnum = objnum;
fq.ignore_obj_list = NULL;
fq.flags = FQ_TRANSWALL;
hit_type = find_vector_intersection(&fq, &hit_data);
// Hit_pos = Hit_data.hit_pnt;
// Hit_seg = Hit_data.hit_seg;
if (hit_type == HIT_OBJECT)
Int3(); // Hey, we're not supposed to be checking objects!
if (hit_type == HIT_NONE)
apply_light = 1;
else if (hit_type == HIT_WALL) {
fix dist_dist;
dist_dist = vm_vec_dist_quick(&hit_data.hit_pnt, obj_pos);
if (dist_dist < F1_0/4) {
apply_light = 1;
// -- Int3(); // Curious, did fvi detect intersection with wall containing vertex?
}
}
Lighting_cache[((segnum << LIGHTING_CACHE_SHIFT) ^ vertnum) & (LIGHTING_CACHE_SIZE-1)] = apply_light + (FrameCount << 1);
return apply_light;
} else {
Cache_hits++;
return cache_vis;
}
}
#define HEADLIGHT_CONE_DOT (F1_0*9/10)
#define HEADLIGHT_SCALE (F1_0*10)
// ----------------------------------------------------------------------------------------------
void apply_light(g3s_lrgb obj_light_emission, int obj_seg, vms_vector *obj_pos, int n_render_vertices, short *render_vertices, int objnum)
{
int vv;
if (((obj_light_emission.r+obj_light_emission.g+obj_light_emission.b)/3) > 0)
{
fix obji_64 = ((obj_light_emission.r+obj_light_emission.g+obj_light_emission.b)/3)*64;
// for pretty dim sources, only process vertices in object's own segment.
// 12/04/95, MK, markers only cast light in own segment.
if ((abs(obji_64) <= F1_0*8) || (Objects[objnum].type == OBJ_MARKER)) {
short *vp = Segments[obj_seg].verts;
for (vv=0; vv<MAX_VERTICES_PER_SEGMENT; vv++) {
int vertnum;
vms_vector *vertpos;
fix dist;
vertnum = vp[vv];
if ((vertnum ^ FrameCount) & 1) {
vertpos = &Vertices[vertnum];
dist = vm_vec_dist_quick(obj_pos, vertpos);
dist = fixmul(dist/4, dist/4);
if (dist < abs(obji_64)) {
if (dist < MIN_LIGHT_DIST)
dist = MIN_LIGHT_DIST;
Dynamic_light[vertnum].r += fixdiv(obj_light_emission.r, dist);
Dynamic_light[vertnum].g += fixdiv(obj_light_emission.g, dist);
Dynamic_light[vertnum].b += fixdiv(obj_light_emission.b, dist);
}
}
}
} else {
int headlight_shift = 0;
fix max_headlight_dist = F1_0*200;
if (Objects[objnum].type == OBJ_PLAYER)
if (Players[Objects[objnum].id].flags & PLAYER_FLAGS_HEADLIGHT_ON) {
headlight_shift = 3;
if (Objects[objnum].id != Player_num) {
vms_vector tvec;
fvi_query fq;
fvi_info hit_data;
int fate;
vm_vec_scale_add(&tvec, &Objects[objnum].pos, &Objects[objnum].orient.fvec, F1_0*200);
fq.startseg = Objects[objnum].segnum;
fq.p0 = &Objects[objnum].pos;
fq.p1 = &tvec;
fq.rad = 0;
fq.thisobjnum = objnum;
fq.ignore_obj_list = NULL;
fq.flags = FQ_TRANSWALL;
fate = find_vector_intersection(&fq, &hit_data);
if (fate != HIT_NONE)
max_headlight_dist = vm_vec_mag_quick(vm_vec_sub(&tvec, &hit_data.hit_pnt, &Objects[objnum].pos)) + F1_0*4;
}
}
// -- for (vv=FrameCount&1; vv<n_render_vertices; vv+=2) {
for (vv=0; vv<n_render_vertices; vv++) {
int vertnum;
vms_vector *vertpos;
fix dist;
int apply_light;
vertnum = render_vertices[vv];
if ((vertnum ^ FrameCount) & 1) {
vertpos = &Vertices[vertnum];
dist = vm_vec_dist_quick(obj_pos, vertpos);
apply_light = 0;
if ((dist >> headlight_shift) < abs(obji_64)) {
if (dist < MIN_LIGHT_DIST)
dist = MIN_LIGHT_DIST;
//if (Use_fvi_lighting) {
// if (lighting_cache_visible(vertnum, obj_seg, objnum, obj_pos, obj_seg, vertpos)) {
// apply_light = 1;
// }
//} else
apply_light = 1;
if (apply_light)
{
if (headlight_shift)
{
fix dot;
vms_vector vec_to_point;
vm_vec_sub(&vec_to_point, vertpos, obj_pos);
vm_vec_normalize_quick(&vec_to_point); // MK, Optimization note: You compute distance about 15 lines up, this is partially redundant
dot = vm_vec_dot(&vec_to_point, &Objects[objnum].orient.fvec);
if (dot < F1_0/2)
{
// Do the normal thing, but darken around headlight.
Dynamic_light[vertnum].r += fixdiv(obj_light_emission.r, fixmul(HEADLIGHT_SCALE, dist));
Dynamic_light[vertnum].g += fixdiv(obj_light_emission.g, fixmul(HEADLIGHT_SCALE, dist));
Dynamic_light[vertnum].b += fixdiv(obj_light_emission.b, fixmul(HEADLIGHT_SCALE, dist));
}
else
{
if (Game_mode & GM_MULTI)
{
if (dist < max_headlight_dist)
{
Dynamic_light[vertnum].r += fixmul(fixmul(dot, dot), obj_light_emission.r)/8;
Dynamic_light[vertnum].g += fixmul(fixmul(dot, dot), obj_light_emission.g)/8;
Dynamic_light[vertnum].b += fixmul(fixmul(dot, dot), obj_light_emission.b)/8;
}
}
else
{
Dynamic_light[vertnum].r += fixmul(fixmul(dot, dot), obj_light_emission.r)/8;
Dynamic_light[vertnum].g += fixmul(fixmul(dot, dot), obj_light_emission.g)/8;
Dynamic_light[vertnum].b += fixmul(fixmul(dot, dot), obj_light_emission.b)/8;
}
}
}
else
{
Dynamic_light[vertnum].r += fixdiv(obj_light_emission.r, dist);
Dynamic_light[vertnum].g += fixdiv(obj_light_emission.g, dist);
Dynamic_light[vertnum].b += fixdiv(obj_light_emission.b, dist);
}
}
}
}
}
}
}
}
#define FLASH_LEN_FIXED_SECONDS (F1_0/3)
#define FLASH_SCALE (3*F1_0/FLASH_LEN_FIXED_SECONDS)
// ----------------------------------------------------------------------------------------------
void cast_muzzle_flash_light(int n_render_vertices, short *render_vertices)
{
fix64 current_time;
int i;
short time_since_flash;
current_time = timer_query();
for (i=0; i<MUZZLE_QUEUE_MAX; i++)
{
if (Muzzle_data[i].create_time)
{
time_since_flash = current_time - Muzzle_data[i].create_time;
if (time_since_flash < FLASH_LEN_FIXED_SECONDS)
{
g3s_lrgb ml;
ml.r = ml.g = ml.b = ((FLASH_LEN_FIXED_SECONDS - time_since_flash) * FLASH_SCALE);
apply_light(ml, Muzzle_data[i].segnum, &Muzzle_data[i].pos, n_render_vertices, render_vertices, -1);
}
else
{
Muzzle_data[i].create_time = 0; // turn off this muzzle flash
}
}
}
}
// Translation table to make flares flicker at different rates
fix Obj_light_xlate[16] = { 0x1234, 0x3321, 0x2468, 0x1735,
0x0123, 0x19af, 0x3f03, 0x232a,
0x2123, 0x39af, 0x0f03, 0x132a,
0x3123, 0x29af, 0x1f03, 0x032a };
// Flag array of objects lit last frame. Guaranteed to process this frame if lit last frame.
sbyte Lighting_objects[MAX_OBJECTS];
#define MAX_HEADLIGHTS 8
object *Headlights[MAX_HEADLIGHTS];
int Num_headlights;
g3s_lrgb compute_lrgb_on(object *obj)
{
int i, x, y, color, count = 0, t_idx_s = -1, t_idx_e = -1;
g3s_lrgb t_color = {0,0,0}, obj_color = {255,255,255};
switch (obj->render_type)
{
case RT_POLYOBJ:
{
polymodel *po = &Polygon_models[obj->rtype.pobj_info.model_num];
if (po->n_textures <= 0)
{
int color = g3_poly_get_color(po->model_data);
if (color)
{
obj_color.r = gr_current_pal[color*3];
obj_color.g = gr_current_pal[color*3+1];
obj_color.b = gr_current_pal[color*3+2];
}
}
else
{
t_idx_s = ObjBitmaps[ObjBitmapPtrs[po->first_texture]].index;
t_idx_e = t_idx_s + po->n_textures - 1;
}
break;
}
case RT_WEAPON_VCLIP:
{
t_idx_s = Vclip[Weapon_info[obj->id].weapon_vclip].frames[0].index;
t_idx_e = Vclip[Weapon_info[obj->id].weapon_vclip].frames[Vclip[Weapon_info[obj->id].weapon_vclip].num_frames-1].index;
break;
}
default:
{
t_idx_s = Vclip[obj->id].frames[0].index;
t_idx_e = Vclip[obj->id].frames[Vclip[obj->id].num_frames-1].index;
break;
}
}
if (t_idx_s != -1 && t_idx_e != -1)
{
for (i = t_idx_s; i <= t_idx_e; i++)
{
grs_bitmap *bm = &GameBitmaps[i];
ubyte buf[bm->bm_w*bm->bm_h];
if (!bm->bm_data)
return obj_color;
memset(&buf,0,bm->bm_w*bm->bm_h);
if (bm->bm_flags & BM_FLAG_RLE){
unsigned char * dbits;
unsigned char * sbits;
int data_offset;
data_offset = 1;
if (bm->bm_flags & BM_FLAG_RLE_BIG)
data_offset = 2;
sbits = &bm->bm_data[4 + (bm->bm_h * data_offset)];
dbits = buf;
for (i=0; i < bm->bm_h; i++ ) {
gr_rle_decode(sbits,dbits);
if ( bm->bm_flags & BM_FLAG_RLE_BIG )
sbits += (int)INTEL_SHORT(*((short *)&(bm->bm_data[4+(i*data_offset)])));
else
sbits += (int)bm->bm_data[4+i];
dbits += bm->bm_w;
}
}
else
{
memcpy(&buf, bm->bm_data, sizeof(unsigned char)*(bm->bm_w*bm->bm_h));
}
i = 0;
for (x = 0; x < bm->bm_h; x++)
{
for (y = 0; y < bm->bm_w; y++)
{
color = buf[i++];
t_color.r = gr_palette[color*3];
t_color.g = gr_palette[color*3+1];
t_color.b = gr_palette[color*3+2];
if (!(color == TRANSPARENCY_COLOR || (t_color.r == t_color.g && t_color.r == t_color.b)))
{
obj_color.r += t_color.r;
obj_color.g += t_color.g;
obj_color.b += t_color.b;
count++;
}
}
}
}
if (count)
{
obj_color.r /= count;
obj_color.g /= count;
obj_color.b /= count;
}
}
if (obj->render_type == RT_POLYOBJ)
obj->rtype.pobj_info.lrgb = obj_color;
else
obj->rtype.vclip_info.lrgb = obj_color;
return obj_color;
}
// ---------------------------------------------------------
g3s_lrgb compute_light_emission(int objnum)
{
object *obj = &Objects[objnum];
int compute_color = 0;
float cscale = 255.0;
fix hoardlight,s,light_intensity = 0;
g3s_lrgb lemission, obj_color = { 255, 255, 255 };
switch (obj->type)
{
case OBJ_PLAYER:
if (Players[obj->id].flags & PLAYER_FLAGS_HEADLIGHT_ON)
{
if (Num_headlights < MAX_HEADLIGHTS)
Headlights[Num_headlights++] = obj;
light_intensity = HEADLIGHT_SCALE;
}
else if ((Game_mode & GM_HOARD) && Players[obj->id].secondary_ammo[PROXIMITY_INDEX]) // If hoard game and player, add extra light based on how many orbs you have Pulse as well.
{
hoardlight=i2f(Players[obj->id].secondary_ammo[PROXIMITY_INDEX])/2; //i2f(12));
hoardlight++;
fix_sincos (((fix)(GameTime64/2)) & 0xFFFF,&s,NULL); // probably a bad way to do it
s+=F1_0;
s>>=1;
hoardlight=fixmul (s,hoardlight);
light_intensity = (hoardlight);
}
else
{
vms_vector sthrust = obj->mtype.phys_info.thrust;
fix k = fixmuldiv(obj->mtype.phys_info.mass,obj->mtype.phys_info.drag,(f1_0-obj->mtype.phys_info.drag));
// smooth thrust value like set_thrust_from_velocity()
vm_vec_copy_scale(&sthrust,&obj->mtype.phys_info.velocity,k);
light_intensity = max(vm_vec_mag_quick(&sthrust)/4, F1_0*2) + F1_0/2;
}
break;
case OBJ_FIREBALL:
if (obj->id != 0xff)
{
if (obj->lifeleft < F1_0*4)
light_intensity = fixmul(fixdiv(obj->lifeleft, Vclip[obj->id].play_time), Vclip[obj->id].light_value);
else
light_intensity = Vclip[obj->id].light_value;
}
else
light_intensity = 0;
break;
case OBJ_ROBOT:
light_intensity = F1_0*Robot_info[obj->id].lightcast;
break;
case OBJ_WEAPON:
{
fix tval = Weapon_info[obj->id].light;
if (Game_mode & GM_MULTI)
if (obj->id == OMEGA_ID)
if (d_rand() > 8192)
light_intensity = 0; // 3/4 of time, omega blobs will cast 0 light!
if (obj->id == FLARE_ID )
light_intensity = 2*(min(tval, obj->lifeleft) + ((((fix)GameTime64) ^ Obj_light_xlate[objnum&0x0f]) & 0x3fff));
else
light_intensity = tval;
break;
}
case OBJ_MARKER:
{
fix lightval = obj->lifeleft;
lightval &= 0xffff;
lightval = 8 * abs(F1_0/2 - lightval);
if (obj->lifeleft < F1_0*1000)
obj->lifeleft += F1_0; // Make sure this object doesn't go out.
light_intensity = lightval;
break;
}
case OBJ_POWERUP:
light_intensity = Powerup_info[obj->id].light;
break;
case OBJ_DEBRIS:
light_intensity = F1_0/4;
break;
case OBJ_LIGHT:
light_intensity = obj->ctype.light_info.intensity;
break;
default:
light_intensity = 0;
break;
}
lemission.r = lemission.g = lemission.b = light_intensity;
if (!PlayerCfg.DynLightColor) // colored lights not desired so use intensity only OR no intensity (== no light == no color) at all
return lemission;
switch (obj->type) // find out if given object should cast colored light and compute if so
{
case OBJ_FIREBALL:
case OBJ_WEAPON:
case OBJ_FLARE:
case OBJ_MARKER:
compute_color = 1;
break;
case OBJ_POWERUP:
{
switch (obj->id)
{
case POW_EXTRA_LIFE:
case POW_ENERGY:
case POW_SHIELD_BOOST:
case POW_KEY_BLUE:
case POW_KEY_RED:
case POW_KEY_GOLD:
case POW_CLOAK:
case POW_INVULNERABILITY:
case POW_HOARD_ORB:
compute_color = 1;
break;
default:
break;
}
break;
}
}
if (compute_color)
{
if (light_intensity < F1_0) // for every effect we want color, increase light_intensity so the effect becomes barely visible
light_intensity = F1_0;
obj_color = (obj->render_type == RT_POLYOBJ)?obj->rtype.pobj_info.lrgb:obj->rtype.vclip_info.lrgb;
if (obj_color.r == -1 || obj_color.g == -1 || obj_color.b == -1) // object does not have any color value, yet. compute!
obj_color = compute_lrgb_on(obj);
// scale color to light intensity
cscale = ((float)(light_intensity*3)/(obj_color.r+obj_color.g+obj_color.b));
lemission.r = obj_color.r * cscale;
lemission.g = obj_color.g * cscale;
lemission.b = obj_color.b * cscale;
}
return lemission;
}
// ----------------------------------------------------------------------------------------------
void set_dynamic_light(void)
{
int vv;
int objnum;
int n_render_vertices;
short render_vertices[MAX_VERTICES];
sbyte render_vertex_flags[MAX_VERTICES];
int render_seg,segnum, v;
sbyte new_lighting_objects[MAX_OBJECTS];
Num_headlights = 0;
if (!Do_dynamic_light)
return;
memset(render_vertex_flags, 0, Highest_vertex_index+1);
// Create list of vertices that need to be looked at for setting of ambient light.
n_render_vertices = 0;
for (render_seg=0; render_seg<N_render_segs; render_seg++) {
segnum = Render_list[render_seg];
if (segnum != -1) {
short *vp = Segments[segnum].verts;
for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) {
int vnum = vp[v];
if (vnum<0 || vnum>Highest_vertex_index) {
Int3(); //invalid vertex number
continue; //ignore it, and go on to next one
}
if (!render_vertex_flags[vnum]) {
render_vertex_flags[vnum] = 1;
render_vertices[n_render_vertices++] = vnum;
}
//--old way-- for (s=0; s<n_render_vertices; s++)
//--old way-- if (render_vertices[s] == vnum)
//--old way-- break;
//--old way-- if (s == n_render_vertices)
//--old way-- render_vertices[n_render_vertices++] = vnum;
}
}
}
// -- for (vertnum=FrameCount&1; vertnum<n_render_vertices; vertnum+=2) {
for (vv=0; vv<n_render_vertices; vv++) {
int vertnum;
vertnum = render_vertices[vv];
Assert(vertnum >= 0 && vertnum <= Highest_vertex_index);
if ((vertnum ^ FrameCount) & 1)
Dynamic_light[vertnum].r = Dynamic_light[vertnum].g = Dynamic_light[vertnum].b = 0;
}
cast_muzzle_flash_light(n_render_vertices, render_vertices);
for (objnum=0; objnum<=Highest_object_index; objnum++)
new_lighting_objects[objnum] = 0;
// July 5, 1995: New faster dynamic lighting code. About 5% faster on the PC (un-optimized).
// Only objects which are in rendered segments cast dynamic light. We might wad6 to extend this
// one or two segments if we notice light changing as objects go offscreen. I couldn't see any
// serious visual degradation. In fact, I could see no humorous degradation, either. --MK
for (render_seg=0; render_seg<N_render_segs; render_seg++) {
int segnum = Render_list[render_seg];
objnum = Segments[segnum].objects;
while (objnum != -1) {
object *obj = &Objects[objnum];
vms_vector *objpos = &obj->pos;
g3s_lrgb obj_light_emission;
obj_light_emission = compute_light_emission(objnum);
if (((obj_light_emission.r+obj_light_emission.g+obj_light_emission.b)/3) > 0) {
apply_light(obj_light_emission, obj->segnum, objpos, n_render_vertices, render_vertices, obj-Objects);
new_lighting_objects[objnum] = 1;
}
objnum = obj->next;
}
}
// Now, process all lights from last frame which haven't been processed this frame.
for (objnum=0; objnum<=Highest_object_index; objnum++) {
// In multiplayer games, process even unprocessed objects every 4th frame, else don't know about player sneaking up.
if ((Lighting_objects[objnum]) || ((Game_mode & GM_MULTI) && (((objnum ^ FrameCount) & 3) == 0))) {
if (!new_lighting_objects[objnum]) {
// Lighted last frame, but not this frame. Get intensity...
object *obj = &Objects[objnum];
vms_vector *objpos = &obj->pos;
g3s_lrgb obj_light_emission;
obj_light_emission = compute_light_emission(objnum);
if (((obj_light_emission.r+obj_light_emission.g+obj_light_emission.b)/3) > 0) {
apply_light(obj_light_emission, obj->segnum, objpos, n_render_vertices, render_vertices, objnum);
Lighting_objects[objnum] = 1;
} else
Lighting_objects[objnum] = 0;
}
} else {
// Not lighted last frame, so we don't need to light it. (Already lit if casting light this frame.)
// But copy value from new_lighting_objects to update Lighting_objects array.
Lighting_objects[objnum] = new_lighting_objects[objnum];
}
}
}
// ---------------------------------------------------------
void toggle_headlight_active()
{
if (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT) {
Players[Player_num].flags ^= PLAYER_FLAGS_HEADLIGHT_ON;
#ifdef NETWORK
if (Game_mode & GM_MULTI)
multi_send_flags(Player_num);
#endif
}
}
#define HEADLIGHT_BOOST_SCALE 8 //how much to scale light when have headlight boost
fix Beam_brightness = (F1_0/2); //global saying how bright the light beam is
#define MAX_DIST_LOG 6 //log(MAX_DIST-expressed-as-integer)
#define MAX_DIST (f1_0<<MAX_DIST_LOG) //no light beyond this dist
fix compute_headlight_light_on_object(object *objp)
{
int i;
fix light;
// Let's just illuminate players and robots for speed reasons, ok?
if ((objp->type != OBJ_ROBOT) && (objp->type != OBJ_PLAYER))
return 0;
light = 0;
for (i=0; i<Num_headlights; i++) {
fix dot, dist;
vms_vector vec_to_obj;
object *light_objp;
light_objp = Headlights[i];
vm_vec_sub(&vec_to_obj, &objp->pos, &light_objp->pos);
dist = vm_vec_normalize_quick(&vec_to_obj);
if (dist > 0) {
dot = vm_vec_dot(&light_objp->orient.fvec, &vec_to_obj);
if (dot < F1_0/2)
light += fixdiv(HEADLIGHT_SCALE, fixmul(HEADLIGHT_SCALE, dist)); // Do the normal thing, but darken around headlight.
else
light += fixmul(fixmul(dot, dot), HEADLIGHT_SCALE)/8;
}
}
return light;
}
//compute the average dynamic light in a segment. Takes the segment number
g3s_lrgb compute_seg_dynamic_light(int segnum)
{
g3s_lrgb sum, seg_lrgb;
segment *seg;
short *verts;
seg = &Segments[segnum];
verts = seg->verts;
sum.r = sum.g = sum.b = 0;
sum.r += Dynamic_light[*verts].r;
sum.g += Dynamic_light[*verts].g;
sum.b += Dynamic_light[*verts++].b;
sum.r += Dynamic_light[*verts].r;
sum.g += Dynamic_light[*verts].g;
sum.b += Dynamic_light[*verts++].b;
sum.r += Dynamic_light[*verts].r;
sum.g += Dynamic_light[*verts].g;
sum.b += Dynamic_light[*verts++].b;
sum.r += Dynamic_light[*verts].r;
sum.g += Dynamic_light[*verts].g;
sum.b += Dynamic_light[*verts++].b;
sum.r += Dynamic_light[*verts].r;
sum.g += Dynamic_light[*verts].g;
sum.b += Dynamic_light[*verts++].b;
sum.r += Dynamic_light[*verts].r;
sum.g += Dynamic_light[*verts].g;
sum.b += Dynamic_light[*verts++].b;
sum.r += Dynamic_light[*verts].r;
sum.g += Dynamic_light[*verts].g;
sum.b += Dynamic_light[*verts++].b;
sum.r += Dynamic_light[*verts].r;
sum.g += Dynamic_light[*verts].g;
sum.b += Dynamic_light[*verts].b;
seg_lrgb.r = sum.r >> 3;
seg_lrgb.g = sum.g >> 3;
seg_lrgb.b = sum.b >> 3;
return seg_lrgb;
}
g3s_lrgb object_light[MAX_OBJECTS];
int object_sig[MAX_OBJECTS];
object *old_viewer;
int reset_lighting_hack;
#define LIGHT_RATE i2f(4) //how fast the light ramps up
void start_lighting_frame(object *viewer)
{
reset_lighting_hack = (viewer != old_viewer);
old_viewer = viewer;
}
//compute the lighting for an object. Takes a pointer to the object,
//and possibly a rotated 3d point. If the point isn't specified, the
//object's center point is rotated.
g3s_lrgb compute_object_light(object *obj,vms_vector *rotated_pnt)
{
g3s_lrgb light, seg_dl;
fix mlight;
g3s_point objpnt;
int objnum = obj-Objects;
if (!rotated_pnt)
{
g3_rotate_point(&objpnt,&obj->pos);
rotated_pnt = &objpnt.p3_vec;
}
//First, get static (mono) light for this segment
light.r = light.g = light.b = Segment2s[obj->segnum].static_light;
//Now, maybe return different value to smooth transitions
if (!reset_lighting_hack && object_sig[objnum] == obj->signature)
{
fix frame_delta;
g3s_lrgb delta_light;
delta_light.r = light.r - object_light[objnum].r;
delta_light.g = light.g - object_light[objnum].g;
delta_light.b = light.b - object_light[objnum].b;
frame_delta = fixmul(LIGHT_RATE,FrameTime);
if (abs(((delta_light.r+delta_light.g+delta_light.b)/3)) <= frame_delta)
{
object_light[objnum] = light; //we've hit the goal
}
else
{
if (((delta_light.r+delta_light.g+delta_light.b)/3) < 0)
{
light.r = object_light[objnum].r -= frame_delta;
light.g = object_light[objnum].g -= frame_delta;
light.b = object_light[objnum].b -= frame_delta;
}
else
{
light.r = object_light[objnum].r += frame_delta;
light.g = object_light[objnum].g += frame_delta;
light.b = object_light[objnum].b += frame_delta;
}
}
}
else //new object, initialize
{
object_sig[objnum] = obj->signature;
object_light[objnum].r = light.r;
object_light[objnum].g = light.g;
object_light[objnum].b = light.b;
}
//Next, add in (NOTE: WHITE) headlight on this object
mlight = compute_headlight_light_on_object(obj);
light.r += mlight;
light.g += mlight;
light.b += mlight;
//Finally, add in dynamic light for this segment
seg_dl = compute_seg_dynamic_light(obj->segnum);
light.r += seg_dl.r;
light.g += seg_dl.g;
light.b += seg_dl.b;
return light;
}