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