//------------------------------------------------------------------- // wfmodel1.cpp // // This is an initial prototype for our 3D wireframe modeling. // It reads a wireframe data-file, then computes a perspective // projection of the model's vertices and calls a line-drawing // routine to display the model's line-segments on the screen. // When a key is pressed, the viewer's eye-location is changed // and the entire perspective projection gets recomputed, then // is redrawn, unless it was the ESCAPE-key that was pressed. // // compile using: $ g++ wfmodel1.cpp int86.cpp -o wfmodel1 // // programmer: ALLAN CRUSE // written on: 11 OCT 2005 //------------------------------------------------------------------- #include // for printf(), perror() #include // for open(), fcntl() #include // for read(), lseek() #include // for exit() #include // for mmap() #include // for inb() #include // for sqrt() #include // for strncpy() #include // for tcsetattr() #include "int86.h" // for init8086(), int86() #define MAXVERT 50 #define MAXEDGE 50 typedef struct { float x, y, z; } vector_t; typedef int edge_t[ 2 ]; typedef struct { vector_t vrp, vpn, vup; } camera_t; typedef struct { int numverts; vector_t vert[ MAXVERT ]; int numedges; edge_t edge[ MAXEDGE ]; } model_t; unsigned char *vram = (unsigned char*)0xA0000000; const int vesa_mode = 0x4101, hres = 640, vres = 480; struct vm86plus_struct vm; double transX = 320.0, transY = 240.0; double scaleX = 100.0, scaleY = 100.0; double dot_product( vector_t a, vector_t b ) { return a.x * b.x + a.y * b.y + a.z * b.z; } void normalize( vector_t &v ) { double norm = sqrt( dot_product( v, v ) ); v.x /= norm; v.y /= norm; v.z /= norm; } void cross_product( vector_t u, vector_t v, vector_t &w ) { w.x = u.y * v.z - u.z * v.y; w.y = u.z * v.x - u.x * v.z; w.z = u.x * v.y - u.y * v.x; } void draw_pixel( int x, int y, int color ) { if (( x < 0 )||( x >= hres )) return; if (( y < 0 )||( y >= vres )) return; vram[ y*hres + x ] = color; } void draw_line( int x1, int y1, int x2, int y2, int color ) { int deltax = x2 - x1; int deltay = y2 - y1; int xinc = ( deltax < 0 ) ? -1 : 1; int yinc = ( deltay < 0 ) ? -1 : 1; if ( deltax < 0 ) deltax = - deltax; if ( deltay < 0 ) deltay = - deltay; if (( deltax == 0 )&&( deltay == 0 )) { draw_pixel( x1, y1, color ); return; } if ( deltay <= deltax ) // odd octant { int errorterm = - deltax; do { draw_pixel( x1, y1, color ); errorterm += 2*deltay; if ( errorterm >= 0 ) { y1 += yinc; errorterm -= 2*deltax; } x1 += xinc; } while ( x1 != x2 ); } else { int errorterm = - deltay; do { draw_pixel( x1, y1, color ); errorterm += 2*deltax; if ( errorterm >= 0 ) { x1 += xinc; errorterm -= 2*deltay; } y1 += yinc; } while ( y1 != y2 ); } } void draw_rectangle( int x, int y, int h, int v, int color ) { int minx = 0, miny = 0, maxx = hres-1, maxy = vres-1; do { vram[ y*hres + x ] = color; ++x; } while ( x < maxx ); do { vram[ y*hres + x ] = color; ++y; } while ( y < maxy ); do { vram[ y*hres + x ] = color; --x; } while ( x > minx ); do { vram[ y*hres + x ] = color; --y; } while ( y > miny ); } void fill_rectangle( int x, int y, int h, int v, int color ) { int minx = x, miny = y, maxx = x+h-1, maxy = y+v-1; for (y = miny; y <= maxy; y++) for (x = minx; x <= maxx; x++) vram[ y*hres + x ] = color; } int get_model_data( char *filename, model_t &model ) { FILE *fp = fopen( filename, "ra" ); if ( fp == NULL ) return -1; if ( fscanf( fp, "%d", &model.numverts ) != 1 ) return -1; for (int i = 0; i < model.numverts; i++) { if ( fscanf( fp, "%f", &model.vert[i].x ) != 1 ) return -1; if ( fscanf( fp, "%f", &model.vert[i].y ) != 1 ) return -1; if ( fscanf( fp, "%f", &model.vert[i].z ) != 1 ) return -1; } if ( fscanf( fp, "%d", &model.numedges ) != 1 ) return -1; for (int i = 0; i < model.numedges; i++) { if ( fscanf( fp, "%d", &model.edge[i][0] ) != 1 ) return -1; if ( fscanf( fp, "%d", &model.edge[i][1] ) != 1 ) return -1; } fclose( fp ); return 0; } void draw_model( model_t model, camera_t camera, vector_t eye ) { // setup the view coordinate-vectors vector_t n = camera.vpn; normalize( n ); vector_t v = camera.vup; normalize( v ); vector_t u; cross_product( v, n, u ); // transform points of the model to view-coordinates model_t view = model; for (int i = 0; i < model.numverts; i++) { vector_t p = model.vert[i]; p.x -= camera.vrp.x; p.y -= camera.vrp.y; p.z -= camera.vrp.z; view.vert[i].x = dot_product( u, p ); view.vert[i].y = dot_product( v, p ); view.vert[i].z = dot_product( n, p ); } // compute the distance from viewer's eye to the viewplane vector_t c = camera.vrp; c.x -= eye.x; c.y -= eye.y; c.z -= eye.z; double eyedist = sqrt( c.x * c.x + c.y * c.y + c.z * c.z ); // perform the perspective projection for (int i = 0; i < model.numverts; i++) { vector_t vertex = view.vert[i]; double hittime = 1.0 / ( 1.0 - vertex.z / eyedist ); view.vert[i].x *= hittime; view.vert[i].y *= hittime; view.vert[i].z = 0.0; } // clear the screen-window fill_rectangle( 1, 1, hres-2, vres-2, 0 ); // draw each edge of the wireframe model int color = 15; for (int i = 0; i < view.numedges; i++) { int j = view.edge[i][0]; int k = view.edge[i][1]; vector_t vj = view.vert[j]; vector_t vk = view.vert[k]; int x0 = (int)( vj.x * scaleX + transX ); int y0 = (int)( vj.y * scaleY + transY ); int x1 = (int)( vk.x * scaleX + transX ); int y1 = (int)( vk.y * scaleY + transY ); draw_line( x0, y0, x1, y1, color ); } } int main( int argc, char **argv ) { // setup filename of the wireframe data-file char filename[ 64 ] = "cube.dat"; // default if ( argc > 1 ) strncpy( filename, argv[1], 63 ); // read the data-file (and display a summary report) model_t model; if ( get_model_data( filename, model ) < 0 ) exit(1); printf( "\nfilename: \'%s\' ", filename ); printf( "\nnumverts=%d ", model.numverts ); printf( "\nnumedges=%d ", model.numedges ); printf( "\n" ); getchar(); // setup the i/o permission and video memory-mapping int fd = open( "/dev/vram", O_RDWR ); if ( fd < 0 ) { perror( "/dev/vram" ); return -1; } int size = lseek( fd, 0, SEEK_END ); int prot = PROT_READ | PROT_WRITE; int flag = MAP_FIXED | MAP_SHARED; if ( mmap( (void*)vram, size, prot, flag, fd, 0 ) == MAP_FAILED ) { perror( "mmap" ); return -1; } // enable raw keyboard input struct termios otty; tcgetattr( 0, &otty ); struct termios tty = otty; tty.c_lflag &= ~( ICANON | ECHO ); tty.c_cc[ VMIN ] = 1; tty.c_cc[ VTIME ] = 0; tcsetattr( 0, TCSAFLUSH, &tty ); // enter graphics mode init8086(); vm.regs.eax = 0x4F02; vm.regs.ebx = vesa_mode; int86( 0x10, vm ); // draw screen border int color = 15; draw_rectangle( 0, 0, hres, vres, color ); getchar(); // main loop to compute and draw perspective projections double t = 0.0; int done = 0; while ( !done ) { vector_t eye, vrp, vpn, vup; // compute the eye-position eye.x = 8.0 * sin( t ); eye.y = 0.0; eye.z = 8.0 * cos( t ); // setup the view-plane reference-point vrp.x = 0.0, vrp.y = 0.0, vrp.z = 0.0; // setup the view-plane normal vpn.x = eye.x, vpn.y = eye.y, vpn.z = eye.z; // setup the vector up-direction vup.x = 0.0, vup.y = -1.0, vup.z = 0.0; // adjust the camera and draw the model camera_t camera = { vrp, vpn, vup }; draw_model( model, camera, eye ); // await the user's keypress int inch = 0; read( 0, &inch, 4 ); if ( inch == '\e' ) done = 1; // increment the viewing angle t += 0.03; } // leave graphics mode vm.regs.eax = 0x0003; int86( 0x10, vm ); tcsetattr( 0, TCSAFLUSH, &otty ); }