/* 3D animation program to view a cube as it rotates in mode X. The viewpoint is fixed at the origin (0,0,0) of world space, looking in the direction of increasingly negative Z. A right-handed coordinate system is used throughout. All C code tested with Borland C++ 2.0 in C compilation mode */ #include #include #include #include "polygon.h" #define ROTATION (M_PI / 30.0) /* rotate by 6 degrees at a time */ /* Base offset of page to which to draw */ unsigned int CurrentPageBase = 0; /* Clip rectangle; clips to the screen */ int ClipMinX=0, ClipMinY=0; int ClipMaxX=SCREEN_WIDTH, ClipMaxY=SCREEN_HEIGHT; /* Rectangle specifying extent to be erased in each page */ struct Rect EraseRect[2] = { {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}, {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT} }; static unsigned int PageStartOffsets[2] = {PAGE0_START_OFFSET,PAGE1_START_OFFSET}; int DisplayedPage, NonDisplayedPage; /* Transformation from cube's object space to world space. Initially set up to perform no rotation and to move the cube into world space -100 units away from the origin down the Z axis. Given the viewing point, -100 down the Z axis means 100 units away in the direction of view. The program dynamically changes both the translation and the rotation. */ static double CubeWorldXform[4][4] = { {1.0, 0.0, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0}, {0.0, 0.0, 1.0, -100.0}, {0.0, 0.0, 0.0, 1.0} }; /* Transformation from world space into view space. Because in this application the view point is fixed at the origin of world space, looking down the Z axis in the direction of increasing Z, view space is identical to world space, and this is the identity matrix */ static double WorldViewXform[4][4] = { {1.0, 0.0, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0}, {0.0, 0.0, 1.0, 0.0}, {0.0, 0.0, 0.0, 1.0} }; /* All vertices in the cube */ static struct Point3 CubeVerts[] = { {15,15,15,1},{15,15,-15,1},{15,-15,15,1},{15,-15,-15,1}, {-15,15,15,1},{-15,15,-15,1},{-15,-15,15,1},{-15,-15,-15,1}}; /* Vertices after transformation */ static struct Point3 XformedCubeVerts[sizeof(CubeVerts)/sizeof(struct Point3)]; /* Vertices after projection */ static struct Point3 ProjectedCubeVerts[sizeof(CubeVerts)/sizeof(struct Point3)]; /* Vertices in screen coordinates */ static struct Point ScreenCubeVerts[sizeof(CubeVerts)/sizeof(struct Point3)]; /* Vertex indices for individual faces */ static int Face1[] = {1,3,2,0}; static int Face2[] = {5,7,3,1}; static int Face3[] = {4,5,1,0}; static int Face4[] = {3,7,6,2}; static int Face5[] = {5,4,6,7}; static int Face6[] = {0,2,6,4}; /* List of cube faces */ static struct Face CubeFaces[] = {{Face1,4,15},{Face2,4,14}, {Face3,4,12},{Face4,4,11},{Face5,4,10},{Face6,4,9}}; /* Master description for cube */ static struct Object Cube = {sizeof(CubeVerts)/sizeof(struct Point3), CubeVerts, XformedCubeVerts, ProjectedCubeVerts, ScreenCubeVerts, sizeof(CubeFaces)/sizeof(struct Face), CubeFaces}; void main() { int Done = 0, RecalcXform = 1; double WorkingXform[4][4]; union REGS regset; /* Set up the initial transformation */ Set320x240Mode(); /* set the screen to mode X */ ShowPage(PageStartOffsets[DisplayedPage = 0]); /* Keep transforming the cube, drawing it to the undisplayed page, and flipping the page to show it */ do { /* Regenerate the object->view transformation and retransform/project if necessary */ if (RecalcXform) { ConcatXforms(WorldViewXform, CubeWorldXform, WorkingXform); /* Transform and project all the vertices in the cube */ XformAndProjectPoints(WorkingXform, &Cube); RecalcXform = 0; } CurrentPageBase = /* select other page for drawing to */ PageStartOffsets[NonDisplayedPage = DisplayedPage ^ 1]; /* Clear the portion of the non-displayed page that was drawn to last time, then reset the erase extent */ FillRectangleX(EraseRect[NonDisplayedPage].Left, EraseRect[NonDisplayedPage].Top, EraseRect[NonDisplayedPage].Right, EraseRect[NonDisplayedPage].Bottom, CurrentPageBase, 0); EraseRect[NonDisplayedPage].Left = EraseRect[NonDisplayedPage].Top = 0x7FFF; EraseRect[NonDisplayedPage].Right = EraseRect[NonDisplayedPage].Bottom = 0; /* Draw all visible faces of the cube */ DrawVisibleFaces(&Cube); /* Flip to display the page into which we just drew */ ShowPage(PageStartOffsets[DisplayedPage = NonDisplayedPage]); while (kbhit()) { switch (getch()) { case 0x1B: /* Esc to exit */ Done = 1; break; case 'A': case 'a': /* away (-Z) */ CubeWorldXform[2][3] -= 3.0; RecalcXform = 1; break; case 'T': /* towards (+Z). Don't allow to get too */ case 't': /* close, so Z clipping isn't needed */ if (CubeWorldXform[2][3] < -40.0) { CubeWorldXform[2][3] += 3.0; RecalcXform = 1; } break; case '4': /* rotate clockwise around Y */ AppendRotationY(CubeWorldXform, -ROTATION); RecalcXform=1; break; case '6': /* rotate counterclockwise around Y */ AppendRotationY(CubeWorldXform, ROTATION); RecalcXform=1; break; case '8': /* rotate clockwise around X */ AppendRotationX(CubeWorldXform, -ROTATION); RecalcXform=1; break; case '2': /* rotate counterclockwise around X */ AppendRotationX(CubeWorldXform, ROTATION); RecalcXform=1; break; case 0: /* extended code */ switch (getch()) { case 0x3B: /* rotate counterclockwise around Z */ AppendRotationZ(CubeWorldXform, ROTATION); RecalcXform=1; break; case 0x3C: /* rotate clockwise around Z */ AppendRotationZ(CubeWorldXform, -ROTATION); RecalcXform=1; break; case 0x4B: /* left (-X) */ CubeWorldXform[0][3] -= 3.0; RecalcXform=1; break; case 0x4D: /* right (+X) */ CubeWorldXform[0][3] += 3.0; RecalcXform=1; break; case 0x48: /* up (+Y) */ CubeWorldXform[1][3] += 3.0; RecalcXform=1; break; case 0x50: /* down (-Y) */ CubeWorldXform[1][3] -= 3.0; RecalcXform=1; break; default: break; } break; default: /* any other key to pause */ getch(); break; } } } while (!Done); /* Return to text mode and exit */ regset.x.ax = 0x0003; /* AL = 3 selects 80x25 text mode */ int86(0x10, ®set, ®set); }