/* Module : GAMEVID.C Author : Kim Moser, Leon Moser Date : System : IBM PC / Borland Turbo C 2.0 Descrip: Low-level routines for GAME.C */ #include #include #include #include "gameglob.h" #include "gamevid.h" static cardinal GRIDSTEP = 10; /* Must be a divisor of DIMENSION */ static cardinal DIMENSION = 150; /* Default */ #define SCALE 1 #define pi 3.141592 int DRIVER = DETECT, MODE = 0; static int RATIO_X, RATIO_Y, RATIO; static cardinal L; static cardinal MAX_X, MAX_Y; char *PILE = NULL; /* Pointer to (array [DIMENSION,DIMENSION,DIMENSION] of char) */ static cardinal CEN_X, CEN_Y; /* Coords of center of screen */ char *MIRROR = NULL; /* Background of area taken by block */ cardinal MIRROR_X, MIRROR_Y; /* Coordinates of top-left corner of where mirror was memorized from */ /*************************************************************************/ static void perspective(cardinal x, cardinal y, cardinal z, cardinal *a, cardinal *b); static void perspective(cardinal x, cardinal y, cardinal z, cardinal *a, cardinal *b) /* Given x, y, and z, returns (in 'a' and 'b') absolute screen coordinates of where it will be displayed (taking into account perspective, where 0,0,0 is farthest away from user) */ { #ifdef NOT_DEFINED c = 1732 * (x + y + z) / 1000; /* 172 comes from sqrt(3)*1000 */ *a = CEN_X + ((c) * (L * 87 * (y - x))) / 10000L; /* 866 comes from 1000*sqrt(3)/2 */ *b = CEN_Y + ((c) * (L * (x + y - (2 * z)) / 2)) / 100; #endif *a = CEN_X + (y-x)*sqrt(0.75); *b = CEN_Y + (x + y - 2*z) / 2; } static void perline(cardinal x1, cardinal y1, cardinal z1, cardinal x2, cardinal y2, cardinal z2); static void perline(cardinal x1, cardinal y1, cardinal z1, cardinal x2, cardinal y2, cardinal z2) /* Draws line from virtual coordinates [x1,y1,z1] to [x2,y2,z2], first converting all coordinates to proper absolute (screen) coordinates */ { cardinal a, b, c, d; perspective(x1, y1, z1, &a, &b); perspective(x2, y2, z2, &c, &d); line(a, b, c, d); } /*************************************************************************/ void getedgecoords(struct block *b, int i, cardinal *x1, cardinal *y1, cardinal *z1, cardinal *x2, cardinal *y2, cardinal *z2) { *x1 = b->v[b->e[i].v1].x; *y1 = b->v[b->e[i].v1].y; *z1 = b->v[b->e[i].v1].z; *x2 = b->v[b->e[i].v2].x; *y2 = b->v[b->e[i].v2].y; *z2 = b->v[b->e[i].v2].z; } void setedgeverts(struct block *b, int i, int v1, int v2) { b->e[i].v1 = v1; b->e[i].v2 = v2; } void setvertcoords(struct block *b, int i, cardinal x, cardinal y, cardinal z) { b->v[i].x = x; b->v[i].y = y; b->v[i].z = z; } void resetvert(struct block *b, int i, struct vertex *v) { b->v[i].x = v->x; b->v[i].y = v->y; b->v[i].z = v->z; } static void setcenter(struct block *b, cardinal x, cardinal y, cardinal z) { b->cm.x = x; b->cm.y = y; b->cm.z = z; } void getvertcoords(struct block *b, int i, struct vertex *v) { memcpy((void*)v, (void*)&(b->v[i]), sizeof(struct vertex)); } /************************************************************************/ void setupscreen(void) { cardinal x, y, z; float r; if (PILE != NULL) free(PILE); CEN_X = (MAX_X = (getmaxx() - 50)) / 2 - 50; CEN_Y = (MAX_Y = getmaxy()) / 2; L = CEN_Y / DIMENSION; getaspectratio(&RATIO_X, &RATIO_Y); r = (float)RATIO_X / (float)RATIO_Y; RATIO = (cardinal)(100 * r); /* closegraph(); printf("x=%d, y=%d, ratio=%d\n", x, y, RATIO); exit(-1); */ if ((PILE = (char*) malloc(DIMENSION*DIMENSION*DIMENSION)) == NULL) { halt("Not enough memory to allocate pile.\n"); } /* Draw grid: */ setcolor(GREEN); for (x=y=z=0; x<=DIMENSION; x+=GRIDSTEP) { setlinestyle(SOLID_LINE, SOLID_FILL, (x ? NORM_WIDTH : THICK_WIDTH)); perline(x, y, z, x, DIMENSION, z); perline(x, y, z, x, y, DIMENSION); } for (x=y=z=0; y<=DIMENSION; y+=GRIDSTEP) { setlinestyle(SOLID_LINE, SOLID_FILL, (y ? NORM_WIDTH : THICK_WIDTH)); perline(x, y, z, x, y, DIMENSION); perline(x, y, z, DIMENSION, y, z); } for (x=y=z=0; z<=DIMENSION; z+=GRIDSTEP) { setlinestyle(SOLID_LINE, SOLID_FILL, (z ? NORM_WIDTH : THICK_WIDTH)); perline(x, y, z, DIMENSION, y, z); perline(x, y, z, x, DIMENSION, z); } /* Do outside (dotted) lines: */ setcolor(RED); for (x=0; x<=MAX_X; x+=GRIDSTEP) { setlinestyle(DOTTED_LINE, SOLID_FILL, NORM_WIDTH); perline(x, (x > DIMENSION ? 0 : DIMENSION), 0, x, MAX_X, 0); perline(x, 0, (x > DIMENSION ? 0 : DIMENSION), x, 0, MAX_X); } for (y=0; y<=MAX_X; y+=GRIDSTEP) { setlinestyle(DOTTED_LINE, SOLID_FILL, NORM_WIDTH); perline((y > DIMENSION ? 0 : DIMENSION), y, 0, MAX_X, y, 0); perline(0, y, (y > DIMENSION ? 0 : DIMENSION), 0, y, MAX_X); } for (z=0; z<=MAX_X; z+=GRIDSTEP) { setlinestyle(DOTTED_LINE, SOLID_FILL, NORM_WIDTH); perline(0, (z > DIMENSION ? 0 : DIMENSION), z, 0, MAX_X, z); perline((z > DIMENSION ? 0 : DIMENSION), 0, z, MAX_X, 0, z); } /* Allocate room for temporary bitmap: */ if (MIRROR != NULL) free(MIRROR); /* Allocate room for temporary bitmap (must be at least as big as area covered by largest block): */ if ((MIRROR = (char far *) malloc(32767)) == NULL) { halt("gamevid.setupscreen(): malloc() returned NULL when allocating 32767 bytes for MIRROR."); } } void drawedge(struct block *b, int i) { cardinal x1, y1, z1, x2, y2, z2; getedgecoords(b,i,&x1,&y1,&z1,&x2,&y2,&z2); perline(x1/SCALE,y1/SCALE,z1/SCALE,x2/SCALE,y2/SCALE,z2/SCALE); } static void edgescreencoords(struct block *blk, int i, cardinal *a, cardinal *b, cardinal *c, cardinal *d) /* Returns screen coordinates of i'th edge of block 'b' */ { cardinal x1, y1, z1, x2, y2, z2; getedgecoords(blk,i,&x1,&y1,&z1,&x2,&y2,&z2); perspective(x1, y1, z1, a, b); perspective(x2, y2, z2, c, d); } static void blockarea(struct block *blk, cardinal *x1, cardinal *y1, cardinal *x2, cardinal *y2); static void blockarea(struct block *blk, cardinal *x1, cardinal *y1, cardinal *x2, cardinal *y2) /* Returns in 'x1', 'y1', 'x2', 'y2' the smallest rectangle that block 'b' will fit in */ { int i; cardinal a, b, c, d; *x1 = *y1 = 1000; /* !!! A large cardinal, > maxx and maxy */ *x2 = *y2 = 0; for (i=0; i < blk->edgecount; i++) { edgescreencoords(blk, i, &a, &b, &c, &d); if (a < *x1) *x1 = a; if (b < *y1) *y1 = b; if (c > *x2) *x2 = c; if (d > *y2) *y2 = d; } *x1 -= 1; *y1 -= 1; *x2 += 1; *y2 += 1; } void saveblockbackground(struct block *b); void saveblockbackground(struct block *b) /* Saves (into MIRROR) background of area taken by block 'b'. */ { cardinal x2, y2; blockarea(b, &MIRROR_X, &MIRROR_Y, &x2, &y2); getimage(MIRROR_X, MIRROR_Y, x2, y2, MIRROR); } void restorebackground(void); void restorebackground(void) /* Restores (from MIRROR) background to screen. Uses coordinates MIRROR_X and MIRROR_Y. */ { putimage(MIRROR_X, MIRROR_Y, MIRROR, COPY_PUT); } void drawblock(struct block *b, int c) { int i; setcolor(c); setlinestyle(SOLID_LINE, SOLID_FILL, THICK_WIDTH); for (i=0; i < b->edgecount; i++) { drawedge(b,i); } } void makerect(struct block *b, int h, int w, int d, int x, int y, int z) { /* unit block */ b->edgecount = 12; b->vercount = 8; /* setup edge vertex identifiers */ setedgeverts(b,0,0,1); setedgeverts(b,1,1,2); setedgeverts(b,2,3,2); setedgeverts(b,3,0,3); setedgeverts(b,4,4,0); setedgeverts(b,5,1,5); setedgeverts(b,6,2,6); setedgeverts(b,7,3,7); setedgeverts(b,8,5,4); setedgeverts(b,9,6,5); setedgeverts(b,10,7,6); setedgeverts(b,11,7,4); setvertcoords(b,0,x,y,z+d); setvertcoords(b,1,x,y+w,z+d); setvertcoords(b,2,x+h,y+w,z+d); setvertcoords(b,3,x+h,y,z+d); setvertcoords(b,4,x,y,z); setvertcoords(b,5,x,y+w,z); setvertcoords(b,6,x+h,y+w,z); setvertcoords(b,7,x+h,y,z); setcenter(b,x+h/2,y+w/2,z+d/2); } void makeblock(struct block *b, int h, int w, int d, int x, int y, int z) { makerect(b, h*GRIDSTEP, w*GRIDSTEP, d*GRIDSTEP, x*GRIDSTEP, y*GRIDSTEP, z*GRIDSTEP); saveblockbackground(b); } void rotateblock(struct block *b, int axis) { static int initialized=0; /* whether sin and cos tables are initialized */ static float sn[10], cs[10]; struct vertex v1, v2; struct block *old_b; int t, i; if ((old_b = (struct block *) malloc(sizeof(struct block))) == NULL) { halt("gamevid.rotateblock(): malloc() returned NULL."); } if (!initialized) { initialized = 1; for (t=0; t<10; t++) { sn[t] = sin((t+1)*pi/20); cs[t] = cos((t+1)*pi/20); } } memcpy(old_b, b, sizeof(struct block)); for (t=0; t<10; t++) { for (i=0; i < old_b->vercount; i++){ getvertcoords(old_b,i,&v1); switch (axis) { case 1: /* rotate about x axis */ v2.x = v1.x; v2.y = (v1.y - b->cm.y)*cs[t] - (v1.z - b->cm.z)*sn[t] + b->cm.y; v2.z = (v1.y - b->cm.y)*sn[t] + (v1.z - b->cm.z)*cs[t] + b->cm.z; break; case 2: /* rotate about y axis */ v2.x = (v1.x - b->cm.x)*cs[t] - (v1.z - b->cm.z)*sn[t] + b->cm.x; v2.y = v1.y; v2.z = (v1.x - b->cm.x)*sn[t] + (v1.z - b->cm.z)*cs[t] + b->cm.z; break; case 3: /* rotate about z axis */ v2.x = (v1.x - b->cm.x)*cs[t] - (v1.y - b->cm.y)*sn[t] + b->cm.x; v2.y = (v1.x - b->cm.x)*sn[t] + (v1.y - b->cm.y)*cs[t] + b->cm.y; v2.z = v1.z; break; default: halt("gamevid.rotateblock(): invalid axis."); break; } resetvert(b,i,&v2); } restorebackground(); /* Memorize area taken by newly rotated block: */ saveblockbackground(b); /* Draw block: */ drawblock(b, WHITE); } free(old_b); } void terminategamevid(void) { if (MIRROR != NULL) free(MIRROR); }