/* IMPACT - Virtual Reality Workshop First implementation by John Henckel on Oct 7, 1993 Acknowledgements: * This program was developed using Borland Turbo C++ 3.0. The speed and versatility of the graphics of this program is a tribute to the talented programmers at Borland. Also their online help is fabulous. * The equations for collision of irregularly shaped objects are taken from "Dynamics" by Pestel and Thomson, 1968, I haven't found such a detailed analysis of this problem in any other book. */ #include #include #include #include #include #include #include #include "mouse.h" // Macros and constants #define MA 10 // array size #define MB 50 // bodies #define MX 4 // virtual x dimension size (origin lower-left) #define MY 3 // virtual y size #define EPS 0.00001 // small number #define PI 3.1415926 #define frand(x) (x/1000.0*random(1000)) #define HOME "\x1B[;H" // ansi escape code #define rline(a,b,c,d) line((a)*xm/MX,ym-(b)*ym/MY,(c)*xm/MX,ym-(d)*ym/MY) // Types typedef float fa[MA]; typedef struct { int x,y; } pt; typedef struct { // Body: (- means scratch field) int color; // color of the body float r; // radius (maximum) float m; // mass float i; // coefficient of moment of inertia ( = I/m ) int n; // number of points in the shape (0=ball,1=??) fa sx,sy; // shape (center of mass is always origin) fa px,py; //- corner points fa len; // length of each edge (from i to i+1) pt poly[2][MA+1]; //- polygons for draw and erase int pp; //- current poly 0 or 1 float vx,vy,w; // velocity float cx,cy,a; // center position float ocx,ocy,oa; //- old center position } body; // Global Variables int xm,ym; body bod[MB]; // Array of bodies int numb; // Global options float fric=0.0; // air friction float grav=0.0; // gravity float scut=1.0; // collision center adjustment float rest=1.0; // coefficient of restitution int trace=0; // erase image on redraw int clear=0; // clear screen int mous=0; // mouse present? int delet=0; // delete last body int debug=0; float covel=0; // maximum contact velocity float pene=0; /*---------------------------------------------------------------------- Redraw_all - erase all bodies and redraw them in the new position */ int redraw_all(int p) { int b; if (clear) cleardevice(); clear=0; for (b=0; b EPS) { bod[j].vx = (x1-bod[j].cx)*VV; bod[j].vy = (y1-bod[j].cy)*VV; } if (b>1) bod[j].w = 0; // stop spin } else { // attract everything for (j=0; j2) rest=2; } if (b=='v') { msg(0,"Enter minimum collision velocity (0.0 - 0.1)"); scanf("%f",&covel); } if (b=='p') { msg(0,"Enter penetration acceleration*10000 (0.0 - 0.1)"); scanf("%f",&pene); pene /= 10000; } if (b=='e') { t=a=d=m=0; for (i=0; i2) scut=2; } if (b=='d') delet=1; if (b=='a' && numb2) d=2; if (m==0) m=PI*d*d*x1*100; if (a==0) a=d*d*x1; if (j<2) j=2; if (j>=MA) j=MA-1; if (n<0) n=0; if (n+numb > MB) n=MB-numb; if (y1>1) y1=1; // Set up the first new body bod[numb].color = c; bod[numb].r = d; bod[numb].m = m; bod[numb].i = a; for (i=numb; inumb) bod[i].cy = bod[i-1].cy + 2*d; if (bod[i].cy+d > MY) { bod[i].cy = d; bod[i].cx = bod[i-1].cx + 2*d*x1; } bod[i].n = j; for (k=0; kd2) { i2=j; d2=d; } } d1 = -dptl(px,py,b2,i2); if ((d2+d1) < EPS) d2 = d1+EPS; // slow moving body! t = d1 / (d2+d1); // t=how long ago did impact occur //--------------------------------------------------------- // Find the first impact that actually occurred ******/ /*---------------------------------------------------------------------- distance from a point to a line (the edge of another body). result is negative inside (clockwise). */ float dptl(float x,float y,int b,int i) { int t; float nx,ny; t = i+1; if (t==bod[b].n) t=0; nx = bod[b].py[i] - bod[b].py[t]; ny = bod[b].px[t] - bod[b].px[i]; // n is normal vector x -= bod[b].px[i]; y -= bod[b].py[i]; nx = (x*nx + y*ny) / bod[b].len[i]; return nx; } /*----------------------------------------------------------------- collision - test for a collision between two bodies, if so, return the point of impact, and unit vector normal to surface of impact (points into b2), and penetration distance. */ int collision(int b1,int b2, int *B1,int *B2, float *hx,float *hy, float *nx,float *ny,float *pen) { float d1,d2,d,px,py; int i,j,i1,i2,t=0; d2 = -1; while (++t < 3) { // repeat only two times for (i=0; i 0) break; // not inside if (d > d1) { d1=d; i1=j; } // find nearest edge to px,py } if (j==bod[b2].n && d1 bod[b1].px[i1]) { // find leftmost x1=bod[b1].px[i1]; y1=bod[b1].py[i1]; } if (x2 < bod[b1].px[i1]) { // find rightmost x2=bod[b1].px[i1]; y2=bod[b1].py[i1]; } } if (x2 > MX) { x1=x2-MX; y1=y2; } else if (x1 > 0) x1=0; if (x1) { y1 = bod[b1].ocy - y1; w1 = bod[b1].w; v1 = bod[b1].vx; a = y1*y1/bod[b1].i; c1 = ((a-rest)*v1 - y1*(1+rest)*w1)/(a+1); bod[b1].vx += c1-v1; bod[b1].w += y1*(c1 - v1)/bod[b1].i; bod[b1].cx -= scut*x1; } y1=MY; y2=0; for (i1=0; i1 bod[b1].py[i1]) { // find bottommost x1=bod[b1].px[i1]; y1=bod[b1].py[i1]; } if (y2 < bod[b1].py[i1]) { // find topmost x2=bod[b1].px[i1]; y2=bod[b1].py[i1]; } } if (y2 > MY) { y1=y2-MY; x1=x2; } else if (y1 > 0) y1=0; y2=y1; if (y2) { y1 = -bod[b1].ocx + x1; w1 = bod[b1].w; v1 = bod[b1].vy; a = y1*y1/bod[b1].i; c1 = ((a-rest)*v1 - y1*(1+rest)*w1)/(a+1); bod[b1].vy += c1-v1; bod[b1].w += y1*(c1 - v1)/bod[b1].i; bod[b1].cy -= scut*y2; } } // For every pair of shapes, test if any of b1's corners are inside b2. for (t1=0; t1MX) { t=1; bod[b1].cx = MX; } if (bod[b1].cy < 0) { t=1; bod[b1].cy = 0; } if (bod[b1].cy >MY) { t=1; bod[b1].cy = MY; } if (t) { bod[b1].vx *= 0.9; // slow it down! bod[b1].vy *= 0.9; bod[b1].w *= 0.9; } } return 0; } /*---------------------------------------------------------------------- Animate This is the main action loop of the program. On entry these must be set. bod, numb, xm, ym and graphics screen must be up and clear. */ int animate(void) { int p=0; compute_pxy(); // compute corners based on new c,a set_poly(p); // initialize old poly while (1) { p = 1-p; set_poly(p); // set poly based on corners if (get_input()) break; // test mouse and keyboard redraw_all(p); // erase and draw all bodies set_old(); // set old c,a,v,w to new ones. compute_new(); // new c,a = old c,a + old v,w compute_pxy(); // compute corners based on new c,a get_accel(); // test for any collisions, etc. } return 0; } /*---------------------------------------------------------------------- M A I N */ int main(int argc, char **argv) { int gm,gd=0; registerbgidriver(EGAVGA_driver); initgraph(&gd,&gm,""); srand(time(NULL)); if (graphresult() != grOk) return printf("Unable to initialize VGA adapter"); xm = getmaxx(); ym = getmaxy(); if (init_mouse()==-1) { // closegraph(); // printf("Unable to initialize mouse."); // return 0; // } // else { mous = 1; hide_mouse(); // we make our own pointer. } numb = 0; // if (argc>1) sscanf(argv[1],"%d",&numb); // if (numb >= MB) numb=MB-1; // for (b=0; b