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