2 * $Logfile: /Freespace2/code/Render/3ddraw.cpp $
7 * 3D rendering primitives
10 * Revision 1.1 2002/05/03 03:28:10 root
14 * 18 9/06/99 3:23p Andsager
15 * Make fireball and weapon expl ani LOD choice look at resolution of the
18 * 17 8/27/99 9:07p Dave
19 * LOD explosions. Improved beam weapon accuracy.
21 * 16 7/29/99 12:05a Dave
22 * Nebula speed optimizations.
24 * 15 7/13/99 1:16p Dave
25 * 32 bit support. Whee!
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.
31 * 13 6/29/99 10:35a Dave
32 * Interface polygon bitmaps! Whee!
34 * 12 6/22/99 7:03p Dave
35 * New detail options screen.
37 * 11 6/16/99 4:06p Dave
38 * New pilot info popup. Added new draw-bitmap-as-poly function.
40 * 10 6/08/99 5:17p Dave
41 * Fixed up perspective bitmap drawing.
43 * 9 6/03/99 6:37p Dave
44 * More TNT fun. Made perspective bitmaps more flexible.
46 * 8 5/28/99 1:58p Dave
47 * Fixed problem with overwriting bank value drawing perspective bitmaps.
49 * 7 5/28/99 1:45p Dave
50 * Fixed up perspective bitmap drawing.
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.
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.
61 * 4 3/09/99 6:24p Dave
62 * More work on object update revamping. Identified several sources of
63 * unnecessary bandwidth.
65 * 3 2/11/99 3:08p Dave
66 * PXO refresh button. Very preliminary squad war support.
68 * 2 10/07/98 10:53a Dave
71 * 1 10/07/98 10:51a Dave
73 * 37 3/22/98 2:34p John
74 * If alpha effects is on lowest detail level, draw rotated bitmaps as
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.
81 * 35 3/04/98 9:29a John
82 * Made rotated bitmaps force all sw's to the same value after clipping.
84 * 34 3/03/98 6:59p John
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
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.
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).
99 * 30 12/30/97 6:44p John
100 * Made g3_Draw_bitmap functions account for aspect of bitmap.
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.
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.
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
115 * 26 10/20/97 4:49p John
116 * added weapon trails.
117 * added facing bitmap code to 3d lib.
119 * 25 10/03/97 9:10a John
120 * added better antialiased line drawer
122 * 24 9/09/97 3:39p Sandeep
123 * warning level 4 bugs
125 * 23 7/11/97 11:54a John
126 * added rotated 3d bitmaps.
128 * 22 5/07/97 2:59p John
129 * Initial rev of D3D texturing.
131 * 21 4/29/97 9:55a John
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.
138 * 19 3/06/97 5:36p Mike
139 * Change vec_normalize_safe() back to vec_normalize().
140 * Spruce up docking a bit.
142 * 18 3/06/97 10:56a Mike
143 * Write error checking version of vm_vec_normalize().
144 * Fix resultant problems.
146 * 17 2/26/97 5:24p John
147 * Added g3_draw_sphere_ez
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
156 #include "3dinternal.h"
160 #include "floating.h"
161 #include "physics.h" // For Physics_viewer_bank for g3_draw_rotated_bitmap
163 #include "systemvars.h"
164 #include "alphacolors.h"
168 //deal with a clipped line
169 int must_clip_line(vertex *p0,vertex *p1,ubyte codes_or, uint flags)
173 clip_line(&p0,&p1,codes_or, flags);
175 if (p0->codes & p1->codes) goto free_points;
177 codes_or = (unsigned char)(p0->codes | p1->codes);
179 if (codes_or & CC_BEHIND) goto free_points;
181 if (!(p0->flags&PF_PROJECTED))
182 g3_project_vertex(p0);
184 if (p0->flags&PF_OVERFLOW) goto free_points;
186 if (!(p1->flags&PF_PROJECTED))
187 g3_project_vertex(p1);
189 if (p1->flags&PF_OVERFLOW) goto free_points;
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);
201 if (p0->flags & PF_TEMP_POINT)
204 if (p1->flags & PF_TEMP_POINT)
210 //draws a line. takes two points. returns true if drew
211 int g3_draw_line(vertex *p0,vertex *p1)
215 Assert( G3_count == 1 );
217 if (p0->codes & p1->codes)
220 codes_or = (unsigned char)(p0->codes | p1->codes);
222 if (codes_or & CC_BEHIND)
223 return must_clip_line(p0,p1,codes_or,0);
225 if (!(p0->flags&PF_PROJECTED))
226 g3_project_vertex(p0);
228 if (p0->flags&PF_OVERFLOW)
230 return must_clip_line(p0,p1,codes_or,0);
233 if (!(p1->flags&PF_PROJECTED))
234 g3_project_vertex(p1);
236 if (p1->flags&PF_OVERFLOW)
238 return must_clip_line(p0,p1,codes_or,0);
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);
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)
255 Assert( G3_count == 1 );
257 vm_vec_sub(&tempv,&View_position,v);
259 return (vm_vec_dot(&tempv,norm) > 0.0f);
262 int do_facing_check(vector *norm,vertex **vertlist,vector *p)
264 Assert( G3_count == 1 );
266 if (norm) { //have normal
268 Assert(norm->x || norm->y || norm->z);
270 return g3_check_normal_facing(p,norm);
272 else { //normal not specified, so must compute
276 //get three points (rotated) and compute normal
278 vm_vec_perp(&tempv,(vector *)&vertlist[0]->x,(vector *)&vertlist[1]->x,(vector *)&vertlist[2]->x);
280 return (vm_vec_dot(&tempv,(vector *)&vertlist[1]->x ) < 0.0);
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
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)
292 Assert( G3_count == 1 );
294 if (do_facing_check(norm,pointlist,pnt))
295 return g3_draw_poly(nv,pointlist,tmap_flags);
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)
309 Assert( G3_count == 1 );
311 cc.or = 0; cc.and = 0xff;
318 p = bufptr[i] = pointlist[i];
325 return 1; //all points off screen
328 Assert( G3_count == 1 );
330 bufptr = clip_polygon(Vbuf0,Vbuf1,&nv,&cc,tmap_flags);
332 if (nv && !(cc.or&CC_BEHIND) && !cc.and) {
335 vertex *p = bufptr[i];
337 if (!(p->flags&PF_PROJECTED))
338 g3_project_vertex(p);
340 if (p->flags&PF_OVERFLOW) {
341 //Int3(); //should not overflow after clip
342 //printf( "overflow in must_clip_tmap_face\n" );
347 gr_tmapper( nv, bufptr, tmap_flags );
354 if (bufptr[i]->flags & PF_TEMP_POINT)
355 free_temp_point(bufptr[i]);
358 //now make list of 2d coords (& check for overflow)
361 vertex *p = bufptr[i];
363 if (!(p->flags&PF_PROJECTED))
364 g3_project_vertex(p);
366 if (p->flags&PF_OVERFLOW) {
367 //Int3(); //should not overflow after clip
368 //printf( "3d: Point overflowed, but flags say OK!\n" );
374 gr_tmapper( nv, bufptr, tmap_flags );
376 return 0; //say it drew
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)
391 Assert( G3_count == 1 );
393 cc.or = 0; cc.and = 0xff;
400 p = bufptr[i] = pointlist[i];
407 return 1; //all points off screen
410 Assert( G3_count == 1 );
412 bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags);
414 if (nv && !(cc.or&CC_BEHIND) && !cc.and) {
417 vertex *p = bufptr[i];
419 if (!(p->flags&PF_PROJECTED))
420 g3_project_vertex(p);
422 if (p->flags&PF_OVERFLOW) {
423 //Int3(); //should not overflow after clip
424 //printf( "overflow in must_clip_tmap_face\n" );
431 gr_tmapper( nv, bufptr, tmap_flags );
433 // draw lines connecting the faces
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]);
439 g3_draw_line(bufptr[0], bufptr[i]);
447 if (bufptr[i]->flags & PF_TEMP_POINT){
448 free_temp_point(bufptr[i]);
452 //now make list of 2d coords (& check for overflow)
455 vertex *p = bufptr[i];
457 if (!(p->flags&PF_PROJECTED))
458 g3_project_vertex(p);
460 if (p->flags&PF_OVERFLOW) {
461 //Int3(); //should not overflow after clip
462 //printf( "3d: Point overflowed, but flags say OK!\n" );
469 gr_tmapper( nv, bufptr, tmap_flags );
471 // draw lines connecting the faces
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]);
477 g3_draw_line(bufptr[0], bufptr[i]);
480 return 0; //say it drew
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)
487 Assert( G3_count == 1 );
489 if (! (pnt->codes & CC_BEHIND)) {
491 if (! (pnt->flags & PF_PROJECTED))
492 g3_project_vertex(pnt);
494 if (! (pnt->codes & PF_OVERFLOW)) {
497 r2 = rad*Matrix_scale.x;
501 gr_circle(fl2i(pnt->sx),fl2i(pnt->sy),fl2i(t*2.0f));
508 int g3_draw_sphere_ez(vector *pnt,float rad)
513 Assert( G3_count == 1 );
515 flags = g3_rotate_vertex(&pt,pnt);
519 g3_project_vertex(&pt);
521 if (!(pt.flags & PF_OVERFLOW)) {
523 g3_draw_sphere( &pt, rad );
531 //draws a bitmap with the specified 3d width & height
532 //returns 1 if off screen, 0 if drew
534 int g3_draw_bitmap(vertex *pnt,int orient, float rad,uint tmap_flags)
540 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
543 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
547 height = width*i2fl(bh)/i2fl(bw);
548 } else if ( bw > bh ) {
550 width = height*i2fl(bw)/i2fl(bh);
552 width = height = rad*2.0f;
555 width = height = rad*2.0f;
558 Assert( G3_count == 1 );
560 if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) )
563 if (!(pnt->flags&PF_PROJECTED))
564 g3_project_vertex(pnt);
566 if (pnt->flags & PF_OVERFLOW)
569 t = (width*Canv_w2)/pnt->z;
570 w = t*Matrix_scale.x;
572 t = (height*Canv_h2)/pnt->z;
573 h = t*Matrix_scale.y;
576 z = pnt->z - rad/2.0f;
584 va.sx = pnt->sx - w/2.0f;
585 va.sy = pnt->sy - h/2.0f;
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)
623 bm_get_info( bitmap, &bw, &bh, NULL );
627 height = width*i2fl(bh)/i2fl(bw);
628 } else if ( bw > bh ) {
630 width = height*i2fl(bw)/i2fl(bh);
632 width = height = rad*2.0f;
635 Assert( G3_count == 1 );
637 if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) ) {
641 if (!(pnt->flags&PF_PROJECTED)){
642 g3_project_vertex(pnt);
645 if (pnt->flags & PF_OVERFLOW){
649 t = (width*Canv_w2)/pnt->z;
650 *w = (int)(t*Matrix_scale.x);
652 t = (height*Canv_h2)/pnt->z;
653 *h = (int)(t*Matrix_scale.y);
655 *x = (int)(pnt->sx - *w/2.0f);
656 *y = (int)(pnt->sy - *h/2.0f);
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)
668 vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] };
673 if ( !Detail.alpha_effects ) {
675 if ( angle < PI/2 ) {
677 } else if ( angle < PI ) {
679 } else if ( angle < PI+PI/2 ) {
684 return g3_draw_bitmap( pnt, ang, rad, tmap_flags );
688 Assert( G3_count == 1 );
690 angle+=Physics_viewer_bank;
693 else if ( angle > PI2 )
697 sa = (float)sin(angle);
698 ca = (float)cos(angle);
702 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
705 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
709 height = width*i2fl(bh)/i2fl(bw);
710 } else if ( bw > bh ) {
712 width = height*i2fl(bw)/i2fl(bh);
714 width = height = rad;
717 width = height = rad;
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;
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;
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;
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;
749 ubyte codes_and=0xff;
752 z = pnt->z - rad / 4.0f;
753 if ( z < 0.0f ) z = 0.0f;
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]);
764 return 1; //1 means off screen
767 g3_draw_poly_constant_sw(4, vertlist, tmap_flags, sw );
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)
776 float total_area = 0.0f;
779 for(idx=1; idx<nv-1; idx++){
780 TRIANGLE_AREA(pointlist[0], pointlist[idx], pointlist[idx+1]);
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)
798 Assert( G3_count == 1 );
800 cc.or = 0; cc.and = 0xff;
807 p = bufptr[i] = pointlist[i];
814 return 0.0f; //all points off screen
818 Assert( G3_count == 1 );
820 bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags);
822 if (nv && !(cc.or&CC_BEHIND) && !cc.and) {
825 vertex *p = bufptr[i];
827 if (!(p->flags&PF_PROJECTED))
828 g3_project_vertex(p);
830 if (p->flags&PF_OVERFLOW) {
831 //Int3(); //should not overflow after clip
832 //printf( "overflow in must_clip_tmap_face\n" );
840 p_area = g3_get_poly_area(nv, bufptr);
845 gr_tmapper( nv, bufptr, tmap_flags );
852 if (bufptr[i]->flags & PF_TEMP_POINT){
853 free_temp_point(bufptr[i]);
857 //now make list of 2d coords (& check for overflow)
860 vertex *p = bufptr[i];
862 if (!(p->flags&PF_PROJECTED))
863 g3_project_vertex(p);
865 if (p->flags&PF_OVERFLOW) {
873 p_area = g3_get_poly_area(nv, bufptr);
878 gr_tmapper( nv, bufptr, tmap_flags );
881 // how much area we drew
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)
891 vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] };
895 Assert( G3_count == 1 );
897 angle+=Physics_viewer_bank;
900 } else if ( angle > PI2 ) {
904 sa = (float)sin(angle);
905 ca = (float)cos(angle);
909 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
912 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
916 height = width*i2fl(bh)/i2fl(bw);
917 } else if ( bw > bh ) {
919 width = height*i2fl(bw)/i2fl(bh);
921 width = height = rad;
924 width = height = rad;
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;
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;
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;
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;
956 ubyte codes_and=0xff;
959 z = pnt->z - rad / 4.0f;
960 if ( z < 0.0f ) z = 0.0f;
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]);
975 return g3_draw_poly_constant_sw_area(4, vertlist, tmap_flags, sw, area );
981 typedef struct horz_pt {
986 //draws a horizon. takes eax=sky_color, edx=ground_color
987 void g3_draw_horizon_line()
989 //int sky_color,int ground_color
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
999 float up_right, down_right,down_left,up_left;
1001 // color_swap = 0; //assume no swap
1002 // sky_ground_flag = 0; //assume both
1004 // if ( View_matrix.uvec.y < 0.0f )
1006 // else if ( View_matrix.uvec.y == 0.0f ) {
1007 // if ( View_matrix.uvec.x > 0.0f )
1011 // if (color_swap) {
1012 // top_color = ground_color;
1013 // bot_color = sky_color;
1015 // top_color = sky_color;
1016 // bot_color = ground_color;
1019 Assert( G3_count == 1 );
1022 //compute horizon_vector
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;
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;
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" ));
1040 if ( (up_right>0.0f)&&(down_right>0.0f)&&(down_left>0.0f)&&(up_left>0.0f) ) {
1041 // mprintf(( "All sky.\n" ));
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 ));
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
1053 // check intersection with left edge
1054 s1 = up_left > 0.0f;
1055 s2 = down_left > 0.0f;
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;
1063 // check intersection with top edge
1064 s1 = up_left > 0.0f;
1065 s2 = up_right > 0.0f;
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;
1074 // check intersection with right edge
1075 s1 = up_right > 0.0f;
1076 s2 = down_right > 0.0f;
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;
1084 //check intersection with bottom edge
1085 s1 = down_right > 0.0f;
1086 s2 = down_left > 0.0f;
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;
1095 mprintf(( "HORZ: Wrong number of points (%d)\n", cpnt ));
1099 //make sure first edge is left
1101 if ( horz_pts[0].x > horz_pts[1].x ) {
1104 horz_pts[0] = horz_pts[1];
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) );
1117 horizon_poly dw 5 dup (?,?) ;max of 5 points
1119 ;for g3_compute_horz_vecs
1126 ;for compute corner vec
1137 _TEXT segment dword public USE32 'CODE'
1139 extn gr_setcolor_,gr_clear_canvas_
1142 ;draw a polygon (one half of horizon) from the horizon line
1143 draw_horz_poly: lea ebx,horizon_poly
1145 ;copy horizon line as first points in poly
1157 ;add corners to polygon
1159 mov eax,8[esi] ;edge number of start edge
1161 mov ecx,8[edi] ;edge number of end point
1162 sub ecx,eax ;number of edges
1166 mov edx,ecx ;save count
1168 lea esi,corners[eax] ;first corner
1169 lea edi,16[ebx] ;rest of poly
1171 movsd ;copy a corner
1172 cmp esi,offset corners+8*4 ;end of list?
1175 no_wrap: loop corner_loop
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!
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
1194 pushm ebx,ecx,edx,esi,edi
1200 xchg esi,edi ;sky isn't top
1203 ;copy horizon line as first points in poly
1205 mov eax,[edi] ;copy end point
1210 mov eax,[esi] ;copy start point
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
1222 pop esi ;get back start point
1224 call compute_horz_end_vec
1226 pop edi ;get back end point
1228 add ebx,16 ;past two x,y pairs
1229 add ecx,24 ;past two vectors
1233 ;add corners to polygon
1235 mov eax,8[esi] ;edge number of start edge
1238 mov ecx,8[edi] ;edge number of end point
1239 sub ecx,eax ;number of edges
1243 push ecx ;save count
1245 lea esi,corners[eax] ;first corner
1246 mov edi,ebx ;rest of poly 2d points
1249 movsd ;copy a corner
1250 cmp esi,offset corners+8*4 ;end of list?
1257 call compute_corner_vec
1264 ;now return with count
1265 pop eax ;get corner count
1266 add eax,2 ;..plus horz line end points
1268 popm ebx,ecx,edx,esi,edi
1272 ;we drew all ground, so there was no horizon drawn
1273 was_all_ground: xor eax,eax ;no points in poly
1276 ;we drew all sky, so find 4 corners
1277 was_all_sky: pushm ebx,ecx,edx,esi,edi
1286 xor eax,eax ;start corner 0
1287 sky_loop: pushm eax,ecx,edi
1288 call compute_corner_vec
1293 mov eax,4 ;4 corners
1294 popm ebx,ecx,edx,esi,edi
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:
1301 ;compute rotated x/z & y/z ratios
1303 mov eax,[esi] ;get x coord
1308 mov eax,4[esi] ;get y coord
1311 neg eax ;y inversion
1314 ;compute fraction unrotated x/z
1319 fixmul View_matrix.m9
1320 sub eax,View_matrix.m7
1321 sub eax,View_matrix.m8
1322 mov ebx,eax ;save numerator
1325 fixmul View_matrix.m3
1327 mov eax,View_matrix.m1
1328 add eax,View_matrix.m2
1331 ;now eax/ebx = z/x. do divide in way to give result < 0
1337 cmp eax,ebx ;which is bigger?
1341 ;x is bigger, so do as z/x
1345 ;now eax = z/x ratio. Compute vector by normalizing and correcting sign
1347 push eax ;save ratio
1349 imul eax ;compute z*z
1350 inc edx ;+ x*x (x==1)
1353 mov ecx,eax ;mag in ecx
1354 pop eax ;get ratio, x part
1366 ;z is bigger, so do as x/z
1371 ;now eax = x/z ratio. Compute vector by normalizing and correcting sign
1373 push eax ;save ratio
1375 imul eax ;compute x*x
1376 inc edx ;+ z*z (z==1)
1379 mov ecx,eax ;mag in ecx
1380 pop eax ;get ratio, x part
1390 finish_end: xor eax,eax ;y = 0
1393 ;now make sure that this vector is in front of you, not behind
1403 jns vec_ok ;has positive z, ok
1405 ;z is neg, flip vector
1420 ;compute vector decribing a corner of the screen.
1421 ;takes edi=vector, eax=corner num
1430 mov ebx,View_matrix.m1
1431 mov ecx,View_matrix.m4
1432 mov edx,View_matrix.m7
1443 sub ebx,View_matrix.m3
1445 sub ecx,View_matrix.m6
1447 sub edx,View_matrix.m9
1450 mov ebx,View_matrix.m5
1451 mov ecx,View_matrix.m2
1452 mov edx,View_matrix.m8
1461 sub ebx,View_matrix.m6
1463 sub ecx,View_matrix.m3
1465 sub edx,View_matrix.m9
1470 ;compute denomonator
1478 sub eax,ebx ;eax = denominator
1480 ;now we have the denominator. If it is too small, try x/y, z/y or z/x, y/x
1482 mov ecx,eax ;save den
1490 ;now do x/z numerator
1493 fixmul m56 ;* (m5-m6)
1497 fixmul m46 ;* (m4-m6)
1500 ;now, eax/ecx = x/z ratio
1502 fixdiv ecx ;eax = x/z
1504 mov [edi].x,eax ;save x
1516 ;now eax/ecx = y/z ratio
1525 call vm_vec_normalize
1527 ;make sure this vec is pointing in right direction
1529 lea edi,View_matrix.fvec
1531 or eax,eax ;check sign
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)
1554 vector uvec, fvec, rvec, center;
1556 vm_vec_sub( &fvec, p0, p1 );
1557 vm_vec_normalize_safe( &fvec );
1559 vm_vec_avg( ¢er, p0, p1 );
1560 vm_vec_sub( &rvec, &Eye_position, ¢er );
1561 vm_vec_normalize( &rvec );
1563 vm_vec_crossprod(&uvec,&fvec,&rvec);
1565 //normalize new perpendicular vector
1566 vm_vec_normalize(&uvec);
1568 //now recompute right vector, in case it wasn't entirely perpendiclar
1569 vm_vec_crossprod(&rvec,&uvec,&fvec);
1571 // Now have uvec, which is up vector and rvec which is the normal
1577 vertex *ptlist[4] = { &pts[3], &pts[2], &pts[1], &pts[0] };
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 );
1584 for (i=0; i<4; i++ ) {
1588 g3_rotate_vertex( &pts[i], &vecs[i] );
1591 return g3_draw_poly(4,ptlist,tmap_flags);
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}
1602 #define MAX_PERSPECTIVE_DIVISIONS 5 // should never even come close to this limit
1604 void stars_project_2d_onto_sphere( vector *pnt, float rho, float phi, float theta )
1606 float a = 3.14159f * phi;
1607 float b = 6.28318f * theta;
1608 float sin_a = (float)sin(a);
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);
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)
1621 vector s_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
1622 vector t_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
1627 int saved_zbuffer_mode;
1631 // cap division values
1632 // div_x = div_x > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_x;
1634 div_y = div_y > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_y;
1636 // texture increment values
1637 ui = 1.0f / (float)div_x;
1638 vi = 1.0f / (float)div_y;
1640 // adjust for aspect ratio
1641 scale_x *= ((float)gr_screen.max_w / (float)gr_screen.max_h) + 0.55f; // fudge factor
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));
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);
1654 // convert angles to matrix
1655 float b_save = a->b;
1657 vm_angles_2_matrix(&m, a);
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));
1666 // bank the bitmap first
1667 vm_vec_rotate(&t_points[idx][s_idx], &s_points[idx][s_idx], &m_bank);
1669 // rotate on the sphere
1670 vm_vec_rotate(&s_points[idx][s_idx], &t_points[idx][s_idx], &m);
1674 // turn off zbuffering
1675 saved_zbuffer_mode = gr_zbuffer_get();
1676 gr_zbuffer_set(GR_ZBUFF_NONE);
1683 tmap_flags &= ~(TMAP_FLAG_CORRECT);
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);
1693 v[1].u = ui * float(idx+1);
1694 v[1].v = vi * float(s_idx);
1696 v[2].u = ui * float(idx+1);
1697 v[2].v = vi * float(s_idx+1);
1699 v[3].u = ui * float(idx);
1700 v[3].v = vi * float(s_idx+1);
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);
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);
1732 gr_zbuffer_set(saved_zbuffer_mode);
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)
1742 int saved_zbuffer_mode;
1744 vertex *vertlist[4] = { &v[0], &v[1], &v[2], &v[3] };
1750 // turn off zbuffering
1751 saved_zbuffer_mode = gr_zbuffer_get();
1752 gr_zbuffer_set(GR_ZBUFF_NONE);
1754 bm_get_section_size(gr_screen.current_bitmap, gr_screen.current_bitmap_sx, gr_screen.current_bitmap_sy, &bw, &bh);
1762 v[0].flags = PF_PROJECTED;
1765 v[1].sx = (float)(x + w);
1770 v[1].flags = PF_PROJECTED;
1773 v[2].sx = (float)(x + w);
1774 v[2].sy = (float)(y + h);
1778 v[2].flags = PF_PROJECTED;
1782 v[3].sy = (float)(y + h);
1786 v[3].flags = PF_PROJECTED;
1793 v[0].u = 0.5f / i2fl(bw);
1794 v[0].v = 0.5f / i2fl(bh);
1795 v[0].flags = PF_PROJECTED;
1798 v[1].sx = (float)(x + w);
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;
1806 v[2].sx = (float)(x + w);
1807 v[2].sy = (float)(y + h);
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;
1815 v[3].sy = (float)(y + h);
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;
1827 ret = g3_draw_poly_constant_sw(4, vertlist, TMAP_FLAG_TEXTURED | additional_tmap_flags, 0.1f);
1831 gr_zbuffer_set(saved_zbuffer_mode);
1833 // put filtering back on