import java.util.BitSet; /** Ray tracer camera (observer) * (tbd split off Lens/Projection class) * @author: Phil Gage */ abstract class AbstractCamera { World world; RayTrace raytracer; FrameBuffer framebuffer; TemporalBitmap bitmap; //Temporal coherence int width, height; Point eye = new Point(0.0,0.0,0.0); //observer location Vector3 look = new Vector3(0.0,0.0,1.0); //view direction Vector3 up= new Vector3(0.0,-1.0,0.0); //world up direction double az = 0.0; // view azimuth double el = 0.0; // view elevation double oldaz = 0.0; // previous view azimuth double oldel = 0.0; // previous view elevation boolean oldactive = true; double fov = 45.0*Constants.DTOR; // horiz field of view in radians double vfov; // vert field of view in radians int bitmapWidth, bitmapHeight; Ray tempRay = new Ray(); boolean debug = false; //true for debug print output boolean active = true; //true for redraw boolean redraw = true; //true for total redraw boolean fileOutput = true; //true for TGA file output boolean equalArea = true; //true for fast pan capability boolean fastPan = true; //true for fast pan on // boolean fastPan = false; // Flags to control fast preview and acceleration // boolean doShadows = true; // boolean doReflection = true; // boolean doRefraction = true; // boolean doGrid = true; // boolean doBitmap = true; AbstractCamera (World world, int width, int height) { this.world = world; //save the world! this.width = width; this.height = height; this.vfov = this.fov*height/width; raytracer = new RayTrace (world, width, height); framebuffer = new FrameBuffer(width, height); } /** Enable temporal coherence acceleration */ void enableTemporalCoherence (int rows, int cols) { if (world.grid == null) System.out.println("Error, must use World(grid) " + "before enabling temporal coherence"); else { bitmap = new TemporalBitmap (world.grid, rows, cols, width, height); raytracer.bitmap = bitmap; //fix bitmapWidth = width / cols; bitmapHeight = height / rows; } } /** Pan camera to look vector direction */ void pan (Vector3 look) { this.look = look; // this.az = tbd // this.el = tbd update(); } /** Pan camera to look at point in scene */ void pan (Point point) { Vector3 dir = new Vector3(point); dir.subtract(eye); dir.normalize(); pan(dir); //pan to look vector } /** Pan camera to azimuth, elevation in degrees */ void pan (double az, double el) { if (az > 360.0) az -= 360.0; if (az < -360.0) az += 360.0; if (el >= 89.0) el = 89.0; if (el <= -89.0) el = -89.0; oldaz = this.az; oldel = this.el; this.az = az*Constants.DTOR; this.el = el*Constants.DTOR; // this.look = new Vector3( ,-sin(this.el), ); //tbd update(); } /** Pan camera in pixels (for viewport autopan) */ void pan (int az, int el) { //tbd } /** Zoom camera to field of view in degrees */ void zoom (double fov) { this.fov = fov*Constants.DTOR; this.vfov = this.fov*height/width; update(); redraw = true; } /** Move camera to new location */ void translate (Point eye) { this.eye = eye; redraw = true; } /** Get pixel color from framebuffer, or trace if needed, used by View to sample pixels for image-based rendering */ void getPixel (int x, int y, Color color) { if (x < 0 || x >= width || y < 0 || y >= height) // tbd could raytrace and not save if proj allows? color.set(Color.BLACK); else { // If pixel not cached, ray trace and save if (!framebuffer.validPixel (x, y)) { primaryRay (x,y,tempRay); raytracer.trace (tempRay,color,1); framebuffer.setPixel (x,y,color); ++Statistics.pixelCount; } else framebuffer.getPixel (x,y,color); } } /** Fast pan image and render new area, this is a new algorithm for this thesis */ void panImage (int xshift, int yshift) { // Shift image pixels in framebuffer // This assumes Cylindrical projection framebuffer.shift(xshift,yshift); // If temporal coherence, shift and update bitmap if (bitmap != null) bitmap.shift(xshift,yshift); // If image shifted to right (pan left) if (xshift > 0) { // Redraw new strip at left edge renderArea (0,0,xshift,height); // Check for vertical Y shift if (yshift > 0) renderArea (xshift,0,width-xshift,yshift); else renderArea (xshift,height+yshift,width-xshift,-yshift); } // Else image shifted to left (pan right) else { // Redraw new strip at right edge renderArea (width+xshift,0,-xshift,height); // Check for vertical Y shift if (yshift > 0) renderArea (0,0,width+xshift,yshift); else renderArea (0,height+yshift,width+xshift,-yshift); } } /** Render specified area of image */ void renderArea (int xmin, int ymin, int xsize, int ysize) { if (debug) System.out.println("renderArea " + xmin + " " + ymin + " " + xsize + " " + ysize); Ray ray = new Ray(); Color color = new Color(); // If camera active, ray trace each pixel, else set null color for (int y=ymin; ywidth-1 || y<0 || y>height-1) { if (debug) System.out.println("renderArea bad x y=" + x + " " + y); } else if (active) { //tbd move up primaryRay (x,y,ray); raytracer.trace (ray,color,1); framebuffer.setPixel (x,y,color); ++Statistics.pixelCount; } else framebuffer.erasePixel (x,y); } } } /** Erase whole image after redraw when idle camera */ void eraseImage () { if (debug) System.out.println("eraseImage "); for (int y=0; y= width || Math.abs(yshift) >=height) redraw = true; // If full redraw needed or no fast pan, draw whole image if (redraw || (!coherence && objectChanged) || (panned && (!fastPan || !equalArea))) { if (coherence) bitmap.clearBitmaps (); if (active) renderArea (0,0,width,height); else eraseImage(); } // Else full redraw not needed, draw only changed parts else { // If pan needed, shift image and draw only new part if (panned) { panImage(xshift,yshift); } // If temporal coherence on, redraw animated areas if (coherence) { changed = bitmap.getChanged (); for (int i=0; i width) { w = width - x; } if (y + h > height) { h = height - y; } renderArea (x,y,w,h); } } } // If just reactivated, retrace all null pixels that // were set null by pan or animation while inactive if (activated) { if (debug) System.out.println("activated!"); updateImage(); } } // If file output turned on, write frame TGA file if (fileOutput) output(frame); // Save values for next frame redraw = false; oldaz = az; oldel = el; oldactive = active; } /** Output current frame TGA file */ void output (int frame) { framebuffer.savetga ("frame" + frame + ".tga"); } /** Get primary ray from eye thru pixel */ abstract void primaryRay (int x, int y, Ray r); /** Override to update data after transform if needed */ void update() {} /** Override for animation */ void animate (int frame) {} }