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
 * Rendering Stuff
23
 *
24
 */
25
 
26
#include <algorithm>
27
#include <bitset>
28
#include <limits>
29
#include <cstdlib>
30
#include <stdio.h>
31
#include <string.h>
32
#include <math.h>
33
#include "render_state.h"
34
#include "inferno.h"
35
#include "segment.h"
36
#include "dxxerror.h"
37
#include "bm.h"
38
#include "texmap.h"
39
#include "render.h"
40
#include "game.h"
41
#include "object.h"
42
#include "laser.h"
43
#include "textures.h"
44
#include "screens.h"
45
#include "segpoint.h"
46
#include "wall.h"
47
#include "texmerge.h"
48
#include "physics.h"
49
#include "3d.h"
50
#include "gameseg.h"
51
#include "vclip.h"
52
#include "lighting.h"
53
#include "cntrlcen.h"
54
#include "newdemo.h"
55
#include "automap.h"
56
#include "endlevel.h"
57
#include "key.h"
58
#include "newmenu.h"
59
#include "u_mem.h"
60
#include "piggy.h"
61
#include "timer.h"
62
#include "effects.h"
63
#include "playsave.h"
64
#if DXX_USE_OGL
65
#include "ogl_init.h"
66
#endif
67
#include "args.h"
68
 
69
#include "compiler-range_for.h"
70
#include "d_range.h"
71
#include "partial_range.h"
72
#include "segiter.h"
73
 
74
#if DXX_USE_EDITOR
75
#include "editor/editor.h"
76
#include "editor/esegment.h"
77
#endif
78
#include <utility>
79
 
80
using std::min;
81
using std::max;
82
 
83
// (former) "detail level" values
84
#if DXX_USE_OGL
85
int Render_depth = MAX_RENDER_SEGS; //how many segments deep to render
86
#else
87
int Render_depth = 20; //how many segments deep to render
88
unsigned Max_linear_depth = 50; // Deepest segment at which linear interpolation will be used.
89
#endif
90
 
91
//used for checking if points have been rotated
92
int     Clear_window_color=-1;
93
int     Clear_window=2; // 1 = Clear whole background window, 2 = clear view portals into rest of world, 0 = no clear
94
 
95
static uint16_t s_current_generation;
96
 
97
// When any render function needs to know what's looking at it, it should 
98
// access Viewer members.
99
namespace dsx {
100
const object * Viewer = NULL;
101
}
102
 
103
#if !DXX_USE_EDITOR && defined(RELEASE)
104
constexpr
105
#endif
106
fix Render_zoom = 0x9000;                                       //the player's zoom factor
107
 
108
#ifndef NDEBUG
109
static std::bitset<MAX_OBJECTS> object_rendered;
110
#endif
111
 
112
#if DXX_USE_EDITOR
113
int     Render_only_bottom=0;
114
int     Bottom_bitmap_num = 9;
115
#endif
116
 
117
namespace dcx {
118
 
119
//Global vars for window clip test
120
int Window_clip_left,Window_clip_top,Window_clip_right,Window_clip_bot;
121
 
122
}
123
 
124
#if DXX_USE_EDITOR
125
int _search_mode = 0;                   //true if looking for curseg,side,face
126
short _search_x,_search_y;      //pixel we're looking at
127
static int found_side,found_face;
128
static segnum_t found_seg;
129
static objnum_t found_obj;
130
#else
131
constexpr int _search_mode = 0;
132
#endif
133
 
134
#ifdef NDEBUG           //if no debug code, set these vars to constants
135
#else
136
 
137
int Outline_mode=0;
138
 
139
int toggle_outline_mode(void)
140
{
141
        return Outline_mode = !Outline_mode;
142
}
143
#endif
144
 
145
#ifndef NDEBUG
146
#if DXX_USE_OGL
147
#define draw_outline(C,a,b)     draw_outline(a,b)
148
#endif
149
static void draw_outline(grs_canvas &canvas, const unsigned nverts, cg3s_point *const *const pointlist)
150
{
151
        const uint8_t color = BM_XRGB(63, 63, 63);
152
 
153
        const unsigned e = nverts - 1;
154
        range_for (const unsigned i, xrange(e))
155
                g3_draw_line(canvas, *pointlist[i], *pointlist[i + 1], color);
156
        g3_draw_line(canvas, *pointlist[e], *pointlist[0], color);
157
}
158
#endif
159
 
160
fix flash_scale;
161
 
162
#define FLASH_CYCLE_RATE f1_0
163
 
164
constexpr std::integral_constant<fix, FLASH_CYCLE_RATE> Flash_rate{};
165
 
166
//cycle the flashing light for when mine destroyed
167
namespace dsx {
168
void flash_frame()
169
{
170
        auto &LevelUniqueControlCenterState = LevelUniqueObjectState.ControlCenterState;
171
        static fixang flash_ang=0;
172
 
173
        if (Endlevel_sequence)
174
                return;
175
 
176
        if (PaletteBlueAdd > 10 )               //whiting out
177
                return;
178
 
179
//      flash_ang += fixmul(FLASH_CYCLE_RATE,FrameTime);
180
#if defined(DXX_BUILD_DESCENT_II)
181
        if (const auto Seismic_tremor_magnitude = LevelUniqueSeismicState.Seismic_tremor_magnitude)
182
        {
183
                fix     added_flash;
184
 
185
                added_flash = abs(Seismic_tremor_magnitude);
186
                if (added_flash < F1_0)
187
                        added_flash *= 16;
188
 
189
                flash_ang += fixmul(Flash_rate, fixmul(FrameTime, added_flash+F1_0));
190
                flash_scale = fix_fastsin(flash_ang);
191
                flash_scale = (flash_scale + F1_0*3)/4; //      gets in range 0.5 to 1.0
192
        } else
193
#endif
194
        if (LevelUniqueControlCenterState.Control_center_destroyed)
195
        {
196
                flash_ang += fixmul(Flash_rate,FrameTime);
197
                flash_scale = fix_fastsin(flash_ang);
198
                flash_scale = (flash_scale + f1_0)/2;
199
#if defined(DXX_BUILD_DESCENT_II)
200
                if (GameUniqueState.Difficulty_level == 0)
201
                        flash_scale = (flash_scale+F1_0*3)/4;
202
#endif
203
        }
204
 
205
 
206
}
207
 
208
static inline int is_alphablend_eclip(int eclip_num)
209
{
210
#if defined(DXX_BUILD_DESCENT_II)
211
        if (eclip_num == ECLIP_NUM_FORCE_FIELD || eclip_num == ECLIP_NUM_FORCE_FIELD2)
212
                return 1;
213
#endif
214
        return eclip_num == ECLIP_NUM_FUELCEN;
215
}
216
 
217
// ----------------------------------------------------------------------------
218
//      Render a face.
219
//      It would be nice to not have to pass in segnum and sidenum, but
220
//      they are used for our hideously hacked in headlight system.
221
//      vp is a pointer to vertex ids.
222
//      tmap1, tmap2 are texture map ids.  tmap2 is the pasty one.
223
static void render_face(grs_canvas &canvas, const shared_segment &segp, const unsigned sidenum, const unsigned nv, const std::array<unsigned, 4> &vp, const unsigned tmap1, const unsigned tmap2, std::array<g3s_uvl, 4> uvl_copy, const WALL_IS_DOORWAY_result_t wid_flags)
224
{
225
        auto &LevelUniqueControlCenterState = LevelUniqueObjectState.ControlCenterState;
226
        auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
227
        grs_bitmap  *bm;
228
 
229
        std::array<cg3s_point *, 4> pointlist;
230
 
231
        Assert(nv <= pointlist.size());
232
 
233
        range_for (const uint_fast32_t i, xrange(nv))
234
        {
235
                pointlist[i] = &Segment_points[vp[i]];
236
        }
237
 
238
#if defined(DXX_BUILD_DESCENT_I)
239
        (void)segp;
240
        (void)wid_flags;
241
#if !DXX_USE_EDITOR
242
        (void)sidenum;
243
#endif
244
#elif defined(DXX_BUILD_DESCENT_II)
245
        //handle cloaked walls
246
        if (wid_flags & WID_CLOAKED_FLAG) {
247
                const auto wall_num = segp.shared_segment::sides[sidenum].wall_num;
248
                auto &Walls = LevelUniqueWallSubsystemState.Walls;
249
                auto &vcwallptr = Walls.vcptr;
250
                gr_settransblend(canvas, vcwallptr(wall_num)->cloak_value, gr_blend::normal);
251
                const uint8_t color = BM_XRGB(0, 0, 0);
252
                // set to black (matters for s3)
253
 
254
                g3_draw_poly(canvas, nv, pointlist, color);    // draw as flat poly
255
                gr_settransblend(canvas, GR_FADE_OFF, gr_blend::normal);
256
 
257
                return;
258
        }
259
#endif
260
 
261
        if (tmap1 >= NumTextures) {
262
                Int3();
263
        }
264
 
265
#if DXX_USE_OGL
266
        grs_bitmap *bm2 = nullptr;
267
        if (!CGameArg.DbgUseOldTextureMerge)
268
        {
269
                PIGGY_PAGE_IN(Textures[tmap1]);
270
                bm = &GameBitmaps[Textures[tmap1].index];
271
                if (tmap2){
272
                        PIGGY_PAGE_IN(Textures[tmap2&0x3FFF]);
273
                        bm2 = &GameBitmaps[Textures[tmap2&0x3FFF].index];
274
                        if (bm2->get_flag_mask(BM_FLAG_SUPER_TRANSPARENT))
275
                        {
276
                                bm2 = nullptr;
277
                        bm = &texmerge_get_cached_bitmap( tmap1, tmap2 );
278
                        }
279
                }
280
        }else
281
#endif
282
 
283
                // New code for overlapping textures...
284
                if (tmap2 != 0) {
285
                        bm = &texmerge_get_cached_bitmap( tmap1, tmap2 );
286
                } else {
287
                        bm = &GameBitmaps[Textures[tmap1].index];
288
                        PIGGY_PAGE_IN(Textures[tmap1]);
289
                }
290
 
291
        assert(!bm->get_flag_mask(BM_FLAG_PAGED_OUT));
292
 
293
        std::array<g3s_lrgb, 4>         dyn_light;
294
#if defined(DXX_BUILD_DESCENT_I)
295
        const auto Seismic_tremor_magnitude = 0;
296
#elif defined(DXX_BUILD_DESCENT_II)
297
        const auto Seismic_tremor_magnitude = LevelUniqueSeismicState.Seismic_tremor_magnitude;
298
#endif
299
        const auto control_center_destroyed = LevelUniqueControlCenterState.Control_center_destroyed;
300
        const auto need_flashing_lights = (control_center_destroyed | Seismic_tremor_magnitude);        //make lights flash
301
        auto &Dynamic_light = LevelUniqueLightState.Dynamic_light;
302
        //set light values for each vertex & build pointlist
303
        range_for (const uint_fast32_t i, xrange(nv))
304
        {
305
                auto &dli = dyn_light[i];
306
                auto &uvli = uvl_copy[i];
307
                auto &Dlvpi = Dynamic_light[vp[i]];
308
                dli.r = dli.g = dli.b = uvli.l;
309
                //the uvl struct has static light already in it
310
 
311
                //scale static light for destruction effect
312
                if (need_flashing_lights)       //make lights flash
313
                        uvli.l = fixmul(flash_scale, uvli.l);
314
                //add in dynamic light (from explosions, etc.)
315
                uvli.l += (Dlvpi.r + Dlvpi.g + Dlvpi.b) / 3;
316
                //saturate at max value
317
                if (uvli.l > MAX_LIGHT)
318
                        uvli.l = MAX_LIGHT;
319
 
320
                // And now the same for the ACTUAL (rgb) light we want to use
321
 
322
                //scale static light for destruction effect
323
                if (need_flashing_lights)       //make lights flash
324
                {
325
                        dli.g = dli.b = fixmul(flash_scale, uvli.l);
326
                        dli.r = (!Seismic_tremor_magnitude && PlayerCfg.DynLightColor)
327
                                ? fixmul(std::max(static_cast<double>(flash_scale), f0_5 * 1.5), uvli.l) // let the mine glow red a little
328
                                : dli.g;
329
                }
330
 
331
                // add light color
332
                dli.r += Dlvpi.r;
333
                dli.g += Dlvpi.g;
334
                dli.b += Dlvpi.b;
335
                // saturate at max value
336
                if (dli.r > MAX_LIGHT)
337
                        dli.r = MAX_LIGHT;
338
                if (dli.g > MAX_LIGHT)
339
                        dli.g = MAX_LIGHT;
340
                if (dli.b > MAX_LIGHT)
341
                        dli.b = MAX_LIGHT;
342
                if (PlayerCfg.AlphaEffects) // due to additive blending, transparent sprites will become invivible in font of white surfaces (lamps). Fix that with a little desaturation
343
                {
344
                        dli.r *= .93;
345
                        dli.g *= .93;
346
                        dli.b *= .93;
347
                }
348
        }
349
 
350
        bool alpha = false;
351
        if (PlayerCfg.AlphaBlendEClips && is_alphablend_eclip(TmapInfo[tmap1].eclip_num)) // set nice transparency/blending for some special effects (if we do more, we should maybe use switch here)
352
        {
353
                alpha = true;
354
                gr_settransblend(canvas, GR_FADE_OFF, gr_blend::additive_c);
355
        }
356
 
357
#if DXX_USE_EDITOR
358
        if ((Render_only_bottom) && (sidenum == WBOTTOM))
359
                g3_draw_tmap(canvas, nv, pointlist, uvl_copy, dyn_light, GameBitmaps[Textures[Bottom_bitmap_num].index]);
360
        else
361
#endif
362
 
363
#if DXX_USE_OGL
364
                if (bm2){
365
                        g3_draw_tmap_2(canvas, nv, pointlist, uvl_copy, dyn_light, *bm, *bm2, ((tmap2 & 0xC000) >> 14) & 3);
366
                }else
367
#endif
368
                        g3_draw_tmap(canvas, nv, pointlist, uvl_copy, dyn_light, *bm);
369
 
370
        if (alpha)
371
                gr_settransblend(canvas, GR_FADE_OFF, gr_blend::normal); // revert any transparency / blending setting back to normal
372
 
373
#ifndef NDEBUG
374
        if (Outline_mode) draw_outline(canvas, nv, &pointlist[0]);
375
#endif
376
}
377
}
378
 
379
// ----------------------------------------------------------------------------
380
//      Only called if editor active.
381
//      Used to determine which face was clicked on.
382
static void check_face(grs_canvas &canvas, const vmsegidx_t segnum, const unsigned sidenum, const unsigned facenum, const unsigned nv, const std::array<unsigned, 4> &vp, const unsigned tmap1, const unsigned tmap2, const std::array<g3s_uvl, 4> &uvl_copy)
383
{
384
#if DXX_USE_EDITOR
385
        if (_search_mode) {
386
                std::array<g3s_lrgb, 4> dyn_light{};
387
                std::array<cg3s_point *, 4> pointlist;
388
#if DXX_USE_OGL
389
                (void)tmap1;
390
                (void)tmap2;
391
#else
392
                grs_bitmap *bm;
393
                if (tmap2 > 0 )
394
                        bm = &texmerge_get_cached_bitmap( tmap1, tmap2 );
395
                else
396
                        bm = &GameBitmaps[Textures[tmap1].index];
397
#endif
398
                range_for (const uint_fast32_t i, xrange(nv))
399
                {
400
                        dyn_light[i].r = dyn_light[i].g = dyn_light[i].b = uvl_copy[i].l;
401
                        pointlist[i] = &Segment_points[vp[i]];
402
                }
403
 
404
#if DXX_USE_OGL
405
                ogl_end_frame();
406
#endif
407
                {
408
                uint8_t color = 0;
409
                        gr_pixel(canvas.cv_bitmap, _search_x, _search_y, color);        //set our search pixel to color zero
410
                }
411
#if DXX_USE_OGL
412
                ogl_start_frame(canvas);
413
#endif
414
                {
415
#if DXX_USE_OGL
416
                        const uint8_t color = 1;
417
                        g3_draw_poly(canvas, nv, pointlist, color);
418
#else
419
                        const auto save_lighting = Lighting_on;
420
                        Lighting_on = 2;
421
                        g3_draw_tmap(canvas, nv, pointlist, uvl_copy, dyn_light, *bm);
422
                        Lighting_on = save_lighting;
423
#endif
424
                }
425
 
426
                if (gr_ugpixel(canvas.cv_bitmap,_search_x,_search_y) == 1) {
427
                        found_seg = segnum;
428
                        found_obj = object_none;
429
                        found_side = sidenum;
430
                        found_face = facenum;
431
                }
432
        }
433
#else
434
        (void)canvas;
435
        (void)segnum;
436
        (void)sidenum;
437
        (void)facenum;
438
        (void)nv;
439
        (void)vp;
440
        (void)tmap1;
441
        (void)tmap2;
442
        (void)uvl_copy;
443
#endif
444
}
445
 
446
template <std::size_t... N>
447
static inline void check_render_face(grs_canvas &canvas, std::index_sequence<N...>, const vcsegptridx_t segnum, const unsigned sidenum, const unsigned facenum, const std::array<unsigned, 4> &ovp, const unsigned tmap1, const unsigned tmap2, const std::array<uvl, 4> &uvlp, const WALL_IS_DOORWAY_result_t wid_flags, const std::size_t nv)
448
{
449
        const std::array<unsigned, 4> vp{{ovp[N]...}};
450
        const std::array<g3s_uvl, 4> uvl_copy{{
451
                {uvlp[N].u, uvlp[N].v, uvlp[N].l}...
452
        }};
453
        render_face(canvas, segnum, sidenum, nv, vp, tmap1, tmap2, uvl_copy, wid_flags);
454
        check_face(canvas, segnum, sidenum, facenum, nv, vp, tmap1, tmap2, uvl_copy);
455
}
456
 
457
template <std::size_t N0, std::size_t N1, std::size_t N2, std::size_t N3>
458
static inline void check_render_face(grs_canvas &canvas, std::index_sequence<N0, N1, N2, N3> is, const vcsegptridx_t segnum, const unsigned sidenum, const unsigned facenum, const std::array<unsigned, 4> &vp, const unsigned tmap1, const unsigned tmap2, const std::array<uvl, 4> &uvlp, const WALL_IS_DOORWAY_result_t wid_flags)
459
{
460
        check_render_face(canvas, is, segnum, sidenum, facenum, vp, tmap1, tmap2, uvlp, wid_flags, 4);
461
}
462
 
463
/* Avoid default constructing final element of uvl_copy; if any members
464
 * are default constructed, gcc zero initializes all members.
465
 */
466
template <std::size_t N0, std::size_t N1, std::size_t N2>
467
static inline void check_render_face(grs_canvas &canvas, std::index_sequence<N0, N1, N2>, const vcsegptridx_t segnum, const unsigned sidenum, const unsigned facenum, const std::array<unsigned, 4> &vp, const unsigned tmap1, const unsigned tmap2, const std::array<uvl, 4> &uvlp, const WALL_IS_DOORWAY_result_t wid_flags)
468
{
469
        check_render_face(canvas, std::index_sequence<N0, N1, N2, 3>(), segnum, sidenum, facenum, vp, tmap1, tmap2, uvlp, wid_flags, 3);
470
}
471
 
472
constexpr std::integral_constant<fix, (F1_0/4)> Tulate_min_dot{};
473
//--unused-- fix        Tulate_min_ratio = (2*F1_0);
474
constexpr std::integral_constant<fix, (F1_0*15/16)> Min_n0_n1_dot{};
475
 
476
// -----------------------------------------------------------------------------------
477
//      Render a side.
478
//      Check for normal facing.  If so, render faces on side dictated by sidep->type.
479
namespace dsx {
480
static void render_side(fvcvertptr &vcvertptr, grs_canvas &canvas, const vcsegptridx_t segp, const unsigned sidenum, const WALL_IS_DOORWAY_result_t wid_flags, const vms_vector &Viewer_eye)
481
{
482
        fix             min_dot, max_dot;
483
 
484
        if (!(wid_flags & WID_RENDER_FLAG))             //if (WALL_IS_DOORWAY(segp, sidenum) == WID_NO_WALL)
485
                return;
486
 
487
        const auto vertnum_list = get_side_verts(segp,sidenum);
488
 
489
        //      Regardless of whether this side is comprised of a single quad, or two triangles, we need to know one normal, so
490
        //      deal with it, get the dot product.
491
        const auto &sside = segp->shared_segment::sides[sidenum];
492
        const unsigned which_vertnum =
493
                (sside.get_type() == side_type::tri_13)
494
                        ? 1
495
                        : 0;
496
        const auto tvec = vm_vec_normalized_quick(vm_vec_sub(Viewer_eye, vcvertptr(vertnum_list[which_vertnum])));
497
        auto &normals = sside.normals;
498
        const auto v_dot_n0 = vm_vec_dot(tvec, normals[0]);
499
        //      ========== Mark: Here is the change...beginning here: ==========
500
 
501
        std::index_sequence<0, 1, 2, 3> is_quad;
502
        const auto &uside = segp->unique_segment::sides[sidenum];
503
        if (sside.get_type() == side_type::quad)
504
        {
505
                if (v_dot_n0 >= 0) {
506
                        check_render_face(canvas, is_quad, segp, sidenum, 0, vertnum_list, uside.tmap_num, uside.tmap_num2, uside.uvls, wid_flags);
507
                }
508
        } else {
509
                //      ========== Mark: The change ends here. ==========
510
 
511
                //      Although this side has been triangulated, because it is not planar, see if it is acceptable
512
                //      to render it as a single quadrilateral.  This is a function of how far away the viewer is, how non-planar
513
                //      the face is, how normal to the surfaces the view is.
514
                //      Now, if both dot products are close to 1.0, then render two triangles as a single quad.
515
                const auto v_dot_n1 = vm_vec_dot(tvec, normals[1]);
516
 
517
                if (v_dot_n0 < v_dot_n1) {
518
                        min_dot = v_dot_n0;
519
                        max_dot = v_dot_n1;
520
                } else {
521
                        min_dot = v_dot_n1;
522
                        max_dot = v_dot_n0;
523
                }
524
 
525
                //      Determine whether to detriangulate side: (speed hack, assumes Tulate_min_ratio == F1_0*2, should fixmul(min_dot, Tulate_min_ratio))
526
                if (DETRIANGULATION && ((min_dot+F1_0/256 > max_dot) || ((Viewer->segnum != segp) &&  (min_dot > Tulate_min_dot) && (max_dot < min_dot*2)))) {
527
                        fix     n0_dot_n1;
528
 
529
                        //      The other detriangulation code doesn't deal well with badly non-planar sides.
530
                        n0_dot_n1 = vm_vec_dot(normals[0], normals[1]);
531
                        if (n0_dot_n1 < Min_n0_n1_dot)
532
                                goto im_so_ashamed;
533
 
534
                        check_render_face(canvas, is_quad, segp, sidenum, 0, vertnum_list, uside.tmap_num, uside.tmap_num2, uside.uvls, wid_flags);
535
                } else {
536
im_so_ashamed: ;
537
                        if (sside.get_type() == side_type::tri_02)
538
                        {
539
                                if (v_dot_n0 >= 0) {
540
                                        check_render_face(canvas, std::index_sequence<0, 1, 2>(), segp, sidenum, 0, vertnum_list, uside.tmap_num, uside.tmap_num2, uside.uvls, wid_flags);
541
                                }
542
 
543
                                if (v_dot_n1 >= 0) {
544
                                        // want to render from vertices 0, 2, 3 on side
545
                                        check_render_face(canvas, std::index_sequence<0, 2, 3>(), segp, sidenum, 1, vertnum_list, uside.tmap_num, uside.tmap_num2, uside.uvls, wid_flags);
546
                                }
547
                        }
548
                        else if (sside.get_type() == side_type::tri_13)
549
                        {
550
                                if (v_dot_n1 >= 0) {
551
                                        // rendering 1,2,3, so just skip 0
552
                                        check_render_face(canvas, std::index_sequence<1, 2, 3>(), segp, sidenum, 1, vertnum_list, uside.tmap_num, uside.tmap_num2, uside.uvls, wid_flags);
553
                                }
554
 
555
                                if (v_dot_n0 >= 0) {
556
                                        // want to render from vertices 0,1,3
557
                                        check_render_face(canvas, std::index_sequence<0, 1, 3>(), segp, sidenum, 0, vertnum_list, uside.tmap_num, uside.tmap_num2, uside.uvls, wid_flags);
558
                                }
559
 
560
                        } else
561
                                throw shared_side::illegal_type(segp, sside);
562
                }
563
        }
564
 
565
}
566
 
567
#if DXX_USE_EDITOR
568
static void render_object_search(grs_canvas &canvas, const d_level_unique_light_state &LevelUniqueLightState, const vmobjptridx_t obj)
569
{
570
        int changed=0;
571
 
572
        //note that we draw each pixel object twice, since we cannot control
573
        //what color the object draws in, so we try color 0, then color 1,
574
        //in case the object itself is rendering color 0
575
 
576
        {
577
        const uint8_t color = 0;
578
        //set our search pixel to color zero
579
#if DXX_USE_OGL
580
        ogl_end_frame();
581
 
582
        // For OpenGL we use gr_rect instead of gr_pixel,
583
        // because in some implementations (like my Macbook Pro 5,1)
584
        // point smoothing can't be turned off.
585
        // Point smoothing would change the pixel to dark grey, but it MUST be black.
586
        // Making a 3x3 rectangle wouldn't matter
587
        // (but it only seems to draw a single pixel anyway)
588
        gr_rect(canvas, _search_x - 1, _search_y - 1, _search_x + 1, _search_y + 1, color);
589
 
590
        ogl_start_frame(canvas);
591
#else
592
        gr_pixel(canvas.cv_bitmap, _search_x, _search_y, color);
593
#endif
594
        }
595
        render_object(canvas, LevelUniqueLightState, obj);
596
        if (gr_ugpixel(canvas.cv_bitmap,_search_x,_search_y) != 0)
597
                changed=1;
598
 
599
        {
600
                const uint8_t color = 1;
601
#if DXX_USE_OGL
602
        ogl_end_frame();
603
        gr_rect(canvas, _search_x - 1, _search_y - 1, _search_x + 1, _search_y + 1, color);
604
        ogl_start_frame(canvas);
605
#else
606
        gr_pixel(canvas.cv_bitmap, _search_x, _search_y, color);
607
#endif
608
        }
609
        render_object(canvas, LevelUniqueLightState, obj);
610
        if (gr_ugpixel(canvas.cv_bitmap,_search_x,_search_y) != 1)
611
                changed=1;
612
 
613
        if (changed) {
614
                if (obj->segnum != segment_none)
615
                        Cursegp = imsegptridx(obj->segnum);
616
                found_seg = segment_none;
617
                found_obj = obj;
618
        }
619
}
620
#endif
621
 
622
static void do_render_object(grs_canvas &canvas, const d_level_unique_light_state &LevelUniqueLightState, const vmobjptridx_t obj, window_rendered_data &window)
623
{
624
#if DXX_USE_EDITOR
625
        int save_3d_outline=0;
626
        #endif
627
        int count = 0;
628
 
629
        #ifndef NDEBUG
630
        if (object_rendered[obj]) {             //already rendered this...
631
                Int3();         //get Matt!!!
632
                return;
633
        }
634
 
635
        object_rendered[obj] = true;
636
        #endif
637
 
638
#if defined(DXX_BUILD_DESCENT_II)
639
   if (Newdemo_state==ND_STATE_PLAYBACK)  
640
         {
641
          if ((DemoDoingLeft==6 || DemoDoingRight==6) && obj->type==OBJ_PLAYER)
642
                {
643
                        // A nice fat hack: keeps the player ship from showing up in the
644
                        // small extra view when guiding a missile in the big window
645
 
646
                        return;
647
                }
648
         }
649
#endif
650
 
651
        //      Added by MK on 09/07/94 (at about 5:28 pm, CDT, on a beautiful, sunny late summer day!) so
652
        //      that the guided missile system will know what objects to look at.
653
        //      I didn't know we had guided missiles before the release of D1. --MK
654
        if (obj->type == OBJ_ROBOT)
655
                window.rendered_robots.emplace_back(obj);
656
 
657
        if ((count++ > MAX_OBJECTS) || (obj->next == obj)) {
658
                Int3();                                 // infinite loop detected
659
                obj->next = object_none;                // won't this clean things up?
660
                return;                                 // get out of this infinite loop!
661
        }
662
 
663
                //g3_draw_object(obj->class_id,&obj->pos,&obj->orient,obj->size);
664
 
665
        //check for editor object
666
 
667
#if DXX_USE_EDITOR
668
        if (EditorWindow && obj==Cur_object_index) {
669
                save_3d_outline = g3d_interp_outline;
670
                g3d_interp_outline=1;
671
        }
672
        #endif
673
 
674
#if DXX_USE_EDITOR
675
        if (_search_mode)
676
                render_object_search(canvas, LevelUniqueLightState, obj);
677
        else
678
        #endif
679
                //NOTE LINK TO ABOVE
680
                render_object(canvas, LevelUniqueLightState, obj);
681
 
682
        for (auto n = obj->attached_obj; n != object_none;)
683
        {
684
                const auto &&o = obj.absolute_sibling(n);
685
                Assert(o->type == OBJ_FIREBALL);
686
                Assert(o->control_type == CT_EXPLOSION);
687
                Assert(o->flags & OF_ATTACHED);
688
                n = o->ctype.expl_info.next_attach;
689
 
690
                render_object(canvas, LevelUniqueLightState, o);
691
        }
692
 
693
 
694
#if DXX_USE_EDITOR
695
        if (EditorWindow && obj==Cur_object_index)
696
                g3d_interp_outline = save_3d_outline;
697
        #endif
698
 
699
 
700
}
701
}
702
 
703
//increment counter for checking if points rotated
704
//This must be called at the start of the frame if rotate_list() will be used
705
void render_start_frame()
706
{
707
        if (s_current_generation == std::numeric_limits<decltype(s_current_generation)>::max())
708
        {
709
                Segment_points = {};
710
                s_current_generation = 0;
711
        }
712
        ++ s_current_generation;
713
}
714
 
715
//Given a lit of point numbers, rotate any that haven't been rotated this frame
716
g3s_codes rotate_list(fvcvertptr &vcvertptr, const std::size_t nv, const unsigned *const pointnumlist)
717
{
718
        g3s_codes cc;
719
        const auto current_generation = s_current_generation;
720
        const auto cheats_acid = cheats.acid;
721
        const float f = likely(!cheats_acid)
722
                ? 0.0f /* unused */
723
                : 2.0f * (static_cast<float>(timer_query()) / F1_0);
724
 
725
        range_for (const auto pnum, unchecked_partial_range(pointnumlist, nv))
726
        {
727
                auto &pnt = Segment_points[pnum];
728
                if (pnt.p3_last_generation != current_generation)
729
                {
730
                        pnt.p3_last_generation = current_generation;
731
                        auto &v = *vcvertptr(pnum);
732
                        vertex tmpv;
733
                        g3_rotate_point(pnt, likely(!cheats_acid) ? v : (
734
                                tmpv = v,
735
                                tmpv.x += fl2f(sinf(f + f2fl(tmpv.x))),
736
                                tmpv.y += fl2f(sinf(f * 1.5f + f2fl(tmpv.y))),
737
                                tmpv.z += fl2f(sinf(f * 2.5f + f2fl(tmpv.z))),
738
                                tmpv
739
                        ));
740
                }
741
                cc.uand &= pnt.p3_codes;
742
                cc.uor  |= pnt.p3_codes;
743
        }
744
 
745
        return cc;
746
 
747
}
748
 
749
//Given a lit of point numbers, project any that haven't been projected
750
static void project_list(const std::array<unsigned, 8> &pointnumlist)
751
{
752
        range_for (const auto pnum, pointnumlist)
753
        {
754
                auto &p = Segment_points[pnum];
755
                if (!(p.p3_flags & PF_PROJECTED))
756
                        g3_project_point(p);
757
        }
758
}
759
 
760
 
761
// -----------------------------------------------------------------------------------
762
#if !DXX_USE_OGL
763
namespace dsx {
764
static void render_segment(fvcvertptr &vcvertptr, fvcwallptr &vcwallptr, const vms_vector &Viewer_eye, grs_canvas &canvas, const vcsegptridx_t seg)
765
{
766
        if (!rotate_list(vcvertptr, seg->verts).uand)
767
        {               //all off screen?
768
 
769
#if defined(DXX_BUILD_DESCENT_II)
770
                if (Viewer->type != OBJ_ROBOT)
771
#endif
772
                {
773
                        LevelUniqueAutomapState.Automap_visited[seg] = 1;
774
                }
775
 
776
                range_for (const uint_fast32_t sn, xrange(MAX_SIDES_PER_SEGMENT))
777
                        render_side(vcvertptr, canvas, seg, sn, WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, seg, sn), Viewer_eye);
778
        }
779
 
780
        //draw any objects that happen to be in this segment
781
 
782
        //sort objects!
783
        //object_sort_segment_objects( seg );
784
}
785
}
786
#endif
787
 
788
#if DXX_USE_EDITOR
789
#ifndef NDEBUG
790
 
791
constexpr fix CROSS_WIDTH = i2f(8);
792
constexpr fix CROSS_HEIGHT = i2f(8);
793
 
794
//draw outline for curside
795
static void outline_seg_side(grs_canvas &canvas, const shared_segment &seg, const unsigned _side, const unsigned edge, const unsigned vert)
796
{
797
        auto &verts = seg.verts;
798
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
799
        auto &Vertices = LevelSharedVertexState.get_vertices();
800
        auto &vcvertptr = Vertices.vcptr;
801
        if (!rotate_list(vcvertptr, verts).uand)
802
        {               //all off screen?
803
                g3s_point *pnt;
804
 
805
                //render curedge of curside of curseg in green
806
 
807
                const uint8_t color = BM_XRGB(0, 63, 0);
808
                auto &sv = Side_to_verts[_side];
809
                g3_draw_line(canvas, Segment_points[verts[sv[edge]]], Segment_points[verts[sv[(edge + 1)%4]]], color);
810
 
811
                //draw a little cross at the current vert
812
 
813
                pnt = &Segment_points[verts[Side_to_verts[_side][vert]]];
814
 
815
                g3_project_point(*pnt);         //make sure projected
816
 
817
//              gr_line(pnt->p3_sx-CROSS_WIDTH,pnt->p3_sy,pnt->p3_sx+CROSS_WIDTH,pnt->p3_sy);
818
//              gr_line(pnt->p3_sx,pnt->p3_sy-CROSS_HEIGHT,pnt->p3_sx,pnt->p3_sy+CROSS_HEIGHT);
819
 
820
                gr_line(canvas, pnt->p3_sx - CROSS_WIDTH, pnt->p3_sy, pnt->p3_sx, pnt->p3_sy - CROSS_HEIGHT, color);
821
                gr_line(canvas, pnt->p3_sx, pnt->p3_sy - CROSS_HEIGHT, pnt->p3_sx + CROSS_WIDTH, pnt->p3_sy, color);
822
                gr_line(canvas, pnt->p3_sx + CROSS_WIDTH, pnt->p3_sy, pnt->p3_sx, pnt->p3_sy + CROSS_HEIGHT, color);
823
                gr_line(canvas, pnt->p3_sx, pnt->p3_sy + CROSS_HEIGHT, pnt->p3_sx - CROSS_WIDTH, pnt->p3_sy, color);
824
        }
825
}
826
 
827
#endif
828
#endif
829
 
830
static ubyte code_window_point(fix x,fix y,const rect &w)
831
{
832
        ubyte code=0;
833
 
834
        if (x <= w.left)  code |= 1;
835
        if (x >= w.right) code |= 2;
836
 
837
        if (y <= w.top) code |= 4;
838
        if (y >= w.bot) code |= 8;
839
 
840
        return code;
841
}
842
 
843
//Given two sides of segment, tell the two verts which form the 
844
//edge between them
845
constexpr std::array<
846
        std::array<
847
                std::array<int_fast8_t, 2>,
848
                6>,
849
        6> Two_sides_to_edge = {{
850
        {{  {{edge_none,edge_none}},     {{3,7}},        {{edge_none,edge_none}},        {{2,6}},        {{6,7}},        {{2,3}}        }},
851
        {{  {{3,7}},     {{edge_none,edge_none}},        {{0,4}},        {{edge_none,edge_none}},        {{4,7}},        {{0,3}}        }},
852
        {{  {{edge_none,edge_none}},     {{0,4}},        {{edge_none,edge_none}},        {{1,5}},        {{4,5}},        {{0,1}}        }},
853
        {{  {{2,6}},     {{edge_none,edge_none}},        {{1,5}},        {{edge_none,edge_none}},        {{5,6}},        {{1,2}}        }},
854
        {{  {{6,7}},     {{4,7}},        {{4,5}},        {{5,6}},        {{edge_none,edge_none}},        {{edge_none,edge_none}}        }},
855
        {{  {{2,3}},     {{0,3}},        {{0,1}},        {{1,2}},        {{edge_none,edge_none}},        {{edge_none,edge_none}}        }}
856
}};
857
 
858
//given an edge specified by two verts, give the two sides on that edge
859
constexpr std::array<
860
        std::array<
861
                std::array<int_fast8_t, 2>,
862
                8>,
863
        8> Edge_to_sides = {{
864
        {{  {{side_none,side_none}},     {{2,5}},        {{side_none,side_none}},        {{1,5}},        {{1,2}},        {{side_none,side_none}},        {{side_none,side_none}},        {{side_none,side_none}}        }},
865
        {{  {{2,5}},     {{side_none,side_none}},        {{3,5}},        {{side_none,side_none}},        {{side_none,side_none}},        {{2,3}},        {{side_none,side_none}},        {{side_none,side_none}}        }},
866
        {{  {{side_none,side_none}},     {{3,5}},        {{side_none,side_none}},        {{0,5}},        {{side_none,side_none}},        {{side_none,side_none}},        {{0,3}},        {{side_none,side_none}}        }},
867
        {{  {{1,5}},     {{side_none,side_none}},        {{0,5}},        {{side_none,side_none}},        {{side_none,side_none}},        {{side_none,side_none}},        {{side_none,side_none}},        {{0,1}}        }},
868
        {{  {{1,2}},     {{side_none,side_none}},        {{side_none,side_none}},        {{side_none,side_none}},        {{side_none,side_none}},        {{2,4}},        {{side_none,side_none}},        {{1,4}}        }},
869
        {{  {{side_none,side_none}},     {{2,3}},        {{side_none,side_none}},        {{side_none,side_none}},        {{2,4}},        {{side_none,side_none}},        {{3,4}},        {{side_none,side_none}}        }},
870
        {{  {{side_none,side_none}},     {{side_none,side_none}},        {{0,3}},        {{side_none,side_none}},        {{side_none,side_none}},        {{3,4}},        {{side_none,side_none}},        {{0,4}}        }},
871
        {{  {{side_none,side_none}},     {{side_none,side_none}},        {{side_none,side_none}},        {{0,1}},        {{1,4}},        {{side_none,side_none}},        {{0,4}},        {{side_none,side_none}}        }},
872
}};
873
 
874
//@@//perform simple check on tables
875
//@@check_check()
876
//@@{
877
//@@    int i,j;
878
//@@
879
//@@    for (i=0;i<8;i++)
880
//@@            for (j=0;j<8;j++)
881
//@@                    Assert(Edge_to_sides[i][j][0] == Edge_to_sides[j][i][0] && 
882
//@@                                    Edge_to_sides[i][j][1] == Edge_to_sides[j][i][1]);
883
//@@
884
//@@    for (i=0;i<6;i++)
885
//@@            for (j=0;j<6;j++)
886
//@@                    Assert(Two_sides_to_edge[i][j][0] == Two_sides_to_edge[j][i][0] && 
887
//@@                                    Two_sides_to_edge[i][j][1] == Two_sides_to_edge[j][i][1]);
888
//@@
889
//@@
890
//@@}
891
 
892
 
893
//given an edge, tell what side is on that edge
894
__attribute_warn_unused_result
895
static int find_seg_side(const shared_segment &seg, const std::array<unsigned, 2> &verts, const unsigned notside)
896
{
897
        if (notside >= MAX_SIDES_PER_SEGMENT)
898
                throw std::logic_error("invalid notside");
899
 
900
        const auto v0 = verts[0];
901
        const auto v1 = verts[1];
902
 
903
        const auto b = begin(seg.verts);
904
        const auto e = end(seg.verts);
905
        auto iv0 = e;
906
        auto iv1 = e;
907
        for (auto i = b;;)
908
        {
909
                if (iv0 == e && *i == v0)
910
                {
911
                        iv0 = i;
912
                        if (iv1 != e)
913
                                break;
914
                }
915
                if (iv1 == e && *i == v1)
916
                {
917
                        iv1 = i;
918
                        if (iv0 != e)
919
                                break;
920
                }
921
                if (++i == e)
922
                        return side_none;
923
        }
924
 
925
        const auto &eptr = Edge_to_sides[std::distance(b, iv0)][std::distance(b, iv1)];
926
 
927
        const auto side0 = eptr[0];
928
        const auto side1 = eptr[1];
929
 
930
        Assert(side0 != side_none && side1 != side_none);
931
 
932
        if (side0 != notside) {
933
                Assert(side1==notside);
934
                return side0;
935
        }
936
        else {
937
                Assert(side0==notside);
938
                return side1;
939
        }
940
 
941
}
942
 
943
__attribute_warn_unused_result
944
static bool compare_child(fvcvertptr &vcvertptr, const vms_vector &Viewer_eye, const shared_segment &seg, const shared_segment &cseg, const sidenum_fast_t edgeside)
945
{
946
        const auto &cside = cseg.sides[edgeside];
947
        const auto &sv = Side_to_verts[edgeside][cside.get_type() == side_type::tri_13 ? 1 : 0];
948
        const auto &temp = vm_vec_sub(Viewer_eye, vcvertptr(seg.verts[sv]));
949
        const auto &cnormal = cside.normals;
950
        return vm_vec_dot(cnormal[0], temp) < 0 || vm_vec_dot(cnormal[1], temp) < 0;
951
}
952
 
953
//see if the order matters for these two children.
954
//returns 0 if order doesn't matter, 1 if c0 before c1, -1 if c1 before c0
955
__attribute_warn_unused_result
956
static bool compare_children(fvcvertptr &vcvertptr, const vms_vector &Viewer_eye, const vcsegptridx_t seg, const sidenum_fast_t s0, const sidenum_fast_t s1)
957
{
958
        Assert(s0 != side_none && s1 != side_none);
959
 
960
        if (s0 == s1)
961
                return false;
962
        if (Side_opposite[s0] == s1)
963
                return false;
964
        //find normals of adjoining sides
965
        const std::array<unsigned, 2> edge_verts = {
966
                {seg->verts[Two_sides_to_edge[s0][s1][0]], seg->verts[Two_sides_to_edge[s0][s1][1]]}
967
        };
968
        if (edge_verts[0] == -1 || edge_verts[1] == -1)
969
                throw std::logic_error("invalid edge vert");
970
        const auto &&seg0 = seg.absolute_sibling(seg->children[s0]);
971
        const auto edgeside0 = find_seg_side(seg0, edge_verts, find_connect_side(seg, seg0));
972
        if (edgeside0 == side_none)
973
                return false;
974
        const auto r0 = compare_child(vcvertptr, Viewer_eye, seg, seg0, edgeside0);
975
        if (!r0)
976
                return r0;
977
        const auto &&seg1 = seg.absolute_sibling(seg->children[s1]);
978
        const auto edgeside1 = find_seg_side(seg1, edge_verts, find_connect_side(seg, seg1));
979
        if (edgeside1 == side_none)
980
                return false;
981
        return !compare_child(vcvertptr, Viewer_eye, seg, seg1, edgeside1);
982
}
983
 
984
//short the children of segment to render in the correct order
985
//returns non-zero if swaps were made
986
using sort_child_array_t = std::array<sidenum_fast_t, MAX_SIDES_PER_SEGMENT>;
987
static void sort_seg_children(fvcvertptr &vcvertptr, const vms_vector &Viewer_eye, const vcsegptridx_t seg, const partial_range_t<sort_child_array_t::iterator> &r)
988
{
989
        //for each child,  compare with other children and see if order matters
990
        //if order matters, fix if wrong
991
        auto predicate = [&vcvertptr, &Viewer_eye, seg](const sidenum_fast_t a, const sidenum_fast_t b)
992
        {
993
                return compare_children(vcvertptr, Viewer_eye, seg, a, b);
994
        };
995
                std::sort(r.begin(), r.end(), predicate);
996
}
997
 
998
static void add_obj_to_seglist(render_state_t &rstate, objnum_t objnum, segnum_t segnum)
999
{
1000
        auto p = rstate.render_seg_map.emplace(segnum, render_state_t::per_segment_state_t{});
1001
        auto &o = p.first->second.objects;
1002
        if (p.second)
1003
                o.reserve(16);
1004
        o.emplace_back(render_state_t::per_segment_state_t::distant_object{objnum});
1005
}
1006
 
1007
namespace {
1008
 
1009
using visited_twobit_array_t = visited_segment_mask_t<2>;
1010
 
1011
class render_compare_context_t
1012
{
1013
        typedef render_state_t::per_segment_state_t::distant_object distant_object;
1014
        struct element
1015
        {
1016
                fix64 dist_squared;
1017
#if defined(DXX_BUILD_DESCENT_II)
1018
                const object *objp;
1019
#endif
1020
        };
1021
        using array_t = std::array<element, MAX_OBJECTS>;
1022
        array_t m_array;
1023
public:
1024
        array_t::reference operator[](std::size_t i) { return m_array[i]; }
1025
        array_t::const_reference operator[](std::size_t i) const { return m_array[i]; }
1026
        render_compare_context_t(fvcobjptr &vcobjptr, const vms_vector &Viewer_eye, const render_state_t::per_segment_state_t &segstate)
1027
        {
1028
                range_for (const auto t, segstate.objects)
1029
                {
1030
                        const auto objnum = t.objnum;
1031
                        auto &objp = *vcobjptr(objnum);
1032
                        auto &e = (*this)[objnum];
1033
#if defined(DXX_BUILD_DESCENT_II)
1034
                        e.objp = &objp;
1035
#endif
1036
                        e.dist_squared = vm_vec_dist2(objp.pos, Viewer_eye);
1037
                }
1038
        }
1039
        bool operator()(const distant_object &a, const distant_object &b) const;
1040
};
1041
 
1042
//compare function for object sort. 
1043
bool render_compare_context_t::operator()(const distant_object &a, const distant_object &b) const
1044
{
1045
        const auto delta_dist_squared = (*this)[a.objnum].dist_squared - (*this)[b.objnum].dist_squared;
1046
 
1047
#if defined(DXX_BUILD_DESCENT_II)
1048
        const auto obj_a = (*this)[a.objnum].objp;
1049
        const auto obj_b = (*this)[b.objnum].objp;
1050
 
1051
        auto abs_delta_dist_squared = std::abs(delta_dist_squared);
1052
        fix combined_size = obj_a->size + obj_b->size;
1053
        /*
1054
         * First check without squaring.  If true, the square can be
1055
         * skipped.
1056
         */
1057
        if (abs_delta_dist_squared < combined_size || abs_delta_dist_squared < (static_cast<fix64>(combined_size) * combined_size))
1058
        {               //same position
1059
 
1060
                //these two objects are in the same position.  see if one is a fireball
1061
                //or laser or something that should plot on top.  Don't do this for
1062
                //the afterburner blobs, though.
1063
 
1064
                if (obj_a->type == OBJ_WEAPON || (obj_a->type == OBJ_FIREBALL && get_fireball_id(*obj_a) != VCLIP_AFTERBURNER_BLOB))
1065
                {
1066
                        if (!(obj_b->type == OBJ_WEAPON || obj_b->type == OBJ_FIREBALL))
1067
                                return true;    //a is weapon, b is not, so say a is closer
1068
                        //both are weapons 
1069
                }
1070
                else
1071
                {
1072
                        if (obj_b->type == OBJ_WEAPON || (obj_b->type == OBJ_FIREBALL && get_fireball_id(*obj_b) != VCLIP_AFTERBURNER_BLOB))
1073
                                return false;   //b is weapon, a is not, so say a is farther
1074
                }
1075
 
1076
                //no special case, fall through to normal return
1077
        }
1078
#endif
1079
        return delta_dist_squared > 0;  //return distance
1080
}
1081
 
1082
}
1083
 
1084
static void sort_segment_object_list(fvcobjptr &vcobjptr, const vms_vector &Viewer_eye, render_state_t::per_segment_state_t &segstate)
1085
{
1086
        render_compare_context_t context(vcobjptr, Viewer_eye, segstate);
1087
        auto &v = segstate.objects;
1088
        std::sort(v.begin(), v.end(), std::cref(context));
1089
}
1090
 
1091
namespace dsx {
1092
 
1093
static void build_object_lists(object_array &Objects, fvcsegptr &vcsegptr, const vms_vector &Viewer_eye, render_state_t &rstate)
1094
{
1095
        const auto viewer = Viewer;
1096
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1097
        auto &Vertices = LevelSharedVertexState.get_vertices();
1098
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1099
        auto &vcvertptr = Vertices.vcptr;
1100
        auto &vcwallptr = Walls.vcptr;
1101
        const auto N_render_segs = rstate.N_render_segs;
1102
        range_for (const unsigned nn, xrange(N_render_segs))
1103
        {
1104
                const auto segnum = rstate.Render_list[nn];
1105
                if (segnum != segment_none) {
1106
                        range_for (const auto obj, objects_in(vcsegptr(segnum), Objects.vcptridx, vcsegptr))
1107
                        {
1108
                                int list_pos;
1109
                                if (obj->type == OBJ_NONE)
1110
                                {
1111
                                        assert(obj->type != OBJ_NONE);
1112
                                        continue;
1113
                                }
1114
                                if (unlikely(obj == viewer) && likely(obj->attached_obj == object_none))
1115
                                        continue;
1116
                                if (obj->flags & OF_ATTACHED)
1117
                                        continue;               //ignore this object
1118
 
1119
                                auto new_segnum = segnum;
1120
                                list_pos = nn;
1121
 
1122
#if defined(DXX_BUILD_DESCENT_I)
1123
                                int did_migrate;
1124
                                if (obj->type != OBJ_CNTRLCEN)          //don't migrate controlcen
1125
#elif defined(DXX_BUILD_DESCENT_II)
1126
                                const int did_migrate = 0;
1127
                                if (obj->type != OBJ_CNTRLCEN && !(obj->type==OBJ_ROBOT && get_robot_id(obj)==65))              //don't migrate controlcen
1128
#endif
1129
                                do {
1130
#if defined(DXX_BUILD_DESCENT_I)
1131
                                        did_migrate = 0;
1132
#endif
1133
                                        const uint_fast32_t sidemask = get_seg_masks(vcvertptr, obj->pos, vcsegptr(new_segnum), obj->size).sidemask;
1134
 
1135
                                        if (sidemask) {
1136
                                                int sn,sf;
1137
 
1138
                                                for (sn=0,sf=1;sn<6;sn++,sf<<=1)
1139
                                                        if (sidemask & sf)
1140
                                                        {
1141
#if defined(DXX_BUILD_DESCENT_I)
1142
                                                                const auto &&seg = vcsegptr(obj->segnum);
1143
#elif defined(DXX_BUILD_DESCENT_II)
1144
                                                                const auto &&seg = vcsegptr(new_segnum);
1145
#endif
1146
 
1147
                                                                if (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, seg, sn) & WID_FLY_FLAG)
1148
                                                                {               //can explosion migrate through
1149
                                                                        int child = seg->children[sn];
1150
                                                                        int checknp;
1151
 
1152
                                                                        for (checknp=list_pos;checknp--;)
1153
                                                                                if (rstate.Render_list[checknp] == child) {
1154
                                                                                        new_segnum = child;
1155
                                                                                        list_pos = checknp;
1156
#if defined(DXX_BUILD_DESCENT_I)
1157
                                                                                        did_migrate = 1;
1158
#endif
1159
                                                                                }
1160
                                                                }
1161
                                                                if (sidemask <= sf)
1162
                                                                        break;
1163
                                                        }
1164
                                        }
1165
 
1166
                                } while (did_migrate);
1167
                                add_obj_to_seglist(rstate, obj, new_segnum);
1168
                        }
1169
                }
1170
        }
1171
 
1172
        //now that there's a list for each segment, sort the items in those lists
1173
        range_for (const auto segnum, partial_const_range(rstate.Render_list, rstate.N_render_segs))
1174
        {
1175
                if (segnum != segment_none) {
1176
                        sort_segment_object_list(Objects.vcptr, Viewer_eye, rstate.render_seg_map[segnum]);
1177
                }
1178
        }
1179
}
1180
}
1181
 
1182
int Rear_view=0;
1183
 
1184
namespace dsx {
1185
//renders onto current canvas
1186
void render_frame(grs_canvas &canvas, fix eye_offset, window_rendered_data &window)
1187
{
1188
        auto &Objects = LevelUniqueObjectState.Objects;
1189
        auto &vcobjptridx = Objects.vcptridx;
1190
        if (Endlevel_sequence) {
1191
                render_endlevel_frame(canvas, eye_offset);
1192
                return;
1193
        }
1194
 
1195
        if ( Newdemo_state == ND_STATE_RECORDING && eye_offset >= 0 )   {
1196
 
1197
      if (RenderingType==0)
1198
                newdemo_record_start_frame(FrameTime );
1199
      if (RenderingType!=255)
1200
                newdemo_record_viewer_object(vcobjptridx(Viewer));
1201
        }
1202
 
1203
   //Here:
1204
 
1205
        start_lighting_frame(*Viewer);          //this is for ugly light-smoothing hack
1206
 
1207
        g3_start_frame(canvas);
1208
 
1209
        auto Viewer_eye = Viewer->pos;
1210
 
1211
//      if (Viewer->type == OBJ_PLAYER && (PlayerCfg.CockpitMode[1]!=CM_REAR_VIEW))
1212
//              vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.fvec,(Viewer->size*3)/4);
1213
 
1214
        if (eye_offset) {
1215
                vm_vec_scale_add2(Viewer_eye,Viewer->orient.rvec,eye_offset);
1216
        }
1217
 
1218
#if DXX_USE_EDITOR
1219
        if (EditorWindow)
1220
                Viewer_eye = Viewer->pos;
1221
        #endif
1222
 
1223
        const auto &&viewer_segp = Segments.vmptridx(Viewer->segnum);
1224
        auto start_seg_num = find_point_seg(LevelSharedSegmentState, LevelUniqueSegmentState, Viewer_eye, viewer_segp);
1225
 
1226
        if (start_seg_num==segment_none)
1227
                start_seg_num = viewer_segp;
1228
 
1229
        g3_set_view_matrix(Viewer_eye,
1230
                (Rear_view && Viewer == ConsoleObject)
1231
                ? vm_matrix_x_matrix(Viewer->orient, vm_angles_2_matrix(vms_angvec{0, 0, INT16_MAX}))
1232
                : Viewer->orient, Render_zoom);
1233
 
1234
        if (Clear_window == 1) {
1235
                if (Clear_window_color == -1)
1236
                        Clear_window_color = BM_XRGB(0, 0, 0);  //BM_XRGB(31, 15, 7);
1237
                gr_clear_canvas(canvas, Clear_window_color);
1238
        }
1239
 
1240
        render_mine(canvas, Viewer_eye, start_seg_num, eye_offset, window);
1241
 
1242
        g3_end_frame();
1243
 
1244
   //RenderingType=0;
1245
 
1246
        // -- Moved from here by MK, 05/17/95, wrong if multiple renders/frame! FrameCount++;           //we have rendered a frame
1247
}
1248
 
1249
#if defined(DXX_BUILD_DESCENT_II)
1250
void update_rendered_data(window_rendered_data &window, const object &viewer, int rear_view_flag)
1251
{
1252
        window.time = timer_query();
1253
        window.viewer = &viewer;
1254
        window.rear_view = rear_view_flag;
1255
}
1256
#endif
1257
 
1258
//build a list of segments to be rendered
1259
//fills in Render_list & N_render_segs
1260
static void build_segment_list(render_state_t &rstate, const vms_vector &Viewer_eye, visited_twobit_array_t &visited, unsigned &first_terminal_seg, const vcsegidx_t start_seg_num)
1261
{
1262
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1263
        auto &Vertices = LevelSharedVertexState.get_vertices();
1264
        int     lcnt,scnt,ecnt;
1265
        int     l;
1266
 
1267
        rstate.render_pos.fill(-1);
1268
 
1269
        lcnt = scnt = 0;
1270
 
1271
        rstate.Render_list[lcnt] = start_seg_num;
1272
        visited[start_seg_num]=1;
1273
        lcnt++;
1274
        ecnt = lcnt;
1275
        rstate.render_pos[start_seg_num] = 0;
1276
        {
1277
                auto &rsm_start_seg = rstate.render_seg_map[start_seg_num];
1278
                auto &rw = rsm_start_seg.render_window;
1279
                rw.left = rw.top = 0;
1280
                rw.right = grd_curcanv->cv_bitmap.bm_w-1;
1281
                rw.bot = grd_curcanv->cv_bitmap.bm_h-1;
1282
        }
1283
 
1284
        //breadth-first renderer
1285
 
1286
        //build list
1287
 
1288
        auto &vcvertptr = Vertices.vcptr;
1289
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1290
        auto &vcwallptr = Walls.vcptr;
1291
        for (l=0;l<Render_depth;l++) {
1292
                for (scnt=0;scnt < ecnt;scnt++) {
1293
                        auto segnum = rstate.Render_list[scnt];
1294
                        if (unlikely(segnum == segment_none))
1295
                        {
1296
                                assert(segnum != segment_none);
1297
                                continue;
1298
                        }
1299
 
1300
                        auto &srsm = rstate.render_seg_map[segnum];
1301
                        auto &processed = srsm.processed;
1302
                        if (processed)
1303
                                continue;
1304
                        const auto &check_w = srsm.render_window;
1305
 
1306
                        processed = true;
1307
 
1308
                        const auto &&seg = vcsegptridx(segnum);
1309
                        const auto uor = rotate_list(vcvertptr, seg->verts).uor & CC_BEHIND;
1310
 
1311
                        //look at all sides of this segment.
1312
                        //tricky code to look at sides in correct order follows
1313
 
1314
                        sort_child_array_t child_list;          //list of ordered sides to process
1315
                        uint_fast32_t n_children = 0;                                                   //how many sides in child_list
1316
                        for (uint_fast32_t c = 0;c < MAX_SIDES_PER_SEGMENT;c++) {               //build list of sides
1317
                                const auto wid = WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, seg, c);
1318
                                if (wid & WID_RENDPAST_FLAG)
1319
                                {
1320
                                        if (auto codes_and = uor)
1321
                                        {
1322
                                                range_for (const auto i, Side_to_verts[c])
1323
                                                        codes_and &= Segment_points[seg->verts[i]].p3_codes;
1324
                                                if (codes_and)
1325
                                                        continue;
1326
                                        }
1327
                                        child_list[n_children++] = c;
1328
                                }
1329
                        }
1330
                        if (!n_children)
1331
                                continue;
1332
 
1333
                        //now order the sides in some magical way
1334
                        const auto &&child_range = partial_range(child_list, n_children);
1335
                        sort_seg_children(vcvertptr, Viewer_eye, seg, child_range);
1336
                        project_list(seg->verts);
1337
                        range_for (const auto siden, child_range)
1338
                        {
1339
                                const auto ch = seg->children[siden];
1340
                                {
1341
                                        {
1342
                                                short min_x=32767,max_x=-32767,min_y=32767,max_y=-32767;
1343
                                                int no_proj_flag=0;     //a point wasn't projected
1344
                                                uint8_t codes_and_3d = 0xff, codes_and_2d = codes_and_3d;
1345
                                                range_for (const auto i, Side_to_verts[siden])
1346
                                                {
1347
                                                        g3s_point *pnt = &Segment_points[seg->verts[i]];
1348
 
1349
                                                        if (! (pnt->p3_flags&PF_PROJECTED)) {no_proj_flag=1; break;}
1350
 
1351
                                                        const int16_t _x = f2i(pnt->p3_sx), _y = f2i(pnt->p3_sy);
1352
 
1353
                                                        if (_x < min_x) min_x = _x;
1354
                                                        if (_x > max_x) max_x = _x;
1355
 
1356
                                                        if (_y < min_y) min_y = _y;
1357
                                                        if (_y > max_y) max_y = _y;
1358
                                                        codes_and_3d &= pnt->p3_codes;
1359
                                                        codes_and_2d &= code_window_point(_x,_y,check_w);
1360
                                                }
1361
                                                if (no_proj_flag || (!codes_and_3d && !codes_and_2d)) { //maybe add this segment
1362
                                                        auto rp = rstate.render_pos[ch];
1363
                                                        rect nw;
1364
 
1365
                                                        if (no_proj_flag)
1366
                                                                nw = check_w;
1367
                                                        else {
1368
                                                                nw.left  = max(check_w.left,min_x);
1369
                                                                nw.right = min(check_w.right,max_x);
1370
                                                                nw.top   = max(check_w.top,min_y);
1371
                                                                nw.bot   = min(check_w.bot,max_y);
1372
                                                        }
1373
 
1374
                                                        //see if this seg already visited, and if so, does current window
1375
                                                        //expand the old window?
1376
                                                        if (rp != -1) {
1377
                                                                auto &old_w = rstate.render_seg_map[rstate.Render_list[rp]].render_window;
1378
                                                                if (nw.left < old_w.left ||
1379
                                                                                 nw.top < old_w.top ||
1380
                                                                                 nw.right > old_w.right ||
1381
                                                                                 nw.bot > old_w.bot) {
1382
 
1383
                                                                        nw.left  = min(nw.left, old_w.left);
1384
                                                                        nw.right = max(nw.right, old_w.right);
1385
                                                                        nw.top   = min(nw.top, old_w.top);
1386
                                                                        nw.bot   = max(nw.bot, old_w.bot);
1387
 
1388
                                                                        {
1389
                                                                                //no_render_flag[lcnt] = 1;
1390
                                                                                rstate.render_seg_map[ch].processed = false;            //force reprocess
1391
                                                                                rstate.Render_list[lcnt] = segment_none;
1392
                                                                                old_w = nw;             //get updated window
1393
                                                                                goto no_add;
1394
                                                                        }
1395
                                                                }
1396
                                                                else goto no_add;
1397
                                                        }
1398
                                                        rstate.render_pos[ch] = lcnt;
1399
                                                        rstate.Render_list[lcnt] = ch;
1400
                                                        {
1401
                                                                auto &chrsm = rstate.render_seg_map[ch];
1402
                                                                chrsm.Seg_depth = l;
1403
                                                                chrsm.render_window = nw;
1404
                                                        }
1405
                                                        lcnt++;
1406
                                                        if (lcnt >= MAX_RENDER_SEGS) {goto done_list;}
1407
                                                        visited[ch] = 1;
1408
no_add:
1409
        ;
1410
 
1411
                                                }
1412
                                        }
1413
                                }
1414
                        }
1415
                }
1416
 
1417
                scnt = ecnt;
1418
                ecnt = lcnt;
1419
 
1420
        }
1421
done_list:
1422
 
1423
        first_terminal_seg = scnt;
1424
        rstate.N_render_segs = lcnt;
1425
 
1426
}
1427
 
1428
//renders onto current canvas
1429
void render_mine(grs_canvas &canvas, const vms_vector &Viewer_eye, const vcsegidx_t start_seg_num, const fix eye_offset, window_rendered_data &window)
1430
{
1431
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1432
        auto &Objects = LevelUniqueObjectState.Objects;
1433
        auto &Vertices = LevelSharedVertexState.get_vertices();
1434
        auto &vmobjptridx = Objects.vmptridx;
1435
#if DXX_USE_OGL
1436
        auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
1437
#else
1438
        auto &vcvertptr = Vertices.vcptr;
1439
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1440
        auto &vcwallptr = Walls.vcptr;
1441
#endif
1442
        using std::advance;
1443
        render_state_t rstate;
1444
        #ifndef NDEBUG
1445
        object_rendered = {};
1446
        #endif
1447
 
1448
        //set up for rendering
1449
 
1450
        render_start_frame();
1451
 
1452
        visited_twobit_array_t visited;
1453
 
1454
        unsigned first_terminal_seg;
1455
#if DXX_USE_EDITOR
1456
#if defined(DXX_BUILD_DESCENT_I)
1457
        if (_search_mode || eye_offset>0)
1458
#elif defined(DXX_BUILD_DESCENT_II)
1459
        if (_search_mode)
1460
#endif
1461
        {
1462
                first_terminal_seg = 0;
1463
        }
1464
        //else
1465
        #endif
1466
                //NOTE LINK TO ABOVE!!  -Link killed by kreatordxx to get editor selection working again
1467
                build_segment_list(rstate, Viewer_eye, visited, first_terminal_seg, start_seg_num);             //fills in Render_list & N_render_segs
1468
 
1469
        const auto &&render_range = partial_const_range(rstate.Render_list, rstate.N_render_segs);
1470
        const auto &&reversed_render_range = render_range.reversed();
1471
        //render away
1472
 
1473
        //if (!(_search_mode))
1474
                build_object_lists(Objects, vcsegptr, Viewer_eye, rstate);
1475
 
1476
        if (eye_offset<=0) // Do for left eye or zero.
1477
                set_dynamic_light(rstate);
1478
 
1479
        if (reversed_render_range.empty())
1480
                /* Impossible, but later code has undefined behavior if this
1481
                 * happens
1482
                 */
1483
                return;
1484
 
1485
        if (!_search_mode && Clear_window == 2) {
1486
                if (first_terminal_seg < rstate.N_render_segs) {
1487
                        if (Clear_window_color == -1)
1488
                                Clear_window_color = BM_XRGB(0, 0, 0);  //BM_XRGB(31, 15, 7);
1489
 
1490
                        const uint8_t color = Clear_window_color;
1491
 
1492
                        range_for (const auto segnum, partial_const_range(rstate.Render_list, first_terminal_seg, rstate.N_render_segs))
1493
                        {
1494
                                if (segnum != segment_none) {
1495
                                        const auto &rw = rstate.render_seg_map[segnum].render_window;
1496
                                        #ifndef NDEBUG
1497
                                        if (rw.left == -1 || rw.top == -1 || rw.right == -1 || rw.bot == -1)
1498
                                                Int3();
1499
                                        else
1500
                                        #endif
1501
                                                //NOTE LINK TO ABOVE!
1502
                                                gr_rect(canvas, rw.left, rw.top, rw.right, rw.bot, color);
1503
                                }
1504
                        }
1505
                }
1506
        }
1507
#if !DXX_USE_OGL
1508
        range_for (const auto segnum, reversed_render_range)
1509
        {
1510
                // Interpolation_method = 0;
1511
                auto &srsm = rstate.render_seg_map[segnum];
1512
 
1513
                //if (!no_render_flag[nn])
1514
                if (segnum!=segment_none && (_search_mode || visited[segnum]!=3)) {
1515
                        //set global render window vars
1516
 
1517
                        Current_seg_depth = srsm.Seg_depth;
1518
                        {
1519
                                const auto &rw = srsm.render_window;
1520
                                Window_clip_left  = rw.left;
1521
                                Window_clip_top   = rw.top;
1522
                                Window_clip_right = rw.right;
1523
                                Window_clip_bot   = rw.bot;
1524
                        }
1525
 
1526
                        render_segment(vcvertptr, vcwallptr, Viewer_eye, *grd_curcanv, vcsegptridx(segnum));
1527
                        visited[segnum]=3;
1528
                        if (srsm.objects.empty())
1529
                                continue;
1530
 
1531
                        {               //reset for objects
1532
                                Window_clip_left  = Window_clip_top = 0;
1533
                                Window_clip_right = canvas.cv_bitmap.bm_w-1;
1534
                                Window_clip_bot   = canvas.cv_bitmap.bm_h-1;
1535
                        }
1536
 
1537
                        {
1538
                                //int n_expl_objs=0,expl_objs[5],i;
1539
                                const auto save_linear_depth = std::exchange(Max_linear_depth, Max_linear_depth_objects);
1540
                                range_for (auto &v, srsm.objects)
1541
                                {
1542
                                        do_render_object(canvas, LevelUniqueLightState, vmobjptridx(v.objnum), window); // note link to above else
1543
                                }
1544
                                Max_linear_depth = save_linear_depth;
1545
                        }
1546
 
1547
                }
1548
        }
1549
#else
1550
        // Two pass rendering. Since sprites and some level geometry can have transparency (blending), we need some fancy sorting.
1551
        // GL_DEPTH_TEST helps to sort everything in view but we should make sure translucent sprites are rendered after geometry to prevent them to turn walls invisible (if rendered BEFORE geometry but still in FRONT of it).
1552
        // If walls use blending, they should be rendered along with objects (in same pass) to prevent some ugly clipping.
1553
 
1554
        auto &vcvertptr = Vertices.vcptr;
1555
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
1556
        auto &vcwallptr = Walls.vcptr;
1557
        // First Pass: render opaque level geometry and level geometry with alpha pixels (high Alpha-Test func)
1558
        range_for (const auto segnum, reversed_render_range)
1559
        {
1560
                auto &srsm = rstate.render_seg_map[segnum];
1561
 
1562
                if (segnum!=segment_none && (_search_mode || visited[segnum]!=3)) {
1563
                        //set global render window vars
1564
 
1565
                        {
1566
                                const auto &rw = srsm.render_window;
1567
                                Window_clip_left  = rw.left;
1568
                                Window_clip_top   = rw.top;
1569
                                Window_clip_right = rw.right;
1570
                                Window_clip_bot   = rw.bot;
1571
                        }
1572
 
1573
                        // render segment
1574
                        {
1575
                                const auto &&seg = vcsegptridx(segnum);
1576
                                Assert(segnum!=segment_none && segnum<=Highest_segment_index);
1577
                                if (!rotate_list(vcvertptr, seg->verts).uand)
1578
                                {               //all off screen?
1579
 
1580
                                        if (Viewer->type!=OBJ_ROBOT)
1581
                                                LevelUniqueAutomapState.Automap_visited[segnum] = 1;
1582
 
1583
                                        range_for (const uint_fast32_t sn, xrange(MAX_SIDES_PER_SEGMENT))
1584
                                        {
1585
                                                const auto wid = WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, seg, sn);
1586
                                                if (wid == WID_TRANSPARENT_WALL || wid == WID_TRANSILLUSORY_WALL
1587
#if defined(DXX_BUILD_DESCENT_II)
1588
                                                        || (wid & WID_CLOAKED_FLAG)
1589
#endif
1590
                                                        )
1591
                                                {
1592
                                                        if (PlayerCfg.AlphaBlendEClips && is_alphablend_eclip(TmapInfo[seg->unique_segment::sides[sn].tmap_num].eclip_num)) // Do NOT render geometry with blending textures. Since we've not rendered any objects, yet, they would disappear behind them.
1593
                                                                continue;
1594
                                                        glAlphaFunc(GL_GEQUAL,0.8); // prevent ugly outlines if an object (which is rendered later) is shown behind a grate, door, etc. if texture filtering is enabled. These sides are rendered later again with normal AlphaFunc
1595
                                                        render_side(vcvertptr, canvas, seg, sn, wid, Viewer_eye);
1596
                                                        glAlphaFunc(GL_GEQUAL,0.02);
1597
                                                }
1598
                                                else
1599
                                                        render_side(vcvertptr, canvas, seg, sn, wid, Viewer_eye);
1600
                                        }
1601
                                }
1602
                        }
1603
                }
1604
        }
1605
 
1606
        // Second pass: Render objects and level geometry with alpha pixels (normal Alpha-Test func) and eclips with blending
1607
        range_for (const auto segnum, reversed_render_range)
1608
        {
1609
                auto &srsm = rstate.render_seg_map[segnum];
1610
 
1611
                if (segnum!=segment_none && (_search_mode || visited[segnum]!=3)) {
1612
                        //set global render window vars
1613
 
1614
                        {
1615
                                const auto &rw = srsm.render_window;
1616
                                Window_clip_left  = rw.left;
1617
                                Window_clip_top   = rw.top;
1618
                                Window_clip_right = rw.right;
1619
                                Window_clip_bot   = rw.bot;
1620
                        }
1621
 
1622
                        // render segment
1623
                        {
1624
                                const auto &&seg = vcsegptridx(segnum);
1625
                                Assert(segnum!=segment_none && segnum<=Highest_segment_index);
1626
                                if (!rotate_list(vcvertptr, seg->verts).uand)
1627
                                {               //all off screen?
1628
 
1629
                                        if (Viewer->type!=OBJ_ROBOT)
1630
                                                LevelUniqueAutomapState.Automap_visited[segnum] = 1;
1631
 
1632
                                        range_for (const uint_fast32_t sn, xrange(MAX_SIDES_PER_SEGMENT))
1633
                                        {
1634
                                                const auto wid = WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, seg, sn);
1635
                                                if (wid == WID_TRANSPARENT_WALL || wid == WID_TRANSILLUSORY_WALL
1636
#if defined(DXX_BUILD_DESCENT_II)
1637
                                                        || (wid & WID_CLOAKED_FLAG)
1638
#endif
1639
                                                        )
1640
                                                {
1641
                                                        render_side(vcvertptr, canvas, seg, sn, wid, Viewer_eye);
1642
                                                }
1643
                                        }
1644
                                }
1645
                        }
1646
                        visited[segnum]=3;
1647
                        if (srsm.objects.empty())
1648
                                continue;
1649
                        {               //reset for objects
1650
                                Window_clip_left  = Window_clip_top = 0;
1651
                                Window_clip_right = canvas.cv_bitmap.bm_w-1;
1652
                                Window_clip_bot   = canvas.cv_bitmap.bm_h-1;
1653
                        }
1654
 
1655
                        {
1656
                                range_for (auto &v, srsm.objects)
1657
                                {
1658
                                        do_render_object(canvas, LevelUniqueLightState, vmobjptridx(v.objnum), window); // note link to above else
1659
                                }
1660
                        }
1661
                }
1662
        }
1663
#endif
1664
 
1665
        // -- commented out by mk on 09/14/94...did i do a good thing??  object_render_targets();
1666
 
1667
#if DXX_USE_EDITOR
1668
        #ifndef NDEBUG
1669
        //draw curedge stuff
1670
        if (Outline_mode)
1671
                outline_seg_side(canvas, Cursegp, Curside, Curedge, Curvert);
1672
        #endif
1673
#endif
1674
}
1675
 
1676
//-------------- Renders a hostage --------------------------------------------
1677
void draw_hostage(const d_vclip_array &Vclip, grs_canvas &canvas, const d_level_unique_light_state &LevelUniqueLightState, const vmobjptridx_t obj)
1678
{
1679
        auto &vci = obj->rtype.vclip_info;
1680
        draw_object_tmap_rod(canvas, &LevelUniqueLightState, obj, Vclip[vci.vclip_num].frames[vci.framenum]);
1681
}
1682
 
1683
}
1684
#if DXX_USE_EDITOR
1685
//finds what segment is at a given x&y -  seg,side,face are filled in
1686
//works on last frame rendered. returns true if found
1687
//if seg<0, then an object was found, and the object number is -seg-1
1688
int find_seg_side_face(short x,short y,segnum_t &seg,objnum_t &obj,int &side,int &face)
1689
{
1690
        _search_mode = -1;
1691
 
1692
        _search_x = x; _search_y = y;
1693
 
1694
        found_seg = segment_none;
1695
        found_obj = object_none;
1696
 
1697
        render_frame(*(render_3d_in_big_window ? LargeView.ev_canv : Canv_editor_game), 0);
1698
 
1699
        _search_mode = 0;
1700
 
1701
        seg = found_seg;
1702
        obj = found_obj;
1703
        side = found_side;
1704
        face = found_face;
1705
        return found_seg != segment_none || found_obj != object_none;
1706
}
1707
#endif