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