import java.applet.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.URL; /** * SimpleTracer implements a thread that performs the ray * tracing. * * A separate thread is used so that we can periodically release to * another thread that draws the current state of the ray-tracing. * * @author Anthony Steed * @version 1.0 */ class SimpleTracer extends Thread { /* The scene to trace */ private Scene scene; /* The image to draw to */ private Image image; /* Dimensions of the image */ private int sceneWidth; private int sceneHeight; /* The defined camera */ SimpleCamera camera; Graphics imageGraphics; /** * Flag becomes true when the ray tracing * terminates. Is false until this point. */ public boolean finished; /** * Create a tracing thread * @param s the scene to trace * @param w the width of the image and camera * @param h the height of the image and camera * @param c the SimpleCamera defining the view * @param i the image to render to */ public SimpleTracer(Scene s, int w, int h, SimpleCamera c, Image i) { scene = s; sceneWidth = w; sceneHeight = h; camera = c; image = i; imageGraphics = image.getGraphics(); finished=false; } /** * Method that performs the actual computation * * Once this terminates the field finished will be set to * true */ public void run() { Colour col=new Colour(); System.out.println("SimpleTracer started\n"); for (int j = 0; j < sceneHeight; j++) { for (int i = 0; i < sceneWidth; i++) { Ray ray = camera.ray(i,j); if (scene.intersect(ray, col,0)) { imageGraphics.setColor(col.getColor()); } else { imageGraphics.setColor(Color.cyan); } imageGraphics.drawRect(i,j,1,1); } } System.out.println("SimpleTracer is done\n"); finished=true; } } /** * SimpleRay implements the a framework for the ray-tracing. * * It operates as both an application or an applet. It also * implements a thread which periodically forces a screen repaint so * that the user is kept aware of how far the ray-tracing has * proceeded. * * @author Anthony Steed * @version 1.0 */ public class SimpleRay extends Applet implements KeyListener, Runnable { /* We ray case to this offscreen image and periodically draw this to the window */ Image offScreen; /* Dimensions of the screen and image */ private static int screenWidth=300; private static int screenHeight=300; /* Thread to periodically force a repaint */ Thread kicker = null; /* Thread to do actual ray trace */ SimpleTracer tracer = null; /* Scene to ray trace */ Scene scene; /* Camera to specify view on the scene */ SimpleCamera camera; /** * The main method is called when an application version starts. * * We create an applet and attach it to a new Frame so that it is * visible on the screen. * * The scene to read is taken from the 1st argument on the command * line. */ public static void main (String args[]) { Frame f = new Frame("SimpleRay"); SimpleRay tmp = new SimpleRay(); String sceneName = null; SceneReader is = null; f.add("Center", tmp); f.setSize(screenWidth+30,screenHeight+30); f.show(); tmp.addKeyListener(tmp); // Find the input file from the args list and pass to if (args.length > 0) { sceneName = args[0]; } else { sceneName = "simplescene.dat"; } // Attempt to open the given file try { is = new SceneReader(new File(sceneName)); } catch (Exception e) { System.out.println("Could not load file " + sceneName + " from the given document base"); return; } // Now we have the reader intialised open the file via initCommon System.out.println("Opening scene " + sceneName); if (!tmp.initCommon(is)) { System.out.println("Could not open or read scene"); return; } // Finally in the application context, we start the applet we // created tmp.start(); } /** * Method for applet initialise * * The scene to read is taken from the "scene" property in the * applet tag on the web page. */ public synchronized void init() { String sceneName = null; SceneReader is=null; // Read parameters from applet sceneName = getParameter("scene"); if (sceneName == null) { sceneName = "simplescene.dat"; } // Try to load scene file via URL try { is = new SceneReader(new URL(getDocumentBase(), sceneName)); } catch (Exception e) { System.out.println("Could not open file " + sceneName + " from the given document base:\n" + e.getMessage()); } // Now we have the reader intialised open the file via initCommon System.out.println("Opening scene " + sceneName); if (!initCommon(is)) { System.out.println("Could not open or read scene\n"); } } /** * Initialisation that is common to both applet and application * contexts. * @return true if successful, false if not */ private boolean initCommon(SceneReader is) { // Read scene file scene = new Scene(); try { scene.read(is); } catch (Exception e) { System.out.println("Could not open scene:\n" + e.getMessage()); return false; } // Print scene to stdout - disable this if you wish try { SceneWriter tmp = new SceneWriter(System.out); scene.print(tmp); tmp.flush(); } catch (Exception e) { System.out.println("Could not write scene to stdout:\n" + e.getMessage()); return false; } // Create camera camera = new SimpleCamera(screenWidth,screenHeight); camera.setVPWindow(-2.0,2.0,-2.0,2.0); camera.setCOP(2.0); // Misc setup setBackground(Color.black); return true; } /** * Run method is called by the "kicker" thread. * * Forces a re-draw every 500ms. It terminates as soon as the * tracing thread exits. */ public synchronized void run() { while((tracer !=null) && !tracer.finished) { repaint(); try { Thread.sleep(500); } catch(Exception e) { ; } } repaint(); } /** * Method called to start an applet after initialisation. * * If we started the program via a java application we must call * this ourselves. */ public void start() { // Start the tracing thread if(tracer == null) { offScreen = createImage(screenWidth,screenHeight); tracer = new SimpleTracer(scene, screenWidth, screenHeight, camera, offScreen); tracer.start(); } // Start the drawing thread, this one must be started second if(kicker == null) { kicker = new Thread(this); kicker.start(); kicker.setPriority(5); } } /** * Method called to stop an applet after initialisation. */ public void stop() { kicker = null; tracer = null; } public void update(Graphics g) { paint(g); } /** * Paints the screen */ public void paint(Graphics g) { if (offScreen!=null) { g.drawImage(offScreen,0,0,this); } } /* * Keyboard interaction */ public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); switch (key) { case KeyEvent.VK_Q: case KeyEvent.VK_ESCAPE: System.exit(0); break; default: } } }