#include #include #include #include #undef NULL #include /* ** balls - a simulation of that kinetic thingy with three-d ** smooth shaded spheres with diffuse and specular ** reflections. It'd be nice if someone could add ** sound. A good demonstration of using the ffp ** math subroutines. I plan to add texture mapping ** to the spheres in the future. ** ** ** perry s. kivolowitz - ihnp4!ptsfa!well!perry ** ** not to be distributed for commercial purposes. any ** distribution must include this notice, please. ** */ #ifdef MY_DEBUG FILE *dfp; #endif #define RADIUS 20 /* radius of the balls ve are goink to draw */ #define DEPTH 5 /* number of pixel planes */ #define NMAP 32 /* 2 to the DEPTH power */ #define AMBIENT 2 /* how much light on the dark side ofthe moon */ #define NSTEPS 6 /* how many discreet frames in bouncers */ #define SH 200 /* screen height */ #define SW 320 /* screen width */ #define WH (SH-10) /* window height */ #define WW SW /* window width */ #define MW (WW / 2) /* middle of window */ #define D (2 * RADIUS) struct IntuitionBase *IntuitionBase; struct GfxBase *GfxBase; int MathBase; int MathTransBase; int is_cli = 0; struct Window *w; /* window structure returned by exec */ struct Screen *s; /* screen structure returned by exec */ struct ColorMap *color_map; /* pointer to c_map returned by exec */ short displacements[D]; /* place for sphere's scanline dx's */ short surface[D]; /* place for spehre's scanline dz's */ extern int SPAdd() , SPSub() , SPMul() , SPDiv() , SPNeg() ; extern int SPFix() , SPFlt() , SPCmp() , SPTst() , SPAbs() ; extern int SPSqrt() , SPFieee() , SPTieee(); extern int SPSin() , SPCos() , SPPow(); union kludge { float f; int i; }; struct bouncer { struct RastPort rp; struct BitMap bm; int sx , sy; } left[NSTEPS] , right[NSTEPS]; struct point { union kludge x; union kludge y; union kludge z; } light; /* ** mask is a bit mask of things that I should close or deallocate ** when the program terminates for any reason. after opening or ** allocating some resource set the appropriate bit in mask. */ unsigned int mask = 0; #define INTUITION 0x00000001 #define GRAPHICS 0x00000002 #define SCREEN 0x00000004 #define WINDOW 0x00000008 #define COLORMAP 0x00000010 #define MATH 0x00000020 #define MATHTRANS 0x00000040 int rastcount = 0; /* easy way to free rasters at termination */ struct NewScreen ns = { /*****************/ 0 , /* LeftEdge */ 0 , /* TopEdge */ SW , /* Width */ SH , /* Height */ DEPTH , /* Depth */ 0 , /* DetailPen */ 1 , /* BlockPen */ 0 , /* ViewModes */ CUSTOMSCREEN , /* Type */ NULL , /* *Font */ " spheres by p.s.kivolowitz" , /* *DefaultTitle */ NULL , /* *Gadgets */ NULL /* *CustomBitMap */ }; /*****************/ struct NewWindow nw = { /*****************/ 0 , /* LeftEdge */ 10 , /* TopEdge */ WW , /* Width */ WH , /* Height */ -1 , /* DetailPen */ -1 , /* BlockPen */ CLOSEWINDOW , /* IDCMP---Flags */ WINDOWCLOSE /* F */ | BACKDROP /* l */ | BORDERLESS /* a */ | NOCAREREFRESH /* g */ | ACTIVATE , /* s */ NULL , /* *FirstGadget */ NULL , /* *CheckMark */ "(still under development)" ,/* *Title */ NULL , /* *Screen */ /* to be filled in */ NULL , /* *BitMap */ 0 , /* MinWidth */ 0 , /* MinHeight */ 0 , /* MaxWidth */ 0 , /* MaxHeight */ CUSTOMSCREEN /* Type */ }; /*****phew!!!*****/ float convert(i) { union kludge k; k.i = SPTieee(i); return(k.f); } degrad(degrees) { union kludge pi; pi.f = 355.0 / 113.0; /* chinese approximation */ pi.i = SPMul(SPFieee(pi.i) , SPFlt(degrees)); return(SPDiv(SPFlt(180) , pi.i)); } main(argc , argv) char *argv[]; { int i; struct IntuiMessage *message; if (argc) is_cli = 1; #ifdef MY_DEBUG if ((dfp = fopen("debug.file" , "w")) == NULL) { if (is_cli) printf("can't open debugging file\n"); exit(1); } fprintf(dfp,"debugging information\n"); fflush(dfp); #endif if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0))) { if (is_cli) printf("no graphics library!!!\n"); close_things(); exit(1); } mask |= GRAPHICS; if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library"))) { if (is_cli) printf("no intuition here!!\n"); close_things(); exit(1); } mask |= INTUITION; if ((MathBase = OpenLibrary("mathffp.library" , 0)) == NULL) { if (is_cli) printf("couldn't open mathffp library\n"); close_things(); exit(1); } mask |= MATH; if ((MathTransBase = OpenLibrary("mathtrans.library" , 0)) == NULL) { if (is_cli) printf("couldn't open mathtrans library\n"); close_things(); exit(1); } mask |= MATHTRANS; allocate_rasters(); if ((s = (struct Screen *) OpenScreen(&ns)) == (struct Screen *) NULL) { if (is_cli) printf("could not open the screen!\n"); close_things(); exit(2); } mask |= SCREEN; nw.Screen = s; if((w = (struct Window *)OpenWindow(&nw)) == (struct Window *) NULL) { if (is_cli) printf("could not open the window!\n"); close_things(); exit(2); } mask |= WINDOW; init_color_map(); light.x.f = 0.0; light.y.f = light.x.f + 150.0 - light.x.f; light.z.f = light.x.f + 25.0 - light.x.f; light.x.i = SPFieee(light.x.i); /* */ light.y.i = SPFieee(light.y.i); /* convert light vector to FFP */ light.z.i = SPFieee(light.z.i); /* */ bres(RADIUS , displacements); /* ** make the three bottom balls */ make_ball(w->RPort , MW - D , WH - RADIUS , -D , 0 , 0); make_ball(w->RPort , MW , WH - RADIUS , 0 , 0 , 0); make_ball(w->RPort , MW + D , WH - RADIUS , D , 0 , 0); make_rotated_ball(&left[0] , -15 , MW - 2 * D , 10 , WH - 10 - RADIUS); make_rotated_ball(&right[0] , 15 , MW + 2 * D , 10 , WH - 10 - RADIUS); make_rotated_ball(&left[1] , -14 , MW - 2 * D , 10 , WH - 10 - RADIUS); make_rotated_ball(&right[1] , 14 , MW + 2 * D , 10 , WH - 10 - RADIUS); make_rotated_ball(&left[2] , -12 , MW - 2 * D , 10 , WH - 10 - RADIUS); make_rotated_ball(&right[2] , 12 , MW + 2 * D , 10 , WH - 10 - RADIUS); make_rotated_ball(&left[3] , -9 , MW - 2 * D , 10 , WH - 10 - RADIUS); make_rotated_ball(&right[3], 9 , MW + 2 * D , 10 , WH - 10 - RADIUS); make_rotated_ball(&left[4] , -5 , MW - 2 * D , 10 , WH - 10 - RADIUS); make_rotated_ball(&right[4], 5 , MW + 2 * D , 10 , WH - 10 - RADIUS); make_rotated_ball(&left[5] , 0 , MW - 2 * D , 10 , WH - 10 - RADIUS); make_rotated_ball(&right[5], 0 , MW + 2 * D , 10 , WH - 10 - RADIUS); ClipBlit(&left[0].rp,0,0,w->RPort,left[0].sx,left[0].sy,D,D,0xC0); ClipBlit(&right[NSTEPS-1].rp,0,0,w->RPort,right[NSTEPS-1].sx, right[NSTEPS-1].sy,D,D,0xC0); message = (struct IntuiMessage *) GetMsg(w->UserPort); while (!message || (message->Class != CLOSEWINDOW)) { for (i = 1; i < NSTEPS; i++) { Delay(2); WaitBOVP(&s->ViewPort); clear_rect(w->RPort,left[i-1].sx,left[i-1].sy,D,D); ClipBlit(&left[i].rp,0,0,w->RPort,left[i].sx,left[i].sy,D,D,0xC0); } for (i = NSTEPS-2; i >= 0; i--) { WaitBOVP(&s->ViewPort); clear_rect(w->RPort,right[i+1].sx,right[i+1].sy,D,D); ClipBlit(&right[i].rp,0,0,w->RPort,right[i].sx,right[i].sy,D,D,0xC0); Delay(2); } Delay(1); for (i = 1; i < NSTEPS; i++) { Delay(2); WaitBOVP(&s->ViewPort); clear_rect(w->RPort,right[i-1].sx,right[i-1].sy,D,D); ClipBlit(&right[i].rp,0,0,w->RPort,right[i].sx,right[i].sy,D,D,0xC0); } for (i = NSTEPS-2; i >= 0; i--) { WaitBOVP(&s->ViewPort); clear_rect(w->RPort,left[i+1].sx,left[i+1].sy,D,D); ClipBlit(&left[i].rp,0,0,w->RPort,left[i].sx,left[i].sy,D,D,0xC0); Delay(2); } Delay(1); message = (struct IntuiMessage *) GetMsg(w->UserPort); } #ifdef MY_DEBUG fclose(dfp); #endif close_things(); exit(0); } clear_rect(rp , sx , sy , dx , dy) struct RastPort *rp; { ClipBlit(rp,sx,sy,rp,sx,sy,dx,dy,0x20); } make_rotated_ball(b , degrees , cx , cy , length) struct bouncer *b; { int dx , dy; dx = SPMul(SPFlt(length) , SPSin(degrad(abs(degrees)))); dy = SPMul(SPFlt(length) , SPCos(degrad(abs(degrees)))); b->sx = cx + ((degrees < 0) ? -SPFix(dx) : SPFix(dx)); b->sy = cy + SPFix(dy); make_ball(&b->rp , RADIUS , RADIUS , b->sx - MW , WH - RADIUS - b->sy , 0); b->sx -= RADIUS; b->sy -= RADIUS; } close_things() { if (rastcount) deallocate_rasters(); if (mask & WINDOW) CloseWindow(w); if (mask & SCREEN) CloseScreen(s); if (mask & GRAPHICS) CloseLibrary(GfxBase); (void) OpenWorkBench(); if (mask & INTUITION) CloseLibrary(IntuitionBase); } init_color_map() { static short map_values[NMAP] = { /* format 0x0RGB */ /* ooooooooh ychhhhhh! fix this later! */ /* 0 */ 0x0430 , /* 1 */ 0x0FFF , /* 2 */ 0x0F01 , /* 3 */ 0x0F11 , /* 4 */ 0x0F12 , /* 5 */ 0x0F22 , /* 6 */ 0x0F23 , /* 7 */ 0x0F33 , /* 8 */ 0x0F34 , /* 9 */ 0x0F44 , /* 10 */ 0x0F45 , /* 11 */ 0x0F55 , /* 12 */ 0x0F56 , /* 13 */ 0x0F66 , /* 14 */ 0x0F67 , /* 15 */ 0x0F77 , /* 16 */ 0x0F78 , /* 17 */ 0x0F88 , /* 18 */ 0x0F89 , /* 19 */ 0x0F99 , /* 20 */ 0x0F9A , /* 21 */ 0x0FAA , /* 22 */ 0x0FAB , /* 23 */ 0x0FBB , /* 24 */ 0x0FBC , /* 25 */ 0x0FCC , /* 26 */ 0x0FCD , /* 27 */ 0x0FDD , /* 28 */ 0x0FDE , /* 29 */ 0x0FEE , /* 30 */ 0x0FEF , /* 31 */ 0x0FFF }; LoadRGB4(&s->ViewPort , map_values , NMAP); } normalize(p) struct point *p; { union kludge length; int xsquared, ysquared, zsquared; xsquared = SPMul (p -> x.i, p -> x.i); ysquared = SPMul (p -> y.i, p -> y.i); zsquared = SPMul (p -> z.i, p -> z.i); length.i = SPSqrt (SPAdd (SPAdd (xsquared, ysquared), zsquared)); p->x.i = SPDiv(length.i , p->x.i); p->y.i = SPDiv(length.i , p->y.i); p->z.i = SPDiv(length.i , p->z.i); } make_ball(rp , basex , basey , cx , cy , cz) struct RastPort *rp; { int I , scanline; int twelve , x , y; struct point H , l; struct point pnt; union kludge tmp , rad , d; rad.i = SPDiv(SPFlt(RADIUS) , SPFlt(1)); basex -= RADIUS; basey -= RADIUS; twelve = SPFlt(12); l.x.i = SPSub(SPFlt(cx) , light.x.i); /* translate light source to */ l.y.i = SPSub(SPFlt(cy) , light.y.i); /* make center of sphere the */ l.z.i = SPSub(SPFlt(cz) , light.z.i); /* origin relative to light */ normalize(&l); for (scanline = 0; scanline < 2 * RADIUS; scanline++) { register int r; register int i; r = displacements[scanline]; y = scanline + basey; pnt.y.i = SPMul(SPFlt(RADIUS - scanline) , rad.i); bres(r , surface); for (i = 0; i < 2 * r; i++) { pnt.x.i = SPMul(SPFlt(-r + i) , rad.i); pnt.z.i = SPMul(SPFlt(surface[i]) , rad.i); d.i = SPAdd(SPMul(pnt.x.i , l.x.i) , SPMul(pnt.y.i, l.y.i)); d.i = SPAdd(d.i , SPMul(pnt.z.i , l.z.i)); I = AMBIENT; tmp.i = SPTieee(d.i); if (tmp.f > 0.0) { I += SPFix(SPMul(d.i , SPFlt(NMAP - AMBIENT))); H.x.i = l.x.i; H.y.i = l.y.i; H.z.i = SPAdd(SPFlt(1) , l.z.i); normalize(&H); /* reusing d */ d.i = SPAdd(SPMul(H.x.i , pnt.x.i), SPMul(H.y.i , pnt.y.i)); d.i = SPAdd(d.i, SPMul(H.z.i , pnt.z.i)); d.i = SPPow(twelve , d.i); tmp.i = SPTieee(d.i); if (tmp.f > 0.0) I += SPFix(SPMul(d.i , twelve)); } x = RADIUS - r + i + basex; if (I >= NMAP) I = NMAP - 1; SetAPen(rp , I); (void) WritePixel(rp , x , y); } } } allocate_rasters() { int i , j; for (i = 0; i < NSTEPS; i++) { InitRastPort(&left[i].rp); InitRastPort(&right[i].rp); InitBitMap(&left[i].bm , DEPTH , D , D); InitBitMap(&right[i].bm , DEPTH , D , D); for (j = 0; j < DEPTH; j++) { left[i].bm.Planes[j] = AllocRaster(D , D); if (left[i].bm.Planes[j] == NULL) { outer: if (is_cli) printf("cannot allocate raster space\n"); close_things(); exit(1); } rastcount++; right[i].bm.Planes[j] = AllocRaster(D , D); if (right[i].bm.Planes[j] == NULL) goto outer; rastcount++; } left[i].rp.BitMap = &left[i].bm; right[i].rp.BitMap = &right[i].bm; SetRast(&left[i].rp , 0); SetRast(&right[i].rp , 0); } } deallocate_rasters() { int i , j; for (i = 0; i < NSTEPS && rastcount >= 0; i++) { for (j = 0; j < DEPTH && rastcount >= 0; j++) { if (rastcount-- == 0) continue; FreeRaster(left[i].bm.Planes[j] , D , D); if (rastcount-- == 0) continue; FreeRaster(right[i].bm.Planes[j] , D , D); } } }