//------------------------------------------------------------------- // trytorus.cpp // // This prototype employs principles of perspective projection // to build and draw a wireframe model of a torus described by // // [C - sqrt( x^2 + z^2 )]^2 + y^2 = A^2 // // where C denotes the radius from the center of the torus's // hole and A denotes the radius of the torus's tube. (Thus // this ring torus is azimuthally symmetric about the y-axis.) // // compile using: $ g++ trytorus.cpp int86.cpp -o trytorus // // programmer: ALLAN CRUSE // date begun: 23 DEC 2005 // completion: 24 DEC 2005 //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for lseek() #include // for sqrt(), fabs(), sin(), cos() #include // for mmap() #include // for inb(), outb(), etc. #include "int86.h" // for init8086(), int86() #define PI 3.14159 unsigned char *vram = (unsigned char*)0xA0000000; int vesa_mode = 0x4103, hres = 800, vres = 600; struct vm86plus_struct vm; // function prototypes void create_the_model_vertices( void ); void create_the_model_polygons( void ); void setup_the_view_parameters( void ); void render_the_wireframe_view( void ); int main( int argc, char **argv ) { // prepare the problem data create_the_model_vertices(); create_the_model_polygons(); setup_the_view_parameters(); // 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); } // 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 torus render_the_wireframe_view(); getchar(); // leave graphics mode vm.regs.eax = 0x0003; int86( 0x10, vm ); printf( "\n" ); } #define N 32 #define MAXVERT N*N typedef float scalar_t; typedef struct { scalar_t x, y, z; } vector_t; typedef struct { int vert[ 4 ]; } face_t; typedef struct { int numverts; vector_t vert[ MAXVERT ]; int numquads; face_t quad[ MAXVERT ]; } model_t; const scalar_t A = 1.0; const scalar_t C = 2.0; const scalar_t theta = 2*PI/N; vector_t eye = { 6.0, 6.5, 8.0 }; model_t world; model_t view; scalar_t transX = hres/2, transY = vres/2; scalar_t scaleX = vres/8, scaleY = -vres/8; void normalize( vector_t &v ) { scalar_t 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; } } scalar_t dot_product( vector_t a, vector_t b ) { return a.x * b.x + a.y * b.y + a.z * b.z; } 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; } 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 create_the_model_vertices( void ) { // we will employ parametric equations for our torus // our outer loop subdivides the circle that generates // the torus tube into N equal-width sectors // our inner loop subdivides the circle that generates // the torus hole into N equal-width sectors vector_t vertex; for (int i = 0; i < N; i++) { scalar_t mu = i * theta; scalar_t rx = (C + A*cos( mu )); vertex.y = A*sin( mu ); for (int j = 0; j < N; j++) { scalar_t nu = j * theta; vertex.x = rx * cos( nu ); vertex.z = rx * sin( nu ); world.vert[ i*N + j ] = vertex; } } world.numverts = N*N; } void create_the_model_polygons( void ) { for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) { int ii = (i+1)%N; int jj = (j+1)%N; int v0 = i * N + j; int v1 = ii * N + j; int v2 = ii * N + jj; int v3 = i * N + jj; world.quad[ v0 ].vert[ 0 ] = v0; world.quad[ v0 ].vert[ 1 ] = v1; world.quad[ v0 ].vert[ 2 ] = v2; world.quad[ v0 ].vert[ 3 ] = v3; } world.numquads = N*N; } void setup_the_view_parameters( void ) { vector_t vrp = { 0, 0, 0 }; vector_t vup = { 0, 1, 0 }; vector_t vpn; vector_fromto( vrp, eye, vpn ); 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 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; } } void draw_pixel( int x, int y, int color ) { if (( x < 1 )||( x >= hres-1 )) return; if (( y < 1 )||( y >= vres-1 )) 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 ); } void render_the_wireframe_view( void ) { int color = 14; for (int i = 0; i < view.numquads; i++) // view.numquads { face_t quad = view.quad[ i ]; for (int j = 0; j < 4; j++) { int k = (j+1)%4; vector_t p0 = view.vert[ quad.vert[ j ] ]; vector_t p1 = view.vert[ quad.vert[ k ] ]; int x0 = (int)( p0.x * scaleX + transX ); int y0 = (int)( p0.y * scaleY + transY ); int x1 = (int)( p1.x * scaleX + transX ); int y1 = (int)( p1.y * scaleY + transY ); draw_line( x0, y0, x1, y1, color ); } } }