//------------------------------------------------------------------- // plato.cpp // // This program draws a raytraced image of one of the platonic // solids (the dodecahedron), based on data-points from a file // named 'plato.dat' which was also used in our 'filldemo.cpp' // program earlier. (Here we apply our mathematical algorithm // for testing whether a point lies inside or outside a convex // polyhedron, in order to determine if a ray "hits" a face of // of the dodecahedron.) The DAC Controller's color registers // are reprogrammed to display 64 different intensities of the // color yellow (equal parts of red and green, with no blue). // // compile using: g++ plato.cpp int86.cpp -o plato // // programmer: ALLAN CRUSE // date begun: 22 NOV 2003 // completion: 24 NOV 2003 // revised on: 20 NOV 2005 -- replace 'svgalib' with 'int86()' //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for read(), write(), close() #include // for tcgetattr(), tcsetattr() #include // for mmap() #include // for iopl() #include // for sqrt() #include "int86.h" // for init8086(), int86() typedef unsigned char PEL; unsigned char *vram = (unsigned char*)0xA0000000; int vesa_mode = 0x4101, hres = 640, vres = 480; struct vm86plus_struct vm; void draw_rectangle( int x, int y, int h, int v, int color ) { int minx = x, miny = y, maxx = x+h-1, maxy = y+v-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 ); } int main( int argc, char **argv ) { // map display memory into userspace 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; } struct termios otty; tcgetattr( 0, &otty ); struct termios tty = otty; tty.c_lflag &= ~( ICANON | ECHO | ISIG ); tty.c_cc[ VMIN ] = 1; tty.c_cc[ VTIME ] = 0; tcsetattr( 0, TCSAFLUSH, &tty ); init8086(); vm.regs.eax = 0x4F02; vm.regs.ebx = vesa_mode; int86( 0x10, vm ); int color = 15; draw_rectangle( 0, 0, hres, vres, color ); getchar(); void draw_raytraced_scene( void ); draw_raytraced_scene(); getchar(); vm.regs.eax = 0x4F02; vm.regs.ebx = 0x0003; int86( 0x10, vm ); tcsetattr( 0, TCSAFLUSH, &otty ); } //-------------- OUR RAY-TRACING CODE BEGINS HERE -------------- #define MAXVERT 30 #define MAXEDGE 30 #define MAXFACE 30 typedef float scalar_t; typedef struct { scalar_t x, y, z; } vector_t; typedef struct { vector_t base, perp; } plane_t; typedef struct { vector_t origin, toward; } ray_t; typedef int edge_t[ 2 ]; typedef struct { int numsides; int poly[ 10 ]; } face_t; typedef struct { int numverts; vector_t vert[ MAXVERT ]; int numedges; edge_t edge[ MAXEDGE ]; int numfaces; face_t face[ MAXFACE ]; int facecolor[ MAXFACE ]; } model_t; scalar_t transX = (float)hres/2; scalar_t transY = (float)vres/2; scalar_t scaleX = 100.0; scalar_t scaleY = -100.0; scalar_t Iambient = 24.0; scalar_t Idiffuse = 27.0; scalar_t Ispecular = 50.0; vector_t eye = { 1000.0, 0.0, 1800.0 }; vector_t light = { -1200.0, 900.0, 2000.0 }; model_t solid; plane_t polyface[ MAXFACE ]; void read_model_data( char *filename, model_t &model ) { FILE *fp = fopen( filename, "ra" ); if ( !fp ) { perror( filename ); exit(1); } fscanf( fp, "%d", &model.numverts ); for (int i = 0; i < model.numverts; i++) { vector_t vertex; fscanf( fp, "%f", &vertex.x ); fscanf( fp, "%f", &vertex.y ); fscanf( fp, "%f", &vertex.z ); model.vert[ i ] = vertex; } fscanf( fp, "%d", &model.numedges ); fscanf( fp, "%d", &model.numfaces ); for (int i = 0; i < model.numfaces; i++) { int n; fscanf( fp, "%d", &n ); model.face[i].numsides = n; for (int j = 1; j <= n; j++) fscanf( fp, "%d", &model.face[i].poly[j] ); model.face[i].poly[0] = model.face[i].poly[n]; fscanf( fp, "%d", &model.facecolor[i] ); } fclose( fp ); } scalar_t dot_product( vector_t p, vector_t q ) { return p.x*q.x + p.y*q.y + p.z*q.z; } void normalize( vector_t &v ) { double norm = sqrt( v.x*v.x + v.y*v.y + v.z*v.z ); v.x /= norm; v.y /= norm; v.z /= norm; } void vector_fromto( vector_t a, vector_t b, vector_t &c ) { c.x = b.x - a.x; c.y = b.y - a.y; c.z = b.z - a.z; } scalar_t dist( vector_t p, vector_t q ) { vector_t r; vector_fromto( p, q, r ); return sqrt( dot_product( r, r ) ); } scalar_t det( vector_t a, vector_t b, vector_t c ) { scalar_t sum = 0.0; sum += a.x * b.y * c.z; sum += a.y * b.z * c.x; sum += a.z * b.x * c.y; sum -= a.x * b.z * c.y; sum -= a.z * b.y * c.x; sum -= a.y * b.x * c.z; return sum; } void setup_color_palette( void ) { for (int i = 0; i < 64; i++) { outb( i, 0x3C8 ); outb( i, 0x3C9 ); outb( i, 0x3C9 ); outb( 0, 0x3C9 ); } } void construct_planes( model_t model, plane_t *plane ) { for (int i = 0; i < model.numfaces; i++) { int n = model.face[i].numsides; vector_t cent = { 0.0, 0.0, 0.0 }; for (int j = 0; j < n; j++) { int k = model.face[i].poly[j]; vector_t vert = model.vert[k]; cent.x += vert.x; cent.y += vert.y; cent.z += vert.z; } cent.x /= n; cent.y /= n; cent.z /= n; plane[ i ].base = cent; normalize( cent ); plane[ i ].perp = cent; } } int ray_hits_plane( ray_t ray, plane_t plane, vector_t &spot ) { vector_t v; // vector from eye to plane's base vector_t d; // vector from eye in ray's direction vector_t n; // vector normal to the plane vector_fromto( eye, plane.base, v ); d = ray.toward; n = plane.perp; scalar_t dot_vn = dot_product( v, n ); scalar_t dot_dn = dot_product( d, n ); if ( dot_dn >= 0.0 ) return 0; // hidden face removal if ( dot_vn * dot_dn <= 0.0 ) return 0; // ok, find spot where ray hits plane scalar_t t_hit = dot_vn / dot_dn; spot.x = ray.origin.x + ray.toward.x * t_hit; spot.y = ray.origin.y + ray.toward.y * t_hit; spot.z = ray.origin.z + ray.toward.z * t_hit; return 1; } int spot_on_face( vector_t spot, face_t face, model_t model ) { vector_t s = spot; vector_t a = model.vert[ face.poly[0] ]; for (int i = 1; i < face.numsides; i++) { vector_t b = model.vert[ face.poly[i] ]; vector_t c = model.vert[ face.poly[i+1] ]; scalar_t den = det( a, b, c ); if ( den == 0.0 ) continue; scalar_t c1 = det( s, b, c )/den; scalar_t c2 = det( a, s, c )/den; scalar_t c3 = det( a, b, s )/den; if (( c1 < 0.0 )||( c2 < 0.0 )||( c3 < 0.0 )) continue; return 1; } return 0; } PEL plane_color( vector_t light, vector_t spot, vector_t eye, plane_t plane, model_t solid ) { vector_t se; // unit-vector from spot to eye vector_fromto( spot, eye, se ); normalize( se ); vector_t ss; // unit-vector from spot to light-source vector_fromto( spot, light, ss ); normalize( ss ); vector_t nn = plane.perp; // unit-normal to plane normalize( nn ); // compute cosine of angle between ss and nn (for diffuse component) scalar_t dot_sn = dot_product( ss, nn ); // find the reflection rr of ss about nn vector_t rr; rr.x = -ss.x + 2*( nn.x * dot_sn ); rr.y = -ss.y + 2*( nn.y * dot_sn ); rr.z = -ss.z + 2*( nn.z * dot_sn ); normalize( rr ); // compute cosine of angle between rr and se (for specular component) scalar_t dot_re = dot_product( rr, se ); // calculate the intensities scalar_t cos_power = 1.0; cos_power *= dot_re; cos_power *= dot_re; cos_power *= dot_re; cos_power *= dot_re; cos_power *= dot_re; scalar_t intensity = Iambient; if ( dot_sn > 0.0 ) intensity += Idiffuse * dot_sn; if ( dot_re > 0.0 ) intensity += Ispecular * cos_power; return (PEL)intensity; } PEL pixel_color( ray_t ray ) { vector_t spot; for (int i = 0; i < 12; i++) { if (( ray_hits_plane( ray, polyface[i], spot ) ) &&( spot_on_face( spot, solid.face[i], solid ) )) return plane_color( light, spot, eye, polyface[i], solid ); } return 0; } void paint_the_scene( void ) { for (int v = 0; v < vres; v++) for (int h = 0; h < hres; h++) { vector_t pixel; pixel.x = (float)h; pixel.y = (float)v; pixel.z = (float)0; // transform pixel to world coordinates pixel.x -= transX; pixel.y -= transY; pixel.x /= scaleX; pixel.y /= scaleY; // construct ray from eye toward pixel ray_t ray; ray.origin = eye; vector_fromto( eye, pixel, ray.toward ); // set the pixel's color PEL color = pixel_color( ray ); vram[ v*hres + h ] = color; } } void draw_raytraced_scene( void ) { read_model_data( "plato.dat", solid ); construct_planes( solid, polyface ); setup_color_palette(); paint_the_scene(); }