2 * $Logfile: /Freespace2/code/Render/3ddraw.cpp $
7 * 3D rendering primitives
10 * Revision 1.3 2002/05/07 03:16:51 theoddone33
11 * The Great Newline Fix
13 * Revision 1.2 2002/05/03 13:34:33 theoddone33
16 * Revision 1.1.1.1 2002/05/03 03:28:10 root
20 * 18 9/06/99 3:23p Andsager
21 * Make fireball and weapon expl ani LOD choice look at resolution of the
24 * 17 8/27/99 9:07p Dave
25 * LOD explosions. Improved beam weapon accuracy.
27 * 16 7/29/99 12:05a Dave
28 * Nebula speed optimizations.
30 * 15 7/13/99 1:16p Dave
31 * 32 bit support. Whee!
33 * 14 7/02/99 3:05p Anoop
34 * Oops. Fixed g3_draw_2d_poly() so that it properly handles poly bitmap
35 * and LFB bitmap calls.
37 * 13 6/29/99 10:35a Dave
38 * Interface polygon bitmaps! Whee!
40 * 12 6/22/99 7:03p Dave
41 * New detail options screen.
43 * 11 6/16/99 4:06p Dave
44 * New pilot info popup. Added new draw-bitmap-as-poly function.
46 * 10 6/08/99 5:17p Dave
47 * Fixed up perspective bitmap drawing.
49 * 9 6/03/99 6:37p Dave
50 * More TNT fun. Made perspective bitmaps more flexible.
52 * 8 5/28/99 1:58p Dave
53 * Fixed problem with overwriting bank value drawing perspective bitmaps.
55 * 7 5/28/99 1:45p Dave
56 * Fixed up perspective bitmap drawing.
58 * 6 5/24/99 5:45p Dave
59 * Added detail levels to the nebula, with a decent speedup. Split nebula
60 * lightning into its own section.
62 * 5 4/07/99 6:22p Dave
63 * Fred and Freespace support for multiple background bitmaps and suns.
64 * Fixed link errors on all subprojects. Moved encrypt_init() to
65 * cfile_init() and lcl_init(), since its safe to call twice.
67 * 4 3/09/99 6:24p Dave
68 * More work on object update revamping. Identified several sources of
69 * unnecessary bandwidth.
71 * 3 2/11/99 3:08p Dave
72 * PXO refresh button. Very preliminary squad war support.
74 * 2 10/07/98 10:53a Dave
77 * 1 10/07/98 10:51a Dave
79 * 37 3/22/98 2:34p John
80 * If alpha effects is on lowest detail level, draw rotated bitmaps as
83 * 36 3/16/98 4:51p John
84 * Added low-level code to clip all polygons against an arbritary plane.
85 * Took out all old model_interp_zclip and used this new method instead.
87 * 35 3/04/98 9:29a John
88 * Made rotated bitmaps force all sw's to the same value after clipping.
90 * 34 3/03/98 6:59p John
92 * 33 1/26/98 5:12p John
93 * Added in code for Pentium Pro specific optimizations. Speed up
94 * zbuffered correct tmapper about 35%. Speed up non-zbuffered scalers
97 * 32 1/15/98 2:16p John
98 * Made bitmaps zbuffer at center minus radius.
99 * Made fireballs sort after objects they are around.
101 * 31 1/06/98 2:11p John
102 * Made g3_draw_rotated bitmap draw the same orientation as g3_draw_bitmap
103 * (orient=0) and made it be the same size (it was 2x too big before).
105 * 30 12/30/97 6:44p John
106 * Made g3_Draw_bitmap functions account for aspect of bitmap.
108 * 29 12/04/97 12:09p John
109 * Made glows use scaler instead of tmapper so they don't rotate. Had to
110 * add a zbuffered scaler.
112 * 28 12/02/97 4:00p John
113 * Added first rev of thruster glow, along with variable levels of
114 * translucency, which retquired some restructing of palman.
116 * 27 11/29/97 2:05p John
117 * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2,
118 * like they used to incorrectly assume. Added code to model to read in
121 * 26 10/20/97 4:49p John
122 * added weapon trails.
123 * added facing bitmap code to 3d lib.
125 * 25 10/03/97 9:10a John
126 * added better antialiased line drawer
128 * 24 9/09/97 3:39p Sandeep
129 * warning level 4 bugs
131 * 23 7/11/97 11:54a John
132 * added rotated 3d bitmaps.
134 * 22 5/07/97 2:59p John
135 * Initial rev of D3D texturing.
137 * 21 4/29/97 9:55a John
139 * 20 3/10/97 2:25p John
140 * Made pofview zbuffer. Made textest work with new model code. Took
141 * out some unnecessary Asserts in the 3d clipper.
144 * 19 3/06/97 5:36p Mike
145 * Change vec_normalize_safe() back to vec_normalize().
146 * Spruce up docking a bit.
148 * 18 3/06/97 10:56a Mike
149 * Write error checking version of vm_vec_normalize().
150 * Fix resultant problems.
152 * 17 2/26/97 5:24p John
153 * Added g3_draw_sphere_ez
155 * 16 2/17/97 5:18p John
156 * Added a bunch of RCS headers to a bunch of old files that don't have
162 #include "3dinternal.h"
166 #include "floating.h"
167 #include "physics.h" // For Physics_viewer_bank for g3_draw_rotated_bitmap
169 #include "systemvars.h"
170 #include "alphacolors.h"
174 //deal with a clipped line
175 int must_clip_line(vertex *p0,vertex *p1,ubyte codes_or, uint flags)
179 clip_line(&p0,&p1,codes_or, flags);
181 if (p0->codes & p1->codes) goto free_points;
183 codes_or = (unsigned char)(p0->codes | p1->codes);
185 if (codes_or & CC_BEHIND) goto free_points;
187 if (!(p0->flags&PF_PROJECTED))
188 g3_project_vertex(p0);
190 if (p0->flags&PF_OVERFLOW) goto free_points;
192 if (!(p1->flags&PF_PROJECTED))
193 g3_project_vertex(p1);
195 if (p1->flags&PF_OVERFLOW) goto free_points;
197 //gr_line(fl2i(p0->sx),fl2i(p0->sy),fl2i(p1->sx),fl2i(p1->sy));
198 // gr_line_float(p0->sx,p0->sy,p1->sx,p1->sy);
207 if (p0->flags & PF_TEMP_POINT)
210 if (p1->flags & PF_TEMP_POINT)
216 //draws a line. takes two points. returns true if drew
217 int g3_draw_line(vertex *p0,vertex *p1)
221 Assert( G3_count == 1 );
223 if (p0->codes & p1->codes)
226 codes_or = (unsigned char)(p0->codes | p1->codes);
228 if (codes_or & CC_BEHIND)
229 return must_clip_line(p0,p1,codes_or,0);
231 if (!(p0->flags&PF_PROJECTED))
232 g3_project_vertex(p0);
234 if (p0->flags&PF_OVERFLOW)
236 return must_clip_line(p0,p1,codes_or,0);
239 if (!(p1->flags&PF_PROJECTED))
240 g3_project_vertex(p1);
242 if (p1->flags&PF_OVERFLOW)
244 return must_clip_line(p0,p1,codes_or,0);
247 // gr_line(fl2i(p0->sx),fl2i(p0->sy),fl2i(p1->sx),fl2i(p1->sy));
248 // gr_line_float(p0->sx,p0->sy,p1->sx,p1->sy);
255 //returns true if a plane is facing the viewer. takes the unrotated surface
256 //normal of the plane, and a point on it. The normal need not be normalized
257 int g3_check_normal_facing(vector *v,vector *norm)
261 Assert( G3_count == 1 );
263 vm_vec_sub(&tempv,&View_position,v);
265 return (vm_vec_dot(&tempv,norm) > 0.0f);
268 int do_facing_check(vector *norm,vertex **vertlist,vector *p)
270 Assert( G3_count == 1 );
272 if (norm) { //have normal
274 Assert(norm->x || norm->y || norm->z);
276 return g3_check_normal_facing(p,norm);
278 else { //normal not specified, so must compute
282 //get three points (rotated) and compute normal
284 vm_vec_perp(&tempv,(vector *)&vertlist[0]->x,(vector *)&vertlist[1]->x,(vector *)&vertlist[2]->x);
286 return (vm_vec_dot(&tempv,(vector *)&vertlist[1]->x ) < 0.0);
290 //like g3_draw_poly(), but checks to see if facing. If surface normal is
291 //NULL, this routine must compute it, which will be slow. It is better to
292 //pre-compute the normal, and pass it to this function. When the normal
293 //is passed, this function works like g3_check_normal_facing() plus
295 //returns -1 if not facing, 1 if off screen, 0 if drew
296 int g3_draw_poly_if_facing(int nv,vertex **pointlist,uint tmap_flags,vector *norm,vector *pnt)
298 Assert( G3_count == 1 );
300 if (do_facing_check(norm,pointlist,pnt))
301 return g3_draw_poly(nv,pointlist,tmap_flags);
307 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
308 //returns 1 if off screen, 0 if drew
309 int g3_draw_poly(int nv,vertex **pointlist,uint tmap_flags)
315 Assert( G3_count == 1 );
317 cc.vor = 0; cc.vand = 0xff;
324 p = bufptr[i] = pointlist[i];
331 return 1; //all points off screen
334 Assert( G3_count == 1 );
336 bufptr = clip_polygon(Vbuf0,Vbuf1,&nv,&cc,tmap_flags);
338 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
341 vertex *p = bufptr[i];
343 if (!(p->flags&PF_PROJECTED))
344 g3_project_vertex(p);
346 if (p->flags&PF_OVERFLOW) {
347 //Int3(); //should not overflow after clip
348 //printf( "overflow in must_clip_tmap_face\n" );
353 gr_tmapper( nv, bufptr, tmap_flags );
360 if (bufptr[i]->flags & PF_TEMP_POINT)
361 free_temp_point(bufptr[i]);
364 //now make list of 2d coords (& check for overflow)
367 vertex *p = bufptr[i];
369 if (!(p->flags&PF_PROJECTED))
370 g3_project_vertex(p);
372 if (p->flags&PF_OVERFLOW) {
373 //Int3(); //should not overflow after clip
374 //printf( "3d: Point overflowed, but flags say OK!\n" );
380 gr_tmapper( nv, bufptr, tmap_flags );
382 return 0; //say it drew
387 // Draw a polygon. Same as g3_draw_poly, but it bashes sw to a constant value
388 // for all vertexes. Needs to be done after clipping to get them all.
389 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
390 //returns 1 if off screen, 0 if drew
391 int g3_draw_poly_constant_sw(int nv,vertex **pointlist,uint tmap_flags, float constant_sw)
397 Assert( G3_count == 1 );
399 cc.vor = 0; cc.vand = 0xff;
406 p = bufptr[i] = pointlist[i];
413 return 1; //all points off screen
416 Assert( G3_count == 1 );
418 bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags);
420 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
423 vertex *p = bufptr[i];
425 if (!(p->flags&PF_PROJECTED))
426 g3_project_vertex(p);
428 if (p->flags&PF_OVERFLOW) {
429 //Int3(); //should not overflow after clip
430 //printf( "overflow in must_clip_tmap_face\n" );
437 gr_tmapper( nv, bufptr, tmap_flags );
439 // draw lines connecting the faces
441 gr_set_color_fast(&Color_bright_green);
442 for(i=0; i<nv-1; i++){
443 g3_draw_line(bufptr[i], bufptr[i+1]);
445 g3_draw_line(bufptr[0], bufptr[i]);
453 if (bufptr[i]->flags & PF_TEMP_POINT){
454 free_temp_point(bufptr[i]);
458 //now make list of 2d coords (& check for overflow)
461 vertex *p = bufptr[i];
463 if (!(p->flags&PF_PROJECTED))
464 g3_project_vertex(p);
466 if (p->flags&PF_OVERFLOW) {
467 //Int3(); //should not overflow after clip
468 //printf( "3d: Point overflowed, but flags say OK!\n" );
475 gr_tmapper( nv, bufptr, tmap_flags );
477 // draw lines connecting the faces
479 gr_set_color_fast(&Color_bright_green);
480 for(i=0; i<nv-1; i++){
481 g3_draw_line(bufptr[i], bufptr[i+1]);
483 g3_draw_line(bufptr[0], bufptr[i]);
486 return 0; //say it drew
489 //draw a sortof sphere - i.e., the 2d radius is proportional to the 3d
490 //radius, but not to the distance from the eye
491 int g3_draw_sphere(vertex *pnt,float rad)
493 Assert( G3_count == 1 );
495 if (! (pnt->codes & CC_BEHIND)) {
497 if (! (pnt->flags & PF_PROJECTED))
498 g3_project_vertex(pnt);
500 if (! (pnt->codes & PF_OVERFLOW)) {
503 r2 = rad*Matrix_scale.x;
507 gr_circle(fl2i(pnt->sx),fl2i(pnt->sy),fl2i(t*2.0f));
514 int g3_draw_sphere_ez(vector *pnt,float rad)
519 Assert( G3_count == 1 );
521 flags = g3_rotate_vertex(&pt,pnt);
525 g3_project_vertex(&pt);
527 if (!(pt.flags & PF_OVERFLOW)) {
529 g3_draw_sphere( &pt, rad );
537 //draws a bitmap with the specified 3d width & height
538 //returns 1 if off screen, 0 if drew
540 int g3_draw_bitmap(vertex *pnt,int orient, float rad,uint tmap_flags)
546 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
549 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
553 height = width*i2fl(bh)/i2fl(bw);
554 } else if ( bw > bh ) {
556 width = height*i2fl(bw)/i2fl(bh);
558 width = height = rad*2.0f;
561 width = height = rad*2.0f;
564 Assert( G3_count == 1 );
566 if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) )
569 if (!(pnt->flags&PF_PROJECTED))
570 g3_project_vertex(pnt);
572 if (pnt->flags & PF_OVERFLOW)
575 t = (width*Canv_w2)/pnt->z;
576 w = t*Matrix_scale.x;
578 t = (height*Canv_h2)/pnt->z;
579 h = t*Matrix_scale.y;
582 z = pnt->z - rad/2.0f;
590 va.sx = pnt->sx - w/2.0f;
591 va.sy = pnt->sy - h/2.0f;
621 // get bitmap dims onscreen as if g3_draw_bitmap() had been called
622 int g3_get_bitmap_dims(int bitmap, vertex *pnt, float rad, int *x, int *y, int *w, int *h, int *size)
629 bm_get_info( bitmap, &bw, &bh, NULL );
633 height = width*i2fl(bh)/i2fl(bw);
634 } else if ( bw > bh ) {
636 width = height*i2fl(bw)/i2fl(bh);
638 width = height = rad*2.0f;
641 Assert( G3_count == 1 );
643 if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) ) {
647 if (!(pnt->flags&PF_PROJECTED)){
648 g3_project_vertex(pnt);
651 if (pnt->flags & PF_OVERFLOW){
655 t = (width*Canv_w2)/pnt->z;
656 *w = (int)(t*Matrix_scale.x);
658 t = (height*Canv_h2)/pnt->z;
659 *h = (int)(t*Matrix_scale.y);
661 *x = (int)(pnt->sx - *w/2.0f);
662 *y = (int)(pnt->sy - *h/2.0f);
669 //draws a bitmap with the specified 3d width & height
670 //returns 1 if off screen, 0 if drew
671 int g3_draw_rotated_bitmap(vertex *pnt,float angle, float rad,uint tmap_flags)
674 vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] };
679 if ( !Detail.alpha_effects ) {
681 if ( angle < PI/2 ) {
683 } else if ( angle < PI ) {
685 } else if ( angle < PI+PI/2 ) {
690 return g3_draw_bitmap( pnt, ang, rad, tmap_flags );
694 Assert( G3_count == 1 );
696 angle+=Physics_viewer_bank;
699 else if ( angle > PI2 )
703 sa = (float)sin(angle);
704 ca = (float)cos(angle);
708 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
711 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
715 height = width*i2fl(bh)/i2fl(bw);
716 } else if ( bw > bh ) {
718 width = height*i2fl(bw)/i2fl(bh);
720 width = height = rad;
723 width = height = rad;
727 v[0].x = (-width*ca + height*sa)*Matrix_scale.x + pnt->x;
728 v[0].y = (-width*sa - height*ca)*Matrix_scale.y + pnt->y;
734 v[1].x = (width*ca + height*sa)*Matrix_scale.x + pnt->x;
735 v[1].y = (width*sa - height*ca)*Matrix_scale.y + pnt->y;
741 v[2].x = (width*ca - height*sa)*Matrix_scale.x + pnt->x;
742 v[2].y = (width*sa + height*ca)*Matrix_scale.y + pnt->y;
748 v[3].x = (-width*ca - height*sa)*Matrix_scale.x + pnt->x;
749 v[3].y = (-width*sa + height*ca)*Matrix_scale.y + pnt->y;
755 ubyte codes_and=0xff;
758 z = pnt->z - rad / 4.0f;
759 if ( z < 0.0f ) z = 0.0f;
762 for (i=0; i<4; i++ ) {
763 //now code the four points
764 codes_and &= g3_code_vertex(&v[i]);
765 v[i].flags = 0; // mark as not yet projected
766 //g3_project_vertex(&v[i]);
770 return 1; //1 means off screen
773 g3_draw_poly_constant_sw(4, vertlist, tmap_flags, sw );
778 #define TRIANGLE_AREA(_p, _q, _r) do { vector a, b, cross; a.x = _q->x - _p->x; a.y = _q->y - _p->y; a.z = 0.0f; b.x = _r->x - _p->x; b.y = _r->y - _p->y; b.z = 0.0f; vm_vec_crossprod(&cross, &a, &b); total_area += vm_vec_mag(&cross) * 0.5f; } while(0);
779 float g3_get_poly_area(int nv, vertex **pointlist)
782 float total_area = 0.0f;
785 for(idx=1; idx<nv-1; idx++){
786 TRIANGLE_AREA(pointlist[0], pointlist[idx], pointlist[idx+1]);
793 // Draw a polygon. Same as g3_draw_poly, but it bashes sw to a constant value
794 // for all vertexes. Needs to be done after clipping to get them all.
795 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
796 //returns 1 if off screen, 0 if drew
797 float g3_draw_poly_constant_sw_area(int nv, vertex **pointlist, uint tmap_flags, float constant_sw, float area)
804 Assert( G3_count == 1 );
806 cc.vor = 0; cc.vand = 0xff;
813 p = bufptr[i] = pointlist[i];
820 return 0.0f; //all points off screen
824 Assert( G3_count == 1 );
826 bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags);
828 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
831 vertex *p = bufptr[i];
833 if (!(p->flags&PF_PROJECTED))
834 g3_project_vertex(p);
836 if (p->flags&PF_OVERFLOW) {
837 //Int3(); //should not overflow after clip
838 //printf( "overflow in must_clip_tmap_face\n" );
846 p_area = g3_get_poly_area(nv, bufptr);
851 gr_tmapper( nv, bufptr, tmap_flags );
858 if (bufptr[i]->flags & PF_TEMP_POINT){
859 free_temp_point(bufptr[i]);
863 //now make list of 2d coords (& check for overflow)
866 vertex *p = bufptr[i];
868 if (!(p->flags&PF_PROJECTED))
869 g3_project_vertex(p);
871 if (p->flags&PF_OVERFLOW) {
879 p_area = g3_get_poly_area(nv, bufptr);
884 gr_tmapper( nv, bufptr, tmap_flags );
887 // how much area we drew
892 //draws a bitmap with the specified 3d width & height
893 //returns 1 if off screen, 0 if drew
894 float g3_draw_rotated_bitmap_area(vertex *pnt,float angle, float rad,uint tmap_flags, float area)
897 vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] };
901 Assert( G3_count == 1 );
903 angle+=Physics_viewer_bank;
906 } else if ( angle > PI2 ) {
910 sa = (float)sin(angle);
911 ca = (float)cos(angle);
915 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
918 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
922 height = width*i2fl(bh)/i2fl(bw);
923 } else if ( bw > bh ) {
925 width = height*i2fl(bw)/i2fl(bh);
927 width = height = rad;
930 width = height = rad;
934 v[0].x = (-width*ca + height*sa)*Matrix_scale.x + pnt->x;
935 v[0].y = (-width*sa - height*ca)*Matrix_scale.y + pnt->y;
941 v[1].x = (width*ca + height*sa)*Matrix_scale.x + pnt->x;
942 v[1].y = (width*sa - height*ca)*Matrix_scale.y + pnt->y;
948 v[2].x = (width*ca - height*sa)*Matrix_scale.x + pnt->x;
949 v[2].y = (width*sa + height*ca)*Matrix_scale.y + pnt->y;
955 v[3].x = (-width*ca - height*sa)*Matrix_scale.x + pnt->x;
956 v[3].y = (-width*sa + height*ca)*Matrix_scale.y + pnt->y;
962 ubyte codes_and=0xff;
965 z = pnt->z - rad / 4.0f;
966 if ( z < 0.0f ) z = 0.0f;
969 for (i=0; i<4; i++ ) {
970 //now code the four points
971 codes_and &= g3_code_vertex(&v[i]);
972 v[i].flags = 0; // mark as not yet projected
973 //g3_project_vertex(&v[i]);
981 return g3_draw_poly_constant_sw_area(4, vertlist, tmap_flags, sw, area );
987 typedef struct horz_pt {
992 //draws a horizon. takes eax=sky_color, edx=ground_color
993 void g3_draw_horizon_line()
995 //int sky_color,int ground_color
998 horz_pt horz_pts[4]; // 0 = left, 1 = right
999 // int top_color, bot_color;
1000 // int color_swap; //flag for if we swapped
1001 // int sky_ground_flag; //0=both, 1=all sky, -1=all gnd
1005 float up_right, down_right,down_left,up_left;
1007 // color_swap = 0; //assume no swap
1008 // sky_ground_flag = 0; //assume both
1010 // if ( View_matrix.uvec.y < 0.0f )
1012 // else if ( View_matrix.uvec.y == 0.0f ) {
1013 // if ( View_matrix.uvec.x > 0.0f )
1017 // if (color_swap) {
1018 // top_color = ground_color;
1019 // bot_color = sky_color;
1021 // top_color = sky_color;
1022 // bot_color = ground_color;
1025 Assert( G3_count == 1 );
1028 //compute horizon_vector
1030 horizon_vec.x = Unscaled_matrix.rvec.y*Matrix_scale.y*Matrix_scale.z;
1031 horizon_vec.y = Unscaled_matrix.uvec.y*Matrix_scale.x*Matrix_scale.z;
1032 horizon_vec.z = Unscaled_matrix.fvec.y*Matrix_scale.x*Matrix_scale.y;
1034 // now compute values & flag for 4 corners.
1035 up_right = horizon_vec.x + horizon_vec.y + horizon_vec.z;
1036 down_right = horizon_vec.x - horizon_vec.y + horizon_vec.z;
1037 down_left = -horizon_vec.x - horizon_vec.y + horizon_vec.z;
1038 up_left = -horizon_vec.x + horizon_vec.y + horizon_vec.z;
1040 //check flags for all sky or all ground.
1041 if ( (up_right<0.0f)&&(down_right<0.0f)&&(down_left<0.0f)&&(up_left<0.0f) ) {
1042 // mprintf(( "All ground.\n" ));
1046 if ( (up_right>0.0f)&&(down_right>0.0f)&&(down_left>0.0f)&&(up_left>0.0f) ) {
1047 // mprintf(( "All sky.\n" ));
1051 //mprintf(( "Horizon vec = %.4f, %.4f, %.4f\n", horizon_vec.x, horizon_vec.y, horizon_vec.z ));
1052 //mprintf(( "%.4f, %.4f, %.4f, %.4f\n", up_right, down_right, down_left, up_left ));
1055 // mprintf(( "u: %.4f %.4f %.4f c: %.4f %.4f %.4f %.4f\n",Unscaled_matrix.uvec.y,Unscaled_matrix.uvec.z,Unscaled_matrix.uvec.x,up_left,up_right,down_right,down_left ));
1056 // check for intesection with each of four edges & compute horizon line
1059 // check intersection with left edge
1060 s1 = up_left > 0.0f;
1061 s2 = down_left > 0.0f;
1063 horz_pts[cpnt].x = 0.0f;
1064 horz_pts[cpnt].y = fl_abs(up_left * Canv_h2 / horizon_vec.y);
1065 horz_pts[cpnt].edge = 0;
1069 // check intersection with top edge
1070 s1 = up_left > 0.0f;
1071 s2 = up_right > 0.0f;
1073 horz_pts[cpnt].x = fl_abs(up_left * Canv_w2 / horizon_vec.x);
1074 horz_pts[cpnt].y = 0.0f;
1075 horz_pts[cpnt].edge = 1;
1080 // check intersection with right edge
1081 s1 = up_right > 0.0f;
1082 s2 = down_right > 0.0f;
1084 horz_pts[cpnt].x = i2fl(Canvas_width)-1;
1085 horz_pts[cpnt].y = fl_abs(up_right * Canv_h2 / horizon_vec.y);
1086 horz_pts[cpnt].edge = 2;
1090 //check intersection with bottom edge
1091 s1 = down_right > 0.0f;
1092 s2 = down_left > 0.0f;
1094 horz_pts[cpnt].x = fl_abs(down_left * Canv_w2 / horizon_vec.x);
1095 horz_pts[cpnt].y = i2fl(Canvas_height)-1;
1096 horz_pts[cpnt].edge = 3;
1101 mprintf(( "HORZ: Wrong number of points (%d)\n", cpnt ));
1105 //make sure first edge is left
1107 if ( horz_pts[0].x > horz_pts[1].x ) {
1110 horz_pts[0] = horz_pts[1];
1115 // draw from left to right.
1116 gr_line( fl2i(horz_pts[0].x),fl2i(horz_pts[0].y),fl2i(horz_pts[1].x),fl2i(horz_pts[1].y) );
1123 horizon_poly dw 5 dup (?,?) ;max of 5 points
1125 ;for g3_compute_horz_vecs
1132 ;for compute corner vec
1143 _TEXT segment dword public USE32 'CODE'
1145 extn gr_setcolor_,gr_clear_canvas_
1148 ;draw a polygon (one half of horizon) from the horizon line
1149 draw_horz_poly: lea ebx,horizon_poly
1151 ;copy horizon line as first points in poly
1163 ;add corners to polygon
1165 mov eax,8[esi] ;edge number of start edge
1167 mov ecx,8[edi] ;edge number of end point
1168 sub ecx,eax ;number of edges
1172 mov edx,ecx ;save count
1174 lea esi,corners[eax] ;first corner
1175 lea edi,16[ebx] ;rest of poly
1177 movsd ;copy a corner
1178 cmp esi,offset corners+8*4 ;end of list?
1181 no_wrap: loop corner_loop
1183 ;now draw the polygon
1184 mov eax,edx ;get corner count
1185 add eax,2 ;..plus horz line end points
1186 lea edx,horizon_poly ;get the points
1187 ;; call gr_poly_ ;draw it!
1191 ;return information on the polygon that is the sky.
1192 ;takes ebx=ptr to x,y pairs, ecx=ptr to vecs for each point
1193 ;returns eax=number of points
1194 ;IMPORTANT: g3_draw_horizon() must be called before this routine.
1195 g3_compute_sky_polygon:
1196 test sky_ground_flag,-1 ;what was drawn
1200 pushm ebx,ecx,edx,esi,edi
1206 xchg esi,edi ;sky isn't top
1209 ;copy horizon line as first points in poly
1211 mov eax,[edi] ;copy end point
1216 mov eax,[esi] ;copy start point
1222 push edi ;save end point
1223 push esi ;save start point
1224 mov esi,edi ;end point is first point
1225 mov edi,ecx ;dest buffer
1226 call compute_horz_end_vec
1228 pop esi ;get back start point
1230 call compute_horz_end_vec
1232 pop edi ;get back end point
1234 add ebx,16 ;past two x,y pairs
1235 add ecx,24 ;past two vectors
1239 ;add corners to polygon
1241 mov eax,8[esi] ;edge number of start edge
1244 mov ecx,8[edi] ;edge number of end point
1245 sub ecx,eax ;number of edges
1249 push ecx ;save count
1251 lea esi,corners[eax] ;first corner
1252 mov edi,ebx ;rest of poly 2d points
1255 movsd ;copy a corner
1256 cmp esi,offset corners+8*4 ;end of list?
1263 call compute_corner_vec
1270 ;now return with count
1271 pop eax ;get corner count
1272 add eax,2 ;..plus horz line end points
1274 popm ebx,ecx,edx,esi,edi
1278 ;we drew all ground, so there was no horizon drawn
1279 was_all_ground: xor eax,eax ;no points in poly
1282 ;we drew all sky, so find 4 corners
1283 was_all_sky: pushm ebx,ecx,edx,esi,edi
1292 xor eax,eax ;start corner 0
1293 sky_loop: pushm eax,ecx,edi
1294 call compute_corner_vec
1299 mov eax,4 ;4 corners
1300 popm ebx,ecx,edx,esi,edi
1303 ;compute vector describing horizon intersection with a point.
1304 ;takes esi=2d point, edi=vec. trashes eax,ebx,ecx,edx
1305 compute_horz_end_vec:
1307 ;compute rotated x/z & y/z ratios
1309 mov eax,[esi] ;get x coord
1314 mov eax,4[esi] ;get y coord
1317 neg eax ;y inversion
1320 ;compute fraction unrotated x/z
1325 fixmul View_matrix.m9
1326 sub eax,View_matrix.m7
1327 sub eax,View_matrix.m8
1328 mov ebx,eax ;save numerator
1331 fixmul View_matrix.m3
1333 mov eax,View_matrix.m1
1334 add eax,View_matrix.m2
1337 ;now eax/ebx = z/x. do divide in way to give result < 0
1343 cmp eax,ebx ;which is bigger?
1347 ;x is bigger, so do as z/x
1351 ;now eax = z/x ratio. Compute vector by normalizing and correcting sign
1353 push eax ;save ratio
1355 imul eax ;compute z*z
1356 inc edx ;+ x*x (x==1)
1359 mov ecx,eax ;mag in ecx
1360 pop eax ;get ratio, x part
1372 ;z is bigger, so do as x/z
1377 ;now eax = x/z ratio. Compute vector by normalizing and correcting sign
1379 push eax ;save ratio
1381 imul eax ;compute x*x
1382 inc edx ;+ z*z (z==1)
1385 mov ecx,eax ;mag in ecx
1386 pop eax ;get ratio, x part
1396 finish_end: xor eax,eax ;y = 0
1399 ;now make sure that this vector is in front of you, not behind
1409 jns vec_ok ;has positive z, ok
1411 ;z is neg, flip vector
1426 ;compute vector decribing a corner of the screen.
1427 ;takes edi=vector, eax=corner num
1436 mov ebx,View_matrix.m1
1437 mov ecx,View_matrix.m4
1438 mov edx,View_matrix.m7
1449 sub ebx,View_matrix.m3
1451 sub ecx,View_matrix.m6
1453 sub edx,View_matrix.m9
1456 mov ebx,View_matrix.m5
1457 mov ecx,View_matrix.m2
1458 mov edx,View_matrix.m8
1467 sub ebx,View_matrix.m6
1469 sub ecx,View_matrix.m3
1471 sub edx,View_matrix.m9
1476 ;compute denomonator
1484 sub eax,ebx ;eax = denominator
1486 ;now we have the denominator. If it is too small, try x/y, z/y or z/x, y/x
1488 mov ecx,eax ;save den
1496 ;now do x/z numerator
1499 fixmul m56 ;* (m5-m6)
1503 fixmul m46 ;* (m4-m6)
1506 ;now, eax/ecx = x/z ratio
1508 fixdiv ecx ;eax = x/z
1510 mov [edi].x,eax ;save x
1522 ;now eax/ecx = y/z ratio
1531 call vm_vec_normalize
1533 ;make sure this vec is pointing in right direction
1535 lea edi,View_matrix.fvec
1537 or eax,eax ;check sign
1555 // Draws a polygon always facing the viewer.
1556 // compute the corners of a rod. fills in vertbuf.
1557 // Verts has any needs uv's or l's or can be NULL if none needed.
1558 int g3_draw_rod(vector *p0,float width1,vector *p1,float width2, vertex * verts, uint tmap_flags)
1560 vector uvec, fvec, rvec, center;
1562 vm_vec_sub( &fvec, p0, p1 );
1563 vm_vec_normalize_safe( &fvec );
1565 vm_vec_avg( ¢er, p0, p1 );
1566 vm_vec_sub( &rvec, &Eye_position, ¢er );
1567 vm_vec_normalize( &rvec );
1569 vm_vec_crossprod(&uvec,&fvec,&rvec);
1571 //normalize new perpendicular vector
1572 vm_vec_normalize(&uvec);
1574 //now recompute right vector, in case it wasn't entirely perpendiclar
1575 vm_vec_crossprod(&rvec,&uvec,&fvec);
1577 // Now have uvec, which is up vector and rvec which is the normal
1583 vertex *ptlist[4] = { &pts[3], &pts[2], &pts[1], &pts[0] };
1585 vm_vec_scale_add( &vecs[0], p0, &uvec, width1/2.0f );
1586 vm_vec_scale_add( &vecs[1], p1, &uvec, width2/2.0f );
1587 vm_vec_scale_add( &vecs[2], p1, &uvec, -width2/2.0f );
1588 vm_vec_scale_add( &vecs[3], p0, &uvec, -width1/2.0f );
1590 for (i=0; i<4; i++ ) {
1594 g3_rotate_vertex( &pts[i], &vecs[i] );
1597 return g3_draw_poly(4,ptlist,tmap_flags);
1600 // draw a perspective bitmap based on angles and radius
1601 vector g3_square[4] = {
1602 {-1.0f, -1.0f, 20.0f},
1603 {-1.0f, 1.0f, 20.0f},
1604 {1.0f, 1.0f, 20.0f},
1605 {1.0f, -1.0f, 20.0f}
1608 #define MAX_PERSPECTIVE_DIVISIONS 5 // should never even come close to this limit
1610 void stars_project_2d_onto_sphere( vector *pnt, float rho, float phi, float theta )
1612 float a = 3.14159f * phi;
1613 float b = 6.28318f * theta;
1614 float sin_a = (float)sin(a);
1617 pnt->z = rho * sin_a * (float)cos(b);
1618 pnt->y = rho * sin_a * (float)sin(b);
1619 pnt->x = rho * (float)cos(a);
1622 // draw a perspective bitmap based on angles and radius
1623 float p_phi = 10.0f;
1624 float p_theta = 10.0f;
1625 int g3_draw_perspective_bitmap(angles *a, float scale_x, float scale_y, int div_x, int div_y, uint tmap_flags)
1627 vector s_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
1628 vector t_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
1633 int saved_zbuffer_mode;
1637 // cap division values
1638 // div_x = div_x > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_x;
1640 div_y = div_y > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_y;
1642 // texture increment values
1643 ui = 1.0f / (float)div_x;
1644 vi = 1.0f / (float)div_y;
1646 // adjust for aspect ratio
1647 scale_x *= ((float)gr_screen.max_w / (float)gr_screen.max_h) + 0.55f; // fudge factor
1649 float s_phi = 0.5f + (((p_phi * scale_x) / 360.0f) / 2.0f);
1650 float s_theta = (((p_theta * scale_y) / 360.0f) / 2.0f);
1651 float d_phi = -(((p_phi * scale_x) / 360.0f) / (float)(div_x));
1652 float d_theta = -(((p_theta * scale_y) / 360.0f) / (float)(div_y));
1655 bank_first.p = 0.0f;
1656 bank_first.b = a->b;
1657 bank_first.h = 0.0f;
1658 vm_angles_2_matrix(&m_bank, &bank_first);
1660 // convert angles to matrix
1661 float b_save = a->b;
1663 vm_angles_2_matrix(&m, a);
1666 // generate the bitmap points
1667 for(idx=0; idx<=div_x; idx++){
1668 for(s_idx=0; s_idx<=div_y; s_idx++){
1669 // get world spherical coords
1670 stars_project_2d_onto_sphere(&s_points[idx][s_idx], 1000.0f, s_phi + ((float)idx*d_phi), s_theta + ((float)s_idx*d_theta));
1672 // bank the bitmap first
1673 vm_vec_rotate(&t_points[idx][s_idx], &s_points[idx][s_idx], &m_bank);
1675 // rotate on the sphere
1676 vm_vec_rotate(&s_points[idx][s_idx], &t_points[idx][s_idx], &m);
1680 // turn off zbuffering
1681 saved_zbuffer_mode = gr_zbuffer_get();
1682 gr_zbuffer_set(GR_ZBUFF_NONE);
1689 tmap_flags &= ~(TMAP_FLAG_CORRECT);
1693 for(idx=0; idx<div_x; idx++){
1694 for(s_idx=0; s_idx<div_y; s_idx++){
1695 // stuff texture coords
1696 v[0].u = ui * float(idx);
1697 v[0].v = vi * float(s_idx);
1699 v[1].u = ui * float(idx+1);
1700 v[1].v = vi * float(s_idx);
1702 v[2].u = ui * float(idx+1);
1703 v[2].v = vi * float(s_idx+1);
1705 v[3].u = ui * float(idx);
1706 v[3].v = vi * float(s_idx+1);
1715 g3_rotate_faraway_vertex(verts[0], &s_points[idx][s_idx]);
1716 g3_rotate_faraway_vertex(verts[1], &s_points[idx+1][s_idx]);
1717 g3_rotate_faraway_vertex(verts[2], &s_points[idx+1][s_idx+1]);
1718 g3_draw_poly(3, verts, tmap_flags);
1727 g3_rotate_faraway_vertex(verts[0], &s_points[idx][s_idx]);
1728 g3_rotate_faraway_vertex(verts[1], &s_points[idx+1][s_idx+1]);
1729 g3_rotate_faraway_vertex(verts[2], &s_points[idx][s_idx+1]);
1730 g3_draw_poly(3, verts, tmap_flags);
1738 gr_zbuffer_set(saved_zbuffer_mode);
1744 // draw a 2d bitmap on a poly
1745 int g3_draw_2d_poly_bitmap(int x, int y, int w, int h, uint additional_tmap_flags)
1748 int saved_zbuffer_mode;
1750 vertex *vertlist[4] = { &v[0], &v[1], &v[2], &v[3] };
1756 // turn off zbuffering
1757 saved_zbuffer_mode = gr_zbuffer_get();
1758 gr_zbuffer_set(GR_ZBUFF_NONE);
1760 bm_get_section_size(gr_screen.current_bitmap, gr_screen.current_bitmap_sx, gr_screen.current_bitmap_sy, &bw, &bh);
1768 v[0].flags = PF_PROJECTED;
1771 v[1].sx = (float)(x + w);
1776 v[1].flags = PF_PROJECTED;
1779 v[2].sx = (float)(x + w);
1780 v[2].sy = (float)(y + h);
1784 v[2].flags = PF_PROJECTED;
1788 v[3].sy = (float)(y + h);
1792 v[3].flags = PF_PROJECTED;
1799 v[0].u = 0.5f / i2fl(bw);
1800 v[0].v = 0.5f / i2fl(bh);
1801 v[0].flags = PF_PROJECTED;
1804 v[1].sx = (float)(x + w);
1807 v[1].u = 1.0f + (0.5f / i2fl(bw));
1808 v[1].v = 0.0f + (0.5f / i2fl(bh));
1809 v[1].flags = PF_PROJECTED;
1812 v[2].sx = (float)(x + w);
1813 v[2].sy = (float)(y + h);
1815 v[2].u = 1.0f + (0.5f / i2fl(bw));
1816 v[2].v = 1.0f + (0.5f / i2fl(bh));
1817 v[2].flags = PF_PROJECTED;
1821 v[3].sy = (float)(y + h);
1823 v[3].u = 0.0f + (0.5f / i2fl(bw));
1824 v[3].v = 1.0f + (0.5f / i2fl(bh));
1825 v[3].flags = PF_PROJECTED;
1833 ret = g3_draw_poly_constant_sw(4, vertlist, TMAP_FLAG_TEXTURED | additional_tmap_flags, 0.1f);
1837 gr_zbuffer_set(saved_zbuffer_mode);
1839 // put filtering back on