abrash-black-book/51-03.md
2013-12-30 20:26:41 +11:00

162 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

**LISTING 51.1 L51-1.C**
/* 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++ in C compilation mode. */
#include <conio.h>
#include <dos.h>
#include <math.h>
#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, &regset, &regset);
}