/* %kw # %v %n %d %t # */ /* Version # 1 ANIMPOLY.C 12-Apr-94 15:57:52 # */ /***************************************************************/ /* METAGRAPHICS SOFTWARE CORPORATION (c) 1994 */ /* */ /* Description: This program is an example of how to do */ /* 3D polygon animation using page flipping */ /* */ /* 3D examples taken from "Flights of Fantasy" by */ /* Chistopher Lampton, Waite Group Press, ISBN 1-878739-18-2 */ /* An excellent book on 3D graphics, highly recommended. */ /* */ /***************************************************************/ #include #include #include #include #include #include "metawndo.h" /* master MetaWINDOW include file */ /* special stack size declaration if using Turbo or Borland C++ */ #ifdef TurboC extern unsigned _stklen = 14336U; /* stack size 14K */ #endif // Variable structures to hold shape data: typedef struct { // Structure for individual vertices long lx,ly,lz,lt; // Local coordinates of vertex long wx,wy,wz,wt; // World coordinates of vertex long sx,sy,st; // Screen coordinates of vertex } vertex_type; typedef struct { int number_of_vertices; // Number of vertices in polygon int *vertexList; // List of vertices in poly int color; // Color of polygon } polygon_type; typedef struct { int number_of_vertices; // Number of vertices in object int number_of_polygons; // Number of polygons in object polygon_type *polygon; // List of polygons in object vertex_type *vertex; // Array of vertices in object } object_type; // define a pyramid object // vertex list vertex_type pyramid_vtx[5] = { {-10, 10,-10,1, 0,0,0,1, 0,0,0}, // Vertex #0 { 10, 10,-10,1, 0,0,0,1, 0,0,0}, // Vertex #1 { 0,-10, 0,1, 0,0,0,1, 0,0,0}, // Vertex #2 { 10, 10, 10,1, 0,0,0,1, 0,0,0}, // Vertex #3 {-10, 10, 10,1, 0,0,0,1, 0,0,0} // Vertex #4 }; // polygon list int p1[] = { 1,2,0 }; int p2[] = { 3,2,1 }; int p3[] = { 4,2,3 }; int p4[] = { 0,2,4 }; int p5[] = { 4,3,1,0 }; polygon_type pyramid_ply[5] = { { 3, p1, Red }, { 3, p2, Green }, { 3, p3, Blue }, { 3, p4, White }, { 4, p5, Magenta } }; object_type pyramid = { 5, 5, pyramid_ply, pyramid_vtx }; // Function prototypes: void draw_object(object_type *object ); int backface(polygon_type *polygon, vertex_type *vertex); void transform(object_type *object); void project(object_type *object,int distance, int xorg, int yorg); void inittrans(void); void scale(float sf); void rotate(float ax,float ay,float az); void translate(int xt,int yt,int zt); void matmult(float result[4][4],float mat1[4][4],float mat2[4][4]); void matcopy(float dest[4][4],float source[4][4]); // Global transformation arrays: float matrix[4][4]; // Master transformation matrix float smat[4][4]; // Scaling matrix float zmat[4][4]; // Z rotation matrix float xmat[4][4]; // X rotation matrix float ymat[4][4]; // Y rotation matrix float tmat[4][4]; // Translation matrix void main() { int i, quit, page, x,y, sw; grafPort *scrnport; rect scrnR; float xangle, yangle, zangle, sf; int xpos, ypos, zpos; int zdelta; i = InitGraphics(VESA640x480X); if( i != 0 ) { printf( "MetaWINDOW InitGraphics error - %d\n",i ); exit(1); } /* get pointer to current (default) port */ GetPort( &scrnport ); ScreenRect( &scrnR ); /* extend the ports bitmap to cover both display pages */ SetBitmap( GrafPgAll, scrnport->portMap ); SetDisplay (GrafPg0); InitMouse (MsDriver); // enable tracking only to sync-up mouse position with // cursor position, after that we are going // to track the cursor ourselves in the frame loop TrackCursor( True ); TrackCursor(False); // no way to keep mouse from 'leaking' onto second page // (cursors always clip to bitmap limits only) // when it goes below first page, so we have to // limit the mouse position so it can never go off // the page LimitMouse (0, 0, scrnR.Xmax, scrnR.Ymax - 32 ); ShowCursor (); MoveCursor( -40 , -40 ); // set initial values xangle = yangle = zangle = 0; xpos = 0; ypos = 0; zpos = 0; sf = 1; zdelta = 1; page = 0; quit = False; while( !quit ) { if( kbhit() ) { i = getch(); i = toupper( i ); switch( i ) { case 'U': ypos--; break; case 'D': ypos++; break; case 'R': xpos++; break; case 'L': xpos--; break; case '1': sf = 1; break; case '2': sf = 2; break; case '3': sf = 3; break; default: quit = True; } } // advance rotation settings xangle += .02; yangle += .05; zangle += .1; // change z zpos += zdelta; if( zpos <= -300 || zpos > 300 ) zdelta = -zdelta; /* draw to page */ if( page ) MovePortTo( 0, scrnR.Ymax); else MovePortTo( 0, 0 ); inittrans(); // Initialize translations scale(sf); // Create scaling matrix rotate(xangle,yangle,zangle); // Create rotation matrices translate( xpos, ypos, zpos ); // Create translation matrix transform(&pyramid); // Transform OBJECT using MATRIX project(&pyramid,400, 320, 150 ); // Perform perspective projection // update page EraseRect( &scrnR ); draw_object(&pyramid); // track cursor by hand so that we can // explicitly move it from page to page ReadMouse( &x, &y, &sw ); /* display page */ WaitRetrace( inVideo ); // wait for in-display SetDisplay( GrafPg + page); // change it WaitRetrace( inRetrace ); // will take effect after this MoveCursor( x , y ); /* use other page */ if( page ) page = 0; else page = 1; } SetDisplay(TextPg); StopGraphics(); } void draw_object(object_type *object ) // Draw object in structure OBJECT { int p, n; polygon_type *poly; point pts[50]; // Loop through all polygons in object: for( p = 0; p < object->number_of_polygons; p++ ) { poly = &(object->polygon[p]); // remove any backface polygons if( backface( poly, object->vertex ) ) continue; PenColor( poly->color ); for( n = 0; n < poly->number_of_vertices; n++ ) { pts[n].X = object->vertex[ poly->vertexList[n] ].sx; pts[n].Y = object->vertex[ poly->vertexList[n] ].sy; } FillPolygon( pts, n, coordAbs, shapeConvex ); } } int backface(polygon_type *p, vertex_type *v ) { // Returns 0 if POLYGON is visible, -1 if not. // POLYGON must be part of a convex polyhedron vertex_type *v0,*v1,*v2; // Pointers to three vertices long z; // Point to vertices: v0 = &v[ p->vertexList[0] ]; v1 = &v[ p->vertexList[1] ]; v2 = &v[ p->vertexList[2] ]; z = (v1->sx-v0->sx)*(v2->sy-v0->sy)-(v1->sy-v0->sy)*(v2->sx-v0->sx); return( z >= 0); } void transform(object_type *object) { // Multiply all vertices in OBJECT with master transformation matrix: int v; vertex_type *vptr; for( v = 0; v < object->number_of_vertices; v++) { vptr = &(object->vertex[v]); vptr->wx = vptr->lx * matrix[0][0] + vptr->ly * matrix[1][0] +vptr->lz*matrix[2][0]+matrix[3][0]; vptr->wy=vptr->lx*matrix[0][1]+vptr->ly*matrix[1][1] +vptr->lz*matrix[2][1]+matrix[3][1]; vptr->wz=vptr->lx*matrix[0][2]+vptr->ly*matrix[1][2] +vptr->lz*matrix[2][2]+matrix[3][2]; } } void project(object_type *object,int distance, int xorg, int yorg) { // Project object onto screen with perspective projection int v; vertex_type *vptr; for( v = 0; v < object->number_of_vertices; v++) { // Loop through vertices vptr = &(object->vertex[v]); // Point to current vertex vptr->sx = distance*vptr->wx/(distance-vptr->wz)+xorg; // ...divide world x&y coords vptr->sy = distance*vptr->wy/(distance-vptr->wz)+yorg; // ... by z coordinates } } // Transformation functions: void inittrans() { // Initialize master transformation matrix to the identity matrix matrix[0][0]=1; matrix[0][1]=0; matrix[0][2]=0; matrix[0][3]=0; matrix[1][0]=0; matrix[1][1]=1; matrix[1][2]=0; matrix[1][3]=0; matrix[2][0]=0; matrix[2][1]=0; matrix[2][2]=1; matrix[2][3]=0; matrix[3][0]=0; matrix[3][1]=0; matrix[3][2]=0; matrix[3][3]=1; } void scale(float sf) { float mat[4][4]; // Initialize scaling matrix: smat[0][0] = sf; smat[0][1] = 0; smat[0][2] = 0; smat[0][3] = 0; smat[1][0] = 0; smat[1][1] = sf; smat[1][2] = 0; smat[1][3] = 0; smat[2][0] = 0; smat[2][1] = 0; smat[2][2] = sf; smat[2][3] = 0; smat[3][0] = 0; smat[3][1] = 0; smat[3][2] = 0; smat[3][3] = 1; // Concatenate with master matrix: matmult(mat,smat,matrix); matcopy(matrix,mat); } void rotate(float ax,float ay,float az) { // Create three rotation matrices that will rotate an object // AX radians on the X axis, AY radians on the Y axis and // AZ radians on the Z axis float mat1[4][4]; float mat2[4][4]; // Initialize X rotation matrix: xmat[0][0]=1; xmat[0][1]=0; xmat[0][2]=0; xmat[0][3]=0; xmat[1][0]=0; xmat[1][1]=cos(ax); xmat[1][2]=sin(ax); xmat[1][3]=0; xmat[2][0]=0; xmat[2][1]=-sin(ax); xmat[2][2]=cos(ax); xmat[2][3]=0; xmat[3][0]=0; xmat[3][1]=0; xmat[3][2]=0; xmat[3][3]=1; // Concatenate this matrix with master matrix: matmult(mat1,xmat,matrix); // Initialize Y rotation matrix: ymat[0][0]=cos(ay); ymat[0][1]=0; ymat[0][2]=-sin(ay); ymat[0][3]=0; ymat[1][0]=0; ymat[1][1]=1; ymat[1][2]=0; ymat[1][3]=0; ymat[2][0]=sin(ay); ymat[2][1]=0; ymat[2][2]=cos(ay); ymat[2][3]=0; ymat[3][0]=0; ymat[3][1]=0; ymat[3][2]=0; ymat[3][3]=1; // Concatenate this matrix with master matrix: matmult(mat2,ymat,mat1); // Initialize Z rotation matrix: zmat[0][0]=cos(az); zmat[0][1]=sin(az); zmat[0][2]=0; zmat[0][3]=0; zmat[1][0]=-sin(az); zmat[1][1]=cos(az); zmat[1][2]=0; zmat[1][3]=0; zmat[2][0]=0; zmat[2][1]=0; zmat[2][2]=1; zmat[2][3]=0; zmat[3][0]=0; zmat[3][1]=0; zmat[3][2]=0; zmat[3][3]=1; // Concatenate this matrix with master matrix: matmult(matrix,zmat,mat2); } void translate(int xt,int yt,int zt) { // Create a translation matrix that will translate an object an // X distance of XT, a Y distance of YT, and a Z distance of ZT // from the screen origin float mat[4][4]; tmat[0][0]=1; tmat[0][1]=0; tmat[0][2]=0; tmat[0][3]=0; tmat[1][0]=0; tmat[1][1]=1; tmat[1][2]=0; tmat[1][3]=0; tmat[2][0]=0; tmat[2][1]=0; tmat[2][2]=1; tmat[2][3]=0; tmat[3][0]=xt; tmat[3][1]=yt; tmat[3][2]=zt; tmat[3][3]=1; // Concatenate with master matrix: matmult(mat,matrix,tmat); matcopy(matrix,mat); } void matmult(float result[4][4],float mat1[4][4],float mat2[4][4]) { // Multiply matrix MAT1 by matrix MAT2, returning the result in RESULT int i, j, k; for( i=0; i<4; i++) for( j=0; j<4; j++) { result[i][j]=0; for( k=0; k<4; k++) result[i][j]+=mat1[i][k] * mat2[k][j]; } } void matcopy(float dest[4][4],float source[4][4]) { // Copy matrix SOURCE to matrix DEST int i, j; for( i=0; i<4; i++) for( j=0; j<4; j++) dest[i][j]=source[i][j]; }