//------------------------------------------------------------------- // unitcube.cpp // // This program shows the unit cube in perspective projection. // // compile using: $ g++ unitcube.cpp int86.cpp -o unitcube // // programmer: ALLAN CRUSE // written on: 28 NOV 2005 // correction: 24 DEC 2005 -- changed the vrp and vpn vectors //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for lseek() #include // for sqrt(), fabs() #include // for mmap() #include // for inb(), outb(), etc. #include "int86.h" // for init8086(), int86() unsigned char *vram = (unsigned char*)0xA0000000; int vesa_mode = 0x4101, hres = 640, vres = 480; struct vm86plus_struct vm; // the types of data objects typedef float scalar_t; typedef struct { scalar_t x, y, z; } vector_t; typedef struct { int ends[ 2 ]; } edge_t; typedef struct { int numverts; vector_t vert[ 11 ]; int numedges; edge_t edge[ 15 ]; } model_t; // the problem's data objects model_t world = { 11, { { 0, 0, 0 }, { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 }, { 1, 1, 1 }, { 0, 1, 1 }, { 1, 0, 1 }, { 1, 1, 0 }, { 3, 0, 0 }, { 0, 3, 0 }, { 0, 0, 3 } }, 15, { { { 0, 8 } }, { { 0, 9 } }, { { 0, 10 } }, { { 0, 1 } }, { { 0, 2 } }, { { 0, 3 } }, { { 4, 5 } }, { { 4, 6 } }, { { 4, 7 } }, { { 7, 1 } }, { { 7, 2 } }, { { 6, 1 } }, { { 6, 3 } }, { { 5, 2 } }, { { 5, 3 } } } }; vector_t eye = { 3.0, 0.5, 4.0 }; // parameters for mapping the view to the device scalar_t transX = hres/2, transY = vres/2; scalar_t scaleX = vres/8, scaleY = -vres/8; void normalize( vector_t &v ) { double norm = sqrt( v.x * v.x + v.y * v.y + v.z * v.z ); if ( norm > 0.0 ) { v.x /= norm; v.y /= norm; v.z /= norm; } } void cross_product( vector_t a, vector_t b, vector_t &c ) { c.x = a.y * b.z - b.y * a.z; c.y = a.z * b.x - b.z * a.x; c.z = a.x * b.y - b.x * a.y; } void vector_fromto( vector_t from, vector_t to, vector_t &v ) { v.x = to.x - from.x; v.y = to.y - from.y; v.z = to.z - from.z; } scalar_t dot_product( vector_t a, vector_t b ) { return a.x * b.x + a.y * b.y + a.z * b.z; } 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 exchange( int &x1, int &y1, int &x2, int &y2 ) { int xx, yy; xx = x2; x2 = x1; x1 = xx; yy = y2; y2 = y1; y1 = yy; } void octant_odd( int x1, int y1, int x2, int y2, int color ) { // make sure x1 <= x2 if ( x1 > x2 ) exchange( x1, y1, x2, y2 ); int xinc = ( x2 > x1 ) ? 1 : -1; int yinc = ( y2 > y1 ) ? 1 : -1; int dx = (x2 - x1)*xinc; int dy = (y2 - y1)*yinc; int errorterm = -dx; for (int x = x1, y = y1; x <= x2; x++) { draw_pixel( x, y, color ); errorterm += (dy+dy); if ( errorterm >= 0 ) { y += yinc; errorterm -= (dx+dx); } } } void octant_even( int x1, int y1, int x2, int y2, int color ) { // make sure y1 <= y2 if ( y1 > y2 ) exchange( x1, y1, x2, y2 ); int xinc = ( x2 > x1 ) ? 1 : -1; int yinc = ( y2 > y1 ) ? 1 : -1; int dx = (x2 - x1)*xinc; int dy = (y2 - y1)*yinc; int errorterm = -dy; for (int x = x1, y = y1; y <= y2; y++) { draw_pixel( x, y, color ); errorterm += (dx+dx); if ( errorterm >= 0 ) { x += xinc; errorterm -= (dy+dy); } } } void draw_line( int x1, int y1, int x2, int y2, int color ) { // use the Bresenham Algorithm int deltaX = ( x1 < x2 ) ? x2-x1 : x1-x2; int deltaY = ( y1 < y2 ) ? y2-y1 : y1-y2; if ( deltaX > deltaY ) octant_odd( x1, y1, x2, y2, color ); else octant_even( x1, y1, x2, y2, color ); } int main( int argc, char **argv ) { // memory-map the display-memory into user-space int vd = open( "/dev/vram", O_RDWR ); if ( vd < 0 ) { perror( "/dev/vram" ); exit(1); } int size = lseek( vd, 0, SEEK_END ); int prot = PROT_READ | PROT_WRITE; int flag = MAP_FIXED | MAP_SHARED; if ( mmap( (void*)vram, size, prot, flag, vd, 0 ) == MAP_FAILED ) { perror( "mmap" ); exit(1); } // setup the view parameters vector_t vrp = { 0.5, 0.5, 0.5 }; // view reference-point vector_t vup = { 0, 1, 0 }; // vector upward-pointing vector_t vpn; // view-plane normal vector_fromto( vrp, eye, vpn ); // setup the view's coordinate-vectors vector_t u, v, n = vpn; normalize( n ); cross_product( vup, n, u ); normalize( u ); cross_product( n, u, v ); normalize( v ); // transform vertices of the model to camera-coordinates model_t view = world; for (int i = 0; i < view.numverts; i++) { vector_t p; vector_fromto( vrp, world.vert[ i ], p ); 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 distance from viewer's eye to the viewplane vector_t eo; vector_fromto( eye, vrp, eo ); scalar_t eyedist = sqrt( dot_product( eo, eo ) ); // perform the perspective projection for (int i = 0; i < view.numverts; i++) { vector_t vertex = view.vert[ i ]; scalar_t hittime = 1.0 / ( 1.0 - vertex.z / eyedist ); vertex.x *= hittime; vertex.y *= hittime; vertex.z *= 0.0; view.vert[ i ] = vertex; } // enter graphics mode init8086(); vm.regs.eax = 0x4F02; vm.regs.ebx = vesa_mode; int86( 0x10, vm ); // draw screen border int x = 0, y = 0, color = 15; do { vram[ y * hres + x ] = color; ++x; } while ( x < hres-1 ); do { vram[ y * hres + x ] = color; ++y; } while ( y < vres-1 ); do { vram[ y * hres + x ] = color; --x; } while ( x > 0 ); do { vram[ y * hres + x ] = color; --y; } while ( y > 0 ); getchar(); // draw the vertices in perspective projection for (int i = 0; i < view.numverts; i++) { vector_t vertex = view.vert[ i ]; x = (int)( vertex.x * scaleX + transX ); y = (int)( vertex.y * scaleY + transY ); draw_pixel( x, y, color ); } getchar(); // draw those edges which join vertex pairs for (int i = 0; i < view.numedges; i++) { vector_t v1 = view.vert[ view.edge[i].ends[ 0 ] ]; vector_t v2 = view.vert[ view.edge[i].ends[ 1 ] ]; color = ( i < 3 ) ? 11 : 14; int x1 = (int)( v1.x * scaleX + transX ); int x2 = (int)( v2.x * scaleX + transX ); int y1 = (int)( v1.y * scaleY + transY ); int y2 = (int)( v2.y * scaleY + transY ); draw_line( x1, y1, x2, y2, color ); } getchar(); // leave graphics mode vm.regs.eax = 0x0003; int86( 0x10, vm ); printf( "\n" ); }