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.5 2002/06/17 06:33:10 relnev
19 * ryan's struct patch for gcc 2.95
21 * Revision 1.4 2002/06/09 04:41:25 relnev
22 * added copyright header
24 * Revision 1.3 2002/05/07 03:16:51 theoddone33
25 * The Great Newline Fix
27 * Revision 1.2 2002/05/03 13:34:33 theoddone33
30 * Revision 1.1.1.1 2002/05/03 03:28:10 root
34 * 18 9/06/99 3:23p Andsager
35 * Make fireball and weapon expl ani LOD choice look at resolution of the
38 * 17 8/27/99 9:07p Dave
39 * LOD explosions. Improved beam weapon accuracy.
41 * 16 7/29/99 12:05a Dave
42 * Nebula speed optimizations.
44 * 15 7/13/99 1:16p Dave
45 * 32 bit support. Whee!
47 * 14 7/02/99 3:05p Anoop
48 * Oops. Fixed g3_draw_2d_poly() so that it properly handles poly bitmap
49 * and LFB bitmap calls.
51 * 13 6/29/99 10:35a Dave
52 * Interface polygon bitmaps! Whee!
54 * 12 6/22/99 7:03p Dave
55 * New detail options screen.
57 * 11 6/16/99 4:06p Dave
58 * New pilot info popup. Added new draw-bitmap-as-poly function.
60 * 10 6/08/99 5:17p Dave
61 * Fixed up perspective bitmap drawing.
63 * 9 6/03/99 6:37p Dave
64 * More TNT fun. Made perspective bitmaps more flexible.
66 * 8 5/28/99 1:58p Dave
67 * Fixed problem with overwriting bank value drawing perspective bitmaps.
69 * 7 5/28/99 1:45p Dave
70 * Fixed up perspective bitmap drawing.
72 * 6 5/24/99 5:45p Dave
73 * Added detail levels to the nebula, with a decent speedup. Split nebula
74 * lightning into its own section.
76 * 5 4/07/99 6:22p Dave
77 * Fred and Freespace support for multiple background bitmaps and suns.
78 * Fixed link errors on all subprojects. Moved encrypt_init() to
79 * cfile_init() and lcl_init(), since its safe to call twice.
81 * 4 3/09/99 6:24p Dave
82 * More work on object update revamping. Identified several sources of
83 * unnecessary bandwidth.
85 * 3 2/11/99 3:08p Dave
86 * PXO refresh button. Very preliminary squad war support.
88 * 2 10/07/98 10:53a Dave
91 * 1 10/07/98 10:51a Dave
93 * 37 3/22/98 2:34p John
94 * If alpha effects is on lowest detail level, draw rotated bitmaps as
97 * 36 3/16/98 4:51p John
98 * Added low-level code to clip all polygons against an arbritary plane.
99 * Took out all old model_interp_zclip and used this new method instead.
101 * 35 3/04/98 9:29a John
102 * Made rotated bitmaps force all sw's to the same value after clipping.
104 * 34 3/03/98 6:59p John
106 * 33 1/26/98 5:12p John
107 * Added in code for Pentium Pro specific optimizations. Speed up
108 * zbuffered correct tmapper about 35%. Speed up non-zbuffered scalers
111 * 32 1/15/98 2:16p John
112 * Made bitmaps zbuffer at center minus radius.
113 * Made fireballs sort after objects they are around.
115 * 31 1/06/98 2:11p John
116 * Made g3_draw_rotated bitmap draw the same orientation as g3_draw_bitmap
117 * (orient=0) and made it be the same size (it was 2x too big before).
119 * 30 12/30/97 6:44p John
120 * Made g3_Draw_bitmap functions account for aspect of bitmap.
122 * 29 12/04/97 12:09p John
123 * Made glows use scaler instead of tmapper so they don't rotate. Had to
124 * add a zbuffered scaler.
126 * 28 12/02/97 4:00p John
127 * Added first rev of thruster glow, along with variable levels of
128 * translucency, which retquired some restructing of palman.
130 * 27 11/29/97 2:05p John
131 * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2,
132 * like they used to incorrectly assume. Added code to model to read in
135 * 26 10/20/97 4:49p John
136 * added weapon trails.
137 * added facing bitmap code to 3d lib.
139 * 25 10/03/97 9:10a John
140 * added better antialiased line drawer
142 * 24 9/09/97 3:39p Sandeep
143 * warning level 4 bugs
145 * 23 7/11/97 11:54a John
146 * added rotated 3d bitmaps.
148 * 22 5/07/97 2:59p John
149 * Initial rev of D3D texturing.
151 * 21 4/29/97 9:55a John
153 * 20 3/10/97 2:25p John
154 * Made pofview zbuffer. Made textest work with new model code. Took
155 * out some unnecessary Asserts in the 3d clipper.
158 * 19 3/06/97 5:36p Mike
159 * Change vec_normalize_safe() back to vec_normalize().
160 * Spruce up docking a bit.
162 * 18 3/06/97 10:56a Mike
163 * Write error checking version of vm_vec_normalize().
164 * Fix resultant problems.
166 * 17 2/26/97 5:24p John
167 * Added g3_draw_sphere_ez
169 * 16 2/17/97 5:18p John
170 * Added a bunch of RCS headers to a bunch of old files that don't have
176 #include "3dinternal.h"
179 #include "floating.h"
180 #include "physics.h" // For Physics_viewer_bank for g3_draw_rotated_bitmap
182 #include "systemvars.h"
183 #include "alphacolors.h"
187 //deal with a clipped line
188 int must_clip_line(vertex *p0,vertex *p1,ubyte codes_or, uint flags)
192 clip_line(&p0,&p1,codes_or, flags);
194 if (p0->codes & p1->codes) goto free_points;
196 codes_or = (unsigned char)(p0->codes | p1->codes);
198 if (codes_or & CC_BEHIND) goto free_points;
200 if (!(p0->flags&PF_PROJECTED))
201 g3_project_vertex(p0);
203 if (p0->flags&PF_OVERFLOW) goto free_points;
205 if (!(p1->flags&PF_PROJECTED))
206 g3_project_vertex(p1);
208 if (p1->flags&PF_OVERFLOW) goto free_points;
210 //gr_line(fl2i(p0->sx),fl2i(p0->sy),fl2i(p1->sx),fl2i(p1->sy));
211 // gr_line_float(p0->sx,p0->sy,p1->sx,p1->sy);
220 if (p0->flags & PF_TEMP_POINT)
223 if (p1->flags & PF_TEMP_POINT)
229 //draws a line. takes two points. returns true if drew
230 int g3_draw_line(vertex *p0,vertex *p1)
234 SDL_assert( G3_count == 1 );
236 if (p0->codes & p1->codes)
239 codes_or = (unsigned char)(p0->codes | p1->codes);
241 if (codes_or & CC_BEHIND)
242 return must_clip_line(p0,p1,codes_or,0);
244 if (!(p0->flags&PF_PROJECTED))
245 g3_project_vertex(p0);
247 if (p0->flags&PF_OVERFLOW)
249 return must_clip_line(p0,p1,codes_or,0);
252 if (!(p1->flags&PF_PROJECTED))
253 g3_project_vertex(p1);
255 if (p1->flags&PF_OVERFLOW)
257 return must_clip_line(p0,p1,codes_or,0);
260 // gr_line(fl2i(p0->sx),fl2i(p0->sy),fl2i(p1->sx),fl2i(p1->sy));
261 // gr_line_float(p0->sx,p0->sy,p1->sx,p1->sy);
268 //returns true if a plane is facing the viewer. takes the unrotated surface
269 //normal of the plane, and a point on it. The normal need not be normalized
270 int g3_check_normal_facing(vector *v,vector *norm)
274 SDL_assert( G3_count == 1 );
276 vm_vec_sub(&tempv,&View_position,v);
278 return (vm_vec_dot(&tempv,norm) > 0.0f);
281 int do_facing_check(vector *norm,vertex **vertlist,vector *p)
283 SDL_assert( G3_count == 1 );
285 if (norm) { //have normal
287 SDL_assert(norm->xyz.x || norm->xyz.y || norm->xyz.z);
289 return g3_check_normal_facing(p,norm);
291 else { //normal not specified, so must compute
295 //get three points (rotated) and compute normal
297 vm_vec_perp(&tempv,(vector *)&vertlist[0]->x,(vector *)&vertlist[1]->x,(vector *)&vertlist[2]->x);
299 return (vm_vec_dot(&tempv,(vector *)&vertlist[1]->x ) < 0.0);
303 //like g3_draw_poly(), but checks to see if facing. If surface normal is
304 //NULL, this routine must compute it, which will be slow. It is better to
305 //pre-compute the normal, and pass it to this function. When the normal
306 //is passed, this function works like g3_check_normal_facing() plus
308 //returns -1 if not facing, 1 if off screen, 0 if drew
309 int g3_draw_poly_if_facing(int nv,vertex **pointlist,uint tmap_flags,vector *norm,vector *pnt)
311 SDL_assert( G3_count == 1 );
313 if (do_facing_check(norm,pointlist,pnt))
314 return g3_draw_poly(nv,pointlist,tmap_flags);
320 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
321 //returns 1 if off screen, 0 if drew
322 int g3_draw_poly(int nv,vertex **pointlist,uint tmap_flags)
328 SDL_assert( G3_count == 1 );
330 cc.vor = 0; cc.vand = 0xff;
337 p = bufptr[i] = pointlist[i];
344 return 1; //all points off screen
347 SDL_assert( G3_count == 1 );
349 bufptr = clip_polygon(Vbuf0,Vbuf1,&nv,&cc,tmap_flags);
351 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
354 vertex *p = bufptr[i];
356 if (!(p->flags&PF_PROJECTED))
357 g3_project_vertex(p);
359 if (p->flags&PF_OVERFLOW) {
360 //Int3(); //should not overflow after clip
361 //printf( "overflow in must_clip_tmap_face\n" );
366 gr_tmapper( nv, bufptr, tmap_flags );
373 if (bufptr[i]->flags & PF_TEMP_POINT)
374 free_temp_point(bufptr[i]);
377 //now make list of 2d coords (& check for overflow)
380 vertex *p = bufptr[i];
382 if (!(p->flags&PF_PROJECTED))
383 g3_project_vertex(p);
385 if (p->flags&PF_OVERFLOW) {
386 //Int3(); //should not overflow after clip
387 //printf( "3d: Point overflowed, but flags say OK!\n" );
393 gr_tmapper( nv, bufptr, tmap_flags );
395 return 0; //say it drew
400 // Draw a polygon. Same as g3_draw_poly, but it bashes sw to a constant value
401 // for all vertexes. Needs to be done after clipping to get them all.
402 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
403 //returns 1 if off screen, 0 if drew
404 int g3_draw_poly_constant_sw(int nv,vertex **pointlist,uint tmap_flags, float constant_sw)
410 SDL_assert( G3_count == 1 );
412 cc.vor = 0; cc.vand = 0xff;
419 p = bufptr[i] = pointlist[i];
426 return 1; //all points off screen
429 SDL_assert( G3_count == 1 );
431 bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags);
433 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
436 vertex *p = bufptr[i];
438 if (!(p->flags&PF_PROJECTED))
439 g3_project_vertex(p);
441 if (p->flags&PF_OVERFLOW) {
442 //Int3(); //should not overflow after clip
443 //printf( "overflow in must_clip_tmap_face\n" );
450 gr_tmapper( nv, bufptr, tmap_flags );
452 // draw lines connecting the faces
454 gr_set_color_fast(&Color_bright_green);
455 for(i=0; i<nv-1; i++){
456 g3_draw_line(bufptr[i], bufptr[i+1]);
458 g3_draw_line(bufptr[0], bufptr[i]);
466 if (bufptr[i]->flags & PF_TEMP_POINT){
467 free_temp_point(bufptr[i]);
471 //now make list of 2d coords (& check for overflow)
474 vertex *p = bufptr[i];
476 if (!(p->flags&PF_PROJECTED))
477 g3_project_vertex(p);
479 if (p->flags&PF_OVERFLOW) {
480 //Int3(); //should not overflow after clip
481 //printf( "3d: Point overflowed, but flags say OK!\n" );
488 gr_tmapper( nv, bufptr, tmap_flags );
490 // draw lines connecting the faces
492 gr_set_color_fast(&Color_bright_green);
493 for(i=0; i<nv-1; i++){
494 g3_draw_line(bufptr[i], bufptr[i+1]);
496 g3_draw_line(bufptr[0], bufptr[i]);
499 return 0; //say it drew
502 //draw a sortof sphere - i.e., the 2d radius is proportional to the 3d
503 //radius, but not to the distance from the eye
504 int g3_draw_sphere(vertex *pnt,float rad)
506 SDL_assert( G3_count == 1 );
508 if (! (pnt->codes & CC_BEHIND)) {
510 if (! (pnt->flags & PF_PROJECTED))
511 g3_project_vertex(pnt);
513 if (! (pnt->codes & PF_OVERFLOW)) {
516 r2 = rad*Matrix_scale.xyz.x;
520 gr_circle(fl2i(pnt->sx),fl2i(pnt->sy),fl2i(t*2.0f));
527 int g3_draw_sphere_ez(vector *pnt,float rad)
532 SDL_assert( G3_count == 1 );
534 flags = g3_rotate_vertex(&pt,pnt);
538 g3_project_vertex(&pt);
540 if (!(pt.flags & PF_OVERFLOW)) {
542 g3_draw_sphere( &pt, rad );
550 //draws a bitmap with the specified 3d width & height
551 //returns 1 if off screen, 0 if drew
553 int g3_draw_bitmap(vertex *pnt,int orient, float rad,uint tmap_flags)
559 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
562 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
566 height = width*i2fl(bh)/i2fl(bw);
567 } else if ( bw > bh ) {
569 width = height*i2fl(bw)/i2fl(bh);
571 width = height = rad*2.0f;
574 width = height = rad*2.0f;
577 SDL_assert( G3_count == 1 );
579 if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) )
582 if (!(pnt->flags&PF_PROJECTED))
583 g3_project_vertex(pnt);
585 if (pnt->flags & PF_OVERFLOW)
588 t = (width*Canv_w2)/pnt->z;
589 w = t*Matrix_scale.xyz.x;
591 t = (height*Canv_h2)/pnt->z;
592 h = t*Matrix_scale.xyz.y;
595 z = pnt->z - rad/2.0f;
603 va.sx = pnt->sx - w/2.0f;
604 va.sy = pnt->sy - h/2.0f;
634 // get bitmap dims onscreen as if g3_draw_bitmap() had been called
635 int g3_get_bitmap_dims(int bitmap, vertex *pnt, float rad, int *x, int *y, int *w, int *h, int *size)
642 bm_get_info( bitmap, &bw, &bh, NULL );
646 height = width*i2fl(bh)/i2fl(bw);
647 } else if ( bw > bh ) {
649 width = height*i2fl(bw)/i2fl(bh);
651 width = height = rad*2.0f;
654 SDL_assert( G3_count == 1 );
656 if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) ) {
660 if (!(pnt->flags&PF_PROJECTED)){
661 g3_project_vertex(pnt);
664 if (pnt->flags & PF_OVERFLOW){
668 t = (width*Canv_w2)/pnt->z;
669 *w = (int)(t*Matrix_scale.xyz.x);
671 t = (height*Canv_h2)/pnt->z;
672 *h = (int)(t*Matrix_scale.xyz.y);
674 *x = (int)(pnt->sx - *w/2.0f);
675 *y = (int)(pnt->sy - *h/2.0f);
682 //draws a bitmap with the specified 3d width & height
683 //returns 1 if off screen, 0 if drew
684 int g3_draw_rotated_bitmap(vertex *pnt,float angle, float rad,uint tmap_flags)
687 vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] };
692 if ( !Detail.alpha_effects ) {
694 if ( angle < PI/2 ) {
696 } else if ( angle < PI ) {
698 } else if ( angle < PI+PI/2 ) {
703 return g3_draw_bitmap( pnt, ang, rad, tmap_flags );
707 SDL_assert( G3_count == 1 );
709 angle+=Physics_viewer_bank;
712 else if ( angle > PI2 )
716 sa = (float)sin(angle);
717 ca = (float)cos(angle);
721 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
724 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
728 height = width*i2fl(bh)/i2fl(bw);
729 } else if ( bw > bh ) {
731 width = height*i2fl(bw)/i2fl(bh);
733 width = height = rad;
736 width = height = rad;
740 v[0].x = (-width*ca + height*sa)*Matrix_scale.xyz.x + pnt->x;
741 v[0].y = (-width*sa - height*ca)*Matrix_scale.xyz.y + pnt->y;
747 v[1].x = (width*ca + height*sa)*Matrix_scale.xyz.x + pnt->x;
748 v[1].y = (width*sa - height*ca)*Matrix_scale.xyz.y + pnt->y;
754 v[2].x = (width*ca - height*sa)*Matrix_scale.xyz.x + pnt->x;
755 v[2].y = (width*sa + height*ca)*Matrix_scale.xyz.y + pnt->y;
761 v[3].x = (-width*ca - height*sa)*Matrix_scale.xyz.x + pnt->x;
762 v[3].y = (-width*sa + height*ca)*Matrix_scale.xyz.y + pnt->y;
768 ubyte codes_and=0xff;
771 z = pnt->z - rad / 4.0f;
772 if ( z < 0.0f ) z = 0.0f;
775 for (i=0; i<4; i++ ) {
776 //now code the four points
777 codes_and &= g3_code_vertex(&v[i]);
778 v[i].flags = 0; // mark as not yet projected
779 //g3_project_vertex(&v[i]);
783 return 1; //1 means off screen
786 g3_draw_poly_constant_sw(4, vertlist, tmap_flags, sw );
791 #define TRIANGLE_AREA(_p, _q, _r) do { vector a, b, cross; a.xyz.x = _q->x - _p->x; a.xyz.y = _q->y - _p->y; a.xyz.z = 0.0f; b.xyz.x = _r->x - _p->x; b.xyz.y = _r->y - _p->y; b.xyz.z = 0.0f; vm_vec_crossprod(&cross, &a, &b); total_area += vm_vec_mag(&cross) * 0.5f; } while(0);
792 float g3_get_poly_area(int nv, vertex **pointlist)
795 float total_area = 0.0f;
798 for(idx=1; idx<nv-1; idx++){
799 TRIANGLE_AREA(pointlist[0], pointlist[idx], pointlist[idx+1]);
806 // Draw a polygon. Same as g3_draw_poly, but it bashes sw to a constant value
807 // for all vertexes. Needs to be done after clipping to get them all.
808 //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture.
809 //returns 1 if off screen, 0 if drew
810 float g3_draw_poly_constant_sw_area(int nv, vertex **pointlist, uint tmap_flags, float constant_sw, float area)
817 SDL_assert( G3_count == 1 );
819 cc.vor = 0; cc.vand = 0xff;
826 p = bufptr[i] = pointlist[i];
833 return 0.0f; //all points off screen
837 SDL_assert( G3_count == 1 );
839 bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags);
841 if (nv && !(cc.vor&CC_BEHIND) && !cc.vand) {
844 vertex *p = bufptr[i];
846 if (!(p->flags&PF_PROJECTED))
847 g3_project_vertex(p);
849 if (p->flags&PF_OVERFLOW) {
850 //Int3(); //should not overflow after clip
851 //printf( "overflow in must_clip_tmap_face\n" );
859 p_area = g3_get_poly_area(nv, bufptr);
864 gr_tmapper( nv, bufptr, tmap_flags );
871 if (bufptr[i]->flags & PF_TEMP_POINT){
872 free_temp_point(bufptr[i]);
876 //now make list of 2d coords (& check for overflow)
879 vertex *p = bufptr[i];
881 if (!(p->flags&PF_PROJECTED))
882 g3_project_vertex(p);
884 if (p->flags&PF_OVERFLOW) {
892 p_area = g3_get_poly_area(nv, bufptr);
897 gr_tmapper( nv, bufptr, tmap_flags );
900 // how much area we drew
905 //draws a bitmap with the specified 3d width & height
906 //returns 1 if off screen, 0 if drew
907 float g3_draw_rotated_bitmap_area(vertex *pnt,float angle, float rad,uint tmap_flags, float area)
910 vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] };
914 SDL_assert( G3_count == 1 );
916 angle+=Physics_viewer_bank;
919 } else if ( angle > PI2 ) {
923 sa = (float)sin(angle);
924 ca = (float)cos(angle);
928 if ( tmap_flags & TMAP_FLAG_TEXTURED ) {
931 bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL );
935 height = width*i2fl(bh)/i2fl(bw);
936 } else if ( bw > bh ) {
938 width = height*i2fl(bw)/i2fl(bh);
940 width = height = rad;
943 width = height = rad;
947 v[0].x = (-width*ca + height*sa)*Matrix_scale.xyz.x + pnt->x;
948 v[0].y = (-width*sa - height*ca)*Matrix_scale.xyz.y + pnt->y;
954 v[1].x = (width*ca + height*sa)*Matrix_scale.xyz.x + pnt->x;
955 v[1].y = (width*sa - height*ca)*Matrix_scale.xyz.y + pnt->y;
961 v[2].x = (width*ca - height*sa)*Matrix_scale.xyz.x + pnt->x;
962 v[2].y = (width*sa + height*ca)*Matrix_scale.xyz.y + pnt->y;
968 v[3].x = (-width*ca - height*sa)*Matrix_scale.xyz.x + pnt->x;
969 v[3].y = (-width*sa + height*ca)*Matrix_scale.xyz.y + pnt->y;
975 ubyte codes_and=0xff;
978 z = pnt->z - rad / 4.0f;
979 if ( z < 0.0f ) z = 0.0f;
982 for (i=0; i<4; i++ ) {
983 //now code the four points
984 codes_and &= g3_code_vertex(&v[i]);
985 v[i].flags = 0; // mark as not yet projected
986 //g3_project_vertex(&v[i]);
994 return g3_draw_poly_constant_sw_area(4, vertlist, tmap_flags, sw, area );
1000 typedef struct horz_pt {
1005 //draws a horizon. takes eax=sky_color, edx=ground_color
1006 void g3_draw_horizon_line()
1008 //int sky_color,int ground_color
1011 horz_pt horz_pts[4]; // 0 = left, 1 = right
1012 // int top_color, bot_color;
1013 // int color_swap; //flag for if we swapped
1014 // int sky_ground_flag; //0=both, 1=all sky, -1=all gnd
1018 float up_right, down_right,down_left,up_left;
1020 // color_swap = 0; //assume no swap
1021 // sky_ground_flag = 0; //assume both
1023 // if ( View_matrix.uvec.y < 0.0f )
1025 // else if ( View_matrix.uvec.y == 0.0f ) {
1026 // if ( View_matrix.uvec.xyz.x > 0.0f )
1030 // if (color_swap) {
1031 // top_color = ground_color;
1032 // bot_color = sky_color;
1034 // top_color = sky_color;
1035 // bot_color = ground_color;
1038 SDL_assert( G3_count == 1 );
1041 //compute horizon_vector
1043 horizon_vec.xyz.x = Unscaled_matrix.v.rvec.xyz.y*Matrix_scale.xyz.y*Matrix_scale.xyz.z;
1044 horizon_vec.xyz.y = Unscaled_matrix.v.uvec.xyz.y*Matrix_scale.xyz.x*Matrix_scale.xyz.z;
1045 horizon_vec.xyz.z = Unscaled_matrix.v.fvec.xyz.y*Matrix_scale.xyz.x*Matrix_scale.xyz.y;
1047 // now compute values & flag for 4 corners.
1048 up_right = horizon_vec.xyz.x + horizon_vec.xyz.y + horizon_vec.xyz.z;
1049 down_right = horizon_vec.xyz.x - horizon_vec.xyz.y + horizon_vec.xyz.z;
1050 down_left = -horizon_vec.xyz.x - horizon_vec.xyz.y + horizon_vec.xyz.z;
1051 up_left = -horizon_vec.xyz.x + horizon_vec.xyz.y + horizon_vec.xyz.z;
1053 //check flags for all sky or all ground.
1054 if ( (up_right<0.0f)&&(down_right<0.0f)&&(down_left<0.0f)&&(up_left<0.0f) ) {
1055 // mprintf(( "All ground.\n" ));
1059 if ( (up_right>0.0f)&&(down_right>0.0f)&&(down_left>0.0f)&&(up_left>0.0f) ) {
1060 // mprintf(( "All sky.\n" ));
1064 //mprintf(( "Horizon vec = %.4f, %.4f, %.4f\n", horizon_vec.x, horizon_vec.y, horizon_vec.z ));
1065 //mprintf(( "%.4f, %.4f, %.4f, %.4f\n", up_right, down_right, down_left, up_left ));
1068 // 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 ));
1069 // check for intesection with each of four edges & compute horizon line
1072 // check intersection with left edge
1073 s1 = up_left > 0.0f;
1074 s2 = down_left > 0.0f;
1076 horz_pts[cpnt].x = 0.0f;
1077 horz_pts[cpnt].y = fl_abs(up_left * Canv_h2 / horizon_vec.xyz.y);
1078 horz_pts[cpnt].edge = 0;
1082 // check intersection with top edge
1083 s1 = up_left > 0.0f;
1084 s2 = up_right > 0.0f;
1086 horz_pts[cpnt].x = fl_abs(up_left * Canv_w2 / horizon_vec.xyz.x);
1087 horz_pts[cpnt].y = 0.0f;
1088 horz_pts[cpnt].edge = 1;
1093 // check intersection with right edge
1094 s1 = up_right > 0.0f;
1095 s2 = down_right > 0.0f;
1097 horz_pts[cpnt].x = i2fl(Canvas_width)-1;
1098 horz_pts[cpnt].y = fl_abs(up_right * Canv_h2 / horizon_vec.xyz.y);
1099 horz_pts[cpnt].edge = 2;
1103 //check intersection with bottom edge
1104 s1 = down_right > 0.0f;
1105 s2 = down_left > 0.0f;
1107 horz_pts[cpnt].x = fl_abs(down_left * Canv_w2 / horizon_vec.xyz.x);
1108 horz_pts[cpnt].y = i2fl(Canvas_height)-1;
1109 horz_pts[cpnt].edge = 3;
1114 mprintf(( "HORZ: Wrong number of points (%d)\n", cpnt ));
1118 //make sure first edge is left
1120 if ( horz_pts[0].x > horz_pts[1].x ) {
1123 horz_pts[0] = horz_pts[1];
1128 // draw from left to right.
1129 gr_line( fl2i(horz_pts[0].x),fl2i(horz_pts[0].y),fl2i(horz_pts[1].x),fl2i(horz_pts[1].y) );
1136 horizon_poly dw 5 dup (?,?) ;max of 5 points
1138 ;for g3_compute_horz_vecs
1145 ;for compute corner vec
1156 _TEXT segment dword public USE32 'CODE'
1158 extn gr_setcolor_,gr_clear_canvas_
1161 ;draw a polygon (one half of horizon) from the horizon line
1162 draw_horz_poly: lea ebx,horizon_poly
1164 ;copy horizon line as first points in poly
1176 ;add corners to polygon
1178 mov eax,8[esi] ;edge number of start edge
1180 mov ecx,8[edi] ;edge number of end point
1181 sub ecx,eax ;number of edges
1185 mov edx,ecx ;save count
1187 lea esi,corners[eax] ;first corner
1188 lea edi,16[ebx] ;rest of poly
1190 movsd ;copy a corner
1191 cmp esi,offset corners+8*4 ;end of list?
1194 no_wrap: loop corner_loop
1196 ;now draw the polygon
1197 mov eax,edx ;get corner count
1198 add eax,2 ;..plus horz line end points
1199 lea edx,horizon_poly ;get the points
1200 ;; call gr_poly_ ;draw it!
1204 ;return information on the polygon that is the sky.
1205 ;takes ebx=ptr to x,y pairs, ecx=ptr to vecs for each point
1206 ;returns eax=number of points
1207 ;IMPORTANT: g3_draw_horizon() must be called before this routine.
1208 g3_compute_sky_polygon:
1209 test sky_ground_flag,-1 ;what was drawn
1213 pushm ebx,ecx,edx,esi,edi
1219 xchg esi,edi ;sky isn't top
1222 ;copy horizon line as first points in poly
1224 mov eax,[edi] ;copy end point
1229 mov eax,[esi] ;copy start point
1235 push edi ;save end point
1236 push esi ;save start point
1237 mov esi,edi ;end point is first point
1238 mov edi,ecx ;dest buffer
1239 call compute_horz_end_vec
1241 pop esi ;get back start point
1243 call compute_horz_end_vec
1245 pop edi ;get back end point
1247 add ebx,16 ;past two x,y pairs
1248 add ecx,24 ;past two vectors
1252 ;add corners to polygon
1254 mov eax,8[esi] ;edge number of start edge
1257 mov ecx,8[edi] ;edge number of end point
1258 sub ecx,eax ;number of edges
1262 push ecx ;save count
1264 lea esi,corners[eax] ;first corner
1265 mov edi,ebx ;rest of poly 2d points
1268 movsd ;copy a corner
1269 cmp esi,offset corners+8*4 ;end of list?
1276 call compute_corner_vec
1283 ;now return with count
1284 pop eax ;get corner count
1285 add eax,2 ;..plus horz line end points
1287 popm ebx,ecx,edx,esi,edi
1291 ;we drew all ground, so there was no horizon drawn
1292 was_all_ground: xor eax,eax ;no points in poly
1295 ;we drew all sky, so find 4 corners
1296 was_all_sky: pushm ebx,ecx,edx,esi,edi
1305 xor eax,eax ;start corner 0
1306 sky_loop: pushm eax,ecx,edi
1307 call compute_corner_vec
1312 mov eax,4 ;4 corners
1313 popm ebx,ecx,edx,esi,edi
1316 ;compute vector describing horizon intersection with a point.
1317 ;takes esi=2d point, edi=vec. trashes eax,ebx,ecx,edx
1318 compute_horz_end_vec:
1320 ;compute rotated x/z & y/z ratios
1322 mov eax,[esi] ;get x coord
1327 mov eax,4[esi] ;get y coord
1330 neg eax ;y inversion
1333 ;compute fraction unrotated x/z
1338 fixmul View_matrix.m9
1339 sub eax,View_matrix.m7
1340 sub eax,View_matrix.m8
1341 mov ebx,eax ;save numerator
1344 fixmul View_matrix.m3
1346 mov eax,View_matrix.m1
1347 add eax,View_matrix.m2
1350 ;now eax/ebx = z/x. do divide in way to give result < 0
1356 cmp eax,ebx ;which is bigger?
1360 ;x is bigger, so do as z/x
1364 ;now eax = z/x ratio. Compute vector by normalizing and correcting sign
1366 push eax ;save ratio
1368 imul eax ;compute z*z
1369 inc edx ;+ x*x (x==1)
1372 mov ecx,eax ;mag in ecx
1373 pop eax ;get ratio, x part
1385 ;z is bigger, so do as x/z
1390 ;now eax = x/z ratio. Compute vector by normalizing and correcting sign
1392 push eax ;save ratio
1394 imul eax ;compute x*x
1395 inc edx ;+ z*z (z==1)
1398 mov ecx,eax ;mag in ecx
1399 pop eax ;get ratio, x part
1409 finish_end: xor eax,eax ;y = 0
1412 ;now make sure that this vector is in front of you, not behind
1422 jns vec_ok ;has positive z, ok
1424 ;z is neg, flip vector
1439 ;compute vector decribing a corner of the screen.
1440 ;takes edi=vector, eax=corner num
1449 mov ebx,View_matrix.m1
1450 mov ecx,View_matrix.m4
1451 mov edx,View_matrix.m7
1462 sub ebx,View_matrix.m3
1464 sub ecx,View_matrix.m6
1466 sub edx,View_matrix.m9
1469 mov ebx,View_matrix.m5
1470 mov ecx,View_matrix.m2
1471 mov edx,View_matrix.m8
1480 sub ebx,View_matrix.m6
1482 sub ecx,View_matrix.m3
1484 sub edx,View_matrix.m9
1489 ;compute denomonator
1497 sub eax,ebx ;eax = denominator
1499 ;now we have the denominator. If it is too small, try x/y, z/y or z/x, y/x
1501 mov ecx,eax ;save den
1509 ;now do x/z numerator
1512 fixmul m56 ;* (m5-m6)
1516 fixmul m46 ;* (m4-m6)
1519 ;now, eax/ecx = x/z ratio
1521 fixdiv ecx ;eax = x/z
1523 mov [edi].x,eax ;save x
1535 ;now eax/ecx = y/z ratio
1544 call vm_vec_normalize
1546 ;make sure this vec is pointing in right direction
1548 lea edi,View_matrix.fvec
1550 or eax,eax ;check sign
1568 // Draws a polygon always facing the viewer.
1569 // compute the corners of a rod. fills in vertbuf.
1570 // Verts has any needs uv's or l's or can be NULL if none needed.
1571 int g3_draw_rod(vector *p0,float width1,vector *p1,float width2, vertex * verts, uint tmap_flags)
1573 vector uvec, fvec, rvec, center;
1575 vm_vec_sub( &fvec, p0, p1 );
1576 vm_vec_normalize_safe( &fvec );
1578 vm_vec_avg( ¢er, p0, p1 );
1579 vm_vec_sub( &rvec, &Eye_position, ¢er );
1580 vm_vec_normalize( &rvec );
1582 vm_vec_crossprod(&uvec,&fvec,&rvec);
1584 //normalize new perpendicular vector
1585 vm_vec_normalize(&uvec);
1587 //now recompute right vector, in case it wasn't entirely perpendiclar
1588 vm_vec_crossprod(&rvec,&uvec,&fvec);
1590 // Now have uvec, which is up vector and rvec which is the normal
1596 vertex *ptlist[4] = { &pts[3], &pts[2], &pts[1], &pts[0] };
1598 vm_vec_scale_add( &vecs[0], p0, &uvec, width1/2.0f );
1599 vm_vec_scale_add( &vecs[1], p1, &uvec, width2/2.0f );
1600 vm_vec_scale_add( &vecs[2], p1, &uvec, -width2/2.0f );
1601 vm_vec_scale_add( &vecs[3], p0, &uvec, -width1/2.0f );
1603 for (i=0; i<4; i++ ) {
1607 g3_rotate_vertex( &pts[i], &vecs[i] );
1610 return g3_draw_poly(4,ptlist,tmap_flags);
1613 // draw a perspective bitmap based on angles and radius
1614 vector g3_square[4] = {
1615 { { { -1.0f, -1.0f, 20.0f } } },
1616 { { { -1.0f, 1.0f, 20.0f } } },
1617 { { { 1.0f, 1.0f, 20.0f } } },
1618 { { { 1.0f, -1.0f, 20.0f } } }
1621 #define MAX_PERSPECTIVE_DIVISIONS 5 // should never even come close to this limit
1623 void stars_project_2d_onto_sphere( vector *pnt, float rho, float phi, float theta )
1625 float a = 3.14159f * phi;
1626 float b = 6.28318f * theta;
1627 float sin_a = (float)sin(a);
1630 pnt->xyz.z = rho * sin_a * (float)cos(b);
1631 pnt->xyz.y = rho * sin_a * (float)sin(b);
1632 pnt->xyz.x = rho * (float)cos(a);
1635 // draw a perspective bitmap based on angles and radius
1636 float p_phi = 10.0f;
1637 float p_theta = 10.0f;
1638 int g3_draw_perspective_bitmap(angles *a, float scale_x, float scale_y, int div_x, int div_y, uint tmap_flags)
1640 vector s_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
1641 vector t_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
1646 int saved_zbuffer_mode;
1650 // cap division values
1651 // div_x = div_x > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_x;
1653 div_y = div_y > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_y;
1655 // texture increment values
1656 ui = 1.0f / (float)div_x;
1657 vi = 1.0f / (float)div_y;
1659 // adjust for aspect ratio
1660 scale_x *= ((float)gr_screen.max_w / (float)gr_screen.max_h) + 0.55f; // fudge factor
1662 float s_phi = 0.5f + (((p_phi * scale_x) / 360.0f) / 2.0f);
1663 float s_theta = (((p_theta * scale_y) / 360.0f) / 2.0f);
1664 float d_phi = -(((p_phi * scale_x) / 360.0f) / (float)(div_x));
1665 float d_theta = -(((p_theta * scale_y) / 360.0f) / (float)(div_y));
1668 bank_first.p = 0.0f;
1669 bank_first.b = a->b;
1670 bank_first.h = 0.0f;
1671 vm_angles_2_matrix(&m_bank, &bank_first);
1673 // convert angles to matrix
1674 float b_save = a->b;
1676 vm_angles_2_matrix(&m, a);
1679 // generate the bitmap points
1680 for(idx=0; idx<=div_x; idx++){
1681 for(s_idx=0; s_idx<=div_y; s_idx++){
1682 // get world spherical coords
1683 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));
1685 // bank the bitmap first
1686 vm_vec_rotate(&t_points[idx][s_idx], &s_points[idx][s_idx], &m_bank);
1688 // rotate on the sphere
1689 vm_vec_rotate(&s_points[idx][s_idx], &t_points[idx][s_idx], &m);
1693 // turn off zbuffering
1694 saved_zbuffer_mode = gr_zbuffer_get();
1695 gr_zbuffer_set(GR_ZBUFF_NONE);
1702 tmap_flags &= ~(TMAP_FLAG_CORRECT);
1706 for(idx=0; idx<div_x; idx++){
1707 for(s_idx=0; s_idx<div_y; s_idx++){
1708 // stuff texture coords
1709 v[0].u = ui * float(idx);
1710 v[0].v = vi * float(s_idx);
1712 v[1].u = ui * float(idx+1);
1713 v[1].v = vi * float(s_idx);
1715 v[2].u = ui * float(idx+1);
1716 v[2].v = vi * float(s_idx+1);
1718 v[3].u = ui * float(idx);
1719 v[3].v = vi * float(s_idx+1);
1728 g3_rotate_faraway_vertex(verts[0], &s_points[idx][s_idx]);
1729 g3_rotate_faraway_vertex(verts[1], &s_points[idx+1][s_idx]);
1730 g3_rotate_faraway_vertex(verts[2], &s_points[idx+1][s_idx+1]);
1731 g3_draw_poly(3, verts, tmap_flags);
1740 g3_rotate_faraway_vertex(verts[0], &s_points[idx][s_idx]);
1741 g3_rotate_faraway_vertex(verts[1], &s_points[idx+1][s_idx+1]);
1742 g3_rotate_faraway_vertex(verts[2], &s_points[idx][s_idx+1]);
1743 g3_draw_poly(3, verts, tmap_flags);
1751 gr_zbuffer_set(saved_zbuffer_mode);
1757 // draw a 2d bitmap on a poly
1758 int g3_draw_2d_poly_bitmap(int x, int y, int w, int h, uint additional_tmap_flags)
1761 int saved_zbuffer_mode;
1763 vertex *vertlist[4] = { &v[0], &v[1], &v[2], &v[3] };
1769 // turn off zbuffering
1770 saved_zbuffer_mode = gr_zbuffer_get();
1771 gr_zbuffer_set(GR_ZBUFF_NONE);
1773 // bm_get_section_size(gr_screen.current_bitmap, gr_screen.current_bitmap_sx, gr_screen.current_bitmap_sy, &bw, &bh);
1781 v[0].flags = PF_PROJECTED;
1784 v[1].sx = (float)(x + w);
1789 v[1].flags = PF_PROJECTED;
1792 v[2].sx = (float)(x + w);
1793 v[2].sy = (float)(y + h);
1797 v[2].flags = PF_PROJECTED;
1801 v[3].sy = (float)(y + h);
1805 v[3].flags = PF_PROJECTED;
1812 v[0].u = 0.5f / i2fl(bw);
1813 v[0].v = 0.5f / i2fl(bh);
1814 v[0].flags = PF_PROJECTED;
1817 v[1].sx = (float)(x + w);
1820 v[1].u = 1.0f + (0.5f / i2fl(bw));
1821 v[1].v = 0.0f + (0.5f / i2fl(bh));
1822 v[1].flags = PF_PROJECTED;
1825 v[2].sx = (float)(x + w);
1826 v[2].sy = (float)(y + h);
1828 v[2].u = 1.0f + (0.5f / i2fl(bw));
1829 v[2].v = 1.0f + (0.5f / i2fl(bh));
1830 v[2].flags = PF_PROJECTED;
1834 v[3].sy = (float)(y + h);
1836 v[3].u = 0.0f + (0.5f / i2fl(bw));
1837 v[3].v = 1.0f + (0.5f / i2fl(bh));
1838 v[3].flags = PF_PROJECTED;
1843 ret = g3_draw_poly_constant_sw(4, vertlist, TMAP_FLAG_TEXTURED | additional_tmap_flags, 0.1f);
1847 gr_zbuffer_set(saved_zbuffer_mode);