1 /* $Id: lighting.c,v 1.5 2004-08-28 23:17:45 schaffner Exp $ */
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
26 static char rcsid[] = "$Id: lighting.c,v 1.5 2004-08-28 23:17:45 schaffner Exp $";
30 #include <string.h> // for memset()
53 int Do_dynamic_light=1;
54 //int Use_fvi_lighting = 0;
56 fix Dynamic_light[MAX_VERTICES];
58 #define LIGHTING_CACHE_SIZE 4096 // Must be power of 2!
59 #define LIGHTING_FRAME_DELTA 256 // Recompute cache value every 8 frames.
60 #define LIGHTING_CACHE_SHIFT 8
62 int Lighting_frame_delta = 1;
64 int Lighting_cache[LIGHTING_CACHE_SIZE];
66 int Cache_hits=0, Cache_lookups=1;
68 // Return true if we think vertex vertnum is visible from segment segnum.
69 // If some amount of time has gone by, then recompute, else use cached value.
70 int lighting_cache_visible(int vertnum, int segnum, int objnum, vms_vector *obj_pos, int obj_seg, vms_vector *vertpos)
72 int cache_val, cache_frame, cache_vis;
74 cache_val = Lighting_cache[((segnum << LIGHTING_CACHE_SHIFT) ^ vertnum) & (LIGHTING_CACHE_SIZE-1)];
76 cache_frame = cache_val >> 1;
77 cache_vis = cache_val & 1;
79 //mprintf((0, "%i %i %5i %i ", vertnum, segnum, cache_frame, cache_vis));
82 if ((cache_frame == 0) || (cache_frame + Lighting_frame_delta <= FrameCount)) {
91 segnum = find_point_seg(obj_pos, obj_seg);
93 Int3(); // Obj_pos is not in obj_seg!
94 return 0; // Done processing this object.
99 fq.startseg = obj_seg;
102 fq.thisobjnum = objnum;
103 fq.ignore_obj_list = NULL;
104 fq.flags = FQ_TRANSWALL;
106 hit_type = find_vector_intersection(&fq, &hit_data);
108 // Hit_pos = Hit_data.hit_pnt;
109 // Hit_seg = Hit_data.hit_seg;
111 if (hit_type == HIT_OBJECT)
112 Int3(); // Hey, we're not supposed to be checking objects!
114 if (hit_type == HIT_NONE)
116 else if (hit_type == HIT_WALL) {
118 dist_dist = vm_vec_dist_quick(&hit_data.hit_pnt, obj_pos);
119 if (dist_dist < F1_0/4) {
121 // -- Int3(); // Curious, did fvi detect intersection with wall containing vertex?
124 Lighting_cache[((segnum << LIGHTING_CACHE_SHIFT) ^ vertnum) & (LIGHTING_CACHE_SIZE-1)] = apply_light + (FrameCount << 1);
125 //mprintf((0, "%i\n", apply_light));
128 //mprintf((0, "\n"));
134 #define HEADLIGHT_CONE_DOT (F1_0*9/10)
135 #define HEADLIGHT_SCALE (F1_0*10)
137 // ----------------------------------------------------------------------------------------------
138 void apply_light(fix obj_intensity, int obj_seg, vms_vector *obj_pos, int n_render_vertices, short *render_vertices, int objnum)
143 fix obji_64 = obj_intensity*64;
145 // for pretty dim sources, only process vertices in object's own segment.
146 // 12/04/95, MK, markers only cast light in own segment.
147 if ((abs(obji_64) <= F1_0*8) || (Objects[objnum].type == OBJ_MARKER)) {
148 short *vp = Segments[obj_seg].verts;
150 for (vv=0; vv<MAX_VERTICES_PER_SEGMENT; vv++) {
156 if ((vertnum ^ FrameCount) & 1) {
157 vertpos = &Vertices[vertnum];
158 dist = vm_vec_dist_quick(obj_pos, vertpos);
159 dist = fixmul(dist/4, dist/4);
160 if (dist < abs(obji_64)) {
161 if (dist < MIN_LIGHT_DIST)
162 dist = MIN_LIGHT_DIST;
164 Dynamic_light[vertnum] += fixdiv(obj_intensity, dist);
169 int headlight_shift = 0;
170 fix max_headlight_dist = F1_0*200;
172 if (Objects[objnum].type == OBJ_PLAYER)
173 if (Players[Objects[objnum].id].flags & PLAYER_FLAGS_HEADLIGHT_ON) {
175 if (Objects[objnum].id != Player_num) {
181 vm_vec_scale_add(&tvec, &Objects[objnum].pos, &Objects[objnum].orient.fvec, F1_0*200);
183 fq.startseg = Objects[objnum].segnum;
184 fq.p0 = &Objects[objnum].pos;
187 fq.thisobjnum = objnum;
188 fq.ignore_obj_list = NULL;
189 fq.flags = FQ_TRANSWALL;
191 fate = find_vector_intersection(&fq, &hit_data);
192 if (fate != HIT_NONE)
193 max_headlight_dist = vm_vec_mag_quick(vm_vec_sub(&tvec, &hit_data.hit_pnt, &Objects[objnum].pos)) + F1_0*4;
196 // -- for (vv=FrameCount&1; vv<n_render_vertices; vv+=2) {
197 for (vv=0; vv<n_render_vertices; vv++) {
203 vertnum = render_vertices[vv];
204 if ((vertnum ^ FrameCount) & 1) {
205 vertpos = &Vertices[vertnum];
206 dist = vm_vec_dist_quick(obj_pos, vertpos);
209 if ((dist >> headlight_shift) < abs(obji_64)) {
211 if (dist < MIN_LIGHT_DIST)
212 dist = MIN_LIGHT_DIST;
214 //if (Use_fvi_lighting) {
215 // if (lighting_cache_visible(vertnum, obj_seg, objnum, obj_pos, obj_seg, vertpos)) {
222 if (headlight_shift) {
224 vms_vector vec_to_point;
226 vm_vec_sub(&vec_to_point, vertpos, obj_pos);
227 vm_vec_normalize_quick(&vec_to_point); // MK, Optimization note: You compute distance about 15 lines up, this is partially redundant
228 dot = vm_vec_dot(&vec_to_point, &Objects[objnum].orient.fvec);
230 Dynamic_light[vertnum] += fixdiv(obj_intensity, fixmul(HEADLIGHT_SCALE, dist)); // Do the normal thing, but darken around headlight.
232 if (Game_mode & GM_MULTI) {
233 if (dist < max_headlight_dist)
234 Dynamic_light[vertnum] += fixmul(fixmul(dot, dot), obj_intensity)/8;
236 Dynamic_light[vertnum] += fixmul(fixmul(dot, dot), obj_intensity)/8;
239 Dynamic_light[vertnum] += fixdiv(obj_intensity, dist);
248 #define FLASH_LEN_FIXED_SECONDS (F1_0/3)
249 #define FLASH_SCALE (3*F1_0/FLASH_LEN_FIXED_SECONDS)
251 // ----------------------------------------------------------------------------------------------
252 void cast_muzzle_flash_light(int n_render_vertices, short *render_vertices)
256 short time_since_flash;
258 current_time = timer_get_fixed_seconds();
260 for (i=0; i<MUZZLE_QUEUE_MAX; i++) {
261 if (Muzzle_data[i].create_time) {
262 time_since_flash = current_time - Muzzle_data[i].create_time;
263 if (time_since_flash < FLASH_LEN_FIXED_SECONDS)
264 apply_light((FLASH_LEN_FIXED_SECONDS - time_since_flash) * FLASH_SCALE, Muzzle_data[i].segnum, &Muzzle_data[i].pos, n_render_vertices, render_vertices, -1);
266 Muzzle_data[i].create_time = 0; // turn off this muzzle flash
271 // Translation table to make flares flicker at different rates
272 fix Obj_light_xlate[16] =
273 {0x1234, 0x3321, 0x2468, 0x1735,
274 0x0123, 0x19af, 0x3f03, 0x232a,
275 0x2123, 0x39af, 0x0f03, 0x132a,
276 0x3123, 0x29af, 0x1f03, 0x032a};
278 // Flag array of objects lit last frame. Guaranteed to process this frame if lit last frame.
279 sbyte Lighting_objects[MAX_OBJECTS];
281 #define MAX_HEADLIGHTS 8
282 object *Headlights[MAX_HEADLIGHTS];
285 // ---------------------------------------------------------
286 fix compute_light_intensity(int objnum)
288 object *obj = &Objects[objnum];
289 int objtype = obj->type;
294 if (Players[obj->id].flags & PLAYER_FLAGS_HEADLIGHT_ON) {
295 if (Num_headlights < MAX_HEADLIGHTS)
296 Headlights[Num_headlights++] = obj;
297 return HEADLIGHT_SCALE;
298 } else if ((Game_mode & GM_HOARD) && Players[obj->id].secondary_ammo[PROXIMITY_INDEX]) {
300 // If hoard game and player, add extra light based on how many orbs you have
303 hoardlight=i2f(Players[obj->id].secondary_ammo[PROXIMITY_INDEX])/2; //i2f(12));
305 fix_sincos ((GameTime/2) & 0xFFFF,&s,NULL); // probably a bad way to do it
308 hoardlight=fixmul (s,hoardlight);
309 // mprintf ((0,"Hoardlight is %f!\n",f2fl(hoardlight)));
313 return max(vm_vec_mag_quick(&obj->mtype.phys_info.thrust)/4, F1_0*2) + F1_0/2;
316 if (obj->id != 0xff) {
317 if (obj->lifeleft < F1_0*4)
318 return fixmul(fixdiv(obj->lifeleft, Vclip[obj->id].play_time), Vclip[obj->id].light_value);
320 return Vclip[obj->id].light_value;
325 return F1_0*Robot_info[obj->id].lightcast;
328 fix tval = Weapon_info[obj->id].light;
329 if (Game_mode & GM_MULTI)
330 if (obj->id == OMEGA_ID)
332 return 0; // 3/4 of time, omega blobs will cast 0 light!
334 if (obj->id == FLARE_ID )
335 return 2* (min(tval, obj->lifeleft) + ((GameTime ^ Obj_light_xlate[objnum&0x0f]) & 0x3fff));
341 fix lightval = obj->lifeleft;
345 lightval = 8 * abs(F1_0/2 - lightval);
347 if (obj->lifeleft < F1_0*1000)
348 obj->lifeleft += F1_0; // Make sure this object doesn't go out.
354 return Powerup_info[obj->id].light;
360 return obj->ctype.light_info.intensity;
368 // ----------------------------------------------------------------------------------------------
369 void set_dynamic_light(void)
373 int n_render_vertices;
374 short render_vertices[MAX_VERTICES];
375 sbyte render_vertex_flags[MAX_VERTICES];
376 int render_seg,segnum, v;
377 sbyte new_lighting_objects[MAX_OBJECTS];
381 if (!Do_dynamic_light)
384 //if (Use_fvi_lighting)
385 // mprintf((0, "hits = %8i, misses = %8i, lookups = %8i, hit ratio = %7.4f\n", Cache_hits, Cache_lookups - Cache_hits, Cache_lookups, (float) Cache_hits / Cache_lookups));
387 memset(render_vertex_flags, 0, Highest_vertex_index+1);
389 // Create list of vertices that need to be looked at for setting of ambient light.
390 n_render_vertices = 0;
391 for (render_seg=0; render_seg<N_render_segs; render_seg++) {
392 segnum = Render_list[render_seg];
394 short *vp = Segments[segnum].verts;
395 for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) {
397 if (vnum<0 || vnum>Highest_vertex_index) {
398 Int3(); //invalid vertex number
399 continue; //ignore it, and go on to next one
401 if (!render_vertex_flags[vnum]) {
402 render_vertex_flags[vnum] = 1;
403 render_vertices[n_render_vertices++] = vnum;
405 //--old way-- for (s=0; s<n_render_vertices; s++)
406 //--old way-- if (render_vertices[s] == vnum)
408 //--old way-- if (s == n_render_vertices)
409 //--old way-- render_vertices[n_render_vertices++] = vnum;
414 // -- for (vertnum=FrameCount&1; vertnum<n_render_vertices; vertnum+=2) {
415 for (vv=0; vv<n_render_vertices; vv++) {
418 vertnum = render_vertices[vv];
419 Assert(vertnum >= 0 && vertnum <= Highest_vertex_index);
420 if ((vertnum ^ FrameCount) & 1)
421 Dynamic_light[vertnum] = 0;
424 cast_muzzle_flash_light(n_render_vertices, render_vertices);
426 for (objnum=0; objnum<=Highest_object_index; objnum++)
427 new_lighting_objects[objnum] = 0;
429 // July 5, 1995: New faster dynamic lighting code. About 5% faster on the PC (un-optimized).
430 // Only objects which are in rendered segments cast dynamic light. We might wad6 to extend this
431 // one or two segments if we notice light changing as objects go offscreen. I couldn't see any
432 // serious visual degradation. In fact, I could see no humorous degradation, either. --MK
433 for (render_seg=0; render_seg<N_render_segs; render_seg++) {
434 int segnum = Render_list[render_seg];
436 objnum = Segments[segnum].objects;
438 while (objnum != -1) {
439 object *obj = &Objects[objnum];
440 vms_vector *objpos = &obj->pos;
443 obj_intensity = compute_light_intensity(objnum);
446 apply_light(obj_intensity, obj->segnum, objpos, n_render_vertices, render_vertices, obj-Objects);
447 new_lighting_objects[objnum] = 1;
454 // Now, process all lights from last frame which haven't been processed this frame.
455 for (objnum=0; objnum<=Highest_object_index; objnum++) {
456 // In multiplayer games, process even unprocessed objects every 4th frame, else don't know about player sneaking up.
457 if ((Lighting_objects[objnum]) || ((Game_mode & GM_MULTI) && (((objnum ^ FrameCount) & 3) == 0))) {
458 if (!new_lighting_objects[objnum]) {
459 // Lighted last frame, but not this frame. Get intensity...
460 object *obj = &Objects[objnum];
461 vms_vector *objpos = &obj->pos;
464 obj_intensity = compute_light_intensity(objnum);
467 apply_light(obj_intensity, obj->segnum, objpos, n_render_vertices, render_vertices, objnum);
468 Lighting_objects[objnum] = 1;
470 Lighting_objects[objnum] = 0;
473 // Not lighted last frame, so we don't need to light it. (Already lit if casting light this frame.)
474 // But copy value from new_lighting_objects to update Lighting_objects array.
475 Lighting_objects[objnum] = new_lighting_objects[objnum];
480 // ---------------------------------------------------------
482 void toggle_headlight_active()
484 if (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT) {
485 Players[Player_num].flags ^= PLAYER_FLAGS_HEADLIGHT_ON;
487 if (Game_mode & GM_MULTI)
488 multi_send_flags(Player_num);
493 #define HEADLIGHT_BOOST_SCALE 8 //how much to scale light when have headlight boost
495 fix Beam_brightness = (F1_0/2); //global saying how bright the light beam is
497 #define MAX_DIST_LOG 6 //log(MAX_DIST-expressed-as-integer)
498 #define MAX_DIST (f1_0<<MAX_DIST_LOG) //no light beyond this dist
500 fix compute_headlight_light_on_object(object *objp)
505 // Let's just illuminate players and robots for speed reasons, ok?
506 if ((objp->type != OBJ_ROBOT) && (objp->type != OBJ_PLAYER))
511 for (i=0; i<Num_headlights; i++) {
513 vms_vector vec_to_obj;
516 light_objp = Headlights[i];
518 vm_vec_sub(&vec_to_obj, &objp->pos, &light_objp->pos);
519 dist = vm_vec_normalize_quick(&vec_to_obj);
521 dot = vm_vec_dot(&light_objp->orient.fvec, &vec_to_obj);
524 light += fixdiv(HEADLIGHT_SCALE, fixmul(HEADLIGHT_SCALE, dist)); // Do the normal thing, but darken around headlight.
526 light += fixmul(fixmul(dot, dot), HEADLIGHT_SCALE)/8;
534 // -- Unused -- //Compute the lighting from the headlight for a given vertex on a face.
535 // -- Unused -- //Takes:
536 // -- Unused -- // point - the 3d coords of the point
537 // -- Unused -- // face_light - a scale factor derived from the surface normal of the face
538 // -- Unused -- //If no surface normal effect is wanted, pass F1_0 for face_light
539 // -- Unused -- fix compute_headlight_light(vms_vector *point,fix face_light)
541 // -- Unused -- fix light;
542 // -- Unused -- int use_beam = 0; //flag for beam effect
544 // -- Unused -- light = Beam_brightness;
546 // -- Unused -- if ((Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT) && (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT_ON) && Viewer==&Objects[Players[Player_num].objnum] && Players[Player_num].energy > 0) {
547 // -- Unused -- light *= HEADLIGHT_BOOST_SCALE;
548 // -- Unused -- use_beam = 1; //give us beam effect
551 // -- Unused -- if (light) { //if no beam, don't bother with the rest of this
552 // -- Unused -- fix point_dist;
554 // -- Unused -- point_dist = vm_vec_mag_quick(point);
556 // -- Unused -- if (point_dist >= MAX_DIST)
558 // -- Unused -- light = 0;
560 // -- Unused -- else {
561 // -- Unused -- fix dist_scale,face_scale;
563 // -- Unused -- dist_scale = (MAX_DIST - point_dist) >> MAX_DIST_LOG;
564 // -- Unused -- light = fixmul(light,dist_scale);
566 // -- Unused -- if (face_light < 0)
567 // -- Unused -- face_light = 0;
569 // -- Unused -- face_scale = f1_0/4 + face_light/2;
570 // -- Unused -- light = fixmul(light,face_scale);
572 // -- Unused -- if (use_beam) {
573 // -- Unused -- fix beam_scale;
575 // -- Unused -- if (face_light > f1_0*3/4 && point->z > i2f(12)) {
576 // -- Unused -- beam_scale = fixdiv(point->z,point_dist);
577 // -- Unused -- beam_scale = fixmul(beam_scale,beam_scale); //square it
578 // -- Unused -- light = fixmul(light,beam_scale);
584 // -- Unused -- return light;
587 //compute the average dynamic light in a segment. Takes the segment number
588 fix compute_seg_dynamic_light(int segnum)
594 seg = &Segments[segnum];
599 sum += Dynamic_light[*verts++];
600 sum += Dynamic_light[*verts++];
601 sum += Dynamic_light[*verts++];
602 sum += Dynamic_light[*verts++];
603 sum += Dynamic_light[*verts++];
604 sum += Dynamic_light[*verts++];
605 sum += Dynamic_light[*verts++];
606 sum += Dynamic_light[*verts];
612 fix object_light[MAX_OBJECTS];
613 int object_sig[MAX_OBJECTS];
615 int reset_lighting_hack;
617 #define LIGHT_RATE i2f(4) //how fast the light ramps up
619 void start_lighting_frame(object *viewer)
621 reset_lighting_hack = (viewer != old_viewer);
626 //compute the lighting for an object. Takes a pointer to the object,
627 //and possibly a rotated 3d point. If the point isn't specified, the
628 //object's center point is rotated.
629 fix compute_object_light(object *obj,vms_vector *rotated_pnt)
633 int objnum = obj-Objects;
636 g3_rotate_point(&objpnt,&obj->pos);
637 rotated_pnt = &objpnt.p3_vec;
640 //First, get static light for this segment
642 light = Segment2s[obj->segnum].static_light;
647 //Now, maybe return different value to smooth transitions
649 if (!reset_lighting_hack && object_sig[objnum] == obj->signature) {
650 fix delta_light,frame_delta;
652 delta_light = light - object_light[objnum];
654 frame_delta = fixmul(LIGHT_RATE,FrameTime);
656 if (abs(delta_light) <= frame_delta)
658 object_light[objnum] = light; //we've hit the goal
663 light = object_light[objnum] -= frame_delta;
665 light = object_light[objnum] += frame_delta;
668 else { //new object, initialize
670 object_sig[objnum] = obj->signature;
671 object_light[objnum] = light;
676 //Next, add in headlight on this object
678 // -- Matt code: light += compute_headlight_light(rotated_pnt,f1_0);
679 light += compute_headlight_light_on_object(obj);
681 //Finally, add in dynamic light for this segment
683 light += compute_seg_dynamic_light(obj->segnum);