// galaga.c (starble galaxy) // simulation of n-body galaxy // with initial random velocity distribution // and collisions like marbles with u=.1 // will add other models later #include #include #include #include #include #include #include #include #include #define X_OFFS 160.0 #define Y_OFFS 100.0 #define X_RES 320 #define Y_RES 200 #define X_RESM 319 #define Y_RESM 199 #define VIDEO 0xA000 #define PUTPIXS(x,y,color) canvas[x+((y)<<8)+((y)<<6)] = color #define GETPIXS(x,y) canvas[x+((y)<<8)+((y)<<6)] #define M_PIO2 1.57079632679489661923 #define M_PIT2 6.28318530717958647692 #define LOOPSPTICK 18.0 // central pick of loops per clock tick for option to scale moves speeds by LOOPSPTICK/prevloopcts #define GRAV 3.50e-6 //some gravitational constant #define MAXNSTARS 100 unsigned char far *SCREEN=MK_FP(VIDEO,0); unsigned char far *canvas; typedef struct { float x; float y; float z; } vector ; typedef struct { vector r; //position vector v; //velocity float m; //mass float rad; //radius vector w; //angular velocity int c; //color } star ; star stars[MAXNSTARS], oldstars[MAXNSTARS], saved[MAXNSTARS]; // oldstars is for erase, with some wasted fields int nstars=20; int savedstars; float zthr=50.0; typedef struct { int x; int y; int c; } pixel ; void vga_mode(int mode) { union REGS regs; regs.x.ax = mode; int86(0x10, ®s, ®s); } void clear_screen(void) { _fmemset(canvas, 0, (unsigned)64000); } void vgapalset(void) { int i,j,k; int vgapal[256][3]; //custom vga palette { k=0; for (j=0;j<3;j++) for (i=0;i<(16);i++){ vgapal[i+16*j][j]= (4*i<64) ? (4*i) : 63; vgapal[i+16*j][(j+1)%3]=0; vgapal[i+16*j][(j+2)%3]=0;} k+=16*3; //48 for (j=0;j<3;j++) for (i=0;i<(16);i++){ vgapal[k+i+16*j][j]= (4*i<64) ? (4*i) : 63; vgapal[k+i+16*j][(j+1)%3]= (4*i<64) ? (4*i) : 63; vgapal[k+i+16*j][(j+2)%3]=0;} k+=16*3; //96 for (i=0;i<16;i++) for (j=0;j<3;j++) vgapal[k+i][j]= (4*i<64) ? (4*i) : 63; k+=16; //112 for (j=0;j<3;j++) for (i=0;i<(16);i++){ vgapal[k+i+16*j][j]= (4*i<64) ? (4*i) : 63; vgapal[k+i+16*j][(j+1)%3]= 2*i; vgapal[k+i+16*j][(j+2)%3]= 2*i;} k+=16*3; //164 for (j=0;j<3;j++) for (i=0;i<(16);i++){ vgapal[k+i+16*j][j]= (4*i<64) ? (4*i) : 63; vgapal[k+i+16*j][(j+1)%3]= (4*i<64) ? (4*i) : 63; vgapal[k+i+16*j][(j+2)%3]= 2*i;} k+=16*3; //208 for (j=0;j<3;j++) for (i=0;i<(16);i++){ vgapal[k+i+16*j][j]= (4*i<64) ? (4*i) : 63; vgapal[k+i+16*j][(j+1)%3]= 1.3*i; vgapal[k+i+16*j][(j+2)%3]= 1.3*i;} k+=16*3; //256 for(j=0;j<3;j++) {vgapal[0][j]=0;} } //outp(0x03C6,0xff); outp(0x03c8, 0); for(i=0;i<256;i++) { for(j=0;j<3;j++) { outp(0x03c9, vgapal[i][j]); } } } void pauser(int *pause) { while (!kbhit()) ; getch(); *pause=0; } void steppauser(int *steppause) { int ch; while (!kbhit()); ch=getch(); if(ch!=' ') *steppause=0; } int yres(int y) { if (y<0) return 0; if (y>=200) return 199; return y; } int imod( int a, int b ) //integer mod, no negative returns { a = a % b ; if (a<0) a += b; return a; } float twopimod( float angle ) { float answer; answer = fmod( angle , M_PIT2 ); if ( answer<0.0 ) answer += M_PIT2; return answer; } float pimod( float angle ) { float answer; answer = fmod( angle , M_PI ); if ( answer<0.0 ) answer += M_PI; return answer; } float nearfmodmult( float quant, float grain ) { //quantizes quantity quant to nearest multiple of grain //this should work, but test this function float flt; flt = fmod(quant,grain) ; if(flt==0.0) return quant; else { if(quant>0.0) { if ( 2.0*flt>=grain ) // fabs(grain) ) ; assume positive grain return quant+grain-flt; else return quant-flt; } if(quant<0.0) { if ( -2.0*flt>=grain ) // fabs(grain) ) ; assume positive grain return quant-grain-flt; else return quant-flt; } } } pixel screenproj(vector r) { // projection to screen //#define ZTHR 50 #define XF 0.833333333333 pixel p; if (r.zX_RESM)p.x=X_RESM; if(p.y<0)p.y=0; else if(p.y>Y_RESM)p.y=Y_RESM; if(p.c<-15)p.c=-15; } else { p.x=-1; p.y=-1; p.c=0; } return p; } vector vecttimes(float a, vector b) { b.x *= a ; b.y *= a ; b.z *= a ; return b ; } vector vectsum(vector a, vector b) { a.x += b.x ; a.y += b.y ; a.z += b.z ; return a ; } vector vectdiff(vector a, vector b) { a.x -= b.x ; a.y -= b.y ; a.z -= b.z ; return a ; } float vectdot(vector a, vector b) { a.x = a.x*b.x; a.x += a.y*b.y; a.x += a.z*b.z; return a.x ; } vector vectcross(vector a, vector b) { vector c ; c.x = a.y * b.z - b.y * a.z ; c.y = a.z * b.x - b.z * a.x ; c.z = a.x * b.y - b.x * a.y ; return c ; } float vabs2(vector a) { return vectdot(a,a); } float vabs(vector a) { return sqrt(vectdot(a,a)); } vector unitvect(vector b) { float flt; flt = vectdot(b,b); if (flt!=0.0) return vecttimes(1.0/sqrt(flt),b); else { b.x=b.y=b.z=0.0; return b; } } vector vectproj(vector a, vector b) { //returns vector of projection of a onto b b = unitvect(b); return vecttimes( vectdot(a,b), b) ; } float metric(vector a, vector b) // distance between two points { vector r; r = vectdiff(a,b); return sqrt(vectdot(r,r)); } void centerdists(void) { int i; vector ar={0.0,0.0,0.0},av={0.0,0.0,0.0}; for (i=0;i abs(x1 - x0) ) ; if (steep) { t=x0; x0=y0; y0=t; t=x1; x1=y1; y1=t; } if (x0 > x1) { t=x0; x0=x1; x1=t; t=y0; y0=y1; y1=t; } deltax = x1 - x0; deltay = abs(y1 - y0); error = 0; y = y0; if (y0 < y1) ystep = 1; else ystep = -1; for(x=x0;x<=x1;x++) { if (steep) { PUTPIXS(y,x,c); } else { PUTPIXS(x,y,c); } error = error + deltay; if ( error<<1 >= deltax ) { y = y + ystep; error = error - deltax; } } } void erasestar(int i) // { // vector r; //position // vector rad; //radius // oldstars[nstars]; pixel p; vector sp,spp; float ra=oldstars[i].rad; sp = oldstars[i].r; spp = sp; spp.z += ra; p = screenproj( spp ); if(p.x==-1)return; PUTPIXS(p.x,p.y,0); spp = sp; spp.x += ra; p = screenproj( spp ); PUTPIXS(p.x,p.y,0); spp = sp; spp.x -= ra; p = screenproj( spp ); PUTPIXS(p.x,p.y,0); spp = sp; spp.y += ra; p = screenproj( spp ); PUTPIXS(p.x,p.y,0); spp = sp; spp.y -= ra; p = screenproj( spp ); PUTPIXS(p.x,p.y,0); } void drawstar(int i, int erase) // { // vector r; //position // vector rad; //radius // int c; //color // stars[nstars]; pixel p; vector sp,spp; float ra=stars[i].rad; sp = stars[i].r; spp = sp; spp.z += ra; p = screenproj( spp ); if(p.x==-1)return; p.c += stars[i].c; if(erase) p.c=0; PUTPIXS(p.x,p.y,p.c); spp = sp; spp.x += ra; p = screenproj( spp ); p.c += stars[i].c; if(erase) p.c=0; PUTPIXS(p.x,p.y,p.c); spp = sp; spp.x -= ra; p = screenproj( spp ); p.c += stars[i].c; if(erase) p.c=0; PUTPIXS(p.x,p.y,p.c); spp = sp; spp.y += ra; p = screenproj( spp ); p.c += stars[i].c; if(erase) p.c=0; PUTPIXS(p.x,p.y,p.c); spp = sp; spp.y -= ra; p = screenproj( spp ); p.c += stars[i].c; if(erase) p.c=0; PUTPIXS(p.x,p.y,p.c); } void erasestars() { int i; for(i=0;i lastclear + (64>>(autoclear-1)) ) //~4 seconds /2^(autoclear-1) { clear_screen(); lastclear = tock; return 1;} else return 0; } /*void p1p2pushout4(float dist) { // from sphrspwa float th1,th2; //collision line bearings, theta float halfovrlp,midovrlp1,midovrlp2; float p12radii; float loni,lati; float fraction; p12radii = (p1rad+p2rad)*M_PI/Y_RES; halfovrlp = ( p12radii - dist ) *0.5 ; midovrlp1 = ( p12radii - dist )*p2rad/(p1rad+p2rad) ; midovrlp2 = p12radii - dist - midovrlp1 ; fraction = ( p1rad*M_PI/Y_RES - halfovrlp ) / dist ; intermediatepoint(p1long, p1lat, p2long, p2lat, fraction, &loni, &lati); th1 = M_PI + angtofrom(loni, lati, p1long, p1lat) ; th2 = M_PI + angtofrom(loni, lati, p2long, p2lat) ; surfacerun(p1long,p1lat,th1,midovrlp1,&p1long,&p1lat); surfacerun(p2long,p2lat,th2,midovrlp2,&p2long,&p2lat); }*/ void starblebounce(int j, int k, float uf) //collision response treating stars as marbles { vector disp; float dist, flt; vector cl; //collision line float cldot1, cldot2; star s1=stars[j],s2=stars[k]; vector v1=s1.v, v2=s2.v; vector v1icl,v2icl; // collision line initial velocities float v1iclm,v2iclm; // collision line initial velocities magnitudes vector v1fcl,v2fcl; // collision line final velocities float v1fclm,v2fclm; // collision line final velocities magnitudes vector v1ici,v2ici; // collision transverse initial velocities vector v1fci,v2fci; // collision transverse final velocities float m1=s1.m, m2=s2.m; disp = vectdiff(s2.r,s1.r); dist = vabs(disp); if(dist==0.0) return; cl = vecttimes( 1.0/dist , disp ); // collision line unit vector from 1 to 2 v1iclm = vectdot( v1 , cl ); v2iclm = vectdot( v2 , cl ); if (v1iclm-v2iclm<0.0) return; //not colliding v1icl = vecttimes( v1iclm , cl ); v2icl = vecttimes( v2iclm , cl ); v1ici = vectdiff( v1 , v1icl ); v2ici = vectdiff( v2 , v2icl ); v1fclm = ( (m1-m2) * v1iclm + 2.0 * m2 * v2iclm ) / ( m1 + m2 ) ; v2fclm = ( (m2-m1) * v2iclm + 2.0 * m1 * v1iclm ) / ( m1 + m2 ) ; v1fcl = vecttimes( v1fclm , cl ); v2fcl = vecttimes( v2fclm , cl ); if ( uf!=0.0 ) { vector cp; //collision point position float rad1=s1.rad, rad2=s2.rad; vector v1cp,v2cp; // collision-transverse velocities at the contact point vector rv1cp; // contact point relative collision-transverse velocity vector rv1cpuv,rv2cpuv; // '' unit vectors vector w1i=s1.w, w2i=s2.w, w1f, w2f; float cldv1,cldv2; //collision velocity delta for friction normal float i1,i2; // moments of inertia int overfric1,overfric2; float td1=1.0, td2=1.0; vector cpr1,cpr2; //contact point from ball centers cp = vectsum( vecttimes( 0.5*rad1 - 0.5*rad2 + 0.5*dist , cl ), s1.r ); // i1 = ballmoment(m1,rad1); i2 = ballmoment(m2,rad2); cpr1 = vectdiff(cp,s1.r); // cpr2 = vectdiff(cp,s2.r); //cpr1 = vecttimes( rad1, unitvect( vectdiff(cp,s1.r) ) ); // //cpr2 = vecttimes( rad2, unitvect( vectdiff(cp,s2.r) ) ); cldv1 = vabs(vectdiff(v1fcl,v1icl)); cldv2 = vabs(vectdiff(v2fcl,v2icl)); v1cp = vectsum( v1ici, vectcross( w1i, cpr1 ) ); v2cp = vectsum( v2ici, vectcross( w2i, cpr2 ) ); rv1cp = vectdiff( v1cp, v2cp ); //rv2cp = vectdiff( v2cp, v1cp ); rv1cpuv = unitvect(rv1cp); rv2cpuv = vecttimes(-1.0,rv1cpuv); v1fci = vectsum( v1ici, vecttimes( -uf*cldv1, rv1cpuv ) ); v2fci = vectsum( v2ici, vecttimes( -uf*cldv2, rv2cpuv ) ); w1f = vectsum( w1i, vectcross( cpr1, vecttimes( -uf*cldv1*m1/i1, rv1cpuv ) ) ); w2f = vectsum( w2i, vectcross( cpr2, vecttimes( -uf*cldv2*m2/i2, rv2cpuv ) ) ); if( vectdot( vectsum( v1fci, vectcross( w1f, cpr1) ), rv1cpuv) - vectdot( v2cp, rv1cpuv) < 0.0 ) overfric1=1; else overfric1=0; if( vectdot( vectsum( v2fci, vectcross( w2f, cpr2) ), rv2cpuv) - vectdot( v1cp, rv2cpuv) < 0.0 ) overfric2=1; else overfric2=0; if( overfric1 || overfric2 ) { if ( overfric1 ) td1 = vectdot(rv1cp,rv1cpuv) / ( uf * cldv1 * (1.0 + m1*vabs2(cpr1)/i1) ); if ( overfric2 ) td2 = vectdot(rv1cp,rv1cpuv) / ( uf * cldv2 * (1.0 + m2*vabs2(cpr2)/i2) ); if(td1>1.0 || td2>1.0){ gotoxy(20,1); printf("%4f %4f",td1,td2); }//I hope this never happens if ( td2m1*vabs2(v1ici)+i1*vabs2(w1i)+m2*vabs2(v2ici)+i2*vabs2(w2i) ) { float tlke,a,b; b = -2.0*m1*uf*cldv1*td1*vectdot(v1ici,rv1cpuv) -2.0*vabs(cpr1)*uf*cldv1*m1*td1*vectdot(w1i,rv1cpuv) -2.0*m2*uf*cldv2*td1*vectdot(v2ici,rv2cpuv) -2.0*vabs(cpr2)*uf*cldv2*m2*td1*vectdot(w2i,rv2cpuv); a = m1*pow(uf*cldv1*td1,2) + i1*pow(vabs(cpr1)*uf*cldv1*m1/i1*td1,2) + m2*pow(uf*cldv2*td2,2) + i2*pow(vabs(cpr2)*uf*cldv2*m2/i2*td2,2); tlke = -b/a; if(tlke<0.0)tlke=0.0; v1fci = vectsum( v1ici, vecttimes( -uf*cldv1*td1*tlke, rv1cpuv ) ); v2fci = vectsum( v2ici, vecttimes( -uf*cldv2*td1*tlke, rv2cpuv ) ); w1f = vectsum( w1i, vectcross( cpr1, vecttimes( -uf*cldv1*m1/i1*td1*tlke, rv1cpuv ) ) ); w2f = vectsum( w2i, vectcross( cpr2, vecttimes( -uf*cldv2*m2/i2*td1*tlke, rv2cpuv ) ) ); } stars[j].w=w1f; stars[k].w=w2f; stars[j].v=vectsum(v1fcl,v1fci); stars[k].v=vectsum(v2fcl,v2fci); } else { stars[j].v=vectsum(v1fcl,v1ici); stars[k].v=vectsum(v2fcl,v2ici); } } /* void forwardafterbackout(,,float a, float dt) { //forward moves after collision bounce after back-out of pre-collision penetration } float backoutprecollpenet(,,, float dt) { //backs out the pre-collision penetration } */ void starblecollisions(float dt) { //static int colliding[((nstars-1)*nstars)/2]; float radii; int j,k; for (j=0;j tick ) // how long does it take for clock to flip a u long integer? 7 years. { prevloopcts = loopctr - prevtickloopct; // (int)(loopctr - prevtickloopct); ch=0; for(i=1;i<16;i++) ch+=smoothedloopcts[i]; smoothedloopcts[0] = (ch+prevloopcts)>>4; smoothedloopcts[smoothedlci]=prevloopcts; smoothedlci = ( smoothedlci + 1 )%15 + 1; prevtickloopct = loopctr; tick = tock; } //system clock() ticks ~18.2 times a second if (pause) { pauser(&pause); } if(kbhit()) { ch=getch(); switch( ch ) { case 0x1b: { if(!intextscr) quit=1; else { intextscr = 0; vga_mode(0x13); vgapalset(); } } break; case '-': case '`': clear_screen(); break; case '~': vga_mode(0x13); vgapalset(); break; case ' ': pause=1; break; case '|': steppause=1; //hit ' ' to step in steppause break; case '%': delnum++; break; case '5': {if (delnum!=0) {delnummem=delnum; delnum=0;} else { delnum=delnummem; } } break; case 'T': case 't': erasetrails=!erasetrails; break; case 'A': zthr*=0.99; if(zthr<1.0)zthr=1.0; break; case 'a': zthr+=3.0; break; case '[': physdisp=(physdisp+1)%3; break; case 'k': kollisions=!kollisions; break; case 'b': starbreak(); //remove a starble to a distance break; case 'B': starbreak2(); //drop in another starble break; case 'o': orbitshot(); //throw a starble break; case 'r': nstars--; if(nstars==0)nstars=1; break; case 'c': centerdists(); //recenter the galaga break; case 'H': savecopy(); break; case 'h': restoresaved(); break; case '\\': fpsreadout=!fpsreadout; break; case '=': case 'I': nstars=20; case 'i': init_stars(); //random distribution of velocities and positions, centered break; } // end switch( ch ) } if( autoclear && autocls(tock,autoclear) && !intextscr ) gtick=0; if ( !pause ) { /* float dt=1.0, flt; int dts=1, mtdts=1, h; flt=; for(i=0;i2.0*vinc ) { dts=ceil(flt/vinc); dt=1.0/dts; } if( movestiming ) { flt = (float)LOOPSPTICK/prevloopcts; mtdts=ceil(flt); dt*=flt/mtdts; } //{ gotoxy(30,1); printf("%3d %3d",mtdts,dts); } for (h=0;hgtick) { gtick=tock; if(erasetrails) { erasestars(); starserasecopy(); } drawstars(0); } if( fpsreadout ) { gotoxy(2,1); printf("%4d",smoothedloopcts[0]*18); } //*18.2); } if( physdisp && (loopctr-physdisploopct>prevloopcts || pause) ) { physdisploopct = loopctr; physdisplay(physdisp); } if(steppause) {steppauser(&steppause);} // } tock = clock(); if(!intextscr) vga_mode(0x03); printf("Time elapsed: %.1f seconds.\n", (tock - start) / CLK_TCK); printf("In 1/18.2 second clock ticks: %d.\n", (tock - start) ); pauser(&pause); return 0; }