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