/*_______________________________________________________________ tc-017.c Function: This program demonstrates real-time animation techniques applied to a flight simulation prototype. Compatibility: Supports VGA and EGA graphics adapters and monitors. The software uses the 640x200 16-color mode. Remarks: Refer to the book for an explanation of the process control logic, the 3D formulas, and the line clipping routines which are used by this program. Copyright 1988 Lee Adams and TAB BOOKS Inc. _________________________________________________________________ I N C L U D E F I L E S */ #include /* supports the printf function */ #include /* supports the graphics functions */ #include /* supports the exit() function */ #include /* supports read of keyboard buffer */ #include /* supports the sine and cosine functions */ /*_______________________________________________________________ D E C L A R A T I O N S */ void graphics_setup(void);void keyboard(void);void quit_pgm(void); void notice(float x,float y);void calc_3D(void);void window(void); void clip_2D(void);void yaw_change(void);void translation(void); void crash(void);void draw_horizon(void);void horiz3D(void); void corner(void);void window(void);void grid(void); void draw_grid(void);void clip_3D(void);void window_terrain(void); void draw_line(void); int t1=1,t2=1; /* loop counters */ int p=0; /* toggle for active page and visual page */ int C0=0,C1=1,C2=2,C3=3,C4=4,C5=5,C6=6,C7=7,C8=8,C9=9,C10=10, C11=11,C12=12,C13=13,C14=14,C15=15,mode_flag=0; /* colors */ int p1=1; /* status flag for clipping lines in 3D space */ int p2=1; /* status flag for clipping lines in 2D space */ int g=7; /* color attribute */ float x=0,y=0,z=0; /* xyz world coordinates */ float x01=0,x2=0,x3=0,x4=0,x5=0,x6=0,x7=0,x8=0,x9=0,x10=0,x11=0, x12=0,x13=0,x14=0,x15=0,x16=0; /* vertices of grid */ float y01=0,y2=0,y3=0,y4=0,y5=0,y6=0,y7=0,y8=0,y9=0,y10=0,y11=0, y12=0,y13=0,y14=0,y15=0,y16=0; /* vertices of grid */ float z01=0,z2=0,z3=0,z4=0,z5=0,z6=0,z7=0,z8=0,z9=0,z10=0,z11=0, z12=0,z13=0,z14=0,z15=0,z16=0; /* vertices of grid */ float L=0; /* offset used to extrapolate grid vertices */ float sx=0,sy=0; /* display coords, output of 3D formulas */ float xa=0,ya=0,za=0; /* used in 3D formulas & 3D line clipping */ float xb=0,yb=0,zb=0; /* used in 3D line clipping routine */ float xc=0,yc=0,zc=0; /* temporary values in 3D line clipping */ float sxa=0,sya=0,sxb=0,syb=0; /* 2D line endpoints */ float sxs=0,sys=0; /* temp storage of 2D line startpoint */ float temp_swap=0; /* used for variable swaps */ float d=620; /* angular perspective factor */ double r1=6.28319; /* yaw angle, expressed in radians */ double r2=6.28319; /* roll angle, expressed in radians */ double r3=6.28319; /* pitch angle, expressed in radians */ float r1a=0,r2a=0,r3a=0; /* angle change factors */ double sr1=0,sr2=0,sr3=0; /* sine rotation factors */ double cr1=0,cr2=0,cr3=0; /* cosine rotation factors */ float mx=24,my=-7.7,mz=-88; /* viewpoint position (translation) */ float m=1; /* viewpoint change factor */ float m1=0; /* lateral movement factor */ float my1=0; /* up-down movement change */ float mx1=0; /* left-right movement change */ float mz1=0; /* forward movement change */ float maxx=639,minx=0,maxy=199,miny=0; /* 2D clipping viewport */ float c=0; /* used in line-clipping routine */ int crash_flag=0; /* toggle flag to control animation re-start */ float rx=0,ry=0; /* scaling values used in window mapping */ float screen_x=639,screen_y=199; /* used in window mapping */ /*_______________________________________________________________ M A I N R O U T I N E */ main(){ graphics_setup(); /* establish graphics mode */ rx=screen_x/799;ry=screen_y/599; /* define windowing ratio */ RESTART: /* re-start here after the crash routine */ setactivepage(p);setvisualpage(1-p);p=1-p; /* set active page */ ANIMATE: /* animation loop begins here */ cleardevice(); /* blank the hidden page */ keyboard(); /* check the keyboard */ r2=r2+r2a;r3=r3+r3a; /* new r2 roll & r3 pitch angles */ if (r2>6.28319) r2=r2-6.28319; /* inhibit range of roll angle */ if (r2<=0) r2=r2+6.28319; /* inhibit range of roll angle */ if (r3>6.28319) r3=r3-6.28319; /* inhibit range of pitch angle */ if (r3<=0) r3=r3+6.28319; /* inhibit range of pitch angle */ yaw_change(); /* calculate new yaw change, based upon roll angle */ r1=r1+r1a; /* new yaw angle */ if (r1>6.28319) r1=r1-6.28319; /* inhibit range of yaw angle */ if (r1<=0) r1=r1+6.28319; /* inhibit range of yaw angle */ sr1=sin(r1);sr2=sin(r2);sr3=sin(r3); /* new sine factors */ cr1=cos(r1);cr2=cos(r2);cr3=cos(r3); /* new cosine factors */ translation(); /* calculate movement factors */ if (my>0) crash(); /* if crash, jump to crash routine */ if (crash_flag==1){ crash_flag=0;goto RESTART;}; /* reset flag, loop to restart */ g=C1;draw_horizon(); /* set color and draw the horizon line */ corner(); /* calculate view coords for corners of terrain map */ grid(); /* calculate all other vertices for terrain map */ g=C2;draw_grid(); /* set color and draw the terrain */ setcolor(C7);circle(319,99,30); /* gunsight */ moveto(319,86);lineto(319,112);moveto(290,99);lineto(348,99); outtextxy(208,0," USING C FOR FLIGHT SIMULATION "); setactivepage(p);setvisualpage(1-p);p=1-p; /* flip pages */ goto ANIMATE;} /* infinite loop */ /*_______________________________________________________________ SUBROUTINE: yaw change This subroutine calculates the new r1a yaw change factor, based upon the r2 roll angle. */ void yaw_change(void){ if (r2>=0){ /* normal roll right */ if (r2<=1.57079){ r1a=(r2/.017453)*.00349;return;}; }; if (r2<=6.28319){ /* normal roll left */ if (r2>=4.71239){ r1a=((6.28319-r2)/.017453)*(-.00349);return;}; }; if (r2>1.57079){ /* inverted roll right */ if (r2<=3.14159){ r1a=((3.14159-r2)/.017453)*.00349;return;}; }; if (r2>3.14159){ /* inverted roll left */ if (r2<4.71239){ r1a=((r2-3.14159)/.017453)*(-.00349);return;}; }; return;} /*_______________________________________________________________ SUBROUTINE: movement routine This subroutine calculates the translation factors which control the movement of the viewpoint over the landmarks, dependent upon r1 yaw and r3 pitch. */ void translation(void){ m1=cr3*m; /* lateral movement factor linked to pitch */ my1=(-1)*sr3*m; /* vertical movement linked to pitch */ if (r3>0){ /* airspeed decreases as pitch increases */ if (r3<=1.57079) my1=cr3*my1; }; if (r3>1.57079){ /* inverted nose up mode */ if (r3<3.14159) my1=(-1)*cr3*my1; }; mx1=(-1)*sr1*m1;mz1=cr1*m1; /* lateral movement linked to yaw */ mx=mx+mx1;my=my+my1;mz=mz+mz1; /* new movement factors */ return;} /*_______________________________________________________________ SUBROUTINE: crash scenario This subroutine handles a ground crash. After a pause, the user is placed back into the simulation at start-up. */ void crash(void){ setactivepage(0);cleardevice();setvisualpage(0); setcolor(C12);moveto(280,80); outtext("C R A S H !");setcolor(C7); /* message */ for (t1=1;t1<=5;t1++){for (t2=1;t2<=30000;t2++);}; /* pause */ p1=1;g=C7;p=0; /* restore start-up values */ r1=6.28319;r2=6.28319;r3=6.28319; /* restore start-up values */ r1a=0;r2a=0;r3a=0; /* restore start-up values */ mx=24;my=-7.7;mz=-78;m=1; /* restore start-up values */ crash_flag=1; /* set flag for inspection by main routine */ return;} /*_______________________________________________________________ SUBROUTINE: draw the horizon line */ void draw_horizon(void){ if (r3>1.57079){ /* test if inverted flight */ if (r3<4.71239) goto INVERTED; }; x=-8000;y=0;z=-10000; /* left world coordinates of horizon */ horiz3D(); /* calculate unclipped display coordinates */ window(); /* map display coordinates to fit 640x200 screen */ sxa=sx;sya=sy; /* left end of line */ x=8000;y=0;z=-10000; /* right world coordinates of horizon */ horiz3D(); /* calculate unclipped display coordinates */ window(); /* map display coordinates to fit 640x200 screen */ sxb=sx;syb=sy; /* right end of line */ clip_2D(); /* clip line to fit display screen */ setcolor(g);moveto(sxa,sya);lineto(sxb,syb); /* draw line */ return; INVERTED: /* same as above but (-1)*z */ x=-8000;y=0;z=10000;horiz3D();window();sxa=sx;sya=sy; x=8000;y=0;z=10000;horiz3D();window();sxb=sx;syb=sy;clip_2D(); setcolor(g);moveto(sxa,sya);lineto(sxb,syb); /* draw line */ return;} /*_______________________________________________________________ SUBROUTINE: 3D formulas for horizon */ void horiz3D(void){ x=(-1)*x;za=cr3*z-sr3*y;ya=sr3*z+cr3*y;xa=cr2*x+sr2*ya; y=cr2*ya-sr2*x;sx=d*xa/za;sy=d*y/za;return;} /*_______________________________________________________________ SUBROUTINE: 2D LINE-CLIPPING Enter with sxa,sya and sxb,syb endpoints of 2D line to be clipped. Returns display coordinates for line clipped to fit physical screen viewport defined by minx,miny and maxx,maxy. Sets toggle flag p2 to zero if entire line is off the screen. */ void clip_2D(void){ if (sxa>sxb) {temp_swap=sxa;sxa=sxb;sxb=temp_swap; temp_swap=sya;sya=syb;syb=temp_swap;}; if (sxamaxx) {if (sxb>maxx) {p2=0;return;}} if (syamaxy) {if (syb>maxy) {p2=0;return;}} if (sxamaxy) if (syb>maxy) return; }; if (sxb>maxx) {{c=(syb-sya)/(sxb-sxa)*(maxx-sxa); /* push left */ sxb=maxx;syb=sya+c;}; if (syamaxy) if (syb>maxy) return; }; if (sya>syb) {temp_swap=sya;sya=syb;syb=temp_swap; temp_swap=sxa;sxa=sxb;sxb=temp_swap;}; if (syamaxy) {c=(sxb-sxa)/(syb-sya)*(maxy-sya); /* push up */ sxb=sxa+c;syb=maxy;}; return;} /*_______________________________________________________________ SUBROUTINE: window mapping function for horizon */ void window(void){ sx=sx+399;sy=sy+299;rx=screen_x/799;ry=screen_y/599;sx=sx*rx; sy=sy*ry;return;} /*_______________________________________________________________ SUBROUTINE: view coords for corners of terrain This subroutine calculates the rotated and translated view coordinates for the four corners of the terrain. The rest of the terrain's vertices can be extrapolated from these four vertices. */ void corner(void){ x=-80;y=0;z=-80;calc_3D();x01=x;y01=y;z01=z; x=80;y=0;z=-80;calc_3D();x2=x;y2=y;z2=z; x=80;y=0;z=80;calc_3D();x3=x;y3=y;z3=z; x=-80;y=0;z=80;calc_3D();x4=x;y4=y;z4=z; return;} /*_______________________________________________________________ SUBROUTINE: generic 3D translation & rotation formulas This subroutine first translates the terrain landmarks in order to simulate the movement of the aircraft, then the translated coordinates are rotated to simulate the effects of yaw, pitch, and roll in 3D airspace. */ void calc_3D(void){ x=x-mx;y=y+my;z=z+mz; /* translation */ xa=cr1*x-sr1*z;za=sr1*x+cr1*z; /* yaw */ z=cr3*za-sr3*y;ya=sr3*za+cr3*y; /* pitch */ x=cr2*xa+sr2*ya;y=cr2*ya-sr2*xa; /* roll */ return;} /*_______________________________________________________________ SUBROUTINE: extrapolation of vertices for grid This subroutine uses simple geometry to extrapolate 16 vertices of a grid in 3D space from four known corner locations. */ void grid(void){ L=.25*(x2-x01);x5=x01+L;x6=x5+L;x7=x6+L; L=.25*(y2-y01);y5=y01+L;y6=y5+L;y7=y6+L; L=.25*(z2-z01);z5=z01+L;z6=z5+L;z7=z6+L; L=.25*(x3-x4);x8=x4+L;x9=x8+L;x10=x9+L; L=.25*(y3-y4);y8=y4+L;y9=y8+L;y10=y9+L; L=.25*(z3-z4);z8=z4+L;z9=z8+L;z10=z9+L; L=.25*(x4-x01);x11=x01+L;x12=x11+L;x13=x12+L; L=.25*(y4-y01);y11=y01+L;y12=y11+L;y13=y12+L; L=.25*(z4-z01);z11=z01+L;z12=z11+L;z13=z12+L; L=.25*(x3-x2);x14=x2+L;x15=x14+L;x16=x15+L; L=.25*(y3-y2);y14=y2+L;y15=y14+L;y16=y15+L; L=.25*(z3-z2);z14=z2+L;z15=z14+L;z16=z15+L; return;} /*_______________________________________________________________ SUBROUTINE: draw the grid terrain This subroutine draws a sixteen-square grid. If p1 equals zero then the line is completely clipped and invisible. */ void draw_grid(void){ setcolor(g); xa=x01;ya=y01;za=z01;xb=x2;yb=y2;zb=z2;p1=1;clip_3D(); if (p1==1) draw_line(); xa=x11;ya=y11;za=z11;xb=x14;yb=y14;zb=z14;p1=1;clip_3D(); if (p1==1) draw_line(); xa=x12;ya=y12;za=z12;xb=x15;yb=y15;zb=z15;p1=1;clip_3D(); if (p1==1) draw_line(); xa=x13;ya=y13;za=z13;xb=x16;yb=y16;zb=z16;p1=1;clip_3D(); if (p1==1) draw_line(); xa=x4;ya=y4;za=z4;xb=x3;yb=y3;zb=z3;p1=1;clip_3D(); if (p1==1) draw_line(); xa=x01;ya=y01;za=z01;xb=x4;yb=y4;zb=z4;p1=1;clip_3D(); if (p1==1) draw_line(); xa=x5;ya=y5;za=z5;xb=x8;yb=y8;zb=z8;p1=1;clip_3D(); if (p1==1) draw_line(); xa=x6;ya=y6;za=z6;xb=x9;yb=y9;zb=z9;p1=1;clip_3D(); if (p1==1) draw_line(); xa=x7;ya=y7;za=z7;xb=x10;yb=y10;zb=z10;p1=1;clip_3D(); if (p1==1) draw_line(); xa=x2;ya=y2;za=z2;xb=x3;yb=y3;zb=z3;p1=1;clip_3D(); if (p1==1) draw_line(); return;} /*_______________________________________________________________ SUBROUTINE: draw clipped line on screen */ void draw_line(void){ p2=1;clip_2D(); /* p2 will be set to zero if line is invisible */ if (p2==1){moveto(sxa,sya);lineto(sxb,syb);} return;} /*_______________________________________________________________ SUBROUTINE: window mapping function for terrain This subroutine maps a world space window of 800x600 to fit the 640x200 screen, thereby ensuring the integrity of the 4:3 screen ratio and avoid distortion during 3D rotations. */ void window_terrain(void){ sxa=sxa+399;sya=sya+299;sxa=sxa*rx;sya=sya*ry; sxb=sxb+399;syb=syb+299;sxb=sxb*rx;syb=syb*ry; return;} /*_______________________________________________________________ SUBROUTINE: clip lines in 3D space This subroutine clips portions of lines which fall behind the viewpoint in 3D space and which would be invisible to the observer. Enter with xa,ya,za,xb,yb,zb view coordinates for endpoints of line to be clipped in 3D space. */ void clip_3D(void){ if (za>=-1) goto LABEL1630; /* xa,ya,za requires clipping */ goto LABEL1640; /* xa,ya,za is ok */ LABEL1630: if (zb>=-1) {p1=0;return;}; /* both endpoints hidden */ temp_swap=xb;xb=xa;xa=temp_swap; temp_swap=yb;yb=ya;ya=temp_swap; temp_swap=zb;zb=za;za=temp_swap; goto LABEL1660; /* only xb,yb,zb requires clipping */ LABEL1640: /* xa,ya,za is ok, now test xb,yb,zb */ if (zb>=-1) goto LABEL1660; /* only xb,yb,zb requires clipping */ LABEL1650: /* calculate display coords and map to screen */ sxa=d*xa/za;sya=d*ya/za;sxb=d*xb/zb;syb=d*yb/zb; window_terrain();return; LABEL1660: /* clip xb,yb,zb */ c=(xb-xa)/(zb-za)*(zb+1);xc=xb-c; c=(yb-ya)/(zb-za)*(zb+1);yc=yb-c;zc=-1; xb=xc;yb=yc;zb=zc;goto LABEL1650; return;} /*_______________________________________________________________ SUBROUTINE: dynamic keyboard input The subroutine is called on each pass through the animation loop. If the Esc key is pressed, the flight simulation will terminate. Press to roll right, to roll left. Press to push the aircraft's nose down, press to raise the nose. The aircraft will continue to roll, climb, or dive unless the key is pressed to hold its current attitude. Press <+> to increase throttle, press <-> to decrease throttle. */ void keyboard(void){ union u_type{int a;char b[3];}keystroke;char inkey=0; if (bioskey(1)==0) return; /* if no key, return */ keystroke.a=bioskey(0); /* fetch ASCII code... */ inkey=keystroke.b[0]; /* ...and load code into variable */ switch (inkey){ /* make decision based upon ASCII value */ case 27: quit_pgm(); /* Esc key */ case 104: r2a=r2a+.017453;return; /* h key roll right */ case 102: r2a=r2a-.017453;return; /* f key roll left */ case 116: r3a=r3a-.008726;return; /* t key dive */ case 98: r3a=r3a+.008726;return; /* b key climb */ case 103: r2a=0;r3a=0;return; /* g key hold */ case 61: m=m+.1;return; /* + key increase throttle */ case 45: m=m-.1;return; /* - key decrease throttle */ default: return;} /* make routine bullet-proof */ } /*_______________________________________________________________ SUBROUTINE: GRACEFUL EXIT FROM THE PROGRAM */ void quit_pgm(void){ setvisualpage(0);setactivepage(0); cleardevice();restorecrtmode();exit(0);} /*______________________________________________________________ SUBROUTINE: VGA/EGA/CGA/MCGA compatibility module */ void graphics_setup(void){ int graphics_adapter,graphics_mode; detectgraph(&graphics_adapter,&graphics_mode); if (graphics_adapter==VGA) goto VGA_EGA_mode; /* if VGA */ if (graphics_mode==EGAHI) goto VGA_EGA_mode; /* if EGA and ECD */ if (graphics_mode==EGALO) goto VGA_EGA_mode; /* if EGA and SCD */ if (graphics_adapter==CGA) goto abort_message; /* if CGA */ if (graphics_adapter==MCGA) goto abort_message; /* if MCGA */ goto abort_message; /* if no VGA, EGA, CGA, or MCGA */ VGA_EGA_mode: /* establish 640x200 16-color mode */ graphics_adapter=EGA;graphics_mode=EGALO; initgraph(&graphics_adapter,&graphics_mode,""); maxx=639;minx=0;maxy=199;miny=0; /* clipping viewport */ screen_x=639;screen_y=199; /* windowing viewport */ setcolor(C7); return; abort_message: printf("\n\nUnable to proceed.\n"); printf("Requires VGA or EGA adapter\n"); printf(" with appropriate monitor.\n"); printf("Please refer to the book.\n\n"); exit(0); } /*_______________________________________________________________ SUBROUTINE: Copyright Notice This subroutine displays the standard copyright notice. If you are typing in this program from the book you can safely omit this subroutine, provided that you also remove the instruction "notice()" from the main routine. */ int copyright[][3]={0x7c00,0x0000,0x0000,0x8231, 0x819c,0x645e,0xba4a,0x4252,0x96d0,0xa231,0x8252,0x955e,0xba4a, 0x43d2,0xf442,0x8231,0x825c,0x945e,0x7c00,0x0000,0x0000}; void notice(float x, float y){ int a,b,c; int t1=0; for (t1=0;t1<=6;t1++){a=copyright[t1][0];b=copyright[t1][1]; c=copyright[t1][2]; setlinestyle(USERBIT_LINE,a,NORM_WIDTH); moveto(x,y);lineto(x+15,y); setlinestyle(USERBIT_LINE,b,NORM_WIDTH); moveto(x+16,y);lineto(x+31,y); setlinestyle(USERBIT_LINE,c,NORM_WIDTH); moveto(x+32,y);lineto(x+47,y);y=y+1;}; setlinestyle(USERBIT_LINE,0xFFFF,NORM_WIDTH); return;} /*_______________________________________________________________ End of source code */