/**************************************************************** * * * rubik.c - An Animated Rubik's Cube for the Amiga * * by Bill Kinnersley * * Physics Department * * Montana State University * * Bozeman, MT 59717 * * * * This program compiles under Aztec C version 3.20a * * using either the 16-bit or 32-bit integer options. * * It is placed in the public domain. * * * * It was largely inspired by and adapted from two * * other public domain programs: * * amiga3d by Barry A. Whitebook * * skewb by Raymond S. Brand * * * * Rubik's Cube is a trademark of the Ideal Toy Corp. * * * ****************************************************************/ #include #include #include #include "rubik.h" struct TmpRas tmpras; struct BitMap bitmap[5]; struct RastPort r[5], *rp[5]; struct RasInfo ri[2], *rip[2], *oldrip; /* Rasters 0 and 1 are used alternately for the screen display. Raster 2 holds slices in the background which do not currently have to move, and 3 holds foreground slices. Raster 4 holds a black-and-white version of Raster 3 to be used for masking. */ UWORD frametoggle=0, framecount=0, axis=0, slice=0; UWORD newaxis=YES, newcube=YES; long palette[8] = { 0x0456, 0x0000, 0x0fff, 0x0fd0, 0x0f50, 0x0d00, 0x0060, 0x000d}; extern struct Tile ff[3][3][3], fb[3][3][3]; extern struct Objectinfo SliceInfo[3][3]; long GfxBase, IntuitionBase; extern void *AllocMem(), *AllocRaster(), *GetMsg(); main() { WORD i, j, error = FALSE; cubeinit(); /* Build the cube and all 9 slices */ if ((GfxBase = OpenLibrary("graphics.library",0))==NULL) exit(-1); /* Allocate all rasters */ if ((tmpras.RasPtr = (BYTE *) AllocRaster(TMPWIDTH, TMPHEIGHT)) ==NULL) error = YES; else tmpras.Size = RASSIZE(TMPWIDTH, TMPHEIGHT); for (j=0; j<5; j++) { InitBitMap(&bitmap[j], N_BIT_PLANES, WIDTH, MAXHINOLACE); for (i=0; i< N_BIT_PLANES; i++) if (!error) if ((bitmap[j].Planes[i] = (PLANEPTR) AllocMem( RASSIZE(WIDTH, MAXHINOLACE), CLEAR))==NULL) { error = YES; for (--i; i>=0; --i) FreeRaster(bitmap[j]. Planes[i],WIDTH, MAXHINOLACE); } } if (!error) dographics(); /* Main routine */ /* Deallocate rasters and quit */ FreeRaster(tmpras.RasPtr, TMPWIDTH, TMPHEIGHT); for (j=0; j<5; j++) for (i=0; i< N_BIT_PLANES; i++) FreeRaster(bitmap[j].Planes[i], WIDTH, MAXHINOLACE); } #define MAXPOINTS 64L struct AreaInfo areainfo; UWORD areafill[(MAXPOINTS/2)*5]; struct NewScreen ns = {LEFT, 0, WIDTH, MAXHINOLACE, N_BIT_PLANES, -1, -1, 0, CUSTOMSCREEN, NULL, " ", NULL, NULL}; struct NewWindow nw = {0, TOP, WIDTH, HEIGHT, -1, -1, CLOSEWINDOW, WINDOWCLOSE | SIMPLE_REFRESH | ACTIVATE, NULL, NULL, " ", NULL, NULL, 0, 0, 0, 0, CUSTOMSCREEN}; dographics() { struct Screen *screen; struct Window *window; struct RastPort *w; if ((IntuitionBase = OpenLibrary("intuition.library",0L))==NULL) return (-1); if ((screen = (struct Screen *)OpenScreen(&ns))==NULL) return(-1); /* Center the screen by hand, since Intuition won't. */ screen->ViewPort.DxOffset = LEFT; MakeScreen(screen); RethinkDisplay(); nw.Screen = screen; if ((window = (struct Window *)OpenWindow(&nw))==NULL) return (-1); InitArea(&areainfo, areafill, MAXPOINTS); window->RPort->AreaInfo = &areainfo; window->RPort->TmpRas = &tmpras; rubik(window->WScreen, window); /* Main routine */ CloseWindow(window); CloseScreen(screen); } rubik(screen, window) struct Screen *screen; struct Window *window; { struct Tile *tile; struct Objectinfo *obj; WORD i, j, error; struct IntuiMessage *message; /* Set colors for this screen */ for (i=0; i < (1<ViewPort, (long)i, palette[i]>>8 & 0xfL, palette[i]>>4 & 0xfL, palette[i] & 0xfL); oldrip = screen->ViewPort.RasInfo; /* Save ptr to old info */ for (j=0; j<5; j++) { r[j] = *(window->RPort); /* Struct copy */ r[j].TmpRas = &tmpras; r[j].BitMap = &bitmap[j]; r[j].Layer = NULL; rp[j] = &r[j]; } for (j=0; j<2; j++) { ri[j] = *(screen->ViewPort.RasInfo); /* Struct copy */ ri[j].BitMap = &bitmap[j]; rip[j] = &ri[j]; } /*SetRast(rp[frametoggle],0L); /* Clear raster we're displaying */ for (j=0; j<4; j++) SetOPen(rp[j],1L); /* Pixels in rp[4] will be either "all bits on" or "all bits off" */ SetOPen(rp[4],0L); SetAPen(rp[4],7L); /* Allocate info structures */ for (i=0; i<3; i++) for (j=0; j<3; j++) { error = allocinfo(&SliceInfo[i][j]); if (error) puts("Allocation error"); } while (TRUE) { /* Main Display Loop */ if (newaxis) { /* Have to build the back and front slices */ SetRast(rp[2],0L); SetRast(rp[3],0L); /* The 9 slices are represented by SliceInfo[slice][axis] where slice=0 is in back and slice=2 is in the front */ /* First do any background slices */ if (slice>0) doobject(rp[2], &SliceInfo[0][axis], FALSE); /* FALSE means don't rotate */ if (slice>1) doobject(rp[2], &SliceInfo[1][axis], FALSE); /* Do any foreground slices */ if (slice<1) doobject(rp[3], &SliceInfo[1][axis], FALSE); if (slice<2) { doobject(rp[3], &SliceInfo[2][axis], FALSE); /* Copy foreground to rp[4] and flood with color 7 (all bits on) to produce a mask. */ ClipBlit(rp[3], 0L, 0L, rp[4], 0L, 0L, WIDTH, 200L, 0xc0L); Flood(rp[4],0L,128L,100L); } } do { /* Check for user termination */ message = (struct IntuiMessage *) GetMsg(window->UserPort); if (message) { if (message->Class==CLOSEWINDOW) goto finis; ReplyMsg(message); } } while (message); doframe(screen, window); /* Advance the position */ /* Here goes the screen flip */ /* Take a deep breath and hold it */ Forbid(); WaitTOF(); WaitBlit(); Disable(); screen->ViewPort.RasInfo = rip[frametoggle]; ScrollVPort(&screen->ViewPort); /* OK, you may breathe again */ Enable(); Permit(); } finis: for (i=0; i<3; i++) for (j=0; j<3; j++) deallocinfo(&SliceInfo[i][j]); /* Deallocate structures */ screen->ViewPort.RasInfo = oldrip; /* Restore rasinfo pointer */ } doframe(screen) struct Screen *screen; { WORD i, j, pcount, a1, a2, s1, temp; newaxis = FALSE; if (++framecount >= MAXFRAMES) { /* Start a new slice */ framecount = 0; newaxis = TRUE; matrixinit(SliceInfo[slice][axis].matrix); a1 = (axis + 1)%3; a2 = (axis + 2)%3; s1 = (axis==1) ? slice : 2-slice; /* Rotate side face colors */ for (i=0; i<3; i++) { temp = ff[axis][i][s1].color; ff[axis][i][s1].color = ff[a1][s1][2-i].color; ff[a1][s1][2-i].color = fb[axis][s1][2-i].color; fb[axis][s1][2-i].color = fb[a1][i][s1].color; fb[a1][i][s1].color = temp; } /* Rotate front face colors */ if (s1==0) for (i=0; i<2; i++) { temp = fb[a2][i][0].color; fb[a2][i][0].color = fb[a2][0][2-i].color; fb[a2][0][2-i].color = fb[a2][2-i][2].color; fb[a2][2-i][2].color = fb[a2][2][i].color; fb[a2][2][i].color = temp; } else if (s1==2) for (i=0; i<2; i++) { temp = ff[a2][0][i].color; ff[a2][0][i].color = ff[a2][2-i][0].color; ff[a2][2-i][0].color = ff[a2][2][2-i].color; ff[a2][2][2-i].color = ff[a2][i][2].color; ff[a2][i][2].color = temp; } /* Repaint all slices */ for (i=0; i<3; i++) for (j=0; j<3; j++) { struct Objectinfo *objectinfo; WORD *nextcolor; objectinfo = &SliceInfo[i][j]; nextcolor = objectinfo->colorbuf; for (pcount=0; pcount < objectinfo->numtiles; pcount++) { struct Tile *np; np = (objectinfo->tiles)[pcount]; *nextcolor++ = np->color; } *nextcolor = 0; } if (++axis>2) { axis = 0; if (++slice>2) slice = 0; } if ((axis==0) && (slice==0)) newcube = FALSE; return; } /* Copy the background slices to the raster not being displayed */ ClipBlit(rp[2], 0L, 0L, rp[frametoggle^1], 0L,0L,WIDTH,200L,0xc0L); /* Add the rotating slice */ doobject(rp[frametoggle^1], &SliceInfo[slice][axis], YES); if (slice<2) { ClipBlit(rp[4],44L,24L,rp[frametoggle^1],44L,24L,168L,150L, 0x20L); /* Subtract the masked area */ ClipBlit(rp[3],44L,24L,rp[frametoggle^1],44L,24L,168L,150L, 0xe0L); /* Add the foreground slices */ } frametoggle ^= 1; /* Next time, we'll use the other screen */ } deallocinfo(objectinfo) struct Objectinfo *objectinfo; { if ((objectinfo->bufpoints) && (objectinfo->bufpointsize)) FreeMem(objectinfo->bufpoints, (long)objectinfo->bufpointsize); if ((objectinfo->pptrbuf) && (objectinfo->pptrbufsize)) FreeMem(objectinfo->pptrbuf, (long)objectinfo->pptrbufsize); if ((objectinfo->colorbuf) && (objectinfo->colorbufsize)) FreeMem(objectinfo->colorbuf, (long)objectinfo->colorbufsize); } allocinfo(objectinfo) struct Objectinfo *objectinfo; { WORD *nextcolor; long tcount, vcount; struct Vector **nextp; /* Bufpoints contains space for a list of Vectors arranged as an array: V[point][frame] */ if (objectinfo->numpoints==0) { objectinfo->bufpointsize = 0; return(FALSE); } if ((objectinfo->bufpoints = (struct Vector *)AllocMem( objectinfo->numpoints * (long)sizeof(struct Vector) * MAXFRAMES, CLEAR))==NULL) { objectinfo->bufpointsize = 0; return(TRUE); } objectinfo->bufpointsize = objectinfo->numpoints * sizeof(struct Vector) * MAXFRAMES; /* Traverse the Tile list and initialize buffers for color and offset lists. Pptrbuf contains pointers into the list of points. */ vcount = 4 * objectinfo->numtiles; if ((objectinfo->pptrbuf = (struct Vector **) AllocMem((long)sizeof(APTR) * (vcount + 1), CLEAR))==NULL) { objectinfo->pptrbufsize = 0; return(TRUE); } else objectinfo->pptrbufsize = sizeof(APTR) * (vcount + 1); if ((objectinfo->colorbuf = (WORD *) AllocMem((long)sizeof(WORD)* (objectinfo->numtiles + 1), CLEAR))==NULL) { objectinfo->colorbufsize = 0; return(TRUE); } else objectinfo->colorbufsize = sizeof(WORD) * (objectinfo->numtiles + 1); /* Initialize buffer pointers */ nextcolor = objectinfo->colorbuf; nextp = objectinfo->pptrbuf; for (tcount=0; tcount < objectinfo->numtiles; tcount++) { struct Tile *tp; WORD vc; struct Vector **v; tp = (objectinfo->tiles)[tcount]; *nextcolor++ = tp->color; v = tp->vertexstart; for (vc=0; vc<4; vc++) { long poff; /* Search objectinfo->points for a pointer which matches */ for (poff=0; poff < objectinfo->numpoints; poff++) if (v[vc]==(objectinfo->points)[poff]) break; *nextp = (struct Vector *)(objectinfo->bufpoints + poff*MAXFRAMES); nextp++; } } /* Terminate pointer buffer arrays with a null pointer */ *nextcolor = 0; *nextp = 0; return(FALSE); }