2 * $Logfile: /Freespace2/code/Render/3ddraw.cpp $
7 * 3D rendering primitives
10 * Revision 1.2 2002/05/03 13:34:33 theoddone33
13 * Revision 1.1.1.1 2002/05/03 03:28:10 root
17 * 18 9/06/99 3:23p Andsager
18 * Make fireball and weapon expl ani LOD choice look at resolution of the
21 * 17 8/27/99 9:07p Dave
22 * LOD explosions. Improved beam weapon accuracy.
24 * 16 7/29/99 12:05a Dave
25 * Nebula speed optimizations.
27 * 15 7/13/99 1:16p Dave
28 * 32 bit support. Whee!
30 * 14 7/02/99 3:05p Anoop
31 * Oops. Fixed g3_draw_2d_poly() so that it properly handles poly bitmap
32 * and LFB bitmap calls.
34 * 13 6/29/99 10:35a Dave
35 * Interface polygon bitmaps! Whee!
37 * 12 6/22/99 7:03p Dave
38 * New detail options screen.
40 * 11 6/16/99 4:06p Dave
41 * New pilot info popup. Added new draw-bitmap-as-poly function.
43 * 10 6/08/99 5:17p Dave
44 * Fixed up perspective bitmap drawing.
46 * 9 6/03/99 6:37p Dave
47 * More TNT fun. Made perspective bitmaps more flexible.
49 * 8 5/28/99 1:58p Dave
50 * Fixed problem with overwriting bank value drawing perspective bitmaps.
52 * 7 5/28/99 1:45p Dave
53 * Fixed up perspective bitmap drawing.
55 * 6 5/24/99 5:45p Dave
56 * Added detail levels to the nebula, with a decent speedup. Split nebula
57 * lightning into its own section.
59 * 5 4/07/99 6:22p Dave
60 * Fred and Freespace support for multiple background bitmaps and suns.
61 * Fixed link errors on all subprojects. Moved encrypt_init() to
62 * cfile_init() and lcl_init(), since its safe to call twice.
64 * 4 3/09/99 6:24p Dave
65 * More work on object update revamping. Identified several sources of
66 * unnecessary bandwidth.
68 * 3 2/11/99 3:08p Dave
69 * PXO refresh button. Very preliminary squad war support.
71 * 2 10/07/98 10:53a Dave
74 * 1 10/07/98 10:51a Dave
76 * 37 3/22/98 2:34p John
77 * If alpha effects is on lowest detail level, draw rotated bitmaps as
80 * 36 3/16/98 4:51p John
81 * Added low-level code to clip all polygons against an arbritary plane.
82 * Took out all old model_interp_zclip and used this new method instead.
84 * 35 3/04/98 9:29a John
85 * Made rotated bitmaps force all sw's to the same value after clipping.
87 * 34 3/03/98 6:59p John
89 * 33 1/26/98 5:12p John
90 * Added in code for Pentium Pro specific optimizations. Speed up
91 * zbuffered correct tmapper about 35%. Speed up non-zbuffered scalers
94 * 32 1/15/98 2:16p John
95 * Made bitmaps zbuffer at center minus radius.
96 * Made fireballs sort after objects they are around.
98 * 31 1/06/98 2:11p John
99 * Made g3_draw_rotated bitmap draw the same orientation as g3_draw_bitmap
100 * (orient=0) and made it be the same size (it was 2x too big before).
102 * 30 12/30/97 6:44p John
103 * Made g3_Draw_bitmap functions account for aspect of bitmap.
105 * 29 12/04/97 12:09p John
106 * Made glows use scaler instead of tmapper so they don't rotate. Had to
107 * add a zbuffered scaler.
109 * 28 12/02/97 4:00p John
110 * Added first rev of thruster glow, along with variable levels of
111 * translucency, which retquired some restructing of palman.
113 * 27 11/29/97 2:05p John
114 * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2,
115 * like they used to incorrectly assume. Added code to model to read in
118 * 26 10/20/97 4:49p John
119 * added weapon trails.
120 * added facing bitmap code to 3d lib.
122 * 25 10/03/97 9:10a John
123 * added better antialiased line drawer
125 * 24 9/09/97 3:39p Sandeep
126 * warning level 4 bugs
128 * 23 7/11/97 11:54a John
129 * added rotated 3d bitmaps.
131 * 22 5/07/97 2:59p John
132 * Initial rev of D3D texturing.
134 * 21 4/29/97 9:55a John
136 * 20 3/10/97 2:25p John
137 * Made pofview zbuffer. Made textest work with new model code. Took
138 * out some unnecessary Asserts in the 3d clipper.
141 * 19 3/06/97 5:36p Mike
142 * Change vec_normalize_safe() back to vec_normalize().
143 * Spruce up docking a bit.
145 * 18 3/06/97 10:56a Mike
146 * Write error checking version of vm_vec_normalize().
147 * Fix resultant problems.
149 * 17 2/26/97 5:24p John
150 * Added g3_draw_sphere_ez
152 * 16 2/17/97 5:18p John
153 * Added a bunch of RCS headers to a bunch of old files that don't have
159 #include "3dinternal.h"
163 #include "floating.h"
164 #include "physics.h" // For Physics_viewer_bank for g3_draw_rotated_bitmap
166 #include "systemvars.h"
167 #include "alphacolors.h"
171 //deal with a clipped line
172 int must_clip_line(vertex *p0,vertex *p1,ubyte codes_or, uint flags)
176 clip_line(&p0,&p1,codes_or, flags);
178 if (p0->codes & p1->codes) goto free_points;
180 codes_or = (unsigned char)(p0->codes | p1->codes);
182 if (codes_or & CC_BEHIND) goto free_points;
184 if (!(p0->flags&PF_PROJECTED))
185 g3_project_vertex(p0);
187 if (p0->flags&PF_OVERFLOW) goto free_points;
189 if (!(p1->flags&PF_PROJECTED))
190 g3_project_vertex(p1);
192 if (p1->flags&PF_OVERFLOW) goto free_points;
194 //gr_line(fl2i(p0->sx),fl2i(p0->sy),fl2i(p1->sx),fl2i(p1->sy));
195 // gr_line_float(p0->sx,p0->sy,p1->sx,p1->sy);
204 if (p0->flags & PF_TEMP_POINT)
207 if (p1->flags & PF_TEMP_POINT)
213 //draws a line. takes two points. returns true if drew
214 int g3_draw_line(vertex *p0,vertex *p1)
218 Assert( G3_count == 1 );
220 if (p0->codes & p1->codes)
223 codes_or = (unsigned char)(p0->codes | p1->codes);
225 if (codes_or & CC_BEHIND)
226 return must_clip_line(p0,p1,codes_or,0);
228 if (!(p0->flags&PF_PROJECTED))
229 g3_project_vertex(p0);
231 if (p0->flags&PF_OVERFLOW)
233 return must_clip_line(p0,p1,codes_or,0);
236 if (!(p1->flags&PF_PROJECTED))
237 g3_project_vertex(p1);
239 if (p1->flags&PF_OVERFLOW)
241 return must_clip_line(p0,p1,codes_or,0);
244 // gr_line(fl2i(p0->sx),fl2i(p0->sy),fl2i(p1->sx),fl2i(p1->sy));
245 // gr_line_float(p0->sx,p0->sy,p1->sx,p1->sy);
252 //returns true if a plane is facing the viewer. takes the unrotated surface
253 //normal of the plane, and a point on it. The normal need not be normalized
254 int g3_check_normal_facing(vector *v,vector *norm)
258 Assert( G3_count == 1 );
260 vm_vec_sub(&tempv,&View_position,v);
262 return (vm_vec_dot(&tempv,norm) > 0.0f);
265 int do_facing_check(vector *norm,vertex **vertlist,vector *p)
267 Assert( G3_count == 1 );
269 if (norm) { //have normal
271 Assert(norm->x || norm->y || norm->z);
273 return g3_check_normal_facing(p,norm);
275 else { //normal not specified, so must compute
279 //get three points (rotated) and compute normal
281 vm_vec_perp(&tempv,(vector *)&vertlist[0]->x,(vector *)&vertlist[1]->x,(vector *)&vertlist[2]->x);
283 return (vm_vec_dot(&tempv,(vector *)&vertlist[1]->x ) < 0.0);
287 //like g3_draw_poly(), but checks to see if facing. If surface normal is
288 //NULL, this routine must compute it, which will be slow. It is better to
289 //pre-compute the normal, and pass it to this function. When the normal
290 //is passed, this function works like g3_check_normal_facing() plus
292 //returns -1 if not facing, 1 if off screen, 0 if drew
293 int g3_draw_poly_if_facing(int nv,vertex **pointlist,uint tmap_flags,vector *norm,vector *pnt)
295 Assert( G3_count == 1 );
297 if (do_facing_check(norm,pointlist,pnt))
298 return g3_draw_poly(nv,pointlist,tmap_flags);
304 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
305 //returns 1 if off screen, 0 if drew
306 int g3_draw_poly(int nv,vertex **pointlist,uint tmap_flags)
312 Assert( G3_count == 1 );
314 cc.vor = 0; cc.vand = 0xff;
321 p = bufptr[i] = pointlist[i];
328 return 1; //all points off screen
331 Assert( G3_count == 1 );
333 bufptr = clip_polygon(Vbuf0,Vbuf1,&nv,&cc,tmap_flags);
335 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
338 vertex *p = bufptr[i];
340 if (!(p->flags&PF_PROJECTED))
341 g3_project_vertex(p);
343 if (p->flags&PF_OVERFLOW) {
344 //Int3(); //should not overflow after clip
345 //printf( "overflow in must_clip_tmap_face\n" );
350 gr_tmapper( nv, bufptr, tmap_flags );
357 if (bufptr[i]->flags & PF_TEMP_POINT)
358 free_temp_point(bufptr[i]);
361 //now make list of 2d coords (& check for overflow)
364 vertex *p = bufptr[i];
366 if (!(p->flags&PF_PROJECTED))
367 g3_project_vertex(p);
369 if (p->flags&PF_OVERFLOW) {
370 //Int3(); //should not overflow after clip
371 //printf( "3d: Point overflowed, but flags say OK!\n" );
377 gr_tmapper( nv, bufptr, tmap_flags );
379 return 0; //say it drew
384 // Draw a polygon. Same as g3_draw_poly, but it bashes sw to a constant value
385 // for all vertexes. Needs to be done after clipping to get them all.
386 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
387 //returns 1 if off screen, 0 if drew
388 int g3_draw_poly_constant_sw(int nv,vertex **pointlist,uint tmap_flags, float constant_sw)
394 Assert( G3_count == 1 );
396 cc.vor = 0; cc.vand = 0xff;
403 p = bufptr[i] = pointlist[i];
410 return 1; //all points off screen
413 Assert( G3_count == 1 );
415 bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags);
417 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
420 vertex *p = bufptr[i];
422 if (!(p->flags&PF_PROJECTED))
423 g3_project_vertex(p);
425 if (p->flags&PF_OVERFLOW) {
426 //Int3(); //should not overflow after clip
427 //printf( "overflow in must_clip_tmap_face\n" );
434 gr_tmapper( nv, bufptr, tmap_flags );
436 // draw lines connecting the faces
438 gr_set_color_fast(&Color_bright_green);
439 for(i=0; i<nv-1; i++){
440 g3_draw_line(bufptr[i], bufptr[i+1]);
442 g3_draw_line(bufptr[0], bufptr[i]);
450 if (bufptr[i]->flags & PF_TEMP_POINT){
451 free_temp_point(bufptr[i]);
455 //now make list of 2d coords (& check for overflow)
458 vertex *p = bufptr[i];
460 if (!(p->flags&PF_PROJECTED))
461 g3_project_vertex(p);
463 if (p->flags&PF_OVERFLOW) {
464 //Int3(); //should not overflow after clip
465 //printf( "3d: Point overflowed, but flags say OK!\n" );
472 gr_tmapper( nv, bufptr, tmap_flags );
474 // draw lines connecting the faces
476 gr_set_color_fast(&Color_bright_green);
477 for(i=0; i<nv-1; i++){
478 g3_draw_line(bufptr[i], bufptr[i+1]);
480 g3_draw_line(bufptr[0], bufptr[i]);
483 return 0; //say it drew
486 //draw a sortof sphere - i.e., the 2d radius is proportional to the 3d
487 //radius, but not to the distance from the eye
488 int g3_draw_sphere(vertex *pnt,float rad)
490 Assert( G3_count == 1 );
492 if (! (pnt->codes & CC_BEHIND)) {
494 if (! (pnt->flags & PF_PROJECTED))
495 g3_project_vertex(pnt);
497 if (! (pnt->codes & PF_OVERFLOW)) {
500 r2 = rad*Matrix_scale.x;
504 gr_circle(fl2i(pnt->sx),fl2i(pnt->sy),fl2i(t*2.0f));
511 int g3_draw_sphere_ez(vector *pnt,float rad)
516 Assert( G3_count == 1 );
518 flags = g3_rotate_vertex(&pt,pnt);
522 g3_project_vertex(&pt);
524 if (!(pt.flags & PF_OVERFLOW)) {
526 g3_draw_sphere( &pt, rad );
534 //draws a bitmap with the specified 3d width & height
535 //returns 1 if off screen, 0 if drew
537 int g3_draw_bitmap(vertex *pnt,int orient, float rad,uint tmap_flags)
543 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
546 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
550 height = width*i2fl(bh)/i2fl(bw);
551 } else if ( bw > bh ) {
553 width = height*i2fl(bw)/i2fl(bh);
555 width = height = rad*2.0f;
558 width = height = rad*2.0f;
561 Assert( G3_count == 1 );
563 if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) )
566 if (!(pnt->flags&PF_PROJECTED))
567 g3_project_vertex(pnt);
569 if (pnt->flags & PF_OVERFLOW)
572 t = (width*Canv_w2)/pnt->z;
573 w = t*Matrix_scale.x;
575 t = (height*Canv_h2)/pnt->z;
576 h = t*Matrix_scale.y;
579 z = pnt->z - rad/2.0f;
587 va.sx = pnt->sx - w/2.0f;
588 va.sy = pnt->sy - h/2.0f;
618 // get bitmap dims onscreen as if g3_draw_bitmap() had been called
619 int g3_get_bitmap_dims(int bitmap, vertex *pnt, float rad, int *x, int *y, int *w, int *h, int *size)
626 bm_get_info( bitmap, &bw, &bh, NULL );
630 height = width*i2fl(bh)/i2fl(bw);
631 } else if ( bw > bh ) {
633 width = height*i2fl(bw)/i2fl(bh);
635 width = height = rad*2.0f;
638 Assert( G3_count == 1 );
640 if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) ) {
644 if (!(pnt->flags&PF_PROJECTED)){
645 g3_project_vertex(pnt);
648 if (pnt->flags & PF_OVERFLOW){
652 t = (width*Canv_w2)/pnt->z;
653 *w = (int)(t*Matrix_scale.x);
655 t = (height*Canv_h2)/pnt->z;
656 *h = (int)(t*Matrix_scale.y);
658 *x = (int)(pnt->sx - *w/2.0f);
659 *y = (int)(pnt->sy - *h/2.0f);
666 //draws a bitmap with the specified 3d width & height
667 //returns 1 if off screen, 0 if drew
668 int g3_draw_rotated_bitmap(vertex *pnt,float angle, float rad,uint tmap_flags)
671 vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] };
676 if ( !Detail.alpha_effects ) {
678 if ( angle < PI/2 ) {
680 } else if ( angle < PI ) {
682 } else if ( angle < PI+PI/2 ) {
687 return g3_draw_bitmap( pnt, ang, rad, tmap_flags );
691 Assert( G3_count == 1 );
693 angle+=Physics_viewer_bank;
696 else if ( angle > PI2 )
700 sa = (float)sin(angle);
701 ca = (float)cos(angle);
705 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
708 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
712 height = width*i2fl(bh)/i2fl(bw);
713 } else if ( bw > bh ) {
715 width = height*i2fl(bw)/i2fl(bh);
717 width = height = rad;
720 width = height = rad;
724 v[0].x = (-width*ca + height*sa)*Matrix_scale.x + pnt->x;
725 v[0].y = (-width*sa - height*ca)*Matrix_scale.y + pnt->y;
731 v[1].x = (width*ca + height*sa)*Matrix_scale.x + pnt->x;
732 v[1].y = (width*sa - height*ca)*Matrix_scale.y + pnt->y;
738 v[2].x = (width*ca - height*sa)*Matrix_scale.x + pnt->x;
739 v[2].y = (width*sa + height*ca)*Matrix_scale.y + pnt->y;
745 v[3].x = (-width*ca - height*sa)*Matrix_scale.x + pnt->x;
746 v[3].y = (-width*sa + height*ca)*Matrix_scale.y + pnt->y;
752 ubyte codes_and=0xff;
755 z = pnt->z - rad / 4.0f;
756 if ( z < 0.0f ) z = 0.0f;
759 for (i=0; i<4; i++ ) {
760 //now code the four points
761 codes_and &= g3_code_vertex(&v[i]);
762 v[i].flags = 0; // mark as not yet projected
763 //g3_project_vertex(&v[i]);
767 return 1; //1 means off screen
770 g3_draw_poly_constant_sw(4, vertlist, tmap_flags, sw );
775 #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);
776 float g3_get_poly_area(int nv, vertex **pointlist)
779 float total_area = 0.0f;
782 for(idx=1; idx<nv-1; idx++){
783 TRIANGLE_AREA(pointlist[0], pointlist[idx], pointlist[idx+1]);
790 // Draw a polygon. Same as g3_draw_poly, but it bashes sw to a constant value
791 // for all vertexes. Needs to be done after clipping to get them all.
792 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
793 //returns 1 if off screen, 0 if drew
794 float g3_draw_poly_constant_sw_area(int nv, vertex **pointlist, uint tmap_flags, float constant_sw, float area)
801 Assert( G3_count == 1 );
803 cc.vor = 0; cc.vand = 0xff;
810 p = bufptr[i] = pointlist[i];
817 return 0.0f; //all points off screen
821 Assert( G3_count == 1 );
823 bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags);
825 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
828 vertex *p = bufptr[i];
830 if (!(p->flags&PF_PROJECTED))
831 g3_project_vertex(p);
833 if (p->flags&PF_OVERFLOW) {
834 //Int3(); //should not overflow after clip
835 //printf( "overflow in must_clip_tmap_face\n" );
843 p_area = g3_get_poly_area(nv, bufptr);
848 gr_tmapper( nv, bufptr, tmap_flags );
855 if (bufptr[i]->flags & PF_TEMP_POINT){
856 free_temp_point(bufptr[i]);
860 //now make list of 2d coords (& check for overflow)
863 vertex *p = bufptr[i];
865 if (!(p->flags&PF_PROJECTED))
866 g3_project_vertex(p);
868 if (p->flags&PF_OVERFLOW) {
876 p_area = g3_get_poly_area(nv, bufptr);
881 gr_tmapper( nv, bufptr, tmap_flags );
884 // how much area we drew
889 //draws a bitmap with the specified 3d width & height
890 //returns 1 if off screen, 0 if drew
891 float g3_draw_rotated_bitmap_area(vertex *pnt,float angle, float rad,uint tmap_flags, float area)
894 vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] };
898 Assert( G3_count == 1 );
900 angle+=Physics_viewer_bank;
903 } else if ( angle > PI2 ) {
907 sa = (float)sin(angle);
908 ca = (float)cos(angle);
912 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
915 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
919 height = width*i2fl(bh)/i2fl(bw);
920 } else if ( bw > bh ) {
922 width = height*i2fl(bw)/i2fl(bh);
924 width = height = rad;
927 width = height = rad;
931 v[0].x = (-width*ca + height*sa)*Matrix_scale.x + pnt->x;
932 v[0].y = (-width*sa - height*ca)*Matrix_scale.y + pnt->y;
938 v[1].x = (width*ca + height*sa)*Matrix_scale.x + pnt->x;
939 v[1].y = (width*sa - height*ca)*Matrix_scale.y + pnt->y;
945 v[2].x = (width*ca - height*sa)*Matrix_scale.x + pnt->x;
946 v[2].y = (width*sa + height*ca)*Matrix_scale.y + pnt->y;
952 v[3].x = (-width*ca - height*sa)*Matrix_scale.x + pnt->x;
953 v[3].y = (-width*sa + height*ca)*Matrix_scale.y + pnt->y;
959 ubyte codes_and=0xff;
962 z = pnt->z - rad / 4.0f;
963 if ( z < 0.0f ) z = 0.0f;
966 for (i=0; i<4; i++ ) {
967 //now code the four points
968 codes_and &= g3_code_vertex(&v[i]);
969 v[i].flags = 0; // mark as not yet projected
970 //g3_project_vertex(&v[i]);
978 return g3_draw_poly_constant_sw_area(4, vertlist, tmap_flags, sw, area );
984 typedef struct horz_pt {
989 //draws a horizon. takes eax=sky_color, edx=ground_color
990 void g3_draw_horizon_line()
992 //int sky_color,int ground_color
995 horz_pt horz_pts[4]; // 0 = left, 1 = right
996 // int top_color, bot_color;
997 // int color_swap; //flag for if we swapped
998 // int sky_ground_flag; //0=both, 1=all sky, -1=all gnd
1002 float up_right, down_right,down_left,up_left;
1004 // color_swap = 0; //assume no swap
1005 // sky_ground_flag = 0; //assume both
1007 // if ( View_matrix.uvec.y < 0.0f )
1009 // else if ( View_matrix.uvec.y == 0.0f ) {
1010 // if ( View_matrix.uvec.x > 0.0f )
1014 // if (color_swap) {
1015 // top_color = ground_color;
1016 // bot_color = sky_color;
1018 // top_color = sky_color;
1019 // bot_color = ground_color;
1022 Assert( G3_count == 1 );
1025 //compute horizon_vector
1027 horizon_vec.x = Unscaled_matrix.rvec.y*Matrix_scale.y*Matrix_scale.z;
1028 horizon_vec.y = Unscaled_matrix.uvec.y*Matrix_scale.x*Matrix_scale.z;
1029 horizon_vec.z = Unscaled_matrix.fvec.y*Matrix_scale.x*Matrix_scale.y;
1031 // now compute values & flag for 4 corners.
1032 up_right = horizon_vec.x + horizon_vec.y + horizon_vec.z;
1033 down_right = horizon_vec.x - horizon_vec.y + horizon_vec.z;
1034 down_left = -horizon_vec.x - horizon_vec.y + horizon_vec.z;
1035 up_left = -horizon_vec.x + horizon_vec.y + horizon_vec.z;
1037 //check flags for all sky or all ground.
1038 if ( (up_right<0.0f)&&(down_right<0.0f)&&(down_left<0.0f)&&(up_left<0.0f) ) {
1039 // mprintf(( "All ground.\n" ));
1043 if ( (up_right>0.0f)&&(down_right>0.0f)&&(down_left>0.0f)&&(up_left>0.0f) ) {
1044 // mprintf(( "All sky.\n" ));
1048 //mprintf(( "Horizon vec = %.4f, %.4f, %.4f\n", horizon_vec.x, horizon_vec.y, horizon_vec.z ));
1049 //mprintf(( "%.4f, %.4f, %.4f, %.4f\n", up_right, down_right, down_left, up_left ));
1052 // 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 ));
1053 // check for intesection with each of four edges & compute horizon line
1056 // check intersection with left edge
1057 s1 = up_left > 0.0f;
1058 s2 = down_left > 0.0f;
1060 horz_pts[cpnt].x = 0.0f;
1061 horz_pts[cpnt].y = fl_abs(up_left * Canv_h2 / horizon_vec.y);
1062 horz_pts[cpnt].edge = 0;
1066 // check intersection with top edge
1067 s1 = up_left > 0.0f;
1068 s2 = up_right > 0.0f;
1070 horz_pts[cpnt].x = fl_abs(up_left * Canv_w2 / horizon_vec.x);
1071 horz_pts[cpnt].y = 0.0f;
1072 horz_pts[cpnt].edge = 1;
1077 // check intersection with right edge
1078 s1 = up_right > 0.0f;
1079 s2 = down_right > 0.0f;
1081 horz_pts[cpnt].x = i2fl(Canvas_width)-1;
1082 horz_pts[cpnt].y = fl_abs(up_right * Canv_h2 / horizon_vec.y);
1083 horz_pts[cpnt].edge = 2;
1087 //check intersection with bottom edge
1088 s1 = down_right > 0.0f;
1089 s2 = down_left > 0.0f;
1091 horz_pts[cpnt].x = fl_abs(down_left * Canv_w2 / horizon_vec.x);
1092 horz_pts[cpnt].y = i2fl(Canvas_height)-1;
1093 horz_pts[cpnt].edge = 3;
1098 mprintf(( "HORZ: Wrong number of points (%d)\n", cpnt ));
1102 //make sure first edge is left
1104 if ( horz_pts[0].x > horz_pts[1].x ) {
1107 horz_pts[0] = horz_pts[1];
1112 // draw from left to right.
1113 gr_line( fl2i(horz_pts[0].x),fl2i(horz_pts[0].y),fl2i(horz_pts[1].x),fl2i(horz_pts[1].y) );
1120 horizon_poly dw 5 dup (?,?) ;max of 5 points
1122 ;for g3_compute_horz_vecs
1129 ;for compute corner vec
1140 _TEXT segment dword public USE32 'CODE'
1142 extn gr_setcolor_,gr_clear_canvas_
1145 ;draw a polygon (one half of horizon) from the horizon line
1146 draw_horz_poly: lea ebx,horizon_poly
1148 ;copy horizon line as first points in poly
1160 ;add corners to polygon
1162 mov eax,8[esi] ;edge number of start edge
1164 mov ecx,8[edi] ;edge number of end point
1165 sub ecx,eax ;number of edges
1169 mov edx,ecx ;save count
1171 lea esi,corners[eax] ;first corner
1172 lea edi,16[ebx] ;rest of poly
1174 movsd ;copy a corner
1175 cmp esi,offset corners+8*4 ;end of list?
1178 no_wrap: loop corner_loop
1180 ;now draw the polygon
1181 mov eax,edx ;get corner count
1182 add eax,2 ;..plus horz line end points
1183 lea edx,horizon_poly ;get the points
1184 ;; call gr_poly_ ;draw it!
1188 ;return information on the polygon that is the sky.
1189 ;takes ebx=ptr to x,y pairs, ecx=ptr to vecs for each point
1190 ;returns eax=number of points
1191 ;IMPORTANT: g3_draw_horizon() must be called before this routine.
1192 g3_compute_sky_polygon:
1193 test sky_ground_flag,-1 ;what was drawn
1197 pushm ebx,ecx,edx,esi,edi
1203 xchg esi,edi ;sky isn't top
1206 ;copy horizon line as first points in poly
1208 mov eax,[edi] ;copy end point
1213 mov eax,[esi] ;copy start point
1219 push edi ;save end point
1220 push esi ;save start point
1221 mov esi,edi ;end point is first point
1222 mov edi,ecx ;dest buffer
1223 call compute_horz_end_vec
1225 pop esi ;get back start point
1227 call compute_horz_end_vec
1229 pop edi ;get back end point
1231 add ebx,16 ;past two x,y pairs
1232 add ecx,24 ;past two vectors
1236 ;add corners to polygon
1238 mov eax,8[esi] ;edge number of start edge
1241 mov ecx,8[edi] ;edge number of end point
1242 sub ecx,eax ;number of edges
1246 push ecx ;save count
1248 lea esi,corners[eax] ;first corner
1249 mov edi,ebx ;rest of poly 2d points
1252 movsd ;copy a corner
1253 cmp esi,offset corners+8*4 ;end of list?
1260 call compute_corner_vec
1267 ;now return with count
1268 pop eax ;get corner count
1269 add eax,2 ;..plus horz line end points
1271 popm ebx,ecx,edx,esi,edi
1275 ;we drew all ground, so there was no horizon drawn
1276 was_all_ground: xor eax,eax ;no points in poly
1279 ;we drew all sky, so find 4 corners
1280 was_all_sky: pushm ebx,ecx,edx,esi,edi
1289 xor eax,eax ;start corner 0
1290 sky_loop: pushm eax,ecx,edi
1291 call compute_corner_vec
1296 mov eax,4 ;4 corners
1297 popm ebx,ecx,edx,esi,edi
1300 ;compute vector describing horizon intersection with a point.
1301 ;takes esi=2d point, edi=vec. trashes eax,ebx,ecx,edx
1302 compute_horz_end_vec:
1304 ;compute rotated x/z & y/z ratios
1306 mov eax,[esi] ;get x coord
1311 mov eax,4[esi] ;get y coord
1314 neg eax ;y inversion
1317 ;compute fraction unrotated x/z
1322 fixmul View_matrix.m9
1323 sub eax,View_matrix.m7
1324 sub eax,View_matrix.m8
1325 mov ebx,eax ;save numerator
1328 fixmul View_matrix.m3
1330 mov eax,View_matrix.m1
1331 add eax,View_matrix.m2
1334 ;now eax/ebx = z/x. do divide in way to give result < 0
1340 cmp eax,ebx ;which is bigger?
1344 ;x is bigger, so do as z/x
1348 ;now eax = z/x ratio. Compute vector by normalizing and correcting sign
1350 push eax ;save ratio
1352 imul eax ;compute z*z
1353 inc edx ;+ x*x (x==1)
1356 mov ecx,eax ;mag in ecx
1357 pop eax ;get ratio, x part
1369 ;z is bigger, so do as x/z
1374 ;now eax = x/z ratio. Compute vector by normalizing and correcting sign
1376 push eax ;save ratio
1378 imul eax ;compute x*x
1379 inc edx ;+ z*z (z==1)
1382 mov ecx,eax ;mag in ecx
1383 pop eax ;get ratio, x part
1393 finish_end: xor eax,eax ;y = 0
1396 ;now make sure that this vector is in front of you, not behind
1406 jns vec_ok ;has positive z, ok
1408 ;z is neg, flip vector
1423 ;compute vector decribing a corner of the screen.
1424 ;takes edi=vector, eax=corner num
1433 mov ebx,View_matrix.m1
1434 mov ecx,View_matrix.m4
1435 mov edx,View_matrix.m7
1446 sub ebx,View_matrix.m3
1448 sub ecx,View_matrix.m6
1450 sub edx,View_matrix.m9
1453 mov ebx,View_matrix.m5
1454 mov ecx,View_matrix.m2
1455 mov edx,View_matrix.m8
1464 sub ebx,View_matrix.m6
1466 sub ecx,View_matrix.m3
1468 sub edx,View_matrix.m9
1473 ;compute denomonator
1481 sub eax,ebx ;eax = denominator
1483 ;now we have the denominator. If it is too small, try x/y, z/y or z/x, y/x
1485 mov ecx,eax ;save den
1493 ;now do x/z numerator
1496 fixmul m56 ;* (m5-m6)
1500 fixmul m46 ;* (m4-m6)
1503 ;now, eax/ecx = x/z ratio
1505 fixdiv ecx ;eax = x/z
1507 mov [edi].x,eax ;save x
1519 ;now eax/ecx = y/z ratio
1528 call vm_vec_normalize
1530 ;make sure this vec is pointing in right direction
1532 lea edi,View_matrix.fvec
1534 or eax,eax ;check sign
1552 // Draws a polygon always facing the viewer.
1553 // compute the corners of a rod. fills in vertbuf.
1554 // Verts has any needs uv's or l's or can be NULL if none needed.
1555 int g3_draw_rod(vector *p0,float width1,vector *p1,float width2, vertex * verts, uint tmap_flags)
1557 vector uvec, fvec, rvec, center;
1559 vm_vec_sub( &fvec, p0, p1 );
1560 vm_vec_normalize_safe( &fvec );
1562 vm_vec_avg( ¢er, p0, p1 );
1563 vm_vec_sub( &rvec, &Eye_position, ¢er );
1564 vm_vec_normalize( &rvec );
1566 vm_vec_crossprod(&uvec,&fvec,&rvec);
1568 //normalize new perpendicular vector
1569 vm_vec_normalize(&uvec);
1571 //now recompute right vector, in case it wasn't entirely perpendiclar
1572 vm_vec_crossprod(&rvec,&uvec,&fvec);
1574 // Now have uvec, which is up vector and rvec which is the normal
1580 vertex *ptlist[4] = { &pts[3], &pts[2], &pts[1], &pts[0] };
1582 vm_vec_scale_add( &vecs[0], p0, &uvec, width1/2.0f );
1583 vm_vec_scale_add( &vecs[1], p1, &uvec, width2/2.0f );
1584 vm_vec_scale_add( &vecs[2], p1, &uvec, -width2/2.0f );
1585 vm_vec_scale_add( &vecs[3], p0, &uvec, -width1/2.0f );
1587 for (i=0; i<4; i++ ) {
1591 g3_rotate_vertex( &pts[i], &vecs[i] );
1594 return g3_draw_poly(4,ptlist,tmap_flags);
1597 // draw a perspective bitmap based on angles and radius
1598 vector g3_square[4] = {
1599 {-1.0f, -1.0f, 20.0f},
1600 {-1.0f, 1.0f, 20.0f},
1601 {1.0f, 1.0f, 20.0f},
1602 {1.0f, -1.0f, 20.0f}
1605 #define MAX_PERSPECTIVE_DIVISIONS 5 // should never even come close to this limit
1607 void stars_project_2d_onto_sphere( vector *pnt, float rho, float phi, float theta )
1609 float a = 3.14159f * phi;
1610 float b = 6.28318f * theta;
1611 float sin_a = (float)sin(a);
1614 pnt->z = rho * sin_a * (float)cos(b);
1615 pnt->y = rho * sin_a * (float)sin(b);
1616 pnt->x = rho * (float)cos(a);
1619 // draw a perspective bitmap based on angles and radius
1620 float p_phi = 10.0f;
1621 float p_theta = 10.0f;
1622 int g3_draw_perspective_bitmap(angles *a, float scale_x, float scale_y, int div_x, int div_y, uint tmap_flags)
1624 vector s_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
1625 vector t_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
1630 int saved_zbuffer_mode;
1634 // cap division values
1635 // div_x = div_x > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_x;
1637 div_y = div_y > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_y;
1639 // texture increment values
1640 ui = 1.0f / (float)div_x;
1641 vi = 1.0f / (float)div_y;
1643 // adjust for aspect ratio
1644 scale_x *= ((float)gr_screen.max_w / (float)gr_screen.max_h) + 0.55f; // fudge factor
1646 float s_phi = 0.5f + (((p_phi * scale_x) / 360.0f) / 2.0f);
1647 float s_theta = (((p_theta * scale_y) / 360.0f) / 2.0f);
1648 float d_phi = -(((p_phi * scale_x) / 360.0f) / (float)(div_x));
1649 float d_theta = -(((p_theta * scale_y) / 360.0f) / (float)(div_y));
1652 bank_first.p = 0.0f;
1653 bank_first.b = a->b;
1654 bank_first.h = 0.0f;
1655 vm_angles_2_matrix(&m_bank, &bank_first);
1657 // convert angles to matrix
1658 float b_save = a->b;
1660 vm_angles_2_matrix(&m, a);
1663 // generate the bitmap points
1664 for(idx=0; idx<=div_x; idx++){
1665 for(s_idx=0; s_idx<=div_y; s_idx++){
1666 // get world spherical coords
1667 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));
1669 // bank the bitmap first
1670 vm_vec_rotate(&t_points[idx][s_idx], &s_points[idx][s_idx], &m_bank);
1672 // rotate on the sphere
1673 vm_vec_rotate(&s_points[idx][s_idx], &t_points[idx][s_idx], &m);
1677 // turn off zbuffering
1678 saved_zbuffer_mode = gr_zbuffer_get();
1679 gr_zbuffer_set(GR_ZBUFF_NONE);
1686 tmap_flags &= ~(TMAP_FLAG_CORRECT);
1690 for(idx=0; idx<div_x; idx++){
1691 for(s_idx=0; s_idx<div_y; s_idx++){
1692 // stuff texture coords
1693 v[0].u = ui * float(idx);
1694 v[0].v = vi * float(s_idx);
1696 v[1].u = ui * float(idx+1);
1697 v[1].v = vi * float(s_idx);
1699 v[2].u = ui * float(idx+1);
1700 v[2].v = vi * float(s_idx+1);
1702 v[3].u = ui * float(idx);
1703 v[3].v = vi * float(s_idx+1);
1712 g3_rotate_faraway_vertex(verts[0], &s_points[idx][s_idx]);
1713 g3_rotate_faraway_vertex(verts[1], &s_points[idx+1][s_idx]);
1714 g3_rotate_faraway_vertex(verts[2], &s_points[idx+1][s_idx+1]);
1715 g3_draw_poly(3, verts, tmap_flags);
1724 g3_rotate_faraway_vertex(verts[0], &s_points[idx][s_idx]);
1725 g3_rotate_faraway_vertex(verts[1], &s_points[idx+1][s_idx+1]);
1726 g3_rotate_faraway_vertex(verts[2], &s_points[idx][s_idx+1]);
1727 g3_draw_poly(3, verts, tmap_flags);
1735 gr_zbuffer_set(saved_zbuffer_mode);
1741 // draw a 2d bitmap on a poly
1742 int g3_draw_2d_poly_bitmap(int x, int y, int w, int h, uint additional_tmap_flags)
1745 int saved_zbuffer_mode;
1747 vertex *vertlist[4] = { &v[0], &v[1], &v[2], &v[3] };
1753 // turn off zbuffering
1754 saved_zbuffer_mode = gr_zbuffer_get();
1755 gr_zbuffer_set(GR_ZBUFF_NONE);
1757 bm_get_section_size(gr_screen.current_bitmap, gr_screen.current_bitmap_sx, gr_screen.current_bitmap_sy, &bw, &bh);
1765 v[0].flags = PF_PROJECTED;
1768 v[1].sx = (float)(x + w);
1773 v[1].flags = PF_PROJECTED;
1776 v[2].sx = (float)(x + w);
1777 v[2].sy = (float)(y + h);
1781 v[2].flags = PF_PROJECTED;
1785 v[3].sy = (float)(y + h);
1789 v[3].flags = PF_PROJECTED;
1796 v[0].u = 0.5f / i2fl(bw);
1797 v[0].v = 0.5f / i2fl(bh);
1798 v[0].flags = PF_PROJECTED;
1801 v[1].sx = (float)(x + w);
1804 v[1].u = 1.0f + (0.5f / i2fl(bw));
1805 v[1].v = 0.0f + (0.5f / i2fl(bh));
1806 v[1].flags = PF_PROJECTED;
1809 v[2].sx = (float)(x + w);
1810 v[2].sy = (float)(y + h);
1812 v[2].u = 1.0f + (0.5f / i2fl(bw));
1813 v[2].v = 1.0f + (0.5f / i2fl(bh));
1814 v[2].flags = PF_PROJECTED;
1818 v[3].sy = (float)(y + h);
1820 v[3].u = 0.0f + (0.5f / i2fl(bw));
1821 v[3].v = 1.0f + (0.5f / i2fl(bh));
1822 v[3].flags = PF_PROJECTED;
1830 ret = g3_draw_poly_constant_sw(4, vertlist, TMAP_FLAG_TEXTURED | additional_tmap_flags, 0.1f);
1834 gr_zbuffer_set(saved_zbuffer_mode);
1836 // put filtering back on