2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Render/3ddraw.cpp $
15 * 3D rendering primitives
18 * Revision 1.4 2002/06/09 04:41:25 relnev
19 * added copyright header
21 * Revision 1.3 2002/05/07 03:16:51 theoddone33
22 * The Great Newline Fix
24 * Revision 1.2 2002/05/03 13:34:33 theoddone33
27 * Revision 1.1.1.1 2002/05/03 03:28:10 root
31 * 18 9/06/99 3:23p Andsager
32 * Make fireball and weapon expl ani LOD choice look at resolution of the
35 * 17 8/27/99 9:07p Dave
36 * LOD explosions. Improved beam weapon accuracy.
38 * 16 7/29/99 12:05a Dave
39 * Nebula speed optimizations.
41 * 15 7/13/99 1:16p Dave
42 * 32 bit support. Whee!
44 * 14 7/02/99 3:05p Anoop
45 * Oops. Fixed g3_draw_2d_poly() so that it properly handles poly bitmap
46 * and LFB bitmap calls.
48 * 13 6/29/99 10:35a Dave
49 * Interface polygon bitmaps! Whee!
51 * 12 6/22/99 7:03p Dave
52 * New detail options screen.
54 * 11 6/16/99 4:06p Dave
55 * New pilot info popup. Added new draw-bitmap-as-poly function.
57 * 10 6/08/99 5:17p Dave
58 * Fixed up perspective bitmap drawing.
60 * 9 6/03/99 6:37p Dave
61 * More TNT fun. Made perspective bitmaps more flexible.
63 * 8 5/28/99 1:58p Dave
64 * Fixed problem with overwriting bank value drawing perspective bitmaps.
66 * 7 5/28/99 1:45p Dave
67 * Fixed up perspective bitmap drawing.
69 * 6 5/24/99 5:45p Dave
70 * Added detail levels to the nebula, with a decent speedup. Split nebula
71 * lightning into its own section.
73 * 5 4/07/99 6:22p Dave
74 * Fred and Freespace support for multiple background bitmaps and suns.
75 * Fixed link errors on all subprojects. Moved encrypt_init() to
76 * cfile_init() and lcl_init(), since its safe to call twice.
78 * 4 3/09/99 6:24p Dave
79 * More work on object update revamping. Identified several sources of
80 * unnecessary bandwidth.
82 * 3 2/11/99 3:08p Dave
83 * PXO refresh button. Very preliminary squad war support.
85 * 2 10/07/98 10:53a Dave
88 * 1 10/07/98 10:51a Dave
90 * 37 3/22/98 2:34p John
91 * If alpha effects is on lowest detail level, draw rotated bitmaps as
94 * 36 3/16/98 4:51p John
95 * Added low-level code to clip all polygons against an arbritary plane.
96 * Took out all old model_interp_zclip and used this new method instead.
98 * 35 3/04/98 9:29a John
99 * Made rotated bitmaps force all sw's to the same value after clipping.
101 * 34 3/03/98 6:59p John
103 * 33 1/26/98 5:12p John
104 * Added in code for Pentium Pro specific optimizations. Speed up
105 * zbuffered correct tmapper about 35%. Speed up non-zbuffered scalers
108 * 32 1/15/98 2:16p John
109 * Made bitmaps zbuffer at center minus radius.
110 * Made fireballs sort after objects they are around.
112 * 31 1/06/98 2:11p John
113 * Made g3_draw_rotated bitmap draw the same orientation as g3_draw_bitmap
114 * (orient=0) and made it be the same size (it was 2x too big before).
116 * 30 12/30/97 6:44p John
117 * Made g3_Draw_bitmap functions account for aspect of bitmap.
119 * 29 12/04/97 12:09p John
120 * Made glows use scaler instead of tmapper so they don't rotate. Had to
121 * add a zbuffered scaler.
123 * 28 12/02/97 4:00p John
124 * Added first rev of thruster glow, along with variable levels of
125 * translucency, which retquired some restructing of palman.
127 * 27 11/29/97 2:05p John
128 * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2,
129 * like they used to incorrectly assume. Added code to model to read in
132 * 26 10/20/97 4:49p John
133 * added weapon trails.
134 * added facing bitmap code to 3d lib.
136 * 25 10/03/97 9:10a John
137 * added better antialiased line drawer
139 * 24 9/09/97 3:39p Sandeep
140 * warning level 4 bugs
142 * 23 7/11/97 11:54a John
143 * added rotated 3d bitmaps.
145 * 22 5/07/97 2:59p John
146 * Initial rev of D3D texturing.
148 * 21 4/29/97 9:55a John
150 * 20 3/10/97 2:25p John
151 * Made pofview zbuffer. Made textest work with new model code. Took
152 * out some unnecessary Asserts in the 3d clipper.
155 * 19 3/06/97 5:36p Mike
156 * Change vec_normalize_safe() back to vec_normalize().
157 * Spruce up docking a bit.
159 * 18 3/06/97 10:56a Mike
160 * Write error checking version of vm_vec_normalize().
161 * Fix resultant problems.
163 * 17 2/26/97 5:24p John
164 * Added g3_draw_sphere_ez
166 * 16 2/17/97 5:18p John
167 * Added a bunch of RCS headers to a bunch of old files that don't have
173 #include "3dinternal.h"
177 #include "floating.h"
178 #include "physics.h" // For Physics_viewer_bank for g3_draw_rotated_bitmap
180 #include "systemvars.h"
181 #include "alphacolors.h"
185 //deal with a clipped line
186 int must_clip_line(vertex *p0,vertex *p1,ubyte codes_or, uint flags)
190 clip_line(&p0,&p1,codes_or, flags);
192 if (p0->codes & p1->codes) goto free_points;
194 codes_or = (unsigned char)(p0->codes | p1->codes);
196 if (codes_or & CC_BEHIND) goto free_points;
198 if (!(p0->flags&PF_PROJECTED))
199 g3_project_vertex(p0);
201 if (p0->flags&PF_OVERFLOW) goto free_points;
203 if (!(p1->flags&PF_PROJECTED))
204 g3_project_vertex(p1);
206 if (p1->flags&PF_OVERFLOW) goto free_points;
208 //gr_line(fl2i(p0->sx),fl2i(p0->sy),fl2i(p1->sx),fl2i(p1->sy));
209 // gr_line_float(p0->sx,p0->sy,p1->sx,p1->sy);
218 if (p0->flags & PF_TEMP_POINT)
221 if (p1->flags & PF_TEMP_POINT)
227 //draws a line. takes two points. returns true if drew
228 int g3_draw_line(vertex *p0,vertex *p1)
232 Assert( G3_count == 1 );
234 if (p0->codes & p1->codes)
237 codes_or = (unsigned char)(p0->codes | p1->codes);
239 if (codes_or & CC_BEHIND)
240 return must_clip_line(p0,p1,codes_or,0);
242 if (!(p0->flags&PF_PROJECTED))
243 g3_project_vertex(p0);
245 if (p0->flags&PF_OVERFLOW)
247 return must_clip_line(p0,p1,codes_or,0);
250 if (!(p1->flags&PF_PROJECTED))
251 g3_project_vertex(p1);
253 if (p1->flags&PF_OVERFLOW)
255 return must_clip_line(p0,p1,codes_or,0);
258 // gr_line(fl2i(p0->sx),fl2i(p0->sy),fl2i(p1->sx),fl2i(p1->sy));
259 // gr_line_float(p0->sx,p0->sy,p1->sx,p1->sy);
266 //returns true if a plane is facing the viewer. takes the unrotated surface
267 //normal of the plane, and a point on it. The normal need not be normalized
268 int g3_check_normal_facing(vector *v,vector *norm)
272 Assert( G3_count == 1 );
274 vm_vec_sub(&tempv,&View_position,v);
276 return (vm_vec_dot(&tempv,norm) > 0.0f);
279 int do_facing_check(vector *norm,vertex **vertlist,vector *p)
281 Assert( G3_count == 1 );
283 if (norm) { //have normal
285 Assert(norm->x || norm->y || norm->z);
287 return g3_check_normal_facing(p,norm);
289 else { //normal not specified, so must compute
293 //get three points (rotated) and compute normal
295 vm_vec_perp(&tempv,(vector *)&vertlist[0]->x,(vector *)&vertlist[1]->x,(vector *)&vertlist[2]->x);
297 return (vm_vec_dot(&tempv,(vector *)&vertlist[1]->x ) < 0.0);
301 //like g3_draw_poly(), but checks to see if facing. If surface normal is
302 //NULL, this routine must compute it, which will be slow. It is better to
303 //pre-compute the normal, and pass it to this function. When the normal
304 //is passed, this function works like g3_check_normal_facing() plus
306 //returns -1 if not facing, 1 if off screen, 0 if drew
307 int g3_draw_poly_if_facing(int nv,vertex **pointlist,uint tmap_flags,vector *norm,vector *pnt)
309 Assert( G3_count == 1 );
311 if (do_facing_check(norm,pointlist,pnt))
312 return g3_draw_poly(nv,pointlist,tmap_flags);
318 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
319 //returns 1 if off screen, 0 if drew
320 int g3_draw_poly(int nv,vertex **pointlist,uint tmap_flags)
326 Assert( G3_count == 1 );
328 cc.vor = 0; cc.vand = 0xff;
335 p = bufptr[i] = pointlist[i];
342 return 1; //all points off screen
345 Assert( G3_count == 1 );
347 bufptr = clip_polygon(Vbuf0,Vbuf1,&nv,&cc,tmap_flags);
349 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
352 vertex *p = bufptr[i];
354 if (!(p->flags&PF_PROJECTED))
355 g3_project_vertex(p);
357 if (p->flags&PF_OVERFLOW) {
358 //Int3(); //should not overflow after clip
359 //printf( "overflow in must_clip_tmap_face\n" );
364 gr_tmapper( nv, bufptr, tmap_flags );
371 if (bufptr[i]->flags & PF_TEMP_POINT)
372 free_temp_point(bufptr[i]);
375 //now make list of 2d coords (& check for overflow)
378 vertex *p = bufptr[i];
380 if (!(p->flags&PF_PROJECTED))
381 g3_project_vertex(p);
383 if (p->flags&PF_OVERFLOW) {
384 //Int3(); //should not overflow after clip
385 //printf( "3d: Point overflowed, but flags say OK!\n" );
391 gr_tmapper( nv, bufptr, tmap_flags );
393 return 0; //say it drew
398 // Draw a polygon. Same as g3_draw_poly, but it bashes sw to a constant value
399 // for all vertexes. Needs to be done after clipping to get them all.
400 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
401 //returns 1 if off screen, 0 if drew
402 int g3_draw_poly_constant_sw(int nv,vertex **pointlist,uint tmap_flags, float constant_sw)
408 Assert( G3_count == 1 );
410 cc.vor = 0; cc.vand = 0xff;
417 p = bufptr[i] = pointlist[i];
424 return 1; //all points off screen
427 Assert( G3_count == 1 );
429 bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags);
431 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
434 vertex *p = bufptr[i];
436 if (!(p->flags&PF_PROJECTED))
437 g3_project_vertex(p);
439 if (p->flags&PF_OVERFLOW) {
440 //Int3(); //should not overflow after clip
441 //printf( "overflow in must_clip_tmap_face\n" );
448 gr_tmapper( nv, bufptr, tmap_flags );
450 // draw lines connecting the faces
452 gr_set_color_fast(&Color_bright_green);
453 for(i=0; i<nv-1; i++){
454 g3_draw_line(bufptr[i], bufptr[i+1]);
456 g3_draw_line(bufptr[0], bufptr[i]);
464 if (bufptr[i]->flags & PF_TEMP_POINT){
465 free_temp_point(bufptr[i]);
469 //now make list of 2d coords (& check for overflow)
472 vertex *p = bufptr[i];
474 if (!(p->flags&PF_PROJECTED))
475 g3_project_vertex(p);
477 if (p->flags&PF_OVERFLOW) {
478 //Int3(); //should not overflow after clip
479 //printf( "3d: Point overflowed, but flags say OK!\n" );
486 gr_tmapper( nv, bufptr, tmap_flags );
488 // draw lines connecting the faces
490 gr_set_color_fast(&Color_bright_green);
491 for(i=0; i<nv-1; i++){
492 g3_draw_line(bufptr[i], bufptr[i+1]);
494 g3_draw_line(bufptr[0], bufptr[i]);
497 return 0; //say it drew
500 //draw a sortof sphere - i.e., the 2d radius is proportional to the 3d
501 //radius, but not to the distance from the eye
502 int g3_draw_sphere(vertex *pnt,float rad)
504 Assert( G3_count == 1 );
506 if (! (pnt->codes & CC_BEHIND)) {
508 if (! (pnt->flags & PF_PROJECTED))
509 g3_project_vertex(pnt);
511 if (! (pnt->codes & PF_OVERFLOW)) {
514 r2 = rad*Matrix_scale.x;
518 gr_circle(fl2i(pnt->sx),fl2i(pnt->sy),fl2i(t*2.0f));
525 int g3_draw_sphere_ez(vector *pnt,float rad)
530 Assert( G3_count == 1 );
532 flags = g3_rotate_vertex(&pt,pnt);
536 g3_project_vertex(&pt);
538 if (!(pt.flags & PF_OVERFLOW)) {
540 g3_draw_sphere( &pt, rad );
548 //draws a bitmap with the specified 3d width & height
549 //returns 1 if off screen, 0 if drew
551 int g3_draw_bitmap(vertex *pnt,int orient, float rad,uint tmap_flags)
557 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
560 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
564 height = width*i2fl(bh)/i2fl(bw);
565 } else if ( bw > bh ) {
567 width = height*i2fl(bw)/i2fl(bh);
569 width = height = rad*2.0f;
572 width = height = rad*2.0f;
575 Assert( G3_count == 1 );
577 if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) )
580 if (!(pnt->flags&PF_PROJECTED))
581 g3_project_vertex(pnt);
583 if (pnt->flags & PF_OVERFLOW)
586 t = (width*Canv_w2)/pnt->z;
587 w = t*Matrix_scale.x;
589 t = (height*Canv_h2)/pnt->z;
590 h = t*Matrix_scale.y;
593 z = pnt->z - rad/2.0f;
601 va.sx = pnt->sx - w/2.0f;
602 va.sy = pnt->sy - h/2.0f;
632 // get bitmap dims onscreen as if g3_draw_bitmap() had been called
633 int g3_get_bitmap_dims(int bitmap, vertex *pnt, float rad, int *x, int *y, int *w, int *h, int *size)
640 bm_get_info( bitmap, &bw, &bh, NULL );
644 height = width*i2fl(bh)/i2fl(bw);
645 } else if ( bw > bh ) {
647 width = height*i2fl(bw)/i2fl(bh);
649 width = height = rad*2.0f;
652 Assert( G3_count == 1 );
654 if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) ) {
658 if (!(pnt->flags&PF_PROJECTED)){
659 g3_project_vertex(pnt);
662 if (pnt->flags & PF_OVERFLOW){
666 t = (width*Canv_w2)/pnt->z;
667 *w = (int)(t*Matrix_scale.x);
669 t = (height*Canv_h2)/pnt->z;
670 *h = (int)(t*Matrix_scale.y);
672 *x = (int)(pnt->sx - *w/2.0f);
673 *y = (int)(pnt->sy - *h/2.0f);
680 //draws a bitmap with the specified 3d width & height
681 //returns 1 if off screen, 0 if drew
682 int g3_draw_rotated_bitmap(vertex *pnt,float angle, float rad,uint tmap_flags)
685 vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] };
690 if ( !Detail.alpha_effects ) {
692 if ( angle < PI/2 ) {
694 } else if ( angle < PI ) {
696 } else if ( angle < PI+PI/2 ) {
701 return g3_draw_bitmap( pnt, ang, rad, tmap_flags );
705 Assert( G3_count == 1 );
707 angle+=Physics_viewer_bank;
710 else if ( angle > PI2 )
714 sa = (float)sin(angle);
715 ca = (float)cos(angle);
719 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
722 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
726 height = width*i2fl(bh)/i2fl(bw);
727 } else if ( bw > bh ) {
729 width = height*i2fl(bw)/i2fl(bh);
731 width = height = rad;
734 width = height = rad;
738 v[0].x = (-width*ca + height*sa)*Matrix_scale.x + pnt->x;
739 v[0].y = (-width*sa - height*ca)*Matrix_scale.y + pnt->y;
745 v[1].x = (width*ca + height*sa)*Matrix_scale.x + pnt->x;
746 v[1].y = (width*sa - height*ca)*Matrix_scale.y + pnt->y;
752 v[2].x = (width*ca - height*sa)*Matrix_scale.x + pnt->x;
753 v[2].y = (width*sa + height*ca)*Matrix_scale.y + pnt->y;
759 v[3].x = (-width*ca - height*sa)*Matrix_scale.x + pnt->x;
760 v[3].y = (-width*sa + height*ca)*Matrix_scale.y + pnt->y;
766 ubyte codes_and=0xff;
769 z = pnt->z - rad / 4.0f;
770 if ( z < 0.0f ) z = 0.0f;
773 for (i=0; i<4; i++ ) {
774 //now code the four points
775 codes_and &= g3_code_vertex(&v[i]);
776 v[i].flags = 0; // mark as not yet projected
777 //g3_project_vertex(&v[i]);
781 return 1; //1 means off screen
784 g3_draw_poly_constant_sw(4, vertlist, tmap_flags, sw );
789 #define TRIANGLE_AREA(_p, _q, _r) do { vector a, b, cross; a.x = _q->x - _p->x; a.y = _q->y - _p->y; a.z = 0.0f; b.x = _r->x - _p->x; b.y = _r->y - _p->y; b.z = 0.0f; vm_vec_crossprod(&cross, &a, &b); total_area += vm_vec_mag(&cross) * 0.5f; } while(0);
790 float g3_get_poly_area(int nv, vertex **pointlist)
793 float total_area = 0.0f;
796 for(idx=1; idx<nv-1; idx++){
797 TRIANGLE_AREA(pointlist[0], pointlist[idx], pointlist[idx+1]);
804 // Draw a polygon. Same as g3_draw_poly, but it bashes sw to a constant value
805 // for all vertexes. Needs to be done after clipping to get them all.
806 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
807 //returns 1 if off screen, 0 if drew
808 float g3_draw_poly_constant_sw_area(int nv, vertex **pointlist, uint tmap_flags, float constant_sw, float area)
815 Assert( G3_count == 1 );
817 cc.vor = 0; cc.vand = 0xff;
824 p = bufptr[i] = pointlist[i];
831 return 0.0f; //all points off screen
835 Assert( G3_count == 1 );
837 bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags);
839 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
842 vertex *p = bufptr[i];
844 if (!(p->flags&PF_PROJECTED))
845 g3_project_vertex(p);
847 if (p->flags&PF_OVERFLOW) {
848 //Int3(); //should not overflow after clip
849 //printf( "overflow in must_clip_tmap_face\n" );
857 p_area = g3_get_poly_area(nv, bufptr);
862 gr_tmapper( nv, bufptr, tmap_flags );
869 if (bufptr[i]->flags & PF_TEMP_POINT){
870 free_temp_point(bufptr[i]);
874 //now make list of 2d coords (& check for overflow)
877 vertex *p = bufptr[i];
879 if (!(p->flags&PF_PROJECTED))
880 g3_project_vertex(p);
882 if (p->flags&PF_OVERFLOW) {
890 p_area = g3_get_poly_area(nv, bufptr);
895 gr_tmapper( nv, bufptr, tmap_flags );
898 // how much area we drew
903 //draws a bitmap with the specified 3d width & height
904 //returns 1 if off screen, 0 if drew
905 float g3_draw_rotated_bitmap_area(vertex *pnt,float angle, float rad,uint tmap_flags, float area)
908 vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] };
912 Assert( G3_count == 1 );
914 angle+=Physics_viewer_bank;
917 } else if ( angle > PI2 ) {
921 sa = (float)sin(angle);
922 ca = (float)cos(angle);
926 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
929 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
933 height = width*i2fl(bh)/i2fl(bw);
934 } else if ( bw > bh ) {
936 width = height*i2fl(bw)/i2fl(bh);
938 width = height = rad;
941 width = height = rad;
945 v[0].x = (-width*ca + height*sa)*Matrix_scale.x + pnt->x;
946 v[0].y = (-width*sa - height*ca)*Matrix_scale.y + pnt->y;
952 v[1].x = (width*ca + height*sa)*Matrix_scale.x + pnt->x;
953 v[1].y = (width*sa - height*ca)*Matrix_scale.y + pnt->y;
959 v[2].x = (width*ca - height*sa)*Matrix_scale.x + pnt->x;
960 v[2].y = (width*sa + height*ca)*Matrix_scale.y + pnt->y;
966 v[3].x = (-width*ca - height*sa)*Matrix_scale.x + pnt->x;
967 v[3].y = (-width*sa + height*ca)*Matrix_scale.y + pnt->y;
973 ubyte codes_and=0xff;
976 z = pnt->z - rad / 4.0f;
977 if ( z < 0.0f ) z = 0.0f;
980 for (i=0; i<4; i++ ) {
981 //now code the four points
982 codes_and &= g3_code_vertex(&v[i]);
983 v[i].flags = 0; // mark as not yet projected
984 //g3_project_vertex(&v[i]);
992 return g3_draw_poly_constant_sw_area(4, vertlist, tmap_flags, sw, area );
998 typedef struct horz_pt {
1003 //draws a horizon. takes eax=sky_color, edx=ground_color
1004 void g3_draw_horizon_line()
1006 //int sky_color,int ground_color
1009 horz_pt horz_pts[4]; // 0 = left, 1 = right
1010 // int top_color, bot_color;
1011 // int color_swap; //flag for if we swapped
1012 // int sky_ground_flag; //0=both, 1=all sky, -1=all gnd
1016 float up_right, down_right,down_left,up_left;
1018 // color_swap = 0; //assume no swap
1019 // sky_ground_flag = 0; //assume both
1021 // if ( View_matrix.uvec.y < 0.0f )
1023 // else if ( View_matrix.uvec.y == 0.0f ) {
1024 // if ( View_matrix.uvec.x > 0.0f )
1028 // if (color_swap) {
1029 // top_color = ground_color;
1030 // bot_color = sky_color;
1032 // top_color = sky_color;
1033 // bot_color = ground_color;
1036 Assert( G3_count == 1 );
1039 //compute horizon_vector
1041 horizon_vec.x = Unscaled_matrix.rvec.y*Matrix_scale.y*Matrix_scale.z;
1042 horizon_vec.y = Unscaled_matrix.uvec.y*Matrix_scale.x*Matrix_scale.z;
1043 horizon_vec.z = Unscaled_matrix.fvec.y*Matrix_scale.x*Matrix_scale.y;
1045 // now compute values & flag for 4 corners.
1046 up_right = horizon_vec.x + horizon_vec.y + horizon_vec.z;
1047 down_right = horizon_vec.x - horizon_vec.y + horizon_vec.z;
1048 down_left = -horizon_vec.x - horizon_vec.y + horizon_vec.z;
1049 up_left = -horizon_vec.x + horizon_vec.y + horizon_vec.z;
1051 //check flags for all sky or all ground.
1052 if ( (up_right<0.0f)&&(down_right<0.0f)&&(down_left<0.0f)&&(up_left<0.0f) ) {
1053 // mprintf(( "All ground.\n" ));
1057 if ( (up_right>0.0f)&&(down_right>0.0f)&&(down_left>0.0f)&&(up_left>0.0f) ) {
1058 // mprintf(( "All sky.\n" ));
1062 //mprintf(( "Horizon vec = %.4f, %.4f, %.4f\n", horizon_vec.x, horizon_vec.y, horizon_vec.z ));
1063 //mprintf(( "%.4f, %.4f, %.4f, %.4f\n", up_right, down_right, down_left, up_left ));
1066 // mprintf(( "u: %.4f %.4f %.4f c: %.4f %.4f %.4f %.4f\n",Unscaled_matrix.uvec.y,Unscaled_matrix.uvec.z,Unscaled_matrix.uvec.x,up_left,up_right,down_right,down_left ));
1067 // check for intesection with each of four edges & compute horizon line
1070 // check intersection with left edge
1071 s1 = up_left > 0.0f;
1072 s2 = down_left > 0.0f;
1074 horz_pts[cpnt].x = 0.0f;
1075 horz_pts[cpnt].y = fl_abs(up_left * Canv_h2 / horizon_vec.y);
1076 horz_pts[cpnt].edge = 0;
1080 // check intersection with top edge
1081 s1 = up_left > 0.0f;
1082 s2 = up_right > 0.0f;
1084 horz_pts[cpnt].x = fl_abs(up_left * Canv_w2 / horizon_vec.x);
1085 horz_pts[cpnt].y = 0.0f;
1086 horz_pts[cpnt].edge = 1;
1091 // check intersection with right edge
1092 s1 = up_right > 0.0f;
1093 s2 = down_right > 0.0f;
1095 horz_pts[cpnt].x = i2fl(Canvas_width)-1;
1096 horz_pts[cpnt].y = fl_abs(up_right * Canv_h2 / horizon_vec.y);
1097 horz_pts[cpnt].edge = 2;
1101 //check intersection with bottom edge
1102 s1 = down_right > 0.0f;
1103 s2 = down_left > 0.0f;
1105 horz_pts[cpnt].x = fl_abs(down_left * Canv_w2 / horizon_vec.x);
1106 horz_pts[cpnt].y = i2fl(Canvas_height)-1;
1107 horz_pts[cpnt].edge = 3;
1112 mprintf(( "HORZ: Wrong number of points (%d)\n", cpnt ));
1116 //make sure first edge is left
1118 if ( horz_pts[0].x > horz_pts[1].x ) {
1121 horz_pts[0] = horz_pts[1];
1126 // draw from left to right.
1127 gr_line( fl2i(horz_pts[0].x),fl2i(horz_pts[0].y),fl2i(horz_pts[1].x),fl2i(horz_pts[1].y) );
1134 horizon_poly dw 5 dup (?,?) ;max of 5 points
1136 ;for g3_compute_horz_vecs
1143 ;for compute corner vec
1154 _TEXT segment dword public USE32 'CODE'
1156 extn gr_setcolor_,gr_clear_canvas_
1159 ;draw a polygon (one half of horizon) from the horizon line
1160 draw_horz_poly: lea ebx,horizon_poly
1162 ;copy horizon line as first points in poly
1174 ;add corners to polygon
1176 mov eax,8[esi] ;edge number of start edge
1178 mov ecx,8[edi] ;edge number of end point
1179 sub ecx,eax ;number of edges
1183 mov edx,ecx ;save count
1185 lea esi,corners[eax] ;first corner
1186 lea edi,16[ebx] ;rest of poly
1188 movsd ;copy a corner
1189 cmp esi,offset corners+8*4 ;end of list?
1192 no_wrap: loop corner_loop
1194 ;now draw the polygon
1195 mov eax,edx ;get corner count
1196 add eax,2 ;..plus horz line end points
1197 lea edx,horizon_poly ;get the points
1198 ;; call gr_poly_ ;draw it!
1202 ;return information on the polygon that is the sky.
1203 ;takes ebx=ptr to x,y pairs, ecx=ptr to vecs for each point
1204 ;returns eax=number of points
1205 ;IMPORTANT: g3_draw_horizon() must be called before this routine.
1206 g3_compute_sky_polygon:
1207 test sky_ground_flag,-1 ;what was drawn
1211 pushm ebx,ecx,edx,esi,edi
1217 xchg esi,edi ;sky isn't top
1220 ;copy horizon line as first points in poly
1222 mov eax,[edi] ;copy end point
1227 mov eax,[esi] ;copy start point
1233 push edi ;save end point
1234 push esi ;save start point
1235 mov esi,edi ;end point is first point
1236 mov edi,ecx ;dest buffer
1237 call compute_horz_end_vec
1239 pop esi ;get back start point
1241 call compute_horz_end_vec
1243 pop edi ;get back end point
1245 add ebx,16 ;past two x,y pairs
1246 add ecx,24 ;past two vectors
1250 ;add corners to polygon
1252 mov eax,8[esi] ;edge number of start edge
1255 mov ecx,8[edi] ;edge number of end point
1256 sub ecx,eax ;number of edges
1260 push ecx ;save count
1262 lea esi,corners[eax] ;first corner
1263 mov edi,ebx ;rest of poly 2d points
1266 movsd ;copy a corner
1267 cmp esi,offset corners+8*4 ;end of list?
1274 call compute_corner_vec
1281 ;now return with count
1282 pop eax ;get corner count
1283 add eax,2 ;..plus horz line end points
1285 popm ebx,ecx,edx,esi,edi
1289 ;we drew all ground, so there was no horizon drawn
1290 was_all_ground: xor eax,eax ;no points in poly
1293 ;we drew all sky, so find 4 corners
1294 was_all_sky: pushm ebx,ecx,edx,esi,edi
1303 xor eax,eax ;start corner 0
1304 sky_loop: pushm eax,ecx,edi
1305 call compute_corner_vec
1310 mov eax,4 ;4 corners
1311 popm ebx,ecx,edx,esi,edi
1314 ;compute vector describing horizon intersection with a point.
1315 ;takes esi=2d point, edi=vec. trashes eax,ebx,ecx,edx
1316 compute_horz_end_vec:
1318 ;compute rotated x/z & y/z ratios
1320 mov eax,[esi] ;get x coord
1325 mov eax,4[esi] ;get y coord
1328 neg eax ;y inversion
1331 ;compute fraction unrotated x/z
1336 fixmul View_matrix.m9
1337 sub eax,View_matrix.m7
1338 sub eax,View_matrix.m8
1339 mov ebx,eax ;save numerator
1342 fixmul View_matrix.m3
1344 mov eax,View_matrix.m1
1345 add eax,View_matrix.m2
1348 ;now eax/ebx = z/x. do divide in way to give result < 0
1354 cmp eax,ebx ;which is bigger?
1358 ;x is bigger, so do as z/x
1362 ;now eax = z/x ratio. Compute vector by normalizing and correcting sign
1364 push eax ;save ratio
1366 imul eax ;compute z*z
1367 inc edx ;+ x*x (x==1)
1370 mov ecx,eax ;mag in ecx
1371 pop eax ;get ratio, x part
1383 ;z is bigger, so do as x/z
1388 ;now eax = x/z ratio. Compute vector by normalizing and correcting sign
1390 push eax ;save ratio
1392 imul eax ;compute x*x
1393 inc edx ;+ z*z (z==1)
1396 mov ecx,eax ;mag in ecx
1397 pop eax ;get ratio, x part
1407 finish_end: xor eax,eax ;y = 0
1410 ;now make sure that this vector is in front of you, not behind
1420 jns vec_ok ;has positive z, ok
1422 ;z is neg, flip vector
1437 ;compute vector decribing a corner of the screen.
1438 ;takes edi=vector, eax=corner num
1447 mov ebx,View_matrix.m1
1448 mov ecx,View_matrix.m4
1449 mov edx,View_matrix.m7
1460 sub ebx,View_matrix.m3
1462 sub ecx,View_matrix.m6
1464 sub edx,View_matrix.m9
1467 mov ebx,View_matrix.m5
1468 mov ecx,View_matrix.m2
1469 mov edx,View_matrix.m8
1478 sub ebx,View_matrix.m6
1480 sub ecx,View_matrix.m3
1482 sub edx,View_matrix.m9
1487 ;compute denomonator
1495 sub eax,ebx ;eax = denominator
1497 ;now we have the denominator. If it is too small, try x/y, z/y or z/x, y/x
1499 mov ecx,eax ;save den
1507 ;now do x/z numerator
1510 fixmul m56 ;* (m5-m6)
1514 fixmul m46 ;* (m4-m6)
1517 ;now, eax/ecx = x/z ratio
1519 fixdiv ecx ;eax = x/z
1521 mov [edi].x,eax ;save x
1533 ;now eax/ecx = y/z ratio
1542 call vm_vec_normalize
1544 ;make sure this vec is pointing in right direction
1546 lea edi,View_matrix.fvec
1548 or eax,eax ;check sign
1566 // Draws a polygon always facing the viewer.
1567 // compute the corners of a rod. fills in vertbuf.
1568 // Verts has any needs uv's or l's or can be NULL if none needed.
1569 int g3_draw_rod(vector *p0,float width1,vector *p1,float width2, vertex * verts, uint tmap_flags)
1571 vector uvec, fvec, rvec, center;
1573 vm_vec_sub( &fvec, p0, p1 );
1574 vm_vec_normalize_safe( &fvec );
1576 vm_vec_avg( ¢er, p0, p1 );
1577 vm_vec_sub( &rvec, &Eye_position, ¢er );
1578 vm_vec_normalize( &rvec );
1580 vm_vec_crossprod(&uvec,&fvec,&rvec);
1582 //normalize new perpendicular vector
1583 vm_vec_normalize(&uvec);
1585 //now recompute right vector, in case it wasn't entirely perpendiclar
1586 vm_vec_crossprod(&rvec,&uvec,&fvec);
1588 // Now have uvec, which is up vector and rvec which is the normal
1594 vertex *ptlist[4] = { &pts[3], &pts[2], &pts[1], &pts[0] };
1596 vm_vec_scale_add( &vecs[0], p0, &uvec, width1/2.0f );
1597 vm_vec_scale_add( &vecs[1], p1, &uvec, width2/2.0f );
1598 vm_vec_scale_add( &vecs[2], p1, &uvec, -width2/2.0f );
1599 vm_vec_scale_add( &vecs[3], p0, &uvec, -width1/2.0f );
1601 for (i=0; i<4; i++ ) {
1605 g3_rotate_vertex( &pts[i], &vecs[i] );
1608 return g3_draw_poly(4,ptlist,tmap_flags);
1611 // draw a perspective bitmap based on angles and radius
1612 vector g3_square[4] = {
1613 {-1.0f, -1.0f, 20.0f},
1614 {-1.0f, 1.0f, 20.0f},
1615 {1.0f, 1.0f, 20.0f},
1616 {1.0f, -1.0f, 20.0f}
1619 #define MAX_PERSPECTIVE_DIVISIONS 5 // should never even come close to this limit
1621 void stars_project_2d_onto_sphere( vector *pnt, float rho, float phi, float theta )
1623 float a = 3.14159f * phi;
1624 float b = 6.28318f * theta;
1625 float sin_a = (float)sin(a);
1628 pnt->z = rho * sin_a * (float)cos(b);
1629 pnt->y = rho * sin_a * (float)sin(b);
1630 pnt->x = rho * (float)cos(a);
1633 // draw a perspective bitmap based on angles and radius
1634 float p_phi = 10.0f;
1635 float p_theta = 10.0f;
1636 int g3_draw_perspective_bitmap(angles *a, float scale_x, float scale_y, int div_x, int div_y, uint tmap_flags)
1638 vector s_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
1639 vector t_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
1644 int saved_zbuffer_mode;
1648 // cap division values
1649 // div_x = div_x > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_x;
1651 div_y = div_y > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_y;
1653 // texture increment values
1654 ui = 1.0f / (float)div_x;
1655 vi = 1.0f / (float)div_y;
1657 // adjust for aspect ratio
1658 scale_x *= ((float)gr_screen.max_w / (float)gr_screen.max_h) + 0.55f; // fudge factor
1660 float s_phi = 0.5f + (((p_phi * scale_x) / 360.0f) / 2.0f);
1661 float s_theta = (((p_theta * scale_y) / 360.0f) / 2.0f);
1662 float d_phi = -(((p_phi * scale_x) / 360.0f) / (float)(div_x));
1663 float d_theta = -(((p_theta * scale_y) / 360.0f) / (float)(div_y));
1666 bank_first.p = 0.0f;
1667 bank_first.b = a->b;
1668 bank_first.h = 0.0f;
1669 vm_angles_2_matrix(&m_bank, &bank_first);
1671 // convert angles to matrix
1672 float b_save = a->b;
1674 vm_angles_2_matrix(&m, a);
1677 // generate the bitmap points
1678 for(idx=0; idx<=div_x; idx++){
1679 for(s_idx=0; s_idx<=div_y; s_idx++){
1680 // get world spherical coords
1681 stars_project_2d_onto_sphere(&s_points[idx][s_idx], 1000.0f, s_phi + ((float)idx*d_phi), s_theta + ((float)s_idx*d_theta));
1683 // bank the bitmap first
1684 vm_vec_rotate(&t_points[idx][s_idx], &s_points[idx][s_idx], &m_bank);
1686 // rotate on the sphere
1687 vm_vec_rotate(&s_points[idx][s_idx], &t_points[idx][s_idx], &m);
1691 // turn off zbuffering
1692 saved_zbuffer_mode = gr_zbuffer_get();
1693 gr_zbuffer_set(GR_ZBUFF_NONE);
1700 tmap_flags &= ~(TMAP_FLAG_CORRECT);
1704 for(idx=0; idx<div_x; idx++){
1705 for(s_idx=0; s_idx<div_y; s_idx++){
1706 // stuff texture coords
1707 v[0].u = ui * float(idx);
1708 v[0].v = vi * float(s_idx);
1710 v[1].u = ui * float(idx+1);
1711 v[1].v = vi * float(s_idx);
1713 v[2].u = ui * float(idx+1);
1714 v[2].v = vi * float(s_idx+1);
1716 v[3].u = ui * float(idx);
1717 v[3].v = vi * float(s_idx+1);
1726 g3_rotate_faraway_vertex(verts[0], &s_points[idx][s_idx]);
1727 g3_rotate_faraway_vertex(verts[1], &s_points[idx+1][s_idx]);
1728 g3_rotate_faraway_vertex(verts[2], &s_points[idx+1][s_idx+1]);
1729 g3_draw_poly(3, verts, tmap_flags);
1738 g3_rotate_faraway_vertex(verts[0], &s_points[idx][s_idx]);
1739 g3_rotate_faraway_vertex(verts[1], &s_points[idx+1][s_idx+1]);
1740 g3_rotate_faraway_vertex(verts[2], &s_points[idx][s_idx+1]);
1741 g3_draw_poly(3, verts, tmap_flags);
1749 gr_zbuffer_set(saved_zbuffer_mode);
1755 // draw a 2d bitmap on a poly
1756 int g3_draw_2d_poly_bitmap(int x, int y, int w, int h, uint additional_tmap_flags)
1759 int saved_zbuffer_mode;
1761 vertex *vertlist[4] = { &v[0], &v[1], &v[2], &v[3] };
1767 // turn off zbuffering
1768 saved_zbuffer_mode = gr_zbuffer_get();
1769 gr_zbuffer_set(GR_ZBUFF_NONE);
1771 bm_get_section_size(gr_screen.current_bitmap, gr_screen.current_bitmap_sx, gr_screen.current_bitmap_sy, &bw, &bh);
1779 v[0].flags = PF_PROJECTED;
1782 v[1].sx = (float)(x + w);
1787 v[1].flags = PF_PROJECTED;
1790 v[2].sx = (float)(x + w);
1791 v[2].sy = (float)(y + h);
1795 v[2].flags = PF_PROJECTED;
1799 v[3].sy = (float)(y + h);
1803 v[3].flags = PF_PROJECTED;
1810 v[0].u = 0.5f / i2fl(bw);
1811 v[0].v = 0.5f / i2fl(bh);
1812 v[0].flags = PF_PROJECTED;
1815 v[1].sx = (float)(x + w);
1818 v[1].u = 1.0f + (0.5f / i2fl(bw));
1819 v[1].v = 0.0f + (0.5f / i2fl(bh));
1820 v[1].flags = PF_PROJECTED;
1823 v[2].sx = (float)(x + w);
1824 v[2].sy = (float)(y + h);
1826 v[2].u = 1.0f + (0.5f / i2fl(bw));
1827 v[2].v = 1.0f + (0.5f / i2fl(bh));
1828 v[2].flags = PF_PROJECTED;
1832 v[3].sy = (float)(y + h);
1834 v[3].u = 0.0f + (0.5f / i2fl(bw));
1835 v[3].v = 1.0f + (0.5f / i2fl(bh));
1836 v[3].flags = PF_PROJECTED;
1844 ret = g3_draw_poly_constant_sw(4, vertlist, TMAP_FLAG_TEXTURED | additional_tmap_flags, 0.1f);
1848 gr_zbuffer_set(saved_zbuffer_mode);
1850 // put filtering back on