1 /* $Id: ntmap.c,v 1.8 2003/03/19 19:21:34 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
13 */
14
15 /*
16 *
17 * Start of conversion to new texture mapper.
18 *
19 * Old Log:
20 * Revision 1.52 1995/03/14 15:13:06 john
21 * Increased MAX_Y_Pointers to 480.
22 *
23 * Revision 1.51 1995/02/23 14:25:09 john
24 * Added editor tmap.
25 *
26 * Revision 1.50 1995/02/20 18:22:58 john
27 * Put all the externs in the assembly modules into tmap_inc.asm.
28 * Also, moved all the C versions of the inner loops into a new module,
29 * scanline.c.
30 *
31 * Revision 1.49 1995/02/20 17:09:11 john
32 * Added code so that you can build the tmapper with no assembly!
33 *
34 * Revision 1.48 1995/01/06 11:11:30 mike
35 * even when not in editor, have 400 lines in texture map scanline table.
36 *
37 * Revision 1.47 1994/12/15 16:43:25 matt
38 * Took out code only needed by editor
39 *
40 * Revision 1.46 1994/12/09 22:35:37 mike
41 * fix bug in before call to asm_tmap_scanline_per causing write of pixel onto past right border onto left.
42 *
43 * Revision 1.45 1994/12/06 16:31:06 mike
44 * fix bug in asm_tmap_scanline_matt interface.
45 *
46 * Revision 1.44 1994/12/04 20:37:18 mike
47 * *** empty log message ***
48 *
49 * Revision 1.43 1994/12/02 23:30:04 mike
50 * optimizations.
51 *
52 * Revision 1.42 1994/11/30 00:57:43 mike
53 * optimizations.
54 *
55 * Revision 1.41 1994/11/28 13:34:27 mike
56 * optimizations.
57 *
58 * Revision 1.40 1994/11/28 01:30:01 mike
59 * kill warning.
60 *
61 * Revision 1.39 1994/11/28 01:28:59 mike
62 * optimizations.
63 *
64 * Revision 1.38 1994/11/21 14:08:07 john
65 * Took out all multiple instead of divide code.
66 *
67 * Revision 1.37 1994/11/19 15:21:52 mike
68 * rip out unused code.
69 *
70 * Revision 1.36 1994/11/14 11:42:51 mike
71 * optimization.
72 *
73 * Revision 1.35 1994/11/12 16:41:36 mike
74 * *** empty log message ***
75 *
76 * Revision 1.34 1994/11/10 21:28:41 mike
77 * remove call to init_interface_vars_to_assembler.
78 *
79 * Revision 1.33 1994/11/10 11:08:59 mike
80 * detail level stuff.
81 *
82 * Revision 1.32 1994/11/09 22:55:52 matt
83 * Added variable Current_seg_depth for detail level optimization
84 *
85 * Revision 1.31 1994/11/09 19:57:31 john
86 * Added texture rle caching.
87 *
88 * Revision 1.30 1994/11/09 19:54:48 mike
89 * Call flat shader if Tmap_flat_flag set.
90 *
91 * Revision 1.29 1994/11/02 21:33:31 john
92 * Added Burger Bill's optimization, ie.. 2 muls per 8 pixels.
93 *
94 * Revision 1.28 1994/11/02 11:32:16 john
95 * Added code for c callable inner loop and code to
96 * test dividing out z0.
97 *
98 * Revision 1.27 1994/10/28 20:54:32 matt
99 * Added error checking
100 *
101 * Revision 1.26 1994/10/25 11:20:20 mike
102 * fix bug in lighting overflow checking for one scanline tall linear texture maps.
103 *
104 * Revision 1.25 1994/08/03 15:40:33 mike
105 * Prevent divide overflows, decrease occurrence of precision-caused glitches.
106 *
107 * Revision 1.24 1994/07/27 09:31:16 mike
108 * Fix concave texture map problem, decrease occurrence of unimportant int 3.
109 *
110 * Revision 1.23 1994/06/17 12:23:31 mike
111 * Support non-lighted texture maps.
112 *
113 * Revision 1.22 1994/06/11 08:10:24 mike
114 * Fix mysterious hang bug, lighting value was out of range.
115 *
116 * Revision 1.21 1994/06/09 16:10:16 mike
117 * Change SC2000 from constant to variable.
118 *
119 */
120
121 #ifdef HAVE_CONFIG_H
122 #include <conf.h>
123 #endif
124
125 #ifdef RCS
126 static char rcsid[] = "$Id: ntmap.c,v 1.8 2003/03/19 19:21:34 btb Exp $";
127 #endif
128
129 #define VESA 0
130 #define NUM_TMAPS 16
131
132 #define HEADLIGHT_LIGHTING 0
133
134 #define WIREFRAME 0
135 #define PERSPECTIVE 1
136
137 #include "pstypes.h"
138 #include "fix.h"
139 #include "vecmat.h"
140 #include "gr.h"
141 #include "3d.h"
142 #include "error.h"
143
144 #include "texmap.h"
145 #include "texmapl.h"
146 #include "rle.h"
147 #include "scanline.h"
148
149 #ifdef EDITOR
150 #define EDITOR_TMAP 1 //if in, include extra stuff
151 #endif
152
153 #define F15_5 (F1_0*15 + F0_5)
154
155 // Temporary texture map, interface from Matt's 3d system to Mike's texture mapper.
156 g3ds_tmap Tmap1;
157
158 grs_bitmap Texmap_ptrs[NUM_TMAPS];
159 grs_bitmap Texmap4_ptrs[NUM_TMAPS];
160
161 fix Range_max=0; // debug, kill me
162
163 int Interpolation_method=0; // 0 = choose best method
164 int Lighting_on=1; // initialize to no lighting
165 int Tmap_flat_flag = 0; // 1 = render texture maps as flat shaded polygons.
166 int Current_seg_depth; // HACK INTERFACE: how far away the current segment (& thus texture) is
167 int Max_perspective_depth;
168 int Max_linear_depth;
169 int Max_flat_depth;
170
171 extern int Window_clip_left, Window_clip_bot, Window_clip_right, Window_clip_top;
172
173 // These variables are the interface to assembler. They get set for each texture map, which is a real waste of time.
174 // They should be set only when they change, which is generally when the window bounds change. And, even still, it's
175 // a pretty bad interface.
176 int bytes_per_row=-1;
177 unsigned char *write_buffer;
178 int window_left;
179 int window_right;
180 int window_top;
181 int window_bottom;
182 int window_width;
183 int window_height;
184
185 #define MAX_Y_POINTERS 1024
186
187 int y_pointers[MAX_Y_POINTERS];
188
189 fix fix_recip[FIX_RECIP_TABLE_SIZE];
190
191 int Lighting_enabled;
192 int Fix_recip_table_computed=0;
193
194 fix fx_l, fx_u, fx_v, fx_z, fx_du_dx, fx_dv_dx, fx_dz_dx, fx_dl_dx;
195 int fx_xleft, fx_xright, fx_y;
196 unsigned char * pixptr;
197 int per2_flag = 0;
198 int Transparency_on = 0;
199 int dither_intensity_lighting = 0;
200
201 ubyte * tmap_flat_cthru_table;
202 ubyte tmap_flat_color;
203 ubyte tmap_flat_shade_value;
204
205
206
207 // -------------------------------------------------------------------------------------
init_fix_recip_table(void)208 void init_fix_recip_table(void)
209 {
210 int i;
211
212 fix_recip[0] = F1_0;
213
214 for (i=1; i<FIX_RECIP_TABLE_SIZE; i++)
215 fix_recip[i] = F1_0/i;
216
217 Fix_recip_table_computed = 1;
218 }
219
220 // -------------------------------------------------------------------------------------
221 // Initialize interface variables to assembler.
222 // These things used to be constants. This routine is now (10/6/93) getting called for
223 // every texture map. It should get called whenever the window changes, or, preferably,
224 // not at all. I'm pretty sure these variables are only being used for range checking.
init_interface_vars_to_assembler(void)225 void init_interface_vars_to_assembler(void)
226 {
227 grs_bitmap *bp;
228
229 bp = &grd_curcanv->cv_bitmap;
230
231 Assert(bp!=NULL);
232 Assert(bp->bm_data!=NULL);
233 Assert(bp->bm_h <= MAX_Y_POINTERS);
234
235 // If bytes_per_row has changed, create new table of pointers.
236 if (bytes_per_row != (int) bp->bm_rowsize) {
237 int y_val, i;
238
239 bytes_per_row = (int) bp->bm_rowsize;
240
241 y_val = 0;
242 for (i=0; i<MAX_Y_POINTERS; i++) {
243 y_pointers[i] = y_val;
244 y_val += bytes_per_row;
245 }
246 }
247
248 write_buffer = (unsigned char *) bp->bm_data;
249
250 window_left = 0;
251 window_right = (int) bp->bm_w-1;
252 window_top = 0;
253 window_bottom = (int) bp->bm_h-1;
254
255 Window_clip_left = window_left;
256 Window_clip_right = window_right;
257 Window_clip_top = window_top;
258 Window_clip_bot = window_bottom;
259
260 window_width = bp->bm_w;
261 window_height = bp->bm_h;
262
263 if (!Fix_recip_table_computed)
264 init_fix_recip_table();
265 }
266
267 // -------------------------------------------------------------------------------------
268 // VARIABLES
269 extern g3ds_tmap Tmap1;
270
271 // -------------------------------------------------------------------------------------
272 // Returns number preceding val modulo modulus.
273 // prevmod(3,4) = 2
274 // prevmod(0,4) = 3
prevmod(int val,int modulus)275 int prevmod(int val,int modulus)
276 {
277 if (val > 0)
278 return val-1;
279 else
280 return modulus-1;
281 // return (val + modulus - 1) % modulus;
282 }
283
284
285 // Returns number succeeding val modulo modulus.
286 // succmod(3,4) = 0
287 // succmod(0,4) = 1
succmod(int val,int modulus)288 int succmod(int val,int modulus)
289 {
290 if (val < modulus-1)
291 return val+1;
292 else
293 return 0;
294
295 // return (val + 1) % modulus;
296 }
297
298 // -------------------------------------------------------------------------------------
299 // Select topmost vertex (minimum y coordinate) and bottommost (maximum y coordinate) in
300 // texture map. If either is part of a horizontal edge, then select leftmost vertex for
301 // top, rightmost vertex for bottom.
302 // Important: Vertex is selected with integer precision. So, if there are vertices at
303 // (0.0,0.7) and (0.5,0.3), the first vertex is selected, because they y coordinates are
304 // considered the same, so the smaller x is favored.
305 // Parameters:
306 // nv number of vertices
307 // v3d pointer to 3d vertices containing u,v,x2d,y2d coordinates
308 // Results in:
309 // *min_y_ind
310 // *max_y_ind
311 // -------------------------------------------------------------------------------------
compute_y_bounds(g3ds_tmap * t,int * vlt,int * vlb,int * vrt,int * vrb,int * bottom_y_ind)312 void compute_y_bounds(g3ds_tmap *t, int *vlt, int *vlb, int *vrt, int *vrb,int *bottom_y_ind)
313 {
314 int i;
315 int min_y,max_y;
316 int min_y_ind;
317 int original_vrt;
318 fix min_x;
319
320 // Scan all vertices, set min_y_ind to vertex with smallest y coordinate.
321 min_y = f2i(t->verts[0].y2d);
322 max_y = min_y;
323 min_y_ind = 0;
324 min_x = f2i(t->verts[0].x2d);
325 *bottom_y_ind = 0;
326
327 for (i=1; i<t->nv; i++) {
328 if (f2i(t->verts[i].y2d) < min_y) {
329 min_y = f2i(t->verts[i].y2d);
330 min_y_ind = i;
331 min_x = f2i(t->verts[i].x2d);
332 } else if (f2i(t->verts[i].y2d) == min_y) {
333 if (f2i(t->verts[i].x2d) < min_x) {
334 min_y_ind = i;
335 min_x = f2i(t->verts[i].x2d);
336 }
337 }
338 if (f2i(t->verts[i].y2d) > max_y) {
339 max_y = f2i(t->verts[i].y2d);
340 *bottom_y_ind = i;
341 }
342 }
343
344 //--removed mk, 11/27/94-- // Check for a non-upright-hourglass polygon and fix, if necessary, by bashing a y coordinate.
345 //--removed mk, 11/27/94-- // min_y_ind = index of minimum y coordinate, *bottom_y_ind = index of maximum y coordinate
346 //--removed mk, 11/27/94--{
347 //--removed mk, 11/27/94-- int max_temp, min_temp;
348 //--removed mk, 11/27/94--
349 //--removed mk, 11/27/94-- max_temp = *bottom_y_ind;
350 //--removed mk, 11/27/94-- if (*bottom_y_ind < min_y_ind)
351 //--removed mk, 11/27/94-- max_temp += t->nv;
352 //--removed mk, 11/27/94--
353 //--removed mk, 11/27/94-- for (i=min_y_ind; i<max_temp; i++) {
354 //--removed mk, 11/27/94-- if (f2i(t->verts[i%t->nv].y2d) > f2i(t->verts[(i+1)%t->nv].y2d)) {
355 //--removed mk, 11/27/94-- Int3();
356 //--removed mk, 11/27/94-- t->verts[(i+1)%t->nv].y2d = t->verts[i%t->nv].y2d;
357 //--removed mk, 11/27/94-- }
358 //--removed mk, 11/27/94-- }
359 //--removed mk, 11/27/94--
360 //--removed mk, 11/27/94-- min_temp = min_y_ind;
361 //--removed mk, 11/27/94-- if (min_y_ind < *bottom_y_ind)
362 //--removed mk, 11/27/94-- min_temp += t->nv;
363 //--removed mk, 11/27/94--
364 //--removed mk, 11/27/94-- for (i=*bottom_y_ind; i<min_temp; i++) {
365 //--removed mk, 11/27/94-- if (f2i(t->verts[i%t->nv].y2d) < f2i(t->verts[(i+1)%t->nv].y2d)) {
366 //--removed mk, 11/27/94-- Int3();
367 //--removed mk, 11/27/94-- t->verts[(i+1)%t->nv].y2d = t->verts[i%t->nv].y2d;
368 //--removed mk, 11/27/94-- }
369 //--removed mk, 11/27/94-- }
370 //--removed mk, 11/27/94--}
371
372 // Set "vertex left top", etc. based on vertex with topmost y coordinate
373 *vlt = min_y_ind;
374 *vrt = *vlt;
375 *vlb = prevmod(*vlt,t->nv);
376 *vrb = succmod(*vrt,t->nv);
377
378 // If right edge is horizontal, then advance along polygon bound until it no longer is or until all
379 // vertices have been examined.
380 // (Left edge cannot be horizontal, because *vlt is set to leftmost point with highest y coordinate.)
381
382 original_vrt = *vrt;
383
384 while (f2i(t->verts[*vrt].y2d) == f2i(t->verts[*vrb].y2d)) {
385 if (succmod(*vrt,t->nv) == original_vrt) {
386 break;
387 }
388 *vrt = succmod(*vrt,t->nv);
389 *vrb = succmod(*vrt,t->nv);
390 }
391 }
392
393 // -------------------------------------------------------------------------------------
394 // Returns dx/dy given two vertices.
395 // If dy == 0, returns 0.0
396 // -------------------------------------------------------------------------------------
397 //--fix compute_dx_dy_lin(g3ds_tmap *t, int top_vertex,int bottom_vertex)
398 //--{
399 //-- int dy;
400 //--
401 //-- // compute delta x with respect to y for any edge
402 //-- dy = f2i(t->verts[bottom_vertex].y2d - t->verts[top_vertex].y2d) + 1;
403 //-- if (dy)
404 //-- return (t->verts[bottom_vertex].x2d - t->verts[top_vertex].x2d) / dy;
405 //-- else
406 //-- return 0;
407 //--
408 //--}
409
compute_du_dy_lin(g3ds_tmap * t,int top_vertex,int bottom_vertex,fix recip_dy)410 fix compute_du_dy_lin(g3ds_tmap *t, int top_vertex,int bottom_vertex, fix recip_dy)
411 {
412 return fixmul(t->verts[bottom_vertex].u - t->verts[top_vertex].u, recip_dy);
413 }
414
415
compute_dv_dy_lin(g3ds_tmap * t,int top_vertex,int bottom_vertex,fix recip_dy)416 fix compute_dv_dy_lin(g3ds_tmap *t, int top_vertex,int bottom_vertex, fix recip_dy)
417 {
418 return fixmul(t->verts[bottom_vertex].v - t->verts[top_vertex].v, recip_dy);
419 }
420
compute_dl_dy_lin(g3ds_tmap * t,int top_vertex,int bottom_vertex,fix recip_dy)421 fix compute_dl_dy_lin(g3ds_tmap *t, int top_vertex,int bottom_vertex, fix recip_dy)
422 {
423 return fixmul(t->verts[bottom_vertex].l - t->verts[top_vertex].l, recip_dy);
424
425 }
426
compute_dx_dy(g3ds_tmap * t,int top_vertex,int bottom_vertex,fix recip_dy)427 fix compute_dx_dy(g3ds_tmap *t, int top_vertex,int bottom_vertex, fix recip_dy)
428 {
429 return fixmul(t->verts[bottom_vertex].x2d - t->verts[top_vertex].x2d, recip_dy);
430 }
431
compute_du_dy(g3ds_tmap * t,int top_vertex,int bottom_vertex,fix recip_dy)432 fix compute_du_dy(g3ds_tmap *t, int top_vertex,int bottom_vertex, fix recip_dy)
433 {
434 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);
435 }
436
437
compute_dv_dy(g3ds_tmap * t,int top_vertex,int bottom_vertex,fix recip_dy)438 fix compute_dv_dy(g3ds_tmap *t, int top_vertex,int bottom_vertex, fix recip_dy)
439 {
440 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);
441
442 }
443
compute_dz_dy(g3ds_tmap * t,int top_vertex,int bottom_vertex,fix recip_dy)444 fix compute_dz_dy(g3ds_tmap *t, int top_vertex,int bottom_vertex, fix recip_dy)
445 {
446 return fixmul(t->verts[bottom_vertex].z - t->verts[top_vertex].z, recip_dy);
447
448 }
449 int Skip_short_flag=0;
450
451 // -------------------------------------------------------------------------------------
452 // Texture map current scanline in perspective.
453 // -------------------------------------------------------------------------------------
ntmap_scanline_lighted(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)454 void ntmap_scanline_lighted(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)
455 {
456 fix dx,recip_dx;
457
458 fx_xright = f2i(xright);
459 //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?
460 if (fx_xright < Window_clip_left)
461 return;
462 fx_xleft = f2i(xleft);
463 if (fx_xleft > Window_clip_right)
464 return;
465 //end edit -MM
466
467 dx = fx_xright - fx_xleft;
468 if ((dx < 0) || (xright < 0) || (xleft > xright)) // the (xleft > xright) term is not redundant with (dx < 0) because dx is computed using integers
469 return;
470
471 // setup to call assembler scanline renderer
472 if (dx < FIX_RECIP_TABLE_SIZE)
473 recip_dx = fix_recip[dx];
474 else
475 recip_dx = F1_0/dx;
476
477 fx_u = uleft;
478 fx_v = vleft;
479 fx_z = zleft;
480
481 fx_du_dx = fixmul(uright - uleft,recip_dx);
482 fx_dv_dx = fixmul(vright - vleft,recip_dx);
483 fx_dz_dx = fixmul(zright - zleft,recip_dx);
484 fx_y = y;
485 pixptr = srcb->bm_data;
486
487 switch (Lighting_enabled) {
488 case 0:
489 //added 05/17/99 Matt Mueller - prevent writing before the buffer
490 if ((fx_y == 0) && (fx_xleft < 0))
491 fx_xleft = 0;
492 //end addition -MM
493 if (fx_xright > Window_clip_right)
494 fx_xright = Window_clip_right;
495
496 cur_tmap_scanline_per();
497 break;
498 case 1: {
499 fix mul_thing;
500
501 if (lleft < 0) lleft = 0;
502 if (lright < 0) lright = 0;
503 if (lleft > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2)) lleft = (NUM_LIGHTING_LEVELS*F1_0-F1_0/2);
504 if (lright > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2)) lright = (NUM_LIGHTING_LEVELS*F1_0-F1_0/2);
505
506 fx_l = lleft;
507 fx_dl_dx = fixmul(lright - lleft,recip_dx);
508
509 // This is a pretty ugly hack to prevent lighting overflows.
510 mul_thing = dx * fx_dl_dx;
511 if (lleft + mul_thing < 0)
512 fx_dl_dx += 12;
513 else if (lleft + mul_thing > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2))
514 fx_dl_dx -= 12;
515
516 //added 05/17/99 Matt Mueller - prevent writing before the buffer
517 if ((fx_y == 0) && (fx_xleft < 0))
518 fx_xleft = 0;
519 //end addition -MM
520 if (fx_xright > Window_clip_right)
521 fx_xright = Window_clip_right;
522
523 cur_tmap_scanline_per();
524 break;
525 }
526 case 2:
527 #ifdef EDITOR_TMAP
528 fx_xright = f2i(xright);
529 fx_xleft = f2i(xleft);
530
531 tmap_flat_color = 1;
532 cur_tmap_scanline_flat();
533 #else
534 Int3(); // Illegal, called an editor only routine!
535 #endif
536 break;
537 }
538
539 }
540
541 int Do_vertical_scan=0;
542
543 int Break_on_flat=0;
544
545 // -------------------------------------------------------------------------------------
546 // Render a texture map with lighting using perspective interpolation in inner and outer loops.
547 // -------------------------------------------------------------------------------------
ntexture_map_lighted(grs_bitmap * srcb,g3ds_tmap * t)548 void ntexture_map_lighted(grs_bitmap *srcb, g3ds_tmap *t)
549 {
550 int vlt,vrt,vlb,vrb; // vertex left top, vertex right top, vertex left bottom, vertex right bottom
551 int topy,boty,y, dy;
552 fix dx_dy_left,dx_dy_right;
553 fix du_dy_left,du_dy_right;
554 fix dv_dy_left,dv_dy_right;
555 fix dz_dy_left,dz_dy_right;
556 fix dl_dy_left,dl_dy_right;
557 fix recip_dyl, recip_dyr;
558 int max_y_vertex;
559 fix xleft,xright,uleft,vleft,uright,vright,zleft,zright,lleft,lright;
560 int next_break_left, next_break_right;
561
562 g3ds_vertex *v3d;
563
564 //remove stupid warnings in compile
565 dl_dy_left = F1_0;
566 dl_dy_right = F1_0;
567 lleft = F1_0;
568 lright = F1_0;
569
570 v3d = t->verts;
571
572 // Determine top and bottom y coords.
573 compute_y_bounds(t,&vlt,&vlb,&vrt,&vrb,&max_y_vertex);
574
575 // Set top and bottom (of entire texture map) y coordinates.
576 topy = f2i(v3d[vlt].y2d);
577 boty = f2i(v3d[max_y_vertex].y2d);
578 if (topy > Window_clip_bot)
579 return;
580 if (boty > Window_clip_bot)
581 boty = Window_clip_bot;
582
583 // Set amount to change x coordinate for each advance to next scanline.
584 dy = f2i(t->verts[vlb].y2d) - f2i(t->verts[vlt].y2d);
585 if (dy < FIX_RECIP_TABLE_SIZE)
586 recip_dyl = fix_recip[dy];
587 else
588 recip_dyl = F1_0/dy;
589
590 dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dyl);
591 du_dy_left = compute_du_dy(t,vlt,vlb, recip_dyl);
592 dv_dy_left = compute_dv_dy(t,vlt,vlb, recip_dyl);
593 dz_dy_left = compute_dz_dy(t,vlt,vlb, recip_dyl);
594
595 dy = f2i(t->verts[vrb].y2d) - f2i(t->verts[vrt].y2d);
596 if (dy < FIX_RECIP_TABLE_SIZE)
597 recip_dyr = fix_recip[dy];
598 else
599 recip_dyr = F1_0/dy;
600
601 du_dy_right = compute_du_dy(t,vrt,vrb, recip_dyr);
602 dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dyr);
603 dv_dy_right = compute_dv_dy(t,vrt,vrb, recip_dyr);
604 dz_dy_right = compute_dz_dy(t,vrt,vrb, recip_dyr);
605
606 if (Lighting_enabled) {
607 dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dyl);
608 dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dyr);
609
610 lleft = v3d[vlt].l;
611 lright = v3d[vrt].l;
612 }
613
614 // Set initial values for x, u, v
615 xleft = v3d[vlt].x2d;
616 xright = v3d[vrt].x2d;
617
618 zleft = v3d[vlt].z;
619 zright = v3d[vrt].z;
620
621 uleft = fixmul(v3d[vlt].u,zleft);
622 uright = fixmul(v3d[vrt].u,zright);
623 vleft = fixmul(v3d[vlt].v,zleft);
624 vright = fixmul(v3d[vrt].v,zright);
625
626 // scan all rows in texture map from top through first break.
627 next_break_left = f2i(v3d[vlb].y2d);
628 next_break_right = f2i(v3d[vrb].y2d);
629
630 for (y = topy; y < boty; y++) {
631
632 // See if we have reached the end of the current left edge, and if so, set
633 // new values for dx_dy and x,u,v
634 if (y == next_break_left) {
635 fix recip_dy;
636
637 // Handle problem of double points. Search until y coord is different. Cannot get
638 // hung in an infinite loop because we know there is a vertex with a lower y coordinate
639 // because in the for loop, we don't scan all spanlines.
640 while (y == f2i(v3d[vlb].y2d)) {
641 vlt = vlb;
642 vlb = prevmod(vlb,t->nv);
643 }
644 next_break_left = f2i(v3d[vlb].y2d);
645
646 dy = f2i(t->verts[vlb].y2d) - f2i(t->verts[vlt].y2d);
647 if (dy < FIX_RECIP_TABLE_SIZE)
648 recip_dy = fix_recip[dy];
649 else
650 recip_dy = F1_0/dy;
651
652 dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dy);
653
654 xleft = v3d[vlt].x2d;
655 zleft = v3d[vlt].z;
656 uleft = fixmul(v3d[vlt].u,zleft);
657 vleft = fixmul(v3d[vlt].v,zleft);
658 lleft = v3d[vlt].l;
659
660 du_dy_left = compute_du_dy(t,vlt,vlb, recip_dy);
661 dv_dy_left = compute_dv_dy(t,vlt,vlb, recip_dy);
662 dz_dy_left = compute_dz_dy(t,vlt,vlb, recip_dy);
663
664 if (Lighting_enabled) {
665 dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dy);
666 lleft = v3d[vlt].l;
667 }
668 }
669
670 // See if we have reached the end of the current left edge, and if so, set
671 // new values for dx_dy and x. Not necessary to set new values for u,v.
672 if (y == next_break_right) {
673 fix recip_dy;
674
675 while (y == f2i(v3d[vrb].y2d)) {
676 vrt = vrb;
677 vrb = succmod(vrb,t->nv);
678 }
679
680 next_break_right = f2i(v3d[vrb].y2d);
681
682 dy = f2i(t->verts[vrb].y2d) - f2i(t->verts[vrt].y2d);
683 if (dy < FIX_RECIP_TABLE_SIZE)
684 recip_dy = fix_recip[dy];
685 else
686 recip_dy = F1_0/dy;
687
688 dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dy);
689
690 xright = v3d[vrt].x2d;
691 zright = v3d[vrt].z;
692 uright = fixmul(v3d[vrt].u,zright);
693 vright = fixmul(v3d[vrt].v,zright);
694
695 du_dy_right = compute_du_dy(t,vrt,vrb, recip_dy);
696 dv_dy_right = compute_dv_dy(t,vrt,vrb, recip_dy);
697 dz_dy_right = compute_dz_dy(t,vrt,vrb, recip_dy);
698
699 if (Lighting_enabled) {
700 dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dy);
701 lright = v3d[vrt].l;
702 }
703 }
704
705 if (Lighting_enabled) {
706 if (y >= Window_clip_top)
707 ntmap_scanline_lighted(srcb,y,xleft,xright,uleft,uright,vleft,vright,zleft,zright,lleft,lright);
708 lleft += dl_dy_left;
709 lright += dl_dy_right;
710 } else
711 if (y >= Window_clip_top)
712 ntmap_scanline_lighted(srcb,y,xleft,xright,uleft,uright,vleft,vright,zleft,zright,lleft,lright);
713
714 uleft += du_dy_left;
715 vleft += dv_dy_left;
716
717 uright += du_dy_right;
718 vright += dv_dy_right;
719
720 xleft += dx_dy_left;
721 xright += dx_dy_right;
722
723 zleft += dz_dy_left;
724 zright += dz_dy_right;
725
726 }
727
728 // We can get lleft or lright out of bounds here because we compute dl_dy using fixed point values,
729 // but we plot an integer number of scanlines, therefore doing an integer number of additions of the delta.
730
731 //if (Break_on_flat)
732 // mprintf(0, "[%i %i %i] ", y, f2i(xleft), f2i(xright));
733
734 ntmap_scanline_lighted(srcb,y,xleft,xright,uleft,uright,vleft,vright,zleft,zright,lleft,lright);
735 }
736
737
738 // -------------------------------------------------------------------------------------
739 // Texture map current scanline using linear interpolation.
740 // -------------------------------------------------------------------------------------
ntmap_scanline_lighted_linear(grs_bitmap * srcb,int y,fix xleft,fix xright,fix uleft,fix uright,fix vleft,fix vright,fix lleft,fix lright)741 void ntmap_scanline_lighted_linear(grs_bitmap *srcb, int y, fix xleft, fix xright, fix uleft, fix uright, fix vleft, fix vright, fix lleft, fix lright)
742 {
743 fix u,v,l;
744 fix dx,recip_dx;
745
746 fix du_dx,dv_dx,dl_dx;
747
748 u = uleft;
749 v = vleft;
750 l = lleft;
751
752 dx = f2i(xright) - f2i(xleft);
753 if ((dx < 0) || (xright < 0) || (xleft > xright)) // the (xleft > xright) term is not redundant with (dx < 0) because dx is computed using integers
754 return;
755
756 // setup to call assembler scanline renderer
757 if (dx < FIX_RECIP_TABLE_SIZE)
758 recip_dx = fix_recip[dx];
759 else
760 recip_dx = F1_0/dx;
761
762 du_dx = fixmul(uright - uleft,recip_dx);
763 dv_dx = fixmul(vright - vleft,recip_dx);
764
765 fx_u = uleft;
766 fx_v = vleft;
767 fx_du_dx = du_dx;
768 fx_dv_dx = dv_dx;
769 fx_y = y;
770 fx_xright = f2i(xright);
771 fx_xleft = f2i(xleft);
772 pixptr = srcb->bm_data;
773
774 switch (Lighting_enabled) {
775 case 0:
776 //added 07/11/99 adb - prevent writing before the buffer
777 if (fx_xleft < 0)
778 fx_xleft = 0;
779 //end addition -adb
780
781 cur_tmap_scanline_lin_nolight();
782 break;
783 case 1:
784 if (lleft < F1_0/2)
785 lleft = F1_0/2;
786 if (lright < F1_0/2)
787 lright = F1_0/2;
788
789 if (lleft > MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS)
790 lleft = MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS;
791 if (lright > MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS)
792 lright = MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS;
793
794 //added 07/11/99 adb - prevent writing before the buffer
795 if (fx_xleft < 0)
796 fx_xleft = 0;
797 //end addition -adb
798
799 {
800 fix mul_thing;
801
802 fx_l = lleft;
803 fx_dl_dx = fixmul(lright - lleft,recip_dx);
804
805 // This is a pretty ugly hack to prevent lighting overflows.
806 mul_thing = dx * fx_dl_dx;
807 if (lleft + mul_thing < 0)
808 fx_dl_dx += 12;
809 else if (lleft + mul_thing > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2))
810 fx_dl_dx -= 12;
811 }
812
813 fx_l = lleft;
814 dl_dx = fixmul(lright - lleft,recip_dx);
815 fx_dl_dx = dl_dx;
816 cur_tmap_scanline_lin();
817 break;
818 case 2:
819 #ifdef EDITOR_TMAP
820 fx_xright = f2i(xright);
821 fx_xleft = f2i(xleft);
822 tmap_flat_color = 1;
823 cur_tmap_scanline_flat();
824 #else
825 Int3(); // Illegal, called an editor only routine!
826 #endif
827 break;
828 }
829 }
830
831 // -------------------------------------------------------------------------------------
832 // Render a texture map with lighting using perspective interpolation in inner and outer loops.
833 // -------------------------------------------------------------------------------------
ntexture_map_lighted_linear(grs_bitmap * srcb,g3ds_tmap * t)834 void ntexture_map_lighted_linear(grs_bitmap *srcb, g3ds_tmap *t)
835 {
836 int vlt,vrt,vlb,vrb; // vertex left top, vertex right top, vertex left bottom, vertex right bottom
837 int topy,boty,y, dy;
838 fix dx_dy_left,dx_dy_right;
839 fix du_dy_left,du_dy_right;
840 fix dv_dy_left,dv_dy_right;
841 fix dl_dy_left,dl_dy_right;
842 int max_y_vertex;
843 fix xleft,xright,uleft,vleft,uright,vright,lleft,lright;
844 int next_break_left, next_break_right;
845 fix recip_dyl, recip_dyr;
846
847 g3ds_vertex *v3d;
848
849 //remove stupid warnings in compile
850 dl_dy_left = F1_0;
851 dl_dy_right = F1_0;
852 lleft = F1_0;
853 lright = F1_0;
854
855 v3d = t->verts;
856
857 // Determine top and bottom y coords.
858 compute_y_bounds(t,&vlt,&vlb,&vrt,&vrb,&max_y_vertex);
859
860 // Set top and bottom (of entire texture map) y coordinates.
861 topy = f2i(v3d[vlt].y2d);
862 boty = f2i(v3d[max_y_vertex].y2d);
863
864 if (topy > Window_clip_bot)
865 return;
866 if (boty > Window_clip_bot)
867 boty = Window_clip_bot;
868
869 dy = f2i(t->verts[vlb].y2d) - f2i(t->verts[vlt].y2d);
870 if (dy < FIX_RECIP_TABLE_SIZE)
871 recip_dyl = fix_recip[dy];
872 else
873 recip_dyl = F1_0/dy;
874
875 dy = f2i(t->verts[vrb].y2d) - f2i(t->verts[vrt].y2d);
876 if (dy < FIX_RECIP_TABLE_SIZE)
877 recip_dyr = fix_recip[dy];
878 else
879 recip_dyr = F1_0/dy;
880
881 // Set amount to change x coordinate for each advance to next scanline.
882 dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dyl);
883 dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dyr);
884
885 du_dy_left = compute_du_dy_lin(t,vlt,vlb, recip_dyl);
886 du_dy_right = compute_du_dy_lin(t,vrt,vrb, recip_dyr);
887
888 dv_dy_left = compute_dv_dy_lin(t,vlt,vlb, recip_dyl);
889 dv_dy_right = compute_dv_dy_lin(t,vrt,vrb, recip_dyr);
890
891 if (Lighting_enabled) {
892 dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dyl);
893 dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dyr);
894
895 lleft = v3d[vlt].l;
896 lright = v3d[vrt].l;
897 }
898
899 // Set initial values for x, u, v
900 xleft = v3d[vlt].x2d;
901 xright = v3d[vrt].x2d;
902
903 uleft = v3d[vlt].u;
904 uright = v3d[vrt].u;
905 vleft = v3d[vlt].v;
906 vright = v3d[vrt].v;
907
908 // scan all rows in texture map from top through first break.
909 next_break_left = f2i(v3d[vlb].y2d);
910 next_break_right = f2i(v3d[vrb].y2d);
911
912 for (y = topy; y < boty; y++) {
913
914 // See if we have reached the end of the current left edge, and if so, set
915 // new values for dx_dy and x,u,v
916 if (y == next_break_left) {
917 fix recip_dy;
918
919 // Handle problem of double points. Search until y coord is different. Cannot get
920 // hung in an infinite loop because we know there is a vertex with a lower y coordinate
921 // because in the for loop, we don't scan all spanlines.
922 while (y == f2i(v3d[vlb].y2d)) {
923 vlt = vlb;
924 vlb = prevmod(vlb,t->nv);
925 }
926 next_break_left = f2i(v3d[vlb].y2d);
927
928 dy = f2i(t->verts[vlb].y2d) - f2i(t->verts[vlt].y2d);
929 if (dy < FIX_RECIP_TABLE_SIZE)
930 recip_dy = fix_recip[dy];
931 else
932 recip_dy = F1_0/dy;
933
934 dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dy);
935
936 xleft = v3d[vlt].x2d;
937 uleft = v3d[vlt].u;
938 vleft = v3d[vlt].v;
939 lleft = v3d[vlt].l;
940
941 du_dy_left = compute_du_dy_lin(t,vlt,vlb, recip_dy);
942 dv_dy_left = compute_dv_dy_lin(t,vlt,vlb, recip_dy);
943
944 if (Lighting_enabled) {
945 dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dy);
946 lleft = v3d[vlt].l;
947 }
948 }
949
950 // See if we have reached the end of the current left edge, and if so, set
951 // new values for dx_dy and x. Not necessary to set new values for u,v.
952 if (y == next_break_right) {
953 fix recip_dy;
954
955 while (y == f2i(v3d[vrb].y2d)) {
956 vrt = vrb;
957 vrb = succmod(vrb,t->nv);
958 }
959
960 dy = f2i(t->verts[vrb].y2d) - f2i(t->verts[vrt].y2d);
961 if (dy < FIX_RECIP_TABLE_SIZE)
962 recip_dy = fix_recip[dy];
963 else
964 recip_dy = F1_0/dy;
965
966 next_break_right = f2i(v3d[vrb].y2d);
967 dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dy);
968
969 xright = v3d[vrt].x2d;
970 uright = v3d[vrt].u;
971 vright = v3d[vrt].v;
972
973 du_dy_right = compute_du_dy_lin(t,vrt,vrb, recip_dy);
974 dv_dy_right = compute_dv_dy_lin(t,vrt,vrb, recip_dy);
975
976 if (Lighting_enabled) {
977 dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dy);
978 lright = v3d[vrt].l;
979 }
980 }
981
982 if (Lighting_enabled) {
983 ntmap_scanline_lighted_linear(srcb,y,xleft,xright,uleft,uright,vleft,vright,lleft,lright);
984 lleft += dl_dy_left;
985 lright += dl_dy_right;
986 } else
987 ntmap_scanline_lighted_linear(srcb,y,xleft,xright,uleft,uright,vleft,vright,lleft,lright);
988
989 uleft += du_dy_left;
990 vleft += dv_dy_left;
991
992 uright += du_dy_right;
993 vright += dv_dy_right;
994
995 xleft += dx_dy_left;
996 xright += dx_dy_right;
997
998 }
999
1000 // We can get lleft or lright out of bounds here because we compute dl_dy using fixed point values,
1001 // but we plot an integer number of scanlines, therefore doing an integer number of additions of the delta.
1002
1003 ntmap_scanline_lighted_linear(srcb,y,xleft,xright,uleft,uright,vleft,vright,lleft,lright);
1004 }
1005
1006 // fix DivNum = F1_0*12;
1007
1008 extern void draw_tmap_flat(grs_bitmap *bp,int nverts,g3s_point **vertbuf);
1009
1010 // -------------------------------------------------------------------------------------
1011 // Interface from Matt's data structures to Mike's texture mapper.
1012 // -------------------------------------------------------------------------------------
draw_tmap(grs_bitmap * bp,int nverts,g3s_point ** vertbuf)1013 void draw_tmap(grs_bitmap *bp,int nverts,g3s_point **vertbuf)
1014 {
1015 int i;
1016
1017 // These variables are used in system which renders texture maps which lie on one scanline as a line.
1018 // fix div_numerator;
1019 int lighting_on_save = Lighting_on;
1020
1021 Assert(nverts <= MAX_TMAP_VERTS);
1022
1023
1024 #ifdef USE_MULT_CODE
1025 if ( !divide_table_filled ) fill_divide_table();
1026 #endif
1027
1028 // -- now called from g3_start_frame -- init_interface_vars_to_assembler();
1029
1030 // If no transparency and seg depth is large, render as flat shaded.
1031 if ((Current_seg_depth > Max_linear_depth) && ((bp->bm_flags & 3) == 0)) {
1032 draw_tmap_flat(bp, nverts, vertbuf);
1033 return;
1034 }
1035
1036 if ( bp->bm_flags & BM_FLAG_RLE )
1037 bp = rle_expand_texture( bp ); // Expand if rle'd
1038
1039 Transparency_on = bp->bm_flags & BM_FLAG_TRANSPARENT;
1040 if (bp->bm_flags & BM_FLAG_NO_LIGHTING)
1041 Lighting_on = 0;
1042
1043
1044 // Setup texture map in Tmap1
1045 Tmap1.nv = nverts; // Initialize number of vertices
1046
1047 // div_numerator = DivNum; //f1_0*3;
1048
1049 for (i=0; i<nverts; i++) {
1050 g3ds_vertex *tvp = &Tmap1.verts[i];
1051 g3s_point *vp = vertbuf[i];
1052
1053 tvp->x2d = vp->p3_sx;
1054 tvp->y2d = vp->p3_sy;
1055
1056 // Check for overflow on fixdiv. Will overflow on vp->z <= something small. Allow only as low as 256.
1057 if (vp->p3_z < 256) {
1058 vp->p3_z = 256;
1059 // Int3(); // we would overflow if we divided!
1060 }
1061
1062 tvp->z = fixdiv(F1_0*12, vp->p3_z);
1063 tvp->u = vp->p3_u << 6; //* bp->bm_w;
1064 tvp->v = vp->p3_v << 6; //* bp->bm_h;
1065
1066 Assert(Lighting_on < 3);
1067
1068 if (Lighting_on)
1069 tvp->l = vp->p3_l * NUM_LIGHTING_LEVELS;
1070 }
1071
1072
1073 Lighting_enabled = Lighting_on;
1074
1075 // Now, call my texture mapper.
1076 if (Lighting_on) {
1077 switch (Interpolation_method) { // 0 = choose, 1 = linear, 2 = /8 perspective, 3 = full perspective
1078 case 0: // choose best interpolation
1079 per2_flag = 1;
1080 if (Current_seg_depth > Max_perspective_depth)
1081 ntexture_map_lighted_linear(bp, &Tmap1);
1082 else
1083 ntexture_map_lighted(bp, &Tmap1);
1084 break;
1085 case 1: // linear interpolation
1086 per2_flag = 1;
1087 ntexture_map_lighted_linear(bp, &Tmap1);
1088 break;
1089 case 2: // perspective every 8th pixel interpolation
1090 per2_flag = 1;
1091 ntexture_map_lighted(bp, &Tmap1);
1092 break;
1093 case 3: // perspective every pixel interpolation
1094 per2_flag = 0; // this hack means do divide every pixel
1095 ntexture_map_lighted(bp, &Tmap1);
1096 break;
1097 default:
1098 Assert(0); // Illegal value for Interpolation_method, must be 0,1,2,3
1099 }
1100 } else {
1101 switch (Interpolation_method) { // 0 = choose, 1 = linear, 2 = /8 perspective, 3 = full perspective
1102 case 0: // choose best interpolation
1103 per2_flag = 1;
1104 if (Current_seg_depth > Max_perspective_depth)
1105 ntexture_map_lighted_linear(bp, &Tmap1);
1106 else
1107 ntexture_map_lighted(bp, &Tmap1);
1108 break;
1109 case 1: // linear interpolation
1110 per2_flag = 1;
1111 ntexture_map_lighted_linear(bp, &Tmap1);
1112 break;
1113 case 2: // perspective every 8th pixel interpolation
1114 per2_flag = 1;
1115 ntexture_map_lighted(bp, &Tmap1);
1116 break;
1117 case 3: // perspective every pixel interpolation
1118 per2_flag = 0; // this hack means do divide every pixel
1119 ntexture_map_lighted(bp, &Tmap1);
1120 break;
1121 default:
1122 Assert(0); // Illegal value for Interpolation_method, must be 0,1,2,3
1123 }
1124 }
1125
1126 Lighting_on = lighting_on_save;
1127
1128 }
1129