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:
}
}
}