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-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18
*/
19
 
20
/*
21
 *
22
 * Start of conversion to new texture mapper.
23
 *
24
 */
25
 
26
#include "pstypes.h"
27
#include "maths.h"
28
#include "vecmat.h"
29
#include "gr.h"
30
#include "3d.h"
31
#include "dxxerror.h"
32
#include "render.h"
33
#include "texmap.h"
34
#include "texmapl.h"
35
#include "rle.h"
36
#include "scanline.h"
37
#include "u_mem.h"
38
 
39
#include "dxxsconf.h"
40
#include "dsx-ns.h"
41
#include <utility>
42
 
43
namespace dcx {
44
 
45
#if DXX_USE_EDITOR
46
#define EDITOR_TMAP 1       //if in, include extra stuff
47
#endif
48
 
49
// Temporary texture map, interface from Matt's 3d system to Mike's texture mapper.
50
 
51
int     Lighting_on=1;                  // initialize to no lighting
52
unsigned        Current_seg_depth;              // HACK INTERFACE: how far away the current segment (& thus texture) is
53
 
54
// These variables are the interface to assembler.  They get set for each texture map, which is a real waste of time.
55
//      They should be set only when they change, which is generally when the window bounds change.  And, even still, it's
56
//      a pretty bad interface.
57
int     bytes_per_row=-1;
58
unsigned char *write_buffer;
59
 
60
fix fx_l, fx_u, fx_v, fx_z, fx_du_dx, fx_dv_dx, fx_dz_dx, fx_dl_dx;
61
int fx_xleft, fx_xright, fx_y;
62
const unsigned char *pixptr;
63
uint8_t Transparency_on = 0;
64
uint8_t tmap_flat_color;
65
 
66
int     Interpolation_method;   // 0 = choose best method
67
// -------------------------------------------------------------------------------------
68
template <std::size_t... N>
69
static inline constexpr const std::array<fix, 1 + sizeof...(N)> init_fix_recip_table(std::index_sequence<0, N...>)
70
{
71
        /* gcc 4.5 fails on bare initializer list */
72
        return std::array<fix, 1 + sizeof...(N)>{{F1_0, (F1_0 / N)...}};
73
}
74
 
75
constexpr std::array<fix, FIX_RECIP_TABLE_SIZE> fix_recip_table = init_fix_recip_table(std::make_index_sequence<FIX_RECIP_TABLE_SIZE>());
76
 
77
// -------------------------------------------------------------------------------------
78
//      Initialize interface variables to assembler.
79
//      These things used to be constants.  This routine is now (10/6/93) getting called for
80
//      every texture map.  It should get called whenever the window changes, or, preferably,
81
//      not at all.  I'm pretty sure these variables are only being used for range checking.
82
void init_interface_vars_to_assembler(void)
83
{
84
        grs_bitmap      *bp;
85
        bp = &grd_curcanv->cv_bitmap;
86
 
87
        Assert(bp!=NULL);
88
        Assert(bp->bm_data!=NULL);
89
        //      If bytes_per_row has changed, create new table of pointers.
90
        if (bytes_per_row != static_cast<int>(bp->bm_rowsize)) {
91
                bytes_per_row = static_cast<int>(bp->bm_rowsize);
92
        }
93
 
94
        write_buffer = bp->bm_mdata;
95
 
96
        Window_clip_left = 0;
97
        Window_clip_right = static_cast<int>(bp->bm_w)-1;
98
        Window_clip_top = 0;
99
        Window_clip_bot = static_cast<int>(bp->bm_h)-1;
100
}
101
 
102
static int Lighting_enabled;
103
// -------------------------------------------------------------------------------------
104
//                             VARIABLES
105
 
106
// -------------------------------------------------------------------------------------
107
//      Returns number preceding val modulo modulus.
108
//      prevmod(3,4) = 2
109
//      prevmod(0,4) = 3
110
int prevmod(int val,int modulus)
111
{
112
        if (val > 0)
113
                return val-1;
114
        else
115
                return modulus-1;
116
//      return (val + modulus - 1) % modulus;
117
}
118
 
119
 
120
//      Returns number succeeding val modulo modulus.
121
//      succmod(3,4) = 0
122
//      succmod(0,4) = 1
123
int succmod(int val,int modulus)
124
{
125
        if (val < modulus-1)
126
                return val+1;
127
        else
128
                return 0;
129
 
130
//      return (val + 1) % modulus;
131
}
132
 
133
// -------------------------------------------------------------------------------------
134
//      Select topmost vertex (minimum y coordinate) and bottommost (maximum y coordinate) in
135
//      texture map.  If either is part of a horizontal edge, then select leftmost vertex for
136
//      top, rightmost vertex for bottom.
137
//      Important: Vertex is selected with integer precision.  So, if there are vertices at
138
//      (0.0,0.7) and (0.5,0.3), the first vertex is selected, because they y coordinates are
139
//      considered the same, so the smaller x is favored.
140
//      Parameters:
141
//              nv              number of vertices
142
//              v3d     pointer to 3d vertices containing u,v,x2d,y2d coordinates
143
//      Results in:
144
//              *min_y_ind
145
//              *max_y_ind
146
// -------------------------------------------------------------------------------------
147
void compute_y_bounds(const g3ds_tmap &t, int &vlt, int &vlb, int &vrt, int &vrb,int &bottom_y_ind)
148
{
149
        int     min_y,max_y;
150
        int     min_y_ind;
151
        int     original_vrt;
152
        fix     min_x;
153
 
154
        // Scan all vertices, set min_y_ind to vertex with smallest y coordinate.
155
        min_y = f2i(t.verts[0].y2d);
156
        max_y = min_y;
157
        min_y_ind = 0;
158
        min_x = f2i(t.verts[0].x2d);
159
        bottom_y_ind = 0;
160
 
161
        for (int i=1; i<t.nv; i++) {
162
                if (f2i(t.verts[i].y2d) < min_y) {
163
                        min_y = f2i(t.verts[i].y2d);
164
                        min_y_ind = i;
165
                        min_x = f2i(t.verts[i].x2d);
166
                } else if (f2i(t.verts[i].y2d) == min_y) {
167
                        if (f2i(t.verts[i].x2d) < min_x) {
168
                                min_y_ind = i;
169
                                min_x = f2i(t.verts[i].x2d);
170
                        }
171
                }
172
                if (f2i(t.verts[i].y2d) > max_y) {
173
                        max_y = f2i(t.verts[i].y2d);
174
                        bottom_y_ind = i;
175
                }
176
        }
177
 
178
//--removed mk, 11/27/94--      //      Check for a non-upright-hourglass polygon and fix, if necessary, by bashing a y coordinate.
179
//--removed mk, 11/27/94--      //      min_y_ind = index of minimum y coordinate, *bottom_y_ind = index of maximum y coordinate
180
//--removed mk, 11/27/94--{
181
//--removed mk, 11/27/94--      int     max_temp, min_temp;
182
//--removed mk, 11/27/94--
183
//--removed mk, 11/27/94--      max_temp = *bottom_y_ind;
184
//--removed mk, 11/27/94--      if (*bottom_y_ind < min_y_ind)
185
//--removed mk, 11/27/94--              max_temp += t->nv;
186
//--removed mk, 11/27/94--
187
//--removed mk, 11/27/94--      for (i=min_y_ind; i<max_temp; i++) {
188
//--removed mk, 11/27/94--              if (f2i(t->verts[i%t->nv].y2d) > f2i(t->verts[(i+1)%t->nv].y2d)) {
189
//--removed mk, 11/27/94--                      Int3();
190
//--removed mk, 11/27/94--                      t->verts[(i+1)%t->nv].y2d = t->verts[i%t->nv].y2d;
191
//--removed mk, 11/27/94--              }
192
//--removed mk, 11/27/94--      }
193
//--removed mk, 11/27/94--
194
//--removed mk, 11/27/94--      min_temp = min_y_ind;
195
//--removed mk, 11/27/94--      if (min_y_ind < *bottom_y_ind)
196
//--removed mk, 11/27/94--              min_temp += t->nv;
197
//--removed mk, 11/27/94--
198
//--removed mk, 11/27/94--      for (i=*bottom_y_ind; i<min_temp; i++) {
199
//--removed mk, 11/27/94--              if (f2i(t->verts[i%t->nv].y2d) < f2i(t->verts[(i+1)%t->nv].y2d)) {
200
//--removed mk, 11/27/94--                      Int3();
201
//--removed mk, 11/27/94--                      t->verts[(i+1)%t->nv].y2d = t->verts[i%t->nv].y2d;
202
//--removed mk, 11/27/94--              }
203
//--removed mk, 11/27/94--      }
204
//--removed mk, 11/27/94--}
205
 
206
        // Set "vertex left top", etc. based on vertex with topmost y coordinate
207
        vlb = prevmod(vlt = min_y_ind,t.nv);
208
        vrb = succmod(vrt = vlt,t.nv);
209
 
210
        // If right edge is horizontal, then advance along polygon bound until it no longer is or until all
211
        // vertices have been examined.
212
        // (Left edge cannot be horizontal, because *vlt is set to leftmost point with highest y coordinate.)
213
 
214
        original_vrt = vrt;
215
 
216
        while (f2i(t.verts[vrt].y2d) == f2i(t.verts[vrb].y2d)) {
217
                if (succmod(vrt,t.nv) == original_vrt) {
218
                        break;
219
                }
220
                vrt = succmod(vrt,t.nv);
221
                vrb = succmod(vrt,t.nv);
222
        }
223
}
224
 
225
// -------------------------------------------------------------------------------------
226
//      Returns dx/dy given two vertices.
227
//      If dy == 0, returns 0.0
228
// -------------------------------------------------------------------------------------
229
//--fix compute_dx_dy_lin(g3ds_tmap *t, int top_vertex,int bottom_vertex)
230
//--{
231
//--    int     dy;
232
//--
233
//--    // compute delta x with respect to y for any edge
234
//--    dy = f2i(t->verts[bottom_vertex].y2d - t->verts[top_vertex].y2d) + 1;
235
//--    if (dy)
236
//--            return (t->verts[bottom_vertex].x2d - t->verts[top_vertex].x2d) / dy;
237
//--    else
238
//--            return 0;
239
//--
240
//--}
241
 
242
//#if !DXX_USE_OGL
243
static fix compute_du_dy_lin(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
244
{
245
        return fixmul(t.verts[bottom_vertex].u - t.verts[top_vertex].u, recip_dy);
246
}
247
 
248
 
249
static fix compute_dv_dy_lin(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
250
{
251
        return fixmul(t.verts[bottom_vertex].v - t.verts[top_vertex].v, recip_dy);
252
}
253
 
254
static fix compute_dl_dy_lin(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
255
{
256
        return fixmul(t.verts[bottom_vertex].l - t.verts[top_vertex].l, recip_dy);
257
}
258
 
259
fix compute_dx_dy(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
260
{
261
        return fixmul(t.verts[bottom_vertex].x2d - t.verts[top_vertex].x2d, recip_dy);
262
}
263
 
264
static fix compute_du_dy(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
265
{
266
        return fixmul(fixmul(t.verts[bottom_vertex].u,t.verts[bottom_vertex].z) - fixmul(t.verts[top_vertex].u,t.verts[top_vertex].z), recip_dy);
267
}
268
 
269
static fix compute_dv_dy(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
270
{
271
        return fixmul(fixmul(t.verts[bottom_vertex].v,t.verts[bottom_vertex].z) - fixmul(t.verts[top_vertex].v,t.verts[top_vertex].z), recip_dy);
272
}
273
 
274
static fix compute_dz_dy(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
275
{
276
        return fixmul(t.verts[bottom_vertex].z - t.verts[top_vertex].z, recip_dy);
277
 
278
}
279
 
280
// -------------------------------------------------------------------------------------
281
//      Texture map current scanline in perspective.
282
// -------------------------------------------------------------------------------------
283
static void ntmap_scanline_lighted(const grs_bitmap &srcb, int y, fix xleft, fix xright, fix uleft, fix uright, fix vleft, fix vright, fix zleft, fix zright, fix lleft, fix lright)
284
{
285
        fix     dx,recip_dx;
286
 
287
        fx_xright = f2i(xright);
288
        //edited 06/27/99 Matt Mueller - moved these tests up from within the switch so as not to do a bunch of needless calculations when we are just gonna return anyway.  Slight fps boost?
289
        if (fx_xright < Window_clip_left)
290
                return;
291
        fx_xleft = f2i(xleft);
292
        if (fx_xleft > Window_clip_right)
293
                return;
294
        //end edit -MM
295
 
296
        dx = fx_xright - fx_xleft;
297
        if ((dx < 0) || (xright < 0) || (xleft > xright))               // the (xleft > xright) term is not redundant with (dx < 0) because dx is computed using integers
298
                return;
299
 
300
        // setup to call assembler scanline renderer
301
        recip_dx = fix_recip(dx);
302
 
303
        fx_u = uleft;
304
        fx_v = vleft;
305
        fx_z = zleft;
306
 
307
        fx_du_dx = fixmul(uright - uleft,recip_dx);
308
        fx_dv_dx = fixmul(vright - vleft,recip_dx);
309
        fx_dz_dx = fixmul(zright - zleft,recip_dx);
310
        fx_y = y;
311
        pixptr = srcb.bm_data;
312
 
313
        switch (Lighting_enabled) {
314
                case 0:
315
                        //added 05/17/99 Matt Mueller - prevent writing before the buffer
316
            if ((fx_y == 0) && (fx_xleft < 0))
317
                                fx_xleft = 0;
318
                        //end addition -MM
319
                        if (fx_xright > Window_clip_right)
320
                                fx_xright = Window_clip_right;
321
 
322
                        cur_tmap_scanline_per();
323
                        break;
324
                case 1: {
325
                        fix     mul_thing;
326
 
327
                        if (lleft < 0) lleft = 0;
328
                        if (lright < 0) lright = 0;
329
                        if (lleft > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2)) lleft = (NUM_LIGHTING_LEVELS*F1_0-F1_0/2);
330
                        if (lright > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2)) lright = (NUM_LIGHTING_LEVELS*F1_0-F1_0/2);
331
 
332
                        fx_l = lleft;
333
                        fx_dl_dx = fixmul(lright - lleft,recip_dx);
334
 
335
                        //      This is a pretty ugly hack to prevent lighting overflows.
336
                        mul_thing = dx * fx_dl_dx;
337
                        if (lleft + mul_thing < 0)
338
                                fx_dl_dx += 12;
339
                        else if (lleft + mul_thing > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2))
340
                                fx_dl_dx -= 12;
341
 
342
                        //added 05/17/99 Matt Mueller - prevent writing before the buffer
343
            if ((fx_y == 0) && (fx_xleft < 0))
344
                                fx_xleft = 0;
345
                        //end addition -MM
346
                        if (fx_xright > Window_clip_right)
347
                                fx_xright = Window_clip_right;
348
 
349
                        cur_tmap_scanline_per();
350
                        break;
351
                }
352
                case 2:
353
#ifdef EDITOR_TMAP
354
                        fx_xright = f2i(xright);
355
                        fx_xleft = f2i(xleft);
356
 
357
                        tmap_flat_color = 1;
358
                        cur_tmap_scanline_flat();
359
#else
360
                        Int3(); //      Illegal, called an editor only routine!
361
#endif
362
                        break;
363
        }
364
 
365
}
366
 
367
// -------------------------------------------------------------------------------------
368
//      Render a texture map with lighting using perspective interpolation in inner and outer loops.
369
// -------------------------------------------------------------------------------------
370
static void ntexture_map_lighted(const grs_bitmap &srcb, const g3ds_tmap &t)
371
{
372
        int     vlt,vrt,vlb,vrb;        // vertex left top, vertex right top, vertex left bottom, vertex right bottom
373
        int     topy,boty,dy;
374
        fix     dx_dy_left,dx_dy_right;
375
        fix     du_dy_left,du_dy_right;
376
        fix     dv_dy_left,dv_dy_right;
377
        fix     dz_dy_left,dz_dy_right;
378
        fix     dl_dy_left,dl_dy_right;
379
        fix     recip_dyl, recip_dyr;
380
        int     max_y_vertex;
381
        fix     xleft,xright,uleft,vleft,uright,vright,zleft,zright,lleft,lright;
382
        int     next_break_left, next_break_right;
383
 
384
        //remove stupid warnings in compile
385
        dl_dy_left = F1_0;
386
        dl_dy_right = F1_0;
387
        lleft = F1_0;
388
        lright = F1_0;
389
 
390
        auto &v3d = t.verts;
391
 
392
        // Determine top and bottom y coords.
393
        compute_y_bounds(t,vlt,vlb,vrt,vrb,max_y_vertex);
394
 
395
        // Set top and bottom (of entire texture map) y coordinates.
396
        topy = f2i(v3d[vlt].y2d);
397
        boty = f2i(v3d[max_y_vertex].y2d);
398
        if (topy > Window_clip_bot)
399
                return;
400
        if (boty > Window_clip_bot)
401
                boty = Window_clip_bot;
402
 
403
        // Set amount to change x coordinate for each advance to next scanline.
404
        dy = f2i(t.verts[vlb].y2d) - f2i(t.verts[vlt].y2d);
405
        recip_dyl = fix_recip(dy);
406
 
407
        dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dyl);
408
        du_dy_left = compute_du_dy(t,vlt,vlb, recip_dyl);
409
        dv_dy_left = compute_dv_dy(t,vlt,vlb, recip_dyl);
410
        dz_dy_left = compute_dz_dy(t,vlt,vlb, recip_dyl);
411
 
412
        dy = f2i(t.verts[vrb].y2d) - f2i(t.verts[vrt].y2d);
413
        recip_dyr = fix_recip(dy);
414
 
415
        du_dy_right = compute_du_dy(t,vrt,vrb, recip_dyr);
416
        dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dyr);
417
        dv_dy_right = compute_dv_dy(t,vrt,vrb, recip_dyr);
418
        dz_dy_right = compute_dz_dy(t,vrt,vrb, recip_dyr);
419
 
420
        if (Lighting_enabled) {
421
                dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dyl);
422
                dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dyr);
423
 
424
                lleft = v3d[vlt].l;
425
                lright = v3d[vrt].l;
426
        }
427
 
428
        // Set initial values for x, u, v
429
        xleft = v3d[vlt].x2d;
430
        xright = v3d[vrt].x2d;
431
 
432
        zleft = v3d[vlt].z;
433
        zright = v3d[vrt].z;
434
 
435
        uleft = fixmul(v3d[vlt].u,zleft);
436
        uright = fixmul(v3d[vrt].u,zright);
437
        vleft = fixmul(v3d[vlt].v,zleft);
438
        vright = fixmul(v3d[vrt].v,zright);
439
 
440
        // scan all rows in texture map from top through first break.
441
        next_break_left = f2i(v3d[vlb].y2d);
442
        next_break_right = f2i(v3d[vrb].y2d);
443
 
444
        for (int y = topy; y < boty; y++) {
445
 
446
                // See if we have reached the end of the current left edge, and if so, set
447
                // new values for dx_dy and x,u,v
448
                if (y == next_break_left) {
449
                        fix     recip_dy;
450
 
451
                        // Handle problem of double points.  Search until y coord is different.  Cannot get
452
                        // hung in an infinite loop because we know there is a vertex with a lower y coordinate
453
                        // because in the for loop, we don't scan all spanlines.
454
                        while (y == f2i(v3d[vlb].y2d)) {
455
                                vlt = vlb;
456
                                vlb = prevmod(vlb,t.nv);
457
                        }
458
                        next_break_left = f2i(v3d[vlb].y2d);
459
 
460
                        dy = f2i(t.verts[vlb].y2d) - f2i(t.verts[vlt].y2d);
461
                        recip_dy = fix_recip(dy);
462
 
463
                        dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dy);
464
 
465
                        xleft = v3d[vlt].x2d;
466
                        zleft = v3d[vlt].z;
467
                        uleft = fixmul(v3d[vlt].u,zleft);
468
                        vleft = fixmul(v3d[vlt].v,zleft);
469
                        lleft = v3d[vlt].l;
470
 
471
                        du_dy_left = compute_du_dy(t,vlt,vlb, recip_dy);
472
                        dv_dy_left = compute_dv_dy(t,vlt,vlb, recip_dy);
473
                        dz_dy_left = compute_dz_dy(t,vlt,vlb, recip_dy);
474
 
475
                        if (Lighting_enabled) {
476
                                dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dy);
477
                                lleft = v3d[vlt].l;
478
                        }
479
                }
480
 
481
                // See if we have reached the end of the current left edge, and if so, set
482
                // new values for dx_dy and x.  Not necessary to set new values for u,v.
483
                if (y == next_break_right) {
484
                        fix     recip_dy;
485
 
486
                        while (y == f2i(v3d[vrb].y2d)) {
487
                                vrt = vrb;
488
                                vrb = succmod(vrb,t.nv);
489
                        }
490
 
491
                        next_break_right = f2i(v3d[vrb].y2d);
492
 
493
                        dy = f2i(t.verts[vrb].y2d) - f2i(t.verts[vrt].y2d);
494
                        recip_dy = fix_recip(dy);
495
 
496
                        dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dy);
497
 
498
                        xright = v3d[vrt].x2d;
499
                        zright = v3d[vrt].z;
500
                        uright = fixmul(v3d[vrt].u,zright);
501
                        vright = fixmul(v3d[vrt].v,zright);
502
 
503
                        du_dy_right = compute_du_dy(t,vrt,vrb, recip_dy);
504
                        dv_dy_right = compute_dv_dy(t,vrt,vrb, recip_dy);
505
                        dz_dy_right = compute_dz_dy(t,vrt,vrb, recip_dy);
506
 
507
                        if (Lighting_enabled) {
508
                                dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dy);
509
                                lright = v3d[vrt].l;
510
                        }
511
                }
512
 
513
                if (Lighting_enabled) {
514
                        if (y >= Window_clip_top)
515
                                ntmap_scanline_lighted(srcb,y,xleft,xright,uleft,uright,vleft,vright,zleft,zright,lleft,lright);
516
                        lleft += dl_dy_left;
517
                        lright += dl_dy_right;
518
                } else
519
                        if (y >= Window_clip_top)
520
                                ntmap_scanline_lighted(srcb,y,xleft,xright,uleft,uright,vleft,vright,zleft,zright,lleft,lright);
521
 
522
                uleft += du_dy_left;
523
                vleft += dv_dy_left;
524
 
525
                uright += du_dy_right;
526
                vright += dv_dy_right;
527
 
528
                xleft += dx_dy_left;
529
                xright += dx_dy_right;
530
 
531
                zleft += dz_dy_left;
532
                zright += dz_dy_right;
533
 
534
        }
535
 
536
        // We can get lleft or lright out of bounds here because we compute dl_dy using fixed point values,
537
        //      but we plot an integer number of scanlines, therefore doing an integer number of additions of the delta.
538
 
539
        ntmap_scanline_lighted(srcb,boty,xleft,xright,uleft,uright,vleft,vright,zleft,zright,lleft,lright);
540
}
541
 
542
 
543
// -------------------------------------------------------------------------------------
544
//      Texture map current scanline using linear interpolation.
545
// -------------------------------------------------------------------------------------
546
static void ntmap_scanline_lighted_linear(const grs_bitmap &srcb, int y, fix xleft, fix xright, fix uleft, fix uright, fix vleft, fix vright, fix lleft, fix lright)
547
{
548
        fix     dx,recip_dx,du_dx,dv_dx,dl_dx;
549
 
550
        dx = f2i(xright) - f2i(xleft);
551
        if ((dx < 0) || (xright < 0) || (xleft > xright))               // the (xleft > xright) term is not redundant with (dx < 0) because dx is computed using integers
552
                return;
553
 
554
                // setup to call assembler scanline renderer
555
        recip_dx = fix_recip(dx);
556
 
557
                du_dx = fixmul(uright - uleft,recip_dx);
558
                dv_dx = fixmul(vright - vleft,recip_dx);
559
 
560
                fx_u = uleft;
561
                fx_v = vleft;
562
                fx_du_dx = du_dx;
563
                fx_dv_dx = dv_dx;
564
                fx_y = y;
565
                fx_xright = f2i(xright);
566
                fx_xleft = f2i(xleft);
567
                pixptr = srcb.bm_data;
568
 
569
                switch (Lighting_enabled) {
570
                        case 0:
571
                                //added 07/11/99 adb - prevent writing before the buffer
572
                                if (fx_xleft < 0)
573
                                        fx_xleft = 0;
574
                                //end addition -adb
575
 
576
                                cur_tmap_scanline_lin_nolight();
577
                                break;
578
                        case 1:
579
                                if (lleft < F1_0/2)
580
                                        lleft = F1_0/2;
581
                                if (lright < F1_0/2)
582
                                        lright = F1_0/2;
583
 
584
                                if (lleft > MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS)
585
                                        lleft = MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS;
586
                                if (lright > MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS)
587
                                        lright = MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS;
588
 
589
                                //added 07/11/99 adb - prevent writing before the buffer
590
                                if (fx_xleft < 0)
591
                                        fx_xleft = 0;
592
                                //end addition -adb
593
 
594
{
595
                        fix mul_thing;
596
 
597
                        fx_l = lleft;
598
                        fx_dl_dx = fixmul(lright - lleft,recip_dx);
599
 
600
                        //      This is a pretty ugly hack to prevent lighting overflows.
601
                        mul_thing = dx * fx_dl_dx;
602
                        if (lleft + mul_thing < 0)
603
                                fx_dl_dx += 12;
604
                        else if (lleft + mul_thing > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2))
605
                                fx_dl_dx -= 12;
606
}
607
 
608
                                fx_l = lleft;
609
                                dl_dx = fixmul(lright - lleft,recip_dx);
610
                                fx_dl_dx = dl_dx;
611
                                cur_tmap_scanline_lin();
612
                                break;
613
                        case 2:
614
#ifdef EDITOR_TMAP
615
                                fx_xright = f2i(xright);
616
                                fx_xleft = f2i(xleft);
617
                                tmap_flat_color = 1;
618
                                cur_tmap_scanline_flat();
619
#else
620
                                Int3(); //      Illegal, called an editor only routine!
621
#endif
622
                                break;
623
                }
624
}
625
 
626
// -------------------------------------------------------------------------------------
627
//      Render a texture map with lighting using perspective interpolation in inner and outer loops.
628
// -------------------------------------------------------------------------------------
629
static void ntexture_map_lighted_linear(const grs_bitmap &srcb, const g3ds_tmap &t)
630
{
631
        int     vlt,vrt,vlb,vrb;        // vertex left top, vertex right top, vertex left bottom, vertex right bottom
632
        int     topy,boty,dy;
633
        fix     dx_dy_left,dx_dy_right;
634
        fix     du_dy_left,du_dy_right;
635
        fix     dv_dy_left,dv_dy_right;
636
        fix     dl_dy_left,dl_dy_right;
637
        int     max_y_vertex;
638
        fix     xleft,xright,uleft,vleft,uright,vright,lleft,lright;
639
        int     next_break_left, next_break_right;
640
        fix     recip_dyl, recip_dyr;
641
 
642
        //remove stupid warnings in compile
643
        dl_dy_left = F1_0;
644
        dl_dy_right = F1_0;
645
        lleft = F1_0;
646
        lright = F1_0;
647
 
648
        auto &v3d = t.verts;
649
 
650
        // Determine top and bottom y coords.
651
        compute_y_bounds(t,vlt,vlb,vrt,vrb,max_y_vertex);
652
 
653
        // Set top and bottom (of entire texture map) y coordinates.
654
        topy = f2i(v3d[vlt].y2d);
655
        boty = f2i(v3d[max_y_vertex].y2d);
656
 
657
        if (topy > Window_clip_bot)
658
                return;
659
        if (boty > Window_clip_bot)
660
                boty = Window_clip_bot;
661
 
662
        dy = f2i(t.verts[vlb].y2d) - f2i(t.verts[vlt].y2d);
663
        recip_dyl = fix_recip(dy);
664
 
665
        dy = f2i(t.verts[vrb].y2d) - f2i(t.verts[vrt].y2d);
666
        recip_dyr = fix_recip(dy);
667
 
668
        // Set amount to change x coordinate for each advance to next scanline.
669
        dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dyl);
670
        dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dyr);
671
 
672
        du_dy_left = compute_du_dy_lin(t,vlt,vlb, recip_dyl);
673
        du_dy_right = compute_du_dy_lin(t,vrt,vrb, recip_dyr);
674
 
675
        dv_dy_left = compute_dv_dy_lin(t,vlt,vlb, recip_dyl);
676
        dv_dy_right = compute_dv_dy_lin(t,vrt,vrb, recip_dyr);
677
 
678
        if (Lighting_enabled) {
679
                dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dyl);
680
                dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dyr);
681
 
682
                lleft = v3d[vlt].l;
683
                lright = v3d[vrt].l;
684
        }
685
 
686
        // Set initial values for x, u, v
687
        xleft = v3d[vlt].x2d;
688
        xright = v3d[vrt].x2d;
689
 
690
        uleft = v3d[vlt].u;
691
        uright = v3d[vrt].u;
692
        vleft = v3d[vlt].v;
693
        vright = v3d[vrt].v;
694
 
695
        // scan all rows in texture map from top through first break.
696
        next_break_left = f2i(v3d[vlb].y2d);
697
        next_break_right = f2i(v3d[vrb].y2d);
698
 
699
        for (int y = topy; y < boty; y++) {
700
 
701
                // See if we have reached the end of the current left edge, and if so, set
702
                // new values for dx_dy and x,u,v
703
                if (y == next_break_left) {
704
                        fix     recip_dy;
705
 
706
                        // Handle problem of double points.  Search until y coord is different.  Cannot get
707
                        // hung in an infinite loop because we know there is a vertex with a lower y coordinate
708
                        // because in the for loop, we don't scan all spanlines.
709
                        while (y == f2i(v3d[vlb].y2d)) {
710
                                vlt = vlb;
711
                                vlb = prevmod(vlb,t.nv);
712
                        }
713
                        next_break_left = f2i(v3d[vlb].y2d);
714
 
715
                        dy = f2i(t.verts[vlb].y2d) - f2i(t.verts[vlt].y2d);
716
                        recip_dy = fix_recip(dy);
717
 
718
                        dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dy);
719
 
720
                        xleft = v3d[vlt].x2d;
721
                        uleft = v3d[vlt].u;
722
                        vleft = v3d[vlt].v;
723
                        lleft = v3d[vlt].l;
724
 
725
                        du_dy_left = compute_du_dy_lin(t,vlt,vlb, recip_dy);
726
                        dv_dy_left = compute_dv_dy_lin(t,vlt,vlb, recip_dy);
727
 
728
                        if (Lighting_enabled) {
729
                                dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dy);
730
                                lleft = v3d[vlt].l;
731
                        }
732
                }
733
 
734
                // See if we have reached the end of the current left edge, and if so, set
735
                // new values for dx_dy and x.  Not necessary to set new values for u,v.
736
                if (y == next_break_right) {
737
                        fix     recip_dy;
738
 
739
                        while (y == f2i(v3d[vrb].y2d)) {
740
                                vrt = vrb;
741
                                vrb = succmod(vrb,t.nv);
742
                        }
743
 
744
                        dy = f2i(t.verts[vrb].y2d) - f2i(t.verts[vrt].y2d);
745
                        recip_dy = fix_recip(dy);
746
 
747
                        next_break_right = f2i(v3d[vrb].y2d);
748
                        dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dy);
749
 
750
                        xright = v3d[vrt].x2d;
751
                        uright = v3d[vrt].u;
752
                        vright = v3d[vrt].v;
753
 
754
                        du_dy_right = compute_du_dy_lin(t,vrt,vrb, recip_dy);
755
                        dv_dy_right = compute_dv_dy_lin(t,vrt,vrb, recip_dy);
756
 
757
                        if (Lighting_enabled) {
758
                                dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dy);
759
                                lright = v3d[vrt].l;
760
                        }
761
                }
762
 
763
                if (Lighting_enabled) {
764
                        ntmap_scanline_lighted_linear(srcb,y,xleft,xright,uleft,uright,vleft,vright,lleft,lright);
765
                        lleft += dl_dy_left;
766
                        lright += dl_dy_right;
767
                } else
768
                        ntmap_scanline_lighted_linear(srcb,y,xleft,xright,uleft,uright,vleft,vright,lleft,lright);
769
 
770
                uleft += du_dy_left;
771
                vleft += dv_dy_left;
772
 
773
                uright += du_dy_right;
774
                vright += dv_dy_right;
775
 
776
                xleft += dx_dy_left;
777
                xright += dx_dy_right;
778
 
779
        }
780
 
781
        // We can get lleft or lright out of bounds here because we compute dl_dy using fixed point values,
782
        //      but we plot an integer number of scanlines, therefore doing an integer number of additions of the delta.
783
 
784
        ntmap_scanline_lighted_linear(srcb,boty,xleft,xright,uleft,uright,vleft,vright,lleft,lright);
785
}
786
 
787
// fix  DivNum = F1_0*12;
788
 
789
// -------------------------------------------------------------------------------------
790
// Interface from Matt's data structures to Mike's texture mapper.
791
// -------------------------------------------------------------------------------------
792
void draw_tmap(grs_canvas &canvas, const grs_bitmap &rbp, uint_fast32_t nverts, const g3s_point *const *vertbuf)
793
{
794
        //      These variables are used in system which renders texture maps which lie on one scanline as a line.
795
        // fix  div_numerator;
796
        int     lighting_on_save = Lighting_on;
797
 
798
        Assert(nverts <= MAX_TMAP_VERTS);
799
 
800
        const grs_bitmap *bp = &rbp;
801
        //      If no transparency and seg depth is large, render as flat shaded.
802
        if ((Current_seg_depth > Max_linear_depth) && ((bp->get_flag_mask(3)) == 0)) {
803
                draw_tmap_flat(canvas, rbp, nverts, vertbuf);
804
                return;
805
        }
806
 
807
        bp = rle_expand_texture(*bp);           // Expand if rle'd
808
 
809
        Transparency_on = bp->get_flag_mask(BM_FLAG_TRANSPARENT);
810
        if (bp->get_flag_mask(BM_FLAG_NO_LIGHTING))
811
                Lighting_on = 0;
812
 
813
 
814
        // Setup texture map in Tmap1
815
        g3ds_tmap Tmap1;
816
        Tmap1.nv = nverts;                                              // Initialize number of vertices
817
 
818
//      div_numerator = DivNum; //f1_0*3;
819
 
820
        for (int i=0; i<nverts; i++) {
821
                g3ds_vertex     *tvp = &Tmap1.verts[i];
822
                auto vp = vertbuf[i];
823
 
824
                tvp->x2d = vp->p3_sx;
825
                tvp->y2d = vp->p3_sy;
826
 
827
                //      Check for overflow on fixdiv.  Will overflow on vp->z <= something small.  Allow only as low as 256.
828
                auto clipped_p3_z = std::max(256, vp->p3_z);
829
                tvp->z = fixdiv(F1_0*12, clipped_p3_z);
830
                tvp->u = vp->p3_u << 6; //* bp->bm_w;
831
                tvp->v = vp->p3_v << 6; //* bp->bm_h;
832
 
833
                Assert(Lighting_on < 3);
834
 
835
                if (Lighting_on)
836
                        tvp->l = vp->p3_l * NUM_LIGHTING_LEVELS;
837
        }
838
 
839
 
840
        Lighting_enabled = Lighting_on;
841
 
842
        // Now, call my texture mapper.
843
                switch (Interpolation_method) { // 0 = choose, 1 = linear, 2 = /8 perspective, 3 = full perspective
844
                        case 0:                                                         // choose best interpolation
845
                                if (Current_seg_depth > Max_perspective_depth)
846
                                {
847
                                case 1:                                                         // linear interpolation
848
                                        ntexture_map_lighted_linear(*bp, Tmap1);
849
                                }
850
                                else
851
                                {
852
                                        DXX_BOOST_FALLTHROUGH;
853
                                case 2:                                                         // perspective every 8th pixel interpolation
854
                                case 3:                                                         // perspective every pixel interpolation
855
                                        ntexture_map_lighted(*bp, Tmap1);
856
                                }
857
                                break;
858
                        default:
859
                                Assert(0);                              // Illegal value for Interpolation_method, must be 0,1,2,3
860
                }
861
 
862
        Lighting_on = lighting_on_save;
863
 
864
}
865
 
866
}