]> icculus.org git repositories - taylor/freespace2.git/blob - src/render/3ddraw.cpp
More stuff compiles
[taylor/freespace2.git] / src / render / 3ddraw.cpp
1 /*
2  * $Logfile: /Freespace2/code/Render/3ddraw.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * 3D rendering primitives
8  *
9  * $Log$
10  * Revision 1.2  2002/05/03 13:34:33  theoddone33
11  * More stuff compiles
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:10  root
14  * Initial import.
15  *
16  * 
17  * 18    9/06/99 3:23p Andsager
18  * Make fireball and weapon expl ani LOD choice look at resolution of the
19  * bitmap
20  * 
21  * 17    8/27/99 9:07p Dave
22  * LOD explosions. Improved beam weapon accuracy.
23  * 
24  * 16    7/29/99 12:05a Dave
25  * Nebula speed optimizations.
26  * 
27  * 15    7/13/99 1:16p Dave
28  * 32 bit support. Whee!
29  * 
30  * 14    7/02/99 3:05p Anoop
31  * Oops. Fixed g3_draw_2d_poly() so that it properly handles poly bitmap
32  * and LFB bitmap calls.
33  * 
34  * 13    6/29/99 10:35a Dave
35  * Interface polygon bitmaps! Whee!
36  * 
37  * 12    6/22/99 7:03p Dave
38  * New detail options screen.
39  * 
40  * 11    6/16/99 4:06p Dave
41  * New pilot info popup. Added new draw-bitmap-as-poly function.
42  * 
43  * 10    6/08/99 5:17p Dave
44  * Fixed up perspective bitmap drawing.
45  * 
46  * 9     6/03/99 6:37p Dave
47  * More TNT fun. Made perspective bitmaps more flexible.
48  * 
49  * 8     5/28/99 1:58p Dave
50  * Fixed problem with overwriting bank value drawing perspective bitmaps.
51  * 
52  * 7     5/28/99 1:45p Dave
53  * Fixed up perspective bitmap drawing.
54  * 
55  * 6     5/24/99 5:45p Dave
56  * Added detail levels to the nebula, with a decent speedup. Split nebula
57  * lightning into its own section.
58  * 
59  * 5     4/07/99 6:22p Dave
60  * Fred and Freespace support for multiple background bitmaps and suns.
61  * Fixed link errors on all subprojects. Moved encrypt_init() to
62  * cfile_init() and lcl_init(), since its safe to call twice.
63  * 
64  * 4     3/09/99 6:24p Dave
65  * More work on object update revamping. Identified several sources of
66  * unnecessary bandwidth.
67  * 
68  * 3     2/11/99 3:08p Dave
69  * PXO refresh button. Very preliminary squad war support.
70  * 
71  * 2     10/07/98 10:53a Dave
72  * Initial checkin.
73  * 
74  * 1     10/07/98 10:51a Dave
75  * 
76  * 37    3/22/98 2:34p John
77  * If alpha effects is on lowest detail level, draw rotated bitmaps as
78  * scaled bitmaps.
79  * 
80  * 36    3/16/98 4:51p John
81  * Added low-level code to clip all polygons against an arbritary plane.
82  * Took out all old model_interp_zclip and used this new method instead.  
83  * 
84  * 35    3/04/98 9:29a John
85  * Made rotated bitmaps force all sw's to the same value after clipping.
86  * 
87  * 34    3/03/98 6:59p John
88  * 
89  * 33    1/26/98 5:12p John
90  * Added in code for Pentium Pro specific optimizations. Speed up
91  * zbuffered correct tmapper about 35%.   Speed up non-zbuffered scalers
92  * by about 25%.
93  * 
94  * 32    1/15/98 2:16p John
95  * Made bitmaps zbuffer at center minus radius.
96  * Made fireballs sort after objects they are around.
97  * 
98  * 31    1/06/98 2:11p John
99  * Made g3_draw_rotated bitmap draw the same orientation as g3_draw_bitmap
100  * (orient=0) and made it be the same size (it was 2x too big before).
101  * 
102  * 30    12/30/97 6:44p John
103  * Made g3_Draw_bitmap functions account for aspect of bitmap.
104  * 
105  * 29    12/04/97 12:09p John
106  * Made glows use scaler instead of tmapper so they don't rotate.  Had to
107  * add a zbuffered scaler.
108  * 
109  * 28    12/02/97 4:00p John
110  * Added first rev of thruster glow, along with variable levels of
111  * translucency, which retquired some restructing of palman.
112  * 
113  * 27    11/29/97 2:05p John
114  * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2,
115  * like they used to incorrectly assume.   Added code to model to read in
116  * thruster radius's.
117  * 
118  * 26    10/20/97 4:49p John
119  * added weapon trails.
120  * added facing bitmap code to 3d lib.
121  * 
122  * 25    10/03/97 9:10a John
123  * added better antialiased line drawer
124  * 
125  * 24    9/09/97 3:39p Sandeep
126  * warning level 4 bugs
127  * 
128  * 23    7/11/97 11:54a John
129  * added rotated 3d bitmaps.
130  * 
131  * 22    5/07/97 2:59p John
132  * Initial rev of D3D texturing.
133  * 
134  * 21    4/29/97 9:55a John
135  * 
136  * 20    3/10/97 2:25p John
137  * Made pofview zbuffer.   Made textest work with new model code.  Took
138  * out some unnecessary Asserts in the 3d clipper.
139  * 
140  * 
141  * 19    3/06/97 5:36p Mike
142  * Change vec_normalize_safe() back to vec_normalize().
143  * Spruce up docking a bit.
144  * 
145  * 18    3/06/97 10:56a Mike
146  * Write error checking version of vm_vec_normalize().
147  * Fix resultant problems.
148  * 
149  * 17    2/26/97 5:24p John
150  * Added g3_draw_sphere_ez
151  * 
152  * 16    2/17/97 5:18p John
153  * Added a bunch of RCS headers to a bunch of old files that don't have
154  * them.
155  *
156  * $NoKeywords: $
157  */
158
159 #include "3dinternal.h"
160 #include "tmapper.h"
161 #include "scaler.h"
162 #include "2d.h"
163 #include "floating.h"
164 #include "physics.h"            // For Physics_viewer_bank for g3_draw_rotated_bitmap
165 #include "bmpman.h"
166 #include "systemvars.h"
167 #include "alphacolors.h"
168
169 #include "key.h"
170
171 //deal with a clipped line
172 int must_clip_line(vertex *p0,vertex *p1,ubyte codes_or, uint flags)
173 {
174         int ret = 0;
175         
176         clip_line(&p0,&p1,codes_or, flags);
177         
178         if (p0->codes & p1->codes) goto free_points;
179
180         codes_or = (unsigned char)(p0->codes | p1->codes);
181
182         if (codes_or & CC_BEHIND) goto free_points;
183
184         if (!(p0->flags&PF_PROJECTED))
185                 g3_project_vertex(p0);
186
187         if (p0->flags&PF_OVERFLOW) goto free_points;
188
189         if (!(p1->flags&PF_PROJECTED))
190                 g3_project_vertex(p1);
191
192         if (p1->flags&PF_OVERFLOW) goto free_points;
193         
194         //gr_line(fl2i(p0->sx),fl2i(p0->sy),fl2i(p1->sx),fl2i(p1->sy));
195         //      gr_line_float(p0->sx,p0->sy,p1->sx,p1->sy);
196
197         gr_aaline( p0, p1 );
198
199         ret = 1;
200
201         //frees temp points
202 free_points:
203
204         if (p0->flags & PF_TEMP_POINT)
205                 free_temp_point(p0);
206
207         if (p1->flags & PF_TEMP_POINT)
208                 free_temp_point(p1);
209
210         return ret;
211 }
212
213 //draws a line. takes two points.  returns true if drew
214 int g3_draw_line(vertex *p0,vertex *p1)
215 {
216         ubyte codes_or;
217
218         Assert( G3_count == 1 );
219
220         if (p0->codes & p1->codes)
221                 return 0;
222
223         codes_or = (unsigned char)(p0->codes | p1->codes);
224
225         if (codes_or & CC_BEHIND)
226                 return must_clip_line(p0,p1,codes_or,0);
227
228         if (!(p0->flags&PF_PROJECTED))
229                 g3_project_vertex(p0);
230
231         if (p0->flags&PF_OVERFLOW) 
232 //              return 1;
233                 return must_clip_line(p0,p1,codes_or,0);
234
235
236         if (!(p1->flags&PF_PROJECTED))
237                 g3_project_vertex(p1);
238
239         if (p1->flags&PF_OVERFLOW)
240 //              return 1;
241                 return must_clip_line(p0,p1,codes_or,0);
242
243         
244 //      gr_line(fl2i(p0->sx),fl2i(p0->sy),fl2i(p1->sx),fl2i(p1->sy));
245 //      gr_line_float(p0->sx,p0->sy,p1->sx,p1->sy);
246
247         gr_aaline( p0, p1 );
248
249         return 0;
250 }
251
252 //returns true if a plane is facing the viewer. takes the unrotated surface
253 //normal of the plane, and a point on it.  The normal need not be normalized
254 int g3_check_normal_facing(vector *v,vector *norm)
255 {
256         vector tempv;
257
258         Assert( G3_count == 1 );
259
260         vm_vec_sub(&tempv,&View_position,v);
261
262         return (vm_vec_dot(&tempv,norm) > 0.0f);
263 }
264
265 int do_facing_check(vector *norm,vertex **vertlist,vector *p)
266 {
267         Assert( G3_count == 1 );
268
269         if (norm) {             //have normal
270
271                 Assert(norm->x || norm->y || norm->z);
272
273                 return g3_check_normal_facing(p,norm);
274         }
275         else {  //normal not specified, so must compute
276
277                 vector tempv;
278
279                 //get three points (rotated) and compute normal
280
281                 vm_vec_perp(&tempv,(vector *)&vertlist[0]->x,(vector *)&vertlist[1]->x,(vector *)&vertlist[2]->x);
282
283                 return (vm_vec_dot(&tempv,(vector *)&vertlist[1]->x ) < 0.0);
284         }
285 }
286
287 //like g3_draw_poly(), but checks to see if facing.  If surface normal is
288 //NULL, this routine must compute it, which will be slow.  It is better to
289 //pre-compute the normal, and pass it to this function.  When the normal
290 //is passed, this function works like g3_check_normal_facing() plus
291 //g3_draw_poly().
292 //returns -1 if not facing, 1 if off screen, 0 if drew
293 int g3_draw_poly_if_facing(int nv,vertex **pointlist,uint tmap_flags,vector *norm,vector *pnt)
294 {
295         Assert( G3_count == 1 );
296
297         if (do_facing_check(norm,pointlist,pnt))
298                 return g3_draw_poly(nv,pointlist,tmap_flags);
299         else
300                 return 255;
301 }
302
303 //draw a polygon.
304 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
305 //returns 1 if off screen, 0 if drew
306 int g3_draw_poly(int nv,vertex **pointlist,uint tmap_flags)
307 {
308         int i;
309         vertex **bufptr;
310         ccodes cc;
311
312         Assert( G3_count == 1 );
313
314         cc.vor = 0; cc.vand = 0xff;
315
316         bufptr = Vbuf0;
317
318         for (i=0;i<nv;i++) {
319                 vertex *p;
320
321                 p = bufptr[i] = pointlist[i];
322
323                 cc.vand &= p->codes;
324                 cc.vor  |= p->codes;
325         }
326
327         if (cc.vand)
328                 return 1;       //all points off screen
329
330         if (cc.vor)     {
331                 Assert( G3_count == 1 );
332
333                 bufptr = clip_polygon(Vbuf0,Vbuf1,&nv,&cc,tmap_flags);
334
335                 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
336
337                         for (i=0;i<nv;i++) {
338                                 vertex *p = bufptr[i];
339
340                                 if (!(p->flags&PF_PROJECTED))
341                                         g3_project_vertex(p);
342                 
343                                 if (p->flags&PF_OVERFLOW) {
344                                         //Int3();               //should not overflow after clip
345                                         //printf( "overflow in must_clip_tmap_face\n" );
346                                         goto free_points;
347                                 }                               
348                         }
349
350                         gr_tmapper( nv, bufptr, tmap_flags );
351                 }
352
353 free_points:
354                 ;
355
356                 for (i=0;i<nv;i++)
357                         if (bufptr[i]->flags & PF_TEMP_POINT)
358                                 free_temp_point(bufptr[i]);
359
360         } else {
361                 //now make list of 2d coords (& check for overflow)
362
363                 for (i=0;i<nv;i++) {
364                         vertex *p = bufptr[i];
365
366                         if (!(p->flags&PF_PROJECTED))
367                                 g3_project_vertex(p);
368
369                         if (p->flags&PF_OVERFLOW) {
370                                 //Int3();               //should not overflow after clip
371                                 //printf( "3d: Point overflowed, but flags say OK!\n" );
372                                 return 255;
373                         }
374
375                 }
376
377                 gr_tmapper( nv, bufptr, tmap_flags );
378         }
379         return 0;       //say it drew
380 }
381
382
383
384 // Draw a polygon.  Same as g3_draw_poly, but it bashes sw to a constant value
385 // for all vertexes.  Needs to be done after clipping to get them all.
386 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
387 //returns 1 if off screen, 0 if drew
388 int g3_draw_poly_constant_sw(int nv,vertex **pointlist,uint tmap_flags, float constant_sw)
389 {
390         int i;
391         vertex **bufptr;
392         ccodes cc;
393
394         Assert( G3_count == 1 );
395
396         cc.vor = 0; cc.vand = 0xff;
397
398         bufptr = Vbuf0;
399
400         for (i=0;i<nv;i++) {
401                 vertex *p;
402
403                 p = bufptr[i] = pointlist[i];
404
405                 cc.vand &= p->codes;
406                 cc.vor  |= p->codes;
407         }
408
409         if (cc.vand)
410                 return 1;       //all points off screen
411
412         if (cc.vor)     {
413                 Assert( G3_count == 1 );
414
415                 bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags);
416
417                 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
418
419                         for (i=0;i<nv;i++) {
420                                 vertex *p = bufptr[i];
421
422                                 if (!(p->flags&PF_PROJECTED))
423                                         g3_project_vertex(p);
424                 
425                                 if (p->flags&PF_OVERFLOW) {
426                                         //Int3();               //should not overflow after clip
427                                         //printf( "overflow in must_clip_tmap_face\n" );
428                                         goto free_points;
429                                 }
430
431                                 p->sw = constant_sw;
432                         }
433
434                         gr_tmapper( nv, bufptr, tmap_flags );
435
436                         // draw lines connecting the faces
437                         /*
438                         gr_set_color_fast(&Color_bright_green);
439                         for(i=0; i<nv-1; i++){
440                                 g3_draw_line(bufptr[i], bufptr[i+1]);
441                         } 
442                         g3_draw_line(bufptr[0], bufptr[i]);
443                         */
444                 }
445
446 free_points:
447                 ;
448
449                 for (i=0;i<nv;i++){
450                         if (bufptr[i]->flags & PF_TEMP_POINT){
451                                 free_temp_point(bufptr[i]);
452                         }
453                 }
454         } else {
455                 //now make list of 2d coords (& check for overflow)
456
457                 for (i=0;i<nv;i++) {
458                         vertex *p = bufptr[i];
459
460                         if (!(p->flags&PF_PROJECTED))
461                                 g3_project_vertex(p);
462
463                         if (p->flags&PF_OVERFLOW) {
464                                 //Int3();               //should not overflow after clip
465                                 //printf( "3d: Point overflowed, but flags say OK!\n" );
466                                 return 255;
467                         }
468
469                         p->sw = constant_sw;
470                 }
471
472                 gr_tmapper( nv, bufptr, tmap_flags );
473
474                 // draw lines connecting the faces
475                 /*
476                 gr_set_color_fast(&Color_bright_green);
477                 for(i=0; i<nv-1; i++){
478                         g3_draw_line(bufptr[i], bufptr[i+1]);
479                 } 
480                 g3_draw_line(bufptr[0], bufptr[i]);
481                 */
482         }
483         return 0;       //say it drew
484 }
485
486 //draw a sortof sphere - i.e., the 2d radius is proportional to the 3d
487 //radius, but not to the distance from the eye
488 int g3_draw_sphere(vertex *pnt,float rad)
489 {
490         Assert( G3_count == 1 );
491
492         if (! (pnt->codes & CC_BEHIND)) {
493
494                 if (! (pnt->flags & PF_PROJECTED))
495                         g3_project_vertex(pnt);
496
497                 if (! (pnt->codes & PF_OVERFLOW)) {
498                         float r2,t;
499
500                         r2 = rad*Matrix_scale.x;
501
502                         t=r2*Canv_w2/pnt->z;
503
504                         gr_circle(fl2i(pnt->sx),fl2i(pnt->sy),fl2i(t*2.0f));
505                 }
506         }
507
508         return 0;
509 }
510
511 int g3_draw_sphere_ez(vector *pnt,float rad)
512 {
513         vertex pt;
514         ubyte flags;
515
516         Assert( G3_count == 1 );
517
518         flags = g3_rotate_vertex(&pt,pnt);
519
520         if (flags == 0) {
521
522                 g3_project_vertex(&pt);
523
524                 if (!(pt.flags & PF_OVERFLOW))  {
525
526                         g3_draw_sphere( &pt, rad );
527                 }
528         }
529
530         return 0;
531 }
532
533
534 //draws a bitmap with the specified 3d width & height 
535 //returns 1 if off screen, 0 if drew
536 // Orient
537 int g3_draw_bitmap(vertex *pnt,int orient, float rad,uint tmap_flags)
538 {
539         vertex va, vb;
540         float t,w,h;
541         float width, height;
542
543         if ( tmap_flags & TMAP_FLAG_TEXTURED )  {
544                 int bw, bh;
545
546                 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
547
548                 if ( bw < bh )  {
549                         width = rad*2.0f;
550                         height = width*i2fl(bh)/i2fl(bw);
551                 } else if ( bw > bh )   {
552                         height = rad*2.0f;
553                         width = height*i2fl(bw)/i2fl(bh);
554                 } else {
555                         width = height = rad*2.0f;
556                 }               
557         } else {
558                 width = height = rad*2.0f;
559         }
560
561         Assert( G3_count == 1 );
562
563         if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) ) 
564                 return 1;
565
566         if (!(pnt->flags&PF_PROJECTED))
567                 g3_project_vertex(pnt);
568
569         if (pnt->flags & PF_OVERFLOW)
570                 return 1;
571
572         t = (width*Canv_w2)/pnt->z;
573         w = t*Matrix_scale.x;
574
575         t = (height*Canv_h2)/pnt->z;
576         h = t*Matrix_scale.y;
577
578         float z,sw;
579         z = pnt->z - rad/2.0f;
580         if ( z <= 0.0f ) {
581                 z = 0.0f;
582                 sw = 0.0f;
583         } else {
584                 sw = 1.0f / z;
585         }
586
587         va.sx = pnt->sx - w/2.0f;
588         va.sy = pnt->sy - h/2.0f;
589         va.sw = sw;
590         va.z = z;
591
592         vb.sx = va.sx + w;
593         vb.sy = va.sy + h;
594         vb.sw = sw;
595         vb.z = z;
596
597         if ( orient & 1 )       {
598                 va.u = 1.0f;
599                 vb.u = 0.0f;
600         } else {
601                 va.u = 0.0f;
602                 vb.u = 1.0f;
603         }
604
605         if ( orient & 2 )       {
606                 va.v = 1.0f;
607                 vb.v = 0.0f;
608         } else {
609                 va.v = 0.0f;
610                 vb.v = 1.0f;
611         }
612
613         gr_scaler(&va, &vb);
614
615         return 0;
616 }
617
618 // get bitmap dims onscreen as if g3_draw_bitmap() had been called
619 int g3_get_bitmap_dims(int bitmap, vertex *pnt, float rad, int *x, int *y, int *w, int *h, int *size)
620 {       
621         float t;
622         float width, height;
623         
624         int bw, bh;
625
626         bm_get_info( bitmap, &bw, &bh, NULL );
627
628         if ( bw < bh )  {
629                 width = rad*2.0f;
630                 height = width*i2fl(bh)/i2fl(bw);
631         } else if ( bw > bh )   {
632                 height = rad*2.0f;
633                 width = height*i2fl(bw)/i2fl(bh);
634         } else {
635                 width = height = rad*2.0f;
636         }                       
637
638         Assert( G3_count == 1 );
639
640         if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) ) {
641                 return 1;
642         }
643
644         if (!(pnt->flags&PF_PROJECTED)){
645                 g3_project_vertex(pnt);
646         }
647
648         if (pnt->flags & PF_OVERFLOW){
649                 return 1;
650         }
651
652         t = (width*Canv_w2)/pnt->z;
653         *w = (int)(t*Matrix_scale.x);
654
655         t = (height*Canv_h2)/pnt->z;
656         *h = (int)(t*Matrix_scale.y);   
657
658         *x = (int)(pnt->sx - *w/2.0f);
659         *y = (int)(pnt->sy - *h/2.0f);  
660
661         *size = max(bw, bh);
662
663         return 0;
664 }
665
666 //draws a bitmap with the specified 3d width & height 
667 //returns 1 if off screen, 0 if drew
668 int g3_draw_rotated_bitmap(vertex *pnt,float angle, float rad,uint tmap_flags)
669 {
670         vertex v[4];
671         vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] };
672         float sa, ca;
673         int i;
674
675         /*
676         if ( !Detail.alpha_effects )    {
677                 int ang;
678                 if ( angle < PI/2 )     {
679                         ang = 0;
680                 } else if ( angle < PI )        {
681                         ang = 1;
682                 } else if ( angle < PI+PI/2 )   {
683                         ang = 2;
684                 } else {
685                         ang = 3;
686                 }
687                 return g3_draw_bitmap( pnt, ang, rad, tmap_flags );
688         }
689         */
690
691         Assert( G3_count == 1 );
692
693         angle+=Physics_viewer_bank;
694         if ( angle < 0.0f )
695                 angle += PI2;
696         else if ( angle > PI2 )
697                 angle -= PI2;
698 //      angle = 0.0f;
699                         
700         sa = (float)sin(angle);
701         ca = (float)cos(angle);
702
703         float width, height;
704
705         if ( tmap_flags & TMAP_FLAG_TEXTURED )  {
706                 int bw, bh;
707
708                 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
709
710                 if ( bw < bh )  {
711                         width = rad;
712                         height = width*i2fl(bh)/i2fl(bw);
713                 } else if ( bw > bh )   {
714                         height = rad;
715                         width = height*i2fl(bw)/i2fl(bh);
716                 } else {
717                         width = height = rad;
718                 }               
719         } else {
720                 width = height = rad;
721         }
722
723
724         v[0].x = (-width*ca + height*sa)*Matrix_scale.x + pnt->x;
725         v[0].y = (-width*sa - height*ca)*Matrix_scale.y + pnt->y;
726         v[0].z = pnt->z;
727         v[0].sw = 0.0f;
728         v[0].u = 0.0f;
729         v[0].v = 1.0f;
730
731         v[1].x = (width*ca + height*sa)*Matrix_scale.x + pnt->x;
732         v[1].y = (width*sa - height*ca)*Matrix_scale.y + pnt->y;
733         v[1].z = pnt->z;
734         v[1].sw = 0.0f;
735         v[1].u = 1.0f;
736         v[1].v = 1.0f;
737
738         v[2].x = (width*ca - height*sa)*Matrix_scale.x + pnt->x;
739         v[2].y = (width*sa + height*ca)*Matrix_scale.y + pnt->y;
740         v[2].z = pnt->z;
741         v[2].sw = 0.0f;
742         v[2].u = 1.0f;
743         v[2].v = 0.0f;
744
745         v[3].x = (-width*ca - height*sa)*Matrix_scale.x + pnt->x;
746         v[3].y = (-width*sa + height*ca)*Matrix_scale.y + pnt->y;
747         v[3].z = pnt->z;
748         v[3].sw = 0.0f;
749         v[3].u = 0.0f;
750         v[3].v = 0.0f;
751
752         ubyte codes_and=0xff;
753
754         float sw,z;
755         z = pnt->z - rad / 4.0f;
756         if ( z < 0.0f ) z = 0.0f;
757         sw = 1.0f / z;
758
759         for (i=0; i<4; i++ )    {
760                 //now code the four points
761                 codes_and &= g3_code_vertex(&v[i]);
762                 v[i].flags = 0;         // mark as not yet projected
763                 //g3_project_vertex(&v[i]);
764         }
765
766         if (codes_and)
767                 return 1;               //1 means off screen
768
769         // clip and draw it
770         g3_draw_poly_constant_sw(4, vertlist, tmap_flags, sw ); 
771         
772         return 0;
773 }
774
775 #define TRIANGLE_AREA(_p, _q, _r)       do { vector a, b, cross; a.x = _q->x - _p->x; a.y = _q->y - _p->y; a.z = 0.0f; b.x = _r->x - _p->x; b.y = _r->y - _p->y; b.z = 0.0f; vm_vec_crossprod(&cross, &a, &b); total_area += vm_vec_mag(&cross) * 0.5f; } while(0);
776 float g3_get_poly_area(int nv, vertex **pointlist)
777 {
778         int idx;
779         float total_area = 0.0f;        
780
781         // each triangle
782         for(idx=1; idx<nv-1; idx++){
783                 TRIANGLE_AREA(pointlist[0], pointlist[idx], pointlist[idx+1]);
784         }
785
786         // done
787         return total_area;
788 }
789
790 // Draw a polygon.  Same as g3_draw_poly, but it bashes sw to a constant value
791 // for all vertexes.  Needs to be done after clipping to get them all.
792 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
793 //returns 1 if off screen, 0 if drew
794 float g3_draw_poly_constant_sw_area(int nv, vertex **pointlist, uint tmap_flags, float constant_sw, float area)
795 {
796         int i;
797         vertex **bufptr;
798         ccodes cc;
799         float p_area = 0.0f;
800
801         Assert( G3_count == 1 );
802
803         cc.vor = 0; cc.vand = 0xff;
804
805         bufptr = Vbuf0;
806
807         for (i=0;i<nv;i++) {
808                 vertex *p;
809
810                 p = bufptr[i] = pointlist[i];
811
812                 cc.vand &= p->codes;
813                 cc.vor  |= p->codes;
814         }
815
816         if (cc.vand){
817                 return 0.0f;    //all points off screen
818         }
819
820         if (cc.vor)     {
821                 Assert( G3_count == 1 );
822
823                 bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags);
824
825                 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
826
827                         for (i=0;i<nv;i++) {
828                                 vertex *p = bufptr[i];
829
830                                 if (!(p->flags&PF_PROJECTED))
831                                         g3_project_vertex(p);
832                 
833                                 if (p->flags&PF_OVERFLOW) {
834                                         //Int3();               //should not overflow after clip
835                                         //printf( "overflow in must_clip_tmap_face\n" );
836                                         goto free_points;
837                                 }
838
839                                 p->sw = constant_sw;
840                         }
841
842                         // check area
843                         p_area = g3_get_poly_area(nv, bufptr);
844                         if(p_area > area){
845                                 return 0.0f;
846                         }
847
848                         gr_tmapper( nv, bufptr, tmap_flags );                   
849                 }
850
851 free_points:
852                 ;
853
854                 for (i=0;i<nv;i++){
855                         if (bufptr[i]->flags & PF_TEMP_POINT){
856                                 free_temp_point(bufptr[i]);
857                         }
858                 }
859         } else {
860                 //now make list of 2d coords (& check for overflow)
861
862                 for (i=0;i<nv;i++) {
863                         vertex *p = bufptr[i];
864
865                         if (!(p->flags&PF_PROJECTED))
866                                 g3_project_vertex(p);
867
868                         if (p->flags&PF_OVERFLOW) {                             
869                                 return 0.0f;
870                         }
871
872                         p->sw = constant_sw;
873                 }
874
875                 // check area
876                 p_area = g3_get_poly_area(nv, bufptr);
877                 if(p_area > area){
878                         return 0.0f;
879                 }               
880
881                 gr_tmapper( nv, bufptr, tmap_flags );           
882         }
883
884         // how much area we drew
885         return p_area;
886 }
887
888
889 //draws a bitmap with the specified 3d width & height 
890 //returns 1 if off screen, 0 if drew
891 float g3_draw_rotated_bitmap_area(vertex *pnt,float angle, float rad,uint tmap_flags, float area)
892 {
893         vertex v[4];
894         vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] };
895         float sa, ca;
896         int i;  
897
898         Assert( G3_count == 1 );
899
900         angle+=Physics_viewer_bank;
901         if ( angle < 0.0f ){
902                 angle += PI2;
903         } else if ( angle > PI2 ) {
904                 angle -= PI2;
905         }
906                         
907         sa = (float)sin(angle);
908         ca = (float)cos(angle);
909
910         float width, height;
911
912         if ( tmap_flags & TMAP_FLAG_TEXTURED )  {
913                 int bw, bh;
914
915                 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
916
917                 if ( bw < bh )  {
918                         width = rad;
919                         height = width*i2fl(bh)/i2fl(bw);
920                 } else if ( bw > bh )   {
921                         height = rad;
922                         width = height*i2fl(bw)/i2fl(bh);
923                 } else {
924                         width = height = rad;
925                 }               
926         } else {
927                 width = height = rad;
928         }
929
930
931         v[0].x = (-width*ca + height*sa)*Matrix_scale.x + pnt->x;
932         v[0].y = (-width*sa - height*ca)*Matrix_scale.y + pnt->y;
933         v[0].z = pnt->z;
934         v[0].sw = 0.0f;
935         v[0].u = 0.0f;
936         v[0].v = 1.0f;
937
938         v[1].x = (width*ca + height*sa)*Matrix_scale.x + pnt->x;
939         v[1].y = (width*sa - height*ca)*Matrix_scale.y + pnt->y;
940         v[1].z = pnt->z;
941         v[1].sw = 0.0f;
942         v[1].u = 1.0f;
943         v[1].v = 1.0f;
944
945         v[2].x = (width*ca - height*sa)*Matrix_scale.x + pnt->x;
946         v[2].y = (width*sa + height*ca)*Matrix_scale.y + pnt->y;
947         v[2].z = pnt->z;
948         v[2].sw = 0.0f;
949         v[2].u = 1.0f;
950         v[2].v = 0.0f;
951
952         v[3].x = (-width*ca - height*sa)*Matrix_scale.x + pnt->x;
953         v[3].y = (-width*sa + height*ca)*Matrix_scale.y + pnt->y;
954         v[3].z = pnt->z;
955         v[3].sw = 0.0f;
956         v[3].u = 0.0f;
957         v[3].v = 0.0f;
958
959         ubyte codes_and=0xff;
960
961         float sw,z;
962         z = pnt->z - rad / 4.0f;
963         if ( z < 0.0f ) z = 0.0f;
964         sw = 1.0f / z;
965
966         for (i=0; i<4; i++ )    {
967                 //now code the four points
968                 codes_and &= g3_code_vertex(&v[i]);
969                 v[i].flags = 0;         // mark as not yet projected
970                 //g3_project_vertex(&v[i]);
971         }
972
973         if (codes_and){
974                 return 0.0f;
975         }
976
977         // clip and draw it
978         return g3_draw_poly_constant_sw_area(4, vertlist, tmap_flags, sw, area );               
979 }
980
981
982
983 #include "2d.h"
984 typedef struct horz_pt {
985         float x, y;
986         int edge;
987 } horz_pt;
988
989 //draws a horizon. takes eax=sky_color, edx=ground_color
990 void g3_draw_horizon_line()
991 {
992         //int sky_color,int ground_color
993         int s1, s2;
994         int cpnt;
995         horz_pt horz_pts[4];            // 0 = left, 1 = right
996 //      int top_color, bot_color;
997 //      int color_swap;         //flag for if we swapped
998 //      int sky_ground_flag;    //0=both, 1=all sky, -1=all gnd
999
1000         vector horizon_vec;
1001         
1002         float up_right, down_right,down_left,up_left;
1003
1004 //      color_swap = 0;         //assume no swap
1005 //      sky_ground_flag = 0;    //assume both
1006
1007 //      if ( View_matrix.uvec.y < 0.0f )
1008 //              color_swap = 1;
1009 //      else if ( View_matrix.uvec.y == 0.0f )  {
1010 //              if ( View_matrix.uvec.x > 0.0f )
1011 //                      color_swap = 1;
1012 //      }
1013
1014 //      if (color_swap) {
1015 //              top_color  = ground_color;
1016 //              bot_color = sky_color;
1017 //      } else {
1018 //              top_color  = sky_color;
1019 //              bot_color = ground_color;
1020 //      }
1021
1022         Assert( G3_count == 1 );
1023
1024
1025         //compute horizon_vector
1026         
1027         horizon_vec.x = Unscaled_matrix.rvec.y*Matrix_scale.y*Matrix_scale.z;
1028         horizon_vec.y = Unscaled_matrix.uvec.y*Matrix_scale.x*Matrix_scale.z;
1029         horizon_vec.z = Unscaled_matrix.fvec.y*Matrix_scale.x*Matrix_scale.y;
1030
1031         // now compute values & flag for 4 corners.
1032         up_right = horizon_vec.x + horizon_vec.y + horizon_vec.z;
1033         down_right = horizon_vec.x - horizon_vec.y + horizon_vec.z;
1034         down_left = -horizon_vec.x - horizon_vec.y + horizon_vec.z;
1035         up_left = -horizon_vec.x + horizon_vec.y + horizon_vec.z;
1036
1037         //check flags for all sky or all ground.
1038         if ( (up_right<0.0f)&&(down_right<0.0f)&&(down_left<0.0f)&&(up_left<0.0f) )     {
1039 //              mprintf(( "All ground.\n" ));
1040                 return;
1041         }
1042
1043         if ( (up_right>0.0f)&&(down_right>0.0f)&&(down_left>0.0f)&&(up_left>0.0f) )     {
1044 //              mprintf(( "All sky.\n" ));
1045                 return;
1046         }
1047
1048 //mprintf(( "Horizon vec = %.4f, %.4f, %.4f\n", horizon_vec.x, horizon_vec.y, horizon_vec.z ));
1049 //mprintf(( "%.4f, %.4f, %.4f, %.4f\n", up_right, down_right, down_left, up_left ));
1050
1051         
1052 //      mprintf(( "u: %.4f %.4f %.4f  c: %.4f %.4f %.4f %.4f\n",Unscaled_matrix.uvec.y,Unscaled_matrix.uvec.z,Unscaled_matrix.uvec.x,up_left,up_right,down_right,down_left ));
1053         // check for intesection with each of four edges & compute horizon line
1054         cpnt = 0;
1055         
1056         // check intersection with left edge
1057         s1 = up_left > 0.0f;
1058         s2 = down_left > 0.0f;
1059         if ( s1 != s2 ) {
1060                 horz_pts[cpnt].x = 0.0f;
1061                 horz_pts[cpnt].y = fl_abs(up_left * Canv_h2 / horizon_vec.y);
1062                 horz_pts[cpnt].edge = 0;
1063                 cpnt++;
1064         }
1065
1066         // check intersection with top edge
1067         s1 = up_left > 0.0f;
1068         s2 = up_right > 0.0f;
1069         if ( s1 != s2 ) {
1070                 horz_pts[cpnt].x = fl_abs(up_left * Canv_w2 / horizon_vec.x);
1071                 horz_pts[cpnt].y = 0.0f;
1072                 horz_pts[cpnt].edge = 1;
1073                 cpnt++;
1074         }
1075
1076         
1077         // check intersection with right edge
1078         s1 = up_right > 0.0f;
1079         s2 = down_right > 0.0f;
1080         if ( s1 != s2 ) {
1081                 horz_pts[cpnt].x = i2fl(Canvas_width)-1;
1082                 horz_pts[cpnt].y = fl_abs(up_right * Canv_h2 / horizon_vec.y);
1083                 horz_pts[cpnt].edge = 2;
1084                 cpnt++;
1085         }
1086         
1087         //check intersection with bottom edge
1088         s1 = down_right > 0.0f;
1089         s2 = down_left > 0.0f;
1090         if ( s1 != s2 ) {
1091                 horz_pts[cpnt].x = fl_abs(down_left * Canv_w2 / horizon_vec.x);
1092                 horz_pts[cpnt].y = i2fl(Canvas_height)-1;
1093                 horz_pts[cpnt].edge = 3;
1094                 cpnt++;
1095         }
1096
1097         if ( cpnt != 2 )        {
1098                 mprintf(( "HORZ: Wrong number of points (%d)\n", cpnt ));
1099                 return;
1100         }
1101
1102         //make sure first edge is left
1103
1104         if ( horz_pts[0].x > horz_pts[1].x )    {
1105                 horz_pt tmp;
1106                 tmp = horz_pts[0];
1107                 horz_pts[0] = horz_pts[1];
1108                 horz_pts[1] = tmp;
1109         }
1110
1111
1112         // draw from left to right.
1113         gr_line( fl2i(horz_pts[0].x),fl2i(horz_pts[0].y),fl2i(horz_pts[1].x),fl2i(horz_pts[1].y) );
1114         
1115 }
1116
1117
1118 /*
1119
1120 horizon_poly    dw      5 dup (?,?)     ;max of 5 points
1121
1122 ;for g3_compute_horz_vecs
1123 xfrac   fix     ?
1124 yfrac   fix     ?
1125
1126 vec_ptr dd      ?
1127 corner_num      dd      ?
1128
1129 ;for compute corner vec
1130 m13     fix     ?       ;m1-m3
1131 m46     fix     ?
1132 m79     fix     ?
1133 m56     fix     ?
1134 m23     fix     ?
1135 m89     fix     ?
1136
1137 _DATA   ends
1138
1139
1140 _TEXT   segment dword public USE32 'CODE'
1141
1142         extn    gr_setcolor_,gr_clear_canvas_
1143         extn    gr_upoly_tmap_
1144
1145 ;draw a polygon (one half of horizon) from the horizon line
1146 draw_horz_poly: lea     ebx,horizon_poly
1147
1148 ;copy horizon line as first points in poly
1149
1150         mov     eax,[edi]
1151         mov     [ebx],eax
1152         mov     eax,4[edi]
1153         mov     4[ebx],eax
1154
1155         mov     eax,[esi]
1156         mov     8[ebx],eax
1157         mov     eax,4[esi]
1158         mov     12[ebx],eax
1159
1160 ;add corners to polygon
1161
1162         mov     eax,8[esi]      ;edge number of start edge
1163
1164         mov     ecx,8[edi]      ;edge number of end point
1165         sub     ecx,eax ;number of edges
1166         jns     edgenum_ok
1167         add     ecx,4
1168 edgenum_ok:
1169         mov     edx,ecx ;save count
1170         sal     eax,3   ;edge * 8
1171         lea     esi,corners[eax]        ;first corner
1172         lea     edi,16[ebx]     ;rest of poly
1173 corner_loop:    movsd
1174         movsd           ;copy a corner
1175         cmp     esi,offset corners+8*4  ;end of list?
1176         jne     no_wrap
1177         lea     esi,corners
1178 no_wrap:        loop    corner_loop
1179
1180 ;now draw the polygon
1181         mov     eax,edx ;get corner count
1182         add     eax,2   ;..plus horz line end points
1183         lea     edx,horizon_poly        ;get the points
1184 ;;      call    gr_poly_        ;draw it!
1185  call gr_upoly_tmap_
1186         ret
1187
1188 ;return information on the polygon that is the sky. 
1189 ;takes ebx=ptr to x,y pairs, ecx=ptr to vecs for each point
1190 ;returns eax=number of points
1191 ;IMPORTANT: g3_draw_horizon() must be called before this routine.
1192 g3_compute_sky_polygon:
1193         test    sky_ground_flag,-1      ;what was drawn
1194         js      was_all_ground
1195         jg      was_all_sky     
1196
1197         pushm   ebx,ecx,edx,esi,edi
1198
1199         lea     esi,left_point
1200         lea     edi,right_point
1201         test    color_swap,-1
1202         jz      no_swap_ends
1203         xchg    esi,edi ;sky isn't top
1204 no_swap_ends:
1205
1206 ;copy horizon line as first points in poly
1207
1208         mov     eax,[edi]       ;copy end point
1209         mov     [ebx],eax
1210         mov     eax,4[edi]
1211         mov     4[ebx],eax
1212
1213         mov     eax,[esi]       ;copy start point
1214         mov     8[ebx],eax
1215         mov     eax,4[esi]
1216         mov     12[ebx],eax
1217
1218         pushm   ebx,ecx
1219         push    edi     ;save end point
1220         push    esi     ;save start point
1221         mov     esi,edi ;end point is first point
1222         mov     edi,ecx ;dest buffer
1223         call    compute_horz_end_vec
1224
1225         pop     esi     ;get back start point
1226         add     edi,12  ;2nd vec
1227         call    compute_horz_end_vec
1228
1229         pop     edi     ;get back end point
1230         popm    ebx,ecx
1231         add     ebx,16  ;past two x,y pairs
1232         add     ecx,24  ;past two vectors
1233
1234         mov     vec_ptr,ecx
1235
1236 ;add corners to polygon
1237
1238         mov     eax,8[esi]      ;edge number of start edge
1239         mov     corner_num,eax
1240
1241         mov     ecx,8[edi]      ;edge number of end point
1242         sub     ecx,eax ;number of edges
1243         jns     edgenum_ok2
1244         add     ecx,4
1245 edgenum_ok2:
1246         push    ecx     ;save count
1247         sal     eax,3   ;edge * 8
1248         lea     esi,corners[eax]        ;first corner
1249         mov     edi,ebx ;rest of poly 2d points
1250 corner_loop2:
1251         movsd
1252         movsd           ;copy a corner
1253         cmp     esi,offset corners+8*4  ;end of list?
1254         jne     no_wrap2
1255         lea     esi,corners
1256 no_wrap2:
1257         pushm   ecx,esi,edi
1258         mov     edi,vec_ptr
1259         mov     eax,corner_num
1260         call    compute_corner_vec
1261         add     vec_ptr,12
1262         inc     corner_num
1263         popm    ecx,esi,edi
1264
1265         loop    corner_loop2
1266
1267 ;now return with count
1268         pop     eax     ;get corner count
1269         add     eax,2   ;..plus horz line end points
1270
1271         popm    ebx,ecx,edx,esi,edi
1272
1273         ret
1274
1275 ;we drew all ground, so there was no horizon drawn
1276 was_all_ground: xor     eax,eax ;no points in poly
1277         ret
1278
1279 ;we drew all sky, so find 4 corners
1280 was_all_sky:    pushm   ebx,ecx,edx,esi,edi
1281         push    ecx
1282         lea     esi,corners
1283         mov     edi,ebx
1284         mov     ecx,8
1285         rep     movsd
1286         pop     edi
1287
1288         mov     ecx,4
1289         xor     eax,eax ;start corner 0
1290 sky_loop:       pushm   eax,ecx,edi
1291         call    compute_corner_vec
1292         popm    eax,ecx,edi
1293         add     edi,12
1294         inc     eax
1295         loop    sky_loop
1296         mov     eax,4   ;4 corners
1297         popm    ebx,ecx,edx,esi,edi
1298         ret
1299
1300 ;compute vector describing horizon intersection with a point.
1301 ;takes esi=2d point, edi=vec. trashes eax,ebx,ecx,edx
1302 compute_horz_end_vec:
1303
1304 ;compute rotated x/z & y/z ratios
1305
1306         mov     eax,[esi]       ;get x coord
1307         sub     eax,Canv_w2
1308         fixdiv  Canv_w2
1309         mov     xfrac,eax       ;save
1310
1311         mov     eax,4[esi]      ;get y coord
1312         sub     eax,Canv_h2
1313         fixdiv  Canv_h2
1314         neg     eax     ;y inversion
1315         mov     yfrac,eax       ;save
1316
1317 ;compute fraction unrotated x/z
1318
1319         mov     eax,xfrac
1320         add     eax,yfrac
1321         mov     ecx,eax ;save
1322         fixmul  View_matrix.m9
1323         sub     eax,View_matrix.m7
1324         sub     eax,View_matrix.m8
1325         mov     ebx,eax ;save numerator
1326
1327         mov     eax,ecx
1328         fixmul  View_matrix.m3
1329         mov     ecx,eax
1330         mov     eax,View_matrix.m1
1331         add     eax,View_matrix.m2
1332         sub     eax,ecx
1333
1334 ;now eax/ebx = z/x. do divide in way to give result < 0
1335
1336         pushm   eax,ebx
1337         abs_eax
1338         xchg    eax,ebx
1339         abs_eax
1340         cmp     eax,ebx ;which is bigger?
1341         popm    eax,ebx
1342         jl      do_xz
1343
1344 ;x is bigger, so do as z/x
1345
1346         fixdiv  ebx
1347         
1348 ;now eax = z/x ratio.  Compute vector by normalizing and correcting sign
1349
1350         push    eax     ;save ratio
1351
1352         imul    eax     ;compute z*z
1353         inc     edx     ;+ x*x (x==1)
1354         call    quad_sqrt
1355
1356         mov     ecx,eax ;mag in ecx
1357         pop     eax     ;get ratio, x part
1358
1359         fixdiv  ecx
1360         mov     [edi].z,eax
1361
1362         mov     eax,f1_0
1363         fixdiv  ecx
1364
1365         mov     [edi].x,eax
1366
1367         jmp     finish_end
1368
1369 ;z is bigger, so do as x/z
1370 do_xz:
1371         xchg    eax,ebx
1372         fixdiv  ebx
1373         
1374 ;now eax = x/z ratio.  Compute vector by normalizing and correcting sign
1375
1376         push    eax     ;save ratio
1377
1378         imul    eax     ;compute x*x
1379         inc     edx     ;+ z*z (z==1)
1380         call    quad_sqrt
1381
1382         mov     ecx,eax ;mag in ecx
1383         pop     eax     ;get ratio, x part
1384
1385         fixdiv  ecx
1386         mov     [edi].x,eax
1387
1388         mov     eax,f1_0
1389         fixdiv  ecx
1390
1391         mov     [edi].z,eax
1392
1393 finish_end:     xor     eax,eax ;y = 0
1394         mov     [edi].y,eax
1395
1396 ;now make sure that this vector is in front of you, not behind
1397
1398         mov     eax,[edi].x
1399         imul    View_matrix.m3
1400         mov     ebx,eax
1401         mov     ecx,edx
1402         mov     eax,[edi].z
1403         imul    View_matrix.m9
1404         add     eax,ebx
1405         adc     edx,ecx
1406         jns     vec_ok  ;has positive z, ok
1407
1408 ;z is neg, flip vector
1409
1410         neg     [edi].x
1411         neg     [edi].z
1412 vec_ok:
1413         ret
1414
1415 MIN_DEN equ 7fffh
1416
1417 sub2    macro   dest,src
1418         mov     eax,src
1419         sal     eax,1
1420         sub     dest,eax
1421         endm
1422
1423 ;compute vector decribing a corner of the screen.
1424 ;takes edi=vector, eax=corner num
1425 compute_corner_vec:
1426
1427         cmp     eax,4
1428         jl      num_ok
1429         sub     eax,4
1430 num_ok:
1431
1432 ;compute all deltas
1433         mov     ebx,View_matrix.m1
1434         mov     ecx,View_matrix.m4
1435         mov     edx,View_matrix.m7
1436
1437         or      eax,eax
1438         jz      neg_x
1439         cmp     eax,3
1440         jne     no_neg_x
1441 neg_x:
1442         neg     ebx
1443         neg     ecx
1444         neg     edx
1445 no_neg_x:       
1446         sub     ebx,View_matrix.m3
1447         mov     m13,ebx ;m1-m3
1448         sub     ecx,View_matrix.m6
1449         mov     m46,ecx ;m4-m6
1450         sub     edx,View_matrix.m9
1451         mov     m79,edx ;m7-m9
1452
1453         mov     ebx,View_matrix.m5
1454         mov     ecx,View_matrix.m2
1455         mov     edx,View_matrix.m8
1456
1457         cmp     eax,2
1458         jl      no_neg_y
1459 neg_y:
1460         neg     ebx
1461         neg     ecx
1462         neg     edx
1463 no_neg_y:       
1464         sub     ebx,View_matrix.m6
1465         mov     m56,ebx ;m5-m6
1466         sub     ecx,View_matrix.m3
1467         mov     m23,ecx ;m2-m3
1468         sub     edx,View_matrix.m9
1469         mov     m89,edx ;m8-m9
1470
1471 ;compute x/z ratio
1472
1473 ;compute denomonator
1474
1475         mov     eax,m46
1476         fixmul  m23
1477         mov     ebx,eax ;save
1478
1479         mov     eax,m56
1480         fixmul  m13
1481         sub     eax,ebx ;eax = denominator
1482
1483 ;now we have the denominator.  If it is too small, try x/y, z/y or z/x, y/x
1484
1485         mov     ecx,eax ;save den
1486
1487         abs_eax
1488         cmp     eax,MIN_DEN
1489         jl      z_too_small
1490
1491 z_too_small:
1492
1493 ;now do x/z numerator
1494
1495         mov     eax,m79
1496         fixmul  m56     ;* (m5-m6)
1497         mov     ebx,eax
1498
1499         mov     eax,m89
1500         fixmul  m46     ;* (m4-m6)
1501         sub     eax,ebx
1502
1503 ;now, eax/ecx = x/z ratio
1504
1505         fixdiv  ecx     ;eax = x/z
1506
1507         mov     [edi].x,eax     ;save x
1508
1509 ;now do y/z
1510
1511         mov     eax,m89
1512         fixmul  m13
1513         mov     ebx,eax
1514
1515         mov     eax,m79
1516         fixmul  m23
1517         sub     eax,ebx
1518
1519 ;now eax/ecx = y/z ratio
1520
1521         fixdiv  ecx
1522
1523         mov     [edi].y,eax
1524
1525         mov     [edi].z,f1_0
1526
1527         mov     esi,edi
1528         call    vm_vec_normalize
1529
1530 ;make sure this vec is pointing in right direction
1531
1532         lea     edi,View_matrix.fvec
1533         call    vm_vec_dotprod
1534         or      eax,eax ;check sign
1535         jg      vec_sign_ok
1536
1537         neg     [esi].x
1538         neg     [esi].y
1539         neg     [esi].z
1540 vec_sign_ok:
1541
1542         ret
1543
1544
1545 _TEXT   ends
1546
1547         end
1548
1549 */
1550
1551
1552 // Draws a polygon always facing the viewer.
1553 // compute the corners of a rod.  fills in vertbuf.
1554 // Verts has any needs uv's or l's or can be NULL if none needed.
1555 int g3_draw_rod(vector *p0,float width1,vector *p1,float width2, vertex * verts, uint tmap_flags)
1556 {
1557         vector uvec, fvec, rvec, center;
1558
1559         vm_vec_sub( &fvec, p0, p1 );
1560         vm_vec_normalize_safe( &fvec );
1561
1562         vm_vec_avg( &center, p0, p1 );
1563         vm_vec_sub( &rvec, &Eye_position, &center );
1564         vm_vec_normalize( &rvec );
1565
1566         vm_vec_crossprod(&uvec,&fvec,&rvec);
1567                         
1568         //normalize new perpendicular vector
1569         vm_vec_normalize(&uvec);
1570          
1571         //now recompute right vector, in case it wasn't entirely perpendiclar
1572         vm_vec_crossprod(&rvec,&uvec,&fvec);
1573
1574         // Now have uvec, which is up vector and rvec which is the normal
1575         // of the face.
1576
1577         int i;
1578         vector vecs[4];
1579         vertex pts[4];
1580         vertex *ptlist[4] = { &pts[3], &pts[2], &pts[1], &pts[0] };
1581
1582         vm_vec_scale_add( &vecs[0], p0, &uvec, width1/2.0f );
1583         vm_vec_scale_add( &vecs[1], p1, &uvec, width2/2.0f );
1584         vm_vec_scale_add( &vecs[2], p1, &uvec, -width2/2.0f );
1585         vm_vec_scale_add( &vecs[3], p0, &uvec, -width1/2.0f );
1586         
1587         for (i=0; i<4; i++ )    {
1588                 if ( verts )    {
1589                         pts[i] = verts[i];
1590                 }
1591                 g3_rotate_vertex( &pts[i], &vecs[i] );
1592         }
1593
1594         return g3_draw_poly(4,ptlist,tmap_flags);
1595 }
1596
1597 // draw a perspective bitmap based on angles and radius
1598 vector g3_square[4] = {
1599         {-1.0f, -1.0f, 20.0f},
1600         {-1.0f, 1.0f, 20.0f},
1601         {1.0f, 1.0f, 20.0f},
1602         {1.0f, -1.0f, 20.0f}
1603 };
1604
1605 #define MAX_PERSPECTIVE_DIVISIONS                       5                               // should never even come close to this limit
1606
1607 void stars_project_2d_onto_sphere( vector *pnt, float rho, float phi, float theta )
1608 {               
1609         float a = 3.14159f * phi;
1610         float b = 6.28318f * theta;
1611         float sin_a = (float)sin(a);    
1612
1613         // coords
1614         pnt->z = rho * sin_a * (float)cos(b);
1615         pnt->y = rho * sin_a * (float)sin(b);
1616         pnt->x = rho * (float)cos(a);
1617 }
1618
1619 // draw a perspective bitmap based on angles and radius
1620 float p_phi = 10.0f;
1621 float p_theta = 10.0f;
1622 int g3_draw_perspective_bitmap(angles *a, float scale_x, float scale_y, int div_x, int div_y, uint tmap_flags)
1623 {
1624         vector s_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
1625         vector t_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
1626         vertex v[4];
1627         vertex *verts[4];
1628         matrix m, m_bank;
1629         int idx, s_idx; 
1630         int saved_zbuffer_mode; 
1631         float ui, vi;   
1632         angles bank_first;              
1633
1634         // cap division values
1635         // div_x = div_x > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_x;
1636         div_x = 1;
1637         div_y = div_y > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_y;  
1638
1639         // texture increment values
1640         ui = 1.0f / (float)div_x;
1641         vi = 1.0f / (float)div_y;       
1642
1643         // adjust for aspect ratio
1644         scale_x *= ((float)gr_screen.max_w / (float)gr_screen.max_h) + 0.55f;           // fudge factor
1645
1646         float s_phi = 0.5f + (((p_phi * scale_x) / 360.0f) / 2.0f);
1647         float s_theta = (((p_theta * scale_y) / 360.0f) / 2.0f);        
1648         float d_phi = -(((p_phi * scale_x) / 360.0f) / (float)(div_x));
1649         float d_theta = -(((p_theta * scale_y) / 360.0f) / (float)(div_y));
1650
1651         // bank matrix
1652         bank_first.p = 0.0f;
1653         bank_first.b = a->b;
1654         bank_first.h = 0.0f;
1655         vm_angles_2_matrix(&m_bank, &bank_first);
1656
1657         // convert angles to matrix
1658         float b_save = a->b;
1659         a->b = 0.0f;
1660         vm_angles_2_matrix(&m, a);
1661         a->b = b_save;  
1662
1663         // generate the bitmap points   
1664         for(idx=0; idx<=div_x; idx++){
1665                 for(s_idx=0; s_idx<=div_y; s_idx++){                            
1666                         // get world spherical coords                   
1667                         stars_project_2d_onto_sphere(&s_points[idx][s_idx], 1000.0f, s_phi + ((float)idx*d_phi), s_theta + ((float)s_idx*d_theta));                     
1668                         
1669                         // bank the bitmap first
1670                         vm_vec_rotate(&t_points[idx][s_idx], &s_points[idx][s_idx], &m_bank);
1671
1672                         // rotate on the sphere
1673                         vm_vec_rotate(&s_points[idx][s_idx], &t_points[idx][s_idx], &m);                                        
1674                 }
1675         }               
1676
1677         // turn off zbuffering
1678         saved_zbuffer_mode = gr_zbuffer_get();
1679         gr_zbuffer_set(GR_ZBUFF_NONE);
1680
1681         // turn off culling
1682         gr_set_cull(0);
1683
1684         // draw the bitmap
1685         if(Fred_running){
1686                 tmap_flags &= ~(TMAP_FLAG_CORRECT);
1687         }
1688
1689         // render all polys
1690         for(idx=0; idx<div_x; idx++){
1691                 for(s_idx=0; s_idx<div_y; s_idx++){                                             
1692                         // stuff texture coords
1693                         v[0].u = ui * float(idx);
1694                         v[0].v = vi * float(s_idx);
1695                         
1696                         v[1].u = ui * float(idx+1);
1697                         v[1].v = vi * float(s_idx);
1698
1699                         v[2].u = ui * float(idx+1);
1700                         v[2].v = vi * float(s_idx+1);                                           
1701
1702                         v[3].u = ui * float(idx);
1703                         v[3].v = vi * float(s_idx+1);                   
1704
1705                         // poly 1
1706                         v[0].flags = 0;
1707                         v[1].flags = 0;
1708                         v[2].flags = 0;
1709                         verts[0] = &v[0];
1710                         verts[1] = &v[1];
1711                         verts[2] = &v[2];
1712                         g3_rotate_faraway_vertex(verts[0], &s_points[idx][s_idx]);                      
1713                         g3_rotate_faraway_vertex(verts[1], &s_points[idx+1][s_idx]);                    
1714                         g3_rotate_faraway_vertex(verts[2], &s_points[idx+1][s_idx+1]);                                          
1715                         g3_draw_poly(3, verts, tmap_flags);
1716
1717                         // poly 2                       
1718                         v[0].flags = 0;
1719                         v[2].flags = 0;
1720                         v[3].flags = 0;
1721                         verts[0] = &v[0];
1722                         verts[1] = &v[2];
1723                         verts[2] = &v[3];
1724                         g3_rotate_faraway_vertex(verts[0], &s_points[idx][s_idx]);                      
1725                         g3_rotate_faraway_vertex(verts[1], &s_points[idx+1][s_idx+1]);                  
1726                         g3_rotate_faraway_vertex(verts[2], &s_points[idx][s_idx+1]);                                            
1727                         g3_draw_poly(3, verts, tmap_flags);
1728                 }
1729         }
1730
1731         // turn on culling
1732         gr_set_cull(1);
1733
1734         // restore zbuffer
1735         gr_zbuffer_set(saved_zbuffer_mode);
1736
1737         // return
1738         return 1;
1739 }
1740
1741 // draw a 2d bitmap on a poly
1742 int g3_draw_2d_poly_bitmap(int x, int y, int w, int h, uint additional_tmap_flags)
1743 {
1744         int ret;
1745         int saved_zbuffer_mode;
1746         vertex v[4];
1747         vertex *vertlist[4] = { &v[0], &v[1], &v[2], &v[3] };
1748
1749         int bw, bh;
1750
1751         g3_start_frame(1);
1752
1753         // turn off zbuffering  
1754         saved_zbuffer_mode = gr_zbuffer_get();
1755         gr_zbuffer_set(GR_ZBUFF_NONE);  
1756
1757         bm_get_section_size(gr_screen.current_bitmap, gr_screen.current_bitmap_sx, gr_screen.current_bitmap_sy, &bw, &bh);
1758
1759         // stuff coords 
1760         v[0].sx = (float)x;
1761         v[0].sy = (float)y;     
1762         v[0].sw = 0.0f;
1763         v[0].u = 0.0f;
1764         v[0].v = 0.0f;
1765         v[0].flags = PF_PROJECTED;
1766         v[0].codes = 0;
1767
1768         v[1].sx = (float)(x + w);
1769         v[1].sy = (float)y;     
1770         v[1].sw = 0.0f;
1771         v[1].u = 1.0f;
1772         v[1].v = 0.0f;
1773         v[1].flags = PF_PROJECTED;
1774         v[1].codes = 0;
1775
1776         v[2].sx = (float)(x + w);
1777         v[2].sy = (float)(y + h);       
1778         v[2].sw = 0.0f;
1779         v[2].u = 1.0f;
1780         v[2].v = 1.0f;
1781         v[2].flags = PF_PROJECTED;
1782         v[2].codes = 0;
1783
1784         v[3].sx = (float)x;
1785         v[3].sy = (float)(y + h);       
1786         v[3].sw = 0.0f;
1787         v[3].u = 0.0f;
1788         v[3].v = 1.0f;
1789         v[3].flags = PF_PROJECTED;
1790         v[3].codes = 0;         
1791
1792         /*
1793         v[0].sx = (float)x;
1794         v[0].sy = (float)y;     
1795         v[0].sw = 0.0f;
1796         v[0].u = 0.5f / i2fl(bw);
1797         v[0].v = 0.5f / i2fl(bh);
1798         v[0].flags = PF_PROJECTED;
1799         v[0].codes = 0;
1800
1801         v[1].sx = (float)(x + w);
1802         v[1].sy = (float)y;     
1803         v[1].sw = 0.0f;
1804         v[1].u = 1.0f + (0.5f / i2fl(bw));
1805         v[1].v = 0.0f + (0.5f / i2fl(bh));
1806         v[1].flags = PF_PROJECTED;
1807         v[1].codes = 0;
1808
1809         v[2].sx = (float)(x + w);
1810         v[2].sy = (float)(y + h);       
1811         v[2].sw = 0.0f;
1812         v[2].u = 1.0f + (0.5f / i2fl(bw));
1813         v[2].v = 1.0f + (0.5f / i2fl(bh));
1814         v[2].flags = PF_PROJECTED;
1815         v[2].codes = 0;
1816
1817         v[3].sx = (float)x;
1818         v[3].sy = (float)(y + h);       
1819         v[3].sw = 0.0f;
1820         v[3].u = 0.0f + (0.5f / i2fl(bw));
1821         v[3].v = 1.0f + (0.5f / i2fl(bh));
1822         v[3].flags = PF_PROJECTED;
1823         v[3].codes = 0; 
1824         */
1825                 
1826         // no filtering
1827         gr_filter_set(0);
1828
1829         // set debrief  
1830         ret = g3_draw_poly_constant_sw(4, vertlist, TMAP_FLAG_TEXTURED | additional_tmap_flags, 0.1f);
1831
1832         g3_end_frame();
1833         
1834         gr_zbuffer_set(saved_zbuffer_mode);     
1835
1836         // put filtering back on
1837         gr_filter_set(1);
1838
1839         return ret;
1840 }