Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * Portions of this file are copyright Rebirth contributors and licensed as
3
 * described in COPYING.txt.
4
 * Portions of this file are copyright Parallax Software and licensed
5
 * according to the Parallax license below.
6
 * See COPYING.txt for license details.
7
 
8
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9
SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12
IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14
FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18
*/
19
 
20
/*
21
 *
22
 * Lighting functions.
23
 *
24
 */
25
 
26
#include <algorithm>
27
#include <bitset>
28
#include <numeric>
29
#include <stdio.h>
30
#include <string.h>     // for memset()
31
 
32
#include "render_state.h"
33
#include "maths.h"
34
#include "vecmat.h"
35
#include "gr.h"
36
#include "inferno.h"
37
#include "segment.h"
38
#include "dxxerror.h"
39
#include "render.h"
40
#include "game.h"
41
#include "vclip.h"
42
#include "lighting.h"
43
#include "3d.h"
44
#include "interp.h"
45
#include "gameseg.h"
46
#include "laser.h"
47
#include "timer.h"
48
#include "player.h"
49
#include "playsave.h"
50
#include "weapon.h"
51
#include "powerup.h"
52
#include "fvi.h"
53
#include "object.h"
54
#include "robot.h"
55
#include "multi.h"
56
#include "palette.h"
57
#include "bm.h"
58
#include "rle.h"
59
#include "wall.h"
60
 
61
#include "compiler-range_for.h"
62
#include "partial_range.h"
63
#include "d_range.h"
64
 
65
using std::min;
66
 
67
static int Do_dynamic_light=1;
68
static int use_fcd_lighting;
69
 
70
#define HEADLIGHT_CONE_DOT      (F1_0*9/10)
71
#define HEADLIGHT_SCALE         (F1_0*10)
72
 
73
namespace dcx {
74
 
75
static void add_light_div(g3s_lrgb &d, const g3s_lrgb &light, const fix &scale)
76
{
77
        d.r += fixdiv(light.r, scale);
78
        d.g += fixdiv(light.g, scale);
79
        d.b += fixdiv(light.b, scale);
80
}
81
 
82
static void add_light_dot_square(g3s_lrgb &d, const g3s_lrgb &light, const fix &dot)
83
{
84
        auto square = fixmul(dot, dot);
85
        d.r += fixmul(square, light.r)/8;
86
        d.g += fixmul(square, light.g)/8;
87
        d.b += fixmul(square, light.b)/8;
88
}
89
 
90
static fix compute_player_light_emission_intensity(const object_base &objp)
91
{
92
        auto &phys_info = objp.mtype.phys_info;
93
        const auto drag = phys_info.drag;
94
        const fix k = fixmuldiv(phys_info.mass, drag, (F1_0 - drag));
95
        // smooth thrust value like set_thrust_from_velocity()
96
        const auto sthrust = vm_vec_copy_scale(phys_info.velocity, k);
97
        return std::max(static_cast<fix>(vm_vec_mag_quick(sthrust) / 4), F2_0) + F0_5;
98
}
99
 
100
static fix compute_fireball_light_emission_intensity(const d_vclip_array &Vclip, const object_base &objp)
101
{
102
        const auto oid = get_fireball_id(objp);
103
        if (oid >= Vclip.size())
104
                return 0;
105
        auto &v = Vclip[oid];
106
        const auto light_intensity = v.light_value;
107
        if (objp.lifeleft < F1_0*4)
108
                return fixmul(fixdiv(objp.lifeleft, v.play_time), light_intensity);
109
        return light_intensity;
110
}
111
 
112
}
113
 
114
// ----------------------------------------------------------------------------------------------
115
namespace dsx {
116
 
117
static void apply_light(fvmsegptridx &vmsegptridx, const g3s_lrgb obj_light_emission, const vcsegptridx_t obj_seg, const vms_vector &obj_pos, const unsigned n_render_vertices, std::array<unsigned, MAX_VERTICES> &render_vertices, const std::array<segnum_t, MAX_VERTICES> &vert_segnum_list, const icobjptridx_t objnum)
118
{
119
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
120
        auto &Vertices = LevelSharedVertexState.get_vertices();
121
        if (((obj_light_emission.r+obj_light_emission.g+obj_light_emission.b)/3) > 0)
122
        {
123
                fix obji_64 = ((obj_light_emission.r+obj_light_emission.g+obj_light_emission.b)/3)*64;
124
                sbyte is_marker = 0;
125
#if defined(DXX_BUILD_DESCENT_II)
126
                if (objnum && objnum->type == OBJ_MARKER)
127
                                is_marker = 1;
128
#endif
129
 
130
                auto &Dynamic_light = LevelUniqueLightState.Dynamic_light;
131
                auto &vcvertptr = Vertices.vcptr;
132
                // for pretty dim sources, only process vertices in object's own segment.
133
                //      12/04/95, MK, markers only cast light in own segment.
134
                if ((abs(obji_64) <= F1_0*8) || is_marker) {
135
                        auto &vp = obj_seg->verts;
136
 
137
                        range_for (const auto vertnum, vp)
138
                        {
139
                                fix                     dist;
140
                                auto &vertpos = *vcvertptr(vertnum);
141
                                dist = vm_vec_dist_quick(obj_pos, vertpos);
142
                                dist = fixmul(dist/4, dist/4);
143
                                if (dist < abs(obji_64)) {
144
                                        if (dist < MIN_LIGHT_DIST)
145
                                                dist = MIN_LIGHT_DIST;
146
 
147
                                        add_light_div(Dynamic_light[vertnum], obj_light_emission, dist);
148
                                }
149
                        }
150
                } else {
151
                        int     headlight_shift = 0;
152
                        fix     max_headlight_dist = F1_0*200;
153
 
154
#if defined(DXX_BUILD_DESCENT_II)
155
                        if (objnum)
156
                        {
157
                                const object &obj = *objnum;
158
                                if (obj.type == OBJ_PLAYER)
159
                                        if (obj.ctype.player_info.powerup_flags & PLAYER_FLAGS_HEADLIGHT_ON) {
160
                                                headlight_shift = 3;
161
                                                if (get_player_id(obj) != Player_num)
162
                                                {
163
                                                        fvi_query       fq;
164
                                                        fvi_info                hit_data;
165
                                                        int                     fate;
166
 
167
                                                        const auto tvec = vm_vec_scale_add(obj.pos, obj.orient.fvec, F1_0*200);
168
 
169
                                                        fq.startseg                             = obj_seg;
170
                                                        fq.p0                                           = &obj.pos;
171
                                                        fq.p1                                           = &tvec;
172
                                                        fq.rad                                  = 0;
173
                                                        fq.thisobjnum                   = objnum;
174
                                                        fq.ignore_obj_list.first = nullptr;
175
                                                        fq.flags                                        = FQ_TRANSWALL;
176
 
177
                                                        fate = find_vector_intersection(fq, hit_data);
178
                                                        if (fate != HIT_NONE)
179
                                                                max_headlight_dist = vm_vec_mag_quick(vm_vec_sub(hit_data.hit_pnt, obj.pos)) + F1_0*4;
180
                                                }
181
                                        }
182
                        }
183
#endif
184
                        range_for (const unsigned vv, xrange(n_render_vertices))
185
                        {
186
                                fix                     dist;
187
                                int                     apply_light = 0;
188
 
189
                                const auto vertnum = render_vertices[vv];
190
                                auto vsegnum = vert_segnum_list[vv];
191
                                auto &vertpos = *vcvertptr(vertnum);
192
 
193
                                if (use_fcd_lighting && abs(obji_64) > F1_0*32)
194
                                {
195
                                        dist = find_connected_distance(obj_pos, obj_seg, vertpos, vmsegptridx(vsegnum), n_render_vertices, WID_RENDPAST_FLAG|WID_FLY_FLAG);
196
                                        if (dist >= 0)
197
                                                apply_light = 1;
198
                                }
199
                                else
200
                                {
201
                                        dist = vm_vec_dist_quick(obj_pos, vertpos);
202
                                        apply_light = 1;
203
                                }
204
 
205
                                if (apply_light && ((dist >> headlight_shift) < abs(obji_64))) {
206
 
207
                                        if (dist < MIN_LIGHT_DIST)
208
                                                dist = MIN_LIGHT_DIST;
209
 
210
                                        if (headlight_shift && objnum)
211
                                        {
212
                                                fix dot;
213
                                                // MK, Optimization note: You compute distance about 15 lines up, this is partially redundant
214
                                                const auto vec_to_point = vm_vec_normalized_quick(vm_vec_sub(vertpos, obj_pos));
215
                                                dot = vm_vec_dot(vec_to_point, objnum->orient.fvec);
216
                                                if (dot < F1_0/2)
217
                                                {
218
                                                        // Do the normal thing, but darken around headlight.
219
                                                        add_light_div(Dynamic_light[vertnum], obj_light_emission, fixmul(HEADLIGHT_SCALE, dist));
220
                                                }
221
                                                else
222
                                                {
223
                                                        if (!(Game_mode & GM_MULTI) || dist < max_headlight_dist)
224
                                                        {
225
                                                                add_light_dot_square(Dynamic_light[vertnum], obj_light_emission, dot);
226
                                                        }
227
                                                }
228
                                        }
229
                                        else
230
                                        {
231
                                                add_light_div(Dynamic_light[vertnum], obj_light_emission, dist);
232
                                        }
233
                                }
234
                        }
235
                }
236
        }
237
}
238
}
239
 
240
#define FLASH_LEN_FIXED_SECONDS (F1_0/3)
241
#define FLASH_SCALE             (3*F1_0/FLASH_LEN_FIXED_SECONDS)
242
 
243
// ----------------------------------------------------------------------------------------------
244
static void cast_muzzle_flash_light(fvmsegptridx &vmsegptridx, int n_render_vertices, std::array<unsigned, MAX_VERTICES> &render_vertices, const std::array<segnum_t, MAX_VERTICES> &vert_segnum_list)
245
{
246
        fix64 current_time;
247
        short time_since_flash;
248
 
249
        current_time = timer_query();
250
 
251
        range_for (auto &i, Muzzle_data)
252
        {
253
                if (i.create_time)
254
                {
255
                        time_since_flash = current_time - i.create_time;
256
                        if (time_since_flash < FLASH_LEN_FIXED_SECONDS)
257
                        {
258
                                g3s_lrgb ml;
259
                                ml.r = ml.g = ml.b = ((FLASH_LEN_FIXED_SECONDS - time_since_flash) * FLASH_SCALE);
260
                                apply_light(vmsegptridx, ml, vmsegptridx(i.segnum), i.pos, n_render_vertices, render_vertices, vert_segnum_list, object_none);
261
                        }
262
                        else
263
                        {
264
                                i.create_time = 0; // turn off this muzzle flash
265
                        }
266
                }
267
        }
268
}
269
 
270
// Translation table to make flares flicker at different rates
271
const std::array<fix, 16> Obj_light_xlate{{0x1234, 0x3321, 0x2468, 0x1735,
272
                            0x0123, 0x19af, 0x3f03, 0x232a,
273
                            0x2123, 0x39af, 0x0f03, 0x132a,
274
                            0x3123, 0x29af, 0x1f03, 0x032a
275
}};
276
#if defined(DXX_BUILD_DESCENT_I)
277
#define compute_player_light_emission_intensity(LevelUniqueHeadlightState, obj) compute_player_light_emission_intensity(obj)
278
#define compute_light_emission(LevelSharedRobotInfoState, LevelUniqueHeadlightState, Vclip, obj)        compute_light_emission(Vclip, obj)
279
#elif defined(DXX_BUILD_DESCENT_II)
280
#undef compute_player_light_emission_intensity
281
#undef compute_light_emission
282
#endif
283
 
284
// ---------------------------------------------------------
285
namespace dsx {
286
 
287
#if defined(DXX_BUILD_DESCENT_II)
288
static fix compute_player_light_emission_intensity(d_level_unique_headlight_state &LevelUniqueHeadlightState, const object &objp)
289
{
290
        if (objp.ctype.player_info.powerup_flags & PLAYER_FLAGS_HEADLIGHT_ON)
291
        {
292
                auto &Headlights = LevelUniqueHeadlightState.Headlights;
293
                auto &Num_headlights = LevelUniqueHeadlightState.Num_headlights;
294
                if (Num_headlights < Headlights.size())
295
                        Headlights[Num_headlights++] = &objp;
296
                return HEADLIGHT_SCALE;
297
        }
298
        uint8_t hoard_orbs;
299
        // If hoard game and player, add extra light based on how many orbs you have Pulse as well.
300
        if (game_mode_hoard() && (hoard_orbs = objp.ctype.player_info.hoard.orbs))
301
        {
302
                const fix hoardlight = 1 + (i2f(hoard_orbs) / 2);
303
                const auto s = fix_sin(static_cast<fix>(GameTime64 >> 1) & 0xFFFF); // probably a bad way to do it
304
                return fixmul((s + F1_0) >> 1, hoardlight);
305
        }
306
        return compute_player_light_emission_intensity(objp);
307
}
308
#endif
309
 
310
static g3s_lrgb compute_light_emission(const d_level_shared_robot_info_state &LevelSharedRobotInfoState, d_level_unique_headlight_state &LevelUniqueHeadlightState, const d_vclip_array &Vclip, const vcobjptridx_t obj)
311
{
312
        int compute_color = 0;
313
        fix light_intensity = 0;
314
#if defined(DXX_BUILD_DESCENT_II)
315
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
316
#endif
317
        const object &objp = obj;
318
        switch (objp.type)
319
        {
320
                case OBJ_PLAYER:
321
                        light_intensity = compute_player_light_emission_intensity(LevelUniqueHeadlightState, objp);
322
                        break;
323
                case OBJ_FIREBALL:
324
                        light_intensity = compute_fireball_light_emission_intensity(Vclip, objp);
325
                        break;
326
                case OBJ_ROBOT:
327
#if defined(DXX_BUILD_DESCENT_I)
328
                        light_intensity = F1_0/2;       // F1_0*Robot_info[obj->id].lightcast;
329
#elif defined(DXX_BUILD_DESCENT_II)
330
                        light_intensity = F1_0*Robot_info[get_robot_id(objp)].lightcast;
331
#endif
332
                        break;
333
                case OBJ_WEAPON:
334
                {
335
                        const auto wid = get_weapon_id(objp);
336
                        const fix tval = Weapon_info[wid].light;
337
                        if (wid == weapon_id_type::FLARE_ID)
338
                                light_intensity = 2 * (min(tval, objp.lifeleft) + ((static_cast<fix>(GameTime64) ^ Obj_light_xlate[obj.get_unchecked_index() % Obj_light_xlate.size()]) & 0x3fff));
339
                        else
340
                                light_intensity = tval;
341
                        break;
342
                }
343
#if defined(DXX_BUILD_DESCENT_II)
344
                case OBJ_MARKER:
345
                {
346
                        fix lightval = objp.lifeleft;
347
 
348
                        lightval &= 0xffff;
349
                        lightval = 8 * abs(F1_0/2 - lightval);
350
 
351
                        light_intensity = lightval;
352
                        break;
353
                }
354
#endif
355
                case OBJ_POWERUP:
356
                        light_intensity = Powerup_info[get_powerup_id(objp)].light;
357
                        break;
358
                case OBJ_DEBRIS:
359
                        light_intensity = F1_0/4;
360
                        break;
361
                case OBJ_LIGHT:
362
                        light_intensity = objp.ctype.light_info.intensity;
363
                        break;
364
                default:
365
                        light_intensity = 0;
366
                        break;
367
        }
368
 
369
        const auto &&white_light = [light_intensity] {
370
                return g3s_lrgb{light_intensity, light_intensity, light_intensity};
371
        };
372
 
373
        if (!PlayerCfg.DynLightColor) // colored lights not desired so use intensity only OR no intensity (== no light == no color) at all
374
                return white_light();
375
 
376
        switch (objp.type) // find out if given object should cast colored light and compute if so
377
        {
378
                default:
379
                        break;
380
                case OBJ_FIREBALL:
381
                case OBJ_WEAPON:
382
#if defined(DXX_BUILD_DESCENT_II)
383
                case OBJ_MARKER:
384
#endif
385
                        compute_color = 1;
386
                        break;
387
                case OBJ_POWERUP:
388
                {
389
                        switch (get_powerup_id(objp))
390
                        {
391
                                case POW_EXTRA_LIFE:
392
                                case POW_ENERGY:
393
                                case POW_SHIELD_BOOST:
394
                                case POW_KEY_BLUE:
395
                                case POW_KEY_RED:
396
                                case POW_KEY_GOLD:
397
                                case POW_CLOAK:
398
                                case POW_INVULNERABILITY:
399
#if defined(DXX_BUILD_DESCENT_II)
400
                                case POW_HOARD_ORB:
401
#endif
402
                                        compute_color = 1;
403
                                        break;
404
                                default:
405
                                        break;
406
                        }
407
                        break;
408
                }
409
        }
410
 
411
        if (compute_color)
412
        {
413
                int t_idx_s = -1, t_idx_e = -1;
414
 
415
                if (light_intensity < F1_0) // for every effect we want color, increase light_intensity so the effect becomes barely visible
416
                        light_intensity = F1_0;
417
 
418
                g3s_lrgb obj_color = { 255, 255, 255 };
419
 
420
                switch (objp.render_type)
421
                {
422
                        case RT_NONE:
423
                                break; // no object - no light
424
                        case RT_POLYOBJ:
425
                        {
426
                                auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
427
                                const polymodel *const po = &Polygon_models[objp.rtype.pobj_info.model_num];
428
                                if (po->n_textures <= 0)
429
                                {
430
                                        int color = g3_poly_get_color(po->model_data.get());
431
                                        if (color)
432
                                        {
433
                                                obj_color.r = gr_current_pal[color].r;
434
                                                obj_color.g = gr_current_pal[color].g;
435
                                                obj_color.b = gr_current_pal[color].b;
436
                                        }
437
                                }
438
                                else
439
                                {
440
                                        t_idx_s = ObjBitmaps[ObjBitmapPtrs[po->first_texture]].index;
441
                                        t_idx_e = t_idx_s + po->n_textures - 1;
442
                                }
443
                                break;
444
                        }
445
                        case RT_LASER:
446
                        {
447
                                t_idx_s = t_idx_e = Weapon_info[get_weapon_id(objp)].bitmap.index;
448
                                break;
449
                        }
450
                        case RT_POWERUP:
451
                        {
452
                                auto &v = Vclip[objp.rtype.vclip_info.vclip_num];
453
                                auto &f = v.frames;
454
                                t_idx_s = f[0].index;
455
                                t_idx_e = f[v.num_frames - 1].index;
456
                                break;
457
                        }
458
                        case RT_WEAPON_VCLIP:
459
                        {
460
                                auto &v = Vclip[Weapon_info[get_weapon_id(objp)].weapon_vclip];
461
                                auto &f = v.frames;
462
                                t_idx_s = f[0].index;
463
                                t_idx_e = f[v.num_frames - 1].index;
464
                                break;
465
                        }
466
                        default:
467
                        {
468
                                const auto &vc = Vclip[objp.id];
469
                                t_idx_s = vc.frames[0].index;
470
                                t_idx_e = vc.frames[vc.num_frames-1].index;
471
                                break;
472
                        }
473
                }
474
 
475
                if (t_idx_s != -1 && t_idx_e != -1)
476
                {
477
                        obj_color.r = obj_color.g = obj_color.b = 0;
478
                        range_for (const int i, xrange(t_idx_s, t_idx_e + 1))
479
                        {
480
                                grs_bitmap *bm = &GameBitmaps[i];
481
                                bitmap_index bi;
482
                                bi.index = i;
483
                                PIGGY_PAGE_IN(bi);
484
                                obj_color.r += bm->avg_color_rgb[0];
485
                                obj_color.g += bm->avg_color_rgb[1];
486
                                obj_color.b += bm->avg_color_rgb[2];
487
                        }
488
                }
489
 
490
                const fix rgbsum = obj_color.r + obj_color.g + obj_color.b;
491
                // obviously this object did not give us any usable color. so let's do our own but with blackjack and hookers!
492
                if (rgbsum <= 0)
493
                        return white_light();
494
                // scale color to light intensity
495
                const float cscale = static_cast<float>(light_intensity * 3) / rgbsum;
496
                return g3s_lrgb{
497
                        static_cast<fix>(obj_color.r * cscale),
498
                        static_cast<fix>(obj_color.g * cscale),
499
                        static_cast<fix>(obj_color.b * cscale)
500
                };
501
        }
502
 
503
        return white_light();
504
}
505
 
506
// ----------------------------------------------------------------------------------------------
507
void set_dynamic_light(render_state_t &rstate)
508
{
509
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
510
        auto &Objects = LevelUniqueObjectState.Objects;
511
        auto &Vertices = LevelSharedVertexState.get_vertices();
512
        auto &vcobjptridx = Objects.vcptridx;
513
        std::array<unsigned, MAX_VERTICES> render_vertices;
514
        std::array<segnum_t, MAX_VERTICES> vert_segnum_list;
515
        static fix light_time;
516
 
517
#if defined(DXX_BUILD_DESCENT_II)
518
        LevelUniqueLightState.Num_headlights = 0;
519
#endif
520
 
521
        if (!Do_dynamic_light)
522
                return;
523
 
524
        light_time += FrameTime;
525
        if (light_time < (F1_0/60)) // it's enough to stress the CPU 60 times per second
526
                return;
527
        light_time = light_time - (F1_0/60);
528
 
529
        std::bitset<MAX_VERTICES> render_vertex_flags;
530
 
531
        //      Create list of vertices that need to be looked at for setting of ambient light.
532
        auto &Dynamic_light = LevelUniqueLightState.Dynamic_light;
533
        uint_fast32_t n_render_vertices = 0;
534
        range_for (const auto segnum, partial_const_range(rstate.Render_list, rstate.N_render_segs))
535
        {
536
                if (segnum != segment_none) {
537
                        auto &vp = Segments[segnum].verts;
538
                        range_for (const auto vnum, vp)
539
                        {
540
                                if (vnum > Vertices.get_count() - 1)
541
                                {
542
                                        Int3();         //invalid vertex number
543
                                        continue;       //ignore it, and go on to next one
544
                                }
545
                                auto &&b = render_vertex_flags[vnum];
546
                                if (!b)
547
                                {
548
                                        b = true;
549
                                        render_vertices[n_render_vertices] = vnum;
550
                                        vert_segnum_list[n_render_vertices] = segnum;
551
                                        n_render_vertices++;
552
                                        Dynamic_light[vnum] = {};
553
                                }
554
                        }
555
                }
556
        }
557
 
558
        cast_muzzle_flash_light(vmsegptridx, n_render_vertices, render_vertices, vert_segnum_list);
559
 
560
        range_for (const auto &&obj, vcobjptridx)
561
        {
562
                const object &objp = obj;
563
                if (objp.type == OBJ_NONE)
564
                        continue;
565
                const auto &&obj_light_emission = compute_light_emission(LevelSharedRobotInfoState, LevelUniqueLightState, Vclip, obj);
566
 
567
                if (((obj_light_emission.r+obj_light_emission.g+obj_light_emission.b)/3) > 0)
568
                        apply_light(vmsegptridx, obj_light_emission, vcsegptridx(objp.segnum), objp.pos, n_render_vertices, render_vertices, vert_segnum_list, obj);
569
        }
570
}
571
 
572
// ---------------------------------------------------------
573
 
574
#if defined(DXX_BUILD_DESCENT_II)
575
 
576
void toggle_headlight_active(object &player)
577
{
578
        auto &player_info = player.ctype.player_info;
579
        if (player_info.powerup_flags & PLAYER_FLAGS_HEADLIGHT) {
580
                player_info.powerup_flags ^= PLAYER_FLAGS_HEADLIGHT_ON;
581
                if (Game_mode & GM_MULTI)
582
                        multi_send_flags(player.id);
583
        }
584
}
585
 
586
static fix compute_headlight_light_on_object(const d_level_unique_headlight_state &LevelUniqueHeadlightState, const object_base &objp)
587
{
588
        fix     light;
589
 
590
        //      Let's just illuminate players and robots for speed reasons, ok?
591
        if (objp.type != OBJ_ROBOT && objp.type != OBJ_PLAYER)
592
                return 0;
593
 
594
        light = 0;
595
 
596
        range_for (const object_base *const light_objp, partial_const_range(LevelUniqueHeadlightState.Headlights, LevelUniqueHeadlightState.Num_headlights))
597
        {
598
                auto vec_to_obj = vm_vec_sub(objp.pos, light_objp->pos);
599
                const fix dist = vm_vec_normalize_quick(vec_to_obj);
600
                if (dist > 0) {
601
                        const fix dot = vm_vec_dot(light_objp->orient.fvec, vec_to_obj);
602
 
603
                        if (dot < F1_0/2)
604
                                light += fixdiv(HEADLIGHT_SCALE, fixmul(HEADLIGHT_SCALE, dist));        //      Do the normal thing, but darken around headlight.
605
                        else
606
                                light += fixmul(fixmul(dot, dot), HEADLIGHT_SCALE)/8;
607
                }
608
        }
609
        return light;
610
}
611
#endif
612
 
613
}
614
 
615
//compute the average dynamic light in a segment.  Takes the segment number
616
static g3s_lrgb compute_seg_dynamic_light(const std::array<g3s_lrgb, MAX_VERTICES> &Dynamic_light, const shared_segment &seg)
617
{
618
        const auto &&op = [&Dynamic_light](g3s_lrgb r, const unsigned v) {
619
                r.r += Dynamic_light[v].r;
620
                r.g += Dynamic_light[v].g;
621
                r.b += Dynamic_light[v].b;
622
                return r;
623
        };
624
        g3s_lrgb sum = std::accumulate(begin(seg.verts), end(seg.verts), g3s_lrgb{0, 0, 0}, op);
625
        sum.r >>= 3;
626
        sum.g >>= 3;
627
        sum.b >>= 3;
628
        return sum;
629
}
630
 
631
static std::array<g3s_lrgb, MAX_OBJECTS> object_light;
632
static std::array<object_signature_t, MAX_OBJECTS> object_sig;
633
const object *old_viewer;
634
static int reset_lighting_hack;
635
#define LIGHT_RATE i2f(4) //how fast the light ramps up
636
 
637
void start_lighting_frame(const object &viewer)
638
{
639
        reset_lighting_hack = (&viewer != old_viewer);
640
        old_viewer = &viewer;
641
}
642
 
643
namespace dsx {
644
 
645
//compute the lighting for an object.  Takes a pointer to the object,
646
//and possibly a rotated 3d point.  If the point isn't specified, the
647
//object's center point is rotated.
648
g3s_lrgb compute_object_light(const d_level_unique_light_state &LevelUniqueLightState, const vcobjptridx_t obj)
649
{
650
        g3s_lrgb light;
651
        const vcobjidx_t objnum = obj;
652
 
653
        //First, get static (mono) light for this segment
654
        const auto &&objsegp = vcsegptr(obj->segnum);
655
        light.r = light.g = light.b = objsegp->static_light;
656
 
657
        auto &os = object_sig[objnum];
658
        auto &ol = object_light[objnum];
659
        //Now, maybe return different value to smooth transitions
660
        if (!reset_lighting_hack && os == obj->signature)
661
        {
662
                fix frame_delta;
663
                g3s_lrgb delta_light;
664
 
665
                delta_light.r = light.r - ol.r;
666
                delta_light.g = light.g - ol.g;
667
                delta_light.b = light.b - ol.b;
668
 
669
                frame_delta = fixmul(LIGHT_RATE,FrameTime);
670
 
671
                if (abs(((delta_light.r+delta_light.g+delta_light.b)/3)) <= frame_delta)
672
                {
673
                        ol = light;             //we've hit the goal
674
                }
675
                else
676
                {
677
                        if (((delta_light.r+delta_light.g+delta_light.b)/3) < 0)
678
                                frame_delta = -frame_delta;
679
                        ol.r += frame_delta;
680
                        ol.g += frame_delta;
681
                        ol.b += frame_delta;
682
                        light = ol;
683
                }
684
 
685
        }
686
        else //new object, initialize 
687
        {
688
                os = obj->signature;
689
                ol = light;
690
        }
691
 
692
        //Finally, add in dynamic light for this segment
693
        auto &Dynamic_light = LevelUniqueLightState.Dynamic_light;
694
        const auto &&seg_dl = compute_seg_dynamic_light(Dynamic_light, objsegp);
695
#if defined(DXX_BUILD_DESCENT_II)
696
        //Next, add in (NOTE: WHITE) headlight on this object
697
        const fix mlight = compute_headlight_light_on_object(LevelUniqueLightState, obj);
698
        light.r += mlight;
699
        light.g += mlight;
700
        light.b += mlight;
701
#endif
702
 
703
        light.r += seg_dl.r;
704
        light.g += seg_dl.g;
705
        light.b += seg_dl.b;
706
 
707
        return light;
708
}
709
 
710
}