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 color_palette_index *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>
init_fix_recip_table(std::index_sequence<0,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.
init_interface_vars_to_assembler(void)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
prevmod(int val,int modulus)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
succmod(int val,int modulus)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 // -------------------------------------------------------------------------------------
compute_y_bounds(const g3ds_tmap & t,int & vlt,int & vlb,int & vrt,int & vrb,int & bottom_y_ind)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
compute_du_dy_lin(const g3ds_tmap & t,int top_vertex,int bottom_vertex,fix recip_dy)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
compute_dv_dy_lin(const g3ds_tmap & t,int top_vertex,int bottom_vertex,fix recip_dy)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
compute_dl_dy_lin(const g3ds_tmap & t,int top_vertex,int bottom_vertex,fix recip_dy)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
compute_dx_dy(const g3ds_tmap & t,int top_vertex,int bottom_vertex,fix recip_dy)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
compute_du_dy(const g3ds_tmap & t,int top_vertex,int bottom_vertex,fix recip_dy)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
compute_dv_dy(const g3ds_tmap & t,int top_vertex,int bottom_vertex,fix recip_dy)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
compute_dz_dy(const g3ds_tmap & t,int top_vertex,int bottom_vertex,fix recip_dy)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 // -------------------------------------------------------------------------------------
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)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 c_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 // -------------------------------------------------------------------------------------
ntexture_map_lighted(const grs_bitmap & srcb,const g3ds_tmap & t)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 // -------------------------------------------------------------------------------------
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)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 c_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 c_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 c_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 // -------------------------------------------------------------------------------------
ntexture_map_lighted_linear(const grs_bitmap & srcb,const g3ds_tmap & t)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 // -------------------------------------------------------------------------------------
draw_tmap(grs_canvas & canvas,const grs_bitmap & rbp,uint_fast32_t nverts,const g3s_point * const * vertbuf)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 }
867